nested.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. /*
  2. * $Id: nested.c,v 4.32 2009/08/01 17:43:06 bkorb Exp $
  3. * Time-stamp: "2008-07-28 19:18:28 bkorb"
  4. *
  5. * Automated Options Nested Values module.
  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. typedef struct {
  28. int xml_ch;
  29. int xml_len;
  30. char xml_txt[8];
  31. } xml_xlate_t;
  32. static xml_xlate_t const xml_xlate[] = {
  33. { '&', 4, "amp;" },
  34. { '<', 3, "lt;" },
  35. { '>', 3, "gt;" },
  36. { '"', 5, "quot;" },
  37. { '\'',5, "apos;" }
  38. };
  39. /* = = = START-STATIC-FORWARD = = = */
  40. /* static forward declarations maintained by mk-fwd */
  41. static void
  42. removeLineContinue( char* pzSrc );
  43. static char const*
  44. scanQuotedString( char const* pzTxt );
  45. static tOptionValue*
  46. addStringValue( void** pp, char const* pzName, size_t nameLen,
  47. char const* pzValue, size_t dataLen );
  48. static tOptionValue*
  49. addBoolValue( void** pp, char const* pzName, size_t nameLen,
  50. char const* pzValue, size_t dataLen );
  51. static tOptionValue*
  52. addNumberValue( void** pp, char const* pzName, size_t nameLen,
  53. char const* pzValue, size_t dataLen );
  54. static tOptionValue*
  55. addNestedValue( void** pp, char const* pzName, size_t nameLen,
  56. char* pzValue, size_t dataLen );
  57. static char const*
  58. scanNameEntry(char const* pzName, tOptionValue* pRes);
  59. static char const*
  60. scanXmlEntry( char const* pzName, tOptionValue* pRes );
  61. static void
  62. unloadNestedArglist( tArgList* pAL );
  63. static void
  64. sortNestedList( tArgList* pAL );
  65. /* = = = END-STATIC-FORWARD = = = */
  66. /* removeLineContinue
  67. *
  68. * Backslashes are used for line continuations. We keep the newline
  69. * characters, but trim out the backslash:
  70. */
  71. static void
  72. removeLineContinue( char* pzSrc )
  73. {
  74. char* pzD;
  75. do {
  76. while (*pzSrc == '\n') pzSrc++;
  77. pzD = strchr(pzSrc, '\n');
  78. if (pzD == NULL)
  79. return;
  80. /*
  81. * pzD has skipped at least one non-newline character and now
  82. * points to a newline character. It now becomes the source and
  83. * pzD goes to the previous character.
  84. */
  85. pzSrc = pzD--;
  86. if (*pzD != '\\')
  87. pzD++;
  88. } while (pzD == pzSrc);
  89. /*
  90. * Start shifting text.
  91. */
  92. for (;;) {
  93. char ch = ((*pzD++) = *(pzSrc++));
  94. switch (ch) {
  95. case NUL: return;
  96. case '\\':
  97. if (*pzSrc == '\n')
  98. --pzD; /* rewrite on next iteration */
  99. }
  100. }
  101. }
  102. /* scanQuotedString
  103. *
  104. * Find the end of a quoted string, skipping escaped quote characters.
  105. */
  106. static char const*
  107. scanQuotedString( char const* pzTxt )
  108. {
  109. char q = *(pzTxt++); /* remember the type of quote */
  110. for (;;) {
  111. char ch = *(pzTxt++);
  112. if (ch == NUL)
  113. return pzTxt-1;
  114. if (ch == q)
  115. return pzTxt;
  116. if (ch == '\\') {
  117. ch = *(pzTxt++);
  118. /*
  119. * IF the next character is NUL, drop the backslash, too.
  120. */
  121. if (ch == NUL)
  122. return pzTxt - 2;
  123. /*
  124. * IF the quote character or the escape character were escaped,
  125. * then skip both, as long as the string does not end.
  126. */
  127. if ((ch == q) || (ch == '\\')) {
  128. if (*(pzTxt++) == NUL)
  129. return pzTxt-1;
  130. }
  131. }
  132. }
  133. }
  134. /* addStringValue
  135. *
  136. * Associate a name with either a string or no value.
  137. */
  138. static tOptionValue*
  139. addStringValue( void** pp, char const* pzName, size_t nameLen,
  140. char const* pzValue, size_t dataLen )
  141. {
  142. tOptionValue* pNV;
  143. size_t sz = nameLen + dataLen + sizeof(*pNV);
  144. pNV = AGALOC( sz, "option name/str value pair" );
  145. if (pNV == NULL)
  146. return NULL;
  147. if (pzValue == NULL) {
  148. pNV->valType = OPARG_TYPE_NONE;
  149. pNV->pzName = pNV->v.strVal;
  150. } else {
  151. pNV->valType = OPARG_TYPE_STRING;
  152. if (dataLen > 0) {
  153. char const * pzSrc = pzValue;
  154. char * pzDst = pNV->v.strVal;
  155. int ct = dataLen;
  156. do {
  157. int ch = *(pzSrc++) & 0xFF;
  158. if (ch == NUL) goto data_copy_done;
  159. if (ch == '&')
  160. ch = get_special_char(&pzSrc, &ct);
  161. *(pzDst++) = ch;
  162. } while (--ct > 0);
  163. data_copy_done:
  164. *pzDst = NUL;
  165. } else {
  166. pNV->v.strVal[0] = NUL;
  167. }
  168. pNV->pzName = pNV->v.strVal + dataLen + 1;
  169. }
  170. memcpy( pNV->pzName, pzName, nameLen );
  171. pNV->pzName[ nameLen ] = NUL;
  172. addArgListEntry( pp, pNV );
  173. return pNV;
  174. }
  175. /* addBoolValue
  176. *
  177. * Associate a name with either a string or no value.
  178. */
  179. static tOptionValue*
  180. addBoolValue( void** pp, char const* pzName, size_t nameLen,
  181. char const* pzValue, size_t dataLen )
  182. {
  183. tOptionValue* pNV;
  184. size_t sz = nameLen + sizeof(*pNV) + 1;
  185. pNV = AGALOC( sz, "option name/bool value pair" );
  186. if (pNV == NULL)
  187. return NULL;
  188. while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) {
  189. dataLen--; pzValue++;
  190. }
  191. if (dataLen == 0)
  192. pNV->v.boolVal = 0;
  193. else if (IS_DEC_DIGIT_CHAR(*pzValue))
  194. pNV->v.boolVal = atoi(pzValue);
  195. else pNV->v.boolVal = ! IS_FALSE_TYPE_CHAR(*pzValue);
  196. pNV->valType = OPARG_TYPE_BOOLEAN;
  197. pNV->pzName = (char*)(pNV + 1);
  198. memcpy( pNV->pzName, pzName, nameLen );
  199. pNV->pzName[ nameLen ] = NUL;
  200. addArgListEntry( pp, pNV );
  201. return pNV;
  202. }
  203. /* addNumberValue
  204. *
  205. * Associate a name with either a string or no value.
  206. */
  207. static tOptionValue*
  208. addNumberValue( void** pp, char const* pzName, size_t nameLen,
  209. char const* pzValue, size_t dataLen )
  210. {
  211. tOptionValue* pNV;
  212. size_t sz = nameLen + sizeof(*pNV) + 1;
  213. pNV = AGALOC( sz, "option name/bool value pair" );
  214. if (pNV == NULL)
  215. return NULL;
  216. while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) {
  217. dataLen--; pzValue++;
  218. }
  219. if (dataLen == 0)
  220. pNV->v.longVal = 0;
  221. else
  222. pNV->v.longVal = strtol(pzValue, 0, 0);
  223. pNV->valType = OPARG_TYPE_NUMERIC;
  224. pNV->pzName = (char*)(pNV + 1);
  225. memcpy( pNV->pzName, pzName, nameLen );
  226. pNV->pzName[ nameLen ] = NUL;
  227. addArgListEntry( pp, pNV );
  228. return pNV;
  229. }
  230. /* addNestedValue
  231. *
  232. * Associate a name with either a string or no value.
  233. */
  234. static tOptionValue*
  235. addNestedValue( void** pp, char const* pzName, size_t nameLen,
  236. char* pzValue, size_t dataLen )
  237. {
  238. tOptionValue* pNV;
  239. if (dataLen == 0) {
  240. size_t sz = nameLen + sizeof(*pNV) + 1;
  241. pNV = AGALOC( sz, "empty nested value pair" );
  242. if (pNV == NULL)
  243. return NULL;
  244. pNV->v.nestVal = NULL;
  245. pNV->valType = OPARG_TYPE_HIERARCHY;
  246. pNV->pzName = (char*)(pNV + 1);
  247. memcpy( pNV->pzName, pzName, nameLen );
  248. pNV->pzName[ nameLen ] = NUL;
  249. } else {
  250. pNV = optionLoadNested( pzValue, pzName, nameLen );
  251. }
  252. if (pNV != NULL)
  253. addArgListEntry( pp, pNV );
  254. return pNV;
  255. }
  256. /* scanNameEntry
  257. *
  258. * We have an entry that starts with a name. Find the end of it, cook it
  259. * (if called for) and create the name/value association.
  260. */
  261. static char const*
  262. scanNameEntry(char const* pzName, tOptionValue* pRes)
  263. {
  264. tOptionValue* pNV;
  265. char const * pzScan = pzName+1; /* we know first char is a name char */
  266. char const * pzVal;
  267. size_t nameLen = 1;
  268. size_t dataLen = 0;
  269. /*
  270. * Scan over characters that name a value. These names may not end
  271. * with a colon, but they may contain colons.
  272. */
  273. while (IS_VALUE_NAME_CHAR(*pzScan)) { pzScan++; nameLen++; }
  274. if (pzScan[-1] == ':') { pzScan--; nameLen--; }
  275. while (IS_HORIZ_WHITE_CHAR(*pzScan)) pzScan++;
  276. re_switch:
  277. switch (*pzScan) {
  278. case '=':
  279. case ':':
  280. while (IS_HORIZ_WHITE_CHAR( (int)*++pzScan )) ;
  281. if ((*pzScan == '=') || (*pzScan == ':'))
  282. goto default_char;
  283. goto re_switch;
  284. case '\n':
  285. case ',':
  286. pzScan++;
  287. /* FALLTHROUGH */
  288. case NUL:
  289. addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
  290. break;
  291. case '"':
  292. case '\'':
  293. pzVal = pzScan;
  294. pzScan = scanQuotedString( pzScan );
  295. dataLen = pzScan - pzVal;
  296. pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal,
  297. dataLen );
  298. if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
  299. ao_string_cook( pNV->v.strVal, NULL );
  300. break;
  301. default:
  302. default_char:
  303. /*
  304. * We have found some strange text value. It ends with a newline
  305. * or a comma.
  306. */
  307. pzVal = pzScan;
  308. for (;;) {
  309. char ch = *(pzScan++);
  310. switch (ch) {
  311. case NUL:
  312. pzScan--;
  313. dataLen = pzScan - pzVal;
  314. goto string_done;
  315. /* FALLTHROUGH */
  316. case '\n':
  317. if ( (pzScan > pzVal + 2)
  318. && (pzScan[-2] == '\\')
  319. && (pzScan[ 0] != NUL))
  320. continue;
  321. /* FALLTHROUGH */
  322. case ',':
  323. dataLen = (pzScan - pzVal) - 1;
  324. string_done:
  325. pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen,
  326. pzVal, dataLen );
  327. if (pNV != NULL)
  328. removeLineContinue( pNV->v.strVal );
  329. goto leave_scan_name;
  330. }
  331. }
  332. break;
  333. } leave_scan_name:;
  334. return pzScan;
  335. }
  336. /* scanXmlEntry
  337. *
  338. * We've found a '<' character. We ignore this if it is a comment or a
  339. * directive. If it is something else, then whatever it is we are looking
  340. * at is bogus. Returning NULL stops processing.
  341. */
  342. static char const*
  343. scanXmlEntry( char const* pzName, tOptionValue* pRes )
  344. {
  345. size_t nameLen = 1, valLen = 0;
  346. char const* pzScan = ++pzName;
  347. char const* pzVal;
  348. tOptionValue valu;
  349. tOptionValue* pNewVal;
  350. tOptionLoadMode save_mode = option_load_mode;
  351. if (! IS_VAR_FIRST_CHAR(*pzName)) {
  352. switch (*pzName) {
  353. default:
  354. pzName = NULL;
  355. break;
  356. case '!':
  357. pzName = strstr( pzName, "-->" );
  358. if (pzName != NULL)
  359. pzName += 3;
  360. break;
  361. case '?':
  362. pzName = strchr( pzName, '>' );
  363. if (pzName != NULL)
  364. pzName++;
  365. break;
  366. }
  367. return pzName;
  368. }
  369. pzScan++;
  370. while (IS_VALUE_NAME_CHAR( (int)*pzScan )) { pzScan++; nameLen++; }
  371. if (nameLen > 64)
  372. return NULL;
  373. valu.valType = OPARG_TYPE_STRING;
  374. switch (*pzScan) {
  375. case ' ':
  376. case '\t':
  377. pzScan = parseAttributes(
  378. NULL, (char*)pzScan, &option_load_mode, &valu );
  379. if (*pzScan == '>') {
  380. pzScan++;
  381. break;
  382. }
  383. if (*pzScan != '/') {
  384. option_load_mode = save_mode;
  385. return NULL;
  386. }
  387. /* FALLTHROUGH */
  388. case '/':
  389. if (*++pzScan != '>') {
  390. option_load_mode = save_mode;
  391. return NULL;
  392. }
  393. addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
  394. option_load_mode = save_mode;
  395. return pzScan+1;
  396. default:
  397. option_load_mode = save_mode;
  398. return NULL;
  399. case '>':
  400. pzScan++;
  401. break;
  402. }
  403. pzVal = pzScan;
  404. {
  405. char z[68];
  406. char* pzD = z;
  407. int ct = nameLen;
  408. char const* pzS = pzName;
  409. *(pzD++) = '<';
  410. *(pzD++) = '/';
  411. do {
  412. *(pzD++) = *(pzS++);
  413. } while (--ct > 0);
  414. *(pzD++) = '>';
  415. *pzD = NUL;
  416. pzScan = strstr( pzScan, z );
  417. if (pzScan == NULL) {
  418. option_load_mode = save_mode;
  419. return NULL;
  420. }
  421. valLen = (pzScan - pzVal);
  422. pzScan += nameLen + 3;
  423. while (IS_WHITESPACE_CHAR(*pzScan)) pzScan++;
  424. }
  425. switch (valu.valType) {
  426. case OPARG_TYPE_NONE:
  427. addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
  428. break;
  429. case OPARG_TYPE_STRING:
  430. pNewVal = addStringValue(
  431. &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
  432. if (option_load_mode == OPTION_LOAD_KEEP)
  433. break;
  434. mungeString( pNewVal->v.strVal, option_load_mode );
  435. break;
  436. case OPARG_TYPE_BOOLEAN:
  437. addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
  438. break;
  439. case OPARG_TYPE_NUMERIC:
  440. addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
  441. break;
  442. case OPARG_TYPE_HIERARCHY:
  443. {
  444. char* pz = AGALOC( valLen+1, "hierarchical scan" );
  445. if (pz == NULL)
  446. break;
  447. memcpy( pz, pzVal, valLen );
  448. pz[valLen] = NUL;
  449. addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen );
  450. AGFREE(pz);
  451. break;
  452. }
  453. case OPARG_TYPE_ENUMERATION:
  454. case OPARG_TYPE_MEMBERSHIP:
  455. default:
  456. break;
  457. }
  458. option_load_mode = save_mode;
  459. return pzScan;
  460. }
  461. /* unloadNestedArglist
  462. *
  463. * Deallocate a list of option arguments. This must have been gotten from
  464. * a hierarchical option argument, not a stacked list of strings. It is
  465. * an internal call, so it is not validated. The caller is responsible for
  466. * knowing what they are doing.
  467. */
  468. static void
  469. unloadNestedArglist( tArgList* pAL )
  470. {
  471. int ct = pAL->useCt;
  472. tCC** ppNV = pAL->apzArgs;
  473. while (ct-- > 0) {
  474. tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
  475. if (pNV->valType == OPARG_TYPE_HIERARCHY)
  476. unloadNestedArglist( pNV->v.nestVal );
  477. AGFREE( pNV );
  478. }
  479. AGFREE( (void*)pAL );
  480. }
  481. /*=export_func optionUnloadNested
  482. *
  483. * what: Deallocate the memory for a nested value
  484. * arg: + tOptionValue const * + pOptVal + the hierarchical value +
  485. *
  486. * doc:
  487. * A nested value needs to be deallocated. The pointer passed in should
  488. * have been gotten from a call to @code{configFileLoad()} (See
  489. * @pxref{libopts-configFileLoad}).
  490. =*/
  491. void
  492. optionUnloadNested( tOptionValue const * pOV )
  493. {
  494. if (pOV == NULL) return;
  495. if (pOV->valType != OPARG_TYPE_HIERARCHY) {
  496. errno = EINVAL;
  497. return;
  498. }
  499. unloadNestedArglist( pOV->v.nestVal );
  500. AGFREE( (void*)pOV );
  501. }
  502. /* sortNestedList
  503. *
  504. * This is a _stable_ sort. The entries are sorted alphabetically,
  505. * but within entries of the same name the ordering is unchanged.
  506. * Typically, we also hope the input is sorted.
  507. */
  508. static void
  509. sortNestedList( tArgList* pAL )
  510. {
  511. int ix;
  512. int lm = pAL->useCt;
  513. /*
  514. * This loop iterates "useCt" - 1 times.
  515. */
  516. for (ix = 0; ++ix < lm;) {
  517. int iy = ix-1;
  518. tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
  519. tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
  520. /*
  521. * For as long as the new entry precedes the "old" entry,
  522. * move the old pointer. Stop before trying to extract the
  523. * "-1" entry.
  524. */
  525. while (strcmp( pOldNV->pzName, pNewNV->pzName ) > 0) {
  526. pAL->apzArgs[iy+1] = (void*)pOldNV;
  527. pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
  528. if (iy < 0)
  529. break;
  530. }
  531. /*
  532. * Always store the pointer. Sometimes it is redundant,
  533. * but the redundancy is cheaper than a test and branch sequence.
  534. */
  535. pAL->apzArgs[iy+1] = (void*)pNewNV;
  536. }
  537. }
  538. /* optionLoadNested
  539. * private:
  540. *
  541. * what: parse a hierarchical option argument
  542. * arg: + char const* + pzTxt + the text to scan +
  543. * arg: + char const* + pzName + the name for the text +
  544. * arg: + size_t + nameLen + the length of "name" +
  545. *
  546. * ret_type: tOptionValue*
  547. * ret_desc: An allocated, compound value structure
  548. *
  549. * doc:
  550. * A block of text represents a series of values. It may be an
  551. * entire configuration file, or it may be an argument to an
  552. * option that takes a hierarchical value.
  553. */
  554. LOCAL tOptionValue*
  555. optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
  556. {
  557. tOptionValue* pRes;
  558. tArgList* pAL;
  559. /*
  560. * Make sure we have some data and we have space to put what we find.
  561. */
  562. if (pzTxt == NULL) {
  563. errno = EINVAL;
  564. return NULL;
  565. }
  566. while (IS_WHITESPACE_CHAR(*pzTxt)) pzTxt++;
  567. if (*pzTxt == NUL) {
  568. errno = ENOENT;
  569. return NULL;
  570. }
  571. pRes = AGALOC( sizeof(*pRes) + nameLen + 1, "nested args" );
  572. if (pRes == NULL) {
  573. errno = ENOMEM;
  574. return NULL;
  575. }
  576. pRes->valType = OPARG_TYPE_HIERARCHY;
  577. pRes->pzName = (char*)(pRes + 1);
  578. memcpy( pRes->pzName, pzName, nameLen );
  579. pRes->pzName[ nameLen ] = NUL;
  580. pAL = AGALOC( sizeof(*pAL), "nested arg list" );
  581. if (pAL == NULL) {
  582. AGFREE( pRes );
  583. return NULL;
  584. }
  585. pRes->v.nestVal = pAL;
  586. pAL->useCt = 0;
  587. pAL->allocCt = MIN_ARG_ALLOC_CT;
  588. /*
  589. * Scan until we hit a NUL.
  590. */
  591. do {
  592. while (IS_WHITESPACE_CHAR( (int)*pzTxt )) pzTxt++;
  593. if (IS_VAR_FIRST_CHAR( (int)*pzTxt )) {
  594. pzTxt = scanNameEntry( pzTxt, pRes );
  595. }
  596. else switch (*pzTxt) {
  597. case NUL: goto scan_done;
  598. case '<': pzTxt = scanXmlEntry( pzTxt, pRes );
  599. if (pzTxt == NULL) goto woops;
  600. if (*pzTxt == ',') pzTxt++; break;
  601. case '#': pzTxt = strchr( pzTxt, '\n' ); break;
  602. default: goto woops;
  603. }
  604. } while (pzTxt != NULL); scan_done:;
  605. pAL = pRes->v.nestVal;
  606. if (pAL->useCt != 0) {
  607. sortNestedList( pAL );
  608. return pRes;
  609. }
  610. woops:
  611. AGFREE( pRes->v.nestVal );
  612. AGFREE( pRes );
  613. return NULL;
  614. }
  615. /*=export_func optionNestedVal
  616. * private:
  617. *
  618. * what: parse a hierarchical option argument
  619. * arg: + tOptions* + pOpts + program options descriptor +
  620. * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
  621. *
  622. * doc:
  623. * Nested value was found on the command line
  624. =*/
  625. void
  626. optionNestedVal(tOptions* pOpts, tOptDesc* pOD)
  627. {
  628. if (pOpts < OPTPROC_EMIT_LIMIT)
  629. return;
  630. if (pOD->fOptState & OPTST_RESET) {
  631. tArgList* pAL = pOD->optCookie;
  632. int ct;
  633. tCC ** av;
  634. if (pAL == NULL)
  635. return;
  636. ct = pAL->useCt;
  637. av = pAL->apzArgs;
  638. while (--ct >= 0) {
  639. void * p = (void *)*(av++);
  640. optionUnloadNested((tOptionValue const *)p);
  641. }
  642. AGFREE(pOD->optCookie);
  643. } else {
  644. tOptionValue* pOV = optionLoadNested(
  645. pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
  646. if (pOV != NULL)
  647. addArgListEntry( &(pOD->optCookie), (void*)pOV );
  648. }
  649. }
  650. /*
  651. * get_special_char
  652. */
  653. LOCAL int
  654. get_special_char(char const ** ppz, int * ct)
  655. {
  656. char const * pz = *ppz;
  657. if (*ct < 3)
  658. return '&';
  659. if (*pz == '#') {
  660. int base = 10;
  661. int retch;
  662. pz++;
  663. if (*pz == 'x') {
  664. base = 16;
  665. pz++;
  666. }
  667. retch = (int)strtoul(pz, (char **)&pz, base);
  668. if (*pz != ';')
  669. return '&';
  670. base = ++pz - *ppz;
  671. if (base > *ct)
  672. return '&';
  673. *ct -= base;
  674. *ppz = pz;
  675. return retch;
  676. }
  677. {
  678. int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
  679. xml_xlate_t const * xlatp = xml_xlate;
  680. for (;;) {
  681. if ( (*ct >= xlatp->xml_len)
  682. && (strncmp(pz, xlatp->xml_txt, xlatp->xml_len) == 0)) {
  683. *ppz += xlatp->xml_len;
  684. *ct -= xlatp->xml_len;
  685. return xlatp->xml_ch;
  686. }
  687. if (--ctr <= 0)
  688. break;
  689. xlatp++;
  690. }
  691. }
  692. return '&';
  693. }
  694. /*
  695. * emit_special_char
  696. */
  697. LOCAL void
  698. emit_special_char(FILE * fp, int ch)
  699. {
  700. int ctr = sizeof(xml_xlate) / sizeof(xml_xlate[0]);
  701. xml_xlate_t const * xlatp = xml_xlate;
  702. putc('&', fp);
  703. for (;;) {
  704. if (ch == xlatp->xml_ch) {
  705. fputs(xlatp->xml_txt, fp);
  706. return;
  707. }
  708. if (--ctr <= 0)
  709. break;
  710. xlatp++;
  711. }
  712. fprintf(fp, "#x%02X;", (ch & 0xFF));
  713. }
  714. /*
  715. * Local Variables:
  716. * mode: C
  717. * c-file-style: "stroustrup"
  718. * indent-tabs-mode: nil
  719. * End:
  720. * end of autoopts/nested.c */