1
0

configfile.c 33 KB

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