1285612Sdelphij/** 2285612Sdelphij * \file configfile.c 3181834Sroberto * 4181834Sroberto * configuration/rc/ini file handling. 5285612Sdelphij * 6285612Sdelphij * @addtogroup autoopts 7285612Sdelphij * @{ 8181834Sroberto */ 9181834Sroberto/* 10285612Sdelphij * This file is part of AutoOpts, a companion to AutoGen. 11285612Sdelphij * AutoOpts is free software. 12285612Sdelphij * AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved 13181834Sroberto * 14285612Sdelphij * AutoOpts is available under any one of two licenses. The license 15285612Sdelphij * in use must be one of these two and the choice is under the control 16285612Sdelphij * of the user of the license. 17181834Sroberto * 18285612Sdelphij * The GNU Lesser General Public License, version 3 or later 19285612Sdelphij * See the files "COPYING.lgplv3" and "COPYING.gplv3" 20181834Sroberto * 21285612Sdelphij * The Modified Berkeley Software Distribution License 22285612Sdelphij * See the file "COPYING.mbsd" 23181834Sroberto * 24285612Sdelphij * These files have the following sha256 sums: 25181834Sroberto * 26285612Sdelphij * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 27285612Sdelphij * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 28285612Sdelphij * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 29181834Sroberto */ 30181834Sroberto 31181834Sroberto/* = = = START-STATIC-FORWARD = = = */ 32181834Srobertostatic void 33285612Sdelphijfile_preset(tOptions * opts, char const * fname, int dir); 34181834Sroberto 35285612Sdelphijstatic char * 36285612Sdelphijhandle_comment(char * txt); 37181834Sroberto 38285612Sdelphijstatic char * 39285612Sdelphijhandle_cfg(tOptions * opts, tOptState * ost, char * txt, int dir); 40181834Sroberto 41285612Sdelphijstatic char * 42285612Sdelphijhandle_directive(tOptions * opts, char * txt); 43181834Sroberto 44285612Sdelphijstatic char * 45285612Sdelphijaoflags_directive(tOptions * opts, char * txt); 46181834Sroberto 47285612Sdelphijstatic char * 48285612Sdelphijprogram_directive(tOptions * opts, char * txt); 49181834Sroberto 50285612Sdelphijstatic char * 51285612Sdelphijhandle_section(tOptions * opts, char * txt); 52181834Sroberto 53285612Sdelphijstatic int 54285612Sdelphijparse_xml_encoding(char ** ppz); 55181834Sroberto 56285612Sdelphijstatic char * 57285612Sdelphijtrim_xml_text(char * intxt, char const * pznm, tOptionLoadMode mode); 58181834Sroberto 59285612Sdelphijstatic void 60285612Sdelphijcook_xml_text(char * pzData); 61181834Sroberto 62285612Sdelphijstatic char * 63285612Sdelphijhandle_struct(tOptions * opts, tOptState * ost, char * txt, int dir); 64285612Sdelphij 65285612Sdelphijstatic char const * 66285612Sdelphijparse_keyword(tOptions * opts, char const * txt, tOptionValue * typ); 67285612Sdelphij 68285612Sdelphijstatic char const * 69285612Sdelphijparse_set_mem(tOptions * opts, char const * txt, tOptionValue * typ); 70285612Sdelphij 71285612Sdelphijstatic char const * 72285612Sdelphijparse_value(char const * txt, tOptionValue * typ); 73181834Sroberto/* = = = END-STATIC-FORWARD = = = */ 74181834Sroberto 75285612Sdelphij/** 76285612Sdelphij * Skip over some unknown attribute 77285612Sdelphij * @param[in] txt start of skpped text 78285612Sdelphij * @returns character after skipped text 79285612Sdelphij */ 80285612Sdelphijinline static char const * 81285612Sdelphijskip_unkn(char const * txt) 82285612Sdelphij{ 83285612Sdelphij txt = BRK_END_XML_TOKEN_CHARS(txt); 84285612Sdelphij return (*txt == NUL) ? NULL : txt; 85285612Sdelphij} 86181834Sroberto 87181834Sroberto/*=export_func configFileLoad 88181834Sroberto * 89181834Sroberto * what: parse a configuration file 90285612Sdelphij * arg: + char const * + fname + the file to load + 91181834Sroberto * 92285612Sdelphij * ret_type: const tOptionValue * 93181834Sroberto * ret_desc: An allocated, compound value structure 94181834Sroberto * 95181834Sroberto * doc: 96181834Sroberto * This routine will load a named configuration file and parse the 97181834Sroberto * text as a hierarchically valued option. The option descriptor 98181834Sroberto * created from an option definition file is not used via this interface. 99181834Sroberto * The returned value is "named" with the input file name and is of 100181834Sroberto * type "@code{OPARG_TYPE_HIERARCHY}". It may be used in calls to 101181834Sroberto * @code{optionGetValue()}, @code{optionNextValue()} and 102181834Sroberto * @code{optionUnloadNested()}. 103181834Sroberto * 104181834Sroberto * err: 105181834Sroberto * If the file cannot be loaded or processed, @code{NULL} is returned and 106181834Sroberto * @var{errno} is set. It may be set by a call to either @code{open(2)} 107181834Sroberto * @code{mmap(2)} or other file system calls, or it may be: 108181834Sroberto * @itemize @bullet 109181834Sroberto * @item 110285612Sdelphij * @code{ENOENT} - the file was not found. 111181834Sroberto * @item 112285612Sdelphij * @code{ENOMSG} - the file was empty. 113285612Sdelphij * @item 114181834Sroberto * @code{EINVAL} - the file contents are invalid -- not properly formed. 115181834Sroberto * @item 116181834Sroberto * @code{ENOMEM} - not enough memory to allocate the needed structures. 117181834Sroberto * @end itemize 118181834Sroberto=*/ 119285612Sdelphijconst tOptionValue * 120285612SdelphijconfigFileLoad(char const * fname) 121181834Sroberto{ 122285612Sdelphij tmap_info_t cfgfile; 123285612Sdelphij tOptionValue * res = NULL; 124181834Sroberto tOptionLoadMode save_mode = option_load_mode; 125181834Sroberto 126285612Sdelphij char * txt = text_mmap(fname, PROT_READ, MAP_PRIVATE, &cfgfile); 127181834Sroberto 128285612Sdelphij if (TEXT_MMAP_FAILED_ADDR(txt)) 129181834Sroberto return NULL; /* errno is set */ 130181834Sroberto 131181834Sroberto option_load_mode = OPTION_LOAD_COOKED; 132285612Sdelphij res = optionLoadNested(txt, fname, strlen(fname)); 133181834Sroberto 134285612Sdelphij if (res == NULL) { 135181834Sroberto int err = errno; 136285612Sdelphij text_munmap(&cfgfile); 137181834Sroberto errno = err; 138181834Sroberto } else 139285612Sdelphij text_munmap(&cfgfile); 140181834Sroberto 141181834Sroberto option_load_mode = save_mode; 142285612Sdelphij return res; 143181834Sroberto} 144181834Sroberto 145181834Sroberto 146181834Sroberto/*=export_func optionFindValue 147181834Sroberto * 148181834Sroberto * what: find a hierarcicaly valued option instance 149285612Sdelphij * arg: + const tOptDesc * + odesc + an option with a nested arg type + 150285612Sdelphij * arg: + char const * + name + name of value to find + 151285612Sdelphij * arg: + char const * + val + the matching value + 152181834Sroberto * 153285612Sdelphij * ret_type: const tOptionValue * 154181834Sroberto * ret_desc: a compound value structure 155181834Sroberto * 156181834Sroberto * doc: 157181834Sroberto * This routine will find an entry in a nested value option or configurable. 158181834Sroberto * It will search through the list and return a matching entry. 159181834Sroberto * 160181834Sroberto * err: 161181834Sroberto * The returned result is NULL and errno is set: 162181834Sroberto * @itemize @bullet 163181834Sroberto * @item 164181834Sroberto * @code{EINVAL} - the @code{pOptValue} does not point to a valid 165181834Sroberto * hierarchical option value. 166181834Sroberto * @item 167181834Sroberto * @code{ENOENT} - no entry matched the given name. 168181834Sroberto * @end itemize 169181834Sroberto=*/ 170285612Sdelphijconst tOptionValue * 171285612SdelphijoptionFindValue(const tOptDesc * odesc, char const * name, char const * val) 172181834Sroberto{ 173285612Sdelphij const tOptionValue * res = NULL; 174181834Sroberto 175285612Sdelphij if ( (odesc == NULL) 176285612Sdelphij || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY)) { 177181834Sroberto errno = EINVAL; 178181834Sroberto } 179181834Sroberto 180285612Sdelphij else if (odesc->optCookie == NULL) { 181181834Sroberto errno = ENOENT; 182181834Sroberto } 183181834Sroberto 184181834Sroberto else do { 185294569Sdelphij tArgList * argl = odesc->optCookie; 186294569Sdelphij int argct = argl->useCt; 187294569Sdelphij const void ** poptv = VOIDP(argl->apzArgs); 188181834Sroberto 189285612Sdelphij if (argct == 0) { 190181834Sroberto errno = ENOENT; 191181834Sroberto break; 192181834Sroberto } 193181834Sroberto 194285612Sdelphij if (name == NULL) { 195294569Sdelphij res = (const tOptionValue *)*poptv; 196181834Sroberto break; 197181834Sroberto } 198181834Sroberto 199285612Sdelphij while (--argct >= 0) { 200285612Sdelphij const tOptionValue * ov = *(poptv++); 201285612Sdelphij const tOptionValue * rv = optionGetValue(ov, name); 202181834Sroberto 203285612Sdelphij if (rv == NULL) 204181834Sroberto continue; 205181834Sroberto 206285612Sdelphij if (val == NULL) { 207285612Sdelphij res = ov; 208181834Sroberto break; 209181834Sroberto } 210181834Sroberto } 211285612Sdelphij if (res == NULL) 212181834Sroberto errno = ENOENT; 213285612Sdelphij } while (false); 214181834Sroberto 215285612Sdelphij return res; 216181834Sroberto} 217181834Sroberto 218181834Sroberto 219181834Sroberto/*=export_func optionFindNextValue 220181834Sroberto * 221285612Sdelphij * FIXME: the handling of 'pzName' and 'pzVal' is just wrong. 222285612Sdelphij * 223181834Sroberto * what: find a hierarcicaly valued option instance 224285612Sdelphij * arg: + const tOptDesc * + odesc + an option with a nested arg type + 225285612Sdelphij * arg: + const tOptionValue * + pPrevVal + the last entry + 226285612Sdelphij * arg: + char const * + name + name of value to find + 227285612Sdelphij * arg: + char const * + value + the matching value + 228181834Sroberto * 229285612Sdelphij * ret_type: const tOptionValue * 230181834Sroberto * ret_desc: a compound value structure 231181834Sroberto * 232181834Sroberto * doc: 233181834Sroberto * This routine will find the next entry in a nested value option or 234181834Sroberto * configurable. It will search through the list and return the next entry 235181834Sroberto * that matches the criteria. 236181834Sroberto * 237181834Sroberto * err: 238181834Sroberto * The returned result is NULL and errno is set: 239181834Sroberto * @itemize @bullet 240181834Sroberto * @item 241181834Sroberto * @code{EINVAL} - the @code{pOptValue} does not point to a valid 242181834Sroberto * hierarchical option value. 243181834Sroberto * @item 244181834Sroberto * @code{ENOENT} - no entry matched the given name. 245181834Sroberto * @end itemize 246181834Sroberto=*/ 247285612SdelphijtOptionValue const * 248285612SdelphijoptionFindNextValue(const tOptDesc * odesc, const tOptionValue * pPrevVal, 249285612Sdelphij char const * pzName, char const * pzVal) 250181834Sroberto{ 251285612Sdelphij bool old_found = false; 252294569Sdelphij const tOptionValue * res = NULL; 253181834Sroberto 254285612Sdelphij (void)pzName; 255285612Sdelphij (void)pzVal; 256285612Sdelphij 257285612Sdelphij if ( (odesc == NULL) 258285612Sdelphij || (OPTST_GET_ARGTYPE(odesc->fOptState) != OPARG_TYPE_HIERARCHY)) { 259181834Sroberto errno = EINVAL; 260181834Sroberto } 261181834Sroberto 262285612Sdelphij else if (odesc->optCookie == NULL) { 263181834Sroberto errno = ENOENT; 264181834Sroberto } 265181834Sroberto 266181834Sroberto else do { 267294569Sdelphij tArgList * argl = odesc->optCookie; 268294569Sdelphij int ct = argl->useCt; 269294569Sdelphij const void ** poptv = VOIDP(argl->apzArgs); 270181834Sroberto 271181834Sroberto while (--ct >= 0) { 272294569Sdelphij const tOptionValue * pOV = *(poptv++); 273285612Sdelphij if (old_found) { 274285612Sdelphij res = pOV; 275181834Sroberto break; 276181834Sroberto } 277181834Sroberto if (pOV == pPrevVal) 278285612Sdelphij old_found = true; 279181834Sroberto } 280285612Sdelphij if (res == NULL) 281181834Sroberto errno = ENOENT; 282285612Sdelphij } while (false); 283181834Sroberto 284285612Sdelphij return res; 285181834Sroberto} 286181834Sroberto 287181834Sroberto 288181834Sroberto/*=export_func optionGetValue 289181834Sroberto * 290181834Sroberto * what: get a specific value from a hierarcical list 291285612Sdelphij * arg: + const tOptionValue * + pOptValue + a hierarchcal value + 292285612Sdelphij * arg: + char const * + valueName + name of value to get + 293181834Sroberto * 294285612Sdelphij * ret_type: const tOptionValue * 295181834Sroberto * ret_desc: a compound value structure 296181834Sroberto * 297181834Sroberto * doc: 298181834Sroberto * This routine will find an entry in a nested value option or configurable. 299181834Sroberto * If "valueName" is NULL, then the first entry is returned. Otherwise, 300181834Sroberto * the first entry with a name that exactly matches the argument will be 301285612Sdelphij * returned. If there is no matching value, NULL is returned and errno is 302285612Sdelphij * set to ENOENT. If the provided option value is not a hierarchical value, 303285612Sdelphij * NULL is also returned and errno is set to EINVAL. 304181834Sroberto * 305181834Sroberto * err: 306181834Sroberto * The returned result is NULL and errno is set: 307181834Sroberto * @itemize @bullet 308181834Sroberto * @item 309181834Sroberto * @code{EINVAL} - the @code{pOptValue} does not point to a valid 310181834Sroberto * hierarchical option value. 311181834Sroberto * @item 312181834Sroberto * @code{ENOENT} - no entry matched the given name. 313181834Sroberto * @end itemize 314181834Sroberto=*/ 315285612SdelphijtOptionValue const * 316285612SdelphijoptionGetValue(tOptionValue const * oov, char const * vname) 317181834Sroberto{ 318294569Sdelphij tArgList * arg_list; 319294569Sdelphij const tOptionValue * res = NULL; 320181834Sroberto 321285612Sdelphij if ((oov == NULL) || (oov->valType != OPARG_TYPE_HIERARCHY)) { 322181834Sroberto errno = EINVAL; 323285612Sdelphij return res; 324181834Sroberto } 325285612Sdelphij arg_list = oov->v.nestVal; 326181834Sroberto 327285612Sdelphij if (arg_list->useCt > 0) { 328294569Sdelphij int ct = arg_list->useCt; 329294569Sdelphij const void ** ovlist = VOIDP(arg_list->apzArgs); 330181834Sroberto 331285612Sdelphij if (vname == NULL) { 332294569Sdelphij res = (const tOptionValue *)*ovlist; 333181834Sroberto 334285612Sdelphij } else do { 335294569Sdelphij const tOptionValue * opt_val = *(ovlist++); 336285612Sdelphij if (strcmp(opt_val->pzName, vname) == 0) { 337285612Sdelphij res = opt_val; 338181834Sroberto break; 339181834Sroberto } 340181834Sroberto } while (--ct > 0); 341181834Sroberto } 342285612Sdelphij if (res == NULL) 343181834Sroberto errno = ENOENT; 344285612Sdelphij return res; 345181834Sroberto} 346181834Sroberto 347181834Sroberto/*=export_func optionNextValue 348181834Sroberto * 349181834Sroberto * what: get the next value from a hierarchical list 350285612Sdelphij * arg: + const tOptionValue * + pOptValue + a hierarchcal list value + 351285612Sdelphij * arg: + const tOptionValue * + pOldValue + a value from this list + 352181834Sroberto * 353285612Sdelphij * ret_type: const tOptionValue * 354181834Sroberto * ret_desc: a compound value structure 355181834Sroberto * 356181834Sroberto * doc: 357181834Sroberto * This routine will return the next entry after the entry passed in. At the 358181834Sroberto * end of the list, NULL will be returned. If the entry is not found on the 359181834Sroberto * list, NULL will be returned and "@var{errno}" will be set to EINVAL. 360181834Sroberto * The "@var{pOldValue}" must have been gotten from a prior call to this 361181834Sroberto * routine or to "@code{opitonGetValue()}". 362181834Sroberto * 363181834Sroberto * err: 364181834Sroberto * The returned result is NULL and errno is set: 365181834Sroberto * @itemize @bullet 366181834Sroberto * @item 367181834Sroberto * @code{EINVAL} - the @code{pOptValue} does not point to a valid 368181834Sroberto * hierarchical option value or @code{pOldValue} does not point to a 369181834Sroberto * member of that option value. 370181834Sroberto * @item 371181834Sroberto * @code{ENOENT} - the supplied @code{pOldValue} pointed to the last entry. 372181834Sroberto * @end itemize 373181834Sroberto=*/ 374181834SrobertotOptionValue const * 375285612SdelphijoptionNextValue(tOptionValue const * ov_list,tOptionValue const * oov ) 376181834Sroberto{ 377294569Sdelphij tArgList * arg_list; 378294569Sdelphij const tOptionValue * res = NULL; 379294569Sdelphij int err = EINVAL; 380181834Sroberto 381285612Sdelphij if ((ov_list == NULL) || (ov_list->valType != OPARG_TYPE_HIERARCHY)) { 382181834Sroberto errno = EINVAL; 383181834Sroberto return NULL; 384181834Sroberto } 385285612Sdelphij arg_list = ov_list->v.nestVal; 386181834Sroberto { 387294569Sdelphij int ct = arg_list->useCt; 388294569Sdelphij const void ** o_list = VOIDP(arg_list->apzArgs); 389181834Sroberto 390181834Sroberto while (ct-- > 0) { 391294569Sdelphij const tOptionValue * nov = *(o_list++); 392285612Sdelphij if (nov == oov) { 393181834Sroberto if (ct == 0) { 394181834Sroberto err = ENOENT; 395181834Sroberto 396181834Sroberto } else { 397285612Sdelphij err = 0; 398294569Sdelphij res = (const tOptionValue *)*o_list; 399181834Sroberto } 400181834Sroberto break; 401181834Sroberto } 402181834Sroberto } 403181834Sroberto } 404181834Sroberto if (err != 0) 405181834Sroberto errno = err; 406285612Sdelphij return res; 407181834Sroberto} 408181834Sroberto 409285612Sdelphij/** 410181834Sroberto * Load a file containing presetting information (a configuration file). 411181834Sroberto */ 412181834Srobertostatic void 413285612Sdelphijfile_preset(tOptions * opts, char const * fname, int dir) 414181834Sroberto{ 415285612Sdelphij tmap_info_t cfgfile; 416285612Sdelphij tOptState optst = OPTSTATE_INITIALIZER(PRESET); 417285612Sdelphij opt_state_mask_t st_flags = optst.flags; 418285612Sdelphij opt_state_mask_t fl_save = opts->fOptSet; 419285612Sdelphij char * ftext = 420285612Sdelphij text_mmap(fname, PROT_READ|PROT_WRITE, MAP_PRIVATE, &cfgfile); 421181834Sroberto 422285612Sdelphij if (TEXT_MMAP_FAILED_ADDR(ftext)) 423181834Sroberto return; 424181834Sroberto 425285612Sdelphij /* 426285612Sdelphij * While processing config files, we ignore errors. 427285612Sdelphij */ 428285612Sdelphij opts->fOptSet &= ~OPTPROC_ERRSTOP; 429285612Sdelphij 430285612Sdelphij if (dir == DIRECTION_CALLED) { 431285612Sdelphij st_flags = OPTST_DEFINED; 432285612Sdelphij dir = DIRECTION_PROCESS; 433181834Sroberto } 434181834Sroberto 435181834Sroberto /* 436181834Sroberto * IF this is called via "optionProcess", then we are presetting. 437181834Sroberto * This is the default and the PRESETTING bit will be set. 438181834Sroberto * If this is called via "optionFileLoad", then the bit is not set 439181834Sroberto * and we consider stuff set herein to be "set" by the client program. 440181834Sroberto */ 441285612Sdelphij if ((opts->fOptSet & OPTPROC_PRESETTING) == 0) 442285612Sdelphij st_flags = OPTST_SET; 443181834Sroberto 444181834Sroberto do { 445285612Sdelphij optst.flags = st_flags; 446285612Sdelphij ftext = SPN_WHITESPACE_CHARS(ftext); 447181834Sroberto 448285612Sdelphij if (IS_VAR_FIRST_CHAR(*ftext)) { 449285612Sdelphij ftext = handle_cfg(opts, &optst, ftext, dir); 450181834Sroberto 451285612Sdelphij } else switch (*ftext) { 452181834Sroberto case '<': 453285612Sdelphij if (IS_VAR_FIRST_CHAR(ftext[1])) 454285612Sdelphij ftext = handle_struct(opts, &optst, ftext, dir); 455181834Sroberto 456285612Sdelphij else switch (ftext[1]) { 457181834Sroberto case '?': 458285612Sdelphij ftext = handle_directive(opts, ftext); 459181834Sroberto break; 460181834Sroberto 461181834Sroberto case '!': 462285612Sdelphij ftext = handle_comment(ftext); 463181834Sroberto break; 464181834Sroberto 465181834Sroberto case '/': 466285612Sdelphij ftext = strchr(ftext + 2, '>'); 467285612Sdelphij if (ftext++ != NULL) 468181834Sroberto break; 469181834Sroberto 470181834Sroberto default: 471285612Sdelphij ftext = NULL; 472285612Sdelphij } 473285612Sdelphij if (ftext == NULL) 474181834Sroberto goto all_done; 475181834Sroberto break; 476181834Sroberto 477181834Sroberto case '[': 478285612Sdelphij ftext = handle_section(opts, ftext); 479181834Sroberto break; 480181834Sroberto 481181834Sroberto case '#': 482285612Sdelphij ftext = strchr(ftext + 1, NL); 483181834Sroberto break; 484181834Sroberto 485181834Sroberto default: 486181834Sroberto goto all_done; /* invalid format */ 487181834Sroberto } 488285612Sdelphij } while (ftext != NULL); 489181834Sroberto 490181834Sroberto all_done: 491285612Sdelphij text_munmap(&cfgfile); 492285612Sdelphij opts->fOptSet = fl_save; 493181834Sroberto} 494181834Sroberto 495285612Sdelphij/** 496285612Sdelphij * "txt" points to a "<!" sequence. 497181834Sroberto * Theoretically, we should ensure that it begins with "<!--", 498181834Sroberto * but actually I don't care that much. It ends with "-->". 499181834Sroberto */ 500285612Sdelphijstatic char * 501285612Sdelphijhandle_comment(char * txt) 502181834Sroberto{ 503285612Sdelphij char * pz = strstr(txt, "-->"); 504181834Sroberto if (pz != NULL) 505181834Sroberto pz += 3; 506181834Sroberto return pz; 507181834Sroberto} 508181834Sroberto 509285612Sdelphij/** 510285612Sdelphij * "txt" points to the start of some value name. 511181834Sroberto * The end of the entry is the end of the line that is not preceded by 512181834Sroberto * a backslash escape character. The string value is always processed 513181834Sroberto * in "cooked" mode. 514181834Sroberto */ 515285612Sdelphijstatic char * 516285612Sdelphijhandle_cfg(tOptions * opts, tOptState * ost, char * txt, int dir) 517181834Sroberto{ 518285612Sdelphij char * pzName = txt++; 519285612Sdelphij char * pzEnd = strchr(txt, NL); 520181834Sroberto 521181834Sroberto if (pzEnd == NULL) 522285612Sdelphij return txt + strlen(txt); 523181834Sroberto 524285612Sdelphij txt = SPN_VALUE_NAME_CHARS(txt); 525285612Sdelphij txt = SPN_WHITESPACE_CHARS(txt); 526285612Sdelphij if (txt > pzEnd) { 527181834Sroberto name_only: 528181834Sroberto *pzEnd++ = NUL; 529285612Sdelphij load_opt_line(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED); 530181834Sroberto return pzEnd; 531181834Sroberto } 532181834Sroberto 533181834Sroberto /* 534181834Sroberto * Either the first character after the name is a ':' or '=', 535181834Sroberto * or else we must have skipped over white space. Anything else 536181834Sroberto * is an invalid format and we give up parsing the text. 537181834Sroberto */ 538285612Sdelphij if ((*txt == '=') || (*txt == ':')) { 539285612Sdelphij txt = SPN_WHITESPACE_CHARS(txt+1); 540285612Sdelphij if (txt > pzEnd) 541181834Sroberto goto name_only; 542285612Sdelphij } else if (! IS_WHITESPACE_CHAR(txt[-1])) 543181834Sroberto return NULL; 544181834Sroberto 545181834Sroberto /* 546181834Sroberto * IF the value is continued, remove the backslash escape and push "pzEnd" 547181834Sroberto * on to a newline *not* preceded by a backslash. 548181834Sroberto */ 549181834Sroberto if (pzEnd[-1] == '\\') { 550285612Sdelphij char * pcD = pzEnd-1; 551285612Sdelphij char * pcS = pzEnd; 552181834Sroberto 553181834Sroberto for (;;) { 554181834Sroberto char ch = *(pcS++); 555181834Sroberto switch (ch) { 556181834Sroberto case NUL: 557181834Sroberto pcS = NULL; 558285612Sdelphij /* FALLTHROUGH */ 559181834Sroberto 560285612Sdelphij case NL: 561181834Sroberto *pcD = NUL; 562181834Sroberto pzEnd = pcS; 563181834Sroberto goto copy_done; 564181834Sroberto 565181834Sroberto case '\\': 566285612Sdelphij if (*pcS == NL) 567181834Sroberto ch = *(pcS++); 568181834Sroberto /* FALLTHROUGH */ 569181834Sroberto default: 570181834Sroberto *(pcD++) = ch; 571181834Sroberto } 572181834Sroberto } copy_done:; 573181834Sroberto 574181834Sroberto } else { 575181834Sroberto /* 576181834Sroberto * The newline was not preceded by a backslash. NUL it out 577181834Sroberto */ 578181834Sroberto *(pzEnd++) = NUL; 579181834Sroberto } 580181834Sroberto 581181834Sroberto /* 582181834Sroberto * "pzName" points to what looks like text for one option/configurable. 583181834Sroberto * It is NUL terminated. Process it. 584181834Sroberto */ 585285612Sdelphij load_opt_line(opts, ost, pzName, dir, OPTION_LOAD_UNCOOKED); 586181834Sroberto 587181834Sroberto return pzEnd; 588181834Sroberto} 589181834Sroberto 590285612Sdelphij/** 591285612Sdelphij * "txt" points to a "<?" sequence. 592285612Sdelphij * We handle "<?program" and "<?auto-options" directives. 593285612Sdelphij * All others are treated as comments. 594285612Sdelphij * 595285612Sdelphij * @param[in,out] opts program option descriptor 596285612Sdelphij * @param[in] txt scanning pointer 597285612Sdelphij * @returns the next character to look at 598285612Sdelphij */ 599285612Sdelphijstatic char * 600285612Sdelphijhandle_directive(tOptions * opts, char * txt) 601285612Sdelphij{ 602285612Sdelphij# define DIRECTIVE_TABLE \ 603285612Sdelphij _dt_(zCfgProg, program_directive) \ 604285612Sdelphij _dt_(zCfgAO_Flags, aoflags_directive) 605181834Sroberto 606285612Sdelphij typedef char * (directive_func_t)(tOptions *, char *); 607285612Sdelphij# define _dt_(_s, _fn) _fn, 608285612Sdelphij static directive_func_t * dir_disp[] = { 609285612Sdelphij DIRECTIVE_TABLE 610285612Sdelphij }; 611285612Sdelphij# undef _dt_ 612285612Sdelphij 613285612Sdelphij# define _dt_(_s, _fn) 1 + 614285612Sdelphij static int const dir_ct = DIRECTIVE_TABLE 0; 615285612Sdelphij static char const * dir_names[DIRECTIVE_TABLE 0]; 616285612Sdelphij# undef _dt_ 617285612Sdelphij 618285612Sdelphij int ix; 619285612Sdelphij 620285612Sdelphij if (dir_names[0] == NULL) { 621285612Sdelphij ix = 0; 622285612Sdelphij# define _dt_(_s, _fn) dir_names[ix++] = _s; 623285612Sdelphij DIRECTIVE_TABLE; 624285612Sdelphij# undef _dt_ 625285612Sdelphij } 626285612Sdelphij 627285612Sdelphij for (ix = 0; ix < dir_ct; ix++) { 628285612Sdelphij size_t len = strlen(dir_names[ix]); 629285612Sdelphij if ( (strncmp(txt + 2, dir_names[ix], len) == 0) 630285612Sdelphij && (! IS_VALUE_NAME_CHAR(txt[len+2])) ) 631285612Sdelphij return dir_disp[ix](opts, txt + len + 2); 632285612Sdelphij } 633285612Sdelphij 634285612Sdelphij /* 635285612Sdelphij * We don't know what this is. Skip it. 636285612Sdelphij */ 637285612Sdelphij txt = strchr(txt+2, '>'); 638285612Sdelphij if (txt != NULL) 639285612Sdelphij txt++; 640285612Sdelphij return txt; 641285612Sdelphij# undef DIRECTIVE_TABLE 642285612Sdelphij} 643285612Sdelphij 644285612Sdelphij/** 645285612Sdelphij * handle AutoOpts mode flags. 646181834Sroberto * 647285612Sdelphij * @param[in,out] opts program option descriptor 648285612Sdelphij * @param[in] txt scanning pointer 649285612Sdelphij * @returns the next character to look at 650181834Sroberto */ 651285612Sdelphijstatic char * 652285612Sdelphijaoflags_directive(tOptions * opts, char * txt) 653181834Sroberto{ 654285612Sdelphij char * pz; 655181834Sroberto 656285612Sdelphij pz = SPN_WHITESPACE_CHARS(txt+1); 657285612Sdelphij txt = strchr(pz, '>'); 658285612Sdelphij if (txt != NULL) { 659285612Sdelphij 660285612Sdelphij size_t len = (unsigned)(txt - pz); 661285612Sdelphij char * ftxt = AGALOC(len + 1, "aoflags"); 662285612Sdelphij 663285612Sdelphij memcpy(ftxt, pz, len); 664285612Sdelphij ftxt[len] = NUL; 665285612Sdelphij set_usage_flags(opts, ftxt); 666285612Sdelphij AGFREE(ftxt); 667285612Sdelphij 668285612Sdelphij txt++; 669181834Sroberto } 670181834Sroberto 671285612Sdelphij return txt; 672285612Sdelphij} 673181834Sroberto 674285612Sdelphij/** 675285612Sdelphij * handle program segmentation of config file. 676285612Sdelphij * 677285612Sdelphij * @param[in,out] opts program option descriptor 678285612Sdelphij * @param[in] txt scanning pointer 679285612Sdelphij * @returns the next character to look at 680285612Sdelphij */ 681285612Sdelphijstatic char * 682285612Sdelphijprogram_directive(tOptions * opts, char * txt) 683285612Sdelphij{ 684285612Sdelphij static char const ttlfmt[] = "<?"; 685285612Sdelphij size_t ttl_len = sizeof(ttlfmt) + strlen(zCfgProg); 686285612Sdelphij char * ttl = AGALOC(ttl_len, "prog title"); 687285612Sdelphij size_t name_len = strlen(opts->pzProgName); 688285612Sdelphij 689285612Sdelphij memcpy(ttl, ttlfmt, sizeof(ttlfmt) - 1); 690285612Sdelphij memcpy(ttl + sizeof(ttlfmt) - 1, zCfgProg, ttl_len - (sizeof(ttlfmt) - 1)); 691285612Sdelphij 692181834Sroberto do { 693285612Sdelphij txt = SPN_WHITESPACE_CHARS(txt+1); 694181834Sroberto 695285612Sdelphij if ( (strneqvcmp(txt, opts->pzProgName, (int)name_len) == 0) 696285612Sdelphij && (IS_END_XML_TOKEN_CHAR(txt[name_len])) ) { 697285612Sdelphij txt += name_len; 698285612Sdelphij break; 699285612Sdelphij } 700285612Sdelphij 701285612Sdelphij txt = strstr(txt, ttl); 702285612Sdelphij } while (txt != NULL); 703285612Sdelphij 704285612Sdelphij AGFREE(ttl); 705285612Sdelphij if (txt != NULL) 706285612Sdelphij for (;;) { 707285612Sdelphij if (*txt == NUL) { 708285612Sdelphij txt = NULL; 709181834Sroberto break; 710181834Sroberto } 711285612Sdelphij if (*(txt++) == '>') 712285612Sdelphij break; 713181834Sroberto } 714181834Sroberto 715285612Sdelphij return txt; 716181834Sroberto} 717181834Sroberto 718285612Sdelphij/** 719285612Sdelphij * "txt" points to a '[' character. 720181834Sroberto * The "traditional" [PROG_NAME] segmentation of the config file. 721181834Sroberto * Do not ever mix with the "<?program prog-name>" variation. 722285612Sdelphij * 723285612Sdelphij * @param[in,out] opts program option descriptor 724285612Sdelphij * @param[in] txt scanning pointer 725285612Sdelphij * @returns the next character to look at 726181834Sroberto */ 727285612Sdelphijstatic char * 728285612Sdelphijhandle_section(tOptions * opts, char * txt) 729181834Sroberto{ 730285612Sdelphij size_t len = strlen(opts->pzPROGNAME); 731285612Sdelphij if ( (strncmp(txt+1, opts->pzPROGNAME, len) == 0) 732285612Sdelphij && (txt[len+1] == ']')) 733285612Sdelphij return strchr(txt + len + 2, NL); 734181834Sroberto 735181834Sroberto if (len > 16) 736181834Sroberto return NULL; 737181834Sroberto 738181834Sroberto { 739181834Sroberto char z[24]; 740285612Sdelphij sprintf(z, "[%s]", opts->pzPROGNAME); 741285612Sdelphij txt = strstr(txt, z); 742181834Sroberto } 743181834Sroberto 744285612Sdelphij if (txt != NULL) 745285612Sdelphij txt = strchr(txt, NL); 746285612Sdelphij return txt; 747181834Sroberto} 748181834Sroberto 749285612Sdelphij/** 750285612Sdelphij * parse XML encodings 751285612Sdelphij */ 752285612Sdelphijstatic int 753285612Sdelphijparse_xml_encoding(char ** ppz) 754285612Sdelphij{ 755285612Sdelphij# define XMLTABLE \ 756285612Sdelphij _xmlNm_(amp, '&') \ 757285612Sdelphij _xmlNm_(lt, '<') \ 758285612Sdelphij _xmlNm_(gt, '>') \ 759285612Sdelphij _xmlNm_(ff, '\f') \ 760285612Sdelphij _xmlNm_(ht, '\t') \ 761285612Sdelphij _xmlNm_(cr, '\r') \ 762285612Sdelphij _xmlNm_(vt, '\v') \ 763285612Sdelphij _xmlNm_(bel, '\a') \ 764285612Sdelphij _xmlNm_(nl, NL) \ 765285612Sdelphij _xmlNm_(space, ' ') \ 766285612Sdelphij _xmlNm_(quot, '"') \ 767285612Sdelphij _xmlNm_(apos, '\'') 768181834Sroberto 769285612Sdelphij static struct { 770285612Sdelphij char const * const nm_str; 771285612Sdelphij unsigned short nm_len; 772285612Sdelphij short nm_val; 773285612Sdelphij } const xml_names[] = { 774285612Sdelphij# define _xmlNm_(_n, _v) { #_n ";", sizeof(#_n), _v }, 775285612Sdelphij XMLTABLE 776285612Sdelphij# undef _xmlNm_ 777285612Sdelphij# undef XMLTABLE 778285612Sdelphij }; 779285612Sdelphij 780285612Sdelphij static int const nm_ct = sizeof(xml_names) / sizeof(xml_names[0]); 781285612Sdelphij int base = 10; 782285612Sdelphij 783285612Sdelphij char * pz = *ppz; 784285612Sdelphij 785285612Sdelphij if (*pz == '#') { 786285612Sdelphij pz++; 787285612Sdelphij goto parse_number; 788285612Sdelphij } 789285612Sdelphij 790285612Sdelphij if (IS_DEC_DIGIT_CHAR(*pz)) { 791285612Sdelphij unsigned long v; 792285612Sdelphij 793285612Sdelphij parse_number: 794285612Sdelphij switch (*pz) { 795285612Sdelphij case 'x': case 'X': 796285612Sdelphij /* 797285612Sdelphij * Some forms specify hex with: &#xNN; 798285612Sdelphij */ 799285612Sdelphij base = 16; 800285612Sdelphij pz++; 801285612Sdelphij break; 802285612Sdelphij 803285612Sdelphij case '0': 804285612Sdelphij /* 805285612Sdelphij *  is hex and  is decimal. Cool. 806285612Sdelphij * Ya gotta love it. 807285612Sdelphij */ 808285612Sdelphij if (pz[1] == '0') 809285612Sdelphij base = 16; 810285612Sdelphij break; 811285612Sdelphij } 812285612Sdelphij 813285612Sdelphij v = strtoul(pz, &pz, base); 814285612Sdelphij if ((*pz != ';') || (v > 0x7F)) 815285612Sdelphij return NUL; 816285612Sdelphij *ppz = pz + 1; 817285612Sdelphij return (int)v; 818285612Sdelphij } 819285612Sdelphij 820285612Sdelphij { 821285612Sdelphij int ix = 0; 822285612Sdelphij do { 823285612Sdelphij if (strncmp(pz, xml_names[ix].nm_str, xml_names[ix].nm_len) 824285612Sdelphij == 0) { 825285612Sdelphij *ppz = pz + xml_names[ix].nm_len; 826285612Sdelphij return xml_names[ix].nm_val; 827285612Sdelphij } 828285612Sdelphij } while (++ix < nm_ct); 829285612Sdelphij } 830285612Sdelphij 831285612Sdelphij return NUL; 832285612Sdelphij} 833285612Sdelphij 834285612Sdelphij/** 835285612Sdelphij * Find the end marker for the named section of XML. 836285612Sdelphij * Trim that text there, trimming trailing white space for all modes 837285612Sdelphij * except for OPTION_LOAD_UNCOOKED. 838285612Sdelphij */ 839285612Sdelphijstatic char * 840285612Sdelphijtrim_xml_text(char * intxt, char const * pznm, tOptionLoadMode mode) 841285612Sdelphij{ 842285612Sdelphij static char const fmt[] = "</%s>"; 843285612Sdelphij size_t len = strlen(pznm) + sizeof(fmt) - 2 /* for %s */; 844285612Sdelphij char * etext; 845285612Sdelphij 846285612Sdelphij { 847285612Sdelphij char z[64], *pz = z; 848285612Sdelphij if (len >= sizeof(z)) 849285612Sdelphij pz = AGALOC(len, "scan name"); 850285612Sdelphij 851285612Sdelphij len = (size_t)sprintf(pz, fmt, pznm); 852285612Sdelphij *intxt = ' '; 853285612Sdelphij etext = strstr(intxt, pz); 854285612Sdelphij if (pz != z) AGFREE(pz); 855285612Sdelphij } 856285612Sdelphij 857285612Sdelphij if (etext == NULL) 858285612Sdelphij return etext; 859285612Sdelphij 860285612Sdelphij { 861285612Sdelphij char * result = etext + len; 862285612Sdelphij 863285612Sdelphij if (mode != OPTION_LOAD_UNCOOKED) 864285612Sdelphij etext = SPN_WHITESPACE_BACK(intxt, etext); 865285612Sdelphij 866285612Sdelphij *etext = NUL; 867285612Sdelphij return result; 868285612Sdelphij } 869285612Sdelphij} 870285612Sdelphij 871285612Sdelphij/** 872285612Sdelphij */ 873285612Sdelphijstatic void 874285612Sdelphijcook_xml_text(char * pzData) 875285612Sdelphij{ 876285612Sdelphij char * pzs = pzData; 877285612Sdelphij char * pzd = pzData; 878285612Sdelphij char bf[4]; 879285612Sdelphij bf[2] = NUL; 880285612Sdelphij 881285612Sdelphij for (;;) { 882285612Sdelphij int ch = ((int)*(pzs++)) & 0xFF; 883285612Sdelphij switch (ch) { 884285612Sdelphij case NUL: 885285612Sdelphij *pzd = NUL; 886285612Sdelphij return; 887285612Sdelphij 888285612Sdelphij case '&': 889285612Sdelphij ch = parse_xml_encoding(&pzs); 890285612Sdelphij *(pzd++) = (char)ch; 891285612Sdelphij if (ch == NUL) 892285612Sdelphij return; 893285612Sdelphij break; 894285612Sdelphij 895285612Sdelphij case '%': 896285612Sdelphij bf[0] = *(pzs++); 897285612Sdelphij bf[1] = *(pzs++); 898285612Sdelphij if ((bf[0] == NUL) || (bf[1] == NUL)) { 899285612Sdelphij *pzd = NUL; 900285612Sdelphij return; 901285612Sdelphij } 902285612Sdelphij 903285612Sdelphij ch = (int)strtoul(bf, NULL, 16); 904285612Sdelphij /* FALLTHROUGH */ 905285612Sdelphij 906285612Sdelphij default: 907285612Sdelphij *(pzd++) = (char)ch; 908285612Sdelphij } 909285612Sdelphij } 910285612Sdelphij} 911285612Sdelphij 912285612Sdelphij/** 913285612Sdelphij * "txt" points to a '<' character, followed by an alpha. 914181834Sroberto * The end of the entry is either the "/>" following the name, or else a 915181834Sroberto * "</name>" string. 916181834Sroberto */ 917285612Sdelphijstatic char * 918285612Sdelphijhandle_struct(tOptions * opts, tOptState * ost, char * txt, int dir) 919181834Sroberto{ 920181834Sroberto tOptionLoadMode mode = option_load_mode; 921285612Sdelphij tOptionValue valu; 922181834Sroberto 923285612Sdelphij char * pzName = ++txt; 924285612Sdelphij char * pzData; 925285612Sdelphij char * pcNulPoint; 926181834Sroberto 927285612Sdelphij txt = SPN_VALUE_NAME_CHARS(txt); 928285612Sdelphij pcNulPoint = txt; 929181834Sroberto valu.valType = OPARG_TYPE_STRING; 930181834Sroberto 931285612Sdelphij switch (*txt) { 932181834Sroberto case ' ': 933181834Sroberto case '\t': 934285612Sdelphij txt = VOIDP(parse_attrs( 935285612Sdelphij opts, SPN_WHITESPACE_CHARS(txt), &mode, &valu)); 936285612Sdelphij if (txt == NULL) 937285612Sdelphij return txt; 938285612Sdelphij if (*txt == '>') 939181834Sroberto break; 940285612Sdelphij if (*txt != '/') 941181834Sroberto return NULL; 942181834Sroberto /* FALLTHROUGH */ 943181834Sroberto 944181834Sroberto case '/': 945285612Sdelphij if (txt[1] != '>') 946181834Sroberto return NULL; 947285612Sdelphij *txt = NUL; 948285612Sdelphij txt += 2; 949285612Sdelphij load_opt_line(opts, ost, pzName, dir, mode); 950285612Sdelphij return txt; 951181834Sroberto 952181834Sroberto case '>': 953181834Sroberto break; 954181834Sroberto 955181834Sroberto default: 956285612Sdelphij txt = strchr(txt, '>'); 957285612Sdelphij if (txt != NULL) 958285612Sdelphij txt++; 959285612Sdelphij return txt; 960181834Sroberto } 961181834Sroberto 962181834Sroberto /* 963285612Sdelphij * If we are here, we have a value. "txt" points to a closing angle 964181834Sroberto * bracket. Separate the name from the value for a moment. 965181834Sroberto */ 966181834Sroberto *pcNulPoint = NUL; 967285612Sdelphij pzData = ++txt; 968285612Sdelphij txt = trim_xml_text(txt, pzName, mode); 969285612Sdelphij if (txt == NULL) 970285612Sdelphij return txt; 971181834Sroberto 972181834Sroberto /* 973285612Sdelphij * Rejoin the name and value for parsing by "load_opt_line()". 974285612Sdelphij * Erase any attributes parsed by "parse_attrs()". 975181834Sroberto */ 976285612Sdelphij memset(pcNulPoint, ' ', (size_t)(pzData - pcNulPoint)); 977181834Sroberto 978181834Sroberto /* 979285612Sdelphij * If we are getting a "string" value that is to be cooked, 980285612Sdelphij * then process the XML-ish &xx; XML-ish and %XX hex characters. 981181834Sroberto */ 982285612Sdelphij if ( (valu.valType == OPARG_TYPE_STRING) 983285612Sdelphij && (mode == OPTION_LOAD_COOKED)) 984285612Sdelphij cook_xml_text(pzData); 985181834Sroberto 986181834Sroberto /* 987181834Sroberto * "pzName" points to what looks like text for one option/configurable. 988181834Sroberto * It is NUL terminated. Process it. 989181834Sroberto */ 990285612Sdelphij load_opt_line(opts, ost, pzName, dir, mode); 991181834Sroberto 992285612Sdelphij return txt; 993181834Sroberto} 994181834Sroberto 995285612Sdelphij/** 996181834Sroberto * Load a configuration file. This may be invoked either from 997181834Sroberto * scanning the "homerc" list, or from a specific file request. 998181834Sroberto * (see "optionFileLoad()", the implementation for --load-opts) 999181834Sroberto */ 1000181834SrobertoLOCAL void 1001285612Sdelphijintern_file_load(tOptions * opts) 1002181834Sroberto{ 1003285612Sdelphij uint32_t svfl; 1004285612Sdelphij int idx; 1005285612Sdelphij int inc; 1006285612Sdelphij char f_name[ AG_PATH_MAX+1 ]; 1007181834Sroberto 1008285612Sdelphij if (opts->papzHomeList == NULL) 1009181834Sroberto return; 1010181834Sroberto 1011285612Sdelphij svfl = opts->fOptSet; 1012285612Sdelphij inc = DIRECTION_PRESET; 1013285612Sdelphij 1014181834Sroberto /* 1015285612Sdelphij * Never stop on errors in config files. 1016285612Sdelphij */ 1017285612Sdelphij opts->fOptSet &= ~OPTPROC_ERRSTOP; 1018285612Sdelphij 1019285612Sdelphij /* 1020181834Sroberto * Find the last RC entry (highest priority entry) 1021181834Sroberto */ 1022285612Sdelphij for (idx = 0; opts->papzHomeList[ idx+1 ] != NULL; ++idx) ; 1023181834Sroberto 1024181834Sroberto /* 1025181834Sroberto * For every path in the home list, ... *TWICE* We start at the last 1026181834Sroberto * (highest priority) entry, work our way down to the lowest priority, 1027181834Sroberto * handling the immediate options. 1028181834Sroberto * Then we go back up, doing the normal options. 1029181834Sroberto */ 1030181834Sroberto for (;;) { 1031285612Sdelphij struct stat sb; 1032285612Sdelphij cch_t * path; 1033181834Sroberto 1034181834Sroberto /* 1035181834Sroberto * IF we've reached the bottom end, change direction 1036181834Sroberto */ 1037181834Sroberto if (idx < 0) { 1038181834Sroberto inc = DIRECTION_PROCESS; 1039181834Sroberto idx = 0; 1040181834Sroberto } 1041181834Sroberto 1042285612Sdelphij path = opts->papzHomeList[ idx ]; 1043181834Sroberto 1044181834Sroberto /* 1045181834Sroberto * IF we've reached the top end, bail out 1046181834Sroberto */ 1047285612Sdelphij if (path == NULL) 1048181834Sroberto break; 1049181834Sroberto 1050181834Sroberto idx += inc; 1051181834Sroberto 1052285612Sdelphij if (! optionMakePath(f_name, (int)sizeof(f_name), 1053285612Sdelphij path, opts->pzProgPath)) 1054181834Sroberto continue; 1055181834Sroberto 1056181834Sroberto /* 1057181834Sroberto * IF the file name we constructed is a directory, 1058181834Sroberto * THEN append the Resource Configuration file name 1059181834Sroberto * ELSE we must have the complete file name 1060181834Sroberto */ 1061285612Sdelphij if (stat(f_name, &sb) != 0) 1062181834Sroberto continue; /* bogus name - skip the home list entry */ 1063181834Sroberto 1064285612Sdelphij if (S_ISDIR(sb.st_mode)) { 1065285612Sdelphij size_t len = strlen(f_name); 1066285612Sdelphij size_t nln = strlen(opts->pzRcName) + 1; 1067285612Sdelphij char * pz = f_name + len; 1068181834Sroberto 1069285612Sdelphij if (len + 1 + nln >= sizeof(f_name)) 1070181834Sroberto continue; 1071181834Sroberto 1072181834Sroberto if (pz[-1] != DIRCH) 1073181834Sroberto *(pz++) = DIRCH; 1074285612Sdelphij memcpy(pz, opts->pzRcName, nln); 1075181834Sroberto } 1076181834Sroberto 1077285612Sdelphij file_preset(opts, f_name, inc); 1078181834Sroberto 1079181834Sroberto /* 1080181834Sroberto * IF we are now to skip config files AND we are presetting, 1081181834Sroberto * THEN change direction. We must go the other way. 1082181834Sroberto */ 1083181834Sroberto { 1084285612Sdelphij tOptDesc * od = opts->pOptDesc + opts->specOptIdx.save_opts + 1; 1085285612Sdelphij if (DISABLED_OPT(od) && PRESETTING(inc)) { 1086181834Sroberto idx -= inc; /* go back and reprocess current file */ 1087181834Sroberto inc = DIRECTION_PROCESS; 1088181834Sroberto } 1089181834Sroberto } 1090181834Sroberto } /* twice for every path in the home list, ... */ 1091285612Sdelphij 1092285612Sdelphij opts->fOptSet = svfl; 1093181834Sroberto} 1094181834Sroberto 1095181834Sroberto/*=export_func optionFileLoad 1096181834Sroberto * 1097181834Sroberto * what: Load the locatable config files, in order 1098181834Sroberto * 1099285612Sdelphij * arg: + tOptions * + opts + program options descriptor + 1100285612Sdelphij * arg: + char const * + prog + program name + 1101181834Sroberto * 1102181834Sroberto * ret_type: int 1103181834Sroberto * ret_desc: 0 -> SUCCESS, -1 -> FAILURE 1104181834Sroberto * 1105181834Sroberto * doc: 1106181834Sroberto * 1107181834Sroberto * This function looks in all the specified directories for a configuration 1108181834Sroberto * file ("rc" file or "ini" file) and processes any found twice. The first 1109181834Sroberto * time through, they are processed in reverse order (last file first). At 1110181834Sroberto * that time, only "immediate action" configurables are processed. For 1111181834Sroberto * example, if the last named file specifies not processing any more 1112181834Sroberto * configuration files, then no more configuration files will be processed. 1113181834Sroberto * Such an option in the @strong{first} named directory will have no effect. 1114181834Sroberto * 1115181834Sroberto * Once the immediate action configurables have been handled, then the 1116181834Sroberto * directories are handled in normal, forward order. In that way, later 1117181834Sroberto * config files can override the settings of earlier config files. 1118181834Sroberto * 1119181834Sroberto * See the AutoOpts documentation for a thorough discussion of the 1120181834Sroberto * config file format. 1121181834Sroberto * 1122181834Sroberto * Configuration files not found or not decipherable are simply ignored. 1123181834Sroberto * 1124181834Sroberto * err: Returns the value, "-1" if the program options descriptor 1125181834Sroberto * is out of date or indecipherable. Otherwise, the value "0" will 1126181834Sroberto * always be returned. 1127181834Sroberto=*/ 1128181834Srobertoint 1129285612SdelphijoptionFileLoad(tOptions * opts, char const * prog) 1130181834Sroberto{ 1131285612Sdelphij if (! SUCCESSFUL(validate_struct(opts, prog))) 1132181834Sroberto return -1; 1133181834Sroberto 1134285612Sdelphij /* 1135285612Sdelphij * The pointer to the program name is "const". However, the 1136285612Sdelphij * structure is in writable memory, so we coerce the address 1137285612Sdelphij * of this pointer to point to writable memory. 1138285612Sdelphij */ 1139285612Sdelphij { 1140285612Sdelphij char const ** pp = VOIDP(&(opts->pzProgName)); 1141285612Sdelphij *pp = prog; 1142285612Sdelphij } 1143285612Sdelphij 1144285612Sdelphij intern_file_load(opts); 1145181834Sroberto return 0; 1146181834Sroberto} 1147181834Sroberto 1148181834Sroberto/*=export_func optionLoadOpt 1149181834Sroberto * private: 1150181834Sroberto * 1151181834Sroberto * what: Load an option rc/ini file 1152285612Sdelphij * arg: + tOptions * + opts + program options descriptor + 1153285612Sdelphij * arg: + tOptDesc * + odesc + the descriptor for this arg + 1154181834Sroberto * 1155181834Sroberto * doc: 1156181834Sroberto * Processes the options found in the file named with 1157285612Sdelphij * odesc->optArg.argString. 1158181834Sroberto=*/ 1159181834Srobertovoid 1160285612SdelphijoptionLoadOpt(tOptions * opts, tOptDesc * odesc) 1161181834Sroberto{ 1162285612Sdelphij struct stat sb; 1163285612Sdelphij 1164285612Sdelphij if (opts <= OPTPROC_EMIT_LIMIT) 1165285612Sdelphij return; 1166285612Sdelphij 1167181834Sroberto /* 1168181834Sroberto * IF the option is not being disabled, THEN load the file. There must 1169181834Sroberto * be a file. (If it is being disabled, then the disablement processing 1170181834Sroberto * already took place. It must be done to suppress preloading of ini/rc 1171181834Sroberto * files.) 1172181834Sroberto */ 1173285612Sdelphij if ( DISABLED_OPT(odesc) 1174285612Sdelphij || ((odesc->fOptState & OPTST_RESET) != 0)) 1175285612Sdelphij return; 1176181834Sroberto 1177285612Sdelphij if (stat(odesc->optArg.argString, &sb) != 0) { 1178285612Sdelphij if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0) 1179285612Sdelphij return; 1180181834Sroberto 1181285612Sdelphij fserr_exit(opts->pzProgName, "stat", odesc->optArg.argString); 1182285612Sdelphij /* NOT REACHED */ 1183285612Sdelphij } 1184181834Sroberto 1185285612Sdelphij if (! S_ISREG(sb.st_mode)) { 1186285612Sdelphij if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0) 1187285612Sdelphij return; 1188285612Sdelphij errno = EINVAL; 1189285612Sdelphij fserr_exit(opts->pzProgName, "stat", odesc->optArg.argString); 1190285612Sdelphij /* NOT REACHED */ 1191285612Sdelphij } 1192181834Sroberto 1193285612Sdelphij file_preset(opts, odesc->optArg.argString, DIRECTION_CALLED); 1194181834Sroberto} 1195181834Sroberto 1196285612Sdelphij/** 1197285612Sdelphij * Parse the various attributes of an XML-styled config file entry 1198181834Sroberto * 1199285612Sdelphij * @returns NULL on failure, otherwise the scan point 1200181834Sroberto */ 1201285612SdelphijLOCAL char const * 1202285612Sdelphijparse_attrs(tOptions * opts, char const * txt, tOptionLoadMode * pMode, 1203285612Sdelphij tOptionValue * pType) 1204181834Sroberto{ 1205285612Sdelphij size_t len = 0; 1206181834Sroberto 1207285612Sdelphij for (;;) { 1208285612Sdelphij len = (size_t)(SPN_LOWER_CASE_CHARS(txt) - txt); 1209181834Sroberto 1210285612Sdelphij /* 1211285612Sdelphij * The enumeration used in this switch is derived from this switch 1212285612Sdelphij * statement itself. The "find_option_xat_attribute_cmd" function 1213285612Sdelphij * will return XAT_CMD_MEMBERS for the "txt" string value 1214285612Sdelphij * "members", etc. 1215285612Sdelphij */ 1216285612Sdelphij switch (find_option_xat_attribute_cmd(txt, len)) { 1217285612Sdelphij case XAT_CMD_TYPE: 1218285612Sdelphij txt = parse_value(txt+len, pType); 1219285612Sdelphij break; 1220181834Sroberto 1221285612Sdelphij case XAT_CMD_WORDS: 1222285612Sdelphij txt = parse_keyword(opts, txt+len, pType); 1223181834Sroberto break; 1224181834Sroberto 1225285612Sdelphij case XAT_CMD_MEMBERS: 1226285612Sdelphij txt = parse_set_mem(opts, txt+len, pType); 1227285612Sdelphij break; 1228181834Sroberto 1229285612Sdelphij case XAT_CMD_COOKED: 1230285612Sdelphij txt += len; 1231285612Sdelphij if (! IS_END_XML_TOKEN_CHAR(*txt)) 1232285612Sdelphij goto invalid_kwd; 1233181834Sroberto 1234285612Sdelphij *pMode = OPTION_LOAD_COOKED; 1235285612Sdelphij break; 1236181834Sroberto 1237285612Sdelphij case XAT_CMD_UNCOOKED: 1238285612Sdelphij txt += len; 1239285612Sdelphij if (! IS_END_XML_TOKEN_CHAR(*txt)) 1240285612Sdelphij goto invalid_kwd; 1241181834Sroberto 1242285612Sdelphij *pMode = OPTION_LOAD_UNCOOKED; 1243285612Sdelphij break; 1244181834Sroberto 1245285612Sdelphij case XAT_CMD_KEEP: 1246285612Sdelphij txt += len; 1247285612Sdelphij if (! IS_END_XML_TOKEN_CHAR(*txt)) 1248285612Sdelphij goto invalid_kwd; 1249181834Sroberto 1250285612Sdelphij *pMode = OPTION_LOAD_KEEP; 1251285612Sdelphij break; 1252181834Sroberto 1253285612Sdelphij default: 1254285612Sdelphij case XAT_INVALID_CMD: 1255285612Sdelphij invalid_kwd: 1256285612Sdelphij pType->valType = OPARG_TYPE_NONE; 1257285612Sdelphij return skip_unkn(txt); 1258181834Sroberto } 1259181834Sroberto 1260285612Sdelphij if (txt == NULL) 1261285612Sdelphij return NULL; 1262285612Sdelphij txt = SPN_WHITESPACE_CHARS(txt); 1263285612Sdelphij switch (*txt) { 1264285612Sdelphij case '/': pType->valType = OPARG_TYPE_NONE; 1265285612Sdelphij /* FALLTHROUGH */ 1266285612Sdelphij case '>': return txt; 1267181834Sroberto } 1268285612Sdelphij if (! IS_LOWER_CASE_CHAR(*txt)) 1269285612Sdelphij return NULL; 1270181834Sroberto } 1271181834Sroberto} 1272181834Sroberto 1273285612Sdelphij/** 1274285612Sdelphij * "txt" points to the character after "words=". 1275285612Sdelphij * What should follow is a name of a keyword (enumeration) list. 1276181834Sroberto * 1277285612Sdelphij * @param opts unused 1278285612Sdelphij * @param[in] txt keyword to skip over 1279285612Sdelphij * @param type unused value type 1280285612Sdelphij * @returns pointer after skipped text 1281181834Sroberto */ 1282285612Sdelphijstatic char const * 1283285612Sdelphijparse_keyword(tOptions * opts, char const * txt, tOptionValue * typ) 1284181834Sroberto{ 1285285612Sdelphij (void)opts; 1286285612Sdelphij (void)typ; 1287285612Sdelphij 1288285612Sdelphij return skip_unkn(txt); 1289181834Sroberto} 1290181834Sroberto 1291285612Sdelphij/** 1292285612Sdelphij * "txt" points to the character after "members=" 1293285612Sdelphij * What should follow is a name of a "set membership". 1294285612Sdelphij * A collection of bit flags. 1295181834Sroberto * 1296285612Sdelphij * @param opts unused 1297285612Sdelphij * @param[in] txt keyword to skip over 1298285612Sdelphij * @param type unused value type 1299285612Sdelphij * @returns pointer after skipped text 1300181834Sroberto */ 1301285612Sdelphijstatic char const * 1302285612Sdelphijparse_set_mem(tOptions * opts, char const * txt, tOptionValue * typ) 1303181834Sroberto{ 1304285612Sdelphij (void)opts; 1305285612Sdelphij (void)typ; 1306181834Sroberto 1307285612Sdelphij return skip_unkn(txt); 1308181834Sroberto} 1309181834Sroberto 1310285612Sdelphij/** 1311285612Sdelphij * parse the type. The keyword "type" was found, now figure out 1312285612Sdelphij * the type that follows the type. 1313181834Sroberto * 1314285612Sdelphij * @param[in] txt points to the '=' character after the "type" keyword. 1315285612Sdelphij * @param[out] typ where to store the type found 1316285612Sdelphij * @returns the next byte after the type name 1317181834Sroberto */ 1318285612Sdelphijstatic char const * 1319285612Sdelphijparse_value(char const * txt, tOptionValue * typ) 1320181834Sroberto{ 1321285612Sdelphij size_t len = 0; 1322181834Sroberto 1323285612Sdelphij if (*(txt++) != '=') 1324285612Sdelphij goto woops; 1325181834Sroberto 1326285612Sdelphij len = (size_t)(SPN_OPTION_NAME_CHARS(txt) - txt); 1327181834Sroberto 1328285612Sdelphij if ((len == 0) || (! IS_END_XML_TOKEN_CHAR(txt[len]))) { 1329285612Sdelphij woops: 1330285612Sdelphij typ->valType = OPARG_TYPE_NONE; 1331285612Sdelphij return skip_unkn(txt + len); 1332181834Sroberto } 1333181834Sroberto 1334181834Sroberto /* 1335285612Sdelphij * The enumeration used in this switch is derived from this switch 1336285612Sdelphij * statement itself. The "find_option_value_type_cmd" function 1337285612Sdelphij * will return VTP_CMD_INTEGER for the "txt" string value 1338285612Sdelphij * "integer", etc. 1339181834Sroberto */ 1340285612Sdelphij switch (find_option_value_type_cmd(txt, len)) { 1341285612Sdelphij default: 1342285612Sdelphij case VTP_INVALID_CMD: goto woops; 1343181834Sroberto 1344285612Sdelphij case VTP_CMD_STRING: 1345285612Sdelphij typ->valType = OPARG_TYPE_STRING; 1346285612Sdelphij break; 1347181834Sroberto 1348285612Sdelphij case VTP_CMD_INTEGER: 1349285612Sdelphij typ->valType = OPARG_TYPE_NUMERIC; 1350285612Sdelphij break; 1351181834Sroberto 1352285612Sdelphij case VTP_CMD_BOOL: 1353285612Sdelphij case VTP_CMD_BOOLEAN: 1354285612Sdelphij typ->valType = OPARG_TYPE_BOOLEAN; 1355285612Sdelphij break; 1356181834Sroberto 1357285612Sdelphij case VTP_CMD_KEYWORD: 1358285612Sdelphij typ->valType = OPARG_TYPE_ENUMERATION; 1359285612Sdelphij break; 1360181834Sroberto 1361285612Sdelphij case VTP_CMD_SET: 1362285612Sdelphij case VTP_CMD_SET_MEMBERSHIP: 1363285612Sdelphij typ->valType = OPARG_TYPE_MEMBERSHIP; 1364285612Sdelphij break; 1365181834Sroberto 1366285612Sdelphij case VTP_CMD_NESTED: 1367285612Sdelphij case VTP_CMD_HIERARCHY: 1368285612Sdelphij typ->valType = OPARG_TYPE_HIERARCHY; 1369181834Sroberto } 1370181834Sroberto 1371285612Sdelphij return txt + len; 1372181834Sroberto} 1373181834Sroberto 1374285612Sdelphij/** @} 1375285612Sdelphij * 1376181834Sroberto * Local Variables: 1377181834Sroberto * mode: C 1378181834Sroberto * c-file-style: "stroustrup" 1379181834Sroberto * indent-tabs-mode: nil 1380181834Sroberto * End: 1381181834Sroberto * end of autoopts/configfile.c */ 1382