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 */