nested.c 18 KB

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