File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / sntp / libopts / configfile.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:38 2012 UTC (12 years, 7 months ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

    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:              *  &#0022; is hex and &#22; 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>