3
* $Id: nested.c,v 4.14 2007/02/04 17:44:12 bkorb Exp $
4
* Time-stamp: "2007-01-26 11:04:35 bkorb"
6
* Automated Options Nested Values module.
10
* Automated Options copyright 1992-2007 Bruce Korb
12
* Automated Options is free software.
13
* You may redistribute it and/or modify it under the terms of the
14
* GNU General Public License, as published by the Free Software
15
* Foundation; either version 2, or (at your option) any later version.
17
* Automated Options is distributed in the hope that it will be useful,
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
* GNU General Public License for more details.
22
* You should have received a copy of the GNU General Public License
23
* along with Automated Options. See the file "COPYING". If not,
24
* write to: The Free Software Foundation, Inc.,
25
* 51 Franklin Street, Fifth Floor,
26
* Boston, MA 02110-1301, USA.
28
* As a special exception, Bruce Korb gives permission for additional
29
* uses of the text contained in his release of AutoOpts.
31
* The exception is that, if you link the AutoOpts library with other
32
* files to produce an executable, this does not by itself cause the
33
* resulting executable to be covered by the GNU General Public License.
34
* Your use of that executable is in no way restricted on account of
35
* linking the AutoOpts library code into it.
37
* This exception does not however invalidate any other reasons why
38
* the executable file might be covered by the GNU General Public License.
40
* This exception applies only to the code released by Bruce Korb under
41
* the name AutoOpts. If you copy code from other sources under the
42
* General Public License into a copy of AutoOpts, as the General Public
43
* License permits, the exception does not apply to the code that you add
44
* in this way. To avoid misleading anyone as to the status of such
45
* modified files, you must delete this exception notice from them.
47
* If you write modifications of your own for AutoOpts, it is your choice
48
* whether to permit this exception to apply to your modifications.
49
* If you do not wish that, delete this exception notice.
51
/* = = = START-STATIC-FORWARD = = = */
52
/* static forward declarations maintained by :mkfwd */
54
removeBackslashes( char* pzSrc );
57
scanQuotedString( char const* pzTxt );
60
addStringValue( void** pp, char const* pzName, size_t nameLen,
61
char const* pzValue, size_t dataLen );
64
addBoolValue( void** pp, char const* pzName, size_t nameLen,
65
char const* pzValue, size_t dataLen );
68
addNumberValue( void** pp, char const* pzName, size_t nameLen,
69
char const* pzValue, size_t dataLen );
72
addNestedValue( void** pp, char const* pzName, size_t nameLen,
73
char* pzValue, size_t dataLen );
76
scanNameEntry(char const* pzName, tOptionValue* pRes);
79
scanXmlEntry( char const* pzName, tOptionValue* pRes );
82
unloadNestedArglist( tArgList* pAL );
85
sortNestedList( tArgList* pAL );
86
/* = = = END-STATIC-FORWARD = = = */
90
* This function assumes that all newline characters were preceeded by
91
* backslashes that need removal.
94
removeBackslashes( char* pzSrc )
96
char* pzD = strchr(pzSrc, '\n');
103
char ch = ((*pzD++) = *(pzSrc++));
105
case '\n': *--pzD = ch; break;
116
* Find the end of a quoted string, skipping escaped quote characters.
119
scanQuotedString( char const* pzTxt )
121
char q = *(pzTxt++); /* remember the type of quote */
124
char ch = *(pzTxt++);
134
* IF the next character is NUL, drop the backslash, too.
140
* IF the quote character or the escape character were escaped,
141
* then skip both, as long as the string does not end.
143
if ((ch == q) || (ch == '\\')) {
144
if (*(pzTxt++) == NUL)
154
* Associate a name with either a string or no value.
157
addStringValue( void** pp, char const* pzName, size_t nameLen,
158
char const* pzValue, size_t dataLen )
161
size_t sz = nameLen + dataLen + sizeof(*pNV);
163
pNV = AGALOC( sz, "option name/str value pair" );
167
if (pzValue == NULL) {
168
pNV->valType = OPARG_TYPE_NONE;
169
pNV->pzName = pNV->v.strVal;
172
pNV->valType = OPARG_TYPE_STRING;
174
memcpy( pNV->v.strVal, pzValue, dataLen );
175
pNV->v.strVal[dataLen] = NUL;
176
pNV->pzName = pNV->v.strVal + dataLen + 1;
179
memcpy( pNV->pzName, pzName, nameLen );
180
pNV->pzName[ nameLen ] = NUL;
181
addArgListEntry( pp, pNV );
188
* Associate a name with either a string or no value.
191
addBoolValue( void** pp, char const* pzName, size_t nameLen,
192
char const* pzValue, size_t dataLen )
195
size_t sz = nameLen + sizeof(*pNV) + 1;
197
pNV = AGALOC( sz, "option name/bool value pair" );
200
while (isspace( (int)*pzValue ) && (dataLen > 0)) {
201
dataLen--; pzValue++;
205
else if (isdigit( (int)*pzValue ))
206
pNV->v.boolVal = atoi( pzValue );
207
else switch (*pzValue) {
212
pNV->v.boolVal = 0; break;
217
pNV->valType = OPARG_TYPE_BOOLEAN;
218
pNV->pzName = (char*)(pNV + 1);
219
memcpy( pNV->pzName, pzName, nameLen );
220
pNV->pzName[ nameLen ] = NUL;
221
addArgListEntry( pp, pNV );
228
* Associate a name with either a string or no value.
231
addNumberValue( void** pp, char const* pzName, size_t nameLen,
232
char const* pzValue, size_t dataLen )
235
size_t sz = nameLen + sizeof(*pNV) + 1;
237
pNV = AGALOC( sz, "option name/bool value pair" );
240
while (isspace( (int)*pzValue ) && (dataLen > 0)) {
241
dataLen--; pzValue++;
246
pNV->v.boolVal = atoi( pzValue );
248
pNV->valType = OPARG_TYPE_NUMERIC;
249
pNV->pzName = (char*)(pNV + 1);
250
memcpy( pNV->pzName, pzName, nameLen );
251
pNV->pzName[ nameLen ] = NUL;
252
addArgListEntry( pp, pNV );
259
* Associate a name with either a string or no value.
262
addNestedValue( void** pp, char const* pzName, size_t nameLen,
263
char* pzValue, size_t dataLen )
268
size_t sz = nameLen + sizeof(*pNV) + 1;
269
pNV = AGALOC( sz, "empty nested value pair" );
272
pNV->v.nestVal = NULL;
273
pNV->valType = OPARG_TYPE_HIERARCHY;
274
pNV->pzName = (char*)(pNV + 1);
275
memcpy( pNV->pzName, pzName, nameLen );
276
pNV->pzName[ nameLen ] = NUL;
279
pNV = optionLoadNested( pzValue, pzName, nameLen );
283
addArgListEntry( pp, pNV );
291
* We have an entry that starts with a name. Find the end of it, cook it
292
* (if called for) and create the name/value association.
295
scanNameEntry(char const* pzName, tOptionValue* pRes)
298
char const * pzScan = pzName+1;
303
while (ISNAMECHAR( (int)*pzScan )) { pzScan++; nameLen++; }
305
while (isspace( (int)*pzScan )) {
306
char ch = *(pzScan++);
307
if ((ch == '\n') || (ch == ',')) {
308
addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL,(size_t)0);
316
while (isspace( (int)*++pzScan )) ;
318
case ',': goto comma_char;
320
case '\'': goto quote_char;
321
case NUL: goto nul_byte;
322
default: goto default_char;
332
addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
339
pzScan = scanQuotedString( pzScan );
340
dataLen = pzScan - pzVal;
341
pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal,
343
if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
344
ao_string_cook( pNV->v.strVal, NULL );
350
* We have found some strange text value. It ends with a newline
355
char ch = *(pzScan++);
359
dataLen = pzScan - pzVal;
364
if ( (pzScan > pzVal + 2)
365
&& (pzScan[-2] == '\\')
366
&& (pzScan[ 0] != NUL))
371
dataLen = (pzScan - pzVal) - 1;
373
pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen,
376
removeBackslashes( pNV->v.strVal );
377
goto leave_scan_name;
389
* We've found a '<' character. We ignore this if it is a comment or a
390
* directive. If it is something else, then whatever it is we are looking
391
* at is bogus. Returning NULL stops processing.
394
scanXmlEntry( char const* pzName, tOptionValue* pRes )
396
size_t nameLen = 1, valLen = 0;
397
char const* pzScan = ++pzName;
400
tOptionValue* pNewVal;
401
tOptionLoadMode save_mode = option_load_mode;
403
if (! isalpha((int)*pzName)) {
410
pzName = strstr( pzName, "-->" );
416
pzName = strchr( pzName, '>' );
424
while (isalpha( (int)*++pzScan )) nameLen++;
427
valu.valType = OPARG_TYPE_STRING;
432
pzScan = parseAttributes(
433
NULL, (char*)pzScan, &option_load_mode, &valu );
434
if (*pzScan == '>') {
439
if (*pzScan != '/') {
440
option_load_mode = save_mode;
446
if (*++pzScan != '>') {
447
option_load_mode = save_mode;
450
addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
451
option_load_mode = save_mode;
455
option_load_mode = save_mode;
469
char const* pzS = pzName;
480
pzScan = strstr( pzScan, z );
481
if (pzScan == NULL) {
482
option_load_mode = save_mode;
485
valLen = (pzScan - pzVal);
486
pzScan += nameLen + 3;
487
while (isspace( (int)*pzScan )) pzScan++;
490
switch (valu.valType) {
491
case OPARG_TYPE_NONE:
492
addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
495
case OPARG_TYPE_STRING:
496
pNewVal = addStringValue(
497
&(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
499
if (option_load_mode == OPTION_LOAD_KEEP)
501
mungeString( pNewVal->v.strVal, option_load_mode );
504
case OPARG_TYPE_BOOLEAN:
505
addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
508
case OPARG_TYPE_NUMERIC:
509
addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
512
case OPARG_TYPE_HIERARCHY:
514
char* pz = AGALOC( valLen+1, "hierarchical scan" );
517
memcpy( pz, pzVal, valLen );
519
addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen );
524
case OPARG_TYPE_ENUMERATION:
525
case OPARG_TYPE_MEMBERSHIP:
530
option_load_mode = save_mode;
535
/* unloadNestedArglist
537
* Deallocate a list of option arguments. This must have been gotten from
538
* a hierarchical option argument, not a stacked list of strings. It is
539
* an internal call, so it is not validated. The caller is responsible for
540
* knowing what they are doing.
543
unloadNestedArglist( tArgList* pAL )
546
tCC** ppNV = pAL->apzArgs;
549
tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
550
if (pNV->valType == OPARG_TYPE_HIERARCHY)
551
unloadNestedArglist( pNV->v.nestVal );
555
AGFREE( (void*)pAL );
559
/*=export_func optionUnloadNested
561
* what: Deallocate the memory for a nested value
562
* arg: + tOptionValue const * + pOptVal + the hierarchical value +
565
* A nested value needs to be deallocated. The pointer passed in should
566
* have been gotten from a call to @code{configFileLoad()} (See
567
* @pxref{libopts-configFileLoad}).
570
optionUnloadNested( tOptionValue const * pOV )
572
if (pOV == NULL) return;
573
if (pOV->valType != OPARG_TYPE_HIERARCHY) {
578
unloadNestedArglist( pOV->v.nestVal );
580
AGFREE( (void*)pOV );
586
* This is a _stable_ sort. The entries are sorted alphabetically,
587
* but within entries of the same name the ordering is unchanged.
588
* Typically, we also hope the input is sorted.
591
sortNestedList( tArgList* pAL )
597
* This loop iterates "useCt" - 1 times.
599
for (ix = 0; ++ix < lm;) {
601
tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
602
tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
605
* For as long as the new entry precedes the "old" entry,
606
* move the old pointer. Stop before trying to extract the
609
while (strcmp( pOldNV->pzName, pNewNV->pzName ) > 0) {
610
pAL->apzArgs[iy+1] = (void*)pOldNV;
611
pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
617
* Always store the pointer. Sometimes it is redundant,
618
* but the redundancy is cheaper than a test and branch sequence.
620
pAL->apzArgs[iy+1] = (void*)pNewNV;
628
* what: parse a hierarchical option argument
629
* arg: + char const* + pzTxt + the text to scan +
630
* arg: + char const* + pzName + the name for the text +
631
* arg: + size_t + nameLen + the length of "name" +
633
* ret_type: tOptionValue*
634
* ret_desc: An allocated, compound value structure
637
* A block of text represents a series of values. It may be an
638
* entire configuration file, or it may be an argument to an
639
* option that takes a hierarchical value.
642
optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
648
* Make sure we have some data and we have space to put what we find.
654
while (isspace( (int)*pzTxt )) pzTxt++;
659
pRes = AGALOC( sizeof(*pRes) + nameLen + 1, "nested args" );
664
pRes->valType = OPARG_TYPE_HIERARCHY;
665
pRes->pzName = (char*)(pRes + 1);
666
memcpy( pRes->pzName, pzName, nameLen );
667
pRes->pzName[ nameLen ] = NUL;
669
pAL = AGALOC( sizeof(*pAL), "nested arg list" );
674
pRes->v.nestVal = pAL;
676
pAL->allocCt = MIN_ARG_ALLOC_CT;
679
* Scan until we hit a NUL.
682
while (isspace( (int)*pzTxt )) pzTxt++;
683
if (isalpha( (int)*pzTxt )) {
684
pzTxt = scanNameEntry( pzTxt, pRes );
686
else switch (*pzTxt) {
687
case NUL: goto scan_done;
688
case '<': pzTxt = scanXmlEntry( pzTxt, pRes );
689
if (*pzTxt == ',') pzTxt++; break;
690
case '#': pzTxt = strchr( pzTxt, '\n' ); break;
693
} while (pzTxt != NULL); scan_done:;
695
pAL = pRes->v.nestVal;
696
if (pAL->useCt != 0) {
697
sortNestedList( pAL );
702
AGFREE( pRes->v.nestVal );
708
/*=export_func optionNestedVal
711
* what: parse a hierarchical option argument
712
* arg: + tOptions* + pOpts + program options descriptor +
713
* arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
716
* Nested value was found on the command line
719
optionNestedVal( tOptions* pOpts, tOptDesc* pOD )
721
tOptionValue* pOV = optionLoadNested(
722
pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
725
addArgListEntry( &(pOD->optCookie), (void*)pOV );
730
* c-file-style: "stroustrup"
731
* indent-tabs-mode: nil
733
* end of autoopts/nested.c */