Annotation of embedaddon/ntp/sntp/libopts/nested.c, revision 1.1.1.1

1.1       misho       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>