nested.c 21 KB

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