configfile.c 34 KB

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