1 /* $NetBSD: configfile.c,v 1.1.1.1 2009/12/13 16:57:18 kardel Exp $ */ 2 3 /* 4 * Id: f1650b45a91ec95af830ff76041cc4f0048e60f0 5 * Time-stamp: "2009-01-18 10:21:58 bkorb" 6 * 7 * configuration/rc/ini file handling. 8 * 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 12 * 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. 16 * 17 * The GNU Lesser General Public License, version 3 or later 18 * See the files "COPYING.lgplv3" and "COPYING.gplv3" 19 * 20 * The Modified Berkeley Software Distribution License 21 * See the file "COPYING.mbsd" 22 * 23 * These files have the following md5sums: 24 * 25 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3 26 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3 27 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd 28 */ 29 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 ); 37 38 static char* 39 handleComment( char* pzText ); 40 41 static char* 42 handleConfig( 43 tOptions* pOpts, 44 tOptState* pOS, 45 char* pzText, 46 int direction ); 47 48 static char* 49 handleDirective( 50 tOptions* pOpts, 51 char* pzText ); 52 53 static char* 54 handleProgramSection( 55 tOptions* pOpts, 56 char* pzText ); 57 58 static char* 59 handleStructure( 60 tOptions* pOpts, 61 tOptState* pOS, 62 char* pzText, 63 int direction ); 64 65 static char* 66 parseKeyWordType( 67 tOptions* pOpts, 68 char* pzText, 69 tOptionValue* pType ); 70 71 static char* 72 parseSetMemType( 73 tOptions* pOpts, 74 char* pzText, 75 tOptionValue* pType ); 76 77 static char* 78 parseValueType( 79 char* pzText, 80 tOptionValue* pType ); 81 82 static char* 83 skipUnknown( char* pzText ); 84 /* = = = END-STATIC-FORWARD = = = */ 85 86 87 /*=export_func configFileLoad 88 * 89 * what: parse a configuration file 90 * arg: + char const* + pzFile + the file to load + 91 * 92 * ret_type: const tOptionValue* 93 * ret_desc: An allocated, compound value structure 94 * 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()}. 103 * 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 116 =*/ 117 const tOptionValue* 118 configFileLoad( char const* pzFile ) 119 { 120 tmap_info_t cfgfile; 121 tOptionValue* pRes = NULL; 122 tOptionLoadMode save_mode = option_load_mode; 123 124 char* pzText = 125 text_mmap( pzFile, PROT_READ, MAP_PRIVATE, &cfgfile ); 126 127 if (TEXT_MMAP_FAILED_ADDR(pzText)) 128 return NULL; /* errno is set */ 129 130 option_load_mode = OPTION_LOAD_COOKED; 131 pRes = optionLoadNested(pzText, pzFile, strlen(pzFile)); 132 133 if (pRes == NULL) { 134 int err = errno; 135 text_munmap( &cfgfile ); 136 errno = err; 137 } else 138 text_munmap( &cfgfile ); 139 140 option_load_mode = save_mode; 141 return pRes; 142 } 143 144 145 /*=export_func optionFindValue 146 * 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 + 151 * 152 * ret_type: const tOptionValue* 153 * ret_desc: a compound value structure 154 * 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. 158 * 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 168 =*/ 169 const tOptionValue* 170 optionFindValue( const tOptDesc* pOptDesc, 171 char const* pzName, char const* pzVal ) 172 { 173 const tOptionValue* pRes = NULL; 174 175 if ( (pOptDesc == NULL) 176 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) { 177 errno = EINVAL; 178 } 179 180 else if (pOptDesc->optCookie == NULL) { 181 errno = ENOENT; 182 } 183 184 else do { 185 tArgList* pAL = pOptDesc->optCookie; 186 int ct = pAL->useCt; 187 void** ppOV = (void**)(pAL->apzArgs); 188 189 if (ct == 0) { 190 errno = ENOENT; 191 break; 192 } 193 194 if (pzName == NULL) { 195 pRes = (tOptionValue*)*ppOV; 196 break; 197 } 198 199 while (--ct >= 0) { 200 const tOptionValue* pOV = *(ppOV++); 201 const tOptionValue* pRV = optionGetValue( pOV, pzName ); 202 203 if (pRV == NULL) 204 continue; 205 206 if (pzVal == NULL) { 207 pRes = pOV; 208 break; 209 } 210 } 211 if (pRes == NULL) 212 errno = ENOENT; 213 } while (0); 214 215 return pRes; 216 } 217 218 219 /*=export_func optionFindNextValue 220 * 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 + 226 * 227 * ret_type: const tOptionValue* 228 * ret_desc: a compound value structure 229 * 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. 234 * 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 244 =*/ 245 const tOptionValue* 246 optionFindNextValue( const tOptDesc* pOptDesc, const tOptionValue* pPrevVal, 247 char const* pzName, char const* pzVal ) 248 { 249 int foundOldVal = 0; 250 tOptionValue* pRes = NULL; 251 252 if ( (pOptDesc == NULL) 253 || (OPTST_GET_ARGTYPE(pOptDesc->fOptState) != OPARG_TYPE_HIERARCHY)) { 254 errno = EINVAL; 255 } 256 257 else if (pOptDesc->optCookie == NULL) { 258 errno = ENOENT; 259 } 260 261 else do { 262 tArgList* pAL = pOptDesc->optCookie; 263 int ct = pAL->useCt; 264 void** ppOV = (void**)pAL->apzArgs; 265 266 if (ct == 0) { 267 errno = ENOENT; 268 break; 269 } 270 271 while (--ct >= 0) { 272 tOptionValue* pOV = *(ppOV++); 273 if (foundOldVal) { 274 pRes = pOV; 275 break; 276 } 277 if (pOV == pPrevVal) 278 foundOldVal = 1; 279 } 280 if (pRes == NULL) 281 errno = ENOENT; 282 } while (0); 283 284 return pRes; 285 } 286 287 288 /*=export_func optionGetValue 289 * 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 + 293 * 294 * ret_type: const tOptionValue* 295 * ret_desc: a compound value structure 296 * 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. 302 * 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 312 =*/ 313 const tOptionValue* 314 optionGetValue( const tOptionValue* pOld, char const* pzValName ) 315 { 316 tArgList* pAL; 317 tOptionValue* pRes = NULL; 318 319 if ((pOld == NULL) || (pOld->valType != OPARG_TYPE_HIERARCHY)) { 320 errno = EINVAL; 321 return NULL; 322 } 323 pAL = pOld->v.nestVal; 324 325 if (pAL->useCt > 0) { 326 int ct = pAL->useCt; 327 void** papOV = (void**)(pAL->apzArgs); 328 329 if (pzValName == NULL) { 330 pRes = (tOptionValue*)*papOV; 331 } 332 333 else do { 334 tOptionValue* pOV = *(papOV++); 335 if (strcmp( pOV->pzName, pzValName ) == 0) { 336 pRes = pOV; 337 break; 338 } 339 } while (--ct > 0); 340 } 341 if (pRes == NULL) 342 errno = ENOENT; 343 return pRes; 344 } 345 346 347 /*=export_func optionNextValue 348 * 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 + 352 * 353 * ret_type: const tOptionValue* 354 * ret_desc: a compound value structure 355 * 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()}". 362 * 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 373 =*/ 374 tOptionValue const * 375 optionNextValue(tOptionValue const * pOVList,tOptionValue const * pOldOV ) 376 { 377 tArgList* pAL; 378 tOptionValue* pRes = NULL; 379 int err = EINVAL; 380 381 if ((pOVList == NULL) || (pOVList->valType != OPARG_TYPE_HIERARCHY)) { 382 errno = EINVAL; 383 return NULL; 384 } 385 pAL = pOVList->v.nestVal; 386 { 387 int ct = pAL->useCt; 388 void** papNV = (void**)(pAL->apzArgs); 389 390 while (ct-- > 0) { 391 tOptionValue* pNV = *(papNV++); 392 if (pNV == pOldOV) { 393 if (ct == 0) { 394 err = ENOENT; 395 396 } else { 397 err = 0; 398 pRes = (tOptionValue*)*papNV; 399 } 400 break; 401 } 402 } 403 } 404 if (err != 0) 405 errno = err; 406 return pRes; 407 } 408 409 410 /* filePreset 411 * 412 * Load a file containing presetting information (a configuration file). 413 */ 414 static void 415 filePreset( 416 tOptions* pOpts, 417 char const* pzFileName, 418 int direction ) 419 { 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 ); 424 425 if (TEXT_MMAP_FAILED_ADDR(pzFileText)) 426 return; 427 428 if (direction == DIRECTION_CALLED) { 429 optst.flags = OPTST_DEFINED; 430 direction = DIRECTION_PROCESS; 431 } 432 433 /* 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. 438 */ 439 if ((pOpts->fOptSet & OPTPROC_PRESETTING) == 0) 440 optst.flags = OPTST_SET; 441 442 do { 443 while (IS_WHITESPACE_CHAR(*pzFileText)) pzFileText++; 444 445 if (IS_VAR_FIRST_CHAR(*pzFileText)) { 446 pzFileText = handleConfig(pOpts, &optst, pzFileText, direction); 447 448 } else switch (*pzFileText) { 449 case '<': 450 if (IS_VAR_FIRST_CHAR(pzFileText[1])) 451 pzFileText = 452 handleStructure(pOpts, &optst, pzFileText, direction); 453 454 else switch (pzFileText[1]) { 455 case '?': 456 pzFileText = handleDirective( pOpts, pzFileText ); 457 break; 458 459 case '!': 460 pzFileText = handleComment( pzFileText ); 461 break; 462 463 case '/': 464 pzFileText = strchr( pzFileText+2, '>' ); 465 if (pzFileText++ != NULL) 466 break; 467 468 default: 469 goto all_done; 470 } 471 break; 472 473 case '[': 474 pzFileText = handleProgramSection( pOpts, pzFileText ); 475 break; 476 477 case '#': 478 pzFileText = strchr( pzFileText+1, '\n' ); 479 break; 480 481 default: 482 goto all_done; /* invalid format */ 483 } 484 } while (pzFileText != NULL); 485 486 all_done: 487 text_munmap( &cfgfile ); 488 } 489 490 491 /* handleComment 492 * 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 "-->". 496 */ 497 static char* 498 handleComment( char* pzText ) 499 { 500 char* pz = strstr( pzText, "-->" ); 501 if (pz != NULL) 502 pz += 3; 503 return pz; 504 } 505 506 507 /* handleConfig 508 * 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. 513 */ 514 static char* 515 handleConfig( 516 tOptions* pOpts, 517 tOptState* pOS, 518 char* pzText, 519 int direction ) 520 { 521 char* pzName = pzText++; 522 char* pzEnd = strchr( pzText, '\n' ); 523 524 if (pzEnd == NULL) 525 return pzText + strlen(pzText); 526 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; 534 } 535 536 /* 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. 540 */ 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; 547 548 /* 549 * IF the value is continued, remove the backslash escape and push "pzEnd" 550 * on to a newline *not* preceded by a backslash. 551 */ 552 if (pzEnd[-1] == '\\') { 553 char* pcD = pzEnd-1; 554 char* pcS = pzEnd; 555 556 for (;;) { 557 char ch = *(pcS++); 558 switch (ch) { 559 case NUL: 560 pcS = NULL; 561 562 case '\n': 563 *pcD = NUL; 564 pzEnd = pcS; 565 goto copy_done; 566 567 case '\\': 568 if (*pcS == '\n') { 569 ch = *(pcS++); 570 } 571 /* FALLTHROUGH */ 572 default: 573 *(pcD++) = ch; 574 } 575 } copy_done:; 576 577 } else { 578 /* 579 * The newline was not preceded by a backslash. NUL it out 580 */ 581 *(pzEnd++) = NUL; 582 } 583 584 /* 585 * "pzName" points to what looks like text for one option/configurable. 586 * It is NUL terminated. Process it. 587 */ 588 loadOptionLine( pOpts, pOS, pzName, direction, OPTION_LOAD_UNCOOKED ); 589 590 return pzEnd; 591 } 592 593 594 /* handleDirective 595 * 596 * "pzText" points to a "<?" sequence. 597 * For the moment, we only handle "<?program" directives. 598 */ 599 static char* 600 handleDirective( 601 tOptions* pOpts, 602 char* pzText ) 603 { 604 char ztitle[32] = "<?"; 605 size_t title_len = strlen( zProg ); 606 size_t name_len; 607 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; 614 } 615 616 name_len = strlen( pOpts->pzProgName ); 617 strcpy( ztitle+2, zProg ); 618 title_len += 2; 619 620 do { 621 pzText += title_len; 622 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; 629 } 630 } 631 632 pzText = strstr( pzText, ztitle ); 633 } while (pzText != NULL); 634 635 return pzText; 636 } 637 638 639 /* handleProgramSection 640 * 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. 644 */ 645 static char* 646 handleProgramSection( 647 tOptions* pOpts, 648 char* pzText ) 649 { 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' ); 654 655 if (len > 16) 656 return NULL; 657 658 { 659 char z[24]; 660 sprintf( z, "[%s]", pOpts->pzPROGNAME ); 661 pzText = strstr( pzText, z ); 662 } 663 664 if (pzText != NULL) 665 pzText = strchr( pzText, '\n' ); 666 return pzText; 667 } 668 669 670 /* handleStructure 671 * 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. 675 */ 676 static char* 677 handleStructure( 678 tOptions* pOpts, 679 tOptState* pOS, 680 char* pzText, 681 int direction ) 682 { 683 tOptionLoadMode mode = option_load_mode; 684 tOptionValue valu; 685 686 char* pzName = ++pzText; 687 char* pzData; 688 char* pcNulPoint; 689 690 while (IS_VALUE_NAME_CHAR(*pzText)) pzText++; 691 pcNulPoint = pzText; 692 valu.valType = OPARG_TYPE_STRING; 693 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 */ 703 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; 711 712 case '>': 713 break; 714 715 default: 716 pzText = strchr( pzText, '>'); 717 if (pzText != NULL) 718 pzText++; 719 return pzText; 720 } 721 722 /* 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. 725 */ 726 *pcNulPoint = NUL; 727 pzData = ++pzText; 728 729 /* 730 * Find the end of the option text and NUL terminate it 731 */ 732 { 733 char z[64], *pz = z; 734 size_t len = strlen(pzName) + 4; 735 if (len > sizeof(z)) 736 pz = AGALOC(len, "scan name"); 737 738 sprintf( pz, "</%s>", pzName ); 739 *pzText = ' '; 740 pzText = strstr( pzText, pz ); 741 if (pz != z) AGFREE(pz); 742 743 if (pzText == NULL) 744 return pzText; 745 746 *pzText = NUL; 747 748 pzText += len-1; 749 } 750 751 /* 752 * Rejoin the name and value for parsing by "loadOptionLine()". 753 * Erase any attributes parsed by "parseAttributes()". 754 */ 755 memset(pcNulPoint, ' ', pzData - pcNulPoint); 756 757 /* 758 * If we are getting a "string" value, the process the XML-ish 759 * %XX hex characters. 760 */ 761 if (valu.valType == OPARG_TYPE_STRING) { 762 char * pzSrc = pzData; 763 char * pzDst = pzData; 764 char bf[4]; 765 bf[2] = NUL; 766 767 for (;;) { 768 int ch = ((int)*(pzSrc++)) & 0xFF; 769 switch (ch) { 770 case NUL: goto string_fixup_done; 771 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 */ 779 780 default: 781 *(pzDst++) = ch; 782 } 783 } string_fixup_done:; 784 *pzDst = NUL; 785 } 786 787 /* 788 * "pzName" points to what looks like text for one option/configurable. 789 * It is NUL terminated. Process it. 790 */ 791 loadOptionLine( pOpts, pOS, pzName, direction, mode ); 792 793 return pzText; 794 } 795 796 797 /* internalFileLoad 798 * 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) 802 */ 803 LOCAL void 804 internalFileLoad( tOptions* pOpts ) 805 { 806 uint32_t svfl; 807 int idx; 808 int inc; 809 char zFileName[ AG_PATH_MAX+1 ]; 810 811 if (pOpts->papzHomeList == NULL) 812 return; 813 814 svfl = pOpts->fOptSet; 815 inc = DIRECTION_PRESET; 816 817 /* 818 * Never stop on errors in config files. 819 */ 820 pOpts->fOptSet &= ~OPTPROC_ERRSTOP; 821 822 /* 823 * Find the last RC entry (highest priority entry) 824 */ 825 for (idx = 0; pOpts->papzHomeList[ idx+1 ] != NULL; ++idx) ; 826 827 /* 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. 832 */ 833 for (;;) { 834 struct stat StatBuf; 835 cch_t* pzPath; 836 837 /* 838 * IF we've reached the bottom end, change direction 839 */ 840 if (idx < 0) { 841 inc = DIRECTION_PROCESS; 842 idx = 0; 843 } 844 845 pzPath = pOpts->papzHomeList[ idx ]; 846 847 /* 848 * IF we've reached the top end, bail out 849 */ 850 if (pzPath == NULL) 851 break; 852 853 idx += inc; 854 855 if (! optionMakePath( zFileName, (int)sizeof(zFileName), 856 pzPath, pOpts->pzProgPath )) 857 continue; 858 859 /* 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 863 */ 864 if (stat( zFileName, &StatBuf ) != 0) 865 continue; /* bogus name - skip the home list entry */ 866 867 if (S_ISDIR( StatBuf.st_mode )) { 868 size_t len = strlen( zFileName ); 869 char* pz; 870 871 if (len + 1 + strlen( pOpts->pzRcName ) >= sizeof( zFileName )) 872 continue; 873 874 pz = zFileName + len; 875 if (pz[-1] != DIRCH) 876 *(pz++) = DIRCH; 877 strcpy( pz, pOpts->pzRcName ); 878 } 879 880 filePreset( pOpts, zFileName, inc ); 881 882 /* 883 * IF we are now to skip config files AND we are presetting, 884 * THEN change direction. We must go the other way. 885 */ 886 { 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; 891 } 892 } 893 } /* twice for every path in the home list, ... */ 894 895 pOpts->fOptSet = svfl; 896 } 897 898 899 /*=export_func optionFileLoad 900 * 901 * what: Load the locatable config files, in order 902 * 903 * arg: + tOptions* + pOpts + program options descriptor + 904 * arg: + char const* + pzProg + program name + 905 * 906 * ret_type: int 907 * ret_desc: 0 -> SUCCESS, -1 -> FAILURE 908 * 909 * doc: 910 * 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. 918 * 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. 922 * 923 * See the AutoOpts documentation for a thorough discussion of the 924 * config file format. 925 * 926 * Configuration files not found or not decipherable are simply ignored. 927 * 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. 931 =*/ 932 int 933 optionFileLoad( tOptions* pOpts, char const* pzProgram ) 934 { 935 if (! SUCCESSFUL( validateOptionsStruct( pOpts, pzProgram ))) 936 return -1; 937 938 pOpts->pzProgName = pzProgram; 939 internalFileLoad( pOpts ); 940 return 0; 941 } 942 943 944 /*=export_func optionLoadOpt 945 * private: 946 * 947 * what: Load an option rc/ini file 948 * arg: + tOptions* + pOpts + program options descriptor + 949 * arg: + tOptDesc* + pOptDesc + the descriptor for this arg + 950 * 951 * doc: 952 * Processes the options found in the file named with 953 * pOptDesc->optArg.argString. 954 =*/ 955 void 956 optionLoadOpt( tOptions* pOpts, tOptDesc* pOptDesc ) 957 { 958 struct stat sb; 959 960 /* 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.) 965 */ 966 if ( DISABLED_OPT(pOptDesc) 967 || ((pOptDesc->fOptState & OPTST_RESET) != 0)) 968 return; 969 970 if (stat( pOptDesc->optArg.argString, &sb ) != 0) { 971 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0) 972 return; 973 974 fprintf( stderr, zFSErrOptLoad, errno, strerror( errno ), 975 pOptDesc->optArg.argString ); 976 exit(EX_NOINPUT); 977 /* NOT REACHED */ 978 } 979 980 if (! S_ISREG( sb.st_mode )) { 981 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0) 982 return; 983 984 fprintf( stderr, zNotFile, pOptDesc->optArg.argString ); 985 exit(EX_NOINPUT); 986 /* NOT REACHED */ 987 } 988 989 filePreset(pOpts, pOptDesc->optArg.argString, DIRECTION_CALLED); 990 } 991 992 993 /* parseAttributes 994 * 995 * Parse the various attributes of an XML-styled config file entry 996 */ 997 LOCAL char* 998 parseAttributes( 999 tOptions* pOpts, 1000 char* pzText, 1001 tOptionLoadMode* pMode, 1002 tOptionValue* pType ) 1003 { 1004 size_t len; 1005 1006 do { 1007 if (! IS_WHITESPACE_CHAR(*pzText)) 1008 switch (*pzText) { 1009 case '/': pType->valType = OPARG_TYPE_NONE; 1010 case '>': return pzText; 1011 1012 default: 1013 case NUL: return NULL; 1014 } 1015 1016 while (IS_WHITESPACE_CHAR(*++pzText)) ; 1017 len = 0; 1018 while (IS_LOWER_CASE_CHAR(pzText[len])) len++; 1019 1020 switch (find_xat_attribute_id(pzText, len)) { 1021 case XAT_KWD_TYPE: 1022 pzText = parseValueType( pzText+len, pType ); 1023 break; 1024 1025 case XAT_KWD_WORDS: 1026 pzText = parseKeyWordType( pOpts, pzText+len, pType ); 1027 break; 1028 1029 case XAT_KWD_MEMBERS: 1030 pzText = parseSetMemType( pOpts, pzText+len, pType ); 1031 break; 1032 1033 case XAT_KWD_COOKED: 1034 pzText += len; 1035 if (! IS_END_XML_TOKEN_CHAR(*pzText)) 1036 goto invalid_kwd; 1037 1038 *pMode = OPTION_LOAD_COOKED; 1039 break; 1040 1041 case XAT_KWD_UNCOOKED: 1042 pzText += len; 1043 if (! IS_END_XML_TOKEN_CHAR(*pzText)) 1044 goto invalid_kwd; 1045 1046 *pMode = OPTION_LOAD_UNCOOKED; 1047 break; 1048 1049 case XAT_KWD_KEEP: 1050 pzText += len; 1051 if (! IS_END_XML_TOKEN_CHAR(*pzText)) 1052 goto invalid_kwd; 1053 1054 *pMode = OPTION_LOAD_KEEP; 1055 break; 1056 1057 default: 1058 case XAT_KWD_INVALID: 1059 invalid_kwd: 1060 pType->valType = OPARG_TYPE_NONE; 1061 return skipUnknown( pzText ); 1062 } 1063 } while (pzText != NULL); 1064 1065 return pzText; 1066 } 1067 1068 1069 /* parseKeyWordType 1070 * 1071 * "pzText" points to the character after "words=". 1072 * What should follow is a name of a keyword (enumeration) list. 1073 */ 1074 static char* 1075 parseKeyWordType( 1076 tOptions* pOpts, 1077 char* pzText, 1078 tOptionValue* pType ) 1079 { 1080 return skipUnknown( pzText ); 1081 } 1082 1083 1084 /* parseSetMemType 1085 * 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. 1089 */ 1090 static char* 1091 parseSetMemType( 1092 tOptions* pOpts, 1093 char* pzText, 1094 tOptionValue* pType ) 1095 { 1096 return skipUnknown( pzText ); 1097 } 1098 1099 1100 /* parseValueType 1101 * 1102 * "pzText" points to the character after "type=" 1103 */ 1104 static char* 1105 parseValueType( 1106 char* pzText, 1107 tOptionValue* pType ) 1108 { 1109 size_t len = 0; 1110 1111 if (*(pzText++) != '=') 1112 goto woops; 1113 1114 while (IS_OPTION_NAME_CHAR(pzText[len])) len++; 1115 pzText += len; 1116 1117 if ((len == 0) || (! IS_END_XML_TOKEN_CHAR(*pzText))) { 1118 woops: 1119 pType->valType = OPARG_TYPE_NONE; 1120 return skipUnknown( pzText ); 1121 } 1122 1123 switch (find_value_type_id(pzText - len, len)) { 1124 default: 1125 case VTP_KWD_INVALID: goto woops; 1126 1127 case VTP_KWD_STRING: 1128 pType->valType = OPARG_TYPE_STRING; 1129 break; 1130 1131 case VTP_KWD_INTEGER: 1132 pType->valType = OPARG_TYPE_NUMERIC; 1133 break; 1134 1135 case VTP_KWD_BOOL: 1136 case VTP_KWD_BOOLEAN: 1137 pType->valType = OPARG_TYPE_BOOLEAN; 1138 break; 1139 1140 case VTP_KWD_KEYWORD: 1141 pType->valType = OPARG_TYPE_ENUMERATION; 1142 break; 1143 1144 case VTP_KWD_SET: 1145 case VTP_KWD_SET_MEMBERSHIP: 1146 pType->valType = OPARG_TYPE_MEMBERSHIP; 1147 break; 1148 1149 case VTP_KWD_NESTED: 1150 case VTP_KWD_HIERARCHY: 1151 pType->valType = OPARG_TYPE_HIERARCHY; 1152 } 1153 1154 return pzText; 1155 } 1156 1157 1158 /* skipUnknown 1159 * 1160 * Skip over some unknown attribute 1161 */ 1162 static char* 1163 skipUnknown( char* pzText ) 1164 { 1165 for (;; pzText++) { 1166 if (IS_END_XML_TOKEN_CHAR(*pzText)) return pzText; 1167 if (*pzText == NUL) return NULL; 1168 } 1169 } 1170 1171 1172 /* validateOptionsStruct 1173 * 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.) 1179 */ 1180 LOCAL tSuccess 1181 validateOptionsStruct( tOptions* pOpts, char const* pzProgram ) 1182 { 1183 if (pOpts == NULL) { 1184 fputs( zAO_Bad, stderr ); 1185 exit( EX_CONFIG ); 1186 } 1187 1188 /* 1189 * IF the client has enabled translation and the translation procedure 1190 * is available, then go do it. 1191 */ 1192 if ( ((pOpts->fOptSet & OPTPROC_TRANSLATE) != 0) 1193 && (pOpts->pTransProc != NULL) ) { 1194 /* 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.) 1198 */ 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; 1203 } 1204 1205 /* 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 1209 */ 1210 if ( ( pOpts->structVersion != OPTIONS_STRUCT_VERSION ) 1211 && ( (pOpts->structVersion > OPTIONS_STRUCT_VERSION ) 1212 || (pOpts->structVersion < OPTIONS_MINIMUM_VERSION ) 1213 ) ) { 1214 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 ); 1220 1221 return FAILURE; 1222 } 1223 1224 /* 1225 * If the program name hasn't been set, then set the name and the path 1226 * and the set of equivalent characters. 1227 */ 1228 if (pOpts->pzProgName == NULL) { 1229 char const* pz = strrchr( pzProgram, DIRCH ); 1230 1231 if (pz == NULL) 1232 pOpts->pzProgName = pzProgram; 1233 else pOpts->pzProgName = pz+1; 1234 1235 pOpts->pzProgPath = pzProgram; 1236 1237 /* 1238 * when comparing long names, these are equivalent 1239 */ 1240 strequate( zSepChars ); 1241 } 1242 1243 return SUCCESS; 1244 } 1245 1246 1247 /** 1248 * Local Variables: 1249 * mode: C 1250 * c-file-style: "stroustrup" 1251 * indent-tabs-mode: nil 1252 * End: 1253 * end of autoopts/configfile.c */ 1254