Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / ntp / sntp / libopts / nested.c
blobcfe18c00b2f09ea4194a4c7002830a3936376362
1 /* $NetBSD$ */
4 /*
5 * Id: nested.c,v 4.14 2007/02/04 17:44:12 bkorb Exp
6 * Time-stamp: "2007-01-26 11:04:35 bkorb"
8 * Automated Options Nested Values module.
9 */
12 * Automated Options copyright 1992-2007 Bruce Korb
14 * Automated Options is free software.
15 * You may redistribute it and/or modify it under the terms of the
16 * GNU General Public License, as published by the Free Software
17 * Foundation; either version 2, or (at your option) any later version.
19 * Automated Options is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with Automated Options. See the file "COPYING". If not,
26 * write to: The Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor,
28 * Boston, MA 02110-1301, USA.
30 * As a special exception, Bruce Korb gives permission for additional
31 * uses of the text contained in his release of AutoOpts.
33 * The exception is that, if you link the AutoOpts library with other
34 * files to produce an executable, this does not by itself cause the
35 * resulting executable to be covered by the GNU General Public License.
36 * Your use of that executable is in no way restricted on account of
37 * linking the AutoOpts library code into it.
39 * This exception does not however invalidate any other reasons why
40 * the executable file might be covered by the GNU General Public License.
42 * This exception applies only to the code released by Bruce Korb under
43 * the name AutoOpts. If you copy code from other sources under the
44 * General Public License into a copy of AutoOpts, as the General Public
45 * License permits, the exception does not apply to the code that you add
46 * in this way. To avoid misleading anyone as to the status of such
47 * modified files, you must delete this exception notice from them.
49 * If you write modifications of your own for AutoOpts, it is your choice
50 * whether to permit this exception to apply to your modifications.
51 * If you do not wish that, delete this exception notice.
53 /* = = = START-STATIC-FORWARD = = = */
54 /* static forward declarations maintained by :mkfwd */
55 static void
56 removeBackslashes( char* pzSrc );
58 static char const*
59 scanQuotedString( char const* pzTxt );
61 static tOptionValue*
62 addStringValue( void** pp, char const* pzName, size_t nameLen,
63 char const* pzValue, size_t dataLen );
65 static tOptionValue*
66 addBoolValue( void** pp, char const* pzName, size_t nameLen,
67 char const* pzValue, size_t dataLen );
69 static tOptionValue*
70 addNumberValue( void** pp, char const* pzName, size_t nameLen,
71 char const* pzValue, size_t dataLen );
73 static tOptionValue*
74 addNestedValue( void** pp, char const* pzName, size_t nameLen,
75 char* pzValue, size_t dataLen );
77 static char const*
78 scanNameEntry(char const* pzName, tOptionValue* pRes);
80 static char const*
81 scanXmlEntry( char const* pzName, tOptionValue* pRes );
83 static void
84 unloadNestedArglist( tArgList* pAL );
86 static void
87 sortNestedList( tArgList* pAL );
88 /* = = = END-STATIC-FORWARD = = = */
90 /* removeBackslashes
92 * This function assumes that all newline characters were preceeded by
93 * backslashes that need removal.
95 static void
96 removeBackslashes( char* pzSrc )
98 char* pzD = strchr(pzSrc, '\n');
100 if (pzD == NULL)
101 return;
102 *--pzD = '\n';
104 for (;;) {
105 char ch = ((*pzD++) = *(pzSrc++));
106 switch (ch) {
107 case '\n': *--pzD = ch; break;
108 case NUL: return;
109 default:
116 /* scanQuotedString
118 * Find the end of a quoted string, skipping escaped quote characters.
120 static char const*
121 scanQuotedString( char const* pzTxt )
123 char q = *(pzTxt++); /* remember the type of quote */
125 for (;;) {
126 char ch = *(pzTxt++);
127 if (ch == NUL)
128 return pzTxt-1;
130 if (ch == q)
131 return pzTxt;
133 if (ch == '\\') {
134 ch = *(pzTxt++);
136 * IF the next character is NUL, drop the backslash, too.
138 if (ch == NUL)
139 return pzTxt - 2;
142 * IF the quote character or the escape character were escaped,
143 * then skip both, as long as the string does not end.
145 if ((ch == q) || (ch == '\\')) {
146 if (*(pzTxt++) == NUL)
147 return pzTxt-1;
154 /* addStringValue
156 * Associate a name with either a string or no value.
158 static tOptionValue*
159 addStringValue( void** pp, char const* pzName, size_t nameLen,
160 char const* pzValue, size_t dataLen )
162 tOptionValue* pNV;
163 size_t sz = nameLen + dataLen + sizeof(*pNV);
165 pNV = AGALOC( sz, "option name/str value pair" );
166 if (pNV == NULL)
167 return NULL;
169 if (pzValue == NULL) {
170 pNV->valType = OPARG_TYPE_NONE;
171 pNV->pzName = pNV->v.strVal;
173 } else {
174 pNV->valType = OPARG_TYPE_STRING;
175 if (dataLen > 0)
176 memcpy( pNV->v.strVal, pzValue, dataLen );
177 pNV->v.strVal[dataLen] = NUL;
178 pNV->pzName = pNV->v.strVal + dataLen + 1;
181 memcpy( pNV->pzName, pzName, nameLen );
182 pNV->pzName[ nameLen ] = NUL;
183 addArgListEntry( pp, pNV );
184 return pNV;
188 /* addBoolValue
190 * Associate a name with either a string or no value.
192 static tOptionValue*
193 addBoolValue( void** pp, char const* pzName, size_t nameLen,
194 char const* pzValue, size_t dataLen )
196 tOptionValue* pNV;
197 size_t sz = nameLen + sizeof(*pNV) + 1;
199 pNV = AGALOC( sz, "option name/bool value pair" );
200 if (pNV == NULL)
201 return NULL;
202 while (isspace( (int)*pzValue ) && (dataLen > 0)) {
203 dataLen--; pzValue++;
205 if (dataLen == 0)
206 pNV->v.boolVal = 0;
207 else if (isdigit( (int)*pzValue ))
208 pNV->v.boolVal = atoi( pzValue );
209 else switch (*pzValue) {
210 case 'f':
211 case 'F':
212 case 'n':
213 case 'N':
214 pNV->v.boolVal = 0; break;
215 default:
216 pNV->v.boolVal = 1;
219 pNV->valType = OPARG_TYPE_BOOLEAN;
220 pNV->pzName = (char*)(pNV + 1);
221 memcpy( pNV->pzName, pzName, nameLen );
222 pNV->pzName[ nameLen ] = NUL;
223 addArgListEntry( pp, pNV );
224 return pNV;
228 /* addNumberValue
230 * Associate a name with either a string or no value.
232 static tOptionValue*
233 addNumberValue( void** pp, char const* pzName, size_t nameLen,
234 char const* pzValue, size_t dataLen )
236 tOptionValue* pNV;
237 size_t sz = nameLen + sizeof(*pNV) + 1;
239 pNV = AGALOC( sz, "option name/bool value pair" );
240 if (pNV == NULL)
241 return NULL;
242 while (isspace( (int)*pzValue ) && (dataLen > 0)) {
243 dataLen--; pzValue++;
245 if (dataLen == 0)
246 pNV->v.boolVal = 0;
247 else
248 pNV->v.boolVal = atoi( pzValue );
250 pNV->valType = OPARG_TYPE_NUMERIC;
251 pNV->pzName = (char*)(pNV + 1);
252 memcpy( pNV->pzName, pzName, nameLen );
253 pNV->pzName[ nameLen ] = NUL;
254 addArgListEntry( pp, pNV );
255 return pNV;
259 /* addNestedValue
261 * Associate a name with either a string or no value.
263 static tOptionValue*
264 addNestedValue( void** pp, char const* pzName, size_t nameLen,
265 char* pzValue, size_t dataLen )
267 tOptionValue* pNV;
269 if (dataLen == 0) {
270 size_t sz = nameLen + sizeof(*pNV) + 1;
271 pNV = AGALOC( sz, "empty nested value pair" );
272 if (pNV == NULL)
273 return NULL;
274 pNV->v.nestVal = NULL;
275 pNV->valType = OPARG_TYPE_HIERARCHY;
276 pNV->pzName = (char*)(pNV + 1);
277 memcpy( pNV->pzName, pzName, nameLen );
278 pNV->pzName[ nameLen ] = NUL;
280 } else {
281 pNV = optionLoadNested( pzValue, pzName, nameLen );
284 if (pNV != NULL)
285 addArgListEntry( pp, pNV );
287 return pNV;
291 /* scanNameEntry
293 * We have an entry that starts with a name. Find the end of it, cook it
294 * (if called for) and create the name/value association.
296 static char const*
297 scanNameEntry(char const* pzName, tOptionValue* pRes)
299 tOptionValue* pNV;
300 char const * pzScan = pzName+1;
301 char const * pzVal;
302 size_t nameLen = 1;
303 size_t dataLen = 0;
305 while (ISNAMECHAR( (int)*pzScan )) { pzScan++; nameLen++; }
307 while (isspace( (int)*pzScan )) {
308 char ch = *(pzScan++);
309 if ((ch == '\n') || (ch == ',')) {
310 addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL,(size_t)0);
311 return pzScan - 1;
315 switch (*pzScan) {
316 case '=':
317 case ':':
318 while (isspace( (int)*++pzScan )) ;
319 switch (*pzScan) {
320 case ',': goto comma_char;
321 case '"':
322 case '\'': goto quote_char;
323 case NUL: goto nul_byte;
324 default: goto default_char;
327 case ',':
328 comma_char:
329 pzScan++;
330 /* FALLTHROUGH */
332 case NUL:
333 nul_byte:
334 addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
335 break;
337 case '"':
338 case '\'':
339 quote_char:
340 pzVal = pzScan;
341 pzScan = scanQuotedString( pzScan );
342 dataLen = pzScan - pzVal;
343 pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal,
344 dataLen );
345 if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
346 ao_string_cook( pNV->v.strVal, NULL );
347 break;
349 default:
350 default_char:
352 * We have found some strange text value. It ends with a newline
353 * or a comma.
355 pzVal = pzScan;
356 for (;;) {
357 char ch = *(pzScan++);
358 switch (ch) {
359 case NUL:
360 pzScan--;
361 dataLen = pzScan - pzVal;
362 goto string_done;
363 /* FALLTHROUGH */
365 case '\n':
366 if ( (pzScan > pzVal + 2)
367 && (pzScan[-2] == '\\')
368 && (pzScan[ 0] != NUL))
369 continue;
370 /* FALLTHROUGH */
372 case ',':
373 dataLen = (pzScan - pzVal) - 1;
374 string_done:
375 pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen,
376 pzVal, dataLen );
377 if (pNV != NULL)
378 removeBackslashes( pNV->v.strVal );
379 goto leave_scan_name;
382 break;
383 } leave_scan_name:;
385 return pzScan;
389 /* scanXmlEntry
391 * We've found a '<' character. We ignore this if it is a comment or a
392 * directive. If it is something else, then whatever it is we are looking
393 * at is bogus. Returning NULL stops processing.
395 static char const*
396 scanXmlEntry( char const* pzName, tOptionValue* pRes )
398 size_t nameLen = 1, valLen = 0;
399 char const* pzScan = ++pzName;
400 char const* pzVal;
401 tOptionValue valu;
402 tOptionValue* pNewVal;
403 tOptionLoadMode save_mode = option_load_mode;
405 if (! isalpha((int)*pzName)) {
406 switch (*pzName) {
407 default:
408 pzName = NULL;
409 break;
411 case '!':
412 pzName = strstr( pzName, "-->" );
413 if (pzName != NULL)
414 pzName += 3;
415 break;
417 case '?':
418 pzName = strchr( pzName, '>' );
419 if (pzName != NULL)
420 pzName++;
421 break;
423 return pzName;
426 while (isalpha( (int)*++pzScan )) nameLen++;
427 if (nameLen > 64)
428 return NULL;
429 valu.valType = OPARG_TYPE_STRING;
431 switch (*pzScan) {
432 case ' ':
433 case '\t':
434 pzScan = parseAttributes(
435 NULL, (char*)pzScan, &option_load_mode, &valu );
436 if (*pzScan == '>') {
437 pzScan++;
438 break;
441 if (*pzScan != '/') {
442 option_load_mode = save_mode;
443 return NULL;
445 /* FALLTHROUGH */
447 case '/':
448 if (*++pzScan != '>') {
449 option_load_mode = save_mode;
450 return NULL;
452 addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
453 option_load_mode = save_mode;
454 return pzScan+2;
456 default:
457 option_load_mode = save_mode;
458 return NULL;
460 case '>':
461 pzScan++;
462 break;
465 pzVal = pzScan;
468 char z[68];
469 char* pzD = z;
470 int ct = nameLen;
471 char const* pzS = pzName;
473 *(pzD++) = '<';
474 *(pzD++) = '/';
476 do {
477 *(pzD++) = *(pzS++);
478 } while (--ct > 0);
479 *(pzD++) = '>';
480 *pzD = NUL;
482 pzScan = strstr( pzScan, z );
483 if (pzScan == NULL) {
484 option_load_mode = save_mode;
485 return NULL;
487 valLen = (pzScan - pzVal);
488 pzScan += nameLen + 3;
489 while (isspace( (int)*pzScan )) pzScan++;
492 switch (valu.valType) {
493 case OPARG_TYPE_NONE:
494 addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
495 break;
497 case OPARG_TYPE_STRING:
498 pNewVal = addStringValue(
499 &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
501 if (option_load_mode == OPTION_LOAD_KEEP)
502 break;
503 mungeString( pNewVal->v.strVal, option_load_mode );
504 break;
506 case OPARG_TYPE_BOOLEAN:
507 addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
508 break;
510 case OPARG_TYPE_NUMERIC:
511 addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
512 break;
514 case OPARG_TYPE_HIERARCHY:
516 char* pz = AGALOC( valLen+1, "hierarchical scan" );
517 if (pz == NULL)
518 break;
519 memcpy( pz, pzVal, valLen );
520 pz[valLen] = NUL;
521 addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen );
522 AGFREE(pz);
523 break;
526 case OPARG_TYPE_ENUMERATION:
527 case OPARG_TYPE_MEMBERSHIP:
528 default:
529 break;
532 option_load_mode = save_mode;
533 return pzScan;
537 /* unloadNestedArglist
539 * Deallocate a list of option arguments. This must have been gotten from
540 * a hierarchical option argument, not a stacked list of strings. It is
541 * an internal call, so it is not validated. The caller is responsible for
542 * knowing what they are doing.
544 static void
545 unloadNestedArglist( tArgList* pAL )
547 int ct = pAL->useCt;
548 tCC** ppNV = pAL->apzArgs;
550 while (ct-- > 0) {
551 tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
552 if (pNV->valType == OPARG_TYPE_HIERARCHY)
553 unloadNestedArglist( pNV->v.nestVal );
554 AGFREE( pNV );
557 AGFREE( (void*)pAL );
561 /*=export_func optionUnloadNested
563 * what: Deallocate the memory for a nested value
564 * arg: + tOptionValue const * + pOptVal + the hierarchical value +
566 * doc:
567 * A nested value needs to be deallocated. The pointer passed in should
568 * have been gotten from a call to @code{configFileLoad()} (See
569 * @pxref{libopts-configFileLoad}).
571 void
572 optionUnloadNested( tOptionValue const * pOV )
574 if (pOV == NULL) return;
575 if (pOV->valType != OPARG_TYPE_HIERARCHY) {
576 errno = EINVAL;
577 return;
580 unloadNestedArglist( pOV->v.nestVal );
582 AGFREE( (void*)pOV );
586 /* sortNestedList
588 * This is a _stable_ sort. The entries are sorted alphabetically,
589 * but within entries of the same name the ordering is unchanged.
590 * Typically, we also hope the input is sorted.
592 static void
593 sortNestedList( tArgList* pAL )
595 int ix;
596 int lm = pAL->useCt;
599 * This loop iterates "useCt" - 1 times.
601 for (ix = 0; ++ix < lm;) {
602 int iy = ix-1;
603 tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
604 tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
607 * For as long as the new entry precedes the "old" entry,
608 * move the old pointer. Stop before trying to extract the
609 * "-1" entry.
611 while (strcmp( pOldNV->pzName, pNewNV->pzName ) > 0) {
612 pAL->apzArgs[iy+1] = (void*)pOldNV;
613 pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
614 if (iy < 0)
615 break;
619 * Always store the pointer. Sometimes it is redundant,
620 * but the redundancy is cheaper than a test and branch sequence.
622 pAL->apzArgs[iy+1] = (void*)pNewNV;
627 /* optionLoadNested
628 * private:
630 * what: parse a hierarchical option argument
631 * arg: + char const* + pzTxt + the text to scan +
632 * arg: + char const* + pzName + the name for the text +
633 * arg: + size_t + nameLen + the length of "name" +
635 * ret_type: tOptionValue*
636 * ret_desc: An allocated, compound value structure
638 * doc:
639 * A block of text represents a series of values. It may be an
640 * entire configuration file, or it may be an argument to an
641 * option that takes a hierarchical value.
643 LOCAL tOptionValue*
644 optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
646 tOptionValue* pRes;
647 tArgList* pAL;
650 * Make sure we have some data and we have space to put what we find.
652 if (pzTxt == NULL) {
653 errno = EINVAL;
654 return NULL;
656 while (isspace( (int)*pzTxt )) pzTxt++;
657 if (*pzTxt == NUL) {
658 errno = ENOENT;
659 return NULL;
661 pRes = AGALOC( sizeof(*pRes) + nameLen + 1, "nested args" );
662 if (pRes == NULL) {
663 errno = ENOMEM;
664 return NULL;
666 pRes->valType = OPARG_TYPE_HIERARCHY;
667 pRes->pzName = (char*)(pRes + 1);
668 memcpy( pRes->pzName, pzName, nameLen );
669 pRes->pzName[ nameLen ] = NUL;
671 pAL = AGALOC( sizeof(*pAL), "nested arg list" );
672 if (pAL == NULL) {
673 AGFREE( pRes );
674 return NULL;
676 pRes->v.nestVal = pAL;
677 pAL->useCt = 0;
678 pAL->allocCt = MIN_ARG_ALLOC_CT;
681 * Scan until we hit a NUL.
683 do {
684 while (isspace( (int)*pzTxt )) pzTxt++;
685 if (isalpha( (int)*pzTxt )) {
686 pzTxt = scanNameEntry( pzTxt, pRes );
688 else switch (*pzTxt) {
689 case NUL: goto scan_done;
690 case '<': pzTxt = scanXmlEntry( pzTxt, pRes );
691 if (*pzTxt == ',') pzTxt++; break;
692 case '#': pzTxt = strchr( pzTxt, '\n' ); break;
693 default: goto woops;
695 } while (pzTxt != NULL); scan_done:;
697 pAL = pRes->v.nestVal;
698 if (pAL->useCt != 0) {
699 sortNestedList( pAL );
700 return pRes;
703 woops:
704 AGFREE( pRes->v.nestVal );
705 AGFREE( pRes );
706 return NULL;
710 /*=export_func optionNestedVal
711 * private:
713 * what: parse a hierarchical option argument
714 * arg: + tOptions* + pOpts + program options descriptor +
715 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
717 * doc:
718 * Nested value was found on the command line
720 void
721 optionNestedVal( tOptions* pOpts, tOptDesc* pOD )
723 tOptionValue* pOV = optionLoadNested(
724 pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
726 if (pOV != NULL)
727 addArgListEntry( &(pOD->optCookie), (void*)pOV );
730 * Local Variables:
731 * mode: C
732 * c-file-style: "stroustrup"
733 * indent-tabs-mode: nil
734 * End:
735 * end of autoopts/nested.c */