Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / ntp / dist / sntp / libopts / nested.c
blob8dc53d887d0120309b2a8dcf026f4a871280efca
1 /* $NetBSD$ */
4 /*
5 * Id: 43877d3ade0b626b4bec87f2f340a8358c118333
6 * Time-stamp: "2008-07-28 19:18:28 bkorb"
8 * Automated Options Nested Values module.
10 * This file is part of AutoOpts, a companion to AutoGen.
11 * AutoOpts is free software.
12 * AutoOpts is copyright (c) 1992-2009 by Bruce Korb - all rights reserved
14 * AutoOpts is available under any one of two licenses. The license
15 * in use must be one of these two and the choice is under the control
16 * of the user of the license.
18 * The GNU Lesser General Public License, version 3 or later
19 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
21 * The Modified Berkeley Software Distribution License
22 * See the file "COPYING.mbsd"
24 * These files have the following md5sums:
26 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
27 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
28 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
31 typedef struct {
32 int xml_ch;
33 int xml_len;
34 char xml_txt[8];
35 } xml_xlate_t;
37 static xml_xlate_t const xml_xlate[] = {
38 { '&', 4, "amp;" },
39 { '<', 3, "lt;" },
40 { '>', 3, "gt;" },
41 { '"', 5, "quot;" },
42 { '\'',5, "apos;" }
45 /* = = = START-STATIC-FORWARD = = = */
46 /* static forward declarations maintained by mk-fwd */
47 static void
48 removeLineContinue( char* pzSrc );
50 static char const*
51 scanQuotedString( char const* pzTxt );
53 static tOptionValue*
54 addStringValue( void** pp, char const* pzName, size_t nameLen,
55 char const* pzValue, size_t dataLen );
57 static tOptionValue*
58 addBoolValue( void** pp, char const* pzName, size_t nameLen,
59 char const* pzValue, size_t dataLen );
61 static tOptionValue*
62 addNumberValue( void** pp, char const* pzName, size_t nameLen,
63 char const* pzValue, size_t dataLen );
65 static tOptionValue*
66 addNestedValue( void** pp, char const* pzName, size_t nameLen,
67 char* pzValue, size_t dataLen );
69 static char const*
70 scanNameEntry(char const* pzName, tOptionValue* pRes);
72 static char const*
73 scanXmlEntry( char const* pzName, tOptionValue* pRes );
75 static void
76 unloadNestedArglist( tArgList* pAL );
78 static void
79 sortNestedList( tArgList* pAL );
80 /* = = = END-STATIC-FORWARD = = = */
82 /* removeLineContinue
84 * Backslashes are used for line continuations. We keep the newline
85 * characters, but trim out the backslash:
87 static void
88 removeLineContinue( char* pzSrc )
90 char* pzD;
92 do {
93 while (*pzSrc == '\n') pzSrc++;
94 pzD = strchr(pzSrc, '\n');
95 if (pzD == NULL)
96 return;
99 * pzD has skipped at least one non-newline character and now
100 * points to a newline character. It now becomes the source and
101 * pzD goes to the previous character.
103 pzSrc = pzD--;
104 if (*pzD != '\\')
105 pzD++;
106 } while (pzD == pzSrc);
109 * Start shifting text.
111 for (;;) {
112 char ch = ((*pzD++) = *(pzSrc++));
113 switch (ch) {
114 case NUL: return;
115 case '\\':
116 if (*pzSrc == '\n')
117 --pzD; /* rewrite on next iteration */
123 /* scanQuotedString
125 * Find the end of a quoted string, skipping escaped quote characters.
127 static char const*
128 scanQuotedString( char const* pzTxt )
130 char q = *(pzTxt++); /* remember the type of quote */
132 for (;;) {
133 char ch = *(pzTxt++);
134 if (ch == NUL)
135 return pzTxt-1;
137 if (ch == q)
138 return pzTxt;
140 if (ch == '\\') {
141 ch = *(pzTxt++);
143 * IF the next character is NUL, drop the backslash, too.
145 if (ch == NUL)
146 return pzTxt - 2;
149 * IF the quote character or the escape character were escaped,
150 * then skip both, as long as the string does not end.
152 if ((ch == q) || (ch == '\\')) {
153 if (*(pzTxt++) == NUL)
154 return pzTxt-1;
161 /* addStringValue
163 * Associate a name with either a string or no value.
165 static tOptionValue*
166 addStringValue( void** pp, char const* pzName, size_t nameLen,
167 char const* pzValue, size_t dataLen )
169 tOptionValue* pNV;
170 size_t sz = nameLen + dataLen + sizeof(*pNV);
172 pNV = AGALOC( sz, "option name/str value pair" );
173 if (pNV == NULL)
174 return NULL;
176 if (pzValue == NULL) {
177 pNV->valType = OPARG_TYPE_NONE;
178 pNV->pzName = pNV->v.strVal;
180 } else {
181 pNV->valType = OPARG_TYPE_STRING;
182 if (dataLen > 0) {
183 char const * pzSrc = pzValue;
184 char * pzDst = pNV->v.strVal;
185 int ct = dataLen;
186 do {
187 int ch = *(pzSrc++) & 0xFF;
188 if (ch == NUL) goto data_copy_done;
189 if (ch == '&')
190 ch = get_special_char(&pzSrc, &ct);
191 *(pzDst++) = ch;
192 } while (--ct > 0);
193 data_copy_done:
194 *pzDst = NUL;
196 } else {
197 pNV->v.strVal[0] = NUL;
200 pNV->pzName = pNV->v.strVal + dataLen + 1;
203 memcpy( pNV->pzName, pzName, nameLen );
204 pNV->pzName[ nameLen ] = NUL;
205 addArgListEntry( pp, pNV );
206 return pNV;
210 /* addBoolValue
212 * Associate a name with either a string or no value.
214 static tOptionValue*
215 addBoolValue( void** pp, char const* pzName, size_t nameLen,
216 char const* pzValue, size_t dataLen )
218 tOptionValue* pNV;
219 size_t sz = nameLen + sizeof(*pNV) + 1;
221 pNV = AGALOC( sz, "option name/bool value pair" );
222 if (pNV == NULL)
223 return NULL;
224 while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) {
225 dataLen--; pzValue++;
227 if (dataLen == 0)
228 pNV->v.boolVal = 0;
230 else if (IS_DEC_DIGIT_CHAR(*pzValue))
231 pNV->v.boolVal = atoi(pzValue);
233 else pNV->v.boolVal = ! IS_FALSE_TYPE_CHAR(*pzValue);
235 pNV->valType = OPARG_TYPE_BOOLEAN;
236 pNV->pzName = (char*)(pNV + 1);
237 memcpy( pNV->pzName, pzName, nameLen );
238 pNV->pzName[ nameLen ] = NUL;
239 addArgListEntry( pp, pNV );
240 return pNV;
244 /* addNumberValue
246 * Associate a name with either a string or no value.
248 static tOptionValue*
249 addNumberValue( void** pp, char const* pzName, size_t nameLen,
250 char const* pzValue, size_t dataLen )
252 tOptionValue* pNV;
253 size_t sz = nameLen + sizeof(*pNV) + 1;
255 pNV = AGALOC( sz, "option name/bool value pair" );
256 if (pNV == NULL)
257 return NULL;
258 while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) {
259 dataLen--; pzValue++;
261 if (dataLen == 0)
262 pNV->v.longVal = 0;
263 else
264 pNV->v.longVal = strtol(pzValue, 0, 0);
266 pNV->valType = OPARG_TYPE_NUMERIC;
267 pNV->pzName = (char*)(pNV + 1);
268 memcpy( pNV->pzName, pzName, nameLen );
269 pNV->pzName[ nameLen ] = NUL;
270 addArgListEntry( pp, pNV );
271 return pNV;
275 /* addNestedValue
277 * Associate a name with either a string or no value.
279 static tOptionValue*
280 addNestedValue( void** pp, char const* pzName, size_t nameLen,
281 char* pzValue, size_t dataLen )
283 tOptionValue* pNV;
285 if (dataLen == 0) {
286 size_t sz = nameLen + sizeof(*pNV) + 1;
287 pNV = AGALOC( sz, "empty nested value pair" );
288 if (pNV == NULL)
289 return NULL;
290 pNV->v.nestVal = NULL;
291 pNV->valType = OPARG_TYPE_HIERARCHY;
292 pNV->pzName = (char*)(pNV + 1);
293 memcpy( pNV->pzName, pzName, nameLen );
294 pNV->pzName[ nameLen ] = NUL;
296 } else {
297 pNV = optionLoadNested( pzValue, pzName, nameLen );
300 if (pNV != NULL)
301 addArgListEntry( pp, pNV );
303 return pNV;
307 /* scanNameEntry
309 * We have an entry that starts with a name. Find the end of it, cook it
310 * (if called for) and create the name/value association.
312 static char const*
313 scanNameEntry(char const* pzName, tOptionValue* pRes)
315 tOptionValue* pNV;
316 char const * pzScan = pzName+1; /* we know first char is a name char */
317 char const * pzVal;
318 size_t nameLen = 1;
319 size_t dataLen = 0;
322 * Scan over characters that name a value. These names may not end
323 * with a colon, but they may contain colons.
325 while (IS_VALUE_NAME_CHAR(*pzScan)) { pzScan++; nameLen++; }
326 if (pzScan[-1] == ':') { pzScan--; nameLen--; }
327 while (IS_HORIZ_WHITE_CHAR(*pzScan)) pzScan++;
329 re_switch:
330 switch (*pzScan) {
331 case '=':
332 case ':':
333 while (IS_HORIZ_WHITE_CHAR( (int)*++pzScan )) ;
334 if ((*pzScan == '=') || (*pzScan == ':'))
335 goto default_char;
336 goto re_switch;
338 case '\n':
339 case ',':
340 pzScan++;
341 /* FALLTHROUGH */
343 case NUL:
344 addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
345 break;
347 case '"':
348 case '\'':
349 pzVal = pzScan;
350 pzScan = scanQuotedString( pzScan );
351 dataLen = pzScan - pzVal;
352 pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal,
353 dataLen );
354 if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
355 ao_string_cook( pNV->v.strVal, NULL );
356 break;
358 default:
359 default_char:
361 * We have found some strange text value. It ends with a newline
362 * or a comma.
364 pzVal = pzScan;
365 for (;;) {
366 char ch = *(pzScan++);
367 switch (ch) {
368 case NUL:
369 pzScan--;
370 dataLen = pzScan - pzVal;
371 goto string_done;
372 /* FALLTHROUGH */
374 case '\n':
375 if ( (pzScan > pzVal + 2)
376 && (pzScan[-2] == '\\')
377 && (pzScan[ 0] != NUL))
378 continue;
379 /* FALLTHROUGH */
381 case ',':
382 dataLen = (pzScan - pzVal) - 1;
383 string_done:
384 pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen,
385 pzVal, dataLen );
386 if (pNV != NULL)
387 removeLineContinue( pNV->v.strVal );
388 goto leave_scan_name;
391 break;
392 } leave_scan_name:;
394 return pzScan;
398 /* scanXmlEntry
400 * We've found a '<' character. We ignore this if it is a comment or a
401 * directive. If it is something else, then whatever it is we are looking
402 * at is bogus. Returning NULL stops processing.
404 static char const*
405 scanXmlEntry( char const* pzName, tOptionValue* pRes )
407 size_t nameLen = 1, valLen = 0;
408 char const* pzScan = ++pzName;
409 char const* pzVal;
410 tOptionValue valu;
411 tOptionValue* pNewVal;
412 tOptionLoadMode save_mode = option_load_mode;
414 if (! IS_VAR_FIRST_CHAR(*pzName)) {
415 switch (*pzName) {
416 default:
417 pzName = NULL;
418 break;
420 case '!':
421 pzName = strstr( pzName, "-->" );
422 if (pzName != NULL)
423 pzName += 3;
424 break;
426 case '?':
427 pzName = strchr( pzName, '>' );
428 if (pzName != NULL)
429 pzName++;
430 break;
432 return pzName;
435 pzScan++;
436 while (IS_VALUE_NAME_CHAR( (int)*pzScan )) { pzScan++; nameLen++; }
437 if (nameLen > 64)
438 return NULL;
439 valu.valType = OPARG_TYPE_STRING;
441 switch (*pzScan) {
442 case ' ':
443 case '\t':
444 pzScan = parseAttributes(
445 NULL, (char*)pzScan, &option_load_mode, &valu );
446 if (*pzScan == '>') {
447 pzScan++;
448 break;
451 if (*pzScan != '/') {
452 option_load_mode = save_mode;
453 return NULL;
455 /* FALLTHROUGH */
457 case '/':
458 if (*++pzScan != '>') {
459 option_load_mode = save_mode;
460 return NULL;
462 addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
463 option_load_mode = save_mode;
464 return pzScan+1;
466 default:
467 option_load_mode = save_mode;
468 return NULL;
470 case '>':
471 pzScan++;
472 break;
475 pzVal = pzScan;
478 char z[68];
479 char* pzD = z;
480 int ct = nameLen;
481 char const* pzS = pzName;
483 *(pzD++) = '<';
484 *(pzD++) = '/';
486 do {
487 *(pzD++) = *(pzS++);
488 } while (--ct > 0);
489 *(pzD++) = '>';
490 *pzD = NUL;
492 pzScan = strstr( pzScan, z );
493 if (pzScan == NULL) {
494 option_load_mode = save_mode;
495 return NULL;
497 valLen = (pzScan - pzVal);
498 pzScan += nameLen + 3;
499 while (IS_WHITESPACE_CHAR(*pzScan)) pzScan++;
502 switch (valu.valType) {
503 case OPARG_TYPE_NONE:
504 addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
505 break;
507 case OPARG_TYPE_STRING:
508 pNewVal = addStringValue(
509 &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
511 if (option_load_mode == OPTION_LOAD_KEEP)
512 break;
513 mungeString( pNewVal->v.strVal, option_load_mode );
514 break;
516 case OPARG_TYPE_BOOLEAN:
517 addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
518 break;
520 case OPARG_TYPE_NUMERIC:
521 addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
522 break;
524 case OPARG_TYPE_HIERARCHY:
526 char* pz = AGALOC( valLen+1, "hierarchical scan" );
527 if (pz == NULL)
528 break;
529 memcpy( pz, pzVal, valLen );
530 pz[valLen] = NUL;
531 addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen );
532 AGFREE(pz);
533 break;
536 case OPARG_TYPE_ENUMERATION:
537 case OPARG_TYPE_MEMBERSHIP:
538 default:
539 break;
542 option_load_mode = save_mode;
543 return pzScan;
547 /* unloadNestedArglist
549 * Deallocate a list of option arguments. This must have been gotten from
550 * a hierarchical option argument, not a stacked list of strings. It is
551 * an internal call, so it is not validated. The caller is responsible for
552 * knowing what they are doing.
554 static void
555 unloadNestedArglist( tArgList* pAL )
557 int ct = pAL->useCt;
558 tCC** ppNV = pAL->apzArgs;
560 while (ct-- > 0) {
561 tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
562 if (pNV->valType == OPARG_TYPE_HIERARCHY)
563 unloadNestedArglist( pNV->v.nestVal );
564 AGFREE( pNV );
567 AGFREE( (void*)pAL );
571 /*=export_func optionUnloadNested
573 * what: Deallocate the memory for a nested value
574 * arg: + tOptionValue const * + pOptVal + the hierarchical value +
576 * doc:
577 * A nested value needs to be deallocated. The pointer passed in should
578 * have been gotten from a call to @code{configFileLoad()} (See
579 * @pxref{libopts-configFileLoad}).
581 void
582 optionUnloadNested( tOptionValue const * pOV )
584 if (pOV == NULL) return;
585 if (pOV->valType != OPARG_TYPE_HIERARCHY) {
586 errno = EINVAL;
587 return;
590 unloadNestedArglist( pOV->v.nestVal );
592 AGFREE( (void*)pOV );
596 /* sortNestedList
598 * This is a _stable_ sort. The entries are sorted alphabetically,
599 * but within entries of the same name the ordering is unchanged.
600 * Typically, we also hope the input is sorted.
602 static void
603 sortNestedList( tArgList* pAL )
605 int ix;
606 int lm = pAL->useCt;
609 * This loop iterates "useCt" - 1 times.
611 for (ix = 0; ++ix < lm;) {
612 int iy = ix-1;
613 tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
614 tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
617 * For as long as the new entry precedes the "old" entry,
618 * move the old pointer. Stop before trying to extract the
619 * "-1" entry.
621 while (strcmp( pOldNV->pzName, pNewNV->pzName ) > 0) {
622 pAL->apzArgs[iy+1] = (void*)pOldNV;
623 pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
624 if (iy < 0)
625 break;
629 * Always store the pointer. Sometimes it is redundant,
630 * but the redundancy is cheaper than a test and branch sequence.
632 pAL->apzArgs[iy+1] = (void*)pNewNV;
637 /* optionLoadNested
638 * private:
640 * what: parse a hierarchical option argument
641 * arg: + char const* + pzTxt + the text to scan +
642 * arg: + char const* + pzName + the name for the text +
643 * arg: + size_t + nameLen + the length of "name" +
645 * ret_type: tOptionValue*
646 * ret_desc: An allocated, compound value structure
648 * doc:
649 * A block of text represents a series of values. It may be an
650 * entire configuration file, or it may be an argument to an
651 * option that takes a hierarchical value.
653 LOCAL tOptionValue*
654 optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
656 tOptionValue* pRes;
657 tArgList* pAL;
660 * Make sure we have some data and we have space to put what we find.
662 if (pzTxt == NULL) {
663 errno = EINVAL;
664 return NULL;
666 while (IS_WHITESPACE_CHAR(*pzTxt)) pzTxt++;
667 if (*pzTxt == NUL) {
668 errno = ENOENT;
669 return NULL;
671 pRes = AGALOC( sizeof(*pRes) + nameLen + 1, "nested args" );
672 if (pRes == NULL) {
673 errno = ENOMEM;
674 return NULL;
676 pRes->valType = OPARG_TYPE_HIERARCHY;
677 pRes->pzName = (char*)(pRes + 1);
678 memcpy( pRes->pzName, pzName, nameLen );
679 pRes->pzName[ nameLen ] = NUL;
681 pAL = AGALOC( sizeof(*pAL), "nested arg list" );
682 if (pAL == NULL) {
683 AGFREE( pRes );
684 return NULL;
686 pRes->v.nestVal = pAL;
687 pAL->useCt = 0;
688 pAL->allocCt = MIN_ARG_ALLOC_CT;
691 * Scan until we hit a NUL.
693 do {
694 while (IS_WHITESPACE_CHAR( (int)*pzTxt )) pzTxt++;
695 if (IS_VAR_FIRST_CHAR( (int)*pzTxt )) {
696 pzTxt = scanNameEntry( pzTxt, pRes );
698 else switch (*pzTxt) {
699 case NUL: goto scan_done;
700 case '<': pzTxt = scanXmlEntry( pzTxt, pRes );
701 if (pzTxt == NULL) goto woops;
702 if (*pzTxt == ',') pzTxt++; break;
703 case '#': pzTxt = strchr( pzTxt, '\n' ); break;
704 default: goto woops;
706 } while (pzTxt != NULL); scan_done:;
708 pAL = pRes->v.nestVal;
709 if (pAL->useCt != 0) {
710 sortNestedList( pAL );
711 return pRes;
714 woops:
715 AGFREE( pRes->v.nestVal );
716 AGFREE( pRes );
717 return NULL;
721 /*=export_func optionNestedVal
722 * private:
724 * what: parse a hierarchical option argument
725 * arg: + tOptions* + pOpts + program options descriptor +
726 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
728 * doc:
729 * Nested value was found on the command line
731 void
732 optionNestedVal(tOptions* pOpts, tOptDesc* pOD)
734 if (pOpts < OPTPROC_EMIT_LIMIT)
735 return;
737 if (pOD->fOptState & OPTST_RESET) {
738 tArgList* pAL = pOD->optCookie;
739 int ct;
740 tCC ** av;
742 if (pAL == NULL)
743 return;
744 ct = pAL->useCt;
745 av = pAL->apzArgs;
747 while (--ct >= 0) {
748 void * p = (void *)*(av++);
749 optionUnloadNested((tOptionValue const *)p);
752 AGFREE(pOD->optCookie);
754 } else {
755 tOptionValue* pOV = optionLoadNested(
756 pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
758 if (pOV != NULL)
759 addArgListEntry( &(pOD->optCookie), (void*)pOV );
765 * get_special_char
767 LOCAL int
768 get_special_char(char const ** ppz, int * ct)
770 char const * pz = *ppz;
772 if (*ct < 3)
773 return '&';
775 if (*pz == '#') {
776 int base = 10;
777 int retch;
779 pz++;
780 if (*pz == 'x') {
781 base = 16;
782 pz++;
784 retch = (int)strtoul(pz, (char **)&pz, base);
785 if (*pz != ';')
786 return '&';
787 base = ++pz - *ppz;
788 if (base > *ct)
789 return '&';
791 *ct -= base;
792 *ppz = pz;
793 return retch;
797 int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
798 xml_xlate_t const * xlatp = xml_xlate;
800 for (;;) {
801 if ( (*ct >= xlatp->xml_len)
802 && (strncmp(pz, xlatp->xml_txt, xlatp->xml_len) == 0)) {
803 *ppz += xlatp->xml_len;
804 *ct -= xlatp->xml_len;
805 return xlatp->xml_ch;
808 if (--ctr <= 0)
809 break;
810 xlatp++;
813 return '&';
818 * emit_special_char
820 LOCAL void
821 emit_special_char(FILE * fp, int ch)
823 int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
824 xml_xlate_t const * xlatp = xml_xlate;
826 putc('&', fp);
827 for (;;) {
828 if (ch == xlatp->xml_ch) {
829 fputs(xlatp->xml_txt, fp);
830 return;
832 if (--ctr <= 0)
833 break;
834 xlatp++;
836 fprintf(fp, "#x%02X;", (ch & 0xFF));
840 * Local Variables:
841 * mode: C
842 * c-file-style: "stroustrup"
843 * indent-tabs-mode: nil
844 * End:
845 * end of autoopts/nested.c */