1181834Sroberto
2181834Sroberto/*
3181834Sroberto *  $Id: nested.c,v 4.14 2007/02/04 17:44:12 bkorb Exp $
4181834Sroberto *  Time-stamp:      "2007-01-26 11:04:35 bkorb"
5181834Sroberto *
6181834Sroberto *   Automated Options Nested Values module.
7181834Sroberto */
8181834Sroberto
9181834Sroberto/*
10181834Sroberto *  Automated Options copyright 1992-2007 Bruce Korb
11181834Sroberto *
12181834Sroberto *  Automated Options is free software.
13181834Sroberto *  You may redistribute it and/or modify it under the terms of the
14181834Sroberto *  GNU General Public License, as published by the Free Software
15181834Sroberto *  Foundation; either version 2, or (at your option) any later version.
16181834Sroberto *
17181834Sroberto *  Automated Options is distributed in the hope that it will be useful,
18181834Sroberto *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19181834Sroberto *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20181834Sroberto *  GNU General Public License for more details.
21181834Sroberto *
22181834Sroberto *  You should have received a copy of the GNU General Public License
23181834Sroberto *  along with Automated Options.  See the file "COPYING".  If not,
24181834Sroberto *  write to:  The Free Software Foundation, Inc.,
25181834Sroberto *             51 Franklin Street, Fifth Floor,
26181834Sroberto *             Boston, MA  02110-1301, USA.
27181834Sroberto *
28181834Sroberto * As a special exception, Bruce Korb gives permission for additional
29181834Sroberto * uses of the text contained in his release of AutoOpts.
30181834Sroberto *
31181834Sroberto * The exception is that, if you link the AutoOpts library with other
32181834Sroberto * files to produce an executable, this does not by itself cause the
33181834Sroberto * resulting executable to be covered by the GNU General Public License.
34181834Sroberto * Your use of that executable is in no way restricted on account of
35181834Sroberto * linking the AutoOpts library code into it.
36181834Sroberto *
37181834Sroberto * This exception does not however invalidate any other reasons why
38181834Sroberto * the executable file might be covered by the GNU General Public License.
39181834Sroberto *
40181834Sroberto * This exception applies only to the code released by Bruce Korb under
41181834Sroberto * the name AutoOpts.  If you copy code from other sources under the
42181834Sroberto * General Public License into a copy of AutoOpts, as the General Public
43181834Sroberto * License permits, the exception does not apply to the code that you add
44181834Sroberto * in this way.  To avoid misleading anyone as to the status of such
45181834Sroberto * modified files, you must delete this exception notice from them.
46181834Sroberto *
47181834Sroberto * If you write modifications of your own for AutoOpts, it is your choice
48181834Sroberto * whether to permit this exception to apply to your modifications.
49181834Sroberto * If you do not wish that, delete this exception notice.
50181834Sroberto */
51181834Sroberto/* = = = START-STATIC-FORWARD = = = */
52181834Sroberto/* static forward declarations maintained by :mkfwd */
53181834Srobertostatic void
54181834SrobertoremoveBackslashes( char* pzSrc );
55181834Sroberto
56181834Srobertostatic char const*
57181834SrobertoscanQuotedString( char const* pzTxt );
58181834Sroberto
59181834Srobertostatic tOptionValue*
60181834SrobertoaddStringValue( void** pp, char const* pzName, size_t nameLen,
61181834Sroberto                char const* pzValue, size_t dataLen );
62181834Sroberto
63181834Srobertostatic tOptionValue*
64181834SrobertoaddBoolValue( void** pp, char const* pzName, size_t nameLen,
65181834Sroberto                char const* pzValue, size_t dataLen );
66181834Sroberto
67181834Srobertostatic tOptionValue*
68181834SrobertoaddNumberValue( void** pp, char const* pzName, size_t nameLen,
69181834Sroberto                char const* pzValue, size_t dataLen );
70181834Sroberto
71181834Srobertostatic tOptionValue*
72181834SrobertoaddNestedValue( void** pp, char const* pzName, size_t nameLen,
73181834Sroberto                char* pzValue, size_t dataLen );
74181834Sroberto
75181834Srobertostatic char const*
76181834SrobertoscanNameEntry(char const* pzName, tOptionValue* pRes);
77181834Sroberto
78181834Srobertostatic char const*
79181834SrobertoscanXmlEntry( char const* pzName, tOptionValue* pRes );
80181834Sroberto
81181834Srobertostatic void
82181834SrobertounloadNestedArglist( tArgList* pAL );
83181834Sroberto
84181834Srobertostatic void
85181834SrobertosortNestedList( tArgList* pAL );
86181834Sroberto/* = = = END-STATIC-FORWARD = = = */
87181834Sroberto
88181834Sroberto/*  removeBackslashes
89181834Sroberto *
90181834Sroberto *  This function assumes that all newline characters were preceeded by
91181834Sroberto *  backslashes that need removal.
92181834Sroberto */
93181834Srobertostatic void
94181834SrobertoremoveBackslashes( char* pzSrc )
95181834Sroberto{
96181834Sroberto    char* pzD = strchr(pzSrc, '\n');
97181834Sroberto
98181834Sroberto    if (pzD == NULL)
99181834Sroberto        return;
100181834Sroberto    *--pzD = '\n';
101181834Sroberto
102181834Sroberto    for (;;) {
103181834Sroberto        char ch = ((*pzD++) = *(pzSrc++));
104181834Sroberto        switch (ch) {
105181834Sroberto        case '\n': *--pzD = ch; break;
106181834Sroberto        case NUL:  return;
107181834Sroberto        default:
108181834Sroberto            ;
109181834Sroberto        }
110181834Sroberto    }
111181834Sroberto}
112181834Sroberto
113181834Sroberto
114181834Sroberto/*  scanQuotedString
115181834Sroberto *
116181834Sroberto *  Find the end of a quoted string, skipping escaped quote characters.
117181834Sroberto */
118181834Srobertostatic char const*
119181834SrobertoscanQuotedString( char const* pzTxt )
120181834Sroberto{
121181834Sroberto    char q = *(pzTxt++); /* remember the type of quote */
122181834Sroberto
123181834Sroberto    for (;;) {
124181834Sroberto        char ch = *(pzTxt++);
125181834Sroberto        if (ch == NUL)
126181834Sroberto            return pzTxt-1;
127181834Sroberto
128181834Sroberto        if (ch == q)
129181834Sroberto            return pzTxt;
130181834Sroberto
131181834Sroberto        if (ch == '\\') {
132181834Sroberto            ch = *(pzTxt++);
133181834Sroberto            /*
134181834Sroberto             *  IF the next character is NUL, drop the backslash, too.
135181834Sroberto             */
136181834Sroberto            if (ch == NUL)
137181834Sroberto                return pzTxt - 2;
138181834Sroberto
139181834Sroberto            /*
140181834Sroberto             *  IF the quote character or the escape character were escaped,
141181834Sroberto             *  then skip both, as long as the string does not end.
142181834Sroberto             */
143181834Sroberto            if ((ch == q) || (ch == '\\')) {
144181834Sroberto                if (*(pzTxt++) == NUL)
145181834Sroberto                    return pzTxt-1;
146181834Sroberto            }
147181834Sroberto        }
148181834Sroberto    }
149181834Sroberto}
150181834Sroberto
151181834Sroberto
152181834Sroberto/*  addStringValue
153181834Sroberto *
154181834Sroberto *  Associate a name with either a string or no value.
155181834Sroberto */
156181834Srobertostatic tOptionValue*
157181834SrobertoaddStringValue( void** pp, char const* pzName, size_t nameLen,
158181834Sroberto                char const* pzValue, size_t dataLen )
159181834Sroberto{
160181834Sroberto    tOptionValue* pNV;
161181834Sroberto    size_t sz = nameLen + dataLen + sizeof(*pNV);
162181834Sroberto
163181834Sroberto    pNV = AGALOC( sz, "option name/str value pair" );
164181834Sroberto    if (pNV == NULL)
165181834Sroberto        return NULL;
166181834Sroberto
167181834Sroberto    if (pzValue == NULL) {
168181834Sroberto        pNV->valType = OPARG_TYPE_NONE;
169181834Sroberto        pNV->pzName = pNV->v.strVal;
170181834Sroberto
171181834Sroberto    } else {
172181834Sroberto        pNV->valType = OPARG_TYPE_STRING;
173181834Sroberto        if (dataLen > 0)
174181834Sroberto            memcpy( pNV->v.strVal, pzValue, dataLen );
175181834Sroberto        pNV->v.strVal[dataLen] = NUL;
176181834Sroberto        pNV->pzName = pNV->v.strVal + dataLen + 1;
177181834Sroberto    }
178181834Sroberto
179181834Sroberto    memcpy( pNV->pzName, pzName, nameLen );
180181834Sroberto    pNV->pzName[ nameLen ] = NUL;
181181834Sroberto    addArgListEntry( pp, pNV );
182181834Sroberto    return pNV;
183181834Sroberto}
184181834Sroberto
185181834Sroberto
186181834Sroberto/*  addBoolValue
187181834Sroberto *
188181834Sroberto *  Associate a name with either a string or no value.
189181834Sroberto */
190181834Srobertostatic tOptionValue*
191181834SrobertoaddBoolValue( void** pp, char const* pzName, size_t nameLen,
192181834Sroberto                char const* pzValue, size_t dataLen )
193181834Sroberto{
194181834Sroberto    tOptionValue* pNV;
195181834Sroberto    size_t sz = nameLen + sizeof(*pNV) + 1;
196181834Sroberto
197181834Sroberto    pNV = AGALOC( sz, "option name/bool value pair" );
198181834Sroberto    if (pNV == NULL)
199181834Sroberto        return NULL;
200181834Sroberto    while (isspace( (int)*pzValue ) && (dataLen > 0)) {
201181834Sroberto        dataLen--; pzValue++;
202181834Sroberto    }
203181834Sroberto    if (dataLen == 0)
204181834Sroberto        pNV->v.boolVal = 0;
205181834Sroberto    else if (isdigit( (int)*pzValue ))
206181834Sroberto        pNV->v.boolVal = atoi( pzValue );
207181834Sroberto    else switch (*pzValue) {
208181834Sroberto    case 'f':
209181834Sroberto    case 'F':
210181834Sroberto    case 'n':
211181834Sroberto    case 'N':
212181834Sroberto        pNV->v.boolVal = 0; break;
213181834Sroberto    default:
214181834Sroberto        pNV->v.boolVal = 1;
215181834Sroberto    }
216181834Sroberto
217181834Sroberto    pNV->valType = OPARG_TYPE_BOOLEAN;
218181834Sroberto    pNV->pzName = (char*)(pNV + 1);
219181834Sroberto    memcpy( pNV->pzName, pzName, nameLen );
220181834Sroberto    pNV->pzName[ nameLen ] = NUL;
221181834Sroberto    addArgListEntry( pp, pNV );
222181834Sroberto    return pNV;
223181834Sroberto}
224181834Sroberto
225181834Sroberto
226181834Sroberto/*  addNumberValue
227181834Sroberto *
228181834Sroberto *  Associate a name with either a string or no value.
229181834Sroberto */
230181834Srobertostatic tOptionValue*
231181834SrobertoaddNumberValue( void** pp, char const* pzName, size_t nameLen,
232181834Sroberto                char const* pzValue, size_t dataLen )
233181834Sroberto{
234181834Sroberto    tOptionValue* pNV;
235181834Sroberto    size_t sz = nameLen + sizeof(*pNV) + 1;
236181834Sroberto
237181834Sroberto    pNV = AGALOC( sz, "option name/bool value pair" );
238181834Sroberto    if (pNV == NULL)
239181834Sroberto        return NULL;
240181834Sroberto    while (isspace( (int)*pzValue ) && (dataLen > 0)) {
241181834Sroberto        dataLen--; pzValue++;
242181834Sroberto    }
243181834Sroberto    if (dataLen == 0)
244181834Sroberto        pNV->v.boolVal = 0;
245181834Sroberto    else
246181834Sroberto        pNV->v.boolVal = atoi( pzValue );
247181834Sroberto
248181834Sroberto    pNV->valType = OPARG_TYPE_NUMERIC;
249181834Sroberto    pNV->pzName = (char*)(pNV + 1);
250181834Sroberto    memcpy( pNV->pzName, pzName, nameLen );
251181834Sroberto    pNV->pzName[ nameLen ] = NUL;
252181834Sroberto    addArgListEntry( pp, pNV );
253181834Sroberto    return pNV;
254181834Sroberto}
255181834Sroberto
256181834Sroberto
257181834Sroberto/*  addNestedValue
258181834Sroberto *
259181834Sroberto *  Associate a name with either a string or no value.
260181834Sroberto */
261181834Srobertostatic tOptionValue*
262181834SrobertoaddNestedValue( void** pp, char const* pzName, size_t nameLen,
263181834Sroberto                char* pzValue, size_t dataLen )
264181834Sroberto{
265181834Sroberto    tOptionValue* pNV;
266181834Sroberto
267181834Sroberto    if (dataLen == 0) {
268181834Sroberto        size_t sz = nameLen + sizeof(*pNV) + 1;
269181834Sroberto        pNV = AGALOC( sz, "empty nested value pair" );
270181834Sroberto        if (pNV == NULL)
271181834Sroberto            return NULL;
272181834Sroberto        pNV->v.nestVal = NULL;
273181834Sroberto        pNV->valType = OPARG_TYPE_HIERARCHY;
274181834Sroberto        pNV->pzName = (char*)(pNV + 1);
275181834Sroberto        memcpy( pNV->pzName, pzName, nameLen );
276181834Sroberto        pNV->pzName[ nameLen ] = NUL;
277181834Sroberto
278181834Sroberto    } else {
279181834Sroberto        pNV = optionLoadNested( pzValue, pzName, nameLen );
280181834Sroberto    }
281181834Sroberto
282181834Sroberto    if (pNV != NULL)
283181834Sroberto        addArgListEntry( pp, pNV );
284181834Sroberto
285181834Sroberto    return pNV;
286181834Sroberto}
287181834Sroberto
288181834Sroberto
289181834Sroberto/*  scanNameEntry
290181834Sroberto *
291181834Sroberto *  We have an entry that starts with a name.  Find the end of it, cook it
292181834Sroberto *  (if called for) and create the name/value association.
293181834Sroberto */
294181834Srobertostatic char const*
295181834SrobertoscanNameEntry(char const* pzName, tOptionValue* pRes)
296181834Sroberto{
297181834Sroberto    tOptionValue* pNV;
298181834Sroberto    char const * pzScan = pzName+1;
299181834Sroberto    char const * pzVal;
300181834Sroberto    size_t       nameLen = 1;
301181834Sroberto    size_t       dataLen = 0;
302181834Sroberto
303181834Sroberto    while (ISNAMECHAR( (int)*pzScan ))  { pzScan++; nameLen++; }
304181834Sroberto
305181834Sroberto    while (isspace( (int)*pzScan )) {
306181834Sroberto        char ch = *(pzScan++);
307181834Sroberto        if ((ch == '\n') || (ch == ',')) {
308181834Sroberto            addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL,(size_t)0);
309181834Sroberto            return pzScan - 1;
310181834Sroberto        }
311181834Sroberto    }
312181834Sroberto
313181834Sroberto    switch (*pzScan) {
314181834Sroberto    case '=':
315181834Sroberto    case ':':
316181834Sroberto        while (isspace( (int)*++pzScan ))  ;
317181834Sroberto        switch (*pzScan) {
318181834Sroberto        case ',':  goto comma_char;
319181834Sroberto        case '"':
320181834Sroberto        case '\'': goto quote_char;
321181834Sroberto        case NUL:  goto nul_byte;
322181834Sroberto        default:   goto default_char;
323181834Sroberto        }
324181834Sroberto
325181834Sroberto    case ',':
326181834Sroberto    comma_char:
327181834Sroberto        pzScan++;
328181834Sroberto        /* FALLTHROUGH */
329181834Sroberto
330181834Sroberto    case NUL:
331181834Sroberto    nul_byte:
332181834Sroberto        addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
333181834Sroberto        break;
334181834Sroberto
335181834Sroberto    case '"':
336181834Sroberto    case '\'':
337181834Sroberto    quote_char:
338181834Sroberto        pzVal = pzScan;
339181834Sroberto        pzScan = scanQuotedString( pzScan );
340181834Sroberto        dataLen = pzScan - pzVal;
341181834Sroberto        pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal,
342181834Sroberto                              dataLen );
343181834Sroberto        if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
344181834Sroberto            ao_string_cook( pNV->v.strVal, NULL );
345181834Sroberto        break;
346181834Sroberto
347181834Sroberto    default:
348181834Sroberto    default_char:
349181834Sroberto        /*
350181834Sroberto         *  We have found some strange text value.  It ends with a newline
351181834Sroberto         *  or a comma.
352181834Sroberto         */
353181834Sroberto        pzVal = pzScan;
354181834Sroberto        for (;;) {
355181834Sroberto            char ch = *(pzScan++);
356181834Sroberto            switch (ch) {
357181834Sroberto            case NUL:
358181834Sroberto                pzScan--;
359181834Sroberto                dataLen = pzScan - pzVal;
360181834Sroberto                goto string_done;
361181834Sroberto                /* FALLTHROUGH */
362181834Sroberto
363181834Sroberto            case '\n':
364181834Sroberto                if (   (pzScan > pzVal + 2)
365181834Sroberto                    && (pzScan[-2] == '\\')
366181834Sroberto                    && (pzScan[ 0] != NUL))
367181834Sroberto                    continue;
368181834Sroberto                /* FALLTHROUGH */
369181834Sroberto
370181834Sroberto            case ',':
371181834Sroberto                dataLen = (pzScan - pzVal) - 1;
372181834Sroberto            string_done:
373181834Sroberto                pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen,
374181834Sroberto                                      pzVal, dataLen );
375181834Sroberto                if (pNV != NULL)
376181834Sroberto                    removeBackslashes( pNV->v.strVal );
377181834Sroberto                goto leave_scan_name;
378181834Sroberto            }
379181834Sroberto        }
380181834Sroberto        break;
381181834Sroberto    } leave_scan_name:;
382181834Sroberto
383181834Sroberto    return pzScan;
384181834Sroberto}
385181834Sroberto
386181834Sroberto
387181834Sroberto/*  scanXmlEntry
388181834Sroberto *
389181834Sroberto *  We've found a '<' character.  We ignore this if it is a comment or a
390181834Sroberto *  directive.  If it is something else, then whatever it is we are looking
391181834Sroberto *  at is bogus.  Returning NULL stops processing.
392181834Sroberto */
393181834Srobertostatic char const*
394181834SrobertoscanXmlEntry( char const* pzName, tOptionValue* pRes )
395181834Sroberto{
396181834Sroberto    size_t nameLen = 1, valLen = 0;
397181834Sroberto    char const*   pzScan = ++pzName;
398181834Sroberto    char const*   pzVal;
399181834Sroberto    tOptionValue  valu;
400181834Sroberto    tOptionValue* pNewVal;
401181834Sroberto    tOptionLoadMode save_mode = option_load_mode;
402181834Sroberto
403181834Sroberto    if (! isalpha((int)*pzName)) {
404181834Sroberto        switch (*pzName) {
405181834Sroberto        default:
406181834Sroberto            pzName = NULL;
407181834Sroberto            break;
408181834Sroberto
409181834Sroberto        case '!':
410181834Sroberto            pzName = strstr( pzName, "-->" );
411181834Sroberto            if (pzName != NULL)
412181834Sroberto                pzName += 3;
413181834Sroberto            break;
414181834Sroberto
415181834Sroberto        case '?':
416181834Sroberto            pzName = strchr( pzName, '>' );
417181834Sroberto            if (pzName != NULL)
418181834Sroberto                pzName++;
419181834Sroberto            break;
420181834Sroberto        }
421181834Sroberto        return pzName;
422181834Sroberto    }
423181834Sroberto
424181834Sroberto    while (isalpha( (int)*++pzScan ))  nameLen++;
425181834Sroberto    if (nameLen > 64)
426181834Sroberto        return NULL;
427181834Sroberto    valu.valType = OPARG_TYPE_STRING;
428181834Sroberto
429181834Sroberto    switch (*pzScan) {
430181834Sroberto    case ' ':
431181834Sroberto    case '\t':
432181834Sroberto        pzScan = parseAttributes(
433181834Sroberto            NULL, (char*)pzScan, &option_load_mode, &valu );
434181834Sroberto        if (*pzScan == '>') {
435181834Sroberto            pzScan++;
436181834Sroberto            break;
437181834Sroberto        }
438181834Sroberto
439181834Sroberto        if (*pzScan != '/') {
440181834Sroberto            option_load_mode = save_mode;
441181834Sroberto            return NULL;
442181834Sroberto        }
443181834Sroberto        /* FALLTHROUGH */
444181834Sroberto
445181834Sroberto    case '/':
446181834Sroberto        if (*++pzScan != '>') {
447181834Sroberto            option_load_mode = save_mode;
448181834Sroberto            return NULL;
449181834Sroberto        }
450181834Sroberto        addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
451181834Sroberto        option_load_mode = save_mode;
452181834Sroberto        return pzScan+2;
453181834Sroberto
454181834Sroberto    default:
455181834Sroberto        option_load_mode = save_mode;
456181834Sroberto        return NULL;
457181834Sroberto
458181834Sroberto    case '>':
459181834Sroberto        pzScan++;
460181834Sroberto        break;
461181834Sroberto    }
462181834Sroberto
463181834Sroberto    pzVal = pzScan;
464181834Sroberto
465181834Sroberto    {
466181834Sroberto        char z[68];
467181834Sroberto        char* pzD = z;
468181834Sroberto        int  ct = nameLen;
469181834Sroberto        char const* pzS = pzName;
470181834Sroberto
471181834Sroberto        *(pzD++) = '<';
472181834Sroberto        *(pzD++) = '/';
473181834Sroberto
474181834Sroberto        do  {
475181834Sroberto            *(pzD++) = *(pzS++);
476181834Sroberto        } while (--ct > 0);
477181834Sroberto        *(pzD++) = '>';
478181834Sroberto        *pzD = NUL;
479181834Sroberto
480181834Sroberto        pzScan = strstr( pzScan, z );
481181834Sroberto        if (pzScan == NULL) {
482181834Sroberto            option_load_mode = save_mode;
483181834Sroberto            return NULL;
484181834Sroberto        }
485181834Sroberto        valLen = (pzScan - pzVal);
486181834Sroberto        pzScan += nameLen + 3;
487181834Sroberto        while (isspace(  (int)*pzScan ))  pzScan++;
488181834Sroberto    }
489181834Sroberto
490181834Sroberto    switch (valu.valType) {
491181834Sroberto    case OPARG_TYPE_NONE:
492181834Sroberto        addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
493181834Sroberto        break;
494181834Sroberto
495181834Sroberto    case OPARG_TYPE_STRING:
496181834Sroberto        pNewVal = addStringValue(
497181834Sroberto            &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
498181834Sroberto
499181834Sroberto        if (option_load_mode == OPTION_LOAD_KEEP)
500181834Sroberto            break;
501181834Sroberto        mungeString( pNewVal->v.strVal, option_load_mode );
502181834Sroberto        break;
503181834Sroberto
504181834Sroberto    case OPARG_TYPE_BOOLEAN:
505181834Sroberto        addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
506181834Sroberto        break;
507181834Sroberto
508181834Sroberto    case OPARG_TYPE_NUMERIC:
509181834Sroberto        addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
510181834Sroberto        break;
511181834Sroberto
512181834Sroberto    case OPARG_TYPE_HIERARCHY:
513181834Sroberto    {
514181834Sroberto        char* pz = AGALOC( valLen+1, "hierarchical scan" );
515181834Sroberto        if (pz == NULL)
516181834Sroberto            break;
517181834Sroberto        memcpy( pz, pzVal, valLen );
518181834Sroberto        pz[valLen] = NUL;
519181834Sroberto        addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen );
520181834Sroberto        AGFREE(pz);
521181834Sroberto        break;
522181834Sroberto    }
523181834Sroberto
524181834Sroberto    case OPARG_TYPE_ENUMERATION:
525181834Sroberto    case OPARG_TYPE_MEMBERSHIP:
526181834Sroberto    default:
527181834Sroberto        break;
528181834Sroberto    }
529181834Sroberto
530181834Sroberto    option_load_mode = save_mode;
531181834Sroberto    return pzScan;
532181834Sroberto}
533181834Sroberto
534181834Sroberto
535181834Sroberto/*  unloadNestedArglist
536181834Sroberto *
537181834Sroberto *  Deallocate a list of option arguments.  This must have been gotten from
538181834Sroberto *  a hierarchical option argument, not a stacked list of strings.  It is
539181834Sroberto *  an internal call, so it is not validated.  The caller is responsible for
540181834Sroberto *  knowing what they are doing.
541181834Sroberto */
542181834Srobertostatic void
543181834SrobertounloadNestedArglist( tArgList* pAL )
544181834Sroberto{
545181834Sroberto    int ct = pAL->useCt;
546181834Sroberto    tCC** ppNV = pAL->apzArgs;
547181834Sroberto
548181834Sroberto    while (ct-- > 0) {
549181834Sroberto        tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
550181834Sroberto        if (pNV->valType == OPARG_TYPE_HIERARCHY)
551181834Sroberto            unloadNestedArglist( pNV->v.nestVal );
552181834Sroberto        AGFREE( pNV );
553181834Sroberto    }
554181834Sroberto
555181834Sroberto    AGFREE( (void*)pAL );
556181834Sroberto}
557181834Sroberto
558181834Sroberto
559181834Sroberto/*=export_func  optionUnloadNested
560181834Sroberto *
561181834Sroberto * what:  Deallocate the memory for a nested value
562181834Sroberto * arg:   + tOptionValue const * + pOptVal + the hierarchical value +
563181834Sroberto *
564181834Sroberto * doc:
565181834Sroberto *  A nested value needs to be deallocated.  The pointer passed in should
566181834Sroberto *  have been gotten from a call to @code{configFileLoad()} (See
567181834Sroberto *  @pxref{libopts-configFileLoad}).
568181834Sroberto=*/
569181834Srobertovoid
570181834SrobertooptionUnloadNested( tOptionValue const * pOV )
571181834Sroberto{
572181834Sroberto    if (pOV == NULL) return;
573181834Sroberto    if (pOV->valType != OPARG_TYPE_HIERARCHY) {
574181834Sroberto        errno = EINVAL;
575181834Sroberto        return;
576181834Sroberto    }
577181834Sroberto
578181834Sroberto    unloadNestedArglist( pOV->v.nestVal );
579181834Sroberto
580181834Sroberto    AGFREE( (void*)pOV );
581181834Sroberto}
582181834Sroberto
583181834Sroberto
584181834Sroberto/*  sortNestedList
585181834Sroberto *
586181834Sroberto *  This is a _stable_ sort.  The entries are sorted alphabetically,
587181834Sroberto *  but within entries of the same name the ordering is unchanged.
588181834Sroberto *  Typically, we also hope the input is sorted.
589181834Sroberto */
590181834Srobertostatic void
591181834SrobertosortNestedList( tArgList* pAL )
592181834Sroberto{
593181834Sroberto    int ix;
594181834Sroberto    int lm = pAL->useCt;
595181834Sroberto
596181834Sroberto    /*
597181834Sroberto     *  This loop iterates "useCt" - 1 times.
598181834Sroberto     */
599181834Sroberto    for (ix = 0; ++ix < lm;) {
600181834Sroberto        int iy = ix-1;
601181834Sroberto        tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
602181834Sroberto        tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
603181834Sroberto
604181834Sroberto        /*
605181834Sroberto         *  For as long as the new entry precedes the "old" entry,
606181834Sroberto         *  move the old pointer.  Stop before trying to extract the
607181834Sroberto         *  "-1" entry.
608181834Sroberto         */
609181834Sroberto        while (strcmp( pOldNV->pzName, pNewNV->pzName ) > 0) {
610181834Sroberto            pAL->apzArgs[iy+1] = (void*)pOldNV;
611181834Sroberto            pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
612181834Sroberto            if (iy < 0)
613181834Sroberto                break;
614181834Sroberto        }
615181834Sroberto
616181834Sroberto        /*
617181834Sroberto         *  Always store the pointer.  Sometimes it is redundant,
618181834Sroberto         *  but the redundancy is cheaper than a test and branch sequence.
619181834Sroberto         */
620181834Sroberto        pAL->apzArgs[iy+1] = (void*)pNewNV;
621181834Sroberto    }
622181834Sroberto}
623181834Sroberto
624181834Sroberto
625181834Sroberto/* optionLoadNested
626181834Sroberto * private:
627181834Sroberto *
628181834Sroberto * what:  parse a hierarchical option argument
629181834Sroberto * arg:   + char const*     + pzTxt   + the text to scan +
630181834Sroberto * arg:   + char const*     + pzName  + the name for the text +
631181834Sroberto * arg:   + size_t          + nameLen + the length of "name"  +
632181834Sroberto *
633181834Sroberto * ret_type:  tOptionValue*
634181834Sroberto * ret_desc:  An allocated, compound value structure
635181834Sroberto *
636181834Sroberto * doc:
637181834Sroberto *  A block of text represents a series of values.  It may be an
638181834Sroberto *  entire configuration file, or it may be an argument to an
639181834Sroberto *  option that takes a hierarchical value.
640181834Sroberto */
641181834SrobertoLOCAL tOptionValue*
642181834SrobertooptionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
643181834Sroberto{
644181834Sroberto    tOptionValue* pRes;
645181834Sroberto    tArgList*     pAL;
646181834Sroberto
647181834Sroberto    /*
648181834Sroberto     *  Make sure we have some data and we have space to put what we find.
649181834Sroberto     */
650181834Sroberto    if (pzTxt == NULL) {
651181834Sroberto        errno = EINVAL;
652181834Sroberto        return NULL;
653181834Sroberto    }
654181834Sroberto    while (isspace( (int)*pzTxt ))  pzTxt++;
655181834Sroberto    if (*pzTxt == NUL) {
656181834Sroberto        errno = ENOENT;
657181834Sroberto        return NULL;
658181834Sroberto    }
659181834Sroberto    pRes = AGALOC( sizeof(*pRes) + nameLen + 1, "nested args" );
660181834Sroberto    if (pRes == NULL) {
661181834Sroberto        errno = ENOMEM;
662181834Sroberto        return NULL;
663181834Sroberto    }
664181834Sroberto    pRes->valType   = OPARG_TYPE_HIERARCHY;
665181834Sroberto    pRes->pzName    = (char*)(pRes + 1);
666181834Sroberto    memcpy( pRes->pzName, pzName, nameLen );
667181834Sroberto    pRes->pzName[ nameLen ] = NUL;
668181834Sroberto
669181834Sroberto    pAL = AGALOC( sizeof(*pAL), "nested arg list" );
670181834Sroberto    if (pAL == NULL) {
671181834Sroberto        AGFREE( pRes );
672181834Sroberto        return NULL;
673181834Sroberto    }
674181834Sroberto    pRes->v.nestVal = pAL;
675181834Sroberto    pAL->useCt   = 0;
676181834Sroberto    pAL->allocCt = MIN_ARG_ALLOC_CT;
677181834Sroberto
678181834Sroberto    /*
679181834Sroberto     *  Scan until we hit a NUL.
680181834Sroberto     */
681181834Sroberto    do  {
682181834Sroberto        while (isspace( (int)*pzTxt ))  pzTxt++;
683181834Sroberto        if (isalpha( (int)*pzTxt )) {
684181834Sroberto            pzTxt = scanNameEntry( pzTxt, pRes );
685181834Sroberto        }
686181834Sroberto        else switch (*pzTxt) {
687181834Sroberto        case NUL: goto scan_done;
688181834Sroberto        case '<': pzTxt = scanXmlEntry( pzTxt, pRes );
689181834Sroberto                  if (*pzTxt == ',') pzTxt++;     break;
690181834Sroberto        case '#': pzTxt = strchr( pzTxt, '\n' );  break;
691181834Sroberto        default:  goto woops;
692181834Sroberto        }
693181834Sroberto    } while (pzTxt != NULL); scan_done:;
694181834Sroberto
695181834Sroberto    pAL = pRes->v.nestVal;
696181834Sroberto    if (pAL->useCt != 0) {
697181834Sroberto        sortNestedList( pAL );
698181834Sroberto        return pRes;
699181834Sroberto    }
700181834Sroberto
701181834Sroberto woops:
702181834Sroberto    AGFREE( pRes->v.nestVal );
703181834Sroberto    AGFREE( pRes );
704181834Sroberto    return NULL;
705181834Sroberto}
706181834Sroberto
707181834Sroberto
708181834Sroberto/*=export_func  optionNestedVal
709181834Sroberto * private:
710181834Sroberto *
711181834Sroberto * what:  parse a hierarchical option argument
712181834Sroberto * arg:   + tOptions* + pOpts    + program options descriptor +
713181834Sroberto * arg:   + tOptDesc* + pOptDesc + the descriptor for this arg +
714181834Sroberto *
715181834Sroberto * doc:
716181834Sroberto *  Nested value was found on the command line
717181834Sroberto=*/
718181834Srobertovoid
719181834SrobertooptionNestedVal( tOptions* pOpts, tOptDesc* pOD )
720181834Sroberto{
721181834Sroberto    tOptionValue* pOV = optionLoadNested(
722181834Sroberto        pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
723181834Sroberto
724181834Sroberto    if (pOV != NULL)
725181834Sroberto        addArgListEntry( &(pOD->optCookie), (void*)pOV );
726181834Sroberto}
727181834Sroberto/*
728181834Sroberto * Local Variables:
729181834Sroberto * mode: C
730181834Sroberto * c-file-style: "stroustrup"
731181834Sroberto * indent-tabs-mode: nil
732181834Sroberto * End:
733181834Sroberto * end of autoopts/nested.c */
734