configfile.c 36 KB

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