testmxml.c 19.7 KB
Newer Older
Michael R Sweet's avatar
Michael R Sweet committed
1
/*
Michael Sweet's avatar
Michael Sweet committed
2
 * Test program for Mini-XML, a small XML file parsing library.
Michael R Sweet's avatar
Michael R Sweet committed
3
 *
4 5 6 7 8
 * Usage:
 *
 *   ./testmxml input.xml [string-output.xml] >stdio-output.xml
 *   ./testmxml "<?xml ..." [string-output.xml] >stdio-output.xml
 *
Michael Sweet's avatar
Michael Sweet committed
9
 * Copyright 2003-2017 by Michael R Sweet.
Michael R Sweet's avatar
Michael R Sweet committed
10
 *
11 12 13 14 15
 * These coded instructions, statements, and computer programs are the
 * property of Michael R Sweet and are protected by Federal copyright
 * law.  Distribution and use rights are outlined in the file "COPYING"
 * which should have been included with this file.  If this file is
 * missing or damaged, see the license at:
Michael R Sweet's avatar
Michael R Sweet committed
16
 *
Michael Sweet's avatar
Michael Sweet committed
17
 *     https://michaelrsweet.github.io/mxml
Michael R Sweet's avatar
Michael R Sweet committed
18 19 20 21 22 23
 */

/*
 * Include necessary headers...
 */

24
#include "config.h"
25
#include "mxml.h"
26
#ifndef WIN32
27
#  include <unistd.h>
28
#endif /* !WIN32 */
29 30 31 32
#include <fcntl.h>
#ifndef O_BINARY
#  define O_BINARY 0
#endif /* !O_BINARY */
Michael R Sweet's avatar
Michael R Sweet committed
33 34


35 36 37 38 39 40 41
/*
 * Globals...
 */

int		event_counts[6];


Michael R Sweet's avatar
Michael R Sweet committed
42 43 44 45
/*
 * Local functions...
 */

46
void		sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *data);
Michael R Sweet's avatar
Michael R Sweet committed
47
mxml_type_t	type_cb(mxml_node_t *node);
48
const char	*whitespace_cb(mxml_node_t *node, int where);
Michael R Sweet's avatar
Michael R Sweet committed
49 50 51 52 53 54 55 56 57 58


/*
 * 'main()' - Main entry for test program.
 */

int					/* O - Exit status */
main(int  argc,				/* I - Number of command-line args */
     char *argv[])			/* I - Command-line args */
{
59
  int			i;		/* Looping var */
60
  FILE			*fp;		/* File to read */
61
  int			fd;		/* File descriptor */
62 63
  mxml_node_t		*tree,		/* XML tree */
			*node;		/* Node which should be in test.xml */
64
  mxml_index_t		*ind;		/* XML index */
65
  char			buffer[16384];	/* Save string */
66 67 68 69 70 71 72 73
  static const char	*types[] =	/* Strings for node types */
			{
			  "MXML_ELEMENT",
			  "MXML_INTEGER",
			  "MXML_OPAQUE",
			  "MXML_REAL",
			  "MXML_TEXT"
			};
Michael R Sweet's avatar
Michael R Sweet committed
74 75 76 77 78 79


 /*
  * Check arguments...
  */

80
  if (argc != 2 && argc != 3)
Michael R Sweet's avatar
Michael R Sweet committed
81
  {
82
    fputs("Usage: testmxml filename.xml [string-output.xml]\n", stderr);
Michael R Sweet's avatar
Michael R Sweet committed
83 84 85
    return (1);
  }

86 87 88 89 90 91 92 93
 /*
  * Test the basic functionality...
  */

  tree = mxmlNewElement(MXML_NO_PARENT, "element");

  if (!tree)
  {
94
    fputs("ERROR: No parent node in basic test.\n", stderr);
95 96 97 98 99
    return (1);
  }

  if (tree->type != MXML_ELEMENT)
  {
100
    fprintf(stderr, "ERROR: Parent has type %s (%d), expected MXML_ELEMENT.\n",
101 102 103 104 105 106 107 108
            tree->type < MXML_ELEMENT || tree->type > MXML_TEXT ?
	        "UNKNOWN" : types[tree->type], tree->type);
    mxmlDelete(tree);
    return (1);
  }

  if (strcmp(tree->value.element.name, "element"))
  {
109
    fprintf(stderr, "ERROR: Parent value is \"%s\", expected \"element\".\n",
110 111 112 113 114 115 116 117 118 119
            tree->value.element.name);
    mxmlDelete(tree);
    return (1);
  }

  mxmlNewInteger(tree, 123);
  mxmlNewOpaque(tree, "opaque");
  mxmlNewReal(tree, 123.4f);
  mxmlNewText(tree, 1, "text");

120
  mxmlLoadString(tree, "<group type='string'>string string string</group>",
121
                 MXML_NO_CALLBACK);
122
  mxmlLoadString(tree, "<group type='integer'>1 2 3</group>",
123
                 MXML_INTEGER_CALLBACK);
124
  mxmlLoadString(tree, "<group type='real'>1.0 2.0 3.0</group>",
125 126 127
                 MXML_REAL_CALLBACK);
  mxmlLoadString(tree, "<group>opaque opaque opaque</group>",
                 MXML_OPAQUE_CALLBACK);
128 129
  mxmlLoadString(tree, "<foo><bar><one><two>value<two>value2</two></two></one>"
                       "</bar></foo>", MXML_OPAQUE_CALLBACK);
130

131 132 133 134
  node = tree->child;

  if (!node)
  {
135
    fputs("ERROR: No first child node in basic test.\n", stderr);
136 137 138 139 140 141
    mxmlDelete(tree);
    return (1);
  }

  if (node->type != MXML_INTEGER)
  {
142
    fprintf(stderr, "ERROR: First child has type %s (%d), expected MXML_INTEGER.\n",
143 144 145 146 147 148 149 150
            node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
	        "UNKNOWN" : types[node->type], node->type);
    mxmlDelete(tree);
    return (1);
  }

  if (node->value.integer != 123)
  {
151
    fprintf(stderr, "ERROR: First child value is %d, expected 123.\n",
152 153 154 155 156 157 158 159 160
            node->value.integer);
    mxmlDelete(tree);
    return (1);
  }

  node = node->next;

  if (!node)
  {
161
    fputs("ERROR: No second child node in basic test.\n", stderr);
162 163 164 165 166 167
    mxmlDelete(tree);
    return (1);
  }

  if (node->type != MXML_OPAQUE)
  {
168
    fprintf(stderr, "ERROR: Second child has type %s (%d), expected MXML_OPAQUE.\n",
169 170 171 172 173 174 175 176
            node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
	        "UNKNOWN" : types[node->type], node->type);
    mxmlDelete(tree);
    return (1);
  }

  if (!node->value.opaque || strcmp(node->value.opaque, "opaque"))
  {
177
    fprintf(stderr, "ERROR: Second child value is \"%s\", expected \"opaque\".\n",
178 179 180 181 182 183 184 185 186
            node->value.opaque ? node->value.opaque : "(null)");
    mxmlDelete(tree);
    return (1);
  }

  node = node->next;

  if (!node)
  {
187
    fputs("ERROR: No third child node in basic test.\n", stderr);
188 189 190 191 192 193
    mxmlDelete(tree);
    return (1);
  }

  if (node->type != MXML_REAL)
  {
194
    fprintf(stderr, "ERROR: Third child has type %s (%d), expected MXML_REAL.\n",
195 196 197 198 199 200 201 202
            node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
	        "UNKNOWN" : types[node->type], node->type);
    mxmlDelete(tree);
    return (1);
  }

  if (node->value.real != 123.4f)
  {
203
    fprintf(stderr, "ERROR: Third child value is %f, expected 123.4.\n",
204 205 206 207 208 209 210 211 212
            node->value.real);
    mxmlDelete(tree);
    return (1);
  }

  node = node->next;

  if (!node)
  {
213
    fputs("ERROR: No fourth child node in basic test.\n", stderr);
214 215 216 217 218 219
    mxmlDelete(tree);
    return (1);
  }

  if (node->type != MXML_TEXT)
  {
220
    fprintf(stderr, "ERROR: Fourth child has type %s (%d), expected MXML_TEXT.\n",
221 222 223 224 225 226 227 228 229
            node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
	        "UNKNOWN" : types[node->type], node->type);
    mxmlDelete(tree);
    return (1);
  }

  if (!node->value.text.whitespace ||
      !node->value.text.string || strcmp(node->value.text.string, "text"))
  {
230
    fprintf(stderr, "ERROR: Fourth child value is %d,\"%s\", expected 1,\"text\".\n",
231 232 233 234 235 236
            node->value.text.whitespace,
	    node->value.text.string ? node->value.text.string : "(null)");
    mxmlDelete(tree);
    return (1);
  }

237 238 239 240 241 242
  for (i = 0; i < 4; i ++)
  {
    node = node->next;

    if (!node)
    {
243
      fprintf(stderr, "ERROR: No group #%d child node in basic test.\n", i + 1);
244 245 246 247 248 249
      mxmlDelete(tree);
      return (1);
    }

    if (node->type != MXML_ELEMENT)
    {
250
      fprintf(stderr, "ERROR: Group child #%d has type %s (%d), expected MXML_ELEMENT.\n",
251 252 253 254 255
              i + 1, node->type < MXML_ELEMENT || node->type > MXML_TEXT ?
	                 "UNKNOWN" : types[node->type], node->type);
      mxmlDelete(tree);
      return (1);
    }
256 257
  }

258
 /*
259
  * Test mxmlFindPath...
260 261
  */

262
  node = mxmlFindPath(tree, "*/two");
263 264 265 266 267 268 269 270 271 272 273 274 275
  if (!node)
  {
    fputs("ERROR: Unable to find value for \"*/two\".\n", stderr);
    mxmlDelete(tree);
    return (1);
  }
  else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value"))
  {
    fputs("ERROR: Bad value for \"*/two\".\n", stderr);
    mxmlDelete(tree);
    return (1);
  }

276
  node = mxmlFindPath(tree, "foo/*/two");
277 278 279 280 281 282 283 284 285 286 287 288 289
  if (!node)
  {
    fputs("ERROR: Unable to find value for \"foo/*/two\".\n", stderr);
    mxmlDelete(tree);
    return (1);
  }
  else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value"))
  {
    fputs("ERROR: Bad value for \"foo/*/two\".\n", stderr);
    mxmlDelete(tree);
    return (1);
  }

290
  node = mxmlFindPath(tree, "foo/bar/one/two");
291 292 293 294 295 296 297 298 299 300 301 302 303
  if (!node)
  {
    fputs("ERROR: Unable to find value for \"foo/bar/one/two\".\n", stderr);
    mxmlDelete(tree);
    return (1);
  }
  else if (node->type != MXML_OPAQUE || strcmp(node->value.opaque, "value"))
  {
    fputs("ERROR: Bad value for \"foo/bar/one/two\".\n", stderr);
    mxmlDelete(tree);
    return (1);
  }

304 305 306 307 308 309 310
 /*
  * Test indices...
  */

  ind = mxmlIndexNew(tree, NULL, NULL);
  if (!ind)
  {
311
    fputs("ERROR: Unable to create index of all nodes.\n", stderr);
312 313 314 315
    mxmlDelete(tree);
    return (1);
  }

316
  if (ind->num_nodes != 10)
317 318
  {
    fprintf(stderr, "ERROR: Index of all nodes contains %d "
319
                    "nodes; expected 10.\n", ind->num_nodes);
320 321 322 323 324 325 326 327
    mxmlIndexDelete(ind);
    mxmlDelete(tree);
    return (1);
  }

  mxmlIndexReset(ind);
  if (!mxmlIndexFind(ind, "group", NULL))
  {
328
    fputs("ERROR: mxmlIndexFind for \"group\" failed.\n", stderr);
329 330 331 332 333 334 335 336 337 338
    mxmlIndexDelete(ind);
    mxmlDelete(tree);
    return (1);
  }

  mxmlIndexDelete(ind);

  ind = mxmlIndexNew(tree, "group", NULL);
  if (!ind)
  {
339
    fputs("ERROR: Unable to create index of groups.\n", stderr);
340 341 342 343 344 345 346
    mxmlDelete(tree);
    return (1);
  }

  if (ind->num_nodes != 4)
  {
    fprintf(stderr, "ERROR: Index of groups contains %d "
347
                    "nodes; expected 4.\n", ind->num_nodes);
348 349 350 351 352 353 354 355
    mxmlIndexDelete(ind);
    mxmlDelete(tree);
    return (1);
  }

  mxmlIndexReset(ind);
  if (!mxmlIndexEnum(ind))
  {
356
    fputs("ERROR: mxmlIndexEnum failed.\n", stderr);
357 358 359 360 361 362 363 364 365 366
    mxmlIndexDelete(ind);
    mxmlDelete(tree);
    return (1);
  }

  mxmlIndexDelete(ind);

  ind = mxmlIndexNew(tree, NULL, "type");
  if (!ind)
  {
367
    fputs("ERROR: Unable to create index of type attributes.\n", stderr);
368 369 370 371 372 373 374
    mxmlDelete(tree);
    return (1);
  }

  if (ind->num_nodes != 3)
  {
    fprintf(stderr, "ERROR: Index of type attributes contains %d "
375
                    "nodes; expected 3.\n", ind->num_nodes);
376 377 378 379 380 381 382 383
    mxmlIndexDelete(ind);
    mxmlDelete(tree);
    return (1);
  }

  mxmlIndexReset(ind);
  if (!mxmlIndexFind(ind, NULL, "string"))
  {
384
    fputs("ERROR: mxmlIndexFind for \"string\" failed.\n", stderr);
385 386 387 388 389 390 391 392 393 394
    mxmlIndexDelete(ind);
    mxmlDelete(tree);
    return (1);
  }

  mxmlIndexDelete(ind);

  ind = mxmlIndexNew(tree, "group", "type");
  if (!ind)
  {
395
    fputs("ERROR: Unable to create index of elements and attributes.\n", stderr);
396 397 398 399 400 401 402
    mxmlDelete(tree);
    return (1);
  }

  if (ind->num_nodes != 3)
  {
    fprintf(stderr, "ERROR: Index of elements and attributes contains %d "
403
                    "nodes; expected 3.\n", ind->num_nodes);
404 405 406 407 408 409 410 411
    mxmlIndexDelete(ind);
    mxmlDelete(tree);
    return (1);
  }

  mxmlIndexReset(ind);
  if (!mxmlIndexFind(ind, "group", "string"))
  {
412
    fputs("ERROR: mxmlIndexFind for \"string\" failed.\n", stderr);
413 414 415 416 417 418 419 420 421 422
    mxmlIndexDelete(ind);
    mxmlDelete(tree);
    return (1);
  }

  mxmlIndexDelete(ind);

 /*
  * Check the mxmlDelete() works properly...
  */
423

424
  for (i = 0; i < 9; i ++)
425 426 427 428 429 430 431 432 433 434 435
  {
    if (tree->child)
      mxmlDelete(tree->child);
    else
    {
      fprintf(stderr, "ERROR: Child pointer prematurely NULL on child #%d\n",
              i + 1);
      mxmlDelete(tree);
      return (1);
    }
  }
436 437 438

  if (tree->child)
  {
439
    fputs("ERROR: Child pointer not NULL after deleting all children.\n", stderr);
440 441 442 443 444
    return (1);
  }

  if (tree->last_child)
  {
445
    fputs("ERROR: Last child pointer not NULL after deleting all children.\n", stderr);
446 447 448 449 450
    return (1);
  }

  mxmlDelete(tree);

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
 /*
  * Open the file/string using the default (MXML_NO_CALLBACK) callback...
  */

  if (argv[1][0] == '<')
    tree = mxmlLoadString(NULL, argv[1], MXML_NO_CALLBACK);
  else if ((fp = fopen(argv[1], "rb")) == NULL)
  {
    perror(argv[1]);
    return (1);
  }
  else
  {
   /*
    * Read the file...
    */

    tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);

    fclose(fp);
  }

  if (!tree)
  {
475
    fputs("Unable to read XML file with default callback.\n", stderr);
476 477 478 479 480 481 482 483 484 485 486 487 488 489
    return (1);
  }

  if (!strcmp(argv[1], "test.xml"))
  {
    const char	*text;			/* Text value */

   /*
    * Verify that mxmlFindElement() and indirectly mxmlWalkNext() work
    * properly...
    */

    if ((node = mxmlFindPath(tree, "group/option/keyword")) == NULL)
    {
490
      fputs("Unable to find group/option/keyword element in XML tree.\n", stderr);
491 492 493 494 495 496
      mxmlDelete(tree);
      return (1);
    }

    if (node->type != MXML_TEXT)
    {
497
      fputs("No child node of group/option/keyword.\n", stderr);
498 499 500 501 502 503 504
      mxmlSaveFile(tree, stderr, MXML_NO_CALLBACK);
      mxmlDelete(tree);
      return (1);
    }

    if ((text = mxmlGetText(node, NULL)) == NULL || strcmp(text, "InputSlot"))
    {
505
      fprintf(stderr, "Child node of group/option/value has value \"%s\" instead of \"InputSlot\".\n", text ? text : "(null)");
506 507 508 509 510 511 512
      mxmlDelete(tree);
      return (1);
    }
  }

  mxmlDelete(tree);

Michael R Sweet's avatar
Michael R Sweet committed
513 514 515 516
 /*
  * Open the file...
  */

517 518
  if (argv[1][0] == '<')
    tree = mxmlLoadString(NULL, argv[1], type_cb);
519
  else if ((fp = fopen(argv[1], "rb")) == NULL)
Michael R Sweet's avatar
Michael R Sweet committed
520 521 522 523
  {
    perror(argv[1]);
    return (1);
  }
524 525 526 527 528
  else
  {
   /*
    * Read the file...
    */
Michael R Sweet's avatar
Michael R Sweet committed
529

530
    tree = mxmlLoadFile(NULL, fp, type_cb);
Michael R Sweet's avatar
Michael R Sweet committed
531

532 533
    fclose(fp);
  }
Michael R Sweet's avatar
Michael R Sweet committed
534 535 536

  if (!tree)
  {
537
    fputs("Unable to read XML file.\n", stderr);
Michael R Sweet's avatar
Michael R Sweet committed
538 539 540
    return (1);
  }

Michael R Sweet's avatar
Michael R Sweet committed
541
  if (!strcmp(argv[1], "test.xml"))
Michael R Sweet's avatar
Michael R Sweet committed
542
  {
Michael R Sweet's avatar
Michael R Sweet committed
543 544 545 546 547
   /*
    * Verify that mxmlFindElement() and indirectly mxmlWalkNext() work
    * properly...
    */

548 549
    if ((node = mxmlFindElement(tree, tree, "choice", NULL, NULL,
                                MXML_DESCEND)) == NULL)
Michael R Sweet's avatar
Michael R Sweet committed
550
    {
551
      fputs("Unable to find first <choice> element in XML tree.\n", stderr);
Michael R Sweet's avatar
Michael R Sweet committed
552 553 554 555
      mxmlDelete(tree);
      return (1);
    }

556
    if (!mxmlFindElement(node, tree, "choice", NULL, NULL, MXML_NO_DESCEND))
Michael R Sweet's avatar
Michael R Sweet committed
557
    {
558
      fputs("Unable to find second <choice> element in XML tree.\n", stderr);
Michael R Sweet's avatar
Michael R Sweet committed
559 560 561
      mxmlDelete(tree);
      return (1);
    }
Michael R Sweet's avatar
Michael R Sweet committed
562 563
  }

Michael R Sweet's avatar
Michael R Sweet committed
564 565 566 567
 /*
  * Print the XML tree...
  */

568
  mxmlSaveFile(tree, stdout, whitespace_cb);
Michael R Sweet's avatar
Michael R Sweet committed
569

570 571 572 573 574
 /*
  * Save the XML tree to a string and print it...
  */

  if (mxmlSaveString(tree, buffer, sizeof(buffer), whitespace_cb) > 0)
575 576 577 578 579 580 581 582
  {
    if (argc == 3)
    {
      fp = fopen(argv[2], "w");
      fputs(buffer, fp);
      fclose(fp);
    }
  }
583

Michael R Sweet's avatar
Michael R Sweet committed
584
 /*
585
  * Delete the tree...
Michael R Sweet's avatar
Michael R Sweet committed
586 587 588 589
  */

  mxmlDelete(tree);

590 591 592 593 594 595 596 597 598 599
 /*
  * Read from/write to file descriptors...
  */

  if (argv[1][0] != '<')
  {
   /*
    * Open the file again...
    */

600
    if ((fd = open(argv[1], O_RDONLY | O_BINARY)) < 0)
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
    {
      perror(argv[1]);
      return (1);
    }

   /*
    * Read the file...
    */

    tree = mxmlLoadFd(NULL, fd, type_cb);

    close(fd);

   /*
    * Create filename.xmlfd...
    */

    snprintf(buffer, sizeof(buffer), "%sfd", argv[1]);

620
    if ((fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666)) < 0)
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
    {
      perror(buffer);
      mxmlDelete(tree);
      return (1);
    }

   /*
    * Write the file...
    */

    mxmlSaveFd(tree, fd, whitespace_cb);

    close(fd);

   /*
    * Delete the tree...
    */

    mxmlDelete(tree);
  }

642 643 644 645 646 647 648
 /*
  * Test SAX methods...
  */

  memset(event_counts, 0, sizeof(event_counts));

  if (argv[1][0] == '<')
649
    mxmlSAXLoadString(NULL, argv[1], type_cb, sax_cb, NULL);
650 651 652 653 654 655 656 657 658 659 660
  else if ((fp = fopen(argv[1], "rb")) == NULL)
  {
    perror(argv[1]);
    return (1);
  }
  else
  {
   /*
    * Read the file...
    */

661
    mxmlSAXLoadFile(NULL, fp, type_cb, sax_cb, NULL);
662 663 664 665 666 667 668 669

    fclose(fp);
  }

  if (!strcmp(argv[1], "test.xml"))
  {
    if (event_counts[MXML_SAX_CDATA] != 1)
    {
670
      fprintf(stderr, "MXML_SAX_CDATA seen %d times, expected 1 times.\n",
671 672 673 674 675 676
              event_counts[MXML_SAX_CDATA]);
      return (1);
    }

    if (event_counts[MXML_SAX_COMMENT] != 1)
    {
677
      fprintf(stderr, "MXML_SAX_COMMENT seen %d times, expected 1 times.\n",
678 679 680 681
              event_counts[MXML_SAX_COMMENT]);
      return (1);
    }

682
    if (event_counts[MXML_SAX_DATA] != 60)
683
    {
684
      fprintf(stderr, "MXML_SAX_DATA seen %d times, expected 60 times.\n",
685 686 687 688 689 690
              event_counts[MXML_SAX_DATA]);
      return (1);
    }

    if (event_counts[MXML_SAX_DIRECTIVE] != 1)
    {
691
      fprintf(stderr, "MXML_SAX_DIRECTIVE seen %d times, expected 1 times.\n",
692 693 694 695 696 697
              event_counts[MXML_SAX_DIRECTIVE]);
      return (1);
    }

    if (event_counts[MXML_SAX_ELEMENT_CLOSE] != 20)
    {
698
      fprintf(stderr, "MXML_SAX_ELEMENT_CLOSE seen %d times, expected 20 times.\n",
699 700 701 702 703 704
              event_counts[MXML_SAX_ELEMENT_CLOSE]);
      return (1);
    }

    if (event_counts[MXML_SAX_ELEMENT_OPEN] != 20)
    {
705
      fprintf(stderr, "MXML_SAX_ELEMENT_OPEN seen %d times, expected 20 times.\n",
706 707 708 709 710
              event_counts[MXML_SAX_ELEMENT_OPEN]);
      return (1);
    }
  }

Michael R Sweet's avatar
Michael R Sweet committed
711
#ifndef WIN32
712 713 714 715
 /*
  * Debug hooks...
  */

716 717
  if (getenv("TEST_DELAY") != NULL)
    sleep(atoi(getenv("TEST_DELAY")));
718
#  ifdef __APPLE__
719 720 721 722 723
  if (getenv("TEST_LEAKS") != NULL)
  {
    char command[1024];

    snprintf(command, sizeof(command), "leaks %d", (int)getpid());
724 725
    if (system(command))
      puts("Unable to check for leaks.");
726
  }
727
#  endif /* __APPLE__ */
Michael R Sweet's avatar
Michael R Sweet committed
728
#endif /* !WIN32 */
729

730 731 732 733
 /*
  * Return...
  */

Michael R Sweet's avatar
Michael R Sweet committed
734 735 736 737
  return (0);
}


738 739 740 741 742 743 744 745 746
/*
 * 'sax_cb()' - Process nodes via SAX.
 */

void
sax_cb(mxml_node_t      *node,		/* I - Current node */
       mxml_sax_event_t event,		/* I - SAX event */
       void             *data)		/* I - SAX user data */
{
747 748 749 750 751 752 753 754 755 756 757
  static const char * const events[] =	/* Events */
  {
    "MXML_SAX_CDATA",			/* CDATA node */
    "MXML_SAX_COMMENT",			/* Comment node */
    "MXML_SAX_DATA",			/* Data node */
    "MXML_SAX_DIRECTIVE",		/* Processing directive node */
    "MXML_SAX_ELEMENT_CLOSE",		/* Element closed */
    "MXML_SAX_ELEMENT_OPEN"		/* Element opened */
  };


758 759
  (void)data;

760 761 762 763
 /*
  * This SAX callback just counts the different events.
  */

764 765 766
  if (!node)
    fprintf(stderr, "ERROR: SAX callback for event %s has NULL node.\n", events[event]);

767 768 769 770
  event_counts[event] ++;
}


Michael R Sweet's avatar
Michael R Sweet committed
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
/*
 * 'type_cb()' - XML data type callback for mxmlLoadFile()...
 */

mxml_type_t				/* O - Data type */
type_cb(mxml_node_t *node)		/* I - Element node */
{
  const char	*type;			/* Type string */


 /*
  * You can lookup attributes and/or use the element name, hierarchy, etc...
  */

  if ((type = mxmlElementGetAttr(node, "type")) == NULL)
    type = node->value.element.name;

  if (!strcmp(type, "integer"))
    return (MXML_INTEGER);
790
  else if (!strcmp(type, "opaque") || !strcmp(type, "pre"))
Michael R Sweet's avatar
Michael R Sweet committed
791 792 793 794 795 796 797 798 799
    return (MXML_OPAQUE);
  else if (!strcmp(type, "real"))
    return (MXML_REAL);
  else
    return (MXML_TEXT);
}


/*
800 801 802 803
 * 'whitespace_cb()' - Let the mxmlSaveFile() function know when to insert
 *                     newlines and tabs...
 */

804
const char *				/* O - Whitespace string or NULL */
805 806 807
whitespace_cb(mxml_node_t *node,	/* I - Element node */
              int         where)	/* I - Open or close tag? */
{
808 809 810 811 812 813
  mxml_node_t	*parent;		/* Parent node */
  int		level;			/* Indentation level */
  const char	*name;			/* Name of element */
  static const char *tabs = "\t\t\t\t\t\t\t\t";
					/* Tabs for indentation */

814 815 816 817 818 819 820 821 822 823 824 825

 /*
  * We can conditionally break to a new line before or after any element.
  * These are just common HTML elements...
  */

  name = node->value.element.name;

  if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") ||
      !strcmp(name, "pre") || !strcmp(name, "p") ||
      !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") ||
      !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6"))
826 827 828 829 830 831
  {
   /*
    * Newlines before open and after close...
    */

    if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE)
832
      return ("\n");
833 834 835 836 837 838 839
  }
  else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul"))
  {
   /*
    * Put a newline before and after list elements...
    */

840
    return ("\n");
841 842
  }
  else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li"))
843 844
  {
   /*
845
    * Put a tab before <li>'s, <dd>'s, and <dt>'s, and a newline after them...
846 847
    */

848
    if (where == MXML_WS_BEFORE_OPEN)
849
      return ("\t");
850
    else if (where == MXML_WS_AFTER_CLOSE)
851
      return ("\n");
852
  }
853
  else if (!strncmp(name, "?xml", 4))
854
  {
855 856 857 858
    if (where == MXML_WS_AFTER_OPEN)
      return ("\n");
    else
      return (NULL);
859
  }
860 861 862
  else if (where == MXML_WS_BEFORE_OPEN ||
           ((!strcmp(name, "choice") || !strcmp(name, "option")) &&
	    where == MXML_WS_BEFORE_CLOSE))
863 864 865 866 867 868 869
  {
    for (level = -1, parent = node->parent;
         parent;
	 level ++, parent = parent->parent);

    if (level > 8)
      level = 8;
870 871
    else if (level < 0)
      level = 0;
872 873 874

    return (tabs + 8 - level);
  }
875 876 877 878
  else if (where == MXML_WS_AFTER_CLOSE ||
           ((!strcmp(name, "group") || !strcmp(name, "option") ||
	     !strcmp(name, "choice")) &&
            where == MXML_WS_AFTER_OPEN))
879
    return ("\n");
Michael R Sweet's avatar
Michael R Sweet committed
880
  else if (where == MXML_WS_AFTER_OPEN && !node->child)
881
    return ("\n");
882 883

 /*
884
  * Return NULL for no added whitespace...
885 886
  */

887
  return (NULL);
888
}