configfile.c 34 KB

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