5 * Time-stamp: "2012-03-04 13:30:07 bkorb"
7 * Automated Options Nested Values module.
9 * This file is part of AutoOpts, a companion to AutoGen.
10 * AutoOpts is free software.
11 * AutoOpts is Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
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.
17 * The GNU Lesser General Public License, version 3 or later
18 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
20 * The Modified Berkeley Software Distribution License
21 * See the file "COPYING.mbsd"
23 * These files have the following md5sums:
25 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
26 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
27 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
36 static xml_xlate_t
const xml_xlate
[] = {
48 /* = = = START-STATIC-FORWARD = = = */
50 remove_continuation(char* pzSrc
);
53 scan_q_str(char const* pzTxt
);
56 add_string(void ** pp
, char const * pzName
, size_t nameLen
,
57 char const* pzValue
, size_t dataLen
);
60 add_bool(void ** pp
, char const * pzName
, size_t nameLen
,
61 char const* pzValue
, size_t dataLen
);
64 add_number(void** pp
, char const* pzName
, size_t nameLen
,
65 char const* pzValue
, size_t dataLen
);
68 add_nested(void** pp
, char const* pzName
, size_t nameLen
,
69 char* pzValue
, size_t dataLen
);
72 scan_name(char const* pzName
, tOptionValue
* pRes
);
75 scan_xml(char const* pzName
, tOptionValue
* pRes
);
78 sort_list(tArgList
* pAL
);
79 /* = = = END-STATIC-FORWARD = = = */
82 * Backslashes are used for line continuations. We keep the newline
83 * characters, but trim out the backslash:
86 remove_continuation(char* pzSrc
)
91 while (*pzSrc
== NL
) pzSrc
++;
92 pzD
= strchr(pzSrc
, NL
);
97 * pzD has skipped at least one non-newline character and now
98 * points to a newline character. It now becomes the source and
99 * pzD goes to the previous character.
104 } while (pzD
== pzSrc
);
107 * Start shifting text.
110 char ch
= ((*pzD
++) = *(pzSrc
++));
115 --pzD
; /* rewrite on next iteration */
121 * Find the end of a quoted string, skipping escaped quote characters.
124 scan_q_str(char const* pzTxt
)
126 char q
= *(pzTxt
++); /* remember the type of quote */
129 char ch
= *(pzTxt
++);
139 * IF the next character is NUL, drop the backslash, too.
145 * IF the quote character or the escape character were escaped,
146 * then skip both, as long as the string does not end.
148 if ((ch
== q
) || (ch
== '\\')) {
149 if (*(pzTxt
++) == NUL
)
158 * Associate a name with either a string or no value.
160 static tOptionValue
*
161 add_string(void ** pp
, char const * pzName
, size_t nameLen
,
162 char const* pzValue
, size_t dataLen
)
165 size_t sz
= nameLen
+ dataLen
+ sizeof(*pNV
);
167 pNV
= AGALOC(sz
, "option name/str value pair");
171 if (pzValue
== NULL
) {
172 pNV
->valType
= OPARG_TYPE_NONE
;
173 pNV
->pzName
= pNV
->v
.strVal
;
176 pNV
->valType
= OPARG_TYPE_STRING
;
178 char const * pzSrc
= pzValue
;
179 char * pzDst
= pNV
->v
.strVal
;
182 int ch
= *(pzSrc
++) & 0xFF;
183 if (ch
== NUL
) goto data_copy_done
;
185 ch
= get_special_char(&pzSrc
, &ct
);
186 *(pzDst
++) = (char)ch
;
192 pNV
->v
.strVal
[0] = NUL
;
195 pNV
->pzName
= pNV
->v
.strVal
+ dataLen
+ 1;
198 memcpy(pNV
->pzName
, pzName
, nameLen
);
199 pNV
->pzName
[ nameLen
] = NUL
;
200 addArgListEntry(pp
, pNV
);
205 * Associate a name with either a string or no value.
207 static tOptionValue
*
208 add_bool(void ** pp
, char const * pzName
, size_t nameLen
,
209 char const* pzValue
, size_t dataLen
)
214 size_t sz
= nameLen
+ sizeof(tOptionValue
) + 1;
215 pNV
= AGALOC(sz
, "name/bool value");
219 char * p
= SPN_WHITESPACE_CHARS(pzValue
);
220 dataLen
-= p
- pzValue
;
227 else if (IS_DEC_DIGIT_CHAR(*pzValue
))
228 pNV
->v
.boolVal
= atoi(pzValue
);
230 else pNV
->v
.boolVal
= ! IS_FALSE_TYPE_CHAR(*pzValue
);
232 pNV
->valType
= OPARG_TYPE_BOOLEAN
;
233 pNV
->pzName
= (char*)(pNV
+ 1);
234 memcpy(pNV
->pzName
, pzName
, nameLen
);
235 pNV
->pzName
[ nameLen
] = NUL
;
236 addArgListEntry(pp
, pNV
);
241 * Associate a name with either a string or no value.
244 add_number(void** pp
, char const* pzName
, size_t nameLen
,
245 char const* pzValue
, size_t dataLen
)
248 size_t sz
= nameLen
+ sizeof(*pNV
) + 1;
250 pNV
= AGALOC(sz
, "option name/bool value pair");
253 while (IS_WHITESPACE_CHAR(*pzValue
) && (dataLen
> 0)) {
254 dataLen
--; pzValue
++;
259 pNV
->v
.longVal
= strtol(pzValue
, 0, 0);
261 pNV
->valType
= OPARG_TYPE_NUMERIC
;
262 pNV
->pzName
= (char*)(pNV
+ 1);
263 memcpy(pNV
->pzName
, pzName
, nameLen
);
264 pNV
->pzName
[ nameLen
] = NUL
;
265 addArgListEntry(pp
, pNV
);
270 * Associate a name with either a string or no value.
273 add_nested(void** pp
, char const* pzName
, size_t nameLen
,
274 char* pzValue
, size_t dataLen
)
279 size_t sz
= nameLen
+ sizeof(*pNV
) + 1;
280 pNV
= AGALOC(sz
, "empty nested value pair");
283 pNV
->v
.nestVal
= NULL
;
284 pNV
->valType
= OPARG_TYPE_HIERARCHY
;
285 pNV
->pzName
= (char*)(pNV
+ 1);
286 memcpy(pNV
->pzName
, pzName
, nameLen
);
287 pNV
->pzName
[ nameLen
] = NUL
;
290 pNV
= optionLoadNested(pzValue
, pzName
, nameLen
);
294 addArgListEntry(pp
, pNV
);
300 * We have an entry that starts with a name. Find the end of it, cook it
301 * (if called for) and create the name/value association.
304 scan_name(char const* pzName
, tOptionValue
* pRes
)
307 char const * pzScan
= pzName
+1; /* we know first char is a name char */
313 * Scan over characters that name a value. These names may not end
314 * with a colon, but they may contain colons.
316 pzScan
= SPN_VALUE_NAME_CHARS(pzName
+ 1);
317 if (pzScan
[-1] == ':')
319 nameLen
= pzScan
- pzName
;
321 pzScan
= SPN_HORIZ_WHITE_CHARS(pzScan
);
328 pzScan
= SPN_HORIZ_WHITE_CHARS(pzScan
+ 1);
329 if ((*pzScan
== '=') || (*pzScan
== ':'))
339 add_string(&(pRes
->v
.nestVal
), pzName
, nameLen
, NULL
, (size_t)0);
345 pzScan
= scan_q_str(pzScan
);
346 dataLen
= pzScan
- pzVal
;
347 pNV
= add_string(&(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
,
349 if ((pNV
!= NULL
) && (option_load_mode
== OPTION_LOAD_COOKED
))
350 ao_string_cook(pNV
->v
.strVal
, NULL
);
356 * We have found some strange text value. It ends with a newline
361 char ch
= *(pzScan
++);
365 dataLen
= pzScan
- pzVal
;
370 if ( (pzScan
> pzVal
+ 2)
371 && (pzScan
[-2] == '\\')
372 && (pzScan
[ 0] != NUL
))
377 dataLen
= (pzScan
- pzVal
) - 1;
379 pNV
= add_string(&(pRes
->v
.nestVal
), pzName
, nameLen
,
382 remove_continuation(pNV
->v
.strVal
);
383 goto leave_scan_name
;
393 * We've found a '<' character. We ignore this if it is a comment or a
394 * directive. If it is something else, then whatever it is we are looking
395 * at is bogus. Returning NULL stops processing.
398 scan_xml(char const* pzName
, tOptionValue
* pRes
)
402 char const* pzScan
= ++pzName
;
405 tOptionValue
* pNewVal
;
406 tOptionLoadMode save_mode
= option_load_mode
;
408 if (! IS_VAR_FIRST_CHAR(*pzName
)) {
415 pzName
= strstr(pzName
, "-->");
421 pzName
= strchr(pzName
, '>');
429 pzScan
= SPN_VALUE_NAME_CHARS(pzName
+1);
430 nameLen
= pzScan
- pzName
;
433 valu
.valType
= OPARG_TYPE_STRING
;
438 pzScan
= parse_attrs(
439 NULL
, (char*)pzScan
, &option_load_mode
, &valu
);
440 if (*pzScan
== '>') {
445 if (*pzScan
!= '/') {
446 option_load_mode
= save_mode
;
452 if (*++pzScan
!= '>') {
453 option_load_mode
= save_mode
;
456 add_string(&(pRes
->v
.nestVal
), pzName
, nameLen
, NULL
, (size_t)0);
457 option_load_mode
= save_mode
;
461 option_load_mode
= save_mode
;
475 char const* pzS
= pzName
;
486 pzScan
= strstr(pzScan
, z
);
487 if (pzScan
== NULL
) {
488 option_load_mode
= save_mode
;
491 valLen
= (pzScan
- pzVal
);
492 pzScan
+= nameLen
+ 3;
493 pzScan
= SPN_WHITESPACE_CHARS(pzScan
);
496 switch (valu
.valType
) {
497 case OPARG_TYPE_NONE
:
498 add_string(&(pRes
->v
.nestVal
), pzName
, nameLen
, NULL
, (size_t)0);
501 case OPARG_TYPE_STRING
:
502 pNewVal
= add_string(
503 &(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
, valLen
);
505 if (option_load_mode
== OPTION_LOAD_KEEP
)
507 mungeString(pNewVal
->v
.strVal
, option_load_mode
);
510 case OPARG_TYPE_BOOLEAN
:
511 add_bool(&(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
, valLen
);
514 case OPARG_TYPE_NUMERIC
:
515 add_number(&(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
, valLen
);
518 case OPARG_TYPE_HIERARCHY
:
520 char* pz
= AGALOC(valLen
+1, "hierarchical scan");
523 memcpy(pz
, pzVal
, valLen
);
525 add_nested(&(pRes
->v
.nestVal
), pzName
, nameLen
, pz
, valLen
);
530 case OPARG_TYPE_ENUMERATION
:
531 case OPARG_TYPE_MEMBERSHIP
:
536 option_load_mode
= save_mode
;
542 * Deallocate a list of option arguments. This must have been gotten from
543 * a hierarchical option argument, not a stacked list of strings. It is
544 * an internal call, so it is not validated. The caller is responsible for
545 * knowing what they are doing.
548 unload_arg_list(tArgList
* pAL
)
551 tCC
** ppNV
= pAL
->apzArgs
;
554 tOptionValue
* pNV
= (tOptionValue
*)(void*)*(ppNV
++);
555 if (pNV
->valType
== OPARG_TYPE_HIERARCHY
)
556 unload_arg_list(pNV
->v
.nestVal
);
563 /*=export_func optionUnloadNested
565 * what: Deallocate the memory for a nested value
566 * arg: + tOptionValue const * + pOptVal + the hierarchical value +
569 * A nested value needs to be deallocated. The pointer passed in should
570 * have been gotten from a call to @code{configFileLoad()} (See
571 * @pxref{libopts-configFileLoad}).
574 optionUnloadNested(tOptionValue
const * pOV
)
576 if (pOV
== NULL
) return;
577 if (pOV
->valType
!= OPARG_TYPE_HIERARCHY
) {
582 unload_arg_list(pOV
->v
.nestVal
);
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.
593 sort_list(tArgList
* pAL
)
599 * This loop iterates "useCt" - 1 times.
601 for (ix
= 0; ++ix
< lm
;) {
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
611 while (strcmp(pOldNV
->pzName
, pNewNV
->pzName
) > 0) {
612 pAL
->apzArgs
[iy
+1] = (void*)pOldNV
;
613 pOldNV
= (tOptionValue
*)(void*)(pAL
->apzArgs
[--iy
]);
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
;
629 * what: parse a hierarchical option argument
630 * arg: + char const* + pzTxt + the text to scan +
631 * arg: + char const* + pzName + the name for the text +
632 * arg: + size_t + nameLen + the length of "name" +
634 * ret_type: tOptionValue*
635 * ret_desc: An allocated, compound value structure
638 * A block of text represents a series of values. It may be an
639 * entire configuration file, or it may be an argument to an
640 * option that takes a hierarchical value.
642 * If NULL is returned, errno will be set:
645 * @code{EINVAL} the input text was NULL.
647 * @code{ENOMEM} the storage structures could not be allocated
649 * @code{ENOMSG} no configuration values were found
653 optionLoadNested(char const* pzTxt
, char const* pzName
, size_t nameLen
)
658 * Make sure we have some data and we have space to put what we find.
664 pzTxt
= SPN_WHITESPACE_CHARS(pzTxt
);
669 pRes
= AGALOC(sizeof(*pRes
) + nameLen
+ 1, "nested args");
674 pRes
->valType
= OPARG_TYPE_HIERARCHY
;
675 pRes
->pzName
= (char*)(pRes
+ 1);
676 memcpy(pRes
->pzName
, pzName
, nameLen
);
677 pRes
->pzName
[nameLen
] = NUL
;
680 tArgList
* pAL
= AGALOC(sizeof(*pAL
), "nested arg list");
686 pRes
->v
.nestVal
= pAL
;
688 pAL
->allocCt
= MIN_ARG_ALLOC_CT
;
692 * Scan until we hit a NUL.
695 pzTxt
= SPN_WHITESPACE_CHARS(pzTxt
);
696 if (IS_VAR_FIRST_CHAR(*pzTxt
))
697 pzTxt
= scan_name(pzTxt
, pRes
);
699 else switch (*pzTxt
) {
700 case NUL
: goto scan_done
;
701 case '<': pzTxt
= scan_xml(pzTxt
, pRes
);
702 if (pzTxt
== NULL
) goto woops
;
703 if (*pzTxt
== ',') pzTxt
++; break;
704 case '#': pzTxt
= strchr(pzTxt
, NL
); break;
707 } while (pzTxt
!= NULL
); scan_done
:;
710 tArgList
* al
= pRes
->v
.nestVal
;
711 if (al
->useCt
== 0) {
722 AGFREE(pRes
->v
.nestVal
);
727 /*=export_func optionNestedVal
730 * what: parse a hierarchical option argument
731 * arg: + tOptions* + pOpts + program options descriptor +
732 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
735 * Nested value was found on the command line
738 optionNestedVal(tOptions
* pOpts
, tOptDesc
* pOD
)
740 if (pOpts
< OPTPROC_EMIT_LIMIT
)
743 if (pOD
->fOptState
& OPTST_RESET
) {
744 tArgList
* pAL
= pOD
->optCookie
;
754 void * p
= (void *)*(av
++);
755 optionUnloadNested((tOptionValue
const *)p
);
758 AGFREE(pOD
->optCookie
);
761 tOptionValue
* pOV
= optionLoadNested(
762 pOD
->optArg
.argString
, pOD
->pz_Name
, strlen(pOD
->pz_Name
));
765 addArgListEntry(&(pOD
->optCookie
), (void*)pOV
);
773 get_special_char(char const ** ppz
, int * ct
)
775 char const * pz
= *ppz
;
789 retch
= (int)strtoul(pz
, (char **)&pz
, base
);
802 int ctr
= sizeof(xml_xlate
) / sizeof(xml_xlate
[0]);
803 xml_xlate_t
const * xlatp
= xml_xlate
;
806 if ( (*ct
>= xlatp
->xml_len
)
807 && (strncmp(pz
, xlatp
->xml_txt
, xlatp
->xml_len
) == 0)) {
808 *ppz
+= xlatp
->xml_len
;
809 *ct
-= xlatp
->xml_len
;
810 return xlatp
->xml_ch
;
825 emit_special_char(FILE * fp
, int ch
)
827 int ctr
= sizeof(xml_xlate
) / sizeof(xml_xlate
[0]);
828 xml_xlate_t
const * xlatp
= xml_xlate
;
832 if (ch
== xlatp
->xml_ch
) {
833 fputs(xlatp
->xml_txt
, fp
);
840 fprintf(fp
, XML_HEX_BYTE_FMT
, (ch
& 0xFF));
846 * c-file-style: "stroustrup"
847 * indent-tabs-mode: nil
849 * end of autoopts/nested.c */