123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947 |
- /**
- * \file nested.c
- *
- * Handle options with arguments that contain nested values.
- *
- * @addtogroup autoopts
- * @{
- */
- /*
- * Automated Options Nested Values module.
- *
- * This file is part of AutoOpts, a companion to AutoGen.
- * AutoOpts is free software.
- * AutoOpts is Copyright (C) 1992-2016 by Bruce Korb - all rights reserved
- *
- * AutoOpts is available under any one of two licenses. The license
- * in use must be one of these two and the choice is under the control
- * of the user of the license.
- *
- * The GNU Lesser General Public License, version 3 or later
- * See the files "COPYING.lgplv3" and "COPYING.gplv3"
- *
- * The Modified Berkeley Software Distribution License
- * See the file "COPYING.mbsd"
- *
- * These files have the following sha256 sums:
- *
- * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
- * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
- * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
- */
- typedef struct {
- int xml_ch;
- int xml_len;
- char xml_txt[8];
- } xml_xlate_t;
- static xml_xlate_t const xml_xlate[] = {
- { '&', 4, "amp;" },
- { '<', 3, "lt;" },
- { '>', 3, "gt;" },
- { '"', 5, "quot;" },
- { '\'',5, "apos;" }
- };
- #ifndef ENOMSG
- #define ENOMSG ENOENT
- #endif
- /* = = = START-STATIC-FORWARD = = = */
- static void
- remove_continuation(char * src);
- static char const *
- scan_q_str(char const * pzTxt);
- static tOptionValue *
- add_string(void ** pp, char const * name, size_t nm_len,
- char const * val, size_t d_len);
- static tOptionValue *
- add_bool(void ** pp, char const * name, size_t nm_len,
- char const * val, size_t d_len);
- static tOptionValue *
- add_number(void ** pp, char const * name, size_t nm_len,
- char const * val, size_t d_len);
- static tOptionValue *
- add_nested(void ** pp, char const * name, size_t nm_len,
- char * val, size_t d_len);
- static char const *
- scan_name(char const * name, tOptionValue * res);
- static char const *
- unnamed_xml(char const * txt);
- static char const *
- scan_xml_name(char const * name, size_t * nm_len, tOptionValue * val);
- static char const *
- find_end_xml(char const * src, size_t nm_len, char const * val, size_t * len);
- static char const *
- scan_xml(char const * xml_name, tOptionValue * res_val);
- static void
- sort_list(tArgList * arg_list);
- /* = = = END-STATIC-FORWARD = = = */
- /**
- * Backslashes are used for line continuations. We keep the newline
- * characters, but trim out the backslash:
- */
- static void
- remove_continuation(char * src)
- {
- char * pzD;
- do {
- while (*src == NL) src++;
- pzD = strchr(src, NL);
- if (pzD == NULL)
- return;
- /*
- * pzD has skipped at least one non-newline character and now
- * points to a newline character. It now becomes the source and
- * pzD goes to the previous character.
- */
- src = pzD--;
- if (*pzD != '\\')
- pzD++;
- } while (pzD == src);
- /*
- * Start shifting text.
- */
- for (;;) {
- char ch = ((*pzD++) = *(src++));
- switch (ch) {
- case NUL: return;
- case '\\':
- if (*src == NL)
- --pzD; /* rewrite on next iteration */
- }
- }
- }
- /**
- * Find the end of a quoted string, skipping escaped quote characters.
- */
- static char const *
- scan_q_str(char const * pzTxt)
- {
- char q = *(pzTxt++); /* remember the type of quote */
- for (;;) {
- char ch = *(pzTxt++);
- if (ch == NUL)
- return pzTxt-1;
- if (ch == q)
- return pzTxt;
- if (ch == '\\') {
- ch = *(pzTxt++);
- /*
- * IF the next character is NUL, drop the backslash, too.
- */
- if (ch == NUL)
- return pzTxt - 2;
- /*
- * IF the quote character or the escape character were escaped,
- * then skip both, as long as the string does not end.
- */
- if ((ch == q) || (ch == '\\')) {
- if (*(pzTxt++) == NUL)
- return pzTxt-1;
- }
- }
- }
- }
- /**
- * Associate a name with either a string or no value.
- *
- * @param[in,out] pp argument list to add to
- * @param[in] name the name of the "suboption"
- * @param[in] nm_len the length of the name
- * @param[in] val the string value for the suboption
- * @param[in] d_len the length of the value
- *
- * @returns the new value structure
- */
- static tOptionValue *
- add_string(void ** pp, char const * name, size_t nm_len,
- char const * val, size_t d_len)
- {
- tOptionValue * pNV;
- size_t sz = nm_len + d_len + sizeof(*pNV);
- pNV = AGALOC(sz, "option name/str value pair");
- if (val == NULL) {
- pNV->valType = OPARG_TYPE_NONE;
- pNV->pzName = pNV->v.strVal;
- } else {
- pNV->valType = OPARG_TYPE_STRING;
- if (d_len > 0) {
- char const * src = val;
- char * pzDst = pNV->v.strVal;
- int ct = (int)d_len;
- do {
- int ch = *(src++) & 0xFF;
- if (ch == NUL) goto data_copy_done;
- if (ch == '&')
- ch = get_special_char(&src, &ct);
- *(pzDst++) = (char)ch;
- } while (--ct > 0);
- data_copy_done:
- *pzDst = NUL;
- } else {
- pNV->v.strVal[0] = NUL;
- }
- pNV->pzName = pNV->v.strVal + d_len + 1;
- }
- memcpy(pNV->pzName, name, nm_len);
- pNV->pzName[ nm_len ] = NUL;
- addArgListEntry(pp, pNV);
- return pNV;
- }
- /**
- * Associate a name with a boolean value
- *
- * @param[in,out] pp argument list to add to
- * @param[in] name the name of the "suboption"
- * @param[in] nm_len the length of the name
- * @param[in] val the boolean value for the suboption
- * @param[in] d_len the length of the value
- *
- * @returns the new value structure
- */
- static tOptionValue *
- add_bool(void ** pp, char const * name, size_t nm_len,
- char const * val, size_t d_len)
- {
- size_t sz = nm_len + sizeof(tOptionValue) + 1;
- tOptionValue * new_val = AGALOC(sz, "bool val");
- /*
- * Scan over whitespace is constrained by "d_len"
- */
- while (IS_WHITESPACE_CHAR(*val) && (d_len > 0)) {
- d_len--; val++;
- }
- if (d_len == 0)
- new_val->v.boolVal = 0;
- else if (IS_DEC_DIGIT_CHAR(*val))
- new_val->v.boolVal = (unsigned)atoi(val);
- else new_val->v.boolVal = ! IS_FALSE_TYPE_CHAR(*val);
- new_val->valType = OPARG_TYPE_BOOLEAN;
- new_val->pzName = (char *)(new_val + 1);
- memcpy(new_val->pzName, name, nm_len);
- new_val->pzName[ nm_len ] = NUL;
- addArgListEntry(pp, new_val);
- return new_val;
- }
- /**
- * Associate a name with strtol() value, defaulting to zero.
- *
- * @param[in,out] pp argument list to add to
- * @param[in] name the name of the "suboption"
- * @param[in] nm_len the length of the name
- * @param[in] val the numeric value for the suboption
- * @param[in] d_len the length of the value
- *
- * @returns the new value structure
- */
- static tOptionValue *
- add_number(void ** pp, char const * name, size_t nm_len,
- char const * val, size_t d_len)
- {
- size_t sz = nm_len + sizeof(tOptionValue) + 1;
- tOptionValue * new_val = AGALOC(sz, "int val");
- /*
- * Scan over whitespace is constrained by "d_len"
- */
- while (IS_WHITESPACE_CHAR(*val) && (d_len > 0)) {
- d_len--; val++;
- }
- if (d_len == 0)
- new_val->v.longVal = 0;
- else
- new_val->v.longVal = strtol(val, 0, 0);
- new_val->valType = OPARG_TYPE_NUMERIC;
- new_val->pzName = (char *)(new_val + 1);
- memcpy(new_val->pzName, name, nm_len);
- new_val->pzName[ nm_len ] = NUL;
- addArgListEntry(pp, new_val);
- return new_val;
- }
- /**
- * Associate a name with a nested/hierarchical value.
- *
- * @param[in,out] pp argument list to add to
- * @param[in] name the name of the "suboption"
- * @param[in] nm_len the length of the name
- * @param[in] val the nested values for the suboption
- * @param[in] d_len the length of the value
- *
- * @returns the new value structure
- */
- static tOptionValue *
- add_nested(void ** pp, char const * name, size_t nm_len,
- char * val, size_t d_len)
- {
- tOptionValue * new_val;
- if (d_len == 0) {
- size_t sz = nm_len + sizeof(*new_val) + 1;
- new_val = AGALOC(sz, "empty nest");
- new_val->v.nestVal = NULL;
- new_val->valType = OPARG_TYPE_HIERARCHY;
- new_val->pzName = (char *)(new_val + 1);
- memcpy(new_val->pzName, name, nm_len);
- new_val->pzName[ nm_len ] = NUL;
- } else {
- new_val = optionLoadNested(val, name, nm_len);
- }
- if (new_val != NULL)
- addArgListEntry(pp, new_val);
- return new_val;
- }
- /**
- * 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 *
- scan_name(char const * name, tOptionValue * res)
- {
- tOptionValue * new_val;
- char const * pzScan = name+1; /* we know first char is a name char */
- char const * pzVal;
- size_t nm_len = 1;
- size_t d_len = 0;
- /*
- * Scan over characters that name a value. These names may not end
- * with a colon, but they may contain colons.
- */
- pzScan = SPN_VALUE_NAME_CHARS(name + 1);
- if (pzScan[-1] == ':')
- pzScan--;
- nm_len = (size_t)(pzScan - name);
- pzScan = SPN_HORIZ_WHITE_CHARS(pzScan);
- re_switch:
- switch (*pzScan) {
- case '=':
- case ':':
- pzScan = SPN_HORIZ_WHITE_CHARS(pzScan + 1);
- if ((*pzScan == '=') || (*pzScan == ':'))
- goto default_char;
- goto re_switch;
- case NL:
- case ',':
- pzScan++;
- /* FALLTHROUGH */
- case NUL:
- add_string(&(res->v.nestVal), name, nm_len, NULL, (size_t)0);
- break;
- case '"':
- case '\'':
- pzVal = pzScan;
- pzScan = scan_q_str(pzScan);
- d_len = (size_t)(pzScan - pzVal);
- new_val = add_string(&(res->v.nestVal), name, nm_len, pzVal,
- d_len);
- if ((new_val != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
- ao_string_cook(new_val->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--;
- d_len = (size_t)(pzScan - pzVal);
- goto string_done;
- /* FALLTHROUGH */
- case NL:
- if ( (pzScan > pzVal + 2)
- && (pzScan[-2] == '\\')
- && (pzScan[ 0] != NUL))
- continue;
- /* FALLTHROUGH */
- case ',':
- d_len = (size_t)(pzScan - pzVal) - 1;
- string_done:
- new_val = add_string(&(res->v.nestVal), name, nm_len,
- pzVal, d_len);
- if (new_val != NULL)
- remove_continuation(new_val->v.strVal);
- goto leave_scan_name;
- }
- }
- break;
- } leave_scan_name:;
- return pzScan;
- }
- /**
- * Some xml element that does not start with a name.
- * The next character must be either '!' (introducing a comment),
- * or '?' (introducing an XML meta-marker of some sort).
- * We ignore these and indicate an error (NULL result) otherwise.
- *
- * @param[in] txt the text within an xml bracket
- * @returns the address of the character after the closing marker, or NULL.
- */
- static char const *
- unnamed_xml(char const * txt)
- {
- switch (*txt) {
- default:
- txt = NULL;
- break;
- case '!':
- txt = strstr(txt, "-->");
- if (txt != NULL)
- txt += 3;
- break;
- case '?':
- txt = strchr(txt, '>');
- if (txt != NULL)
- txt++;
- break;
- }
- return txt;
- }
- /**
- * Scan off the xml element name, and the rest of the header, too.
- * Set the value type to NONE if it ends with "/>".
- *
- * @param[in] name the first name character (alphabetic)
- * @param[out] nm_len the length of the name
- * @param[out] val set valType field to STRING or NONE.
- *
- * @returns the scan resumption point, or NULL on error
- */
- static char const *
- scan_xml_name(char const * name, size_t * nm_len, tOptionValue * val)
- {
- char const * scan = SPN_VALUE_NAME_CHARS(name + 1);
- *nm_len = (size_t)(scan - name);
- if (*nm_len > 64)
- return NULL;
- val->valType = OPARG_TYPE_STRING;
- if (IS_WHITESPACE_CHAR(*scan)) {
- /*
- * There are attributes following the name. Parse 'em.
- */
- scan = SPN_WHITESPACE_CHARS(scan);
- scan = parse_attrs(NULL, scan, &option_load_mode, val);
- if (scan == NULL)
- return NULL; /* oops */
- }
- if (! IS_END_XML_TOKEN_CHAR(*scan))
- return NULL; /* oops */
- if (*scan == '/') {
- /*
- * Single element XML entries get inserted as an empty string.
- */
- if (*++scan != '>')
- return NULL;
- val->valType = OPARG_TYPE_NONE;
- }
- return scan+1;
- }
- /**
- * We've found a closing '>' without a preceding '/', thus we must search
- * the text for '<name/>' where "name" is the name of the XML element.
- *
- * @param[in] name the start of the name in the element header
- * @param[in] nm_len the length of that name
- * @param[out] len the length of the value (string between header and
- * the trailer/tail.
- * @returns the character after the trailer, or NULL if not found.
- */
- static char const *
- find_end_xml(char const * src, size_t nm_len, char const * val, size_t * len)
- {
- char z[72] = "</";
- char * dst = z + 2;
- do {
- *(dst++) = *(src++);
- } while (--nm_len > 0); /* nm_len is known to be 64 or less */
- *(dst++) = '>';
- *dst = NUL;
- {
- char const * res = strstr(val, z);
- if (res != NULL) {
- char const * end = (option_load_mode != OPTION_LOAD_KEEP)
- ? SPN_WHITESPACE_BACK(val, res)
- : res;
- *len = (size_t)(end - val); /* includes trailing white space */
- res = SPN_WHITESPACE_CHARS(res + (dst - z));
- }
- return res;
- }
- }
- /**
- * 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.
- *
- * @param[in] xml_name the name of an xml bracket (usually)
- * @param[in,out] res_val the option data derived from the XML element
- *
- * @returns the place to resume scanning input
- */
- static char const *
- scan_xml(char const * xml_name, tOptionValue * res_val)
- {
- size_t nm_len, v_len;
- char const * scan;
- char const * val_str;
- tOptionValue valu;
- tOptionLoadMode save_mode = option_load_mode;
- if (! IS_VAR_FIRST_CHAR(*++xml_name))
- return unnamed_xml(xml_name);
- /*
- * "scan_xml_name()" may change "option_load_mode".
- */
- val_str = scan_xml_name(xml_name, &nm_len, &valu);
- if (val_str == NULL)
- goto bail_scan_xml;
- if (valu.valType == OPARG_TYPE_NONE)
- scan = val_str;
- else {
- if (option_load_mode != OPTION_LOAD_KEEP)
- val_str = SPN_WHITESPACE_CHARS(val_str);
- scan = find_end_xml(xml_name, nm_len, val_str, &v_len);
- if (scan == NULL)
- goto bail_scan_xml;
- }
- /*
- * "scan" now points to where the scan is to resume after returning.
- * It either points after "/>" at the end of the XML element header,
- * or it points after the "</name>" tail based on the name in the header.
- */
- switch (valu.valType) {
- case OPARG_TYPE_NONE:
- add_string(&(res_val->v.nestVal), xml_name, nm_len, NULL, 0);
- break;
- case OPARG_TYPE_STRING:
- {
- tOptionValue * new_val = add_string(
- &(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
- if (option_load_mode != OPTION_LOAD_KEEP)
- munge_str(new_val->v.strVal, option_load_mode);
- break;
- }
- case OPARG_TYPE_BOOLEAN:
- add_bool(&(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
- break;
- case OPARG_TYPE_NUMERIC:
- add_number(&(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
- break;
- case OPARG_TYPE_HIERARCHY:
- {
- char * pz = AGALOC(v_len+1, "h scan");
- memcpy(pz, val_str, v_len);
- pz[v_len] = NUL;
- add_nested(&(res_val->v.nestVal), xml_name, nm_len, pz, v_len);
- AGFREE(pz);
- break;
- }
- case OPARG_TYPE_ENUMERATION:
- case OPARG_TYPE_MEMBERSHIP:
- default:
- break;
- }
- option_load_mode = save_mode;
- return scan;
- bail_scan_xml:
- option_load_mode = save_mode;
- return NULL;
- }
- /**
- * Deallocate a list of option arguments. This must have been gotten from
- * a hierarchical option argument, not a stacked list of strings. It is
- * an internal call, so it is not validated. The caller is responsible for
- * knowing what they are doing.
- */
- LOCAL void
- unload_arg_list(tArgList * arg_list)
- {
- int ct = arg_list->useCt;
- char const ** pnew_val = arg_list->apzArgs;
- while (ct-- > 0) {
- tOptionValue * new_val = (tOptionValue *)VOIDP(*(pnew_val++));
- if (new_val->valType == OPARG_TYPE_HIERARCHY)
- unload_arg_list(new_val->v.nestVal);
- AGFREE(new_val);
- }
- AGFREE(arg_list);
- }
- /*=export_func optionUnloadNested
- *
- * what: Deallocate the memory for a nested value
- * arg: + tOptionValue const * + pOptVal + the hierarchical value +
- *
- * doc:
- * A nested value needs to be deallocated. The pointer passed in should
- * have been gotten from a call to @code{configFileLoad()} (See
- * @pxref{libopts-configFileLoad}).
- =*/
- void
- optionUnloadNested(tOptionValue const * opt_val)
- {
- if (opt_val == NULL) return;
- if (opt_val->valType != OPARG_TYPE_HIERARCHY) {
- errno = EINVAL;
- return;
- }
- unload_arg_list(opt_val->v.nestVal);
- AGFREE(opt_val);
- }
- /**
- * This is a _stable_ sort. The entries are sorted alphabetically,
- * but within entries of the same name the ordering is unchanged.
- * Typically, we also hope the input is sorted.
- */
- static void
- sort_list(tArgList * arg_list)
- {
- int ix;
- int lm = arg_list->useCt;
- /*
- * This loop iterates "useCt" - 1 times.
- */
- for (ix = 0; ++ix < lm;) {
- int iy = ix-1;
- tOptionValue * new_v = C(tOptionValue *, arg_list->apzArgs[ix]);
- tOptionValue * old_v = C(tOptionValue *, arg_list->apzArgs[iy]);
- /*
- * For as long as the new entry precedes the "old" entry,
- * move the old pointer. Stop before trying to extract the
- * "-1" entry.
- */
- while (strcmp(old_v->pzName, new_v->pzName) > 0) {
- arg_list->apzArgs[iy+1] = VOIDP(old_v);
- old_v = (tOptionValue *)VOIDP(arg_list->apzArgs[--iy]);
- if (iy < 0)
- break;
- }
- /*
- * Always store the pointer. Sometimes it is redundant,
- * but the redundancy is cheaper than a test and branch sequence.
- */
- arg_list->apzArgs[iy+1] = VOIDP(new_v);
- }
- }
- /*=
- * private:
- *
- * what: parse a hierarchical option argument
- * arg: + char const * + pzTxt + the text to scan +
- * arg: + char const * + pzName + the name for the text +
- * arg: + size_t + nm_len + the length of "name" +
- *
- * ret_type: tOptionValue *
- * ret_desc: An allocated, compound value structure
- *
- * doc:
- * A block of text represents a series of values. It may be an
- * entire configuration file, or it may be an argument to an
- * option that takes a hierarchical value.
- *
- * If NULL is returned, errno will be set:
- * @itemize @bullet
- * @item
- * @code{EINVAL} the input text was NULL.
- * @item
- * @code{ENOMEM} the storage structures could not be allocated
- * @item
- * @code{ENOMSG} no configuration values were found
- * @end itemize
- =*/
- LOCAL tOptionValue *
- optionLoadNested(char const * text, char const * name, size_t nm_len)
- {
- tOptionValue * res_val;
- /*
- * Make sure we have some data and we have space to put what we find.
- */
- if (text == NULL) {
- errno = EINVAL;
- return NULL;
- }
- text = SPN_WHITESPACE_CHARS(text);
- if (*text == NUL) {
- errno = ENOMSG;
- return NULL;
- }
- res_val = AGALOC(sizeof(*res_val) + nm_len + 1, "nest args");
- res_val->valType = OPARG_TYPE_HIERARCHY;
- res_val->pzName = (char *)(res_val + 1);
- memcpy(res_val->pzName, name, nm_len);
- res_val->pzName[nm_len] = NUL;
- {
- tArgList * arg_list = AGALOC(sizeof(*arg_list), "nest arg l");
- res_val->v.nestVal = arg_list;
- arg_list->useCt = 0;
- arg_list->allocCt = MIN_ARG_ALLOC_CT;
- }
- /*
- * Scan until we hit a NUL.
- */
- do {
- text = SPN_WHITESPACE_CHARS(text);
- if (IS_VAR_FIRST_CHAR(*text))
- text = scan_name(text, res_val);
- else switch (*text) {
- case NUL:
- goto scan_done;
- case '<':
- text = scan_xml(text, res_val);
- if (text == NULL)
- goto woops;
- if (*text == ',')
- text++;
- break;
- case '#':
- text = strchr(text, NL);
- break;
- default:
- goto woops;
- }
- } while (text != NULL); scan_done:;
- {
- tArgList * al = res_val->v.nestVal;
- if (al->useCt == 0) {
- errno = ENOMSG;
- goto woops;
- }
- if (al->useCt > 1)
- sort_list(al);
- }
- return res_val;
- woops:
- AGFREE(res_val->v.nestVal);
- AGFREE(res_val);
- return NULL;
- }
- /*=export_func optionNestedVal
- * private:
- *
- * what: parse a hierarchical option argument
- * arg: + tOptions * + opts + program options descriptor +
- * arg: + tOptDesc * + od + the descriptor for this arg +
- *
- * doc:
- * Nested value was found on the command line
- =*/
- void
- optionNestedVal(tOptions * opts, tOptDesc * od)
- {
- if (opts < OPTPROC_EMIT_LIMIT)
- return;
- if (od->fOptState & OPTST_RESET) {
- tArgList * arg_list = od->optCookie;
- int ct;
- char const ** av;
- if (arg_list == NULL)
- return;
- ct = arg_list->useCt;
- av = arg_list->apzArgs;
- while (--ct >= 0) {
- void * p = VOIDP(*(av++));
- optionUnloadNested((tOptionValue const *)p);
- }
- AGFREE(od->optCookie);
- } else {
- tOptionValue * opt_val = optionLoadNested(
- od->optArg.argString, od->pz_Name, strlen(od->pz_Name));
- if (opt_val != NULL)
- addArgListEntry(&(od->optCookie), VOIDP(opt_val));
- }
- }
- /**
- * get_special_char
- */
- LOCAL int
- get_special_char(char const ** ppz, int * ct)
- {
- char const * pz = *ppz;
- if (*ct < 3)
- return '&';
- if (*pz == '#') {
- int base = 10;
- int retch;
- pz++;
- if (*pz == 'x') {
- base = 16;
- pz++;
- }
- retch = (int)strtoul(pz, (char **)&pz, base);
- if (*pz != ';')
- return '&';
- base = (int)(++pz - *ppz);
- if (base > *ct)
- return '&';
- *ct -= base;
- *ppz = pz;
- return retch;
- }
- {
- int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
- xml_xlate_t const * xlatp = xml_xlate;
- for (;;) {
- if ( (*ct >= xlatp->xml_len)
- && (strncmp(pz, xlatp->xml_txt, (size_t)xlatp->xml_len) == 0)) {
- *ppz += xlatp->xml_len;
- *ct -= xlatp->xml_len;
- return xlatp->xml_ch;
- }
- if (--ctr <= 0)
- break;
- xlatp++;
- }
- }
- return '&';
- }
- /**
- * emit_special_char
- */
- LOCAL void
- emit_special_char(FILE * fp, int ch)
- {
- int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
- xml_xlate_t const * xlatp = xml_xlate;
- putc('&', fp);
- for (;;) {
- if (ch == xlatp->xml_ch) {
- fputs(xlatp->xml_txt, fp);
- return;
- }
- if (--ctr <= 0)
- break;
- xlatp++;
- }
- fprintf(fp, XML_HEX_BYTE_FMT, (ch & 0xFF));
- }
- /** @}
- *
- * Local Variables:
- * mode: C
- * c-file-style: "stroustrup"
- * indent-tabs-mode: nil
- * End:
- * end of autoopts/nested.c */
|