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

    1: 
    2: /**
    3:  * \file nested.c
    4:  *
    5:  *  Time-stamp:      "2010-08-22 11:17:56 bkorb"
    6:  *
    7:  *   Automated Options Nested Values module.
    8:  *
    9:  *  This file is part of AutoOpts, a companion to AutoGen.
   10:  *  AutoOpts is free software.
   11:  *  AutoOpts is Copyright (c) 1992-2011 by Bruce Korb - all rights reserved
   12:  *
   13:  *  AutoOpts is available under any one of two licenses.  The license
   14:  *  in use must be one of these two and the choice is under the control
   15:  *  of the user of the license.
   16:  *
   17:  *   The GNU Lesser General Public License, version 3 or later
   18:  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
   19:  *
   20:  *   The Modified Berkeley Software Distribution License
   21:  *      See the file "COPYING.mbsd"
   22:  *
   23:  *  These files have the following md5sums:
   24:  *
   25:  *  43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
   26:  *  06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
   27:  *  66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
   28:  */
   29: 
   30: typedef struct {
   31:     int     xml_ch;
   32:     int     xml_len;
   33:     char    xml_txt[8];
   34: } xml_xlate_t;
   35: 
   36: static xml_xlate_t const xml_xlate[] = {
   37:     { '&', 4, "amp;"  },
   38:     { '<', 3, "lt;"   },
   39:     { '>', 3, "gt;"   },
   40:     { '"', 5, "quot;" },
   41:     { '\'',5, "apos;" }
   42: };
   43: 
   44: /* = = = START-STATIC-FORWARD = = = */
   45: static void
   46: remove_continuation(char* pzSrc);
   47: 
   48: static char const*
   49: scan_q_str(char const* pzTxt);
   50: 
   51: static tOptionValue*
   52: add_string(void** pp, char const* pzName, size_t nameLen,
   53:                char const* pzValue, size_t dataLen);
   54: 
   55: static tOptionValue*
   56: add_bool(void** pp, char const* pzName, size_t nameLen,
   57:          char const* pzValue, size_t dataLen);
   58: 
   59: static tOptionValue*
   60: add_number(void** pp, char const* pzName, size_t nameLen,
   61:            char const* pzValue, size_t dataLen);
   62: 
   63: static tOptionValue*
   64: add_nested(void** pp, char const* pzName, size_t nameLen,
   65:            char* pzValue, size_t dataLen);
   66: 
   67: static char const *
   68: scan_name(char const* pzName, tOptionValue* pRes);
   69: 
   70: static char const*
   71: scan_xml(char const* pzName, tOptionValue* pRes);
   72: 
   73: static void
   74: sort_list(tArgList* pAL);
   75: /* = = = END-STATIC-FORWARD = = = */
   76: 
   77: /**
   78:  *  Backslashes are used for line continuations.  We keep the newline
   79:  *  characters, but trim out the backslash:
   80:  */
   81: static void
   82: remove_continuation(char* pzSrc)
   83: {
   84:     char* pzD;
   85: 
   86:     do  {
   87:         while (*pzSrc == '\n')  pzSrc++;
   88:         pzD = strchr(pzSrc, '\n');
   89:         if (pzD == NULL)
   90:             return;
   91: 
   92:         /*
   93:          *  pzD has skipped at least one non-newline character and now
   94:          *  points to a newline character.  It now becomes the source and
   95:          *  pzD goes to the previous character.
   96:          */
   97:         pzSrc = pzD--;
   98:         if (*pzD != '\\')
   99:             pzD++;
  100:     } while (pzD == pzSrc);
  101: 
  102:     /*
  103:      *  Start shifting text.
  104:      */
  105:     for (;;) {
  106:         char ch = ((*pzD++) = *(pzSrc++));
  107:         switch (ch) {
  108:         case NUL:  return;
  109:         case '\\':
  110:             if (*pzSrc == '\n')
  111:                 --pzD; /* rewrite on next iteration */
  112:         }
  113:     }
  114: }
  115: 
  116: /**
  117:  *  Find the end of a quoted string, skipping escaped quote characters.
  118:  */
  119: static char const*
  120: scan_q_str(char const* pzTxt)
  121: {
  122:     char q = *(pzTxt++); /* remember the type of quote */
  123: 
  124:     for (;;) {
  125:         char ch = *(pzTxt++);
  126:         if (ch == NUL)
  127:             return pzTxt-1;
  128: 
  129:         if (ch == q)
  130:             return pzTxt;
  131: 
  132:         if (ch == '\\') {
  133:             ch = *(pzTxt++);
  134:             /*
  135:              *  IF the next character is NUL, drop the backslash, too.
  136:              */
  137:             if (ch == NUL)
  138:                 return pzTxt - 2;
  139: 
  140:             /*
  141:              *  IF the quote character or the escape character were escaped,
  142:              *  then skip both, as long as the string does not end.
  143:              */
  144:             if ((ch == q) || (ch == '\\')) {
  145:                 if (*(pzTxt++) == NUL)
  146:                     return pzTxt-1;
  147:             }
  148:         }
  149:     }
  150: }
  151: 
  152: 
  153: /**
  154:  *  Associate a name with either a string or no value.
  155:  */
  156: static tOptionValue*
  157: add_string(void** pp, char const* pzName, size_t nameLen,
  158:                char const* pzValue, size_t dataLen)
  159: {
  160:     tOptionValue* pNV;
  161:     size_t sz = nameLen + dataLen + sizeof(*pNV);
  162: 
  163:     pNV = AGALOC(sz, "option name/str value pair");
  164:     if (pNV == NULL)
  165:         return NULL;
  166: 
  167:     if (pzValue == NULL) {
  168:         pNV->valType = OPARG_TYPE_NONE;
  169:         pNV->pzName = pNV->v.strVal;
  170: 
  171:     } else {
  172:         pNV->valType = OPARG_TYPE_STRING;
  173:         if (dataLen > 0) {
  174:             char const * pzSrc = pzValue;
  175:             char * pzDst = pNV->v.strVal;
  176:             int    ct    = dataLen;
  177:             do  {
  178:                 int ch = *(pzSrc++) & 0xFF;
  179:                 if (ch == NUL) goto data_copy_done;
  180:                 if (ch == '&')
  181:                     ch = get_special_char(&pzSrc, &ct);
  182:                 *(pzDst++) = ch;
  183:             } while (--ct > 0);
  184:         data_copy_done:
  185:             *pzDst = NUL;
  186: 
  187:         } else {
  188:             pNV->v.strVal[0] = NUL;
  189:         }
  190: 
  191:         pNV->pzName = pNV->v.strVal + dataLen + 1;
  192:     }
  193: 
  194:     memcpy(pNV->pzName, pzName, nameLen);
  195:     pNV->pzName[ nameLen ] = NUL;
  196:     addArgListEntry(pp, pNV);
  197:     return pNV;
  198: }
  199: 
  200: /**
  201:  *  Associate a name with either a string or no value.
  202:  */
  203: static tOptionValue*
  204: add_bool(void** pp, char const* pzName, size_t nameLen,
  205:          char const* pzValue, size_t dataLen)
  206: {
  207:     tOptionValue* pNV;
  208:     size_t sz = nameLen + sizeof(*pNV) + 1;
  209: 
  210:     pNV = AGALOC(sz, "option name/bool value pair");
  211:     if (pNV == NULL)
  212:         return NULL;
  213:     while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) {
  214:         dataLen--; pzValue++;
  215:     }
  216:     if (dataLen == 0)
  217:         pNV->v.boolVal = 0;
  218: 
  219:     else if (IS_DEC_DIGIT_CHAR(*pzValue))
  220:         pNV->v.boolVal = atoi(pzValue);
  221: 
  222:     else pNV->v.boolVal = ! IS_FALSE_TYPE_CHAR(*pzValue);
  223: 
  224:     pNV->valType = OPARG_TYPE_BOOLEAN;
  225:     pNV->pzName = (char*)(pNV + 1);
  226:     memcpy(pNV->pzName, pzName, nameLen);
  227:     pNV->pzName[ nameLen ] = NUL;
  228:     addArgListEntry(pp, pNV);
  229:     return pNV;
  230: }
  231: 
  232: /**
  233:  *  Associate a name with either a string or no value.
  234:  */
  235: static tOptionValue*
  236: add_number(void** pp, char const* pzName, size_t nameLen,
  237:            char const* pzValue, size_t dataLen)
  238: {
  239:     tOptionValue* pNV;
  240:     size_t sz = nameLen + sizeof(*pNV) + 1;
  241: 
  242:     pNV = AGALOC(sz, "option name/bool value pair");
  243:     if (pNV == NULL)
  244:         return NULL;
  245:     while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) {
  246:         dataLen--; pzValue++;
  247:     }
  248:     if (dataLen == 0)
  249:         pNV->v.longVal = 0;
  250:     else
  251:         pNV->v.longVal = strtol(pzValue, 0, 0);
  252: 
  253:     pNV->valType = OPARG_TYPE_NUMERIC;
  254:     pNV->pzName  = (char*)(pNV + 1);
  255:     memcpy(pNV->pzName, pzName, nameLen);
  256:     pNV->pzName[ nameLen ] = NUL;
  257:     addArgListEntry(pp, pNV);
  258:     return pNV;
  259: }
  260: 
  261: /**
  262:  *  Associate a name with either a string or no value.
  263:  */
  264: static tOptionValue*
  265: add_nested(void** pp, char const* pzName, size_t nameLen,
  266:            char* pzValue, size_t dataLen)
  267: {
  268:     tOptionValue* pNV;
  269: 
  270:     if (dataLen == 0) {
  271:         size_t sz = nameLen + sizeof(*pNV) + 1;
  272:         pNV = AGALOC(sz, "empty nested value pair");
  273:         if (pNV == NULL)
  274:             return NULL;
  275:         pNV->v.nestVal = NULL;
  276:         pNV->valType = OPARG_TYPE_HIERARCHY;
  277:         pNV->pzName = (char*)(pNV + 1);
  278:         memcpy(pNV->pzName, pzName, nameLen);
  279:         pNV->pzName[ nameLen ] = NUL;
  280: 
  281:     } else {
  282:         pNV = optionLoadNested(pzValue, pzName, nameLen);
  283:     }
  284: 
  285:     if (pNV != NULL)
  286:         addArgListEntry(pp, pNV);
  287: 
  288:     return pNV;
  289: }
  290: 
  291: /**
  292:  *  We have an entry that starts with a name.  Find the end of it, cook it
  293:  *  (if called for) and create the name/value association.
  294:  */
  295: static char const *
  296: scan_name(char const* pzName, tOptionValue* pRes)
  297: {
  298:     tOptionValue* pNV;
  299:     char const * pzScan = pzName+1; /* we know first char is a name char */
  300:     char const * pzVal;
  301:     size_t       nameLen = 1;
  302:     size_t       dataLen = 0;
  303: 
  304:     /*
  305:      *  Scan over characters that name a value.  These names may not end
  306:      *  with a colon, but they may contain colons.
  307:      */
  308:     while (IS_VALUE_NAME_CHAR(*pzScan))   { pzScan++; nameLen++; }
  309:     if (pzScan[-1] == ':')                { pzScan--; nameLen--; }
  310:     while (IS_HORIZ_WHITE_CHAR(*pzScan))    pzScan++;
  311: 
  312:  re_switch:
  313: 
  314:     switch (*pzScan) {
  315:     case '=':
  316:     case ':':
  317:         while (IS_HORIZ_WHITE_CHAR((int)*++pzScan))  ;
  318:         if ((*pzScan == '=') || (*pzScan == ':'))
  319:             goto default_char;
  320:         goto re_switch;
  321: 
  322:     case '\n':
  323:     case ',':
  324:         pzScan++;
  325:         /* FALLTHROUGH */
  326: 
  327:     case NUL:
  328:         add_string(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
  329:         break;
  330: 
  331:     case '"':
  332:     case '\'':
  333:         pzVal = pzScan;
  334:         pzScan = scan_q_str(pzScan);
  335:         dataLen = pzScan - pzVal;
  336:         pNV = add_string(&(pRes->v.nestVal), pzName, nameLen, pzVal,
  337:                              dataLen);
  338:         if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
  339:             ao_string_cook(pNV->v.strVal, NULL);
  340:         break;
  341: 
  342:     default:
  343:     default_char:
  344:         /*
  345:          *  We have found some strange text value.  It ends with a newline
  346:          *  or a comma.
  347:          */
  348:         pzVal = pzScan;
  349:         for (;;) {
  350:             char ch = *(pzScan++);
  351:             switch (ch) {
  352:             case NUL:
  353:                 pzScan--;
  354:                 dataLen = pzScan - pzVal;
  355:                 goto string_done;
  356:                 /* FALLTHROUGH */
  357: 
  358:             case '\n':
  359:                 if (   (pzScan > pzVal + 2)
  360:                     && (pzScan[-2] == '\\')
  361:                     && (pzScan[ 0] != NUL))
  362:                     continue;
  363:                 /* FALLTHROUGH */
  364: 
  365:             case ',':
  366:                 dataLen = (pzScan - pzVal) - 1;
  367:             string_done:
  368:                 pNV = add_string(&(pRes->v.nestVal), pzName, nameLen,
  369:                                      pzVal, dataLen);
  370:                 if (pNV != NULL)
  371:                     remove_continuation(pNV->v.strVal);
  372:                 goto leave_scan_name;
  373:             }
  374:         }
  375:         break;
  376:     } leave_scan_name:;
  377: 
  378:     return pzScan;
  379: }
  380: 
  381: /**
  382:  *  We've found a '<' character.  We ignore this if it is a comment or a
  383:  *  directive.  If it is something else, then whatever it is we are looking
  384:  *  at is bogus.  Returning NULL stops processing.
  385:  */
  386: static char const*
  387: scan_xml(char const* pzName, tOptionValue* pRes)
  388: {
  389:     size_t nameLen = 1, valLen = 0;
  390:     char const*   pzScan = ++pzName;
  391:     char const*   pzVal;
  392:     tOptionValue  valu;
  393:     tOptionValue* pNewVal;
  394:     tOptionLoadMode save_mode = option_load_mode;
  395: 
  396:     if (! IS_VAR_FIRST_CHAR(*pzName)) {
  397:         switch (*pzName) {
  398:         default:
  399:             pzName = NULL;
  400:             break;
  401: 
  402:         case '!':
  403:             pzName = strstr(pzName, "-->");
  404:             if (pzName != NULL)
  405:                 pzName += 3;
  406:             break;
  407: 
  408:         case '?':
  409:             pzName = strchr(pzName, '>');
  410:             if (pzName != NULL)
  411:                 pzName++;
  412:             break;
  413:         }
  414:         return pzName;
  415:     }
  416: 
  417:     pzScan++;
  418:     while (IS_VALUE_NAME_CHAR((int)*pzScan))  { pzScan++; nameLen++; }
  419:     if (nameLen > 64)
  420:         return NULL;
  421:     valu.valType = OPARG_TYPE_STRING;
  422: 
  423:     switch (*pzScan) {
  424:     case ' ':
  425:     case '\t':
  426:         pzScan = parseAttributes(
  427:             NULL, (char*)pzScan, &option_load_mode, &valu );
  428:         if (*pzScan == '>') {
  429:             pzScan++;
  430:             break;
  431:         }
  432: 
  433:         if (*pzScan != '/') {
  434:             option_load_mode = save_mode;
  435:             return NULL;
  436:         }
  437:         /* FALLTHROUGH */
  438: 
  439:     case '/':
  440:         if (*++pzScan != '>') {
  441:             option_load_mode = save_mode;
  442:             return NULL;
  443:         }
  444:         add_string(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
  445:         option_load_mode = save_mode;
  446:         return pzScan+1;
  447: 
  448:     default:
  449:         option_load_mode = save_mode;
  450:         return NULL;
  451: 
  452:     case '>':
  453:         pzScan++;
  454:         break;
  455:     }
  456: 
  457:     pzVal = pzScan;
  458: 
  459:     {
  460:         char z[68];
  461:         char* pzD = z;
  462:         int  ct = nameLen;
  463:         char const* pzS = pzName;
  464: 
  465:         *(pzD++) = '<';
  466:         *(pzD++) = '/';
  467: 
  468:         do  {
  469:             *(pzD++) = *(pzS++);
  470:         } while (--ct > 0);
  471:         *(pzD++) = '>';
  472:         *pzD = NUL;
  473: 
  474:         pzScan = strstr(pzScan, z);
  475:         if (pzScan == NULL) {
  476:             option_load_mode = save_mode;
  477:             return NULL;
  478:         }
  479:         valLen = (pzScan - pzVal);
  480:         pzScan += nameLen + 3;
  481:         while (IS_WHITESPACE_CHAR(*pzScan))  pzScan++;
  482:     }
  483: 
  484:     switch (valu.valType) {
  485:     case OPARG_TYPE_NONE:
  486:         add_string(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
  487:         break;
  488: 
  489:     case OPARG_TYPE_STRING:
  490:         pNewVal = add_string(
  491:             &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
  492: 
  493:         if (option_load_mode == OPTION_LOAD_KEEP)
  494:             break;
  495:         mungeString(pNewVal->v.strVal, option_load_mode);
  496:         break;
  497: 
  498:     case OPARG_TYPE_BOOLEAN:
  499:         add_bool(&(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
  500:         break;
  501: 
  502:     case OPARG_TYPE_NUMERIC:
  503:         add_number(&(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
  504:         break;
  505: 
  506:     case OPARG_TYPE_HIERARCHY:
  507:     {
  508:         char* pz = AGALOC(valLen+1, "hierarchical scan");
  509:         if (pz == NULL)
  510:             break;
  511:         memcpy(pz, pzVal, valLen);
  512:         pz[valLen] = NUL;
  513:         add_nested(&(pRes->v.nestVal), pzName, nameLen, pz, valLen);
  514:         AGFREE(pz);
  515:         break;
  516:     }
  517: 
  518:     case OPARG_TYPE_ENUMERATION:
  519:     case OPARG_TYPE_MEMBERSHIP:
  520:     default:
  521:         break;
  522:     }
  523: 
  524:     option_load_mode = save_mode;
  525:     return pzScan;
  526: }
  527: 
  528: 
  529: /**
  530:  *  Deallocate a list of option arguments.  This must have been gotten from
  531:  *  a hierarchical option argument, not a stacked list of strings.  It is
  532:  *  an internal call, so it is not validated.  The caller is responsible for
  533:  *  knowing what they are doing.
  534:  */
  535: LOCAL void
  536: unload_arg_list(tArgList* pAL)
  537: {
  538:     int ct = pAL->useCt;
  539:     tCC** ppNV = pAL->apzArgs;
  540: 
  541:     while (ct-- > 0) {
  542:         tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
  543:         if (pNV->valType == OPARG_TYPE_HIERARCHY)
  544:             unload_arg_list(pNV->v.nestVal);
  545:         AGFREE(pNV);
  546:     }
  547: 
  548:     AGFREE((void*)pAL);
  549: }
  550: 
  551: /*=export_func  optionUnloadNested
  552:  *
  553:  * what:  Deallocate the memory for a nested value
  554:  * arg:   + tOptionValue const * + pOptVal + the hierarchical value +
  555:  *
  556:  * doc:
  557:  *  A nested value needs to be deallocated.  The pointer passed in should
  558:  *  have been gotten from a call to @code{configFileLoad()} (See
  559:  *  @pxref{libopts-configFileLoad}).
  560: =*/
  561: void
  562: optionUnloadNested(tOptionValue const * pOV)
  563: {
  564:     if (pOV == NULL) return;
  565:     if (pOV->valType != OPARG_TYPE_HIERARCHY) {
  566:         errno = EINVAL;
  567:         return;
  568:     }
  569: 
  570:     unload_arg_list(pOV->v.nestVal);
  571: 
  572:     AGFREE((void*)pOV);
  573: }
  574: 
  575: /**
  576:  *  This is a _stable_ sort.  The entries are sorted alphabetically,
  577:  *  but within entries of the same name the ordering is unchanged.
  578:  *  Typically, we also hope the input is sorted.
  579:  */
  580: static void
  581: sort_list(tArgList* pAL)
  582: {
  583:     int ix;
  584:     int lm = pAL->useCt;
  585: 
  586:     /*
  587:      *  This loop iterates "useCt" - 1 times.
  588:      */
  589:     for (ix = 0; ++ix < lm;) {
  590:         int iy = ix-1;
  591:         tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
  592:         tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
  593: 
  594:         /*
  595:          *  For as long as the new entry precedes the "old" entry,
  596:          *  move the old pointer.  Stop before trying to extract the
  597:          *  "-1" entry.
  598:          */
  599:         while (strcmp(pOldNV->pzName, pNewNV->pzName) > 0) {
  600:             pAL->apzArgs[iy+1] = (void*)pOldNV;
  601:             pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
  602:             if (iy < 0)
  603:                 break;
  604:         }
  605: 
  606:         /*
  607:          *  Always store the pointer.  Sometimes it is redundant,
  608:          *  but the redundancy is cheaper than a test and branch sequence.
  609:          */
  610:         pAL->apzArgs[iy+1] = (void*)pNewNV;
  611:     }
  612: }
  613: 
  614: /* optionLoadNested
  615:  * private:
  616:  *
  617:  * what:  parse a hierarchical option argument
  618:  * arg:   + char const*     + pzTxt   + the text to scan +
  619:  * arg:   + char const*     + pzName  + the name for the text +
  620:  * arg:   + size_t          + nameLen + the length of "name"  +
  621:  *
  622:  * ret_type:  tOptionValue*
  623:  * ret_desc:  An allocated, compound value structure
  624:  *
  625:  * doc:
  626:  *  A block of text represents a series of values.  It may be an
  627:  *  entire configuration file, or it may be an argument to an
  628:  *  option that takes a hierarchical value.
  629:  */
  630: LOCAL tOptionValue*
  631: optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
  632: {
  633:     tOptionValue* pRes;
  634: 
  635:     /*
  636:      *  Make sure we have some data and we have space to put what we find.
  637:      */
  638:     if (pzTxt == NULL) {
  639:         errno = EINVAL;
  640:         return NULL;
  641:     }
  642:     while (IS_WHITESPACE_CHAR(*pzTxt))  pzTxt++;
  643:     if (*pzTxt == NUL) {
  644:         errno = ENOENT;
  645:         return NULL;
  646:     }
  647:     pRes = AGALOC(sizeof(*pRes) + nameLen + 1, "nested args");
  648:     if (pRes == NULL) {
  649:         errno = ENOMEM;
  650:         return NULL;
  651:     }
  652:     pRes->valType   = OPARG_TYPE_HIERARCHY;
  653:     pRes->pzName    = (char*)(pRes + 1);
  654:     memcpy(pRes->pzName, pzName, nameLen);
  655:     pRes->pzName[nameLen] = NUL;
  656: 
  657:     {
  658:         tArgList * pAL = AGALOC(sizeof(*pAL), "nested arg list");
  659:         if (pAL == NULL) {
  660:             AGFREE(pRes);
  661:             return NULL;
  662:         }
  663: 
  664:         pRes->v.nestVal = pAL;
  665:         pAL->useCt   = 0;
  666:         pAL->allocCt = MIN_ARG_ALLOC_CT;
  667:     }
  668: 
  669:     /*
  670:      *  Scan until we hit a NUL.
  671:      */
  672:     do  {
  673:         while (IS_WHITESPACE_CHAR((int)*pzTxt))  pzTxt++;
  674:         if (IS_VAR_FIRST_CHAR((int)*pzTxt)) {
  675:             pzTxt = scan_name(pzTxt, pRes);
  676:         }
  677:         else switch (*pzTxt) {
  678:         case NUL: goto scan_done;
  679:         case '<': pzTxt = scan_xml(pzTxt, pRes);
  680:                   if (pzTxt == NULL) goto woops;
  681:                   if (*pzTxt == ',') pzTxt++;     break;
  682:         case '#': pzTxt = strchr(pzTxt, '\n');  break;
  683:         default:  goto woops;
  684:         }
  685:     } while (pzTxt != NULL); scan_done:;
  686: 
  687:     {
  688:         tArgList * al = pRes->v.nestVal;
  689:         if (al->useCt != 0)
  690:             sort_list(al);
  691:     }
  692: 
  693:     return pRes;
  694: 
  695:  woops:
  696:     AGFREE(pRes->v.nestVal);
  697:     AGFREE(pRes);
  698:     return NULL;
  699: }
  700: 
  701: /*=export_func  optionNestedVal
  702:  * private:
  703:  *
  704:  * what:  parse a hierarchical option argument
  705:  * arg:   + tOptions* + pOpts    + program options descriptor +
  706:  * arg:   + tOptDesc* + pOptDesc + the descriptor for this arg +
  707:  *
  708:  * doc:
  709:  *  Nested value was found on the command line
  710: =*/
  711: void
  712: optionNestedVal(tOptions* pOpts, tOptDesc* pOD)
  713: {
  714:     if (pOpts < OPTPROC_EMIT_LIMIT)
  715:         return;
  716: 
  717:     if (pOD->fOptState & OPTST_RESET) {
  718:         tArgList* pAL = pOD->optCookie;
  719:         int       ct;
  720:         tCC **    av;
  721: 
  722:         if (pAL == NULL)
  723:             return;
  724:         ct = pAL->useCt;
  725:         av = pAL->apzArgs;
  726: 
  727:         while (--ct >= 0) {
  728:             void * p = (void *)*(av++);
  729:             optionUnloadNested((tOptionValue const *)p);
  730:         }
  731: 
  732:         AGFREE(pOD->optCookie);
  733: 
  734:     } else {
  735:         tOptionValue* pOV = optionLoadNested(
  736:             pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
  737: 
  738:         if (pOV != NULL)
  739:             addArgListEntry(&(pOD->optCookie), (void*)pOV);
  740:     }
  741: }
  742: 
  743: /*
  744:  * get_special_char
  745:  */
  746: LOCAL int
  747: get_special_char(char const ** ppz, int * ct)
  748: {
  749:     char const * pz = *ppz;
  750: 
  751:     if (*ct < 3)
  752:         return '&';
  753: 
  754:     if (*pz == '#') {
  755:         int base = 10;
  756:         int retch;
  757: 
  758:         pz++;
  759:         if (*pz == 'x') {
  760:             base = 16;
  761:             pz++;
  762:         }
  763:         retch = (int)strtoul(pz, (char **)&pz, base);
  764:         if (*pz != ';')
  765:             return '&';
  766:         base = ++pz - *ppz;
  767:         if (base > *ct)
  768:             return '&';
  769: 
  770:         *ct -= base;
  771:         *ppz = pz;
  772:         return retch;
  773:     }
  774: 
  775:     {
  776:         int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
  777:         xml_xlate_t const * xlatp = xml_xlate;
  778: 
  779:         for (;;) {
  780:             if (  (*ct >= xlatp->xml_len)
  781:                && (strncmp(pz, xlatp->xml_txt, xlatp->xml_len) == 0)) {
  782:                 *ppz += xlatp->xml_len;
  783:                 *ct  -= xlatp->xml_len;
  784:                 return xlatp->xml_ch;
  785:             }
  786: 
  787:             if (--ctr <= 0)
  788:                 break;
  789:             xlatp++;
  790:         }
  791:     }
  792:     return '&';
  793: }
  794: 
  795: /*
  796:  * emit_special_char
  797:  */
  798: LOCAL void
  799: emit_special_char(FILE * fp, int ch)
  800: {
  801:     int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
  802:     xml_xlate_t const * xlatp = xml_xlate;
  803: 
  804:     putc('&', fp);
  805:     for (;;) {
  806:         if (ch == xlatp->xml_ch) {
  807:             fputs(xlatp->xml_txt, fp);
  808:             return;
  809:         }
  810:         if (--ctr <= 0)
  811:             break;
  812:         xlatp++;
  813:     }
  814:     fprintf(fp, "#x%02X;", (ch & 0xFF));
  815: }
  816: 
  817: /*
  818:  * Local Variables:
  819:  * mode: C
  820:  * c-file-style: "stroustrup"
  821:  * indent-tabs-mode: nil
  822:  * End:
  823:  * end of autoopts/nested.c */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>