configfile.c 32 KB

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