1/* 2 * $Id: f1650b45a91ec95af830ff76041cc4f0048e60f0 $ 3 * Time-stamp: "2009-01-18 10:21:58 bkorb" 4 * 5 * configuration/rc/ini file handling. 6 * 7 * This file is part of AutoOpts, a companion to AutoGen. 8 * AutoOpts is free software. 9 * AutoOpts is copyright (c) 1992-2009 by Bruce Korb - all rights reserved 10 * 11 * AutoOpts is available under any one of two licenses. The license 12 * in use must be one of these two and the choice is under the control 13 * of the user of the license. 14 * 15 * The GNU Lesser General Public License, version 3 or later 16 * See the files "COPYING.lgplv3" and "COPYING.gplv3" 17 * 18 * The Modified Berkeley Software Distribution License 19 * See the file "COPYING.mbsd" 20 * 21 * These files have the following md5sums: 22 * 23 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3 24 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3 25 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd 26 */ 27 28/* = = = START-STATIC-FORWARD = = = */ 29/* static forward declarations maintained by mk-fwd */ 30static void 31filePreset( 32 tOptions* pOpts, 33 char const* pzFileName, 34 int direction ); 35 36static char* 37handleComment( char* pzText ); 38 39static char* 40handleConfig( 41 tOptions* pOpts, 42 tOptState* pOS, 43 char* pzText, 44 int direction ); 45 46static char* 47handleDirective( 48 tOptions* pOpts, 49 char* pzText ); 50 51static char* 52handleProgramSection( 53 tOptions* pOpts, 54 char* pzText ); 55 56static char* 57handleStructure( 58 tOptions* pOpts, 59 tOptState* pOS, 60 char* pzText, 61 int direction ); 62 63static char* 64parseKeyWordType( 65 tOptions* pOpts, 66 char* pzText, 67 tOptionValue* pType ); 68 69static char* 70parseSetMemType( 71 tOptions* pOpts, 72 char* pzText, 73 tOptionValue* pType ); 74 75static char* 76parseValueType( 77 char* pzText, 78 tOptionValue* pType ); 79 80static char* 81skipUnknown( char* pzText ); 82/* = = = END-STATIC-FORWARD = = = */ 83 84 85/*=export_func configFileLoad 86 * 87 * what: parse a configuration file 88 * arg: + char const* + pzFile + the file to load + 89 * 90 * ret_type: const tOptionValue* 91 * ret_desc: An allocated, compound value structure 92 * 93 * doc: 94 * This routine will load a named configuration file and parse the 95 * text as a hierarchically valued option. The option descriptor 96 * created from an option definition file is not used via this interface. 97 * The returned value is "named" with the input file name and is of 98 * type "@code{OPARG_TYPE_HIERARCHY}". It may be used in calls to 99 * @code{optionGetValue()}, @code{optionNextValue()} and 100 * @code{optionUnloadNested()}. 101 * 102 * err: 103 * If the file cannot be loaded or processed, @code{NULL} is returned and 104 * @var{errno} is set. It may be set by a call to either @code{open(2)} 105 * @code{mmap(2)} or other file system calls, or it may be: 106 * @itemize @bullet 107 * @item 108 * @code{ENOENT} - the file was empty. 109 * @item 110 * @code{EINVAL} - the file contents are invalid -- not properly formed. 111 * @item 112 * @code{ENOMEM} - not enough memory to allocate the needed structures. 113 * @end itemize 114=*/ 115const tOptionValue* 116configFileLoad( char const* pzFile ) 117{ 118 tmap_info_t cfgfile; 119 tOptionValue* pRes = NULL; 120 tOptionLoadMode save_mode = option_load_mode; 121 122 char* pzText = 123 text_mmap( pzFile, PROT_READ, MAP_PRIVATE, &cfgfile ); 124 125 if (TEXT_MMAP_FAILED_ADDR(pzText)) 126 return NULL; /* errno is set */ 127 128 option_load_mode = OPTION_LOAD_COOKED; 129 pRes = optionLoadNested(pzText, pzFile, strlen(pzFile)); 130 131 if (pRes == NULL) { 132 int err = errno; 133 text_munmap( &cfgfile ); 134 errno = err; 135 } else 136 text_munmap( &cfgfile ); 137 138 option_load_mode = save_mode; 139 return pRes; 140} 141 142 143/*=export_func optionFindValue 144 * 145 * what: find a hierarcicaly valued option instance 146 * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type + 147 * arg: + char const* + name + name of value to find + 148 * arg: + char const* + value + the matching value + 149 * 150 * ret_type: const tOptionValue* 151 * ret_desc: a compound value structure 152 * 153 * doc: 154 * This routine will find an entry in a nested value option or configurable. 155 * It will search through the list and return a matching entry. 156 * 157 * err: 158 * The returned result is NULL and errno is set: 159 * @itemize @bullet 160 * @item 161 * @code{EINVAL} - the @code{pOptValue} does not point to a valid 162 * hierarchical option value. 163 * @item 164 * @code{ENOENT} - no entry matched the given name. 165 * @end itemize 166=*/ 167const tOptionValue* 168optionFindValue( const tOptDesc* pOptDesc, 169 char const* pzName, char const* pzVal ) 170{ 171 const tOptionValue* pRes = NULL; 172 173 if ( (pOptDesc == NULL) 174 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) { 175 errno = EINVAL; 176 } 177 178 else if (pOptDesc->optCookie == NULL) { 179 errno = ENOENT; 180 } 181 182 else do { 183 tArgList* pAL = pOptDesc->optCookie; 184 int ct = pAL->useCt; 185 void** ppOV = (void**)(pAL->apzArgs); 186 187 if (ct == 0) { 188 errno = ENOENT; 189 break; 190 } 191 192 if (pzName == NULL) { 193 pRes = (tOptionValue*)*ppOV; 194 break; 195 } 196 197 while (--ct >= 0) { 198 const tOptionValue* pOV = *(ppOV++); 199 const tOptionValue* pRV = optionGetValue( pOV, pzName ); 200 201 if (pRV == NULL) 202 continue; 203 204 if (pzVal == NULL) { 205 pRes = pOV; 206 break; 207 } 208 } 209 if (pRes == NULL) 210 errno = ENOENT; 211 } while (0); 212 213 return pRes; 214} 215 216 217/*=export_func optionFindNextValue 218 * 219 * what: find a hierarcicaly valued option instance 220 * arg: + const tOptDesc* + pOptDesc + an option with a nested arg type + 221 * arg: + const tOptionValue* + pPrevVal + the last entry + 222 * arg: + char const* + name + name of value to find + 223 * arg: + char const* + value + the matching value + 224 * 225 * ret_type: const tOptionValue* 226 * ret_desc: a compound value structure 227 * 228 * doc: 229 * This routine will find the next entry in a nested value option or 230 * configurable. It will search through the list and return the next entry 231 * that matches the criteria. 232 * 233 * err: 234 * The returned result is NULL and errno is set: 235 * @itemize @bullet 236 * @item 237 * @code{EINVAL} - the @code{pOptValue} does not point to a valid 238 * hierarchical option value. 239 * @item 240 * @code{ENOENT} - no entry matched the given name. 241 * @end itemize 242=*/ 243const tOptionValue* 244optionFindNextValue( const tOptDesc* pOptDesc, const tOptionValue* pPrevVal, 245 char const* pzName, char const* pzVal ) 246{ 247 int foundOldVal = 0; 248 tOptionValue* pRes = NULL; 249 250 if ( (pOptDesc == NULL) 251 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) { 252 errno = EINVAL; 253 } 254 255 else if (pOptDesc->optCookie == NULL) { 256 errno = ENOENT; 257 } 258 259 else do { 260 tArgList* pAL = pOptDesc->optCookie; 261 int ct = pAL->useCt; 262 void** ppOV = (void**)pAL->apzArgs; 263 264 if (ct == 0) { 265 errno = ENOENT; 266 break; 267 } 268 269 while (--ct >= 0) { 270 tOptionValue* pOV = *(ppOV++); 271 if (foundOldVal) { 272 pRes = pOV; 273 break; 274 } 275 if (pOV == pPrevVal) 276 foundOldVal = 1; 277 } 278 if (pRes == NULL) 279 errno = ENOENT; 280 } while (0); 281 282 return pRes; 283} 284 285 286/*=export_func optionGetValue 287 * 288 * what: get a specific value from a hierarcical list 289 * arg: + const tOptionValue* + pOptValue + a hierarchcal value + 290 * arg: + char const* + valueName + name of value to get + 291 * 292 * ret_type: const tOptionValue* 293 * ret_desc: a compound value structure 294 * 295 * doc: 296 * This routine will find an entry in a nested value option or configurable. 297 * If "valueName" is NULL, then the first entry is returned. Otherwise, 298 * the first entry with a name that exactly matches the argument will be 299 * returned. 300 * 301 * err: 302 * The returned result is NULL and errno is set: 303 * @itemize @bullet 304 * @item 305 * @code{EINVAL} - the @code{pOptValue} does not point to a valid 306 * hierarchical option value. 307 * @item 308 * @code{ENOENT} - no entry matched the given name. 309 * @end itemize 310=*/ 311const tOptionValue* 312optionGetValue( const tOptionValue* pOld, char const* pzValName ) 313{ 314 tArgList* pAL; 315 tOptionValue* pRes = NULL; 316 317 if ((pOld == NULL) || (pOld->valType != OPARG_TYPE_HIERARCHY)) { 318 errno = EINVAL; 319 return NULL; 320 } 321 pAL = pOld->v.nestVal; 322 323 if (pAL->useCt > 0) { 324 int ct = pAL->useCt; 325 void** papOV = (void**)(pAL->apzArgs); 326 327 if (pzValName == NULL) { 328 pRes = (tOptionValue*)*papOV; 329 } 330 331 else do { 332 tOptionValue* pOV = *(papOV++); 333 if (strcmp( pOV->pzName, pzValName ) == 0) { 334 pRes = pOV; 335 break; 336 } 337 } while (--ct > 0); 338 } 339 if (pRes == NULL) 340 errno = ENOENT; 341 return pRes; 342} 343 344 345/*=export_func optionNextValue 346 * 347 * what: get the next value from a hierarchical list 348 * arg: + const tOptionValue* + pOptValue + a hierarchcal list value + 349 * arg: + const tOptionValue* + pOldValue + a value from this list + 350 * 351 * ret_type: const tOptionValue* 352 * ret_desc: a compound value structure 353 * 354 * doc: 355 * This routine will return the next entry after the entry passed in. At the 356 * end of the list, NULL will be returned. If the entry is not found on the 357 * list, NULL will be returned and "@var{errno}" will be set to EINVAL. 358 * The "@var{pOldValue}" must have been gotten from a prior call to this 359 * routine or to "@code{opitonGetValue()}". 360 * 361 * err: 362 * The returned result is NULL and errno is set: 363 * @itemize @bullet 364 * @item 365 * @code{EINVAL} - the @code{pOptValue} does not point to a valid 366 * hierarchical option value or @code{pOldValue} does not point to a 367 * member of that option value. 368 * @item 369 * @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry. 370 * @end itemize 371=*/ 372tOptionValue const * 373optionNextValue(tOptionValue const * pOVList,tOptionValue const * pOldOV ) 374{ 375 tArgList* pAL; 376 tOptionValue* pRes = NULL; 377 int err = EINVAL; 378 379 if ((pOVList == NULL) || (pOVList->valType != OPARG_TYPE_HIERARCHY)) { 380 errno = EINVAL; 381 return NULL; 382 } 383 pAL = pOVList->v.nestVal; 384 { 385 int ct = pAL->useCt; 386 void** papNV = (void**)(pAL->apzArgs); 387 388 while (ct-- > 0) { 389 tOptionValue* pNV = *(papNV++); 390 if (pNV == pOldOV) { 391 if (ct == 0) { 392 err = ENOENT; 393 394 } else { 395 err = 0; 396 pRes = (tOptionValue*)*papNV; 397 } 398 break; 399 } 400 } 401 } 402 if (err != 0) 403 errno = err; 404 return pRes; 405} 406 407 408/* filePreset 409 * 410 * Load a file containing presetting information (a configuration file). 411 */ 412static void 413filePreset( 414 tOptions* pOpts, 415 char const* pzFileName, 416 int direction ) 417{ 418 tmap_info_t cfgfile; 419 tOptState optst = OPTSTATE_INITIALIZER(PRESET); 420 char* pzFileText = 421 text_mmap( pzFileName, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile ); 422 423 if (TEXT_MMAP_FAILED_ADDR(pzFileText)) 424 return; 425 426 if (direction == DIRECTION_CALLED) { 427 optst.flags = OPTST_DEFINED; 428 direction = DIRECTION_PROCESS; 429 } 430 431 /* 432 * IF this is called via "optionProcess", then we are presetting. 433 * This is the default and the PRESETTING bit will be set. 434 * If this is called via "optionFileLoad", then the bit is not set 435 * and we consider stuff set herein to be "set" by the client program. 436 */ 437 if ((pOpts->fOptSet & OPTPROC_PRESETTING) == 0) 438 optst.flags = OPTST_SET; 439 440 do { 441 while (IS_WHITESPACE_CHAR(*pzFileText)) pzFileText++; 442 443 if (IS_VAR_FIRST_CHAR(*pzFileText)) { 444 pzFileText = handleConfig(pOpts, &optst, pzFileText, direction); 445 446 } else switch (*pzFileText) { 447 case '<': 448 if (IS_VAR_FIRST_CHAR(pzFileText[1])) 449 pzFileText = 450 handleStructure(pOpts, &optst, pzFileText, direction); 451 452 else switch (pzFileText[1]) { 453 case '?': 454 pzFileText = handleDirective( pOpts, pzFileText ); 455 break; 456 457 case '!': 458 pzFileText = handleComment( pzFileText ); 459 break; 460 461 case '/': 462 pzFileText = strchr( pzFileText+2, '>' ); 463 if (pzFileText++ != NULL) 464 break; 465 466 default: 467 goto all_done; 468 } 469 break; 470 471 case '[': 472 pzFileText = handleProgramSection( pOpts, pzFileText ); 473 break; 474 475 case '#': 476 pzFileText = strchr( pzFileText+1, '\n' ); 477 break; 478 479 default: 480 goto all_done; /* invalid format */ 481 } 482 } while (pzFileText != NULL); 483 484 all_done: 485 text_munmap( &cfgfile ); 486} 487 488 489/* handleComment 490 * 491 * "pzText" points to a "<!" sequence. 492 * Theoretically, we should ensure that it begins with "<!--", 493 * but actually I don't care that much. It ends with "-->". 494 */ 495static char* 496handleComment( char* pzText ) 497{ 498 char* pz = strstr( pzText, "-->" ); 499 if (pz != NULL) 500 pz += 3; 501 return pz; 502} 503 504 505/* handleConfig 506 * 507 * "pzText" points to the start of some value name. 508 * The end of the entry is the end of the line that is not preceded by 509 * a backslash escape character. The string value is always processed 510 * in "cooked" mode. 511 */ 512static char* 513handleConfig( 514 tOptions* pOpts, 515 tOptState* pOS, 516 char* pzText, 517 int direction ) 518{ 519 char* pzName = pzText++; 520 char* pzEnd = strchr( pzText, '\n' ); 521 522 if (pzEnd == NULL) 523 return pzText + strlen(pzText); 524 525 while (IS_VALUE_NAME_CHAR(*pzText)) pzText++; 526 while (IS_WHITESPACE_CHAR(*pzText)) pzText++; 527 if (pzText > pzEnd) { 528 name_only: 529 *pzEnd++ = NUL; 530 loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED ); 531 return pzEnd; 532 } 533 534 /* 535 * Either the first character after the name is a ':' or '=', 536 * or else we must have skipped over white space. Anything else 537 * is an invalid format and we give up parsing the text. 538 */ 539 if ((*pzText == '=') || (*pzText == ':')) { 540 while (IS_WHITESPACE_CHAR(*++pzText)) ; 541 if (pzText > pzEnd) 542 goto name_only; 543 } else if (! IS_WHITESPACE_CHAR(pzText[-1])) 544 return NULL; 545 546 /* 547 * IF the value is continued, remove the backslash escape and push "pzEnd" 548 * on to a newline *not* preceded by a backslash. 549 */ 550 if (pzEnd[-1] == '\\') { 551 char* pcD = pzEnd-1; 552 char* pcS = pzEnd; 553 554 for (;;) { 555 char ch = *(pcS++); 556 switch (ch) { 557 case NUL: 558 pcS = NULL; 559 560 case '\n': 561 *pcD = NUL; 562 pzEnd = pcS; 563 goto copy_done; 564 565 case '\\': 566 if (*pcS == '\n') { 567 ch = *(pcS++); 568 } 569 /* FALLTHROUGH */ 570 default: 571 *(pcD++) = ch; 572 } 573 } copy_done:; 574 575 } else { 576 /* 577 * The newline was not preceded by a backslash. NUL it out 578 */ 579 *(pzEnd++) = NUL; 580 } 581 582 /* 583 * "pzName" points to what looks like text for one option/configurable. 584 * It is NUL terminated. Process it. 585 */ 586 loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED ); 587 588 return pzEnd; 589} 590 591 592/* handleDirective 593 * 594 * "pzText" points to a "<?" sequence. 595 * For the moment, we only handle "<?program" directives. 596 */ 597static char* 598handleDirective( 599 tOptions* pOpts, 600 char* pzText ) 601{ 602 char ztitle[32] = "<?"; 603 size_t title_len = strlen( zProg ); 604 size_t name_len; 605 606 if ( (strncmp( pzText+2, zProg, title_len ) != 0) 607 || (! IS_WHITESPACE_CHAR(pzText[title_len+2])) ) { 608 pzText = strchr( pzText+2, '>' ); 609 if (pzText != NULL) 610 pzText++; 611 return pzText; 612 } 613 614 name_len = strlen( pOpts->pzProgName ); 615 strcpy( ztitle+2, zProg ); 616 title_len += 2; 617 618 do { 619 pzText += title_len; 620 621 if (IS_WHITESPACE_CHAR(*pzText)) { 622 while (IS_WHITESPACE_CHAR(*++pzText)) ; 623 if ( (strneqvcmp( pzText, pOpts->pzProgName, (int)name_len) == 0) 624 && (pzText[name_len] == '>')) { 625 pzText += name_len + 1; 626 break; 627 } 628 } 629 630 pzText = strstr( pzText, ztitle ); 631 } while (pzText != NULL); 632 633 return pzText; 634} 635 636 637/* handleProgramSection 638 * 639 * "pzText" points to a '[' character. 640 * The "traditional" [PROG_NAME] segmentation of the config file. 641 * Do not ever mix with the "<?program prog-name>" variation. 642 */ 643static char* 644handleProgramSection( 645 tOptions* pOpts, 646 char* pzText ) 647{ 648 size_t len = strlen( pOpts->pzPROGNAME ); 649 if ( (strncmp( pzText+1, pOpts->pzPROGNAME, len ) == 0) 650 && (pzText[len+1] == ']')) 651 return strchr( pzText + len + 2, '\n' ); 652 653 if (len > 16) 654 return NULL; 655 656 { 657 char z[24]; 658 sprintf( z, "[%s]", pOpts->pzPROGNAME ); 659 pzText = strstr( pzText, z ); 660 } 661 662 if (pzText != NULL) 663 pzText = strchr( pzText, '\n' ); 664 return pzText; 665} 666 667 668/* handleStructure 669 * 670 * "pzText" points to a '<' character, followed by an alpha. 671 * The end of the entry is either the "/>" following the name, or else a 672 * "</name>" string. 673 */ 674static char* 675handleStructure( 676 tOptions* pOpts, 677 tOptState* pOS, 678 char* pzText, 679 int direction ) 680{ 681 tOptionLoadMode mode = option_load_mode; 682 tOptionValue valu; 683 684 char* pzName = ++pzText; 685 char* pzData; 686 char* pcNulPoint; 687 688 while (IS_VALUE_NAME_CHAR(*pzText)) pzText++; 689 pcNulPoint = pzText; 690 valu.valType = OPARG_TYPE_STRING; 691 692 switch (*pzText) { 693 case ' ': 694 case '\t': 695 pzText = parseAttributes( pOpts, pzText, &mode, &valu ); 696 if (*pzText == '>') 697 break; 698 if (*pzText != '/') 699 return NULL; 700 /* FALLTHROUGH */ 701 702 case '/': 703 if (pzText[1] != '>') 704 return NULL; 705 *pzText = NUL; 706 pzText += 2; 707 loadOptionLine( pOpts, pOS, pzName, direction, mode ); 708 return pzText; 709 710 case '>': 711 break; 712 713 default: 714 pzText = strchr( pzText, '>'); 715 if (pzText != NULL) 716 pzText++; 717 return pzText; 718 } 719 720 /* 721 * If we are here, we have a value. "pzText" points to a closing angle 722 * bracket. Separate the name from the value for a moment. 723 */ 724 *pcNulPoint = NUL; 725 pzData = ++pzText; 726 727 /* 728 * Find the end of the option text and NUL terminate it 729 */ 730 { 731 char z[64], *pz = z; 732 size_t len = strlen(pzName) + 4; 733 if (len > sizeof(z)) 734 pz = AGALOC(len, "scan name"); 735 736 sprintf( pz, "</%s>", pzName ); 737 *pzText = ' '; 738 pzText = strstr( pzText, pz ); 739 if (pz != z) AGFREE(pz); 740 741 if (pzText == NULL) 742 return pzText; 743 744 *pzText = NUL; 745 746 pzText += len-1; 747 } 748 749 /* 750 * Rejoin the name and value for parsing by "loadOptionLine()". 751 * Erase any attributes parsed by "parseAttributes()". 752 */ 753 memset(pcNulPoint, ' ', pzData - pcNulPoint); 754 755 /* 756 * If we are getting a "string" value, the process the XML-ish 757 * %XX hex characters. 758 */ 759 if (valu.valType == OPARG_TYPE_STRING) { 760 char * pzSrc = pzData; 761 char * pzDst = pzData; 762 char bf[4]; 763 bf[2] = NUL; 764 765 for (;;) { 766 int ch = ((int)*(pzSrc++)) & 0xFF; 767 switch (ch) { 768 case NUL: goto string_fixup_done; 769 770 case '%': 771 bf[0] = *(pzSrc++); 772 bf[1] = *(pzSrc++); 773 if ((bf[0] == NUL) || (bf[1] == NUL)) 774 goto string_fixup_done; 775 ch = strtoul(bf, NULL, 16); 776 /* FALLTHROUGH */ 777 778 default: 779 *(pzDst++) = ch; 780 } 781 } string_fixup_done:; 782 *pzDst = NUL; 783 } 784 785 /* 786 * "pzName" points to what looks like text for one option/configurable. 787 * It is NUL terminated. Process it. 788 */ 789 loadOptionLine( pOpts, pOS, pzName, direction, mode ); 790 791 return pzText; 792} 793 794 795/* internalFileLoad 796 * 797 * Load a configuration file. This may be invoked either from 798 * scanning the "homerc" list, or from a specific file request. 799 * (see "optionFileLoad()", the implementation for --load-opts) 800 */ 801LOCAL void 802internalFileLoad( tOptions* pOpts ) 803{ 804 uint32_t svfl; 805 int idx; 806 int inc; 807 char zFileName[ AG_PATH_MAX+1 ]; 808 809 if (pOpts->papzHomeList == NULL) 810 return; 811 812 svfl = pOpts->fOptSet; 813 inc = DIRECTION_PRESET; 814 815 /* 816 * Never stop on errors in config files. 817 */ 818 pOpts->fOptSet &= ~OPTPROC_ERRSTOP; 819 820 /* 821 * Find the last RC entry (highest priority entry) 822 */ 823 for (idx = 0; pOpts->papzHomeList[ idx+1 ] != NULL; ++idx) ; 824 825 /* 826 * For every path in the home list, ... *TWICE* We start at the last 827 * (highest priority) entry, work our way down to the lowest priority, 828 * handling the immediate options. 829 * Then we go back up, doing the normal options. 830 */ 831 for (;;) { 832 struct stat StatBuf; 833 cch_t* pzPath; 834 835 /* 836 * IF we've reached the bottom end, change direction 837 */ 838 if (idx < 0) { 839 inc = DIRECTION_PROCESS; 840 idx = 0; 841 } 842 843 pzPath = pOpts->papzHomeList[ idx ]; 844 845 /* 846 * IF we've reached the top end, bail out 847 */ 848 if (pzPath == NULL) 849 break; 850 851 idx += inc; 852 853 if (! optionMakePath( zFileName, (int)sizeof(zFileName), 854 pzPath, pOpts->pzProgPath )) 855 continue; 856 857 /* 858 * IF the file name we constructed is a directory, 859 * THEN append the Resource Configuration file name 860 * ELSE we must have the complete file name 861 */ 862 if (stat( zFileName, &StatBuf ) != 0) 863 continue; /* bogus name - skip the home list entry */ 864 865 if (S_ISDIR( StatBuf.st_mode )) { 866 size_t len = strlen( zFileName ); 867 char* pz; 868 869 if (len + 1 + strlen( pOpts->pzRcName ) >= sizeof( zFileName )) 870 continue; 871 872 pz = zFileName + len; 873 if (pz[-1] != DIRCH) 874 *(pz++) = DIRCH; 875 strcpy( pz, pOpts->pzRcName ); 876 } 877 878 filePreset( pOpts, zFileName, inc ); 879 880 /* 881 * IF we are now to skip config files AND we are presetting, 882 * THEN change direction. We must go the other way. 883 */ 884 { 885 tOptDesc * pOD = pOpts->pOptDesc + pOpts->specOptIdx.save_opts+1; 886 if (DISABLED_OPT(pOD) && PRESETTING(inc)) { 887 idx -= inc; /* go back and reprocess current file */ 888 inc = DIRECTION_PROCESS; 889 } 890 } 891 } /* twice for every path in the home list, ... */ 892 893 pOpts->fOptSet = svfl; 894} 895 896 897/*=export_func optionFileLoad 898 * 899 * what: Load the locatable config files, in order 900 * 901 * arg: + tOptions* + pOpts + program options descriptor + 902 * arg: + char const* + pzProg + program name + 903 * 904 * ret_type: int 905 * ret_desc: 0 -> SUCCESS, -1 -> FAILURE 906 * 907 * doc: 908 * 909 * This function looks in all the specified directories for a configuration 910 * file ("rc" file or "ini" file) and processes any found twice. The first 911 * time through, they are processed in reverse order (last file first). At 912 * that time, only "immediate action" configurables are processed. For 913 * example, if the last named file specifies not processing any more 914 * configuration files, then no more configuration files will be processed. 915 * Such an option in the @strong{first} named directory will have no effect. 916 * 917 * Once the immediate action configurables have been handled, then the 918 * directories are handled in normal, forward order. In that way, later 919 * config files can override the settings of earlier config files. 920 * 921 * See the AutoOpts documentation for a thorough discussion of the 922 * config file format. 923 * 924 * Configuration files not found or not decipherable are simply ignored. 925 * 926 * err: Returns the value, "-1" if the program options descriptor 927 * is out of date or indecipherable. Otherwise, the value "0" will 928 * always be returned. 929=*/ 930int 931optionFileLoad( tOptions* pOpts, char const* pzProgram ) 932{ 933 if (! SUCCESSFUL( validateOptionsStruct( pOpts, pzProgram ))) 934 return -1; 935 936 pOpts->pzProgName = pzProgram; 937 internalFileLoad( pOpts ); 938 return 0; 939} 940 941 942/*=export_func optionLoadOpt 943 * private: 944 * 945 * what: Load an option rc/ini file 946 * arg: + tOptions* + pOpts + program options descriptor + 947 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg + 948 * 949 * doc: 950 * Processes the options found in the file named with 951 * pOptDesc->optArg.argString. 952=*/ 953void 954optionLoadOpt( tOptions* pOpts, tOptDesc* pOptDesc ) 955{ 956 struct stat sb; 957 958 /* 959 * IF the option is not being disabled, THEN load the file. There must 960 * be a file. (If it is being disabled, then the disablement processing 961 * already took place. It must be done to suppress preloading of ini/rc 962 * files.) 963 */ 964 if ( DISABLED_OPT(pOptDesc) 965 || ((pOptDesc->fOptState & OPTST_RESET) != 0)) 966 return; 967 968 if (stat( pOptDesc->optArg.argString, &sb ) != 0) { 969 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0) 970 return; 971 972 fprintf( stderr, zFSErrOptLoad, errno, strerror( errno ), 973 pOptDesc->optArg.argString ); 974 exit(EX_NOINPUT); 975 /* NOT REACHED */ 976 } 977 978 if (! S_ISREG( sb.st_mode )) { 979 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0) 980 return; 981 982 fprintf( stderr, zNotFile, pOptDesc->optArg.argString ); 983 exit(EX_NOINPUT); 984 /* NOT REACHED */ 985 } 986 987 filePreset(pOpts, pOptDesc->optArg.argString, DIRECTION_CALLED); 988} 989 990 991/* parseAttributes 992 * 993 * Parse the various attributes of an XML-styled config file entry 994 */ 995LOCAL char* 996parseAttributes( 997 tOptions* pOpts, 998 char* pzText, 999 tOptionLoadMode* pMode, 1000 tOptionValue* pType ) 1001{ 1002 size_t len; 1003 1004 do { 1005 if (! IS_WHITESPACE_CHAR(*pzText)) 1006 switch (*pzText) { 1007 case '/': pType->valType = OPARG_TYPE_NONE; 1008 case '>': return pzText; 1009 1010 default: 1011 case NUL: return NULL; 1012 } 1013 1014 while (IS_WHITESPACE_CHAR(*++pzText)) ; 1015 len = 0; 1016 while (IS_LOWER_CASE_CHAR(pzText[len])) len++; 1017 1018 switch (find_xat_attribute_id(pzText, len)) { 1019 case XAT_KWD_TYPE: 1020 pzText = parseValueType( pzText+len, pType ); 1021 break; 1022 1023 case XAT_KWD_WORDS: 1024 pzText = parseKeyWordType( pOpts, pzText+len, pType ); 1025 break; 1026 1027 case XAT_KWD_MEMBERS: 1028 pzText = parseSetMemType( pOpts, pzText+len, pType ); 1029 break; 1030 1031 case XAT_KWD_COOKED: 1032 pzText += len; 1033 if (! IS_END_XML_TOKEN_CHAR(*pzText)) 1034 goto invalid_kwd; 1035 1036 *pMode = OPTION_LOAD_COOKED; 1037 break; 1038 1039 case XAT_KWD_UNCOOKED: 1040 pzText += len; 1041 if (! IS_END_XML_TOKEN_CHAR(*pzText)) 1042 goto invalid_kwd; 1043 1044 *pMode = OPTION_LOAD_UNCOOKED; 1045 break; 1046 1047 case XAT_KWD_KEEP: 1048 pzText += len; 1049 if (! IS_END_XML_TOKEN_CHAR(*pzText)) 1050 goto invalid_kwd; 1051 1052 *pMode = OPTION_LOAD_KEEP; 1053 break; 1054 1055 default: 1056 case XAT_KWD_INVALID: 1057 invalid_kwd: 1058 pType->valType = OPARG_TYPE_NONE; 1059 return skipUnknown( pzText ); 1060 } 1061 } while (pzText != NULL); 1062 1063 return pzText; 1064} 1065 1066 1067/* parseKeyWordType 1068 * 1069 * "pzText" points to the character after "words=". 1070 * What should follow is a name of a keyword (enumeration) list. 1071 */ 1072static char* 1073parseKeyWordType( 1074 tOptions* pOpts, 1075 char* pzText, 1076 tOptionValue* pType ) 1077{ 1078 return skipUnknown( pzText ); 1079} 1080 1081 1082/* parseSetMemType 1083 * 1084 * "pzText" points to the character after "members=" 1085 * What should follow is a name of a "set membership". 1086 * A collection of bit flags. 1087 */ 1088static char* 1089parseSetMemType( 1090 tOptions* pOpts, 1091 char* pzText, 1092 tOptionValue* pType ) 1093{ 1094 return skipUnknown( pzText ); 1095} 1096 1097 1098/* parseValueType 1099 * 1100 * "pzText" points to the character after "type=" 1101 */ 1102static char* 1103parseValueType( 1104 char* pzText, 1105 tOptionValue* pType ) 1106{ 1107 size_t len = 0; 1108 1109 if (*(pzText++) != '=') 1110 goto woops; 1111 1112 while (IS_OPTION_NAME_CHAR(pzText[len])) len++; 1113 pzText += len; 1114 1115 if ((len == 0) || (! IS_END_XML_TOKEN_CHAR(*pzText))) { 1116 woops: 1117 pType->valType = OPARG_TYPE_NONE; 1118 return skipUnknown( pzText ); 1119 } 1120 1121 switch (find_value_type_id(pzText - len, len)) { 1122 default: 1123 case VTP_KWD_INVALID: goto woops; 1124 1125 case VTP_KWD_STRING: 1126 pType->valType = OPARG_TYPE_STRING; 1127 break; 1128 1129 case VTP_KWD_INTEGER: 1130 pType->valType = OPARG_TYPE_NUMERIC; 1131 break; 1132 1133 case VTP_KWD_BOOL: 1134 case VTP_KWD_BOOLEAN: 1135 pType->valType = OPARG_TYPE_BOOLEAN; 1136 break; 1137 1138 case VTP_KWD_KEYWORD: 1139 pType->valType = OPARG_TYPE_ENUMERATION; 1140 break; 1141 1142 case VTP_KWD_SET: 1143 case VTP_KWD_SET_MEMBERSHIP: 1144 pType->valType = OPARG_TYPE_MEMBERSHIP; 1145 break; 1146 1147 case VTP_KWD_NESTED: 1148 case VTP_KWD_HIERARCHY: 1149 pType->valType = OPARG_TYPE_HIERARCHY; 1150 } 1151 1152 return pzText; 1153} 1154 1155 1156/* skipUnknown 1157 * 1158 * Skip over some unknown attribute 1159 */ 1160static char* 1161skipUnknown( char* pzText ) 1162{ 1163 for (;; pzText++) { 1164 if (IS_END_XML_TOKEN_CHAR(*pzText)) return pzText; 1165 if (*pzText == NUL) return NULL; 1166 } 1167} 1168 1169 1170/* validateOptionsStruct 1171 * 1172 * Make sure the option descriptor is there and that we understand it. 1173 * This should be called from any user entry point where one needs to 1174 * worry about validity. (Some entry points are free to assume that 1175 * the call is not the first to the library and, thus, that this has 1176 * already been called.) 1177 */ 1178LOCAL tSuccess 1179validateOptionsStruct( tOptions* pOpts, char const* pzProgram ) 1180{ 1181 if (pOpts == NULL) { 1182 fputs( zAO_Bad, stderr ); 1183 exit( EX_CONFIG ); 1184 } 1185 1186 /* 1187 * IF the client has enabled translation and the translation procedure 1188 * is available, then go do it. 1189 */ 1190 if ( ((pOpts->fOptSet & OPTPROC_TRANSLATE) != 0) 1191 && (pOpts->pTransProc != NULL) ) { 1192 /* 1193 * If option names are not to be translated at all, then do not do 1194 * it for configuration parsing either. (That is the bit that really 1195 * gets tested anyway.) 1196 */ 1197 if ((pOpts->fOptSet & OPTPROC_NO_XLAT_MASK) == OPTPROC_NXLAT_OPT) 1198 pOpts->fOptSet |= OPTPROC_NXLAT_OPT_CFG; 1199 (*pOpts->pTransProc)(); 1200 pOpts->fOptSet &= ~OPTPROC_TRANSLATE; 1201 } 1202 1203 /* 1204 * IF the struct version is not the current, and also 1205 * either too large (?!) or too small, 1206 * THEN emit error message and fail-exit 1207 */ 1208 if ( ( pOpts->structVersion != OPTIONS_STRUCT_VERSION ) 1209 && ( (pOpts->structVersion > OPTIONS_STRUCT_VERSION ) 1210 || (pOpts->structVersion < OPTIONS_MINIMUM_VERSION ) 1211 ) ) { 1212 1213 fprintf(stderr, zAO_Err, pzProgram, NUM_TO_VER(pOpts->structVersion)); 1214 if (pOpts->structVersion > OPTIONS_STRUCT_VERSION ) 1215 fputs( zAO_Big, stderr ); 1216 else 1217 fputs( zAO_Sml, stderr ); 1218 1219 return FAILURE; 1220 } 1221 1222 /* 1223 * If the program name hasn't been set, then set the name and the path 1224 * and the set of equivalent characters. 1225 */ 1226 if (pOpts->pzProgName == NULL) { 1227 char const* pz = strrchr( pzProgram, DIRCH ); 1228 1229 if (pz == NULL) 1230 pOpts->pzProgName = pzProgram; 1231 else pOpts->pzProgName = pz+1; 1232 1233 pOpts->pzProgPath = pzProgram; 1234 1235 /* 1236 * when comparing long names, these are equivalent 1237 */ 1238 strequate( zSepChars ); 1239 } 1240 1241 return SUCCESS; 1242} 1243 1244 1245/** 1246 * Local Variables: 1247 * mode: C 1248 * c-file-style: "stroustrup" 1249 * indent-tabs-mode: nil 1250 * End: 1251 * end of autoopts/configfile.c */ 1252