Return to configfile.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / sntp / libopts |
1.1 ! misho 1: /** ! 2: * \file configfile.c ! 3: * ! 4: * Time-stamp: "2011-04-06 09:31:24 bkorb" ! 5: * ! 6: * configuration/rc/ini file handling. ! 7: * ! 8: * This file is part of AutoOpts, a companion to AutoGen. ! 9: * AutoOpts is free software. ! 10: * AutoOpts is Copyright (c) 1992-2011 by Bruce Korb - all rights reserved ! 11: * ! 12: * AutoOpts is available under any one of two licenses. The license ! 13: * in use must be one of these two and the choice is under the control ! 14: * of the user of the license. ! 15: * ! 16: * The GNU Lesser General Public License, version 3 or later ! 17: * See the files "COPYING.lgplv3" and "COPYING.gplv3" ! 18: * ! 19: * The Modified Berkeley Software Distribution License ! 20: * See the file "COPYING.mbsd" ! 21: * ! 22: * These files have the following md5sums: ! 23: * ! 24: * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3 ! 25: * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3 ! 26: * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd ! 27: */ ! 28: ! 29: static void ! 30: set_usage_flags(tOptions * opts, char const * flg_txt); ! 31: ! 32: /* = = = START-STATIC-FORWARD = = = */ ! 33: static void ! 34: file_preset(tOptions * opts, char const * fname, int dir); ! 35: ! 36: static char* ! 37: handle_comment(char* pzText); ! 38: ! 39: static char * ! 40: handle_cfg(tOptions * pOpts, tOptState * pOS, char * pzText, int dir); ! 41: ! 42: static char * ! 43: handle_directive(tOptions * pOpts, char * pzText); ! 44: ! 45: static char * ! 46: aoflags_directive(tOptions * pOpts, char * pzText); ! 47: ! 48: static char * ! 49: program_directive(tOptions * pOpts, char * pzText); ! 50: ! 51: static char * ! 52: handle_section(tOptions * pOpts, char * pzText); ! 53: ! 54: static int ! 55: parse_xml_encoding(char ** ppz); ! 56: ! 57: static char * ! 58: trim_xml_text(char * pztxt, char const * pznm, tOptionLoadMode mode); ! 59: ! 60: static void ! 61: cook_xml_text(char * pzData); ! 62: ! 63: static char * ! 64: handle_struct(tOptions * pOpts, tOptState * pOS, char * pzText, int dir); ! 65: ! 66: static char* ! 67: parse_keyword(tOptions * pOpts, char * pzText, tOptionValue * pType); ! 68: ! 69: static char* ! 70: parse_set_mem(tOptions * pOpts, char * pzText, tOptionValue * pType); ! 71: ! 72: static char * ! 73: parse_value(char * pzText, tOptionValue * pType); ! 74: ! 75: static char * ! 76: skip_unkn(char* pzText); ! 77: /* = = = END-STATIC-FORWARD = = = */ ! 78: ! 79: ! 80: /*=export_func configFileLoad ! 81: * ! 82: * what: parse a configuration file ! 83: * arg: + char const* + pzFile + the file to load + ! 84: * ! 85: * ret_type: const tOptionValue* ! 86: * ret_desc: An allocated, compound value structure ! 87: * ! 88: * doc: ! 89: * This routine will load a named configuration file and parse the ! 90: * text as a hierarchically valued option. The option descriptor ! 91: * created from an option definition file is not used via this interface. ! 92: * The returned value is "named" with the input file name and is of ! 93: * type "@code{OPARG_TYPE_HIERARCHY}". It may be used in calls to ! 94: * @code{optionGetValue()}, @code{optionNextValue()} and ! 95: * @code{optionUnloadNested()}. ! 96: * ! 97: * err: ! 98: * If the file cannot be loaded or processed, @code{NULL} is returned and ! 99: * @var{errno} is set. It may be set by a call to either @code{open(2)} ! 100: * @code{mmap(2)} or other file system calls, or it may be: ! 101: * @itemize @bullet ! 102: * @item ! 103: * @code{ENOENT} - the file was empty. ! 104: * @item ! 105: * @code{EINVAL} - the file contents are invalid -- not properly formed. ! 106: * @item ! 107: * @code{ENOMEM} - not enough memory to allocate the needed structures. ! 108: * @end itemize ! 109: =*/ ! 110: const tOptionValue* ! 111: configFileLoad(char const* pzFile) ! 112: { ! 113: tmap_info_t cfgfile; ! 114: tOptionValue* pRes = NULL; ! 115: tOptionLoadMode save_mode = option_load_mode; ! 116: ! 117: char* pzText = ! 118: text_mmap(pzFile, PROT_READ, MAP_PRIVATE, &cfgfile); ! 119: ! 120: if (TEXT_MMAP_FAILED_ADDR(pzText)) ! 121: return NULL; /* errno is set */ ! 122: ! 123: option_load_mode = OPTION_LOAD_COOKED; ! 124: pRes = optionLoadNested(pzText, pzFile, strlen(pzFile)); ! 125: ! 126: if (pRes == NULL) { ! 127: int err = errno; ! 128: text_munmap(&cfgfile); ! 129: errno = err; ! 130: } else ! 131: text_munmap(&cfgfile); ! 132: ! 133: option_load_mode = save_mode; ! 134: return pRes; ! 135: } ! 136: ! 137: ! 138: /*=export_func optionFindValue ! 139: * ! 140: * what: find a hierarcicaly valued option instance ! 141: * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type + ! 142: * arg: + char const* + name + name of value to find + ! 143: * arg: + char const* + value + the matching value + ! 144: * ! 145: * ret_type: const tOptionValue* ! 146: * ret_desc: a compound value structure ! 147: * ! 148: * doc: ! 149: * This routine will find an entry in a nested value option or configurable. ! 150: * It will search through the list and return a matching entry. ! 151: * ! 152: * err: ! 153: * The returned result is NULL and errno is set: ! 154: * @itemize @bullet ! 155: * @item ! 156: * @code{EINVAL} - the @code{pOptValue} does not point to a valid ! 157: * hierarchical option value. ! 158: * @item ! 159: * @code{ENOENT} - no entry matched the given name. ! 160: * @end itemize ! 161: =*/ ! 162: const tOptionValue* ! 163: optionFindValue(const tOptDesc* pOptDesc, char const* pzName, ! 164: char const* pzVal) ! 165: { ! 166: const tOptionValue* pRes = NULL; ! 167: ! 168: if ( (pOptDesc == NULL) ! 169: || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) { ! 170: errno = EINVAL; ! 171: } ! 172: ! 173: else if (pOptDesc->optCookie == NULL) { ! 174: errno = ENOENT; ! 175: } ! 176: ! 177: else do { ! 178: tArgList* pAL = pOptDesc->optCookie; ! 179: int ct = pAL->useCt; ! 180: void** ppOV = (void**)(pAL->apzArgs); ! 181: ! 182: if (ct == 0) { ! 183: errno = ENOENT; ! 184: break; ! 185: } ! 186: ! 187: if (pzName == NULL) { ! 188: pRes = (tOptionValue*)*ppOV; ! 189: break; ! 190: } ! 191: ! 192: while (--ct >= 0) { ! 193: const tOptionValue* pOV = *(ppOV++); ! 194: const tOptionValue* pRV = optionGetValue(pOV, pzName); ! 195: ! 196: if (pRV == NULL) ! 197: continue; ! 198: ! 199: if (pzVal == NULL) { ! 200: pRes = pOV; ! 201: break; ! 202: } ! 203: } ! 204: if (pRes == NULL) ! 205: errno = ENOENT; ! 206: } while (0); ! 207: ! 208: return pRes; ! 209: } ! 210: ! 211: ! 212: /*=export_func optionFindNextValue ! 213: * ! 214: * what: find a hierarcicaly valued option instance ! 215: * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type + ! 216: * arg: + const tOptionValue* + pPrevVal + the last entry + ! 217: * arg: + char const* + name + name of value to find + ! 218: * arg: + char const* + value + the matching value + ! 219: * ! 220: * ret_type: const tOptionValue* ! 221: * ret_desc: a compound value structure ! 222: * ! 223: * doc: ! 224: * This routine will find the next entry in a nested value option or ! 225: * configurable. It will search through the list and return the next entry ! 226: * that matches the criteria. ! 227: * ! 228: * err: ! 229: * The returned result is NULL and errno is set: ! 230: * @itemize @bullet ! 231: * @item ! 232: * @code{EINVAL} - the @code{pOptValue} does not point to a valid ! 233: * hierarchical option value. ! 234: * @item ! 235: * @code{ENOENT} - no entry matched the given name. ! 236: * @end itemize ! 237: =*/ ! 238: tOptionValue const * ! 239: optionFindNextValue(const tOptDesc * pOptDesc, const tOptionValue * pPrevVal, ! 240: char const * pzName, char const * pzVal) ! 241: { ! 242: int foundOldVal = 0; ! 243: tOptionValue* pRes = NULL; ! 244: ! 245: if ( (pOptDesc == NULL) ! 246: || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) { ! 247: errno = EINVAL; ! 248: } ! 249: ! 250: else if (pOptDesc->optCookie == NULL) { ! 251: errno = ENOENT; ! 252: } ! 253: ! 254: else do { ! 255: tArgList* pAL = pOptDesc->optCookie; ! 256: int ct = pAL->useCt; ! 257: void** ppOV = (void**)pAL->apzArgs; ! 258: ! 259: if (ct == 0) { ! 260: errno = ENOENT; ! 261: break; ! 262: } ! 263: ! 264: while (--ct >= 0) { ! 265: tOptionValue* pOV = *(ppOV++); ! 266: if (foundOldVal) { ! 267: pRes = pOV; ! 268: break; ! 269: } ! 270: if (pOV == pPrevVal) ! 271: foundOldVal = 1; ! 272: } ! 273: if (pRes == NULL) ! 274: errno = ENOENT; ! 275: } while (0); ! 276: ! 277: return pRes; ! 278: } ! 279: ! 280: ! 281: /*=export_func optionGetValue ! 282: * ! 283: * what: get a specific value from a hierarcical list ! 284: * arg: + const tOptionValue* + pOptValue + a hierarchcal value + ! 285: * arg: + char const* + valueName + name of value to get + ! 286: * ! 287: * ret_type: const tOptionValue* ! 288: * ret_desc: a compound value structure ! 289: * ! 290: * doc: ! 291: * This routine will find an entry in a nested value option or configurable. ! 292: * If "valueName" is NULL, then the first entry is returned. Otherwise, ! 293: * the first entry with a name that exactly matches the argument will be ! 294: * returned. ! 295: * ! 296: * err: ! 297: * The returned result is NULL and errno is set: ! 298: * @itemize @bullet ! 299: * @item ! 300: * @code{EINVAL} - the @code{pOptValue} does not point to a valid ! 301: * hierarchical option value. ! 302: * @item ! 303: * @code{ENOENT} - no entry matched the given name. ! 304: * @end itemize ! 305: =*/ ! 306: const tOptionValue* ! 307: optionGetValue(const tOptionValue* pOld, char const* pzValName) ! 308: { ! 309: tArgList* pAL; ! 310: tOptionValue* pRes = NULL; ! 311: ! 312: if ((pOld == NULL) || (pOld->valType != OPARG_TYPE_HIERARCHY)) { ! 313: errno = EINVAL; ! 314: return NULL; ! 315: } ! 316: pAL = pOld->v.nestVal; ! 317: ! 318: if (pAL->useCt > 0) { ! 319: int ct = pAL->useCt; ! 320: void** papOV = (void**)(pAL->apzArgs); ! 321: ! 322: if (pzValName == NULL) { ! 323: pRes = (tOptionValue*)*papOV; ! 324: } ! 325: ! 326: else do { ! 327: tOptionValue* pOV = *(papOV++); ! 328: if (strcmp(pOV->pzName, pzValName) == 0) { ! 329: pRes = pOV; ! 330: break; ! 331: } ! 332: } while (--ct > 0); ! 333: } ! 334: if (pRes == NULL) ! 335: errno = ENOENT; ! 336: return pRes; ! 337: } ! 338: ! 339: ! 340: /*=export_func optionNextValue ! 341: * ! 342: * what: get the next value from a hierarchical list ! 343: * arg: + const tOptionValue* + pOptValue + a hierarchcal list value + ! 344: * arg: + const tOptionValue* + pOldValue + a value from this list + ! 345: * ! 346: * ret_type: const tOptionValue* ! 347: * ret_desc: a compound value structure ! 348: * ! 349: * doc: ! 350: * This routine will return the next entry after the entry passed in. At the ! 351: * end of the list, NULL will be returned. If the entry is not found on the ! 352: * list, NULL will be returned and "@var{errno}" will be set to EINVAL. ! 353: * The "@var{pOldValue}" must have been gotten from a prior call to this ! 354: * routine or to "@code{opitonGetValue()}". ! 355: * ! 356: * err: ! 357: * The returned result is NULL and errno is set: ! 358: * @itemize @bullet ! 359: * @item ! 360: * @code{EINVAL} - the @code{pOptValue} does not point to a valid ! 361: * hierarchical option value or @code{pOldValue} does not point to a ! 362: * member of that option value. ! 363: * @item ! 364: * @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry. ! 365: * @end itemize ! 366: =*/ ! 367: tOptionValue const * ! 368: optionNextValue(tOptionValue const * pOVList,tOptionValue const * pOldOV ) ! 369: { ! 370: tArgList* pAL; ! 371: tOptionValue* pRes = NULL; ! 372: int err = EINVAL; ! 373: ! 374: if ((pOVList == NULL) || (pOVList->valType != OPARG_TYPE_HIERARCHY)) { ! 375: errno = EINVAL; ! 376: return NULL; ! 377: } ! 378: pAL = pOVList->v.nestVal; ! 379: { ! 380: int ct = pAL->useCt; ! 381: void** papNV = (void**)(pAL->apzArgs); ! 382: ! 383: while (ct-- > 0) { ! 384: tOptionValue* pNV = *(papNV++); ! 385: if (pNV == pOldOV) { ! 386: if (ct == 0) { ! 387: err = ENOENT; ! 388: ! 389: } else { ! 390: err = 0; ! 391: pRes = (tOptionValue*)*papNV; ! 392: } ! 393: break; ! 394: } ! 395: } ! 396: } ! 397: if (err != 0) ! 398: errno = err; ! 399: return pRes; ! 400: } ! 401: ! 402: ! 403: /** ! 404: * Load a file containing presetting information (a configuration file). ! 405: */ ! 406: static void ! 407: file_preset(tOptions * opts, char const * fname, int dir) ! 408: { ! 409: tmap_info_t cfgfile; ! 410: tOptState optst = OPTSTATE_INITIALIZER(PRESET); ! 411: tAoUL st_flags = optst.flags; ! 412: char * ftext = ! 413: text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile); ! 414: ! 415: if (TEXT_MMAP_FAILED_ADDR(ftext)) ! 416: return; ! 417: ! 418: if (dir == DIRECTION_CALLED) { ! 419: st_flags = OPTST_DEFINED; ! 420: dir = DIRECTION_PROCESS; ! 421: } ! 422: ! 423: /* ! 424: * IF this is called via "optionProcess", then we are presetting. ! 425: * This is the default and the PRESETTING bit will be set. ! 426: * If this is called via "optionFileLoad", then the bit is not set ! 427: * and we consider stuff set herein to be "set" by the client program. ! 428: */ ! 429: if ((opts->fOptSet & OPTPROC_PRESETTING) == 0) ! 430: st_flags = OPTST_SET; ! 431: ! 432: do { ! 433: optst.flags = st_flags; ! 434: while (IS_WHITESPACE_CHAR(*ftext)) ftext++; ! 435: ! 436: if (IS_VAR_FIRST_CHAR(*ftext)) { ! 437: ftext = handle_cfg(opts, &optst, ftext, dir); ! 438: ! 439: } else switch (*ftext) { ! 440: case '<': ! 441: if (IS_VAR_FIRST_CHAR(ftext[1])) ! 442: ftext = handle_struct(opts, &optst, ftext, dir); ! 443: ! 444: else switch (ftext[1]) { ! 445: case '?': ! 446: ftext = handle_directive(opts, ftext); ! 447: break; ! 448: ! 449: case '!': ! 450: ftext = handle_comment(ftext); ! 451: break; ! 452: ! 453: case '/': ! 454: ftext = strchr(ftext + 2, '>'); ! 455: if (ftext++ != NULL) ! 456: break; ! 457: ! 458: default: ! 459: goto all_done; ! 460: } ! 461: break; ! 462: ! 463: case '[': ! 464: ftext = handle_section(opts, ftext); ! 465: break; ! 466: ! 467: case '#': ! 468: ftext = strchr(ftext + 1, '\n'); ! 469: break; ! 470: ! 471: default: ! 472: goto all_done; /* invalid format */ ! 473: } ! 474: } while (ftext != NULL); ! 475: ! 476: all_done: ! 477: text_munmap(&cfgfile); ! 478: } ! 479: ! 480: ! 481: /** ! 482: * "pzText" points to a "<!" sequence. ! 483: * Theoretically, we should ensure that it begins with "<!--", ! 484: * but actually I don't care that much. It ends with "-->". ! 485: */ ! 486: static char* ! 487: handle_comment(char* pzText) ! 488: { ! 489: char* pz = strstr(pzText, "-->"); ! 490: if (pz != NULL) ! 491: pz += 3; ! 492: return pz; ! 493: } ! 494: ! 495: ! 496: /** ! 497: * "pzText" points to the start of some value name. ! 498: * The end of the entry is the end of the line that is not preceded by ! 499: * a backslash escape character. The string value is always processed ! 500: * in "cooked" mode. ! 501: */ ! 502: static char * ! 503: handle_cfg(tOptions * pOpts, tOptState * pOS, char * pzText, int dir) ! 504: { ! 505: char* pzName = pzText++; ! 506: char* pzEnd = strchr(pzText, '\n'); ! 507: ! 508: if (pzEnd == NULL) ! 509: return pzText + strlen(pzText); ! 510: ! 511: while (IS_VALUE_NAME_CHAR(*pzText)) pzText++; ! 512: while (IS_WHITESPACE_CHAR(*pzText)) pzText++; ! 513: if (pzText > pzEnd) { ! 514: name_only: ! 515: *pzEnd++ = NUL; ! 516: loadOptionLine(pOpts, pOS, pzName, dir, OPTION_LOAD_UNCOOKED); ! 517: return pzEnd; ! 518: } ! 519: ! 520: /* ! 521: * Either the first character after the name is a ':' or '=', ! 522: * or else we must have skipped over white space. Anything else ! 523: * is an invalid format and we give up parsing the text. ! 524: */ ! 525: if ((*pzText == '=') || (*pzText == ':')) { ! 526: while (IS_WHITESPACE_CHAR(*++pzText)) ; ! 527: if (pzText > pzEnd) ! 528: goto name_only; ! 529: } else if (! IS_WHITESPACE_CHAR(pzText[-1])) ! 530: return NULL; ! 531: ! 532: /* ! 533: * IF the value is continued, remove the backslash escape and push "pzEnd" ! 534: * on to a newline *not* preceded by a backslash. ! 535: */ ! 536: if (pzEnd[-1] == '\\') { ! 537: char* pcD = pzEnd-1; ! 538: char* pcS = pzEnd; ! 539: ! 540: for (;;) { ! 541: char ch = *(pcS++); ! 542: switch (ch) { ! 543: case NUL: ! 544: pcS = NULL; ! 545: ! 546: case '\n': ! 547: *pcD = NUL; ! 548: pzEnd = pcS; ! 549: goto copy_done; ! 550: ! 551: case '\\': ! 552: if (*pcS == '\n') { ! 553: ch = *(pcS++); ! 554: } ! 555: /* FALLTHROUGH */ ! 556: default: ! 557: *(pcD++) = ch; ! 558: } ! 559: } copy_done:; ! 560: ! 561: } else { ! 562: /* ! 563: * The newline was not preceded by a backslash. NUL it out ! 564: */ ! 565: *(pzEnd++) = NUL; ! 566: } ! 567: ! 568: /* ! 569: * "pzName" points to what looks like text for one option/configurable. ! 570: * It is NUL terminated. Process it. ! 571: */ ! 572: loadOptionLine(pOpts, pOS, pzName, dir, OPTION_LOAD_UNCOOKED); ! 573: ! 574: return pzEnd; ! 575: } ! 576: ! 577: ! 578: /** ! 579: * "pzText" points to a "<?" sequence. ! 580: * We handle "<?program" and "<?auto-options" directives. ! 581: * All others are treated as comments. ! 582: */ ! 583: static char * ! 584: handle_directive(tOptions * pOpts, char * pzText) ! 585: { ! 586: # define DIRECTIVE_TABLE \ ! 587: _dt_(zCfgProg, program_directive) \ ! 588: _dt_(zCfgAO_Flags, aoflags_directive) ! 589: ! 590: typedef char * (directive_func_t)(tOptions *, char *); ! 591: # define _dt_(_s, _fn) _fn, ! 592: static directive_func_t * dir_disp[] = { ! 593: DIRECTIVE_TABLE ! 594: }; ! 595: # undef _dt_ ! 596: ! 597: # define _dt_(_s, _fn) 1 + ! 598: static int const dir_ct = DIRECTIVE_TABLE 0; ! 599: static char const * dir_names[DIRECTIVE_TABLE 0]; ! 600: # undef _dt_ ! 601: ! 602: int ix; ! 603: ! 604: if (dir_names[0] == NULL) { ! 605: ix = 0; ! 606: # define _dt_(_s, _fn) dir_names[ix++] = _s; ! 607: DIRECTIVE_TABLE; ! 608: # undef _dt_ ! 609: } ! 610: ! 611: for (ix = 0; ix < dir_ct; ix++) { ! 612: size_t len = strlen(dir_names[ix]); ! 613: if ( (strncmp(pzText + 2, dir_names[ix], len) == 0) ! 614: && (! IS_VALUE_NAME_CHAR(pzText[len+2])) ) ! 615: return dir_disp[ix](pOpts, pzText + len + 2); ! 616: } ! 617: ! 618: /* ! 619: * We don't know what this is. Skip it. ! 620: */ ! 621: pzText = strchr(pzText+2, '>'); ! 622: if (pzText != NULL) ! 623: pzText++; ! 624: return pzText; ! 625: } ! 626: ! 627: /** ! 628: * handle AutoOpts mode flags ! 629: */ ! 630: static char * ! 631: aoflags_directive(tOptions * pOpts, char * pzText) ! 632: { ! 633: char * pz = pzText; ! 634: ! 635: while (IS_WHITESPACE_CHAR(*++pz)) ; ! 636: pzText = strchr(pz, '>'); ! 637: if (pzText != NULL) { ! 638: ! 639: size_t len = pzText - pz; ! 640: char * ftxt = AGALOC(len + 1, "aoflags"); ! 641: ! 642: memcpy(ftxt, pz, len); ! 643: ftxt[len] = NUL; ! 644: set_usage_flags(pOpts, ftxt); ! 645: AGFREE(ftxt); ! 646: ! 647: pzText++; ! 648: } ! 649: ! 650: return pzText; ! 651: } ! 652: ! 653: /** ! 654: * handle program segmentation of config file. ! 655: */ ! 656: static char * ! 657: program_directive(tOptions * pOpts, char * pzText) ! 658: { ! 659: static char const ttlfmt[] = "<?"; ! 660: size_t ttl_len = sizeof(ttlfmt) + strlen(zCfgProg); ! 661: char * ttl = AGALOC(ttl_len, "prog title"); ! 662: size_t name_len = strlen(pOpts->pzProgName); ! 663: ! 664: memcpy(ttl, ttlfmt, sizeof(ttlfmt) - 1); ! 665: memcpy(ttl + sizeof(ttlfmt) - 1, zCfgProg, ttl_len - (sizeof(ttlfmt) - 1)); ! 666: ! 667: do { ! 668: while (IS_WHITESPACE_CHAR(*++pzText)) ; ! 669: ! 670: if ( (strneqvcmp(pzText, pOpts->pzProgName, (int)name_len) == 0) ! 671: && (IS_END_XML_TOKEN_CHAR(pzText[name_len])) ) { ! 672: pzText += name_len; ! 673: break; ! 674: } ! 675: ! 676: pzText = strstr(pzText, ttl); ! 677: } while (pzText != NULL); ! 678: ! 679: AGFREE(ttl); ! 680: if (pzText != NULL) ! 681: for (;;) { ! 682: if (*pzText == NUL) { ! 683: pzText = NULL; ! 684: break; ! 685: } ! 686: if (*(pzText++) == '>') ! 687: break; ! 688: } ! 689: ! 690: return pzText; ! 691: } ! 692: ! 693: ! 694: /** ! 695: * "pzText" points to a '[' character. ! 696: * The "traditional" [PROG_NAME] segmentation of the config file. ! 697: * Do not ever mix with the "<?program prog-name>" variation. ! 698: */ ! 699: static char * ! 700: handle_section(tOptions * pOpts, char * pzText) ! 701: { ! 702: size_t len = strlen(pOpts->pzPROGNAME); ! 703: if ( (strncmp(pzText+1, pOpts->pzPROGNAME, len) == 0) ! 704: && (pzText[len+1] == ']')) ! 705: return strchr(pzText + len + 2, '\n'); ! 706: ! 707: if (len > 16) ! 708: return NULL; ! 709: ! 710: { ! 711: char z[24]; ! 712: sprintf(z, "[%s]", pOpts->pzPROGNAME); ! 713: pzText = strstr(pzText, z); ! 714: } ! 715: ! 716: if (pzText != NULL) ! 717: pzText = strchr(pzText, '\n'); ! 718: return pzText; ! 719: } ! 720: ! 721: /** ! 722: * parse XML encodings ! 723: */ ! 724: static int ! 725: parse_xml_encoding(char ** ppz) ! 726: { ! 727: # define XMLTABLE \ ! 728: _xmlNm_(amp, '&') \ ! 729: _xmlNm_(lt, '<') \ ! 730: _xmlNm_(gt, '>') \ ! 731: _xmlNm_(ff, '\f') \ ! 732: _xmlNm_(ht, '\t') \ ! 733: _xmlNm_(cr, '\r') \ ! 734: _xmlNm_(vt, '\v') \ ! 735: _xmlNm_(bel, '\a') \ ! 736: _xmlNm_(nl, '\n') \ ! 737: _xmlNm_(space, ' ') \ ! 738: _xmlNm_(quot, '"') \ ! 739: _xmlNm_(apos, '\'') ! 740: ! 741: static struct { ! 742: char const * const nm_str; ! 743: unsigned short nm_len; ! 744: short nm_val; ! 745: } const xml_names[] = { ! 746: # define _xmlNm_(_n, _v) { #_n ";", sizeof(#_n), _v }, ! 747: XMLTABLE ! 748: # undef _xmlNm_ ! 749: # undef XMLTABLE ! 750: }; ! 751: ! 752: static int const nm_ct = sizeof(xml_names) / sizeof(xml_names[0]); ! 753: int base = 10; ! 754: ! 755: char * pz = *ppz; ! 756: ! 757: if (*pz == '#') { ! 758: pz++; ! 759: goto parse_number; ! 760: } ! 761: ! 762: if (IS_DEC_DIGIT_CHAR(*pz)) { ! 763: unsigned long v; ! 764: ! 765: parse_number: ! 766: switch (*pz) { ! 767: case 'x': case 'X': ! 768: /* ! 769: * Some forms specify hex with: &#xNN; ! 770: */ ! 771: base = 16; ! 772: pz++; ! 773: break; ! 774: ! 775: case '0': ! 776: /* ! 777: *  is hex and  is decimal. Cool. ! 778: * Ya gotta love it. ! 779: */ ! 780: if (pz[1] == '0') ! 781: base = 16; ! 782: break; ! 783: } ! 784: ! 785: v = strtoul(pz, &pz, base); ! 786: if ((*pz != ';') || (v > 0x7F)) ! 787: return NUL; ! 788: *ppz = pz + 1; ! 789: return (int)v; ! 790: } ! 791: ! 792: { ! 793: int ix = 0; ! 794: do { ! 795: if (strncmp(pz, xml_names[ix].nm_str, xml_names[ix].nm_len) ! 796: == 0) { ! 797: *ppz = pz + xml_names[ix].nm_len; ! 798: return xml_names[ix].nm_val; ! 799: } ! 800: } while (++ix < nm_ct); ! 801: } ! 802: ! 803: return NUL; ! 804: } ! 805: ! 806: /** ! 807: * Find the end marker for the named section of XML. ! 808: * Trim that text there, trimming trailing white space for all modes ! 809: * except for OPTION_LOAD_UNCOOKED. ! 810: */ ! 811: static char * ! 812: trim_xml_text(char * pztxt, char const * pznm, tOptionLoadMode mode) ! 813: { ! 814: static char const fmt[] = "</%s>"; ! 815: char z[64], *pz = z; ! 816: size_t len = strlen(pznm) + sizeof(fmt) - 2 /* for %s */; ! 817: ! 818: if (len > sizeof(z)) ! 819: pz = AGALOC(len, "scan name"); ! 820: ! 821: sprintf(pz, fmt, pznm); ! 822: *pztxt = ' '; ! 823: pztxt = strstr(pztxt, pz); ! 824: if (pz != z) AGFREE(pz); ! 825: ! 826: if (pztxt == NULL) ! 827: return pztxt; ! 828: ! 829: if (mode != OPTION_LOAD_UNCOOKED) ! 830: while (IS_WHITESPACE_CHAR(pztxt[-1])) len++, pztxt--; ! 831: ! 832: *pztxt = NUL; ! 833: return pztxt + len - 1 /* for NUL byte */; ! 834: } ! 835: ! 836: /** ! 837: */ ! 838: static void ! 839: cook_xml_text(char * pzData) ! 840: { ! 841: char * pzs = pzData; ! 842: char * pzd = pzData; ! 843: char bf[4]; ! 844: bf[2] = NUL; ! 845: ! 846: for (;;) { ! 847: int ch = ((int)*(pzs++)) & 0xFF; ! 848: switch (ch) { ! 849: case NUL: ! 850: *pzd = NUL; ! 851: return; ! 852: ! 853: case '&': ! 854: *(pzd++) = \ ! 855: ch = parse_xml_encoding(&pzs); ! 856: if (ch == NUL) ! 857: return; ! 858: break; ! 859: ! 860: case '%': ! 861: bf[0] = *(pzs++); ! 862: bf[1] = *(pzs++); ! 863: if ((bf[0] == NUL) || (bf[1] == NUL)) { ! 864: *pzd = NUL; ! 865: return; ! 866: } ! 867: ! 868: ch = strtoul(bf, NULL, 16); ! 869: /* FALLTHROUGH */ ! 870: ! 871: default: ! 872: *(pzd++) = ch; ! 873: } ! 874: } ! 875: } ! 876: ! 877: /** ! 878: * "pzText" points to a '<' character, followed by an alpha. ! 879: * The end of the entry is either the "/>" following the name, or else a ! 880: * "</name>" string. ! 881: */ ! 882: static char * ! 883: handle_struct(tOptions * pOpts, tOptState * pOS, char * pzText, int dir) ! 884: { ! 885: tOptionLoadMode mode = option_load_mode; ! 886: tOptionValue valu; ! 887: ! 888: char* pzName = ++pzText; ! 889: char* pzData; ! 890: char* pcNulPoint; ! 891: ! 892: while (IS_VALUE_NAME_CHAR(*pzText)) pzText++; ! 893: pcNulPoint = pzText; ! 894: valu.valType = OPARG_TYPE_STRING; ! 895: ! 896: switch (*pzText) { ! 897: case ' ': ! 898: case '\t': ! 899: pzText = parseAttributes(pOpts, pzText, &mode, &valu); ! 900: if (*pzText == '>') ! 901: break; ! 902: if (*pzText != '/') ! 903: return NULL; ! 904: /* FALLTHROUGH */ ! 905: ! 906: case '/': ! 907: if (pzText[1] != '>') ! 908: return NULL; ! 909: *pzText = NUL; ! 910: pzText += 2; ! 911: loadOptionLine(pOpts, pOS, pzName, dir, mode); ! 912: return pzText; ! 913: ! 914: case '>': ! 915: break; ! 916: ! 917: default: ! 918: pzText = strchr(pzText, '>'); ! 919: if (pzText != NULL) ! 920: pzText++; ! 921: return pzText; ! 922: } ! 923: ! 924: /* ! 925: * If we are here, we have a value. "pzText" points to a closing angle ! 926: * bracket. Separate the name from the value for a moment. ! 927: */ ! 928: *pcNulPoint = NUL; ! 929: pzData = ++pzText; ! 930: pzText = trim_xml_text(pzText, pzName, mode); ! 931: if (pzText == NULL) ! 932: return pzText; ! 933: ! 934: /* ! 935: * Rejoin the name and value for parsing by "loadOptionLine()". ! 936: * Erase any attributes parsed by "parseAttributes()". ! 937: */ ! 938: memset(pcNulPoint, ' ', pzData - pcNulPoint); ! 939: ! 940: /* ! 941: * If we are getting a "string" value that is to be cooked, ! 942: * then process the XML-ish &xx; XML-ish and %XX hex characters. ! 943: */ ! 944: if ( (valu.valType == OPARG_TYPE_STRING) ! 945: && (mode == OPTION_LOAD_COOKED)) ! 946: cook_xml_text(pzData); ! 947: ! 948: /* ! 949: * "pzName" points to what looks like text for one option/configurable. ! 950: * It is NUL terminated. Process it. ! 951: */ ! 952: loadOptionLine(pOpts, pOS, pzName, dir, mode); ! 953: ! 954: return pzText; ! 955: } ! 956: ! 957: ! 958: /** ! 959: * Load a configuration file. This may be invoked either from ! 960: * scanning the "homerc" list, or from a specific file request. ! 961: * (see "optionFileLoad()", the implementation for --load-opts) ! 962: */ ! 963: LOCAL void ! 964: internalFileLoad(tOptions* pOpts) ! 965: { ! 966: uint32_t svfl; ! 967: int idx; ! 968: int inc; ! 969: char zFileName[ AG_PATH_MAX+1 ]; ! 970: ! 971: if (pOpts->papzHomeList == NULL) ! 972: return; ! 973: ! 974: svfl = pOpts->fOptSet; ! 975: inc = DIRECTION_PRESET; ! 976: ! 977: /* ! 978: * Never stop on errors in config files. ! 979: */ ! 980: pOpts->fOptSet &= ~OPTPROC_ERRSTOP; ! 981: ! 982: /* ! 983: * Find the last RC entry (highest priority entry) ! 984: */ ! 985: for (idx = 0; pOpts->papzHomeList[ idx+1 ] != NULL; ++idx) ; ! 986: ! 987: /* ! 988: * For every path in the home list, ... *TWICE* We start at the last ! 989: * (highest priority) entry, work our way down to the lowest priority, ! 990: * handling the immediate options. ! 991: * Then we go back up, doing the normal options. ! 992: */ ! 993: for (;;) { ! 994: struct stat StatBuf; ! 995: cch_t* pzPath; ! 996: ! 997: /* ! 998: * IF we've reached the bottom end, change direction ! 999: */ ! 1000: if (idx < 0) { ! 1001: inc = DIRECTION_PROCESS; ! 1002: idx = 0; ! 1003: } ! 1004: ! 1005: pzPath = pOpts->papzHomeList[ idx ]; ! 1006: ! 1007: /* ! 1008: * IF we've reached the top end, bail out ! 1009: */ ! 1010: if (pzPath == NULL) ! 1011: break; ! 1012: ! 1013: idx += inc; ! 1014: ! 1015: if (! optionMakePath(zFileName, (int)sizeof(zFileName), ! 1016: pzPath, pOpts->pzProgPath)) ! 1017: continue; ! 1018: ! 1019: /* ! 1020: * IF the file name we constructed is a directory, ! 1021: * THEN append the Resource Configuration file name ! 1022: * ELSE we must have the complete file name ! 1023: */ ! 1024: if (stat(zFileName, &StatBuf) != 0) ! 1025: continue; /* bogus name - skip the home list entry */ ! 1026: ! 1027: if (S_ISDIR(StatBuf.st_mode)) { ! 1028: size_t len = strlen(zFileName); ! 1029: size_t nln = strlen(pOpts->pzRcName) + 1; ! 1030: char * pz = zFileName + len; ! 1031: ! 1032: if (len + 1 + nln >= sizeof(zFileName)) ! 1033: continue; ! 1034: ! 1035: if (pz[-1] != DIRCH) ! 1036: *(pz++) = DIRCH; ! 1037: memcpy(pz, pOpts->pzRcName, nln); ! 1038: } ! 1039: ! 1040: file_preset(pOpts, zFileName, inc); ! 1041: ! 1042: /* ! 1043: * IF we are now to skip config files AND we are presetting, ! 1044: * THEN change direction. We must go the other way. ! 1045: */ ! 1046: { ! 1047: tOptDesc * pOD = pOpts->pOptDesc + pOpts->specOptIdx.save_opts+1; ! 1048: if (DISABLED_OPT(pOD) && PRESETTING(inc)) { ! 1049: idx -= inc; /* go back and reprocess current file */ ! 1050: inc = DIRECTION_PROCESS; ! 1051: } ! 1052: } ! 1053: } /* twice for every path in the home list, ... */ ! 1054: ! 1055: pOpts->fOptSet = svfl; ! 1056: } ! 1057: ! 1058: ! 1059: /*=export_func optionFileLoad ! 1060: * ! 1061: * what: Load the locatable config files, in order ! 1062: * ! 1063: * arg: + tOptions* + pOpts + program options descriptor + ! 1064: * arg: + char const* + pzProg + program name + ! 1065: * ! 1066: * ret_type: int ! 1067: * ret_desc: 0 -> SUCCESS, -1 -> FAILURE ! 1068: * ! 1069: * doc: ! 1070: * ! 1071: * This function looks in all the specified directories for a configuration ! 1072: * file ("rc" file or "ini" file) and processes any found twice. The first ! 1073: * time through, they are processed in reverse order (last file first). At ! 1074: * that time, only "immediate action" configurables are processed. For ! 1075: * example, if the last named file specifies not processing any more ! 1076: * configuration files, then no more configuration files will be processed. ! 1077: * Such an option in the @strong{first} named directory will have no effect. ! 1078: * ! 1079: * Once the immediate action configurables have been handled, then the ! 1080: * directories are handled in normal, forward order. In that way, later ! 1081: * config files can override the settings of earlier config files. ! 1082: * ! 1083: * See the AutoOpts documentation for a thorough discussion of the ! 1084: * config file format. ! 1085: * ! 1086: * Configuration files not found or not decipherable are simply ignored. ! 1087: * ! 1088: * err: Returns the value, "-1" if the program options descriptor ! 1089: * is out of date or indecipherable. Otherwise, the value "0" will ! 1090: * always be returned. ! 1091: =*/ ! 1092: int ! 1093: optionFileLoad(tOptions* pOpts, char const* pzProgram) ! 1094: { ! 1095: if (! SUCCESSFUL(validateOptionsStruct(pOpts, pzProgram))) ! 1096: return -1; ! 1097: ! 1098: { ! 1099: char const ** pp = ! 1100: (char const **)(void *)&(pOpts->pzProgName); ! 1101: *pp = pzProgram; ! 1102: } ! 1103: ! 1104: internalFileLoad(pOpts); ! 1105: return 0; ! 1106: } ! 1107: ! 1108: ! 1109: /*=export_func optionLoadOpt ! 1110: * private: ! 1111: * ! 1112: * what: Load an option rc/ini file ! 1113: * arg: + tOptions* + pOpts + program options descriptor + ! 1114: * arg: + tOptDesc* + pOptDesc + the descriptor for this arg + ! 1115: * ! 1116: * doc: ! 1117: * Processes the options found in the file named with ! 1118: * pOptDesc->optArg.argString. ! 1119: =*/ ! 1120: void ! 1121: optionLoadOpt(tOptions* pOpts, tOptDesc* pOptDesc) ! 1122: { ! 1123: struct stat sb; ! 1124: ! 1125: /* ! 1126: * IF the option is not being disabled, THEN load the file. There must ! 1127: * be a file. (If it is being disabled, then the disablement processing ! 1128: * already took place. It must be done to suppress preloading of ini/rc ! 1129: * files.) ! 1130: */ ! 1131: if ( DISABLED_OPT(pOptDesc) ! 1132: || ((pOptDesc->fOptState & OPTST_RESET) != 0)) ! 1133: return; ! 1134: ! 1135: if (stat(pOptDesc->optArg.argString, &sb) != 0) { ! 1136: if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0) ! 1137: return; ! 1138: ! 1139: fprintf(stderr, zFSErrOptLoad, errno, strerror(errno), ! 1140: pOptDesc->optArg.argString); ! 1141: exit(EX_NOINPUT); ! 1142: /* NOT REACHED */ ! 1143: } ! 1144: ! 1145: if (! S_ISREG(sb.st_mode)) { ! 1146: if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0) ! 1147: return; ! 1148: ! 1149: fprintf(stderr, zNotFile, pOptDesc->optArg.argString); ! 1150: exit(EX_NOINPUT); ! 1151: /* NOT REACHED */ ! 1152: } ! 1153: ! 1154: file_preset(pOpts, pOptDesc->optArg.argString, DIRECTION_CALLED); ! 1155: } ! 1156: ! 1157: ! 1158: /** ! 1159: * Parse the various attributes of an XML-styled config file entry ! 1160: */ ! 1161: LOCAL char* ! 1162: parseAttributes( ! 1163: tOptions* pOpts, ! 1164: char* pzText, ! 1165: tOptionLoadMode* pMode, ! 1166: tOptionValue* pType ) ! 1167: { ! 1168: size_t len; ! 1169: ! 1170: do { ! 1171: if (! IS_WHITESPACE_CHAR(*pzText)) ! 1172: switch (*pzText) { ! 1173: case '/': pType->valType = OPARG_TYPE_NONE; ! 1174: case '>': return pzText; ! 1175: ! 1176: default: ! 1177: case NUL: return NULL; ! 1178: } ! 1179: ! 1180: while (IS_WHITESPACE_CHAR(*++pzText)) ; ! 1181: len = 0; ! 1182: while (IS_LOWER_CASE_CHAR(pzText[len])) len++; ! 1183: ! 1184: switch (find_xat_attribute_id(pzText, len)) { ! 1185: case XAT_KWD_TYPE: ! 1186: pzText = parse_value(pzText+len, pType); ! 1187: break; ! 1188: ! 1189: case XAT_KWD_WORDS: ! 1190: pzText = parse_keyword(pOpts, pzText+len, pType); ! 1191: break; ! 1192: ! 1193: case XAT_KWD_MEMBERS: ! 1194: pzText = parse_set_mem(pOpts, pzText+len, pType); ! 1195: break; ! 1196: ! 1197: case XAT_KWD_COOKED: ! 1198: pzText += len; ! 1199: if (! IS_END_XML_TOKEN_CHAR(*pzText)) ! 1200: goto invalid_kwd; ! 1201: ! 1202: *pMode = OPTION_LOAD_COOKED; ! 1203: break; ! 1204: ! 1205: case XAT_KWD_UNCOOKED: ! 1206: pzText += len; ! 1207: if (! IS_END_XML_TOKEN_CHAR(*pzText)) ! 1208: goto invalid_kwd; ! 1209: ! 1210: *pMode = OPTION_LOAD_UNCOOKED; ! 1211: break; ! 1212: ! 1213: case XAT_KWD_KEEP: ! 1214: pzText += len; ! 1215: if (! IS_END_XML_TOKEN_CHAR(*pzText)) ! 1216: goto invalid_kwd; ! 1217: ! 1218: *pMode = OPTION_LOAD_KEEP; ! 1219: break; ! 1220: ! 1221: default: ! 1222: case XAT_KWD_INVALID: ! 1223: invalid_kwd: ! 1224: pType->valType = OPARG_TYPE_NONE; ! 1225: return skip_unkn(pzText); ! 1226: } ! 1227: } while (pzText != NULL); ! 1228: ! 1229: return pzText; ! 1230: } ! 1231: ! 1232: ! 1233: /** ! 1234: * "pzText" points to the character after "words=". ! 1235: * What should follow is a name of a keyword (enumeration) list. ! 1236: */ ! 1237: static char* ! 1238: parse_keyword(tOptions * pOpts, char * pzText, tOptionValue * pType) ! 1239: { ! 1240: return skip_unkn(pzText); ! 1241: } ! 1242: ! 1243: ! 1244: /** ! 1245: * "pzText" points to the character after "members=" ! 1246: * What should follow is a name of a "set membership". ! 1247: * A collection of bit flags. ! 1248: */ ! 1249: static char* ! 1250: parse_set_mem(tOptions * pOpts, char * pzText, tOptionValue * pType) ! 1251: { ! 1252: return skip_unkn(pzText); ! 1253: } ! 1254: ! 1255: ! 1256: /** ! 1257: * "pzText" points to the character after "type=" ! 1258: */ ! 1259: static char * ! 1260: parse_value(char * pzText, tOptionValue * pType) ! 1261: { ! 1262: size_t len = 0; ! 1263: ! 1264: if (*(pzText++) != '=') ! 1265: goto woops; ! 1266: ! 1267: while (IS_OPTION_NAME_CHAR(pzText[len])) len++; ! 1268: pzText += len; ! 1269: ! 1270: if ((len == 0) || (! IS_END_XML_TOKEN_CHAR(*pzText))) { ! 1271: woops: ! 1272: pType->valType = OPARG_TYPE_NONE; ! 1273: return skip_unkn(pzText); ! 1274: } ! 1275: ! 1276: switch (find_value_type_id(pzText - len, len)) { ! 1277: default: ! 1278: case VTP_KWD_INVALID: goto woops; ! 1279: ! 1280: case VTP_KWD_STRING: ! 1281: pType->valType = OPARG_TYPE_STRING; ! 1282: break; ! 1283: ! 1284: case VTP_KWD_INTEGER: ! 1285: pType->valType = OPARG_TYPE_NUMERIC; ! 1286: break; ! 1287: ! 1288: case VTP_KWD_BOOL: ! 1289: case VTP_KWD_BOOLEAN: ! 1290: pType->valType = OPARG_TYPE_BOOLEAN; ! 1291: break; ! 1292: ! 1293: case VTP_KWD_KEYWORD: ! 1294: pType->valType = OPARG_TYPE_ENUMERATION; ! 1295: break; ! 1296: ! 1297: case VTP_KWD_SET: ! 1298: case VTP_KWD_SET_MEMBERSHIP: ! 1299: pType->valType = OPARG_TYPE_MEMBERSHIP; ! 1300: break; ! 1301: ! 1302: case VTP_KWD_NESTED: ! 1303: case VTP_KWD_HIERARCHY: ! 1304: pType->valType = OPARG_TYPE_HIERARCHY; ! 1305: } ! 1306: ! 1307: return pzText; ! 1308: } ! 1309: ! 1310: ! 1311: /** ! 1312: * Skip over some unknown attribute ! 1313: */ ! 1314: static char * ! 1315: skip_unkn(char* pzText) ! 1316: { ! 1317: for (;; pzText++) { ! 1318: if (IS_END_XML_TOKEN_CHAR(*pzText)) return pzText; ! 1319: if (*pzText == NUL) return NULL; ! 1320: } ! 1321: } ! 1322: ! 1323: ! 1324: /** ! 1325: * Make sure the option descriptor is there and that we understand it. ! 1326: * This should be called from any user entry point where one needs to ! 1327: * worry about validity. (Some entry points are free to assume that ! 1328: * the call is not the first to the library and, thus, that this has ! 1329: * already been called.) ! 1330: */ ! 1331: LOCAL tSuccess ! 1332: validateOptionsStruct(tOptions* pOpts, char const* pzProgram) ! 1333: { ! 1334: if (pOpts == NULL) { ! 1335: fputs(zAO_Bad, stderr); ! 1336: exit(EX_CONFIG); ! 1337: } ! 1338: ! 1339: /* ! 1340: * IF the client has enabled translation and the translation procedure ! 1341: * is available, then go do it. ! 1342: */ ! 1343: if ( ((pOpts->fOptSet & OPTPROC_TRANSLATE) != 0) ! 1344: && (pOpts->pTransProc != NULL) ) { ! 1345: /* ! 1346: * If option names are not to be translated at all, then do not do ! 1347: * it for configuration parsing either. (That is the bit that really ! 1348: * gets tested anyway.) ! 1349: */ ! 1350: if ((pOpts->fOptSet & OPTPROC_NO_XLAT_MASK) == OPTPROC_NXLAT_OPT) ! 1351: pOpts->fOptSet |= OPTPROC_NXLAT_OPT_CFG; ! 1352: (*pOpts->pTransProc)(); ! 1353: pOpts->fOptSet &= ~OPTPROC_TRANSLATE; ! 1354: } ! 1355: ! 1356: /* ! 1357: * IF the struct version is not the current, and also ! 1358: * either too large (?!) or too small, ! 1359: * THEN emit error message and fail-exit ! 1360: */ ! 1361: if ( ( pOpts->structVersion != OPTIONS_STRUCT_VERSION ) ! 1362: && ( (pOpts->structVersion > OPTIONS_STRUCT_VERSION ) ! 1363: || (pOpts->structVersion < OPTIONS_MINIMUM_VERSION ) ! 1364: ) ) { ! 1365: static char const aover[] = ! 1366: __STR(AO_CURRENT)":"__STR(AO_REVISION)":"__STR(AO_AGE)"\n"; ! 1367: ! 1368: fprintf(stderr, zAO_Err, pzProgram, NUM_TO_VER(pOpts->structVersion)); ! 1369: if (pOpts->structVersion > OPTIONS_STRUCT_VERSION ) ! 1370: fputs(zAO_Big, stderr); ! 1371: else ! 1372: fputs(zAO_Sml, stderr); ! 1373: ! 1374: fwrite(aover, sizeof(aover) - 1, 1, stderr); ! 1375: return FAILURE; ! 1376: } ! 1377: ! 1378: /* ! 1379: * If the program name hasn't been set, then set the name and the path ! 1380: * and the set of equivalent characters. ! 1381: */ ! 1382: if (pOpts->pzProgName == NULL) { ! 1383: char const * pz = strrchr(pzProgram, DIRCH); ! 1384: char const ** pp = ! 1385: (char const **)(void **)&(pOpts->pzProgName); ! 1386: if (pz == NULL) ! 1387: *pp = pzProgram; ! 1388: else *pp = pz+1; ! 1389: ! 1390: pp = (char const **)(void **)&(pOpts->pzProgPath); ! 1391: *pp = pzProgram; ! 1392: ! 1393: /* ! 1394: * when comparing long names, these are equivalent ! 1395: */ ! 1396: strequate(zSepChars); ! 1397: } ! 1398: ! 1399: return SUCCESS; ! 1400: } ! 1401: ! 1402: ! 1403: /** ! 1404: * Local Variables: ! 1405: * mode: C ! 1406: * c-file-style: "stroustrup" ! 1407: * indent-tabs-mode: nil ! 1408: * End: ! 1409: * end of autoopts/configfile.c */