configfile.c 32 KB

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