4 * Time-stamp: "2012-03-31 13:56:11 bkorb"
6 * configuration/rc/ini file handling.
8 * This file is part of AutoOpts, a companion to AutoGen.
9 * AutoOpts is free software.
10 * AutoOpts is Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
12 * AutoOpts is available under any one of two licenses. The license
13 * in use must be one of these two and the choice is under the control
14 * of the user of the license.
16 * The GNU Lesser General Public License, version 3 or later
17 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
19 * The Modified Berkeley Software Distribution License
20 * See the file "COPYING.mbsd"
22 * These files have the following md5sums:
24 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
25 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
26 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
29 /* = = = START-STATIC-FORWARD = = = */
31 file_preset(tOptions
* opts
, char const * fname
, int dir
);
34 handle_comment(char* pzText
);
37 handle_cfg(tOptions
* pOpts
, tOptState
* pOS
, char * pzText
, int dir
);
40 handle_directive(tOptions
* pOpts
, char * pzText
);
43 aoflags_directive(tOptions
* pOpts
, char * pzText
);
46 program_directive(tOptions
* pOpts
, char * pzText
);
49 handle_section(tOptions
* pOpts
, char * pzText
);
52 parse_xml_encoding(char ** ppz
);
55 trim_xml_text(char * intxt
, char const * pznm
, tOptionLoadMode mode
);
58 cook_xml_text(char * pzData
);
61 handle_struct(tOptions
* pOpts
, tOptState
* pOS
, char * pzText
, int dir
);
64 parse_keyword(tOptions
* pOpts
, char * pzText
, tOptionValue
* pType
);
67 parse_set_mem(tOptions
* pOpts
, char * pzText
, tOptionValue
* pType
);
70 parse_value(char * pzText
, tOptionValue
* pType
);
73 skip_unkn(char* pzText
);
74 /* = = = END-STATIC-FORWARD = = = */
77 /*=export_func configFileLoad
79 * what: parse a configuration file
80 * arg: + char const* + pzFile + the file to load +
82 * ret_type: const tOptionValue*
83 * ret_desc: An allocated, compound value structure
86 * This routine will load a named configuration file and parse the
87 * text as a hierarchically valued option. The option descriptor
88 * created from an option definition file is not used via this interface.
89 * The returned value is "named" with the input file name and is of
90 * type "@code{OPARG_TYPE_HIERARCHY}". It may be used in calls to
91 * @code{optionGetValue()}, @code{optionNextValue()} and
92 * @code{optionUnloadNested()}.
95 * If the file cannot be loaded or processed, @code{NULL} is returned and
96 * @var{errno} is set. It may be set by a call to either @code{open(2)}
97 * @code{mmap(2)} or other file system calls, or it may be:
100 * @code{ENOENT} - the file was not found.
102 * @code{ENOMSG} - the file was empty.
104 * @code{EINVAL} - the file contents are invalid -- not properly formed.
106 * @code{ENOMEM} - not enough memory to allocate the needed structures.
110 configFileLoad(char const* pzFile
)
113 tOptionValue
* pRes
= NULL
;
114 tOptionLoadMode save_mode
= option_load_mode
;
117 text_mmap(pzFile
, PROT_READ
, MAP_PRIVATE
, &cfgfile
);
119 if (TEXT_MMAP_FAILED_ADDR(pzText
))
120 return NULL
; /* errno is set */
122 option_load_mode
= OPTION_LOAD_COOKED
;
123 pRes
= optionLoadNested(pzText
, pzFile
, strlen(pzFile
));
127 text_munmap(&cfgfile
);
130 text_munmap(&cfgfile
);
132 option_load_mode
= save_mode
;
137 /*=export_func optionFindValue
139 * what: find a hierarcicaly valued option instance
140 * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type +
141 * arg: + char const* + name + name of value to find +
142 * arg: + char const* + value + the matching value +
144 * ret_type: const tOptionValue*
145 * ret_desc: a compound value structure
148 * This routine will find an entry in a nested value option or configurable.
149 * It will search through the list and return a matching entry.
152 * The returned result is NULL and errno is set:
155 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
156 * hierarchical option value.
158 * @code{ENOENT} - no entry matched the given name.
162 optionFindValue(const tOptDesc
* pOptDesc
, char const* pzName
,
165 const tOptionValue
* pRes
= NULL
;
167 if ( (pOptDesc
== NULL
)
168 || (OPTST_GET_ARGTYPE(pOptDesc
->fOptState
) != OPARG_TYPE_HIERARCHY
)) {
172 else if (pOptDesc
->optCookie
== NULL
) {
177 tArgList
* pAL
= pOptDesc
->optCookie
;
179 void** ppOV
= (void**)(pAL
->apzArgs
);
186 if (pzName
== NULL
) {
187 pRes
= (tOptionValue
*)*ppOV
;
192 const tOptionValue
* pOV
= *(ppOV
++);
193 const tOptionValue
* pRV
= optionGetValue(pOV
, pzName
);
211 /*=export_func optionFindNextValue
213 * FIXME: the handling of 'pzName' and 'pzVal' is just wrong.
215 * what: find a hierarcicaly valued option instance
216 * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type +
217 * arg: + const tOptionValue* + pPrevVal + the last entry +
218 * arg: + char const* + name + name of value to find +
219 * arg: + char const* + value + the matching value +
221 * ret_type: const tOptionValue*
222 * ret_desc: a compound value structure
225 * This routine will find the next entry in a nested value option or
226 * configurable. It will search through the list and return the next entry
227 * that matches the criteria.
230 * The returned result is NULL and errno is set:
233 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
234 * hierarchical option value.
236 * @code{ENOENT} - no entry matched the given name.
240 optionFindNextValue(const tOptDesc
* pOptDesc
, const tOptionValue
* pPrevVal
,
241 char const * pzName
, char const * pzVal
)
243 bool old_found
= false;
244 tOptionValue
* pRes
= NULL
;
249 if ( (pOptDesc
== NULL
)
250 || (OPTST_GET_ARGTYPE(pOptDesc
->fOptState
) != OPARG_TYPE_HIERARCHY
)) {
254 else if (pOptDesc
->optCookie
== NULL
) {
259 tArgList
* pAL
= pOptDesc
->optCookie
;
261 void** ppOV
= (void**)pAL
->apzArgs
;
264 tOptionValue
* pOV
= *(ppOV
++);
280 /*=export_func optionGetValue
282 * what: get a specific value from a hierarcical list
283 * arg: + const tOptionValue* + pOptValue + a hierarchcal value +
284 * arg: + char const* + valueName + name of value to get +
286 * ret_type: const tOptionValue*
287 * ret_desc: a compound value structure
290 * This routine will find an entry in a nested value option or configurable.
291 * If "valueName" is NULL, then the first entry is returned. Otherwise,
292 * the first entry with a name that exactly matches the argument will be
293 * returned. If there is no matching value, NULL is returned and errno is
294 * set to ENOENT. If the provided option value is not a hierarchical value,
295 * NULL is also returned and errno is set to EINVAL.
298 * The returned result is NULL and errno is set:
301 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
302 * hierarchical option value.
304 * @code{ENOENT} - no entry matched the given name.
308 optionGetValue(tOptionValue
const * pOld
, char const * pzValName
)
311 tOptionValue
* pRes
= NULL
;
313 if ((pOld
== NULL
) || (pOld
->valType
!= OPARG_TYPE_HIERARCHY
)) {
317 pAL
= pOld
->v
.nestVal
;
319 if (pAL
->useCt
> 0) {
321 void ** papOV
= (void**)(pAL
->apzArgs
);
323 if (pzValName
== NULL
) {
324 pRes
= (tOptionValue
*)*papOV
;
327 tOptionValue
* pOV
= *(papOV
++);
328 if (strcmp(pOV
->pzName
, pzValName
) == 0) {
340 /*=export_func optionNextValue
342 * what: get the next value from a hierarchical list
343 * arg: + const tOptionValue* + pOptValue + a hierarchcal list value +
344 * arg: + const tOptionValue* + pOldValue + a value from this list +
346 * ret_type: const tOptionValue*
347 * ret_desc: a compound value structure
350 * This routine will return the next entry after the entry passed in. At the
351 * end of the list, NULL will be returned. If the entry is not found on the
352 * list, NULL will be returned and "@var{errno}" will be set to EINVAL.
353 * The "@var{pOldValue}" must have been gotten from a prior call to this
354 * routine or to "@code{opitonGetValue()}".
357 * The returned result is NULL and errno is set:
360 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
361 * hierarchical option value or @code{pOldValue} does not point to a
362 * member of that option value.
364 * @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry.
368 optionNextValue(tOptionValue
const * pOVList
,tOptionValue
const * pOldOV
)
371 tOptionValue
* pRes
= NULL
;
374 if ((pOVList
== NULL
) || (pOVList
->valType
!= OPARG_TYPE_HIERARCHY
)) {
378 pAL
= pOVList
->v
.nestVal
;
381 void** papNV
= (void**)(pAL
->apzArgs
);
384 tOptionValue
* pNV
= *(papNV
++);
391 pRes
= (tOptionValue
*)*papNV
;
404 * Load a file containing presetting information (a configuration file).
407 file_preset(tOptions
* opts
, char const * fname
, int dir
)
410 tOptState optst
= OPTSTATE_INITIALIZER(PRESET
);
411 unsigned long st_flags
= optst
.flags
;
413 text_mmap(fname
, PROT_READ
|PROT_WRITE
, MAP_PRIVATE
, &cfgfile
);
415 if (TEXT_MMAP_FAILED_ADDR(ftext
))
418 if (dir
== DIRECTION_CALLED
) {
419 st_flags
= OPTST_DEFINED
;
420 dir
= DIRECTION_PROCESS
;
424 * IF this is called via "optionProcess", then we are presetting.
425 * This is the default and the PRESETTING bit will be set.
426 * If this is called via "optionFileLoad", then the bit is not set
427 * and we consider stuff set herein to be "set" by the client program.
429 if ((opts
->fOptSet
& OPTPROC_PRESETTING
) == 0)
430 st_flags
= OPTST_SET
;
433 optst
.flags
= st_flags
;
434 ftext
= SPN_WHITESPACE_CHARS(ftext
);
436 if (IS_VAR_FIRST_CHAR(*ftext
)) {
437 ftext
= handle_cfg(opts
, &optst
, ftext
, dir
);
439 } else switch (*ftext
) {
441 if (IS_VAR_FIRST_CHAR(ftext
[1]))
442 ftext
= handle_struct(opts
, &optst
, ftext
, dir
);
444 else switch (ftext
[1]) {
446 ftext
= handle_directive(opts
, ftext
);
450 ftext
= handle_comment(ftext
);
454 ftext
= strchr(ftext
+ 2, '>');
464 ftext
= handle_section(opts
, ftext
);
468 ftext
= strchr(ftext
+ 1, NL
);
472 goto all_done
; /* invalid format */
474 } while (ftext
!= NULL
);
477 text_munmap(&cfgfile
);
482 * "pzText" points to a "<!" sequence.
483 * Theoretically, we should ensure that it begins with "<!--",
484 * but actually I don't care that much. It ends with "-->".
487 handle_comment(char* pzText
)
489 char* pz
= strstr(pzText
, "-->");
497 * "pzText" points to the start of some value name.
498 * The end of the entry is the end of the line that is not preceded by
499 * a backslash escape character. The string value is always processed
503 handle_cfg(tOptions
* pOpts
, tOptState
* pOS
, char * pzText
, int dir
)
505 char* pzName
= pzText
++;
506 char* pzEnd
= strchr(pzText
, NL
);
509 return pzText
+ strlen(pzText
);
511 pzText
= SPN_VALUE_NAME_CHARS(pzText
);
512 pzText
= SPN_WHITESPACE_CHARS(pzText
);
513 if (pzText
> pzEnd
) {
516 loadOptionLine(pOpts
, pOS
, pzName
, dir
, OPTION_LOAD_UNCOOKED
);
521 * Either the first character after the name is a ':' or '=',
522 * or else we must have skipped over white space. Anything else
523 * is an invalid format and we give up parsing the text.
525 if ((*pzText
== '=') || (*pzText
== ':')) {
526 pzText
= SPN_WHITESPACE_CHARS(pzText
+1);
529 } else if (! IS_WHITESPACE_CHAR(pzText
[-1]))
533 * IF the value is continued, remove the backslash escape and push "pzEnd"
534 * on to a newline *not* preceded by a backslash.
536 if (pzEnd
[-1] == '\\') {
563 * The newline was not preceded by a backslash. NUL it out
569 * "pzName" points to what looks like text for one option/configurable.
570 * It is NUL terminated. Process it.
572 loadOptionLine(pOpts
, pOS
, pzName
, dir
, OPTION_LOAD_UNCOOKED
);
579 * "pzText" points to a "<?" sequence.
580 * We handle "<?program" and "<?auto-options" directives.
581 * All others are treated as comments.
584 handle_directive(tOptions
* pOpts
, char * pzText
)
586 # define DIRECTIVE_TABLE \
587 _dt_(zCfgProg, program_directive) \
588 _dt_(zCfgAO_Flags, aoflags_directive)
590 typedef char * (directive_func_t
)(tOptions
*, char *);
591 # define _dt_(_s, _fn) _fn,
592 static directive_func_t
* dir_disp
[] = {
597 # define _dt_(_s, _fn) 1 +
598 static int const dir_ct
= DIRECTIVE_TABLE
0;
599 static char const * dir_names
[DIRECTIVE_TABLE
0];
604 if (dir_names
[0] == NULL
) {
606 # define _dt_(_s, _fn) dir_names[ix++] = _s;
611 for (ix
= 0; ix
< dir_ct
; ix
++) {
612 size_t len
= strlen(dir_names
[ix
]);
613 if ( (strncmp(pzText
+ 2, dir_names
[ix
], len
) == 0)
614 && (! IS_VALUE_NAME_CHAR(pzText
[len
+2])) )
615 return dir_disp
[ix
](pOpts
, pzText
+ len
+ 2);
619 * We don't know what this is. Skip it.
621 pzText
= strchr(pzText
+2, '>');
628 * handle AutoOpts mode flags
631 aoflags_directive(tOptions
* pOpts
, char * pzText
)
635 pz
= SPN_WHITESPACE_CHARS(pzText
+1);
636 pzText
= strchr(pz
, '>');
637 if (pzText
!= NULL
) {
639 size_t len
= pzText
- pz
;
640 char * ftxt
= AGALOC(len
+ 1, "aoflags");
642 memcpy(ftxt
, pz
, len
);
644 set_usage_flags(pOpts
, ftxt
);
654 * handle program segmentation of config file.
657 program_directive(tOptions
* pOpts
, char * pzText
)
659 static char const ttlfmt
[] = "<?";
660 size_t ttl_len
= sizeof(ttlfmt
) + strlen(zCfgProg
);
661 char * ttl
= AGALOC(ttl_len
, "prog title");
662 size_t name_len
= strlen(pOpts
->pzProgName
);
664 memcpy(ttl
, ttlfmt
, sizeof(ttlfmt
) - 1);
665 memcpy(ttl
+ sizeof(ttlfmt
) - 1, zCfgProg
, ttl_len
- (sizeof(ttlfmt
) - 1));
668 pzText
= SPN_WHITESPACE_CHARS(pzText
+1);
670 if ( (strneqvcmp(pzText
, pOpts
->pzProgName
, (int)name_len
) == 0)
671 && (IS_END_XML_TOKEN_CHAR(pzText
[name_len
])) ) {
676 pzText
= strstr(pzText
, ttl
);
677 } while (pzText
!= NULL
);
682 if (*pzText
== NUL
) {
686 if (*(pzText
++) == '>')
695 * "pzText" points to a '[' character.
696 * The "traditional" [PROG_NAME] segmentation of the config file.
697 * Do not ever mix with the "<?program prog-name>" variation.
700 handle_section(tOptions
* pOpts
, char * pzText
)
702 size_t len
= strlen(pOpts
->pzPROGNAME
);
703 if ( (strncmp(pzText
+1, pOpts
->pzPROGNAME
, len
) == 0)
704 && (pzText
[len
+1] == ']'))
705 return strchr(pzText
+ len
+ 2, NL
);
712 sprintf(z
, "[%s]", pOpts
->pzPROGNAME
);
713 pzText
= strstr(pzText
, z
);
717 pzText
= strchr(pzText
, NL
);
722 * parse XML encodings
725 parse_xml_encoding(char ** ppz
)
737 _xmlNm_(space, ' ') \
742 char const * const nm_str
;
743 unsigned short nm_len
;
745 } const xml_names
[] = {
746 # define _xmlNm_(_n, _v) { #_n ";", sizeof(#_n), _v },
752 static int const nm_ct
= sizeof(xml_names
) / sizeof(xml_names
[0]);
762 if (IS_DEC_DIGIT_CHAR(*pz
)) {
769 * Some forms specify hex with: &#xNN;
777 *  is hex and  is decimal. Cool.
785 v
= strtoul(pz
, &pz
, base
);
786 if ((*pz
!= ';') || (v
> 0x7F))
795 if (strncmp(pz
, xml_names
[ix
].nm_str
, xml_names
[ix
].nm_len
)
797 *ppz
= pz
+ xml_names
[ix
].nm_len
;
798 return xml_names
[ix
].nm_val
;
800 } while (++ix
< nm_ct
);
807 * Find the end marker for the named section of XML.
808 * Trim that text there, trimming trailing white space for all modes
809 * except for OPTION_LOAD_UNCOOKED.
812 trim_xml_text(char * intxt
, char const * pznm
, tOptionLoadMode mode
)
814 static char const fmt
[] = "</%s>";
815 size_t len
= strlen(pznm
) + sizeof(fmt
) - 2 /* for %s */;
820 if (len
>= sizeof(z
))
821 pz
= AGALOC(len
, "scan name");
823 len
= sprintf(pz
, fmt
, pznm
);
825 etext
= strstr(intxt
, pz
);
826 if (pz
!= z
) AGFREE(pz
);
833 char * result
= etext
+ len
;
835 if (mode
!= OPTION_LOAD_UNCOOKED
)
836 etext
= SPN_WHITESPACE_BACK(intxt
, etext
);
846 cook_xml_text(char * pzData
)
854 int ch
= ((int)*(pzs
++)) & 0xFF;
861 ch
= parse_xml_encoding(&pzs
);
870 if ((bf
[0] == NUL
) || (bf
[1] == NUL
)) {
875 ch
= strtoul(bf
, NULL
, 16);
885 * "pzText" points to a '<' character, followed by an alpha.
886 * The end of the entry is either the "/>" following the name, or else a
890 handle_struct(tOptions
* pOpts
, tOptState
* pOS
, char * pzText
, int dir
)
892 tOptionLoadMode mode
= option_load_mode
;
895 char* pzName
= ++pzText
;
899 pzText
= SPN_VALUE_NAME_CHARS(pzText
);
901 valu
.valType
= OPARG_TYPE_STRING
;
906 pzText
= parse_attrs(pOpts
, pzText
, &mode
, &valu
);
914 if (pzText
[1] != '>')
918 loadOptionLine(pOpts
, pOS
, pzName
, dir
, mode
);
925 pzText
= strchr(pzText
, '>');
932 * If we are here, we have a value. "pzText" points to a closing angle
933 * bracket. Separate the name from the value for a moment.
937 pzText
= trim_xml_text(pzText
, pzName
, mode
);
942 * Rejoin the name and value for parsing by "loadOptionLine()".
943 * Erase any attributes parsed by "parse_attrs()".
945 memset(pcNulPoint
, ' ', pzData
- pcNulPoint
);
948 * If we are getting a "string" value that is to be cooked,
949 * then process the XML-ish &xx; XML-ish and %XX hex characters.
951 if ( (valu
.valType
== OPARG_TYPE_STRING
)
952 && (mode
== OPTION_LOAD_COOKED
))
953 cook_xml_text(pzData
);
956 * "pzName" points to what looks like text for one option/configurable.
957 * It is NUL terminated. Process it.
959 loadOptionLine(pOpts
, pOS
, pzName
, dir
, mode
);
966 * Load a configuration file. This may be invoked either from
967 * scanning the "homerc" list, or from a specific file request.
968 * (see "optionFileLoad()", the implementation for --load-opts)
971 intern_file_load(tOptions
* pOpts
)
976 char zFileName
[ AG_PATH_MAX
+1 ];
978 if (pOpts
->papzHomeList
== NULL
)
981 svfl
= pOpts
->fOptSet
;
982 inc
= DIRECTION_PRESET
;
985 * Never stop on errors in config files.
987 pOpts
->fOptSet
&= ~OPTPROC_ERRSTOP
;
990 * Find the last RC entry (highest priority entry)
992 for (idx
= 0; pOpts
->papzHomeList
[ idx
+1 ] != NULL
; ++idx
) ;
995 * For every path in the home list, ... *TWICE* We start at the last
996 * (highest priority) entry, work our way down to the lowest priority,
997 * handling the immediate options.
998 * Then we go back up, doing the normal options.
1001 struct stat StatBuf
;
1005 * IF we've reached the bottom end, change direction
1008 inc
= DIRECTION_PROCESS
;
1012 pzPath
= pOpts
->papzHomeList
[ idx
];
1015 * IF we've reached the top end, bail out
1022 if (! optionMakePath(zFileName
, (int)sizeof(zFileName
),
1023 pzPath
, pOpts
->pzProgPath
))
1027 * IF the file name we constructed is a directory,
1028 * THEN append the Resource Configuration file name
1029 * ELSE we must have the complete file name
1031 if (stat(zFileName
, &StatBuf
) != 0)
1032 continue; /* bogus name - skip the home list entry */
1034 if (S_ISDIR(StatBuf
.st_mode
)) {
1035 size_t len
= strlen(zFileName
);
1036 size_t nln
= strlen(pOpts
->pzRcName
) + 1;
1037 char * pz
= zFileName
+ len
;
1039 if (len
+ 1 + nln
>= sizeof(zFileName
))
1042 if (pz
[-1] != DIRCH
)
1044 memcpy(pz
, pOpts
->pzRcName
, nln
);
1047 file_preset(pOpts
, zFileName
, inc
);
1050 * IF we are now to skip config files AND we are presetting,
1051 * THEN change direction. We must go the other way.
1054 tOptDesc
* pOD
= pOpts
->pOptDesc
+ pOpts
->specOptIdx
.save_opts
+1;
1055 if (DISABLED_OPT(pOD
) && PRESETTING(inc
)) {
1056 idx
-= inc
; /* go back and reprocess current file */
1057 inc
= DIRECTION_PROCESS
;
1060 } /* twice for every path in the home list, ... */
1062 pOpts
->fOptSet
= svfl
;
1066 /*=export_func optionFileLoad
1068 * what: Load the locatable config files, in order
1070 * arg: + tOptions* + pOpts + program options descriptor +
1071 * arg: + char const* + pzProg + program name +
1074 * ret_desc: 0 -> SUCCESS, -1 -> FAILURE
1078 * This function looks in all the specified directories for a configuration
1079 * file ("rc" file or "ini" file) and processes any found twice. The first
1080 * time through, they are processed in reverse order (last file first). At
1081 * that time, only "immediate action" configurables are processed. For
1082 * example, if the last named file specifies not processing any more
1083 * configuration files, then no more configuration files will be processed.
1084 * Such an option in the @strong{first} named directory will have no effect.
1086 * Once the immediate action configurables have been handled, then the
1087 * directories are handled in normal, forward order. In that way, later
1088 * config files can override the settings of earlier config files.
1090 * See the AutoOpts documentation for a thorough discussion of the
1091 * config file format.
1093 * Configuration files not found or not decipherable are simply ignored.
1095 * err: Returns the value, "-1" if the program options descriptor
1096 * is out of date or indecipherable. Otherwise, the value "0" will
1097 * always be returned.
1100 optionFileLoad(tOptions
* pOpts
, char const * pzProgram
)
1102 if (! SUCCESSFUL(validate_struct(pOpts
, pzProgram
)))
1107 (char const **)(void *)&(pOpts
->pzProgName
);
1111 intern_file_load(pOpts
);
1116 /*=export_func optionLoadOpt
1119 * what: Load an option rc/ini file
1120 * arg: + tOptions* + pOpts + program options descriptor +
1121 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
1124 * Processes the options found in the file named with
1125 * pOptDesc->optArg.argString.
1128 optionLoadOpt(tOptions
* pOpts
, tOptDesc
* pOptDesc
)
1133 * IF the option is not being disabled, THEN load the file. There must
1134 * be a file. (If it is being disabled, then the disablement processing
1135 * already took place. It must be done to suppress preloading of ini/rc
1138 if ( DISABLED_OPT(pOptDesc
)
1139 || ((pOptDesc
->fOptState
& OPTST_RESET
) != 0))
1142 if (stat(pOptDesc
->optArg
.argString
, &sb
) != 0) {
1143 if ((pOpts
->fOptSet
& OPTPROC_ERRSTOP
) == 0)
1146 fprintf(stderr
, zFSErrOptLoad
, errno
, strerror(errno
),
1147 pOptDesc
->optArg
.argString
);
1152 if (! S_ISREG(sb
.st_mode
)) {
1153 if ((pOpts
->fOptSet
& OPTPROC_ERRSTOP
) == 0)
1156 fprintf(stderr
, zNotFile
, pOptDesc
->optArg
.argString
);
1161 file_preset(pOpts
, pOptDesc
->optArg
.argString
, DIRECTION_CALLED
);
1166 * Parse the various attributes of an XML-styled config file entry
1169 parse_attrs(tOptions
* pOpts
, char * pzText
, tOptionLoadMode
* pMode
,
1170 tOptionValue
* pType
)
1175 if (! IS_WHITESPACE_CHAR(*pzText
))
1177 case '/': pType
->valType
= OPARG_TYPE_NONE
;
1178 case '>': return pzText
;
1181 case NUL
: return NULL
;
1184 pzText
= SPN_WHITESPACE_CHARS(pzText
+1);
1185 len
= SPN_LOWER_CASE_CHARS(pzText
) - pzText
;
1187 switch (find_xat_attribute_id(pzText
, len
)) {
1189 pzText
= parse_value(pzText
+len
, pType
);
1193 pzText
= parse_keyword(pOpts
, pzText
+len
, pType
);
1196 case XAT_KWD_MEMBERS
:
1197 pzText
= parse_set_mem(pOpts
, pzText
+len
, pType
);
1200 case XAT_KWD_COOKED
:
1202 if (! IS_END_XML_TOKEN_CHAR(*pzText
))
1205 *pMode
= OPTION_LOAD_COOKED
;
1208 case XAT_KWD_UNCOOKED
:
1210 if (! IS_END_XML_TOKEN_CHAR(*pzText
))
1213 *pMode
= OPTION_LOAD_UNCOOKED
;
1218 if (! IS_END_XML_TOKEN_CHAR(*pzText
))
1221 *pMode
= OPTION_LOAD_KEEP
;
1225 case XAT_KWD_INVALID
:
1227 pType
->valType
= OPARG_TYPE_NONE
;
1228 return skip_unkn(pzText
);
1230 } while (pzText
!= NULL
);
1237 * "pzText" points to the character after "words=".
1238 * What should follow is a name of a keyword (enumeration) list.
1241 parse_keyword(tOptions
* pOpts
, char * pzText
, tOptionValue
* pType
)
1246 return skip_unkn(pzText
);
1251 * "pzText" points to the character after "members="
1252 * What should follow is a name of a "set membership".
1253 * A collection of bit flags.
1256 parse_set_mem(tOptions
* pOpts
, char * pzText
, tOptionValue
* pType
)
1261 return skip_unkn(pzText
);
1266 * "pzText" points to the character after "type="
1269 parse_value(char * pzText
, tOptionValue
* pType
)
1273 if (*(pzText
++) != '=')
1276 len
= SPN_OPTION_NAME_CHARS(pzText
) - pzText
;
1278 if ((len
== 0) || (! IS_END_XML_TOKEN_CHAR(pzText
[len
]))) {
1280 pType
->valType
= OPARG_TYPE_NONE
;
1281 return skip_unkn(pzText
+ len
);
1284 switch (find_value_type_id(pzText
, len
)) {
1286 case VTP_KWD_INVALID
: goto woops
;
1288 case VTP_KWD_STRING
:
1289 pType
->valType
= OPARG_TYPE_STRING
;
1292 case VTP_KWD_INTEGER
:
1293 pType
->valType
= OPARG_TYPE_NUMERIC
;
1297 case VTP_KWD_BOOLEAN
:
1298 pType
->valType
= OPARG_TYPE_BOOLEAN
;
1301 case VTP_KWD_KEYWORD
:
1302 pType
->valType
= OPARG_TYPE_ENUMERATION
;
1306 case VTP_KWD_SET_MEMBERSHIP
:
1307 pType
->valType
= OPARG_TYPE_MEMBERSHIP
;
1310 case VTP_KWD_NESTED
:
1311 case VTP_KWD_HIERARCHY
:
1312 pType
->valType
= OPARG_TYPE_HIERARCHY
;
1315 return pzText
+ len
;
1320 * Skip over some unknown attribute
1323 skip_unkn(char* pzText
)
1326 if (IS_END_XML_TOKEN_CHAR(*pzText
)) return pzText
;
1327 if (*pzText
== NUL
) return NULL
;
1333 * Make sure the option descriptor is there and that we understand it.
1334 * This should be called from any user entry point where one needs to
1335 * worry about validity. (Some entry points are free to assume that
1336 * the call is not the first to the library and, thus, that this has
1337 * already been called.)
1339 * Upon successful completion, pzProgName and pzProgPath are set.
1341 * @param pOpts program options descriptor
1342 * @param pzProgram name of program, from argv[]
1343 * @returns SUCCESS or FAILURE
1346 validate_struct(tOptions
* pOpts
, char const * pzProgram
)
1348 if (pOpts
== NULL
) {
1349 fputs(zAO_Bad
, stderr
);
1354 * IF the client has enabled translation and the translation procedure
1355 * is available, then go do it.
1357 if ( ((pOpts
->fOptSet
& OPTPROC_TRANSLATE
) != 0)
1358 && (pOpts
->pTransProc
!= NULL
) ) {
1360 * If option names are not to be translated at all, then do not do
1361 * it for configuration parsing either. (That is the bit that really
1362 * gets tested anyway.)
1364 if ((pOpts
->fOptSet
& OPTPROC_NO_XLAT_MASK
) == OPTPROC_NXLAT_OPT
)
1365 pOpts
->fOptSet
|= OPTPROC_NXLAT_OPT_CFG
;
1366 (*pOpts
->pTransProc
)();
1367 pOpts
->fOptSet
&= ~OPTPROC_TRANSLATE
;
1371 * IF the struct version is not the current, and also
1372 * either too large (?!) or too small,
1373 * THEN emit error message and fail-exit
1375 if ( ( pOpts
->structVersion
!= OPTIONS_STRUCT_VERSION
)
1376 && ( (pOpts
->structVersion
> OPTIONS_STRUCT_VERSION
)
1377 || (pOpts
->structVersion
< OPTIONS_MINIMUM_VERSION
)
1379 static char const aover
[] =
1380 STR(AO_CURRENT
)":"STR(AO_REVISION
)":"STR(AO_AGE
)"\n";
1382 fprintf(stderr
, zAO_Err
, pzProgram
, NUM_TO_VER(pOpts
->structVersion
));
1383 if (pOpts
->structVersion
> OPTIONS_STRUCT_VERSION
)
1384 fputs(zAO_Big
, stderr
);
1386 fputs(zAO_Sml
, stderr
);
1388 fwrite(aover
, sizeof(aover
) - 1, 1, stderr
);
1393 * If the program name hasn't been set, then set the name and the path
1394 * and the set of equivalent characters.
1396 if (pOpts
->pzProgName
== NULL
) {
1397 char const * pz
= strrchr(pzProgram
, DIRCH
);
1399 (char const **)(void **)&(pOpts
->pzProgName
);
1405 pz
= pathfind(getenv("PATH"), (char *)pzProgram
, "rx");
1407 pzProgram
= (void *)pz
;
1410 pp
= (char const **)(void **)&(pOpts
->pzProgPath
);
1414 * when comparing long names, these are equivalent
1416 strequate(zSepChars
);
1426 * c-file-style: "stroustrup"
1427 * indent-tabs-mode: nil
1429 * end of autoopts/configfile.c */