1 /*
   2  * runtest.c: C program to run libxml2 regression tests without
   3  *            requiring make or Python, and reducing platform dependancies
   4  *            to a strict minimum.
   5  *
   6  * To compile on Unixes:
   7  * cc -o runtest `xml2-config --cflags` runtest.c `xml2-config --libs` -lpthread
   8  *
   9  * See Copyright for the status of this software.
  10  *
  11  * daniel@veillard.com
  12  */
  13 
  14 #ifdef HAVE_CONFIG_H
  15 #include "libxml.h"
  16 #else
  17 #include <stdio.h>
  18 #endif
  19 
  20 #if !defined(_WIN32) || defined(__CYGWIN__)
  21 #include <unistd.h>
  22 #endif
  23 #include <string.h>
  24 #include <sys/types.h>
  25 #include <sys/stat.h>
  26 #include <fcntl.h>
  27 
  28 #include <libxml/parser.h>
  29 #include <libxml/tree.h>
  30 #include <libxml/uri.h>
  31 
  32 #ifdef LIBXML_OUTPUT_ENABLED
  33 #ifdef LIBXML_READER_ENABLED
  34 #include <libxml/xmlreader.h>
  35 #endif
  36 
  37 #ifdef LIBXML_XINCLUDE_ENABLED
  38 #include <libxml/xinclude.h>
  39 #endif
  40 
  41 #ifdef LIBXML_XPATH_ENABLED
  42 #include <libxml/xpath.h>
  43 #include <libxml/xpathInternals.h>
  44 #ifdef LIBXML_XPTR_ENABLED
  45 #include <libxml/xpointer.h>
  46 #endif
  47 #endif
  48 
  49 #ifdef LIBXML_SCHEMAS_ENABLED
  50 #include <libxml/relaxng.h>
  51 #include <libxml/xmlschemas.h>
  52 #include <libxml/xmlschemastypes.h>
  53 #endif
  54 
  55 #ifdef LIBXML_PATTERN_ENABLED
  56 #include <libxml/pattern.h>
  57 #endif
  58 
  59 #ifdef LIBXML_C14N_ENABLED
  60 #include <libxml/c14n.h>
  61 #endif
  62 
  63 #ifdef LIBXML_HTML_ENABLED
  64 #include <libxml/HTMLparser.h>
  65 #include <libxml/HTMLtree.h>
  66 
  67 /*
  68  * pseudo flag for the unification of HTML and XML tests
  69  */
  70 #define XML_PARSE_HTML 1 << 24
  71 #endif
  72 
  73 #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
  74 #include <libxml/globals.h>
  75 #include <libxml/threads.h>
  76 #include <libxml/parser.h>
  77 #include <libxml/catalog.h>
  78 #include <string.h>
  79 #endif
  80 
  81 /*
  82  * O_BINARY is just for Windows compatibility - if it isn't defined
  83  * on this system, avoid any compilation error
  84  */
  85 #ifdef  O_BINARY
  86 #define RD_FLAGS    O_RDONLY | O_BINARY
  87 #else
  88 #define RD_FLAGS    O_RDONLY
  89 #endif
  90 
  91 typedef int (*functest) (const char *filename, const char *result,
  92                          const char *error, int options);
  93 
  94 typedef struct testDesc testDesc;
  95 typedef testDesc *testDescPtr;
  96 struct testDesc {
  97     const char *desc; /* descripton of the test */
  98     functest    func; /* function implementing the test */
  99     const char *in;   /* glob to path for input files */
 100     const char *out;  /* output directory */
 101     const char *suffix;/* suffix for output files */
 102     const char *err;  /* suffix for error output files */
 103     int     options;  /* parser options for the test */
 104 };
 105 
 106 static int checkTestFile(const char *filename);
 107 
 108 #if defined(_WIN32) && !defined(__CYGWIN__)
 109 
 110 #include <windows.h>
 111 #include <io.h>
 112 
 113 typedef struct
 114 {
 115       size_t gl_pathc;    /* Count of paths matched so far  */
 116       char **gl_pathv;    /* List of matched pathnames.  */
 117       size_t gl_offs;     /* Slots to reserve in 'gl_pathv'.  */
 118 } glob_t;
 119 
 120 #define GLOB_DOOFFS 0
 121 static int glob(const char *pattern, int flags,
 122                 int errfunc(const char *epath, int eerrno),
 123                 glob_t *pglob) {
 124     glob_t *ret;
 125     WIN32_FIND_DATA FindFileData;
 126     HANDLE hFind;
 127     unsigned int nb_paths = 0;
 128     char directory[500];
 129     int len;
 130 
 131     if ((pattern == NULL) || (pglob == NULL)) return(-1);
 132 
 133     strncpy(directory, pattern, 499);
 134     for (len = strlen(directory);len >= 0;len--) {
 135         if (directory[len] == '/') {
 136         len++;
 137         directory[len] = 0;
 138         break;
 139     }
 140     }
 141     if (len <= 0)
 142         len = 0;
 143 
 144 
 145     ret = pglob;
 146     memset(ret, 0, sizeof(glob_t));
 147 
 148     hFind = FindFirstFileA(pattern, &FindFileData);
 149     if (hFind == INVALID_HANDLE_VALUE)
 150         return(0);
 151     nb_paths = 20;
 152     ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *));
 153     if (ret->gl_pathv == NULL) {
 154     FindClose(hFind);
 155         return(-1);
 156     }
 157     strncpy(directory + len, FindFileData.cFileName, 499 - len);
 158     ret->gl_pathv[ret->gl_pathc] = strdup(directory);
 159     if (ret->gl_pathv[ret->gl_pathc] == NULL)
 160         goto done;
 161     ret->gl_pathc++;
 162     while(FindNextFileA(hFind, &FindFileData)) {
 163         if (FindFileData.cFileName[0] == '.')
 164         continue;
 165         if (ret->gl_pathc + 2 > nb_paths) {
 166             char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *));
 167             if (tmp == NULL)
 168                 break;
 169             ret->gl_pathv = tmp;
 170             nb_paths *= 2;
 171     }
 172     strncpy(directory + len, FindFileData.cFileName, 499 - len);
 173     ret->gl_pathv[ret->gl_pathc] = strdup(directory);
 174         if (ret->gl_pathv[ret->gl_pathc] == NULL)
 175             break;
 176         ret->gl_pathc++;
 177     }
 178     ret->gl_pathv[ret->gl_pathc] = NULL;
 179 
 180 done:
 181     FindClose(hFind);
 182     return(0);
 183 }
 184 
 185 
 186 
 187 static void globfree(glob_t *pglob) {
 188     unsigned int i;
 189     if (pglob == NULL)
 190         return;
 191 
 192     for (i = 0;i < pglob->gl_pathc;i++) {
 193          if (pglob->gl_pathv[i] != NULL)
 194              free(pglob->gl_pathv[i]);
 195     }
 196 }
 197 #define vsnprintf _vsnprintf
 198 #define snprintf _snprintf
 199 #else
 200 #include <glob.h>
 201 #endif
 202 
 203 /************************************************************************
 204  *                                  *
 205  *      Libxml2 specific routines               *
 206  *                                  *
 207  ************************************************************************/
 208 
 209 static int nb_tests = 0;
 210 static int nb_errors = 0;
 211 static int nb_leaks = 0;
 212 static int extraMemoryFromResolver = 0;
 213 
 214 static int
 215 fatalError(void) {
 216     fprintf(stderr, "Exitting tests on fatal error\n");
 217     exit(1);
 218 }
 219 
 220 /*
 221  * We need to trap calls to the resolver to not account memory for the catalog
 222  * which is shared to the current running test. We also don't want to have
 223  * network downloads modifying tests.
 224  */
 225 static xmlParserInputPtr
 226 testExternalEntityLoader(const char *URL, const char *ID,
 227              xmlParserCtxtPtr ctxt) {
 228     xmlParserInputPtr ret;
 229 
 230     if (checkTestFile(URL)) {
 231     ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
 232     } else {
 233     int memused = xmlMemUsed();
 234     ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
 235     extraMemoryFromResolver += xmlMemUsed() - memused;
 236     }
 237 
 238     return(ret);
 239 }
 240 
 241 /*
 242  * Trapping the error messages at the generic level to grab the equivalent of
 243  * stderr messages on CLI tools.
 244  */
 245 static char testErrors[32769];
 246 static int testErrorsSize = 0;
 247 
 248 static void XMLCDECL
 249 testErrorHandler(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
 250     va_list args;
 251     int res;
 252 
 253     if (testErrorsSize >= 32768)
 254         return;
 255     va_start(args, msg);
 256     res = vsnprintf(&testErrors[testErrorsSize],
 257                     32768 - testErrorsSize,
 258             msg, args);
 259     va_end(args);
 260     if (testErrorsSize + res >= 32768) {
 261         /* buffer is full */
 262     testErrorsSize = 32768;
 263     testErrors[testErrorsSize] = 0;
 264     } else {
 265         testErrorsSize += res;
 266     }
 267     testErrors[testErrorsSize] = 0;
 268 }
 269 
 270 static void XMLCDECL
 271 channel(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
 272     va_list args;
 273     int res;
 274 
 275     if (testErrorsSize >= 32768)
 276         return;
 277     va_start(args, msg);
 278     res = vsnprintf(&testErrors[testErrorsSize],
 279                     32768 - testErrorsSize,
 280             msg, args);
 281     va_end(args);
 282     if (testErrorsSize + res >= 32768) {
 283         /* buffer is full */
 284     testErrorsSize = 32768;
 285     testErrors[testErrorsSize] = 0;
 286     } else {
 287         testErrorsSize += res;
 288     }
 289     testErrors[testErrorsSize] = 0;
 290 }
 291 
 292 /**
 293  * xmlParserPrintFileContext:
 294  * @input:  an xmlParserInputPtr input
 295  *
 296  * Displays current context within the input content for error tracking
 297  */
 298 
 299 static void
 300 xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
 301         xmlGenericErrorFunc chanl, void *data ) {
 302     const xmlChar *cur, *base;
 303     unsigned int n, col;    /* GCC warns if signed, because compared with sizeof() */
 304     xmlChar  content[81]; /* space for 80 chars + line terminator */
 305     xmlChar *ctnt;
 306 
 307     if (input == NULL) return;
 308     cur = input->cur;
 309     base = input->base;
 310     /* skip backwards over any end-of-lines */
 311     while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
 312     cur--;
 313     }
 314     n = 0;
 315     /* search backwards for beginning-of-line (to max buff size) */
 316     while ((n++ < (sizeof(content)-1)) && (cur > base) &&
 317    (*(cur) != '\n') && (*(cur) != '\r'))
 318         cur--;
 319     if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
 320     /* calculate the error position in terms of the current position */
 321     col = input->cur - cur;
 322     /* search forward for end-of-line (to max buff size) */
 323     n = 0;
 324     ctnt = content;
 325     /* copy selected text to our buffer */
 326     while ((*cur != 0) && (*(cur) != '\n') &&
 327    (*(cur) != '\r') && (n < sizeof(content)-1)) {
 328         *ctnt++ = *cur++;
 329     n++;
 330     }
 331     *ctnt = 0;
 332     /* print out the selected text */
 333     chanl(data ,"%s\n", content);
 334     /* create blank line with problem pointer */
 335     n = 0;
 336     ctnt = content;
 337     /* (leave buffer space for pointer + line terminator) */
 338     while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
 339     if (*(ctnt) != '\t')
 340         *(ctnt) = ' ';
 341     ctnt++;
 342     }
 343     *ctnt++ = '^';
 344     *ctnt = 0;
 345     chanl(data ,"%s\n", content);
 346 }
 347 
 348 static void
 349 testStructuredErrorHandler(void *ctx  ATTRIBUTE_UNUSED, xmlErrorPtr err) {
 350     char *file = NULL;
 351     int line = 0;
 352     int code = -1;
 353     int domain;
 354     void *data = NULL;
 355     const char *str;
 356     const xmlChar *name = NULL;
 357     xmlNodePtr node;
 358     xmlErrorLevel level;
 359     xmlParserInputPtr input = NULL;
 360     xmlParserInputPtr cur = NULL;
 361     xmlParserCtxtPtr ctxt = NULL;
 362 
 363     if (err == NULL)
 364         return;
 365 
 366     file = err->file;
 367     line = err->line;
 368     code = err->code;
 369     domain = err->domain;
 370     level = err->level;
 371     node = err->node;
 372     if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
 373         (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
 374     (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
 375     ctxt = err->ctxt;
 376     }
 377     str = err->message;
 378 
 379     if (code == XML_ERR_OK)
 380         return;
 381 
 382     if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
 383         name = node->name;
 384 
 385     /*
 386      * Maintain the compatibility with the legacy error handling
 387      */
 388     if (ctxt != NULL) {
 389         input = ctxt->input;
 390         if ((input != NULL) && (input->filename == NULL) &&
 391             (ctxt->inputNr > 1)) {
 392             cur = input;
 393             input = ctxt->inputTab[ctxt->inputNr - 2];
 394         }
 395         if (input != NULL) {
 396             if (input->filename)
 397                 channel(data, "%s:%d: ", input->filename, input->line);
 398             else if ((line != 0) && (domain == XML_FROM_PARSER))
 399                 channel(data, "Entity: line %d: ", input->line);
 400         }
 401     } else {
 402         if (file != NULL)
 403             channel(data, "%s:%d: ", file, line);
 404         else if ((line != 0) && (domain == XML_FROM_PARSER))
 405             channel(data, "Entity: line %d: ", line);
 406     }
 407     if (name != NULL) {
 408         channel(data, "element %s: ", name);
 409     }
 410     if (code == XML_ERR_OK)
 411         return;
 412     switch (domain) {
 413         case XML_FROM_PARSER:
 414             channel(data, "parser ");
 415             break;
 416         case XML_FROM_NAMESPACE:
 417             channel(data, "namespace ");
 418             break;
 419         case XML_FROM_DTD:
 420         case XML_FROM_VALID:
 421             channel(data, "validity ");
 422             break;
 423         case XML_FROM_HTML:
 424             channel(data, "HTML parser ");
 425             break;
 426         case XML_FROM_MEMORY:
 427             channel(data, "memory ");
 428             break;
 429         case XML_FROM_OUTPUT:
 430             channel(data, "output ");
 431             break;
 432         case XML_FROM_IO:
 433             channel(data, "I/O ");
 434             break;
 435         case XML_FROM_XINCLUDE:
 436             channel(data, "XInclude ");
 437             break;
 438         case XML_FROM_XPATH:
 439             channel(data, "XPath ");
 440             break;
 441         case XML_FROM_XPOINTER:
 442             channel(data, "parser ");
 443             break;
 444         case XML_FROM_REGEXP:
 445             channel(data, "regexp ");
 446             break;
 447         case XML_FROM_MODULE:
 448             channel(data, "module ");
 449             break;
 450         case XML_FROM_SCHEMASV:
 451             channel(data, "Schemas validity ");
 452             break;
 453         case XML_FROM_SCHEMASP:
 454             channel(data, "Schemas parser ");
 455             break;
 456         case XML_FROM_RELAXNGP:
 457             channel(data, "Relax-NG parser ");
 458             break;
 459         case XML_FROM_RELAXNGV:
 460             channel(data, "Relax-NG validity ");
 461             break;
 462         case XML_FROM_CATALOG:
 463             channel(data, "Catalog ");
 464             break;
 465         case XML_FROM_C14N:
 466             channel(data, "C14N ");
 467             break;
 468         case XML_FROM_XSLT:
 469             channel(data, "XSLT ");
 470             break;
 471         default:
 472             break;
 473     }
 474     if (code == XML_ERR_OK)
 475         return;
 476     switch (level) {
 477         case XML_ERR_NONE:
 478             channel(data, ": ");
 479             break;
 480         case XML_ERR_WARNING:
 481             channel(data, "warning : ");
 482             break;
 483         case XML_ERR_ERROR:
 484             channel(data, "error : ");
 485             break;
 486         case XML_ERR_FATAL:
 487             channel(data, "error : ");
 488             break;
 489     }
 490     if (code == XML_ERR_OK)
 491         return;
 492     if (str != NULL) {
 493         int len;
 494     len = xmlStrlen((const xmlChar *)str);
 495     if ((len > 0) && (str[len - 1] != '\n'))
 496         channel(data, "%s\n", str);
 497     else
 498         channel(data, "%s", str);
 499     } else {
 500         channel(data, "%s\n", "out of memory error");
 501     }
 502     if (code == XML_ERR_OK)
 503         return;
 504 
 505     if (ctxt != NULL) {
 506         xmlParserPrintFileContextInternal(input, channel, data);
 507         if (cur != NULL) {
 508             if (cur->filename)
 509                 channel(data, "%s:%d: \n", cur->filename, cur->line);
 510             else if ((line != 0) && (domain == XML_FROM_PARSER))
 511                 channel(data, "Entity: line %d: \n", cur->line);
 512             xmlParserPrintFileContextInternal(cur, channel, data);
 513         }
 514     }
 515     if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
 516         (err->int1 < 100) &&
 517     (err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
 518     xmlChar buf[150];
 519     int i;
 520 
 521     channel(data, "%s\n", err->str1);
 522     for (i=0;i < err->int1;i++)
 523          buf[i] = ' ';
 524     buf[i++] = '^';
 525     buf[i] = 0;
 526     channel(data, "%s\n", buf);
 527     }
 528 }
 529 
 530 static void
 531 initializeLibxml2(void) {
 532     xmlGetWarningsDefaultValue = 0;
 533     xmlPedanticParserDefault(0);
 534 
 535     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
 536     xmlInitParser();
 537     xmlSetExternalEntityLoader(testExternalEntityLoader);
 538     xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
 539 #ifdef LIBXML_SCHEMAS_ENABLED
 540     xmlSchemaInitTypes();
 541     xmlRelaxNGInitTypes();
 542 #endif
 543 }
 544 
 545 
 546 /************************************************************************
 547  *                                  *
 548  *      File name and path utilities                *
 549  *                                  *
 550  ************************************************************************/
 551 
 552 static const char *baseFilename(const char *filename) {
 553     const char *cur;
 554     if (filename == NULL)
 555         return(NULL);
 556     cur = &filename[strlen(filename)];
 557     while ((cur > filename) && (*cur != '/'))
 558         cur--;
 559     if (*cur == '/')
 560         return(cur + 1);
 561     return(cur);
 562 }
 563 
 564 static char *resultFilename(const char *filename, const char *out,
 565                             const char *suffix) {
 566     const char *base;
 567     char res[500];
 568     char suffixbuff[500];
 569 
 570 /*************
 571     if ((filename[0] == 't') && (filename[1] == 'e') &&
 572         (filename[2] == 's') && (filename[3] == 't') &&
 573     (filename[4] == '/'))
 574     filename = &filename[5];
 575  *************/
 576 
 577     base = baseFilename(filename);
 578     if (suffix == NULL)
 579         suffix = ".tmp";
 580     if (out == NULL)
 581         out = "";
 582 
 583     strncpy(suffixbuff,suffix,499);
 584 #ifdef VMS
 585     if(strstr(base,".") && suffixbuff[0]=='.')
 586       suffixbuff[0]='_';
 587 #endif
 588 
 589     snprintf(res, 499, "%s%s%s", out, base, suffixbuff);
 590     res[499] = 0;
 591     return(strdup(res));
 592 }
 593 
 594 static int checkTestFile(const char *filename) {
 595     struct stat buf;
 596 
 597     if (stat(filename, &buf) == -1)
 598         return(0);
 599 
 600 #if defined(_WIN32) && !defined(__CYGWIN__)
 601     if (!(buf.st_mode & _S_IFREG))
 602         return(0);
 603 #else
 604     if (!S_ISREG(buf.st_mode))
 605         return(0);
 606 #endif
 607 
 608     return(1);
 609 }
 610 
 611 static int compareFiles(const char *r1, const char *r2) {
 612     int res1, res2;
 613     int fd1, fd2;
 614     char bytes1[4096];
 615     char bytes2[4096];
 616 
 617     fd1 = open(r1, RD_FLAGS);
 618     if (fd1 < 0)
 619         return(-1);
 620     fd2 = open(r2, RD_FLAGS);
 621     if (fd2 < 0) {
 622         close(fd1);
 623         return(-1);
 624     }
 625     while (1) {
 626         res1 = read(fd1, bytes1, 4096);
 627         res2 = read(fd2, bytes2, 4096);
 628     if ((res1 != res2) || (res1 < 0)) {
 629         close(fd1);
 630         close(fd2);
 631         return(1);
 632     }
 633     if (res1 == 0)
 634         break;
 635     if (memcmp(bytes1, bytes2, res1) != 0) {
 636         close(fd1);
 637         close(fd2);
 638         return(1);
 639     }
 640     }
 641     close(fd1);
 642     close(fd2);
 643     return(0);
 644 }
 645 
 646 static int compareFileMem(const char *filename, const char *mem, int size) {
 647     int res;
 648     int fd;
 649     char bytes[4096];
 650     int idx = 0;
 651     struct stat info;
 652 
 653     if (stat(filename, &info) < 0)
 654     return(-1);
 655     if (info.st_size != size)
 656         return(-1);
 657     fd = open(filename, RD_FLAGS);
 658     if (fd < 0)
 659         return(-1);
 660     while (idx < size) {
 661         res = read(fd, bytes, 4096);
 662     if (res <= 0)
 663         break;
 664     if (res + idx > size)
 665         break;
 666     if (memcmp(bytes, &mem[idx], res) != 0) {
 667         int ix;
 668         for (ix=0; ix<res; ix++)
 669         if (bytes[ix] != mem[idx+ix])
 670             break;
 671         fprintf(stderr,"Compare error at position %d\n", idx+ix);
 672         close(fd);
 673         return(1);
 674     }
 675     idx += res;
 676     }
 677     close(fd);
 678     return(idx != size);
 679 }
 680 
 681 static int loadMem(const char *filename, const char **mem, int *size) {
 682     int fd, res;
 683     struct stat info;
 684     char *base;
 685     int siz = 0;
 686     if (stat(filename, &info) < 0)
 687     return(-1);
 688     base = malloc(info.st_size + 1);
 689     if (base == NULL)
 690     return(-1);
 691     if ((fd = open(filename, RD_FLAGS)) < 0) {
 692         free(base);
 693     return(-1);
 694     }
 695     while ((res = read(fd, &base[siz], info.st_size - siz)) > 0) {
 696         siz += res;
 697     }
 698     close(fd);
 699 #if !defined(_WIN32)
 700     if (siz != info.st_size) {
 701         free(base);
 702     return(-1);
 703     }
 704 #endif
 705     base[siz] = 0;
 706     *mem = base;
 707     *size = siz;
 708     return(0);
 709 }
 710 
 711 static int unloadMem(const char *mem) {
 712     free((char *)mem);
 713     return(0);
 714 }
 715 
 716 /************************************************************************
 717  *                                  *
 718  *      Tests implementations                   *
 719  *                                  *
 720  ************************************************************************/
 721 
 722 /************************************************************************
 723  *                                  *
 724  *      Parse to SAX based tests                *
 725  *                                  *
 726  ************************************************************************/
 727 
 728 static FILE *SAXdebug = NULL;
 729 
 730 /*
 731  * empty SAX block
 732  */
 733 static xmlSAXHandler emptySAXHandlerStruct = {
 734     NULL, /* internalSubset */
 735     NULL, /* isStandalone */
 736     NULL, /* hasInternalSubset */
 737     NULL, /* hasExternalSubset */
 738     NULL, /* resolveEntity */
 739     NULL, /* getEntity */
 740     NULL, /* entityDecl */
 741     NULL, /* notationDecl */
 742     NULL, /* attributeDecl */
 743     NULL, /* elementDecl */
 744     NULL, /* unparsedEntityDecl */
 745     NULL, /* setDocumentLocator */
 746     NULL, /* startDocument */
 747     NULL, /* endDocument */
 748     NULL, /* startElement */
 749     NULL, /* endElement */
 750     NULL, /* reference */
 751     NULL, /* characters */
 752     NULL, /* ignorableWhitespace */
 753     NULL, /* processingInstruction */
 754     NULL, /* comment */
 755     NULL, /* xmlParserWarning */
 756     NULL, /* xmlParserError */
 757     NULL, /* xmlParserError */
 758     NULL, /* getParameterEntity */
 759     NULL, /* cdataBlock; */
 760     NULL, /* externalSubset; */
 761     1,
 762     NULL,
 763     NULL, /* startElementNs */
 764     NULL, /* endElementNs */
 765     NULL  /* xmlStructuredErrorFunc */
 766 };
 767 
 768 static xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct;
 769 static int callbacks = 0;
 770 static int quiet = 0;
 771 
 772 /**
 773  * isStandaloneDebug:
 774  * @ctxt:  An XML parser context
 775  *
 776  * Is this document tagged standalone ?
 777  *
 778  * Returns 1 if true
 779  */
 780 static int
 781 isStandaloneDebug(void *ctx ATTRIBUTE_UNUSED)
 782 {
 783     callbacks++;
 784     if (quiet)
 785     return(0);
 786     fprintf(SAXdebug, "SAX.isStandalone()\n");
 787     return(0);
 788 }
 789 
 790 /**
 791  * hasInternalSubsetDebug:
 792  * @ctxt:  An XML parser context
 793  *
 794  * Does this document has an internal subset
 795  *
 796  * Returns 1 if true
 797  */
 798 static int
 799 hasInternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
 800 {
 801     callbacks++;
 802     if (quiet)
 803     return(0);
 804     fprintf(SAXdebug, "SAX.hasInternalSubset()\n");
 805     return(0);
 806 }
 807 
 808 /**
 809  * hasExternalSubsetDebug:
 810  * @ctxt:  An XML parser context
 811  *
 812  * Does this document has an external subset
 813  *
 814  * Returns 1 if true
 815  */
 816 static int
 817 hasExternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
 818 {
 819     callbacks++;
 820     if (quiet)
 821     return(0);
 822     fprintf(SAXdebug, "SAX.hasExternalSubset()\n");
 823     return(0);
 824 }
 825 
 826 /**
 827  * internalSubsetDebug:
 828  * @ctxt:  An XML parser context
 829  *
 830  * Does this document has an internal subset
 831  */
 832 static void
 833 internalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
 834            const xmlChar *ExternalID, const xmlChar *SystemID)
 835 {
 836     callbacks++;
 837     if (quiet)
 838     return;
 839     fprintf(SAXdebug, "SAX.internalSubset(%s,", name);
 840     if (ExternalID == NULL)
 841     fprintf(SAXdebug, " ,");
 842     else
 843     fprintf(SAXdebug, " %s,", ExternalID);
 844     if (SystemID == NULL)
 845     fprintf(SAXdebug, " )\n");
 846     else
 847     fprintf(SAXdebug, " %s)\n", SystemID);
 848 }
 849 
 850 /**
 851  * externalSubsetDebug:
 852  * @ctxt:  An XML parser context
 853  *
 854  * Does this document has an external subset
 855  */
 856 static void
 857 externalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
 858            const xmlChar *ExternalID, const xmlChar *SystemID)
 859 {
 860     callbacks++;
 861     if (quiet)
 862     return;
 863     fprintf(SAXdebug, "SAX.externalSubset(%s,", name);
 864     if (ExternalID == NULL)
 865     fprintf(SAXdebug, " ,");
 866     else
 867     fprintf(SAXdebug, " %s,", ExternalID);
 868     if (SystemID == NULL)
 869     fprintf(SAXdebug, " )\n");
 870     else
 871     fprintf(SAXdebug, " %s)\n", SystemID);
 872 }
 873 
 874 /**
 875  * resolveEntityDebug:
 876  * @ctxt:  An XML parser context
 877  * @publicId: The public ID of the entity
 878  * @systemId: The system ID of the entity
 879  *
 880  * Special entity resolver, better left to the parser, it has
 881  * more context than the application layer.
 882  * The default behaviour is to NOT resolve the entities, in that case
 883  * the ENTITY_REF nodes are built in the structure (and the parameter
 884  * values).
 885  *
 886  * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
 887  */
 888 static xmlParserInputPtr
 889 resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId)
 890 {
 891     callbacks++;
 892     if (quiet)
 893     return(NULL);
 894     /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */
 895 
 896 
 897     fprintf(SAXdebug, "SAX.resolveEntity(");
 898     if (publicId != NULL)
 899     fprintf(SAXdebug, "%s", (char *)publicId);
 900     else
 901     fprintf(SAXdebug, " ");
 902     if (systemId != NULL)
 903     fprintf(SAXdebug, ", %s)\n", (char *)systemId);
 904     else
 905     fprintf(SAXdebug, ", )\n");
 906 /*********
 907     if (systemId != NULL) {
 908         return(xmlNewInputFromFile(ctxt, (char *) systemId));
 909     }
 910  *********/
 911     return(NULL);
 912 }
 913 
 914 /**
 915  * getEntityDebug:
 916  * @ctxt:  An XML parser context
 917  * @name: The entity name
 918  *
 919  * Get an entity by name
 920  *
 921  * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
 922  */
 923 static xmlEntityPtr
 924 getEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
 925 {
 926     callbacks++;
 927     if (quiet)
 928     return(NULL);
 929     fprintf(SAXdebug, "SAX.getEntity(%s)\n", name);
 930     return(NULL);
 931 }
 932 
 933 /**
 934  * getParameterEntityDebug:
 935  * @ctxt:  An XML parser context
 936  * @name: The entity name
 937  *
 938  * Get a parameter entity by name
 939  *
 940  * Returns the xmlParserInputPtr
 941  */
 942 static xmlEntityPtr
 943 getParameterEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
 944 {
 945     callbacks++;
 946     if (quiet)
 947     return(NULL);
 948     fprintf(SAXdebug, "SAX.getParameterEntity(%s)\n", name);
 949     return(NULL);
 950 }
 951 
 952 
 953 /**
 954  * entityDeclDebug:
 955  * @ctxt:  An XML parser context
 956  * @name:  the entity name
 957  * @type:  the entity type
 958  * @publicId: The public ID of the entity
 959  * @systemId: The system ID of the entity
 960  * @content: the entity value (without processing).
 961  *
 962  * An entity definition has been parsed
 963  */
 964 static void
 965 entityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
 966           const xmlChar *publicId, const xmlChar *systemId, xmlChar *content)
 967 {
 968 const xmlChar *nullstr = BAD_CAST "(null)";
 969     /* not all libraries handle printing null pointers nicely */
 970     if (publicId == NULL)
 971         publicId = nullstr;
 972     if (systemId == NULL)
 973         systemId = nullstr;
 974     if (content == NULL)
 975         content = (xmlChar *)nullstr;
 976     callbacks++;
 977     if (quiet)
 978     return;
 979     fprintf(SAXdebug, "SAX.entityDecl(%s, %d, %s, %s, %s)\n",
 980             name, type, publicId, systemId, content);
 981 }
 982 
 983 /**
 984  * attributeDeclDebug:
 985  * @ctxt:  An XML parser context
 986  * @name:  the attribute name
 987  * @type:  the attribute type
 988  *
 989  * An attribute definition has been parsed
 990  */
 991 static void
 992 attributeDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar * elem,
 993                    const xmlChar * name, int type, int def,
 994                    const xmlChar * defaultValue, xmlEnumerationPtr tree)
 995 {
 996     callbacks++;
 997     if (quiet)
 998         return;
 999     if (defaultValue == NULL)
1000         fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, NULL, ...)\n",
1001                 elem, name, type, def);
1002     else
1003         fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, %s, ...)\n",
1004                 elem, name, type, def, defaultValue);
1005     xmlFreeEnumeration(tree);
1006 }
1007 
1008 /**
1009  * elementDeclDebug:
1010  * @ctxt:  An XML parser context
1011  * @name:  the element name
1012  * @type:  the element type
1013  * @content: the element value (without processing).
1014  *
1015  * An element definition has been parsed
1016  */
1017 static void
1018 elementDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
1019         xmlElementContentPtr content ATTRIBUTE_UNUSED)
1020 {
1021     callbacks++;
1022     if (quiet)
1023     return;
1024     fprintf(SAXdebug, "SAX.elementDecl(%s, %d, ...)\n",
1025             name, type);
1026 }
1027 
1028 /**
1029  * notationDeclDebug:
1030  * @ctxt:  An XML parser context
1031  * @name: The name of the notation
1032  * @publicId: The public ID of the entity
1033  * @systemId: The system ID of the entity
1034  *
1035  * What to do when a notation declaration has been parsed.
1036  */
1037 static void
1038 notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
1039          const xmlChar *publicId, const xmlChar *systemId)
1040 {
1041     callbacks++;
1042     if (quiet)
1043     return;
1044     fprintf(SAXdebug, "SAX.notationDecl(%s, %s, %s)\n",
1045             (char *) name, (char *) publicId, (char *) systemId);
1046 }
1047 
1048 /**
1049  * unparsedEntityDeclDebug:
1050  * @ctxt:  An XML parser context
1051  * @name: The name of the entity
1052  * @publicId: The public ID of the entity
1053  * @systemId: The system ID of the entity
1054  * @notationName: the name of the notation
1055  *
1056  * What to do when an unparsed entity declaration is parsed
1057  */
1058 static void
1059 unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
1060            const xmlChar *publicId, const xmlChar *systemId,
1061            const xmlChar *notationName)
1062 {
1063 const xmlChar *nullstr = BAD_CAST "(null)";
1064 
1065     if (publicId == NULL)
1066         publicId = nullstr;
1067     if (systemId == NULL)
1068         systemId = nullstr;
1069     if (notationName == NULL)
1070         notationName = nullstr;
1071     callbacks++;
1072     if (quiet)
1073     return;
1074     fprintf(SAXdebug, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n",
1075             (char *) name, (char *) publicId, (char *) systemId,
1076         (char *) notationName);
1077 }
1078 
1079 /**
1080  * setDocumentLocatorDebug:
1081  * @ctxt:  An XML parser context
1082  * @loc: A SAX Locator
1083  *
1084  * Receive the document locator at startup, actually xmlDefaultSAXLocator
1085  * Everything is available on the context, so this is useless in our case.
1086  */
1087 static void
1088 setDocumentLocatorDebug(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
1089 {
1090     callbacks++;
1091     if (quiet)
1092     return;
1093     fprintf(SAXdebug, "SAX.setDocumentLocator()\n");
1094 }
1095 
1096 /**
1097  * startDocumentDebug:
1098  * @ctxt:  An XML parser context
1099  *
1100  * called when the document start being processed.
1101  */
1102 static void
1103 startDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
1104 {
1105     callbacks++;
1106     if (quiet)
1107     return;
1108     fprintf(SAXdebug, "SAX.startDocument()\n");
1109 }
1110 
1111 /**
1112  * endDocumentDebug:
1113  * @ctxt:  An XML parser context
1114  *
1115  * called when the document end has been detected.
1116  */
1117 static void
1118 endDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
1119 {
1120     callbacks++;
1121     if (quiet)
1122     return;
1123     fprintf(SAXdebug, "SAX.endDocument()\n");
1124 }
1125 
1126 /**
1127  * startElementDebug:
1128  * @ctxt:  An XML parser context
1129  * @name:  The element name
1130  *
1131  * called when an opening tag has been processed.
1132  */
1133 static void
1134 startElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts)
1135 {
1136     int i;
1137 
1138     callbacks++;
1139     if (quiet)
1140     return;
1141     fprintf(SAXdebug, "SAX.startElement(%s", (char *) name);
1142     if (atts != NULL) {
1143         for (i = 0;(atts[i] != NULL);i++) {
1144         fprintf(SAXdebug, ", %s='", atts[i++]);
1145         if (atts[i] != NULL)
1146             fprintf(SAXdebug, "%s'", atts[i]);
1147     }
1148     }
1149     fprintf(SAXdebug, ")\n");
1150 }
1151 
1152 /**
1153  * endElementDebug:
1154  * @ctxt:  An XML parser context
1155  * @name:  The element name
1156  *
1157  * called when the end of an element has been detected.
1158  */
1159 static void
1160 endElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
1161 {
1162     callbacks++;
1163     if (quiet)
1164     return;
1165     fprintf(SAXdebug, "SAX.endElement(%s)\n", (char *) name);
1166 }
1167 
1168 /**
1169  * charactersDebug:
1170  * @ctxt:  An XML parser context
1171  * @ch:  a xmlChar string
1172  * @len: the number of xmlChar
1173  *
1174  * receiving some chars from the parser.
1175  * Question: how much at a time ???
1176  */
1177 static void
1178 charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1179 {
1180     char output[40];
1181     int i;
1182 
1183     callbacks++;
1184     if (quiet)
1185     return;
1186     for (i = 0;(i<len) && (i < 30);i++)
1187     output[i] = ch[i];
1188     output[i] = 0;
1189 
1190     fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len);
1191 }
1192 
1193 /**
1194  * referenceDebug:
1195  * @ctxt:  An XML parser context
1196  * @name:  The entity name
1197  *
1198  * called when an entity reference is detected.
1199  */
1200 static void
1201 referenceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
1202 {
1203     callbacks++;
1204     if (quiet)
1205     return;
1206     fprintf(SAXdebug, "SAX.reference(%s)\n", name);
1207 }
1208 
1209 /**
1210  * ignorableWhitespaceDebug:
1211  * @ctxt:  An XML parser context
1212  * @ch:  a xmlChar string
1213  * @start: the first char in the string
1214  * @len: the number of xmlChar
1215  *
1216  * receiving some ignorable whitespaces from the parser.
1217  * Question: how much at a time ???
1218  */
1219 static void
1220 ignorableWhitespaceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1221 {
1222     char output[40];
1223     int i;
1224 
1225     callbacks++;
1226     if (quiet)
1227     return;
1228     for (i = 0;(i<len) && (i < 30);i++)
1229     output[i] = ch[i];
1230     output[i] = 0;
1231     fprintf(SAXdebug, "SAX.ignorableWhitespace(%s, %d)\n", output, len);
1232 }
1233 
1234 /**
1235  * processingInstructionDebug:
1236  * @ctxt:  An XML parser context
1237  * @target:  the target name
1238  * @data: the PI data's
1239  * @len: the number of xmlChar
1240  *
1241  * A processing instruction has been parsed.
1242  */
1243 static void
1244 processingInstructionDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *target,
1245                       const xmlChar *data)
1246 {
1247     callbacks++;
1248     if (quiet)
1249     return;
1250     if (data != NULL)
1251     fprintf(SAXdebug, "SAX.processingInstruction(%s, %s)\n",
1252         (char *) target, (char *) data);
1253     else
1254     fprintf(SAXdebug, "SAX.processingInstruction(%s, NULL)\n",
1255         (char *) target);
1256 }
1257 
1258 /**
1259  * cdataBlockDebug:
1260  * @ctx: the user data (XML parser context)
1261  * @value:  The pcdata content
1262  * @len:  the block length
1263  *
1264  * called when a pcdata block has been parsed
1265  */
1266 static void
1267 cdataBlockDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value, int len)
1268 {
1269     callbacks++;
1270     if (quiet)
1271     return;
1272     fprintf(SAXdebug, "SAX.pcdata(%.20s, %d)\n",
1273         (char *) value, len);
1274 }
1275 
1276 /**
1277  * commentDebug:
1278  * @ctxt:  An XML parser context
1279  * @value:  the comment content
1280  *
1281  * A comment has been parsed.
1282  */
1283 static void
1284 commentDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value)
1285 {
1286     callbacks++;
1287     if (quiet)
1288     return;
1289     fprintf(SAXdebug, "SAX.comment(%s)\n", value);
1290 }
1291 
1292 /**
1293  * warningDebug:
1294  * @ctxt:  An XML parser context
1295  * @msg:  the message to display/transmit
1296  * @...:  extra parameters for the message display
1297  *
1298  * Display and format a warning messages, gives file, line, position and
1299  * extra parameters.
1300  */
1301 static void XMLCDECL
1302 warningDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
1303 {
1304     va_list args;
1305 
1306     callbacks++;
1307     if (quiet)
1308     return;
1309     va_start(args, msg);
1310     fprintf(SAXdebug, "SAX.warning: ");
1311     vfprintf(SAXdebug, msg, args);
1312     va_end(args);
1313 }
1314 
1315 /**
1316  * errorDebug:
1317  * @ctxt:  An XML parser context
1318  * @msg:  the message to display/transmit
1319  * @...:  extra parameters for the message display
1320  *
1321  * Display and format a error messages, gives file, line, position and
1322  * extra parameters.
1323  */
1324 static void XMLCDECL
1325 errorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
1326 {
1327     va_list args;
1328 
1329     callbacks++;
1330     if (quiet)
1331     return;
1332     va_start(args, msg);
1333     fprintf(SAXdebug, "SAX.error: ");
1334     vfprintf(SAXdebug, msg, args);
1335     va_end(args);
1336 }
1337 
1338 /**
1339  * fatalErrorDebug:
1340  * @ctxt:  An XML parser context
1341  * @msg:  the message to display/transmit
1342  * @...:  extra parameters for the message display
1343  *
1344  * Display and format a fatalError messages, gives file, line, position and
1345  * extra parameters.
1346  */
1347 static void XMLCDECL
1348 fatalErrorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
1349 {
1350     va_list args;
1351 
1352     callbacks++;
1353     if (quiet)
1354     return;
1355     va_start(args, msg);
1356     fprintf(SAXdebug, "SAX.fatalError: ");
1357     vfprintf(SAXdebug, msg, args);
1358     va_end(args);
1359 }
1360 
1361 static xmlSAXHandler debugSAXHandlerStruct = {
1362     internalSubsetDebug,
1363     isStandaloneDebug,
1364     hasInternalSubsetDebug,
1365     hasExternalSubsetDebug,
1366     resolveEntityDebug,
1367     getEntityDebug,
1368     entityDeclDebug,
1369     notationDeclDebug,
1370     attributeDeclDebug,
1371     elementDeclDebug,
1372     unparsedEntityDeclDebug,
1373     setDocumentLocatorDebug,
1374     startDocumentDebug,
1375     endDocumentDebug,
1376     startElementDebug,
1377     endElementDebug,
1378     referenceDebug,
1379     charactersDebug,
1380     ignorableWhitespaceDebug,
1381     processingInstructionDebug,
1382     commentDebug,
1383     warningDebug,
1384     errorDebug,
1385     fatalErrorDebug,
1386     getParameterEntityDebug,
1387     cdataBlockDebug,
1388     externalSubsetDebug,
1389     1,
1390     NULL,
1391     NULL,
1392     NULL,
1393     NULL
1394 };
1395 
1396 static xmlSAXHandlerPtr debugSAXHandler = &debugSAXHandlerStruct;
1397 
1398 /*
1399  * SAX2 specific callbacks
1400  */
1401 /**
1402  * startElementNsDebug:
1403  * @ctxt:  An XML parser context
1404  * @name:  The element name
1405  *
1406  * called when an opening tag has been processed.
1407  */
1408 static void
1409 startElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
1410                     const xmlChar *localname,
1411                     const xmlChar *prefix,
1412                     const xmlChar *URI,
1413             int nb_namespaces,
1414             const xmlChar **namespaces,
1415             int nb_attributes,
1416             int nb_defaulted,
1417             const xmlChar **attributes)
1418 {
1419     int i;
1420 
1421     callbacks++;
1422     if (quiet)
1423     return;
1424     fprintf(SAXdebug, "SAX.startElementNs(%s", (char *) localname);
1425     if (prefix == NULL)
1426     fprintf(SAXdebug, ", NULL");
1427     else
1428     fprintf(SAXdebug, ", %s", (char *) prefix);
1429     if (URI == NULL)
1430     fprintf(SAXdebug, ", NULL");
1431     else
1432     fprintf(SAXdebug, ", '%s'", (char *) URI);
1433     fprintf(SAXdebug, ", %d", nb_namespaces);
1434 
1435     if (namespaces != NULL) {
1436         for (i = 0;i < nb_namespaces * 2;i++) {
1437         fprintf(SAXdebug, ", xmlns");
1438         if (namespaces[i] != NULL)
1439             fprintf(SAXdebug, ":%s", namespaces[i]);
1440         i++;
1441         fprintf(SAXdebug, "='%s'", namespaces[i]);
1442     }
1443     }
1444     fprintf(SAXdebug, ", %d, %d", nb_attributes, nb_defaulted);
1445     if (attributes != NULL) {
1446         for (i = 0;i < nb_attributes * 5;i += 5) {
1447         if (attributes[i + 1] != NULL)
1448         fprintf(SAXdebug, ", %s:%s='", attributes[i + 1], attributes[i]);
1449         else
1450         fprintf(SAXdebug, ", %s='", attributes[i]);
1451         fprintf(SAXdebug, "%.4s...', %d", attributes[i + 3],
1452             (int)(attributes[i + 4] - attributes[i + 3]));
1453     }
1454     }
1455     fprintf(SAXdebug, ")\n");
1456 }
1457 
1458 /**
1459  * endElementDebug:
1460  * @ctxt:  An XML parser context
1461  * @name:  The element name
1462  *
1463  * called when the end of an element has been detected.
1464  */
1465 static void
1466 endElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
1467                   const xmlChar *localname,
1468                   const xmlChar *prefix,
1469                   const xmlChar *URI)
1470 {
1471     callbacks++;
1472     if (quiet)
1473     return;
1474     fprintf(SAXdebug, "SAX.endElementNs(%s", (char *) localname);
1475     if (prefix == NULL)
1476     fprintf(SAXdebug, ", NULL");
1477     else
1478     fprintf(SAXdebug, ", %s", (char *) prefix);
1479     if (URI == NULL)
1480     fprintf(SAXdebug, ", NULL)\n");
1481     else
1482     fprintf(SAXdebug, ", '%s')\n", (char *) URI);
1483 }
1484 
1485 static xmlSAXHandler debugSAX2HandlerStruct = {
1486     internalSubsetDebug,
1487     isStandaloneDebug,
1488     hasInternalSubsetDebug,
1489     hasExternalSubsetDebug,
1490     resolveEntityDebug,
1491     getEntityDebug,
1492     entityDeclDebug,
1493     notationDeclDebug,
1494     attributeDeclDebug,
1495     elementDeclDebug,
1496     unparsedEntityDeclDebug,
1497     setDocumentLocatorDebug,
1498     startDocumentDebug,
1499     endDocumentDebug,
1500     NULL,
1501     NULL,
1502     referenceDebug,
1503     charactersDebug,
1504     ignorableWhitespaceDebug,
1505     processingInstructionDebug,
1506     commentDebug,
1507     warningDebug,
1508     errorDebug,
1509     fatalErrorDebug,
1510     getParameterEntityDebug,
1511     cdataBlockDebug,
1512     externalSubsetDebug,
1513     XML_SAX2_MAGIC,
1514     NULL,
1515     startElementNsDebug,
1516     endElementNsDebug,
1517     NULL
1518 };
1519 
1520 static xmlSAXHandlerPtr debugSAX2Handler = &debugSAX2HandlerStruct;
1521 
1522 #ifdef LIBXML_HTML_ENABLED
1523 /**
1524  * htmlstartElementDebug:
1525  * @ctxt:  An XML parser context
1526  * @name:  The element name
1527  *
1528  * called when an opening tag has been processed.
1529  */
1530 static void
1531 htmlstartElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts)
1532 {
1533     int i;
1534 
1535     fprintf(SAXdebug, "SAX.startElement(%s", (char *) name);
1536     if (atts != NULL) {
1537         for (i = 0;(atts[i] != NULL);i++) {
1538         fprintf(SAXdebug, ", %s", atts[i++]);
1539         if (atts[i] != NULL) {
1540         unsigned char output[40];
1541         const unsigned char *att = atts[i];
1542         int outlen, attlen;
1543             fprintf(SAXdebug, "='");
1544         while ((attlen = strlen((char*)att)) > 0) {
1545             outlen = sizeof output - 1;
1546             htmlEncodeEntities(output, &outlen, att, &attlen, '\'');
1547             output[outlen] = 0;
1548             fprintf(SAXdebug, "%s", (char *) output);
1549             att += attlen;
1550         }
1551         fprintf(SAXdebug, "'");
1552         }
1553     }
1554     }
1555     fprintf(SAXdebug, ")\n");
1556 }
1557 
1558 /**
1559  * htmlcharactersDebug:
1560  * @ctxt:  An XML parser context
1561  * @ch:  a xmlChar string
1562  * @len: the number of xmlChar
1563  *
1564  * receiving some chars from the parser.
1565  * Question: how much at a time ???
1566  */
1567 static void
1568 htmlcharactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1569 {
1570     unsigned char output[40];
1571     int inlen = len, outlen = 30;
1572 
1573     htmlEncodeEntities(output, &outlen, ch, &inlen, 0);
1574     output[outlen] = 0;
1575 
1576     fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len);
1577 }
1578 
1579 /**
1580  * htmlcdataDebug:
1581  * @ctxt:  An XML parser context
1582  * @ch:  a xmlChar string
1583  * @len: the number of xmlChar
1584  *
1585  * receiving some cdata chars from the parser.
1586  * Question: how much at a time ???
1587  */
1588 static void
1589 htmlcdataDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1590 {
1591     unsigned char output[40];
1592     int inlen = len, outlen = 30;
1593 
1594     htmlEncodeEntities(output, &outlen, ch, &inlen, 0);
1595     output[outlen] = 0;
1596 
1597     fprintf(SAXdebug, "SAX.cdata(%s, %d)\n", output, len);
1598 }
1599 
1600 static xmlSAXHandler debugHTMLSAXHandlerStruct = {
1601     internalSubsetDebug,
1602     isStandaloneDebug,
1603     hasInternalSubsetDebug,
1604     hasExternalSubsetDebug,
1605     resolveEntityDebug,
1606     getEntityDebug,
1607     entityDeclDebug,
1608     notationDeclDebug,
1609     attributeDeclDebug,
1610     elementDeclDebug,
1611     unparsedEntityDeclDebug,
1612     setDocumentLocatorDebug,
1613     startDocumentDebug,
1614     endDocumentDebug,
1615     htmlstartElementDebug,
1616     endElementDebug,
1617     referenceDebug,
1618     htmlcharactersDebug,
1619     ignorableWhitespaceDebug,
1620     processingInstructionDebug,
1621     commentDebug,
1622     warningDebug,
1623     errorDebug,
1624     fatalErrorDebug,
1625     getParameterEntityDebug,
1626     htmlcdataDebug,
1627     externalSubsetDebug,
1628     1,
1629     NULL,
1630     NULL,
1631     NULL,
1632     NULL
1633 };
1634 
1635 static xmlSAXHandlerPtr debugHTMLSAXHandler = &debugHTMLSAXHandlerStruct;
1636 #endif /* LIBXML_HTML_ENABLED */
1637 
1638 #ifdef LIBXML_SAX1_ENABLED
1639 /**
1640  * saxParseTest:
1641  * @filename: the file to parse
1642  * @result: the file with expected result
1643  * @err: the file with error messages
1644  *
1645  * Parse a file using the SAX API and check for errors.
1646  *
1647  * Returns 0 in case of success, an error code otherwise
1648  */
1649 static int
1650 saxParseTest(const char *filename, const char *result,
1651              const char *err ATTRIBUTE_UNUSED,
1652              int options) {
1653     int ret;
1654     char *temp;
1655 
1656     nb_tests++;
1657     temp = resultFilename(filename, "", ".res");
1658     if (temp == NULL) {
1659         fprintf(stderr, "out of memory\n");
1660         fatalError();
1661     }
1662     SAXdebug = fopen(temp, "wb");
1663     if (SAXdebug == NULL) {
1664         fprintf(stderr, "Failed to write to %s\n", temp);
1665     free(temp);
1666     return(-1);
1667     }
1668 
1669     /* for SAX we really want the callbacks though the context handlers */
1670     xmlSetStructuredErrorFunc(NULL, NULL);
1671     xmlSetGenericErrorFunc(NULL, testErrorHandler);
1672 
1673 #ifdef LIBXML_HTML_ENABLED
1674     if (options & XML_PARSE_HTML) {
1675     htmlSAXParseFile(filename, NULL, emptySAXHandler, NULL);
1676     ret = 0;
1677     } else
1678 #endif
1679     ret = xmlSAXUserParseFile(emptySAXHandler, NULL, filename);
1680     if (ret == XML_WAR_UNDECLARED_ENTITY) {
1681         fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret);
1682         ret = 0;
1683     }
1684     if (ret != 0) {
1685         fprintf(stderr, "Failed to parse %s\n", filename);
1686     return(1);
1687     }
1688 #ifdef LIBXML_HTML_ENABLED
1689     if (options & XML_PARSE_HTML) {
1690     htmlSAXParseFile(filename, NULL, debugHTMLSAXHandler, NULL);
1691     ret = 0;
1692     } else
1693 #endif
1694     if (options & XML_PARSE_SAX1) {
1695     ret = xmlSAXUserParseFile(debugSAXHandler, NULL, filename);
1696     } else {
1697     ret = xmlSAXUserParseFile(debugSAX2Handler, NULL, filename);
1698     }
1699     if (ret == XML_WAR_UNDECLARED_ENTITY) {
1700         fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret);
1701         ret = 0;
1702     }
1703     fclose(SAXdebug);
1704     if (compareFiles(temp, result)) {
1705         fprintf(stderr, "Got a difference for %s\n", filename);
1706         ret = 1;
1707     } else
1708     unlink(temp);
1709     free(temp);
1710 
1711     /* switch back to structured error handling */
1712     xmlSetGenericErrorFunc(NULL, NULL);
1713     xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
1714 
1715     return(ret);
1716 }
1717 #endif
1718 
1719 /************************************************************************
1720  *                                  *
1721  *      Parse to tree based tests               *
1722  *                                  *
1723  ************************************************************************/
1724 /**
1725  * oldParseTest:
1726  * @filename: the file to parse
1727  * @result: the file with expected result
1728  * @err: the file with error messages: unused
1729  *
1730  * Parse a file using the old xmlParseFile API, then serialize back
1731  * reparse the result and serialize again, then check for deviation
1732  * in serialization.
1733  *
1734  * Returns 0 in case of success, an error code otherwise
1735  */
1736 static int
1737 oldParseTest(const char *filename, const char *result,
1738              const char *err ATTRIBUTE_UNUSED,
1739          int options ATTRIBUTE_UNUSED) {
1740     xmlDocPtr doc;
1741     char *temp;
1742     int res = 0;
1743 
1744     nb_tests++;
1745     /*
1746      * base of the test, parse with the old API
1747      */
1748 #ifdef LIBXML_SAX1_ENABLED
1749     doc = xmlParseFile(filename);
1750 #else
1751     doc = xmlReadFile(filename, NULL, 0);
1752 #endif
1753     if (doc == NULL)
1754         return(1);
1755     temp = resultFilename(filename, "", ".res");
1756     if (temp == NULL) {
1757         fprintf(stderr, "out of memory\n");
1758         fatalError();
1759     }
1760     xmlSaveFile(temp, doc);
1761     if (compareFiles(temp, result)) {
1762         res = 1;
1763     }
1764     xmlFreeDoc(doc);
1765 
1766     /*
1767      * Parse the saved result to make sure the round trip is okay
1768      */
1769 #ifdef LIBXML_SAX1_ENABLED
1770     doc = xmlParseFile(temp);
1771 #else
1772     doc = xmlReadFile(temp, NULL, 0);
1773 #endif
1774     if (doc == NULL)
1775         return(1);
1776     xmlSaveFile(temp, doc);
1777     if (compareFiles(temp, result)) {
1778         res = 1;
1779     }
1780     xmlFreeDoc(doc);
1781 
1782     unlink(temp);
1783     free(temp);
1784     return(res);
1785 }
1786 
1787 #ifdef LIBXML_PUSH_ENABLED
1788 /**
1789  * pushParseTest:
1790  * @filename: the file to parse
1791  * @result: the file with expected result
1792  * @err: the file with error messages: unused
1793  *
1794  * Parse a file using the Push API, then serialize back
1795  * to check for content.
1796  *
1797  * Returns 0 in case of success, an error code otherwise
1798  */
1799 static int
1800 pushParseTest(const char *filename, const char *result,
1801              const char *err ATTRIBUTE_UNUSED,
1802          int options) {
1803     xmlParserCtxtPtr ctxt;
1804     xmlDocPtr doc;
1805     const char *base;
1806     int size, res;
1807     int cur = 0;
1808 
1809     nb_tests++;
1810     /*
1811      * load the document in memory and work from there.
1812      */
1813     if (loadMem(filename, &base, &size) != 0) {
1814         fprintf(stderr, "Failed to load %s\n", filename);
1815     return(-1);
1816     }
1817 
1818 #ifdef LIBXML_HTML_ENABLED
1819     if (options & XML_PARSE_HTML)
1820     ctxt = htmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename,
1821                                     XML_CHAR_ENCODING_NONE);
1822     else
1823 #endif
1824     ctxt = xmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename);
1825     xmlCtxtUseOptions(ctxt, options);
1826     cur += 4;
1827     while (cur < size) {
1828         if (cur + 1024 >= size) {
1829 #ifdef LIBXML_HTML_ENABLED
1830         if (options & XML_PARSE_HTML)
1831         htmlParseChunk(ctxt, base + cur, size - cur, 1);
1832         else
1833 #endif
1834         xmlParseChunk(ctxt, base + cur, size - cur, 1);
1835         break;
1836     } else {
1837 #ifdef LIBXML_HTML_ENABLED
1838         if (options & XML_PARSE_HTML)
1839         htmlParseChunk(ctxt, base + cur, 1024, 0);
1840         else
1841 #endif
1842         xmlParseChunk(ctxt, base + cur, 1024, 0);
1843         cur += 1024;
1844     }
1845     }
1846     doc = ctxt->myDoc;
1847 #ifdef LIBXML_HTML_ENABLED
1848     if (options & XML_PARSE_HTML)
1849         res = 1;
1850     else
1851 #endif
1852     res = ctxt->wellFormed;
1853     xmlFreeParserCtxt(ctxt);
1854     free((char *)base);
1855     if (!res) {
1856     xmlFreeDoc(doc);
1857     fprintf(stderr, "Failed to parse %s\n", filename);
1858     return(-1);
1859     }
1860 #ifdef LIBXML_HTML_ENABLED
1861     if (options & XML_PARSE_HTML)
1862     htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1863     else
1864 #endif
1865     xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1866     xmlFreeDoc(doc);
1867     res = compareFileMem(result, base, size);
1868     if ((base == NULL) || (res != 0)) {
1869     if (base != NULL)
1870         xmlFree((char *)base);
1871         fprintf(stderr, "Result for %s failed\n", filename);
1872     return(-1);
1873     }
1874     xmlFree((char *)base);
1875     if (err != NULL) {
1876     res = compareFileMem(err, testErrors, testErrorsSize);
1877     if (res != 0) {
1878         fprintf(stderr, "Error for %s failed\n", filename);
1879         return(-1);
1880     }
1881     }
1882     return(0);
1883 }
1884 #endif
1885 
1886 /**
1887  * memParseTest:
1888  * @filename: the file to parse
1889  * @result: the file with expected result
1890  * @err: the file with error messages: unused
1891  *
1892  * Parse a file using the old xmlReadMemory API, then serialize back
1893  * reparse the result and serialize again, then check for deviation
1894  * in serialization.
1895  *
1896  * Returns 0 in case of success, an error code otherwise
1897  */
1898 static int
1899 memParseTest(const char *filename, const char *result,
1900              const char *err ATTRIBUTE_UNUSED,
1901          int options ATTRIBUTE_UNUSED) {
1902     xmlDocPtr doc;
1903     const char *base;
1904     int size, res;
1905 
1906     nb_tests++;
1907     /*
1908      * load and parse the memory
1909      */
1910     if (loadMem(filename, &base, &size) != 0) {
1911         fprintf(stderr, "Failed to load %s\n", filename);
1912     return(-1);
1913     }
1914 
1915     doc = xmlReadMemory(base, size, filename, NULL, 0);
1916     unloadMem(base);
1917     if (doc == NULL) {
1918         return(1);
1919     }
1920     xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1921     xmlFreeDoc(doc);
1922     res = compareFileMem(result, base, size);
1923     if ((base == NULL) || (res != 0)) {
1924     if (base != NULL)
1925         xmlFree((char *)base);
1926         fprintf(stderr, "Result for %s failed\n", filename);
1927     return(-1);
1928     }
1929     xmlFree((char *)base);
1930     return(0);
1931 }
1932 
1933 /**
1934  * noentParseTest:
1935  * @filename: the file to parse
1936  * @result: the file with expected result
1937  * @err: the file with error messages: unused
1938  *
1939  * Parse a file with entity resolution, then serialize back
1940  * reparse the result and serialize again, then check for deviation
1941  * in serialization.
1942  *
1943  * Returns 0 in case of success, an error code otherwise
1944  */
1945 static int
1946 noentParseTest(const char *filename, const char *result,
1947                const char *err  ATTRIBUTE_UNUSED,
1948            int options) {
1949     xmlDocPtr doc;
1950     char *temp;
1951     int res = 0;
1952 
1953     nb_tests++;
1954     /*
1955      * base of the test, parse with the old API
1956      */
1957     doc = xmlReadFile(filename, NULL, options);
1958     if (doc == NULL)
1959         return(1);
1960     temp = resultFilename(filename, "", ".res");
1961     if (temp == NULL) {
1962         fprintf(stderr, "Out of memory\n");
1963         fatalError();
1964     }
1965     xmlSaveFile(temp, doc);
1966     if (compareFiles(temp, result)) {
1967         res = 1;
1968     }
1969     xmlFreeDoc(doc);
1970 
1971     /*
1972      * Parse the saved result to make sure the round trip is okay
1973      */
1974     doc = xmlReadFile(filename, NULL, options);
1975     if (doc == NULL)
1976         return(1);
1977     xmlSaveFile(temp, doc);
1978     if (compareFiles(temp, result)) {
1979         res = 1;
1980     }
1981     xmlFreeDoc(doc);
1982 
1983     unlink(temp);
1984     free(temp);
1985     return(res);
1986 }
1987 
1988 /**
1989  * errParseTest:
1990  * @filename: the file to parse
1991  * @result: the file with expected result
1992  * @err: the file with error messages
1993  *
1994  * Parse a file using the xmlReadFile API and check for errors.
1995  *
1996  * Returns 0 in case of success, an error code otherwise
1997  */
1998 static int
1999 errParseTest(const char *filename, const char *result, const char *err,
2000              int options) {
2001     xmlDocPtr doc;
2002     const char *base = NULL;
2003     int size, res = 0;
2004 
2005     nb_tests++;
2006 #ifdef LIBXML_HTML_ENABLED
2007     if (options & XML_PARSE_HTML) {
2008         doc = htmlReadFile(filename, NULL, options);
2009     } else
2010 #endif
2011 #ifdef LIBXML_XINCLUDE_ENABLED
2012     if (options & XML_PARSE_XINCLUDE) {
2013     doc = xmlReadFile(filename, NULL, options);
2014     xmlXIncludeProcessFlags(doc, options);
2015     } else
2016 #endif
2017     {
2018     xmlGetWarningsDefaultValue = 1;
2019     doc = xmlReadFile(filename, NULL, options);
2020     }
2021     xmlGetWarningsDefaultValue = 0;
2022     if (result) {
2023     if (doc == NULL) {
2024         base = "";
2025         size = 0;
2026     } else {
2027 #ifdef LIBXML_HTML_ENABLED
2028         if (options & XML_PARSE_HTML) {
2029         htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
2030         } else
2031 #endif
2032         xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
2033     }
2034     res = compareFileMem(result, base, size);
2035     }
2036     if (doc != NULL) {
2037     if (base != NULL)
2038         xmlFree((char *)base);
2039     xmlFreeDoc(doc);
2040     }
2041     if (res != 0) {
2042         fprintf(stderr, "Result for %s failed\n", filename);
2043     return(-1);
2044     }
2045     if (err != NULL) {
2046     res = compareFileMem(err, testErrors, testErrorsSize);
2047     if (res != 0) {
2048         fprintf(stderr, "Error for %s failed\n", filename);
2049         return(-1);
2050     }
2051     } else if (options & XML_PARSE_DTDVALID) {
2052         if (testErrorsSize != 0)
2053         fprintf(stderr, "Validation for %s failed\n", filename);
2054     }
2055 
2056     return(0);
2057 }
2058 
2059 #ifdef LIBXML_READER_ENABLED
2060 /************************************************************************
2061  *                                  *
2062  *      Reader based tests                  *
2063  *                                  *
2064  ************************************************************************/
2065 
2066 static void processNode(FILE *out, xmlTextReaderPtr reader) {
2067     const xmlChar *name, *value;
2068     int type, empty;
2069 
2070     type = xmlTextReaderNodeType(reader);
2071     empty = xmlTextReaderIsEmptyElement(reader);
2072 
2073     name = xmlTextReaderConstName(reader);
2074     if (name == NULL)
2075     name = BAD_CAST "--";
2076 
2077     value = xmlTextReaderConstValue(reader);
2078 
2079 
2080     fprintf(out, "%d %d %s %d %d",
2081         xmlTextReaderDepth(reader),
2082         type,
2083         name,
2084         empty,
2085         xmlTextReaderHasValue(reader));
2086     if (value == NULL)
2087     fprintf(out, "\n");
2088     else {
2089     fprintf(out, " %s\n", value);
2090     }
2091 }
2092 static int
2093 streamProcessTest(const char *filename, const char *result, const char *err,
2094                   xmlTextReaderPtr reader, const char *rng) {
2095     int ret;
2096     char *temp = NULL;
2097     FILE *t = NULL;
2098 
2099     if (reader == NULL)
2100         return(-1);
2101 
2102     nb_tests++;
2103     if (result != NULL) {
2104     temp = resultFilename(filename, "", ".res");
2105     if (temp == NULL) {
2106         fprintf(stderr, "Out of memory\n");
2107         fatalError();
2108     }
2109     t = fopen(temp, "wb");
2110     if (t == NULL) {
2111         fprintf(stderr, "Can't open temp file %s\n", temp);
2112         free(temp);
2113         return(-1);
2114     }
2115     }
2116 #ifdef LIBXML_SCHEMAS_ENABLED
2117     if (rng != NULL) {
2118     ret = xmlTextReaderRelaxNGValidate(reader, rng);
2119     if (ret < 0) {
2120         testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n",
2121                          rng);
2122         fclose(t);
2123         unlink(temp);
2124         free(temp);
2125         return(0);
2126     }
2127     }
2128 #endif
2129     xmlGetWarningsDefaultValue = 1;
2130     ret = xmlTextReaderRead(reader);
2131     while (ret == 1) {
2132     if ((t != NULL) && (rng == NULL))
2133         processNode(t, reader);
2134         ret = xmlTextReaderRead(reader);
2135     }
2136     if (ret != 0) {
2137         testErrorHandler(NULL, "%s : failed to parse\n", filename);
2138     }
2139     if (rng != NULL) {
2140         if (xmlTextReaderIsValid(reader) != 1) {
2141         testErrorHandler(NULL, "%s fails to validate\n", filename);
2142     } else {
2143         testErrorHandler(NULL, "%s validates\n", filename);
2144     }
2145     }
2146     xmlGetWarningsDefaultValue = 0;
2147     if (t != NULL) {
2148         fclose(t);
2149     ret = compareFiles(temp, result);
2150     unlink(temp);
2151     free(temp);
2152     if (ret) {
2153         fprintf(stderr, "Result for %s failed\n", filename);
2154         return(-1);
2155     }
2156     }
2157     if (err != NULL) {
2158     ret = compareFileMem(err, testErrors, testErrorsSize);
2159     if (ret != 0) {
2160         fprintf(stderr, "Error for %s failed\n", filename);
2161         printf("%s", testErrors);
2162         return(-1);
2163     }
2164     }
2165 
2166     return(0);
2167 }
2168 
2169 /**
2170  * streamParseTest:
2171  * @filename: the file to parse
2172  * @result: the file with expected result
2173  * @err: the file with error messages
2174  *
2175  * Parse a file using the reader API and check for errors.
2176  *
2177  * Returns 0 in case of success, an error code otherwise
2178  */
2179 static int
2180 streamParseTest(const char *filename, const char *result, const char *err,
2181                 int options) {
2182     xmlTextReaderPtr reader;
2183     int ret;
2184 
2185     reader = xmlReaderForFile(filename, NULL, options);
2186     ret = streamProcessTest(filename, result, err, reader, NULL);
2187     xmlFreeTextReader(reader);
2188     return(ret);
2189 }
2190 
2191 /**
2192  * walkerParseTest:
2193  * @filename: the file to parse
2194  * @result: the file with expected result
2195  * @err: the file with error messages
2196  *
2197  * Parse a file using the walker, i.e. a reader built from a atree.
2198  *
2199  * Returns 0 in case of success, an error code otherwise
2200  */
2201 static int
2202 walkerParseTest(const char *filename, const char *result, const char *err,
2203                 int options) {
2204     xmlDocPtr doc;
2205     xmlTextReaderPtr reader;
2206     int ret;
2207 
2208     doc = xmlReadFile(filename, NULL, options);
2209     if (doc == NULL) {
2210         fprintf(stderr, "Failed to parse %s\n", filename);
2211     return(-1);
2212     }
2213     reader = xmlReaderWalker(doc);
2214     ret = streamProcessTest(filename, result, err, reader, NULL);
2215     xmlFreeTextReader(reader);
2216     xmlFreeDoc(doc);
2217     return(ret);
2218 }
2219 
2220 /**
2221  * streamMemParseTest:
2222  * @filename: the file to parse
2223  * @result: the file with expected result
2224  * @err: the file with error messages
2225  *
2226  * Parse a file using the reader API from memory and check for errors.
2227  *
2228  * Returns 0 in case of success, an error code otherwise
2229  */
2230 static int
2231 streamMemParseTest(const char *filename, const char *result, const char *err,
2232                    int options) {
2233     xmlTextReaderPtr reader;
2234     int ret;
2235     const char *base;
2236     int size;
2237 
2238     /*
2239      * load and parse the memory
2240      */
2241     if (loadMem(filename, &base, &size) != 0) {
2242         fprintf(stderr, "Failed to load %s\n", filename);
2243     return(-1);
2244     }
2245     reader = xmlReaderForMemory(base, size, filename, NULL, options);
2246     ret = streamProcessTest(filename, result, err, reader, NULL);
2247     free((char *)base);
2248     xmlFreeTextReader(reader);
2249     return(ret);
2250 }
2251 #endif
2252 
2253 #ifdef LIBXML_XPATH_ENABLED
2254 #ifdef LIBXML_DEBUG_ENABLED
2255 /************************************************************************
2256  *                                  *
2257  *      XPath and XPointer based tests              *
2258  *                                  *
2259  ************************************************************************/
2260 
2261 static FILE *xpathOutput;
2262 static xmlDocPtr xpathDocument;
2263 
2264 static void
2265 testXPath(const char *str, int xptr, int expr) {
2266     xmlXPathObjectPtr res;
2267     xmlXPathContextPtr ctxt;
2268 
2269     nb_tests++;
2270 #if defined(LIBXML_XPTR_ENABLED)
2271     if (xptr) {
2272     ctxt = xmlXPtrNewContext(xpathDocument, NULL, NULL);
2273     res = xmlXPtrEval(BAD_CAST str, ctxt);
2274     } else {
2275 #endif
2276     ctxt = xmlXPathNewContext(xpathDocument);
2277     ctxt->node = xmlDocGetRootElement(xpathDocument);
2278     if (expr)
2279         res = xmlXPathEvalExpression(BAD_CAST str, ctxt);
2280     else {
2281         /* res = xmlXPathEval(BAD_CAST str, ctxt); */
2282         xmlXPathCompExprPtr comp;
2283 
2284         comp = xmlXPathCompile(BAD_CAST str);
2285         if (comp != NULL) {
2286         res = xmlXPathCompiledEval(comp, ctxt);
2287         xmlXPathFreeCompExpr(comp);
2288         } else
2289         res = NULL;
2290     }
2291 #if defined(LIBXML_XPTR_ENABLED)
2292     }
2293 #endif
2294     xmlXPathDebugDumpObject(xpathOutput, res, 0);
2295     xmlXPathFreeObject(res);
2296     xmlXPathFreeContext(ctxt);
2297 }
2298 
2299 /**
2300  * xpathExprTest:
2301  * @filename: the file to parse
2302  * @result: the file with expected result
2303  * @err: the file with error messages
2304  *
2305  * Parse a file containing XPath standalone expressions and evaluate them
2306  *
2307  * Returns 0 in case of success, an error code otherwise
2308  */
2309 static int
2310 xpathCommonTest(const char *filename, const char *result,
2311                 int xptr, int expr) {
2312     FILE *input;
2313     char expression[5000];
2314     int len, ret = 0;
2315     char *temp;
2316 
2317     temp = resultFilename(filename, "", ".res");
2318     if (temp == NULL) {
2319         fprintf(stderr, "Out of memory\n");
2320         fatalError();
2321     }
2322     xpathOutput = fopen(temp, "wb");
2323     if (xpathOutput == NULL) {
2324     fprintf(stderr, "failed to open output file %s\n", temp);
2325         free(temp);
2326     return(-1);
2327     }
2328 
2329     input = fopen(filename, "rb");
2330     if (input == NULL) {
2331         xmlGenericError(xmlGenericErrorContext,
2332         "Cannot open %s for reading\n", filename);
2333         free(temp);
2334     return(-1);
2335     }
2336     while (fgets(expression, 4500, input) != NULL) {
2337     len = strlen(expression);
2338     len--;
2339     while ((len >= 0) &&
2340            ((expression[len] == '\n') || (expression[len] == '\t') ||
2341         (expression[len] == '\r') || (expression[len] == ' '))) len--;
2342     expression[len + 1] = 0;
2343     if (len >= 0) {
2344         fprintf(xpathOutput,
2345                 "\n========================\nExpression: %s\n",
2346             expression) ;
2347         testXPath(expression, xptr, expr);
2348     }
2349     }
2350 
2351     fclose(input);
2352     fclose(xpathOutput);
2353     if (result != NULL) {
2354     ret = compareFiles(temp, result);
2355     if (ret) {
2356         fprintf(stderr, "Result for %s failed\n", filename);
2357     }
2358     }
2359 
2360     unlink(temp);
2361     free(temp);
2362     return(ret);
2363 }
2364 
2365 /**
2366  * xpathExprTest:
2367  * @filename: the file to parse
2368  * @result: the file with expected result
2369  * @err: the file with error messages
2370  *
2371  * Parse a file containing XPath standalone expressions and evaluate them
2372  *
2373  * Returns 0 in case of success, an error code otherwise
2374  */
2375 static int
2376 xpathExprTest(const char *filename, const char *result,
2377               const char *err ATTRIBUTE_UNUSED,
2378               int options ATTRIBUTE_UNUSED) {
2379     return(xpathCommonTest(filename, result, 0, 1));
2380 }
2381 
2382 /**
2383  * xpathDocTest:
2384  * @filename: the file to parse
2385  * @result: the file with expected result
2386  * @err: the file with error messages
2387  *
2388  * Parse a file containing XPath expressions and evaluate them against
2389  * a set of corresponding documents.
2390  *
2391  * Returns 0 in case of success, an error code otherwise
2392  */
2393 static int
2394 xpathDocTest(const char *filename,
2395              const char *resul ATTRIBUTE_UNUSED,
2396              const char *err ATTRIBUTE_UNUSED,
2397              int options) {
2398 
2399     char pattern[500];
2400     char result[500];
2401     glob_t globbuf;
2402     size_t i;
2403     int ret = 0, res;
2404 
2405     xpathDocument = xmlReadFile(filename, NULL,
2406                                 options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
2407     if (xpathDocument == NULL) {
2408         fprintf(stderr, "Failed to load %s\n", filename);
2409     return(-1);
2410     }
2411 
2412     snprintf(pattern, 499, "./test/XPath/tests/%s*", baseFilename(filename));
2413     pattern[499] = 0;
2414     globbuf.gl_offs = 0;
2415     glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
2416     for (i = 0;i < globbuf.gl_pathc;i++) {
2417         snprintf(result, 499, "result/XPath/tests/%s",
2418              baseFilename(globbuf.gl_pathv[i]));
2419     res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 0, 0);
2420     if (res != 0)
2421         ret = res;
2422     }
2423     globfree(&globbuf);
2424 
2425     xmlFreeDoc(xpathDocument);
2426     return(ret);
2427 }
2428 
2429 #ifdef LIBXML_XPTR_ENABLED
2430 /**
2431  * xptrDocTest:
2432  * @filename: the file to parse
2433  * @result: the file with expected result
2434  * @err: the file with error messages
2435  *
2436  * Parse a file containing XPath expressions and evaluate them against
2437  * a set of corresponding documents.
2438  *
2439  * Returns 0 in case of success, an error code otherwise
2440  */
2441 static int
2442 xptrDocTest(const char *filename,
2443             const char *resul ATTRIBUTE_UNUSED,
2444             const char *err ATTRIBUTE_UNUSED,
2445             int options) {
2446 
2447     char pattern[500];
2448     char result[500];
2449     glob_t globbuf;
2450     size_t i;
2451     int ret = 0, res;
2452 
2453     xpathDocument = xmlReadFile(filename, NULL,
2454                                 options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
2455     if (xpathDocument == NULL) {
2456         fprintf(stderr, "Failed to load %s\n", filename);
2457     return(-1);
2458     }
2459 
2460     snprintf(pattern, 499, "./test/XPath/xptr/%s*", baseFilename(filename));
2461     pattern[499] = 0;
2462     globbuf.gl_offs = 0;
2463     glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
2464     for (i = 0;i < globbuf.gl_pathc;i++) {
2465         snprintf(result, 499, "result/XPath/xptr/%s",
2466              baseFilename(globbuf.gl_pathv[i]));
2467     res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 1, 0);
2468     if (res != 0)
2469         ret = res;
2470     }
2471     globfree(&globbuf);
2472 
2473     xmlFreeDoc(xpathDocument);
2474     return(ret);
2475 }
2476 #endif /* LIBXML_XPTR_ENABLED */
2477 
2478 /**
2479  * xmlidDocTest:
2480  * @filename: the file to parse
2481  * @result: the file with expected result
2482  * @err: the file with error messages
2483  *
2484  * Parse a file containing xml:id and check for errors and verify
2485  * that XPath queries will work on them as expected.
2486  *
2487  * Returns 0 in case of success, an error code otherwise
2488  */
2489 static int
2490 xmlidDocTest(const char *filename,
2491              const char *result,
2492              const char *err,
2493              int options) {
2494 
2495     int res = 0;
2496     int ret = 0;
2497     char *temp;
2498 
2499     xpathDocument = xmlReadFile(filename, NULL,
2500                                 options | XML_PARSE_DTDATTR | XML_PARSE_NOENT);
2501     if (xpathDocument == NULL) {
2502         fprintf(stderr, "Failed to load %s\n", filename);
2503     return(-1);
2504     }
2505 
2506     temp = resultFilename(filename, "", ".res");
2507     if (temp == NULL) {
2508         fprintf(stderr, "Out of memory\n");
2509         fatalError();
2510     }
2511     xpathOutput = fopen(temp, "wb");
2512     if (xpathOutput == NULL) {
2513     fprintf(stderr, "failed to open output file %s\n", temp);
2514         xmlFreeDoc(xpathDocument);
2515         free(temp);
2516     return(-1);
2517     }
2518 
2519     testXPath("id('bar')", 0, 0);
2520 
2521     fclose(xpathOutput);
2522     if (result != NULL) {
2523     ret = compareFiles(temp, result);
2524     if (ret) {
2525         fprintf(stderr, "Result for %s failed\n", filename);
2526         res = 1;
2527     }
2528     }
2529 
2530     unlink(temp);
2531     free(temp);
2532     xmlFreeDoc(xpathDocument);
2533 
2534     if (err != NULL) {
2535     ret = compareFileMem(err, testErrors, testErrorsSize);
2536     if (ret != 0) {
2537         fprintf(stderr, "Error for %s failed\n", filename);
2538         res = 1;
2539     }
2540     }
2541     return(res);
2542 }
2543 
2544 #endif /* LIBXML_DEBUG_ENABLED */
2545 #endif /* XPATH */
2546 /************************************************************************
2547  *                                  *
2548  *          URI based tests                 *
2549  *                                  *
2550  ************************************************************************/
2551 
2552 static void
2553 handleURI(const char *str, const char *base, FILE *o) {
2554     int ret;
2555     xmlURIPtr uri;
2556     xmlChar *res = NULL;
2557 
2558     uri = xmlCreateURI();
2559 
2560     if (base == NULL) {
2561     ret = xmlParseURIReference(uri, str);
2562     if (ret != 0)
2563         fprintf(o, "%s : error %d\n", str, ret);
2564     else {
2565         xmlNormalizeURIPath(uri->path);
2566         xmlPrintURI(o, uri);
2567         fprintf(o, "\n");
2568     }
2569     } else {
2570     res = xmlBuildURI((xmlChar *)str, (xmlChar *) base);
2571     if (res != NULL) {
2572         fprintf(o, "%s\n", (char *) res);
2573     }
2574     else
2575         fprintf(o, "::ERROR::\n");
2576     }
2577     if (res != NULL)
2578     xmlFree(res);
2579     xmlFreeURI(uri);
2580 }
2581 
2582 /**
2583  * uriCommonTest:
2584  * @filename: the file to parse
2585  * @result: the file with expected result
2586  * @err: the file with error messages
2587  *
2588  * Parse a file containing URI and check for errors
2589  *
2590  * Returns 0 in case of success, an error code otherwise
2591  */
2592 static int
2593 uriCommonTest(const char *filename,
2594              const char *result,
2595              const char *err,
2596              const char *base) {
2597     char *temp;
2598     FILE *o, *f;
2599     char str[1024];
2600     int res = 0, i, ret;
2601 
2602     temp = resultFilename(filename, "", ".res");
2603     if (temp == NULL) {
2604         fprintf(stderr, "Out of memory\n");
2605         fatalError();
2606     }
2607     o = fopen(temp, "wb");
2608     if (o == NULL) {
2609     fprintf(stderr, "failed to open output file %s\n", temp);
2610         free(temp);
2611     return(-1);
2612     }
2613     f = fopen(filename, "rb");
2614     if (f == NULL) {
2615     fprintf(stderr, "failed to open input file %s\n", filename);
2616     fclose(o);
2617     unlink(temp);
2618         free(temp);
2619     return(-1);
2620     }
2621 
2622     while (1) {
2623     /*
2624      * read one line in string buffer.
2625      */
2626     if (fgets (&str[0], sizeof (str) - 1, f) == NULL)
2627        break;
2628 
2629     /*
2630      * remove the ending spaces
2631      */
2632     i = strlen(str);
2633     while ((i > 0) &&
2634            ((str[i - 1] == '\n') || (str[i - 1] == '\r') ||
2635         (str[i - 1] == ' ') || (str[i - 1] == '\t'))) {
2636         i--;
2637         str[i] = 0;
2638     }
2639     nb_tests++;
2640     handleURI(str, base, o);
2641     }
2642 
2643     fclose(f);
2644     fclose(o);
2645 
2646     if (result != NULL) {
2647     ret = compareFiles(temp, result);
2648     if (ret) {
2649         fprintf(stderr, "Result for %s failed\n", filename);
2650         res = 1;
2651     }
2652     }
2653     if (err != NULL) {
2654     ret = compareFileMem(err, testErrors, testErrorsSize);
2655     if (ret != 0) {
2656         fprintf(stderr, "Error for %s failed\n", filename);
2657         res = 1;
2658     }
2659     }
2660 
2661     unlink(temp);
2662     free(temp);
2663     return(res);
2664 }
2665 
2666 /**
2667  * uriParseTest:
2668  * @filename: the file to parse
2669  * @result: the file with expected result
2670  * @err: the file with error messages
2671  *
2672  * Parse a file containing URI and check for errors
2673  *
2674  * Returns 0 in case of success, an error code otherwise
2675  */
2676 static int
2677 uriParseTest(const char *filename,
2678              const char *result,
2679              const char *err,
2680              int options ATTRIBUTE_UNUSED) {
2681     return(uriCommonTest(filename, result, err, NULL));
2682 }
2683 
2684 /**
2685  * uriBaseTest:
2686  * @filename: the file to parse
2687  * @result: the file with expected result
2688  * @err: the file with error messages
2689  *
2690  * Parse a file containing URI, compose them against a fixed base and
2691  * check for errors
2692  *
2693  * Returns 0 in case of success, an error code otherwise
2694  */
2695 static int
2696 uriBaseTest(const char *filename,
2697              const char *result,
2698              const char *err,
2699              int options ATTRIBUTE_UNUSED) {
2700     return(uriCommonTest(filename, result, err,
2701                          "http://foo.com/path/to/index.html?orig#help"));
2702 }
2703 
2704 static int urip_success = 1;
2705 static int urip_current = 0;
2706 static const char *urip_testURLs[] = {
2707     "urip://example.com/a b.html",
2708     "urip://example.com/a%20b.html",
2709     "file:///path/to/a b.html",
2710     "file:///path/to/a%20b.html",
2711     "/path/to/a b.html",
2712     "/path/to/a%20b.html",
2713     "urip://example.com/résumé.html",
2714     "urip://example.com/test?a=1&b=2%263&c=4#foo",
2715     NULL
2716 };
2717 static const char *urip_rcvsURLs[] = {
2718     /* it is an URI the strings must be escaped */
2719     "urip://example.com/a%20b.html",
2720     /* check that % escaping is not broken */
2721     "urip://example.com/a%20b.html",
2722     /* it's an URI path the strings must be escaped */
2723     "file:///path/to/a%20b.html",
2724     /* check that % escaping is not broken */
2725     "file:///path/to/a%20b.html",
2726     /* this is not an URI, this is a path, so this should not be escaped */
2727     "/path/to/a b.html",
2728     /* check that paths with % are not broken */
2729     "/path/to/a%20b.html",
2730     /* out of context the encoding can't be guessed byte by byte conversion */
2731     "urip://example.com/r%E9sum%E9.html",
2732     /* verify we don't destroy URIs especially the query part */
2733     "urip://example.com/test?a=1&b=2%263&c=4#foo",
2734     NULL
2735 };
2736 static const char *urip_res = "<list/>";
2737 static const char *urip_cur = NULL;
2738 static int urip_rlen;
2739 
2740 /**
2741  * uripMatch:
2742  * @URI: an URI to test
2743  *
2744  * Check for an urip: query
2745  *
2746  * Returns 1 if yes and 0 if another Input module should be used
2747  */
2748 static int
2749 uripMatch(const char * URI) {
2750     if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog")))
2751         return(0);
2752     /* Verify we received the escaped URL */
2753     if (strcmp(urip_rcvsURLs[urip_current], URI))
2754     urip_success = 0;
2755     return(1);
2756 }
2757 
2758 /**
2759  * uripOpen:
2760  * @URI: an URI to test
2761  *
2762  * Return a pointer to the urip: query handler, in this example simply
2763  * the urip_current pointer...
2764  *
2765  * Returns an Input context or NULL in case or error
2766  */
2767 static void *
2768 uripOpen(const char * URI) {
2769     if ((URI == NULL) || (!strcmp(URI, "file:///etc/xml/catalog")))
2770         return(NULL);
2771     /* Verify we received the escaped URL */
2772     if (strcmp(urip_rcvsURLs[urip_current], URI))
2773     urip_success = 0;
2774     urip_cur = urip_res;
2775     urip_rlen = strlen(urip_res);
2776     return((void *) urip_cur);
2777 }
2778 
2779 /**
2780  * uripClose:
2781  * @context: the read context
2782  *
2783  * Close the urip: query handler
2784  *
2785  * Returns 0 or -1 in case of error
2786  */
2787 static int
2788 uripClose(void * context) {
2789     if (context == NULL) return(-1);
2790     urip_cur = NULL;
2791     urip_rlen = 0;
2792     return(0);
2793 }
2794 
2795 /**
2796  * uripRead:
2797  * @context: the read context
2798  * @buffer: where to store data
2799  * @len: number of bytes to read
2800  *
2801  * Implement an urip: query read.
2802  *
2803  * Returns the number of bytes read or -1 in case of error
2804  */
2805 static int
2806 uripRead(void * context, char * buffer, int len) {
2807    const char *ptr = (const char *) context;
2808 
2809    if ((context == NULL) || (buffer == NULL) || (len < 0))
2810        return(-1);
2811 
2812    if (len > urip_rlen) len = urip_rlen;
2813    memcpy(buffer, ptr, len);
2814    urip_rlen -= len;
2815    return(len);
2816 }
2817 
2818 static int
2819 urip_checkURL(const char *URL) {
2820     xmlDocPtr doc;
2821 
2822     doc = xmlReadFile(URL, NULL, 0);
2823     if (doc == NULL)
2824         return(-1);
2825     xmlFreeDoc(doc);
2826     return(1);
2827 }
2828 
2829 /**
2830  * uriPathTest:
2831  * @filename: ignored
2832  * @result: ignored
2833  * @err: ignored
2834  *
2835  * Run a set of tests to check how Path and URI are handled before
2836  * being passed to the I/O layer
2837  *
2838  * Returns 0 in case of success, an error code otherwise
2839  */
2840 static int
2841 uriPathTest(const char *filename ATTRIBUTE_UNUSED,
2842              const char *result ATTRIBUTE_UNUSED,
2843              const char *err ATTRIBUTE_UNUSED,
2844              int options ATTRIBUTE_UNUSED) {
2845     int parsed;
2846     int failures = 0;
2847 
2848     /*
2849      * register the new I/O handlers
2850      */
2851     if (xmlRegisterInputCallbacks(uripMatch, uripOpen, uripRead, uripClose) < 0)
2852     {
2853         fprintf(stderr, "failed to register HTTP handler\n");
2854     return(-1);
2855     }
2856 
2857     for (urip_current = 0;urip_testURLs[urip_current] != NULL;urip_current++) {
2858         urip_success = 1;
2859         parsed = urip_checkURL(urip_testURLs[urip_current]);
2860     if (urip_success != 1) {
2861         fprintf(stderr, "failed the URL passing test for %s",
2862                 urip_testURLs[urip_current]);
2863         failures++;
2864     } else if (parsed != 1) {
2865         fprintf(stderr, "failed the parsing test for %s",
2866                 urip_testURLs[urip_current]);
2867         failures++;
2868     }
2869     nb_tests++;
2870     }
2871 
2872     xmlPopInputCallbacks();
2873     return(failures);
2874 }
2875 
2876 #ifdef LIBXML_SCHEMAS_ENABLED
2877 /************************************************************************
2878  *                                  *
2879  *          Schemas tests                   *
2880  *                                  *
2881  ************************************************************************/
2882 static int
2883 schemasOneTest(const char *sch,
2884                const char *filename,
2885                const char *result,
2886            const char *err,
2887            int options,
2888            xmlSchemaPtr schemas) {
2889     xmlDocPtr doc;
2890     xmlSchemaValidCtxtPtr ctxt;
2891     int ret = 0;
2892     int validResult = 0;
2893     char *temp;
2894     FILE *schemasOutput;
2895 
2896     doc = xmlReadFile(filename, NULL, options);
2897     if (doc == NULL) {
2898         fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
2899     return(-1);
2900     }
2901 
2902     temp = resultFilename(result, "", ".res");
2903     if (temp == NULL) {
2904         fprintf(stderr, "Out of memory\n");
2905         fatalError();
2906     }
2907     schemasOutput = fopen(temp, "wb");
2908     if (schemasOutput == NULL) {
2909     fprintf(stderr, "failed to open output file %s\n", temp);
2910     xmlFreeDoc(doc);
2911         free(temp);
2912     return(-1);
2913     }
2914 
2915     ctxt = xmlSchemaNewValidCtxt(schemas);
2916     xmlSchemaSetValidErrors(ctxt,
2917          (xmlSchemaValidityErrorFunc) testErrorHandler,
2918          (xmlSchemaValidityWarningFunc) testErrorHandler,
2919      ctxt);
2920     validResult = xmlSchemaValidateDoc(ctxt, doc);
2921     if (validResult == 0) {
2922     fprintf(schemasOutput, "%s validates\n", filename);
2923     } else if (validResult > 0) {
2924     fprintf(schemasOutput, "%s fails to validate\n", filename);
2925     } else {
2926     fprintf(schemasOutput, "%s validation generated an internal error\n",
2927            filename);
2928     }
2929     fclose(schemasOutput);
2930     if (result) {
2931     if (compareFiles(temp, result)) {
2932         fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
2933         ret = 1;
2934     }
2935     }
2936     unlink(temp);
2937     free(temp);
2938 
2939     if ((validResult != 0) && (err != NULL)) {
2940     if (compareFileMem(err, testErrors, testErrorsSize)) {
2941         fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
2942         ret = 1;
2943     }
2944     }
2945 
2946     xmlSchemaFreeValidCtxt(ctxt);
2947     xmlFreeDoc(doc);
2948     return(ret);
2949 }
2950 /**
2951  * schemasTest:
2952  * @filename: the schemas file
2953  * @result: the file with expected result
2954  * @err: the file with error messages
2955  *
2956  * Parse a file containing URI, compose them against a fixed base and
2957  * check for errors
2958  *
2959  * Returns 0 in case of success, an error code otherwise
2960  */
2961 static int
2962 schemasTest(const char *filename,
2963             const char *resul ATTRIBUTE_UNUSED,
2964             const char *errr ATTRIBUTE_UNUSED,
2965             int options) {
2966     const char *base = baseFilename(filename);
2967     const char *base2;
2968     const char *instance;
2969     xmlSchemaParserCtxtPtr ctxt;
2970     xmlSchemaPtr schemas;
2971     int res = 0, len, ret;
2972     char pattern[500];
2973     char prefix[500];
2974     char result[500];
2975     char err[500];
2976     glob_t globbuf;
2977     size_t i;
2978     char count = 0;
2979 
2980     /* first compile the schemas if possible */
2981     ctxt = xmlSchemaNewParserCtxt(filename);
2982     xmlSchemaSetParserErrors(ctxt,
2983          (xmlSchemaValidityErrorFunc) testErrorHandler,
2984          (xmlSchemaValidityWarningFunc) testErrorHandler,
2985      ctxt);
2986     schemas = xmlSchemaParse(ctxt);
2987     xmlSchemaFreeParserCtxt(ctxt);
2988 
2989     /*
2990      * most of the mess is about the output filenames generated by the Makefile
2991      */
2992     len = strlen(base);
2993     if ((len > 499) || (len < 5)) {
2994         xmlSchemaFree(schemas);
2995     return(-1);
2996     }
2997     len -= 4; /* remove trailing .xsd */
2998     if (base[len - 2] == '_') {
2999         len -= 2; /* remove subtest number */
3000     }
3001     if (base[len - 2] == '_') {
3002         len -= 2; /* remove subtest number */
3003     }
3004     memcpy(prefix, base, len);
3005     prefix[len] = 0;
3006 
3007     snprintf(pattern, 499, "./test/schemas/%s_?.xml", prefix);
3008     pattern[499] = 0;
3009 
3010     if (base[len] == '_') {
3011         len += 2;
3012     memcpy(prefix, base, len);
3013     prefix[len] = 0;
3014     }
3015 
3016     globbuf.gl_offs = 0;
3017     glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
3018     for (i = 0;i < globbuf.gl_pathc;i++) {
3019         testErrorsSize = 0;
3020     testErrors[0] = 0;
3021         instance = globbuf.gl_pathv[i];
3022     base2 = baseFilename(instance);
3023     len = strlen(base2);
3024     if ((len > 6) && (base2[len - 6] == '_')) {
3025         count = base2[len - 5];
3026         snprintf(result, 499, "result/schemas/%s_%c",
3027              prefix, count);
3028         result[499] = 0;
3029         snprintf(err, 499, "result/schemas/%s_%c.err",
3030              prefix, count);
3031         err[499] = 0;
3032     } else {
3033         fprintf(stderr, "don't know how to process %s\n", instance);
3034         continue;
3035     }
3036     if (schemas == NULL) {
3037     } else {
3038         nb_tests++;
3039         ret = schemasOneTest(filename, instance, result, err,
3040                              options, schemas);
3041         if (ret != 0)
3042         res = ret;
3043     }
3044     }
3045     globfree(&globbuf);
3046     xmlSchemaFree(schemas);
3047 
3048     return(res);
3049 }
3050 
3051 /************************************************************************
3052  *                                  *
3053  *          Schemas tests                   *
3054  *                                  *
3055  ************************************************************************/
3056 static int
3057 rngOneTest(const char *sch,
3058                const char *filename,
3059                const char *result,
3060            const char *err,
3061            int options,
3062            xmlRelaxNGPtr schemas) {
3063     xmlDocPtr doc;
3064     xmlRelaxNGValidCtxtPtr ctxt;
3065     int ret = 0;
3066     char *temp;
3067     FILE *schemasOutput;
3068 
3069     doc = xmlReadFile(filename, NULL, options);
3070     if (doc == NULL) {
3071         fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
3072     return(-1);
3073     }
3074 
3075     temp = resultFilename(result, "", ".res");
3076     if (temp == NULL) {
3077         fprintf(stderr, "Out of memory\n");
3078         fatalError();
3079     }
3080     schemasOutput = fopen(temp, "wb");
3081     if (schemasOutput == NULL) {
3082     fprintf(stderr, "failed to open output file %s\n", temp);
3083     xmlFreeDoc(doc);
3084         free(temp);
3085     return(-1);
3086     }
3087 
3088     ctxt = xmlRelaxNGNewValidCtxt(schemas);
3089     xmlRelaxNGSetValidErrors(ctxt,
3090          (xmlRelaxNGValidityErrorFunc) testErrorHandler,
3091          (xmlRelaxNGValidityWarningFunc) testErrorHandler,
3092      ctxt);
3093     ret = xmlRelaxNGValidateDoc(ctxt, doc);
3094     if (ret == 0) {
3095     testErrorHandler(NULL, "%s validates\n", filename);
3096     } else if (ret > 0) {
3097     testErrorHandler(NULL, "%s fails to validate\n", filename);
3098     } else {
3099     testErrorHandler(NULL, "%s validation generated an internal error\n",
3100            filename);
3101     }
3102     fclose(schemasOutput);
3103     if (result) {
3104     if (compareFiles(temp, result)) {
3105         fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
3106         ret = 1;
3107     }
3108     }
3109     unlink(temp);
3110     free(temp);
3111 
3112     if (err != NULL) {
3113     if (compareFileMem(err, testErrors, testErrorsSize)) {
3114         fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
3115         ret = 1;
3116         printf("%s", testErrors);
3117     }
3118     }
3119 
3120 
3121     xmlRelaxNGFreeValidCtxt(ctxt);
3122     xmlFreeDoc(doc);
3123     return(ret);
3124 }
3125 /**
3126  * rngTest:
3127  * @filename: the schemas file
3128  * @result: the file with expected result
3129  * @err: the file with error messages
3130  *
3131  * Parse an RNG schemas and then apply it to the related .xml
3132  *
3133  * Returns 0 in case of success, an error code otherwise
3134  */
3135 static int
3136 rngTest(const char *filename,
3137             const char *resul ATTRIBUTE_UNUSED,
3138             const char *errr ATTRIBUTE_UNUSED,
3139             int options) {
3140     const char *base = baseFilename(filename);
3141     const char *base2;
3142     const char *instance;
3143     xmlRelaxNGParserCtxtPtr ctxt;
3144     xmlRelaxNGPtr schemas;
3145     int res = 0, len, ret;
3146     char pattern[500];
3147     char prefix[500];
3148     char result[500];
3149     char err[500];
3150     glob_t globbuf;
3151     size_t i;
3152     char count = 0;
3153 
3154     /* first compile the schemas if possible */
3155     ctxt = xmlRelaxNGNewParserCtxt(filename);
3156     xmlRelaxNGSetParserErrors(ctxt,
3157          (xmlRelaxNGValidityErrorFunc) testErrorHandler,
3158          (xmlRelaxNGValidityWarningFunc) testErrorHandler,
3159      ctxt);
3160     schemas = xmlRelaxNGParse(ctxt);
3161     xmlRelaxNGFreeParserCtxt(ctxt);
3162 
3163     /*
3164      * most of the mess is about the output filenames generated by the Makefile
3165      */
3166     len = strlen(base);
3167     if ((len > 499) || (len < 5)) {
3168         xmlRelaxNGFree(schemas);
3169     return(-1);
3170     }
3171     len -= 4; /* remove trailing .rng */
3172     memcpy(prefix, base, len);
3173     prefix[len] = 0;
3174 
3175     snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix);
3176     pattern[499] = 0;
3177 
3178     globbuf.gl_offs = 0;
3179     glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
3180     for (i = 0;i < globbuf.gl_pathc;i++) {
3181         testErrorsSize = 0;
3182     testErrors[0] = 0;
3183         instance = globbuf.gl_pathv[i];
3184     base2 = baseFilename(instance);
3185     len = strlen(base2);
3186     if ((len > 6) && (base2[len - 6] == '_')) {
3187         count = base2[len - 5];
3188         snprintf(result, 499, "result/relaxng/%s_%c",
3189              prefix, count);
3190         result[499] = 0;
3191         snprintf(err, 499, "result/relaxng/%s_%c.err",
3192              prefix, count);
3193         err[499] = 0;
3194     } else {
3195         fprintf(stderr, "don't know how to process %s\n", instance);
3196         continue;
3197     }
3198     if (schemas == NULL) {
3199     } else {
3200         nb_tests++;
3201         ret = rngOneTest(filename, instance, result, err,
3202                              options, schemas);
3203         if (res != 0)
3204         ret = res;
3205     }
3206     }
3207     globfree(&globbuf);
3208     xmlRelaxNGFree(schemas);
3209 
3210     return(res);
3211 }
3212 
3213 #ifdef LIBXML_READER_ENABLED
3214 /**
3215  * rngStreamTest:
3216  * @filename: the schemas file
3217  * @result: the file with expected result
3218  * @err: the file with error messages
3219  *
3220  * Parse a set of files with streaming, applying an RNG schemas
3221  *
3222  * Returns 0 in case of success, an error code otherwise
3223  */
3224 static int
3225 rngStreamTest(const char *filename,
3226             const char *resul ATTRIBUTE_UNUSED,
3227             const char *errr ATTRIBUTE_UNUSED,
3228             int options) {
3229     const char *base = baseFilename(filename);
3230     const char *base2;
3231     const char *instance;
3232     int res = 0, len, ret;
3233     char pattern[500];
3234     char prefix[500];
3235     char result[500];
3236     char err[500];
3237     glob_t globbuf;
3238     size_t i;
3239     char count = 0;
3240     xmlTextReaderPtr reader;
3241     int disable_err = 0;
3242 
3243     /*
3244      * most of the mess is about the output filenames generated by the Makefile
3245      */
3246     len = strlen(base);
3247     if ((len > 499) || (len < 5)) {
3248     fprintf(stderr, "len(base) == %d !\n", len);
3249     return(-1);
3250     }
3251     len -= 4; /* remove trailing .rng */
3252     memcpy(prefix, base, len);
3253     prefix[len] = 0;
3254 
3255     /*
3256      * strictly unifying the error messages is nearly impossible this
3257      * hack is also done in the Makefile
3258      */
3259     if ((!strcmp(prefix, "tutor10_1")) || (!strcmp(prefix, "tutor10_2")) ||
3260         (!strcmp(prefix, "tutor3_2")) || (!strcmp(prefix, "307377")))
3261     disable_err = 1;
3262 
3263     snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix);
3264     pattern[499] = 0;
3265 
3266     globbuf.gl_offs = 0;
3267     glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
3268     for (i = 0;i < globbuf.gl_pathc;i++) {
3269         testErrorsSize = 0;
3270     testErrors[0] = 0;
3271         instance = globbuf.gl_pathv[i];
3272     base2 = baseFilename(instance);
3273     len = strlen(base2);
3274     if ((len > 6) && (base2[len - 6] == '_')) {
3275         count = base2[len - 5];
3276         snprintf(result, 499, "result/relaxng/%s_%c",
3277              prefix, count);
3278         result[499] = 0;
3279         snprintf(err, 499, "result/relaxng/%s_%c.err",
3280              prefix, count);
3281         err[499] = 0;
3282     } else {
3283         fprintf(stderr, "don't know how to process %s\n", instance);
3284         continue;
3285     }
3286     reader = xmlReaderForFile(instance, NULL, options);
3287     if (reader == NULL) {
3288         fprintf(stderr, "Failed to build reder for %s\n", instance);
3289     }
3290     if (disable_err == 1)
3291         ret = streamProcessTest(instance, result, NULL, reader, filename);
3292     else
3293         ret = streamProcessTest(instance, result, err, reader, filename);
3294     xmlFreeTextReader(reader);
3295     if (ret != 0) {
3296         fprintf(stderr, "instance %s failed\n", instance);
3297         res = ret;
3298     }
3299     }
3300     globfree(&globbuf);
3301 
3302     return(res);
3303 }
3304 #endif /* READER */
3305 
3306 #endif
3307 
3308 #ifdef LIBXML_PATTERN_ENABLED
3309 #ifdef LIBXML_READER_ENABLED
3310 /************************************************************************
3311  *                                  *
3312  *          Patterns tests                  *
3313  *                                  *
3314  ************************************************************************/
3315 static void patternNode(FILE *out, xmlTextReaderPtr reader,
3316                         const char *pattern, xmlPatternPtr patternc,
3317             xmlStreamCtxtPtr patstream) {
3318     xmlChar *path = NULL;
3319     int match = -1;
3320     int type, empty;
3321 
3322     type = xmlTextReaderNodeType(reader);
3323     empty = xmlTextReaderIsEmptyElement(reader);
3324 
3325     if (type == XML_READER_TYPE_ELEMENT) {
3326     /* do the check only on element start */
3327     match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader));
3328 
3329     if (match) {
3330         path = xmlGetNodePath(xmlTextReaderCurrentNode(reader));
3331         fprintf(out, "Node %s matches pattern %s\n", path, pattern);
3332     }
3333     }
3334     if (patstream != NULL) {
3335     int ret;
3336 
3337     if (type == XML_READER_TYPE_ELEMENT) {
3338         ret = xmlStreamPush(patstream,
3339                 xmlTextReaderConstLocalName(reader),
3340                 xmlTextReaderConstNamespaceUri(reader));
3341         if (ret < 0) {
3342         fprintf(out, "xmlStreamPush() failure\n");
3343         xmlFreeStreamCtxt(patstream);
3344         patstream = NULL;
3345         } else if (ret != match) {
3346         if (path == NULL) {
3347             path = xmlGetNodePath(
3348                    xmlTextReaderCurrentNode(reader));
3349         }
3350         fprintf(out,
3351             "xmlPatternMatch and xmlStreamPush disagree\n");
3352         fprintf(out,
3353             "  pattern %s node %s\n",
3354             pattern, path);
3355         }
3356 
3357 
3358     }
3359     if ((type == XML_READER_TYPE_END_ELEMENT) ||
3360         ((type == XML_READER_TYPE_ELEMENT) && (empty))) {
3361         ret = xmlStreamPop(patstream);
3362         if (ret < 0) {
3363         fprintf(out, "xmlStreamPop() failure\n");
3364         xmlFreeStreamCtxt(patstream);
3365         patstream = NULL;
3366         }
3367     }
3368     }
3369     if (path != NULL)
3370     xmlFree(path);
3371 }
3372 
3373 /**
3374  * patternTest:
3375  * @filename: the schemas file
3376  * @result: the file with expected result
3377  * @err: the file with error messages
3378  *
3379  * Parse a set of files with streaming, applying an RNG schemas
3380  *
3381  * Returns 0 in case of success, an error code otherwise
3382  */
3383 static int
3384 patternTest(const char *filename,
3385             const char *resul ATTRIBUTE_UNUSED,
3386             const char *err ATTRIBUTE_UNUSED,
3387             int options) {
3388     xmlPatternPtr patternc = NULL;
3389     xmlStreamCtxtPtr patstream = NULL;
3390     FILE *o, *f;
3391     char str[1024];
3392     char xml[500];
3393     char result[500];
3394     int len, i;
3395     int ret = 0, res;
3396     char *temp;
3397     xmlTextReaderPtr reader;
3398     xmlDocPtr doc;
3399 
3400     len = strlen(filename);
3401     len -= 4;
3402     memcpy(xml, filename, len);
3403     xml[len] = 0;
3404     snprintf(result, 499, "result/pattern/%s", baseFilename(xml));
3405     result[499] = 0;
3406     memcpy(xml + len, ".xml", 5);
3407 
3408     if (!checkTestFile(xml)) {
3409     fprintf(stderr, "Missing xml file %s\n", xml);
3410     return(-1);
3411     }
3412     if (!checkTestFile(result)) {
3413     fprintf(stderr, "Missing result file %s\n", result);
3414     return(-1);
3415     }
3416     f = fopen(filename, "rb");
3417     if (f == NULL) {
3418         fprintf(stderr, "Failed to open %s\n", filename);
3419     return(-1);
3420     }
3421     temp = resultFilename(filename, "", ".res");
3422     if (temp == NULL) {
3423         fprintf(stderr, "Out of memory\n");
3424         fatalError();
3425     }
3426     o = fopen(temp, "wb");
3427     if (o == NULL) {
3428     fprintf(stderr, "failed to open output file %s\n", temp);
3429     fclose(f);
3430         free(temp);
3431     return(-1);
3432     }
3433     while (1) {
3434     /*
3435      * read one line in string buffer.
3436      */
3437     if (fgets (&str[0], sizeof (str) - 1, f) == NULL)
3438        break;
3439 
3440     /*
3441      * remove the ending spaces
3442      */
3443     i = strlen(str);
3444     while ((i > 0) &&
3445            ((str[i - 1] == '\n') || (str[i - 1] == '\r') ||
3446         (str[i - 1] == ' ') || (str[i - 1] == '\t'))) {
3447         i--;
3448         str[i] = 0;
3449     }
3450     doc = xmlReadFile(xml, NULL, options);
3451     if (doc == NULL) {
3452         fprintf(stderr, "Failed to parse %s\n", xml);
3453         ret = 1;
3454     } else {
3455         xmlNodePtr root;
3456         const xmlChar *namespaces[22];
3457         int j;
3458         xmlNsPtr ns;
3459 
3460         root = xmlDocGetRootElement(doc);
3461         for (ns = root->nsDef, j = 0;ns != NULL && j < 20;ns=ns->next) {
3462         namespaces[j++] = ns->href;
3463         namespaces[j++] = ns->prefix;
3464         }
3465         namespaces[j++] = NULL;
3466         namespaces[j++] = NULL;
3467 
3468         patternc = xmlPatterncompile((const xmlChar *) str, doc->dict,
3469                      0, &namespaces[0]);
3470         if (patternc == NULL) {
3471         testErrorHandler(NULL,
3472             "Pattern %s failed to compile\n", str);
3473         xmlFreeDoc(doc);
3474         ret = 1;
3475         continue;
3476         }
3477         patstream = xmlPatternGetStreamCtxt(patternc);
3478         if (patstream != NULL) {
3479         ret = xmlStreamPush(patstream, NULL, NULL);
3480         if (ret < 0) {
3481             fprintf(stderr, "xmlStreamPush() failure\n");
3482             xmlFreeStreamCtxt(patstream);
3483             patstream = NULL;
3484         }
3485         }
3486         nb_tests++;
3487 
3488         reader = xmlReaderWalker(doc);
3489         res = xmlTextReaderRead(reader);
3490         while (res == 1) {
3491         patternNode(o, reader, str, patternc, patstream);
3492         res = xmlTextReaderRead(reader);
3493         }
3494         if (res != 0) {
3495         fprintf(o, "%s : failed to parse\n", filename);
3496         }
3497         xmlFreeTextReader(reader);
3498         xmlFreeDoc(doc);
3499         xmlFreeStreamCtxt(patstream);
3500         patstream = NULL;
3501         xmlFreePattern(patternc);
3502 
3503     }
3504     }
3505 
3506     fclose(f);
3507     fclose(o);
3508 
3509     ret = compareFiles(temp, result);
3510     if (ret) {
3511     fprintf(stderr, "Result for %s failed\n", filename);
3512     ret = 1;
3513     }
3514     unlink(temp);
3515     free(temp);
3516     return(ret);
3517 }
3518 #endif /* READER */
3519 #endif /* PATTERN */
3520 #ifdef LIBXML_C14N_ENABLED
3521 /************************************************************************
3522  *                                  *
3523  *          Canonicalization tests              *
3524  *                                  *
3525  ************************************************************************/
3526 static xmlXPathObjectPtr
3527 load_xpath_expr (xmlDocPtr parent_doc, const char* filename) {
3528     xmlXPathObjectPtr xpath;
3529     xmlDocPtr doc;
3530     xmlChar *expr;
3531     xmlXPathContextPtr ctx;
3532     xmlNodePtr node;
3533     xmlNsPtr ns;
3534 
3535     /*
3536      * load XPath expr as a file
3537      */
3538     xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
3539     xmlSubstituteEntitiesDefault(1);
3540 
3541     doc = xmlReadFile(filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
3542     if (doc == NULL) {
3543     fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
3544     return(NULL);
3545     }
3546 
3547     /*
3548      * Check the document is of the right kind
3549      */
3550     if(xmlDocGetRootElement(doc) == NULL) {
3551         fprintf(stderr,"Error: empty document for file \"%s\"\n", filename);
3552     xmlFreeDoc(doc);
3553     return(NULL);
3554     }
3555 
3556     node = doc->children;
3557     while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) {
3558     node = node->next;
3559     }
3560 
3561     if(node == NULL) {
3562         fprintf(stderr,"Error: XPath element expected in the file  \"%s\"\n", filename);
3563     xmlFreeDoc(doc);
3564     return(NULL);
3565     }
3566 
3567     expr = xmlNodeGetContent(node);
3568     if(expr == NULL) {
3569         fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename);
3570     xmlFreeDoc(doc);
3571     return(NULL);
3572     }
3573 
3574     ctx = xmlXPathNewContext(parent_doc);
3575     if(ctx == NULL) {
3576         fprintf(stderr,"Error: unable to create new context\n");
3577         xmlFree(expr);
3578         xmlFreeDoc(doc);
3579         return(NULL);
3580     }
3581 
3582     /*
3583      * Register namespaces
3584      */
3585     ns = node->nsDef;
3586     while(ns != NULL) {
3587     if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) {
3588         fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href);
3589     xmlFree(expr);
3590         xmlXPathFreeContext(ctx);
3591         xmlFreeDoc(doc);
3592         return(NULL);
3593     }
3594     ns = ns->next;
3595     }
3596 
3597     /*
3598      * Evaluate xpath
3599      */
3600     xpath = xmlXPathEvalExpression(expr, ctx);
3601     if(xpath == NULL) {
3602         fprintf(stderr,"Error: unable to evaluate xpath expression\n");
3603 xmlFree(expr);
3604         xmlXPathFreeContext(ctx);
3605         xmlFreeDoc(doc);
3606         return(NULL);
3607     }
3608 
3609     /* print_xpath_nodes(xpath->nodesetval); */
3610 
3611     xmlFree(expr);
3612     xmlXPathFreeContext(ctx);
3613     xmlFreeDoc(doc);
3614     return(xpath);
3615 }
3616 
3617 /*
3618  * Macro used to grow the current buffer.
3619  */
3620 #define xxx_growBufferReentrant() {                     \
3621     buffer_size *= 2;                           \
3622     buffer = (xmlChar **)                       \
3623     xmlRealloc(buffer, buffer_size * sizeof(xmlChar*)); \
3624     if (buffer == NULL) {                       \
3625     perror("realloc failed");                   \
3626     return(NULL);                           \
3627     }                                   \
3628 }
3629 
3630 static xmlChar **
3631 parse_list(xmlChar *str) {
3632     xmlChar **buffer;
3633     xmlChar **out = NULL;
3634     int buffer_size = 0;
3635     int len;
3636 
3637     if(str == NULL) {
3638     return(NULL);
3639     }
3640 
3641     len = xmlStrlen(str);
3642     if((str[0] == '\'') && (str[len - 1] == '\'')) {
3643     str[len - 1] = '\0';
3644     str++;
3645     len -= 2;
3646     }
3647     /*
3648      * allocate an translation buffer.
3649      */
3650     buffer_size = 1000;
3651     buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*));
3652     if (buffer == NULL) {
3653     perror("malloc failed");
3654     return(NULL);
3655     }
3656     out = buffer;
3657 
3658     while(*str != '\0') {
3659     if (out - buffer > buffer_size - 10) {
3660         int indx = out - buffer;
3661 
3662         xxx_growBufferReentrant();
3663         out = &buffer[indx];
3664     }
3665     (*out++) = str;
3666     while(*str != ',' && *str != '\0') ++str;
3667     if(*str == ',') *(str++) = '\0';
3668     }
3669     (*out) = NULL;
3670     return buffer;
3671 }
3672 
3673 static int
3674 c14nRunTest(const char* xml_filename, int with_comments, int exclusive,
3675         const char* xpath_filename, const char *ns_filename,
3676         const char* result_file) {
3677     xmlDocPtr doc;
3678     xmlXPathObjectPtr xpath = NULL;
3679     xmlChar *result = NULL;
3680     int ret;
3681     xmlChar **inclusive_namespaces = NULL;
3682     const char *nslist = NULL;
3683     int nssize;
3684 
3685 
3686     /*
3687      * build an XML tree from a the file; we need to add default
3688      * attributes and resolve all character and entities references
3689      */
3690     xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
3691     xmlSubstituteEntitiesDefault(1);
3692 
3693     doc = xmlReadFile(xml_filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT);
3694     if (doc == NULL) {
3695     fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename);
3696     return(-1);
3697     }
3698 
3699     /*
3700      * Check the document is of the right kind
3701      */
3702     if(xmlDocGetRootElement(doc) == NULL) {
3703         fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename);
3704     xmlFreeDoc(doc);
3705     return(-1);
3706     }
3707 
3708     /*
3709      * load xpath file if specified
3710      */
3711     if(xpath_filename) {
3712     xpath = load_xpath_expr(doc, xpath_filename);
3713     if(xpath == NULL) {
3714         fprintf(stderr,"Error: unable to evaluate xpath expression\n");
3715         xmlFreeDoc(doc);
3716         return(-1);
3717     }
3718     }
3719 
3720     if (ns_filename != NULL) {
3721         if (loadMem(ns_filename, &nslist, &nssize)) {
3722         fprintf(stderr,"Error: unable to evaluate xpath expression\n");
3723         if(xpath != NULL) xmlXPathFreeObject(xpath);
3724         xmlFreeDoc(doc);
3725         return(-1);
3726     }
3727         inclusive_namespaces = parse_list((xmlChar *) nslist);
3728     }
3729 
3730     /*
3731      * Canonical form
3732      */
3733     /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */
3734     ret = xmlC14NDocDumpMemory(doc,
3735         (xpath) ? xpath->nodesetval : NULL,
3736         exclusive, inclusive_namespaces,
3737         with_comments, &result);
3738     if (ret >= 0) {
3739     if(result != NULL) {
3740         if (compareFileMem(result_file, (const char *) result, ret)) {
3741         fprintf(stderr, "Result mismatch for %s\n", xml_filename);
3742             ret = -1;
3743         }
3744     }
3745     } else {
3746     fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret);
3747     ret = -1;
3748     }
3749 
3750     /*
3751      * Cleanup
3752      */
3753     if (result != NULL) xmlFree(result);
3754     if(xpath != NULL) xmlXPathFreeObject(xpath);
3755     if (inclusive_namespaces != NULL) xmlFree(inclusive_namespaces);
3756     if (nslist != NULL) free((char *) nslist);
3757     xmlFreeDoc(doc);
3758 
3759     return(ret);
3760 }
3761 
3762 static int
3763 c14nCommonTest(const char *filename, int with_comments, int exclusive,
3764                const char *subdir) {
3765     char buf[500];
3766     char prefix[500];
3767     const char *base;
3768     int len;
3769     char *result = NULL;
3770     char *xpath = NULL;
3771     char *ns = NULL;
3772     int ret = 0;
3773 
3774     base = baseFilename(filename);
3775     len = strlen(base);
3776     len -= 4;
3777     memcpy(prefix, base, len);
3778     prefix[len] = 0;
3779 
3780     snprintf(buf, 499, "result/c14n/%s/%s", subdir,prefix);
3781     if (!checkTestFile(buf)) {
3782         fprintf(stderr, "Missing result file %s", buf);
3783     return(-1);
3784     }
3785     result = strdup(buf);
3786     snprintf(buf, 499, "test/c14n/%s/%s.xpath", subdir,prefix);
3787     if (checkTestFile(buf)) {
3788     xpath = strdup(buf);
3789     }
3790     snprintf(buf, 499, "test/c14n/%s/%s.ns", subdir,prefix);
3791     if (checkTestFile(buf)) {
3792     ns = strdup(buf);
3793     }
3794 
3795     nb_tests++;
3796     if (c14nRunTest(filename, with_comments, exclusive,
3797                     xpath, ns, result) < 0)
3798         ret = 1;
3799 
3800     if (result != NULL) free(result);
3801     if (xpath != NULL) free(xpath);
3802     if (ns != NULL) free(ns);
3803     return(ret);
3804 }
3805 
3806 static int
3807 c14nWithCommentTest(const char *filename,
3808                     const char *resul ATTRIBUTE_UNUSED,
3809             const char *err ATTRIBUTE_UNUSED,
3810             int options ATTRIBUTE_UNUSED) {
3811     return(c14nCommonTest(filename, 1, 0, "with-comments"));
3812 }
3813 static int
3814 c14nWithoutCommentTest(const char *filename,
3815                     const char *resul ATTRIBUTE_UNUSED,
3816             const char *err ATTRIBUTE_UNUSED,
3817             int options ATTRIBUTE_UNUSED) {
3818     return(c14nCommonTest(filename, 0, 0, "without-comments"));
3819 }
3820 static int
3821 c14nExcWithoutCommentTest(const char *filename,
3822                     const char *resul ATTRIBUTE_UNUSED,
3823             const char *err ATTRIBUTE_UNUSED,
3824             int options ATTRIBUTE_UNUSED) {
3825     return(c14nCommonTest(filename, 0, 1, "exc-without-comments"));
3826 }
3827 #endif
3828 #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) && defined (LIBXML_SAX1_ENABLED)
3829 /************************************************************************
3830  *                                  *
3831  *          Catalog and threads test            *
3832  *                                  *
3833  ************************************************************************/
3834 
3835 /*
3836  * mostly a cut and paste from testThreads.c
3837  */
3838 #define MAX_ARGC    20
3839 
3840 static const char *catalog = "test/threads/complex.xml";
3841 static const char *testfiles[] = {
3842     "test/threads/abc.xml",
3843     "test/threads/acb.xml",
3844     "test/threads/bac.xml",
3845     "test/threads/bca.xml",
3846     "test/threads/cab.xml",
3847     "test/threads/cba.xml",
3848     "test/threads/invalid.xml",
3849 };
3850 
3851 static const char *Okay = "OK";
3852 static const char *Failed = "Failed";
3853 
3854 #ifndef xmlDoValidityCheckingDefaultValue
3855 #error xmlDoValidityCheckingDefaultValue is not a macro
3856 #endif
3857 #ifndef xmlGenericErrorContext
3858 #error xmlGenericErrorContext is not a macro
3859 #endif
3860 
3861 static void *
3862 thread_specific_data(void *private_data)
3863 {
3864     xmlDocPtr myDoc;
3865     const char *filename = (const char *) private_data;
3866     int okay = 1;
3867 
3868     if (!strcmp(filename, "test/threads/invalid.xml")) {
3869         xmlDoValidityCheckingDefaultValue = 0;
3870         xmlGenericErrorContext = stdout;
3871     } else {
3872         xmlDoValidityCheckingDefaultValue = 1;
3873         xmlGenericErrorContext = stderr;
3874     }
3875     myDoc = xmlParseFile(filename);
3876     if (myDoc) {
3877         xmlFreeDoc(myDoc);
3878     } else {
3879         printf("parse failed\n");
3880         okay = 0;
3881     }
3882     if (!strcmp(filename, "test/threads/invalid.xml")) {
3883         if (xmlDoValidityCheckingDefaultValue != 0) {
3884             printf("ValidityCheckingDefaultValue override failed\n");
3885             okay = 0;
3886         }
3887         if (xmlGenericErrorContext != stdout) {
3888             printf("xmlGenericErrorContext override failed\n");
3889             okay = 0;
3890         }
3891     } else {
3892         if (xmlDoValidityCheckingDefaultValue != 1) {
3893             printf("ValidityCheckingDefaultValue override failed\n");
3894             okay = 0;
3895         }
3896         if (xmlGenericErrorContext != stderr) {
3897             printf("xmlGenericErrorContext override failed\n");
3898             okay = 0;
3899         }
3900     }
3901     if (okay == 0)
3902         return ((void *) Failed);
3903     return ((void *) Okay);
3904 }
3905 
3906 #if defined(linux) || defined(solaris) || defined(__APPLE_CC__)
3907 
3908 #include <pthread.h>
3909 
3910 static pthread_t tid[MAX_ARGC];
3911 
3912 static int
3913 testThread(void)
3914 {
3915     unsigned int i, repeat;
3916     unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
3917     void *results[MAX_ARGC];
3918     int ret;
3919     int res = 0;
3920 
3921     xmlInitParser();
3922 
3923     for (repeat = 0; repeat < 500; repeat++) {
3924         xmlLoadCatalog(catalog);
3925         nb_tests++;
3926 
3927         for (i = 0; i < num_threads; i++) {
3928             results[i] = NULL;
3929             tid[i] = (pthread_t) - 1;
3930         }
3931 
3932         for (i = 0; i < num_threads; i++) {
3933             ret = pthread_create(&tid[i], 0, thread_specific_data,
3934                                  (void *) testfiles[i]);
3935             if (ret != 0) {
3936                 fprintf(stderr, "pthread_create failed\n");
3937                 return (1);
3938             }
3939         }
3940         for (i = 0; i < num_threads; i++) {
3941             ret = pthread_join(tid[i], &results[i]);
3942             if (ret != 0) {
3943                 fprintf(stderr, "pthread_join failed\n");
3944                 return (1);
3945             }
3946         }
3947 
3948         xmlCatalogCleanup();
3949         for (i = 0; i < num_threads; i++)
3950             if (results[i] != (void *) Okay) {
3951                 fprintf(stderr, "Thread %d handling %s failed\n",
3952                         i, testfiles[i]);
3953                 res = 1;
3954             }
3955     }
3956     return (res);
3957 }
3958 
3959 #elif defined WIN32
3960 #include <windows.h>
3961 #include <string.h>
3962 
3963 #define TEST_REPEAT_COUNT 500
3964 
3965 static HANDLE tid[MAX_ARGC];
3966 
3967 static DWORD WINAPI
3968 win32_thread_specific_data(void *private_data)
3969 {
3970     return((DWORD) thread_specific_data(private_data));
3971 }
3972 
3973 static int
3974 testThread(void)
3975 {
3976     unsigned int i, repeat;
3977     unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
3978     DWORD results[MAX_ARGC];
3979     BOOL ret;
3980     int res = 0;
3981 
3982     xmlInitParser();
3983     for (repeat = 0; repeat < TEST_REPEAT_COUNT; repeat++) {
3984         xmlLoadCatalog(catalog);
3985         nb_tests++;
3986 
3987         for (i = 0; i < num_threads; i++) {
3988             results[i] = 0;
3989             tid[i] = (HANDLE) - 1;
3990         }
3991 
3992         for (i = 0; i < num_threads; i++) {
3993             DWORD useless;
3994 
3995             tid[i] = CreateThread(NULL, 0,
3996                                   win32_thread_specific_data,
3997                   (void *) testfiles[i], 0,
3998                                   &useless);
3999             if (tid[i] == NULL) {
4000                 fprintf(stderr, "CreateThread failed\n");
4001                 return(1);
4002             }
4003         }
4004 
4005         if (WaitForMultipleObjects(num_threads, tid, TRUE, INFINITE) ==
4006             WAIT_FAILED) {
4007             fprintf(stderr, "WaitForMultipleObjects failed\n");
4008         return(1);
4009     }
4010 
4011         for (i = 0; i < num_threads; i++) {
4012             ret = GetExitCodeThread(tid[i], &results[i]);
4013             if (ret == 0) {
4014                 fprintf(stderr, "GetExitCodeThread failed\n");
4015                 return(1);
4016             }
4017             CloseHandle(tid[i]);
4018         }
4019 
4020         xmlCatalogCleanup();
4021         for (i = 0; i < num_threads; i++) {
4022             if (results[i] != (DWORD) Okay) {
4023                 fprintf(stderr, "Thread %d handling %s failed\n",
4024                 i, testfiles[i]);
4025             res = 1;
4026         }
4027         }
4028     }
4029 
4030     return (res);
4031 }
4032 
4033 #elif defined __BEOS__
4034 #include <OS.h>
4035 
4036 static thread_id tid[MAX_ARGC];
4037 
4038 static int
4039 testThread(void)
4040 {
4041     unsigned int i, repeat;
4042     unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
4043     void *results[MAX_ARGC];
4044     status_t ret;
4045     int res = 0;
4046 
4047     xmlInitParser();
4048     for (repeat = 0; repeat < 500; repeat++) {
4049         xmlLoadCatalog(catalog);
4050         for (i = 0; i < num_threads; i++) {
4051             results[i] = NULL;
4052             tid[i] = (thread_id) - 1;
4053         }
4054         for (i = 0; i < num_threads; i++) {
4055             tid[i] =
4056                 spawn_thread(thread_specific_data, "xmlTestThread",
4057                              B_NORMAL_PRIORITY, (void *) testfiles[i]);
4058             if (tid[i] < B_OK) {
4059                 fprintf(stderr, "beos_thread_create failed\n");
4060                 return (1);
4061             }
4062             printf("beos_thread_create %d -> %d\n", i, tid[i]);
4063         }
4064         for (i = 0; i < num_threads; i++) {
4065             ret = wait_for_thread(tid[i], &results[i]);
4066             printf("beos_thread_wait %d -> %d\n", i, ret);
4067             if (ret != B_OK) {
4068                 fprintf(stderr, "beos_thread_wait failed\n");
4069                 return (1);
4070             }
4071         }
4072 
4073         xmlCatalogCleanup();
4074         ret = B_OK;
4075         for (i = 0; i < num_threads; i++)
4076             if (results[i] != (void *) Okay) {
4077                 printf("Thread %d handling %s failed\n", i, testfiles[i]);
4078                 ret = B_ERROR;
4079             }
4080     }
4081     if (ret != B_OK)
4082         return(1);
4083     return (0);
4084 }
4085 #else
4086 static int
4087 testThread(void)
4088 {
4089     fprintf(stderr,
4090             "Specific platform thread support not detected\n");
4091     return (-1);
4092 }
4093 #endif
4094 static int
4095 threadsTest(const char *filename ATTRIBUTE_UNUSED,
4096         const char *resul ATTRIBUTE_UNUSED,
4097         const char *err ATTRIBUTE_UNUSED,
4098         int options ATTRIBUTE_UNUSED) {
4099     return(testThread());
4100 }
4101 #endif
4102 /************************************************************************
4103  *                                  *
4104  *          Tests Descriptions              *
4105  *                                  *
4106  ************************************************************************/
4107 
4108 static
4109 testDesc testDescriptions[] = {
4110     { "XML regression tests" ,
4111       oldParseTest, "./test/*", "result/", "", NULL,
4112       0 },
4113     { "XML regression tests on memory" ,
4114       memParseTest, "./test/*", "result/", "", NULL,
4115       0 },
4116     { "XML entity subst regression tests" ,
4117       noentParseTest, "./test/*", "result/noent/", "", NULL,
4118       XML_PARSE_NOENT },
4119     { "XML Namespaces regression tests",
4120       errParseTest, "./test/namespaces/*", "result/namespaces/", "", ".err",
4121       0 },
4122     { "Error cases regression tests",
4123       errParseTest, "./test/errors/*.xml", "result/errors/", "", ".err",
4124       0 },
4125 #ifdef LIBXML_READER_ENABLED
4126     { "Error cases stream regression tests",
4127       streamParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".str",
4128       0 },
4129     { "Reader regression tests",
4130       streamParseTest, "./test/*", "result/", ".rdr", NULL,
4131       0 },
4132     { "Reader entities substitution regression tests",
4133       streamParseTest, "./test/*", "result/", ".rde", NULL,
4134       XML_PARSE_NOENT },
4135     { "Reader on memory regression tests",
4136       streamMemParseTest, "./test/*", "result/", ".rdr", NULL,
4137       0 },
4138     { "Walker regression tests",
4139       walkerParseTest, "./test/*", "result/", ".rdr", NULL,
4140       0 },
4141 #endif
4142 #ifdef LIBXML_SAX1_ENABLED
4143     { "SAX1 callbacks regression tests" ,
4144       saxParseTest, "./test/*", "result/", ".sax", NULL,
4145       XML_PARSE_SAX1 },
4146     { "SAX2 callbacks regression tests" ,
4147       saxParseTest, "./test/*", "result/", ".sax2", NULL,
4148       0 },
4149 #endif
4150 #ifdef LIBXML_PUSH_ENABLED
4151     { "XML push regression tests" ,
4152       pushParseTest, "./test/*", "result/", "", NULL,
4153       0 },
4154 #endif
4155 #ifdef LIBXML_HTML_ENABLED
4156     { "HTML regression tests" ,
4157       errParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
4158       XML_PARSE_HTML },
4159 #ifdef LIBXML_PUSH_ENABLED
4160     { "Push HTML regression tests" ,
4161       pushParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
4162       XML_PARSE_HTML },
4163 #endif
4164 #ifdef LIBXML_SAX1_ENABLED
4165     { "HTML SAX regression tests" ,
4166       saxParseTest, "./test/HTML/*", "result/HTML/", ".sax", NULL,
4167       XML_PARSE_HTML },
4168 #endif
4169 #endif
4170 #ifdef LIBXML_VALID_ENABLED
4171     { "Valid documents regression tests" ,
4172       errParseTest, "./test/VCM/*", NULL, NULL, NULL,
4173       XML_PARSE_DTDVALID },
4174     { "Validity checking regression tests" ,
4175       errParseTest, "./test/VC/*", "result/VC/", NULL, "",
4176       XML_PARSE_DTDVALID },
4177     { "General documents valid regression tests" ,
4178       errParseTest, "./test/valid/*", "result/valid/", "", ".err",
4179       XML_PARSE_DTDVALID },
4180 #endif
4181 #ifdef LIBXML_XINCLUDE_ENABLED
4182     { "XInclude regression tests" ,
4183       errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL,
4184       /* Ignore errors at this point ".err", */
4185       XML_PARSE_XINCLUDE },
4186 #ifdef LIBXML_READER_ENABLED
4187     { "XInclude xmlReader regression tests",
4188       streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr",
4189       /* Ignore errors at this point ".err", */
4190       NULL, XML_PARSE_XINCLUDE },
4191 #endif
4192     { "XInclude regression tests stripping include nodes" ,
4193       errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL,
4194       /* Ignore errors at this point ".err", */
4195       XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE },
4196 #ifdef LIBXML_READER_ENABLED
4197     { "XInclude xmlReader regression tests stripping include nodes",
4198       streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr",
4199       /* Ignore errors at this point ".err", */
4200       NULL, XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE },
4201 #endif
4202 #endif
4203 #ifdef LIBXML_XPATH_ENABLED
4204 #ifdef LIBXML_DEBUG_ENABLED
4205     { "XPath expressions regression tests" ,
4206       xpathExprTest, "./test/XPath/expr/*", "result/XPath/expr/", "", NULL,
4207       0 },
4208     { "XPath document queries regression tests" ,
4209       xpathDocTest, "./test/XPath/docs/*", NULL, NULL, NULL,
4210       0 },
4211 #ifdef LIBXML_XPTR_ENABLED
4212     { "XPointer document queries regression tests" ,
4213       xptrDocTest, "./test/XPath/docs/*", NULL, NULL, NULL,
4214       0 },
4215 #endif
4216     { "xml:id regression tests" ,
4217       xmlidDocTest, "./test/xmlid/*", "result/xmlid/", "", ".err",
4218       0 },
4219 #endif
4220 #endif
4221     { "URI parsing tests" ,
4222       uriParseTest, "./test/URI/*.uri", "result/URI/", "", NULL,
4223       0 },
4224     { "URI base composition tests" ,
4225       uriBaseTest, "./test/URI/*.data", "result/URI/", "", NULL,
4226       0 },
4227     { "Path URI conversion tests" ,
4228       uriPathTest, NULL, NULL, NULL, NULL,
4229       0 },
4230 #ifdef LIBXML_SCHEMAS_ENABLED
4231     { "Schemas regression tests" ,
4232       schemasTest, "./test/schemas/*_*.xsd", NULL, NULL, NULL,
4233       0 },
4234     { "Relax-NG regression tests" ,
4235       rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
4236       XML_PARSE_DTDATTR | XML_PARSE_NOENT },
4237 #ifdef LIBXML_READER_ENABLED
4238     { "Relax-NG streaming regression tests" ,
4239       rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
4240       XML_PARSE_DTDATTR | XML_PARSE_NOENT },
4241 #endif
4242 #endif
4243 #ifdef LIBXML_PATTERN_ENABLED
4244 #ifdef LIBXML_READER_ENABLED
4245     { "Pattern regression tests" ,
4246       patternTest, "./test/pattern/*.pat", "result/pattern/", NULL, NULL,
4247       0 },
4248 #endif
4249 #endif
4250 #ifdef LIBXML_C14N_ENABLED
4251     { "C14N with comments regression tests" ,
4252       c14nWithCommentTest, "./test/c14n/with-comments/*.xml", NULL, NULL, NULL,
4253       0 },
4254     { "C14N without comments regression tests" ,
4255       c14nWithoutCommentTest, "./test/c14n/without-comments/*.xml", NULL, NULL, NULL,
4256       0 },
4257     { "C14N exclusive without comments regression tests" ,
4258       c14nExcWithoutCommentTest, "./test/c14n/exc-without-comments/*.xml", NULL, NULL, NULL,
4259       0 },
4260 #endif
4261 #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) && defined(LIBXML_SAX1_ENABLED)
4262     { "Catalog and Threads regression tests" ,
4263       threadsTest, NULL, NULL, NULL, NULL,
4264       0 },
4265 #endif
4266     {NULL, NULL, NULL, NULL, NULL, NULL, 0}
4267 };
4268 
4269 /************************************************************************
4270  *                                  *
4271  *      The main code driving the tests             *
4272  *                                  *
4273  ************************************************************************/
4274 
4275 static int
4276 launchTests(testDescPtr tst) {
4277     int res = 0, err = 0;
4278     size_t i;
4279     char *result;
4280     char *error;
4281     int mem;
4282 
4283     if (tst == NULL) return(-1);
4284     if (tst->in != NULL) {
4285     glob_t globbuf;
4286 
4287     globbuf.gl_offs = 0;
4288     glob(tst->in, GLOB_DOOFFS, NULL, &globbuf);
4289     for (i = 0;i < globbuf.gl_pathc;i++) {
4290         if (!checkTestFile(globbuf.gl_pathv[i]))
4291             continue;
4292         if (tst->suffix != NULL) {
4293         result = resultFilename(globbuf.gl_pathv[i], tst->out,
4294                     tst->suffix);
4295         if (result == NULL) {
4296             fprintf(stderr, "Out of memory !\n");
4297             fatalError();
4298         }
4299         } else {
4300             result = NULL;
4301         }
4302         if (tst->err != NULL) {
4303         error = resultFilename(globbuf.gl_pathv[i], tst->out,
4304                                 tst->err);
4305         if (error == NULL) {
4306             fprintf(stderr, "Out of memory !\n");
4307             fatalError();
4308         }
4309         } else {
4310             error = NULL;
4311         }
4312         if ((result) &&(!checkTestFile(result))) {
4313             fprintf(stderr, "Missing result file %s\n", result);
4314         } else if ((error) &&(!checkTestFile(error))) {
4315             fprintf(stderr, "Missing error file %s\n", error);
4316         } else {
4317         mem = xmlMemUsed();
4318         extraMemoryFromResolver = 0;
4319         testErrorsSize = 0;
4320         testErrors[0] = 0;
4321         res = tst->func(globbuf.gl_pathv[i], result, error,
4322                         tst->options | XML_PARSE_COMPACT);
4323         xmlResetLastError();
4324         if (res != 0) {
4325             fprintf(stderr, "File %s generated an error\n",
4326                     globbuf.gl_pathv[i]);
4327             nb_errors++;
4328             err++;
4329         }
4330         else if (xmlMemUsed() != mem) {
4331             if ((xmlMemUsed() != mem) &&
4332                 (extraMemoryFromResolver == 0)) {
4333             fprintf(stderr, "File %s leaked %d bytes\n",
4334                 globbuf.gl_pathv[i], xmlMemUsed() - mem);
4335             nb_leaks++;
4336             err++;
4337             }
4338         }
4339         testErrorsSize = 0;
4340         }
4341         if (result)
4342         free(result);
4343         if (error)
4344         free(error);
4345     }
4346     globfree(&globbuf);
4347     } else {
4348         testErrorsSize = 0;
4349     testErrors[0] = 0;
4350     extraMemoryFromResolver = 0;
4351         res = tst->func(NULL, NULL, NULL, tst->options);
4352     if (res != 0) {
4353         nb_errors++;
4354         err++;
4355     }
4356     }
4357     return(err);
4358 }
4359 
4360 static int verbose = 0;
4361 static int tests_quiet = 0;
4362 
4363 static int
4364 runtest(int i) {
4365     int ret = 0, res;
4366     int old_errors, old_tests, old_leaks;
4367 
4368     old_errors = nb_errors;
4369     old_tests = nb_tests;
4370     old_leaks = nb_leaks;
4371     if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
4372     printf("## %s\n", testDescriptions[i].desc);
4373     res = launchTests(&testDescriptions[i]);
4374     if (res != 0)
4375     ret++;
4376     if (verbose) {
4377     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
4378         printf("Ran %d tests, no errors\n", nb_tests - old_tests);
4379     else
4380         printf("Ran %d tests, %d errors, %d leaks\n",
4381            nb_tests - old_tests,
4382            nb_errors - old_errors,
4383            nb_leaks - old_leaks);
4384     }
4385     return(ret);
4386 }
4387 
4388 int
4389 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
4390     int i, a, ret = 0;
4391     int subset = 0;
4392 
4393     initializeLibxml2();
4394 
4395     for (a = 1; a < argc;a++) {
4396         if (!strcmp(argv[a], "-v"))
4397         verbose = 1;
4398         else if (!strcmp(argv[a], "-quiet"))
4399         tests_quiet = 1;
4400     else {
4401         for (i = 0; testDescriptions[i].func != NULL; i++) {
4402             if (strstr(testDescriptions[i].desc, argv[a])) {
4403             ret += runtest(i);
4404             subset++;
4405         }
4406         }
4407     }
4408     }
4409     if (subset == 0) {
4410     for (i = 0; testDescriptions[i].func != NULL; i++) {
4411         ret += runtest(i);
4412     }
4413     }
4414     if ((nb_errors == 0) && (nb_leaks == 0)) {
4415         ret = 0;
4416     printf("Total %d tests, no errors\n",
4417            nb_tests);
4418     } else {
4419         ret = 1;
4420     printf("Total %d tests, %d errors, %d leaks\n",
4421            nb_tests, nb_errors, nb_leaks);
4422     }
4423     xmlCleanupParser();
4424     xmlMemoryDump();
4425 
4426     return(ret);
4427 }
4428 
4429 #else /* ! LIBXML_OUTPUT_ENABLED */
4430 int
4431 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
4432     fprintf(stderr, "runtest requires output to be enabled in libxml2\n");
4433     return(1);
4434 }
4435 #endif