static ag_bool insertEnvVal(char * pzBuf, int bufSize, char const * pzName, char const * pzProgPath) { char* pzDir = pzBuf; for (;;) { int ch = (int)*++pzName; if (! IS_VALUE_NAME_CHAR(ch)) break; *(pzDir++) = (char)ch; } if (pzDir == pzBuf) return AG_FALSE; *pzDir = NUL; pzDir = getenv(pzBuf); /* * Environment value not found -- skip the home list entry */ if (pzDir == NULL) return AG_FALSE; if (strlen(pzDir) + 1 + strlen(pzName) >= bufSize) return AG_FALSE; sprintf(pzBuf, "%s%s", pzDir, pzName); return AG_TRUE; }
static bool add_env_val(char * buf, int buf_sz, char const * name) { char * dir_part = buf; for (;;) { int ch = (int)*++name; if (! IS_VALUE_NAME_CHAR(ch)) break; *(dir_part++) = (char)ch; } if (dir_part == buf) return false; *dir_part = NUL; dir_part = getenv(buf); /* * Environment value not found -- skip the home list entry */ if (dir_part == NULL) return false; if (strlen(dir_part) + 1 + strlen(name) >= (unsigned)buf_sz) return false; sprintf(buf, "%s%s", dir_part, name); return true; }
/** * "txt" points to a "<?" sequence. * We handle "<?program" and "<?auto-options" directives. * All others are treated as comments. * * @param[in,out] opts program option descriptor * @param[in] txt scanning pointer * @returns the next character to look at */ static char * handle_directive(tOptions * opts, char * txt) { # define DIRECTIVE_TABLE \ _dt_(zCfgProg, program_directive) \ _dt_(zCfgAO_Flags, aoflags_directive) typedef char * (directive_func_t)(tOptions *, char *); # define _dt_(_s, _fn) _fn, static directive_func_t * dir_disp[] = { DIRECTIVE_TABLE }; # undef _dt_ # define _dt_(_s, _fn) 1 + static int const dir_ct = DIRECTIVE_TABLE 0; static char const * dir_names[DIRECTIVE_TABLE 0]; # undef _dt_ int ix; if (dir_names[0] == NULL) { ix = 0; # define _dt_(_s, _fn) dir_names[ix++] = _s; DIRECTIVE_TABLE; # undef _dt_ } for (ix = 0; ix < dir_ct; ix++) { size_t len = strlen(dir_names[ix]); if ( (strncmp(txt + 2, dir_names[ix], len) == 0) && (! IS_VALUE_NAME_CHAR(txt[len+2])) ) return dir_disp[ix](opts, txt + len + 2); } /* * We don't know what this is. Skip it. */ txt = strchr(txt+2, '>'); if (txt != NULL) txt++; return txt; # undef DIRECTIVE_TABLE }
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * It may be a number, a name, a keyword or garbage. * Figure out which. */ static char* assembleName(char * pzScan, te_dp_event * pRetVal) { /* * Check for a number. * Scan it in and advance "pzScan". */ if ( IS_DEC_DIGIT_CHAR(*pzScan) || ( (*pzScan == '-') && IS_DEC_DIGIT_CHAR(pzScan[1]) ) ) { pz_token = pzScan; (void)strtol(pzScan, &pzScan, 0); *pRetVal = DP_EV_NUMBER; return pzScan; } if (! IS_UNQUOTABLE_CHAR(*pzScan)) AG_ABEND(aprf("%s Error: Invalid input char '%c' in %s on line %d\n", pzProg, *pzScan, pCurCtx->pzCtxFname, pCurCtx->lineNo)); { unsigned char* pz = (unsigned char*)pzScan; while (IS_VALUE_NAME_CHAR(*pz)) pz++; if (IS_UNQUOTABLE_CHAR(*pz)) { *pRetVal = DP_EV_OTHER_NAME; while (IS_UNQUOTABLE_CHAR(*++pz)) ; } else *pRetVal = DP_EV_VAR_NAME; /* * Return a NAME token, maybe. * If the name is actually a keyword, * we will return that token code instead. */ pz_token = pzScan; pzScan = (char*)pz; } /* * Now scan the keyword table. */ if (*pRetVal == DP_EV_VAR_NAME) { char sv_ch = *pzScan; /* preserve the following character */ int kw_ix = 0; *pzScan = NUL; /* NUL terminate the name */ do { if (streqvcmp(apzKeywords[ kw_ix ], (char*)pz_token) == 0) { /* * Return the keyword token code instead of DP_EV_NAME */ *pRetVal = aKeywordTkn[ kw_ix ]; break; } } while (++kw_ix < KEYWORD_CT); *pzScan = sv_ch; /* restore the following character */ } return pzScan; }
/* * canonicalizeName: remove white space and roughly verify the syntax. * This procedure will consume everything from the source string that * forms a valid AutoGen compound definition name. * We leave legally when: * 1. the state is "CN_NAME_ENDED", AND * 2. We stumble into a character that is not either '[' or name_sep_ch * (always skipping white space). * We start in CN_START. */ LOCAL int canonicalizeName(char* pzD, char const* pzS, int srcLen) { typedef enum { CN_START_NAME = 0, /* must find a name */ CN_NAME_ENDED, /* must find '[' or name_sep_ch or we end */ CN_INDEX, /* must find name, number, '$' or ']' */ CN_INDEX_CLOSE, /* must find ']' */ CN_INDEX_ENDED /* must find name_sep_ch or we end */ } teConState; teConState state = CN_START_NAME; char const* pzOri = pzS; char* pzDst = pzD; size_t stLen = srcLen; /* * Before anything, skip a leading name_sep_ch as a special hack * to force a current context lookup. */ pzS = SPN_WHITESPACE_CHARS(pzS); if (pzOri != pzS) { srcLen -= pzS - pzOri; if (srcLen <= 0) pzS = zNil; } if (*pzS == name_sep_ch) { *(pzD++) = name_sep_ch; pzS++; if (--srcLen <= 0) pzS = zNil; } nextSegment: /* * The next segment may always start with an alpha character, * but an index may also start with a number. The full number * validation will happen in find_by_index(). */ { char * p = SPN_WHITESPACE_CHARS(pzS); if (p != pzS) { srcLen -= p - pzS; if (srcLen <= 0) pzS = zNil; pzS = p; } } switch (state) { case CN_START_NAME: if (! IS_VAR_FIRST_CHAR(*pzS)) return bad_def_name(pzDst, pzOri, stLen); state = CN_NAME_ENDED; /* we found the start of our first name */ break; /* fall through to name/number consumption code */ case CN_NAME_ENDED: switch (*pzS++) { case '[': *(pzD++) = '['; state = CN_INDEX; break; case '.': case '/': if (pzS[-1] == name_sep_ch) { *(pzD++) = name_sep_ch; state = CN_START_NAME; break; } default: /* legal exit -- we have a name already */ *pzD = NUL; return srcLen; } if (--srcLen <= 0) return bad_def_name(pzDst, pzOri, stLen); goto nextSegment; case CN_INDEX: /* * An index. Valid syntaxes are: * * '[' <#define-d name> ']' * '[' <number> ']' * '[' '$' ']' * '[' ']' * * We will check for and handle the last case right here. * The next cycle must find the index closer (']'). */ state = CN_INDEX_CLOSE; /* * Numbers and #define-d names are handled at the end of the switch. * '$' and ']' are handled immediately below. */ if (IS_ALPHANUMERIC_CHAR(*pzS)) break; /* * A solitary '$' is the highest index, whatever that happens to be * We process that right here because down below we only accept * name-type characters and this is not VMS. */ if (*pzS == '$') { if (--srcLen < 0) return bad_def_name(pzDst, pzOri, stLen); *(pzD++) = *(pzS++); goto nextSegment; } /* FALLTHROUGH */ case CN_INDEX_CLOSE: /* * Nothing else is okay. */ if ((*(pzD++) = *(pzS++)) != ']') return bad_def_name(pzDst, pzOri, stLen); if (--srcLen <= 0) { *pzD = NUL; return srcLen; } state = CN_INDEX_ENDED; goto nextSegment; case CN_INDEX_ENDED: if ((*pzS != name_sep_ch) || (--srcLen < 0)) { *pzD = NUL; return srcLen; } *(pzD++) = *(pzS++); state = CN_START_NAME; goto nextSegment; } /* * The next state must be either looking for what comes after the * end of a name, or for the close bracket after an index. * Whatever, the next token must be a name or a number. */ assert((state == CN_NAME_ENDED) || (state == CN_INDEX_CLOSE)); assert(IS_ALPHANUMERIC_CHAR(*pzS)); /* * Copy the name/number. We already know the first character is valid. * However, we must *NOT* downcase #define names... */ while (IS_VALUE_NAME_CHAR(*pzS)) { char ch = *(pzS++); if ((state != CN_INDEX_CLOSE) && IS_UPPER_CASE_CHAR(ch)) *(pzD++) = tolower(ch); else switch (ch) { /* force the separator chars to be '_' */ case '-': case '^': *(pzD++) = '_'; break; default: *(pzD++) = ch; } if (--srcLen <= 0) { pzS = zNil; break; } } goto nextSegment; }
/** * "pzText" points to a '<' character, followed by an alpha. * The end of the entry is either the "/>" following the name, or else a * "</name>" string. */ static char * handle_struct(tOptions * pOpts, tOptState * pOS, char * pzText, int dir) { tOptionLoadMode mode = option_load_mode; tOptionValue valu; char* pzName = ++pzText; char* pzData; char* pcNulPoint; while (IS_VALUE_NAME_CHAR(*pzText)) pzText++; pcNulPoint = pzText; valu.valType = OPARG_TYPE_STRING; switch (*pzText) { case ' ': case '\t': pzText = parseAttributes(pOpts, pzText, &mode, &valu); if (*pzText == '>') break; if (*pzText != '/') return NULL; /* FALLTHROUGH */ case '/': if (pzText[1] != '>') return NULL; *pzText = NUL; pzText += 2; loadOptionLine(pOpts, pOS, pzName, dir, mode); return pzText; case '>': break; default: pzText = strchr(pzText, '>'); if (pzText != NULL) pzText++; return pzText; } /* * If we are here, we have a value. "pzText" points to a closing angle * bracket. Separate the name from the value for a moment. */ *pcNulPoint = NUL; pzData = ++pzText; /* * Find the end of the option text and NUL terminate it */ { char z[64], *pz = z; size_t len = strlen(pzName) + 4; if (len > sizeof(z)) pz = AGALOC(len, "scan name"); sprintf(pz, "</%s>", pzName); *pzText = ' '; pzText = strstr(pzText, pz); if (pz != z) AGFREE(pz); if (pzText == NULL) return pzText; *pzText = NUL; pzText += len-1; } /* * Rejoin the name and value for parsing by "loadOptionLine()". * Erase any attributes parsed by "parseAttributes()". */ memset(pcNulPoint, ' ', pzData - pcNulPoint); /* * If we are getting a "string" value, the process the XML-ish * %XX hex characters. */ if (valu.valType == OPARG_TYPE_STRING) { char * pzSrc = pzData; char * pzDst = pzData; char bf[4]; bf[2] = NUL; for (;;) { int ch = ((int)*(pzSrc++)) & 0xFF; switch (ch) { case NUL: goto string_fixup_done; case '%': bf[0] = *(pzSrc++); bf[1] = *(pzSrc++); if ((bf[0] == NUL) || (bf[1] == NUL)) goto string_fixup_done; ch = strtoul(bf, NULL, 16); /* FALLTHROUGH */ default: *(pzDst++) = ch; } } string_fixup_done:; *pzDst = NUL; } /* * "pzName" points to what looks like text for one option/configurable. * It is NUL terminated. Process it. */ loadOptionLine(pOpts, pOS, pzName, dir, mode); return pzText; }
/** * "pzText" points to the start of some value name. * The end of the entry is the end of the line that is not preceded by * a backslash escape character. The string value is always processed * in "cooked" mode. */ static char * handle_cfg(tOptions * pOpts, tOptState * pOS, char * pzText, int dir) { char* pzName = pzText++; char* pzEnd = strchr(pzText, '\n'); if (pzEnd == NULL) return pzText + strlen(pzText); while (IS_VALUE_NAME_CHAR(*pzText)) pzText++; while (IS_WHITESPACE_CHAR(*pzText)) pzText++; if (pzText > pzEnd) { name_only: *pzEnd++ = NUL; loadOptionLine(pOpts, pOS, pzName, dir, OPTION_LOAD_UNCOOKED); return pzEnd; } /* * Either the first character after the name is a ':' or '=', * or else we must have skipped over white space. Anything else * is an invalid format and we give up parsing the text. */ if ((*pzText == '=') || (*pzText == ':')) { while (IS_WHITESPACE_CHAR(*++pzText)) ; if (pzText > pzEnd) goto name_only; } else if (! IS_WHITESPACE_CHAR(pzText[-1])) return NULL; /* * IF the value is continued, remove the backslash escape and push "pzEnd" * on to a newline *not* preceded by a backslash. */ if (pzEnd[-1] == '\\') { char* pcD = pzEnd-1; char* pcS = pzEnd; for (;;) { char ch = *(pcS++); switch (ch) { case NUL: pcS = NULL; case '\n': *pcD = NUL; pzEnd = pcS; goto copy_done; case '\\': if (*pcS == '\n') { ch = *(pcS++); } /* FALLTHROUGH */ default: *(pcD++) = ch; } } copy_done:; } else { /* * The newline was not preceded by a backslash. NUL it out */ *(pzEnd++) = NUL; } /* * "pzName" points to what looks like text for one option/configurable. * It is NUL terminated. Process it. */ loadOptionLine(pOpts, pOS, pzName, dir, OPTION_LOAD_UNCOOKED); return pzEnd; }
/* scanXmlEntry * * We've found a '<' character. We ignore this if it is a comment or a * directive. If it is something else, then whatever it is we are looking * at is bogus. Returning NULL stops processing. */ static char const* scanXmlEntry( char const* pzName, tOptionValue* pRes ) { size_t nameLen = 1, valLen = 0; char const* pzScan = ++pzName; char const* pzVal; tOptionValue valu; tOptionValue* pNewVal; tOptionLoadMode save_mode = option_load_mode; if (! IS_VAR_FIRST_CHAR(*pzName)) { switch (*pzName) { default: pzName = NULL; break; case '!': pzName = strstr( pzName, "-->" ); if (pzName != NULL) pzName += 3; break; case '?': pzName = strchr( pzName, '>' ); if (pzName != NULL) pzName++; break; } return pzName; } pzScan++; while (IS_VALUE_NAME_CHAR( (int)*pzScan )) { pzScan++; nameLen++; } if (nameLen > 64) return NULL; valu.valType = OPARG_TYPE_STRING; switch (*pzScan) { case ' ': case '\t': pzScan = parseAttributes(NULL, (char *)(intptr_t)pzScan, &option_load_mode, &valu ); if (*pzScan == '>') { pzScan++; break; } if (*pzScan != '/') { option_load_mode = save_mode; return NULL; } /* FALLTHROUGH */ case '/': if (*++pzScan != '>') { option_load_mode = save_mode; return NULL; } addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0); option_load_mode = save_mode; return pzScan+1; default: option_load_mode = save_mode; return NULL; case '>': pzScan++; break; } pzVal = pzScan; { char z[68]; char* pzD = z; int ct = nameLen; char const* pzS = pzName; *(pzD++) = '<'; *(pzD++) = '/'; do { *(pzD++) = *(pzS++); } while (--ct > 0); *(pzD++) = '>'; *pzD = NUL; pzScan = strstr( pzScan, z ); if (pzScan == NULL) { option_load_mode = save_mode; return NULL; } valLen = (pzScan - pzVal); pzScan += nameLen + 3; while (IS_WHITESPACE_CHAR(*pzScan)) pzScan++; } switch (valu.valType) { case OPARG_TYPE_NONE: addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0); break; case OPARG_TYPE_STRING: pNewVal = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen); if (option_load_mode == OPTION_LOAD_KEEP) break; mungeString( pNewVal->v.strVal, option_load_mode ); break; case OPARG_TYPE_BOOLEAN: addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen ); break; case OPARG_TYPE_NUMERIC: addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen ); break; case OPARG_TYPE_HIERARCHY: { char* pz = AGALOC( valLen+1, "hierarchical scan" ); if (pz == NULL) break; memcpy( pz, pzVal, valLen ); pz[valLen] = NUL; addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen ); AGFREE(pz); break; } case OPARG_TYPE_ENUMERATION: case OPARG_TYPE_MEMBERSHIP: default: break; } option_load_mode = save_mode; return pzScan; }
/* scanNameEntry * * We have an entry that starts with a name. Find the end of it, cook it * (if called for) and create the name/value association. */ static char const* scanNameEntry(char const* pzName, tOptionValue* pRes) { tOptionValue* pNV; char const * pzScan = pzName+1; /* we know first char is a name char */ char const * pzVal; size_t nameLen = 1; size_t dataLen = 0; /* * Scan over characters that name a value. These names may not end * with a colon, but they may contain colons. */ while (IS_VALUE_NAME_CHAR(*pzScan)) { pzScan++; nameLen++; } if (pzScan[-1] == ':') { pzScan--; nameLen--; } while (IS_HORIZ_WHITE_CHAR(*pzScan)) pzScan++; re_switch: switch (*pzScan) { case '=': case ':': while (IS_HORIZ_WHITE_CHAR( (int)*++pzScan )) ; if ((*pzScan == '=') || (*pzScan == ':')) goto default_char; goto re_switch; case '\n': case ',': pzScan++; /* FALLTHROUGH */ case NUL: addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0); break; case '"': case '\'': pzVal = pzScan; pzScan = scanQuotedString( pzScan ); dataLen = pzScan - pzVal; pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, dataLen ); if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED)) ao_string_cook( pNV->v.strVal, NULL ); break; default: default_char: /* * We have found some strange text value. It ends with a newline * or a comma. */ pzVal = pzScan; for (;;) { char ch = *(pzScan++); switch (ch) { case NUL: pzScan--; dataLen = pzScan - pzVal; goto string_done; /* FALLTHROUGH */ case '\n': if ( (pzScan > pzVal + 2) && (pzScan[-2] == '\\') && (pzScan[ 0] != NUL)) continue; /* FALLTHROUGH */ case ',': dataLen = (pzScan - pzVal) - 1; string_done: pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, dataLen ); if (pNV != NULL) removeLineContinue( pNV->v.strVal ); goto leave_scan_name; } } break; } leave_scan_name:; return pzScan; }