Return to nested.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / sntp / libopts |
1.1 ! misho 1: ! 2: /** ! 3: * \file nested.c ! 4: * ! 5: * Time-stamp: "2010-08-22 11:17:56 bkorb" ! 6: * ! 7: * Automated Options Nested Values module. ! 8: * ! 9: * This file is part of AutoOpts, a companion to AutoGen. ! 10: * AutoOpts is free software. ! 11: * AutoOpts is Copyright (c) 1992-2011 by Bruce Korb - all rights reserved ! 12: * ! 13: * AutoOpts is available under any one of two licenses. The license ! 14: * in use must be one of these two and the choice is under the control ! 15: * of the user of the license. ! 16: * ! 17: * The GNU Lesser General Public License, version 3 or later ! 18: * See the files "COPYING.lgplv3" and "COPYING.gplv3" ! 19: * ! 20: * The Modified Berkeley Software Distribution License ! 21: * See the file "COPYING.mbsd" ! 22: * ! 23: * These files have the following md5sums: ! 24: * ! 25: * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3 ! 26: * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3 ! 27: * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd ! 28: */ ! 29: ! 30: typedef struct { ! 31: int xml_ch; ! 32: int xml_len; ! 33: char xml_txt[8]; ! 34: } xml_xlate_t; ! 35: ! 36: static xml_xlate_t const xml_xlate[] = { ! 37: { '&', 4, "amp;" }, ! 38: { '<', 3, "lt;" }, ! 39: { '>', 3, "gt;" }, ! 40: { '"', 5, "quot;" }, ! 41: { '\'',5, "apos;" } ! 42: }; ! 43: ! 44: /* = = = START-STATIC-FORWARD = = = */ ! 45: static void ! 46: remove_continuation(char* pzSrc); ! 47: ! 48: static char const* ! 49: scan_q_str(char const* pzTxt); ! 50: ! 51: static tOptionValue* ! 52: add_string(void** pp, char const* pzName, size_t nameLen, ! 53: char const* pzValue, size_t dataLen); ! 54: ! 55: static tOptionValue* ! 56: add_bool(void** pp, char const* pzName, size_t nameLen, ! 57: char const* pzValue, size_t dataLen); ! 58: ! 59: static tOptionValue* ! 60: add_number(void** pp, char const* pzName, size_t nameLen, ! 61: char const* pzValue, size_t dataLen); ! 62: ! 63: static tOptionValue* ! 64: add_nested(void** pp, char const* pzName, size_t nameLen, ! 65: char* pzValue, size_t dataLen); ! 66: ! 67: static char const * ! 68: scan_name(char const* pzName, tOptionValue* pRes); ! 69: ! 70: static char const* ! 71: scan_xml(char const* pzName, tOptionValue* pRes); ! 72: ! 73: static void ! 74: sort_list(tArgList* pAL); ! 75: /* = = = END-STATIC-FORWARD = = = */ ! 76: ! 77: /** ! 78: * Backslashes are used for line continuations. We keep the newline ! 79: * characters, but trim out the backslash: ! 80: */ ! 81: static void ! 82: remove_continuation(char* pzSrc) ! 83: { ! 84: char* pzD; ! 85: ! 86: do { ! 87: while (*pzSrc == '\n') pzSrc++; ! 88: pzD = strchr(pzSrc, '\n'); ! 89: if (pzD == NULL) ! 90: return; ! 91: ! 92: /* ! 93: * pzD has skipped at least one non-newline character and now ! 94: * points to a newline character. It now becomes the source and ! 95: * pzD goes to the previous character. ! 96: */ ! 97: pzSrc = pzD--; ! 98: if (*pzD != '\\') ! 99: pzD++; ! 100: } while (pzD == pzSrc); ! 101: ! 102: /* ! 103: * Start shifting text. ! 104: */ ! 105: for (;;) { ! 106: char ch = ((*pzD++) = *(pzSrc++)); ! 107: switch (ch) { ! 108: case NUL: return; ! 109: case '\\': ! 110: if (*pzSrc == '\n') ! 111: --pzD; /* rewrite on next iteration */ ! 112: } ! 113: } ! 114: } ! 115: ! 116: /** ! 117: * Find the end of a quoted string, skipping escaped quote characters. ! 118: */ ! 119: static char const* ! 120: scan_q_str(char const* pzTxt) ! 121: { ! 122: char q = *(pzTxt++); /* remember the type of quote */ ! 123: ! 124: for (;;) { ! 125: char ch = *(pzTxt++); ! 126: if (ch == NUL) ! 127: return pzTxt-1; ! 128: ! 129: if (ch == q) ! 130: return pzTxt; ! 131: ! 132: if (ch == '\\') { ! 133: ch = *(pzTxt++); ! 134: /* ! 135: * IF the next character is NUL, drop the backslash, too. ! 136: */ ! 137: if (ch == NUL) ! 138: return pzTxt - 2; ! 139: ! 140: /* ! 141: * IF the quote character or the escape character were escaped, ! 142: * then skip both, as long as the string does not end. ! 143: */ ! 144: if ((ch == q) || (ch == '\\')) { ! 145: if (*(pzTxt++) == NUL) ! 146: return pzTxt-1; ! 147: } ! 148: } ! 149: } ! 150: } ! 151: ! 152: ! 153: /** ! 154: * Associate a name with either a string or no value. ! 155: */ ! 156: static tOptionValue* ! 157: add_string(void** pp, char const* pzName, size_t nameLen, ! 158: char const* pzValue, size_t dataLen) ! 159: { ! 160: tOptionValue* pNV; ! 161: size_t sz = nameLen + dataLen + sizeof(*pNV); ! 162: ! 163: pNV = AGALOC(sz, "option name/str value pair"); ! 164: if (pNV == NULL) ! 165: return NULL; ! 166: ! 167: if (pzValue == NULL) { ! 168: pNV->valType = OPARG_TYPE_NONE; ! 169: pNV->pzName = pNV->v.strVal; ! 170: ! 171: } else { ! 172: pNV->valType = OPARG_TYPE_STRING; ! 173: if (dataLen > 0) { ! 174: char const * pzSrc = pzValue; ! 175: char * pzDst = pNV->v.strVal; ! 176: int ct = dataLen; ! 177: do { ! 178: int ch = *(pzSrc++) & 0xFF; ! 179: if (ch == NUL) goto data_copy_done; ! 180: if (ch == '&') ! 181: ch = get_special_char(&pzSrc, &ct); ! 182: *(pzDst++) = ch; ! 183: } while (--ct > 0); ! 184: data_copy_done: ! 185: *pzDst = NUL; ! 186: ! 187: } else { ! 188: pNV->v.strVal[0] = NUL; ! 189: } ! 190: ! 191: pNV->pzName = pNV->v.strVal + dataLen + 1; ! 192: } ! 193: ! 194: memcpy(pNV->pzName, pzName, nameLen); ! 195: pNV->pzName[ nameLen ] = NUL; ! 196: addArgListEntry(pp, pNV); ! 197: return pNV; ! 198: } ! 199: ! 200: /** ! 201: * Associate a name with either a string or no value. ! 202: */ ! 203: static tOptionValue* ! 204: add_bool(void** pp, char const* pzName, size_t nameLen, ! 205: char const* pzValue, size_t dataLen) ! 206: { ! 207: tOptionValue* pNV; ! 208: size_t sz = nameLen + sizeof(*pNV) + 1; ! 209: ! 210: pNV = AGALOC(sz, "option name/bool value pair"); ! 211: if (pNV == NULL) ! 212: return NULL; ! 213: while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) { ! 214: dataLen--; pzValue++; ! 215: } ! 216: if (dataLen == 0) ! 217: pNV->v.boolVal = 0; ! 218: ! 219: else if (IS_DEC_DIGIT_CHAR(*pzValue)) ! 220: pNV->v.boolVal = atoi(pzValue); ! 221: ! 222: else pNV->v.boolVal = ! IS_FALSE_TYPE_CHAR(*pzValue); ! 223: ! 224: pNV->valType = OPARG_TYPE_BOOLEAN; ! 225: pNV->pzName = (char*)(pNV + 1); ! 226: memcpy(pNV->pzName, pzName, nameLen); ! 227: pNV->pzName[ nameLen ] = NUL; ! 228: addArgListEntry(pp, pNV); ! 229: return pNV; ! 230: } ! 231: ! 232: /** ! 233: * Associate a name with either a string or no value. ! 234: */ ! 235: static tOptionValue* ! 236: add_number(void** pp, char const* pzName, size_t nameLen, ! 237: char const* pzValue, size_t dataLen) ! 238: { ! 239: tOptionValue* pNV; ! 240: size_t sz = nameLen + sizeof(*pNV) + 1; ! 241: ! 242: pNV = AGALOC(sz, "option name/bool value pair"); ! 243: if (pNV == NULL) ! 244: return NULL; ! 245: while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) { ! 246: dataLen--; pzValue++; ! 247: } ! 248: if (dataLen == 0) ! 249: pNV->v.longVal = 0; ! 250: else ! 251: pNV->v.longVal = strtol(pzValue, 0, 0); ! 252: ! 253: pNV->valType = OPARG_TYPE_NUMERIC; ! 254: pNV->pzName = (char*)(pNV + 1); ! 255: memcpy(pNV->pzName, pzName, nameLen); ! 256: pNV->pzName[ nameLen ] = NUL; ! 257: addArgListEntry(pp, pNV); ! 258: return pNV; ! 259: } ! 260: ! 261: /** ! 262: * Associate a name with either a string or no value. ! 263: */ ! 264: static tOptionValue* ! 265: add_nested(void** pp, char const* pzName, size_t nameLen, ! 266: char* pzValue, size_t dataLen) ! 267: { ! 268: tOptionValue* pNV; ! 269: ! 270: if (dataLen == 0) { ! 271: size_t sz = nameLen + sizeof(*pNV) + 1; ! 272: pNV = AGALOC(sz, "empty nested value pair"); ! 273: if (pNV == NULL) ! 274: return NULL; ! 275: pNV->v.nestVal = NULL; ! 276: pNV->valType = OPARG_TYPE_HIERARCHY; ! 277: pNV->pzName = (char*)(pNV + 1); ! 278: memcpy(pNV->pzName, pzName, nameLen); ! 279: pNV->pzName[ nameLen ] = NUL; ! 280: ! 281: } else { ! 282: pNV = optionLoadNested(pzValue, pzName, nameLen); ! 283: } ! 284: ! 285: if (pNV != NULL) ! 286: addArgListEntry(pp, pNV); ! 287: ! 288: return pNV; ! 289: } ! 290: ! 291: /** ! 292: * We have an entry that starts with a name. Find the end of it, cook it ! 293: * (if called for) and create the name/value association. ! 294: */ ! 295: static char const * ! 296: scan_name(char const* pzName, tOptionValue* pRes) ! 297: { ! 298: tOptionValue* pNV; ! 299: char const * pzScan = pzName+1; /* we know first char is a name char */ ! 300: char const * pzVal; ! 301: size_t nameLen = 1; ! 302: size_t dataLen = 0; ! 303: ! 304: /* ! 305: * Scan over characters that name a value. These names may not end ! 306: * with a colon, but they may contain colons. ! 307: */ ! 308: while (IS_VALUE_NAME_CHAR(*pzScan)) { pzScan++; nameLen++; } ! 309: if (pzScan[-1] == ':') { pzScan--; nameLen--; } ! 310: while (IS_HORIZ_WHITE_CHAR(*pzScan)) pzScan++; ! 311: ! 312: re_switch: ! 313: ! 314: switch (*pzScan) { ! 315: case '=': ! 316: case ':': ! 317: while (IS_HORIZ_WHITE_CHAR((int)*++pzScan)) ; ! 318: if ((*pzScan == '=') || (*pzScan == ':')) ! 319: goto default_char; ! 320: goto re_switch; ! 321: ! 322: case '\n': ! 323: case ',': ! 324: pzScan++; ! 325: /* FALLTHROUGH */ ! 326: ! 327: case NUL: ! 328: add_string(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0); ! 329: break; ! 330: ! 331: case '"': ! 332: case '\'': ! 333: pzVal = pzScan; ! 334: pzScan = scan_q_str(pzScan); ! 335: dataLen = pzScan - pzVal; ! 336: pNV = add_string(&(pRes->v.nestVal), pzName, nameLen, pzVal, ! 337: dataLen); ! 338: if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED)) ! 339: ao_string_cook(pNV->v.strVal, NULL); ! 340: break; ! 341: ! 342: default: ! 343: default_char: ! 344: /* ! 345: * We have found some strange text value. It ends with a newline ! 346: * or a comma. ! 347: */ ! 348: pzVal = pzScan; ! 349: for (;;) { ! 350: char ch = *(pzScan++); ! 351: switch (ch) { ! 352: case NUL: ! 353: pzScan--; ! 354: dataLen = pzScan - pzVal; ! 355: goto string_done; ! 356: /* FALLTHROUGH */ ! 357: ! 358: case '\n': ! 359: if ( (pzScan > pzVal + 2) ! 360: && (pzScan[-2] == '\\') ! 361: && (pzScan[ 0] != NUL)) ! 362: continue; ! 363: /* FALLTHROUGH */ ! 364: ! 365: case ',': ! 366: dataLen = (pzScan - pzVal) - 1; ! 367: string_done: ! 368: pNV = add_string(&(pRes->v.nestVal), pzName, nameLen, ! 369: pzVal, dataLen); ! 370: if (pNV != NULL) ! 371: remove_continuation(pNV->v.strVal); ! 372: goto leave_scan_name; ! 373: } ! 374: } ! 375: break; ! 376: } leave_scan_name:; ! 377: ! 378: return pzScan; ! 379: } ! 380: ! 381: /** ! 382: * We've found a '<' character. We ignore this if it is a comment or a ! 383: * directive. If it is something else, then whatever it is we are looking ! 384: * at is bogus. Returning NULL stops processing. ! 385: */ ! 386: static char const* ! 387: scan_xml(char const* pzName, tOptionValue* pRes) ! 388: { ! 389: size_t nameLen = 1, valLen = 0; ! 390: char const* pzScan = ++pzName; ! 391: char const* pzVal; ! 392: tOptionValue valu; ! 393: tOptionValue* pNewVal; ! 394: tOptionLoadMode save_mode = option_load_mode; ! 395: ! 396: if (! IS_VAR_FIRST_CHAR(*pzName)) { ! 397: switch (*pzName) { ! 398: default: ! 399: pzName = NULL; ! 400: break; ! 401: ! 402: case '!': ! 403: pzName = strstr(pzName, "-->"); ! 404: if (pzName != NULL) ! 405: pzName += 3; ! 406: break; ! 407: ! 408: case '?': ! 409: pzName = strchr(pzName, '>'); ! 410: if (pzName != NULL) ! 411: pzName++; ! 412: break; ! 413: } ! 414: return pzName; ! 415: } ! 416: ! 417: pzScan++; ! 418: while (IS_VALUE_NAME_CHAR((int)*pzScan)) { pzScan++; nameLen++; } ! 419: if (nameLen > 64) ! 420: return NULL; ! 421: valu.valType = OPARG_TYPE_STRING; ! 422: ! 423: switch (*pzScan) { ! 424: case ' ': ! 425: case '\t': ! 426: pzScan = parseAttributes( ! 427: NULL, (char*)pzScan, &option_load_mode, &valu ); ! 428: if (*pzScan == '>') { ! 429: pzScan++; ! 430: break; ! 431: } ! 432: ! 433: if (*pzScan != '/') { ! 434: option_load_mode = save_mode; ! 435: return NULL; ! 436: } ! 437: /* FALLTHROUGH */ ! 438: ! 439: case '/': ! 440: if (*++pzScan != '>') { ! 441: option_load_mode = save_mode; ! 442: return NULL; ! 443: } ! 444: add_string(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0); ! 445: option_load_mode = save_mode; ! 446: return pzScan+1; ! 447: ! 448: default: ! 449: option_load_mode = save_mode; ! 450: return NULL; ! 451: ! 452: case '>': ! 453: pzScan++; ! 454: break; ! 455: } ! 456: ! 457: pzVal = pzScan; ! 458: ! 459: { ! 460: char z[68]; ! 461: char* pzD = z; ! 462: int ct = nameLen; ! 463: char const* pzS = pzName; ! 464: ! 465: *(pzD++) = '<'; ! 466: *(pzD++) = '/'; ! 467: ! 468: do { ! 469: *(pzD++) = *(pzS++); ! 470: } while (--ct > 0); ! 471: *(pzD++) = '>'; ! 472: *pzD = NUL; ! 473: ! 474: pzScan = strstr(pzScan, z); ! 475: if (pzScan == NULL) { ! 476: option_load_mode = save_mode; ! 477: return NULL; ! 478: } ! 479: valLen = (pzScan - pzVal); ! 480: pzScan += nameLen + 3; ! 481: while (IS_WHITESPACE_CHAR(*pzScan)) pzScan++; ! 482: } ! 483: ! 484: switch (valu.valType) { ! 485: case OPARG_TYPE_NONE: ! 486: add_string(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0); ! 487: break; ! 488: ! 489: case OPARG_TYPE_STRING: ! 490: pNewVal = add_string( ! 491: &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen); ! 492: ! 493: if (option_load_mode == OPTION_LOAD_KEEP) ! 494: break; ! 495: mungeString(pNewVal->v.strVal, option_load_mode); ! 496: break; ! 497: ! 498: case OPARG_TYPE_BOOLEAN: ! 499: add_bool(&(pRes->v.nestVal), pzName, nameLen, pzVal, valLen); ! 500: break; ! 501: ! 502: case OPARG_TYPE_NUMERIC: ! 503: add_number(&(pRes->v.nestVal), pzName, nameLen, pzVal, valLen); ! 504: break; ! 505: ! 506: case OPARG_TYPE_HIERARCHY: ! 507: { ! 508: char* pz = AGALOC(valLen+1, "hierarchical scan"); ! 509: if (pz == NULL) ! 510: break; ! 511: memcpy(pz, pzVal, valLen); ! 512: pz[valLen] = NUL; ! 513: add_nested(&(pRes->v.nestVal), pzName, nameLen, pz, valLen); ! 514: AGFREE(pz); ! 515: break; ! 516: } ! 517: ! 518: case OPARG_TYPE_ENUMERATION: ! 519: case OPARG_TYPE_MEMBERSHIP: ! 520: default: ! 521: break; ! 522: } ! 523: ! 524: option_load_mode = save_mode; ! 525: return pzScan; ! 526: } ! 527: ! 528: ! 529: /** ! 530: * Deallocate a list of option arguments. This must have been gotten from ! 531: * a hierarchical option argument, not a stacked list of strings. It is ! 532: * an internal call, so it is not validated. The caller is responsible for ! 533: * knowing what they are doing. ! 534: */ ! 535: LOCAL void ! 536: unload_arg_list(tArgList* pAL) ! 537: { ! 538: int ct = pAL->useCt; ! 539: tCC** ppNV = pAL->apzArgs; ! 540: ! 541: while (ct-- > 0) { ! 542: tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++); ! 543: if (pNV->valType == OPARG_TYPE_HIERARCHY) ! 544: unload_arg_list(pNV->v.nestVal); ! 545: AGFREE(pNV); ! 546: } ! 547: ! 548: AGFREE((void*)pAL); ! 549: } ! 550: ! 551: /*=export_func optionUnloadNested ! 552: * ! 553: * what: Deallocate the memory for a nested value ! 554: * arg: + tOptionValue const * + pOptVal + the hierarchical value + ! 555: * ! 556: * doc: ! 557: * A nested value needs to be deallocated. The pointer passed in should ! 558: * have been gotten from a call to @code{configFileLoad()} (See ! 559: * @pxref{libopts-configFileLoad}). ! 560: =*/ ! 561: void ! 562: optionUnloadNested(tOptionValue const * pOV) ! 563: { ! 564: if (pOV == NULL) return; ! 565: if (pOV->valType != OPARG_TYPE_HIERARCHY) { ! 566: errno = EINVAL; ! 567: return; ! 568: } ! 569: ! 570: unload_arg_list(pOV->v.nestVal); ! 571: ! 572: AGFREE((void*)pOV); ! 573: } ! 574: ! 575: /** ! 576: * This is a _stable_ sort. The entries are sorted alphabetically, ! 577: * but within entries of the same name the ordering is unchanged. ! 578: * Typically, we also hope the input is sorted. ! 579: */ ! 580: static void ! 581: sort_list(tArgList* pAL) ! 582: { ! 583: int ix; ! 584: int lm = pAL->useCt; ! 585: ! 586: /* ! 587: * This loop iterates "useCt" - 1 times. ! 588: */ ! 589: for (ix = 0; ++ix < lm;) { ! 590: int iy = ix-1; ! 591: tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]); ! 592: tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]); ! 593: ! 594: /* ! 595: * For as long as the new entry precedes the "old" entry, ! 596: * move the old pointer. Stop before trying to extract the ! 597: * "-1" entry. ! 598: */ ! 599: while (strcmp(pOldNV->pzName, pNewNV->pzName) > 0) { ! 600: pAL->apzArgs[iy+1] = (void*)pOldNV; ! 601: pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]); ! 602: if (iy < 0) ! 603: break; ! 604: } ! 605: ! 606: /* ! 607: * Always store the pointer. Sometimes it is redundant, ! 608: * but the redundancy is cheaper than a test and branch sequence. ! 609: */ ! 610: pAL->apzArgs[iy+1] = (void*)pNewNV; ! 611: } ! 612: } ! 613: ! 614: /* optionLoadNested ! 615: * private: ! 616: * ! 617: * what: parse a hierarchical option argument ! 618: * arg: + char const* + pzTxt + the text to scan + ! 619: * arg: + char const* + pzName + the name for the text + ! 620: * arg: + size_t + nameLen + the length of "name" + ! 621: * ! 622: * ret_type: tOptionValue* ! 623: * ret_desc: An allocated, compound value structure ! 624: * ! 625: * doc: ! 626: * A block of text represents a series of values. It may be an ! 627: * entire configuration file, or it may be an argument to an ! 628: * option that takes a hierarchical value. ! 629: */ ! 630: LOCAL tOptionValue* ! 631: optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen) ! 632: { ! 633: tOptionValue* pRes; ! 634: ! 635: /* ! 636: * Make sure we have some data and we have space to put what we find. ! 637: */ ! 638: if (pzTxt == NULL) { ! 639: errno = EINVAL; ! 640: return NULL; ! 641: } ! 642: while (IS_WHITESPACE_CHAR(*pzTxt)) pzTxt++; ! 643: if (*pzTxt == NUL) { ! 644: errno = ENOENT; ! 645: return NULL; ! 646: } ! 647: pRes = AGALOC(sizeof(*pRes) + nameLen + 1, "nested args"); ! 648: if (pRes == NULL) { ! 649: errno = ENOMEM; ! 650: return NULL; ! 651: } ! 652: pRes->valType = OPARG_TYPE_HIERARCHY; ! 653: pRes->pzName = (char*)(pRes + 1); ! 654: memcpy(pRes->pzName, pzName, nameLen); ! 655: pRes->pzName[nameLen] = NUL; ! 656: ! 657: { ! 658: tArgList * pAL = AGALOC(sizeof(*pAL), "nested arg list"); ! 659: if (pAL == NULL) { ! 660: AGFREE(pRes); ! 661: return NULL; ! 662: } ! 663: ! 664: pRes->v.nestVal = pAL; ! 665: pAL->useCt = 0; ! 666: pAL->allocCt = MIN_ARG_ALLOC_CT; ! 667: } ! 668: ! 669: /* ! 670: * Scan until we hit a NUL. ! 671: */ ! 672: do { ! 673: while (IS_WHITESPACE_CHAR((int)*pzTxt)) pzTxt++; ! 674: if (IS_VAR_FIRST_CHAR((int)*pzTxt)) { ! 675: pzTxt = scan_name(pzTxt, pRes); ! 676: } ! 677: else switch (*pzTxt) { ! 678: case NUL: goto scan_done; ! 679: case '<': pzTxt = scan_xml(pzTxt, pRes); ! 680: if (pzTxt == NULL) goto woops; ! 681: if (*pzTxt == ',') pzTxt++; break; ! 682: case '#': pzTxt = strchr(pzTxt, '\n'); break; ! 683: default: goto woops; ! 684: } ! 685: } while (pzTxt != NULL); scan_done:; ! 686: ! 687: { ! 688: tArgList * al = pRes->v.nestVal; ! 689: if (al->useCt != 0) ! 690: sort_list(al); ! 691: } ! 692: ! 693: return pRes; ! 694: ! 695: woops: ! 696: AGFREE(pRes->v.nestVal); ! 697: AGFREE(pRes); ! 698: return NULL; ! 699: } ! 700: ! 701: /*=export_func optionNestedVal ! 702: * private: ! 703: * ! 704: * what: parse a hierarchical option argument ! 705: * arg: + tOptions* + pOpts + program options descriptor + ! 706: * arg: + tOptDesc* + pOptDesc + the descriptor for this arg + ! 707: * ! 708: * doc: ! 709: * Nested value was found on the command line ! 710: =*/ ! 711: void ! 712: optionNestedVal(tOptions* pOpts, tOptDesc* pOD) ! 713: { ! 714: if (pOpts < OPTPROC_EMIT_LIMIT) ! 715: return; ! 716: ! 717: if (pOD->fOptState & OPTST_RESET) { ! 718: tArgList* pAL = pOD->optCookie; ! 719: int ct; ! 720: tCC ** av; ! 721: ! 722: if (pAL == NULL) ! 723: return; ! 724: ct = pAL->useCt; ! 725: av = pAL->apzArgs; ! 726: ! 727: while (--ct >= 0) { ! 728: void * p = (void *)*(av++); ! 729: optionUnloadNested((tOptionValue const *)p); ! 730: } ! 731: ! 732: AGFREE(pOD->optCookie); ! 733: ! 734: } else { ! 735: tOptionValue* pOV = optionLoadNested( ! 736: pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name)); ! 737: ! 738: if (pOV != NULL) ! 739: addArgListEntry(&(pOD->optCookie), (void*)pOV); ! 740: } ! 741: } ! 742: ! 743: /* ! 744: * get_special_char ! 745: */ ! 746: LOCAL int ! 747: get_special_char(char const ** ppz, int * ct) ! 748: { ! 749: char const * pz = *ppz; ! 750: ! 751: if (*ct < 3) ! 752: return '&'; ! 753: ! 754: if (*pz == '#') { ! 755: int base = 10; ! 756: int retch; ! 757: ! 758: pz++; ! 759: if (*pz == 'x') { ! 760: base = 16; ! 761: pz++; ! 762: } ! 763: retch = (int)strtoul(pz, (char **)&pz, base); ! 764: if (*pz != ';') ! 765: return '&'; ! 766: base = ++pz - *ppz; ! 767: if (base > *ct) ! 768: return '&'; ! 769: ! 770: *ct -= base; ! 771: *ppz = pz; ! 772: return retch; ! 773: } ! 774: ! 775: { ! 776: int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]); ! 777: xml_xlate_t const * xlatp = xml_xlate; ! 778: ! 779: for (;;) { ! 780: if ( (*ct >= xlatp->xml_len) ! 781: && (strncmp(pz, xlatp->xml_txt, xlatp->xml_len) == 0)) { ! 782: *ppz += xlatp->xml_len; ! 783: *ct -= xlatp->xml_len; ! 784: return xlatp->xml_ch; ! 785: } ! 786: ! 787: if (--ctr <= 0) ! 788: break; ! 789: xlatp++; ! 790: } ! 791: } ! 792: return '&'; ! 793: } ! 794: ! 795: /* ! 796: * emit_special_char ! 797: */ ! 798: LOCAL void ! 799: emit_special_char(FILE * fp, int ch) ! 800: { ! 801: int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]); ! 802: xml_xlate_t const * xlatp = xml_xlate; ! 803: ! 804: putc('&', fp); ! 805: for (;;) { ! 806: if (ch == xlatp->xml_ch) { ! 807: fputs(xlatp->xml_txt, fp); ! 808: return; ! 809: } ! 810: if (--ctr <= 0) ! 811: break; ! 812: xlatp++; ! 813: } ! 814: fprintf(fp, "#x%02X;", (ch & 0xFF)); ! 815: } ! 816: ! 817: /* ! 818: * Local Variables: ! 819: * mode: C ! 820: * c-file-style: "stroustrup" ! 821: * indent-tabs-mode: nil ! 822: * End: ! 823: * end of autoopts/nested.c */