configfile.c 34 KB

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