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.
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 */
56 removeBackslashes( char* pzSrc
);
59 scanQuotedString( char const* pzTxt
);
62 addStringValue( void** pp
, char const* pzName
, size_t nameLen
,
63 char const* pzValue
, size_t dataLen
);
66 addBoolValue( void** pp
, char const* pzName
, size_t nameLen
,
67 char const* pzValue
, size_t dataLen
);
70 addNumberValue( void** pp
, char const* pzName
, size_t nameLen
,
71 char const* pzValue
, size_t dataLen
);
74 addNestedValue( void** pp
, char const* pzName
, size_t nameLen
,
75 char* pzValue
, size_t dataLen
);
78 scanNameEntry(char const* pzName
, tOptionValue
* pRes
);
81 scanXmlEntry( char const* pzName
, tOptionValue
* pRes
);
84 unloadNestedArglist( tArgList
* pAL
);
87 sortNestedList( tArgList
* pAL
);
88 /* = = = END-STATIC-FORWARD = = = */
92 * This function assumes that all newline characters were preceeded by
93 * backslashes that need removal.
96 removeBackslashes( char* pzSrc
)
98 char* pzD
= strchr(pzSrc
, '\n');
105 char ch
= ((*pzD
++) = *(pzSrc
++));
107 case '\n': *--pzD
= ch
; break;
118 * Find the end of a quoted string, skipping escaped quote characters.
121 scanQuotedString( char const* pzTxt
)
123 char q
= *(pzTxt
++); /* remember the type of quote */
126 char ch
= *(pzTxt
++);
136 * IF the next character is NUL, drop the backslash, too.
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
)
156 * Associate a name with either a string or no value.
159 addStringValue( void** pp
, char const* pzName
, size_t nameLen
,
160 char const* pzValue
, size_t dataLen
)
163 size_t sz
= nameLen
+ dataLen
+ sizeof(*pNV
);
165 pNV
= AGALOC( sz
, "option name/str value pair" );
169 if (pzValue
== NULL
) {
170 pNV
->valType
= OPARG_TYPE_NONE
;
171 pNV
->pzName
= pNV
->v
.strVal
;
174 pNV
->valType
= OPARG_TYPE_STRING
;
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
);
190 * Associate a name with either a string or no value.
193 addBoolValue( void** pp
, char const* pzName
, size_t nameLen
,
194 char const* pzValue
, size_t dataLen
)
197 size_t sz
= nameLen
+ sizeof(*pNV
) + 1;
199 pNV
= AGALOC( sz
, "option name/bool value pair" );
202 while (isspace( (int)*pzValue
) && (dataLen
> 0)) {
203 dataLen
--; pzValue
++;
207 else if (isdigit( (int)*pzValue
))
208 pNV
->v
.boolVal
= atoi( pzValue
);
209 else switch (*pzValue
) {
214 pNV
->v
.boolVal
= 0; break;
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
);
230 * Associate a name with either a string or no value.
233 addNumberValue( void** pp
, char const* pzName
, size_t nameLen
,
234 char const* pzValue
, size_t dataLen
)
237 size_t sz
= nameLen
+ sizeof(*pNV
) + 1;
239 pNV
= AGALOC( sz
, "option name/bool value pair" );
242 while (isspace( (int)*pzValue
) && (dataLen
> 0)) {
243 dataLen
--; pzValue
++;
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
);
261 * Associate a name with either a string or no value.
264 addNestedValue( void** pp
, char const* pzName
, size_t nameLen
,
265 char* pzValue
, size_t dataLen
)
270 size_t sz
= nameLen
+ sizeof(*pNV
) + 1;
271 pNV
= AGALOC( sz
, "empty nested value pair" );
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
;
281 pNV
= optionLoadNested( pzValue
, pzName
, nameLen
);
285 addArgListEntry( pp
, pNV
);
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.
297 scanNameEntry(char const* pzName
, tOptionValue
* pRes
)
300 char const * pzScan
= pzName
+1;
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);
318 while (isspace( (int)*++pzScan
)) ;
320 case ',': goto comma_char
;
322 case '\'': goto quote_char
;
323 case NUL
: goto nul_byte
;
324 default: goto default_char
;
334 addStringValue(&(pRes
->v
.nestVal
), pzName
, nameLen
, NULL
, (size_t)0);
341 pzScan
= scanQuotedString( pzScan
);
342 dataLen
= pzScan
- pzVal
;
343 pNV
= addStringValue( &(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
,
345 if ((pNV
!= NULL
) && (option_load_mode
== OPTION_LOAD_COOKED
))
346 ao_string_cook( pNV
->v
.strVal
, NULL
);
352 * We have found some strange text value. It ends with a newline
357 char ch
= *(pzScan
++);
361 dataLen
= pzScan
- pzVal
;
366 if ( (pzScan
> pzVal
+ 2)
367 && (pzScan
[-2] == '\\')
368 && (pzScan
[ 0] != NUL
))
373 dataLen
= (pzScan
- pzVal
) - 1;
375 pNV
= addStringValue( &(pRes
->v
.nestVal
), pzName
, nameLen
,
378 removeBackslashes( pNV
->v
.strVal
);
379 goto leave_scan_name
;
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.
396 scanXmlEntry( char const* pzName
, tOptionValue
* pRes
)
398 size_t nameLen
= 1, valLen
= 0;
399 char const* pzScan
= ++pzName
;
402 tOptionValue
* pNewVal
;
403 tOptionLoadMode save_mode
= option_load_mode
;
405 if (! isalpha((int)*pzName
)) {
412 pzName
= strstr( pzName
, "-->" );
418 pzName
= strchr( pzName
, '>' );
426 while (isalpha( (int)*++pzScan
)) nameLen
++;
429 valu
.valType
= OPARG_TYPE_STRING
;
434 pzScan
= parseAttributes(
435 NULL
, (char*)pzScan
, &option_load_mode
, &valu
);
436 if (*pzScan
== '>') {
441 if (*pzScan
!= '/') {
442 option_load_mode
= save_mode
;
448 if (*++pzScan
!= '>') {
449 option_load_mode
= save_mode
;
452 addStringValue(&(pRes
->v
.nestVal
), pzName
, nameLen
, NULL
, (size_t)0);
453 option_load_mode
= save_mode
;
457 option_load_mode
= save_mode
;
471 char const* pzS
= pzName
;
482 pzScan
= strstr( pzScan
, z
);
483 if (pzScan
== NULL
) {
484 option_load_mode
= save_mode
;
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);
497 case OPARG_TYPE_STRING
:
498 pNewVal
= addStringValue(
499 &(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
, valLen
);
501 if (option_load_mode
== OPTION_LOAD_KEEP
)
503 mungeString( pNewVal
->v
.strVal
, option_load_mode
);
506 case OPARG_TYPE_BOOLEAN
:
507 addBoolValue( &(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
, valLen
);
510 case OPARG_TYPE_NUMERIC
:
511 addNumberValue( &(pRes
->v
.nestVal
), pzName
, nameLen
, pzVal
, valLen
);
514 case OPARG_TYPE_HIERARCHY
:
516 char* pz
= AGALOC( valLen
+1, "hierarchical scan" );
519 memcpy( pz
, pzVal
, valLen
);
521 addNestedValue( &(pRes
->v
.nestVal
), pzName
, nameLen
, pz
, valLen
);
526 case OPARG_TYPE_ENUMERATION
:
527 case OPARG_TYPE_MEMBERSHIP
:
532 option_load_mode
= save_mode
;
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.
545 unloadNestedArglist( tArgList
* pAL
)
548 tCC
** ppNV
= pAL
->apzArgs
;
551 tOptionValue
* pNV
= (tOptionValue
*)(void*)*(ppNV
++);
552 if (pNV
->valType
== OPARG_TYPE_HIERARCHY
)
553 unloadNestedArglist( pNV
->v
.nestVal
);
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 +
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}).
572 optionUnloadNested( tOptionValue
const * pOV
)
574 if (pOV
== NULL
) return;
575 if (pOV
->valType
!= OPARG_TYPE_HIERARCHY
) {
580 unloadNestedArglist( pOV
->v
.nestVal
);
582 AGFREE( (void*)pOV
);
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 sortNestedList( 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
;
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
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.
644 optionLoadNested(char const* pzTxt
, char const* pzName
, size_t nameLen
)
650 * Make sure we have some data and we have space to put what we find.
656 while (isspace( (int)*pzTxt
)) pzTxt
++;
661 pRes
= AGALOC( sizeof(*pRes
) + nameLen
+ 1, "nested args" );
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" );
676 pRes
->v
.nestVal
= pAL
;
678 pAL
->allocCt
= MIN_ARG_ALLOC_CT
;
681 * Scan until we hit a NUL.
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;
695 } while (pzTxt
!= NULL
); scan_done
:;
697 pAL
= pRes
->v
.nestVal
;
698 if (pAL
->useCt
!= 0) {
699 sortNestedList( pAL
);
704 AGFREE( pRes
->v
.nestVal
);
710 /*=export_func optionNestedVal
713 * what: parse a hierarchical option argument
714 * arg: + tOptions* + pOpts + program options descriptor +
715 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
718 * Nested value was found on the command line
721 optionNestedVal( tOptions
* pOpts
, tOptDesc
* pOD
)
723 tOptionValue
* pOV
= optionLoadNested(
724 pOD
->optArg
.argString
, pOD
->pz_Name
, strlen(pOD
->pz_Name
));
727 addArgListEntry( &(pOD
->optCookie
), (void*)pOV
);
732 * c-file-style: "stroustrup"
733 * indent-tabs-mode: nil
735 * end of autoopts/nested.c */