/*=export_func optionLoadLine * * what: process a string for an option name and value * * arg: tOptions*, pOpts, program options descriptor * arg: char const*, pzLine, NUL-terminated text * * doc: * * This is a client program callable routine for setting options from, for * example, the contents of a file that they read in. Only one option may * appear in the text. It will be treated as a normal (non-preset) option. * * When passed a pointer to the option struct and a string, it will find * the option named by the first token on the string and set the option * argument to the remainder of the string. The caller must NUL terminate * the string. Any embedded new lines will be included in the option * argument. If the input looks like one or more quoted strings, then the * input will be "cooked". The "cooking" is identical to the string * formation used in AutoGen definition files (@pxref{basic expression}), * except that you may not use backquotes. * * err: Invalid options are silently ignored. Invalid option arguments * will cause a warning to print, but the function should return. =*/ void optionLoadLine(tOptions * pOpts, char const * pzLine) { tOptState st = OPTSTATE_INITIALIZER(SET); char* pz; AGDUPSTR(pz, pzLine, "user option line"); loadOptionLine(pOpts, &st, pz, DIRECTION_PROCESS, OPTION_LOAD_COOKED); AGFREE(pz); }
/** * "txt" 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 * opts, tOptState * ost, char * txt, int dir) { tOptionLoadMode mode = option_load_mode; tOptionValue valu; char* pzName = ++txt; char* pzData; char* pcNulPoint; txt = SPN_VALUE_NAME_CHARS(txt); pcNulPoint = txt; valu.valType = OPARG_TYPE_STRING; switch (*txt) { case ' ': case '\t': txt = (void *)parse_attrs( opts, SPN_WHITESPACE_CHARS(txt), &mode, &valu); if (txt == NULL) return txt; if (*txt == '>') break; if (*txt != '/') return NULL; /* FALLTHROUGH */ case '/': if (txt[1] != '>') return NULL; *txt = NUL; txt += 2; loadOptionLine(opts, ost, pzName, dir, mode); return txt; case '>': break; default: txt = strchr(txt, '>'); if (txt != NULL) txt++; return txt; } /* * If we are here, we have a value. "txt" points to a closing angle * bracket. Separate the name from the value for a moment. */ *pcNulPoint = NUL; pzData = ++txt; txt = trim_xml_text(txt, pzName, mode); if (txt == NULL) return txt; /* * Rejoin the name and value for parsing by "loadOptionLine()". * Erase any attributes parsed by "parse_attrs()". */ memset(pcNulPoint, ' ', (size_t)(pzData - pcNulPoint)); /* * If we are getting a "string" value that is to be cooked, * then process the XML-ish &xx; XML-ish and %XX hex characters. */ if ( (valu.valType == OPARG_TYPE_STRING) && (mode == OPTION_LOAD_COOKED)) cook_xml_text(pzData); /* * "pzName" points to what looks like text for one option/configurable. * It is NUL terminated. Process it. */ loadOptionLine(opts, ost, pzName, dir, mode); return txt; }
/** * "txt" 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 * opts, tOptState * ost, char * txt, int dir) { char* pzName = txt++; char* pzEnd = strchr(txt, NL); if (pzEnd == NULL) return txt + strlen(txt); txt = SPN_VALUE_NAME_CHARS(txt); txt = SPN_WHITESPACE_CHARS(txt); if (txt > pzEnd) { name_only: *pzEnd++ = NUL; loadOptionLine(opts, ost, 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 ((*txt == '=') || (*txt == ':')) { txt = SPN_WHITESPACE_CHARS(txt+1); if (txt > pzEnd) goto name_only; } else if (! IS_WHITESPACE_CHAR(txt[-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; /* FALLTHROUGH */ case NL: *pcD = NUL; pzEnd = pcS; goto copy_done; case '\\': if (*pcS == NL) 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(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED); return pzEnd; }
/* handleStructure * * "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* handleStructure( tOptions* pOpts, tOptState* pOS, char* pzText, int direction ) { tOptionLoadMode mode = option_load_mode; tOptionValue valu; char* pzName = ++pzText; char* pzData; char* pcNulPoint; while (ISNAMECHAR( *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, direction, 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); /* * "pzName" points to what looks like text for one option/configurable. * It is NUL terminated. Process it. */ loadOptionLine( pOpts, pOS, pzName, direction, mode ); return pzText; }
/* handleConfig * * "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* handleConfig( tOptions* pOpts, tOptState* pOS, char* pzText, int direction ) { char* pzName = pzText++; char* pzEnd = strchr( pzText, '\n' ); if (pzEnd == NULL) return pzText + strlen(pzText); while (ISNAMECHAR( (int)*pzText )) pzText++; while (isspace( (int)*pzText )) pzText++; if (pzText > pzEnd) { name_only: *pzEnd++ = NUL; loadOptionLine( pOpts, pOS, pzName, direction, 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 (isspace( (int)*++pzText )) ; if (pzText > pzEnd) goto name_only; } else if (! isspace((int)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, direction, OPTION_LOAD_UNCOOKED ); return pzEnd; }
/** * "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; }