Annotation of embedaddon/ntp/sntp/libopts/configfile.c, revision 1.1.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:              *  &#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>