Annotation of embedaddon/ntp/sntp/libopts/nested.c, revision 1.1
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 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>