xref: /freebsd-src/contrib/ntp/sntp/libopts/configfile.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
12b15cb3dSCy Schubert /**
22b15cb3dSCy Schubert  * \file configfile.c
3ea906c41SOllivier Robert  *
4ea906c41SOllivier Robert  *  configuration/rc/ini file handling.
52b15cb3dSCy Schubert  *
62b15cb3dSCy Schubert  * @addtogroup autoopts
72b15cb3dSCy Schubert  * @{
8ea906c41SOllivier Robert  */
9ea906c41SOllivier Robert /*
102b15cb3dSCy Schubert  *  This file is part of AutoOpts, a companion to AutoGen.
112b15cb3dSCy Schubert  *  AutoOpts is free software.
12*a466cc55SCy Schubert  *  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
13ea906c41SOllivier Robert  *
142b15cb3dSCy Schubert  *  AutoOpts is available under any one of two licenses.  The license
152b15cb3dSCy Schubert  *  in use must be one of these two and the choice is under the control
162b15cb3dSCy Schubert  *  of the user of the license.
17ea906c41SOllivier Robert  *
182b15cb3dSCy Schubert  *   The GNU Lesser General Public License, version 3 or later
192b15cb3dSCy Schubert  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
20ea906c41SOllivier Robert  *
212b15cb3dSCy Schubert  *   The Modified Berkeley Software Distribution License
222b15cb3dSCy Schubert  *      See the file "COPYING.mbsd"
23ea906c41SOllivier Robert  *
242b15cb3dSCy Schubert  *  These files have the following sha256 sums:
25ea906c41SOllivier Robert  *
262b15cb3dSCy Schubert  *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
272b15cb3dSCy Schubert  *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
282b15cb3dSCy Schubert  *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
29ea906c41SOllivier Robert  */
30ea906c41SOllivier Robert 
312b15cb3dSCy Schubert /**
322b15cb3dSCy Schubert  *  Skip over some unknown attribute
332b15cb3dSCy Schubert  *  @param[in] txt   start of skpped text
342b15cb3dSCy Schubert  *  @returns   character after skipped text
352b15cb3dSCy Schubert  */
362b15cb3dSCy Schubert inline static char const *
skip_unkn(char const * txt)372b15cb3dSCy Schubert skip_unkn(char const * txt)
382b15cb3dSCy Schubert {
392b15cb3dSCy Schubert     txt = BRK_END_XML_TOKEN_CHARS(txt);
402b15cb3dSCy Schubert     return (*txt == NUL) ? NULL : txt;
412b15cb3dSCy Schubert }
42ea906c41SOllivier Robert 
43ea906c41SOllivier Robert /*=export_func  configFileLoad
44ea906c41SOllivier Robert  *
45ea906c41SOllivier Robert  * what:  parse a configuration file
462b15cb3dSCy Schubert  * arg:   + char const * + fname + the file to load +
47ea906c41SOllivier Robert  *
48ea906c41SOllivier Robert  * ret_type:  const tOptionValue *
49ea906c41SOllivier Robert  * ret_desc:  An allocated, compound value structure
50ea906c41SOllivier Robert  *
51ea906c41SOllivier Robert  * doc:
52ea906c41SOllivier Robert  *  This routine will load a named configuration file and parse the
53ea906c41SOllivier Robert  *  text as a hierarchically valued option.  The option descriptor
54ea906c41SOllivier Robert  *  created from an option definition file is not used via this interface.
55ea906c41SOllivier Robert  *  The returned value is "named" with the input file name and is of
56ea906c41SOllivier Robert  *  type "@code{OPARG_TYPE_HIERARCHY}".  It may be used in calls to
57ea906c41SOllivier Robert  *  @code{optionGetValue()}, @code{optionNextValue()} and
58ea906c41SOllivier Robert  *  @code{optionUnloadNested()}.
59ea906c41SOllivier Robert  *
60ea906c41SOllivier Robert  * err:
61ea906c41SOllivier Robert  *  If the file cannot be loaded or processed, @code{NULL} is returned and
62ea906c41SOllivier Robert  *  @var{errno} is set.  It may be set by a call to either @code{open(2)}
63ea906c41SOllivier Robert  *  @code{mmap(2)} or other file system calls, or it may be:
64ea906c41SOllivier Robert  *  @itemize @bullet
65ea906c41SOllivier Robert  *  @item
662b15cb3dSCy Schubert  *  @code{ENOENT} - the file was not found.
672b15cb3dSCy Schubert  *  @item
682b15cb3dSCy Schubert  *  @code{ENOMSG} - the file was empty.
69ea906c41SOllivier Robert  *  @item
70ea906c41SOllivier Robert  *  @code{EINVAL} - the file contents are invalid -- not properly formed.
71ea906c41SOllivier Robert  *  @item
72ea906c41SOllivier Robert  *  @code{ENOMEM} - not enough memory to allocate the needed structures.
73ea906c41SOllivier Robert  *  @end itemize
74ea906c41SOllivier Robert =*/
75ea906c41SOllivier Robert const tOptionValue *
configFileLoad(char const * fname)762b15cb3dSCy Schubert configFileLoad(char const * fname)
77ea906c41SOllivier Robert {
78ea906c41SOllivier Robert     tmap_info_t    cfgfile;
792b15cb3dSCy Schubert     tOptionValue * res = NULL;
80ea906c41SOllivier Robert     tOptionLoadMode save_mode = option_load_mode;
81ea906c41SOllivier Robert 
822b15cb3dSCy Schubert     char * txt = text_mmap(fname, PROT_READ, MAP_PRIVATE, &cfgfile);
83ea906c41SOllivier Robert 
842b15cb3dSCy Schubert     if (TEXT_MMAP_FAILED_ADDR(txt))
85ea906c41SOllivier Robert         return NULL; /* errno is set */
86ea906c41SOllivier Robert 
87ea906c41SOllivier Robert     option_load_mode = OPTION_LOAD_COOKED;
882b15cb3dSCy Schubert     res = optionLoadNested(txt, fname, strlen(fname));
89ea906c41SOllivier Robert 
902b15cb3dSCy Schubert     if (res == NULL) {
91ea906c41SOllivier Robert         int err = errno;
92ea906c41SOllivier Robert         text_munmap(&cfgfile);
93ea906c41SOllivier Robert         errno = err;
94ea906c41SOllivier Robert     } else
95ea906c41SOllivier Robert         text_munmap(&cfgfile);
96ea906c41SOllivier Robert 
97ea906c41SOllivier Robert     option_load_mode = save_mode;
982b15cb3dSCy Schubert     return res;
99ea906c41SOllivier Robert }
100ea906c41SOllivier Robert 
101ea906c41SOllivier Robert 
102ea906c41SOllivier Robert /*=export_func  optionFindValue
103ea906c41SOllivier Robert  *
104ea906c41SOllivier Robert  * what:  find a hierarcicaly valued option instance
1052b15cb3dSCy Schubert  * arg:   + const tOptDesc * + odesc + an option with a nested arg type +
106ea906c41SOllivier Robert  * arg:   + char const *     + name  + name of value to find +
1072b15cb3dSCy Schubert  * arg:   + char const *     + val   + the matching value    +
108ea906c41SOllivier Robert  *
109ea906c41SOllivier Robert  * ret_type:  const tOptionValue *
110ea906c41SOllivier Robert  * ret_desc:  a compound value structure
111ea906c41SOllivier Robert  *
112ea906c41SOllivier Robert  * doc:
113ea906c41SOllivier Robert  *  This routine will find an entry in a nested value option or configurable.
114ea906c41SOllivier Robert  *  It will search through the list and return a matching entry.
115ea906c41SOllivier Robert  *
116ea906c41SOllivier Robert  * err:
117ea906c41SOllivier Robert  *  The returned result is NULL and errno is set:
118ea906c41SOllivier Robert  *  @itemize @bullet
119ea906c41SOllivier Robert  *  @item
120ea906c41SOllivier Robert  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
121ea906c41SOllivier Robert  *  hierarchical option value.
122ea906c41SOllivier Robert  *  @item
123ea906c41SOllivier Robert  *  @code{ENOENT} - no entry matched the given name.
124ea906c41SOllivier Robert  *  @end itemize
125ea906c41SOllivier Robert =*/
126ea906c41SOllivier Robert const tOptionValue *
optionFindValue(const tOptDesc * odesc,char const * name,char const * val)1272b15cb3dSCy Schubert optionFindValue(const tOptDesc * odesc, char const * name, char const * val)
128ea906c41SOllivier Robert {
1292b15cb3dSCy Schubert     const tOptionValue * res = NULL;
130ea906c41SOllivier Robert 
1312b15cb3dSCy Schubert     if (  (odesc == NULL)
1322b15cb3dSCy Schubert        || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY))  {
133ea906c41SOllivier Robert         errno = EINVAL;
134ea906c41SOllivier Robert     }
135ea906c41SOllivier Robert 
1362b15cb3dSCy Schubert     else if (odesc->optCookie == NULL) {
137ea906c41SOllivier Robert         errno = ENOENT;
138ea906c41SOllivier Robert     }
139ea906c41SOllivier Robert 
140ea906c41SOllivier Robert     else do {
1412b15cb3dSCy Schubert         tArgList * argl  = odesc->optCookie;
1422b15cb3dSCy Schubert         int        argct = argl->useCt;
143*a466cc55SCy Schubert         void **    poptv = (void **)(argl->apzArgs);
144ea906c41SOllivier Robert 
1452b15cb3dSCy Schubert         if (argct == 0) {
146ea906c41SOllivier Robert             errno = ENOENT;
147ea906c41SOllivier Robert             break;
148ea906c41SOllivier Robert         }
149ea906c41SOllivier Robert 
1502b15cb3dSCy Schubert         if (name == NULL) {
151*a466cc55SCy Schubert             res = (tOptionValue *)*poptv;
152ea906c41SOllivier Robert             break;
153ea906c41SOllivier Robert         }
154ea906c41SOllivier Robert 
1552b15cb3dSCy Schubert         while (--argct >= 0) {
1562b15cb3dSCy Schubert             const tOptionValue * ov = *(poptv++);
1572b15cb3dSCy Schubert             const tOptionValue * rv = optionGetValue(ov, name);
158ea906c41SOllivier Robert 
1592b15cb3dSCy Schubert             if (rv == NULL)
160ea906c41SOllivier Robert                 continue;
161ea906c41SOllivier Robert 
1622b15cb3dSCy Schubert             if (val == NULL) {
1632b15cb3dSCy Schubert                 res = ov;
164ea906c41SOllivier Robert                 break;
165ea906c41SOllivier Robert             }
166ea906c41SOllivier Robert         }
1672b15cb3dSCy Schubert         if (res == NULL)
168ea906c41SOllivier Robert             errno = ENOENT;
1692b15cb3dSCy Schubert     } while (false);
170ea906c41SOllivier Robert 
1712b15cb3dSCy Schubert     return res;
172ea906c41SOllivier Robert }
173ea906c41SOllivier Robert 
174ea906c41SOllivier Robert 
175ea906c41SOllivier Robert /*=export_func  optionFindNextValue
176ea906c41SOllivier Robert  *
1772b15cb3dSCy Schubert  * FIXME: the handling of 'pzName' and 'pzVal' is just wrong.
1782b15cb3dSCy Schubert  *
179ea906c41SOllivier Robert  * what:  find a hierarcicaly valued option instance
1802b15cb3dSCy Schubert  * arg:   + const tOptDesc * + odesc + an option with a nested arg type +
181ea906c41SOllivier Robert  * arg:   + const tOptionValue * + pPrevVal + the last entry +
182ea906c41SOllivier Robert  * arg:   + char const *     + name     + name of value to find +
183ea906c41SOllivier Robert  * arg:   + char const *     + value    + the matching value    +
184ea906c41SOllivier Robert  *
185ea906c41SOllivier Robert  * ret_type:  const tOptionValue *
186ea906c41SOllivier Robert  * ret_desc:  a compound value structure
187ea906c41SOllivier Robert  *
188ea906c41SOllivier Robert  * doc:
189ea906c41SOllivier Robert  *  This routine will find the next entry in a nested value option or
190ea906c41SOllivier Robert  *  configurable.  It will search through the list and return the next entry
191ea906c41SOllivier Robert  *  that matches the criteria.
192ea906c41SOllivier Robert  *
193ea906c41SOllivier Robert  * err:
194ea906c41SOllivier Robert  *  The returned result is NULL and errno is set:
195ea906c41SOllivier Robert  *  @itemize @bullet
196ea906c41SOllivier Robert  *  @item
197ea906c41SOllivier Robert  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
198ea906c41SOllivier Robert  *  hierarchical option value.
199ea906c41SOllivier Robert  *  @item
200ea906c41SOllivier Robert  *  @code{ENOENT} - no entry matched the given name.
201ea906c41SOllivier Robert  *  @end itemize
202ea906c41SOllivier Robert =*/
2032b15cb3dSCy Schubert tOptionValue const *
optionFindNextValue(const tOptDesc * odesc,const tOptionValue * pPrevVal,char const * pzName,char const * pzVal)2042b15cb3dSCy Schubert optionFindNextValue(const tOptDesc * odesc, const tOptionValue * pPrevVal,
205ea906c41SOllivier Robert                     char const * pzName, char const * pzVal)
206ea906c41SOllivier Robert {
2072b15cb3dSCy Schubert     bool old_found = false;
208*a466cc55SCy Schubert     tOptionValue * res = NULL;
209ea906c41SOllivier Robert 
2102b15cb3dSCy Schubert     (void)pzName;
2112b15cb3dSCy Schubert     (void)pzVal;
2122b15cb3dSCy Schubert 
2132b15cb3dSCy Schubert     if (  (odesc == NULL)
2142b15cb3dSCy Schubert        || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY))  {
215ea906c41SOllivier Robert         errno = EINVAL;
216ea906c41SOllivier Robert     }
217ea906c41SOllivier Robert 
2182b15cb3dSCy Schubert     else if (odesc->optCookie == NULL) {
219ea906c41SOllivier Robert         errno = ENOENT;
220ea906c41SOllivier Robert     }
221ea906c41SOllivier Robert 
222ea906c41SOllivier Robert     else do {
2232b15cb3dSCy Schubert         tArgList * argl = odesc->optCookie;
2242b15cb3dSCy Schubert         int        ct   = argl->useCt;
225*a466cc55SCy Schubert         void **   poptv = (void **)argl->apzArgs;
226ea906c41SOllivier Robert 
227ea906c41SOllivier Robert         while (--ct >= 0) {
228*a466cc55SCy Schubert             tOptionValue * pOV = *(poptv++);
2292b15cb3dSCy Schubert             if (old_found) {
2302b15cb3dSCy Schubert                 res = pOV;
231ea906c41SOllivier Robert                 break;
232ea906c41SOllivier Robert             }
233ea906c41SOllivier Robert             if (pOV == pPrevVal)
2342b15cb3dSCy Schubert                 old_found = true;
235ea906c41SOllivier Robert         }
2362b15cb3dSCy Schubert         if (res == NULL)
237ea906c41SOllivier Robert             errno = ENOENT;
2382b15cb3dSCy Schubert     } while (false);
239ea906c41SOllivier Robert 
2402b15cb3dSCy Schubert     return res;
241ea906c41SOllivier Robert }
242ea906c41SOllivier Robert 
243ea906c41SOllivier Robert 
244ea906c41SOllivier Robert /*=export_func  optionGetValue
245ea906c41SOllivier Robert  *
246ea906c41SOllivier Robert  * what:  get a specific value from a hierarcical list
247ea906c41SOllivier Robert  * arg:   + const tOptionValue * + pOptValue + a hierarchcal value +
248ea906c41SOllivier Robert  * arg:   + char const *         + valueName + name of value to get +
249ea906c41SOllivier Robert  *
250ea906c41SOllivier Robert  * ret_type:  const tOptionValue *
251ea906c41SOllivier Robert  * ret_desc:  a compound value structure
252ea906c41SOllivier Robert  *
253ea906c41SOllivier Robert  * doc:
254ea906c41SOllivier Robert  *  This routine will find an entry in a nested value option or configurable.
255ea906c41SOllivier Robert  *  If "valueName" is NULL, then the first entry is returned.  Otherwise,
256ea906c41SOllivier Robert  *  the first entry with a name that exactly matches the argument will be
2572b15cb3dSCy Schubert  *  returned.  If there is no matching value, NULL is returned and errno is
2582b15cb3dSCy Schubert  *  set to ENOENT. If the provided option value is not a hierarchical value,
2592b15cb3dSCy Schubert  *  NULL is also returned and errno is set to EINVAL.
260ea906c41SOllivier Robert  *
261ea906c41SOllivier Robert  * err:
262ea906c41SOllivier Robert  *  The returned result is NULL and errno is set:
263ea906c41SOllivier Robert  *  @itemize @bullet
264ea906c41SOllivier Robert  *  @item
265ea906c41SOllivier Robert  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
266ea906c41SOllivier Robert  *  hierarchical option value.
267ea906c41SOllivier Robert  *  @item
268ea906c41SOllivier Robert  *  @code{ENOENT} - no entry matched the given name.
269ea906c41SOllivier Robert  *  @end itemize
270ea906c41SOllivier Robert =*/
2712b15cb3dSCy Schubert tOptionValue const *
optionGetValue(tOptionValue const * oov,char const * vname)2722b15cb3dSCy Schubert optionGetValue(tOptionValue const * oov, char const * vname)
273ea906c41SOllivier Robert {
2742b15cb3dSCy Schubert     tArgList *     arg_list;
275*a466cc55SCy Schubert     tOptionValue * res = NULL;
276ea906c41SOllivier Robert 
2772b15cb3dSCy Schubert     if ((oov == NULL) || (oov->valType != OPARG_TYPE_HIERARCHY)) {
278ea906c41SOllivier Robert         errno = EINVAL;
2792b15cb3dSCy Schubert         return res;
280ea906c41SOllivier Robert     }
2812b15cb3dSCy Schubert     arg_list = oov->v.nestVal;
282ea906c41SOllivier Robert 
2832b15cb3dSCy Schubert     if (arg_list->useCt > 0) {
2842b15cb3dSCy Schubert         int     ct     = arg_list->useCt;
285*a466cc55SCy Schubert         void ** ovlist = (void **)(arg_list->apzArgs);
286ea906c41SOllivier Robert 
2872b15cb3dSCy Schubert         if (vname == NULL) {
288*a466cc55SCy Schubert             res = (tOptionValue *)*ovlist;
289ea906c41SOllivier Robert 
2902b15cb3dSCy Schubert         } else do {
291*a466cc55SCy Schubert             tOptionValue * opt_val = *(ovlist++);
2922b15cb3dSCy Schubert             if (strcmp(opt_val->pzName, vname) == 0) {
2932b15cb3dSCy Schubert                 res = opt_val;
294ea906c41SOllivier Robert                 break;
295ea906c41SOllivier Robert             }
296ea906c41SOllivier Robert         } while (--ct > 0);
297ea906c41SOllivier Robert     }
2982b15cb3dSCy Schubert     if (res == NULL)
299ea906c41SOllivier Robert         errno = ENOENT;
3002b15cb3dSCy Schubert     return res;
301ea906c41SOllivier Robert }
302ea906c41SOllivier Robert 
303ea906c41SOllivier Robert /*=export_func  optionNextValue
304ea906c41SOllivier Robert  *
305ea906c41SOllivier Robert  * what:  get the next value from a hierarchical list
306ea906c41SOllivier Robert  * arg:   + const tOptionValue * + pOptValue + a hierarchcal list value +
307ea906c41SOllivier Robert  * arg:   + const tOptionValue * + pOldValue + a value from this list   +
308ea906c41SOllivier Robert  *
309ea906c41SOllivier Robert  * ret_type:  const tOptionValue *
310ea906c41SOllivier Robert  * ret_desc:  a compound value structure
311ea906c41SOllivier Robert  *
312ea906c41SOllivier Robert  * doc:
313ea906c41SOllivier Robert  *  This routine will return the next entry after the entry passed in.  At the
314ea906c41SOllivier Robert  *  end of the list, NULL will be returned.  If the entry is not found on the
315ea906c41SOllivier Robert  *  list, NULL will be returned and "@var{errno}" will be set to EINVAL.
316ea906c41SOllivier Robert  *  The "@var{pOldValue}" must have been gotten from a prior call to this
317ea906c41SOllivier Robert  *  routine or to "@code{opitonGetValue()}".
318ea906c41SOllivier Robert  *
319ea906c41SOllivier Robert  * err:
320ea906c41SOllivier Robert  *  The returned result is NULL and errno is set:
321ea906c41SOllivier Robert  *  @itemize @bullet
322ea906c41SOllivier Robert  *  @item
323ea906c41SOllivier Robert  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
324ea906c41SOllivier Robert  *  hierarchical option value or @code{pOldValue} does not point to a
325ea906c41SOllivier Robert  *  member of that option value.
326ea906c41SOllivier Robert  *  @item
327ea906c41SOllivier Robert  *  @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry.
328ea906c41SOllivier Robert  *  @end itemize
329ea906c41SOllivier Robert =*/
330ea906c41SOllivier Robert tOptionValue const *
optionNextValue(tOptionValue const * ov_list,tOptionValue const * oov)3312b15cb3dSCy Schubert optionNextValue(tOptionValue const * ov_list,tOptionValue const * oov )
332ea906c41SOllivier Robert {
3332b15cb3dSCy Schubert     tArgList *     arg_list;
334*a466cc55SCy Schubert     tOptionValue * res = NULL;
335ea906c41SOllivier Robert     int            err = EINVAL;
336ea906c41SOllivier Robert 
3372b15cb3dSCy Schubert     if ((ov_list == NULL) || (ov_list->valType != OPARG_TYPE_HIERARCHY)) {
338ea906c41SOllivier Robert         errno = EINVAL;
339ea906c41SOllivier Robert         return NULL;
340ea906c41SOllivier Robert     }
3412b15cb3dSCy Schubert     arg_list = ov_list->v.nestVal;
342ea906c41SOllivier Robert     {
3432b15cb3dSCy Schubert         int     ct    = arg_list->useCt;
344*a466cc55SCy Schubert         void ** o_list = (void **)(arg_list->apzArgs);
345ea906c41SOllivier Robert 
346ea906c41SOllivier Robert         while (ct-- > 0) {
347*a466cc55SCy Schubert             tOptionValue * nov = *(o_list++);
3482b15cb3dSCy Schubert             if (nov == oov) {
349ea906c41SOllivier Robert                 if (ct == 0) {
350ea906c41SOllivier Robert                     err = ENOENT;
351ea906c41SOllivier Robert 
352ea906c41SOllivier Robert                 } else {
353ea906c41SOllivier Robert                     err = 0;
354*a466cc55SCy Schubert                     res = (tOptionValue *)*o_list;
355ea906c41SOllivier Robert                 }
356ea906c41SOllivier Robert                 break;
357ea906c41SOllivier Robert             }
358ea906c41SOllivier Robert         }
359ea906c41SOllivier Robert     }
360ea906c41SOllivier Robert     if (err != 0)
361ea906c41SOllivier Robert         errno = err;
3622b15cb3dSCy Schubert     return res;
363ea906c41SOllivier Robert }
364ea906c41SOllivier Robert 
3652b15cb3dSCy Schubert /**
366ea906c41SOllivier Robert  *  Load a file containing presetting information (a configuration file).
367ea906c41SOllivier Robert  */
368ea906c41SOllivier Robert static void
file_preset(tOptions * opts,char const * fname,int dir)3692b15cb3dSCy Schubert file_preset(tOptions * opts, char const * fname, int dir)
370ea906c41SOllivier Robert {
371ea906c41SOllivier Robert     tmap_info_t       cfgfile;
3722b15cb3dSCy Schubert     tOptState         optst = OPTSTATE_INITIALIZER(PRESET);
3732b15cb3dSCy Schubert     opt_state_mask_t  st_flags = optst.flags;
3742b15cb3dSCy Schubert     opt_state_mask_t  fl_save  = opts->fOptSet;
3752b15cb3dSCy Schubert     char *            ftext =
3762b15cb3dSCy Schubert         text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile);
377ea906c41SOllivier Robert 
3782b15cb3dSCy Schubert     if (TEXT_MMAP_FAILED_ADDR(ftext))
379ea906c41SOllivier Robert         return;
380ea906c41SOllivier Robert 
3812b15cb3dSCy Schubert     /*
3822b15cb3dSCy Schubert      * While processing config files, we ignore errors.
3832b15cb3dSCy Schubert      */
3842b15cb3dSCy Schubert     opts->fOptSet &= ~OPTPROC_ERRSTOP;
3852b15cb3dSCy Schubert 
3862b15cb3dSCy Schubert     if (dir == DIRECTION_CALLED) {
3872b15cb3dSCy Schubert         st_flags = OPTST_DEFINED;
3882b15cb3dSCy Schubert         dir   = DIRECTION_PROCESS;
389ea906c41SOllivier Robert     }
390ea906c41SOllivier Robert 
391ea906c41SOllivier Robert     /*
392ea906c41SOllivier Robert      *  IF this is called via "optionProcess", then we are presetting.
393ea906c41SOllivier Robert      *  This is the default and the PRESETTING bit will be set.
394ea906c41SOllivier Robert      *  If this is called via "optionFileLoad", then the bit is not set
395ea906c41SOllivier Robert      *  and we consider stuff set herein to be "set" by the client program.
396ea906c41SOllivier Robert      */
3972b15cb3dSCy Schubert     if ((opts->fOptSet & OPTPROC_PRESETTING) == 0)
3982b15cb3dSCy Schubert         st_flags = OPTST_SET;
399ea906c41SOllivier Robert 
400ea906c41SOllivier Robert     do  {
4012b15cb3dSCy Schubert         optst.flags = st_flags;
4022b15cb3dSCy Schubert         ftext = SPN_WHITESPACE_CHARS(ftext);
403ea906c41SOllivier Robert 
4042b15cb3dSCy Schubert         if (IS_VAR_FIRST_CHAR(*ftext)) {
4052b15cb3dSCy Schubert             ftext = handle_cfg(opts, &optst, ftext, dir);
406ea906c41SOllivier Robert 
4072b15cb3dSCy Schubert         } else switch (*ftext) {
408ea906c41SOllivier Robert         case '<':
4092b15cb3dSCy Schubert             if (IS_VAR_FIRST_CHAR(ftext[1]))
4102b15cb3dSCy Schubert                 ftext = handle_struct(opts, &optst, ftext, dir);
411ea906c41SOllivier Robert 
4122b15cb3dSCy Schubert             else switch (ftext[1]) {
413ea906c41SOllivier Robert             case '?':
4142b15cb3dSCy Schubert                 ftext = handle_directive(opts, ftext);
415ea906c41SOllivier Robert                 break;
416ea906c41SOllivier Robert 
417ea906c41SOllivier Robert             case '!':
4182b15cb3dSCy Schubert                 ftext = handle_comment(ftext);
419ea906c41SOllivier Robert                 break;
420ea906c41SOllivier Robert 
421ea906c41SOllivier Robert             case '/':
4222b15cb3dSCy Schubert                 ftext = strchr(ftext + 2, '>');
4232b15cb3dSCy Schubert                 if (ftext++ != NULL)
424ea906c41SOllivier Robert                     break;
425*a466cc55SCy Schubert                 /* FALLTHROUGH */
426ea906c41SOllivier Robert 
427ea906c41SOllivier Robert             default:
4282b15cb3dSCy Schubert                 ftext = NULL;
429ea906c41SOllivier Robert             }
4302b15cb3dSCy Schubert             if (ftext == NULL)
4312b15cb3dSCy Schubert                 goto all_done;
432ea906c41SOllivier Robert             break;
433ea906c41SOllivier Robert 
434ea906c41SOllivier Robert         case '[':
4352b15cb3dSCy Schubert             ftext = handle_section(opts, ftext);
436ea906c41SOllivier Robert             break;
437ea906c41SOllivier Robert 
438ea906c41SOllivier Robert         case '#':
4392b15cb3dSCy Schubert             ftext = strchr(ftext + 1, NL);
440ea906c41SOllivier Robert             break;
441ea906c41SOllivier Robert 
442ea906c41SOllivier Robert         default:
443ea906c41SOllivier Robert             goto all_done; /* invalid format */
444ea906c41SOllivier Robert         }
4452b15cb3dSCy Schubert     } while (ftext != NULL);
446ea906c41SOllivier Robert 
447ea906c41SOllivier Robert  all_done:
448ea906c41SOllivier Robert     text_munmap(&cfgfile);
4492b15cb3dSCy Schubert     opts->fOptSet = fl_save;
450ea906c41SOllivier Robert }
451ea906c41SOllivier Robert 
4522b15cb3dSCy Schubert /**
4532b15cb3dSCy Schubert  *  "txt" points to a "<!" sequence.
454ea906c41SOllivier Robert  *  Theoretically, we should ensure that it begins with "<!--",
455ea906c41SOllivier Robert  *  but actually I don't care that much.  It ends with "-->".
456ea906c41SOllivier Robert  */
457ea906c41SOllivier Robert static char *
handle_comment(char * txt)4582b15cb3dSCy Schubert handle_comment(char * txt)
459ea906c41SOllivier Robert {
4602b15cb3dSCy Schubert     char * pz = strstr(txt, "-->");
461ea906c41SOllivier Robert     if (pz != NULL)
462ea906c41SOllivier Robert         pz += 3;
463ea906c41SOllivier Robert     return pz;
464ea906c41SOllivier Robert }
465ea906c41SOllivier Robert 
4662b15cb3dSCy Schubert /**
4672b15cb3dSCy Schubert  *  "txt" points to the start of some value name.
468ea906c41SOllivier Robert  *  The end of the entry is the end of the line that is not preceded by
469ea906c41SOllivier Robert  *  a backslash escape character.  The string value is always processed
470ea906c41SOllivier Robert  *  in "cooked" mode.
471ea906c41SOllivier Robert  */
472ea906c41SOllivier Robert static char *
handle_cfg(tOptions * opts,tOptState * ost,char * txt,int dir)4732b15cb3dSCy Schubert handle_cfg(tOptions * opts, tOptState * ost, char * txt, int dir)
474ea906c41SOllivier Robert {
4752b15cb3dSCy Schubert     char * pzName = txt++;
4762b15cb3dSCy Schubert     char * pzEnd  = strchr(txt, NL);
477ea906c41SOllivier Robert 
478ea906c41SOllivier Robert     if (pzEnd == NULL)
4792b15cb3dSCy Schubert         return txt + strlen(txt);
480ea906c41SOllivier Robert 
4812b15cb3dSCy Schubert     txt = SPN_VALUE_NAME_CHARS(txt);
4822b15cb3dSCy Schubert     txt = SPN_WHITESPACE_CHARS(txt);
4832b15cb3dSCy Schubert     if (txt > pzEnd) {
484ea906c41SOllivier Robert     name_only:
485ea906c41SOllivier Robert         *pzEnd++ = NUL;
4862b15cb3dSCy Schubert         load_opt_line(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED);
487ea906c41SOllivier Robert         return pzEnd;
488ea906c41SOllivier Robert     }
489ea906c41SOllivier Robert 
490ea906c41SOllivier Robert     /*
491ea906c41SOllivier Robert      *  Either the first character after the name is a ':' or '=',
492ea906c41SOllivier Robert      *  or else we must have skipped over white space.  Anything else
493ea906c41SOllivier Robert      *  is an invalid format and we give up parsing the text.
494ea906c41SOllivier Robert      */
4952b15cb3dSCy Schubert     if ((*txt == '=') || (*txt == ':')) {
4962b15cb3dSCy Schubert         txt = SPN_WHITESPACE_CHARS(txt+1);
4972b15cb3dSCy Schubert         if (txt > pzEnd)
498ea906c41SOllivier Robert             goto name_only;
4992b15cb3dSCy Schubert     } else if (! IS_WHITESPACE_CHAR(txt[-1]))
500ea906c41SOllivier Robert         return NULL;
501ea906c41SOllivier Robert 
502ea906c41SOllivier Robert     /*
503ea906c41SOllivier Robert      *  IF the value is continued, remove the backslash escape and push "pzEnd"
504ea906c41SOllivier Robert      *  on to a newline *not* preceded by a backslash.
505ea906c41SOllivier Robert      */
506ea906c41SOllivier Robert     if (pzEnd[-1] == '\\') {
507ea906c41SOllivier Robert         char * pcD = pzEnd-1;
508ea906c41SOllivier Robert         char * pcS = pzEnd;
509ea906c41SOllivier Robert 
510ea906c41SOllivier Robert         for (;;) {
511ea906c41SOllivier Robert             char ch = *(pcS++);
512ea906c41SOllivier Robert             switch (ch) {
513ea906c41SOllivier Robert             case NUL:
514ea906c41SOllivier Robert                 pcS = NULL;
5152b15cb3dSCy Schubert                 /* FALLTHROUGH */
516ea906c41SOllivier Robert 
5172b15cb3dSCy Schubert             case NL:
518ea906c41SOllivier Robert                 *pcD = NUL;
519ea906c41SOllivier Robert                 pzEnd = pcS;
520ea906c41SOllivier Robert                 goto copy_done;
521ea906c41SOllivier Robert 
522ea906c41SOllivier Robert             case '\\':
5232b15cb3dSCy Schubert                 if (*pcS == NL)
524ea906c41SOllivier Robert                     ch = *(pcS++);
525ea906c41SOllivier Robert                 /* FALLTHROUGH */
526ea906c41SOllivier Robert             default:
527ea906c41SOllivier Robert                 *(pcD++) = ch;
528ea906c41SOllivier Robert             }
529ea906c41SOllivier Robert         } copy_done:;
530ea906c41SOllivier Robert 
531ea906c41SOllivier Robert     } else {
532ea906c41SOllivier Robert         /*
533ea906c41SOllivier Robert          *  The newline was not preceded by a backslash.  NUL it out
534ea906c41SOllivier Robert          */
535ea906c41SOllivier Robert         *(pzEnd++) = NUL;
536ea906c41SOllivier Robert     }
537ea906c41SOllivier Robert 
538ea906c41SOllivier Robert     /*
539ea906c41SOllivier Robert      *  "pzName" points to what looks like text for one option/configurable.
540ea906c41SOllivier Robert      *  It is NUL terminated.  Process it.
541ea906c41SOllivier Robert      */
5422b15cb3dSCy Schubert     load_opt_line(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED);
543ea906c41SOllivier Robert 
544ea906c41SOllivier Robert     return pzEnd;
545ea906c41SOllivier Robert }
546ea906c41SOllivier Robert 
5472b15cb3dSCy Schubert /**
5482b15cb3dSCy Schubert  *  "txt" points to a "<?" sequence.
5492b15cb3dSCy Schubert  *  We handle "<?program" and "<?auto-options" directives.
5502b15cb3dSCy Schubert  *  All others are treated as comments.
551ea906c41SOllivier Robert  *
5522b15cb3dSCy Schubert  *  @param[in,out] opts  program option descriptor
5532b15cb3dSCy Schubert  *  @param[in]     txt   scanning pointer
5542b15cb3dSCy Schubert  *  @returns       the next character to look at
555ea906c41SOllivier Robert  */
556ea906c41SOllivier Robert static char *
handle_directive(tOptions * opts,char * txt)5572b15cb3dSCy Schubert handle_directive(tOptions * opts, char * txt)
558ea906c41SOllivier Robert {
5592b15cb3dSCy Schubert #   define DIRECTIVE_TABLE                      \
5602b15cb3dSCy Schubert     _dt_(zCfgProg,     program_directive)       \
5612b15cb3dSCy Schubert     _dt_(zCfgAO_Flags, aoflags_directive)
562ea906c41SOllivier Robert 
5632b15cb3dSCy Schubert     typedef char * (directive_func_t)(tOptions *, char *);
5642b15cb3dSCy Schubert #   define _dt_(_s, _fn) _fn,
5652b15cb3dSCy Schubert     static directive_func_t * dir_disp[] = {
5662b15cb3dSCy Schubert         DIRECTIVE_TABLE
5672b15cb3dSCy Schubert     };
5682b15cb3dSCy Schubert #   undef  _dt_
5692b15cb3dSCy Schubert 
5702b15cb3dSCy Schubert #   define _dt_(_s, _fn) 1 +
5712b15cb3dSCy Schubert     static int  const   dir_ct  = DIRECTIVE_TABLE 0;
5722b15cb3dSCy Schubert     static char const * dir_names[DIRECTIVE_TABLE 0];
5732b15cb3dSCy Schubert #   undef _dt_
5742b15cb3dSCy Schubert 
5752b15cb3dSCy Schubert     int    ix;
5762b15cb3dSCy Schubert 
5772b15cb3dSCy Schubert     if (dir_names[0] == NULL) {
5782b15cb3dSCy Schubert         ix = 0;
5792b15cb3dSCy Schubert #   define _dt_(_s, _fn) dir_names[ix++] = _s;
5802b15cb3dSCy Schubert         DIRECTIVE_TABLE;
5812b15cb3dSCy Schubert #   undef _dt_
582ea906c41SOllivier Robert     }
583ea906c41SOllivier Robert 
5842b15cb3dSCy Schubert     for (ix = 0; ix < dir_ct; ix++) {
5852b15cb3dSCy Schubert         size_t len = strlen(dir_names[ix]);
586*a466cc55SCy Schubert         if (  (strncmp(txt, dir_names[ix], len) == 0)
587*a466cc55SCy Schubert            && (! IS_VALUE_NAME_CHAR(txt[len])) )
588*a466cc55SCy Schubert             return dir_disp[ix](opts, txt + len);
5892b15cb3dSCy Schubert     }
5902b15cb3dSCy Schubert 
5912b15cb3dSCy Schubert     /*
5922b15cb3dSCy Schubert      *  We don't know what this is.  Skip it.
5932b15cb3dSCy Schubert      */
5942b15cb3dSCy Schubert     txt = strchr(txt+2, '>');
5952b15cb3dSCy Schubert     if (txt != NULL)
5962b15cb3dSCy Schubert         txt++;
5972b15cb3dSCy Schubert     return txt;
5982b15cb3dSCy Schubert #   undef DIRECTIVE_TABLE
5992b15cb3dSCy Schubert }
6002b15cb3dSCy Schubert 
6012b15cb3dSCy Schubert /**
6022b15cb3dSCy Schubert  *  handle AutoOpts mode flags.
6032b15cb3dSCy Schubert  *
6042b15cb3dSCy Schubert  *  @param[in,out] opts  program option descriptor
6052b15cb3dSCy Schubert  *  @param[in]     txt   scanning pointer
6062b15cb3dSCy Schubert  *  @returns       the next character to look at
6072b15cb3dSCy Schubert  */
6082b15cb3dSCy Schubert static char *
aoflags_directive(tOptions * opts,char * txt)6092b15cb3dSCy Schubert aoflags_directive(tOptions * opts, char * txt)
6102b15cb3dSCy Schubert {
6112b15cb3dSCy Schubert     char * pz;
6122b15cb3dSCy Schubert 
6132b15cb3dSCy Schubert     pz = SPN_WHITESPACE_CHARS(txt+1);
6142b15cb3dSCy Schubert     txt = strchr(pz, '>');
6152b15cb3dSCy Schubert     if (txt != NULL) {
6162b15cb3dSCy Schubert 
6172b15cb3dSCy Schubert         size_t len  = (unsigned)(txt - pz);
6182b15cb3dSCy Schubert         char * ftxt = AGALOC(len + 1, "aoflags");
6192b15cb3dSCy Schubert 
6202b15cb3dSCy Schubert         memcpy(ftxt, pz, len);
6212b15cb3dSCy Schubert         ftxt[len] = NUL;
6222b15cb3dSCy Schubert         set_usage_flags(opts, ftxt);
6232b15cb3dSCy Schubert         AGFREE(ftxt);
6242b15cb3dSCy Schubert 
6252b15cb3dSCy Schubert         txt++;
6262b15cb3dSCy Schubert     }
6272b15cb3dSCy Schubert 
6282b15cb3dSCy Schubert     return txt;
6292b15cb3dSCy Schubert }
6302b15cb3dSCy Schubert 
6312b15cb3dSCy Schubert /**
6322b15cb3dSCy Schubert  * handle program segmentation of config file.
6332b15cb3dSCy Schubert  *
6342b15cb3dSCy Schubert  *  @param[in,out] opts  program option descriptor
6352b15cb3dSCy Schubert  *  @param[in]     txt   scanning pointer
6362b15cb3dSCy Schubert  *  @returns       the next character to look at
6372b15cb3dSCy Schubert  */
6382b15cb3dSCy Schubert static char *
program_directive(tOptions * opts,char * txt)6392b15cb3dSCy Schubert program_directive(tOptions * opts, char * txt)
6402b15cb3dSCy Schubert {
6412b15cb3dSCy Schubert     size_t name_len = strlen(opts->pzProgName);
6422b15cb3dSCy Schubert 
643*a466cc55SCy Schubert     for (;; txt += zCfgProg_LEN) {
644*a466cc55SCy Schubert         txt = SPN_WHITESPACE_CHARS(txt);
645ea906c41SOllivier Robert 
6462b15cb3dSCy Schubert         if (  (strneqvcmp(txt, opts->pzProgName, (int)name_len) == 0)
647*a466cc55SCy Schubert            && (IS_END_XML_TOKEN_CHAR(txt[name_len])) )
6482b15cb3dSCy Schubert 
649*a466cc55SCy Schubert             return txt + name_len;
6502b15cb3dSCy Schubert 
651*a466cc55SCy Schubert         txt = strstr(txt, zCfgProg);
652*a466cc55SCy Schubert         if (txt == NULL)
6532b15cb3dSCy Schubert             return txt;
654ea906c41SOllivier Robert     }
655ea906c41SOllivier Robert 
656*a466cc55SCy Schubert     for (;;) {
657*a466cc55SCy Schubert         if (*txt == NUL)
658*a466cc55SCy Schubert             return NULL;
659*a466cc55SCy Schubert 
660*a466cc55SCy Schubert         if (*(txt++) == '>')
661*a466cc55SCy Schubert             return txt;
662*a466cc55SCy Schubert     }
663*a466cc55SCy Schubert }
664*a466cc55SCy Schubert 
6652b15cb3dSCy Schubert /**
6662b15cb3dSCy Schubert  *  "txt" points to a '[' character.
667ea906c41SOllivier Robert  *  The "traditional" [PROG_NAME] segmentation of the config file.
668ea906c41SOllivier Robert  *  Do not ever mix with the "<?program prog-name>" variation.
669*a466cc55SCy Schubert  *  The templates reject program names over 16 characters.
6702b15cb3dSCy Schubert  *
6712b15cb3dSCy Schubert  *  @param[in,out] opts  program option descriptor
6722b15cb3dSCy Schubert  *  @param[in]     txt   scanning pointer
6732b15cb3dSCy Schubert  *  @returns       the next character to look at
674ea906c41SOllivier Robert  */
675ea906c41SOllivier Robert static char *
handle_section(tOptions * opts,char * txt)6762b15cb3dSCy Schubert handle_section(tOptions * opts, char * txt)
677ea906c41SOllivier Robert {
6782b15cb3dSCy Schubert     size_t len = strlen(opts->pzPROGNAME);
6792b15cb3dSCy Schubert     if (   (strncmp(txt+1, opts->pzPROGNAME, len) == 0)
6802b15cb3dSCy Schubert         && (txt[len+1] == ']'))
6812b15cb3dSCy Schubert         return strchr(txt + len + 2, NL);
682ea906c41SOllivier Robert 
683ea906c41SOllivier Robert     if (len > 16)
684ea906c41SOllivier Robert         return NULL;
685ea906c41SOllivier Robert 
686ea906c41SOllivier Robert     {
687*a466cc55SCy Schubert         char z[24] = "[";
688*a466cc55SCy Schubert         memcpy(z+1, opts->pzPROGNAME, len);
689*a466cc55SCy Schubert         z[++len] = ']';
690*a466cc55SCy Schubert         z[++len] = NUL;
6912b15cb3dSCy Schubert         txt = strstr(txt, z);
692ea906c41SOllivier Robert     }
693ea906c41SOllivier Robert 
6942b15cb3dSCy Schubert     if (txt != NULL)
6952b15cb3dSCy Schubert         txt = strchr(txt, NL);
6962b15cb3dSCy Schubert     return txt;
697ea906c41SOllivier Robert }
698ea906c41SOllivier Robert 
6992b15cb3dSCy Schubert /**
7002b15cb3dSCy Schubert  * parse XML encodings
7012b15cb3dSCy Schubert  */
7022b15cb3dSCy Schubert static int
parse_xml_encoding(char ** ppz)7032b15cb3dSCy Schubert parse_xml_encoding(char ** ppz)
7042b15cb3dSCy Schubert {
7052b15cb3dSCy Schubert #   define XMLTABLE             \
7062b15cb3dSCy Schubert         _xmlNm_(amp,   '&')     \
7072b15cb3dSCy Schubert         _xmlNm_(lt,    '<')     \
7082b15cb3dSCy Schubert         _xmlNm_(gt,    '>')     \
7092b15cb3dSCy Schubert         _xmlNm_(ff,    '\f')    \
7102b15cb3dSCy Schubert         _xmlNm_(ht,    '\t')    \
7112b15cb3dSCy Schubert         _xmlNm_(cr,    '\r')    \
7122b15cb3dSCy Schubert         _xmlNm_(vt,    '\v')    \
7132b15cb3dSCy Schubert         _xmlNm_(bel,   '\a')    \
7142b15cb3dSCy Schubert         _xmlNm_(nl,    NL)      \
7152b15cb3dSCy Schubert         _xmlNm_(space, ' ')     \
7162b15cb3dSCy Schubert         _xmlNm_(quot,  '"')     \
7172b15cb3dSCy Schubert         _xmlNm_(apos,  '\'')
718ea906c41SOllivier Robert 
7192b15cb3dSCy Schubert     static struct {
7202b15cb3dSCy Schubert         char const * const  nm_str;
7212b15cb3dSCy Schubert         unsigned short      nm_len;
7222b15cb3dSCy Schubert         short               nm_val;
7232b15cb3dSCy Schubert     } const xml_names[] = {
7242b15cb3dSCy Schubert #   define _xmlNm_(_n, _v) { #_n ";", sizeof(#_n), _v },
7252b15cb3dSCy Schubert         XMLTABLE
7262b15cb3dSCy Schubert #   undef  _xmlNm_
7272b15cb3dSCy Schubert #   undef XMLTABLE
7282b15cb3dSCy Schubert     };
7292b15cb3dSCy Schubert 
7302b15cb3dSCy Schubert     static int const nm_ct = sizeof(xml_names) / sizeof(xml_names[0]);
7312b15cb3dSCy Schubert     int    base = 10;
7322b15cb3dSCy Schubert 
7332b15cb3dSCy Schubert     char * pz = *ppz;
7342b15cb3dSCy Schubert 
7352b15cb3dSCy Schubert     if (*pz == '#') {
7362b15cb3dSCy Schubert         pz++;
7372b15cb3dSCy Schubert         goto parse_number;
7382b15cb3dSCy Schubert     }
7392b15cb3dSCy Schubert 
7402b15cb3dSCy Schubert     if (IS_DEC_DIGIT_CHAR(*pz)) {
7412b15cb3dSCy Schubert         unsigned long v;
7422b15cb3dSCy Schubert 
7432b15cb3dSCy Schubert     parse_number:
7442b15cb3dSCy Schubert         switch (*pz) {
7452b15cb3dSCy Schubert         case 'x': case 'X':
7462b15cb3dSCy Schubert             /*
7472b15cb3dSCy Schubert              * Some forms specify hex with:  &#xNN;
7482b15cb3dSCy Schubert              */
7492b15cb3dSCy Schubert             base = 16;
7502b15cb3dSCy Schubert             pz++;
7512b15cb3dSCy Schubert             break;
7522b15cb3dSCy Schubert 
7532b15cb3dSCy Schubert         case '0':
7542b15cb3dSCy Schubert             /*
7552b15cb3dSCy Schubert              *  &#0022; is hex and &#22; is decimal.  Cool.
7562b15cb3dSCy Schubert              *  Ya gotta love it.
7572b15cb3dSCy Schubert              */
7582b15cb3dSCy Schubert             if (pz[1] == '0')
7592b15cb3dSCy Schubert                 base = 16;
7602b15cb3dSCy Schubert             break;
7612b15cb3dSCy Schubert         }
7622b15cb3dSCy Schubert 
7632b15cb3dSCy Schubert         v = strtoul(pz, &pz, base);
7642b15cb3dSCy Schubert         if ((*pz != ';') || (v > 0x7F))
7652b15cb3dSCy Schubert             return NUL;
7662b15cb3dSCy Schubert         *ppz = pz + 1;
7672b15cb3dSCy Schubert         return (int)v;
7682b15cb3dSCy Schubert     }
7692b15cb3dSCy Schubert 
7702b15cb3dSCy Schubert     {
7712b15cb3dSCy Schubert         int ix = 0;
7722b15cb3dSCy Schubert         do  {
7732b15cb3dSCy Schubert             if (strncmp(pz, xml_names[ix].nm_str, xml_names[ix].nm_len)
7742b15cb3dSCy Schubert                 == 0) {
7752b15cb3dSCy Schubert                 *ppz = pz + xml_names[ix].nm_len;
7762b15cb3dSCy Schubert                 return xml_names[ix].nm_val;
7772b15cb3dSCy Schubert             }
7782b15cb3dSCy Schubert         } while (++ix < nm_ct);
7792b15cb3dSCy Schubert     }
7802b15cb3dSCy Schubert 
7812b15cb3dSCy Schubert     return NUL;
7822b15cb3dSCy Schubert }
7832b15cb3dSCy Schubert 
7842b15cb3dSCy Schubert /**
7852b15cb3dSCy Schubert  * Find the end marker for the named section of XML.
7862b15cb3dSCy Schubert  * Trim that text there, trimming trailing white space for all modes
7872b15cb3dSCy Schubert  * except for OPTION_LOAD_UNCOOKED.
7882b15cb3dSCy Schubert  */
7892b15cb3dSCy Schubert static char *
trim_xml_text(char * intxt,char const * pznm,tOptionLoadMode mode)7902b15cb3dSCy Schubert trim_xml_text(char * intxt, char const * pznm, tOptionLoadMode mode)
7912b15cb3dSCy Schubert {
792*a466cc55SCy Schubert     size_t nm_len = strlen(pznm);
7932b15cb3dSCy Schubert     char * etext;
7942b15cb3dSCy Schubert 
7952b15cb3dSCy Schubert     {
7962b15cb3dSCy Schubert         char z[64], *pz = z;
7972b15cb3dSCy Schubert 
798*a466cc55SCy Schubert         if (nm_len + 4 >= sizeof(z))
799*a466cc55SCy Schubert             pz = AGALOC(nm_len + 4, "scan name");
800*a466cc55SCy Schubert 
801*a466cc55SCy Schubert         pz[0] = '<';
802*a466cc55SCy Schubert         pz[1] = '/';
803*a466cc55SCy Schubert         memcpy(pz+2, pznm, nm_len);
804*a466cc55SCy Schubert         nm_len  += 2;
805*a466cc55SCy Schubert         pz[nm_len++] = '>';
806*a466cc55SCy Schubert         pz[nm_len]   = NUL;
807*a466cc55SCy Schubert 
8082b15cb3dSCy Schubert         *intxt = ' ';
8092b15cb3dSCy Schubert         etext = strstr(intxt, pz);
8102b15cb3dSCy Schubert         if (pz != z) AGFREE(pz);
8112b15cb3dSCy Schubert     }
8122b15cb3dSCy Schubert 
8132b15cb3dSCy Schubert     if (etext == NULL)
8142b15cb3dSCy Schubert         return etext;
8152b15cb3dSCy Schubert 
8162b15cb3dSCy Schubert     {
817*a466cc55SCy Schubert         char * result = etext + nm_len;
8182b15cb3dSCy Schubert 
8192b15cb3dSCy Schubert         if (mode != OPTION_LOAD_UNCOOKED)
8202b15cb3dSCy Schubert             etext = SPN_WHITESPACE_BACK(intxt, etext);
8212b15cb3dSCy Schubert 
8222b15cb3dSCy Schubert         *etext = NUL;
8232b15cb3dSCy Schubert         return result;
8242b15cb3dSCy Schubert     }
8252b15cb3dSCy Schubert }
8262b15cb3dSCy Schubert 
8272b15cb3dSCy Schubert /**
8282b15cb3dSCy Schubert  */
8292b15cb3dSCy Schubert static void
cook_xml_text(char * pzData)8302b15cb3dSCy Schubert cook_xml_text(char * pzData)
8312b15cb3dSCy Schubert {
8322b15cb3dSCy Schubert     char * pzs = pzData;
8332b15cb3dSCy Schubert     char * pzd = pzData;
8342b15cb3dSCy Schubert     char   bf[4];
8352b15cb3dSCy Schubert     bf[2] = NUL;
8362b15cb3dSCy Schubert 
8372b15cb3dSCy Schubert     for (;;) {
8382b15cb3dSCy Schubert         int ch = ((int)*(pzs++)) & 0xFF;
8392b15cb3dSCy Schubert         switch (ch) {
8402b15cb3dSCy Schubert         case NUL:
8412b15cb3dSCy Schubert             *pzd = NUL;
8422b15cb3dSCy Schubert             return;
8432b15cb3dSCy Schubert 
8442b15cb3dSCy Schubert         case '&':
8452b15cb3dSCy Schubert             ch = parse_xml_encoding(&pzs);
8462b15cb3dSCy Schubert             *(pzd++) = (char)ch;
8472b15cb3dSCy Schubert             if (ch == NUL)
8482b15cb3dSCy Schubert                 return;
8492b15cb3dSCy Schubert             break;
8502b15cb3dSCy Schubert 
8512b15cb3dSCy Schubert         case '%':
8522b15cb3dSCy Schubert             bf[0] = *(pzs++);
8532b15cb3dSCy Schubert             bf[1] = *(pzs++);
8542b15cb3dSCy Schubert             if ((bf[0] == NUL) || (bf[1] == NUL)) {
8552b15cb3dSCy Schubert                 *pzd = NUL;
8562b15cb3dSCy Schubert                 return;
8572b15cb3dSCy Schubert             }
8582b15cb3dSCy Schubert 
8592b15cb3dSCy Schubert             ch = (int)strtoul(bf, NULL, 16);
8602b15cb3dSCy Schubert             /* FALLTHROUGH */
8612b15cb3dSCy Schubert 
8622b15cb3dSCy Schubert         default:
8632b15cb3dSCy Schubert             *(pzd++) = (char)ch;
8642b15cb3dSCy Schubert         }
8652b15cb3dSCy Schubert     }
8662b15cb3dSCy Schubert }
8672b15cb3dSCy Schubert 
8682b15cb3dSCy Schubert /**
8692b15cb3dSCy Schubert  *  "txt" points to a '<' character, followed by an alpha.
870ea906c41SOllivier Robert  *  The end of the entry is either the "/>" following the name, or else a
871ea906c41SOllivier Robert  *  "</name>" string.
872ea906c41SOllivier Robert  */
873ea906c41SOllivier Robert static char *
handle_struct(tOptions * opts,tOptState * ost,char * txt,int dir)8742b15cb3dSCy Schubert handle_struct(tOptions * opts, tOptState * ost, char * txt, int dir)
875ea906c41SOllivier Robert {
876ea906c41SOllivier Robert     tOptionLoadMode mode = option_load_mode;
877ea906c41SOllivier Robert     tOptionValue    valu;
878ea906c41SOllivier Robert 
8792b15cb3dSCy Schubert     char * pzName = ++txt;
880ea906c41SOllivier Robert     char * pzData;
881ea906c41SOllivier Robert     char * pcNulPoint;
882ea906c41SOllivier Robert 
8832b15cb3dSCy Schubert     txt = SPN_VALUE_NAME_CHARS(txt);
8842b15cb3dSCy Schubert     pcNulPoint = txt;
885ea906c41SOllivier Robert     valu.valType = OPARG_TYPE_STRING;
886ea906c41SOllivier Robert 
8872b15cb3dSCy Schubert     switch (*txt) {
888ea906c41SOllivier Robert     case ' ':
889ea906c41SOllivier Robert     case '\t':
890276da39aSCy Schubert         txt = VOIDP(parse_attrs(
891276da39aSCy Schubert             opts, SPN_WHITESPACE_CHARS(txt), &mode, &valu));
8922b15cb3dSCy Schubert         if (txt == NULL)
8932b15cb3dSCy Schubert             return txt;
8942b15cb3dSCy Schubert         if (*txt == '>')
895ea906c41SOllivier Robert             break;
8962b15cb3dSCy Schubert         if (*txt != '/')
897ea906c41SOllivier Robert             return NULL;
898ea906c41SOllivier Robert         /* FALLTHROUGH */
899ea906c41SOllivier Robert 
900ea906c41SOllivier Robert     case '/':
9012b15cb3dSCy Schubert         if (txt[1] != '>')
902ea906c41SOllivier Robert             return NULL;
9032b15cb3dSCy Schubert         *txt = NUL;
9042b15cb3dSCy Schubert         txt += 2;
9052b15cb3dSCy Schubert         load_opt_line(opts, ost, pzName, dir, mode);
9062b15cb3dSCy Schubert         return txt;
907ea906c41SOllivier Robert 
908ea906c41SOllivier Robert     case '>':
909ea906c41SOllivier Robert         break;
910ea906c41SOllivier Robert 
911ea906c41SOllivier Robert     default:
9122b15cb3dSCy Schubert         txt = strchr(txt, '>');
9132b15cb3dSCy Schubert         if (txt != NULL)
9142b15cb3dSCy Schubert             txt++;
9152b15cb3dSCy Schubert         return txt;
916ea906c41SOllivier Robert     }
917ea906c41SOllivier Robert 
918ea906c41SOllivier Robert     /*
9192b15cb3dSCy Schubert      *  If we are here, we have a value.  "txt" points to a closing angle
920ea906c41SOllivier Robert      *  bracket.  Separate the name from the value for a moment.
921ea906c41SOllivier Robert      */
922ea906c41SOllivier Robert     *pcNulPoint = NUL;
9232b15cb3dSCy Schubert     pzData = ++txt;
9242b15cb3dSCy Schubert     txt = trim_xml_text(txt, pzName, mode);
9252b15cb3dSCy Schubert     if (txt == NULL)
9262b15cb3dSCy Schubert         return txt;
927ea906c41SOllivier Robert 
928ea906c41SOllivier Robert     /*
9292b15cb3dSCy Schubert      *  Rejoin the name and value for parsing by "load_opt_line()".
9302b15cb3dSCy Schubert      *  Erase any attributes parsed by "parse_attrs()".
931ea906c41SOllivier Robert      */
9322b15cb3dSCy Schubert     memset(pcNulPoint, ' ', (size_t)(pzData - pcNulPoint));
933ea906c41SOllivier Robert 
934ea906c41SOllivier Robert     /*
9352b15cb3dSCy Schubert      *  If we are getting a "string" value that is to be cooked,
9362b15cb3dSCy Schubert      *  then process the XML-ish &xx; XML-ish and %XX hex characters.
937ea906c41SOllivier Robert      */
9382b15cb3dSCy Schubert     if (  (valu.valType == OPARG_TYPE_STRING)
9392b15cb3dSCy Schubert        && (mode == OPTION_LOAD_COOKED))
9402b15cb3dSCy Schubert         cook_xml_text(pzData);
941ea906c41SOllivier Robert 
942ea906c41SOllivier Robert     /*
943ea906c41SOllivier Robert      *  "pzName" points to what looks like text for one option/configurable.
944ea906c41SOllivier Robert      *  It is NUL terminated.  Process it.
945ea906c41SOllivier Robert      */
9462b15cb3dSCy Schubert     load_opt_line(opts, ost, pzName, dir, mode);
947ea906c41SOllivier Robert 
9482b15cb3dSCy Schubert     return txt;
949ea906c41SOllivier Robert }
950ea906c41SOllivier Robert 
9512b15cb3dSCy Schubert /**
952ea906c41SOllivier Robert  *  Load a configuration file.  This may be invoked either from
953ea906c41SOllivier Robert  *  scanning the "homerc" list, or from a specific file request.
954ea906c41SOllivier Robert  *  (see "optionFileLoad()", the implementation for --load-opts)
955ea906c41SOllivier Robert  */
956*a466cc55SCy Schubert static void
intern_file_load(tOptions * opts)9572b15cb3dSCy Schubert intern_file_load(tOptions * opts)
958ea906c41SOllivier Robert {
9592b15cb3dSCy Schubert     uint32_t  svfl;
960ea906c41SOllivier Robert     int       idx;
9612b15cb3dSCy Schubert     int       inc;
9622b15cb3dSCy Schubert     char      f_name[ AG_PATH_MAX+1 ];
963ea906c41SOllivier Robert 
9642b15cb3dSCy Schubert     if (opts->papzHomeList == NULL)
965ea906c41SOllivier Robert         return;
966ea906c41SOllivier Robert 
9672b15cb3dSCy Schubert     svfl = opts->fOptSet;
9682b15cb3dSCy Schubert     inc  = DIRECTION_PRESET;
9692b15cb3dSCy Schubert 
9702b15cb3dSCy Schubert     /*
9712b15cb3dSCy Schubert      *  Never stop on errors in config files.
9722b15cb3dSCy Schubert      */
9732b15cb3dSCy Schubert     opts->fOptSet &= ~OPTPROC_ERRSTOP;
9742b15cb3dSCy Schubert 
975ea906c41SOllivier Robert     /*
976ea906c41SOllivier Robert      *  Find the last RC entry (highest priority entry)
977ea906c41SOllivier Robert      */
9782b15cb3dSCy Schubert     for (idx = 0; opts->papzHomeList[ idx+1 ] != NULL; ++idx)  ;
979ea906c41SOllivier Robert 
980ea906c41SOllivier Robert     /*
981ea906c41SOllivier Robert      *  For every path in the home list, ...  *TWICE* We start at the last
982ea906c41SOllivier Robert      *  (highest priority) entry, work our way down to the lowest priority,
983ea906c41SOllivier Robert      *  handling the immediate options.
984ea906c41SOllivier Robert      *  Then we go back up, doing the normal options.
985ea906c41SOllivier Robert      */
986ea906c41SOllivier Robert     for (;;) {
9872b15cb3dSCy Schubert         struct stat sb;
9882b15cb3dSCy Schubert         cch_t *  path;
989ea906c41SOllivier Robert 
990ea906c41SOllivier Robert         /*
991ea906c41SOllivier Robert          *  IF we've reached the bottom end, change direction
992ea906c41SOllivier Robert          */
993ea906c41SOllivier Robert         if (idx < 0) {
994ea906c41SOllivier Robert             inc = DIRECTION_PROCESS;
995ea906c41SOllivier Robert             idx = 0;
996ea906c41SOllivier Robert         }
997ea906c41SOllivier Robert 
9982b15cb3dSCy Schubert         path = opts->papzHomeList[ idx ];
999ea906c41SOllivier Robert 
1000ea906c41SOllivier Robert         /*
1001ea906c41SOllivier Robert          *  IF we've reached the top end, bail out
1002ea906c41SOllivier Robert          */
10032b15cb3dSCy Schubert         if (path == NULL)
1004ea906c41SOllivier Robert             break;
1005ea906c41SOllivier Robert 
1006ea906c41SOllivier Robert         idx += inc;
1007ea906c41SOllivier Robert 
10082b15cb3dSCy Schubert         if (! optionMakePath(f_name, (int)sizeof(f_name),
10092b15cb3dSCy Schubert                              path, opts->pzProgPath))
1010ea906c41SOllivier Robert             continue;
1011ea906c41SOllivier Robert 
1012ea906c41SOllivier Robert         /*
1013ea906c41SOllivier Robert          *  IF the file name we constructed is a directory,
1014ea906c41SOllivier Robert          *  THEN append the Resource Configuration file name
1015ea906c41SOllivier Robert          *  ELSE we must have the complete file name
1016ea906c41SOllivier Robert          */
10172b15cb3dSCy Schubert         if (stat(f_name, &sb) != 0)
1018ea906c41SOllivier Robert             continue; /* bogus name - skip the home list entry */
1019ea906c41SOllivier Robert 
10202b15cb3dSCy Schubert         if (S_ISDIR(sb.st_mode)) {
10212b15cb3dSCy Schubert             size_t len = strlen(f_name);
10222b15cb3dSCy Schubert             size_t nln = strlen(opts->pzRcName) + 1;
10232b15cb3dSCy Schubert             char * pz  = f_name + len;
1024ea906c41SOllivier Robert 
10252b15cb3dSCy Schubert             if (len + 1 + nln >= sizeof(f_name))
1026ea906c41SOllivier Robert                 continue;
1027ea906c41SOllivier Robert 
1028ea906c41SOllivier Robert             if (pz[-1] != DIRCH)
1029ea906c41SOllivier Robert                 *(pz++) = DIRCH;
10302b15cb3dSCy Schubert             memcpy(pz, opts->pzRcName, nln);
1031ea906c41SOllivier Robert         }
1032ea906c41SOllivier Robert 
10332b15cb3dSCy Schubert         file_preset(opts, f_name, inc);
1034ea906c41SOllivier Robert 
1035ea906c41SOllivier Robert         /*
1036ea906c41SOllivier Robert          *  IF we are now to skip config files AND we are presetting,
1037ea906c41SOllivier Robert          *  THEN change direction.  We must go the other way.
1038ea906c41SOllivier Robert          */
1039ea906c41SOllivier Robert         {
10402b15cb3dSCy Schubert             tOptDesc * od = opts->pOptDesc + opts->specOptIdx.save_opts + 1;
10412b15cb3dSCy Schubert             if (DISABLED_OPT(od) && PRESETTING(inc)) {
1042ea906c41SOllivier Robert                 idx -= inc;  /* go back and reprocess current file */
1043ea906c41SOllivier Robert                 inc =  DIRECTION_PROCESS;
1044ea906c41SOllivier Robert             }
1045ea906c41SOllivier Robert         }
1046ea906c41SOllivier Robert     } /* twice for every path in the home list, ... */
1047ea906c41SOllivier Robert 
10482b15cb3dSCy Schubert     opts->fOptSet = svfl;
10492b15cb3dSCy Schubert }
1050ea906c41SOllivier Robert 
1051ea906c41SOllivier Robert /*=export_func optionFileLoad
1052ea906c41SOllivier Robert  *
1053ea906c41SOllivier Robert  * what: Load the locatable config files, in order
1054ea906c41SOllivier Robert  *
10552b15cb3dSCy Schubert  * arg:  + tOptions *   + opts + program options descriptor +
10562b15cb3dSCy Schubert  * arg:  + char const * + prog + program name +
1057ea906c41SOllivier Robert  *
1058ea906c41SOllivier Robert  * ret_type:  int
1059ea906c41SOllivier Robert  * ret_desc:  0 -> SUCCESS, -1 -> FAILURE
1060ea906c41SOllivier Robert  *
1061ea906c41SOllivier Robert  * doc:
1062ea906c41SOllivier Robert  *
1063ea906c41SOllivier Robert  * This function looks in all the specified directories for a configuration
1064ea906c41SOllivier Robert  * file ("rc" file or "ini" file) and processes any found twice.  The first
1065ea906c41SOllivier Robert  * time through, they are processed in reverse order (last file first).  At
1066ea906c41SOllivier Robert  * that time, only "immediate action" configurables are processed.  For
1067ea906c41SOllivier Robert  * example, if the last named file specifies not processing any more
1068ea906c41SOllivier Robert  * configuration files, then no more configuration files will be processed.
1069ea906c41SOllivier Robert  * Such an option in the @strong{first} named directory will have no effect.
1070ea906c41SOllivier Robert  *
1071ea906c41SOllivier Robert  * Once the immediate action configurables have been handled, then the
1072ea906c41SOllivier Robert  * directories are handled in normal, forward order.  In that way, later
1073ea906c41SOllivier Robert  * config files can override the settings of earlier config files.
1074ea906c41SOllivier Robert  *
1075ea906c41SOllivier Robert  * See the AutoOpts documentation for a thorough discussion of the
1076ea906c41SOllivier Robert  * config file format.
1077ea906c41SOllivier Robert  *
1078ea906c41SOllivier Robert  * Configuration files not found or not decipherable are simply ignored.
1079ea906c41SOllivier Robert  *
1080ea906c41SOllivier Robert  * err:  Returns the value, "-1" if the program options descriptor
1081ea906c41SOllivier Robert  *       is out of date or indecipherable.  Otherwise, the value "0" will
1082ea906c41SOllivier Robert  *       always be returned.
1083ea906c41SOllivier Robert =*/
1084ea906c41SOllivier Robert int
optionFileLoad(tOptions * opts,char const * prog)10852b15cb3dSCy Schubert optionFileLoad(tOptions * opts, char const * prog)
1086ea906c41SOllivier Robert {
10872b15cb3dSCy Schubert     if (! SUCCESSFUL(validate_struct(opts, prog)))
1088ea906c41SOllivier Robert         return -1;
1089ea906c41SOllivier Robert 
10902b15cb3dSCy Schubert     /*
10912b15cb3dSCy Schubert      * The pointer to the program name is "const".  However, the
10922b15cb3dSCy Schubert      * structure is in writable memory, so we coerce the address
10932b15cb3dSCy Schubert      * of this pointer to point to writable memory.
10942b15cb3dSCy Schubert      */
10952b15cb3dSCy Schubert     {
1096276da39aSCy Schubert         char const ** pp = VOIDP(&(opts->pzProgName));
10972b15cb3dSCy Schubert         *pp = prog;
1098ea906c41SOllivier Robert     }
1099ea906c41SOllivier Robert 
11002b15cb3dSCy Schubert     intern_file_load(opts);
11012b15cb3dSCy Schubert     return 0;
11022b15cb3dSCy Schubert }
1103ea906c41SOllivier Robert 
1104ea906c41SOllivier Robert /*=export_func  optionLoadOpt
1105ea906c41SOllivier Robert  * private:
1106ea906c41SOllivier Robert  *
1107ea906c41SOllivier Robert  * what:  Load an option rc/ini file
11082b15cb3dSCy Schubert  * arg:   + tOptions * + opts  + program options descriptor +
11092b15cb3dSCy Schubert  * arg:   + tOptDesc * + odesc + the descriptor for this arg +
1110ea906c41SOllivier Robert  *
1111ea906c41SOllivier Robert  * doc:
1112ea906c41SOllivier Robert  *  Processes the options found in the file named with
11132b15cb3dSCy Schubert  *  odesc->optArg.argString.
1114ea906c41SOllivier Robert =*/
1115ea906c41SOllivier Robert void
optionLoadOpt(tOptions * opts,tOptDesc * odesc)11162b15cb3dSCy Schubert optionLoadOpt(tOptions * opts, tOptDesc * odesc)
1117ea906c41SOllivier Robert {
11182b15cb3dSCy Schubert     struct stat sb;
11192b15cb3dSCy Schubert 
11202b15cb3dSCy Schubert     if (opts <= OPTPROC_EMIT_LIMIT)
11212b15cb3dSCy Schubert         return;
11222b15cb3dSCy Schubert 
1123ea906c41SOllivier Robert     /*
1124ea906c41SOllivier Robert      *  IF the option is not being disabled, THEN load the file.  There must
1125ea906c41SOllivier Robert      *  be a file.  (If it is being disabled, then the disablement processing
1126ea906c41SOllivier Robert      *  already took place.  It must be done to suppress preloading of ini/rc
1127ea906c41SOllivier Robert      *  files.)
1128ea906c41SOllivier Robert      */
11292b15cb3dSCy Schubert     if (  DISABLED_OPT(odesc)
11302b15cb3dSCy Schubert        || ((odesc->fOptState & OPTST_RESET) != 0))
1131ea906c41SOllivier Robert         return;
1132ea906c41SOllivier Robert 
11332b15cb3dSCy Schubert     if (stat(odesc->optArg.argString, &sb) != 0) {
11342b15cb3dSCy Schubert         if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
11352b15cb3dSCy Schubert             return;
11362b15cb3dSCy Schubert 
11372b15cb3dSCy Schubert         fserr_exit(opts->pzProgName, "stat", odesc->optArg.argString);
1138ea906c41SOllivier Robert         /* NOT REACHED */
1139ea906c41SOllivier Robert     }
1140ea906c41SOllivier Robert 
1141ea906c41SOllivier Robert     if (! S_ISREG(sb.st_mode)) {
11422b15cb3dSCy Schubert         if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
1143ea906c41SOllivier Robert             return;
11442b15cb3dSCy Schubert         errno = EINVAL;
11452b15cb3dSCy Schubert         fserr_exit(opts->pzProgName, "stat", odesc->optArg.argString);
1146ea906c41SOllivier Robert         /* NOT REACHED */
1147ea906c41SOllivier Robert     }
1148ea906c41SOllivier Robert 
11492b15cb3dSCy Schubert     file_preset(opts, odesc->optArg.argString, DIRECTION_CALLED);
1150ea906c41SOllivier Robert }
1151ea906c41SOllivier Robert 
1152ea906c41SOllivier Robert /**
11532b15cb3dSCy Schubert  *  Parse the various attributes of an XML-styled config file entry
11542b15cb3dSCy Schubert  *
11552b15cb3dSCy Schubert  * @returns NULL on failure, otherwise the scan point
11562b15cb3dSCy Schubert  */
1157*a466cc55SCy Schubert static char const *
parse_attrs(tOptions * opts,char const * txt,tOptionLoadMode * pMode,tOptionValue * pType)11582b15cb3dSCy Schubert parse_attrs(tOptions * opts, char const * txt, tOptionLoadMode * pMode,
11592b15cb3dSCy Schubert             tOptionValue * pType)
11602b15cb3dSCy Schubert {
11612b15cb3dSCy Schubert     size_t len = 0;
11622b15cb3dSCy Schubert 
11632b15cb3dSCy Schubert     for (;;) {
11642b15cb3dSCy Schubert         len = (size_t)(SPN_LOWER_CASE_CHARS(txt) - txt);
11652b15cb3dSCy Schubert 
11662b15cb3dSCy Schubert         /*
11672b15cb3dSCy Schubert          * The enumeration used in this switch is derived from this switch
11682b15cb3dSCy Schubert          * statement itself.  The "find_option_xat_attribute_cmd" function
11692b15cb3dSCy Schubert          * will return XAT_CMD_MEMBERS for the "txt" string value
11702b15cb3dSCy Schubert          * "members", etc.
11712b15cb3dSCy Schubert          */
11722b15cb3dSCy Schubert         switch (find_option_xat_attribute_cmd(txt, len)) {
11732b15cb3dSCy Schubert         case XAT_CMD_TYPE:
11742b15cb3dSCy Schubert             txt = parse_value(txt+len, pType);
11752b15cb3dSCy Schubert             break;
11762b15cb3dSCy Schubert 
11772b15cb3dSCy Schubert         case XAT_CMD_WORDS:
11782b15cb3dSCy Schubert             txt = parse_keyword(opts, txt+len, pType);
11792b15cb3dSCy Schubert             break;
11802b15cb3dSCy Schubert 
11812b15cb3dSCy Schubert         case XAT_CMD_MEMBERS:
11822b15cb3dSCy Schubert             txt = parse_set_mem(opts, txt+len, pType);
11832b15cb3dSCy Schubert             break;
11842b15cb3dSCy Schubert 
11852b15cb3dSCy Schubert         case XAT_CMD_COOKED:
11862b15cb3dSCy Schubert             txt += len;
11872b15cb3dSCy Schubert             if (! IS_END_XML_TOKEN_CHAR(*txt))
11882b15cb3dSCy Schubert                 goto invalid_kwd;
11892b15cb3dSCy Schubert 
11902b15cb3dSCy Schubert             *pMode = OPTION_LOAD_COOKED;
11912b15cb3dSCy Schubert             break;
11922b15cb3dSCy Schubert 
11932b15cb3dSCy Schubert         case XAT_CMD_UNCOOKED:
11942b15cb3dSCy Schubert             txt += len;
11952b15cb3dSCy Schubert             if (! IS_END_XML_TOKEN_CHAR(*txt))
11962b15cb3dSCy Schubert                 goto invalid_kwd;
11972b15cb3dSCy Schubert 
11982b15cb3dSCy Schubert             *pMode = OPTION_LOAD_UNCOOKED;
11992b15cb3dSCy Schubert             break;
12002b15cb3dSCy Schubert 
12012b15cb3dSCy Schubert         case XAT_CMD_KEEP:
12022b15cb3dSCy Schubert             txt += len;
12032b15cb3dSCy Schubert             if (! IS_END_XML_TOKEN_CHAR(*txt))
12042b15cb3dSCy Schubert                 goto invalid_kwd;
12052b15cb3dSCy Schubert 
12062b15cb3dSCy Schubert             *pMode = OPTION_LOAD_KEEP;
12072b15cb3dSCy Schubert             break;
12082b15cb3dSCy Schubert 
12092b15cb3dSCy Schubert         default:
12102b15cb3dSCy Schubert         case XAT_INVALID_CMD:
12112b15cb3dSCy Schubert         invalid_kwd:
12122b15cb3dSCy Schubert             pType->valType = OPARG_TYPE_NONE;
12132b15cb3dSCy Schubert             return skip_unkn(txt);
12142b15cb3dSCy Schubert         }
12152b15cb3dSCy Schubert 
12162b15cb3dSCy Schubert         if (txt == NULL)
12172b15cb3dSCy Schubert             return NULL;
12182b15cb3dSCy Schubert         txt = SPN_WHITESPACE_CHARS(txt);
12192b15cb3dSCy Schubert         switch (*txt) {
12202b15cb3dSCy Schubert             case '/': pType->valType = OPARG_TYPE_NONE;
12212b15cb3dSCy Schubert                       /* FALLTHROUGH */
12222b15cb3dSCy Schubert             case '>': return txt;
12232b15cb3dSCy Schubert         }
12242b15cb3dSCy Schubert         if (! IS_LOWER_CASE_CHAR(*txt))
12252b15cb3dSCy Schubert             return NULL;
12262b15cb3dSCy Schubert     }
12272b15cb3dSCy Schubert }
12282b15cb3dSCy Schubert 
12292b15cb3dSCy Schubert /**
12302b15cb3dSCy Schubert  *  "txt" points to the character after "words=".
12312b15cb3dSCy Schubert  *  What should follow is a name of a keyword (enumeration) list.
12322b15cb3dSCy Schubert  *
12332b15cb3dSCy Schubert  *  @param     opts  unused
12342b15cb3dSCy Schubert  *  @param[in] txt   keyword to skip over
12352b15cb3dSCy Schubert  *  @param     type  unused value type
12362b15cb3dSCy Schubert  *  @returns   pointer after skipped text
12372b15cb3dSCy Schubert  */
12382b15cb3dSCy Schubert static char const *
parse_keyword(tOptions * opts,char const * txt,tOptionValue * typ)12392b15cb3dSCy Schubert parse_keyword(tOptions * opts, char const * txt, tOptionValue * typ)
12402b15cb3dSCy Schubert {
12412b15cb3dSCy Schubert     (void)opts;
12422b15cb3dSCy Schubert     (void)typ;
12432b15cb3dSCy Schubert 
12442b15cb3dSCy Schubert     return skip_unkn(txt);
12452b15cb3dSCy Schubert }
12462b15cb3dSCy Schubert 
12472b15cb3dSCy Schubert /**
12482b15cb3dSCy Schubert  *  "txt" points to the character after "members="
12492b15cb3dSCy Schubert  *  What should follow is a name of a "set membership".
12502b15cb3dSCy Schubert  *  A collection of bit flags.
12512b15cb3dSCy Schubert  *
12522b15cb3dSCy Schubert  *  @param     opts  unused
12532b15cb3dSCy Schubert  *  @param[in] txt   keyword to skip over
12542b15cb3dSCy Schubert  *  @param     type  unused value type
12552b15cb3dSCy Schubert  *  @returns   pointer after skipped text
12562b15cb3dSCy Schubert  */
12572b15cb3dSCy Schubert static char const *
parse_set_mem(tOptions * opts,char const * txt,tOptionValue * typ)12582b15cb3dSCy Schubert parse_set_mem(tOptions * opts, char const * txt, tOptionValue * typ)
12592b15cb3dSCy Schubert {
12602b15cb3dSCy Schubert     (void)opts;
12612b15cb3dSCy Schubert     (void)typ;
12622b15cb3dSCy Schubert 
12632b15cb3dSCy Schubert     return skip_unkn(txt);
12642b15cb3dSCy Schubert }
12652b15cb3dSCy Schubert 
12662b15cb3dSCy Schubert /**
12672b15cb3dSCy Schubert  *  parse the type.  The keyword "type" was found, now figure out
12682b15cb3dSCy Schubert  *  the type that follows the type.
12692b15cb3dSCy Schubert  *
12702b15cb3dSCy Schubert  *  @param[in]  txt  points to the '=' character after the "type" keyword.
12712b15cb3dSCy Schubert  *  @param[out] typ  where to store the type found
12722b15cb3dSCy Schubert  *  @returns    the next byte after the type name
12732b15cb3dSCy Schubert  */
12742b15cb3dSCy Schubert static char const *
parse_value(char const * txt,tOptionValue * typ)12752b15cb3dSCy Schubert parse_value(char const * txt, tOptionValue * typ)
12762b15cb3dSCy Schubert {
12772b15cb3dSCy Schubert     size_t len = 0;
12782b15cb3dSCy Schubert 
12792b15cb3dSCy Schubert     if (*(txt++) != '=')
12802b15cb3dSCy Schubert         goto woops;
12812b15cb3dSCy Schubert 
12822b15cb3dSCy Schubert     len = (size_t)(SPN_OPTION_NAME_CHARS(txt) - txt);
12832b15cb3dSCy Schubert 
12842b15cb3dSCy Schubert     if ((len == 0) || (! IS_END_XML_TOKEN_CHAR(txt[len]))) {
12852b15cb3dSCy Schubert     woops:
12862b15cb3dSCy Schubert         typ->valType = OPARG_TYPE_NONE;
12872b15cb3dSCy Schubert         return skip_unkn(txt + len);
12882b15cb3dSCy Schubert     }
12892b15cb3dSCy Schubert 
12902b15cb3dSCy Schubert     /*
12912b15cb3dSCy Schubert      * The enumeration used in this switch is derived from this switch
12922b15cb3dSCy Schubert      * statement itself.  The "find_option_value_type_cmd" function
12932b15cb3dSCy Schubert      * will return VTP_CMD_INTEGER for the "txt" string value
12942b15cb3dSCy Schubert      * "integer", etc.
12952b15cb3dSCy Schubert      */
12962b15cb3dSCy Schubert     switch (find_option_value_type_cmd(txt, len)) {
12972b15cb3dSCy Schubert     default:
12982b15cb3dSCy Schubert     case VTP_INVALID_CMD: goto woops;
12992b15cb3dSCy Schubert 
13002b15cb3dSCy Schubert     case VTP_CMD_STRING:
13012b15cb3dSCy Schubert         typ->valType = OPARG_TYPE_STRING;
13022b15cb3dSCy Schubert         break;
13032b15cb3dSCy Schubert 
13042b15cb3dSCy Schubert     case VTP_CMD_INTEGER:
13052b15cb3dSCy Schubert         typ->valType = OPARG_TYPE_NUMERIC;
13062b15cb3dSCy Schubert         break;
13072b15cb3dSCy Schubert 
13082b15cb3dSCy Schubert     case VTP_CMD_BOOL:
13092b15cb3dSCy Schubert     case VTP_CMD_BOOLEAN:
13102b15cb3dSCy Schubert         typ->valType = OPARG_TYPE_BOOLEAN;
13112b15cb3dSCy Schubert         break;
13122b15cb3dSCy Schubert 
13132b15cb3dSCy Schubert     case VTP_CMD_KEYWORD:
13142b15cb3dSCy Schubert         typ->valType = OPARG_TYPE_ENUMERATION;
13152b15cb3dSCy Schubert         break;
13162b15cb3dSCy Schubert 
13172b15cb3dSCy Schubert     case VTP_CMD_SET:
13182b15cb3dSCy Schubert     case VTP_CMD_SET_MEMBERSHIP:
13192b15cb3dSCy Schubert         typ->valType = OPARG_TYPE_MEMBERSHIP;
13202b15cb3dSCy Schubert         break;
13212b15cb3dSCy Schubert 
13222b15cb3dSCy Schubert     case VTP_CMD_NESTED:
13232b15cb3dSCy Schubert     case VTP_CMD_HIERARCHY:
13242b15cb3dSCy Schubert         typ->valType = OPARG_TYPE_HIERARCHY;
13252b15cb3dSCy Schubert     }
13262b15cb3dSCy Schubert 
13272b15cb3dSCy Schubert     return txt + len;
13282b15cb3dSCy Schubert }
13292b15cb3dSCy Schubert 
13302b15cb3dSCy Schubert /** @}
13312b15cb3dSCy Schubert  *
1332ea906c41SOllivier Robert  * Local Variables:
1333ea906c41SOllivier Robert  * mode: C
1334ea906c41SOllivier Robert  * c-file-style: "stroustrup"
1335ea906c41SOllivier Robert  * indent-tabs-mode: nil
1336ea906c41SOllivier Robert  * End:
1337ea906c41SOllivier Robert  * end of autoopts/configfile.c */
1338