testmxml.c 21 KB

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