Annotation of embedaddon/ntp/sntp/libopts/configfile.c, revision 1.1

1.1     ! misho       1: /**
        !             2:  * \file configfile.c
        !             3:  *
        !             4:  *  Time-stamp:      "2011-04-06 09:31:24 bkorb"
        !             5:  *
        !             6:  *  configuration/rc/ini file handling.
        !             7:  *
        !             8:  *  This file is part of AutoOpts, a companion to AutoGen.
        !             9:  *  AutoOpts is free software.
        !            10:  *  AutoOpts is Copyright (c) 1992-2011 by Bruce Korb - all rights reserved
        !            11:  *
        !            12:  *  AutoOpts is available under any one of two licenses.  The license
        !            13:  *  in use must be one of these two and the choice is under the control
        !            14:  *  of the user of the license.
        !            15:  *
        !            16:  *   The GNU Lesser General Public License, version 3 or later
        !            17:  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
        !            18:  *
        !            19:  *   The Modified Berkeley Software Distribution License
        !            20:  *      See the file "COPYING.mbsd"
        !            21:  *
        !            22:  *  These files have the following md5sums:
        !            23:  *
        !            24:  *  43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
        !            25:  *  06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
        !            26:  *  66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
        !            27:  */
        !            28: 
        !            29: static void
        !            30: set_usage_flags(tOptions * opts, char const * flg_txt);
        !            31: 
        !            32: /* = = = START-STATIC-FORWARD = = = */
        !            33: static void
        !            34: file_preset(tOptions * opts, char const * fname, int dir);
        !            35: 
        !            36: static char*
        !            37: handle_comment(char* pzText);
        !            38: 
        !            39: static char *
        !            40: handle_cfg(tOptions * pOpts, tOptState * pOS, char * pzText, int dir);
        !            41: 
        !            42: static char *
        !            43: handle_directive(tOptions * pOpts, char * pzText);
        !            44: 
        !            45: static char *
        !            46: aoflags_directive(tOptions * pOpts, char * pzText);
        !            47: 
        !            48: static char *
        !            49: program_directive(tOptions * pOpts, char * pzText);
        !            50: 
        !            51: static char *
        !            52: handle_section(tOptions * pOpts, char * pzText);
        !            53: 
        !            54: static int
        !            55: parse_xml_encoding(char ** ppz);
        !            56: 
        !            57: static char *
        !            58: trim_xml_text(char * pztxt, char const * pznm, tOptionLoadMode mode);
        !            59: 
        !            60: static void
        !            61: cook_xml_text(char * pzData);
        !            62: 
        !            63: static char *
        !            64: handle_struct(tOptions * pOpts, tOptState * pOS, char * pzText, int dir);
        !            65: 
        !            66: static char*
        !            67: parse_keyword(tOptions * pOpts, char * pzText, tOptionValue * pType);
        !            68: 
        !            69: static char*
        !            70: parse_set_mem(tOptions * pOpts, char * pzText, tOptionValue * pType);
        !            71: 
        !            72: static char *
        !            73: parse_value(char * pzText, tOptionValue * pType);
        !            74: 
        !            75: static char *
        !            76: skip_unkn(char* pzText);
        !            77: /* = = = END-STATIC-FORWARD = = = */
        !            78: 
        !            79: 
        !            80: /*=export_func  configFileLoad
        !            81:  *
        !            82:  * what:  parse a configuration file
        !            83:  * arg:   + char const*     + pzFile + the file to load +
        !            84:  *
        !            85:  * ret_type:  const tOptionValue*
        !            86:  * ret_desc:  An allocated, compound value structure
        !            87:  *
        !            88:  * doc:
        !            89:  *  This routine will load a named configuration file and parse the
        !            90:  *  text as a hierarchically valued option.  The option descriptor
        !            91:  *  created from an option definition file is not used via this interface.
        !            92:  *  The returned value is "named" with the input file name and is of
        !            93:  *  type "@code{OPARG_TYPE_HIERARCHY}".  It may be used in calls to
        !            94:  *  @code{optionGetValue()}, @code{optionNextValue()} and
        !            95:  *  @code{optionUnloadNested()}.
        !            96:  *
        !            97:  * err:
        !            98:  *  If the file cannot be loaded or processed, @code{NULL} is returned and
        !            99:  *  @var{errno} is set.  It may be set by a call to either @code{open(2)}
        !           100:  *  @code{mmap(2)} or other file system calls, or it may be:
        !           101:  *  @itemize @bullet
        !           102:  *  @item
        !           103:  *  @code{ENOENT} - the file was empty.
        !           104:  *  @item
        !           105:  *  @code{EINVAL} - the file contents are invalid -- not properly formed.
        !           106:  *  @item
        !           107:  *  @code{ENOMEM} - not enough memory to allocate the needed structures.
        !           108:  *  @end itemize
        !           109: =*/
        !           110: const tOptionValue*
        !           111: configFileLoad(char const* pzFile)
        !           112: {
        !           113:     tmap_info_t   cfgfile;
        !           114:     tOptionValue* pRes = NULL;
        !           115:     tOptionLoadMode save_mode = option_load_mode;
        !           116: 
        !           117:     char* pzText =
        !           118:         text_mmap(pzFile, PROT_READ, MAP_PRIVATE, &cfgfile);
        !           119: 
        !           120:     if (TEXT_MMAP_FAILED_ADDR(pzText))
        !           121:         return NULL; /* errno is set */
        !           122: 
        !           123:     option_load_mode = OPTION_LOAD_COOKED;
        !           124:     pRes = optionLoadNested(pzText, pzFile, strlen(pzFile));
        !           125: 
        !           126:     if (pRes == NULL) {
        !           127:         int err = errno;
        !           128:         text_munmap(&cfgfile);
        !           129:         errno = err;
        !           130:     } else
        !           131:         text_munmap(&cfgfile);
        !           132: 
        !           133:     option_load_mode = save_mode;
        !           134:     return pRes;
        !           135: }
        !           136: 
        !           137: 
        !           138: /*=export_func  optionFindValue
        !           139:  *
        !           140:  * what:  find a hierarcicaly valued option instance
        !           141:  * arg:   + const tOptDesc* + pOptDesc + an option with a nested arg type +
        !           142:  * arg:   + char const*     + name     + name of value to find +
        !           143:  * arg:   + char const*     + value    + the matching value    +
        !           144:  *
        !           145:  * ret_type:  const tOptionValue*
        !           146:  * ret_desc:  a compound value structure
        !           147:  *
        !           148:  * doc:
        !           149:  *  This routine will find an entry in a nested value option or configurable.
        !           150:  *  It will search through the list and return a matching entry.
        !           151:  *
        !           152:  * err:
        !           153:  *  The returned result is NULL and errno is set:
        !           154:  *  @itemize @bullet
        !           155:  *  @item
        !           156:  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
        !           157:  *  hierarchical option value.
        !           158:  *  @item
        !           159:  *  @code{ENOENT} - no entry matched the given name.
        !           160:  *  @end itemize
        !           161: =*/
        !           162: const tOptionValue*
        !           163: optionFindValue(const tOptDesc* pOptDesc, char const* pzName,
        !           164:                 char const* pzVal)
        !           165: {
        !           166:     const tOptionValue* pRes = NULL;
        !           167: 
        !           168:     if (  (pOptDesc == NULL)
        !           169:        || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY))  {
        !           170:         errno = EINVAL;
        !           171:     }
        !           172: 
        !           173:     else if (pOptDesc->optCookie == NULL) {
        !           174:         errno = ENOENT;
        !           175:     }
        !           176: 
        !           177:     else do {
        !           178:         tArgList* pAL = pOptDesc->optCookie;
        !           179:         int    ct   = pAL->useCt;
        !           180:         void** ppOV = (void**)(pAL->apzArgs);
        !           181: 
        !           182:         if (ct == 0) {
        !           183:             errno = ENOENT;
        !           184:             break;
        !           185:         }
        !           186: 
        !           187:         if (pzName == NULL) {
        !           188:             pRes = (tOptionValue*)*ppOV;
        !           189:             break;
        !           190:         }
        !           191: 
        !           192:         while (--ct >= 0) {
        !           193:             const tOptionValue* pOV = *(ppOV++);
        !           194:             const tOptionValue* pRV = optionGetValue(pOV, pzName);
        !           195: 
        !           196:             if (pRV == NULL)
        !           197:                 continue;
        !           198: 
        !           199:             if (pzVal == NULL) {
        !           200:                 pRes = pOV;
        !           201:                 break;
        !           202:             }
        !           203:         }
        !           204:         if (pRes == NULL)
        !           205:             errno = ENOENT;
        !           206:     } while (0);
        !           207: 
        !           208:     return pRes;
        !           209: }
        !           210: 
        !           211: 
        !           212: /*=export_func  optionFindNextValue
        !           213:  *
        !           214:  * what:  find a hierarcicaly valued option instance
        !           215:  * arg:   + const tOptDesc* + pOptDesc + an option with a nested arg type +
        !           216:  * arg:   + const tOptionValue* + pPrevVal + the last entry +
        !           217:  * arg:   + char const*     + name     + name of value to find +
        !           218:  * arg:   + char const*     + value    + the matching value    +
        !           219:  *
        !           220:  * ret_type:  const tOptionValue*
        !           221:  * ret_desc:  a compound value structure
        !           222:  *
        !           223:  * doc:
        !           224:  *  This routine will find the next entry in a nested value option or
        !           225:  *  configurable.  It will search through the list and return the next entry
        !           226:  *  that matches the criteria.
        !           227:  *
        !           228:  * err:
        !           229:  *  The returned result is NULL and errno is set:
        !           230:  *  @itemize @bullet
        !           231:  *  @item
        !           232:  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
        !           233:  *  hierarchical option value.
        !           234:  *  @item
        !           235:  *  @code{ENOENT} - no entry matched the given name.
        !           236:  *  @end itemize
        !           237: =*/
        !           238: tOptionValue const *
        !           239: optionFindNextValue(const tOptDesc * pOptDesc, const tOptionValue * pPrevVal,
        !           240:                     char const * pzName, char const * pzVal)
        !           241: {
        !           242:     int foundOldVal = 0;
        !           243:     tOptionValue* pRes = NULL;
        !           244: 
        !           245:     if (  (pOptDesc == NULL)
        !           246:        || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY))  {
        !           247:         errno = EINVAL;
        !           248:     }
        !           249: 
        !           250:     else if (pOptDesc->optCookie == NULL) {
        !           251:         errno = ENOENT;
        !           252:     }
        !           253: 
        !           254:     else do {
        !           255:         tArgList* pAL = pOptDesc->optCookie;
        !           256:         int    ct   = pAL->useCt;
        !           257:         void** ppOV = (void**)pAL->apzArgs;
        !           258: 
        !           259:         if (ct == 0) {
        !           260:             errno = ENOENT;
        !           261:             break;
        !           262:         }
        !           263: 
        !           264:         while (--ct >= 0) {
        !           265:             tOptionValue* pOV = *(ppOV++);
        !           266:             if (foundOldVal) {
        !           267:                 pRes = pOV;
        !           268:                 break;
        !           269:             }
        !           270:             if (pOV == pPrevVal)
        !           271:                 foundOldVal = 1;
        !           272:         }
        !           273:         if (pRes == NULL)
        !           274:             errno = ENOENT;
        !           275:     } while (0);
        !           276: 
        !           277:     return pRes;
        !           278: }
        !           279: 
        !           280: 
        !           281: /*=export_func  optionGetValue
        !           282:  *
        !           283:  * what:  get a specific value from a hierarcical list
        !           284:  * arg:   + const tOptionValue* + pOptValue + a hierarchcal value +
        !           285:  * arg:   + char const*   + valueName + name of value to get +
        !           286:  *
        !           287:  * ret_type:  const tOptionValue*
        !           288:  * ret_desc:  a compound value structure
        !           289:  *
        !           290:  * doc:
        !           291:  *  This routine will find an entry in a nested value option or configurable.
        !           292:  *  If "valueName" is NULL, then the first entry is returned.  Otherwise,
        !           293:  *  the first entry with a name that exactly matches the argument will be
        !           294:  *  returned.
        !           295:  *
        !           296:  * err:
        !           297:  *  The returned result is NULL and errno is set:
        !           298:  *  @itemize @bullet
        !           299:  *  @item
        !           300:  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
        !           301:  *  hierarchical option value.
        !           302:  *  @item
        !           303:  *  @code{ENOENT} - no entry matched the given name.
        !           304:  *  @end itemize
        !           305: =*/
        !           306: const tOptionValue*
        !           307: optionGetValue(const tOptionValue* pOld, char const* pzValName)
        !           308: {
        !           309:     tArgList*     pAL;
        !           310:     tOptionValue* pRes = NULL;
        !           311: 
        !           312:     if ((pOld == NULL) || (pOld->valType != OPARG_TYPE_HIERARCHY)) {
        !           313:         errno = EINVAL;
        !           314:         return NULL;
        !           315:     }
        !           316:     pAL = pOld->v.nestVal;
        !           317: 
        !           318:     if (pAL->useCt > 0) {
        !           319:         int    ct    = pAL->useCt;
        !           320:         void** papOV = (void**)(pAL->apzArgs);
        !           321: 
        !           322:         if (pzValName == NULL) {
        !           323:             pRes = (tOptionValue*)*papOV;
        !           324:         }
        !           325: 
        !           326:         else do {
        !           327:             tOptionValue* pOV = *(papOV++);
        !           328:             if (strcmp(pOV->pzName, pzValName) == 0) {
        !           329:                 pRes = pOV;
        !           330:                 break;
        !           331:             }
        !           332:         } while (--ct > 0);
        !           333:     }
        !           334:     if (pRes == NULL)
        !           335:         errno = ENOENT;
        !           336:     return pRes;
        !           337: }
        !           338: 
        !           339: 
        !           340: /*=export_func  optionNextValue
        !           341:  *
        !           342:  * what:  get the next value from a hierarchical list
        !           343:  * arg:   + const tOptionValue* + pOptValue + a hierarchcal list value +
        !           344:  * arg:   + const tOptionValue* + pOldValue + a value from this list   +
        !           345:  *
        !           346:  * ret_type:  const tOptionValue*
        !           347:  * ret_desc:  a compound value structure
        !           348:  *
        !           349:  * doc:
        !           350:  *  This routine will return the next entry after the entry passed in.  At the
        !           351:  *  end of the list, NULL will be returned.  If the entry is not found on the
        !           352:  *  list, NULL will be returned and "@var{errno}" will be set to EINVAL.
        !           353:  *  The "@var{pOldValue}" must have been gotten from a prior call to this
        !           354:  *  routine or to "@code{opitonGetValue()}".
        !           355:  *
        !           356:  * err:
        !           357:  *  The returned result is NULL and errno is set:
        !           358:  *  @itemize @bullet
        !           359:  *  @item
        !           360:  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
        !           361:  *  hierarchical option value or @code{pOldValue} does not point to a
        !           362:  *  member of that option value.
        !           363:  *  @item
        !           364:  *  @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry.
        !           365:  *  @end itemize
        !           366: =*/
        !           367: tOptionValue const *
        !           368: optionNextValue(tOptionValue const * pOVList,tOptionValue const * pOldOV )
        !           369: {
        !           370:     tArgList*     pAL;
        !           371:     tOptionValue* pRes = NULL;
        !           372:     int           err  = EINVAL;
        !           373: 
        !           374:     if ((pOVList == NULL) || (pOVList->valType != OPARG_TYPE_HIERARCHY)) {
        !           375:         errno = EINVAL;
        !           376:         return NULL;
        !           377:     }
        !           378:     pAL = pOVList->v.nestVal;
        !           379:     {
        !           380:         int    ct    = pAL->useCt;
        !           381:         void** papNV = (void**)(pAL->apzArgs);
        !           382: 
        !           383:         while (ct-- > 0) {
        !           384:             tOptionValue* pNV = *(papNV++);
        !           385:             if (pNV == pOldOV) {
        !           386:                 if (ct == 0) {
        !           387:                     err = ENOENT;
        !           388: 
        !           389:                 } else {
        !           390:                     err  = 0;
        !           391:                     pRes = (tOptionValue*)*papNV;
        !           392:                 }
        !           393:                 break;
        !           394:             }
        !           395:         }
        !           396:     }
        !           397:     if (err != 0)
        !           398:         errno = err;
        !           399:     return pRes;
        !           400: }
        !           401: 
        !           402: 
        !           403: /**
        !           404:  *  Load a file containing presetting information (a configuration file).
        !           405:  */
        !           406: static void
        !           407: file_preset(tOptions * opts, char const * fname, int dir)
        !           408: {
        !           409:     tmap_info_t   cfgfile;
        !           410:     tOptState     optst = OPTSTATE_INITIALIZER(PRESET);
        !           411:     tAoUL         st_flags = optst.flags;
        !           412:     char *        ftext =
        !           413:         text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile);
        !           414: 
        !           415:     if (TEXT_MMAP_FAILED_ADDR(ftext))
        !           416:         return;
        !           417: 
        !           418:     if (dir == DIRECTION_CALLED) {
        !           419:         st_flags = OPTST_DEFINED;
        !           420:         dir   = DIRECTION_PROCESS;
        !           421:     }
        !           422: 
        !           423:     /*
        !           424:      *  IF this is called via "optionProcess", then we are presetting.
        !           425:      *  This is the default and the PRESETTING bit will be set.
        !           426:      *  If this is called via "optionFileLoad", then the bit is not set
        !           427:      *  and we consider stuff set herein to be "set" by the client program.
        !           428:      */
        !           429:     if ((opts->fOptSet & OPTPROC_PRESETTING) == 0)
        !           430:         st_flags = OPTST_SET;
        !           431: 
        !           432:     do  {
        !           433:         optst.flags = st_flags;
        !           434:         while (IS_WHITESPACE_CHAR(*ftext))  ftext++;
        !           435: 
        !           436:         if (IS_VAR_FIRST_CHAR(*ftext)) {
        !           437:             ftext = handle_cfg(opts, &optst, ftext, dir);
        !           438: 
        !           439:         } else switch (*ftext) {
        !           440:         case '<':
        !           441:             if (IS_VAR_FIRST_CHAR(ftext[1]))
        !           442:                 ftext = handle_struct(opts, &optst, ftext, dir);
        !           443: 
        !           444:             else switch (ftext[1]) {
        !           445:             case '?':
        !           446:                 ftext = handle_directive(opts, ftext);
        !           447:                 break;
        !           448: 
        !           449:             case '!':
        !           450:                 ftext = handle_comment(ftext);
        !           451:                 break;
        !           452: 
        !           453:             case '/':
        !           454:                 ftext = strchr(ftext + 2, '>');
        !           455:                 if (ftext++ != NULL)
        !           456:                     break;
        !           457: 
        !           458:             default:
        !           459:                 goto all_done;
        !           460:             }
        !           461:             break;
        !           462: 
        !           463:         case '[':
        !           464:             ftext = handle_section(opts, ftext);
        !           465:             break;
        !           466: 
        !           467:         case '#':
        !           468:             ftext = strchr(ftext + 1, '\n');
        !           469:             break;
        !           470: 
        !           471:         default:
        !           472:             goto all_done; /* invalid format */
        !           473:         }
        !           474:     } while (ftext != NULL);
        !           475: 
        !           476: all_done:
        !           477:     text_munmap(&cfgfile);
        !           478: }
        !           479: 
        !           480: 
        !           481: /**
        !           482:  *  "pzText" points to a "<!" sequence.
        !           483:  *  Theoretically, we should ensure that it begins with "<!--",
        !           484:  *  but actually I don't care that much.  It ends with "-->".
        !           485:  */
        !           486: static char*
        !           487: handle_comment(char* pzText)
        !           488: {
        !           489:     char* pz = strstr(pzText, "-->");
        !           490:     if (pz != NULL)
        !           491:         pz += 3;
        !           492:     return pz;
        !           493: }
        !           494: 
        !           495: 
        !           496: /**
        !           497:  *  "pzText" points to the start of some value name.
        !           498:  *  The end of the entry is the end of the line that is not preceded by
        !           499:  *  a backslash escape character.  The string value is always processed
        !           500:  *  in "cooked" mode.
        !           501:  */
        !           502: static char *
        !           503: handle_cfg(tOptions * pOpts, tOptState * pOS, char * pzText, int dir)
        !           504: {
        !           505:     char* pzName = pzText++;
        !           506:     char* pzEnd  = strchr(pzText, '\n');
        !           507: 
        !           508:     if (pzEnd == NULL)
        !           509:         return pzText + strlen(pzText);
        !           510: 
        !           511:     while (IS_VALUE_NAME_CHAR(*pzText)) pzText++;
        !           512:     while (IS_WHITESPACE_CHAR(*pzText)) pzText++;
        !           513:     if (pzText > pzEnd) {
        !           514:     name_only:
        !           515:         *pzEnd++ = NUL;
        !           516:         loadOptionLine(pOpts, pOS, pzName, dir, OPTION_LOAD_UNCOOKED);
        !           517:         return pzEnd;
        !           518:     }
        !           519: 
        !           520:     /*
        !           521:      *  Either the first character after the name is a ':' or '=',
        !           522:      *  or else we must have skipped over white space.  Anything else
        !           523:      *  is an invalid format and we give up parsing the text.
        !           524:      */
        !           525:     if ((*pzText == '=') || (*pzText == ':')) {
        !           526:         while (IS_WHITESPACE_CHAR(*++pzText))   ;
        !           527:         if (pzText > pzEnd)
        !           528:             goto name_only;
        !           529:     } else if (! IS_WHITESPACE_CHAR(pzText[-1]))
        !           530:         return NULL;
        !           531: 
        !           532:     /*
        !           533:      *  IF the value is continued, remove the backslash escape and push "pzEnd"
        !           534:      *  on to a newline *not* preceded by a backslash.
        !           535:      */
        !           536:     if (pzEnd[-1] == '\\') {
        !           537:         char* pcD = pzEnd-1;
        !           538:         char* pcS = pzEnd;
        !           539: 
        !           540:         for (;;) {
        !           541:             char ch = *(pcS++);
        !           542:             switch (ch) {
        !           543:             case NUL:
        !           544:                 pcS = NULL;
        !           545: 
        !           546:             case '\n':
        !           547:                 *pcD = NUL;
        !           548:                 pzEnd = pcS;
        !           549:                 goto copy_done;
        !           550: 
        !           551:             case '\\':
        !           552:                 if (*pcS == '\n') {
        !           553:                     ch = *(pcS++);
        !           554:                 }
        !           555:                 /* FALLTHROUGH */
        !           556:             default:
        !           557:                 *(pcD++) = ch;
        !           558:             }
        !           559:         } copy_done:;
        !           560: 
        !           561:     } else {
        !           562:         /*
        !           563:          *  The newline was not preceded by a backslash.  NUL it out
        !           564:          */
        !           565:         *(pzEnd++) = NUL;
        !           566:     }
        !           567: 
        !           568:     /*
        !           569:      *  "pzName" points to what looks like text for one option/configurable.
        !           570:      *  It is NUL terminated.  Process it.
        !           571:      */
        !           572:     loadOptionLine(pOpts, pOS, pzName, dir, OPTION_LOAD_UNCOOKED);
        !           573: 
        !           574:     return pzEnd;
        !           575: }
        !           576: 
        !           577: 
        !           578: /**
        !           579:  *  "pzText" points to a "<?" sequence.
        !           580:  *  We handle "<?program" and "<?auto-options" directives.
        !           581:  *  All others are treated as comments.
        !           582:  */
        !           583: static char *
        !           584: handle_directive(tOptions * pOpts, char * pzText)
        !           585: {
        !           586: #   define DIRECTIVE_TABLE                      \
        !           587:     _dt_(zCfgProg,     program_directive)       \
        !           588:     _dt_(zCfgAO_Flags, aoflags_directive)
        !           589: 
        !           590:     typedef char * (directive_func_t)(tOptions *, char *);
        !           591: #   define _dt_(_s, _fn) _fn,
        !           592:     static directive_func_t * dir_disp[] = {
        !           593:         DIRECTIVE_TABLE
        !           594:     };
        !           595: #   undef  _dt_
        !           596: 
        !           597: #   define _dt_(_s, _fn) 1 +
        !           598:     static int  const   dir_ct  = DIRECTIVE_TABLE 0;
        !           599:     static char const * dir_names[DIRECTIVE_TABLE 0];
        !           600: #   undef _dt_
        !           601: 
        !           602:     int    ix;
        !           603: 
        !           604:     if (dir_names[0] == NULL) {
        !           605:         ix = 0;
        !           606: #   define _dt_(_s, _fn) dir_names[ix++] = _s;
        !           607:         DIRECTIVE_TABLE;
        !           608: #   undef _dt_
        !           609:     }
        !           610: 
        !           611:     for (ix = 0; ix < dir_ct; ix++) {
        !           612:         size_t len = strlen(dir_names[ix]);
        !           613:         if (  (strncmp(pzText + 2, dir_names[ix], len) == 0)
        !           614:            && (! IS_VALUE_NAME_CHAR(pzText[len+2])) )
        !           615:             return dir_disp[ix](pOpts, pzText + len + 2);
        !           616:     }
        !           617: 
        !           618:     /*
        !           619:      *  We don't know what this is.  Skip it.
        !           620:      */
        !           621:     pzText = strchr(pzText+2, '>');
        !           622:     if (pzText != NULL)
        !           623:         pzText++;
        !           624:     return pzText;
        !           625: }
        !           626: 
        !           627: /**
        !           628:  *  handle AutoOpts mode flags
        !           629:  */
        !           630: static char *
        !           631: aoflags_directive(tOptions * pOpts, char * pzText)
        !           632: {
        !           633:     char * pz = pzText;
        !           634: 
        !           635:     while (IS_WHITESPACE_CHAR(*++pz))  ;
        !           636:     pzText = strchr(pz, '>');
        !           637:     if (pzText != NULL) {
        !           638: 
        !           639:         size_t len  = pzText - pz;
        !           640:         char * ftxt = AGALOC(len + 1, "aoflags");
        !           641: 
        !           642:         memcpy(ftxt, pz, len);
        !           643:         ftxt[len] = NUL;
        !           644:         set_usage_flags(pOpts, ftxt);
        !           645:         AGFREE(ftxt);
        !           646: 
        !           647:         pzText++;
        !           648:     }
        !           649: 
        !           650:     return pzText;
        !           651: }
        !           652: 
        !           653: /**
        !           654:  * handle program segmentation of config file.
        !           655:  */
        !           656: static char *
        !           657: program_directive(tOptions * pOpts, char * pzText)
        !           658: {
        !           659:     static char const ttlfmt[] = "<?";
        !           660:     size_t ttl_len  = sizeof(ttlfmt) + strlen(zCfgProg);
        !           661:     char * ttl      = AGALOC(ttl_len, "prog title");
        !           662:     size_t name_len = strlen(pOpts->pzProgName);
        !           663: 
        !           664:     memcpy(ttl, ttlfmt, sizeof(ttlfmt) - 1);
        !           665:     memcpy(ttl + sizeof(ttlfmt) - 1, zCfgProg, ttl_len - (sizeof(ttlfmt) - 1));
        !           666: 
        !           667:     do  {
        !           668:         while (IS_WHITESPACE_CHAR(*++pzText))  ;
        !           669: 
        !           670:         if (  (strneqvcmp(pzText, pOpts->pzProgName, (int)name_len) == 0)
        !           671:            && (IS_END_XML_TOKEN_CHAR(pzText[name_len])) ) {
        !           672:             pzText += name_len;
        !           673:             break;
        !           674:         }
        !           675: 
        !           676:         pzText = strstr(pzText, ttl);
        !           677:     } while (pzText != NULL);
        !           678: 
        !           679:     AGFREE(ttl);
        !           680:     if (pzText != NULL)
        !           681:         for (;;) {
        !           682:             if (*pzText == NUL) {
        !           683:                 pzText = NULL;
        !           684:                 break;
        !           685:             }
        !           686:             if (*(pzText++) == '>')
        !           687:                 break;
        !           688:         }
        !           689: 
        !           690:     return pzText;
        !           691: }
        !           692: 
        !           693: 
        !           694: /**
        !           695:  *  "pzText" points to a '[' character.
        !           696:  *  The "traditional" [PROG_NAME] segmentation of the config file.
        !           697:  *  Do not ever mix with the "<?program prog-name>" variation.
        !           698:  */
        !           699: static char *
        !           700: handle_section(tOptions * pOpts, char * pzText)
        !           701: {
        !           702:     size_t len = strlen(pOpts->pzPROGNAME);
        !           703:     if (   (strncmp(pzText+1, pOpts->pzPROGNAME, len) == 0)
        !           704:         && (pzText[len+1] == ']'))
        !           705:         return strchr(pzText + len + 2, '\n');
        !           706: 
        !           707:     if (len > 16)
        !           708:         return NULL;
        !           709: 
        !           710:     {
        !           711:         char z[24];
        !           712:         sprintf(z, "[%s]", pOpts->pzPROGNAME);
        !           713:         pzText = strstr(pzText, z);
        !           714:     }
        !           715: 
        !           716:     if (pzText != NULL)
        !           717:         pzText = strchr(pzText, '\n');
        !           718:     return pzText;
        !           719: }
        !           720: 
        !           721: /**
        !           722:  * parse XML encodings
        !           723:  */
        !           724: static int
        !           725: parse_xml_encoding(char ** ppz)
        !           726: {
        !           727: #   define XMLTABLE             \
        !           728:         _xmlNm_(amp,   '&')     \
        !           729:         _xmlNm_(lt,    '<')     \
        !           730:         _xmlNm_(gt,    '>')     \
        !           731:         _xmlNm_(ff,    '\f')    \
        !           732:         _xmlNm_(ht,    '\t')    \
        !           733:         _xmlNm_(cr,    '\r')    \
        !           734:         _xmlNm_(vt,    '\v')    \
        !           735:         _xmlNm_(bel,   '\a')    \
        !           736:         _xmlNm_(nl,    '\n')    \
        !           737:         _xmlNm_(space, ' ')     \
        !           738:         _xmlNm_(quot,  '"')     \
        !           739:         _xmlNm_(apos,  '\'')
        !           740: 
        !           741:     static struct {
        !           742:         char const * const  nm_str;
        !           743:         unsigned short      nm_len;
        !           744:         short               nm_val;
        !           745:     } const xml_names[] = {
        !           746: #   define _xmlNm_(_n, _v) { #_n ";", sizeof(#_n), _v },
        !           747:         XMLTABLE
        !           748: #   undef  _xmlNm_
        !           749: #   undef XMLTABLE
        !           750:     };
        !           751: 
        !           752:     static int const nm_ct = sizeof(xml_names) / sizeof(xml_names[0]);
        !           753:     int    base = 10;
        !           754: 
        !           755:     char * pz = *ppz;
        !           756: 
        !           757:     if (*pz == '#') {
        !           758:         pz++;
        !           759:         goto parse_number;
        !           760:     }
        !           761: 
        !           762:     if (IS_DEC_DIGIT_CHAR(*pz)) {
        !           763:         unsigned long v;
        !           764: 
        !           765:     parse_number:
        !           766:         switch (*pz) {
        !           767:         case 'x': case 'X':
        !           768:             /*
        !           769:              * Some forms specify hex with:  &#xNN;
        !           770:              */
        !           771:             base = 16;
        !           772:             pz++;
        !           773:             break;
        !           774: 
        !           775:         case '0':
        !           776:             /*
        !           777:              *  &#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>