4 * configuration/rc/ini file handling.
10 * This file is part of AutoOpts, a companion to AutoGen.
11 * AutoOpts is free software.
12 * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
14 * AutoOpts is available under any one of two licenses. The license
15 * in use must be one of these two and the choice is under the control
16 * of the user of the license.
18 * The GNU Lesser General Public License, version 3 or later
19 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
21 * The Modified Berkeley Software Distribution License
22 * See the file "COPYING.mbsd"
24 * These files have the following sha256 sums:
26 * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
27 * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
28 * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
31 /* = = = START-STATIC-FORWARD = = = */
33 file_preset(tOptions * opts, char const * fname, int dir);
36 handle_comment(char * txt);
39 handle_cfg(tOptions * opts, tOptState * ost, char * txt, int dir);
42 handle_directive(tOptions * opts, char * txt);
45 aoflags_directive(tOptions * opts, char * txt);
48 program_directive(tOptions * opts, char * txt);
51 handle_section(tOptions * opts, char * txt);
54 parse_xml_encoding(char ** ppz);
57 trim_xml_text(char * intxt, char const * pznm, tOptionLoadMode mode);
60 cook_xml_text(char * pzData);
63 handle_struct(tOptions * opts, tOptState * ost, char * txt, int dir);
66 parse_keyword(tOptions * opts, char const * txt, tOptionValue * typ);
69 parse_set_mem(tOptions * opts, char const * txt, tOptionValue * typ);
72 parse_value(char const * txt, tOptionValue * typ);
73 /* = = = END-STATIC-FORWARD = = = */
76 * Skip over some unknown attribute
77 * @param[in] txt start of skpped text
78 * @returns character after skipped text
80 inline static char const *
81 skip_unkn(char const * txt)
83 txt = BRK_END_XML_TOKEN_CHARS(txt);
84 return (*txt == NUL) ? NULL : txt;
87 /*=export_func configFileLoad
89 * what: parse a configuration file
90 * arg: + char const * + fname + the file to load +
92 * ret_type: const tOptionValue *
93 * ret_desc: An allocated, compound value structure
96 * This routine will load a named configuration file and parse the
97 * text as a hierarchically valued option. The option descriptor
98 * created from an option definition file is not used via this interface.
99 * The returned value is "named" with the input file name and is of
100 * type "@code{OPARG_TYPE_HIERARCHY}". It may be used in calls to
101 * @code{optionGetValue()}, @code{optionNextValue()} and
102 * @code{optionUnloadNested()}.
105 * If the file cannot be loaded or processed, @code{NULL} is returned and
106 * @var{errno} is set. It may be set by a call to either @code{open(2)}
107 * @code{mmap(2)} or other file system calls, or it may be:
110 * @code{ENOENT} - the file was not found.
112 * @code{ENOMSG} - the file was empty.
114 * @code{EINVAL} - the file contents are invalid -- not properly formed.
116 * @code{ENOMEM} - not enough memory to allocate the needed structures.
120 configFileLoad(char const * fname)
123 tOptionValue * res = NULL;
124 tOptionLoadMode save_mode = option_load_mode;
126 char * txt = text_mmap(fname, PROT_READ, MAP_PRIVATE, &cfgfile);
128 if (TEXT_MMAP_FAILED_ADDR(txt))
129 return NULL; /* errno is set */
131 option_load_mode = OPTION_LOAD_COOKED;
132 res = optionLoadNested(txt, fname, strlen(fname));
136 text_munmap(&cfgfile);
139 text_munmap(&cfgfile);
141 option_load_mode = save_mode;
146 /*=export_func optionFindValue
148 * what: find a hierarcicaly valued option instance
149 * arg: + const tOptDesc * + odesc + an option with a nested arg type +
150 * arg: + char const * + name + name of value to find +
151 * arg: + char const * + val + the matching value +
153 * ret_type: const tOptionValue *
154 * ret_desc: a compound value structure
157 * This routine will find an entry in a nested value option or configurable.
158 * It will search through the list and return a matching entry.
161 * The returned result is NULL and errno is set:
164 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
165 * hierarchical option value.
167 * @code{ENOENT} - no entry matched the given name.
171 optionFindValue(const tOptDesc * odesc, char const * name, char const * val)
173 const tOptionValue * res = NULL;
176 || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY)) {
180 else if (odesc->optCookie == NULL) {
185 tArgList * argl = odesc->optCookie;
186 int argct = argl->useCt;
187 const void ** poptv = VOIDP(argl->apzArgs);
195 res = (const tOptionValue *)*poptv;
199 while (--argct >= 0) {
200 const tOptionValue * ov = *(poptv++);
201 const tOptionValue * rv = optionGetValue(ov, name);
219 /*=export_func optionFindNextValue
221 * FIXME: the handling of 'pzName' and 'pzVal' is just wrong.
223 * what: find a hierarcicaly valued option instance
224 * arg: + const tOptDesc * + odesc + an option with a nested arg type +
225 * arg: + const tOptionValue * + pPrevVal + the last entry +
226 * arg: + char const * + name + name of value to find +
227 * arg: + char const * + value + the matching value +
229 * ret_type: const tOptionValue *
230 * ret_desc: a compound value structure
233 * This routine will find the next entry in a nested value option or
234 * configurable. It will search through the list and return the next entry
235 * that matches the criteria.
238 * The returned result is NULL and errno is set:
241 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
242 * hierarchical option value.
244 * @code{ENOENT} - no entry matched the given name.
248 optionFindNextValue(const tOptDesc * odesc, const tOptionValue * pPrevVal,
249 char const * pzName, char const * pzVal)
251 bool old_found = false;
252 const tOptionValue * res = NULL;
258 || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY)) {
262 else if (odesc->optCookie == NULL) {
267 tArgList * argl = odesc->optCookie;
268 int ct = argl->useCt;
269 const void ** poptv = VOIDP(argl->apzArgs);
272 const tOptionValue * pOV = *(poptv++);
288 /*=export_func optionGetValue
290 * what: get a specific value from a hierarcical list
291 * arg: + const tOptionValue * + pOptValue + a hierarchcal value +
292 * arg: + char const * + valueName + name of value to get +
294 * ret_type: const tOptionValue *
295 * ret_desc: a compound value structure
298 * This routine will find an entry in a nested value option or configurable.
299 * If "valueName" is NULL, then the first entry is returned. Otherwise,
300 * the first entry with a name that exactly matches the argument will be
301 * returned. If there is no matching value, NULL is returned and errno is
302 * set to ENOENT. If the provided option value is not a hierarchical value,
303 * NULL is also returned and errno is set to EINVAL.
306 * The returned result is NULL and errno is set:
309 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
310 * hierarchical option value.
312 * @code{ENOENT} - no entry matched the given name.
316 optionGetValue(tOptionValue const * oov, char const * vname)
319 const tOptionValue * res = NULL;
321 if ((oov == NULL) || (oov->valType != OPARG_TYPE_HIERARCHY)) {
325 arg_list = oov->v.nestVal;
327 if (arg_list->useCt > 0) {
328 int ct = arg_list->useCt;
329 const void ** ovlist = VOIDP(arg_list->apzArgs);
332 res = (const tOptionValue *)*ovlist;
335 const tOptionValue * opt_val = *(ovlist++);
336 if (strcmp(opt_val->pzName, vname) == 0) {
347 /*=export_func optionNextValue
349 * what: get the next value from a hierarchical list
350 * arg: + const tOptionValue * + pOptValue + a hierarchcal list value +
351 * arg: + const tOptionValue * + pOldValue + a value from this list +
353 * ret_type: const tOptionValue *
354 * ret_desc: a compound value structure
357 * This routine will return the next entry after the entry passed in. At the
358 * end of the list, NULL will be returned. If the entry is not found on the
359 * list, NULL will be returned and "@var{errno}" will be set to EINVAL.
360 * The "@var{pOldValue}" must have been gotten from a prior call to this
361 * routine or to "@code{opitonGetValue()}".
364 * The returned result is NULL and errno is set:
367 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
368 * hierarchical option value or @code{pOldValue} does not point to a
369 * member of that option value.
371 * @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry.
375 optionNextValue(tOptionValue const * ov_list,tOptionValue const * oov )
378 const tOptionValue * res = NULL;
381 if ((ov_list == NULL) || (ov_list->valType != OPARG_TYPE_HIERARCHY)) {
385 arg_list = ov_list->v.nestVal;
387 int ct = arg_list->useCt;
388 const void ** o_list = VOIDP(arg_list->apzArgs);
391 const tOptionValue * nov = *(o_list++);
398 res = (const tOptionValue *)*o_list;
410 * Load a file containing presetting information (a configuration file).
413 file_preset(tOptions * opts, char const * fname, int dir)
416 tOptState optst = OPTSTATE_INITIALIZER(PRESET);
417 opt_state_mask_t st_flags = optst.flags;
418 opt_state_mask_t fl_save = opts->fOptSet;
420 text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile);
422 if (TEXT_MMAP_FAILED_ADDR(ftext))
426 * While processing config files, we ignore errors.
428 opts->fOptSet &= ~OPTPROC_ERRSTOP;
430 if (dir == DIRECTION_CALLED) {
431 st_flags = OPTST_DEFINED;
432 dir = DIRECTION_PROCESS;
436 * IF this is called via "optionProcess", then we are presetting.
437 * This is the default and the PRESETTING bit will be set.
438 * If this is called via "optionFileLoad", then the bit is not set
439 * and we consider stuff set herein to be "set" by the client program.
441 if ((opts->fOptSet & OPTPROC_PRESETTING) == 0)
442 st_flags = OPTST_SET;
445 optst.flags = st_flags;
446 ftext = SPN_WHITESPACE_CHARS(ftext);
448 if (IS_VAR_FIRST_CHAR(*ftext)) {
449 ftext = handle_cfg(opts, &optst, ftext, dir);
451 } else switch (*ftext) {
453 if (IS_VAR_FIRST_CHAR(ftext[1]))
454 ftext = handle_struct(opts, &optst, ftext, dir);
456 else switch (ftext[1]) {
458 ftext = handle_directive(opts, ftext);
462 ftext = handle_comment(ftext);
466 ftext = strchr(ftext + 2, '>');
478 ftext = handle_section(opts, ftext);
482 ftext = strchr(ftext + 1, NL);
486 goto all_done; /* invalid format */
488 } while (ftext != NULL);
491 text_munmap(&cfgfile);
492 opts->fOptSet = fl_save;
496 * "txt" points to a "<!" sequence.
497 * Theoretically, we should ensure that it begins with "<!--",
498 * but actually I don't care that much. It ends with "-->".
501 handle_comment(char * txt)
503 char * pz = strstr(txt, "-->");
510 * "txt" points to the start of some value name.
511 * The end of the entry is the end of the line that is not preceded by
512 * a backslash escape character. The string value is always processed
516 handle_cfg(tOptions * opts, tOptState * ost, char * txt, int dir)
518 char * pzName = txt++;
519 char * pzEnd = strchr(txt, NL);
522 return txt + strlen(txt);
524 txt = SPN_VALUE_NAME_CHARS(txt);
525 txt = SPN_WHITESPACE_CHARS(txt);
529 load_opt_line(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED);
534 * Either the first character after the name is a ':' or '=',
535 * or else we must have skipped over white space. Anything else
536 * is an invalid format and we give up parsing the text.
538 if ((*txt == '=') || (*txt == ':')) {
539 txt = SPN_WHITESPACE_CHARS(txt+1);
542 } else if (! IS_WHITESPACE_CHAR(txt[-1]))
546 * IF the value is continued, remove the backslash escape and push "pzEnd"
547 * on to a newline *not* preceded by a backslash.
549 if (pzEnd[-1] == '\\') {
550 char * pcD = pzEnd-1;
576 * The newline was not preceded by a backslash. NUL it out
582 * "pzName" points to what looks like text for one option/configurable.
583 * It is NUL terminated. Process it.
585 load_opt_line(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED);
591 * "txt" points to a "<?" sequence.
592 * We handle "<?program" and "<?auto-options" directives.
593 * All others are treated as comments.
595 * @param[in,out] opts program option descriptor
596 * @param[in] txt scanning pointer
597 * @returns the next character to look at
600 handle_directive(tOptions * opts, char * txt)
602 # define DIRECTIVE_TABLE \
603 _dt_(zCfgProg, program_directive) \
604 _dt_(zCfgAO_Flags, aoflags_directive)
606 typedef char * (directive_func_t)(tOptions *, char *);
607 # define _dt_(_s, _fn) _fn,
608 static directive_func_t * dir_disp[] = {
613 # define _dt_(_s, _fn) 1 +
614 static int const dir_ct = DIRECTIVE_TABLE 0;
615 static char const * dir_names[DIRECTIVE_TABLE 0];
620 if (dir_names[0] == NULL) {
622 # define _dt_(_s, _fn) dir_names[ix++] = _s;
627 for (ix = 0; ix < dir_ct; ix++) {
628 size_t len = strlen(dir_names[ix]);
629 if ( (strncmp(txt + 2, dir_names[ix], len) == 0)
630 && (! IS_VALUE_NAME_CHAR(txt[len+2])) )
631 return dir_disp[ix](opts, txt + len + 2);
635 * We don't know what this is. Skip it.
637 txt = strchr(txt+2, '>');
641 # undef DIRECTIVE_TABLE
645 * handle AutoOpts mode flags.
647 * @param[in,out] opts program option descriptor
648 * @param[in] txt scanning pointer
649 * @returns the next character to look at
652 aoflags_directive(tOptions * opts, char * txt)
656 pz = SPN_WHITESPACE_CHARS(txt+1);
657 txt = strchr(pz, '>');
660 size_t len = (unsigned)(txt - pz);
661 char * ftxt = AGALOC(len + 1, "aoflags");
663 memcpy(ftxt, pz, len);
665 set_usage_flags(opts, ftxt);
675 * handle program segmentation of config file.
677 * @param[in,out] opts program option descriptor
678 * @param[in] txt scanning pointer
679 * @returns the next character to look at
682 program_directive(tOptions * opts, char * txt)
684 static char const ttlfmt[] = "<?";
685 size_t ttl_len = sizeof(ttlfmt) + strlen(zCfgProg);
686 char * ttl = AGALOC(ttl_len, "prog title");
687 size_t name_len = strlen(opts->pzProgName);
689 memcpy(ttl, ttlfmt, sizeof(ttlfmt) - 1);
690 memcpy(ttl + sizeof(ttlfmt) - 1, zCfgProg, ttl_len - (sizeof(ttlfmt) - 1));
693 txt = SPN_WHITESPACE_CHARS(txt+1);
695 if ( (strneqvcmp(txt, opts->pzProgName, (int)name_len) == 0)
696 && (IS_END_XML_TOKEN_CHAR(txt[name_len])) ) {
701 txt = strstr(txt, ttl);
702 } while (txt != NULL);
719 * "txt" points to a '[' character.
720 * The "traditional" [PROG_NAME] segmentation of the config file.
721 * Do not ever mix with the "<?program prog-name>" variation.
723 * @param[in,out] opts program option descriptor
724 * @param[in] txt scanning pointer
725 * @returns the next character to look at
728 handle_section(tOptions * opts, char * txt)
730 size_t len = strlen(opts->pzPROGNAME);
731 if ( (strncmp(txt+1, opts->pzPROGNAME, len) == 0)
732 && (txt[len+1] == ']'))
733 return strchr(txt + len + 2, NL);
740 sprintf(z, "[%s]", opts->pzPROGNAME);
741 txt = strstr(txt, z);
745 txt = strchr(txt, NL);
750 * parse XML encodings
753 parse_xml_encoding(char ** ppz)
765 _xmlNm_(space, ' ') \
770 char const * const nm_str;
771 unsigned short nm_len;
773 } const xml_names[] = {
774 # define _xmlNm_(_n, _v) { #_n ";", sizeof(#_n), _v },
780 static int const nm_ct = sizeof(xml_names) / sizeof(xml_names[0]);
790 if (IS_DEC_DIGIT_CHAR(*pz)) {
797 * Some forms specify hex with: &#xNN;
805 *  is hex and  is decimal. Cool.
813 v = strtoul(pz, &pz, base);
814 if ((*pz != ';') || (v > 0x7F))
823 if (strncmp(pz, xml_names[ix].nm_str, xml_names[ix].nm_len)
825 *ppz = pz + xml_names[ix].nm_len;
826 return xml_names[ix].nm_val;
828 } while (++ix < nm_ct);
835 * Find the end marker for the named section of XML.
836 * Trim that text there, trimming trailing white space for all modes
837 * except for OPTION_LOAD_UNCOOKED.
840 trim_xml_text(char * intxt, char const * pznm, tOptionLoadMode mode)
842 static char const fmt[] = "</%s>";
843 size_t len = strlen(pznm) + sizeof(fmt) - 2 /* for %s */;
848 if (len >= sizeof(z))
849 pz = AGALOC(len, "scan name");
851 len = (size_t)sprintf(pz, fmt, pznm);
853 etext = strstr(intxt, pz);
854 if (pz != z) AGFREE(pz);
861 char * result = etext + len;
863 if (mode != OPTION_LOAD_UNCOOKED)
864 etext = SPN_WHITESPACE_BACK(intxt, etext);
874 cook_xml_text(char * pzData)
882 int ch = ((int)*(pzs++)) & 0xFF;
889 ch = parse_xml_encoding(&pzs);
898 if ((bf[0] == NUL) || (bf[1] == NUL)) {
903 ch = (int)strtoul(bf, NULL, 16);
913 * "txt" points to a '<' character, followed by an alpha.
914 * The end of the entry is either the "/>" following the name, or else a
918 handle_struct(tOptions * opts, tOptState * ost, char * txt, int dir)
920 tOptionLoadMode mode = option_load_mode;
923 char * pzName = ++txt;
927 txt = SPN_VALUE_NAME_CHARS(txt);
929 valu.valType = OPARG_TYPE_STRING;
934 txt = VOIDP(parse_attrs(
935 opts, SPN_WHITESPACE_CHARS(txt), &mode, &valu));
949 load_opt_line(opts, ost, pzName, dir, mode);
956 txt = strchr(txt, '>');
963 * If we are here, we have a value. "txt" points to a closing angle
964 * bracket. Separate the name from the value for a moment.
968 txt = trim_xml_text(txt, pzName, mode);
973 * Rejoin the name and value for parsing by "load_opt_line()".
974 * Erase any attributes parsed by "parse_attrs()".
976 memset(pcNulPoint, ' ', (size_t)(pzData - pcNulPoint));
979 * If we are getting a "string" value that is to be cooked,
980 * then process the XML-ish &xx; XML-ish and %XX hex characters.
982 if ( (valu.valType == OPARG_TYPE_STRING)
983 && (mode == OPTION_LOAD_COOKED))
984 cook_xml_text(pzData);
987 * "pzName" points to what looks like text for one option/configurable.
988 * It is NUL terminated. Process it.
990 load_opt_line(opts, ost, pzName, dir, mode);
996 * Load a configuration file. This may be invoked either from
997 * scanning the "homerc" list, or from a specific file request.
998 * (see "optionFileLoad()", the implementation for --load-opts)
1001 intern_file_load(tOptions * opts)
1006 char f_name[ AG_PATH_MAX+1 ];
1008 if (opts->papzHomeList == NULL)
1011 svfl = opts->fOptSet;
1012 inc = DIRECTION_PRESET;
1015 * Never stop on errors in config files.
1017 opts->fOptSet &= ~OPTPROC_ERRSTOP;
1020 * Find the last RC entry (highest priority entry)
1022 for (idx = 0; opts->papzHomeList[ idx+1 ] != NULL; ++idx) ;
1025 * For every path in the home list, ... *TWICE* We start at the last
1026 * (highest priority) entry, work our way down to the lowest priority,
1027 * handling the immediate options.
1028 * Then we go back up, doing the normal options.
1035 * IF we've reached the bottom end, change direction
1038 inc = DIRECTION_PROCESS;
1042 path = opts->papzHomeList[ idx ];
1045 * IF we've reached the top end, bail out
1052 if (! optionMakePath(f_name, (int)sizeof(f_name),
1053 path, opts->pzProgPath))
1057 * IF the file name we constructed is a directory,
1058 * THEN append the Resource Configuration file name
1059 * ELSE we must have the complete file name
1061 if (stat(f_name, &sb) != 0)
1062 continue; /* bogus name - skip the home list entry */
1064 if (S_ISDIR(sb.st_mode)) {
1065 size_t len = strlen(f_name);
1066 size_t nln = strlen(opts->pzRcName) + 1;
1067 char * pz = f_name + len;
1069 if (len + 1 + nln >= sizeof(f_name))
1072 if (pz[-1] != DIRCH)
1074 memcpy(pz, opts->pzRcName, nln);
1077 file_preset(opts, f_name, inc);
1080 * IF we are now to skip config files AND we are presetting,
1081 * THEN change direction. We must go the other way.
1084 tOptDesc * od = opts->pOptDesc + opts->specOptIdx.save_opts + 1;
1085 if (DISABLED_OPT(od) && PRESETTING(inc)) {
1086 idx -= inc; /* go back and reprocess current file */
1087 inc = DIRECTION_PROCESS;
1090 } /* twice for every path in the home list, ... */
1092 opts->fOptSet = svfl;
1095 /*=export_func optionFileLoad
1097 * what: Load the locatable config files, in order
1099 * arg: + tOptions * + opts + program options descriptor +
1100 * arg: + char const * + prog + program name +
1103 * ret_desc: 0 -> SUCCESS, -1 -> FAILURE
1107 * This function looks in all the specified directories for a configuration
1108 * file ("rc" file or "ini" file) and processes any found twice. The first
1109 * time through, they are processed in reverse order (last file first). At
1110 * that time, only "immediate action" configurables are processed. For
1111 * example, if the last named file specifies not processing any more
1112 * configuration files, then no more configuration files will be processed.
1113 * Such an option in the @strong{first} named directory will have no effect.
1115 * Once the immediate action configurables have been handled, then the
1116 * directories are handled in normal, forward order. In that way, later
1117 * config files can override the settings of earlier config files.
1119 * See the AutoOpts documentation for a thorough discussion of the
1120 * config file format.
1122 * Configuration files not found or not decipherable are simply ignored.
1124 * err: Returns the value, "-1" if the program options descriptor
1125 * is out of date or indecipherable. Otherwise, the value "0" will
1126 * always be returned.
1129 optionFileLoad(tOptions * opts, char const * prog)
1131 if (! SUCCESSFUL(validate_struct(opts, prog)))
1135 * The pointer to the program name is "const". However, the
1136 * structure is in writable memory, so we coerce the address
1137 * of this pointer to point to writable memory.
1140 char const ** pp = VOIDP(&(opts->pzProgName));
1144 intern_file_load(opts);
1148 /*=export_func optionLoadOpt
1151 * what: Load an option rc/ini file
1152 * arg: + tOptions * + opts + program options descriptor +
1153 * arg: + tOptDesc * + odesc + the descriptor for this arg +
1156 * Processes the options found in the file named with
1157 * odesc->optArg.argString.
1160 optionLoadOpt(tOptions * opts, tOptDesc * odesc)
1164 if (opts <= OPTPROC_EMIT_LIMIT)
1168 * IF the option is not being disabled, THEN load the file. There must
1169 * be a file. (If it is being disabled, then the disablement processing
1170 * already took place. It must be done to suppress preloading of ini/rc
1173 if ( DISABLED_OPT(odesc)
1174 || ((odesc->fOptState & OPTST_RESET) != 0))
1177 if (stat(odesc->optArg.argString, &sb) != 0) {
1178 if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
1181 fserr_exit(opts->pzProgName, "stat", odesc->optArg.argString);
1185 if (! S_ISREG(sb.st_mode)) {
1186 if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
1189 fserr_exit(opts->pzProgName, "stat", odesc->optArg.argString);
1193 file_preset(opts, odesc->optArg.argString, DIRECTION_CALLED);
1197 * Parse the various attributes of an XML-styled config file entry
1199 * @returns NULL on failure, otherwise the scan point
1202 parse_attrs(tOptions * opts, char const * txt, tOptionLoadMode * pMode,
1203 tOptionValue * pType)
1208 len = (size_t)(SPN_LOWER_CASE_CHARS(txt) - txt);
1211 * The enumeration used in this switch is derived from this switch
1212 * statement itself. The "find_option_xat_attribute_cmd" function
1213 * will return XAT_CMD_MEMBERS for the "txt" string value
1216 switch (find_option_xat_attribute_cmd(txt, len)) {
1218 txt = parse_value(txt+len, pType);
1222 txt = parse_keyword(opts, txt+len, pType);
1225 case XAT_CMD_MEMBERS:
1226 txt = parse_set_mem(opts, txt+len, pType);
1229 case XAT_CMD_COOKED:
1231 if (! IS_END_XML_TOKEN_CHAR(*txt))
1234 *pMode = OPTION_LOAD_COOKED;
1237 case XAT_CMD_UNCOOKED:
1239 if (! IS_END_XML_TOKEN_CHAR(*txt))
1242 *pMode = OPTION_LOAD_UNCOOKED;
1247 if (! IS_END_XML_TOKEN_CHAR(*txt))
1250 *pMode = OPTION_LOAD_KEEP;
1254 case XAT_INVALID_CMD:
1256 pType->valType = OPARG_TYPE_NONE;
1257 return skip_unkn(txt);
1262 txt = SPN_WHITESPACE_CHARS(txt);
1264 case '/': pType->valType = OPARG_TYPE_NONE;
1266 case '>': return txt;
1268 if (! IS_LOWER_CASE_CHAR(*txt))
1274 * "txt" points to the character after "words=".
1275 * What should follow is a name of a keyword (enumeration) list.
1277 * @param opts unused
1278 * @param[in] txt keyword to skip over
1279 * @param type unused value type
1280 * @returns pointer after skipped text
1283 parse_keyword(tOptions * opts, char const * txt, tOptionValue * typ)
1288 return skip_unkn(txt);
1292 * "txt" points to the character after "members="
1293 * What should follow is a name of a "set membership".
1294 * A collection of bit flags.
1296 * @param opts unused
1297 * @param[in] txt keyword to skip over
1298 * @param type unused value type
1299 * @returns pointer after skipped text
1302 parse_set_mem(tOptions * opts, char const * txt, tOptionValue * typ)
1307 return skip_unkn(txt);
1311 * parse the type. The keyword "type" was found, now figure out
1312 * the type that follows the type.
1314 * @param[in] txt points to the '=' character after the "type" keyword.
1315 * @param[out] typ where to store the type found
1316 * @returns the next byte after the type name
1319 parse_value(char const * txt, tOptionValue * typ)
1323 if (*(txt++) != '=')
1326 len = (size_t)(SPN_OPTION_NAME_CHARS(txt) - txt);
1328 if ((len == 0) || (! IS_END_XML_TOKEN_CHAR(txt[len]))) {
1330 typ->valType = OPARG_TYPE_NONE;
1331 return skip_unkn(txt + len);
1335 * The enumeration used in this switch is derived from this switch
1336 * statement itself. The "find_option_value_type_cmd" function
1337 * will return VTP_CMD_INTEGER for the "txt" string value
1340 switch (find_option_value_type_cmd(txt, len)) {
1342 case VTP_INVALID_CMD: goto woops;
1344 case VTP_CMD_STRING:
1345 typ->valType = OPARG_TYPE_STRING;
1348 case VTP_CMD_INTEGER:
1349 typ->valType = OPARG_TYPE_NUMERIC;
1353 case VTP_CMD_BOOLEAN:
1354 typ->valType = OPARG_TYPE_BOOLEAN;
1357 case VTP_CMD_KEYWORD:
1358 typ->valType = OPARG_TYPE_ENUMERATION;
1362 case VTP_CMD_SET_MEMBERSHIP:
1363 typ->valType = OPARG_TYPE_MEMBERSHIP;
1366 case VTP_CMD_NESTED:
1367 case VTP_CMD_HIERARCHY:
1368 typ->valType = OPARG_TYPE_HIERARCHY;
1378 * c-file-style: "stroustrup"
1379 * indent-tabs-mode: nil
1381 * end of autoopts/configfile.c */