Added hack to print the parameters correctly in windows.
[gnutls.git] / src / libopts / configfile.c
blob56e22b5db77bc431bdd82a5bd13e9aa46b980236
1 /**
2 * \file configfile.c
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 = = = */
30 static void
31 file_preset(tOptions * opts, char const * fname, int dir);
33 static char*
34 handle_comment(char* pzText);
36 static char *
37 handle_cfg(tOptions * pOpts, tOptState * pOS, char * pzText, int dir);
39 static char *
40 handle_directive(tOptions * pOpts, char * pzText);
42 static char *
43 aoflags_directive(tOptions * pOpts, char * pzText);
45 static char *
46 program_directive(tOptions * pOpts, char * pzText);
48 static char *
49 handle_section(tOptions * pOpts, char * pzText);
51 static int
52 parse_xml_encoding(char ** ppz);
54 static char *
55 trim_xml_text(char * intxt, char const * pznm, tOptionLoadMode mode);
57 static void
58 cook_xml_text(char * pzData);
60 static char *
61 handle_struct(tOptions * pOpts, tOptState * pOS, char * pzText, int dir);
63 static char*
64 parse_keyword(tOptions * pOpts, char * pzText, tOptionValue * pType);
66 static char*
67 parse_set_mem(tOptions * pOpts, char * pzText, tOptionValue * pType);
69 static char *
70 parse_value(char * pzText, tOptionValue * pType);
72 static char *
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
85 * doc:
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()}.
94 * err:
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:
98 * @itemize @bullet
99 * @item
100 * @code{ENOENT} - the file was not found.
101 * @item
102 * @code{ENOMSG} - the file was empty.
103 * @item
104 * @code{EINVAL} - the file contents are invalid -- not properly formed.
105 * @item
106 * @code{ENOMEM} - not enough memory to allocate the needed structures.
107 * @end itemize
109 const tOptionValue*
110 configFileLoad(char const* pzFile)
112 tmap_info_t cfgfile;
113 tOptionValue* pRes = NULL;
114 tOptionLoadMode save_mode = option_load_mode;
116 char* pzText =
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));
125 if (pRes == NULL) {
126 int err = errno;
127 text_munmap(&cfgfile);
128 errno = err;
129 } else
130 text_munmap(&cfgfile);
132 option_load_mode = save_mode;
133 return pRes;
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
147 * doc:
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.
151 * err:
152 * The returned result is NULL and errno is set:
153 * @itemize @bullet
154 * @item
155 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
156 * hierarchical option value.
157 * @item
158 * @code{ENOENT} - no entry matched the given name.
159 * @end itemize
161 const tOptionValue*
162 optionFindValue(const tOptDesc* pOptDesc, char const* pzName,
163 char const* pzVal)
165 const tOptionValue* pRes = NULL;
167 if ( (pOptDesc == NULL)
168 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) {
169 errno = EINVAL;
172 else if (pOptDesc->optCookie == NULL) {
173 errno = ENOENT;
176 else do {
177 tArgList* pAL = pOptDesc->optCookie;
178 int ct = pAL->useCt;
179 void** ppOV = (void**)(pAL->apzArgs);
181 if (ct == 0) {
182 errno = ENOENT;
183 break;
186 if (pzName == NULL) {
187 pRes = (tOptionValue*)*ppOV;
188 break;
191 while (--ct >= 0) {
192 const tOptionValue* pOV = *(ppOV++);
193 const tOptionValue* pRV = optionGetValue(pOV, pzName);
195 if (pRV == NULL)
196 continue;
198 if (pzVal == NULL) {
199 pRes = pOV;
200 break;
203 if (pRes == NULL)
204 errno = ENOENT;
205 } while (false);
207 return pRes;
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
224 * doc:
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.
229 * err:
230 * The returned result is NULL and errno is set:
231 * @itemize @bullet
232 * @item
233 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
234 * hierarchical option value.
235 * @item
236 * @code{ENOENT} - no entry matched the given name.
237 * @end itemize
239 tOptionValue const *
240 optionFindNextValue(const tOptDesc * pOptDesc, const tOptionValue * pPrevVal,
241 char const * pzName, char const * pzVal)
243 bool old_found = false;
244 tOptionValue* pRes = NULL;
246 (void)pzName;
247 (void)pzVal;
249 if ( (pOptDesc == NULL)
250 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) {
251 errno = EINVAL;
254 else if (pOptDesc->optCookie == NULL) {
255 errno = ENOENT;
258 else do {
259 tArgList* pAL = pOptDesc->optCookie;
260 int ct = pAL->useCt;
261 void** ppOV = (void**)pAL->apzArgs;
263 while (--ct >= 0) {
264 tOptionValue* pOV = *(ppOV++);
265 if (old_found) {
266 pRes = pOV;
267 break;
269 if (pOV == pPrevVal)
270 old_found = true;
272 if (pRes == NULL)
273 errno = ENOENT;
274 } while (false);
276 return pRes;
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
289 * doc:
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.
297 * err:
298 * The returned result is NULL and errno is set:
299 * @itemize @bullet
300 * @item
301 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
302 * hierarchical option value.
303 * @item
304 * @code{ENOENT} - no entry matched the given name.
305 * @end itemize
307 const tOptionValue*
308 optionGetValue(tOptionValue const * pOld, char const * pzValName)
310 tArgList * pAL;
311 tOptionValue * pRes = NULL;
313 if ((pOld == NULL) || (pOld->valType != OPARG_TYPE_HIERARCHY)) {
314 errno = EINVAL;
315 return pRes;
317 pAL = pOld->v.nestVal;
319 if (pAL->useCt > 0) {
320 int ct = pAL->useCt;
321 void ** papOV = (void**)(pAL->apzArgs);
323 if (pzValName == NULL) {
324 pRes = (tOptionValue*)*papOV;
326 } else do {
327 tOptionValue * pOV = *(papOV++);
328 if (strcmp(pOV->pzName, pzValName) == 0) {
329 pRes = pOV;
330 break;
332 } while (--ct > 0);
334 if (pRes == NULL)
335 errno = ENOENT;
336 return pRes;
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
349 * doc:
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()}".
356 * err:
357 * The returned result is NULL and errno is set:
358 * @itemize @bullet
359 * @item
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.
363 * @item
364 * @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry.
365 * @end itemize
367 tOptionValue const *
368 optionNextValue(tOptionValue const * pOVList,tOptionValue const * pOldOV )
370 tArgList* pAL;
371 tOptionValue* pRes = NULL;
372 int err = EINVAL;
374 if ((pOVList == NULL) || (pOVList->valType != OPARG_TYPE_HIERARCHY)) {
375 errno = EINVAL;
376 return NULL;
378 pAL = pOVList->v.nestVal;
380 int ct = pAL->useCt;
381 void** papNV = (void**)(pAL->apzArgs);
383 while (ct-- > 0) {
384 tOptionValue* pNV = *(papNV++);
385 if (pNV == pOldOV) {
386 if (ct == 0) {
387 err = ENOENT;
389 } else {
390 err = 0;
391 pRes = (tOptionValue*)*papNV;
393 break;
397 if (err != 0)
398 errno = err;
399 return pRes;
404 * Load a file containing presetting information (a configuration file).
406 static void
407 file_preset(tOptions * opts, char const * fname, int dir)
409 tmap_info_t cfgfile;
410 tOptState optst = OPTSTATE_INITIALIZER(PRESET);
411 unsigned long st_flags = optst.flags;
412 char * ftext =
413 text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile);
415 if (TEXT_MMAP_FAILED_ADDR(ftext))
416 return;
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;
432 do {
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) {
440 case '<':
441 if (IS_VAR_FIRST_CHAR(ftext[1]))
442 ftext = handle_struct(opts, &optst, ftext, dir);
444 else switch (ftext[1]) {
445 case '?':
446 ftext = handle_directive(opts, ftext);
447 break;
449 case '!':
450 ftext = handle_comment(ftext);
451 break;
453 case '/':
454 ftext = strchr(ftext + 2, '>');
455 if (ftext++ != NULL)
456 break;
458 default:
459 goto all_done;
461 break;
463 case '[':
464 ftext = handle_section(opts, ftext);
465 break;
467 case '#':
468 ftext = strchr(ftext + 1, NL);
469 break;
471 default:
472 goto all_done; /* invalid format */
474 } while (ftext != NULL);
476 all_done:
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 "-->".
486 static char*
487 handle_comment(char* pzText)
489 char* pz = strstr(pzText, "-->");
490 if (pz != NULL)
491 pz += 3;
492 return pz;
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
500 * in "cooked" mode.
502 static char *
503 handle_cfg(tOptions * pOpts, tOptState * pOS, char * pzText, int dir)
505 char* pzName = pzText++;
506 char* pzEnd = strchr(pzText, NL);
508 if (pzEnd == NULL)
509 return pzText + strlen(pzText);
511 pzText = SPN_VALUE_NAME_CHARS(pzText);
512 pzText = SPN_WHITESPACE_CHARS(pzText);
513 if (pzText > pzEnd) {
514 name_only:
515 *pzEnd++ = NUL;
516 loadOptionLine(pOpts, pOS, pzName, dir, OPTION_LOAD_UNCOOKED);
517 return pzEnd;
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);
527 if (pzText > pzEnd)
528 goto name_only;
529 } else if (! IS_WHITESPACE_CHAR(pzText[-1]))
530 return NULL;
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] == '\\') {
537 char* pcD = pzEnd-1;
538 char* pcS = pzEnd;
540 for (;;) {
541 char ch = *(pcS++);
542 switch (ch) {
543 case NUL:
544 pcS = NULL;
546 case NL:
547 *pcD = NUL;
548 pzEnd = pcS;
549 goto copy_done;
551 case '\\':
552 if (*pcS == NL) {
553 ch = *(pcS++);
555 /* FALLTHROUGH */
556 default:
557 *(pcD++) = ch;
559 } copy_done:;
561 } else {
563 * The newline was not preceded by a backslash. NUL it out
565 *(pzEnd++) = NUL;
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);
574 return pzEnd;
579 * "pzText" points to a "<?" sequence.
580 * We handle "<?program" and "<?auto-options" directives.
581 * All others are treated as comments.
583 static char *
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[] = {
593 DIRECTIVE_TABLE
595 # undef _dt_
597 # define _dt_(_s, _fn) 1 +
598 static int const dir_ct = DIRECTIVE_TABLE 0;
599 static char const * dir_names[DIRECTIVE_TABLE 0];
600 # undef _dt_
602 int ix;
604 if (dir_names[0] == NULL) {
605 ix = 0;
606 # define _dt_(_s, _fn) dir_names[ix++] = _s;
607 DIRECTIVE_TABLE;
608 # undef _dt_
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, '>');
622 if (pzText != NULL)
623 pzText++;
624 return pzText;
628 * handle AutoOpts mode flags
630 static char *
631 aoflags_directive(tOptions * pOpts, char * pzText)
633 char * pz;
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);
643 ftxt[len] = NUL;
644 set_usage_flags(pOpts, ftxt);
645 AGFREE(ftxt);
647 pzText++;
650 return pzText;
654 * handle program segmentation of config file.
656 static char *
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));
667 do {
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])) ) {
672 pzText += name_len;
673 break;
676 pzText = strstr(pzText, ttl);
677 } while (pzText != NULL);
679 AGFREE(ttl);
680 if (pzText != NULL)
681 for (;;) {
682 if (*pzText == NUL) {
683 pzText = NULL;
684 break;
686 if (*(pzText++) == '>')
687 break;
690 return 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.
699 static char *
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);
707 if (len > 16)
708 return NULL;
711 char z[24];
712 sprintf(z, "[%s]", pOpts->pzPROGNAME);
713 pzText = strstr(pzText, z);
716 if (pzText != NULL)
717 pzText = strchr(pzText, NL);
718 return pzText;
722 * parse XML encodings
724 static int
725 parse_xml_encoding(char ** ppz)
727 # define XMLTABLE \
728 _xmlNm_(amp, '&') \
729 _xmlNm_(lt, '<') \
730 _xmlNm_(gt, '>') \
731 _xmlNm_(ff, '\f') \
732 _xmlNm_(ht, '\t') \
733 _xmlNm_(cr, '\r') \
734 _xmlNm_(vt, '\v') \
735 _xmlNm_(bel, '\a') \
736 _xmlNm_(nl, NL) \
737 _xmlNm_(space, ' ') \
738 _xmlNm_(quot, '"') \
739 _xmlNm_(apos, '\'')
741 static struct {
742 char const * const nm_str;
743 unsigned short nm_len;
744 short nm_val;
745 } const xml_names[] = {
746 # define _xmlNm_(_n, _v) { #_n ";", sizeof(#_n), _v },
747 XMLTABLE
748 # undef _xmlNm_
749 # undef XMLTABLE
752 static int const nm_ct = sizeof(xml_names) / sizeof(xml_names[0]);
753 int base = 10;
755 char * pz = *ppz;
757 if (*pz == '#') {
758 pz++;
759 goto parse_number;
762 if (IS_DEC_DIGIT_CHAR(*pz)) {
763 unsigned long v;
765 parse_number:
766 switch (*pz) {
767 case 'x': case 'X':
769 * Some forms specify hex with: &#xNN;
771 base = 16;
772 pz++;
773 break;
775 case '0':
777 * &#0022; is hex and &#22; is decimal. Cool.
778 * Ya gotta love it.
780 if (pz[1] == '0')
781 base = 16;
782 break;
785 v = strtoul(pz, &pz, base);
786 if ((*pz != ';') || (v > 0x7F))
787 return NUL;
788 *ppz = pz + 1;
789 return (int)v;
793 int ix = 0;
794 do {
795 if (strncmp(pz, xml_names[ix].nm_str, xml_names[ix].nm_len)
796 == 0) {
797 *ppz = pz + xml_names[ix].nm_len;
798 return xml_names[ix].nm_val;
800 } while (++ix < nm_ct);
803 return NUL;
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.
811 static char *
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 */;
816 char * etext;
819 char z[64], *pz = z;
820 if (len >= sizeof(z))
821 pz = AGALOC(len, "scan name");
823 len = sprintf(pz, fmt, pznm);
824 *intxt = ' ';
825 etext = strstr(intxt, pz);
826 if (pz != z) AGFREE(pz);
829 if (etext == NULL)
830 return etext;
833 char * result = etext + len;
835 if (mode != OPTION_LOAD_UNCOOKED)
836 etext = SPN_WHITESPACE_BACK(intxt, etext);
838 *etext = NUL;
839 return result;
845 static void
846 cook_xml_text(char * pzData)
848 char * pzs = pzData;
849 char * pzd = pzData;
850 char bf[4];
851 bf[2] = NUL;
853 for (;;) {
854 int ch = ((int)*(pzs++)) & 0xFF;
855 switch (ch) {
856 case NUL:
857 *pzd = NUL;
858 return;
860 case '&':
861 ch = parse_xml_encoding(&pzs);
862 *(pzd++) = (char)ch;
863 if (ch == NUL)
864 return;
865 break;
867 case '%':
868 bf[0] = *(pzs++);
869 bf[1] = *(pzs++);
870 if ((bf[0] == NUL) || (bf[1] == NUL)) {
871 *pzd = NUL;
872 return;
875 ch = strtoul(bf, NULL, 16);
876 /* FALLTHROUGH */
878 default:
879 *(pzd++) = (char)ch;
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
887 * "</name>" string.
889 static char *
890 handle_struct(tOptions * pOpts, tOptState * pOS, char * pzText, int dir)
892 tOptionLoadMode mode = option_load_mode;
893 tOptionValue valu;
895 char* pzName = ++pzText;
896 char* pzData;
897 char* pcNulPoint;
899 pzText = SPN_VALUE_NAME_CHARS(pzText);
900 pcNulPoint = pzText;
901 valu.valType = OPARG_TYPE_STRING;
903 switch (*pzText) {
904 case ' ':
905 case '\t':
906 pzText = parse_attrs(pOpts, pzText, &mode, &valu);
907 if (*pzText == '>')
908 break;
909 if (*pzText != '/')
910 return NULL;
911 /* FALLTHROUGH */
913 case '/':
914 if (pzText[1] != '>')
915 return NULL;
916 *pzText = NUL;
917 pzText += 2;
918 loadOptionLine(pOpts, pOS, pzName, dir, mode);
919 return pzText;
921 case '>':
922 break;
924 default:
925 pzText = strchr(pzText, '>');
926 if (pzText != NULL)
927 pzText++;
928 return 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.
935 *pcNulPoint = NUL;
936 pzData = ++pzText;
937 pzText = trim_xml_text(pzText, pzName, mode);
938 if (pzText == NULL)
939 return pzText;
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);
961 return pzText;
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)
970 LOCAL void
971 intern_file_load(tOptions* pOpts)
973 uint32_t svfl;
974 int idx;
975 int inc;
976 char zFileName[ AG_PATH_MAX+1 ];
978 if (pOpts->papzHomeList == NULL)
979 return;
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.
1000 for (;;) {
1001 struct stat StatBuf;
1002 cch_t* pzPath;
1005 * IF we've reached the bottom end, change direction
1007 if (idx < 0) {
1008 inc = DIRECTION_PROCESS;
1009 idx = 0;
1012 pzPath = pOpts->papzHomeList[ idx ];
1015 * IF we've reached the top end, bail out
1017 if (pzPath == NULL)
1018 break;
1020 idx += inc;
1022 if (! optionMakePath(zFileName, (int)sizeof(zFileName),
1023 pzPath, pOpts->pzProgPath))
1024 continue;
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))
1040 continue;
1042 if (pz[-1] != DIRCH)
1043 *(pz++) = 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 +
1073 * ret_type: int
1074 * ret_desc: 0 -> SUCCESS, -1 -> FAILURE
1076 * doc:
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)))
1103 return -1;
1106 char const ** pp =
1107 (char const **)(void *)&(pOpts->pzProgName);
1108 *pp = pzProgram;
1111 intern_file_load(pOpts);
1112 return 0;
1116 /*=export_func optionLoadOpt
1117 * private:
1119 * what: Load an option rc/ini file
1120 * arg: + tOptions* + pOpts + program options descriptor +
1121 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
1123 * doc:
1124 * Processes the options found in the file named with
1125 * pOptDesc->optArg.argString.
1127 void
1128 optionLoadOpt(tOptions * pOpts, tOptDesc * pOptDesc)
1130 struct stat sb;
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
1136 * files.)
1138 if ( DISABLED_OPT(pOptDesc)
1139 || ((pOptDesc->fOptState & OPTST_RESET) != 0))
1140 return;
1142 if (stat(pOptDesc->optArg.argString, &sb) != 0) {
1143 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
1144 return;
1146 fprintf(stderr, zFSErrOptLoad, errno, strerror(errno),
1147 pOptDesc->optArg.argString);
1148 exit(EX_NOINPUT);
1149 /* NOT REACHED */
1152 if (! S_ISREG(sb.st_mode)) {
1153 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
1154 return;
1156 fprintf(stderr, zNotFile, pOptDesc->optArg.argString);
1157 exit(EX_NOINPUT);
1158 /* NOT REACHED */
1161 file_preset(pOpts, pOptDesc->optArg.argString, DIRECTION_CALLED);
1166 * Parse the various attributes of an XML-styled config file entry
1168 LOCAL char*
1169 parse_attrs(tOptions * pOpts, char * pzText, tOptionLoadMode * pMode,
1170 tOptionValue * pType)
1172 size_t len;
1174 do {
1175 if (! IS_WHITESPACE_CHAR(*pzText))
1176 switch (*pzText) {
1177 case '/': pType->valType = OPARG_TYPE_NONE;
1178 case '>': return pzText;
1180 default:
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)) {
1188 case XAT_KWD_TYPE:
1189 pzText = parse_value(pzText+len, pType);
1190 break;
1192 case XAT_KWD_WORDS:
1193 pzText = parse_keyword(pOpts, pzText+len, pType);
1194 break;
1196 case XAT_KWD_MEMBERS:
1197 pzText = parse_set_mem(pOpts, pzText+len, pType);
1198 break;
1200 case XAT_KWD_COOKED:
1201 pzText += len;
1202 if (! IS_END_XML_TOKEN_CHAR(*pzText))
1203 goto invalid_kwd;
1205 *pMode = OPTION_LOAD_COOKED;
1206 break;
1208 case XAT_KWD_UNCOOKED:
1209 pzText += len;
1210 if (! IS_END_XML_TOKEN_CHAR(*pzText))
1211 goto invalid_kwd;
1213 *pMode = OPTION_LOAD_UNCOOKED;
1214 break;
1216 case XAT_KWD_KEEP:
1217 pzText += len;
1218 if (! IS_END_XML_TOKEN_CHAR(*pzText))
1219 goto invalid_kwd;
1221 *pMode = OPTION_LOAD_KEEP;
1222 break;
1224 default:
1225 case XAT_KWD_INVALID:
1226 invalid_kwd:
1227 pType->valType = OPARG_TYPE_NONE;
1228 return skip_unkn(pzText);
1230 } while (pzText != NULL);
1232 return pzText;
1237 * "pzText" points to the character after "words=".
1238 * What should follow is a name of a keyword (enumeration) list.
1240 static char*
1241 parse_keyword(tOptions * pOpts, char * pzText, tOptionValue * pType)
1243 (void)pOpts;
1244 (void)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.
1255 static char*
1256 parse_set_mem(tOptions * pOpts, char * pzText, tOptionValue * pType)
1258 (void)pOpts;
1259 (void)pType;
1261 return skip_unkn(pzText);
1266 * "pzText" points to the character after "type="
1268 static char *
1269 parse_value(char * pzText, tOptionValue * pType)
1271 size_t len = 0;
1273 if (*(pzText++) != '=')
1274 goto woops;
1276 len = SPN_OPTION_NAME_CHARS(pzText) - pzText;
1278 if ((len == 0) || (! IS_END_XML_TOKEN_CHAR(pzText[len]))) {
1279 woops:
1280 pType->valType = OPARG_TYPE_NONE;
1281 return skip_unkn(pzText + len);
1284 switch (find_value_type_id(pzText, len)) {
1285 default:
1286 case VTP_KWD_INVALID: goto woops;
1288 case VTP_KWD_STRING:
1289 pType->valType = OPARG_TYPE_STRING;
1290 break;
1292 case VTP_KWD_INTEGER:
1293 pType->valType = OPARG_TYPE_NUMERIC;
1294 break;
1296 case VTP_KWD_BOOL:
1297 case VTP_KWD_BOOLEAN:
1298 pType->valType = OPARG_TYPE_BOOLEAN;
1299 break;
1301 case VTP_KWD_KEYWORD:
1302 pType->valType = OPARG_TYPE_ENUMERATION;
1303 break;
1305 case VTP_KWD_SET:
1306 case VTP_KWD_SET_MEMBERSHIP:
1307 pType->valType = OPARG_TYPE_MEMBERSHIP;
1308 break;
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
1322 static char *
1323 skip_unkn(char* pzText)
1325 for (;; 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
1345 LOCAL tSuccess
1346 validate_struct(tOptions * pOpts, char const * pzProgram)
1348 if (pOpts == NULL) {
1349 fputs(zAO_Bad, stderr);
1350 return FAILURE;
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 )
1378 ) ) {
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);
1385 else
1386 fputs(zAO_Sml, stderr);
1388 fwrite(aover, sizeof(aover) - 1, 1, stderr);
1389 return FAILURE;
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);
1398 char const ** pp =
1399 (char const **)(void **)&(pOpts->pzProgName);
1401 if (pz != NULL) {
1402 *pp = pz+1;
1403 } else {
1404 *pp = pzProgram;
1405 pz = pathfind(getenv("PATH"), (char *)pzProgram, "rx");
1406 if (pz != NULL)
1407 pzProgram = (void *)pz;
1410 pp = (char const **)(void **)&(pOpts->pzProgPath);
1411 *pp = pzProgram;
1414 * when comparing long names, these are equivalent
1416 strequate(zSepChars);
1419 return SUCCESS;
1424 * Local Variables:
1425 * mode: C
1426 * c-file-style: "stroustrup"
1427 * indent-tabs-mode: nil
1428 * End:
1429 * end of autoopts/configfile.c */