1 /*
   2  * testSAX.c : a small tester program for parsing using the SAX API.
   3  *
   4  * See Copyright for the status of this software.
   5  *
   6  * daniel@veillard.com
   7  */
   8 
   9 #include "libxml.h"
  10 
  11 #ifdef HAVE_SYS_TIME_H
  12 #include <sys/time.h>
  13 #endif
  14 #ifdef HAVE_SYS_TIMEB_H
  15 #include <sys/timeb.h>
  16 #endif
  17 #ifdef HAVE_TIME_H
  18 #include <time.h>
  19 #endif
  20 
  21 #ifdef LIBXML_SAX1_ENABLED
  22 #include <string.h>
  23 #include <stdarg.h>
  24 
  25 #ifdef HAVE_SYS_TYPES_H
  26 #include <sys/types.h>
  27 #endif
  28 #ifdef HAVE_SYS_STAT_H
  29 #include <sys/stat.h>
  30 #endif
  31 #ifdef HAVE_FCNTL_H
  32 #include <fcntl.h>
  33 #endif
  34 #ifdef HAVE_UNISTD_H
  35 #include <unistd.h>
  36 #endif
  37 #ifdef HAVE_STDLIB_H
  38 #include <stdlib.h>
  39 #endif
  40 #ifdef HAVE_STRING_H
  41 #include <string.h>
  42 #endif
  43 
  44 
  45 #include <libxml/globals.h>
  46 #include <libxml/xmlerror.h>
  47 #include <libxml/parser.h>
  48 #include <libxml/parserInternals.h> /* only for xmlNewInputFromFile() */
  49 #include <libxml/tree.h>
  50 #include <libxml/debugXML.h>
  51 #include <libxml/xmlmemory.h>
  52 
  53 static int debug = 0;
  54 static int copy = 0;
  55 static int recovery = 0;
  56 static int push = 0;
  57 static int speed = 0;
  58 static int noent = 0;
  59 static int quiet = 0;
  60 static int nonull = 0;
  61 static int sax2 = 0;
  62 static int repeat = 0;
  63 static int callbacks = 0;
  64 static int timing = 0;
  65 
  66 /*
  67  * Timing routines.
  68  */
  69 /*
  70  * Internal timing routines to remove the necessity to have unix-specific
  71  * function calls
  72  */
  73 
  74 #ifndef HAVE_GETTIMEOFDAY
  75 #ifdef HAVE_SYS_TIMEB_H
  76 #ifdef HAVE_SYS_TIME_H
  77 #ifdef HAVE_FTIME
  78 
  79 static int
  80 my_gettimeofday(struct timeval *tvp, void *tzp)
  81 {
  82     struct timeb timebuffer;
  83 
  84     ftime(&timebuffer);
  85     if (tvp) {
  86         tvp->tv_sec = timebuffer.time;
  87         tvp->tv_usec = timebuffer.millitm * 1000L;
  88     }
  89     return (0);
  90 }
  91 #define HAVE_GETTIMEOFDAY 1
  92 #define gettimeofday my_gettimeofday
  93 
  94 #endif /* HAVE_FTIME */
  95 #endif /* HAVE_SYS_TIME_H */
  96 #endif /* HAVE_SYS_TIMEB_H */
  97 #endif /* !HAVE_GETTIMEOFDAY */
  98 
  99 #if defined(HAVE_GETTIMEOFDAY)
 100 static struct timeval begin, end;
 101 
 102 /*
 103  * startTimer: call where you want to start timing
 104  */
 105 static void
 106 startTimer(void)
 107 {
 108     gettimeofday(&begin, NULL);
 109 }
 110 
 111 /*
 112  * endTimer: call where you want to stop timing and to print out a
 113  *           message about the timing performed; format is a printf
 114  *           type argument
 115  */
 116 static void XMLCDECL
 117 endTimer(const char *fmt, ...)
 118 {
 119     long msec;
 120     va_list ap;
 121 
 122     gettimeofday(&end, NULL);
 123     msec = end.tv_sec - begin.tv_sec;
 124     msec *= 1000;
 125     msec += (end.tv_usec - begin.tv_usec) / 1000;
 126 
 127 #ifndef HAVE_STDARG_H
 128 #error "endTimer required stdarg functions"
 129 #endif
 130     va_start(ap, fmt);
 131     vfprintf(stderr, fmt, ap);
 132     va_end(ap);
 133 
 134     fprintf(stderr, " took %ld ms\n", msec);
 135 }
 136 #elif defined(HAVE_TIME_H)
 137 /*
 138  * No gettimeofday function, so we have to make do with calling clock.
 139  * This is obviously less accurate, but there's little we can do about
 140  * that.
 141  */
 142 #ifndef CLOCKS_PER_SEC
 143 #define CLOCKS_PER_SEC 100
 144 #endif
 145 
 146 static clock_t begin, end;
 147 static void
 148 startTimer(void)
 149 {
 150     begin = clock();
 151 }
 152 static void XMLCDECL
 153 endTimer(const char *fmt, ...)
 154 {
 155     long msec;
 156     va_list ap;
 157 
 158     end = clock();
 159     msec = ((end - begin) * 1000) / CLOCKS_PER_SEC;
 160 
 161 #ifndef HAVE_STDARG_H
 162 #error "endTimer required stdarg functions"
 163 #endif
 164     va_start(ap, fmt);
 165     vfprintf(stderr, fmt, ap);
 166     va_end(ap);
 167     fprintf(stderr, " took %ld ms\n", msec);
 168 }
 169 #else
 170 
 171 /*
 172  * We don't have a gettimeofday or time.h, so we just don't do timing
 173  */
 174 static void
 175 startTimer(void)
 176 {
 177     /*
 178      * Do nothing
 179      */
 180 }
 181 static void XMLCDECL
 182 endTimer(char *format, ...)
 183 {
 184     /*
 185      * We cannot do anything because we don't have a timing function
 186      */
 187 #ifdef HAVE_STDARG_H
 188     va_start(ap, format);
 189     vfprintf(stderr, format, ap);
 190     va_end(ap);
 191     fprintf(stderr, " was not timed\n", msec);
 192 #else
 193     /* We don't have gettimeofday, time or stdarg.h, what crazy world is
 194      * this ?!
 195      */
 196 #endif
 197 }
 198 #endif
 199 
 200 /*
 201  * empty SAX block
 202  */
 203 static xmlSAXHandler emptySAXHandlerStruct = {
 204     NULL, /* internalSubset */
 205     NULL, /* isStandalone */
 206     NULL, /* hasInternalSubset */
 207     NULL, /* hasExternalSubset */
 208     NULL, /* resolveEntity */
 209     NULL, /* getEntity */
 210     NULL, /* entityDecl */
 211     NULL, /* notationDecl */
 212     NULL, /* attributeDecl */
 213     NULL, /* elementDecl */
 214     NULL, /* unparsedEntityDecl */
 215     NULL, /* setDocumentLocator */
 216     NULL, /* startDocument */
 217     NULL, /* endDocument */
 218     NULL, /* startElement */
 219     NULL, /* endElement */
 220     NULL, /* reference */
 221     NULL, /* characters */
 222     NULL, /* ignorableWhitespace */
 223     NULL, /* processingInstruction */
 224     NULL, /* comment */
 225     NULL, /* xmlParserWarning */
 226     NULL, /* xmlParserError */
 227     NULL, /* xmlParserError */
 228     NULL, /* getParameterEntity */
 229     NULL, /* cdataBlock; */
 230     NULL, /* externalSubset; */
 231     1,
 232     NULL,
 233     NULL, /* startElementNs */
 234     NULL, /* endElementNs */
 235     NULL  /* xmlStructuredErrorFunc */
 236 };
 237 
 238 static xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct;
 239 extern xmlSAXHandlerPtr debugSAXHandler;
 240 
 241 /************************************************************************
 242  *                                  *
 243  *              Debug Handlers              *
 244  *                                  *
 245  ************************************************************************/
 246 
 247 /**
 248  * isStandaloneDebug:
 249  * @ctxt:  An XML parser context
 250  *
 251  * Is this document tagged standalone ?
 252  *
 253  * Returns 1 if true
 254  */
 255 static int
 256 isStandaloneDebug(void *ctx ATTRIBUTE_UNUSED)
 257 {
 258     callbacks++;
 259     if (quiet)
 260     return(0);
 261     fprintf(stdout, "SAX.isStandalone()\n");
 262     return(0);
 263 }
 264 
 265 /**
 266  * hasInternalSubsetDebug:
 267  * @ctxt:  An XML parser context
 268  *
 269  * Does this document has an internal subset
 270  *
 271  * Returns 1 if true
 272  */
 273 static int
 274 hasInternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
 275 {
 276     callbacks++;
 277     if (quiet)
 278     return(0);
 279     fprintf(stdout, "SAX.hasInternalSubset()\n");
 280     return(0);
 281 }
 282 
 283 /**
 284  * hasExternalSubsetDebug:
 285  * @ctxt:  An XML parser context
 286  *
 287  * Does this document has an external subset
 288  *
 289  * Returns 1 if true
 290  */
 291 static int
 292 hasExternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
 293 {
 294     callbacks++;
 295     if (quiet)
 296     return(0);
 297     fprintf(stdout, "SAX.hasExternalSubset()\n");
 298     return(0);
 299 }
 300 
 301 /**
 302  * internalSubsetDebug:
 303  * @ctxt:  An XML parser context
 304  *
 305  * Does this document has an internal subset
 306  */
 307 static void
 308 internalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
 309            const xmlChar *ExternalID, const xmlChar *SystemID)
 310 {
 311     callbacks++;
 312     if (quiet)
 313     return;
 314     fprintf(stdout, "SAX.internalSubset(%s,", name);
 315     if (ExternalID == NULL)
 316     fprintf(stdout, " ,");
 317     else
 318     fprintf(stdout, " %s,", ExternalID);
 319     if (SystemID == NULL)
 320     fprintf(stdout, " )\n");
 321     else
 322     fprintf(stdout, " %s)\n", SystemID);
 323 }
 324 
 325 /**
 326  * externalSubsetDebug:
 327  * @ctxt:  An XML parser context
 328  *
 329  * Does this document has an external subset
 330  */
 331 static void
 332 externalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
 333            const xmlChar *ExternalID, const xmlChar *SystemID)
 334 {
 335     callbacks++;
 336     if (quiet)
 337     return;
 338     fprintf(stdout, "SAX.externalSubset(%s,", name);
 339     if (ExternalID == NULL)
 340     fprintf(stdout, " ,");
 341     else
 342     fprintf(stdout, " %s,", ExternalID);
 343     if (SystemID == NULL)
 344     fprintf(stdout, " )\n");
 345     else
 346     fprintf(stdout, " %s)\n", SystemID);
 347 }
 348 
 349 /**
 350  * resolveEntityDebug:
 351  * @ctxt:  An XML parser context
 352  * @publicId: The public ID of the entity
 353  * @systemId: The system ID of the entity
 354  *
 355  * Special entity resolver, better left to the parser, it has
 356  * more context than the application layer.
 357  * The default behaviour is to NOT resolve the entities, in that case
 358  * the ENTITY_REF nodes are built in the structure (and the parameter
 359  * values).
 360  *
 361  * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
 362  */
 363 static xmlParserInputPtr
 364 resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId)
 365 {
 366     callbacks++;
 367     if (quiet)
 368     return(NULL);
 369     /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */
 370 
 371 
 372     fprintf(stdout, "SAX.resolveEntity(");
 373     if (publicId != NULL)
 374     fprintf(stdout, "%s", (char *)publicId);
 375     else
 376     fprintf(stdout, " ");
 377     if (systemId != NULL)
 378     fprintf(stdout, ", %s)\n", (char *)systemId);
 379     else
 380     fprintf(stdout, ", )\n");
 381 /*********
 382     if (systemId != NULL) {
 383         return(xmlNewInputFromFile(ctxt, (char *) systemId));
 384     }
 385  *********/
 386     return(NULL);
 387 }
 388 
 389 /**
 390  * getEntityDebug:
 391  * @ctxt:  An XML parser context
 392  * @name: The entity name
 393  *
 394  * Get an entity by name
 395  *
 396  * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
 397  */
 398 static xmlEntityPtr
 399 getEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
 400 {
 401     callbacks++;
 402     if (quiet)
 403     return(NULL);
 404     fprintf(stdout, "SAX.getEntity(%s)\n", name);
 405     return(NULL);
 406 }
 407 
 408 /**
 409  * getParameterEntityDebug:
 410  * @ctxt:  An XML parser context
 411  * @name: The entity name
 412  *
 413  * Get a parameter entity by name
 414  *
 415  * Returns the xmlParserInputPtr
 416  */
 417 static xmlEntityPtr
 418 getParameterEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
 419 {
 420     callbacks++;
 421     if (quiet)
 422     return(NULL);
 423     fprintf(stdout, "SAX.getParameterEntity(%s)\n", name);
 424     return(NULL);
 425 }
 426 
 427 
 428 /**
 429  * entityDeclDebug:
 430  * @ctxt:  An XML parser context
 431  * @name:  the entity name
 432  * @type:  the entity type
 433  * @publicId: The public ID of the entity
 434  * @systemId: The system ID of the entity
 435  * @content: the entity value (without processing).
 436  *
 437  * An entity definition has been parsed
 438  */
 439 static void
 440 entityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
 441           const xmlChar *publicId, const xmlChar *systemId, xmlChar *content)
 442 {
 443 const xmlChar *nullstr = BAD_CAST "(null)";
 444     /* not all libraries handle printing null pointers nicely */
 445     if (publicId == NULL)
 446         publicId = nullstr;
 447     if (systemId == NULL)
 448         systemId = nullstr;
 449     if (content == NULL)
 450         content = (xmlChar *)nullstr;
 451     callbacks++;
 452     if (quiet)
 453     return;
 454     fprintf(stdout, "SAX.entityDecl(%s, %d, %s, %s, %s)\n",
 455             name, type, publicId, systemId, content);
 456 }
 457 
 458 /**
 459  * attributeDeclDebug:
 460  * @ctxt:  An XML parser context
 461  * @name:  the attribute name
 462  * @type:  the attribute type
 463  *
 464  * An attribute definition has been parsed
 465  */
 466 static void
 467 attributeDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar * elem,
 468                    const xmlChar * name, int type, int def,
 469                    const xmlChar * defaultValue, xmlEnumerationPtr tree)
 470 {
 471     callbacks++;
 472     if (quiet)
 473         return;
 474     if (defaultValue == NULL)
 475         fprintf(stdout, "SAX.attributeDecl(%s, %s, %d, %d, NULL, ...)\n",
 476                 elem, name, type, def);
 477     else
 478         fprintf(stdout, "SAX.attributeDecl(%s, %s, %d, %d, %s, ...)\n",
 479                 elem, name, type, def, defaultValue);
 480     xmlFreeEnumeration(tree);
 481 }
 482 
 483 /**
 484  * elementDeclDebug:
 485  * @ctxt:  An XML parser context
 486  * @name:  the element name
 487  * @type:  the element type
 488  * @content: the element value (without processing).
 489  *
 490  * An element definition has been parsed
 491  */
 492 static void
 493 elementDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
 494         xmlElementContentPtr content ATTRIBUTE_UNUSED)
 495 {
 496     callbacks++;
 497     if (quiet)
 498     return;
 499     fprintf(stdout, "SAX.elementDecl(%s, %d, ...)\n",
 500             name, type);
 501 }
 502 
 503 /**
 504  * notationDeclDebug:
 505  * @ctxt:  An XML parser context
 506  * @name: The name of the notation
 507  * @publicId: The public ID of the entity
 508  * @systemId: The system ID of the entity
 509  *
 510  * What to do when a notation declaration has been parsed.
 511  */
 512 static void
 513 notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
 514          const xmlChar *publicId, const xmlChar *systemId)
 515 {
 516     callbacks++;
 517     if (quiet)
 518     return;
 519     fprintf(stdout, "SAX.notationDecl(%s, %s, %s)\n",
 520             (char *) name, (char *) publicId, (char *) systemId);
 521 }
 522 
 523 /**
 524  * unparsedEntityDeclDebug:
 525  * @ctxt:  An XML parser context
 526  * @name: The name of the entity
 527  * @publicId: The public ID of the entity
 528  * @systemId: The system ID of the entity
 529  * @notationName: the name of the notation
 530  *
 531  * What to do when an unparsed entity declaration is parsed
 532  */
 533 static void
 534 unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
 535            const xmlChar *publicId, const xmlChar *systemId,
 536            const xmlChar *notationName)
 537 {
 538 const xmlChar *nullstr = BAD_CAST "(null)";
 539 
 540     if (publicId == NULL)
 541         publicId = nullstr;
 542     if (systemId == NULL)
 543         systemId = nullstr;
 544     if (notationName == NULL)
 545         notationName = nullstr;
 546     callbacks++;
 547     if (quiet)
 548     return;
 549     fprintf(stdout, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n",
 550             (char *) name, (char *) publicId, (char *) systemId,
 551         (char *) notationName);
 552 }
 553 
 554 /**
 555  * setDocumentLocatorDebug:
 556  * @ctxt:  An XML parser context
 557  * @loc: A SAX Locator
 558  *
 559  * Receive the document locator at startup, actually xmlDefaultSAXLocator
 560  * Everything is available on the context, so this is useless in our case.
 561  */
 562 static void
 563 setDocumentLocatorDebug(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
 564 {
 565     callbacks++;
 566     if (quiet)
 567     return;
 568     fprintf(stdout, "SAX.setDocumentLocator()\n");
 569 }
 570 
 571 /**
 572  * startDocumentDebug:
 573  * @ctxt:  An XML parser context
 574  *
 575  * called when the document start being processed.
 576  */
 577 static void
 578 startDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
 579 {
 580     callbacks++;
 581     if (quiet)
 582     return;
 583     fprintf(stdout, "SAX.startDocument()\n");
 584 }
 585 
 586 /**
 587  * endDocumentDebug:
 588  * @ctxt:  An XML parser context
 589  *
 590  * called when the document end has been detected.
 591  */
 592 static void
 593 endDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
 594 {
 595     callbacks++;
 596     if (quiet)
 597     return;
 598     fprintf(stdout, "SAX.endDocument()\n");
 599 }
 600 
 601 /**
 602  * startElementDebug:
 603  * @ctxt:  An XML parser context
 604  * @name:  The element name
 605  *
 606  * called when an opening tag has been processed.
 607  */
 608 static void
 609 startElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts)
 610 {
 611     int i;
 612 
 613     callbacks++;
 614     if (quiet)
 615     return;
 616     fprintf(stdout, "SAX.startElement(%s", (char *) name);
 617     if (atts != NULL) {
 618         for (i = 0;(atts[i] != NULL);i++) {
 619         fprintf(stdout, ", %s='", atts[i++]);
 620         if (atts[i] != NULL)
 621             fprintf(stdout, "%s'", atts[i]);
 622     }
 623     }
 624     fprintf(stdout, ")\n");
 625 }
 626 
 627 /**
 628  * endElementDebug:
 629  * @ctxt:  An XML parser context
 630  * @name:  The element name
 631  *
 632  * called when the end of an element has been detected.
 633  */
 634 static void
 635 endElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
 636 {
 637     callbacks++;
 638     if (quiet)
 639     return;
 640     fprintf(stdout, "SAX.endElement(%s)\n", (char *) name);
 641 }
 642 
 643 /**
 644  * charactersDebug:
 645  * @ctxt:  An XML parser context
 646  * @ch:  a xmlChar string
 647  * @len: the number of xmlChar
 648  *
 649  * receiving some chars from the parser.
 650  * Question: how much at a time ???
 651  */
 652 static void
 653 charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
 654 {
 655     char output[40];
 656     int i;
 657 
 658     callbacks++;
 659     if (quiet)
 660     return;
 661     for (i = 0;(i<len) && (i < 30);i++)
 662     output[i] = ch[i];
 663     output[i] = 0;
 664 
 665     fprintf(stdout, "SAX.characters(%s, %d)\n", output, len);
 666 }
 667 
 668 /**
 669  * referenceDebug:
 670  * @ctxt:  An XML parser context
 671  * @name:  The entity name
 672  *
 673  * called when an entity reference is detected.
 674  */
 675 static void
 676 referenceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
 677 {
 678     callbacks++;
 679     if (quiet)
 680     return;
 681     fprintf(stdout, "SAX.reference(%s)\n", name);
 682 }
 683 
 684 /**
 685  * ignorableWhitespaceDebug:
 686  * @ctxt:  An XML parser context
 687  * @ch:  a xmlChar string
 688  * @start: the first char in the string
 689  * @len: the number of xmlChar
 690  *
 691  * receiving some ignorable whitespaces from the parser.
 692  * Question: how much at a time ???
 693  */
 694 static void
 695 ignorableWhitespaceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
 696 {
 697     char output[40];
 698     int i;
 699 
 700     callbacks++;
 701     if (quiet)
 702     return;
 703     for (i = 0;(i<len) && (i < 30);i++)
 704     output[i] = ch[i];
 705     output[i] = 0;
 706     fprintf(stdout, "SAX.ignorableWhitespace(%s, %d)\n", output, len);
 707 }
 708 
 709 /**
 710  * processingInstructionDebug:
 711  * @ctxt:  An XML parser context
 712  * @target:  the target name
 713  * @data: the PI data's
 714  * @len: the number of xmlChar
 715  *
 716  * A processing instruction has been parsed.
 717  */
 718 static void
 719 processingInstructionDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *target,
 720                       const xmlChar *data)
 721 {
 722     callbacks++;
 723     if (quiet)
 724     return;
 725     if (data != NULL)
 726     fprintf(stdout, "SAX.processingInstruction(%s, %s)\n",
 727         (char *) target, (char *) data);
 728     else
 729     fprintf(stdout, "SAX.processingInstruction(%s, NULL)\n",
 730         (char *) target);
 731 }
 732 
 733 /**
 734  * cdataBlockDebug:
 735  * @ctx: the user data (XML parser context)
 736  * @value:  The pcdata content
 737  * @len:  the block length
 738  *
 739  * called when a pcdata block has been parsed
 740  */
 741 static void
 742 cdataBlockDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value, int len)
 743 {
 744     callbacks++;
 745     if (quiet)
 746     return;
 747     fprintf(stdout, "SAX.pcdata(%.20s, %d)\n",
 748         (char *) value, len);
 749 }
 750 
 751 /**
 752  * commentDebug:
 753  * @ctxt:  An XML parser context
 754  * @value:  the comment content
 755  *
 756  * A comment has been parsed.
 757  */
 758 static void
 759 commentDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value)
 760 {
 761     callbacks++;
 762     if (quiet)
 763     return;
 764     fprintf(stdout, "SAX.comment(%s)\n", value);
 765 }
 766 
 767 /**
 768  * warningDebug:
 769  * @ctxt:  An XML parser context
 770  * @msg:  the message to display/transmit
 771  * @...:  extra parameters for the message display
 772  *
 773  * Display and format a warning messages, gives file, line, position and
 774  * extra parameters.
 775  */
 776 static void XMLCDECL
 777 warningDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
 778 {
 779     va_list args;
 780 
 781     callbacks++;
 782     if (quiet)
 783     return;
 784     va_start(args, msg);
 785     fprintf(stdout, "SAX.warning: ");
 786     vfprintf(stdout, msg, args);
 787     va_end(args);
 788 }
 789 
 790 /**
 791  * errorDebug:
 792  * @ctxt:  An XML parser context
 793  * @msg:  the message to display/transmit
 794  * @...:  extra parameters for the message display
 795  *
 796  * Display and format a error messages, gives file, line, position and
 797  * extra parameters.
 798  */
 799 static void XMLCDECL
 800 errorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
 801 {
 802     va_list args;
 803 
 804     callbacks++;
 805     if (quiet)
 806     return;
 807     va_start(args, msg);
 808     fprintf(stdout, "SAX.error: ");
 809     vfprintf(stdout, msg, args);
 810     va_end(args);
 811 }
 812 
 813 /**
 814  * fatalErrorDebug:
 815  * @ctxt:  An XML parser context
 816  * @msg:  the message to display/transmit
 817  * @...:  extra parameters for the message display
 818  *
 819  * Display and format a fatalError messages, gives file, line, position and
 820  * extra parameters.
 821  */
 822 static void XMLCDECL
 823 fatalErrorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
 824 {
 825     va_list args;
 826 
 827     callbacks++;
 828     if (quiet)
 829     return;
 830     va_start(args, msg);
 831     fprintf(stdout, "SAX.fatalError: ");
 832     vfprintf(stdout, msg, args);
 833     va_end(args);
 834 }
 835 
 836 static xmlSAXHandler debugSAXHandlerStruct = {
 837     internalSubsetDebug,
 838     isStandaloneDebug,
 839     hasInternalSubsetDebug,
 840     hasExternalSubsetDebug,
 841     resolveEntityDebug,
 842     getEntityDebug,
 843     entityDeclDebug,
 844     notationDeclDebug,
 845     attributeDeclDebug,
 846     elementDeclDebug,
 847     unparsedEntityDeclDebug,
 848     setDocumentLocatorDebug,
 849     startDocumentDebug,
 850     endDocumentDebug,
 851     startElementDebug,
 852     endElementDebug,
 853     referenceDebug,
 854     charactersDebug,
 855     ignorableWhitespaceDebug,
 856     processingInstructionDebug,
 857     commentDebug,
 858     warningDebug,
 859     errorDebug,
 860     fatalErrorDebug,
 861     getParameterEntityDebug,
 862     cdataBlockDebug,
 863     externalSubsetDebug,
 864     1,
 865     NULL,
 866     NULL,
 867     NULL,
 868     NULL
 869 };
 870 
 871 xmlSAXHandlerPtr debugSAXHandler = &debugSAXHandlerStruct;
 872 
 873 /*
 874  * SAX2 specific callbacks
 875  */
 876 /**
 877  * startElementNsDebug:
 878  * @ctxt:  An XML parser context
 879  * @name:  The element name
 880  *
 881  * called when an opening tag has been processed.
 882  */
 883 static void
 884 startElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
 885                     const xmlChar *localname,
 886                     const xmlChar *prefix,
 887                     const xmlChar *URI,
 888             int nb_namespaces,
 889             const xmlChar **namespaces,
 890             int nb_attributes,
 891             int nb_defaulted,
 892             const xmlChar **attributes)
 893 {
 894     int i;
 895 
 896     callbacks++;
 897     if (quiet)
 898     return;
 899     fprintf(stdout, "SAX.startElementNs(%s", (char *) localname);
 900     if (prefix == NULL)
 901     fprintf(stdout, ", NULL");
 902     else
 903     fprintf(stdout, ", %s", (char *) prefix);
 904     if (URI == NULL)
 905     fprintf(stdout, ", NULL");
 906     else
 907     fprintf(stdout, ", '%s'", (char *) URI);
 908     fprintf(stdout, ", %d", nb_namespaces);
 909 
 910     if (namespaces != NULL) {
 911         for (i = 0;i < nb_namespaces * 2;i++) {
 912         fprintf(stdout, ", xmlns");
 913         if (namespaces[i] != NULL)
 914             fprintf(stdout, ":%s", namespaces[i]);
 915         i++;
 916         fprintf(stdout, "='%s'", namespaces[i]);
 917     }
 918     }
 919     fprintf(stdout, ", %d, %d", nb_attributes, nb_defaulted);
 920     if (attributes != NULL) {
 921         for (i = 0;i < nb_attributes * 5;i += 5) {
 922         if (attributes[i + 1] != NULL)
 923         fprintf(stdout, ", %s:%s='", attributes[i + 1], attributes[i]);
 924         else
 925         fprintf(stdout, ", %s='", attributes[i]);
 926         fprintf(stdout, "%.4s...', %d", attributes[i + 3],
 927             (int)(attributes[i + 4] - attributes[i + 3]));
 928     }
 929     }
 930     fprintf(stdout, ")\n");
 931 }
 932 
 933 /**
 934  * endElementDebug:
 935  * @ctxt:  An XML parser context
 936  * @name:  The element name
 937  *
 938  * called when the end of an element has been detected.
 939  */
 940 static void
 941 endElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
 942                   const xmlChar *localname,
 943                   const xmlChar *prefix,
 944                   const xmlChar *URI)
 945 {
 946     callbacks++;
 947     if (quiet)
 948     return;
 949     fprintf(stdout, "SAX.endElementNs(%s", (char *) localname);
 950     if (prefix == NULL)
 951     fprintf(stdout, ", NULL");
 952     else
 953     fprintf(stdout, ", %s", (char *) prefix);
 954     if (URI == NULL)
 955     fprintf(stdout, ", NULL)\n");
 956     else
 957     fprintf(stdout, ", '%s')\n", (char *) URI);
 958 }
 959 
 960 static xmlSAXHandler debugSAX2HandlerStruct = {
 961     internalSubsetDebug,
 962     isStandaloneDebug,
 963     hasInternalSubsetDebug,
 964     hasExternalSubsetDebug,
 965     resolveEntityDebug,
 966     getEntityDebug,
 967     entityDeclDebug,
 968     notationDeclDebug,
 969     attributeDeclDebug,
 970     elementDeclDebug,
 971     unparsedEntityDeclDebug,
 972     setDocumentLocatorDebug,
 973     startDocumentDebug,
 974     endDocumentDebug,
 975     NULL,
 976     NULL,
 977     referenceDebug,
 978     charactersDebug,
 979     ignorableWhitespaceDebug,
 980     processingInstructionDebug,
 981     commentDebug,
 982     warningDebug,
 983     errorDebug,
 984     fatalErrorDebug,
 985     getParameterEntityDebug,
 986     cdataBlockDebug,
 987     externalSubsetDebug,
 988     XML_SAX2_MAGIC,
 989     NULL,
 990     startElementNsDebug,
 991     endElementNsDebug,
 992     NULL
 993 };
 994 
 995 static xmlSAXHandlerPtr debugSAX2Handler = &debugSAX2HandlerStruct;
 996 
 997 /************************************************************************
 998  *                                  *
 999  *              Debug                   *
1000  *                                  *
1001  ************************************************************************/
1002 
1003 static void
1004 parseAndPrintFile(char *filename) {
1005     int res;
1006 
1007 #ifdef LIBXML_PUSH_ENABLED
1008     if (push) {
1009     FILE *f;
1010 
1011         if ((!quiet) && (!nonull)) {
1012         /*
1013          * Empty callbacks for checking
1014          */
1015 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
1016         f = fopen(filename, "rb");
1017 #else
1018         f = fopen(filename, "r");
1019 #endif
1020         if (f != NULL) {
1021         int ret;
1022         char chars[10];
1023         xmlParserCtxtPtr ctxt;
1024 
1025         ret = fread(chars, 1, 4, f);
1026         if (ret > 0) {
1027             ctxt = xmlCreatePushParserCtxt(emptySAXHandler, NULL,
1028                 chars, ret, filename);
1029             while ((ret = fread(chars, 1, 3, f)) > 0) {
1030             xmlParseChunk(ctxt, chars, ret, 0);
1031             }
1032             xmlParseChunk(ctxt, chars, 0, 1);
1033             xmlFreeParserCtxt(ctxt);
1034         }
1035         fclose(f);
1036         } else {
1037         xmlGenericError(xmlGenericErrorContext,
1038             "Cannot read file %s\n", filename);
1039         }
1040     }
1041     /*
1042      * Debug callback
1043      */
1044 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
1045     f = fopen(filename, "rb");
1046 #else
1047     f = fopen(filename, "r");
1048 #endif
1049     if (f != NULL) {
1050         int ret;
1051         char chars[10];
1052         xmlParserCtxtPtr ctxt;
1053 
1054         ret = fread(chars, 1, 4, f);
1055         if (ret > 0) {
1056             if (sax2)
1057             ctxt = xmlCreatePushParserCtxt(debugSAX2Handler, NULL,
1058                 chars, ret, filename);
1059         else
1060             ctxt = xmlCreatePushParserCtxt(debugSAXHandler, NULL,
1061                 chars, ret, filename);
1062         while ((ret = fread(chars, 1, 3, f)) > 0) {
1063             xmlParseChunk(ctxt, chars, ret, 0);
1064         }
1065         ret = xmlParseChunk(ctxt, chars, 0, 1);
1066         xmlFreeParserCtxt(ctxt);
1067         if (ret != 0) {
1068             fprintf(stdout,
1069                     "xmlSAXUserParseFile returned error %d\n", ret);
1070         }
1071         }
1072         fclose(f);
1073     }
1074     } else {
1075 #endif /* LIBXML_PUSH_ENABLED */
1076     if (!speed) {
1077         /*
1078          * Empty callbacks for checking
1079          */
1080         if ((!quiet) && (!nonull)) {
1081         res = xmlSAXUserParseFile(emptySAXHandler, NULL, filename);
1082         if (res != 0) {
1083             fprintf(stdout, "xmlSAXUserParseFile returned error %d\n", res);
1084         }
1085         }
1086 
1087         /*
1088          * Debug callback
1089          */
1090         callbacks = 0;
1091         if (repeat) {
1092             int i;
1093         for (i = 0;i < 99;i++) {
1094             if (sax2)
1095             res = xmlSAXUserParseFile(debugSAX2Handler, NULL,
1096                                       filename);
1097             else
1098             res = xmlSAXUserParseFile(debugSAXHandler, NULL,
1099                                       filename);
1100         }
1101         }
1102         if (sax2)
1103             res = xmlSAXUserParseFile(debugSAX2Handler, NULL, filename);
1104         else
1105         res = xmlSAXUserParseFile(debugSAXHandler, NULL, filename);
1106         if (res != 0) {
1107         fprintf(stdout, "xmlSAXUserParseFile returned error %d\n", res);
1108         }
1109         if (quiet)
1110         fprintf(stdout, "%d callbacks generated\n", callbacks);
1111     } else {
1112         /*
1113          * test 100x the SAX parse
1114          */
1115         int i;
1116 
1117         for (i = 0; i<100;i++)
1118         res = xmlSAXUserParseFile(emptySAXHandler, NULL, filename);
1119         if (res != 0) {
1120         fprintf(stdout, "xmlSAXUserParseFile returned error %d\n", res);
1121         }
1122     }
1123 #ifdef LIBXML_PUSH_ENABLED
1124     }
1125 #endif
1126 }
1127 
1128 
1129 int main(int argc, char **argv) {
1130     int i;
1131     int files = 0;
1132 
1133     LIBXML_TEST_VERSION /* be safe, plus calls xmlInitParser */
1134 
1135     for (i = 1; i < argc ; i++) {
1136     if ((!strcmp(argv[i], "-debug")) || (!strcmp(argv[i], "--debug")))
1137         debug++;
1138     else if ((!strcmp(argv[i], "-copy")) || (!strcmp(argv[i], "--copy")))
1139         copy++;
1140     else if ((!strcmp(argv[i], "-recover")) ||
1141              (!strcmp(argv[i], "--recover")))
1142         recovery++;
1143     else if ((!strcmp(argv[i], "-push")) ||
1144              (!strcmp(argv[i], "--push")))
1145 #ifdef LIBXML_PUSH_ENABLED
1146         push++;
1147 #else
1148         fprintf(stderr,"'push' not enabled in library - ignoring\n");
1149 #endif /* LIBXML_PUSH_ENABLED */
1150     else if ((!strcmp(argv[i], "-speed")) ||
1151              (!strcmp(argv[i], "--speed")))
1152         speed++;
1153     else if ((!strcmp(argv[i], "-timing")) ||
1154              (!strcmp(argv[i], "--timing"))) {
1155         nonull++;
1156         timing++;
1157         quiet++;
1158     } else if ((!strcmp(argv[i], "-repeat")) ||
1159              (!strcmp(argv[i], "--repeat"))) {
1160         repeat++;
1161         quiet++;
1162     } else if ((!strcmp(argv[i], "-noent")) ||
1163              (!strcmp(argv[i], "--noent")))
1164         noent++;
1165     else if ((!strcmp(argv[i], "-quiet")) ||
1166              (!strcmp(argv[i], "--quiet")))
1167         quiet++;
1168     else if ((!strcmp(argv[i], "-sax2")) ||
1169              (!strcmp(argv[i], "--sax2")))
1170         sax2++;
1171     else if ((!strcmp(argv[i], "-nonull")) ||
1172              (!strcmp(argv[i], "--nonull")))
1173         nonull++;
1174     }
1175     if (noent != 0) xmlSubstituteEntitiesDefault(1);
1176     for (i = 1; i < argc ; i++) {
1177     if (argv[i][0] != '-') {
1178         if (timing) {
1179         startTimer();
1180         }
1181         parseAndPrintFile(argv[i]);
1182         if (timing) {
1183         endTimer("Parsing");
1184         }
1185         files ++;
1186     }
1187     }
1188     xmlCleanupParser();
1189     xmlMemoryDump();
1190 
1191     return(0);
1192 }
1193 #else
1194 int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1195     printf("%s : SAX1 parsing support not compiled in\n", argv[0]);
1196     return(0);
1197 }
1198 #endif /* LIBXML_SAX1_ENABLED */