nested.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. /*
  2. * $Id: nested.c,v 4.14 2007/02/04 17:44:12 bkorb Exp $
  3. * Time-stamp: "2007-01-26 11:04:35 bkorb"
  4. *
  5. * Automated Options Nested Values module.
  6. */
  7. /*
  8. * Automated Options copyright 1992-2007 Bruce Korb
  9. *
  10. * Automated Options is free software.
  11. * You may redistribute it and/or modify it under the terms of the
  12. * GNU General Public License, as published by the Free Software
  13. * Foundation; either version 2, or (at your option) any later version.
  14. *
  15. * Automated Options is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with Automated Options. See the file "COPYING". If not,
  22. * write to: The Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor,
  24. * Boston, MA 02110-1301, USA.
  25. *
  26. * As a special exception, Bruce Korb gives permission for additional
  27. * uses of the text contained in his release of AutoOpts.
  28. *
  29. * The exception is that, if you link the AutoOpts library with other
  30. * files to produce an executable, this does not by itself cause the
  31. * resulting executable to be covered by the GNU General Public License.
  32. * Your use of that executable is in no way restricted on account of
  33. * linking the AutoOpts library code into it.
  34. *
  35. * This exception does not however invalidate any other reasons why
  36. * the executable file might be covered by the GNU General Public License.
  37. *
  38. * This exception applies only to the code released by Bruce Korb under
  39. * the name AutoOpts. If you copy code from other sources under the
  40. * General Public License into a copy of AutoOpts, as the General Public
  41. * License permits, the exception does not apply to the code that you add
  42. * in this way. To avoid misleading anyone as to the status of such
  43. * modified files, you must delete this exception notice from them.
  44. *
  45. * If you write modifications of your own for AutoOpts, it is your choice
  46. * whether to permit this exception to apply to your modifications.
  47. * If you do not wish that, delete this exception notice.
  48. */
  49. /* = = = START-STATIC-FORWARD = = = */
  50. /* static forward declarations maintained by :mkfwd */
  51. static void
  52. removeBackslashes( char* pzSrc );
  53. static char const*
  54. scanQuotedString( char const* pzTxt );
  55. static tOptionValue*
  56. addStringValue( void** pp, char const* pzName, size_t nameLen,
  57. char const* pzValue, size_t dataLen );
  58. static tOptionValue*
  59. addBoolValue( void** pp, char const* pzName, size_t nameLen,
  60. char const* pzValue, size_t dataLen );
  61. static tOptionValue*
  62. addNumberValue( void** pp, char const* pzName, size_t nameLen,
  63. char const* pzValue, size_t dataLen );
  64. static tOptionValue*
  65. addNestedValue( void** pp, char const* pzName, size_t nameLen,
  66. char* pzValue, size_t dataLen );
  67. static char const*
  68. scanNameEntry(char const* pzName, tOptionValue* pRes);
  69. static char const*
  70. scanXmlEntry( char const* pzName, tOptionValue* pRes );
  71. static void
  72. unloadNestedArglist( tArgList* pAL );
  73. static void
  74. sortNestedList( tArgList* pAL );
  75. /* = = = END-STATIC-FORWARD = = = */
  76. /* removeBackslashes
  77. *
  78. * This function assumes that all newline characters were preceeded by
  79. * backslashes that need removal.
  80. */
  81. static void
  82. removeBackslashes( char* pzSrc )
  83. {
  84. char* pzD = strchr(pzSrc, '\n');
  85. if (pzD == NULL)
  86. return;
  87. *--pzD = '\n';
  88. for (;;) {
  89. char ch = ((*pzD++) = *(pzSrc++));
  90. switch (ch) {
  91. case '\n': *--pzD = ch; break;
  92. case NUL: return;
  93. default:
  94. ;
  95. }
  96. }
  97. }
  98. /* scanQuotedString
  99. *
  100. * Find the end of a quoted string, skipping escaped quote characters.
  101. */
  102. static char const*
  103. scanQuotedString( char const* pzTxt )
  104. {
  105. char q = *(pzTxt++); /* remember the type of quote */
  106. for (;;) {
  107. char ch = *(pzTxt++);
  108. if (ch == NUL)
  109. return pzTxt-1;
  110. if (ch == q)
  111. return pzTxt;
  112. if (ch == '\\') {
  113. ch = *(pzTxt++);
  114. /*
  115. * IF the next character is NUL, drop the backslash, too.
  116. */
  117. if (ch == NUL)
  118. return pzTxt - 2;
  119. /*
  120. * IF the quote character or the escape character were escaped,
  121. * then skip both, as long as the string does not end.
  122. */
  123. if ((ch == q) || (ch == '\\')) {
  124. if (*(pzTxt++) == NUL)
  125. return pzTxt-1;
  126. }
  127. }
  128. }
  129. }
  130. /* addStringValue
  131. *
  132. * Associate a name with either a string or no value.
  133. */
  134. static tOptionValue*
  135. addStringValue( void** pp, char const* pzName, size_t nameLen,
  136. char const* pzValue, size_t dataLen )
  137. {
  138. tOptionValue* pNV;
  139. size_t sz = nameLen + dataLen + sizeof(*pNV);
  140. pNV = AGALOC( sz, "option name/str value pair" );
  141. if (pNV == NULL)
  142. return NULL;
  143. if (pzValue == NULL) {
  144. pNV->valType = OPARG_TYPE_NONE;
  145. pNV->pzName = pNV->v.strVal;
  146. } else {
  147. pNV->valType = OPARG_TYPE_STRING;
  148. if (dataLen > 0)
  149. memcpy( pNV->v.strVal, pzValue, dataLen );
  150. pNV->v.strVal[dataLen] = NUL;
  151. pNV->pzName = pNV->v.strVal + dataLen + 1;
  152. }
  153. memcpy( pNV->pzName, pzName, nameLen );
  154. pNV->pzName[ nameLen ] = NUL;
  155. addArgListEntry( pp, pNV );
  156. return pNV;
  157. }
  158. /* addBoolValue
  159. *
  160. * Associate a name with either a string or no value.
  161. */
  162. static tOptionValue*
  163. addBoolValue( void** pp, char const* pzName, size_t nameLen,
  164. char const* pzValue, size_t dataLen )
  165. {
  166. tOptionValue* pNV;
  167. size_t sz = nameLen + sizeof(*pNV) + 1;
  168. pNV = AGALOC( sz, "option name/bool value pair" );
  169. if (pNV == NULL)
  170. return NULL;
  171. while (isspace( (int)*pzValue ) && (dataLen > 0)) {
  172. dataLen--; pzValue++;
  173. }
  174. if (dataLen == 0)
  175. pNV->v.boolVal = 0;
  176. else if (isdigit( (int)*pzValue ))
  177. pNV->v.boolVal = atoi( pzValue );
  178. else switch (*pzValue) {
  179. case 'f':
  180. case 'F':
  181. case 'n':
  182. case 'N':
  183. pNV->v.boolVal = 0; break;
  184. default:
  185. pNV->v.boolVal = 1;
  186. }
  187. pNV->valType = OPARG_TYPE_BOOLEAN;
  188. pNV->pzName = (char*)(pNV + 1);
  189. memcpy( pNV->pzName, pzName, nameLen );
  190. pNV->pzName[ nameLen ] = NUL;
  191. addArgListEntry( pp, pNV );
  192. return pNV;
  193. }
  194. /* addNumberValue
  195. *
  196. * Associate a name with either a string or no value.
  197. */
  198. static tOptionValue*
  199. addNumberValue( void** pp, char const* pzName, size_t nameLen,
  200. char const* pzValue, size_t dataLen )
  201. {
  202. tOptionValue* pNV;
  203. size_t sz = nameLen + sizeof(*pNV) + 1;
  204. pNV = AGALOC( sz, "option name/bool value pair" );
  205. if (pNV == NULL)
  206. return NULL;
  207. while (isspace( (int)*pzValue ) && (dataLen > 0)) {
  208. dataLen--; pzValue++;
  209. }
  210. if (dataLen == 0)
  211. pNV->v.boolVal = 0;
  212. else
  213. pNV->v.boolVal = atoi( pzValue );
  214. pNV->valType = OPARG_TYPE_NUMERIC;
  215. pNV->pzName = (char*)(pNV + 1);
  216. memcpy( pNV->pzName, pzName, nameLen );
  217. pNV->pzName[ nameLen ] = NUL;
  218. addArgListEntry( pp, pNV );
  219. return pNV;
  220. }
  221. /* addNestedValue
  222. *
  223. * Associate a name with either a string or no value.
  224. */
  225. static tOptionValue*
  226. addNestedValue( void** pp, char const* pzName, size_t nameLen,
  227. char* pzValue, size_t dataLen )
  228. {
  229. tOptionValue* pNV;
  230. if (dataLen == 0) {
  231. size_t sz = nameLen + sizeof(*pNV) + 1;
  232. pNV = AGALOC( sz, "empty nested value pair" );
  233. if (pNV == NULL)
  234. return NULL;
  235. pNV->v.nestVal = NULL;
  236. pNV->valType = OPARG_TYPE_HIERARCHY;
  237. pNV->pzName = (char*)(pNV + 1);
  238. memcpy( pNV->pzName, pzName, nameLen );
  239. pNV->pzName[ nameLen ] = NUL;
  240. } else {
  241. pNV = optionLoadNested( pzValue, pzName, nameLen );
  242. }
  243. if (pNV != NULL)
  244. addArgListEntry( pp, pNV );
  245. return pNV;
  246. }
  247. /* scanNameEntry
  248. *
  249. * We have an entry that starts with a name. Find the end of it, cook it
  250. * (if called for) and create the name/value association.
  251. */
  252. static char const*
  253. scanNameEntry(char const* pzName, tOptionValue* pRes)
  254. {
  255. tOptionValue* pNV;
  256. char const * pzScan = pzName+1;
  257. char const * pzVal;
  258. size_t nameLen = 1;
  259. size_t dataLen = 0;
  260. while (ISNAMECHAR( (int)*pzScan )) { pzScan++; nameLen++; }
  261. while (isspace( (int)*pzScan )) {
  262. char ch = *(pzScan++);
  263. if ((ch == '\n') || (ch == ',')) {
  264. addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL,(size_t)0);
  265. return pzScan - 1;
  266. }
  267. }
  268. switch (*pzScan) {
  269. case '=':
  270. case ':':
  271. while (isspace( (int)*++pzScan )) ;
  272. switch (*pzScan) {
  273. case ',': goto comma_char;
  274. case '"':
  275. case '\'': goto quote_char;
  276. case NUL: goto nul_byte;
  277. default: goto default_char;
  278. }
  279. case ',':
  280. comma_char:
  281. pzScan++;
  282. /* FALLTHROUGH */
  283. case NUL:
  284. nul_byte:
  285. addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
  286. break;
  287. case '"':
  288. case '\'':
  289. quote_char:
  290. pzVal = pzScan;
  291. pzScan = scanQuotedString( pzScan );
  292. dataLen = pzScan - pzVal;
  293. pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal,
  294. dataLen );
  295. if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
  296. ao_string_cook( pNV->v.strVal, NULL );
  297. break;
  298. default:
  299. default_char:
  300. /*
  301. * We have found some strange text value. It ends with a newline
  302. * or a comma.
  303. */
  304. pzVal = pzScan;
  305. for (;;) {
  306. char ch = *(pzScan++);
  307. switch (ch) {
  308. case NUL:
  309. pzScan--;
  310. dataLen = pzScan - pzVal;
  311. goto string_done;
  312. /* FALLTHROUGH */
  313. case '\n':
  314. if ( (pzScan > pzVal + 2)
  315. && (pzScan[-2] == '\\')
  316. && (pzScan[ 0] != NUL))
  317. continue;
  318. /* FALLTHROUGH */
  319. case ',':
  320. dataLen = (pzScan - pzVal) - 1;
  321. string_done:
  322. pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen,
  323. pzVal, dataLen );
  324. if (pNV != NULL)
  325. removeBackslashes( pNV->v.strVal );
  326. goto leave_scan_name;
  327. }
  328. }
  329. break;
  330. } leave_scan_name:;
  331. return pzScan;
  332. }
  333. /* scanXmlEntry
  334. *
  335. * We've found a '<' character. We ignore this if it is a comment or a
  336. * directive. If it is something else, then whatever it is we are looking
  337. * at is bogus. Returning NULL stops processing.
  338. */
  339. static char const*
  340. scanXmlEntry( char const* pzName, tOptionValue* pRes )
  341. {
  342. size_t nameLen = 1, valLen = 0;
  343. char const* pzScan = ++pzName;
  344. char const* pzVal;
  345. tOptionValue valu;
  346. tOptionValue* pNewVal;
  347. tOptionLoadMode save_mode = option_load_mode;
  348. if (! isalpha((int)*pzName)) {
  349. switch (*pzName) {
  350. default:
  351. pzName = NULL;
  352. break;
  353. case '!':
  354. pzName = strstr( pzName, "-->" );
  355. if (pzName != NULL)
  356. pzName += 3;
  357. break;
  358. case '?':
  359. pzName = strchr( pzName, '>' );
  360. if (pzName != NULL)
  361. pzName++;
  362. break;
  363. }
  364. return pzName;
  365. }
  366. while (isalpha( (int)*++pzScan )) nameLen++;
  367. if (nameLen > 64)
  368. return NULL;
  369. valu.valType = OPARG_TYPE_STRING;
  370. switch (*pzScan) {
  371. case ' ':
  372. case '\t':
  373. pzScan = parseAttributes(
  374. NULL, (char*)pzScan, &option_load_mode, &valu );
  375. if (*pzScan == '>') {
  376. pzScan++;
  377. break;
  378. }
  379. if (*pzScan != '/') {
  380. option_load_mode = save_mode;
  381. return NULL;
  382. }
  383. /* FALLTHROUGH */
  384. case '/':
  385. if (*++pzScan != '>') {
  386. option_load_mode = save_mode;
  387. return NULL;
  388. }
  389. addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
  390. option_load_mode = save_mode;
  391. return pzScan+2;
  392. default:
  393. option_load_mode = save_mode;
  394. return NULL;
  395. case '>':
  396. pzScan++;
  397. break;
  398. }
  399. pzVal = pzScan;
  400. {
  401. char z[68];
  402. char* pzD = z;
  403. int ct = nameLen;
  404. char const* pzS = pzName;
  405. *(pzD++) = '<';
  406. *(pzD++) = '/';
  407. do {
  408. *(pzD++) = *(pzS++);
  409. } while (--ct > 0);
  410. *(pzD++) = '>';
  411. *pzD = NUL;
  412. pzScan = strstr( pzScan, z );
  413. if (pzScan == NULL) {
  414. option_load_mode = save_mode;
  415. return NULL;
  416. }
  417. valLen = (pzScan - pzVal);
  418. pzScan += nameLen + 3;
  419. while (isspace( (int)*pzScan )) pzScan++;
  420. }
  421. switch (valu.valType) {
  422. case OPARG_TYPE_NONE:
  423. addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
  424. break;
  425. case OPARG_TYPE_STRING:
  426. pNewVal = addStringValue(
  427. &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
  428. if (option_load_mode == OPTION_LOAD_KEEP)
  429. break;
  430. mungeString( pNewVal->v.strVal, option_load_mode );
  431. break;
  432. case OPARG_TYPE_BOOLEAN:
  433. addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
  434. break;
  435. case OPARG_TYPE_NUMERIC:
  436. addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
  437. break;
  438. case OPARG_TYPE_HIERARCHY:
  439. {
  440. char* pz = AGALOC( valLen+1, "hierarchical scan" );
  441. if (pz == NULL)
  442. break;
  443. memcpy( pz, pzVal, valLen );
  444. pz[valLen] = NUL;
  445. addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen );
  446. AGFREE(pz);
  447. break;
  448. }
  449. case OPARG_TYPE_ENUMERATION:
  450. case OPARG_TYPE_MEMBERSHIP:
  451. default:
  452. break;
  453. }
  454. option_load_mode = save_mode;
  455. return pzScan;
  456. }
  457. /* unloadNestedArglist
  458. *
  459. * Deallocate a list of option arguments. This must have been gotten from
  460. * a hierarchical option argument, not a stacked list of strings. It is
  461. * an internal call, so it is not validated. The caller is responsible for
  462. * knowing what they are doing.
  463. */
  464. static void
  465. unloadNestedArglist( tArgList* pAL )
  466. {
  467. int ct = pAL->useCt;
  468. tCC** ppNV = pAL->apzArgs;
  469. while (ct-- > 0) {
  470. tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
  471. if (pNV->valType == OPARG_TYPE_HIERARCHY)
  472. unloadNestedArglist( pNV->v.nestVal );
  473. AGFREE( pNV );
  474. }
  475. AGFREE( (void*)pAL );
  476. }
  477. /*=export_func optionUnloadNested
  478. *
  479. * what: Deallocate the memory for a nested value
  480. * arg: + tOptionValue const * + pOptVal + the hierarchical value +
  481. *
  482. * doc:
  483. * A nested value needs to be deallocated. The pointer passed in should
  484. * have been gotten from a call to @code{configFileLoad()} (See
  485. * @pxref{libopts-configFileLoad}).
  486. =*/
  487. void
  488. optionUnloadNested( tOptionValue const * pOV )
  489. {
  490. if (pOV == NULL) return;
  491. if (pOV->valType != OPARG_TYPE_HIERARCHY) {
  492. errno = EINVAL;
  493. return;
  494. }
  495. unloadNestedArglist( pOV->v.nestVal );
  496. AGFREE( (void*)pOV );
  497. }
  498. /* sortNestedList
  499. *
  500. * This is a _stable_ sort. The entries are sorted alphabetically,
  501. * but within entries of the same name the ordering is unchanged.
  502. * Typically, we also hope the input is sorted.
  503. */
  504. static void
  505. sortNestedList( tArgList* pAL )
  506. {
  507. int ix;
  508. int lm = pAL->useCt;
  509. /*
  510. * This loop iterates "useCt" - 1 times.
  511. */
  512. for (ix = 0; ++ix < lm;) {
  513. int iy = ix-1;
  514. tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
  515. tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
  516. /*
  517. * For as long as the new entry precedes the "old" entry,
  518. * move the old pointer. Stop before trying to extract the
  519. * "-1" entry.
  520. */
  521. while (strcmp( pOldNV->pzName, pNewNV->pzName ) > 0) {
  522. pAL->apzArgs[iy+1] = (void*)pOldNV;
  523. pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
  524. if (iy < 0)
  525. break;
  526. }
  527. /*
  528. * Always store the pointer. Sometimes it is redundant,
  529. * but the redundancy is cheaper than a test and branch sequence.
  530. */
  531. pAL->apzArgs[iy+1] = (void*)pNewNV;
  532. }
  533. }
  534. /* optionLoadNested
  535. * private:
  536. *
  537. * what: parse a hierarchical option argument
  538. * arg: + char const* + pzTxt + the text to scan +
  539. * arg: + char const* + pzName + the name for the text +
  540. * arg: + size_t + nameLen + the length of "name" +
  541. *
  542. * ret_type: tOptionValue*
  543. * ret_desc: An allocated, compound value structure
  544. *
  545. * doc:
  546. * A block of text represents a series of values. It may be an
  547. * entire configuration file, or it may be an argument to an
  548. * option that takes a hierarchical value.
  549. */
  550. LOCAL tOptionValue*
  551. optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
  552. {
  553. tOptionValue* pRes;
  554. tArgList* pAL;
  555. /*
  556. * Make sure we have some data and we have space to put what we find.
  557. */
  558. if (pzTxt == NULL) {
  559. errno = EINVAL;
  560. return NULL;
  561. }
  562. while (isspace( (int)*pzTxt )) pzTxt++;
  563. if (*pzTxt == NUL) {
  564. errno = ENOENT;
  565. return NULL;
  566. }
  567. pRes = AGALOC( sizeof(*pRes) + nameLen + 1, "nested args" );
  568. if (pRes == NULL) {
  569. errno = ENOMEM;
  570. return NULL;
  571. }
  572. pRes->valType = OPARG_TYPE_HIERARCHY;
  573. pRes->pzName = (char*)(pRes + 1);
  574. memcpy( pRes->pzName, pzName, nameLen );
  575. pRes->pzName[ nameLen ] = NUL;
  576. pAL = AGALOC( sizeof(*pAL), "nested arg list" );
  577. if (pAL == NULL) {
  578. AGFREE( pRes );
  579. return NULL;
  580. }
  581. pRes->v.nestVal = pAL;
  582. pAL->useCt = 0;
  583. pAL->allocCt = MIN_ARG_ALLOC_CT;
  584. /*
  585. * Scan until we hit a NUL.
  586. */
  587. do {
  588. while (isspace( (int)*pzTxt )) pzTxt++;
  589. if (isalpha( (int)*pzTxt )) {
  590. pzTxt = scanNameEntry( pzTxt, pRes );
  591. }
  592. else switch (*pzTxt) {
  593. case NUL: goto scan_done;
  594. case '<': pzTxt = scanXmlEntry( pzTxt, pRes );
  595. if (*pzTxt == ',') pzTxt++; break;
  596. case '#': pzTxt = strchr( pzTxt, '\n' ); break;
  597. default: goto woops;
  598. }
  599. } while (pzTxt != NULL); scan_done:;
  600. pAL = pRes->v.nestVal;
  601. if (pAL->useCt != 0) {
  602. sortNestedList( pAL );
  603. return pRes;
  604. }
  605. woops:
  606. AGFREE( pRes->v.nestVal );
  607. AGFREE( pRes );
  608. return NULL;
  609. }
  610. /*=export_func optionNestedVal
  611. * private:
  612. *
  613. * what: parse a hierarchical option argument
  614. * arg: + tOptions* + pOpts + program options descriptor +
  615. * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
  616. *
  617. * doc:
  618. * Nested value was found on the command line
  619. =*/
  620. void
  621. optionNestedVal( tOptions* pOpts, tOptDesc* pOD )
  622. {
  623. tOptionValue* pOV = optionLoadNested(
  624. pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
  625. if (pOV != NULL)
  626. addArgListEntry( &(pOD->optCookie), (void*)pOV );
  627. }
  628. /*
  629. * Local Variables:
  630. * mode: C
  631. * c-file-style: "stroustrup"
  632. * indent-tabs-mode: nil
  633. * End:
  634. * end of autoopts/nested.c */