/* | |
* runtest.c: C program to run libxml2 regression tests without | |
* requiring make or Python, and reducing platform dependancies | |
* to a strict minimum. | |
* | |
* To compile on Unixes: | |
* cc -o runtest `xml2-config --cflags` runtest.c `xml2-config --libs` -lpthread | |
* | |
* See Copyright for the status of this software. | |
* | |
* daniel@veillard.com | |
*/ | |
#if !defined(_WIN32) || defined(__CYGWIN__) | |
#include <unistd.h> | |
#endif | |
#include <string.h> | |
#include <stdio.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <libxml/parser.h> | |
#include <libxml/tree.h> | |
#include <libxml/uri.h> | |
#ifdef LIBXML_OUTPUT_ENABLED | |
#ifdef LIBXML_READER_ENABLED | |
#include <libxml/xmlreader.h> | |
#endif | |
#ifdef LIBXML_XINCLUDE_ENABLED | |
#include <libxml/xinclude.h> | |
#endif | |
#ifdef LIBXML_XPATH_ENABLED | |
#include <libxml/xpath.h> | |
#include <libxml/xpathInternals.h> | |
#ifdef LIBXML_XPTR_ENABLED | |
#include <libxml/xpointer.h> | |
#endif | |
#endif | |
#ifdef LIBXML_SCHEMAS_ENABLED | |
#include <libxml/relaxng.h> | |
#include <libxml/xmlschemas.h> | |
#include <libxml/xmlschemastypes.h> | |
#endif | |
#ifdef LIBXML_PATTERN_ENABLED | |
#include <libxml/pattern.h> | |
#endif | |
#ifdef LIBXML_C14N_ENABLED | |
#include <libxml/c14n.h> | |
#endif | |
#ifdef LIBXML_HTML_ENABLED | |
#include <libxml/HTMLparser.h> | |
#include <libxml/HTMLtree.h> | |
/* | |
* pseudo flag for the unification of HTML and XML tests | |
*/ | |
#define XML_PARSE_HTML 1 << 24 | |
#endif | |
#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) | |
#include <libxml/globals.h> | |
#include <libxml/threads.h> | |
#include <libxml/parser.h> | |
#include <libxml/catalog.h> | |
#include <string.h> | |
#endif | |
typedef int (*functest) (const char *filename, const char *result, | |
const char *error, int options); | |
typedef struct testDesc testDesc; | |
typedef testDesc *testDescPtr; | |
struct testDesc { | |
const char *desc; /* descripton of the test */ | |
functest func; /* function implementing the test */ | |
const char *in; /* glob to path for input files */ | |
const char *out; /* output directory */ | |
const char *suffix;/* suffix for output files */ | |
const char *err; /* suffix for error output files */ | |
int options; /* parser options for the test */ | |
}; | |
static int checkTestFile(const char *filename); | |
#if defined(_WIN32) && !defined(__CYGWIN__) | |
#include <windows.h> | |
#include <io.h> | |
typedef struct | |
{ | |
size_t gl_pathc; /* Count of paths matched so far */ | |
char **gl_pathv; /* List of matched pathnames. */ | |
size_t gl_offs; /* Slots to reserve in 'gl_pathv'. */ | |
} glob_t; | |
#define GLOB_DOOFFS 0 | |
static int glob(const char *pattern, int flags, | |
int errfunc(const char *epath, int eerrno), | |
glob_t *pglob) { | |
glob_t *ret; | |
WIN32_FIND_DATA FindFileData; | |
HANDLE hFind; | |
unsigned int nb_paths = 0; | |
char directory[500]; | |
int len; | |
if ((pattern == NULL) || (pglob == NULL)) return(-1); | |
strncpy(directory, pattern, 499); | |
for (len = strlen(directory);len >= 0;len--) { | |
if (directory[len] == '/') { | |
len++; | |
directory[len] = 0; | |
break; | |
} | |
} | |
if (len <= 0) | |
len = 0; | |
ret = pglob; | |
memset(ret, 0, sizeof(glob_t)); | |
hFind = FindFirstFileA(pattern, &FindFileData); | |
if (hFind == INVALID_HANDLE_VALUE) | |
return(0); | |
nb_paths = 20; | |
ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *)); | |
if (ret->gl_pathv == NULL) { | |
FindClose(hFind); | |
return(-1); | |
} | |
strncpy(directory + len, FindFileData.cFileName, 499 - len); | |
ret->gl_pathv[ret->gl_pathc] = strdup(directory); | |
if (ret->gl_pathv[ret->gl_pathc] == NULL) | |
goto done; | |
ret->gl_pathc++; | |
while(FindNextFileA(hFind, &FindFileData)) { | |
if (FindFileData.cFileName[0] == '.') | |
continue; | |
if (ret->gl_pathc + 2 > nb_paths) { | |
char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *)); | |
if (tmp == NULL) | |
break; | |
ret->gl_pathv = tmp; | |
nb_paths *= 2; | |
} | |
strncpy(directory + len, FindFileData.cFileName, 499 - len); | |
ret->gl_pathv[ret->gl_pathc] = strdup(directory); | |
if (ret->gl_pathv[ret->gl_pathc] == NULL) | |
break; | |
ret->gl_pathc++; | |
} | |
ret->gl_pathv[ret->gl_pathc] = NULL; | |
done: | |
FindClose(hFind); | |
return(0); | |
} | |
static void globfree(glob_t *pglob) { | |
unsigned int i; | |
if (pglob == NULL) | |
return; | |
for (i = 0;i < pglob->gl_pathc;i++) { | |
if (pglob->gl_pathv[i] != NULL) | |
free(pglob->gl_pathv[i]); | |
} | |
} | |
#define vsnprintf _vsnprintf | |
#define snprintf _snprintf | |
#else | |
#include <glob.h> | |
#endif | |
/************************************************************************ | |
* * | |
* Libxml2 specific routines * | |
* * | |
************************************************************************/ | |
static int nb_tests = 0; | |
static int nb_errors = 0; | |
static int nb_leaks = 0; | |
static long libxmlMemoryAllocatedBase = 0; | |
static int extraMemoryFromResolver = 0; | |
static int | |
fatalError(void) { | |
fprintf(stderr, "Exitting tests on fatal error\n"); | |
exit(1); | |
} | |
/* | |
* We need to trap calls to the resolver to not account memory for the catalog | |
* which is shared to the current running test. We also don't want to have | |
* network downloads modifying tests. | |
*/ | |
static xmlParserInputPtr | |
testExternalEntityLoader(const char *URL, const char *ID, | |
xmlParserCtxtPtr ctxt) { | |
xmlParserInputPtr ret; | |
if (checkTestFile(URL)) { | |
ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); | |
} else { | |
int memused = xmlMemUsed(); | |
ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); | |
extraMemoryFromResolver += xmlMemUsed() - memused; | |
} | |
return(ret); | |
} | |
/* | |
* Trapping the error messages at the generic level to grab the equivalent of | |
* stderr messages on CLI tools. | |
*/ | |
static char testErrors[32769]; | |
static int testErrorsSize = 0; | |
static void | |
testErrorHandler(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { | |
va_list args; | |
int res; | |
if (testErrorsSize >= 32768) | |
return; | |
va_start(args, msg); | |
res = vsnprintf(&testErrors[testErrorsSize], | |
32768 - testErrorsSize, | |
msg, args); | |
va_end(args); | |
if (testErrorsSize + res >= 32768) { | |
/* buffer is full */ | |
testErrorsSize = 32768; | |
testErrors[testErrorsSize] = 0; | |
} else { | |
testErrorsSize += res; | |
} | |
testErrors[testErrorsSize] = 0; | |
} | |
static void | |
channel(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { | |
va_list args; | |
int res; | |
if (testErrorsSize >= 32768) | |
return; | |
va_start(args, msg); | |
res = vsnprintf(&testErrors[testErrorsSize], | |
32768 - testErrorsSize, | |
msg, args); | |
va_end(args); | |
if (testErrorsSize + res >= 32768) { | |
/* buffer is full */ | |
testErrorsSize = 32768; | |
testErrors[testErrorsSize] = 0; | |
} else { | |
testErrorsSize += res; | |
} | |
testErrors[testErrorsSize] = 0; | |
} | |
/** | |
* xmlParserPrintFileContext: | |
* @input: an xmlParserInputPtr input | |
* | |
* Displays current context within the input content for error tracking | |
*/ | |
static void | |
xmlParserPrintFileContextInternal(xmlParserInputPtr input , | |
xmlGenericErrorFunc chanl, void *data ) { | |
const xmlChar *cur, *base; | |
unsigned int n, col; /* GCC warns if signed, because compared with sizeof() */ | |
xmlChar content[81]; /* space for 80 chars + line terminator */ | |
xmlChar *ctnt; | |
if (input == NULL) return; | |
cur = input->cur; | |
base = input->base; | |
/* skip backwards over any end-of-lines */ | |
while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) { | |
cur--; | |
} | |
n = 0; | |
/* search backwards for beginning-of-line (to max buff size) */ | |
while ((n++ < (sizeof(content)-1)) && (cur > base) && | |
(*(cur) != '\n') && (*(cur) != '\r')) | |
cur--; | |
if ((*(cur) == '\n') || (*(cur) == '\r')) cur++; | |
/* calculate the error position in terms of the current position */ | |
col = input->cur - cur; | |
/* search forward for end-of-line (to max buff size) */ | |
n = 0; | |
ctnt = content; | |
/* copy selected text to our buffer */ | |
while ((*cur != 0) && (*(cur) != '\n') && | |
(*(cur) != '\r') && (n < sizeof(content)-1)) { | |
*ctnt++ = *cur++; | |
n++; | |
} | |
*ctnt = 0; | |
/* print out the selected text */ | |
chanl(data ,"%s\n", content); | |
/* create blank line with problem pointer */ | |
n = 0; | |
ctnt = content; | |
/* (leave buffer space for pointer + line terminator) */ | |
while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) { | |
if (*(ctnt) != '\t') | |
*(ctnt) = ' '; | |
ctnt++; | |
} | |
*ctnt++ = '^'; | |
*ctnt = 0; | |
chanl(data ,"%s\n", content); | |
} | |
static void | |
testStructuredErrorHandler(void *ctx ATTRIBUTE_UNUSED, xmlErrorPtr err) { | |
char *file = NULL; | |
int line = 0; | |
int code = -1; | |
int domain; | |
void *data = NULL; | |
const char *str; | |
const xmlChar *name = NULL; | |
xmlNodePtr node; | |
xmlErrorLevel level; | |
xmlParserInputPtr input = NULL; | |
xmlParserInputPtr cur = NULL; | |
xmlParserCtxtPtr ctxt = NULL; | |
if (err == NULL) | |
return; | |
file = err->file; | |
line = err->line; | |
code = err->code; | |
domain = err->domain; | |
level = err->level; | |
node = err->node; | |
if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) || | |
(domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) || | |
(domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) { | |
ctxt = err->ctxt; | |
} | |
str = err->message; | |
if (code == XML_ERR_OK) | |
return; | |
if ((node != NULL) && (node->type == XML_ELEMENT_NODE)) | |
name = node->name; | |
/* | |
* Maintain the compatibility with the legacy error handling | |
*/ | |
if (ctxt != NULL) { | |
input = ctxt->input; | |
if ((input != NULL) && (input->filename == NULL) && | |
(ctxt->inputNr > 1)) { | |
cur = input; | |
input = ctxt->inputTab[ctxt->inputNr - 2]; | |
} | |
if (input != NULL) { | |
if (input->filename) | |
channel(data, "%s:%d: ", input->filename, input->line); | |
else if ((line != 0) && (domain == XML_FROM_PARSER)) | |
channel(data, "Entity: line %d: ", input->line); | |
} | |
} else { | |
if (file != NULL) | |
channel(data, "%s:%d: ", file, line); | |
else if ((line != 0) && (domain == XML_FROM_PARSER)) | |
channel(data, "Entity: line %d: ", line); | |
} | |
if (name != NULL) { | |
channel(data, "element %s: ", name); | |
} | |
if (code == XML_ERR_OK) | |
return; | |
switch (domain) { | |
case XML_FROM_PARSER: | |
channel(data, "parser "); | |
break; | |
case XML_FROM_NAMESPACE: | |
channel(data, "namespace "); | |
break; | |
case XML_FROM_DTD: | |
case XML_FROM_VALID: | |
channel(data, "validity "); | |
break; | |
case XML_FROM_HTML: | |
channel(data, "HTML parser "); | |
break; | |
case XML_FROM_MEMORY: | |
channel(data, "memory "); | |
break; | |
case XML_FROM_OUTPUT: | |
channel(data, "output "); | |
break; | |
case XML_FROM_IO: | |
channel(data, "I/O "); | |
break; | |
case XML_FROM_XINCLUDE: | |
channel(data, "XInclude "); | |
break; | |
case XML_FROM_XPATH: | |
channel(data, "XPath "); | |
break; | |
case XML_FROM_XPOINTER: | |
channel(data, "parser "); | |
break; | |
case XML_FROM_REGEXP: | |
channel(data, "regexp "); | |
break; | |
case XML_FROM_MODULE: | |
channel(data, "module "); | |
break; | |
case XML_FROM_SCHEMASV: | |
channel(data, "Schemas validity "); | |
break; | |
case XML_FROM_SCHEMASP: | |
channel(data, "Schemas parser "); | |
break; | |
case XML_FROM_RELAXNGP: | |
channel(data, "Relax-NG parser "); | |
break; | |
case XML_FROM_RELAXNGV: | |
channel(data, "Relax-NG validity "); | |
break; | |
case XML_FROM_CATALOG: | |
channel(data, "Catalog "); | |
break; | |
case XML_FROM_C14N: | |
channel(data, "C14N "); | |
break; | |
case XML_FROM_XSLT: | |
channel(data, "XSLT "); | |
break; | |
default: | |
break; | |
} | |
if (code == XML_ERR_OK) | |
return; | |
switch (level) { | |
case XML_ERR_NONE: | |
channel(data, ": "); | |
break; | |
case XML_ERR_WARNING: | |
channel(data, "warning : "); | |
break; | |
case XML_ERR_ERROR: | |
channel(data, "error : "); | |
break; | |
case XML_ERR_FATAL: | |
channel(data, "error : "); | |
break; | |
} | |
if (code == XML_ERR_OK) | |
return; | |
if (str != NULL) { | |
int len; | |
len = xmlStrlen((const xmlChar *)str); | |
if ((len > 0) && (str[len - 1] != '\n')) | |
channel(data, "%s\n", str); | |
else | |
channel(data, "%s", str); | |
} else { | |
channel(data, "%s\n", "out of memory error"); | |
} | |
if (code == XML_ERR_OK) | |
return; | |
if (ctxt != NULL) { | |
xmlParserPrintFileContextInternal(input, channel, data); | |
if (cur != NULL) { | |
if (cur->filename) | |
channel(data, "%s:%d: \n", cur->filename, cur->line); | |
else if ((line != 0) && (domain == XML_FROM_PARSER)) | |
channel(data, "Entity: line %d: \n", cur->line); | |
xmlParserPrintFileContextInternal(cur, channel, data); | |
} | |
} | |
if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) && | |
(err->int1 < 100) && | |
(err->int1 < xmlStrlen((const xmlChar *)err->str1))) { | |
xmlChar buf[150]; | |
int i; | |
channel(data, "%s\n", err->str1); | |
for (i=0;i < err->int1;i++) | |
buf[i] = ' '; | |
buf[i++] = '^'; | |
buf[i] = 0; | |
channel(data, "%s\n", buf); | |
} | |
} | |
static void | |
initializeLibxml2(void) { | |
xmlGetWarningsDefaultValue = 0; | |
xmlPedanticParserDefault(0); | |
xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup); | |
xmlInitParser(); | |
xmlSetExternalEntityLoader(testExternalEntityLoader); | |
xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler); | |
#ifdef LIBXML_SCHEMAS_ENABLED | |
xmlSchemaInitTypes(); | |
xmlRelaxNGInitTypes(); | |
#endif | |
libxmlMemoryAllocatedBase = xmlMemUsed(); | |
} | |
/************************************************************************ | |
* * | |
* File name and path utilities * | |
* * | |
************************************************************************/ | |
static const char *baseFilename(const char *filename) { | |
const char *cur; | |
if (filename == NULL) | |
return(NULL); | |
cur = &filename[strlen(filename)]; | |
while ((cur > filename) && (*cur != '/')) | |
cur--; | |
if (*cur == '/') | |
return(cur + 1); | |
return(cur); | |
} | |
static char *resultFilename(const char *filename, const char *out, | |
const char *suffix) { | |
const char *base; | |
char res[500]; | |
/************* | |
if ((filename[0] == 't') && (filename[1] == 'e') && | |
(filename[2] == 's') && (filename[3] == 't') && | |
(filename[4] == '/')) | |
filename = &filename[5]; | |
*************/ | |
base = baseFilename(filename); | |
if (suffix == NULL) | |
suffix = ".tmp"; | |
if (out == NULL) | |
out = ""; | |
snprintf(res, 499, "%s%s%s", out, base, suffix); | |
res[499] = 0; | |
return(strdup(res)); | |
} | |
static int checkTestFile(const char *filename) { | |
struct stat buf; | |
if (stat(filename, &buf) == -1) | |
return(0); | |
#if defined(_WIN32) && !defined(__CYGWIN__) | |
if (!(buf.st_mode & _S_IFREG)) | |
return(0); | |
#else | |
if (!S_ISREG(buf.st_mode)) | |
return(0); | |
#endif | |
return(1); | |
} | |
static int compareFiles(const char *r1, const char *r2) { | |
int res1, res2; | |
int fd1, fd2; | |
char bytes1[4096]; | |
char bytes2[4096]; | |
fd1 = open(r1, O_RDONLY | O_BINARY); | |
if (fd1 < 0) | |
return(-1); | |
fd2 = open(r2, O_RDONLY | O_BINARY); | |
if (fd2 < 0) { | |
close(fd1); | |
return(-1); | |
} | |
while (1) { | |
res1 = read(fd1, bytes1, 4096); | |
res2 = read(fd2, bytes2, 4096); | |
if (res1 != res2) { | |
close(fd1); | |
close(fd2); | |
return(1); | |
} | |
if (res1 == 0) | |
break; | |
if (memcmp(bytes1, bytes2, res1) != 0) { | |
close(fd1); | |
close(fd2); | |
return(1); | |
} | |
} | |
close(fd1); | |
close(fd2); | |
return(0); | |
} | |
static int compareFileMem(const char *filename, const char *mem, int size) { | |
int res; | |
int fd; | |
char bytes[4096]; | |
int idx = 0; | |
struct stat info; | |
if (stat(filename, &info) < 0) | |
return(-1); | |
if (info.st_size != size) | |
return(-1); | |
fd = open(filename, O_RDONLY | O_BINARY); | |
if (fd < 0) | |
return(-1); | |
while (idx < size) { | |
res = read(fd, bytes, 4096); | |
if (res <= 0) | |
break; | |
if (res + idx > size) | |
break; | |
if (memcmp(bytes, &mem[idx], res) != 0) { | |
int ix; | |
for (ix=0; ix<res; ix++) | |
if (bytes[ix] != mem[idx+ix]) | |
break; | |
fprintf(stderr,"Compare error at position %d\n", idx+ix); | |
close(fd); | |
return(1); | |
} | |
idx += res; | |
} | |
close(fd); | |
return(idx != size); | |
} | |
static int loadMem(const char *filename, const char **mem, int *size) { | |
int fd, res; | |
struct stat info; | |
char *base; | |
int siz = 0; | |
if (stat(filename, &info) < 0) | |
return(-1); | |
base = malloc(info.st_size + 1); | |
if (base == NULL) | |
return(-1); | |
if ((fd = open(filename, O_RDONLY | O_BINARY)) < 0) { | |
free(base); | |
return(-1); | |
} | |
while ((res = read(fd, &base[siz], info.st_size - siz)) > 0) { | |
siz += res; | |
} | |
close(fd); | |
#if !defined(_WIN32) | |
if (siz != info.st_size) { | |
free(base); | |
return(-1); | |
} | |
#endif | |
base[siz] = 0; | |
*mem = base; | |
*size = siz; | |
return(0); | |
} | |
static int unloadMem(const char *mem) { | |
free((char *)mem); | |
return(0); | |
} | |
/************************************************************************ | |
* * | |
* Tests implementations * | |
* * | |
************************************************************************/ | |
/************************************************************************ | |
* * | |
* Parse to SAX based tests * | |
* * | |
************************************************************************/ | |
FILE *SAXdebug = NULL; | |
/* | |
* empty SAX block | |
*/ | |
xmlSAXHandler emptySAXHandlerStruct = { | |
NULL, /* internalSubset */ | |
NULL, /* isStandalone */ | |
NULL, /* hasInternalSubset */ | |
NULL, /* hasExternalSubset */ | |
NULL, /* resolveEntity */ | |
NULL, /* getEntity */ | |
NULL, /* entityDecl */ | |
NULL, /* notationDecl */ | |
NULL, /* attributeDecl */ | |
NULL, /* elementDecl */ | |
NULL, /* unparsedEntityDecl */ | |
NULL, /* setDocumentLocator */ | |
NULL, /* startDocument */ | |
NULL, /* endDocument */ | |
NULL, /* startElement */ | |
NULL, /* endElement */ | |
NULL, /* reference */ | |
NULL, /* characters */ | |
NULL, /* ignorableWhitespace */ | |
NULL, /* processingInstruction */ | |
NULL, /* comment */ | |
NULL, /* xmlParserWarning */ | |
NULL, /* xmlParserError */ | |
NULL, /* xmlParserError */ | |
NULL, /* getParameterEntity */ | |
NULL, /* cdataBlock; */ | |
NULL, /* externalSubset; */ | |
1, | |
NULL, | |
NULL, /* startElementNs */ | |
NULL, /* endElementNs */ | |
NULL /* xmlStructuredErrorFunc */ | |
}; | |
static xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct; | |
int callbacks = 0; | |
int quiet = 0; | |
/** | |
* isStandaloneDebug: | |
* @ctxt: An XML parser context | |
* | |
* Is this document tagged standalone ? | |
* | |
* Returns 1 if true | |
*/ | |
static int | |
isStandaloneDebug(void *ctx ATTRIBUTE_UNUSED) | |
{ | |
callbacks++; | |
if (quiet) | |
return(0); | |
fprintf(SAXdebug, "SAX.isStandalone()\n"); | |
return(0); | |
} | |
/** | |
* hasInternalSubsetDebug: | |
* @ctxt: An XML parser context | |
* | |
* Does this document has an internal subset | |
* | |
* Returns 1 if true | |
*/ | |
static int | |
hasInternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED) | |
{ | |
callbacks++; | |
if (quiet) | |
return(0); | |
fprintf(SAXdebug, "SAX.hasInternalSubset()\n"); | |
return(0); | |
} | |
/** | |
* hasExternalSubsetDebug: | |
* @ctxt: An XML parser context | |
* | |
* Does this document has an external subset | |
* | |
* Returns 1 if true | |
*/ | |
static int | |
hasExternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED) | |
{ | |
callbacks++; | |
if (quiet) | |
return(0); | |
fprintf(SAXdebug, "SAX.hasExternalSubset()\n"); | |
return(0); | |
} | |
/** | |
* internalSubsetDebug: | |
* @ctxt: An XML parser context | |
* | |
* Does this document has an internal subset | |
*/ | |
static void | |
internalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, | |
const xmlChar *ExternalID, const xmlChar *SystemID) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.internalSubset(%s,", name); | |
if (ExternalID == NULL) | |
fprintf(SAXdebug, " ,"); | |
else | |
fprintf(SAXdebug, " %s,", ExternalID); | |
if (SystemID == NULL) | |
fprintf(SAXdebug, " )\n"); | |
else | |
fprintf(SAXdebug, " %s)\n", SystemID); | |
} | |
/** | |
* externalSubsetDebug: | |
* @ctxt: An XML parser context | |
* | |
* Does this document has an external subset | |
*/ | |
static void | |
externalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, | |
const xmlChar *ExternalID, const xmlChar *SystemID) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.externalSubset(%s,", name); | |
if (ExternalID == NULL) | |
fprintf(SAXdebug, " ,"); | |
else | |
fprintf(SAXdebug, " %s,", ExternalID); | |
if (SystemID == NULL) | |
fprintf(SAXdebug, " )\n"); | |
else | |
fprintf(SAXdebug, " %s)\n", SystemID); | |
} | |
/** | |
* resolveEntityDebug: | |
* @ctxt: An XML parser context | |
* @publicId: The public ID of the entity | |
* @systemId: The system ID of the entity | |
* | |
* Special entity resolver, better left to the parser, it has | |
* more context than the application layer. | |
* The default behaviour is to NOT resolve the entities, in that case | |
* the ENTITY_REF nodes are built in the structure (and the parameter | |
* values). | |
* | |
* Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. | |
*/ | |
static xmlParserInputPtr | |
resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId) | |
{ | |
callbacks++; | |
if (quiet) | |
return(NULL); | |
/* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */ | |
fprintf(SAXdebug, "SAX.resolveEntity("); | |
if (publicId != NULL) | |
fprintf(SAXdebug, "%s", (char *)publicId); | |
else | |
fprintf(SAXdebug, " "); | |
if (systemId != NULL) | |
fprintf(SAXdebug, ", %s)\n", (char *)systemId); | |
else | |
fprintf(SAXdebug, ", )\n"); | |
/********* | |
if (systemId != NULL) { | |
return(xmlNewInputFromFile(ctxt, (char *) systemId)); | |
} | |
*********/ | |
return(NULL); | |
} | |
/** | |
* getEntityDebug: | |
* @ctxt: An XML parser context | |
* @name: The entity name | |
* | |
* Get an entity by name | |
* | |
* Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. | |
*/ | |
static xmlEntityPtr | |
getEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) | |
{ | |
callbacks++; | |
if (quiet) | |
return(NULL); | |
fprintf(SAXdebug, "SAX.getEntity(%s)\n", name); | |
return(NULL); | |
} | |
/** | |
* getParameterEntityDebug: | |
* @ctxt: An XML parser context | |
* @name: The entity name | |
* | |
* Get a parameter entity by name | |
* | |
* Returns the xmlParserInputPtr | |
*/ | |
static xmlEntityPtr | |
getParameterEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) | |
{ | |
callbacks++; | |
if (quiet) | |
return(NULL); | |
fprintf(SAXdebug, "SAX.getParameterEntity(%s)\n", name); | |
return(NULL); | |
} | |
/** | |
* entityDeclDebug: | |
* @ctxt: An XML parser context | |
* @name: the entity name | |
* @type: the entity type | |
* @publicId: The public ID of the entity | |
* @systemId: The system ID of the entity | |
* @content: the entity value (without processing). | |
* | |
* An entity definition has been parsed | |
*/ | |
static void | |
entityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type, | |
const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) | |
{ | |
const xmlChar *nullstr = BAD_CAST "(null)"; | |
/* not all libraries handle printing null pointers nicely */ | |
if (publicId == NULL) | |
publicId = nullstr; | |
if (systemId == NULL) | |
systemId = nullstr; | |
if (content == NULL) | |
content = (xmlChar *)nullstr; | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.entityDecl(%s, %d, %s, %s, %s)\n", | |
name, type, publicId, systemId, content); | |
} | |
/** | |
* attributeDeclDebug: | |
* @ctxt: An XML parser context | |
* @name: the attribute name | |
* @type: the attribute type | |
* | |
* An attribute definition has been parsed | |
*/ | |
static void | |
attributeDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar * elem, | |
const xmlChar * name, int type, int def, | |
const xmlChar * defaultValue, xmlEnumerationPtr tree) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
if (defaultValue == NULL) | |
fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, NULL, ...)\n", | |
elem, name, type, def); | |
else | |
fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, %s, ...)\n", | |
elem, name, type, def, defaultValue); | |
xmlFreeEnumeration(tree); | |
} | |
/** | |
* elementDeclDebug: | |
* @ctxt: An XML parser context | |
* @name: the element name | |
* @type: the element type | |
* @content: the element value (without processing). | |
* | |
* An element definition has been parsed | |
*/ | |
static void | |
elementDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type, | |
xmlElementContentPtr content ATTRIBUTE_UNUSED) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.elementDecl(%s, %d, ...)\n", | |
name, type); | |
} | |
/** | |
* notationDeclDebug: | |
* @ctxt: An XML parser context | |
* @name: The name of the notation | |
* @publicId: The public ID of the entity | |
* @systemId: The system ID of the entity | |
* | |
* What to do when a notation declaration has been parsed. | |
*/ | |
static void | |
notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, | |
const xmlChar *publicId, const xmlChar *systemId) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.notationDecl(%s, %s, %s)\n", | |
(char *) name, (char *) publicId, (char *) systemId); | |
} | |
/** | |
* unparsedEntityDeclDebug: | |
* @ctxt: An XML parser context | |
* @name: The name of the entity | |
* @publicId: The public ID of the entity | |
* @systemId: The system ID of the entity | |
* @notationName: the name of the notation | |
* | |
* What to do when an unparsed entity declaration is parsed | |
*/ | |
static void | |
unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, | |
const xmlChar *publicId, const xmlChar *systemId, | |
const xmlChar *notationName) | |
{ | |
const xmlChar *nullstr = BAD_CAST "(null)"; | |
if (publicId == NULL) | |
publicId = nullstr; | |
if (systemId == NULL) | |
systemId = nullstr; | |
if (notationName == NULL) | |
notationName = nullstr; | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n", | |
(char *) name, (char *) publicId, (char *) systemId, | |
(char *) notationName); | |
} | |
/** | |
* setDocumentLocatorDebug: | |
* @ctxt: An XML parser context | |
* @loc: A SAX Locator | |
* | |
* Receive the document locator at startup, actually xmlDefaultSAXLocator | |
* Everything is available on the context, so this is useless in our case. | |
*/ | |
static void | |
setDocumentLocatorDebug(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.setDocumentLocator()\n"); | |
} | |
/** | |
* startDocumentDebug: | |
* @ctxt: An XML parser context | |
* | |
* called when the document start being processed. | |
*/ | |
static void | |
startDocumentDebug(void *ctx ATTRIBUTE_UNUSED) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.startDocument()\n"); | |
} | |
/** | |
* endDocumentDebug: | |
* @ctxt: An XML parser context | |
* | |
* called when the document end has been detected. | |
*/ | |
static void | |
endDocumentDebug(void *ctx ATTRIBUTE_UNUSED) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.endDocument()\n"); | |
} | |
/** | |
* startElementDebug: | |
* @ctxt: An XML parser context | |
* @name: The element name | |
* | |
* called when an opening tag has been processed. | |
*/ | |
static void | |
startElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts) | |
{ | |
int i; | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.startElement(%s", (char *) name); | |
if (atts != NULL) { | |
for (i = 0;(atts[i] != NULL);i++) { | |
fprintf(SAXdebug, ", %s='", atts[i++]); | |
if (atts[i] != NULL) | |
fprintf(SAXdebug, "%s'", atts[i]); | |
} | |
} | |
fprintf(SAXdebug, ")\n"); | |
} | |
/** | |
* endElementDebug: | |
* @ctxt: An XML parser context | |
* @name: The element name | |
* | |
* called when the end of an element has been detected. | |
*/ | |
static void | |
endElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.endElement(%s)\n", (char *) name); | |
} | |
/** | |
* charactersDebug: | |
* @ctxt: An XML parser context | |
* @ch: a xmlChar string | |
* @len: the number of xmlChar | |
* | |
* receiving some chars from the parser. | |
* Question: how much at a time ??? | |
*/ | |
static void | |
charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len) | |
{ | |
char output[40]; | |
int i; | |
callbacks++; | |
if (quiet) | |
return; | |
for (i = 0;(i<len) && (i < 30);i++) | |
output[i] = ch[i]; | |
output[i] = 0; | |
fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len); | |
} | |
/** | |
* referenceDebug: | |
* @ctxt: An XML parser context | |
* @name: The entity name | |
* | |
* called when an entity reference is detected. | |
*/ | |
static void | |
referenceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.reference(%s)\n", name); | |
} | |
/** | |
* ignorableWhitespaceDebug: | |
* @ctxt: An XML parser context | |
* @ch: a xmlChar string | |
* @start: the first char in the string | |
* @len: the number of xmlChar | |
* | |
* receiving some ignorable whitespaces from the parser. | |
* Question: how much at a time ??? | |
*/ | |
static void | |
ignorableWhitespaceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len) | |
{ | |
char output[40]; | |
int i; | |
callbacks++; | |
if (quiet) | |
return; | |
for (i = 0;(i<len) && (i < 30);i++) | |
output[i] = ch[i]; | |
output[i] = 0; | |
fprintf(SAXdebug, "SAX.ignorableWhitespace(%s, %d)\n", output, len); | |
} | |
/** | |
* processingInstructionDebug: | |
* @ctxt: An XML parser context | |
* @target: the target name | |
* @data: the PI data's | |
* @len: the number of xmlChar | |
* | |
* A processing instruction has been parsed. | |
*/ | |
static void | |
processingInstructionDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *target, | |
const xmlChar *data) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
if (data != NULL) | |
fprintf(SAXdebug, "SAX.processingInstruction(%s, %s)\n", | |
(char *) target, (char *) data); | |
else | |
fprintf(SAXdebug, "SAX.processingInstruction(%s, NULL)\n", | |
(char *) target); | |
} | |
/** | |
* cdataBlockDebug: | |
* @ctx: the user data (XML parser context) | |
* @value: The pcdata content | |
* @len: the block length | |
* | |
* called when a pcdata block has been parsed | |
*/ | |
static void | |
cdataBlockDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value, int len) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.pcdata(%.20s, %d)\n", | |
(char *) value, len); | |
} | |
/** | |
* commentDebug: | |
* @ctxt: An XML parser context | |
* @value: the comment content | |
* | |
* A comment has been parsed. | |
*/ | |
static void | |
commentDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.comment(%s)\n", value); | |
} | |
/** | |
* warningDebug: | |
* @ctxt: An XML parser context | |
* @msg: the message to display/transmit | |
* @...: extra parameters for the message display | |
* | |
* Display and format a warning messages, gives file, line, position and | |
* extra parameters. | |
*/ | |
static void | |
warningDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) | |
{ | |
va_list args; | |
callbacks++; | |
if (quiet) | |
return; | |
va_start(args, msg); | |
fprintf(SAXdebug, "SAX.warning: "); | |
vfprintf(SAXdebug, msg, args); | |
va_end(args); | |
} | |
/** | |
* errorDebug: | |
* @ctxt: An XML parser context | |
* @msg: the message to display/transmit | |
* @...: extra parameters for the message display | |
* | |
* Display and format a error messages, gives file, line, position and | |
* extra parameters. | |
*/ | |
static void | |
errorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) | |
{ | |
va_list args; | |
callbacks++; | |
if (quiet) | |
return; | |
va_start(args, msg); | |
fprintf(SAXdebug, "SAX.error: "); | |
vfprintf(SAXdebug, msg, args); | |
va_end(args); | |
} | |
/** | |
* fatalErrorDebug: | |
* @ctxt: An XML parser context | |
* @msg: the message to display/transmit | |
* @...: extra parameters for the message display | |
* | |
* Display and format a fatalError messages, gives file, line, position and | |
* extra parameters. | |
*/ | |
static void | |
fatalErrorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) | |
{ | |
va_list args; | |
callbacks++; | |
if (quiet) | |
return; | |
va_start(args, msg); | |
fprintf(SAXdebug, "SAX.fatalError: "); | |
vfprintf(SAXdebug, msg, args); | |
va_end(args); | |
} | |
xmlSAXHandler debugSAXHandlerStruct = { | |
internalSubsetDebug, | |
isStandaloneDebug, | |
hasInternalSubsetDebug, | |
hasExternalSubsetDebug, | |
resolveEntityDebug, | |
getEntityDebug, | |
entityDeclDebug, | |
notationDeclDebug, | |
attributeDeclDebug, | |
elementDeclDebug, | |
unparsedEntityDeclDebug, | |
setDocumentLocatorDebug, | |
startDocumentDebug, | |
endDocumentDebug, | |
startElementDebug, | |
endElementDebug, | |
referenceDebug, | |
charactersDebug, | |
ignorableWhitespaceDebug, | |
processingInstructionDebug, | |
commentDebug, | |
warningDebug, | |
errorDebug, | |
fatalErrorDebug, | |
getParameterEntityDebug, | |
cdataBlockDebug, | |
externalSubsetDebug, | |
1, | |
NULL, | |
NULL, | |
NULL, | |
NULL | |
}; | |
xmlSAXHandlerPtr debugSAXHandler = &debugSAXHandlerStruct; | |
/* | |
* SAX2 specific callbacks | |
*/ | |
/** | |
* startElementNsDebug: | |
* @ctxt: An XML parser context | |
* @name: The element name | |
* | |
* called when an opening tag has been processed. | |
*/ | |
static void | |
startElementNsDebug(void *ctx ATTRIBUTE_UNUSED, | |
const xmlChar *localname, | |
const xmlChar *prefix, | |
const xmlChar *URI, | |
int nb_namespaces, | |
const xmlChar **namespaces, | |
int nb_attributes, | |
int nb_defaulted, | |
const xmlChar **attributes) | |
{ | |
int i; | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.startElementNs(%s", (char *) localname); | |
if (prefix == NULL) | |
fprintf(SAXdebug, ", NULL"); | |
else | |
fprintf(SAXdebug, ", %s", (char *) prefix); | |
if (URI == NULL) | |
fprintf(SAXdebug, ", NULL"); | |
else | |
fprintf(SAXdebug, ", '%s'", (char *) URI); | |
fprintf(SAXdebug, ", %d", nb_namespaces); | |
if (namespaces != NULL) { | |
for (i = 0;i < nb_namespaces * 2;i++) { | |
fprintf(SAXdebug, ", xmlns"); | |
if (namespaces[i] != NULL) | |
fprintf(SAXdebug, ":%s", namespaces[i]); | |
i++; | |
fprintf(SAXdebug, "='%s'", namespaces[i]); | |
} | |
} | |
fprintf(SAXdebug, ", %d, %d", nb_attributes, nb_defaulted); | |
if (attributes != NULL) { | |
for (i = 0;i < nb_attributes * 5;i += 5) { | |
if (attributes[i + 1] != NULL) | |
fprintf(SAXdebug, ", %s:%s='", attributes[i + 1], attributes[i]); | |
else | |
fprintf(SAXdebug, ", %s='", attributes[i]); | |
fprintf(SAXdebug, "%.4s...', %d", attributes[i + 3], | |
(int)(attributes[i + 4] - attributes[i + 3])); | |
} | |
} | |
fprintf(SAXdebug, ")\n"); | |
} | |
/** | |
* endElementDebug: | |
* @ctxt: An XML parser context | |
* @name: The element name | |
* | |
* called when the end of an element has been detected. | |
*/ | |
static void | |
endElementNsDebug(void *ctx ATTRIBUTE_UNUSED, | |
const xmlChar *localname, | |
const xmlChar *prefix, | |
const xmlChar *URI) | |
{ | |
callbacks++; | |
if (quiet) | |
return; | |
fprintf(SAXdebug, "SAX.endElementNs(%s", (char *) localname); | |
if (prefix == NULL) | |
fprintf(SAXdebug, ", NULL"); | |
else | |
fprintf(SAXdebug, ", %s", (char *) prefix); | |
if (URI == NULL) | |
fprintf(SAXdebug, ", NULL)\n"); | |
else | |
fprintf(SAXdebug, ", '%s')\n", (char *) URI); | |
} | |
xmlSAXHandler debugSAX2HandlerStruct = { | |
internalSubsetDebug, | |
isStandaloneDebug, | |
hasInternalSubsetDebug, | |
hasExternalSubsetDebug, | |
resolveEntityDebug, | |
getEntityDebug, | |
entityDeclDebug, | |
notationDeclDebug, | |
attributeDeclDebug, | |
elementDeclDebug, | |
unparsedEntityDeclDebug, | |
setDocumentLocatorDebug, | |
startDocumentDebug, | |
endDocumentDebug, | |
NULL, | |
NULL, | |
referenceDebug, | |
charactersDebug, | |
ignorableWhitespaceDebug, | |
processingInstructionDebug, | |
commentDebug, | |
warningDebug, | |
errorDebug, | |
fatalErrorDebug, | |
getParameterEntityDebug, | |
cdataBlockDebug, | |
externalSubsetDebug, | |
XML_SAX2_MAGIC, | |
NULL, | |
startElementNsDebug, | |
endElementNsDebug, | |
NULL | |
}; | |
xmlSAXHandlerPtr debugSAX2Handler = &debugSAX2HandlerStruct; | |
#ifdef LIBXML_HTML_ENABLED | |
/** | |
* htmlstartElementDebug: | |
* @ctxt: An XML parser context | |
* @name: The element name | |
* | |
* called when an opening tag has been processed. | |
*/ | |
static void | |
htmlstartElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts) | |
{ | |
int i; | |
fprintf(SAXdebug, "SAX.startElement(%s", (char *) name); | |
if (atts != NULL) { | |
for (i = 0;(atts[i] != NULL);i++) { | |
fprintf(SAXdebug, ", %s", atts[i++]); | |
if (atts[i] != NULL) { | |
unsigned char output[40]; | |
const unsigned char *att = atts[i]; | |
int outlen, attlen; | |
fprintf(SAXdebug, "='"); | |
while ((attlen = strlen((char*)att)) > 0) { | |
outlen = sizeof output - 1; | |
htmlEncodeEntities(output, &outlen, att, &attlen, '\''); | |
output[outlen] = 0; | |
fprintf(SAXdebug, "%s", (char *) output); | |
att += attlen; | |
} | |
fprintf(SAXdebug, "'"); | |
} | |
} | |
} | |
fprintf(SAXdebug, ")\n"); | |
} | |
/** | |
* htmlcharactersDebug: | |
* @ctxt: An XML parser context | |
* @ch: a xmlChar string | |
* @len: the number of xmlChar | |
* | |
* receiving some chars from the parser. | |
* Question: how much at a time ??? | |
*/ | |
static void | |
htmlcharactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len) | |
{ | |
unsigned char output[40]; | |
int inlen = len, outlen = 30; | |
htmlEncodeEntities(output, &outlen, ch, &inlen, 0); | |
output[outlen] = 0; | |
fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len); | |
} | |
/** | |
* htmlcdataDebug: | |
* @ctxt: An XML parser context | |
* @ch: a xmlChar string | |
* @len: the number of xmlChar | |
* | |
* receiving some cdata chars from the parser. | |
* Question: how much at a time ??? | |
*/ | |
static void | |
htmlcdataDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len) | |
{ | |
unsigned char output[40]; | |
int inlen = len, outlen = 30; | |
htmlEncodeEntities(output, &outlen, ch, &inlen, 0); | |
output[outlen] = 0; | |
fprintf(SAXdebug, "SAX.cdata(%s, %d)\n", output, len); | |
} | |
xmlSAXHandler debugHTMLSAXHandlerStruct = { | |
internalSubsetDebug, | |
isStandaloneDebug, | |
hasInternalSubsetDebug, | |
hasExternalSubsetDebug, | |
resolveEntityDebug, | |
getEntityDebug, | |
entityDeclDebug, | |
notationDeclDebug, | |
attributeDeclDebug, | |
elementDeclDebug, | |
unparsedEntityDeclDebug, | |
setDocumentLocatorDebug, | |
startDocumentDebug, | |
endDocumentDebug, | |
htmlstartElementDebug, | |
endElementDebug, | |
referenceDebug, | |
htmlcharactersDebug, | |
ignorableWhitespaceDebug, | |
processingInstructionDebug, | |
commentDebug, | |
warningDebug, | |
errorDebug, | |
fatalErrorDebug, | |
getParameterEntityDebug, | |
htmlcdataDebug, | |
externalSubsetDebug, | |
1, | |
NULL, | |
NULL, | |
NULL, | |
NULL | |
}; | |
xmlSAXHandlerPtr debugHTMLSAXHandler = &debugHTMLSAXHandlerStruct; | |
#endif /* LIBXML_HTML_ENABLED */ | |
#ifdef LIBXML_SAX1_ENABLED | |
/** | |
* saxParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file using the SAX API and check for errors. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
saxParseTest(const char *filename, const char *result, | |
const char *err ATTRIBUTE_UNUSED, | |
int options) { | |
int ret; | |
char *temp; | |
nb_tests++; | |
temp = resultFilename(filename, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "out of memory\n"); | |
fatalError(); | |
} | |
SAXdebug = fopen(temp, "wb"); | |
if (SAXdebug == NULL) { | |
fprintf(stderr, "Failed to write to %s\n", temp); | |
free(temp); | |
return(-1); | |
} | |
/* for SAX we really want the callbacks though the context handlers */ | |
xmlSetStructuredErrorFunc(NULL, NULL); | |
xmlSetGenericErrorFunc(NULL, testErrorHandler); | |
#ifdef LIBXML_HTML_ENABLED | |
if (options & XML_PARSE_HTML) { | |
htmlSAXParseFile(filename, NULL, emptySAXHandler, NULL); | |
ret = 0; | |
} else | |
#endif | |
ret = xmlSAXUserParseFile(emptySAXHandler, NULL, filename); | |
if (ret == XML_WAR_UNDECLARED_ENTITY) { | |
fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret); | |
ret = 0; | |
} | |
if (ret != 0) { | |
fprintf(stderr, "Failed to parse %s\n", filename); | |
return(1); | |
} | |
#ifdef LIBXML_HTML_ENABLED | |
if (options & XML_PARSE_HTML) { | |
htmlSAXParseFile(filename, NULL, debugHTMLSAXHandler, NULL); | |
ret = 0; | |
} else | |
#endif | |
if (options & XML_PARSE_SAX1) { | |
ret = xmlSAXUserParseFile(debugSAXHandler, NULL, filename); | |
} else { | |
ret = xmlSAXUserParseFile(debugSAX2Handler, NULL, filename); | |
} | |
if (ret == XML_WAR_UNDECLARED_ENTITY) { | |
fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret); | |
ret = 0; | |
} | |
fclose(SAXdebug); | |
if (compareFiles(temp, result)) { | |
fprintf(stderr, "Got a difference for %s\n", filename); | |
ret = 1; | |
} else | |
unlink(temp); | |
free(temp); | |
/* switch back to structured error handling */ | |
xmlSetGenericErrorFunc(NULL, NULL); | |
xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler); | |
return(ret); | |
} | |
#endif | |
/************************************************************************ | |
* * | |
* Parse to tree based tests * | |
* * | |
************************************************************************/ | |
/** | |
* oldParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages: unused | |
* | |
* Parse a file using the old xmlParseFile API, then serialize back | |
* reparse the result and serialize again, then check for deviation | |
* in serialization. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
oldParseTest(const char *filename, const char *result, | |
const char *err ATTRIBUTE_UNUSED, | |
int options ATTRIBUTE_UNUSED) { | |
xmlDocPtr doc; | |
char *temp; | |
int res = 0; | |
nb_tests++; | |
/* | |
* base of the test, parse with the old API | |
*/ | |
#ifdef LIBXML_SAX1_ENABLED | |
doc = xmlParseFile(filename); | |
#else | |
doc = xmlReadFile(filename, NULL, 0); | |
#endif | |
if (doc == NULL) | |
return(1); | |
temp = resultFilename(filename, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "out of memory\n"); | |
fatalError(); | |
} | |
xmlSaveFile(temp, doc); | |
if (compareFiles(temp, result)) { | |
res = 1; | |
} | |
xmlFreeDoc(doc); | |
/* | |
* Parse the saved result to make sure the round trip is okay | |
*/ | |
#ifdef LIBXML_SAX1_ENABLED | |
doc = xmlParseFile(temp); | |
#else | |
doc = xmlReadFile(temp, NULL, 0); | |
#endif | |
if (doc == NULL) | |
return(1); | |
xmlSaveFile(temp, doc); | |
if (compareFiles(temp, result)) { | |
res = 1; | |
} | |
xmlFreeDoc(doc); | |
unlink(temp); | |
free(temp); | |
return(res); | |
} | |
#ifdef LIBXML_PUSH_ENABLED | |
/** | |
* pushParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages: unused | |
* | |
* Parse a file using the Push API, then serialize back | |
* to check for content. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
pushParseTest(const char *filename, const char *result, | |
const char *err ATTRIBUTE_UNUSED, | |
int options) { | |
xmlParserCtxtPtr ctxt; | |
xmlDocPtr doc; | |
const char *base; | |
int size, res; | |
int cur = 0; | |
nb_tests++; | |
/* | |
* load the document in memory and work from there. | |
*/ | |
if (loadMem(filename, &base, &size) != 0) { | |
fprintf(stderr, "Failed to load %s\n", filename); | |
return(-1); | |
} | |
#ifdef LIBXML_HTML_ENABLED | |
if (options & XML_PARSE_HTML) | |
ctxt = htmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename, | |
XML_CHAR_ENCODING_NONE); | |
else | |
#endif | |
ctxt = xmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename); | |
xmlCtxtUseOptions(ctxt, options); | |
cur += 4; | |
while (cur < size) { | |
if (cur + 1024 >= size) { | |
#ifdef LIBXML_HTML_ENABLED | |
if (options & XML_PARSE_HTML) | |
htmlParseChunk(ctxt, base + cur, size - cur, 1); | |
else | |
#endif | |
xmlParseChunk(ctxt, base + cur, size - cur, 1); | |
break; | |
} else { | |
#ifdef LIBXML_HTML_ENABLED | |
if (options & XML_PARSE_HTML) | |
htmlParseChunk(ctxt, base + cur, 1024, 0); | |
else | |
#endif | |
xmlParseChunk(ctxt, base + cur, 1024, 0); | |
cur += 1024; | |
} | |
} | |
doc = ctxt->myDoc; | |
#ifdef LIBXML_HTML_ENABLED | |
if (options & XML_PARSE_HTML) | |
res = 1; | |
else | |
#endif | |
res = ctxt->wellFormed; | |
xmlFreeParserCtxt(ctxt); | |
free((char *)base); | |
if (!res) { | |
xmlFreeDoc(doc); | |
fprintf(stderr, "Failed to parse %s\n", filename); | |
return(-1); | |
} | |
#ifdef LIBXML_HTML_ENABLED | |
if (options & XML_PARSE_HTML) | |
htmlDocDumpMemory(doc, (xmlChar **) &base, &size); | |
else | |
#endif | |
xmlDocDumpMemory(doc, (xmlChar **) &base, &size); | |
xmlFreeDoc(doc); | |
res = compareFileMem(result, base, size); | |
if ((base == NULL) || (res != 0)) { | |
if (base != NULL) | |
xmlFree((char *)base); | |
fprintf(stderr, "Result for %s failed\n", filename); | |
return(-1); | |
} | |
xmlFree((char *)base); | |
if (err != NULL) { | |
res = compareFileMem(err, testErrors, testErrorsSize); | |
if (res != 0) { | |
fprintf(stderr, "Error for %s failed\n", filename); | |
return(-1); | |
} | |
} | |
return(0); | |
} | |
#endif | |
/** | |
* memParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages: unused | |
* | |
* Parse a file using the old xmlReadMemory API, then serialize back | |
* reparse the result and serialize again, then check for deviation | |
* in serialization. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
memParseTest(const char *filename, const char *result, | |
const char *err ATTRIBUTE_UNUSED, | |
int options ATTRIBUTE_UNUSED) { | |
xmlDocPtr doc; | |
const char *base; | |
int size, res; | |
nb_tests++; | |
/* | |
* load and parse the memory | |
*/ | |
if (loadMem(filename, &base, &size) != 0) { | |
fprintf(stderr, "Failed to load %s\n", filename); | |
return(-1); | |
} | |
doc = xmlReadMemory(base, size, filename, NULL, 0); | |
unloadMem(base); | |
if (doc == NULL) { | |
return(1); | |
} | |
xmlDocDumpMemory(doc, (xmlChar **) &base, &size); | |
xmlFreeDoc(doc); | |
res = compareFileMem(result, base, size); | |
if ((base == NULL) || (res != 0)) { | |
if (base != NULL) | |
xmlFree((char *)base); | |
fprintf(stderr, "Result for %s failed\n", filename); | |
return(-1); | |
} | |
xmlFree((char *)base); | |
return(0); | |
} | |
/** | |
* noentParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages: unused | |
* | |
* Parse a file with entity resolution, then serialize back | |
* reparse the result and serialize again, then check for deviation | |
* in serialization. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
noentParseTest(const char *filename, const char *result, | |
const char *err ATTRIBUTE_UNUSED, | |
int options) { | |
xmlDocPtr doc; | |
char *temp; | |
int res = 0; | |
nb_tests++; | |
/* | |
* base of the test, parse with the old API | |
*/ | |
doc = xmlReadFile(filename, NULL, options); | |
if (doc == NULL) | |
return(1); | |
temp = resultFilename(filename, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "Out of memory\n"); | |
fatalError(); | |
} | |
xmlSaveFile(temp, doc); | |
if (compareFiles(temp, result)) { | |
res = 1; | |
} | |
xmlFreeDoc(doc); | |
/* | |
* Parse the saved result to make sure the round trip is okay | |
*/ | |
doc = xmlReadFile(filename, NULL, options); | |
if (doc == NULL) | |
return(1); | |
xmlSaveFile(temp, doc); | |
if (compareFiles(temp, result)) { | |
res = 1; | |
} | |
xmlFreeDoc(doc); | |
unlink(temp); | |
free(temp); | |
return(res); | |
} | |
/** | |
* errParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file using the xmlReadFile API and check for errors. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
errParseTest(const char *filename, const char *result, const char *err, | |
int options) { | |
xmlDocPtr doc; | |
const char *base = NULL; | |
int size, res = 0; | |
nb_tests++; | |
#ifdef LIBXML_HTML_ENABLED | |
if (options & XML_PARSE_HTML) { | |
doc = htmlReadFile(filename, NULL, options); | |
} else | |
#endif | |
#ifdef LIBXML_XINCLUDE_ENABLED | |
if (options & XML_PARSE_XINCLUDE) { | |
doc = xmlReadFile(filename, NULL, options); | |
xmlXIncludeProcessFlags(doc, options); | |
} else | |
#endif | |
{ | |
xmlGetWarningsDefaultValue = 1; | |
doc = xmlReadFile(filename, NULL, options); | |
} | |
xmlGetWarningsDefaultValue = 0; | |
if (result) { | |
if (doc == NULL) { | |
base = ""; | |
size = 0; | |
} else { | |
#ifdef LIBXML_HTML_ENABLED | |
if (options & XML_PARSE_HTML) { | |
htmlDocDumpMemory(doc, (xmlChar **) &base, &size); | |
} else | |
#endif | |
xmlDocDumpMemory(doc, (xmlChar **) &base, &size); | |
} | |
res = compareFileMem(result, base, size); | |
} | |
if (doc != NULL) { | |
if (base != NULL) | |
xmlFree((char *)base); | |
xmlFreeDoc(doc); | |
} | |
if (res != 0) { | |
fprintf(stderr, "Result for %s failed\n", filename); | |
return(-1); | |
} | |
if (err != NULL) { | |
res = compareFileMem(err, testErrors, testErrorsSize); | |
if (res != 0) { | |
fprintf(stderr, "Error for %s failed\n", filename); | |
return(-1); | |
} | |
} else if (options & XML_PARSE_DTDVALID) { | |
if (testErrorsSize != 0) | |
fprintf(stderr, "Validation for %s failed\n", filename); | |
} | |
return(0); | |
} | |
#ifdef LIBXML_READER_ENABLED | |
/************************************************************************ | |
* * | |
* Reader based tests * | |
* * | |
************************************************************************/ | |
static void processNode(FILE *out, xmlTextReaderPtr reader) { | |
const xmlChar *name, *value; | |
int type, empty; | |
type = xmlTextReaderNodeType(reader); | |
empty = xmlTextReaderIsEmptyElement(reader); | |
name = xmlTextReaderConstName(reader); | |
if (name == NULL) | |
name = BAD_CAST "--"; | |
value = xmlTextReaderConstValue(reader); | |
fprintf(out, "%d %d %s %d %d", | |
xmlTextReaderDepth(reader), | |
type, | |
name, | |
empty, | |
xmlTextReaderHasValue(reader)); | |
if (value == NULL) | |
fprintf(out, "\n"); | |
else { | |
fprintf(out, " %s\n", value); | |
} | |
} | |
static int | |
streamProcessTest(const char *filename, const char *result, const char *err, | |
xmlTextReaderPtr reader, const char *rng) { | |
int ret; | |
char *temp = NULL; | |
FILE *t = NULL; | |
if (reader == NULL) | |
return(-1); | |
nb_tests++; | |
if (result != NULL) { | |
temp = resultFilename(filename, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "Out of memory\n"); | |
fatalError(); | |
} | |
t = fopen(temp, "wb"); | |
if (t == NULL) { | |
fprintf(stderr, "Can't open temp file %s\n", temp); | |
free(temp); | |
return(-1); | |
} | |
} | |
#ifdef LIBXML_SCHEMAS_ENABLED | |
if (rng != NULL) { | |
ret = xmlTextReaderRelaxNGValidate(reader, rng); | |
if (ret < 0) { | |
testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n", | |
rng); | |
fclose(t); | |
unlink(temp); | |
free(temp); | |
return(0); | |
} | |
} | |
#endif | |
xmlGetWarningsDefaultValue = 1; | |
ret = xmlTextReaderRead(reader); | |
while (ret == 1) { | |
if ((t != NULL) && (rng == NULL)) | |
processNode(t, reader); | |
ret = xmlTextReaderRead(reader); | |
} | |
if (ret != 0) { | |
testErrorHandler(NULL, "%s : failed to parse\n", filename); | |
} | |
if (rng != NULL) { | |
if (xmlTextReaderIsValid(reader) != 1) { | |
testErrorHandler(NULL, "%s fails to validate\n", filename); | |
} else { | |
testErrorHandler(NULL, "%s validates\n", filename); | |
} | |
} | |
xmlGetWarningsDefaultValue = 0; | |
if (t != NULL) { | |
fclose(t); | |
ret = compareFiles(temp, result); | |
unlink(temp); | |
free(temp); | |
if (ret) { | |
fprintf(stderr, "Result for %s failed\n", filename); | |
return(-1); | |
} | |
} | |
if (err != NULL) { | |
ret = compareFileMem(err, testErrors, testErrorsSize); | |
if (ret != 0) { | |
fprintf(stderr, "Error for %s failed\n", filename); | |
printf("%s", testErrors); | |
return(-1); | |
} | |
} | |
return(0); | |
} | |
/** | |
* streamParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file using the reader API and check for errors. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
streamParseTest(const char *filename, const char *result, const char *err, | |
int options) { | |
xmlTextReaderPtr reader; | |
int ret; | |
reader = xmlReaderForFile(filename, NULL, options); | |
ret = streamProcessTest(filename, result, err, reader, NULL); | |
xmlFreeTextReader(reader); | |
return(ret); | |
} | |
/** | |
* walkerParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file using the walker, i.e. a reader built from a atree. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
walkerParseTest(const char *filename, const char *result, const char *err, | |
int options) { | |
xmlDocPtr doc; | |
xmlTextReaderPtr reader; | |
int ret; | |
doc = xmlReadFile(filename, NULL, options); | |
if (doc == NULL) { | |
fprintf(stderr, "Failed to parse %s\n", filename); | |
return(-1); | |
} | |
reader = xmlReaderWalker(doc); | |
ret = streamProcessTest(filename, result, err, reader, NULL); | |
xmlFreeTextReader(reader); | |
xmlFreeDoc(doc); | |
return(ret); | |
} | |
/** | |
* streamMemParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file using the reader API from memory and check for errors. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
streamMemParseTest(const char *filename, const char *result, const char *err, | |
int options) { | |
xmlTextReaderPtr reader; | |
int ret; | |
const char *base; | |
int size; | |
/* | |
* load and parse the memory | |
*/ | |
if (loadMem(filename, &base, &size) != 0) { | |
fprintf(stderr, "Failed to load %s\n", filename); | |
return(-1); | |
} | |
reader = xmlReaderForMemory(base, size, filename, NULL, options); | |
ret = streamProcessTest(filename, result, err, reader, NULL); | |
free((char *)base); | |
xmlFreeTextReader(reader); | |
return(ret); | |
} | |
#endif | |
#ifdef LIBXML_XPATH_ENABLED | |
#ifdef LIBXML_DEBUG_ENABLED | |
/************************************************************************ | |
* * | |
* XPath and XPointer based tests * | |
* * | |
************************************************************************/ | |
FILE *xpathOutput; | |
xmlDocPtr xpathDocument; | |
static void | |
testXPath(const char *str, int xptr, int expr) { | |
xmlXPathObjectPtr res; | |
xmlXPathContextPtr ctxt; | |
nb_tests++; | |
#if defined(LIBXML_XPTR_ENABLED) | |
if (xptr) { | |
ctxt = xmlXPtrNewContext(xpathDocument, NULL, NULL); | |
res = xmlXPtrEval(BAD_CAST str, ctxt); | |
} else { | |
#endif | |
ctxt = xmlXPathNewContext(xpathDocument); | |
ctxt->node = xmlDocGetRootElement(xpathDocument); | |
if (expr) | |
res = xmlXPathEvalExpression(BAD_CAST str, ctxt); | |
else { | |
/* res = xmlXPathEval(BAD_CAST str, ctxt); */ | |
xmlXPathCompExprPtr comp; | |
comp = xmlXPathCompile(BAD_CAST str); | |
if (comp != NULL) { | |
res = xmlXPathCompiledEval(comp, ctxt); | |
xmlXPathFreeCompExpr(comp); | |
} else | |
res = NULL; | |
} | |
#if defined(LIBXML_XPTR_ENABLED) | |
} | |
#endif | |
xmlXPathDebugDumpObject(xpathOutput, res, 0); | |
xmlXPathFreeObject(res); | |
xmlXPathFreeContext(ctxt); | |
} | |
/** | |
* xpathExprTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file containing XPath standalone expressions and evaluate them | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
xpathCommonTest(const char *filename, const char *result, | |
int xptr, int expr) { | |
FILE *input; | |
char expression[5000]; | |
int len, ret = 0; | |
char *temp; | |
temp = resultFilename(filename, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "Out of memory\n"); | |
fatalError(); | |
} | |
xpathOutput = fopen(temp, "wb"); | |
if (xpathOutput == NULL) { | |
fprintf(stderr, "failed to open output file %s\n", temp); | |
free(temp); | |
return(-1); | |
} | |
input = fopen(filename, "rb"); | |
if (input == NULL) { | |
xmlGenericError(xmlGenericErrorContext, | |
"Cannot open %s for reading\n", filename); | |
free(temp); | |
return(-1); | |
} | |
while (fgets(expression, 4500, input) != NULL) { | |
len = strlen(expression); | |
len--; | |
while ((len >= 0) && | |
((expression[len] == '\n') || (expression[len] == '\t') || | |
(expression[len] == '\r') || (expression[len] == ' '))) len--; | |
expression[len + 1] = 0; | |
if (len >= 0) { | |
fprintf(xpathOutput, | |
"\n========================\nExpression: %s\n", | |
expression) ; | |
testXPath(expression, xptr, expr); | |
} | |
} | |
fclose(input); | |
fclose(xpathOutput); | |
if (result != NULL) { | |
ret = compareFiles(temp, result); | |
if (ret) { | |
fprintf(stderr, "Result for %s failed\n", filename); | |
} | |
} | |
unlink(temp); | |
free(temp); | |
return(ret); | |
} | |
/** | |
* xpathExprTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file containing XPath standalone expressions and evaluate them | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
xpathExprTest(const char *filename, const char *result, | |
const char *err ATTRIBUTE_UNUSED, | |
int options ATTRIBUTE_UNUSED) { | |
return(xpathCommonTest(filename, result, 0, 1)); | |
} | |
/** | |
* xpathDocTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file containing XPath expressions and evaluate them against | |
* a set of corresponding documents. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
xpathDocTest(const char *filename, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *err ATTRIBUTE_UNUSED, | |
int options) { | |
char pattern[500]; | |
char result[500]; | |
glob_t globbuf; | |
size_t i; | |
int ret = 0, res; | |
xpathDocument = xmlReadFile(filename, NULL, | |
options | XML_PARSE_DTDATTR | XML_PARSE_NOENT); | |
if (xpathDocument == NULL) { | |
fprintf(stderr, "Failed to load %s\n", filename); | |
return(-1); | |
} | |
snprintf(pattern, 499, "./test/XPath/tests/%s*", baseFilename(filename)); | |
pattern[499] = 0; | |
globbuf.gl_offs = 0; | |
glob(pattern, GLOB_DOOFFS, NULL, &globbuf); | |
for (i = 0;i < globbuf.gl_pathc;i++) { | |
snprintf(result, 499, "result/XPath/tests/%s", | |
baseFilename(globbuf.gl_pathv[i])); | |
res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 0, 0); | |
if (res != 0) | |
ret = res; | |
} | |
globfree(&globbuf); | |
xmlFreeDoc(xpathDocument); | |
return(ret); | |
} | |
#ifdef LIBXML_XPTR_ENABLED | |
/** | |
* xptrDocTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file containing XPath expressions and evaluate them against | |
* a set of corresponding documents. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
xptrDocTest(const char *filename, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *err ATTRIBUTE_UNUSED, | |
int options) { | |
char pattern[500]; | |
char result[500]; | |
glob_t globbuf; | |
size_t i; | |
int ret = 0, res; | |
xpathDocument = xmlReadFile(filename, NULL, | |
options | XML_PARSE_DTDATTR | XML_PARSE_NOENT); | |
if (xpathDocument == NULL) { | |
fprintf(stderr, "Failed to load %s\n", filename); | |
return(-1); | |
} | |
snprintf(pattern, 499, "./test/XPath/xptr/%s*", baseFilename(filename)); | |
pattern[499] = 0; | |
globbuf.gl_offs = 0; | |
glob(pattern, GLOB_DOOFFS, NULL, &globbuf); | |
for (i = 0;i < globbuf.gl_pathc;i++) { | |
snprintf(result, 499, "result/XPath/xptr/%s", | |
baseFilename(globbuf.gl_pathv[i])); | |
res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 1, 0); | |
if (res != 0) | |
ret = res; | |
} | |
globfree(&globbuf); | |
xmlFreeDoc(xpathDocument); | |
return(ret); | |
} | |
#endif /* LIBXML_XPTR_ENABLED */ | |
/** | |
* xmlidDocTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file containing xml:id and check for errors and verify | |
* that XPath queries will work on them as expected. | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
xmlidDocTest(const char *filename, | |
const char *result, | |
const char *err, | |
int options) { | |
int res = 0; | |
int ret = 0; | |
char *temp; | |
xpathDocument = xmlReadFile(filename, NULL, | |
options | XML_PARSE_DTDATTR | XML_PARSE_NOENT); | |
if (xpathDocument == NULL) { | |
fprintf(stderr, "Failed to load %s\n", filename); | |
return(-1); | |
} | |
temp = resultFilename(filename, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "Out of memory\n"); | |
fatalError(); | |
} | |
xpathOutput = fopen(temp, "wb"); | |
if (xpathOutput == NULL) { | |
fprintf(stderr, "failed to open output file %s\n", temp); | |
xmlFreeDoc(xpathDocument); | |
free(temp); | |
return(-1); | |
} | |
testXPath("id('bar')", 0, 0); | |
fclose(xpathOutput); | |
if (result != NULL) { | |
ret = compareFiles(temp, result); | |
if (ret) { | |
fprintf(stderr, "Result for %s failed\n", filename); | |
res = 1; | |
} | |
} | |
unlink(temp); | |
free(temp); | |
xmlFreeDoc(xpathDocument); | |
if (err != NULL) { | |
ret = compareFileMem(err, testErrors, testErrorsSize); | |
if (ret != 0) { | |
fprintf(stderr, "Error for %s failed\n", filename); | |
res = 1; | |
} | |
} | |
return(res); | |
} | |
#endif /* LIBXML_DEBUG_ENABLED */ | |
#endif /* XPATH */ | |
/************************************************************************ | |
* * | |
* URI based tests * | |
* * | |
************************************************************************/ | |
static void | |
handleURI(const char *str, const char *base, FILE *o) { | |
int ret; | |
xmlURIPtr uri; | |
xmlChar *res = NULL, *parsed = NULL; | |
uri = xmlCreateURI(); | |
if (base == NULL) { | |
ret = xmlParseURIReference(uri, str); | |
if (ret != 0) | |
fprintf(o, "%s : error %d\n", str, ret); | |
else { | |
xmlNormalizeURIPath(uri->path); | |
xmlPrintURI(o, uri); | |
fprintf(o, "\n"); | |
} | |
} else { | |
res = xmlBuildURI((xmlChar *)str, (xmlChar *) base); | |
if (res != NULL) { | |
fprintf(o, "%s\n", (char *) res); | |
} | |
else | |
fprintf(o, "::ERROR::\n"); | |
} | |
if (res != NULL) | |
xmlFree(res); | |
if (parsed != NULL) | |
xmlFree(parsed); | |
xmlFreeURI(uri); | |
} | |
/** | |
* uriCommonTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file containing URI and check for errors | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
uriCommonTest(const char *filename, | |
const char *result, | |
const char *err, | |
const char *base) { | |
char *temp; | |
FILE *o, *f; | |
char str[1024]; | |
int res = 0, i, ret; | |
temp = resultFilename(filename, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "Out of memory\n"); | |
fatalError(); | |
} | |
o = fopen(temp, "wb"); | |
if (o == NULL) { | |
fprintf(stderr, "failed to open output file %s\n", temp); | |
free(temp); | |
return(-1); | |
} | |
f = fopen(filename, "rb"); | |
if (f == NULL) { | |
fprintf(stderr, "failed to open input file %s\n", filename); | |
fclose(o); | |
unlink(temp); | |
free(temp); | |
return(-1); | |
} | |
while (1) { | |
/* | |
* read one line in string buffer. | |
*/ | |
if (fgets (&str[0], sizeof (str) - 1, f) == NULL) | |
break; | |
/* | |
* remove the ending spaces | |
*/ | |
i = strlen(str); | |
while ((i > 0) && | |
((str[i - 1] == '\n') || (str[i - 1] == '\r') || | |
(str[i - 1] == ' ') || (str[i - 1] == '\t'))) { | |
i--; | |
str[i] = 0; | |
} | |
nb_tests++; | |
handleURI(str, base, o); | |
} | |
fclose(f); | |
fclose(o); | |
if (result != NULL) { | |
ret = compareFiles(temp, result); | |
if (ret) { | |
fprintf(stderr, "Result for %s failed\n", filename); | |
res = 1; | |
} | |
} | |
if (err != NULL) { | |
ret = compareFileMem(err, testErrors, testErrorsSize); | |
if (ret != 0) { | |
fprintf(stderr, "Error for %s failed\n", filename); | |
res = 1; | |
} | |
} | |
unlink(temp); | |
free(temp); | |
return(res); | |
} | |
/** | |
* uriParseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file containing URI and check for errors | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
uriParseTest(const char *filename, | |
const char *result, | |
const char *err, | |
int options ATTRIBUTE_UNUSED) { | |
return(uriCommonTest(filename, result, err, NULL)); | |
} | |
/** | |
* uriBaseTest: | |
* @filename: the file to parse | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file containing URI, compose them against a fixed base and | |
* check for errors | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
uriBaseTest(const char *filename, | |
const char *result, | |
const char *err, | |
int options ATTRIBUTE_UNUSED) { | |
return(uriCommonTest(filename, result, err, | |
"http://foo.com/path/to/index.html?orig#help")); | |
} | |
#ifdef LIBXML_SCHEMAS_ENABLED | |
/************************************************************************ | |
* * | |
* Schemas tests * | |
* * | |
************************************************************************/ | |
static int | |
schemasOneTest(const char *sch, | |
const char *filename, | |
const char *result, | |
const char *err, | |
int options, | |
xmlSchemaPtr schemas) { | |
xmlDocPtr doc; | |
xmlSchemaValidCtxtPtr ctxt; | |
int ret = 0; | |
char *temp; | |
FILE *schemasOutput; | |
doc = xmlReadFile(filename, NULL, options); | |
if (doc == NULL) { | |
fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch); | |
return(-1); | |
} | |
temp = resultFilename(result, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "Out of memory\n"); | |
fatalError(); | |
} | |
schemasOutput = fopen(temp, "wb"); | |
if (schemasOutput == NULL) { | |
fprintf(stderr, "failed to open output file %s\n", temp); | |
xmlFreeDoc(doc); | |
free(temp); | |
return(-1); | |
} | |
ctxt = xmlSchemaNewValidCtxt(schemas); | |
xmlSchemaSetValidErrors(ctxt, | |
(xmlSchemaValidityErrorFunc) testErrorHandler, | |
(xmlSchemaValidityWarningFunc) testErrorHandler, | |
ctxt); | |
ret = xmlSchemaValidateDoc(ctxt, doc); | |
if (ret == 0) { | |
fprintf(schemasOutput, "%s validates\n", filename); | |
} else if (ret > 0) { | |
fprintf(schemasOutput, "%s fails to validate\n", filename); | |
} else { | |
fprintf(schemasOutput, "%s validation generated an internal error\n", | |
filename); | |
} | |
fclose(schemasOutput); | |
if (result) { | |
if (compareFiles(temp, result)) { | |
fprintf(stderr, "Result for %s on %s failed\n", filename, sch); | |
ret = 1; | |
} | |
} | |
unlink(temp); | |
free(temp); | |
if (err != NULL) { | |
if (compareFileMem(err, testErrors, testErrorsSize)) { | |
fprintf(stderr, "Error for %s on %s failed\n", filename, sch); | |
ret = 1; | |
} | |
} | |
xmlSchemaFreeValidCtxt(ctxt); | |
xmlFreeDoc(doc); | |
return(ret); | |
} | |
/** | |
* schemasTest: | |
* @filename: the schemas file | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a file containing URI, compose them against a fixed base and | |
* check for errors | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
schemasTest(const char *filename, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *errr ATTRIBUTE_UNUSED, | |
int options) { | |
const char *base = baseFilename(filename); | |
const char *base2; | |
const char *instance; | |
xmlSchemaParserCtxtPtr ctxt; | |
xmlSchemaPtr schemas; | |
int res = 0, len, ret; | |
char pattern[500]; | |
char prefix[500]; | |
char result[500]; | |
char err[500]; | |
glob_t globbuf; | |
size_t i; | |
char count = 0; | |
/* first compile the schemas if possible */ | |
ctxt = xmlSchemaNewParserCtxt(filename); | |
xmlSchemaSetParserErrors(ctxt, | |
(xmlSchemaValidityErrorFunc) testErrorHandler, | |
(xmlSchemaValidityWarningFunc) testErrorHandler, | |
ctxt); | |
schemas = xmlSchemaParse(ctxt); | |
xmlSchemaFreeParserCtxt(ctxt); | |
/* | |
* most of the mess is about the output filenames generated by the Makefile | |
*/ | |
len = strlen(base); | |
if ((len > 499) || (len < 5)) { | |
xmlSchemaFree(schemas); | |
return(-1); | |
} | |
len -= 4; /* remove trailing .xsd */ | |
if (base[len - 2] == '_') { | |
len -= 2; /* remove subtest number */ | |
} | |
if (base[len - 2] == '_') { | |
len -= 2; /* remove subtest number */ | |
} | |
memcpy(prefix, base, len); | |
prefix[len] = 0; | |
snprintf(pattern, 499, "./test/schemas/%s_?.xml", prefix); | |
pattern[499] = 0; | |
if (base[len] == '_') { | |
len += 2; | |
memcpy(prefix, base, len); | |
prefix[len] = 0; | |
} | |
globbuf.gl_offs = 0; | |
glob(pattern, GLOB_DOOFFS, NULL, &globbuf); | |
for (i = 0;i < globbuf.gl_pathc;i++) { | |
testErrorsSize = 0; | |
testErrors[0] = 0; | |
instance = globbuf.gl_pathv[i]; | |
base2 = baseFilename(instance); | |
len = strlen(base2); | |
if ((len > 6) && (base2[len - 6] == '_')) { | |
count = base2[len - 5]; | |
snprintf(result, 499, "result/schemas/%s_%c", | |
prefix, count); | |
result[499] = 0; | |
snprintf(err, 499, "result/schemas/%s_%c.err", | |
prefix, count); | |
err[499] = 0; | |
} else { | |
fprintf(stderr, "don't know how to process %s\n", instance); | |
continue; | |
} | |
if (schemas == NULL) { | |
} else { | |
nb_tests++; | |
ret = schemasOneTest(filename, instance, result, err, | |
options, schemas); | |
if (res != 0) | |
ret = res; | |
} | |
} | |
globfree(&globbuf); | |
xmlSchemaFree(schemas); | |
return(res); | |
} | |
/************************************************************************ | |
* * | |
* Schemas tests * | |
* * | |
************************************************************************/ | |
static int | |
rngOneTest(const char *sch, | |
const char *filename, | |
const char *result, | |
const char *err, | |
int options, | |
xmlRelaxNGPtr schemas) { | |
xmlDocPtr doc; | |
xmlRelaxNGValidCtxtPtr ctxt; | |
int ret = 0; | |
char *temp; | |
FILE *schemasOutput; | |
doc = xmlReadFile(filename, NULL, options); | |
if (doc == NULL) { | |
fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch); | |
return(-1); | |
} | |
temp = resultFilename(result, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "Out of memory\n"); | |
fatalError(); | |
} | |
schemasOutput = fopen(temp, "wb"); | |
if (schemasOutput == NULL) { | |
fprintf(stderr, "failed to open output file %s\n", temp); | |
xmlFreeDoc(doc); | |
free(temp); | |
return(-1); | |
} | |
ctxt = xmlRelaxNGNewValidCtxt(schemas); | |
xmlRelaxNGSetValidErrors(ctxt, | |
(xmlRelaxNGValidityErrorFunc) testErrorHandler, | |
(xmlRelaxNGValidityWarningFunc) testErrorHandler, | |
ctxt); | |
ret = xmlRelaxNGValidateDoc(ctxt, doc); | |
if (ret == 0) { | |
testErrorHandler(NULL, "%s validates\n", filename); | |
} else if (ret > 0) { | |
testErrorHandler(NULL, "%s fails to validate\n", filename); | |
} else { | |
testErrorHandler(NULL, "%s validation generated an internal error\n", | |
filename); | |
} | |
fclose(schemasOutput); | |
if (result) { | |
if (compareFiles(temp, result)) { | |
fprintf(stderr, "Result for %s on %s failed\n", filename, sch); | |
ret = 1; | |
} | |
} | |
unlink(temp); | |
free(temp); | |
if (err != NULL) { | |
if (compareFileMem(err, testErrors, testErrorsSize)) { | |
fprintf(stderr, "Error for %s on %s failed\n", filename, sch); | |
ret = 1; | |
printf("%s", testErrors); | |
} | |
} | |
xmlRelaxNGFreeValidCtxt(ctxt); | |
xmlFreeDoc(doc); | |
return(ret); | |
} | |
/** | |
* rngTest: | |
* @filename: the schemas file | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse an RNG schemas and then apply it to the related .xml | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
rngTest(const char *filename, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *errr ATTRIBUTE_UNUSED, | |
int options) { | |
const char *base = baseFilename(filename); | |
const char *base2; | |
const char *instance; | |
xmlRelaxNGParserCtxtPtr ctxt; | |
xmlRelaxNGPtr schemas; | |
int res = 0, len, ret; | |
char pattern[500]; | |
char prefix[500]; | |
char result[500]; | |
char err[500]; | |
glob_t globbuf; | |
size_t i; | |
char count = 0; | |
/* first compile the schemas if possible */ | |
ctxt = xmlRelaxNGNewParserCtxt(filename); | |
xmlRelaxNGSetParserErrors(ctxt, | |
(xmlRelaxNGValidityErrorFunc) testErrorHandler, | |
(xmlRelaxNGValidityWarningFunc) testErrorHandler, | |
ctxt); | |
schemas = xmlRelaxNGParse(ctxt); | |
xmlRelaxNGFreeParserCtxt(ctxt); | |
/* | |
* most of the mess is about the output filenames generated by the Makefile | |
*/ | |
len = strlen(base); | |
if ((len > 499) || (len < 5)) { | |
xmlRelaxNGFree(schemas); | |
return(-1); | |
} | |
len -= 4; /* remove trailing .rng */ | |
memcpy(prefix, base, len); | |
prefix[len] = 0; | |
snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix); | |
pattern[499] = 0; | |
globbuf.gl_offs = 0; | |
glob(pattern, GLOB_DOOFFS, NULL, &globbuf); | |
for (i = 0;i < globbuf.gl_pathc;i++) { | |
testErrorsSize = 0; | |
testErrors[0] = 0; | |
instance = globbuf.gl_pathv[i]; | |
base2 = baseFilename(instance); | |
len = strlen(base2); | |
if ((len > 6) && (base2[len - 6] == '_')) { | |
count = base2[len - 5]; | |
snprintf(result, 499, "result/relaxng/%s_%c", | |
prefix, count); | |
result[499] = 0; | |
snprintf(err, 499, "result/relaxng/%s_%c.err", | |
prefix, count); | |
err[499] = 0; | |
} else { | |
fprintf(stderr, "don't know how to process %s\n", instance); | |
continue; | |
} | |
if (schemas == NULL) { | |
} else { | |
nb_tests++; | |
ret = rngOneTest(filename, instance, result, err, | |
options, schemas); | |
if (res != 0) | |
ret = res; | |
} | |
} | |
globfree(&globbuf); | |
xmlRelaxNGFree(schemas); | |
return(res); | |
} | |
#ifdef LIBXML_READER_ENABLED | |
/** | |
* rngStreamTest: | |
* @filename: the schemas file | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a set of files with streaming, applying an RNG schemas | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
rngStreamTest(const char *filename, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *errr ATTRIBUTE_UNUSED, | |
int options) { | |
const char *base = baseFilename(filename); | |
const char *base2; | |
const char *instance; | |
int res = 0, len, ret; | |
char pattern[500]; | |
char prefix[500]; | |
char result[500]; | |
char err[500]; | |
glob_t globbuf; | |
size_t i; | |
char count = 0; | |
xmlTextReaderPtr reader; | |
int disable_err = 0; | |
/* | |
* most of the mess is about the output filenames generated by the Makefile | |
*/ | |
len = strlen(base); | |
if ((len > 499) || (len < 5)) { | |
fprintf(stderr, "len(base) == %d !\n", len); | |
return(-1); | |
} | |
len -= 4; /* remove trailing .rng */ | |
memcpy(prefix, base, len); | |
prefix[len] = 0; | |
/* | |
* strictly unifying the error messages is nearly impossible this | |
* hack is also done in the Makefile | |
*/ | |
if ((!strcmp(prefix, "tutor10_1")) || (!strcmp(prefix, "tutor10_2")) || | |
(!strcmp(prefix, "tutor3_2"))) | |
disable_err = 1; | |
snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix); | |
pattern[499] = 0; | |
globbuf.gl_offs = 0; | |
glob(pattern, GLOB_DOOFFS, NULL, &globbuf); | |
for (i = 0;i < globbuf.gl_pathc;i++) { | |
testErrorsSize = 0; | |
testErrors[0] = 0; | |
instance = globbuf.gl_pathv[i]; | |
base2 = baseFilename(instance); | |
len = strlen(base2); | |
if ((len > 6) && (base2[len - 6] == '_')) { | |
count = base2[len - 5]; | |
snprintf(result, 499, "result/relaxng/%s_%c", | |
prefix, count); | |
result[499] = 0; | |
snprintf(err, 499, "result/relaxng/%s_%c.err", | |
prefix, count); | |
err[499] = 0; | |
} else { | |
fprintf(stderr, "don't know how to process %s\n", instance); | |
continue; | |
} | |
reader = xmlReaderForFile(instance, NULL, options); | |
if (reader == NULL) { | |
fprintf(stderr, "Failed to build reder for %s\n", instance); | |
} | |
if (disable_err == 1) | |
ret = streamProcessTest(instance, result, NULL, reader, filename); | |
else | |
ret = streamProcessTest(instance, result, err, reader, filename); | |
xmlFreeTextReader(reader); | |
if (ret != 0) { | |
fprintf(stderr, "instance %s failed\n", instance); | |
res = ret; | |
} | |
} | |
globfree(&globbuf); | |
return(res); | |
} | |
#endif /* READER */ | |
#endif | |
#ifdef LIBXML_PATTERN_ENABLED | |
#ifdef LIBXML_READER_ENABLED | |
/************************************************************************ | |
* * | |
* Patterns tests * | |
* * | |
************************************************************************/ | |
static void patternNode(FILE *out, xmlTextReaderPtr reader, | |
const char *pattern, xmlPatternPtr patternc, | |
xmlStreamCtxtPtr patstream) { | |
xmlChar *path = NULL; | |
int match = -1; | |
int type, empty; | |
type = xmlTextReaderNodeType(reader); | |
empty = xmlTextReaderIsEmptyElement(reader); | |
if (type == XML_READER_TYPE_ELEMENT) { | |
/* do the check only on element start */ | |
match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader)); | |
if (match) { | |
path = xmlGetNodePath(xmlTextReaderCurrentNode(reader)); | |
fprintf(out, "Node %s matches pattern %s\n", path, pattern); | |
} | |
} | |
if (patstream != NULL) { | |
int ret; | |
if (type == XML_READER_TYPE_ELEMENT) { | |
ret = xmlStreamPush(patstream, | |
xmlTextReaderConstLocalName(reader), | |
xmlTextReaderConstNamespaceUri(reader)); | |
if (ret < 0) { | |
fprintf(out, "xmlStreamPush() failure\n"); | |
xmlFreeStreamCtxt(patstream); | |
patstream = NULL; | |
} else if (ret != match) { | |
if (path == NULL) { | |
path = xmlGetNodePath( | |
xmlTextReaderCurrentNode(reader)); | |
} | |
fprintf(out, | |
"xmlPatternMatch and xmlStreamPush disagree\n"); | |
fprintf(out, | |
" pattern %s node %s\n", | |
pattern, path); | |
} | |
} | |
if ((type == XML_READER_TYPE_END_ELEMENT) || | |
((type == XML_READER_TYPE_ELEMENT) && (empty))) { | |
ret = xmlStreamPop(patstream); | |
if (ret < 0) { | |
fprintf(out, "xmlStreamPop() failure\n"); | |
xmlFreeStreamCtxt(patstream); | |
patstream = NULL; | |
} | |
} | |
} | |
if (path != NULL) | |
xmlFree(path); | |
} | |
/** | |
* patternTest: | |
* @filename: the schemas file | |
* @result: the file with expected result | |
* @err: the file with error messages | |
* | |
* Parse a set of files with streaming, applying an RNG schemas | |
* | |
* Returns 0 in case of success, an error code otherwise | |
*/ | |
static int | |
patternTest(const char *filename, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *err ATTRIBUTE_UNUSED, | |
int options) { | |
xmlPatternPtr patternc = NULL; | |
xmlStreamCtxtPtr patstream = NULL; | |
FILE *o, *f; | |
char str[1024]; | |
char xml[500]; | |
char result[500]; | |
int len, i; | |
int ret = 0, res; | |
char *temp; | |
xmlTextReaderPtr reader; | |
xmlDocPtr doc; | |
len = strlen(filename); | |
len -= 4; | |
memcpy(xml, filename, len); | |
xml[len] = 0; | |
snprintf(result, 499, "result/pattern/%s", baseFilename(xml)); | |
result[499] = 0; | |
memcpy(xml + len, ".xml", 5); | |
if (!checkTestFile(xml)) { | |
fprintf(stderr, "Missing xml file %s\n", xml); | |
return(-1); | |
} | |
if (!checkTestFile(result)) { | |
fprintf(stderr, "Missing result file %s\n", result); | |
return(-1); | |
} | |
f = fopen(filename, "rb"); | |
if (f == NULL) { | |
fprintf(stderr, "Failed to open %s\n", filename); | |
return(-1); | |
} | |
temp = resultFilename(filename, "", ".res"); | |
if (temp == NULL) { | |
fprintf(stderr, "Out of memory\n"); | |
fatalError(); | |
} | |
o = fopen(temp, "wb"); | |
if (o == NULL) { | |
fprintf(stderr, "failed to open output file %s\n", temp); | |
fclose(f); | |
free(temp); | |
return(-1); | |
} | |
while (1) { | |
/* | |
* read one line in string buffer. | |
*/ | |
if (fgets (&str[0], sizeof (str) - 1, f) == NULL) | |
break; | |
/* | |
* remove the ending spaces | |
*/ | |
i = strlen(str); | |
while ((i > 0) && | |
((str[i - 1] == '\n') || (str[i - 1] == '\r') || | |
(str[i - 1] == ' ') || (str[i - 1] == '\t'))) { | |
i--; | |
str[i] = 0; | |
} | |
doc = xmlReadFile(xml, NULL, options); | |
if (doc == NULL) { | |
fprintf(stderr, "Failed to parse %s\n", xml); | |
ret = 1; | |
} else { | |
xmlNodePtr root; | |
const xmlChar *namespaces[22]; | |
int j; | |
xmlNsPtr ns; | |
root = xmlDocGetRootElement(doc); | |
for (ns = root->nsDef, j = 0;ns != NULL && j < 20;ns=ns->next) { | |
namespaces[j++] = ns->href; | |
namespaces[j++] = ns->prefix; | |
} | |
namespaces[j++] = NULL; | |
namespaces[j++] = NULL; | |
patternc = xmlPatterncompile((const xmlChar *) str, doc->dict, | |
0, &namespaces[0]); | |
if (patternc == NULL) { | |
testErrorHandler(NULL, | |
"Pattern %s failed to compile\n", str); | |
xmlFreeDoc(doc); | |
ret = 1; | |
continue; | |
} | |
patstream = xmlPatternGetStreamCtxt(patternc); | |
if (patstream != NULL) { | |
ret = xmlStreamPush(patstream, NULL, NULL); | |
if (ret < 0) { | |
fprintf(stderr, "xmlStreamPush() failure\n"); | |
xmlFreeStreamCtxt(patstream); | |
patstream = NULL; | |
} | |
} | |
nb_tests++; | |
reader = xmlReaderWalker(doc); | |
res = xmlTextReaderRead(reader); | |
while (res == 1) { | |
patternNode(o, reader, str, patternc, patstream); | |
res = xmlTextReaderRead(reader); | |
} | |
if (res != 0) { | |
fprintf(o, "%s : failed to parse\n", filename); | |
} | |
xmlFreeTextReader(reader); | |
xmlFreeDoc(doc); | |
xmlFreeStreamCtxt(patstream); | |
patstream = NULL; | |
xmlFreePattern(patternc); | |
} | |
} | |
fclose(f); | |
fclose(o); | |
ret = compareFiles(temp, result); | |
if (ret) { | |
fprintf(stderr, "Result for %s failed\n", filename); | |
ret = 1; | |
} | |
unlink(temp); | |
free(temp); | |
return(ret); | |
} | |
#endif /* READER */ | |
#endif /* PATTERN */ | |
#ifdef LIBXML_C14N_ENABLED | |
/************************************************************************ | |
* * | |
* Canonicalization tests * | |
* * | |
************************************************************************/ | |
static xmlXPathObjectPtr | |
load_xpath_expr (xmlDocPtr parent_doc, const char* filename) { | |
xmlXPathObjectPtr xpath; | |
xmlDocPtr doc; | |
xmlChar *expr; | |
xmlXPathContextPtr ctx; | |
xmlNodePtr node; | |
xmlNsPtr ns; | |
/* | |
* load XPath expr as a file | |
*/ | |
xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; | |
xmlSubstituteEntitiesDefault(1); | |
doc = xmlParseFile(filename); | |
if (doc == NULL) { | |
fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename); | |
return(NULL); | |
} | |
/* | |
* Check the document is of the right kind | |
*/ | |
if(xmlDocGetRootElement(doc) == NULL) { | |
fprintf(stderr,"Error: empty document for file \"%s\"\n", filename); | |
xmlFreeDoc(doc); | |
return(NULL); | |
} | |
node = doc->children; | |
while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) { | |
node = node->next; | |
} | |
if(node == NULL) { | |
fprintf(stderr,"Error: XPath element expected in the file \"%s\"\n", filename); | |
xmlFreeDoc(doc); | |
return(NULL); | |
} | |
expr = xmlNodeGetContent(node); | |
if(expr == NULL) { | |
fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename); | |
xmlFreeDoc(doc); | |
return(NULL); | |
} | |
ctx = xmlXPathNewContext(parent_doc); | |
if(ctx == NULL) { | |
fprintf(stderr,"Error: unable to create new context\n"); | |
xmlFree(expr); | |
xmlFreeDoc(doc); | |
return(NULL); | |
} | |
/* | |
* Register namespaces | |
*/ | |
ns = node->nsDef; | |
while(ns != NULL) { | |
if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) { | |
fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href); | |
xmlFree(expr); | |
xmlXPathFreeContext(ctx); | |
xmlFreeDoc(doc); | |
return(NULL); | |
} | |
ns = ns->next; | |
} | |
/* | |
* Evaluate xpath | |
*/ | |
xpath = xmlXPathEvalExpression(expr, ctx); | |
if(xpath == NULL) { | |
fprintf(stderr,"Error: unable to evaluate xpath expression\n"); | |
xmlFree(expr); | |
xmlXPathFreeContext(ctx); | |
xmlFreeDoc(doc); | |
return(NULL); | |
} | |
/* print_xpath_nodes(xpath->nodesetval); */ | |
xmlFree(expr); | |
xmlXPathFreeContext(ctx); | |
xmlFreeDoc(doc); | |
return(xpath); | |
} | |
/* | |
* Macro used to grow the current buffer. | |
*/ | |
#define xxx_growBufferReentrant() { \ | |
buffer_size *= 2; \ | |
buffer = (xmlChar **) \ | |
xmlRealloc(buffer, buffer_size * sizeof(xmlChar*)); \ | |
if (buffer == NULL) { \ | |
perror("realloc failed"); \ | |
return(NULL); \ | |
} \ | |
} | |
static xmlChar ** | |
parse_list(xmlChar *str) { | |
xmlChar **buffer; | |
xmlChar **out = NULL; | |
int buffer_size = 0; | |
int len; | |
if(str == NULL) { | |
return(NULL); | |
} | |
len = xmlStrlen(str); | |
if((str[0] == '\'') && (str[len - 1] == '\'')) { | |
str[len - 1] = '\0'; | |
str++; | |
len -= 2; | |
} | |
/* | |
* allocate an translation buffer. | |
*/ | |
buffer_size = 1000; | |
buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*)); | |
if (buffer == NULL) { | |
perror("malloc failed"); | |
return(NULL); | |
} | |
out = buffer; | |
while(*str != '\0') { | |
if (out - buffer > buffer_size - 10) { | |
int indx = out - buffer; | |
xxx_growBufferReentrant(); | |
out = &buffer[indx]; | |
} | |
(*out++) = str; | |
while(*str != ',' && *str != '\0') ++str; | |
if(*str == ',') *(str++) = '\0'; | |
} | |
(*out) = NULL; | |
return buffer; | |
} | |
static int | |
c14nRunTest(const char* xml_filename, int with_comments, int exclusive, | |
const char* xpath_filename, const char *ns_filename, | |
const char* result_file) { | |
xmlDocPtr doc; | |
xmlXPathObjectPtr xpath = NULL; | |
xmlChar *result = NULL; | |
int ret; | |
xmlChar **inclusive_namespaces = NULL; | |
const char *nslist = NULL; | |
int nssize; | |
/* | |
* build an XML tree from a the file; we need to add default | |
* attributes and resolve all character and entities references | |
*/ | |
xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; | |
xmlSubstituteEntitiesDefault(1); | |
doc = xmlParseFile(xml_filename); | |
if (doc == NULL) { | |
fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename); | |
return(-1); | |
} | |
/* | |
* Check the document is of the right kind | |
*/ | |
if(xmlDocGetRootElement(doc) == NULL) { | |
fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename); | |
xmlFreeDoc(doc); | |
return(-1); | |
} | |
/* | |
* load xpath file if specified | |
*/ | |
if(xpath_filename) { | |
xpath = load_xpath_expr(doc, xpath_filename); | |
if(xpath == NULL) { | |
fprintf(stderr,"Error: unable to evaluate xpath expression\n"); | |
xmlFreeDoc(doc); | |
return(-1); | |
} | |
} | |
if (ns_filename != NULL) { | |
if (loadMem(ns_filename, &nslist, &nssize)) { | |
fprintf(stderr,"Error: unable to evaluate xpath expression\n"); | |
if(xpath != NULL) xmlXPathFreeObject(xpath); | |
xmlFreeDoc(doc); | |
return(-1); | |
} | |
inclusive_namespaces = parse_list((xmlChar *) nslist); | |
} | |
/* | |
* Canonical form | |
*/ | |
/* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */ | |
ret = xmlC14NDocDumpMemory(doc, | |
(xpath) ? xpath->nodesetval : NULL, | |
exclusive, inclusive_namespaces, | |
with_comments, &result); | |
if (ret >= 0) { | |
if(result != NULL) { | |
if (compareFileMem(result_file, (const char *) result, ret)) { | |
fprintf(stderr, "Result mismatch for %s\n", xml_filename); | |
ret = -1; | |
} | |
} | |
} else { | |
fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret); | |
ret = -1; | |
} | |
/* | |
* Cleanup | |
*/ | |
if (result != NULL) xmlFree(result); | |
if(xpath != NULL) xmlXPathFreeObject(xpath); | |
if (inclusive_namespaces != NULL) xmlFree(inclusive_namespaces); | |
if (nslist != NULL) free((char *) nslist); | |
xmlFreeDoc(doc); | |
return(ret); | |
} | |
static int | |
c14nCommonTest(const char *filename, int with_comments, int exclusive, | |
const char *subdir) { | |
char buf[500]; | |
char prefix[500]; | |
const char *base; | |
int len; | |
char *result = NULL; | |
char *xpath = NULL; | |
char *ns = NULL; | |
int ret = 0; | |
base = baseFilename(filename); | |
len = strlen(base); | |
len -= 4; | |
memcpy(prefix, base, len); | |
prefix[len] = 0; | |
snprintf(buf, 499, "result/c14n/%s/%s", subdir,prefix); | |
if (!checkTestFile(buf)) { | |
fprintf(stderr, "Missing result file %s", buf); | |
return(-1); | |
} | |
result = strdup(buf); | |
snprintf(buf, 499, "test/c14n/%s/%s.xpath", subdir,prefix); | |
if (checkTestFile(buf)) { | |
xpath = strdup(buf); | |
} | |
snprintf(buf, 499, "test/c14n/%s/%s.ns", subdir,prefix); | |
if (checkTestFile(buf)) { | |
ns = strdup(buf); | |
} | |
nb_tests++; | |
if (c14nRunTest(filename, with_comments, exclusive, | |
xpath, ns, result) < 0) | |
ret = 1; | |
if (result != NULL) free(result); | |
if (xpath != NULL) free(xpath); | |
if (ns != NULL) free(ns); | |
return(ret); | |
} | |
static int | |
c14nWithCommentTest(const char *filename, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *err ATTRIBUTE_UNUSED, | |
int options ATTRIBUTE_UNUSED) { | |
return(c14nCommonTest(filename, 1, 0, "with-comments")); | |
} | |
static int | |
c14nWithoutCommentTest(const char *filename, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *err ATTRIBUTE_UNUSED, | |
int options ATTRIBUTE_UNUSED) { | |
return(c14nCommonTest(filename, 0, 0, "without-comments")); | |
} | |
static int | |
c14nExcWithoutCommentTest(const char *filename, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *err ATTRIBUTE_UNUSED, | |
int options ATTRIBUTE_UNUSED) { | |
return(c14nCommonTest(filename, 0, 1, "exc-without-comments")); | |
} | |
#endif | |
#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) | |
/************************************************************************ | |
* * | |
* Catalog and threads test * | |
* * | |
************************************************************************/ | |
/* | |
* mostly a cut and paste from testThreads.c | |
*/ | |
#define MAX_ARGC 20 | |
static const char *catalog = "test/threads/complex.xml"; | |
static const char *testfiles[] = { | |
"test/threads/abc.xml", | |
"test/threads/acb.xml", | |
"test/threads/bac.xml", | |
"test/threads/bca.xml", | |
"test/threads/cab.xml", | |
"test/threads/cba.xml", | |
"test/threads/invalid.xml", | |
}; | |
const char *Okay = "OK"; | |
const char *Failed = "Failed"; | |
#ifndef xmlDoValidityCheckingDefaultValue | |
#error xmlDoValidityCheckingDefaultValue is not a macro | |
#endif | |
#ifndef xmlGenericErrorContext | |
#error xmlGenericErrorContext is not a macro | |
#endif | |
static void * | |
thread_specific_data(void *private_data) | |
{ | |
xmlDocPtr myDoc; | |
const char *filename = (const char *) private_data; | |
int okay = 1; | |
if (!strcmp(filename, "test/threads/invalid.xml")) { | |
xmlDoValidityCheckingDefaultValue = 0; | |
xmlGenericErrorContext = stdout; | |
} else { | |
xmlDoValidityCheckingDefaultValue = 1; | |
xmlGenericErrorContext = stderr; | |
} | |
myDoc = xmlParseFile(filename); | |
if (myDoc) { | |
xmlFreeDoc(myDoc); | |
} else { | |
printf("parse failed\n"); | |
okay = 0; | |
} | |
if (!strcmp(filename, "test/threads/invalid.xml")) { | |
if (xmlDoValidityCheckingDefaultValue != 0) { | |
printf("ValidityCheckingDefaultValue override failed\n"); | |
okay = 0; | |
} | |
if (xmlGenericErrorContext != stdout) { | |
printf("xmlGenericErrorContext override failed\n"); | |
okay = 0; | |
} | |
} else { | |
if (xmlDoValidityCheckingDefaultValue != 1) { | |
printf("ValidityCheckingDefaultValue override failed\n"); | |
okay = 0; | |
} | |
if (xmlGenericErrorContext != stderr) { | |
printf("xmlGenericErrorContext override failed\n"); | |
okay = 0; | |
} | |
} | |
if (okay == 0) | |
return ((void *) Failed); | |
return ((void *) Okay); | |
} | |
#if defined(linux) || defined(solaris) | |
#include <pthread.h> | |
static pthread_t tid[MAX_ARGC]; | |
static int | |
testThread(void) | |
{ | |
unsigned int i, repeat; | |
unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]); | |
void *results[MAX_ARGC]; | |
int ret; | |
int res = 0; | |
xmlInitParser(); | |
for (repeat = 0; repeat < 500; repeat++) { | |
xmlLoadCatalog(catalog); | |
nb_tests++; | |
for (i = 0; i < num_threads; i++) { | |
results[i] = NULL; | |
tid[i] = (pthread_t) - 1; | |
} | |
for (i = 0; i < num_threads; i++) { | |
ret = pthread_create(&tid[i], 0, thread_specific_data, | |
(void *) testfiles[i]); | |
if (ret != 0) { | |
fprintf(stderr, "pthread_create failed\n"); | |
return (1); | |
} | |
} | |
for (i = 0; i < num_threads; i++) { | |
ret = pthread_join(tid[i], &results[i]); | |
if (ret != 0) { | |
fprintf(stderr, "pthread_join failed\n"); | |
return (1); | |
} | |
} | |
xmlCatalogCleanup(); | |
for (i = 0; i < num_threads; i++) | |
if (results[i] != (void *) Okay) { | |
fprintf(stderr, "Thread %d handling %s failed\n", | |
i, testfiles[i]); | |
res = 1; | |
} | |
} | |
return (res); | |
} | |
#elif defined WIN32 | |
#include <windows.h> | |
#include <string.h> | |
#define TEST_REPEAT_COUNT 500 | |
static HANDLE tid[MAX_ARGC]; | |
static DWORD WINAPI | |
win32_thread_specific_data(void *private_data) | |
{ | |
return((DWORD) thread_specific_data(private_data)); | |
} | |
static int | |
testThread(void) | |
{ | |
unsigned int i, repeat; | |
unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]); | |
DWORD results[MAX_ARGC]; | |
BOOL ret; | |
int res = 0; | |
xmlInitParser(); | |
for (repeat = 0; repeat < TEST_REPEAT_COUNT; repeat++) { | |
xmlLoadCatalog(catalog); | |
nb_tests++; | |
for (i = 0; i < num_threads; i++) { | |
results[i] = 0; | |
tid[i] = (HANDLE) - 1; | |
} | |
for (i = 0; i < num_threads; i++) { | |
DWORD useless; | |
tid[i] = CreateThread(NULL, 0, | |
win32_thread_specific_data, | |
(void *) testfiles[i], 0, | |
&useless); | |
if (tid[i] == NULL) { | |
fprintf(stderr, "CreateThread failed\n"); | |
return(1); | |
} | |
} | |
if (WaitForMultipleObjects(num_threads, tid, TRUE, INFINITE) == | |
WAIT_FAILED) { | |
fprintf(stderr, "WaitForMultipleObjects failed\n"); | |
return(1); | |
} | |
for (i = 0; i < num_threads; i++) { | |
ret = GetExitCodeThread(tid[i], &results[i]); | |
if (ret == 0) { | |
fprintf(stderr, "GetExitCodeThread failed\n"); | |
return(1); | |
} | |
CloseHandle(tid[i]); | |
} | |
xmlCatalogCleanup(); | |
for (i = 0; i < num_threads; i++) { | |
if (results[i] != (DWORD) Okay) { | |
fprintf(stderr, "Thread %d handling %s failed\n", | |
i, testfiles[i]); | |
res = 1; | |
} | |
} | |
} | |
return (res); | |
} | |
#elif defined __BEOS__ | |
#include <OS.h> | |
static thread_id tid[MAX_ARGC]; | |
static int | |
testThread(void) | |
{ | |
unsigned int i, repeat; | |
unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]); | |
void *results[MAX_ARGC]; | |
status_t ret; | |
int res = 0; | |
xmlInitParser(); | |
for (repeat = 0; repeat < 500; repeat++) { | |
xmlLoadCatalog(catalog); | |
for (i = 0; i < num_threads; i++) { | |
results[i] = NULL; | |
tid[i] = (thread_id) - 1; | |
} | |
for (i = 0; i < num_threads; i++) { | |
tid[i] = | |
spawn_thread(thread_specific_data, "xmlTestThread", | |
B_NORMAL_PRIORITY, (void *) testfiles[i]); | |
if (tid[i] < B_OK) { | |
fprintf(stderr, "beos_thread_create failed\n"); | |
return (1); | |
} | |
printf("beos_thread_create %d -> %d\n", i, tid[i]); | |
} | |
for (i = 0; i < num_threads; i++) { | |
ret = wait_for_thread(tid[i], &results[i]); | |
printf("beos_thread_wait %d -> %d\n", i, ret); | |
if (ret != B_OK) { | |
fprintf(stderr, "beos_thread_wait failed\n"); | |
return (1); | |
} | |
} | |
xmlCatalogCleanup(); | |
ret = B_OK; | |
for (i = 0; i < num_threads; i++) | |
if (results[i] != (void *) Okay) { | |
printf("Thread %d handling %s failed\n", i, testfiles[i]); | |
ret = B_ERROR; | |
} | |
} | |
if (ret != B_OK) | |
return(1); | |
return (0); | |
} | |
#else | |
static int | |
testThread(void) | |
{ | |
fprintf(stderr, | |
"Specific platform thread support not detected\n"); | |
return (-1); | |
} | |
#endif | |
static int | |
threadsTest(const char *filename ATTRIBUTE_UNUSED, | |
const char *resul ATTRIBUTE_UNUSED, | |
const char *err ATTRIBUTE_UNUSED, | |
int options ATTRIBUTE_UNUSED) { | |
return(testThread()); | |
} | |
#endif | |
/************************************************************************ | |
* * | |
* Tests Descriptions * | |
* * | |
************************************************************************/ | |
static | |
testDesc testDescriptions[] = { | |
{ "XML regression tests" , | |
oldParseTest, "./test/*", "result/", "", NULL, | |
0 }, | |
{ "XML regression tests on memory" , | |
memParseTest, "./test/*", "result/", "", NULL, | |
0 }, | |
{ "XML entity subst regression tests" , | |
noentParseTest, "./test/*", "result/noent/", "", NULL, | |
XML_PARSE_NOENT }, | |
{ "XML Namespaces regression tests", | |
errParseTest, "./test/namespaces/*", "result/namespaces/", "", ".err", | |
0 }, | |
{ "Error cases regression tests", | |
errParseTest, "./test/errors/*.xml", "result/errors/", "", ".err", | |
0 }, | |
#ifdef LIBXML_READER_ENABLED | |
{ "Error cases stream regression tests", | |
streamParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".str", | |
0 }, | |
{ "Reader regression tests", | |
streamParseTest, "./test/*", "result/", ".rdr", NULL, | |
0 }, | |
{ "Reader entities substitution regression tests", | |
streamParseTest, "./test/*", "result/", ".rde", NULL, | |
XML_PARSE_NOENT }, | |
{ "Reader on memory regression tests", | |
streamMemParseTest, "./test/*", "result/", ".rdr", NULL, | |
0 }, | |
{ "Walker regression tests", | |
walkerParseTest, "./test/*", "result/", ".rdr", NULL, | |
0 }, | |
#endif | |
#ifdef LIBXML_SAX1_ENABLED | |
{ "SAX1 callbacks regression tests" , | |
saxParseTest, "./test/*", "result/", ".sax", NULL, | |
XML_PARSE_SAX1 }, | |
{ "SAX2 callbacks regression tests" , | |
saxParseTest, "./test/*", "result/", ".sax2", NULL, | |
0 }, | |
#endif | |
#ifdef LIBXML_PUSH_ENABLED | |
{ "XML push regression tests" , | |
pushParseTest, "./test/*", "result/", "", NULL, | |
0 }, | |
#endif | |
#ifdef LIBXML_HTML_ENABLED | |
{ "HTML regression tests" , | |
errParseTest, "./test/HTML/*", "result/HTML/", "", ".err", | |
XML_PARSE_HTML }, | |
#ifdef LIBXML_PUSH_ENABLED | |
{ "Push HTML regression tests" , | |
pushParseTest, "./test/HTML/*", "result/HTML/", "", ".err", | |
XML_PARSE_HTML }, | |
#endif | |
#ifdef LIBXML_SAX1_ENABLED | |
{ "HTML SAX regression tests" , | |
saxParseTest, "./test/HTML/*", "result/HTML/", ".sax", NULL, | |
XML_PARSE_HTML }, | |
#endif | |
#endif | |
#ifdef LIBXML_VALID_ENABLED | |
{ "Valid documents regression tests" , | |
errParseTest, "./test/VCM/*", NULL, NULL, NULL, | |
XML_PARSE_DTDVALID }, | |
{ "Validity checking regression tests" , | |
errParseTest, "./test/VC/*", "result/VC/", NULL, "", | |
XML_PARSE_DTDVALID }, | |
{ "General documents valid regression tests" , | |
errParseTest, "./test/valid/*", "result/valid/", "", ".err", | |
XML_PARSE_DTDVALID }, | |
#endif | |
#ifdef LIBXML_XINCLUDE_ENABLED | |
{ "XInclude regression tests" , | |
errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL, | |
/* Ignore errors at this point ".err", */ | |
XML_PARSE_XINCLUDE }, | |
{ "XInclude xmlReader regression tests", | |
streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr", | |
/* Ignore errors at this point ".err", */ | |
NULL, XML_PARSE_XINCLUDE }, | |
{ "XInclude regression tests stripping include nodes" , | |
errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", NULL, | |
/* Ignore errors at this point ".err", */ | |
XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE }, | |
{ "XInclude xmlReader regression tests stripping include nodes", | |
streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr", | |
/* Ignore errors at this point ".err", */ | |
NULL, XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE }, | |
#endif | |
#ifdef LIBXML_XPATH_ENABLED | |
#ifdef LIBXML_DEBUG_ENABLED | |
{ "XPath expressions regression tests" , | |
xpathExprTest, "./test/XPath/expr/*", "result/XPath/expr/", "", NULL, | |
0 }, | |
{ "XPath document queries regression tests" , | |
xpathDocTest, "./test/XPath/docs/*", NULL, NULL, NULL, | |
0 }, | |
#ifdef LIBXML_XPTR_ENABLED | |
{ "XPointer document queries regression tests" , | |
xptrDocTest, "./test/XPath/docs/*", NULL, NULL, NULL, | |
0 }, | |
#endif | |
{ "xml:id regression tests" , | |
xmlidDocTest, "./test/xmlid/*", "result/xmlid/", "", ".err", | |
0 }, | |
#endif | |
#endif | |
{ "URI parsing tests" , | |
uriParseTest, "./test/URI/*.uri", "result/URI/", "", NULL, | |
0 }, | |
{ "URI base composition tests" , | |
uriBaseTest, "./test/URI/*.data", "result/URI/", "", NULL, | |
0 }, | |
#ifdef LIBXML_SCHEMAS_ENABLED | |
{ "Schemas regression tests" , | |
schemasTest, "./test/schemas/*_*.xsd", NULL, NULL, NULL, | |
0 }, | |
{ "Relax-NG regression tests" , | |
rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL, | |
XML_PARSE_DTDATTR | XML_PARSE_NOENT }, | |
#ifdef LIBXML_READER_ENABLED | |
{ "Relax-NG streaming regression tests" , | |
rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL, | |
XML_PARSE_DTDATTR | XML_PARSE_NOENT }, | |
#endif | |
#endif | |
#ifdef LIBXML_PATTERN_ENABLED | |
#ifdef LIBXML_READER_ENABLED | |
{ "Pattern regression tests" , | |
patternTest, "./test/pattern/*.pat", "result/pattern/", NULL, NULL, | |
0 }, | |
#endif | |
#endif | |
#ifdef LIBXML_C14N_ENABLED | |
{ "C14N with comments regression tests" , | |
c14nWithCommentTest, "./test/c14n/with-comments/*.xml", NULL, NULL, NULL, | |
0 }, | |
{ "C14N without comments regression tests" , | |
c14nWithoutCommentTest, "./test/c14n/without-comments/*.xml", NULL, NULL, NULL, | |
0 }, | |
{ "C14N exclusive without comments regression tests" , | |
c14nExcWithoutCommentTest, "./test/c14n/exc-without-comments/*.xml", NULL, NULL, NULL, | |
0 }, | |
#endif | |
#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) | |
{ "Catalog and Threads regression tests" , | |
threadsTest, NULL, NULL, NULL, NULL, | |
0 }, | |
#endif | |
{NULL, NULL, NULL, NULL, NULL, NULL, 0} | |
}; | |
/************************************************************************ | |
* * | |
* The main code driving the tests * | |
* * | |
************************************************************************/ | |
static int | |
launchTests(testDescPtr tst) { | |
int res = 0, err = 0; | |
size_t i; | |
char *result; | |
char *error; | |
int mem; | |
if (tst == NULL) return(-1); | |
if (tst->in != NULL) { | |
glob_t globbuf; | |
globbuf.gl_offs = 0; | |
glob(tst->in, GLOB_DOOFFS, NULL, &globbuf); | |
for (i = 0;i < globbuf.gl_pathc;i++) { | |
if (!checkTestFile(globbuf.gl_pathv[i])) | |
continue; | |
if (tst->suffix != NULL) { | |
result = resultFilename(globbuf.gl_pathv[i], tst->out, | |
tst->suffix); | |
if (result == NULL) { | |
fprintf(stderr, "Out of memory !\n"); | |
fatalError(); | |
} | |
} else { | |
result = NULL; | |
} | |
if (tst->err != NULL) { | |
error = resultFilename(globbuf.gl_pathv[i], tst->out, | |
tst->err); | |
if (error == NULL) { | |
fprintf(stderr, "Out of memory !\n"); | |
fatalError(); | |
} | |
} else { | |
error = NULL; | |
} | |
if ((result) &&(!checkTestFile(result))) { | |
fprintf(stderr, "Missing result file %s\n", result); | |
} else if ((error) &&(!checkTestFile(error))) { | |
fprintf(stderr, "Missing error file %s\n", error); | |
} else { | |
mem = xmlMemUsed(); | |
extraMemoryFromResolver = 0; | |
testErrorsSize = 0; | |
testErrors[0] = 0; | |
res = tst->func(globbuf.gl_pathv[i], result, error, | |
tst->options); | |
xmlResetLastError(); | |
if (res != 0) { | |
fprintf(stderr, "File %s generated an error\n", | |
globbuf.gl_pathv[i]); | |
nb_errors++; | |
err++; | |
} | |
else if (xmlMemUsed() != mem) { | |
if ((xmlMemUsed() != mem) && | |
(extraMemoryFromResolver == 0)) { | |
fprintf(stderr, "File %s leaked %d bytes\n", | |
globbuf.gl_pathv[i], xmlMemUsed() - mem); | |
nb_leaks++; | |
err++; | |
} | |
} | |
testErrorsSize = 0; | |
} | |
if (result) | |
free(result); | |
if (error) | |
free(error); | |
} | |
globfree(&globbuf); | |
} else { | |
testErrorsSize = 0; | |
testErrors[0] = 0; | |
extraMemoryFromResolver = 0; | |
res = tst->func(NULL, NULL, NULL, tst->options); | |
if (res != 0) { | |
nb_errors++; | |
err++; | |
} | |
} | |
return(err); | |
} | |
int | |
main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { | |
int i = 0, res, ret = 0; | |
int verbose = 0; | |
int old_errors, old_tests, old_leaks; | |
initializeLibxml2(); | |
if ((argc >= 2) && (!strcmp(argv[1], "-v"))) | |
verbose = 1; | |
for (i = 0; testDescriptions[i].func != NULL; i++) { | |
old_errors = nb_errors; | |
old_tests = nb_tests; | |
old_leaks = nb_leaks; | |
if (testDescriptions[i].desc != NULL) | |
printf("## %s\n", testDescriptions[i].desc); | |
res = launchTests(&testDescriptions[i]); | |
if (res != 0) | |
ret++; | |
if (verbose) { | |
if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) | |
printf("Ran %d tests, no errors\n", nb_tests - old_tests); | |
else | |
printf("Ran %d tests, %d errors, %d leaks\n", | |
nb_tests - old_tests, | |
nb_errors - old_errors, | |
nb_leaks - old_leaks); | |
} | |
} | |
if ((nb_errors == 0) && (nb_leaks == 0)) { | |
ret = 0; | |
printf("Total %d tests, no errors\n", | |
nb_tests); | |
} else { | |
ret = 1; | |
printf("Total %d tests, %d errors, %d leaks\n", | |
nb_tests, nb_errors, nb_leaks); | |
} | |
xmlCleanupParser(); | |
xmlMemoryDump(); | |
return(ret); | |
} | |
#else /* ! LIBXML_OUTPUT_ENABLED */ | |
int | |
main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { | |
fprintf(stderr, "runtest requires output to be enabled in libxml2\n"); | |
return(1); | |
} | |
#endif |