1*4efa1683Schristos /*	$NetBSD: configfile.c,v 1.10 2020/05/25 20:54:07 christos Exp $	*/
2abb0f93cSkardel 
34305584aSkardel /**
44305584aSkardel  * \file configfile.c
54305584aSkardel  *
6abb0f93cSkardel  *  configuration/rc/ini file handling.
7abb0f93cSkardel  *
8b3d6264cSchristos  * @addtogroup autoopts
9b3d6264cSchristos  * @{
10b3d6264cSchristos  */
11b3d6264cSchristos /*
12abb0f93cSkardel  *  This file is part of AutoOpts, a companion to AutoGen.
13abb0f93cSkardel  *  AutoOpts is free software.
144e3b3909Schristos  *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
15abb0f93cSkardel  *
16abb0f93cSkardel  *  AutoOpts is available under any one of two licenses.  The license
17abb0f93cSkardel  *  in use must be one of these two and the choice is under the control
18abb0f93cSkardel  *  of the user of the license.
19abb0f93cSkardel  *
20abb0f93cSkardel  *   The GNU Lesser General Public License, version 3 or later
21abb0f93cSkardel  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
22abb0f93cSkardel  *
23abb0f93cSkardel  *   The Modified Berkeley Software Distribution License
24abb0f93cSkardel  *      See the file "COPYING.mbsd"
25abb0f93cSkardel  *
26b3d6264cSchristos  *  These files have the following sha256 sums:
27abb0f93cSkardel  *
28b3d6264cSchristos  *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
29b3d6264cSchristos  *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
30b3d6264cSchristos  *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
31abb0f93cSkardel  */
32abb0f93cSkardel 
334305584aSkardel /* = = = START-STATIC-FORWARD = = = */
344305584aSkardel static void
354305584aSkardel file_preset(tOptions * opts, char const * fname, int dir);
36abb0f93cSkardel 
37abb0f93cSkardel static char *
38b3d6264cSchristos handle_comment(char * txt);
39abb0f93cSkardel 
40abb0f93cSkardel static char *
41b3d6264cSchristos handle_cfg(tOptions * opts, tOptState * ost, char * txt, int dir);
42abb0f93cSkardel 
43abb0f93cSkardel static char *
44b3d6264cSchristos handle_directive(tOptions * opts, char * txt);
45abb0f93cSkardel 
46abb0f93cSkardel static char *
47b3d6264cSchristos aoflags_directive(tOptions * opts, char * txt);
48abb0f93cSkardel 
49abb0f93cSkardel static char *
50b3d6264cSchristos program_directive(tOptions * opts, char * txt);
51abb0f93cSkardel 
52abb0f93cSkardel static char *
53b3d6264cSchristos handle_section(tOptions * opts, char * txt);
544305584aSkardel 
554305584aSkardel static int
564305584aSkardel parse_xml_encoding(char ** ppz);
57abb0f93cSkardel 
58abb0f93cSkardel static char *
59b3d6264cSchristos trim_xml_text(char * intxt, char const * pznm, tOptionLoadMode mode);
604305584aSkardel 
614305584aSkardel static void
624305584aSkardel cook_xml_text(char * pzData);
63abb0f93cSkardel 
64abb0f93cSkardel static char *
65b3d6264cSchristos handle_struct(tOptions * opts, tOptState * ost, char * txt, int dir);
66abb0f93cSkardel 
67b3d6264cSchristos static char const *
68b3d6264cSchristos parse_keyword(tOptions * opts, char const * txt, tOptionValue * typ);
694305584aSkardel 
70b3d6264cSchristos static char const *
71b3d6264cSchristos parse_set_mem(tOptions * opts, char const * txt, tOptionValue * typ);
724305584aSkardel 
73b3d6264cSchristos static char const *
74b3d6264cSchristos parse_value(char const * txt, tOptionValue * typ);
75abb0f93cSkardel /* = = = END-STATIC-FORWARD = = = */
76abb0f93cSkardel 
77b3d6264cSchristos /**
78b3d6264cSchristos  *  Skip over some unknown attribute
79b3d6264cSchristos  *  @param[in] txt   start of skpped text
80b3d6264cSchristos  *  @returns   character after skipped text
81b3d6264cSchristos  */
82b3d6264cSchristos inline static char const *
skip_unkn(char const * txt)83b3d6264cSchristos skip_unkn(char const * txt)
84b3d6264cSchristos {
85b3d6264cSchristos     txt = BRK_END_XML_TOKEN_CHARS(txt);
86b3d6264cSchristos     return (*txt == NUL) ? NULL : txt;
87b3d6264cSchristos }
88abb0f93cSkardel 
89abb0f93cSkardel /*=export_func  configFileLoad
90abb0f93cSkardel  *
91abb0f93cSkardel  * what:  parse a configuration file
92b3d6264cSchristos  * arg:   + char const * + fname + the file to load +
93abb0f93cSkardel  *
94abb0f93cSkardel  * ret_type:  const tOptionValue *
95abb0f93cSkardel  * ret_desc:  An allocated, compound value structure
96abb0f93cSkardel  *
97abb0f93cSkardel  * doc:
98abb0f93cSkardel  *  This routine will load a named configuration file and parse the
99abb0f93cSkardel  *  text as a hierarchically valued option.  The option descriptor
100abb0f93cSkardel  *  created from an option definition file is not used via this interface.
101abb0f93cSkardel  *  The returned value is "named" with the input file name and is of
102abb0f93cSkardel  *  type "@code{OPARG_TYPE_HIERARCHY}".  It may be used in calls to
103abb0f93cSkardel  *  @code{optionGetValue()}, @code{optionNextValue()} and
104abb0f93cSkardel  *  @code{optionUnloadNested()}.
105abb0f93cSkardel  *
106abb0f93cSkardel  * err:
107abb0f93cSkardel  *  If the file cannot be loaded or processed, @code{NULL} is returned and
108abb0f93cSkardel  *  @var{errno} is set.  It may be set by a call to either @code{open(2)}
109abb0f93cSkardel  *  @code{mmap(2)} or other file system calls, or it may be:
110abb0f93cSkardel  *  @itemize @bullet
111abb0f93cSkardel  *  @item
112b3d6264cSchristos  *  @code{ENOENT} - the file was not found.
113b3d6264cSchristos  *  @item
114b3d6264cSchristos  *  @code{ENOMSG} - the file was empty.
115abb0f93cSkardel  *  @item
116abb0f93cSkardel  *  @code{EINVAL} - the file contents are invalid -- not properly formed.
117abb0f93cSkardel  *  @item
118abb0f93cSkardel  *  @code{ENOMEM} - not enough memory to allocate the needed structures.
119abb0f93cSkardel  *  @end itemize
120abb0f93cSkardel =*/
121abb0f93cSkardel const tOptionValue *
configFileLoad(char const * fname)122b3d6264cSchristos configFileLoad(char const * fname)
123abb0f93cSkardel {
124abb0f93cSkardel     tmap_info_t    cfgfile;
125b3d6264cSchristos     tOptionValue * res = NULL;
126abb0f93cSkardel     tOptionLoadMode save_mode = option_load_mode;
127abb0f93cSkardel 
128b3d6264cSchristos     char * txt = text_mmap(fname, PROT_READ, MAP_PRIVATE, &cfgfile);
129abb0f93cSkardel 
130b3d6264cSchristos     if (TEXT_MMAP_FAILED_ADDR(txt))
131abb0f93cSkardel         return NULL; /* errno is set */
132abb0f93cSkardel 
133abb0f93cSkardel     option_load_mode = OPTION_LOAD_COOKED;
134b3d6264cSchristos     res = optionLoadNested(txt, fname, strlen(fname));
135abb0f93cSkardel 
136b3d6264cSchristos     if (res == NULL) {
137abb0f93cSkardel         int err = errno;
138abb0f93cSkardel         text_munmap(&cfgfile);
139abb0f93cSkardel         errno = err;
140abb0f93cSkardel     } else
141abb0f93cSkardel         text_munmap(&cfgfile);
142abb0f93cSkardel 
143abb0f93cSkardel     option_load_mode = save_mode;
144b3d6264cSchristos     return res;
145abb0f93cSkardel }
146abb0f93cSkardel 
147abb0f93cSkardel 
148abb0f93cSkardel /*=export_func  optionFindValue
149abb0f93cSkardel  *
150abb0f93cSkardel  * what:  find a hierarcicaly valued option instance
151b3d6264cSchristos  * arg:   + const tOptDesc * + odesc + an option with a nested arg type +
152abb0f93cSkardel  * arg:   + char const *     + name  + name of value to find +
153b3d6264cSchristos  * arg:   + char const *     + val   + the matching value    +
154abb0f93cSkardel  *
155abb0f93cSkardel  * ret_type:  const tOptionValue *
156abb0f93cSkardel  * ret_desc:  a compound value structure
157abb0f93cSkardel  *
158abb0f93cSkardel  * doc:
159abb0f93cSkardel  *  This routine will find an entry in a nested value option or configurable.
160abb0f93cSkardel  *  It will search through the list and return a matching entry.
161abb0f93cSkardel  *
162abb0f93cSkardel  * err:
163abb0f93cSkardel  *  The returned result is NULL and errno is set:
164abb0f93cSkardel  *  @itemize @bullet
165abb0f93cSkardel  *  @item
166abb0f93cSkardel  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
167abb0f93cSkardel  *  hierarchical option value.
168abb0f93cSkardel  *  @item
169abb0f93cSkardel  *  @code{ENOENT} - no entry matched the given name.
170abb0f93cSkardel  *  @end itemize
171abb0f93cSkardel =*/
172abb0f93cSkardel const tOptionValue *
optionFindValue(const tOptDesc * odesc,char const * name,char const * val)173b3d6264cSchristos optionFindValue(const tOptDesc * odesc, char const * name, char const * val)
174abb0f93cSkardel {
175b3d6264cSchristos     const tOptionValue * res = NULL;
176abb0f93cSkardel 
177b3d6264cSchristos     if (  (odesc == NULL)
178b3d6264cSchristos        || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY))  {
179abb0f93cSkardel         errno = EINVAL;
180abb0f93cSkardel     }
181abb0f93cSkardel 
182b3d6264cSchristos     else if (odesc->optCookie == NULL) {
183abb0f93cSkardel         errno = ENOENT;
184abb0f93cSkardel     }
185abb0f93cSkardel 
186abb0f93cSkardel     else do {
187b3d6264cSchristos         tArgList *    argl  = odesc->optCookie;
188b3d6264cSchristos         int           argct = argl->useCt;
189335f7552Schristos         const void ** poptv = VOIDP(argl->apzArgs);
190abb0f93cSkardel 
191b3d6264cSchristos         if (argct == 0) {
192abb0f93cSkardel             errno = ENOENT;
193abb0f93cSkardel             break;
194abb0f93cSkardel         }
195abb0f93cSkardel 
196b3d6264cSchristos         if (name == NULL) {
1974e3b3909Schristos             res = (const tOptionValue *)*poptv;
198abb0f93cSkardel             break;
199abb0f93cSkardel         }
200abb0f93cSkardel 
201b3d6264cSchristos         while (--argct >= 0) {
202b3d6264cSchristos             const tOptionValue * ov = *(poptv++);
203b3d6264cSchristos             const tOptionValue * rv = optionGetValue(ov, name);
204abb0f93cSkardel 
205b3d6264cSchristos             if (rv == NULL)
206abb0f93cSkardel                 continue;
207abb0f93cSkardel 
208b3d6264cSchristos             if (val == NULL) {
209b3d6264cSchristos                 res = ov;
210abb0f93cSkardel                 break;
211abb0f93cSkardel             }
212abb0f93cSkardel         }
213b3d6264cSchristos         if (res == NULL)
214abb0f93cSkardel             errno = ENOENT;
215b3d6264cSchristos     } while (false);
216abb0f93cSkardel 
217b3d6264cSchristos     return res;
218abb0f93cSkardel }
219abb0f93cSkardel 
220abb0f93cSkardel 
221abb0f93cSkardel /*=export_func  optionFindNextValue
222abb0f93cSkardel  *
223b3d6264cSchristos  * FIXME: the handling of 'pzName' and 'pzVal' is just wrong.
224b3d6264cSchristos  *
225abb0f93cSkardel  * what:  find a hierarcicaly valued option instance
226b3d6264cSchristos  * arg:   + const tOptDesc * + odesc + an option with a nested arg type +
227abb0f93cSkardel  * arg:   + const tOptionValue * + pPrevVal + the last entry +
228abb0f93cSkardel  * arg:   + char const *     + name     + name of value to find +
229abb0f93cSkardel  * arg:   + char const *     + value    + the matching value    +
230abb0f93cSkardel  *
231abb0f93cSkardel  * ret_type:  const tOptionValue *
232abb0f93cSkardel  * ret_desc:  a compound value structure
233abb0f93cSkardel  *
234abb0f93cSkardel  * doc:
235abb0f93cSkardel  *  This routine will find the next entry in a nested value option or
236abb0f93cSkardel  *  configurable.  It will search through the list and return the next entry
237abb0f93cSkardel  *  that matches the criteria.
238abb0f93cSkardel  *
239abb0f93cSkardel  * err:
240abb0f93cSkardel  *  The returned result is NULL and errno is set:
241abb0f93cSkardel  *  @itemize @bullet
242abb0f93cSkardel  *  @item
243abb0f93cSkardel  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
244abb0f93cSkardel  *  hierarchical option value.
245abb0f93cSkardel  *  @item
246abb0f93cSkardel  *  @code{ENOENT} - no entry matched the given name.
247abb0f93cSkardel  *  @end itemize
248abb0f93cSkardel =*/
2494305584aSkardel tOptionValue const *
optionFindNextValue(const tOptDesc * odesc,const tOptionValue * pPrevVal,char const * pzName,char const * pzVal)250b3d6264cSchristos optionFindNextValue(const tOptDesc * odesc, const tOptionValue * pPrevVal,
251abb0f93cSkardel                     char const * pzName, char const * pzVal)
252abb0f93cSkardel {
253b3d6264cSchristos     bool old_found = false;
2544e3b3909Schristos     const tOptionValue * res = NULL;
255abb0f93cSkardel 
256b3d6264cSchristos     (void)pzName;
257b3d6264cSchristos     (void)pzVal;
258b3d6264cSchristos 
259b3d6264cSchristos     if (  (odesc == NULL)
260b3d6264cSchristos        || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY))  {
261abb0f93cSkardel         errno = EINVAL;
262abb0f93cSkardel     }
263abb0f93cSkardel 
264b3d6264cSchristos     else if (odesc->optCookie == NULL) {
265abb0f93cSkardel         errno = ENOENT;
266abb0f93cSkardel     }
267abb0f93cSkardel 
268abb0f93cSkardel     else do {
269b3d6264cSchristos         tArgList *    argl  = odesc->optCookie;
270b3d6264cSchristos         int           ct    = argl->useCt;
271335f7552Schristos         const void ** poptv = VOIDP(argl->apzArgs);
272abb0f93cSkardel 
273abb0f93cSkardel         while (--ct >= 0) {
2744e3b3909Schristos             const tOptionValue * pOV = *(poptv++);
275b3d6264cSchristos             if (old_found) {
276b3d6264cSchristos                 res = pOV;
277abb0f93cSkardel                 break;
278abb0f93cSkardel             }
279abb0f93cSkardel             if (pOV == pPrevVal)
280b3d6264cSchristos                 old_found = true;
281abb0f93cSkardel         }
282b3d6264cSchristos         if (res == NULL)
283abb0f93cSkardel             errno = ENOENT;
284b3d6264cSchristos     } while (false);
285abb0f93cSkardel 
286b3d6264cSchristos     return res;
287abb0f93cSkardel }
288abb0f93cSkardel 
289abb0f93cSkardel 
290abb0f93cSkardel /*=export_func  optionGetValue
291abb0f93cSkardel  *
292abb0f93cSkardel  * what:  get a specific value from a hierarcical list
293abb0f93cSkardel  * arg:   + const tOptionValue * + pOptValue + a hierarchcal value +
294abb0f93cSkardel  * arg:   + char const *         + valueName + name of value to get +
295abb0f93cSkardel  *
296abb0f93cSkardel  * ret_type:  const tOptionValue *
297abb0f93cSkardel  * ret_desc:  a compound value structure
298abb0f93cSkardel  *
299abb0f93cSkardel  * doc:
300abb0f93cSkardel  *  This routine will find an entry in a nested value option or configurable.
301abb0f93cSkardel  *  If "valueName" is NULL, then the first entry is returned.  Otherwise,
302abb0f93cSkardel  *  the first entry with a name that exactly matches the argument will be
303b3d6264cSchristos  *  returned.  If there is no matching value, NULL is returned and errno is
304b3d6264cSchristos  *  set to ENOENT. If the provided option value is not a hierarchical value,
305b3d6264cSchristos  *  NULL is also returned and errno is set to EINVAL.
306abb0f93cSkardel  *
307abb0f93cSkardel  * err:
308abb0f93cSkardel  *  The returned result is NULL and errno is set:
309abb0f93cSkardel  *  @itemize @bullet
310abb0f93cSkardel  *  @item
311abb0f93cSkardel  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
312abb0f93cSkardel  *  hierarchical option value.
313abb0f93cSkardel  *  @item
314abb0f93cSkardel  *  @code{ENOENT} - no entry matched the given name.
315abb0f93cSkardel  *  @end itemize
316abb0f93cSkardel =*/
317b3d6264cSchristos tOptionValue const *
optionGetValue(tOptionValue const * oov,char const * vname)318b3d6264cSchristos optionGetValue(tOptionValue const * oov, char const * vname)
319abb0f93cSkardel {
320b3d6264cSchristos     tArgList *           arg_list;
3214e3b3909Schristos     const tOptionValue * res = NULL;
322abb0f93cSkardel 
323b3d6264cSchristos     if ((oov == NULL) || (oov->valType != OPARG_TYPE_HIERARCHY)) {
324abb0f93cSkardel         errno = EINVAL;
325b3d6264cSchristos         return res;
326abb0f93cSkardel     }
327b3d6264cSchristos     arg_list = oov->v.nestVal;
328abb0f93cSkardel 
329b3d6264cSchristos     if (arg_list->useCt > 0) {
330b3d6264cSchristos         int           ct     = arg_list->useCt;
331335f7552Schristos         const void ** ovlist = VOIDP(arg_list->apzArgs);
332abb0f93cSkardel 
333b3d6264cSchristos         if (vname == NULL) {
3344e3b3909Schristos             res = (const tOptionValue *)*ovlist;
335abb0f93cSkardel 
336b3d6264cSchristos         } else do {
3374e3b3909Schristos             const tOptionValue * opt_val = *(ovlist++);
338b3d6264cSchristos             if (strcmp(opt_val->pzName, vname) == 0) {
339b3d6264cSchristos                 res = opt_val;
340abb0f93cSkardel                 break;
341abb0f93cSkardel             }
342abb0f93cSkardel         } while (--ct > 0);
343abb0f93cSkardel     }
344b3d6264cSchristos     if (res == NULL)
345abb0f93cSkardel         errno = ENOENT;
346b3d6264cSchristos     return res;
347abb0f93cSkardel }
348abb0f93cSkardel 
349abb0f93cSkardel /*=export_func  optionNextValue
350abb0f93cSkardel  *
351abb0f93cSkardel  * what:  get the next value from a hierarchical list
352abb0f93cSkardel  * arg:   + const tOptionValue * + pOptValue + a hierarchcal list value +
353abb0f93cSkardel  * arg:   + const tOptionValue * + pOldValue + a value from this list   +
354abb0f93cSkardel  *
355abb0f93cSkardel  * ret_type:  const tOptionValue *
356abb0f93cSkardel  * ret_desc:  a compound value structure
357abb0f93cSkardel  *
358abb0f93cSkardel  * doc:
359abb0f93cSkardel  *  This routine will return the next entry after the entry passed in.  At the
360abb0f93cSkardel  *  end of the list, NULL will be returned.  If the entry is not found on the
361abb0f93cSkardel  *  list, NULL will be returned and "@var{errno}" will be set to EINVAL.
362abb0f93cSkardel  *  The "@var{pOldValue}" must have been gotten from a prior call to this
363abb0f93cSkardel  *  routine or to "@code{opitonGetValue()}".
364abb0f93cSkardel  *
365abb0f93cSkardel  * err:
366abb0f93cSkardel  *  The returned result is NULL and errno is set:
367abb0f93cSkardel  *  @itemize @bullet
368abb0f93cSkardel  *  @item
369abb0f93cSkardel  *  @code{EINVAL} - the @code{pOptValue} does not point to a valid
370abb0f93cSkardel  *  hierarchical option value or @code{pOldValue} does not point to a
371abb0f93cSkardel  *  member of that option value.
372abb0f93cSkardel  *  @item
373abb0f93cSkardel  *  @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry.
374abb0f93cSkardel  *  @end itemize
375abb0f93cSkardel =*/
376abb0f93cSkardel tOptionValue const *
optionNextValue(tOptionValue const * ov_list,tOptionValue const * oov)377b3d6264cSchristos optionNextValue(tOptionValue const * ov_list,tOptionValue const * oov )
378abb0f93cSkardel {
379b3d6264cSchristos     tArgList *           arg_list;
3804e3b3909Schristos     const tOptionValue * res = NULL;
381abb0f93cSkardel     int                  err = EINVAL;
382abb0f93cSkardel 
383b3d6264cSchristos     if ((ov_list == NULL) || (ov_list->valType != OPARG_TYPE_HIERARCHY)) {
384abb0f93cSkardel         errno = EINVAL;
385abb0f93cSkardel         return NULL;
386abb0f93cSkardel     }
387b3d6264cSchristos     arg_list = ov_list->v.nestVal;
388abb0f93cSkardel     {
389b3d6264cSchristos         int           ct     = arg_list->useCt;
390335f7552Schristos         const void ** o_list = VOIDP(arg_list->apzArgs);
391abb0f93cSkardel 
392abb0f93cSkardel         while (ct-- > 0) {
3934e3b3909Schristos             const tOptionValue * nov = *(o_list++);
394b3d6264cSchristos             if (nov == oov) {
395abb0f93cSkardel                 if (ct == 0) {
396abb0f93cSkardel                     err = ENOENT;
397abb0f93cSkardel 
398abb0f93cSkardel                 } else {
399abb0f93cSkardel                     err = 0;
4004e3b3909Schristos                     res = (const tOptionValue *)*o_list;
401abb0f93cSkardel                 }
402abb0f93cSkardel                 break;
403abb0f93cSkardel             }
404abb0f93cSkardel         }
405abb0f93cSkardel     }
406abb0f93cSkardel     if (err != 0)
407abb0f93cSkardel         errno = err;
408b3d6264cSchristos     return res;
409abb0f93cSkardel }
410abb0f93cSkardel 
4114305584aSkardel /**
412abb0f93cSkardel  *  Load a file containing presetting information (a configuration file).
413abb0f93cSkardel  */
414abb0f93cSkardel static void
file_preset(tOptions * opts,char const * fname,int dir)4154305584aSkardel file_preset(tOptions * opts, char const * fname, int dir)
416abb0f93cSkardel {
417abb0f93cSkardel     tmap_info_t       cfgfile;
418abb0f93cSkardel     tOptState         optst = OPTSTATE_INITIALIZER(PRESET);
419b3d6264cSchristos     opt_state_mask_t  st_flags = optst.flags;
420b3d6264cSchristos     opt_state_mask_t  fl_save  = opts->fOptSet;
4214305584aSkardel     char *            ftext =
4224305584aSkardel         text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile);
423abb0f93cSkardel 
4244305584aSkardel     if (TEXT_MMAP_FAILED_ADDR(ftext))
425abb0f93cSkardel         return;
426abb0f93cSkardel 
427b3d6264cSchristos     /*
428b3d6264cSchristos      * While processing config files, we ignore errors.
429b3d6264cSchristos      */
430b3d6264cSchristos     opts->fOptSet &= ~OPTPROC_ERRSTOP;
431b3d6264cSchristos 
4324305584aSkardel     if (dir == DIRECTION_CALLED) {
4334305584aSkardel         st_flags = OPTST_DEFINED;
4344305584aSkardel         dir   = DIRECTION_PROCESS;
435abb0f93cSkardel     }
436abb0f93cSkardel 
437abb0f93cSkardel     /*
438abb0f93cSkardel      *  IF this is called via "optionProcess", then we are presetting.
439abb0f93cSkardel      *  This is the default and the PRESETTING bit will be set.
440abb0f93cSkardel      *  If this is called via "optionFileLoad", then the bit is not set
441abb0f93cSkardel      *  and we consider stuff set herein to be "set" by the client program.
442abb0f93cSkardel      */
4434305584aSkardel     if ((opts->fOptSet & OPTPROC_PRESETTING) == 0)
4444305584aSkardel         st_flags = OPTST_SET;
445abb0f93cSkardel 
446abb0f93cSkardel     do  {
4474305584aSkardel         optst.flags = st_flags;
448b3d6264cSchristos         ftext = SPN_WHITESPACE_CHARS(ftext);
449abb0f93cSkardel 
4504305584aSkardel         if (IS_VAR_FIRST_CHAR(*ftext)) {
4514305584aSkardel             ftext = handle_cfg(opts, &optst, ftext, dir);
452abb0f93cSkardel 
4534305584aSkardel         } else switch (*ftext) {
454abb0f93cSkardel         case '<':
4554305584aSkardel             if (IS_VAR_FIRST_CHAR(ftext[1]))
4564305584aSkardel                 ftext = handle_struct(opts, &optst, ftext, dir);
457abb0f93cSkardel 
4584305584aSkardel             else switch (ftext[1]) {
459abb0f93cSkardel             case '?':
4604305584aSkardel                 ftext = handle_directive(opts, ftext);
461abb0f93cSkardel                 break;
462abb0f93cSkardel 
463abb0f93cSkardel             case '!':
4644305584aSkardel                 ftext = handle_comment(ftext);
465abb0f93cSkardel                 break;
466abb0f93cSkardel 
467abb0f93cSkardel             case '/':
4684305584aSkardel                 ftext = strchr(ftext + 2, '>');
4694305584aSkardel                 if (ftext++ != NULL)
470abb0f93cSkardel                     break;
471*4efa1683Schristos 		/*FALLTHROUGH*/
472abb0f93cSkardel             default:
473b3d6264cSchristos                 ftext = NULL;
474abb0f93cSkardel             }
475b3d6264cSchristos             if (ftext == NULL)
476b3d6264cSchristos                 goto all_done;
477abb0f93cSkardel             break;
478abb0f93cSkardel 
479abb0f93cSkardel         case '[':
4804305584aSkardel             ftext = handle_section(opts, ftext);
481abb0f93cSkardel             break;
482abb0f93cSkardel 
483abb0f93cSkardel         case '#':
484b3d6264cSchristos             ftext = strchr(ftext + 1, NL);
485abb0f93cSkardel             break;
486abb0f93cSkardel 
487abb0f93cSkardel         default:
488abb0f93cSkardel             goto all_done; /* invalid format */
489abb0f93cSkardel         }
4904305584aSkardel     } while (ftext != NULL);
491abb0f93cSkardel 
492abb0f93cSkardel  all_done:
493abb0f93cSkardel     text_munmap(&cfgfile);
494b3d6264cSchristos     opts->fOptSet = fl_save;
495abb0f93cSkardel }
496abb0f93cSkardel 
4974305584aSkardel /**
498b3d6264cSchristos  *  "txt" points to a "<!" sequence.
499abb0f93cSkardel  *  Theoretically, we should ensure that it begins with "<!--",
500abb0f93cSkardel  *  but actually I don't care that much.  It ends with "-->".
501abb0f93cSkardel  */
502abb0f93cSkardel static char *
handle_comment(char * txt)503b3d6264cSchristos handle_comment(char * txt)
504abb0f93cSkardel {
505b3d6264cSchristos     char * pz = strstr(txt, "-->");
506abb0f93cSkardel     if (pz != NULL)
507abb0f93cSkardel         pz += 3;
508abb0f93cSkardel     return pz;
509abb0f93cSkardel }
510abb0f93cSkardel 
5114305584aSkardel /**
512b3d6264cSchristos  *  "txt" points to the start of some value name.
513abb0f93cSkardel  *  The end of the entry is the end of the line that is not preceded by
514abb0f93cSkardel  *  a backslash escape character.  The string value is always processed
515abb0f93cSkardel  *  in "cooked" mode.
516abb0f93cSkardel  */
517abb0f93cSkardel static char *
handle_cfg(tOptions * opts,tOptState * ost,char * txt,int dir)518b3d6264cSchristos handle_cfg(tOptions * opts, tOptState * ost, char * txt, int dir)
519abb0f93cSkardel {
520b3d6264cSchristos     char * pzName = txt++;
521b3d6264cSchristos     char * pzEnd  = strchr(txt, NL);
522abb0f93cSkardel 
523abb0f93cSkardel     if (pzEnd == NULL)
524b3d6264cSchristos         return txt + strlen(txt);
525abb0f93cSkardel 
526b3d6264cSchristos     txt = SPN_VALUE_NAME_CHARS(txt);
527b3d6264cSchristos     txt = SPN_WHITESPACE_CHARS(txt);
528b3d6264cSchristos     if (txt > pzEnd) {
529abb0f93cSkardel     name_only:
530abb0f93cSkardel         *pzEnd++ = NUL;
531b3d6264cSchristos         load_opt_line(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED);
532abb0f93cSkardel         return pzEnd;
533abb0f93cSkardel     }
534abb0f93cSkardel 
535abb0f93cSkardel     /*
536abb0f93cSkardel      *  Either the first character after the name is a ':' or '=',
537abb0f93cSkardel      *  or else we must have skipped over white space.  Anything else
538abb0f93cSkardel      *  is an invalid format and we give up parsing the text.
539abb0f93cSkardel      */
540b3d6264cSchristos     if ((*txt == '=') || (*txt == ':')) {
541b3d6264cSchristos         txt = SPN_WHITESPACE_CHARS(txt+1);
542b3d6264cSchristos         if (txt > pzEnd)
543abb0f93cSkardel             goto name_only;
544b3d6264cSchristos     } else if (! IS_WHITESPACE_CHAR(txt[-1]))
545abb0f93cSkardel         return NULL;
546abb0f93cSkardel 
547abb0f93cSkardel     /*
548abb0f93cSkardel      *  IF the value is continued, remove the backslash escape and push "pzEnd"
549abb0f93cSkardel      *  on to a newline *not* preceded by a backslash.
550abb0f93cSkardel      */
551abb0f93cSkardel     if (pzEnd[-1] == '\\') {
552abb0f93cSkardel         char * pcD = pzEnd-1;
553abb0f93cSkardel         char * pcS = pzEnd;
554abb0f93cSkardel 
555abb0f93cSkardel         for (;;) {
556abb0f93cSkardel             char ch = *(pcS++);
557abb0f93cSkardel             switch (ch) {
558abb0f93cSkardel             case NUL:
559abb0f93cSkardel                 pcS = NULL;
560b3d6264cSchristos                 /* FALLTHROUGH */
561abb0f93cSkardel 
562b3d6264cSchristos             case NL:
563abb0f93cSkardel                 *pcD = NUL;
564abb0f93cSkardel                 pzEnd = pcS;
565abb0f93cSkardel                 goto copy_done;
566abb0f93cSkardel 
567abb0f93cSkardel             case '\\':
568b3d6264cSchristos                 if (*pcS == NL)
569abb0f93cSkardel                     ch = *(pcS++);
570abb0f93cSkardel                 /* FALLTHROUGH */
571abb0f93cSkardel             default:
572abb0f93cSkardel                 *(pcD++) = ch;
573abb0f93cSkardel             }
574abb0f93cSkardel         } copy_done:;
575abb0f93cSkardel 
576abb0f93cSkardel     } else {
577abb0f93cSkardel         /*
578abb0f93cSkardel          *  The newline was not preceded by a backslash.  NUL it out
579abb0f93cSkardel          */
580abb0f93cSkardel         *(pzEnd++) = NUL;
581abb0f93cSkardel     }
582abb0f93cSkardel 
583abb0f93cSkardel     /*
584abb0f93cSkardel      *  "pzName" points to what looks like text for one option/configurable.
585abb0f93cSkardel      *  It is NUL terminated.  Process it.
586abb0f93cSkardel      */
587b3d6264cSchristos     load_opt_line(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED);
588abb0f93cSkardel 
589abb0f93cSkardel     return pzEnd;
590abb0f93cSkardel }
591abb0f93cSkardel 
5924305584aSkardel /**
593b3d6264cSchristos  *  "txt" points to a "<?" sequence.
5944305584aSkardel  *  We handle "<?program" and "<?auto-options" directives.
5954305584aSkardel  *  All others are treated as comments.
596b3d6264cSchristos  *
597b3d6264cSchristos  *  @param[in,out] opts  program option descriptor
598b3d6264cSchristos  *  @param[in]     txt   scanning pointer
599b3d6264cSchristos  *  @returns       the next character to look at
600abb0f93cSkardel  */
601abb0f93cSkardel static char *
handle_directive(tOptions * opts,char * txt)602b3d6264cSchristos handle_directive(tOptions * opts, char * txt)
603abb0f93cSkardel {
6044305584aSkardel #   define DIRECTIVE_TABLE                      \
6054305584aSkardel     _dt_(zCfgProg,     program_directive)       \
6064305584aSkardel     _dt_(zCfgAO_Flags, aoflags_directive)
607abb0f93cSkardel 
6084305584aSkardel     typedef char * (directive_func_t)(tOptions *, char *);
6094305584aSkardel #   define _dt_(_s, _fn) _fn,
6104305584aSkardel     static directive_func_t * dir_disp[] = {
6114305584aSkardel         DIRECTIVE_TABLE
6124305584aSkardel     };
6134305584aSkardel #   undef  _dt_
6144305584aSkardel 
6154305584aSkardel #   define _dt_(_s, _fn) 1 +
6164305584aSkardel     static int  const   dir_ct  = DIRECTIVE_TABLE 0;
6174305584aSkardel     static char const * dir_names[DIRECTIVE_TABLE 0];
6184305584aSkardel #   undef _dt_
6194305584aSkardel 
6204305584aSkardel     int    ix;
6214305584aSkardel 
6224305584aSkardel     if (dir_names[0] == NULL) {
6234305584aSkardel         ix = 0;
6244305584aSkardel #   define _dt_(_s, _fn) dir_names[ix++] = _s;
6254305584aSkardel         DIRECTIVE_TABLE;
6264305584aSkardel #   undef _dt_
6274305584aSkardel     }
6284305584aSkardel 
6294305584aSkardel     for (ix = 0; ix < dir_ct; ix++) {
6304305584aSkardel         size_t len = strlen(dir_names[ix]);
631b3d6264cSchristos         if (  (strncmp(txt + 2, dir_names[ix], len) == 0)
632b3d6264cSchristos            && (! IS_VALUE_NAME_CHAR(txt[len+2])) )
633b3d6264cSchristos             return dir_disp[ix](opts, txt + len + 2);
6344305584aSkardel     }
6354305584aSkardel 
6364305584aSkardel     /*
6374305584aSkardel      *  We don't know what this is.  Skip it.
6384305584aSkardel      */
639b3d6264cSchristos     txt = strchr(txt+2, '>');
640b3d6264cSchristos     if (txt != NULL)
641b3d6264cSchristos         txt++;
642b3d6264cSchristos     return txt;
643b3d6264cSchristos #   undef DIRECTIVE_TABLE
644abb0f93cSkardel }
645abb0f93cSkardel 
6464305584aSkardel /**
647b3d6264cSchristos  *  handle AutoOpts mode flags.
648b3d6264cSchristos  *
649b3d6264cSchristos  *  @param[in,out] opts  program option descriptor
650b3d6264cSchristos  *  @param[in]     txt   scanning pointer
651b3d6264cSchristos  *  @returns       the next character to look at
6524305584aSkardel  */
6534305584aSkardel static char *
aoflags_directive(tOptions * opts,char * txt)654b3d6264cSchristos aoflags_directive(tOptions * opts, char * txt)
6554305584aSkardel {
656b3d6264cSchristos     char * pz;
6574305584aSkardel 
658b3d6264cSchristos     pz = SPN_WHITESPACE_CHARS(txt+1);
659b3d6264cSchristos     txt = strchr(pz, '>');
660b3d6264cSchristos     if (txt != NULL) {
6614305584aSkardel 
662b3d6264cSchristos         size_t len  = (unsigned)(txt - pz);
6634305584aSkardel         char * ftxt = AGALOC(len + 1, "aoflags");
6644305584aSkardel 
6654305584aSkardel         memcpy(ftxt, pz, len);
6664305584aSkardel         ftxt[len] = NUL;
667b3d6264cSchristos         set_usage_flags(opts, ftxt);
6684305584aSkardel         AGFREE(ftxt);
6694305584aSkardel 
670b3d6264cSchristos         txt++;
6714305584aSkardel     }
6724305584aSkardel 
673b3d6264cSchristos     return txt;
6744305584aSkardel }
6754305584aSkardel 
6764305584aSkardel /**
6774305584aSkardel  * handle program segmentation of config file.
678b3d6264cSchristos  *
679b3d6264cSchristos  *  @param[in,out] opts  program option descriptor
680b3d6264cSchristos  *  @param[in]     txt   scanning pointer
681b3d6264cSchristos  *  @returns       the next character to look at
6824305584aSkardel  */
6834305584aSkardel static char *
program_directive(tOptions * opts,char * txt)684b3d6264cSchristos program_directive(tOptions * opts, char * txt)
6854305584aSkardel {
6864305584aSkardel     static char const ttlfmt[] = "<?";
6874305584aSkardel     size_t ttl_len  = sizeof(ttlfmt) + strlen(zCfgProg);
6884305584aSkardel     char * ttl      = AGALOC(ttl_len, "prog title");
689b3d6264cSchristos     size_t name_len = strlen(opts->pzProgName);
6904305584aSkardel 
6914305584aSkardel     memcpy(ttl, ttlfmt, sizeof(ttlfmt) - 1);
6924305584aSkardel     memcpy(ttl + sizeof(ttlfmt) - 1, zCfgProg, ttl_len - (sizeof(ttlfmt) - 1));
693abb0f93cSkardel 
694abb0f93cSkardel     do  {
695b3d6264cSchristos         txt = SPN_WHITESPACE_CHARS(txt+1);
6964305584aSkardel 
697b3d6264cSchristos         if (  (strneqvcmp(txt, opts->pzProgName, (int)name_len) == 0)
698b3d6264cSchristos            && (IS_END_XML_TOKEN_CHAR(txt[name_len])) ) {
699b3d6264cSchristos             txt += name_len;
700abb0f93cSkardel             break;
701abb0f93cSkardel         }
702abb0f93cSkardel 
703b3d6264cSchristos         txt = strstr(txt, ttl);
704b3d6264cSchristos     } while (txt != NULL);
705abb0f93cSkardel 
7064305584aSkardel     AGFREE(ttl);
707b3d6264cSchristos     if (txt != NULL)
7084305584aSkardel         for (;;) {
709b3d6264cSchristos             if (*txt == NUL) {
710b3d6264cSchristos                 txt = NULL;
7114305584aSkardel                 break;
7124305584aSkardel             }
713b3d6264cSchristos             if (*(txt++) == '>')
7144305584aSkardel                 break;
7154305584aSkardel         }
7164305584aSkardel 
717b3d6264cSchristos     return txt;
718abb0f93cSkardel }
719abb0f93cSkardel 
7204305584aSkardel /**
721b3d6264cSchristos  *  "txt" points to a '[' character.
722abb0f93cSkardel  *  The "traditional" [PROG_NAME] segmentation of the config file.
723abb0f93cSkardel  *  Do not ever mix with the "<?program prog-name>" variation.
724b3d6264cSchristos  *
725b3d6264cSchristos  *  @param[in,out] opts  program option descriptor
726b3d6264cSchristos  *  @param[in]     txt   scanning pointer
727b3d6264cSchristos  *  @returns       the next character to look at
728abb0f93cSkardel  */
729abb0f93cSkardel static char *
handle_section(tOptions * opts,char * txt)730b3d6264cSchristos handle_section(tOptions * opts, char * txt)
731abb0f93cSkardel {
732b3d6264cSchristos     size_t len = strlen(opts->pzPROGNAME);
733b3d6264cSchristos     if (   (strncmp(txt+1, opts->pzPROGNAME, len) == 0)
734b3d6264cSchristos         && (txt[len+1] == ']'))
735b3d6264cSchristos         return strchr(txt + len + 2, NL);
736abb0f93cSkardel 
737abb0f93cSkardel     if (len > 16)
738abb0f93cSkardel         return NULL;
739abb0f93cSkardel 
740abb0f93cSkardel     {
741abb0f93cSkardel         char z[24];
742b3d6264cSchristos         sprintf(z, "[%s]", opts->pzPROGNAME);
743b3d6264cSchristos         txt = strstr(txt, z);
744abb0f93cSkardel     }
745abb0f93cSkardel 
746b3d6264cSchristos     if (txt != NULL)
747b3d6264cSchristos         txt = strchr(txt, NL);
748b3d6264cSchristos     return txt;
749abb0f93cSkardel }
750abb0f93cSkardel 
7514305584aSkardel /**
7524305584aSkardel  * parse XML encodings
7534305584aSkardel  */
7544305584aSkardel static int
parse_xml_encoding(char ** ppz)7554305584aSkardel parse_xml_encoding(char ** ppz)
7564305584aSkardel {
7574305584aSkardel #   define XMLTABLE             \
7584305584aSkardel         _xmlNm_(amp,   '&')     \
7594305584aSkardel         _xmlNm_(lt,    '<')     \
7604305584aSkardel         _xmlNm_(gt,    '>')     \
7614305584aSkardel         _xmlNm_(ff,    '\f')    \
7624305584aSkardel         _xmlNm_(ht,    '\t')    \
7634305584aSkardel         _xmlNm_(cr,    '\r')    \
7644305584aSkardel         _xmlNm_(vt,    '\v')    \
7654305584aSkardel         _xmlNm_(bel,   '\a')    \
766b3d6264cSchristos         _xmlNm_(nl,    NL)      \
7674305584aSkardel         _xmlNm_(space, ' ')     \
7684305584aSkardel         _xmlNm_(quot,  '"')     \
7694305584aSkardel         _xmlNm_(apos,  '\'')
770abb0f93cSkardel 
7714305584aSkardel     static struct {
7724305584aSkardel         char const * const  nm_str;
7734305584aSkardel         unsigned short      nm_len;
7744305584aSkardel         short               nm_val;
7754305584aSkardel     } const xml_names[] = {
7764305584aSkardel #   define _xmlNm_(_n, _v) { #_n ";", sizeof(#_n), _v },
7774305584aSkardel         XMLTABLE
7784305584aSkardel #   undef  _xmlNm_
7794305584aSkardel #   undef XMLTABLE
7804305584aSkardel     };
7814305584aSkardel 
7824305584aSkardel     static int const nm_ct = sizeof(xml_names) / sizeof(xml_names[0]);
7834305584aSkardel     int    base = 10;
7844305584aSkardel 
7854305584aSkardel     char * pz = *ppz;
7864305584aSkardel 
7874305584aSkardel     if (*pz == '#') {
7884305584aSkardel         pz++;
7894305584aSkardel         goto parse_number;
7904305584aSkardel     }
7914305584aSkardel 
7924305584aSkardel     if (IS_DEC_DIGIT_CHAR(*pz)) {
7934305584aSkardel         unsigned long v;
7944305584aSkardel 
7954305584aSkardel     parse_number:
7964305584aSkardel         switch (*pz) {
7974305584aSkardel         case 'x': case 'X':
7984305584aSkardel             /*
7994305584aSkardel              * Some forms specify hex with:  &#xNN;
8004305584aSkardel              */
8014305584aSkardel             base = 16;
8024305584aSkardel             pz++;
8034305584aSkardel             break;
8044305584aSkardel 
8054305584aSkardel         case '0':
8064305584aSkardel             /*
8074305584aSkardel              *  &#0022; is hex and &#22; is decimal.  Cool.
8084305584aSkardel              *  Ya gotta love it.
8094305584aSkardel              */
8104305584aSkardel             if (pz[1] == '0')
8114305584aSkardel                 base = 16;
8124305584aSkardel             break;
8134305584aSkardel         }
8144305584aSkardel 
8154305584aSkardel         v = strtoul(pz, &pz, base);
8164305584aSkardel         if ((*pz != ';') || (v > 0x7F))
8174305584aSkardel             return NUL;
8184305584aSkardel         *ppz = pz + 1;
8194305584aSkardel         return (int)v;
8204305584aSkardel     }
8214305584aSkardel 
8224305584aSkardel     {
8234305584aSkardel         int ix = 0;
8244305584aSkardel         do  {
8254305584aSkardel             if (strncmp(pz, xml_names[ix].nm_str, xml_names[ix].nm_len)
8264305584aSkardel                 == 0) {
8274305584aSkardel                 *ppz = pz + xml_names[ix].nm_len;
8284305584aSkardel                 return xml_names[ix].nm_val;
8294305584aSkardel             }
8304305584aSkardel         } while (++ix < nm_ct);
8314305584aSkardel     }
8324305584aSkardel 
8334305584aSkardel     return NUL;
8344305584aSkardel }
8354305584aSkardel 
8364305584aSkardel /**
8374305584aSkardel  * Find the end marker for the named section of XML.
8384305584aSkardel  * Trim that text there, trimming trailing white space for all modes
8394305584aSkardel  * except for OPTION_LOAD_UNCOOKED.
8404305584aSkardel  */
8414305584aSkardel static char *
trim_xml_text(char * intxt,char const * pznm,tOptionLoadMode mode)842b3d6264cSchristos trim_xml_text(char * intxt, char const * pznm, tOptionLoadMode mode)
8434305584aSkardel {
8444305584aSkardel     static char const fmt[] = "</%s>";
8454305584aSkardel     size_t len = strlen(pznm) + sizeof(fmt) - 2 /* for %s */;
846b3d6264cSchristos     char * etext;
8474305584aSkardel 
848b3d6264cSchristos     {
849b3d6264cSchristos         char z[64], *pz = z;
850b3d6264cSchristos         if (len >= sizeof(z))
8514305584aSkardel             pz = AGALOC(len, "scan name");
8524305584aSkardel 
853b3d6264cSchristos         len = (size_t)sprintf(pz, fmt, pznm);
854b3d6264cSchristos         *intxt = ' ';
855b3d6264cSchristos         etext = strstr(intxt, pz);
8564305584aSkardel         if (pz != z) AGFREE(pz);
857b3d6264cSchristos     }
8584305584aSkardel 
859b3d6264cSchristos     if (etext == NULL)
860b3d6264cSchristos         return etext;
861b3d6264cSchristos 
862b3d6264cSchristos     {
863b3d6264cSchristos         char * result = etext + len;
8644305584aSkardel 
8654305584aSkardel         if (mode != OPTION_LOAD_UNCOOKED)
866b3d6264cSchristos             etext = SPN_WHITESPACE_BACK(intxt, etext);
8674305584aSkardel 
868b3d6264cSchristos         *etext = NUL;
869b3d6264cSchristos         return result;
870b3d6264cSchristos     }
8714305584aSkardel }
8724305584aSkardel 
8734305584aSkardel /**
8744305584aSkardel  */
8754305584aSkardel static void
cook_xml_text(char * pzData)8764305584aSkardel cook_xml_text(char * pzData)
8774305584aSkardel {
8784305584aSkardel     char * pzs = pzData;
8794305584aSkardel     char * pzd = pzData;
8804305584aSkardel     char   bf[4];
8814305584aSkardel     bf[2] = NUL;
8824305584aSkardel 
8834305584aSkardel     for (;;) {
8844305584aSkardel         int ch = ((int)*(pzs++)) & 0xFF;
8854305584aSkardel         switch (ch) {
8864305584aSkardel         case NUL:
8874305584aSkardel             *pzd = NUL;
8884305584aSkardel             return;
8894305584aSkardel 
8904305584aSkardel         case '&':
8914305584aSkardel             ch = parse_xml_encoding(&pzs);
892b3d6264cSchristos             *(pzd++) = (char)ch;
8934305584aSkardel             if (ch == NUL)
8944305584aSkardel                 return;
8954305584aSkardel             break;
8964305584aSkardel 
8974305584aSkardel         case '%':
8984305584aSkardel             bf[0] = *(pzs++);
8994305584aSkardel             bf[1] = *(pzs++);
9004305584aSkardel             if ((bf[0] == NUL) || (bf[1] == NUL)) {
9014305584aSkardel                 *pzd = NUL;
9024305584aSkardel                 return;
9034305584aSkardel             }
9044305584aSkardel 
905b3d6264cSchristos             ch = (int)strtoul(bf, NULL, 16);
9064305584aSkardel             /* FALLTHROUGH */
9074305584aSkardel 
9084305584aSkardel         default:
909b3d6264cSchristos             *(pzd++) = (char)ch;
9104305584aSkardel         }
9114305584aSkardel     }
9124305584aSkardel }
9134305584aSkardel 
9144305584aSkardel /**
915b3d6264cSchristos  *  "txt" points to a '<' character, followed by an alpha.
916abb0f93cSkardel  *  The end of the entry is either the "/>" following the name, or else a
917abb0f93cSkardel  *  "</name>" string.
918abb0f93cSkardel  */
919abb0f93cSkardel static char *
handle_struct(tOptions * opts,tOptState * ost,char * txt,int dir)920b3d6264cSchristos handle_struct(tOptions * opts, tOptState * ost, char * txt, int dir)
921abb0f93cSkardel {
922abb0f93cSkardel     tOptionLoadMode mode = option_load_mode;
923abb0f93cSkardel     tOptionValue    valu;
924abb0f93cSkardel 
925b3d6264cSchristos     char * pzName = ++txt;
926abb0f93cSkardel     char * pzData;
927abb0f93cSkardel     char * pcNulPoint;
928abb0f93cSkardel 
929b3d6264cSchristos     txt = SPN_VALUE_NAME_CHARS(txt);
930b3d6264cSchristos     pcNulPoint = txt;
931abb0f93cSkardel     valu.valType = OPARG_TYPE_STRING;
932abb0f93cSkardel 
933b3d6264cSchristos     switch (*txt) {
934abb0f93cSkardel     case ' ':
935abb0f93cSkardel     case '\t':
9364e3b3909Schristos         txt = VOIDP(parse_attrs(
9374e3b3909Schristos             opts, SPN_WHITESPACE_CHARS(txt), &mode, &valu));
938b3d6264cSchristos         if (txt == NULL)
939b3d6264cSchristos             return txt;
940b3d6264cSchristos         if (*txt == '>')
941abb0f93cSkardel             break;
942b3d6264cSchristos         if (*txt != '/')
943abb0f93cSkardel             return NULL;
944abb0f93cSkardel         /* FALLTHROUGH */
945abb0f93cSkardel 
946abb0f93cSkardel     case '/':
947b3d6264cSchristos         if (txt[1] != '>')
948abb0f93cSkardel             return NULL;
949b3d6264cSchristos         *txt = NUL;
950b3d6264cSchristos         txt += 2;
951b3d6264cSchristos         load_opt_line(opts, ost, pzName, dir, mode);
952b3d6264cSchristos         return txt;
953abb0f93cSkardel 
954abb0f93cSkardel     case '>':
955abb0f93cSkardel         break;
956abb0f93cSkardel 
957abb0f93cSkardel     default:
958b3d6264cSchristos         txt = strchr(txt, '>');
959b3d6264cSchristos         if (txt != NULL)
960b3d6264cSchristos             txt++;
961b3d6264cSchristos         return txt;
962abb0f93cSkardel     }
963abb0f93cSkardel 
964abb0f93cSkardel     /*
965b3d6264cSchristos      *  If we are here, we have a value.  "txt" points to a closing angle
966abb0f93cSkardel      *  bracket.  Separate the name from the value for a moment.
967abb0f93cSkardel      */
968abb0f93cSkardel     *pcNulPoint = NUL;
969b3d6264cSchristos     pzData = ++txt;
970b3d6264cSchristos     txt = trim_xml_text(txt, pzName, mode);
971b3d6264cSchristos     if (txt == NULL)
972b3d6264cSchristos         return txt;
973abb0f93cSkardel 
974abb0f93cSkardel     /*
975b3d6264cSchristos      *  Rejoin the name and value for parsing by "load_opt_line()".
976b3d6264cSchristos      *  Erase any attributes parsed by "parse_attrs()".
977abb0f93cSkardel      */
978b3d6264cSchristos     memset(pcNulPoint, ' ', (size_t)(pzData - pcNulPoint));
979abb0f93cSkardel 
980abb0f93cSkardel     /*
9814305584aSkardel      *  If we are getting a "string" value that is to be cooked,
9824305584aSkardel      *  then process the XML-ish &xx; XML-ish and %XX hex characters.
983abb0f93cSkardel      */
9844305584aSkardel     if (  (valu.valType == OPARG_TYPE_STRING)
9854305584aSkardel        && (mode == OPTION_LOAD_COOKED))
9864305584aSkardel         cook_xml_text(pzData);
987abb0f93cSkardel 
988abb0f93cSkardel     /*
989abb0f93cSkardel      *  "pzName" points to what looks like text for one option/configurable.
990abb0f93cSkardel      *  It is NUL terminated.  Process it.
991abb0f93cSkardel      */
992b3d6264cSchristos     load_opt_line(opts, ost, pzName, dir, mode);
993abb0f93cSkardel 
994b3d6264cSchristos     return txt;
995abb0f93cSkardel }
996abb0f93cSkardel 
9974305584aSkardel /**
998abb0f93cSkardel  *  Load a configuration file.  This may be invoked either from
999abb0f93cSkardel  *  scanning the "homerc" list, or from a specific file request.
1000abb0f93cSkardel  *  (see "optionFileLoad()", the implementation for --load-opts)
1001abb0f93cSkardel  */
1002abb0f93cSkardel LOCAL void
intern_file_load(tOptions * opts)1003b3d6264cSchristos intern_file_load(tOptions * opts)
1004abb0f93cSkardel {
1005abb0f93cSkardel     uint32_t  svfl;
1006abb0f93cSkardel     int       idx;
1007abb0f93cSkardel     int       inc;
1008b3d6264cSchristos     char      f_name[ AG_PATH_MAX+1 ];
1009abb0f93cSkardel 
1010b3d6264cSchristos     if (opts->papzHomeList == NULL)
1011abb0f93cSkardel         return;
1012abb0f93cSkardel 
1013b3d6264cSchristos     svfl = opts->fOptSet;
1014abb0f93cSkardel     inc  = DIRECTION_PRESET;
1015abb0f93cSkardel 
1016abb0f93cSkardel     /*
1017abb0f93cSkardel      *  Never stop on errors in config files.
1018abb0f93cSkardel      */
1019b3d6264cSchristos     opts->fOptSet &= ~OPTPROC_ERRSTOP;
1020abb0f93cSkardel 
1021abb0f93cSkardel     /*
1022abb0f93cSkardel      *  Find the last RC entry (highest priority entry)
1023abb0f93cSkardel      */
1024b3d6264cSchristos     for (idx = 0; opts->papzHomeList[ idx+1 ] != NULL; ++idx)  ;
1025abb0f93cSkardel 
1026abb0f93cSkardel     /*
1027abb0f93cSkardel      *  For every path in the home list, ...  *TWICE* We start at the last
1028abb0f93cSkardel      *  (highest priority) entry, work our way down to the lowest priority,
1029abb0f93cSkardel      *  handling the immediate options.
1030abb0f93cSkardel      *  Then we go back up, doing the normal options.
1031abb0f93cSkardel      */
1032abb0f93cSkardel     for (;;) {
1033b3d6264cSchristos         struct stat sb;
1034b3d6264cSchristos         cch_t *  path;
1035abb0f93cSkardel 
1036abb0f93cSkardel         /*
1037abb0f93cSkardel          *  IF we've reached the bottom end, change direction
1038abb0f93cSkardel          */
1039abb0f93cSkardel         if (idx < 0) {
1040abb0f93cSkardel             inc = DIRECTION_PROCESS;
1041abb0f93cSkardel             idx = 0;
1042abb0f93cSkardel         }
1043abb0f93cSkardel 
1044b3d6264cSchristos         path = opts->papzHomeList[ idx ];
1045abb0f93cSkardel 
1046abb0f93cSkardel         /*
1047abb0f93cSkardel          *  IF we've reached the top end, bail out
1048abb0f93cSkardel          */
1049b3d6264cSchristos         if (path == NULL)
1050abb0f93cSkardel             break;
1051abb0f93cSkardel 
1052abb0f93cSkardel         idx += inc;
1053abb0f93cSkardel 
1054b3d6264cSchristos         if (! optionMakePath(f_name, (int)sizeof(f_name),
1055b3d6264cSchristos                              path, opts->pzProgPath))
1056abb0f93cSkardel             continue;
1057abb0f93cSkardel 
1058abb0f93cSkardel         /*
1059abb0f93cSkardel          *  IF the file name we constructed is a directory,
1060abb0f93cSkardel          *  THEN append the Resource Configuration file name
1061abb0f93cSkardel          *  ELSE we must have the complete file name
1062abb0f93cSkardel          */
1063b3d6264cSchristos         if (stat(f_name, &sb) != 0)
1064abb0f93cSkardel             continue; /* bogus name - skip the home list entry */
1065abb0f93cSkardel 
1066b3d6264cSchristos         if (S_ISDIR(sb.st_mode)) {
1067b3d6264cSchristos             size_t len = strlen(f_name);
1068b3d6264cSchristos             size_t nln = strlen(opts->pzRcName) + 1;
1069b3d6264cSchristos             char * pz  = f_name + len;
1070abb0f93cSkardel 
1071b3d6264cSchristos             if (len + 1 + nln >= sizeof(f_name))
1072abb0f93cSkardel                 continue;
1073abb0f93cSkardel 
1074abb0f93cSkardel             if (pz[-1] != DIRCH)
1075abb0f93cSkardel                 *(pz++) = DIRCH;
1076b3d6264cSchristos             memcpy(pz, opts->pzRcName, nln);
1077abb0f93cSkardel         }
1078abb0f93cSkardel 
1079b3d6264cSchristos         file_preset(opts, f_name, inc);
1080abb0f93cSkardel 
1081abb0f93cSkardel         /*
1082abb0f93cSkardel          *  IF we are now to skip config files AND we are presetting,
1083abb0f93cSkardel          *  THEN change direction.  We must go the other way.
1084abb0f93cSkardel          */
1085abb0f93cSkardel         {
1086b3d6264cSchristos             tOptDesc * od = opts->pOptDesc + opts->specOptIdx.save_opts + 1;
1087b3d6264cSchristos             if (DISABLED_OPT(od) && PRESETTING(inc)) {
1088abb0f93cSkardel                 idx -= inc;  /* go back and reprocess current file */
1089abb0f93cSkardel                 inc =  DIRECTION_PROCESS;
1090abb0f93cSkardel             }
1091abb0f93cSkardel         }
1092abb0f93cSkardel     } /* twice for every path in the home list, ... */
1093abb0f93cSkardel 
1094b3d6264cSchristos     opts->fOptSet = svfl;
1095abb0f93cSkardel }
1096abb0f93cSkardel 
1097abb0f93cSkardel /*=export_func optionFileLoad
1098abb0f93cSkardel  *
1099abb0f93cSkardel  * what: Load the locatable config files, in order
1100abb0f93cSkardel  *
1101b3d6264cSchristos  * arg:  + tOptions *   + opts + program options descriptor +
1102b3d6264cSchristos  * arg:  + char const * + prog + program name +
1103abb0f93cSkardel  *
1104abb0f93cSkardel  * ret_type:  int
1105abb0f93cSkardel  * ret_desc:  0 -> SUCCESS, -1 -> FAILURE
1106abb0f93cSkardel  *
1107abb0f93cSkardel  * doc:
1108abb0f93cSkardel  *
1109abb0f93cSkardel  * This function looks in all the specified directories for a configuration
1110abb0f93cSkardel  * file ("rc" file or "ini" file) and processes any found twice.  The first
1111abb0f93cSkardel  * time through, they are processed in reverse order (last file first).  At
1112abb0f93cSkardel  * that time, only "immediate action" configurables are processed.  For
1113abb0f93cSkardel  * example, if the last named file specifies not processing any more
1114abb0f93cSkardel  * configuration files, then no more configuration files will be processed.
1115abb0f93cSkardel  * Such an option in the @strong{first} named directory will have no effect.
1116abb0f93cSkardel  *
1117abb0f93cSkardel  * Once the immediate action configurables have been handled, then the
1118abb0f93cSkardel  * directories are handled in normal, forward order.  In that way, later
1119abb0f93cSkardel  * config files can override the settings of earlier config files.
1120abb0f93cSkardel  *
1121abb0f93cSkardel  * See the AutoOpts documentation for a thorough discussion of the
1122abb0f93cSkardel  * config file format.
1123abb0f93cSkardel  *
1124abb0f93cSkardel  * Configuration files not found or not decipherable are simply ignored.
1125abb0f93cSkardel  *
1126abb0f93cSkardel  * err:  Returns the value, "-1" if the program options descriptor
1127abb0f93cSkardel  *       is out of date or indecipherable.  Otherwise, the value "0" will
1128abb0f93cSkardel  *       always be returned.
1129abb0f93cSkardel =*/
1130abb0f93cSkardel int
optionFileLoad(tOptions * opts,char const * prog)1131b3d6264cSchristos optionFileLoad(tOptions * opts, char const * prog)
1132abb0f93cSkardel {
1133b3d6264cSchristos     if (! SUCCESSFUL(validate_struct(opts, prog)))
1134abb0f93cSkardel         return -1;
1135abb0f93cSkardel 
1136b3d6264cSchristos     /*
1137b3d6264cSchristos      * The pointer to the program name is "const".  However, the
1138b3d6264cSchristos      * structure is in writable memory, so we coerce the address
1139b3d6264cSchristos      * of this pointer to point to writable memory.
1140b3d6264cSchristos      */
11414305584aSkardel     {
11424e3b3909Schristos         char const ** pp = VOIDP(&(opts->pzProgName));
1143b3d6264cSchristos         *pp = prog;
11444305584aSkardel     }
11454305584aSkardel 
1146b3d6264cSchristos     intern_file_load(opts);
1147abb0f93cSkardel     return 0;
1148abb0f93cSkardel }
1149abb0f93cSkardel 
1150abb0f93cSkardel /*=export_func  optionLoadOpt
1151abb0f93cSkardel  * private:
1152abb0f93cSkardel  *
1153abb0f93cSkardel  * what:  Load an option rc/ini file
1154b3d6264cSchristos  * arg:   + tOptions * + opts  + program options descriptor +
1155b3d6264cSchristos  * arg:   + tOptDesc * + odesc + the descriptor for this arg +
1156abb0f93cSkardel  *
1157abb0f93cSkardel  * doc:
1158abb0f93cSkardel  *  Processes the options found in the file named with
1159b3d6264cSchristos  *  odesc->optArg.argString.
1160abb0f93cSkardel =*/
1161abb0f93cSkardel void
optionLoadOpt(tOptions * opts,tOptDesc * odesc)1162b3d6264cSchristos optionLoadOpt(tOptions * opts, tOptDesc * odesc)
1163abb0f93cSkardel {
1164abb0f93cSkardel     struct stat sb;
1165abb0f93cSkardel 
1166b3d6264cSchristos     if (opts <= OPTPROC_EMIT_LIMIT)
1167b3d6264cSchristos         return;
1168b3d6264cSchristos 
1169abb0f93cSkardel     /*
1170abb0f93cSkardel      *  IF the option is not being disabled, THEN load the file.  There must
1171abb0f93cSkardel      *  be a file.  (If it is being disabled, then the disablement processing
1172abb0f93cSkardel      *  already took place.  It must be done to suppress preloading of ini/rc
1173abb0f93cSkardel      *  files.)
1174abb0f93cSkardel      */
1175b3d6264cSchristos     if (  DISABLED_OPT(odesc)
1176b3d6264cSchristos        || ((odesc->fOptState & OPTST_RESET) != 0))
1177abb0f93cSkardel         return;
1178abb0f93cSkardel 
1179b3d6264cSchristos     if (stat(odesc->optArg.argString, &sb) != 0) {
1180b3d6264cSchristos         if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
1181abb0f93cSkardel             return;
1182abb0f93cSkardel 
1183b3d6264cSchristos         fserr_exit(opts->pzProgName, "stat", odesc->optArg.argString);
1184abb0f93cSkardel         /* NOT REACHED */
1185abb0f93cSkardel     }
1186abb0f93cSkardel 
1187abb0f93cSkardel     if (! S_ISREG(sb.st_mode)) {
1188b3d6264cSchristos         if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
1189abb0f93cSkardel             return;
1190b3d6264cSchristos         errno = EINVAL;
1191b3d6264cSchristos         fserr_exit(opts->pzProgName, "stat", odesc->optArg.argString);
1192abb0f93cSkardel         /* NOT REACHED */
1193abb0f93cSkardel     }
1194abb0f93cSkardel 
1195b3d6264cSchristos     file_preset(opts, odesc->optArg.argString, DIRECTION_CALLED);
1196abb0f93cSkardel }
1197abb0f93cSkardel 
11984305584aSkardel /**
1199abb0f93cSkardel  *  Parse the various attributes of an XML-styled config file entry
1200b3d6264cSchristos  *
1201b3d6264cSchristos  * @returns NULL on failure, otherwise the scan point
1202abb0f93cSkardel  */
1203b3d6264cSchristos LOCAL char const *
parse_attrs(tOptions * opts,char const * txt,tOptionLoadMode * pMode,tOptionValue * pType)1204b3d6264cSchristos parse_attrs(tOptions * opts, char const * txt, tOptionLoadMode * pMode,
1205abb0f93cSkardel             tOptionValue * pType)
1206abb0f93cSkardel {
1207b3d6264cSchristos     size_t len = 0;
1208abb0f93cSkardel 
1209b3d6264cSchristos     for (;;) {
1210b3d6264cSchristos         len = (size_t)(SPN_LOWER_CASE_CHARS(txt) - txt);
1211abb0f93cSkardel 
1212b3d6264cSchristos         /*
1213b3d6264cSchristos          * The enumeration used in this switch is derived from this switch
1214b3d6264cSchristos          * statement itself.  The "find_option_xat_attribute_cmd" function
1215b3d6264cSchristos          * will return XAT_CMD_MEMBERS for the "txt" string value
1216b3d6264cSchristos          * "members", etc.
1217b3d6264cSchristos          */
1218b3d6264cSchristos         switch (find_option_xat_attribute_cmd(txt, len)) {
1219b3d6264cSchristos         case XAT_CMD_TYPE:
1220b3d6264cSchristos             txt = parse_value(txt+len, pType);
1221abb0f93cSkardel             break;
1222abb0f93cSkardel 
1223b3d6264cSchristos         case XAT_CMD_WORDS:
1224b3d6264cSchristos             txt = parse_keyword(opts, txt+len, pType);
1225abb0f93cSkardel             break;
1226abb0f93cSkardel 
1227b3d6264cSchristos         case XAT_CMD_MEMBERS:
1228b3d6264cSchristos             txt = parse_set_mem(opts, txt+len, pType);
1229abb0f93cSkardel             break;
1230abb0f93cSkardel 
1231b3d6264cSchristos         case XAT_CMD_COOKED:
1232b3d6264cSchristos             txt += len;
1233b3d6264cSchristos             if (! IS_END_XML_TOKEN_CHAR(*txt))
1234abb0f93cSkardel                 goto invalid_kwd;
1235abb0f93cSkardel 
1236abb0f93cSkardel             *pMode = OPTION_LOAD_COOKED;
1237abb0f93cSkardel             break;
1238abb0f93cSkardel 
1239b3d6264cSchristos         case XAT_CMD_UNCOOKED:
1240b3d6264cSchristos             txt += len;
1241b3d6264cSchristos             if (! IS_END_XML_TOKEN_CHAR(*txt))
1242abb0f93cSkardel                 goto invalid_kwd;
1243abb0f93cSkardel 
1244abb0f93cSkardel             *pMode = OPTION_LOAD_UNCOOKED;
1245abb0f93cSkardel             break;
1246abb0f93cSkardel 
1247b3d6264cSchristos         case XAT_CMD_KEEP:
1248b3d6264cSchristos             txt += len;
1249b3d6264cSchristos             if (! IS_END_XML_TOKEN_CHAR(*txt))
1250abb0f93cSkardel                 goto invalid_kwd;
1251abb0f93cSkardel 
1252abb0f93cSkardel             *pMode = OPTION_LOAD_KEEP;
1253abb0f93cSkardel             break;
1254abb0f93cSkardel 
1255abb0f93cSkardel         default:
1256b3d6264cSchristos         case XAT_INVALID_CMD:
1257abb0f93cSkardel         invalid_kwd:
1258abb0f93cSkardel             pType->valType = OPARG_TYPE_NONE;
1259b3d6264cSchristos             return skip_unkn(txt);
1260abb0f93cSkardel         }
1261abb0f93cSkardel 
1262b3d6264cSchristos         if (txt == NULL)
1263b3d6264cSchristos             return NULL;
1264b3d6264cSchristos         txt = SPN_WHITESPACE_CHARS(txt);
1265b3d6264cSchristos         switch (*txt) {
1266b3d6264cSchristos             case '/': pType->valType = OPARG_TYPE_NONE;
1267b3d6264cSchristos                       /* FALLTHROUGH */
1268b3d6264cSchristos             case '>': return txt;
1269b3d6264cSchristos         }
1270b3d6264cSchristos         if (! IS_LOWER_CASE_CHAR(*txt))
1271b3d6264cSchristos             return NULL;
1272b3d6264cSchristos     }
1273b3d6264cSchristos }
1274abb0f93cSkardel 
12754305584aSkardel /**
1276b3d6264cSchristos  *  "txt" points to the character after "words=".
1277abb0f93cSkardel  *  What should follow is a name of a keyword (enumeration) list.
1278b3d6264cSchristos  *
1279b3d6264cSchristos  *  @param     opts  unused
1280b3d6264cSchristos  *  @param[in] txt   keyword to skip over
1281b3d6264cSchristos  *  @param     type  unused value type
1282b3d6264cSchristos  *  @returns   pointer after skipped text
1283abb0f93cSkardel  */
1284b3d6264cSchristos static char const *
parse_keyword(tOptions * opts,char const * txt,tOptionValue * typ)1285b3d6264cSchristos parse_keyword(tOptions * opts, char const * txt, tOptionValue * typ)
1286abb0f93cSkardel {
1287b3d6264cSchristos     (void)opts;
1288b3d6264cSchristos     (void)typ;
1289b3d6264cSchristos 
1290b3d6264cSchristos     return skip_unkn(txt);
1291abb0f93cSkardel }
1292abb0f93cSkardel 
12934305584aSkardel /**
1294b3d6264cSchristos  *  "txt" points to the character after "members="
1295abb0f93cSkardel  *  What should follow is a name of a "set membership".
1296abb0f93cSkardel  *  A collection of bit flags.
1297b3d6264cSchristos  *
1298b3d6264cSchristos  *  @param     opts  unused
1299b3d6264cSchristos  *  @param[in] txt   keyword to skip over
1300b3d6264cSchristos  *  @param     type  unused value type
1301b3d6264cSchristos  *  @returns   pointer after skipped text
1302abb0f93cSkardel  */
1303b3d6264cSchristos static char const *
parse_set_mem(tOptions * opts,char const * txt,tOptionValue * typ)1304b3d6264cSchristos parse_set_mem(tOptions * opts, char const * txt, tOptionValue * typ)
1305abb0f93cSkardel {
1306b3d6264cSchristos     (void)opts;
1307b3d6264cSchristos     (void)typ;
1308b3d6264cSchristos 
1309b3d6264cSchristos     return skip_unkn(txt);
1310abb0f93cSkardel }
1311abb0f93cSkardel 
13124305584aSkardel /**
1313b3d6264cSchristos  *  parse the type.  The keyword "type" was found, now figure out
1314b3d6264cSchristos  *  the type that follows the type.
1315b3d6264cSchristos  *
1316b3d6264cSchristos  *  @param[in]  txt  points to the '=' character after the "type" keyword.
1317b3d6264cSchristos  *  @param[out] typ  where to store the type found
1318b3d6264cSchristos  *  @returns    the next byte after the type name
1319abb0f93cSkardel  */
1320b3d6264cSchristos static char const *
parse_value(char const * txt,tOptionValue * typ)1321b3d6264cSchristos parse_value(char const * txt, tOptionValue * typ)
1322abb0f93cSkardel {
1323abb0f93cSkardel     size_t len = 0;
1324abb0f93cSkardel 
1325b3d6264cSchristos     if (*(txt++) != '=')
1326abb0f93cSkardel         goto woops;
1327abb0f93cSkardel 
1328b3d6264cSchristos     len = (size_t)(SPN_OPTION_NAME_CHARS(txt) - txt);
1329abb0f93cSkardel 
1330b3d6264cSchristos     if ((len == 0) || (! IS_END_XML_TOKEN_CHAR(txt[len]))) {
1331abb0f93cSkardel     woops:
1332b3d6264cSchristos         typ->valType = OPARG_TYPE_NONE;
1333b3d6264cSchristos         return skip_unkn(txt + len);
1334abb0f93cSkardel     }
1335abb0f93cSkardel 
1336b3d6264cSchristos     /*
1337b3d6264cSchristos      * The enumeration used in this switch is derived from this switch
1338b3d6264cSchristos      * statement itself.  The "find_option_value_type_cmd" function
1339b3d6264cSchristos      * will return VTP_CMD_INTEGER for the "txt" string value
1340b3d6264cSchristos      * "integer", etc.
1341b3d6264cSchristos      */
1342b3d6264cSchristos     switch (find_option_value_type_cmd(txt, len)) {
1343abb0f93cSkardel     default:
1344b3d6264cSchristos     case VTP_INVALID_CMD: goto woops;
1345abb0f93cSkardel 
1346b3d6264cSchristos     case VTP_CMD_STRING:
1347b3d6264cSchristos         typ->valType = OPARG_TYPE_STRING;
1348abb0f93cSkardel         break;
1349abb0f93cSkardel 
1350b3d6264cSchristos     case VTP_CMD_INTEGER:
1351b3d6264cSchristos         typ->valType = OPARG_TYPE_NUMERIC;
1352abb0f93cSkardel         break;
1353abb0f93cSkardel 
1354b3d6264cSchristos     case VTP_CMD_BOOL:
1355b3d6264cSchristos     case VTP_CMD_BOOLEAN:
1356b3d6264cSchristos         typ->valType = OPARG_TYPE_BOOLEAN;
1357abb0f93cSkardel         break;
1358abb0f93cSkardel 
1359b3d6264cSchristos     case VTP_CMD_KEYWORD:
1360b3d6264cSchristos         typ->valType = OPARG_TYPE_ENUMERATION;
1361abb0f93cSkardel         break;
1362abb0f93cSkardel 
1363b3d6264cSchristos     case VTP_CMD_SET:
1364b3d6264cSchristos     case VTP_CMD_SET_MEMBERSHIP:
1365b3d6264cSchristos         typ->valType = OPARG_TYPE_MEMBERSHIP;
1366abb0f93cSkardel         break;
1367abb0f93cSkardel 
1368b3d6264cSchristos     case VTP_CMD_NESTED:
1369b3d6264cSchristos     case VTP_CMD_HIERARCHY:
1370b3d6264cSchristos         typ->valType = OPARG_TYPE_HIERARCHY;
1371abb0f93cSkardel     }
1372abb0f93cSkardel 
1373b3d6264cSchristos     return txt + len;
1374abb0f93cSkardel }
1375abb0f93cSkardel 
1376b3d6264cSchristos /** @}
1377b3d6264cSchristos  *
1378abb0f93cSkardel  * Local Variables:
1379abb0f93cSkardel  * mode: C
1380abb0f93cSkardel  * c-file-style: "stroustrup"
1381abb0f93cSkardel  * indent-tabs-mode: nil
1382abb0f93cSkardel  * End:
1383abb0f93cSkardel  * end of autoopts/configfile.c */
1384