~ubuntu-branches/debian/squeeze/ntp/squeeze-201010051545

« back to all changes in this revision

Viewing changes to libopts/nested.c

  • Committer: Bazaar Package Importer
  • Author(s): Kurt Roeckx
  • Date: 2009-01-05 21:10:03 UTC
  • mfrom: (1.2.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20090105211003-mh6zc3um4k1uhsj7
Tags: 1:4.2.4p4+dfsg-8
It did not properly check the return value of EVP_VerifyFinal
which results in an malformed DSA signature being treated as
a good signature rather than as an error.  (CVE-2009-0021)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/*
 
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"
 
5
 *
 
6
 *   Automated Options Nested Values module.
 
7
 */
 
8
 
 
9
/*
 
10
 *  Automated Options copyright 1992-2007 Bruce Korb
 
11
 *
 
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.
 
16
 *
 
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.
 
21
 *
 
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.
 
27
 *
 
28
 * As a special exception, Bruce Korb gives permission for additional
 
29
 * uses of the text contained in his release of AutoOpts.
 
30
 *
 
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.
 
36
 *
 
37
 * This exception does not however invalidate any other reasons why
 
38
 * the executable file might be covered by the GNU General Public License.
 
39
 *
 
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.
 
46
 *
 
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.
 
50
 */
 
51
/* = = = START-STATIC-FORWARD = = = */
 
52
/* static forward declarations maintained by :mkfwd */
 
53
static void
 
54
removeBackslashes( char* pzSrc );
 
55
 
 
56
static char const*
 
57
scanQuotedString( char const* pzTxt );
 
58
 
 
59
static tOptionValue*
 
60
addStringValue( void** pp, char const* pzName, size_t nameLen,
 
61
                char const* pzValue, size_t dataLen );
 
62
 
 
63
static tOptionValue*
 
64
addBoolValue( void** pp, char const* pzName, size_t nameLen,
 
65
                char const* pzValue, size_t dataLen );
 
66
 
 
67
static tOptionValue*
 
68
addNumberValue( void** pp, char const* pzName, size_t nameLen,
 
69
                char const* pzValue, size_t dataLen );
 
70
 
 
71
static tOptionValue*
 
72
addNestedValue( void** pp, char const* pzName, size_t nameLen,
 
73
                char* pzValue, size_t dataLen );
 
74
 
 
75
static char const*
 
76
scanNameEntry(char const* pzName, tOptionValue* pRes);
 
77
 
 
78
static char const*
 
79
scanXmlEntry( char const* pzName, tOptionValue* pRes );
 
80
 
 
81
static void
 
82
unloadNestedArglist( tArgList* pAL );
 
83
 
 
84
static void
 
85
sortNestedList( tArgList* pAL );
 
86
/* = = = END-STATIC-FORWARD = = = */
 
87
 
 
88
/*  removeBackslashes
 
89
 *
 
90
 *  This function assumes that all newline characters were preceeded by
 
91
 *  backslashes that need removal.
 
92
 */
 
93
static void
 
94
removeBackslashes( char* pzSrc )
 
95
{
 
96
    char* pzD = strchr(pzSrc, '\n');
 
97
 
 
98
    if (pzD == NULL)
 
99
        return;
 
100
    *--pzD = '\n';
 
101
 
 
102
    for (;;) {
 
103
        char ch = ((*pzD++) = *(pzSrc++));
 
104
        switch (ch) {
 
105
        case '\n': *--pzD = ch; break;
 
106
        case NUL:  return;
 
107
        default:
 
108
            ;
 
109
        }
 
110
    }
 
111
}
 
112
 
 
113
 
 
114
/*  scanQuotedString
 
115
 *
 
116
 *  Find the end of a quoted string, skipping escaped quote characters.
 
117
 */
 
118
static char const*
 
119
scanQuotedString( char const* pzTxt )
 
120
{
 
121
    char q = *(pzTxt++); /* remember the type of quote */
 
122
 
 
123
    for (;;) {
 
124
        char ch = *(pzTxt++);
 
125
        if (ch == NUL)
 
126
            return pzTxt-1;
 
127
 
 
128
        if (ch == q)
 
129
            return pzTxt;
 
130
 
 
131
        if (ch == '\\') {
 
132
            ch = *(pzTxt++);
 
133
            /*
 
134
             *  IF the next character is NUL, drop the backslash, too.
 
135
             */
 
136
            if (ch == NUL)
 
137
                return pzTxt - 2;
 
138
 
 
139
            /*
 
140
             *  IF the quote character or the escape character were escaped,
 
141
             *  then skip both, as long as the string does not end.
 
142
             */
 
143
            if ((ch == q) || (ch == '\\')) {
 
144
                if (*(pzTxt++) == NUL)
 
145
                    return pzTxt-1;
 
146
            }
 
147
        }
 
148
    }
 
149
}
 
150
 
 
151
 
 
152
/*  addStringValue
 
153
 *
 
154
 *  Associate a name with either a string or no value.
 
155
 */
 
156
static tOptionValue*
 
157
addStringValue( void** pp, char const* pzName, size_t nameLen,
 
158
                char const* pzValue, size_t dataLen )
 
159
{
 
160
    tOptionValue* pNV;
 
161
    size_t sz = nameLen + dataLen + sizeof(*pNV);
 
162
 
 
163
    pNV = AGALOC( sz, "option name/str value pair" );
 
164
    if (pNV == NULL)
 
165
        return NULL;
 
166
 
 
167
    if (pzValue == NULL) {
 
168
        pNV->valType = OPARG_TYPE_NONE;
 
169
        pNV->pzName = pNV->v.strVal;
 
170
 
 
171
    } else {
 
172
        pNV->valType = OPARG_TYPE_STRING;
 
173
        if (dataLen > 0)
 
174
            memcpy( pNV->v.strVal, pzValue, dataLen );
 
175
        pNV->v.strVal[dataLen] = NUL;
 
176
        pNV->pzName = pNV->v.strVal + dataLen + 1;
 
177
    }
 
178
 
 
179
    memcpy( pNV->pzName, pzName, nameLen );
 
180
    pNV->pzName[ nameLen ] = NUL;
 
181
    addArgListEntry( pp, pNV );
 
182
    return pNV;
 
183
}
 
184
 
 
185
 
 
186
/*  addBoolValue
 
187
 *
 
188
 *  Associate a name with either a string or no value.
 
189
 */
 
190
static tOptionValue*
 
191
addBoolValue( void** pp, char const* pzName, size_t nameLen,
 
192
                char const* pzValue, size_t dataLen )
 
193
{
 
194
    tOptionValue* pNV;
 
195
    size_t sz = nameLen + sizeof(*pNV) + 1;
 
196
 
 
197
    pNV = AGALOC( sz, "option name/bool value pair" );
 
198
    if (pNV == NULL)
 
199
        return NULL;
 
200
    while (isspace( (int)*pzValue ) && (dataLen > 0)) {
 
201
        dataLen--; pzValue++;
 
202
    }
 
203
    if (dataLen == 0)
 
204
        pNV->v.boolVal = 0;
 
205
    else if (isdigit( (int)*pzValue ))
 
206
        pNV->v.boolVal = atoi( pzValue );
 
207
    else switch (*pzValue) {
 
208
    case 'f':
 
209
    case 'F':
 
210
    case 'n':
 
211
    case 'N':
 
212
        pNV->v.boolVal = 0; break;
 
213
    default:
 
214
        pNV->v.boolVal = 1;
 
215
    }
 
216
 
 
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 );
 
222
    return pNV;
 
223
}
 
224
 
 
225
 
 
226
/*  addNumberValue
 
227
 *
 
228
 *  Associate a name with either a string or no value.
 
229
 */
 
230
static tOptionValue*
 
231
addNumberValue( void** pp, char const* pzName, size_t nameLen,
 
232
                char const* pzValue, size_t dataLen )
 
233
{
 
234
    tOptionValue* pNV;
 
235
    size_t sz = nameLen + sizeof(*pNV) + 1;
 
236
 
 
237
    pNV = AGALOC( sz, "option name/bool value pair" );
 
238
    if (pNV == NULL)
 
239
        return NULL;
 
240
    while (isspace( (int)*pzValue ) && (dataLen > 0)) {
 
241
        dataLen--; pzValue++;
 
242
    }
 
243
    if (dataLen == 0)
 
244
        pNV->v.boolVal = 0;
 
245
    else
 
246
        pNV->v.boolVal = atoi( pzValue );
 
247
 
 
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 );
 
253
    return pNV;
 
254
}
 
255
 
 
256
 
 
257
/*  addNestedValue
 
258
 *
 
259
 *  Associate a name with either a string or no value.
 
260
 */
 
261
static tOptionValue*
 
262
addNestedValue( void** pp, char const* pzName, size_t nameLen,
 
263
                char* pzValue, size_t dataLen )
 
264
{
 
265
    tOptionValue* pNV;
 
266
 
 
267
    if (dataLen == 0) {
 
268
        size_t sz = nameLen + sizeof(*pNV) + 1;
 
269
        pNV = AGALOC( sz, "empty nested value pair" );
 
270
        if (pNV == NULL)
 
271
            return NULL;
 
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;
 
277
 
 
278
    } else {
 
279
        pNV = optionLoadNested( pzValue, pzName, nameLen );
 
280
    }
 
281
 
 
282
    if (pNV != NULL)
 
283
        addArgListEntry( pp, pNV );
 
284
 
 
285
    return pNV;
 
286
}
 
287
 
 
288
 
 
289
/*  scanNameEntry
 
290
 *
 
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.
 
293
 */
 
294
static char const*
 
295
scanNameEntry(char const* pzName, tOptionValue* pRes)
 
296
{
 
297
    tOptionValue* pNV;
 
298
    char const * pzScan = pzName+1;
 
299
    char const * pzVal;
 
300
    size_t       nameLen = 1;
 
301
    size_t       dataLen = 0;
 
302
 
 
303
    while (ISNAMECHAR( (int)*pzScan ))  { pzScan++; nameLen++; }
 
304
 
 
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);
 
309
            return pzScan - 1;
 
310
        }
 
311
    }
 
312
 
 
313
    switch (*pzScan) {
 
314
    case '=':
 
315
    case ':':
 
316
        while (isspace( (int)*++pzScan ))  ;
 
317
        switch (*pzScan) {
 
318
        case ',':  goto comma_char;
 
319
        case '"':
 
320
        case '\'': goto quote_char;
 
321
        case NUL:  goto nul_byte;
 
322
        default:   goto default_char;
 
323
        }
 
324
 
 
325
    case ',':
 
326
    comma_char:
 
327
        pzScan++;
 
328
        /* FALLTHROUGH */
 
329
 
 
330
    case NUL:
 
331
    nul_byte:
 
332
        addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
 
333
        break;
 
334
 
 
335
    case '"':
 
336
    case '\'':
 
337
    quote_char:
 
338
        pzVal = pzScan;
 
339
        pzScan = scanQuotedString( pzScan );
 
340
        dataLen = pzScan - pzVal;
 
341
        pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal,
 
342
                              dataLen );
 
343
        if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
 
344
            ao_string_cook( pNV->v.strVal, NULL );
 
345
        break;
 
346
 
 
347
    default:
 
348
    default_char:
 
349
        /*
 
350
         *  We have found some strange text value.  It ends with a newline
 
351
         *  or a comma.
 
352
         */
 
353
        pzVal = pzScan;
 
354
        for (;;) {
 
355
            char ch = *(pzScan++);
 
356
            switch (ch) {
 
357
            case NUL:
 
358
                pzScan--;
 
359
                dataLen = pzScan - pzVal;
 
360
                goto string_done;
 
361
                /* FALLTHROUGH */
 
362
 
 
363
            case '\n':
 
364
                if (   (pzScan > pzVal + 2)
 
365
                    && (pzScan[-2] == '\\')
 
366
                    && (pzScan[ 0] != NUL))
 
367
                    continue;
 
368
                /* FALLTHROUGH */
 
369
 
 
370
            case ',':
 
371
                dataLen = (pzScan - pzVal) - 1;
 
372
            string_done:
 
373
                pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen,
 
374
                                      pzVal, dataLen );
 
375
                if (pNV != NULL)
 
376
                    removeBackslashes( pNV->v.strVal );
 
377
                goto leave_scan_name;
 
378
            }
 
379
        }
 
380
        break;
 
381
    } leave_scan_name:;
 
382
 
 
383
    return pzScan;
 
384
}
 
385
 
 
386
 
 
387
/*  scanXmlEntry
 
388
 *
 
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.
 
392
 */
 
393
static char const*
 
394
scanXmlEntry( char const* pzName, tOptionValue* pRes )
 
395
{
 
396
    size_t nameLen = 1, valLen = 0;
 
397
    char const*   pzScan = ++pzName;
 
398
    char const*   pzVal;
 
399
    tOptionValue  valu;
 
400
    tOptionValue* pNewVal;
 
401
    tOptionLoadMode save_mode = option_load_mode;
 
402
 
 
403
    if (! isalpha((int)*pzName)) {
 
404
        switch (*pzName) {
 
405
        default:
 
406
            pzName = NULL;
 
407
            break;
 
408
 
 
409
        case '!':
 
410
            pzName = strstr( pzName, "-->" );
 
411
            if (pzName != NULL)
 
412
                pzName += 3;
 
413
            break;
 
414
 
 
415
        case '?':
 
416
            pzName = strchr( pzName, '>' );
 
417
            if (pzName != NULL)
 
418
                pzName++;
 
419
            break;
 
420
        }
 
421
        return pzName;
 
422
    }
 
423
 
 
424
    while (isalpha( (int)*++pzScan ))  nameLen++;
 
425
    if (nameLen > 64)
 
426
        return NULL;
 
427
    valu.valType = OPARG_TYPE_STRING;
 
428
 
 
429
    switch (*pzScan) {
 
430
    case ' ':
 
431
    case '\t':
 
432
        pzScan = parseAttributes(
 
433
            NULL, (char*)pzScan, &option_load_mode, &valu );
 
434
        if (*pzScan == '>') {
 
435
            pzScan++;
 
436
            break;
 
437
        }
 
438
 
 
439
        if (*pzScan != '/') {
 
440
            option_load_mode = save_mode;
 
441
            return NULL;
 
442
        }
 
443
        /* FALLTHROUGH */
 
444
 
 
445
    case '/':
 
446
        if (*++pzScan != '>') {
 
447
            option_load_mode = save_mode;
 
448
            return NULL;
 
449
        }
 
450
        addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
 
451
        option_load_mode = save_mode;
 
452
        return pzScan+2;
 
453
 
 
454
    default:
 
455
        option_load_mode = save_mode;
 
456
        return NULL;
 
457
 
 
458
    case '>':
 
459
        pzScan++;
 
460
        break;
 
461
    }
 
462
 
 
463
    pzVal = pzScan;
 
464
 
 
465
    {
 
466
        char z[68];
 
467
        char* pzD = z;
 
468
        int  ct = nameLen;
 
469
        char const* pzS = pzName;
 
470
 
 
471
        *(pzD++) = '<';
 
472
        *(pzD++) = '/';
 
473
 
 
474
        do  {
 
475
            *(pzD++) = *(pzS++);
 
476
        } while (--ct > 0);
 
477
        *(pzD++) = '>';
 
478
        *pzD = NUL;
 
479
 
 
480
        pzScan = strstr( pzScan, z );
 
481
        if (pzScan == NULL) {
 
482
            option_load_mode = save_mode;
 
483
            return NULL;
 
484
        }
 
485
        valLen = (pzScan - pzVal);
 
486
        pzScan += nameLen + 3;
 
487
        while (isspace(  (int)*pzScan ))  pzScan++;
 
488
    }
 
489
 
 
490
    switch (valu.valType) {
 
491
    case OPARG_TYPE_NONE:
 
492
        addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
 
493
        break;
 
494
 
 
495
    case OPARG_TYPE_STRING:
 
496
        pNewVal = addStringValue(
 
497
            &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
 
498
 
 
499
        if (option_load_mode == OPTION_LOAD_KEEP)
 
500
            break;
 
501
        mungeString( pNewVal->v.strVal, option_load_mode );
 
502
        break;
 
503
 
 
504
    case OPARG_TYPE_BOOLEAN:
 
505
        addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
 
506
        break;
 
507
 
 
508
    case OPARG_TYPE_NUMERIC:
 
509
        addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
 
510
        break;
 
511
 
 
512
    case OPARG_TYPE_HIERARCHY:
 
513
    {
 
514
        char* pz = AGALOC( valLen+1, "hierarchical scan" );
 
515
        if (pz == NULL)
 
516
            break;
 
517
        memcpy( pz, pzVal, valLen );
 
518
        pz[valLen] = NUL;
 
519
        addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen );
 
520
        AGFREE(pz);
 
521
        break;
 
522
    }
 
523
 
 
524
    case OPARG_TYPE_ENUMERATION:
 
525
    case OPARG_TYPE_MEMBERSHIP:
 
526
    default:
 
527
        break;
 
528
    }
 
529
 
 
530
    option_load_mode = save_mode;
 
531
    return pzScan;
 
532
}
 
533
 
 
534
 
 
535
/*  unloadNestedArglist
 
536
 *
 
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.
 
541
 */
 
542
static void
 
543
unloadNestedArglist( tArgList* pAL )
 
544
{
 
545
    int ct = pAL->useCt;
 
546
    tCC** ppNV = pAL->apzArgs;
 
547
 
 
548
    while (ct-- > 0) {
 
549
        tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
 
550
        if (pNV->valType == OPARG_TYPE_HIERARCHY)
 
551
            unloadNestedArglist( pNV->v.nestVal );
 
552
        AGFREE( pNV );
 
553
    }
 
554
 
 
555
    AGFREE( (void*)pAL );
 
556
}
 
557
 
 
558
 
 
559
/*=export_func  optionUnloadNested
 
560
 *
 
561
 * what:  Deallocate the memory for a nested value
 
562
 * arg:   + tOptionValue const * + pOptVal + the hierarchical value +
 
563
 *
 
564
 * doc:
 
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}).
 
568
=*/
 
569
void
 
570
optionUnloadNested( tOptionValue const * pOV )
 
571
{
 
572
    if (pOV == NULL) return;
 
573
    if (pOV->valType != OPARG_TYPE_HIERARCHY) {
 
574
        errno = EINVAL;
 
575
        return;
 
576
    }
 
577
 
 
578
    unloadNestedArglist( pOV->v.nestVal );
 
579
 
 
580
    AGFREE( (void*)pOV );
 
581
}
 
582
 
 
583
 
 
584
/*  sortNestedList
 
585
 *
 
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.
 
589
 */
 
590
static void
 
591
sortNestedList( tArgList* pAL )
 
592
{
 
593
    int ix;
 
594
    int lm = pAL->useCt;
 
595
 
 
596
    /*
 
597
     *  This loop iterates "useCt" - 1 times.
 
598
     */
 
599
    for (ix = 0; ++ix < lm;) {
 
600
        int iy = ix-1;
 
601
        tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
 
602
        tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
 
603
 
 
604
        /*
 
605
         *  For as long as the new entry precedes the "old" entry,
 
606
         *  move the old pointer.  Stop before trying to extract the
 
607
         *  "-1" entry.
 
608
         */
 
609
        while (strcmp( pOldNV->pzName, pNewNV->pzName ) > 0) {
 
610
            pAL->apzArgs[iy+1] = (void*)pOldNV;
 
611
            pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
 
612
            if (iy < 0)
 
613
                break;
 
614
        }
 
615
 
 
616
        /*
 
617
         *  Always store the pointer.  Sometimes it is redundant,
 
618
         *  but the redundancy is cheaper than a test and branch sequence.
 
619
         */
 
620
        pAL->apzArgs[iy+1] = (void*)pNewNV;
 
621
    }
 
622
}
 
623
 
 
624
 
 
625
/* optionLoadNested
 
626
 * private:
 
627
 *
 
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"  +
 
632
 *
 
633
 * ret_type:  tOptionValue*
 
634
 * ret_desc:  An allocated, compound value structure
 
635
 *
 
636
 * doc:
 
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.
 
640
 */
 
641
LOCAL tOptionValue*
 
642
optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
 
643
{
 
644
    tOptionValue* pRes;
 
645
    tArgList*     pAL;
 
646
 
 
647
    /*
 
648
     *  Make sure we have some data and we have space to put what we find.
 
649
     */
 
650
    if (pzTxt == NULL) {
 
651
        errno = EINVAL;
 
652
        return NULL;
 
653
    }
 
654
    while (isspace( (int)*pzTxt ))  pzTxt++;
 
655
    if (*pzTxt == NUL) {
 
656
        errno = ENOENT;
 
657
        return NULL;
 
658
    }
 
659
    pRes = AGALOC( sizeof(*pRes) + nameLen + 1, "nested args" );
 
660
    if (pRes == NULL) {
 
661
        errno = ENOMEM;
 
662
        return NULL;
 
663
    }
 
664
    pRes->valType   = OPARG_TYPE_HIERARCHY;
 
665
    pRes->pzName    = (char*)(pRes + 1);
 
666
    memcpy( pRes->pzName, pzName, nameLen );
 
667
    pRes->pzName[ nameLen ] = NUL;
 
668
 
 
669
    pAL = AGALOC( sizeof(*pAL), "nested arg list" );
 
670
    if (pAL == NULL) {
 
671
        AGFREE( pRes );
 
672
        return NULL;
 
673
    }
 
674
    pRes->v.nestVal = pAL;
 
675
    pAL->useCt   = 0;
 
676
    pAL->allocCt = MIN_ARG_ALLOC_CT;
 
677
 
 
678
    /*
 
679
     *  Scan until we hit a NUL.
 
680
     */
 
681
    do  {
 
682
        while (isspace( (int)*pzTxt ))  pzTxt++;
 
683
        if (isalpha( (int)*pzTxt )) {
 
684
            pzTxt = scanNameEntry( pzTxt, pRes );
 
685
        }
 
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;
 
691
        default:  goto woops;
 
692
        }
 
693
    } while (pzTxt != NULL); scan_done:;
 
694
 
 
695
    pAL = pRes->v.nestVal;
 
696
    if (pAL->useCt != 0) {
 
697
        sortNestedList( pAL );
 
698
        return pRes;
 
699
    }
 
700
 
 
701
 woops:
 
702
    AGFREE( pRes->v.nestVal );
 
703
    AGFREE( pRes );
 
704
    return NULL;
 
705
}
 
706
 
 
707
 
 
708
/*=export_func  optionNestedVal
 
709
 * private:
 
710
 *
 
711
 * what:  parse a hierarchical option argument
 
712
 * arg:   + tOptions* + pOpts    + program options descriptor +
 
713
 * arg:   + tOptDesc* + pOptDesc + the descriptor for this arg +
 
714
 *
 
715
 * doc:
 
716
 *  Nested value was found on the command line
 
717
=*/
 
718
void
 
719
optionNestedVal( tOptions* pOpts, tOptDesc* pOD )
 
720
{
 
721
    tOptionValue* pOV = optionLoadNested(
 
722
        pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
 
723
 
 
724
    if (pOV != NULL)
 
725
        addArgListEntry( &(pOD->optCookie), (void*)pOV );
 
726
}
 
727
/*
 
728
 * Local Variables:
 
729
 * mode: C
 
730
 * c-file-style: "stroustrup"
 
731
 * indent-tabs-mode: nil
 
732
 * End:
 
733
 * end of autoopts/nested.c */