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