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 *  is hex and  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