Obfuscate RCS ID matching so that CVS doesn't expand it.
[netbsd-mini2440.git] / dist / ntp / libopts / configfile.c
blobf829d5d60c6318638bed5259567442a70b78a4e2
1 /* $NetBSD: configfile.c,v 1.1.1.2 2007/06/24 15:49:23 kardel Exp $ */
3 /*
4 * Id: configfile.c,v 1.21 2007/04/15 19:01:18 bkorb Exp
5 * Time-stamp: "2007-04-15 11:22:46 bkorb"
7 * configuration/rc/ini file handling.
8 */
11 * Automated Options copyright 1992-2007 Bruce Korb
13 * Automated Options is free software.
14 * You may redistribute it and/or modify it under the terms of the
15 * GNU General Public License, as published by the Free Software
16 * Foundation; either version 2, or (at your option) any later version.
18 * Automated Options is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with Automated Options. See the file "COPYING". If not,
25 * write to: The Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor,
27 * Boston, MA 02110-1301, USA.
29 * As a special exception, Bruce Korb gives permission for additional
30 * uses of the text contained in his release of AutoOpts.
32 * The exception is that, if you link the AutoOpts library with other
33 * files to produce an executable, this does not by itself cause the
34 * resulting executable to be covered by the GNU General Public License.
35 * Your use of that executable is in no way restricted on account of
36 * linking the AutoOpts library code into it.
38 * This exception does not however invalidate any other reasons why
39 * the executable file might be covered by the GNU General Public License.
41 * This exception applies only to the code released by Bruce Korb under
42 * the name AutoOpts. If you copy code from other sources under the
43 * General Public License into a copy of AutoOpts, as the General Public
44 * License permits, the exception does not apply to the code that you add
45 * in this way. To avoid misleading anyone as to the status of such
46 * modified files, you must delete this exception notice from them.
48 * If you write modifications of your own for AutoOpts, it is your choice
49 * whether to permit this exception to apply to your modifications.
50 * If you do not wish that, delete this exception notice.
53 /* = = = START-STATIC-FORWARD = = = */
54 /* static forward declarations maintained by :mkfwd */
55 static void
56 filePreset(
57 tOptions* pOpts,
58 char const* pzFileName,
59 int direction );
61 static char*
62 handleComment( char* pzText );
64 static char*
65 handleConfig(
66 tOptions* pOpts,
67 tOptState* pOS,
68 char* pzText,
69 int direction );
71 static char*
72 handleDirective(
73 tOptions* pOpts,
74 char* pzText );
76 static char*
77 handleProgramSection(
78 tOptions* pOpts,
79 char* pzText );
81 static char*
82 handleStructure(
83 tOptions* pOpts,
84 tOptState* pOS,
85 char* pzText,
86 int direction );
88 static char*
89 parseKeyWordType(
90 tOptions* pOpts,
91 char* pzText,
92 tOptionValue* pType );
94 static char*
95 parseLoadMode(
96 char* pzText,
97 tOptionLoadMode* pMode );
99 static char*
100 parseSetMemType(
101 tOptions* pOpts,
102 char* pzText,
103 tOptionValue* pType );
105 static char*
106 parseValueType(
107 char* pzText,
108 tOptionValue* pType );
110 static char*
111 skipUnknown( char* pzText );
112 /* = = = END-STATIC-FORWARD = = = */
115 /*=export_func configFileLoad
117 * what: parse a configuration file
118 * arg: + char const* + pzFile + the file to load +
120 * ret_type: const tOptionValue*
121 * ret_desc: An allocated, compound value structure
123 * doc:
124 * This routine will load a named configuration file and parse the
125 * text as a hierarchically valued option. The option descriptor
126 * created from an option definition file is not used via this interface.
127 * The returned value is "named" with the input file name and is of
128 * type "@code{OPARG_TYPE_HIERARCHY}". It may be used in calls to
129 * @code{optionGetValue()}, @code{optionNextValue()} and
130 * @code{optionUnloadNested()}.
132 * err:
133 * If the file cannot be loaded or processed, @code{NULL} is returned and
134 * @var{errno} is set. It may be set by a call to either @code{open(2)}
135 * @code{mmap(2)} or other file system calls, or it may be:
136 * @itemize @bullet
137 * @item
138 * @code{ENOENT} - the file was empty.
139 * @item
140 * @code{EINVAL} - the file contents are invalid -- not properly formed.
141 * @item
142 * @code{ENOMEM} - not enough memory to allocate the needed structures.
143 * @end itemize
145 const tOptionValue*
146 configFileLoad( char const* pzFile )
148 tmap_info_t cfgfile;
149 tOptionValue* pRes = NULL;
150 tOptionLoadMode save_mode = option_load_mode;
152 char* pzText =
153 text_mmap( pzFile, PROT_READ, MAP_PRIVATE, &cfgfile );
155 if (TEXT_MMAP_FAILED_ADDR(pzText))
156 return NULL; /* errno is set */
158 option_load_mode = OPTION_LOAD_COOKED;
159 pRes = optionLoadNested(pzText, pzFile, strlen(pzFile));
161 if (pRes == NULL) {
162 int err = errno;
163 text_munmap( &cfgfile );
164 errno = err;
165 } else
166 text_munmap( &cfgfile );
168 option_load_mode = save_mode;
169 return pRes;
173 /*=export_func optionFindValue
175 * what: find a hierarcicaly valued option instance
176 * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type +
177 * arg: + char const* + name + name of value to find +
178 * arg: + char const* + value + the matching value +
180 * ret_type: const tOptionValue*
181 * ret_desc: a compound value structure
183 * doc:
184 * This routine will find an entry in a nested value option or configurable.
185 * It will search through the list and return a matching entry.
187 * err:
188 * The returned result is NULL and errno is set:
189 * @itemize @bullet
190 * @item
191 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
192 * hierarchical option value.
193 * @item
194 * @code{ENOENT} - no entry matched the given name.
195 * @end itemize
197 const tOptionValue*
198 optionFindValue( const tOptDesc* pOptDesc,
199 char const* pzName, char const* pzVal )
201 const tOptionValue* pRes = NULL;
203 if ( (pOptDesc == NULL)
204 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) {
205 errno = EINVAL;
208 else if (pOptDesc->optCookie == NULL) {
209 errno = ENOENT;
212 else do {
213 tArgList* pAL = pOptDesc->optCookie;
214 int ct = pAL->useCt;
215 void** ppOV = (void**)(pAL->apzArgs);
217 if (ct == 0) {
218 errno = ENOENT;
219 break;
222 if (pzName == NULL) {
223 pRes = (tOptionValue*)*ppOV;
224 break;
227 while (--ct >= 0) {
228 const tOptionValue* pOV = *(ppOV++);
229 const tOptionValue* pRV = optionGetValue( pOV, pzName );
231 if (pRV == NULL)
232 continue;
234 if (pzVal == NULL) {
235 pRes = pOV;
236 break;
239 if (pRes == NULL)
240 errno = ENOENT;
241 } while (0);
243 return pRes;
247 /*=export_func optionFindNextValue
249 * what: find a hierarcicaly valued option instance
250 * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type +
251 * arg: + const tOptionValue* + pPrevVal + the last entry +
252 * arg: + char const* + name + name of value to find +
253 * arg: + char const* + value + the matching value +
255 * ret_type: const tOptionValue*
256 * ret_desc: a compound value structure
258 * doc:
259 * This routine will find the next entry in a nested value option or
260 * configurable. It will search through the list and return the next entry
261 * that matches the criteria.
263 * err:
264 * The returned result is NULL and errno is set:
265 * @itemize @bullet
266 * @item
267 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
268 * hierarchical option value.
269 * @item
270 * @code{ENOENT} - no entry matched the given name.
271 * @end itemize
273 const tOptionValue*
274 optionFindNextValue( const tOptDesc* pOptDesc, const tOptionValue* pPrevVal,
275 char const* pzName, char const* pzVal )
277 int foundOldVal = 0;
278 tOptionValue* pRes = NULL;
280 if ( (pOptDesc == NULL)
281 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) {
282 errno = EINVAL;
285 else if (pOptDesc->optCookie == NULL) {
286 errno = ENOENT;
289 else do {
290 tArgList* pAL = pOptDesc->optCookie;
291 int ct = pAL->useCt;
292 void** ppOV = (void**)pAL->apzArgs;
294 if (ct == 0) {
295 errno = ENOENT;
296 break;
299 while (--ct >= 0) {
300 tOptionValue* pOV = *(ppOV++);
301 if (foundOldVal) {
302 pRes = pOV;
303 break;
305 if (pOV == pPrevVal)
306 foundOldVal = 1;
308 if (pRes == NULL)
309 errno = ENOENT;
310 } while (0);
312 return pRes;
316 /*=export_func optionGetValue
318 * what: get a specific value from a hierarcical list
319 * arg: + const tOptionValue* + pOptValue + a hierarchcal value +
320 * arg: + char const* + valueName + name of value to get +
322 * ret_type: const tOptionValue*
323 * ret_desc: a compound value structure
325 * doc:
326 * This routine will find an entry in a nested value option or configurable.
327 * If "valueName" is NULL, then the first entry is returned. Otherwise,
328 * the first entry with a name that exactly matches the argument will be
329 * returned.
331 * err:
332 * The returned result is NULL and errno is set:
333 * @itemize @bullet
334 * @item
335 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
336 * hierarchical option value.
337 * @item
338 * @code{ENOENT} - no entry matched the given name.
339 * @end itemize
341 const tOptionValue*
342 optionGetValue( const tOptionValue* pOld, char const* pzValName )
344 tArgList* pAL;
345 tOptionValue* pRes = NULL;
347 if ((pOld == NULL) || (pOld->valType != OPARG_TYPE_HIERARCHY)) {
348 errno = EINVAL;
349 return NULL;
351 pAL = pOld->v.nestVal;
353 if (pAL->useCt > 0) {
354 int ct = pAL->useCt;
355 void** papOV = (void**)(pAL->apzArgs);
357 if (pzValName == NULL) {
358 pRes = (tOptionValue*)*papOV;
361 else do {
362 tOptionValue* pOV = *(papOV++);
363 if (strcmp( pOV->pzName, pzValName ) == 0) {
364 pRes = pOV;
365 break;
367 } while (--ct > 0);
369 if (pRes == NULL)
370 errno = ENOENT;
371 return pRes;
375 /*=export_func optionNextValue
377 * what: get the next value from a hierarchical list
378 * arg: + const tOptionValue* + pOptValue + a hierarchcal list value +
379 * arg: + const tOptionValue* + pOldValue + a value from this list +
381 * ret_type: const tOptionValue*
382 * ret_desc: a compound value structure
384 * doc:
385 * This routine will return the next entry after the entry passed in. At the
386 * end of the list, NULL will be returned. If the entry is not found on the
387 * list, NULL will be returned and "@var{errno}" will be set to EINVAL.
388 * The "@var{pOldValue}" must have been gotten from a prior call to this
389 * routine or to "@code{opitonGetValue()}".
391 * err:
392 * The returned result is NULL and errno is set:
393 * @itemize @bullet
394 * @item
395 * @code{EINVAL} - the @code{pOptValue} does not point to a valid
396 * hierarchical option value or @code{pOldValue} does not point to a
397 * member of that option value.
398 * @item
399 * @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry.
400 * @end itemize
402 tOptionValue const *
403 optionNextValue(tOptionValue const * pOVList,tOptionValue const * pOldOV )
405 tArgList* pAL;
406 tOptionValue* pRes = NULL;
407 int err = EINVAL;
409 if ((pOVList == NULL) || (pOVList->valType != OPARG_TYPE_HIERARCHY)) {
410 errno = EINVAL;
411 return NULL;
413 pAL = pOVList->v.nestVal;
415 int ct = pAL->useCt;
416 void** papNV = (void**)(pAL->apzArgs);
418 while (ct-- > 0) {
419 tOptionValue* pNV = *(papNV++);
420 if (pNV == pOldOV) {
421 if (ct == 0) {
422 err = ENOENT;
424 } else {
425 err = 0;
426 pRes = (tOptionValue*)*papNV;
428 break;
432 if (err != 0)
433 errno = err;
434 return pRes;
438 /* filePreset
440 * Load a file containing presetting information (a configuration file).
442 static void
443 filePreset(
444 tOptions* pOpts,
445 char const* pzFileName,
446 int direction )
448 tmap_info_t cfgfile;
449 tOptState st = OPTSTATE_INITIALIZER(PRESET);
450 char* pzFileText =
451 text_mmap( pzFileName, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile );
453 if (TEXT_MMAP_FAILED_ADDR(pzFileText))
454 return;
456 if (direction == DIRECTION_CALLED) {
457 st.flags = OPTST_DEFINED;
458 direction = DIRECTION_PROCESS;
462 * IF this is called via "optionProcess", then we are presetting.
463 * This is the default and the PRESETTING bit will be set.
464 * If this is called via "optionFileLoad", then the bit is not set
465 * and we consider stuff set herein to be "set" by the client program.
467 if ((pOpts->fOptSet & OPTPROC_PRESETTING) == 0)
468 st.flags = OPTST_SET;
470 do {
471 while (isspace( (int)*pzFileText )) pzFileText++;
473 if (isalpha( (int)*pzFileText )) {
474 pzFileText = handleConfig( pOpts, &st, pzFileText, direction );
476 } else switch (*pzFileText) {
477 case '<':
478 if (isalpha( (int)pzFileText[1] ))
479 pzFileText = handleStructure(pOpts, &st, pzFileText, direction);
481 else switch (pzFileText[1]) {
482 case '?':
483 pzFileText = handleDirective( pOpts, pzFileText );
484 break;
486 case '!':
487 pzFileText = handleComment( pzFileText );
488 break;
490 case '/':
491 pzFileText = strchr( pzFileText+2, '>' );
492 if (pzFileText++ != NULL)
493 break;
495 default:
496 goto all_done;
498 break;
500 case '[':
501 pzFileText = handleProgramSection( pOpts, pzFileText );
502 break;
504 case '#':
505 pzFileText = strchr( pzFileText+1, '\n' );
506 break;
508 default:
509 goto all_done; /* invalid format */
511 } while (pzFileText != NULL);
513 all_done:
514 text_munmap( &cfgfile );
518 /* handleComment
520 * "pzText" points to a "<!" sequence.
521 * Theoretically, we should ensure that it begins with "<!--",
522 * but actually I don't care that much. It ends with "-->".
524 static char*
525 handleComment( char* pzText )
527 char* pz = strstr( pzText, "-->" );
528 if (pz != NULL)
529 pz += 3;
530 return pz;
534 /* handleConfig
536 * "pzText" points to the start of some value name.
537 * The end of the entry is the end of the line that is not preceded by
538 * a backslash escape character. The string value is always processed
539 * in "cooked" mode.
541 static char*
542 handleConfig(
543 tOptions* pOpts,
544 tOptState* pOS,
545 char* pzText,
546 int direction )
548 char* pzName = pzText++;
549 char* pzEnd = strchr( pzText, '\n' );
551 if (pzEnd == NULL)
552 return pzText + strlen(pzText);
554 while (ISNAMECHAR( (int)*pzText )) pzText++;
555 while (isspace( (int)*pzText )) pzText++;
556 if (pzText > pzEnd) {
557 name_only:
558 *pzEnd++ = NUL;
559 loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED );
560 return pzEnd;
564 * Either the first character after the name is a ':' or '=',
565 * or else we must have skipped over white space. Anything else
566 * is an invalid format and we give up parsing the text.
568 if ((*pzText == '=') || (*pzText == ':')) {
569 while (isspace( (int)*++pzText )) ;
570 if (pzText > pzEnd)
571 goto name_only;
572 } else if (! isspace((int)pzText[-1]))
573 return NULL;
576 * IF the value is continued, remove the backslash escape and push "pzEnd"
577 * on to a newline *not* preceded by a backslash.
579 if (pzEnd[-1] == '\\') {
580 char* pcD = pzEnd-1;
581 char* pcS = pzEnd;
583 for (;;) {
584 char ch = *(pcS++);
585 switch (ch) {
586 case NUL:
587 pcS = NULL;
589 case '\n':
590 *pcD = NUL;
591 pzEnd = pcS;
592 goto copy_done;
594 case '\\':
595 if (*pcS == '\n') {
596 ch = *(pcS++);
598 /* FALLTHROUGH */
599 default:
600 *(pcD++) = ch;
602 } copy_done:;
604 } else {
606 * The newline was not preceded by a backslash. NUL it out
608 *(pzEnd++) = NUL;
612 * "pzName" points to what looks like text for one option/configurable.
613 * It is NUL terminated. Process it.
615 loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED );
617 return pzEnd;
621 /* handleDirective
623 * "pzText" points to a "<?" sequence.
624 * For the moment, we only handle "<?program" directives.
626 static char*
627 handleDirective(
628 tOptions* pOpts,
629 char* pzText )
631 char ztitle[32] = "<?";
632 size_t title_len = strlen( zProg );
633 size_t name_len;
635 if ( (strncmp( pzText+2, zProg, title_len ) != 0)
636 || (! isspace( (int)pzText[title_len+2] )) ) {
637 pzText = strchr( pzText+2, '>' );
638 if (pzText != NULL)
639 pzText++;
640 return pzText;
643 name_len = strlen( pOpts->pzProgName );
644 strcpy( ztitle+2, zProg );
645 title_len += 2;
647 do {
648 pzText += title_len;
650 if (isspace((int)*pzText)) {
651 while (isspace((int)*pzText)) pzText++;
652 if ( (strneqvcmp( pzText, pOpts->pzProgName, (int)name_len) == 0)
653 && (pzText[name_len] == '>')) {
654 pzText += name_len + 1;
655 break;
659 pzText = strstr( pzText, ztitle );
660 } while (pzText != NULL);
662 return pzText;
666 /* handleProgramSection
668 * "pzText" points to a '[' character.
669 * The "traditional" [PROG_NAME] segmentation of the config file.
670 * Do not ever mix with the "<?program prog-name>" variation.
672 static char*
673 handleProgramSection(
674 tOptions* pOpts,
675 char* pzText )
677 size_t len = strlen( pOpts->pzPROGNAME );
678 if ( (strncmp( pzText+1, pOpts->pzPROGNAME, len ) == 0)
679 && (pzText[len+1] == ']'))
680 return strchr( pzText + len + 2, '\n' );
682 if (len > 16)
683 return NULL;
686 char z[24];
687 sprintf( z, "[%s]", pOpts->pzPROGNAME );
688 pzText = strstr( pzText, z );
691 if (pzText != NULL)
692 pzText = strchr( pzText, '\n' );
693 return pzText;
697 /* handleStructure
699 * "pzText" points to a '<' character, followed by an alpha.
700 * The end of the entry is either the "/>" following the name, or else a
701 * "</name>" string.
703 static char*
704 handleStructure(
705 tOptions* pOpts,
706 tOptState* pOS,
707 char* pzText,
708 int direction )
710 tOptionLoadMode mode = option_load_mode;
711 tOptionValue valu;
713 char* pzName = ++pzText;
714 char* pzData;
715 char* pcNulPoint;
717 while (ISNAMECHAR( (int)*pzText )) pzText++;
718 pcNulPoint = pzText;
719 valu.valType = OPARG_TYPE_STRING;
721 switch (*pzText) {
722 case ' ':
723 case '\t':
724 pzText = parseAttributes( pOpts, pzText, &mode, &valu );
725 if (*pzText == '>')
726 break;
727 if (*pzText != '/')
728 return NULL;
729 /* FALLTHROUGH */
731 case '/':
732 if (pzText[1] != '>')
733 return NULL;
734 *pzText = NUL;
735 pzText += 2;
736 loadOptionLine( pOpts, pOS, pzName, direction, mode );
737 return pzText;
739 case '>':
740 break;
742 default:
743 pzText = strchr( pzText, '>');
744 if (pzText != NULL)
745 pzText++;
746 return pzText;
750 * If we are here, we have a value. "pzText" points to a closing angle
751 * bracket. Separate the name from the value for a moment.
753 *pcNulPoint = NUL;
754 pzData = ++pzText;
757 * Find the end of the option text and NUL terminate it
760 char z[64], *pz = z;
761 size_t len = strlen(pzName) + 4;
762 if (len > sizeof(z))
763 pz = AGALOC(len, "scan name");
765 sprintf( pz, "</%s>", pzName );
766 *pzText = ' ';
767 pzText = strstr( pzText, pz );
768 if (pz != z) AGFREE(pz);
770 if (pzText == NULL)
771 return pzText;
773 *pzText = NUL;
775 pzText += len-1;
779 * Rejoin the name and value for parsing by "loadOptionLine()".
780 * Erase any attributes parsed by "parseAttributes()".
782 memset(pcNulPoint, ' ', pzData - pcNulPoint);
785 * "pzName" points to what looks like text for one option/configurable.
786 * It is NUL terminated. Process it.
788 loadOptionLine( pOpts, pOS, pzName, direction, mode );
790 return pzText;
794 /* internalFileLoad
796 * Load a configuration file. This may be invoked either from
797 * scanning the "homerc" list, or from a specific file request.
798 * (see "optionFileLoad()", the implementation for --load-opts)
800 LOCAL void
801 internalFileLoad( tOptions* pOpts )
803 int idx;
804 int inc = DIRECTION_PRESET;
805 char zFileName[ AG_PATH_MAX+1 ];
807 if (pOpts->papzHomeList == NULL)
808 return;
811 * Find the last RC entry (highest priority entry)
813 for (idx = 0; pOpts->papzHomeList[ idx+1 ] != NULL; ++idx) ;
816 * For every path in the home list, ... *TWICE* We start at the last
817 * (highest priority) entry, work our way down to the lowest priority,
818 * handling the immediate options.
819 * Then we go back up, doing the normal options.
821 for (;;) {
822 struct stat StatBuf;
823 cch_t* pzPath;
826 * IF we've reached the bottom end, change direction
828 if (idx < 0) {
829 inc = DIRECTION_PROCESS;
830 idx = 0;
833 pzPath = pOpts->papzHomeList[ idx ];
836 * IF we've reached the top end, bail out
838 if (pzPath == NULL)
839 break;
841 idx += inc;
843 if (! optionMakePath( zFileName, (int)sizeof(zFileName),
844 pzPath, pOpts->pzProgPath ))
845 continue;
848 * IF the file name we constructed is a directory,
849 * THEN append the Resource Configuration file name
850 * ELSE we must have the complete file name
852 if (stat( zFileName, &StatBuf ) != 0)
853 continue; /* bogus name - skip the home list entry */
855 if (S_ISDIR( StatBuf.st_mode )) {
856 size_t len = strlen( zFileName );
857 char* pz;
859 if (len + 1 + strlen( pOpts->pzRcName ) >= sizeof( zFileName ))
860 continue;
862 pz = zFileName + len;
863 if (pz[-1] != DIRCH)
864 *(pz++) = DIRCH;
865 strcpy( pz, pOpts->pzRcName );
868 filePreset( pOpts, zFileName, inc );
871 * IF we are now to skip config files AND we are presetting,
872 * THEN change direction. We must go the other way.
875 tOptDesc * pOD = pOpts->pOptDesc + pOpts->specOptIdx.save_opts+1;
876 if (DISABLED_OPT(pOD) && PRESETTING(inc)) {
877 idx -= inc; /* go back and reprocess current file */
878 inc = DIRECTION_PROCESS;
881 } /* twice for every path in the home list, ... */
885 /*=export_func optionFileLoad
887 * what: Load the locatable config files, in order
889 * arg: + tOptions* + pOpts + program options descriptor +
890 * arg: + char const* + pzProg + program name +
892 * ret_type: int
893 * ret_desc: 0 -> SUCCESS, -1 -> FAILURE
895 * doc:
897 * This function looks in all the specified directories for a configuration
898 * file ("rc" file or "ini" file) and processes any found twice. The first
899 * time through, they are processed in reverse order (last file first). At
900 * that time, only "immediate action" configurables are processed. For
901 * example, if the last named file specifies not processing any more
902 * configuration files, then no more configuration files will be processed.
903 * Such an option in the @strong{first} named directory will have no effect.
905 * Once the immediate action configurables have been handled, then the
906 * directories are handled in normal, forward order. In that way, later
907 * config files can override the settings of earlier config files.
909 * See the AutoOpts documentation for a thorough discussion of the
910 * config file format.
912 * Configuration files not found or not decipherable are simply ignored.
914 * err: Returns the value, "-1" if the program options descriptor
915 * is out of date or indecipherable. Otherwise, the value "0" will
916 * always be returned.
919 optionFileLoad( tOptions* pOpts, char const* pzProgram )
921 if (! SUCCESSFUL( validateOptionsStruct( pOpts, pzProgram )))
922 return -1;
924 pOpts->pzProgName = pzProgram;
925 internalFileLoad( pOpts );
926 return 0;
930 /*=export_func optionLoadOpt
931 * private:
933 * what: Load an option rc/ini file
934 * arg: + tOptions* + pOpts + program options descriptor +
935 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
937 * doc:
938 * Processes the options found in the file named with
939 * pOptDesc->optArg.argString.
941 void
942 optionLoadOpt( tOptions* pOpts, tOptDesc* pOptDesc )
945 * IF the option is not being disabled, THEN load the file. There must
946 * be a file. (If it is being disabled, then the disablement processing
947 * already took place. It must be done to suppress preloading of ini/rc
948 * files.)
950 if (! DISABLED_OPT( pOptDesc )) {
951 struct stat sb;
952 if (stat( pOptDesc->optArg.argString, &sb ) != 0) {
953 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
954 return;
956 fprintf( stderr, zFSErrOptLoad, errno, strerror( errno ),
957 pOptDesc->optArg.argString );
958 exit(EX_NOINPUT);
959 /* NOT REACHED */
962 if (! S_ISREG( sb.st_mode )) {
963 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
964 return;
966 fprintf( stderr, zNotFile, pOptDesc->optArg.argString );
967 exit(EX_NOINPUT);
968 /* NOT REACHED */
971 filePreset(pOpts, pOptDesc->optArg.argString, DIRECTION_CALLED);
976 /* parseAttributes
978 * Parse the various attributes of an XML-styled config file entry
980 LOCAL char*
981 parseAttributes(
982 tOptions* pOpts,
983 char* pzText,
984 tOptionLoadMode* pMode,
985 tOptionValue* pType )
987 size_t lenLoadType = strlen( zLoadType );
988 size_t lenKeyWords = strlen( zKeyWords );
989 size_t lenSetMem = strlen( zSetMembers );
991 do {
992 switch (*pzText) {
993 case '/': pType->valType = OPARG_TYPE_NONE;
994 case '>': return pzText;
996 default:
997 case NUL: return NULL;
999 case ' ':
1000 case '\t':
1001 case '\n':
1002 case '\f':
1003 case '\r':
1004 case '\v':
1005 break;
1008 while (isspace( (int)*++pzText )) ;
1010 if (strncmp( pzText, zLoadType, lenLoadType ) == 0) {
1011 pzText = parseValueType( pzText+lenLoadType, pType );
1012 continue;
1015 if (strncmp( pzText, zKeyWords, lenKeyWords ) == 0) {
1016 pzText = parseKeyWordType( pOpts, pzText+lenKeyWords, pType );
1017 continue;
1020 if (strncmp( pzText, zSetMembers, lenSetMem ) == 0) {
1021 pzText = parseSetMemType( pOpts, pzText+lenSetMem, pType );
1022 continue;
1025 pzText = parseLoadMode( pzText, pMode );
1026 } while (pzText != NULL);
1028 return pzText;
1032 /* parseKeyWordType
1034 * "pzText" points to the character after "words=".
1035 * What should follow is a name of a keyword (enumeration) list.
1037 static char*
1038 parseKeyWordType(
1039 tOptions* pOpts,
1040 char* pzText,
1041 tOptionValue* pType )
1043 return skipUnknown( pzText );
1047 /* parseLoadMode
1049 * "pzText" points to some name character. We check for "cooked" or
1050 * "uncooked" or "keep". This function should handle any attribute
1051 * that does not have an associated value.
1053 static char*
1054 parseLoadMode(
1055 char* pzText,
1056 tOptionLoadMode* pMode )
1059 size_t len = strlen(zLoadCooked);
1060 if (strncmp( pzText, zLoadCooked, len ) == 0) {
1061 if ( (pzText[len] == '>')
1062 || (pzText[len] == '/')
1063 || isspace((int)pzText[len])) {
1064 *pMode = OPTION_LOAD_COOKED;
1065 return pzText + len;
1067 goto unknown;
1072 size_t len = strlen(zLoadUncooked);
1073 if (strncmp( pzText, zLoadUncooked, len ) == 0) {
1074 if ( (pzText[len] == '>')
1075 || (pzText[len] == '/')
1076 || isspace((int)pzText[len])) {
1077 *pMode = OPTION_LOAD_UNCOOKED;
1078 return pzText + len;
1080 goto unknown;
1085 size_t len = strlen(zLoadKeep);
1086 if (strncmp( pzText, zLoadKeep, len ) == 0) {
1087 if ( (pzText[len] == '>')
1088 || (pzText[len] == '/')
1089 || isspace((int)pzText[len])) {
1090 *pMode = OPTION_LOAD_KEEP;
1091 return pzText + len;
1093 goto unknown;
1097 unknown:
1098 return skipUnknown( pzText );
1102 /* parseSetMemType
1104 * "pzText" points to the character after "members="
1105 * What should follow is a name of a "set membership".
1106 * A collection of bit flags.
1108 static char*
1109 parseSetMemType(
1110 tOptions* pOpts,
1111 char* pzText,
1112 tOptionValue* pType )
1114 return skipUnknown( pzText );
1118 /* parseValueType
1120 * "pzText" points to the character after "type="
1122 static char*
1123 parseValueType(
1124 char* pzText,
1125 tOptionValue* pType )
1128 size_t len = strlen(zLtypeString);
1129 if (strncmp( pzText, zLtypeString, len ) == 0) {
1130 if ((pzText[len] == '>') || isspace((int)pzText[len])) {
1131 pType->valType = OPARG_TYPE_STRING;
1132 return pzText + len;
1134 goto unknown;
1139 size_t len = strlen(zLtypeInteger);
1140 if (strncmp( pzText, zLtypeInteger, len ) == 0) {
1141 if ((pzText[len] == '>') || isspace((int)pzText[len])) {
1142 pType->valType = OPARG_TYPE_NUMERIC;
1143 return pzText + len;
1145 goto unknown;
1150 size_t len = strlen(zLtypeBool);
1151 if (strncmp( pzText, zLtypeBool, len ) == 0) {
1152 if ((pzText[len] == '>') || isspace((int)pzText[len])) {
1153 pType->valType = OPARG_TYPE_BOOLEAN;
1154 return pzText + len;
1156 goto unknown;
1161 size_t len = strlen(zLtypeKeyword);
1162 if (strncmp( pzText, zLtypeKeyword, len ) == 0) {
1163 if ((pzText[len] == '>') || isspace((int)pzText[len])) {
1164 pType->valType = OPARG_TYPE_ENUMERATION;
1165 return pzText + len;
1167 goto unknown;
1172 size_t len = strlen(zLtypeSetMembership);
1173 if (strncmp( pzText, zLtypeSetMembership, len ) == 0) {
1174 if ((pzText[len] == '>') || isspace((int)pzText[len])) {
1175 pType->valType = OPARG_TYPE_MEMBERSHIP;
1176 return pzText + len;
1178 goto unknown;
1183 size_t len = strlen(zLtypeNest);
1184 if (strncmp( pzText, zLtypeNest, len ) == 0) {
1185 if ((pzText[len] == '>') || isspace((int)pzText[len])) {
1186 pType->valType = OPARG_TYPE_HIERARCHY;
1187 return pzText + len;
1189 goto unknown;
1193 unknown:
1194 pType->valType = OPARG_TYPE_NONE;
1195 return skipUnknown( pzText );
1199 /* skipUnknown
1201 * Skip over some unknown attribute
1203 static char*
1204 skipUnknown( char* pzText )
1206 for (;; pzText++) {
1207 if (isspace( (int)*pzText )) return pzText;
1208 switch (*pzText) {
1209 case NUL: return NULL;
1210 case '/':
1211 case '>': return pzText;
1217 /* validateOptionsStruct
1219 * Make sure the option descriptor is there and that we understand it.
1220 * This should be called from any user entry point where one needs to
1221 * worry about validity. (Some entry points are free to assume that
1222 * the call is not the first to the library and, thus, that this has
1223 * already been called.)
1225 LOCAL tSuccess
1226 validateOptionsStruct( tOptions* pOpts, char const* pzProgram )
1228 if (pOpts == NULL) {
1229 fputs( zAO_Bad, stderr );
1230 exit( EX_CONFIG );
1234 * IF the client has enabled translation and the translation procedure
1235 * is available, then go do it.
1237 if ( ((pOpts->fOptSet & OPTPROC_TRANSLATE) != 0)
1238 && (pOpts->pTransProc != 0) ) {
1239 (*pOpts->pTransProc)();
1240 pOpts->fOptSet &= ~OPTPROC_TRANSLATE;
1244 * IF the struct version is not the current, and also
1245 * either too large (?!) or too small,
1246 * THEN emit error message and fail-exit
1248 if ( ( pOpts->structVersion != OPTIONS_STRUCT_VERSION )
1249 && ( (pOpts->structVersion > OPTIONS_STRUCT_VERSION )
1250 || (pOpts->structVersion < OPTIONS_MINIMUM_VERSION )
1251 ) ) {
1253 fprintf( stderr, zAO_Err, pOpts->origArgVect[0],
1254 NUM_TO_VER( pOpts->structVersion ));
1255 if (pOpts->structVersion > OPTIONS_STRUCT_VERSION )
1256 fputs( zAO_Big, stderr );
1257 else
1258 fputs( zAO_Sml, stderr );
1260 return FAILURE;
1264 * If the program name hasn't been set, then set the name and the path
1265 * and the set of equivalent characters.
1267 if (pOpts->pzProgName == NULL) {
1268 char const* pz = strrchr( pzProgram, DIRCH );
1270 if (pz == NULL)
1271 pOpts->pzProgName = pzProgram;
1272 else pOpts->pzProgName = pz+1;
1274 pOpts->pzProgPath = pzProgram;
1277 * when comparing long names, these are equivalent
1279 strequate( zSepChars );
1282 return SUCCESS;
1287 * Local Variables:
1288 * mode: C
1289 * c-file-style: "stroustrup"
1290 * indent-tabs-mode: nil
1291 * End:
1292 * end of autoopts/configfile.c */