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

« back to all changes in this revision

Viewing changes to libopts/configfile.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
 *  $Id: configfile.c,v 1.21 2007/04/15 19:01:18 bkorb Exp $
 
3
 *  Time-stamp:      "2007-04-15 11:22:46 bkorb"
 
4
 *
 
5
 *  configuration/rc/ini file handling.
 
6
 */
 
7
 
 
8
/*
 
9
 *  Automated Options copyright 1992-2007 Bruce Korb
 
10
 *
 
11
 *  Automated Options is free software.
 
12
 *  You may redistribute it and/or modify it under the terms of the
 
13
 *  GNU General Public License, as published by the Free Software
 
14
 *  Foundation; either version 2, or (at your option) any later version.
 
15
 *
 
16
 *  Automated Options is distributed in the hope that it will be useful,
 
17
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
19
 *  GNU General Public License for more details.
 
20
 *
 
21
 *  You should have received a copy of the GNU General Public License
 
22
 *  along with Automated Options.  See the file "COPYING".  If not,
 
23
 *  write to:  The Free Software Foundation, Inc.,
 
24
 *             51 Franklin Street, Fifth Floor,
 
25
 *             Boston, MA  02110-1301, USA.
 
26
 *
 
27
 * As a special exception, Bruce Korb gives permission for additional
 
28
 * uses of the text contained in his release of AutoOpts.
 
29
 *
 
30
 * The exception is that, if you link the AutoOpts library with other
 
31
 * files to produce an executable, this does not by itself cause the
 
32
 * resulting executable to be covered by the GNU General Public License.
 
33
 * Your use of that executable is in no way restricted on account of
 
34
 * linking the AutoOpts library code into it.
 
35
 *
 
36
 * This exception does not however invalidate any other reasons why
 
37
 * the executable file might be covered by the GNU General Public License.
 
38
 *
 
39
 * This exception applies only to the code released by Bruce Korb under
 
40
 * the name AutoOpts.  If you copy code from other sources under the
 
41
 * General Public License into a copy of AutoOpts, as the General Public
 
42
 * License permits, the exception does not apply to the code that you add
 
43
 * in this way.  To avoid misleading anyone as to the status of such
 
44
 * modified files, you must delete this exception notice from them.
 
45
 *
 
46
 * If you write modifications of your own for AutoOpts, it is your choice
 
47
 * whether to permit this exception to apply to your modifications.
 
48
 * If you do not wish that, delete this exception notice.
 
49
 */
 
50
 
 
51
/* = = = START-STATIC-FORWARD = = = */
 
52
/* static forward declarations maintained by :mkfwd */
 
53
static void
 
54
filePreset(
 
55
    tOptions*     pOpts,
 
56
    char const*   pzFileName,
 
57
    int           direction );
 
58
 
 
59
static char*
 
60
handleComment( char* pzText );
 
61
 
 
62
static char*
 
63
handleConfig(
 
64
    tOptions*     pOpts,
 
65
    tOptState*    pOS,
 
66
    char*         pzText,
 
67
    int           direction );
 
68
 
 
69
static char*
 
70
handleDirective(
 
71
    tOptions*     pOpts,
 
72
    char*         pzText );
 
73
 
 
74
static char*
 
75
handleProgramSection(
 
76
    tOptions*     pOpts,
 
77
    char*         pzText );
 
78
 
 
79
static char*
 
80
handleStructure(
 
81
    tOptions*     pOpts,
 
82
    tOptState*    pOS,
 
83
    char*         pzText,
 
84
    int           direction );
 
85
 
 
86
static char*
 
87
parseKeyWordType(
 
88
    tOptions*     pOpts,
 
89
    char*         pzText,
 
90
    tOptionValue* pType );
 
91
 
 
92
static char*
 
93
parseLoadMode(
 
94
    char*               pzText,
 
95
    tOptionLoadMode*    pMode );
 
96
 
 
97
static char*
 
98
parseSetMemType(
 
99
    tOptions*     pOpts,
 
100
    char*         pzText,
 
101
    tOptionValue* pType );
 
102
 
 
103
static char*
 
104
parseValueType(
 
105
    char*         pzText,
 
106
    tOptionValue* pType );
 
107
 
 
108
static char*
 
109
skipUnknown( char* pzText );
 
110
/* = = = END-STATIC-FORWARD = = = */
 
111
 
 
112
 
 
113
/*=export_func  configFileLoad
 
114
 *
 
115
 * what:  parse a configuration file
 
116
 * arg:   + char const*     + pzFile + the file to load +
 
117
 *
 
118
 * ret_type:  const tOptionValue*
 
119
 * ret_desc:  An allocated, compound value structure
 
120
 *
 
121
 * doc:
 
122
 *  This routine will load a named configuration file and parse the
 
123
 *  text as a hierarchically valued option.  The option descriptor
 
124
 *  created from an option definition file is not used via this interface.
 
125
 *  The returned value is "named" with the input file name and is of
 
126
 *  type "@code{OPARG_TYPE_HIERARCHY}".  It may be used in calls to
 
127
 *  @code{optionGetValue()}, @code{optionNextValue()} and
 
128
 *  @code{optionUnloadNested()}.
 
129
 *
 
130
 * err:
 
131
 *  If the file cannot be loaded or processed, @code{NULL} is returned and
 
132
 *  @var{errno} is set.  It may be set by a call to either @code{open(2)}
 
133
 *  @code{mmap(2)} or other file system calls, or it may be:
 
134
 *  @itemize @bullet
 
135
 *  @item
 
136
 *  @code{ENOENT} - the file was empty.
 
137
 *  @item
 
138
 *  @code{EINVAL} - the file contents are invalid -- not properly formed.
 
139
 *  @item
 
140
 *  @code{ENOMEM} - not enough memory to allocate the needed structures.
 
141
 *  @end itemize
 
142
=*/
 
143
const tOptionValue*
 
144
configFileLoad( char const* pzFile )
 
145
{
 
146
    tmap_info_t   cfgfile;
 
147
    tOptionValue* pRes = NULL;
 
148
    tOptionLoadMode save_mode = option_load_mode;
 
149
 
 
150
    char* pzText =
 
151
        text_mmap( pzFile, PROT_READ, MAP_PRIVATE, &cfgfile );
 
152
 
 
153
    if (TEXT_MMAP_FAILED_ADDR(pzText))
 
154
        return NULL; /* errno is set */
 
155
 
 
156
    option_load_mode = OPTION_LOAD_COOKED;
 
157
    pRes = optionLoadNested(pzText, pzFile, strlen(pzFile));
 
158
 
 
159
    if (pRes == NULL) {
 
160
        int err = errno;
 
161
        text_munmap( &cfgfile );
 
162
        errno = err;
 
163
    } else
 
164
        text_munmap( &cfgfile );
 
165
 
 
166
    option_load_mode = save_mode;
 
167
    return pRes;
 
168
}
 
169
 
 
170
 
 
171
/*=export_func  optionFindValue
 
172
 *
 
173
 * what:  find a hierarcicaly valued option instance
 
174
 * arg:   + const tOptDesc* + pOptDesc + an option with a nested arg type +
 
175
 * arg:   + char const*     + name     + name of value to find +
 
176
 * arg:   + char const*     + value    + the matching value    +
 
177
 *
 
178
 * ret_type:  const tOptionValue*
 
179
 * ret_desc:  a compound value structure
 
180
 *
 
181
 * doc:
 
182
 *  This routine will find an entry in a nested value option or configurable.
 
183
 *  It will search through the list and return a matching entry.
 
184
 *
 
185
 * err:
 
186
 *  The returned result is NULL and errno is set:
 
187
 *  @itemize @bullet
 
188
 *  @item
 
189
 *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
 
190
 *  hierarchical option value.
 
191
 *  @item
 
192
 *  @code{ENOENT} - no entry matched the given name.
 
193
 *  @end itemize
 
194
=*/
 
195
const tOptionValue*
 
196
optionFindValue( const tOptDesc* pOptDesc,
 
197
                 char const* pzName, char const* pzVal )
 
198
{
 
199
    const tOptionValue* pRes = NULL;
 
200
 
 
201
    if (  (pOptDesc == NULL)
 
202
       || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY))  {
 
203
        errno = EINVAL;
 
204
    }
 
205
 
 
206
    else if (pOptDesc->optCookie == NULL) {
 
207
        errno = ENOENT;
 
208
    }
 
209
 
 
210
    else do {
 
211
        tArgList* pAL = pOptDesc->optCookie;
 
212
        int    ct   = pAL->useCt;
 
213
        void** ppOV = (void**)(pAL->apzArgs);
 
214
 
 
215
        if (ct == 0) {
 
216
            errno = ENOENT;
 
217
            break;
 
218
        }
 
219
 
 
220
        if (pzName == NULL) {
 
221
            pRes = (tOptionValue*)*ppOV;
 
222
            break;
 
223
        }
 
224
 
 
225
        while (--ct >= 0) {
 
226
            const tOptionValue* pOV = *(ppOV++);
 
227
            const tOptionValue* pRV = optionGetValue( pOV, pzName );
 
228
 
 
229
            if (pRV == NULL)
 
230
                continue;
 
231
 
 
232
            if (pzVal == NULL) {
 
233
                pRes = pOV;
 
234
                break;
 
235
            }
 
236
        }
 
237
        if (pRes == NULL)
 
238
            errno = ENOENT;
 
239
    } while (0);
 
240
 
 
241
    return pRes;
 
242
}
 
243
 
 
244
 
 
245
/*=export_func  optionFindNextValue
 
246
 *
 
247
 * what:  find a hierarcicaly valued option instance
 
248
 * arg:   + const tOptDesc* + pOptDesc + an option with a nested arg type +
 
249
 * arg:   + const tOptionValue* + pPrevVal + the last entry +
 
250
 * arg:   + char const*     + name     + name of value to find +
 
251
 * arg:   + char const*     + value    + the matching value    +
 
252
 *
 
253
 * ret_type:  const tOptionValue*
 
254
 * ret_desc:  a compound value structure
 
255
 *
 
256
 * doc:
 
257
 *  This routine will find the next entry in a nested value option or
 
258
 *  configurable.  It will search through the list and return the next entry
 
259
 *  that matches the criteria.
 
260
 *
 
261
 * err:
 
262
 *  The returned result is NULL and errno is set:
 
263
 *  @itemize @bullet
 
264
 *  @item
 
265
 *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
 
266
 *  hierarchical option value.
 
267
 *  @item
 
268
 *  @code{ENOENT} - no entry matched the given name.
 
269
 *  @end itemize
 
270
=*/
 
271
const tOptionValue*
 
272
optionFindNextValue( const tOptDesc* pOptDesc, const tOptionValue* pPrevVal,
 
273
                 char const* pzName, char const* pzVal )
 
274
{
 
275
    int foundOldVal = 0;
 
276
    tOptionValue* pRes = NULL;
 
277
 
 
278
    if (  (pOptDesc == NULL)
 
279
       || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY))  {
 
280
        errno = EINVAL;
 
281
    }
 
282
 
 
283
    else if (pOptDesc->optCookie == NULL) {
 
284
        errno = ENOENT;
 
285
    }
 
286
 
 
287
    else do {
 
288
        tArgList* pAL = pOptDesc->optCookie;
 
289
        int    ct   = pAL->useCt;
 
290
        void** ppOV = (void**)pAL->apzArgs;
 
291
 
 
292
        if (ct == 0) {
 
293
            errno = ENOENT;
 
294
            break;
 
295
        }
 
296
 
 
297
        while (--ct >= 0) {
 
298
            tOptionValue* pOV = *(ppOV++);
 
299
            if (foundOldVal) {
 
300
                pRes = pOV;
 
301
                break;
 
302
            }
 
303
            if (pOV == pPrevVal)
 
304
                foundOldVal = 1;
 
305
        }
 
306
        if (pRes == NULL)
 
307
            errno = ENOENT;
 
308
    } while (0);
 
309
 
 
310
    return pRes;
 
311
}
 
312
 
 
313
 
 
314
/*=export_func  optionGetValue
 
315
 *
 
316
 * what:  get a specific value from a hierarcical list
 
317
 * arg:   + const tOptionValue* + pOptValue + a hierarchcal value +
 
318
 * arg:   + char const*   + valueName + name of value to get +
 
319
 *
 
320
 * ret_type:  const tOptionValue*
 
321
 * ret_desc:  a compound value structure
 
322
 *
 
323
 * doc:
 
324
 *  This routine will find an entry in a nested value option or configurable.
 
325
 *  If "valueName" is NULL, then the first entry is returned.  Otherwise,
 
326
 *  the first entry with a name that exactly matches the argument will be
 
327
 *  returned.
 
328
 *
 
329
 * err:
 
330
 *  The returned result is NULL and errno is set:
 
331
 *  @itemize @bullet
 
332
 *  @item
 
333
 *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
 
334
 *  hierarchical option value.
 
335
 *  @item
 
336
 *  @code{ENOENT} - no entry matched the given name.
 
337
 *  @end itemize
 
338
=*/
 
339
const tOptionValue*
 
340
optionGetValue( const tOptionValue* pOld, char const* pzValName )
 
341
{
 
342
    tArgList*     pAL;
 
343
    tOptionValue* pRes = NULL;
 
344
 
 
345
    if ((pOld == NULL) || (pOld->valType != OPARG_TYPE_HIERARCHY)) {
 
346
        errno = EINVAL;
 
347
        return NULL;
 
348
    }
 
349
    pAL = pOld->v.nestVal;
 
350
 
 
351
    if (pAL->useCt > 0) {
 
352
        int    ct    = pAL->useCt;
 
353
        void** papOV = (void**)(pAL->apzArgs);
 
354
 
 
355
        if (pzValName == NULL) {
 
356
            pRes = (tOptionValue*)*papOV;
 
357
        }
 
358
 
 
359
        else do {
 
360
            tOptionValue* pOV = *(papOV++);
 
361
            if (strcmp( pOV->pzName, pzValName ) == 0) {
 
362
                pRes = pOV;
 
363
                break;
 
364
            }
 
365
        } while (--ct > 0);
 
366
    }
 
367
    if (pRes == NULL)
 
368
        errno = ENOENT;
 
369
    return pRes;
 
370
}
 
371
 
 
372
 
 
373
/*=export_func  optionNextValue
 
374
 *
 
375
 * what:  get the next value from a hierarchical list
 
376
 * arg:   + const tOptionValue* + pOptValue + a hierarchcal list value +
 
377
 * arg:   + const tOptionValue* + pOldValue + a value from this list   +
 
378
 *
 
379
 * ret_type:  const tOptionValue*
 
380
 * ret_desc:  a compound value structure
 
381
 *
 
382
 * doc:
 
383
 *  This routine will return the next entry after the entry passed in.  At the
 
384
 *  end of the list, NULL will be returned.  If the entry is not found on the
 
385
 *  list, NULL will be returned and "@var{errno}" will be set to EINVAL.
 
386
 *  The "@var{pOldValue}" must have been gotten from a prior call to this
 
387
 *  routine or to "@code{opitonGetValue()}".
 
388
 *
 
389
 * err:
 
390
 *  The returned result is NULL and errno is set:
 
391
 *  @itemize @bullet
 
392
 *  @item
 
393
 *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
 
394
 *  hierarchical option value or @code{pOldValue} does not point to a
 
395
 *  member of that option value.
 
396
 *  @item
 
397
 *  @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry.
 
398
 *  @end itemize
 
399
=*/
 
400
tOptionValue const *
 
401
optionNextValue(tOptionValue const * pOVList,tOptionValue const * pOldOV )
 
402
{
 
403
    tArgList*     pAL;
 
404
    tOptionValue* pRes = NULL;
 
405
    int           err  = EINVAL;
 
406
 
 
407
    if ((pOVList == NULL) || (pOVList->valType != OPARG_TYPE_HIERARCHY)) {
 
408
        errno = EINVAL;
 
409
        return NULL;
 
410
    }
 
411
    pAL = pOVList->v.nestVal;
 
412
    {
 
413
        int    ct    = pAL->useCt;
 
414
        void** papNV = (void**)(pAL->apzArgs);
 
415
 
 
416
        while (ct-- > 0) {
 
417
            tOptionValue* pNV = *(papNV++);
 
418
            if (pNV == pOldOV) {
 
419
                if (ct == 0) {
 
420
                    err = ENOENT;
 
421
 
 
422
                } else {
 
423
                    err  = 0;
 
424
                    pRes = (tOptionValue*)*papNV;
 
425
                }
 
426
                break;
 
427
            }
 
428
        }
 
429
    }
 
430
    if (err != 0)
 
431
        errno = err;
 
432
    return pRes;
 
433
}
 
434
 
 
435
 
 
436
/*  filePreset
 
437
 *
 
438
 *  Load a file containing presetting information (a configuration file).
 
439
 */
 
440
static void
 
441
filePreset(
 
442
    tOptions*     pOpts,
 
443
    char const*   pzFileName,
 
444
    int           direction )
 
445
{
 
446
    tmap_info_t   cfgfile;
 
447
    tOptState     st = OPTSTATE_INITIALIZER(PRESET);
 
448
    char*         pzFileText =
 
449
        text_mmap( pzFileName, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile );
 
450
 
 
451
    if (TEXT_MMAP_FAILED_ADDR(pzFileText))
 
452
        return;
 
453
 
 
454
    if (direction == DIRECTION_CALLED) {
 
455
        st.flags  = OPTST_DEFINED;
 
456
        direction = DIRECTION_PROCESS;
 
457
    }
 
458
 
 
459
    /*
 
460
     *  IF this is called via "optionProcess", then we are presetting.
 
461
     *  This is the default and the PRESETTING bit will be set.
 
462
     *  If this is called via "optionFileLoad", then the bit is not set
 
463
     *  and we consider stuff set herein to be "set" by the client program.
 
464
     */
 
465
    if ((pOpts->fOptSet & OPTPROC_PRESETTING) == 0)
 
466
        st.flags = OPTST_SET;
 
467
 
 
468
    do  {
 
469
        while (isspace( (int)*pzFileText ))  pzFileText++;
 
470
 
 
471
        if (isalpha( (int)*pzFileText )) {
 
472
            pzFileText = handleConfig( pOpts, &st, pzFileText, direction );
 
473
 
 
474
        } else switch (*pzFileText) {
 
475
        case '<':
 
476
            if (isalpha( (int)pzFileText[1] ))
 
477
                pzFileText = handleStructure(pOpts, &st, pzFileText, direction);
 
478
 
 
479
            else switch (pzFileText[1]) {
 
480
            case '?':
 
481
                pzFileText = handleDirective( pOpts, pzFileText );
 
482
                break;
 
483
 
 
484
            case '!':
 
485
                pzFileText = handleComment( pzFileText );
 
486
                break;
 
487
 
 
488
            case '/':
 
489
                pzFileText = strchr( pzFileText+2, '>' );
 
490
                if (pzFileText++ != NULL)
 
491
                    break;
 
492
 
 
493
            default:
 
494
                goto all_done;
 
495
            }
 
496
            break;
 
497
 
 
498
        case '[':
 
499
            pzFileText = handleProgramSection( pOpts, pzFileText );
 
500
            break;
 
501
 
 
502
        case '#':
 
503
            pzFileText = strchr( pzFileText+1, '\n' );
 
504
            break;
 
505
 
 
506
        default:
 
507
            goto all_done; /* invalid format */
 
508
        }
 
509
    } while (pzFileText != NULL);
 
510
 
 
511
 all_done:
 
512
    text_munmap( &cfgfile );
 
513
}
 
514
 
 
515
 
 
516
/*  handleComment
 
517
 *
 
518
 *  "pzText" points to a "<!" sequence.
 
519
 *  Theoretically, we should ensure that it begins with "<!--",
 
520
 *  but actually I don't care that much.  It ends with "-->".
 
521
 */
 
522
static char*
 
523
handleComment( char* pzText )
 
524
{
 
525
    char* pz = strstr( pzText, "-->" );
 
526
    if (pz != NULL)
 
527
        pz += 3;
 
528
    return pz;
 
529
}
 
530
 
 
531
 
 
532
/*  handleConfig
 
533
 *
 
534
 *  "pzText" points to the start of some value name.
 
535
 *  The end of the entry is the end of the line that is not preceded by
 
536
 *  a backslash escape character.  The string value is always processed
 
537
 *  in "cooked" mode.
 
538
 */
 
539
static char*
 
540
handleConfig(
 
541
    tOptions*     pOpts,
 
542
    tOptState*    pOS,
 
543
    char*         pzText,
 
544
    int           direction )
 
545
{
 
546
    char* pzName = pzText++;
 
547
    char* pzEnd  = strchr( pzText, '\n' );
 
548
 
 
549
    if (pzEnd == NULL)
 
550
        return pzText + strlen(pzText);
 
551
 
 
552
    while (ISNAMECHAR( (int)*pzText ))  pzText++;
 
553
    while (isspace( (int)*pzText )) pzText++;
 
554
    if (pzText > pzEnd) {
 
555
    name_only:
 
556
        *pzEnd++ = NUL;
 
557
        loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED );
 
558
        return pzEnd;
 
559
    }
 
560
 
 
561
    /*
 
562
     *  Either the first character after the name is a ':' or '=',
 
563
     *  or else we must have skipped over white space.  Anything else
 
564
     *  is an invalid format and we give up parsing the text.
 
565
     */
 
566
    if ((*pzText == '=') || (*pzText == ':')) {
 
567
        while (isspace( (int)*++pzText ))   ;
 
568
        if (pzText > pzEnd)
 
569
            goto name_only;
 
570
    } else if (! isspace((int)pzText[-1]))
 
571
        return NULL;
 
572
 
 
573
    /*
 
574
     *  IF the value is continued, remove the backslash escape and push "pzEnd"
 
575
     *  on to a newline *not* preceded by a backslash.
 
576
     */
 
577
    if (pzEnd[-1] == '\\') {
 
578
        char* pcD = pzEnd-1;
 
579
        char* pcS = pzEnd;
 
580
 
 
581
        for (;;) {
 
582
            char ch = *(pcS++);
 
583
            switch (ch) {
 
584
            case NUL:
 
585
                pcS = NULL;
 
586
 
 
587
            case '\n':
 
588
                *pcD = NUL;
 
589
                pzEnd = pcS;
 
590
                goto copy_done;
 
591
 
 
592
            case '\\':
 
593
                if (*pcS == '\n') {
 
594
                    ch = *(pcS++);
 
595
                }
 
596
                /* FALLTHROUGH */
 
597
            default:
 
598
                *(pcD++) = ch;
 
599
            }
 
600
        } copy_done:;
 
601
 
 
602
    } else {
 
603
        /*
 
604
         *  The newline was not preceded by a backslash.  NUL it out
 
605
         */
 
606
        *(pzEnd++) = NUL;
 
607
    }
 
608
 
 
609
    /*
 
610
     *  "pzName" points to what looks like text for one option/configurable.
 
611
     *  It is NUL terminated.  Process it.
 
612
     */
 
613
    loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED );
 
614
 
 
615
    return pzEnd;
 
616
}
 
617
 
 
618
 
 
619
/*  handleDirective
 
620
 *
 
621
 *  "pzText" points to a "<?" sequence.
 
622
 *  For the moment, we only handle "<?program" directives.
 
623
 */
 
624
static char*
 
625
handleDirective(
 
626
    tOptions*     pOpts,
 
627
    char*         pzText )
 
628
{
 
629
    char   ztitle[32] = "<?";
 
630
    size_t title_len = strlen( zProg );
 
631
    size_t name_len;
 
632
 
 
633
    if (  (strncmp( pzText+2, zProg, title_len ) != 0)
 
634
       || (! isspace( (int)pzText[title_len+2] )) )  {
 
635
        pzText = strchr( pzText+2, '>' );
 
636
        if (pzText != NULL)
 
637
            pzText++;
 
638
        return pzText;
 
639
    }
 
640
 
 
641
    name_len = strlen( pOpts->pzProgName );
 
642
    strcpy( ztitle+2, zProg );
 
643
    title_len += 2;
 
644
 
 
645
    do  {
 
646
        pzText += title_len;
 
647
 
 
648
        if (isspace((int)*pzText)) {
 
649
            while (isspace((int)*pzText))  pzText++;
 
650
            if (  (strneqvcmp( pzText, pOpts->pzProgName, (int)name_len) == 0)
 
651
               && (pzText[name_len] == '>'))  {
 
652
                pzText += name_len + 1;
 
653
                break;
 
654
            }
 
655
        }
 
656
 
 
657
        pzText = strstr( pzText, ztitle );
 
658
    } while (pzText != NULL);
 
659
 
 
660
    return pzText;
 
661
}
 
662
 
 
663
 
 
664
/*  handleProgramSection
 
665
 *
 
666
 *  "pzText" points to a '[' character.
 
667
 *  The "traditional" [PROG_NAME] segmentation of the config file.
 
668
 *  Do not ever mix with the "<?program prog-name>" variation.
 
669
 */
 
670
static char*
 
671
handleProgramSection(
 
672
    tOptions*     pOpts,
 
673
    char*         pzText )
 
674
{
 
675
    size_t len = strlen( pOpts->pzPROGNAME );
 
676
    if (   (strncmp( pzText+1, pOpts->pzPROGNAME, len ) == 0)
 
677
        && (pzText[len+1] == ']'))
 
678
        return strchr( pzText + len + 2, '\n' );
 
679
 
 
680
    if (len > 16)
 
681
        return NULL;
 
682
 
 
683
    {
 
684
        char z[24];
 
685
        sprintf( z, "[%s]", pOpts->pzPROGNAME );
 
686
        pzText = strstr( pzText, z );
 
687
    }
 
688
 
 
689
    if (pzText != NULL)
 
690
        pzText = strchr( pzText, '\n' );
 
691
    return pzText;
 
692
}
 
693
 
 
694
 
 
695
/*  handleStructure
 
696
 *
 
697
 *  "pzText" points to a '<' character, followed by an alpha.
 
698
 *  The end of the entry is either the "/>" following the name, or else a
 
699
 *  "</name>" string.
 
700
 */
 
701
static char*
 
702
handleStructure(
 
703
    tOptions*     pOpts,
 
704
    tOptState*    pOS,
 
705
    char*         pzText,
 
706
    int           direction )
 
707
{
 
708
    tOptionLoadMode mode = option_load_mode;
 
709
    tOptionValue     valu;
 
710
 
 
711
    char* pzName = ++pzText;
 
712
    char* pzData;
 
713
    char* pcNulPoint;
 
714
 
 
715
    while (ISNAMECHAR( *pzText ))  pzText++;
 
716
    pcNulPoint = pzText;
 
717
    valu.valType = OPARG_TYPE_STRING;
 
718
 
 
719
    switch (*pzText) {
 
720
    case ' ':
 
721
    case '\t':
 
722
        pzText = parseAttributes( pOpts, pzText, &mode, &valu );
 
723
        if (*pzText == '>')
 
724
            break;
 
725
        if (*pzText != '/')
 
726
            return NULL;
 
727
        /* FALLTHROUGH */
 
728
 
 
729
    case '/':
 
730
        if (pzText[1] != '>')
 
731
            return NULL;
 
732
        *pzText = NUL;
 
733
        pzText += 2;
 
734
        loadOptionLine( pOpts, pOS, pzName, direction, mode );
 
735
        return pzText;
 
736
 
 
737
    case '>':
 
738
        break;
 
739
 
 
740
    default:
 
741
        pzText = strchr( pzText, '>');
 
742
        if (pzText != NULL)
 
743
            pzText++;
 
744
        return pzText;
 
745
    }
 
746
 
 
747
    /*
 
748
     *  If we are here, we have a value.  "pzText" points to a closing angle
 
749
     *  bracket.  Separate the name from the value for a moment.
 
750
     */
 
751
    *pcNulPoint = NUL;
 
752
    pzData = ++pzText;
 
753
 
 
754
    /*
 
755
     *  Find the end of the option text and NUL terminate it
 
756
     */
 
757
    {
 
758
        char   z[64], *pz = z;
 
759
        size_t len = strlen(pzName) + 4;
 
760
        if (len > sizeof(z))
 
761
            pz = AGALOC(len, "scan name");
 
762
 
 
763
        sprintf( pz, "</%s>", pzName );
 
764
        *pzText = ' ';
 
765
        pzText = strstr( pzText, pz );
 
766
        if (pz != z) AGFREE(pz);
 
767
 
 
768
        if (pzText == NULL)
 
769
            return pzText;
 
770
 
 
771
        *pzText = NUL;
 
772
 
 
773
        pzText += len-1;
 
774
    }
 
775
 
 
776
    /*
 
777
     *  Rejoin the name and value for parsing by "loadOptionLine()".
 
778
     *  Erase any attributes parsed by "parseAttributes()".
 
779
     */
 
780
    memset(pcNulPoint, ' ', pzData - pcNulPoint);
 
781
 
 
782
    /*
 
783
     *  "pzName" points to what looks like text for one option/configurable.
 
784
     *  It is NUL terminated.  Process it.
 
785
     */
 
786
    loadOptionLine( pOpts, pOS, pzName, direction, mode );
 
787
 
 
788
    return pzText;
 
789
}
 
790
 
 
791
 
 
792
/*  internalFileLoad
 
793
 *
 
794
 *  Load a configuration file.  This may be invoked either from
 
795
 *  scanning the "homerc" list, or from a specific file request.
 
796
 *  (see "optionFileLoad()", the implementation for --load-opts)
 
797
 */
 
798
LOCAL void
 
799
internalFileLoad( tOptions* pOpts )
 
800
{
 
801
    int     idx;
 
802
    int     inc = DIRECTION_PRESET;
 
803
    char    zFileName[ AG_PATH_MAX+1 ];
 
804
 
 
805
    if (pOpts->papzHomeList == NULL)
 
806
        return;
 
807
 
 
808
    /*
 
809
     *  Find the last RC entry (highest priority entry)
 
810
     */
 
811
    for (idx = 0; pOpts->papzHomeList[ idx+1 ] != NULL; ++idx)  ;
 
812
 
 
813
    /*
 
814
     *  For every path in the home list, ...  *TWICE* We start at the last
 
815
     *  (highest priority) entry, work our way down to the lowest priority,
 
816
     *  handling the immediate options.
 
817
     *  Then we go back up, doing the normal options.
 
818
     */
 
819
    for (;;) {
 
820
        struct stat StatBuf;
 
821
        cch_t*  pzPath;
 
822
 
 
823
        /*
 
824
         *  IF we've reached the bottom end, change direction
 
825
         */
 
826
        if (idx < 0) {
 
827
            inc = DIRECTION_PROCESS;
 
828
            idx = 0;
 
829
        }
 
830
 
 
831
        pzPath = pOpts->papzHomeList[ idx ];
 
832
 
 
833
        /*
 
834
         *  IF we've reached the top end, bail out
 
835
         */
 
836
        if (pzPath == NULL)
 
837
            break;
 
838
 
 
839
        idx += inc;
 
840
 
 
841
        if (! optionMakePath( zFileName, (int)sizeof(zFileName),
 
842
                              pzPath, pOpts->pzProgPath ))
 
843
            continue;
 
844
 
 
845
        /*
 
846
         *  IF the file name we constructed is a directory,
 
847
         *  THEN append the Resource Configuration file name
 
848
         *  ELSE we must have the complete file name
 
849
         */
 
850
        if (stat( zFileName, &StatBuf ) != 0)
 
851
            continue; /* bogus name - skip the home list entry */
 
852
 
 
853
        if (S_ISDIR( StatBuf.st_mode )) {
 
854
            size_t len = strlen( zFileName );
 
855
            char* pz;
 
856
 
 
857
            if (len + 1 + strlen( pOpts->pzRcName ) >= sizeof( zFileName ))
 
858
                continue;
 
859
 
 
860
            pz = zFileName + len;
 
861
            if (pz[-1] != DIRCH)
 
862
                *(pz++) = DIRCH;
 
863
            strcpy( pz, pOpts->pzRcName );
 
864
        }
 
865
 
 
866
        filePreset( pOpts, zFileName, inc );
 
867
 
 
868
        /*
 
869
         *  IF we are now to skip config files AND we are presetting,
 
870
         *  THEN change direction.  We must go the other way.
 
871
         */
 
872
        {
 
873
            tOptDesc * pOD = pOpts->pOptDesc + pOpts->specOptIdx.save_opts+1;
 
874
            if (DISABLED_OPT(pOD) && PRESETTING(inc)) {
 
875
                idx -= inc;  /* go back and reprocess current file */
 
876
                inc =  DIRECTION_PROCESS;
 
877
            }
 
878
        }
 
879
    } /* twice for every path in the home list, ... */
 
880
}
 
881
 
 
882
 
 
883
/*=export_func optionFileLoad
 
884
 *
 
885
 * what: Load the locatable config files, in order
 
886
 *
 
887
 * arg:  + tOptions*   + pOpts  + program options descriptor +
 
888
 * arg:  + char const* + pzProg + program name +
 
889
 *
 
890
 * ret_type:  int
 
891
 * ret_desc:  0 -> SUCCESS, -1 -> FAILURE
 
892
 *
 
893
 * doc:
 
894
 *
 
895
 * This function looks in all the specified directories for a configuration
 
896
 * file ("rc" file or "ini" file) and processes any found twice.  The first
 
897
 * time through, they are processed in reverse order (last file first).  At
 
898
 * that time, only "immediate action" configurables are processed.  For
 
899
 * example, if the last named file specifies not processing any more
 
900
 * configuration files, then no more configuration files will be processed.
 
901
 * Such an option in the @strong{first} named directory will have no effect.
 
902
 *
 
903
 * Once the immediate action configurables have been handled, then the
 
904
 * directories are handled in normal, forward order.  In that way, later
 
905
 * config files can override the settings of earlier config files.
 
906
 *
 
907
 * See the AutoOpts documentation for a thorough discussion of the
 
908
 * config file format.
 
909
 *
 
910
 * Configuration files not found or not decipherable are simply ignored.
 
911
 *
 
912
 * err:  Returns the value, "-1" if the program options descriptor
 
913
 *       is out of date or indecipherable.  Otherwise, the value "0" will
 
914
 *       always be returned.
 
915
=*/
 
916
int
 
917
optionFileLoad( tOptions* pOpts, char const* pzProgram )
 
918
{
 
919
    if (! SUCCESSFUL( validateOptionsStruct( pOpts, pzProgram )))
 
920
        return -1;
 
921
 
 
922
    pOpts->pzProgName = pzProgram;
 
923
    internalFileLoad( pOpts );
 
924
    return 0;
 
925
}
 
926
 
 
927
 
 
928
/*=export_func  optionLoadOpt
 
929
 * private:
 
930
 *
 
931
 * what:  Load an option rc/ini file
 
932
 * arg:   + tOptions* + pOpts    + program options descriptor +
 
933
 * arg:   + tOptDesc* + pOptDesc + the descriptor for this arg +
 
934
 *
 
935
 * doc:
 
936
 *  Processes the options found in the file named with
 
937
 *  pOptDesc->optArg.argString.
 
938
=*/
 
939
void
 
940
optionLoadOpt( tOptions* pOpts, tOptDesc* pOptDesc )
 
941
{
 
942
    /*
 
943
     *  IF the option is not being disabled, THEN load the file.  There must
 
944
     *  be a file.  (If it is being disabled, then the disablement processing
 
945
     *  already took place.  It must be done to suppress preloading of ini/rc
 
946
     *  files.)
 
947
     */
 
948
    if (! DISABLED_OPT( pOptDesc )) {
 
949
        struct stat sb;
 
950
        if (stat( pOptDesc->optArg.argString, &sb ) != 0) {
 
951
            if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
 
952
                return;
 
953
 
 
954
            fprintf( stderr, zFSErrOptLoad, errno, strerror( errno ),
 
955
                     pOptDesc->optArg.argString );
 
956
            exit(EX_NOINPUT);
 
957
            /* NOT REACHED */
 
958
        }
 
959
 
 
960
        if (! S_ISREG( sb.st_mode )) {
 
961
            if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
 
962
                return;
 
963
 
 
964
            fprintf( stderr, zNotFile, pOptDesc->optArg.argString );
 
965
            exit(EX_NOINPUT);
 
966
            /* NOT REACHED */
 
967
        }
 
968
 
 
969
        filePreset(pOpts, pOptDesc->optArg.argString, DIRECTION_CALLED);
 
970
    }
 
971
}
 
972
 
 
973
 
 
974
/*  parseAttributes
 
975
 *
 
976
 *  Parse the various attributes of an XML-styled config file entry
 
977
 */
 
978
LOCAL char*
 
979
parseAttributes(
 
980
    tOptions*           pOpts,
 
981
    char*               pzText,
 
982
    tOptionLoadMode*    pMode,
 
983
    tOptionValue*       pType )
 
984
{
 
985
    size_t lenLoadType = strlen( zLoadType );
 
986
    size_t lenKeyWords = strlen( zKeyWords );
 
987
    size_t lenSetMem   = strlen( zSetMembers );
 
988
 
 
989
    do  {
 
990
        switch (*pzText) {
 
991
        case '/': pType->valType = OPARG_TYPE_NONE;
 
992
        case '>': return pzText;
 
993
 
 
994
        default:
 
995
        case NUL: return NULL;
 
996
 
 
997
        case ' ':
 
998
        case '\t':
 
999
        case '\n':
 
1000
        case '\f':
 
1001
        case '\r':
 
1002
        case '\v':
 
1003
            break;
 
1004
        }
 
1005
 
 
1006
        while (isspace( (int)*++pzText ))   ;
 
1007
 
 
1008
        if (strncmp( pzText, zLoadType, lenLoadType ) == 0) {
 
1009
            pzText = parseValueType( pzText+lenLoadType, pType );
 
1010
            continue;
 
1011
        }
 
1012
 
 
1013
        if (strncmp( pzText, zKeyWords, lenKeyWords ) == 0) {
 
1014
            pzText = parseKeyWordType( pOpts, pzText+lenKeyWords, pType );
 
1015
            continue;
 
1016
        }
 
1017
 
 
1018
        if (strncmp( pzText, zSetMembers, lenSetMem ) == 0) {
 
1019
            pzText = parseSetMemType( pOpts, pzText+lenSetMem, pType );
 
1020
            continue;
 
1021
        }
 
1022
 
 
1023
        pzText = parseLoadMode( pzText, pMode );
 
1024
    } while (pzText != NULL);
 
1025
 
 
1026
    return pzText;
 
1027
}
 
1028
 
 
1029
 
 
1030
/*  parseKeyWordType
 
1031
 *
 
1032
 *  "pzText" points to the character after "words=".
 
1033
 *  What should follow is a name of a keyword (enumeration) list.
 
1034
 */
 
1035
static char*
 
1036
parseKeyWordType(
 
1037
    tOptions*     pOpts,
 
1038
    char*         pzText,
 
1039
    tOptionValue* pType )
 
1040
{
 
1041
    return skipUnknown( pzText );
 
1042
}
 
1043
 
 
1044
 
 
1045
/*  parseLoadMode
 
1046
 *
 
1047
 *  "pzText" points to some name character.  We check for "cooked" or
 
1048
 *  "uncooked" or "keep".  This function should handle any attribute
 
1049
 *  that does not have an associated value.
 
1050
 */
 
1051
static char*
 
1052
parseLoadMode(
 
1053
    char*               pzText,
 
1054
    tOptionLoadMode*    pMode )
 
1055
{
 
1056
    {
 
1057
        size_t len = strlen(zLoadCooked);
 
1058
        if (strncmp( pzText, zLoadCooked, len ) == 0) {
 
1059
            if (  (pzText[len] == '>')
 
1060
               || (pzText[len] == '/')
 
1061
               || isspace((int)pzText[len])) {
 
1062
                *pMode = OPTION_LOAD_COOKED;
 
1063
                return pzText + len;
 
1064
            }
 
1065
            goto unknown;
 
1066
        }
 
1067
    }
 
1068
 
 
1069
    {
 
1070
        size_t len = strlen(zLoadUncooked);
 
1071
        if (strncmp( pzText, zLoadUncooked, len ) == 0) {
 
1072
            if (  (pzText[len] == '>')
 
1073
               || (pzText[len] == '/')
 
1074
               || isspace((int)pzText[len])) {
 
1075
                *pMode = OPTION_LOAD_UNCOOKED;
 
1076
                return pzText + len;
 
1077
            }
 
1078
            goto unknown;
 
1079
        }
 
1080
    }
 
1081
 
 
1082
    {
 
1083
        size_t len = strlen(zLoadKeep);
 
1084
        if (strncmp( pzText, zLoadKeep, len ) == 0) {
 
1085
            if (  (pzText[len] == '>')
 
1086
               || (pzText[len] == '/')
 
1087
               || isspace((int)pzText[len])) {
 
1088
                *pMode = OPTION_LOAD_KEEP;
 
1089
                return pzText + len;
 
1090
            }
 
1091
            goto unknown;
 
1092
        }
 
1093
    }
 
1094
 
 
1095
  unknown:
 
1096
    return skipUnknown( pzText );
 
1097
}
 
1098
 
 
1099
 
 
1100
/*  parseSetMemType
 
1101
 *
 
1102
 *  "pzText" points to the character after "members="
 
1103
 *  What should follow is a name of a "set membership".
 
1104
 *  A collection of bit flags.
 
1105
 */
 
1106
static char*
 
1107
parseSetMemType(
 
1108
    tOptions*     pOpts,
 
1109
    char*         pzText,
 
1110
    tOptionValue* pType )
 
1111
{
 
1112
    return skipUnknown( pzText );
 
1113
}
 
1114
 
 
1115
 
 
1116
/*  parseValueType
 
1117
 *
 
1118
 *  "pzText" points to the character after "type="
 
1119
 */
 
1120
static char*
 
1121
parseValueType(
 
1122
    char*         pzText,
 
1123
    tOptionValue* pType )
 
1124
{
 
1125
    {
 
1126
        size_t len = strlen(zLtypeString);
 
1127
        if (strncmp( pzText, zLtypeString, len ) == 0) {
 
1128
            if ((pzText[len] == '>') || isspace((int)pzText[len])) {
 
1129
                pType->valType = OPARG_TYPE_STRING;
 
1130
                return pzText + len;
 
1131
            }
 
1132
            goto unknown;
 
1133
        }
 
1134
    }
 
1135
 
 
1136
    {
 
1137
        size_t len = strlen(zLtypeInteger);
 
1138
        if (strncmp( pzText, zLtypeInteger, len ) == 0) {
 
1139
            if ((pzText[len] == '>') || isspace((int)pzText[len])) {
 
1140
                pType->valType = OPARG_TYPE_NUMERIC;
 
1141
                return pzText + len;
 
1142
            }
 
1143
            goto unknown;
 
1144
        }
 
1145
    }
 
1146
 
 
1147
    {
 
1148
        size_t len = strlen(zLtypeBool);
 
1149
        if (strncmp( pzText, zLtypeBool, len ) == 0) {
 
1150
            if ((pzText[len] == '>') || isspace(pzText[len])) {
 
1151
                pType->valType = OPARG_TYPE_BOOLEAN;
 
1152
                return pzText + len;
 
1153
            }
 
1154
            goto unknown;
 
1155
        }
 
1156
    }
 
1157
 
 
1158
    {
 
1159
        size_t len = strlen(zLtypeKeyword);
 
1160
        if (strncmp( pzText, zLtypeKeyword, len ) == 0) {
 
1161
            if ((pzText[len] == '>') || isspace((int)pzText[len])) {
 
1162
                pType->valType = OPARG_TYPE_ENUMERATION;
 
1163
                return pzText + len;
 
1164
            }
 
1165
            goto unknown;
 
1166
        }
 
1167
    }
 
1168
 
 
1169
    {
 
1170
        size_t len = strlen(zLtypeSetMembership);
 
1171
        if (strncmp( pzText, zLtypeSetMembership, len ) == 0) {
 
1172
            if ((pzText[len] == '>') || isspace((int)pzText[len])) {
 
1173
                pType->valType = OPARG_TYPE_MEMBERSHIP;
 
1174
                return pzText + len;
 
1175
            }
 
1176
            goto unknown;
 
1177
        }
 
1178
    }
 
1179
 
 
1180
    {
 
1181
        size_t len = strlen(zLtypeNest);
 
1182
        if (strncmp( pzText, zLtypeNest, len ) == 0) {
 
1183
            if ((pzText[len] == '>') || isspace((int)pzText[len])) {
 
1184
                pType->valType = OPARG_TYPE_HIERARCHY;
 
1185
                return pzText + len;
 
1186
            }
 
1187
            goto unknown;
 
1188
        }
 
1189
    }
 
1190
 
 
1191
  unknown:
 
1192
    pType->valType = OPARG_TYPE_NONE;
 
1193
    return skipUnknown( pzText );
 
1194
}
 
1195
 
 
1196
 
 
1197
/*  skipUnknown
 
1198
 *
 
1199
 *  Skip over some unknown attribute
 
1200
 */
 
1201
static char*
 
1202
skipUnknown( char* pzText )
 
1203
{
 
1204
    for (;; pzText++) {
 
1205
        if (isspace( (int)*pzText ))  return pzText;
 
1206
        switch (*pzText) {
 
1207
        case NUL: return NULL;
 
1208
        case '/':
 
1209
        case '>': return pzText;
 
1210
        }
 
1211
    }
 
1212
}
 
1213
 
 
1214
 
 
1215
/*  validateOptionsStruct
 
1216
 *
 
1217
 *  Make sure the option descriptor is there and that we understand it.
 
1218
 *  This should be called from any user entry point where one needs to
 
1219
 *  worry about validity.  (Some entry points are free to assume that
 
1220
 *  the call is not the first to the library and, thus, that this has
 
1221
 *  already been called.)
 
1222
 */
 
1223
LOCAL tSuccess
 
1224
validateOptionsStruct( tOptions* pOpts, char const* pzProgram )
 
1225
{
 
1226
    if (pOpts == NULL) {
 
1227
        fputs( zAO_Bad, stderr );
 
1228
        exit( EX_CONFIG );
 
1229
    }
 
1230
 
 
1231
    /*
 
1232
     *  IF the client has enabled translation and the translation procedure
 
1233
     *  is available, then go do it.
 
1234
     */
 
1235
    if (  ((pOpts->fOptSet & OPTPROC_TRANSLATE) != 0)
 
1236
       && (pOpts->pTransProc != 0) ) {
 
1237
        (*pOpts->pTransProc)();
 
1238
        pOpts->fOptSet &= ~OPTPROC_TRANSLATE;
 
1239
    }
 
1240
 
 
1241
    /*
 
1242
     *  IF the struct version is not the current, and also
 
1243
     *     either too large (?!) or too small,
 
1244
     *  THEN emit error message and fail-exit
 
1245
     */
 
1246
    if (  ( pOpts->structVersion  != OPTIONS_STRUCT_VERSION  )
 
1247
       && (  (pOpts->structVersion > OPTIONS_STRUCT_VERSION  )
 
1248
          || (pOpts->structVersion < OPTIONS_MINIMUM_VERSION )
 
1249
       )  )  {
 
1250
 
 
1251
        fprintf( stderr, zAO_Err, pOpts->origArgVect[0],
 
1252
                 NUM_TO_VER( pOpts->structVersion ));
 
1253
        if (pOpts->structVersion > OPTIONS_STRUCT_VERSION )
 
1254
            fputs( zAO_Big, stderr );
 
1255
        else
 
1256
            fputs( zAO_Sml, stderr );
 
1257
 
 
1258
        return FAILURE;
 
1259
    }
 
1260
 
 
1261
    /*
 
1262
     *  If the program name hasn't been set, then set the name and the path
 
1263
     *  and the set of equivalent characters.
 
1264
     */
 
1265
    if (pOpts->pzProgName == NULL) {
 
1266
        char const* pz = strrchr( pzProgram, DIRCH );
 
1267
 
 
1268
        if (pz == NULL)
 
1269
             pOpts->pzProgName = pzProgram;
 
1270
        else pOpts->pzProgName = pz+1;
 
1271
 
 
1272
        pOpts->pzProgPath = pzProgram;
 
1273
 
 
1274
        /*
 
1275
         *  when comparing long names, these are equivalent
 
1276
         */
 
1277
        strequate( zSepChars );
 
1278
    }
 
1279
 
 
1280
    return SUCCESS;
 
1281
}
 
1282
 
 
1283
 
 
1284
/**
 
1285
 * Local Variables:
 
1286
 * mode: C
 
1287
 * c-file-style: "stroustrup"
 
1288
 * indent-tabs-mode: nil
 
1289
 * End:
 
1290
 * end of autoopts/configfile.c */