Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / ntp / dist / sntp / libopts / configfile.c
blob2e264a86d23f4cba3662e604e3a59361abec7362
1 /* $NetBSD$ */
3 /*
4 * Id: f1650b45a91ec95af830ff76041cc4f0048e60f0
5 * Time-stamp: "2009-01-18 10:21:58 bkorb"
7 * configuration/rc/ini file handling.
9 * This file is part of AutoOpts, a companion to AutoGen.
10 * AutoOpts is free software.
11 * AutoOpts is copyright (c) 1992-2009 by Bruce Korb - all rights reserved
13 * AutoOpts is available under any one of two licenses. The license
14 * in use must be one of these two and the choice is under the control
15 * of the user of the license.
17 * The GNU Lesser General Public License, version 3 or later
18 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
20 * The Modified Berkeley Software Distribution License
21 * See the file "COPYING.mbsd"
23 * These files have the following md5sums:
25 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
26 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
27 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
30 /* = = = START-STATIC-FORWARD = = = */
31 /* static forward declarations maintained by mk-fwd */
32 static void
33 filePreset(
34 tOptions* pOpts,
35 char const* pzFileName,
36 int direction );
38 static char*
39 handleComment( char* pzText );
41 static char*
42 handleConfig(
43 tOptions* pOpts,
44 tOptState* pOS,
45 char* pzText,
46 int direction );
48 static char*
49 handleDirective(
50 tOptions* pOpts,
51 char* pzText );
53 static char*
54 handleProgramSection(
55 tOptions* pOpts,
56 char* pzText );
58 static char*
59 handleStructure(
60 tOptions* pOpts,
61 tOptState* pOS,
62 char* pzText,
63 int direction );
65 static char*
66 parseKeyWordType(
67 tOptions* pOpts,
68 char* pzText,
69 tOptionValue* pType );
71 static char*
72 parseSetMemType(
73 tOptions* pOpts,
74 char* pzText,
75 tOptionValue* pType );
77 static char*
78 parseValueType(
79 char* pzText,
80 tOptionValue* pType );
82 static char*
83 skipUnknown( char* pzText );
84 /* = = = END-STATIC-FORWARD = = = */
87 /*=export_func configFileLoad
89 * what: parse a configuration file
90 * arg: + char const* + pzFile + the file to load +
92 * ret_type: const tOptionValue*
93 * ret_desc: An allocated, compound value structure
95 * doc:
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()}.
104 * err:
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:
108 * @itemize @bullet
109 * @item
110 * @code{ENOENT} - the file was empty.
111 * @item
112 * @code{EINVAL} - the file contents are invalid -- not properly formed.
113 * @item
114 * @code{ENOMEM} - not enough memory to allocate the needed structures.
115 * @end itemize
117 const tOptionValue*
118 configFileLoad( char const* pzFile )
120 tmap_info_t cfgfile;
121 tOptionValue* pRes = NULL;
122 tOptionLoadMode save_mode = option_load_mode;
124 char* pzText =
125 text_mmap( pzFile, PROT_READ, MAP_PRIVATE, &cfgfile );
127 if (TEXT_MMAP_FAILED_ADDR(pzText))
128 return NULL; /* errno is set */
130 option_load_mode = OPTION_LOAD_COOKED;
131 pRes = optionLoadNested(pzText, pzFile, strlen(pzFile));
133 if (pRes == NULL) {
134 int err = errno;
135 text_munmap( &cfgfile );
136 errno = err;
137 } else
138 text_munmap( &cfgfile );
140 option_load_mode = save_mode;
141 return pRes;
145 /*=export_func optionFindValue
147 * what: find a hierarcicaly valued option instance
148 * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type +
149 * arg: + char const* + name + name of value to find +
150 * arg: + char const* + value + the matching value +
152 * ret_type: const tOptionValue*
153 * ret_desc: a compound value structure
155 * doc:
156 * This routine will find an entry in a nested value option or configurable.
157 * It will search through the list and return a matching entry.
159 * err:
160 * The returned result is NULL and errno is set:
161 * @itemize @bullet
162 * @item
163 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
164 * hierarchical option value.
165 * @item
166 * @code{ENOENT} - no entry matched the given name.
167 * @end itemize
169 const tOptionValue*
170 optionFindValue( const tOptDesc* pOptDesc,
171 char const* pzName, char const* pzVal )
173 const tOptionValue* pRes = NULL;
175 if ( (pOptDesc == NULL)
176 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) {
177 errno = EINVAL;
180 else if (pOptDesc->optCookie == NULL) {
181 errno = ENOENT;
184 else do {
185 tArgList* pAL = pOptDesc->optCookie;
186 int ct = pAL->useCt;
187 void** ppOV = (void**)(pAL->apzArgs);
189 if (ct == 0) {
190 errno = ENOENT;
191 break;
194 if (pzName == NULL) {
195 pRes = (tOptionValue*)*ppOV;
196 break;
199 while (--ct >= 0) {
200 const tOptionValue* pOV = *(ppOV++);
201 const tOptionValue* pRV = optionGetValue( pOV, pzName );
203 if (pRV == NULL)
204 continue;
206 if (pzVal == NULL) {
207 pRes = pOV;
208 break;
211 if (pRes == NULL)
212 errno = ENOENT;
213 } while (0);
215 return pRes;
219 /*=export_func optionFindNextValue
221 * what: find a hierarcicaly valued option instance
222 * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type +
223 * arg: + const tOptionValue* + pPrevVal + the last entry +
224 * arg: + char const* + name + name of value to find +
225 * arg: + char const* + value + the matching value +
227 * ret_type: const tOptionValue*
228 * ret_desc: a compound value structure
230 * doc:
231 * This routine will find the next entry in a nested value option or
232 * configurable. It will search through the list and return the next entry
233 * that matches the criteria.
235 * err:
236 * The returned result is NULL and errno is set:
237 * @itemize @bullet
238 * @item
239 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
240 * hierarchical option value.
241 * @item
242 * @code{ENOENT} - no entry matched the given name.
243 * @end itemize
245 const tOptionValue*
246 optionFindNextValue( const tOptDesc* pOptDesc, const tOptionValue* pPrevVal,
247 char const* pzName, char const* pzVal )
249 int foundOldVal = 0;
250 tOptionValue* pRes = NULL;
252 if ( (pOptDesc == NULL)
253 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) {
254 errno = EINVAL;
257 else if (pOptDesc->optCookie == NULL) {
258 errno = ENOENT;
261 else do {
262 tArgList* pAL = pOptDesc->optCookie;
263 int ct = pAL->useCt;
264 void** ppOV = (void**)pAL->apzArgs;
266 if (ct == 0) {
267 errno = ENOENT;
268 break;
271 while (--ct >= 0) {
272 tOptionValue* pOV = *(ppOV++);
273 if (foundOldVal) {
274 pRes = pOV;
275 break;
277 if (pOV == pPrevVal)
278 foundOldVal = 1;
280 if (pRes == NULL)
281 errno = ENOENT;
282 } while (0);
284 return pRes;
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
297 * doc:
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.
303 * err:
304 * The returned result is NULL and errno is set:
305 * @itemize @bullet
306 * @item
307 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
308 * hierarchical option value.
309 * @item
310 * @code{ENOENT} - no entry matched the given name.
311 * @end itemize
313 const tOptionValue*
314 optionGetValue( const tOptionValue* pOld, char const* pzValName )
316 tArgList* pAL;
317 tOptionValue* pRes = NULL;
319 if ((pOld == NULL) || (pOld->valType != OPARG_TYPE_HIERARCHY)) {
320 errno = EINVAL;
321 return NULL;
323 pAL = pOld->v.nestVal;
325 if (pAL->useCt > 0) {
326 int ct = pAL->useCt;
327 void** papOV = (void**)(pAL->apzArgs);
329 if (pzValName == NULL) {
330 pRes = (tOptionValue*)*papOV;
333 else do {
334 tOptionValue* pOV = *(papOV++);
335 if (strcmp( pOV->pzName, pzValName ) == 0) {
336 pRes = pOV;
337 break;
339 } while (--ct > 0);
341 if (pRes == NULL)
342 errno = ENOENT;
343 return pRes;
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
356 * doc:
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()}".
363 * err:
364 * The returned result is NULL and errno is set:
365 * @itemize @bullet
366 * @item
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.
370 * @item
371 * @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry.
372 * @end itemize
374 tOptionValue const *
375 optionNextValue(tOptionValue const * pOVList,tOptionValue const * pOldOV )
377 tArgList* pAL;
378 tOptionValue* pRes = NULL;
379 int err = EINVAL;
381 if ((pOVList == NULL) || (pOVList->valType != OPARG_TYPE_HIERARCHY)) {
382 errno = EINVAL;
383 return NULL;
385 pAL = pOVList->v.nestVal;
387 int ct = pAL->useCt;
388 void** papNV = (void**)(pAL->apzArgs);
390 while (ct-- > 0) {
391 tOptionValue* pNV = *(papNV++);
392 if (pNV == pOldOV) {
393 if (ct == 0) {
394 err = ENOENT;
396 } else {
397 err = 0;
398 pRes = (tOptionValue*)*papNV;
400 break;
404 if (err != 0)
405 errno = err;
406 return pRes;
410 /* filePreset
412 * Load a file containing presetting information (a configuration file).
414 static void
415 filePreset(
416 tOptions* pOpts,
417 char const* pzFileName,
418 int direction )
420 tmap_info_t cfgfile;
421 tOptState optst = OPTSTATE_INITIALIZER(PRESET);
422 char* pzFileText =
423 text_mmap( pzFileName, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile );
425 if (TEXT_MMAP_FAILED_ADDR(pzFileText))
426 return;
428 if (direction == DIRECTION_CALLED) {
429 optst.flags = OPTST_DEFINED;
430 direction = DIRECTION_PROCESS;
434 * IF this is called via "optionProcess", then we are presetting.
435 * This is the default and the PRESETTING bit will be set.
436 * If this is called via "optionFileLoad", then the bit is not set
437 * and we consider stuff set herein to be "set" by the client program.
439 if ((pOpts->fOptSet & OPTPROC_PRESETTING) == 0)
440 optst.flags = OPTST_SET;
442 do {
443 while (IS_WHITESPACE_CHAR(*pzFileText)) pzFileText++;
445 if (IS_VAR_FIRST_CHAR(*pzFileText)) {
446 pzFileText = handleConfig(pOpts, &optst, pzFileText, direction);
448 } else switch (*pzFileText) {
449 case '<':
450 if (IS_VAR_FIRST_CHAR(pzFileText[1]))
451 pzFileText =
452 handleStructure(pOpts, &optst, pzFileText, direction);
454 else switch (pzFileText[1]) {
455 case '?':
456 pzFileText = handleDirective( pOpts, pzFileText );
457 break;
459 case '!':
460 pzFileText = handleComment( pzFileText );
461 break;
463 case '/':
464 pzFileText = strchr( pzFileText+2, '>' );
465 if (pzFileText++ != NULL)
466 break;
468 default:
469 goto all_done;
471 break;
473 case '[':
474 pzFileText = handleProgramSection( pOpts, pzFileText );
475 break;
477 case '#':
478 pzFileText = strchr( pzFileText+1, '\n' );
479 break;
481 default:
482 goto all_done; /* invalid format */
484 } while (pzFileText != NULL);
486 all_done:
487 text_munmap( &cfgfile );
491 /* handleComment
493 * "pzText" points to a "<!" sequence.
494 * Theoretically, we should ensure that it begins with "<!--",
495 * but actually I don't care that much. It ends with "-->".
497 static char*
498 handleComment( char* pzText )
500 char* pz = strstr( pzText, "-->" );
501 if (pz != NULL)
502 pz += 3;
503 return pz;
507 /* handleConfig
509 * "pzText" points to the start of some value name.
510 * The end of the entry is the end of the line that is not preceded by
511 * a backslash escape character. The string value is always processed
512 * in "cooked" mode.
514 static char*
515 handleConfig(
516 tOptions* pOpts,
517 tOptState* pOS,
518 char* pzText,
519 int direction )
521 char* pzName = pzText++;
522 char* pzEnd = strchr( pzText, '\n' );
524 if (pzEnd == NULL)
525 return pzText + strlen(pzText);
527 while (IS_VALUE_NAME_CHAR(*pzText)) pzText++;
528 while (IS_WHITESPACE_CHAR(*pzText)) pzText++;
529 if (pzText > pzEnd) {
530 name_only:
531 *pzEnd++ = NUL;
532 loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED );
533 return pzEnd;
537 * Either the first character after the name is a ':' or '=',
538 * or else we must have skipped over white space. Anything else
539 * is an invalid format and we give up parsing the text.
541 if ((*pzText == '=') || (*pzText == ':')) {
542 while (IS_WHITESPACE_CHAR(*++pzText)) ;
543 if (pzText > pzEnd)
544 goto name_only;
545 } else if (! IS_WHITESPACE_CHAR(pzText[-1]))
546 return NULL;
549 * IF the value is continued, remove the backslash escape and push "pzEnd"
550 * on to a newline *not* preceded by a backslash.
552 if (pzEnd[-1] == '\\') {
553 char* pcD = pzEnd-1;
554 char* pcS = pzEnd;
556 for (;;) {
557 char ch = *(pcS++);
558 switch (ch) {
559 case NUL:
560 pcS = NULL;
562 case '\n':
563 *pcD = NUL;
564 pzEnd = pcS;
565 goto copy_done;
567 case '\\':
568 if (*pcS == '\n') {
569 ch = *(pcS++);
571 /* FALLTHROUGH */
572 default:
573 *(pcD++) = ch;
575 } copy_done:;
577 } else {
579 * The newline was not preceded by a backslash. NUL it out
581 *(pzEnd++) = NUL;
585 * "pzName" points to what looks like text for one option/configurable.
586 * It is NUL terminated. Process it.
588 loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED );
590 return pzEnd;
594 /* handleDirective
596 * "pzText" points to a "<?" sequence.
597 * For the moment, we only handle "<?program" directives.
599 static char*
600 handleDirective(
601 tOptions* pOpts,
602 char* pzText )
604 char ztitle[32] = "<?";
605 size_t title_len = strlen( zProg );
606 size_t name_len;
608 if ( (strncmp( pzText+2, zProg, title_len ) != 0)
609 || (! IS_WHITESPACE_CHAR(pzText[title_len+2])) ) {
610 pzText = strchr( pzText+2, '>' );
611 if (pzText != NULL)
612 pzText++;
613 return pzText;
616 name_len = strlen( pOpts->pzProgName );
617 strcpy( ztitle+2, zProg );
618 title_len += 2;
620 do {
621 pzText += title_len;
623 if (IS_WHITESPACE_CHAR(*pzText)) {
624 while (IS_WHITESPACE_CHAR(*++pzText)) ;
625 if ( (strneqvcmp( pzText, pOpts->pzProgName, (int)name_len) == 0)
626 && (pzText[name_len] == '>')) {
627 pzText += name_len + 1;
628 break;
632 pzText = strstr( pzText, ztitle );
633 } while (pzText != NULL);
635 return pzText;
639 /* handleProgramSection
641 * "pzText" points to a '[' character.
642 * The "traditional" [PROG_NAME] segmentation of the config file.
643 * Do not ever mix with the "<?program prog-name>" variation.
645 static char*
646 handleProgramSection(
647 tOptions* pOpts,
648 char* pzText )
650 size_t len = strlen( pOpts->pzPROGNAME );
651 if ( (strncmp( pzText+1, pOpts->pzPROGNAME, len ) == 0)
652 && (pzText[len+1] == ']'))
653 return strchr( pzText + len + 2, '\n' );
655 if (len > 16)
656 return NULL;
659 char z[24];
660 sprintf( z, "[%s]", pOpts->pzPROGNAME );
661 pzText = strstr( pzText, z );
664 if (pzText != NULL)
665 pzText = strchr( pzText, '\n' );
666 return pzText;
670 /* handleStructure
672 * "pzText" points to a '<' character, followed by an alpha.
673 * The end of the entry is either the "/>" following the name, or else a
674 * "</name>" string.
676 static char*
677 handleStructure(
678 tOptions* pOpts,
679 tOptState* pOS,
680 char* pzText,
681 int direction )
683 tOptionLoadMode mode = option_load_mode;
684 tOptionValue valu;
686 char* pzName = ++pzText;
687 char* pzData;
688 char* pcNulPoint;
690 while (IS_VALUE_NAME_CHAR(*pzText)) pzText++;
691 pcNulPoint = pzText;
692 valu.valType = OPARG_TYPE_STRING;
694 switch (*pzText) {
695 case ' ':
696 case '\t':
697 pzText = parseAttributes( pOpts, pzText, &mode, &valu );
698 if (*pzText == '>')
699 break;
700 if (*pzText != '/')
701 return NULL;
702 /* FALLTHROUGH */
704 case '/':
705 if (pzText[1] != '>')
706 return NULL;
707 *pzText = NUL;
708 pzText += 2;
709 loadOptionLine( pOpts, pOS, pzName, direction, mode );
710 return pzText;
712 case '>':
713 break;
715 default:
716 pzText = strchr( pzText, '>');
717 if (pzText != NULL)
718 pzText++;
719 return pzText;
723 * If we are here, we have a value. "pzText" points to a closing angle
724 * bracket. Separate the name from the value for a moment.
726 *pcNulPoint = NUL;
727 pzData = ++pzText;
730 * Find the end of the option text and NUL terminate it
733 char z[64], *pz = z;
734 size_t len = strlen(pzName) + 4;
735 if (len > sizeof(z))
736 pz = AGALOC(len, "scan name");
738 sprintf( pz, "</%s>", pzName );
739 *pzText = ' ';
740 pzText = strstr( pzText, pz );
741 if (pz != z) AGFREE(pz);
743 if (pzText == NULL)
744 return pzText;
746 *pzText = NUL;
748 pzText += len-1;
752 * Rejoin the name and value for parsing by "loadOptionLine()".
753 * Erase any attributes parsed by "parseAttributes()".
755 memset(pcNulPoint, ' ', pzData - pcNulPoint);
758 * If we are getting a "string" value, the process the XML-ish
759 * %XX hex characters.
761 if (valu.valType == OPARG_TYPE_STRING) {
762 char * pzSrc = pzData;
763 char * pzDst = pzData;
764 char bf[4];
765 bf[2] = NUL;
767 for (;;) {
768 int ch = ((int)*(pzSrc++)) & 0xFF;
769 switch (ch) {
770 case NUL: goto string_fixup_done;
772 case '%':
773 bf[0] = *(pzSrc++);
774 bf[1] = *(pzSrc++);
775 if ((bf[0] == NUL) || (bf[1] == NUL))
776 goto string_fixup_done;
777 ch = strtoul(bf, NULL, 16);
778 /* FALLTHROUGH */
780 default:
781 *(pzDst++) = ch;
783 } string_fixup_done:;
784 *pzDst = NUL;
788 * "pzName" points to what looks like text for one option/configurable.
789 * It is NUL terminated. Process it.
791 loadOptionLine( pOpts, pOS, pzName, direction, mode );
793 return pzText;
797 /* internalFileLoad
799 * Load a configuration file. This may be invoked either from
800 * scanning the "homerc" list, or from a specific file request.
801 * (see "optionFileLoad()", the implementation for --load-opts)
803 LOCAL void
804 internalFileLoad( tOptions* pOpts )
806 uint32_t svfl;
807 int idx;
808 int inc;
809 char zFileName[ AG_PATH_MAX+1 ];
811 if (pOpts->papzHomeList == NULL)
812 return;
814 svfl = pOpts->fOptSet;
815 inc = DIRECTION_PRESET;
818 * Never stop on errors in config files.
820 pOpts->fOptSet &= ~OPTPROC_ERRSTOP;
823 * Find the last RC entry (highest priority entry)
825 for (idx = 0; pOpts->papzHomeList[ idx+1 ] != NULL; ++idx) ;
828 * For every path in the home list, ... *TWICE* We start at the last
829 * (highest priority) entry, work our way down to the lowest priority,
830 * handling the immediate options.
831 * Then we go back up, doing the normal options.
833 for (;;) {
834 struct stat StatBuf;
835 cch_t* pzPath;
838 * IF we've reached the bottom end, change direction
840 if (idx < 0) {
841 inc = DIRECTION_PROCESS;
842 idx = 0;
845 pzPath = pOpts->papzHomeList[ idx ];
848 * IF we've reached the top end, bail out
850 if (pzPath == NULL)
851 break;
853 idx += inc;
855 if (! optionMakePath( zFileName, (int)sizeof(zFileName),
856 pzPath, pOpts->pzProgPath ))
857 continue;
860 * IF the file name we constructed is a directory,
861 * THEN append the Resource Configuration file name
862 * ELSE we must have the complete file name
864 if (stat( zFileName, &StatBuf ) != 0)
865 continue; /* bogus name - skip the home list entry */
867 if (S_ISDIR( StatBuf.st_mode )) {
868 size_t len = strlen( zFileName );
869 char* pz;
871 if (len + 1 + strlen( pOpts->pzRcName ) >= sizeof( zFileName ))
872 continue;
874 pz = zFileName + len;
875 if (pz[-1] != DIRCH)
876 *(pz++) = DIRCH;
877 strcpy( pz, pOpts->pzRcName );
880 filePreset( pOpts, zFileName, inc );
883 * IF we are now to skip config files AND we are presetting,
884 * THEN change direction. We must go the other way.
887 tOptDesc * pOD = pOpts->pOptDesc + pOpts->specOptIdx.save_opts+1;
888 if (DISABLED_OPT(pOD) && PRESETTING(inc)) {
889 idx -= inc; /* go back and reprocess current file */
890 inc = DIRECTION_PROCESS;
893 } /* twice for every path in the home list, ... */
895 pOpts->fOptSet = svfl;
899 /*=export_func optionFileLoad
901 * what: Load the locatable config files, in order
903 * arg: + tOptions* + pOpts + program options descriptor +
904 * arg: + char const* + pzProg + program name +
906 * ret_type: int
907 * ret_desc: 0 -> SUCCESS, -1 -> FAILURE
909 * doc:
911 * This function looks in all the specified directories for a configuration
912 * file ("rc" file or "ini" file) and processes any found twice. The first
913 * time through, they are processed in reverse order (last file first). At
914 * that time, only "immediate action" configurables are processed. For
915 * example, if the last named file specifies not processing any more
916 * configuration files, then no more configuration files will be processed.
917 * Such an option in the @strong{first} named directory will have no effect.
919 * Once the immediate action configurables have been handled, then the
920 * directories are handled in normal, forward order. In that way, later
921 * config files can override the settings of earlier config files.
923 * See the AutoOpts documentation for a thorough discussion of the
924 * config file format.
926 * Configuration files not found or not decipherable are simply ignored.
928 * err: Returns the value, "-1" if the program options descriptor
929 * is out of date or indecipherable. Otherwise, the value "0" will
930 * always be returned.
933 optionFileLoad( tOptions* pOpts, char const* pzProgram )
935 if (! SUCCESSFUL( validateOptionsStruct( pOpts, pzProgram )))
936 return -1;
938 pOpts->pzProgName = pzProgram;
939 internalFileLoad( pOpts );
940 return 0;
944 /*=export_func optionLoadOpt
945 * private:
947 * what: Load an option rc/ini file
948 * arg: + tOptions* + pOpts + program options descriptor +
949 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
951 * doc:
952 * Processes the options found in the file named with
953 * pOptDesc->optArg.argString.
955 void
956 optionLoadOpt( tOptions* pOpts, tOptDesc* pOptDesc )
958 struct stat sb;
961 * IF the option is not being disabled, THEN load the file. There must
962 * be a file. (If it is being disabled, then the disablement processing
963 * already took place. It must be done to suppress preloading of ini/rc
964 * files.)
966 if ( DISABLED_OPT(pOptDesc)
967 || ((pOptDesc->fOptState & OPTST_RESET) != 0))
968 return;
970 if (stat( pOptDesc->optArg.argString, &sb ) != 0) {
971 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
972 return;
974 fprintf( stderr, zFSErrOptLoad, errno, strerror( errno ),
975 pOptDesc->optArg.argString );
976 exit(EX_NOINPUT);
977 /* NOT REACHED */
980 if (! S_ISREG( sb.st_mode )) {
981 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
982 return;
984 fprintf( stderr, zNotFile, pOptDesc->optArg.argString );
985 exit(EX_NOINPUT);
986 /* NOT REACHED */
989 filePreset(pOpts, pOptDesc->optArg.argString, DIRECTION_CALLED);
993 /* parseAttributes
995 * Parse the various attributes of an XML-styled config file entry
997 LOCAL char*
998 parseAttributes(
999 tOptions* pOpts,
1000 char* pzText,
1001 tOptionLoadMode* pMode,
1002 tOptionValue* pType )
1004 size_t len;
1006 do {
1007 if (! IS_WHITESPACE_CHAR(*pzText))
1008 switch (*pzText) {
1009 case '/': pType->valType = OPARG_TYPE_NONE;
1010 case '>': return pzText;
1012 default:
1013 case NUL: return NULL;
1016 while (IS_WHITESPACE_CHAR(*++pzText)) ;
1017 len = 0;
1018 while (IS_LOWER_CASE_CHAR(pzText[len])) len++;
1020 switch (find_xat_attribute_id(pzText, len)) {
1021 case XAT_KWD_TYPE:
1022 pzText = parseValueType( pzText+len, pType );
1023 break;
1025 case XAT_KWD_WORDS:
1026 pzText = parseKeyWordType( pOpts, pzText+len, pType );
1027 break;
1029 case XAT_KWD_MEMBERS:
1030 pzText = parseSetMemType( pOpts, pzText+len, pType );
1031 break;
1033 case XAT_KWD_COOKED:
1034 pzText += len;
1035 if (! IS_END_XML_TOKEN_CHAR(*pzText))
1036 goto invalid_kwd;
1038 *pMode = OPTION_LOAD_COOKED;
1039 break;
1041 case XAT_KWD_UNCOOKED:
1042 pzText += len;
1043 if (! IS_END_XML_TOKEN_CHAR(*pzText))
1044 goto invalid_kwd;
1046 *pMode = OPTION_LOAD_UNCOOKED;
1047 break;
1049 case XAT_KWD_KEEP:
1050 pzText += len;
1051 if (! IS_END_XML_TOKEN_CHAR(*pzText))
1052 goto invalid_kwd;
1054 *pMode = OPTION_LOAD_KEEP;
1055 break;
1057 default:
1058 case XAT_KWD_INVALID:
1059 invalid_kwd:
1060 pType->valType = OPARG_TYPE_NONE;
1061 return skipUnknown( pzText );
1063 } while (pzText != NULL);
1065 return pzText;
1069 /* parseKeyWordType
1071 * "pzText" points to the character after "words=".
1072 * What should follow is a name of a keyword (enumeration) list.
1074 static char*
1075 parseKeyWordType(
1076 tOptions* pOpts,
1077 char* pzText,
1078 tOptionValue* pType )
1080 return skipUnknown( pzText );
1084 /* parseSetMemType
1086 * "pzText" points to the character after "members="
1087 * What should follow is a name of a "set membership".
1088 * A collection of bit flags.
1090 static char*
1091 parseSetMemType(
1092 tOptions* pOpts,
1093 char* pzText,
1094 tOptionValue* pType )
1096 return skipUnknown( pzText );
1100 /* parseValueType
1102 * "pzText" points to the character after "type="
1104 static char*
1105 parseValueType(
1106 char* pzText,
1107 tOptionValue* pType )
1109 size_t len = 0;
1111 if (*(pzText++) != '=')
1112 goto woops;
1114 while (IS_OPTION_NAME_CHAR(pzText[len])) len++;
1115 pzText += len;
1117 if ((len == 0) || (! IS_END_XML_TOKEN_CHAR(*pzText))) {
1118 woops:
1119 pType->valType = OPARG_TYPE_NONE;
1120 return skipUnknown( pzText );
1123 switch (find_value_type_id(pzText - len, len)) {
1124 default:
1125 case VTP_KWD_INVALID: goto woops;
1127 case VTP_KWD_STRING:
1128 pType->valType = OPARG_TYPE_STRING;
1129 break;
1131 case VTP_KWD_INTEGER:
1132 pType->valType = OPARG_TYPE_NUMERIC;
1133 break;
1135 case VTP_KWD_BOOL:
1136 case VTP_KWD_BOOLEAN:
1137 pType->valType = OPARG_TYPE_BOOLEAN;
1138 break;
1140 case VTP_KWD_KEYWORD:
1141 pType->valType = OPARG_TYPE_ENUMERATION;
1142 break;
1144 case VTP_KWD_SET:
1145 case VTP_KWD_SET_MEMBERSHIP:
1146 pType->valType = OPARG_TYPE_MEMBERSHIP;
1147 break;
1149 case VTP_KWD_NESTED:
1150 case VTP_KWD_HIERARCHY:
1151 pType->valType = OPARG_TYPE_HIERARCHY;
1154 return pzText;
1158 /* skipUnknown
1160 * Skip over some unknown attribute
1162 static char*
1163 skipUnknown( char* pzText )
1165 for (;; pzText++) {
1166 if (IS_END_XML_TOKEN_CHAR(*pzText)) return pzText;
1167 if (*pzText == NUL) return NULL;
1172 /* validateOptionsStruct
1174 * Make sure the option descriptor is there and that we understand it.
1175 * This should be called from any user entry point where one needs to
1176 * worry about validity. (Some entry points are free to assume that
1177 * the call is not the first to the library and, thus, that this has
1178 * already been called.)
1180 LOCAL tSuccess
1181 validateOptionsStruct( tOptions* pOpts, char const* pzProgram )
1183 if (pOpts == NULL) {
1184 fputs( zAO_Bad, stderr );
1185 exit( EX_CONFIG );
1189 * IF the client has enabled translation and the translation procedure
1190 * is available, then go do it.
1192 if ( ((pOpts->fOptSet & OPTPROC_TRANSLATE) != 0)
1193 && (pOpts->pTransProc != NULL) ) {
1195 * If option names are not to be translated at all, then do not do
1196 * it for configuration parsing either. (That is the bit that really
1197 * gets tested anyway.)
1199 if ((pOpts->fOptSet & OPTPROC_NO_XLAT_MASK) == OPTPROC_NXLAT_OPT)
1200 pOpts->fOptSet |= OPTPROC_NXLAT_OPT_CFG;
1201 (*pOpts->pTransProc)();
1202 pOpts->fOptSet &= ~OPTPROC_TRANSLATE;
1206 * IF the struct version is not the current, and also
1207 * either too large (?!) or too small,
1208 * THEN emit error message and fail-exit
1210 if ( ( pOpts->structVersion != OPTIONS_STRUCT_VERSION )
1211 && ( (pOpts->structVersion > OPTIONS_STRUCT_VERSION )
1212 || (pOpts->structVersion < OPTIONS_MINIMUM_VERSION )
1213 ) ) {
1215 fprintf(stderr, zAO_Err, pzProgram, NUM_TO_VER(pOpts->structVersion));
1216 if (pOpts->structVersion > OPTIONS_STRUCT_VERSION )
1217 fputs( zAO_Big, stderr );
1218 else
1219 fputs( zAO_Sml, stderr );
1221 return FAILURE;
1225 * If the program name hasn't been set, then set the name and the path
1226 * and the set of equivalent characters.
1228 if (pOpts->pzProgName == NULL) {
1229 char const* pz = strrchr( pzProgram, DIRCH );
1231 if (pz == NULL)
1232 pOpts->pzProgName = pzProgram;
1233 else pOpts->pzProgName = pz+1;
1235 pOpts->pzProgPath = pzProgram;
1238 * when comparing long names, these are equivalent
1240 strequate( zSepChars );
1243 return SUCCESS;
1248 * Local Variables:
1249 * mode: C
1250 * c-file-style: "stroustrup"
1251 * indent-tabs-mode: nil
1252 * End:
1253 * end of autoopts/configfile.c */