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
37 static xml_xlate_t
const xml_xlate
[] = {
45 /* = = = START-STATIC-FORWARD = = = */
46 /* static forward declarations maintained by mk-fwd */
48 removeLineContinue( char* pzSrc
);
51 scanQuotedString( char const* pzTxt
);
54 addStringValue( void** pp
, char const* pzName
, size_t nameLen
,
55 char const* pzValue
, size_t dataLen
);
58 addBoolValue( void** pp
, char const* pzName
, size_t nameLen
,
59 char const* pzValue
, size_t dataLen
);
62 addNumberValue( void** pp
, char const* pzName
, size_t nameLen
,
63 char const* pzValue
, size_t dataLen
);
66 addNestedValue( void** pp
, char const* pzName
, size_t nameLen
,
67 char* pzValue
, size_t dataLen
);
70 scanNameEntry(char const* pzName
, tOptionValue
* pRes
);
73 scanXmlEntry( char const* pzName
, tOptionValue
* pRes
);
76 unloadNestedArglist( tArgList
* pAL
);
79 sortNestedList( tArgList
* pAL
);
80 /* = = = END-STATIC-FORWARD = = = */
84 * Backslashes are used for line continuations. We keep the newline
85 * characters, but trim out the backslash:
88 removeLineContinue( char* pzSrc
)
93 while (*pzSrc
== '\n') pzSrc
++;
94 pzD
= strchr(pzSrc
, '\n');
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.
106 } while (pzD
== pzSrc
);
109 * Start shifting text.
112 char ch
= ((*pzD
++) = *(pzSrc
++));
117 --pzD
; /* rewrite on next iteration */
125 * Find the end of a quoted string, skipping escaped quote characters.
128 scanQuotedString( char const* pzTxt
)
130 char q
= *(pzTxt
++); /* remember the type of quote */
133 char ch
= *(pzTxt
++);
143 * IF the next character is NUL, drop the backslash, too.
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
)
163 * Associate a name with either a string or no value.
166 addStringValue( void** pp
, char const* pzName
, size_t nameLen
,
167 char const* pzValue
, size_t dataLen
)
170 size_t sz
= nameLen
+ dataLen
+ sizeof(*pNV
);
172 pNV
= AGALOC( sz
, "option name/str value pair" );
176 if (pzValue
== NULL
) {
177 pNV
->valType
= OPARG_TYPE_NONE
;
178 pNV
->pzName
= pNV
->v
.strVal
;
181 pNV
->valType
= OPARG_TYPE_STRING
;
183 char const * pzSrc
= pzValue
;
184 char * pzDst
= pNV
->v
.strVal
;
187 int ch
= *(pzSrc
++) & 0xFF;
188 if (ch
== NUL
) goto data_copy_done
;
190 ch
= get_special_char(&pzSrc
, &ct
);
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
);
212 * Associate a name with either a string or no value.
215 addBoolValue( void** pp
, char const* pzName
, size_t nameLen
,
216 char const* pzValue
, size_t dataLen
)
219 size_t sz
= nameLen
+ sizeof(*pNV
) + 1;
221 pNV
= AGALOC( sz
, "option name/bool value pair" );
224 while (IS_WHITESPACE_CHAR(*pzValue
) && (dataLen
> 0)) {
225 dataLen
--; pzValue
++;
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
);
246 * Associate a name with either a string or no value.
249 addNumberValue( void** pp
, char const* pzName
, size_t nameLen
,
250 char const* pzValue
, size_t dataLen
)
253 size_t sz
= nameLen
+ sizeof(*pNV
) + 1;
255 pNV
= AGALOC( sz
, "option name/bool value pair" );
258 while (IS_WHITESPACE_CHAR(*pzValue
) && (dataLen
> 0)) {
259 dataLen
--; pzValue
++;
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
);
277 * Associate a name with either a string or no value.
280 addNestedValue( void** pp
, char const* pzName
, size_t nameLen
,
281 char* pzValue
, size_t dataLen
)
286 size_t sz
= nameLen
+ sizeof(*pNV
) + 1;
287 pNV
= AGALOC( sz
, "empty nested value pair" );
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
;
297 pNV
= optionLoadNested( pzValue
, pzName
, nameLen
);
301 addArgListEntry( pp
, pNV
);
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.
313 scanNameEntry(char const* pzName
, tOptionValue
* pRes
)
316 char const * pzScan
= pzName
+1; /* we know first char is a name char */
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
++;
333 while (IS_HORIZ_WHITE_CHAR( (int)*++pzScan
)) ;
334 if ((*pzScan
== '=') || (*pzScan
== ':'))
344 addStringValue(&(pRes
->v
.nestVal
), pzName
, nameLen
, NULL
, (size_t)0);
350 pzScan
= scanQuotedString( pzScan
);
351 dataLen
= pzScan
- pzVal
;
352 pNV
= addStringValue( &(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
,
354 if ((pNV
!= NULL
) && (option_load_mode
== OPTION_LOAD_COOKED
))
355 ao_string_cook( pNV
->v
.strVal
, NULL
);
361 * We have found some strange text value. It ends with a newline
366 char ch
= *(pzScan
++);
370 dataLen
= pzScan
- pzVal
;
375 if ( (pzScan
> pzVal
+ 2)
376 && (pzScan
[-2] == '\\')
377 && (pzScan
[ 0] != NUL
))
382 dataLen
= (pzScan
- pzVal
) - 1;
384 pNV
= addStringValue( &(pRes
->v
.nestVal
), pzName
, nameLen
,
387 removeLineContinue( pNV
->v
.strVal
);
388 goto leave_scan_name
;
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.
405 scanXmlEntry( char const* pzName
, tOptionValue
* pRes
)
407 size_t nameLen
= 1, valLen
= 0;
408 char const* pzScan
= ++pzName
;
411 tOptionValue
* pNewVal
;
412 tOptionLoadMode save_mode
= option_load_mode
;
414 if (! IS_VAR_FIRST_CHAR(*pzName
)) {
421 pzName
= strstr( pzName
, "-->" );
427 pzName
= strchr( pzName
, '>' );
436 while (IS_VALUE_NAME_CHAR( (int)*pzScan
)) { pzScan
++; nameLen
++; }
439 valu
.valType
= OPARG_TYPE_STRING
;
444 pzScan
= parseAttributes(
445 NULL
, (char*)pzScan
, &option_load_mode
, &valu
);
446 if (*pzScan
== '>') {
451 if (*pzScan
!= '/') {
452 option_load_mode
= save_mode
;
458 if (*++pzScan
!= '>') {
459 option_load_mode
= save_mode
;
462 addStringValue(&(pRes
->v
.nestVal
), pzName
, nameLen
, NULL
, (size_t)0);
463 option_load_mode
= save_mode
;
467 option_load_mode
= save_mode
;
481 char const* pzS
= pzName
;
492 pzScan
= strstr( pzScan
, z
);
493 if (pzScan
== NULL
) {
494 option_load_mode
= save_mode
;
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);
507 case OPARG_TYPE_STRING
:
508 pNewVal
= addStringValue(
509 &(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
, valLen
);
511 if (option_load_mode
== OPTION_LOAD_KEEP
)
513 mungeString( pNewVal
->v
.strVal
, option_load_mode
);
516 case OPARG_TYPE_BOOLEAN
:
517 addBoolValue( &(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
, valLen
);
520 case OPARG_TYPE_NUMERIC
:
521 addNumberValue( &(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
, valLen
);
524 case OPARG_TYPE_HIERARCHY
:
526 char* pz
= AGALOC( valLen
+1, "hierarchical scan" );
529 memcpy( pz
, pzVal
, valLen
);
531 addNestedValue( &(pRes
->v
.nestVal
), pzName
, nameLen
, pz
, valLen
);
536 case OPARG_TYPE_ENUMERATION
:
537 case OPARG_TYPE_MEMBERSHIP
:
542 option_load_mode
= save_mode
;
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.
555 unloadNestedArglist( tArgList
* pAL
)
558 tCC
** ppNV
= pAL
->apzArgs
;
561 tOptionValue
* pNV
= (tOptionValue
*)(void*)*(ppNV
++);
562 if (pNV
->valType
== OPARG_TYPE_HIERARCHY
)
563 unloadNestedArglist( pNV
->v
.nestVal
);
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 +
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}).
582 optionUnloadNested( tOptionValue
const * pOV
)
584 if (pOV
== NULL
) return;
585 if (pOV
->valType
!= OPARG_TYPE_HIERARCHY
) {
590 unloadNestedArglist( pOV
->v
.nestVal
);
592 AGFREE( (void*)pOV
);
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.
603 sortNestedList( tArgList
* pAL
)
609 * This loop iterates "useCt" - 1 times.
611 for (ix
= 0; ++ix
< lm
;) {
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
621 while (strcmp( pOldNV
->pzName
, pNewNV
->pzName
) > 0) {
622 pAL
->apzArgs
[iy
+1] = (void*)pOldNV
;
623 pOldNV
= (tOptionValue
*)(void*)(pAL
->apzArgs
[--iy
]);
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
;
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
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.
654 optionLoadNested(char const* pzTxt
, char const* pzName
, size_t nameLen
)
660 * Make sure we have some data and we have space to put what we find.
666 while (IS_WHITESPACE_CHAR(*pzTxt
)) pzTxt
++;
671 pRes
= AGALOC( sizeof(*pRes
) + nameLen
+ 1, "nested args" );
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" );
686 pRes
->v
.nestVal
= pAL
;
688 pAL
->allocCt
= MIN_ARG_ALLOC_CT
;
691 * Scan until we hit a NUL.
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;
706 } while (pzTxt
!= NULL
); scan_done
:;
708 pAL
= pRes
->v
.nestVal
;
709 if (pAL
->useCt
!= 0) {
710 sortNestedList( pAL
);
715 AGFREE( pRes
->v
.nestVal
);
721 /*=export_func optionNestedVal
724 * what: parse a hierarchical option argument
725 * arg: + tOptions* + pOpts + program options descriptor +
726 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
729 * Nested value was found on the command line
732 optionNestedVal(tOptions
* pOpts
, tOptDesc
* pOD
)
734 if (pOpts
< OPTPROC_EMIT_LIMIT
)
737 if (pOD
->fOptState
& OPTST_RESET
) {
738 tArgList
* pAL
= pOD
->optCookie
;
748 void * p
= (void *)*(av
++);
749 optionUnloadNested((tOptionValue
const *)p
);
752 AGFREE(pOD
->optCookie
);
755 tOptionValue
* pOV
= optionLoadNested(
756 pOD
->optArg
.argString
, pOD
->pz_Name
, strlen(pOD
->pz_Name
));
759 addArgListEntry( &(pOD
->optCookie
), (void*)pOV
);
768 get_special_char(char const ** ppz
, int * ct
)
770 char const * pz
= *ppz
;
784 retch
= (int)strtoul(pz
, (char **)&pz
, base
);
797 int ctr
= sizeof(xml_xlate
) / sizeof(xml_xlate
[0]);
798 xml_xlate_t
const * xlatp
= xml_xlate
;
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
;
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
;
828 if (ch
== xlatp
->xml_ch
) {
829 fputs(xlatp
->xml_txt
, fp
);
836 fprintf(fp
, "#x%02X;", (ch
& 0xFF));
842 * c-file-style: "stroustrup"
843 * indent-tabs-mode: nil
845 * end of autoopts/nested.c */