testmxml.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. /*
  2. * "$Id: testmxml.c 466 2016-06-13 00:27:11Z msweet $"
  3. *
  4. * Test program for Mini-XML, a small XML-like file parsing library.
  5. *
  6. * Usage:
  7. *
  8. * ./testmxml input.xml [string-output.xml] >stdio-output.xml
  9. * ./testmxml "<?xml ..." [string-output.xml] >stdio-output.xml
  10. *
  11. * Copyright 2003-2016 by Michael R Sweet.
  12. *
  13. * These coded instructions, statements, and computer programs are the
  14. * property of Michael R Sweet and are protected by Federal copyright
  15. * law. Distribution and use rights are outlined in the file "COPYING"
  16. * which should have been included with this file. If this file is
  17. * missing or damaged, see the license at:
  18. *
  19. * http://www.msweet.org/projects.php/Mini-XML
  20. */
  21. /*
  22. * Include necessary headers...
  23. */
  24. #include "config.h"
  25. #include "mxml.h"
  26. #ifndef WIN32
  27. # include <unistd.h>
  28. #endif /* !WIN32 */
  29. #include <fcntl.h>
  30. #ifndef O_BINARY
  31. # define O_BINARY 0
  32. #endif /* !O_BINARY */
  33. /*
  34. * Globals...
  35. */
  36. int event_counts[6];
  37. /*
  38. * Local functions...
  39. */
  40. void sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *data);
  41. mxml_type_t type_cb(mxml_node_t *node);
  42. const char *whitespace_cb(mxml_node_t *node, int where);
  43. /*
  44. * 'main()' - Main entry for test program.
  45. */
  46. int /* O - Exit status */
  47. main(int argc, /* I - Number of command-line args */
  48. char *argv[]) /* I - Command-line args */
  49. {
  50. int i; /* Looping var */
  51. FILE *fp; /* File to read */
  52. int fd; /* File descriptor */
  53. mxml_node_t *tree, /* XML tree */
  54. *node; /* Node which should be in test.xml */
  55. mxml_index_t *ind; /* XML index */
  56. char buffer[16384]; /* Save string */
  57. static const char *types[] = /* Strings for node types */
  58. {
  59. "MXML_ELEMENT",
  60. "MXML_INTEGER",
  61. "MXML_OPAQUE",
  62. "MXML_REAL",
  63. "MXML_TEXT"
  64. };
  65. /*
  66. * Check arguments...
  67. */
  68. if (argc != 2 && argc != 3)
  69. {
  70. fputs("Usage: testmxml filename.xml [string-output.xml]\n", stderr);
  71. return (1);
  72. }
  73. /*
  74. * Test the basic functionality...
  75. */
  76. tree = mxmlNewElement(MXML_NO_PARENT, "element");
  77. if (!tree)
  78. {
  79. fputs("ERROR: No parent node in basic test.\n", stderr);
  80. return (1);
  81. }
  82. if (tree->type != MXML_ELEMENT)
  83. {
  84. fprintf(stderr, "ERROR: Parent has type %s (%d), expected MXML_ELEMENT.\n",
  85. tree->type < MXML_ELEMENT || tree->type > MXML_TEXT ?
  86. "UNKNOWN" : types[tree->type], tree->type);
  87. mxmlDelete(tree);
  88. return (1);
  89. }
  90. if (strcmp(tree->value.element.name, "element"))
  91. {
  92. fprintf(stderr, "ERROR: Parent value is \"%s\", expected \"element\".\n",
  93. tree->value.element.name);
  94. mxmlDelete(tree);
  95. return (1);
  96. }
  97. mxmlNewInteger(tree, 123);
  98. mxmlNewOpaque(tree, "opaque");
  99. mxmlNewReal(tree, 123.4f);
  100. mxmlNewText(tree, 1, "text");
  101. mxmlLoadString(tree, "<group type='string'>string string string</group>",
  102. MXML_NO_CALLBACK);
  103. mxmlLoadString(tree, "<group type='integer'>1 2 3</group>",
  104. MXML_INTEGER_CALLBACK);
  105. mxmlLoadString(tree, "<group type='real'>1.0 2.0 3.0</group>",
  106. MXML_REAL_CALLBACK);
  107. mxmlLoadString(tree, "<group>opaque opaque opaque</group>",
  108. MXML_OPAQUE_CALLBACK);
  109. mxmlLoadString(tree, "<foo><bar><one><two>value<two>value2</two></two></one>"
  110. "</bar></foo>", MXML_OPAQUE_CALLBACK);
  111. node = tree->child;
  112. if (!node)
  113. {
  114. fputs("ERROR: No first child node in basic test.\n", stderr);
  115. mxmlDelete(tree);
  116. return (1);
  117. }
  118. if (node->type != MXML_INTEGER)
  119. {
  120. fprintf(stderr, "ERROR: First child has type %s (%d), expected MXML_INTEGER.\n",
  121. node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
  122. "UNKNOWN" : types[node->type], node->type);
  123. mxmlDelete(tree);
  124. return (1);
  125. }
  126. if (node->value.integer != 123)
  127. {
  128. fprintf(stderr, "ERROR: First child value is %d, expected 123.\n",
  129. node->value.integer);
  130. mxmlDelete(tree);
  131. return (1);
  132. }
  133. node = node->next;
  134. if (!node)
  135. {
  136. fputs("ERROR: No second child node in basic test.\n", stderr);
  137. mxmlDelete(tree);
  138. return (1);
  139. }
  140. if (node->type != MXML_OPAQUE)
  141. {
  142. fprintf(stderr, "ERROR: Second child has type %s (%d), expected MXML_OPAQUE.\n",
  143. node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
  144. "UNKNOWN" : types[node->type], node->type);
  145. mxmlDelete(tree);
  146. return (1);
  147. }
  148. if (!node->value.opaque || strcmp(node->value.opaque, "opaque"))
  149. {
  150. fprintf(stderr, "ERROR: Second child value is \"%s\", expected \"opaque\".\n",
  151. node->value.opaque ? node->value.opaque : "(null)");
  152. mxmlDelete(tree);
  153. return (1);
  154. }
  155. node = node->next;
  156. if (!node)
  157. {
  158. fputs("ERROR: No third child node in basic test.\n", stderr);
  159. mxmlDelete(tree);
  160. return (1);
  161. }
  162. if (node->type != MXML_REAL)
  163. {
  164. fprintf(stderr, "ERROR: Third child has type %s (%d), expected MXML_REAL.\n",
  165. node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
  166. "UNKNOWN" : types[node->type], node->type);
  167. mxmlDelete(tree);
  168. return (1);
  169. }
  170. if (node->value.real != 123.4f)
  171. {
  172. fprintf(stderr, "ERROR: Third child value is %f, expected 123.4.\n",
  173. node->value.real);
  174. mxmlDelete(tree);
  175. return (1);
  176. }
  177. node = node->next;
  178. if (!node)
  179. {
  180. fputs("ERROR: No fourth child node in basic test.\n", stderr);
  181. mxmlDelete(tree);
  182. return (1);
  183. }
  184. if (node->type != MXML_TEXT)
  185. {
  186. fprintf(stderr, "ERROR: Fourth child has type %s (%d), expected MXML_TEXT.\n",
  187. node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
  188. "UNKNOWN" : types[node->type], node->type);
  189. mxmlDelete(tree);
  190. return (1);
  191. }
  192. if (!node->value.text.whitespace ||
  193. !node->value.text.string || strcmp(node->value.text.string, "text"))
  194. {
  195. fprintf(stderr, "ERROR: Fourth child value is %d,\"%s\", expected 1,\"text\".\n",
  196. node->value.text.whitespace,
  197. node->value.text.string ? node->value.text.string : "(null)");
  198. mxmlDelete(tree);
  199. return (1);
  200. }
  201. for (i = 0; i < 4; i ++)
  202. {
  203. node = node->next;
  204. if (!node)
  205. {
  206. fprintf(stderr, "ERROR: No group #%d child node in basic test.\n", i + 1);
  207. mxmlDelete(tree);
  208. return (1);
  209. }
  210. if (node->type != MXML_ELEMENT)
  211. {
  212. fprintf(stderr, "ERROR: Group child #%d has type %s (%d), expected MXML_ELEMENT.\n",
  213. i + 1, node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
  214. "UNKNOWN" : types[node->type], node->type);
  215. mxmlDelete(tree);
  216. return (1);
  217. }
  218. }
  219. /*
  220. * Test mxmlFindPath...
  221. */
  222. node = mxmlFindPath(tree, "*/two");
  223. if (!node)
  224. {
  225. fputs("ERROR: Unable to find value for \"*/two\".\n", stderr);
  226. mxmlDelete(tree);
  227. return (1);
  228. }
  229. else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value"))
  230. {
  231. fputs("ERROR: Bad value for \"*/two\".\n", stderr);
  232. mxmlDelete(tree);
  233. return (1);
  234. }
  235. node = mxmlFindPath(tree, "foo/*/two");
  236. if (!node)
  237. {
  238. fputs("ERROR: Unable to find value for \"foo/*/two\".\n", stderr);
  239. mxmlDelete(tree);
  240. return (1);
  241. }
  242. else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value"))
  243. {
  244. fputs("ERROR: Bad value for \"foo/*/two\".\n", stderr);
  245. mxmlDelete(tree);
  246. return (1);
  247. }
  248. node = mxmlFindPath(tree, "foo/bar/one/two");
  249. if (!node)
  250. {
  251. fputs("ERROR: Unable to find value for \"foo/bar/one/two\".\n", stderr);
  252. mxmlDelete(tree);
  253. return (1);
  254. }
  255. else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value"))
  256. {
  257. fputs("ERROR: Bad value for \"foo/bar/one/two\".\n", stderr);
  258. mxmlDelete(tree);
  259. return (1);
  260. }
  261. /*
  262. * Test indices...
  263. */
  264. ind = mxmlIndexNew(tree, NULL, NULL);
  265. if (!ind)
  266. {
  267. fputs("ERROR: Unable to create index of all nodes.\n", stderr);
  268. mxmlDelete(tree);
  269. return (1);
  270. }
  271. if (ind->num_nodes != 10)
  272. {
  273. fprintf(stderr, "ERROR: Index of all nodes contains %d "
  274. "nodes; expected 10.\n", ind->num_nodes);
  275. mxmlIndexDelete(ind);
  276. mxmlDelete(tree);
  277. return (1);
  278. }
  279. mxmlIndexReset(ind);
  280. if (!mxmlIndexFind(ind, "group", NULL))
  281. {
  282. fputs("ERROR: mxmlIndexFind for \"group\" failed.\n", stderr);
  283. mxmlIndexDelete(ind);
  284. mxmlDelete(tree);
  285. return (1);
  286. }
  287. mxmlIndexDelete(ind);
  288. ind = mxmlIndexNew(tree, "group", NULL);
  289. if (!ind)
  290. {
  291. fputs("ERROR: Unable to create index of groups.\n", stderr);
  292. mxmlDelete(tree);
  293. return (1);
  294. }
  295. if (ind->num_nodes != 4)
  296. {
  297. fprintf(stderr, "ERROR: Index of groups contains %d "
  298. "nodes; expected 4.\n", ind->num_nodes);
  299. mxmlIndexDelete(ind);
  300. mxmlDelete(tree);
  301. return (1);
  302. }
  303. mxmlIndexReset(ind);
  304. if (!mxmlIndexEnum(ind))
  305. {
  306. fputs("ERROR: mxmlIndexEnum failed.\n", stderr);
  307. mxmlIndexDelete(ind);
  308. mxmlDelete(tree);
  309. return (1);
  310. }
  311. mxmlIndexDelete(ind);
  312. ind = mxmlIndexNew(tree, NULL, "type");
  313. if (!ind)
  314. {
  315. fputs("ERROR: Unable to create index of type attributes.\n", stderr);
  316. mxmlDelete(tree);
  317. return (1);
  318. }
  319. if (ind->num_nodes != 3)
  320. {
  321. fprintf(stderr, "ERROR: Index of type attributes contains %d "
  322. "nodes; expected 3.\n", ind->num_nodes);
  323. mxmlIndexDelete(ind);
  324. mxmlDelete(tree);
  325. return (1);
  326. }
  327. mxmlIndexReset(ind);
  328. if (!mxmlIndexFind(ind, NULL, "string"))
  329. {
  330. fputs("ERROR: mxmlIndexFind for \"string\" failed.\n", stderr);
  331. mxmlIndexDelete(ind);
  332. mxmlDelete(tree);
  333. return (1);
  334. }
  335. mxmlIndexDelete(ind);
  336. ind = mxmlIndexNew(tree, "group", "type");
  337. if (!ind)
  338. {
  339. fputs("ERROR: Unable to create index of elements and attributes.\n", stderr);
  340. mxmlDelete(tree);
  341. return (1);
  342. }
  343. if (ind->num_nodes != 3)
  344. {
  345. fprintf(stderr, "ERROR: Index of elements and attributes contains %d "
  346. "nodes; expected 3.\n", ind->num_nodes);
  347. mxmlIndexDelete(ind);
  348. mxmlDelete(tree);
  349. return (1);
  350. }
  351. mxmlIndexReset(ind);
  352. if (!mxmlIndexFind(ind, "group", "string"))
  353. {
  354. fputs("ERROR: mxmlIndexFind for \"string\" failed.\n", stderr);
  355. mxmlIndexDelete(ind);
  356. mxmlDelete(tree);
  357. return (1);
  358. }
  359. mxmlIndexDelete(ind);
  360. /*
  361. * Check the mxmlDelete() works properly...
  362. */
  363. for (i = 0; i < 9; i ++)
  364. {
  365. if (tree->child)
  366. mxmlDelete(tree->child);
  367. else
  368. {
  369. fprintf(stderr, "ERROR: Child pointer prematurely NULL on child #%d\n",
  370. i + 1);
  371. mxmlDelete(tree);
  372. return (1);
  373. }
  374. }
  375. if (tree->child)
  376. {
  377. fputs("ERROR: Child pointer not NULL after deleting all children.\n", stderr);
  378. return (1);
  379. }
  380. if (tree->last_child)
  381. {
  382. fputs("ERROR: Last child pointer not NULL after deleting all children.\n", stderr);
  383. return (1);
  384. }
  385. mxmlDelete(tree);
  386. /*
  387. * Open the file/string using the default (MXML_NO_CALLBACK) callback...
  388. */
  389. if (argv[1][0] == '<')
  390. tree = mxmlLoadString(NULL, argv[1], MXML_NO_CALLBACK);
  391. else if ((fp = fopen(argv[1], "rb")) == NULL)
  392. {
  393. perror(argv[1]);
  394. return (1);
  395. }
  396. else
  397. {
  398. /*
  399. * Read the file...
  400. */
  401. tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);
  402. fclose(fp);
  403. }
  404. if (!tree)
  405. {
  406. fputs("Unable to read XML file with default callback.\n", stderr);
  407. return (1);
  408. }
  409. if (!strcmp(argv[1], "test.xml"))
  410. {
  411. const char *text; /* Text value */
  412. /*
  413. * Verify that mxmlFindElement() and indirectly mxmlWalkNext() work
  414. * properly...
  415. */
  416. if ((node = mxmlFindPath(tree, "group/option/keyword")) == NULL)
  417. {
  418. fputs("Unable to find group/option/keyword element in XML tree.\n", stderr);
  419. mxmlDelete(tree);
  420. return (1);
  421. }
  422. if (node->type != MXML_TEXT)
  423. {
  424. fputs("No child node of group/option/keyword.\n", stderr);
  425. mxmlSaveFile(tree, stderr, MXML_NO_CALLBACK);
  426. mxmlDelete(tree);
  427. return (1);
  428. }
  429. if ((text = mxmlGetText(node, NULL)) == NULL || strcmp(text, "InputSlot"))
  430. {
  431. fprintf(stderr, "Child node of group/option/value has value \"%s\" instead of \"InputSlot\".\n", text ? text : "(null)");
  432. mxmlDelete(tree);
  433. return (1);
  434. }
  435. }
  436. mxmlDelete(tree);
  437. /*
  438. * Open the file...
  439. */
  440. if (argv[1][0] == '<')
  441. tree = mxmlLoadString(NULL, argv[1], type_cb);
  442. else if ((fp = fopen(argv[1], "rb")) == NULL)
  443. {
  444. perror(argv[1]);
  445. return (1);
  446. }
  447. else
  448. {
  449. /*
  450. * Read the file...
  451. */
  452. tree = mxmlLoadFile(NULL, fp, type_cb);
  453. fclose(fp);
  454. }
  455. if (!tree)
  456. {
  457. fputs("Unable to read XML file.\n", stderr);
  458. return (1);
  459. }
  460. if (!strcmp(argv[1], "test.xml"))
  461. {
  462. /*
  463. * Verify that mxmlFindElement() and indirectly mxmlWalkNext() work
  464. * properly...
  465. */
  466. if ((node = mxmlFindElement(tree, tree, "choice", NULL, NULL,
  467. MXML_DESCEND)) == NULL)
  468. {
  469. fputs("Unable to find first <choice> element in XML tree.\n", stderr);
  470. mxmlDelete(tree);
  471. return (1);
  472. }
  473. if (!mxmlFindElement(node, tree, "choice", NULL, NULL, MXML_NO_DESCEND))
  474. {
  475. fputs("Unable to find second <choice> element in XML tree.\n", stderr);
  476. mxmlDelete(tree);
  477. return (1);
  478. }
  479. }
  480. /*
  481. * Print the XML tree...
  482. */
  483. mxmlSaveFile(tree, stdout, whitespace_cb);
  484. /*
  485. * Save the XML tree to a string and print it...
  486. */
  487. if (mxmlSaveString(tree, buffer, sizeof(buffer), whitespace_cb) > 0)
  488. {
  489. if (argc == 3)
  490. {
  491. fp = fopen(argv[2], "w");
  492. fputs(buffer, fp);
  493. fclose(fp);
  494. }
  495. }
  496. /*
  497. * Delete the tree...
  498. */
  499. mxmlDelete(tree);
  500. /*
  501. * Read from/write to file descriptors...
  502. */
  503. if (argv[1][0] != '<')
  504. {
  505. /*
  506. * Open the file again...
  507. */
  508. if ((fd = open(argv[1], O_RDONLY | O_BINARY)) < 0)
  509. {
  510. perror(argv[1]);
  511. return (1);
  512. }
  513. /*
  514. * Read the file...
  515. */
  516. tree = mxmlLoadFd(NULL, fd, type_cb);
  517. close(fd);
  518. /*
  519. * Create filename.xmlfd...
  520. */
  521. snprintf(buffer, sizeof(buffer), "%sfd", argv[1]);
  522. if ((fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) < 0)
  523. {
  524. perror(buffer);
  525. mxmlDelete(tree);
  526. return (1);
  527. }
  528. /*
  529. * Write the file...
  530. */
  531. mxmlSaveFd(tree, fd, whitespace_cb);
  532. close(fd);
  533. /*
  534. * Delete the tree...
  535. */
  536. mxmlDelete(tree);
  537. }
  538. /*
  539. * Test SAX methods...
  540. */
  541. memset(event_counts, 0, sizeof(event_counts));
  542. if (argv[1][0] == '<')
  543. mxmlSAXLoadString(NULL, argv[1], type_cb, sax_cb, NULL);
  544. else if ((fp = fopen(argv[1], "rb")) == NULL)
  545. {
  546. perror(argv[1]);
  547. return (1);
  548. }
  549. else
  550. {
  551. /*
  552. * Read the file...
  553. */
  554. mxmlSAXLoadFile(NULL, fp, type_cb, sax_cb, NULL);
  555. fclose(fp);
  556. }
  557. if (!strcmp(argv[1], "test.xml"))
  558. {
  559. if (event_counts[MXML_SAX_CDATA] != 1)
  560. {
  561. fprintf(stderr, "MXML_SAX_CDATA seen %d times, expected 1 times.\n",
  562. event_counts[MXML_SAX_CDATA]);
  563. return (1);
  564. }
  565. if (event_counts[MXML_SAX_COMMENT] != 1)
  566. {
  567. fprintf(stderr, "MXML_SAX_COMMENT seen %d times, expected 1 times.\n",
  568. event_counts[MXML_SAX_COMMENT]);
  569. return (1);
  570. }
  571. if (event_counts[MXML_SAX_DATA] != 60)
  572. {
  573. fprintf(stderr, "MXML_SAX_DATA seen %d times, expected 60 times.\n",
  574. event_counts[MXML_SAX_DATA]);
  575. return (1);
  576. }
  577. if (event_counts[MXML_SAX_DIRECTIVE] != 1)
  578. {
  579. fprintf(stderr, "MXML_SAX_DIRECTIVE seen %d times, expected 1 times.\n",
  580. event_counts[MXML_SAX_DIRECTIVE]);
  581. return (1);
  582. }
  583. if (event_counts[MXML_SAX_ELEMENT_CLOSE] != 20)
  584. {
  585. fprintf(stderr, "MXML_SAX_ELEMENT_CLOSE seen %d times, expected 20 times.\n",
  586. event_counts[MXML_SAX_ELEMENT_CLOSE]);
  587. return (1);
  588. }
  589. if (event_counts[MXML_SAX_ELEMENT_OPEN] != 20)
  590. {
  591. fprintf(stderr, "MXML_SAX_ELEMENT_OPEN seen %d times, expected 20 times.\n",
  592. event_counts[MXML_SAX_ELEMENT_OPEN]);
  593. return (1);
  594. }
  595. }
  596. /*
  597. * Return...
  598. */
  599. return (0);
  600. }
  601. /*
  602. * 'sax_cb()' - Process nodes via SAX.
  603. */
  604. void
  605. sax_cb(mxml_node_t *node, /* I - Current node */
  606. mxml_sax_event_t event, /* I - SAX event */
  607. void *data) /* I - SAX user data */
  608. {
  609. static const char * const events[] = /* Events */
  610. {
  611. "MXML_SAX_CDATA", /* CDATA node */
  612. "MXML_SAX_COMMENT", /* Comment node */
  613. "MXML_SAX_DATA", /* Data node */
  614. "MXML_SAX_DIRECTIVE", /* Processing directive node */
  615. "MXML_SAX_ELEMENT_CLOSE", /* Element closed */
  616. "MXML_SAX_ELEMENT_OPEN" /* Element opened */
  617. };
  618. /*
  619. * This SAX callback just counts the different events.
  620. */
  621. if (!node)
  622. fprintf(stderr, "ERROR: SAX callback for event %s has NULL node.\n", events[event]);
  623. event_counts[event] ++;
  624. }
  625. /*
  626. * 'type_cb()' - XML data type callback for mxmlLoadFile()...
  627. */
  628. mxml_type_t /* O - Data type */
  629. type_cb(mxml_node_t *node) /* I - Element node */
  630. {
  631. const char *type; /* Type string */
  632. /*
  633. * You can lookup attributes and/or use the element name, hierarchy, etc...
  634. */
  635. if ((type = mxmlElementGetAttr(node, "type")) == NULL)
  636. type = node->value.element.name;
  637. if (!strcmp(type, "integer"))
  638. return (MXML_INTEGER);
  639. else if (!strcmp(type, "opaque") || !strcmp(type, "pre"))
  640. return (MXML_OPAQUE);
  641. else if (!strcmp(type, "real"))
  642. return (MXML_REAL);
  643. else
  644. return (MXML_TEXT);
  645. }
  646. /*
  647. * 'whitespace_cb()' - Let the mxmlSaveFile() function know when to insert
  648. * newlines and tabs...
  649. */
  650. const char * /* O - Whitespace string or NULL */
  651. whitespace_cb(mxml_node_t *node, /* I - Element node */
  652. int where) /* I - Open or close tag? */
  653. {
  654. mxml_node_t *parent; /* Parent node */
  655. int level; /* Indentation level */
  656. const char *name; /* Name of element */
  657. static const char *tabs = "\t\t\t\t\t\t\t\t";
  658. /* Tabs for indentation */
  659. /*
  660. * We can conditionally break to a new line before or after any element.
  661. * These are just common HTML elements...
  662. */
  663. name = node->value.element.name;
  664. if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") ||
  665. !strcmp(name, "pre") || !strcmp(name, "p") ||
  666. !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") ||
  667. !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6"))
  668. {
  669. /*
  670. * Newlines before open and after close...
  671. */
  672. if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE)
  673. return ("\n");
  674. }
  675. else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul"))
  676. {
  677. /*
  678. * Put a newline before and after list elements...
  679. */
  680. return ("\n");
  681. }
  682. else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li"))
  683. {
  684. /*
  685. * Put a tab before <li>'s, <dd>'s, and <dt>'s, and a newline after them...
  686. */
  687. if (where == MXML_WS_BEFORE_OPEN)
  688. return ("\t");
  689. else if (where == MXML_WS_AFTER_CLOSE)
  690. return ("\n");
  691. }
  692. else if (!strncmp(name, "?xml", 4))
  693. {
  694. if (where == MXML_WS_AFTER_OPEN)
  695. return ("\n");
  696. else
  697. return (NULL);
  698. }
  699. else if (where == MXML_WS_BEFORE_OPEN ||
  700. ((!strcmp(name, "choice") || !strcmp(name, "option")) &&
  701. where == MXML_WS_BEFORE_CLOSE))
  702. {
  703. for (level = -1, parent = node->parent;
  704. parent;
  705. level ++, parent = parent->parent);
  706. if (level > 8)
  707. level = 8;
  708. else if (level < 0)
  709. level = 0;
  710. return (tabs + 8 - level);
  711. }
  712. else if (where == MXML_WS_AFTER_CLOSE ||
  713. ((!strcmp(name, "group") || !strcmp(name, "option") ||
  714. !strcmp(name, "choice")) &&
  715. where == MXML_WS_AFTER_OPEN))
  716. return ("\n");
  717. else if (where == MXML_WS_AFTER_OPEN && !node->child)
  718. return ("\n");
  719. /*
  720. * Return NULL for no added whitespace...
  721. */
  722. return (NULL);
  723. }
  724. /*
  725. * End of "$Id: testmxml.c 466 2016-06-13 00:27:11Z msweet $".
  726. */