File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / testrecurse.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 19:53:29 2014 UTC (9 years, 11 months ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_9_1p0, v2_9_1, HEAD
libxml2 2.9.1

    1: /*
    2:  * testrecurse.c: C program to run libxml2 regression tests checking entities
    3:  *            recursions
    4:  *
    5:  * To compile on Unixes:
    6:  * cc -o testrecurse `xml2-config --cflags` testrecurse.c `xml2-config --libs` -lpthread
    7:  *
    8:  * See Copyright for the status of this software.
    9:  *
   10:  * daniel@veillard.com
   11:  */
   12: 
   13: #include "libxml.h"
   14: #include <stdio.h>
   15: 
   16: #if !defined(_WIN32) || defined(__CYGWIN__)
   17: #include <unistd.h>
   18: #endif
   19: #include <string.h>
   20: #include <sys/types.h>
   21: #include <sys/stat.h>
   22: #include <fcntl.h>
   23: 
   24: #include <libxml/parser.h>
   25: #include <libxml/tree.h>
   26: #include <libxml/uri.h>
   27: #ifdef LIBXML_READER_ENABLED
   28: #include <libxml/xmlreader.h>
   29: #endif
   30: 
   31: /*
   32:  * O_BINARY is just for Windows compatibility - if it isn't defined
   33:  * on this system, avoid any compilation error
   34:  */
   35: #ifdef	O_BINARY
   36: #define RD_FLAGS	O_RDONLY | O_BINARY
   37: #else
   38: #define	RD_FLAGS	O_RDONLY
   39: #endif
   40: 
   41: typedef int (*functest) (const char *filename, const char *result,
   42:                          const char *error, int options);
   43: 
   44: typedef struct testDesc testDesc;
   45: typedef testDesc *testDescPtr;
   46: struct testDesc {
   47:     const char *desc; /* descripton of the test */
   48:     functest    func; /* function implementing the test */
   49:     const char *in;   /* glob to path for input files */
   50:     const char *out;  /* output directory */
   51:     const char *suffix;/* suffix for output files */
   52:     const char *err;  /* suffix for error output files */
   53:     int     options;  /* parser options for the test */
   54: };
   55: 
   56: static int checkTestFile(const char *filename);
   57: 
   58: 
   59: #if defined(_WIN32) && !defined(__CYGWIN__)
   60: 
   61: #include <windows.h>
   62: #include <io.h>
   63: 
   64: typedef struct
   65: {
   66:       size_t gl_pathc;    /* Count of paths matched so far  */
   67:       char **gl_pathv;    /* List of matched pathnames.  */
   68:       size_t gl_offs;     /* Slots to reserve in 'gl_pathv'.  */
   69: } glob_t;
   70: 
   71: #define GLOB_DOOFFS 0
   72: static int glob(const char *pattern, int flags,
   73:                 int errfunc(const char *epath, int eerrno),
   74:                 glob_t *pglob) {
   75:     glob_t *ret;
   76:     WIN32_FIND_DATA FindFileData;
   77:     HANDLE hFind;
   78:     unsigned int nb_paths = 0;
   79:     char directory[500];
   80:     int len;
   81: 
   82:     if ((pattern == NULL) || (pglob == NULL)) return(-1);
   83: 
   84:     strncpy(directory, pattern, 499);
   85:     for (len = strlen(directory);len >= 0;len--) {
   86:         if (directory[len] == '/') {
   87: 	    len++;
   88: 	    directory[len] = 0;
   89: 	    break;
   90: 	}
   91:     }
   92:     if (len <= 0)
   93:         len = 0;
   94: 
   95: 
   96:     ret = pglob;
   97:     memset(ret, 0, sizeof(glob_t));
   98: 
   99:     hFind = FindFirstFileA(pattern, &FindFileData);
  100:     if (hFind == INVALID_HANDLE_VALUE)
  101:         return(0);
  102:     nb_paths = 20;
  103:     ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *));
  104:     if (ret->gl_pathv == NULL) {
  105: 	FindClose(hFind);
  106:         return(-1);
  107:     }
  108:     strncpy(directory + len, FindFileData.cFileName, 499 - len);
  109:     ret->gl_pathv[ret->gl_pathc] = strdup(directory);
  110:     if (ret->gl_pathv[ret->gl_pathc] == NULL)
  111:         goto done;
  112:     ret->gl_pathc++;
  113:     while(FindNextFileA(hFind, &FindFileData)) {
  114:         if (FindFileData.cFileName[0] == '.')
  115: 	    continue;
  116:         if (ret->gl_pathc + 2 > nb_paths) {
  117:             char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *));
  118:             if (tmp == NULL)
  119:                 break;
  120:             ret->gl_pathv = tmp;
  121:             nb_paths *= 2;
  122: 	}
  123: 	strncpy(directory + len, FindFileData.cFileName, 499 - len);
  124: 	ret->gl_pathv[ret->gl_pathc] = strdup(directory);
  125:         if (ret->gl_pathv[ret->gl_pathc] == NULL)
  126:             break;
  127:         ret->gl_pathc++;
  128:     }
  129:     ret->gl_pathv[ret->gl_pathc] = NULL;
  130: 
  131: done:
  132:     FindClose(hFind);
  133:     return(0);
  134: }
  135: 
  136: 
  137: 
  138: static void globfree(glob_t *pglob) {
  139:     unsigned int i;
  140:     if (pglob == NULL)
  141:         return;
  142: 
  143:     for (i = 0;i < pglob->gl_pathc;i++) {
  144:          if (pglob->gl_pathv[i] != NULL)
  145:              free(pglob->gl_pathv[i]);
  146:     }
  147: }
  148: 
  149: #else
  150: #include <glob.h>
  151: #endif
  152: 
  153: /************************************************************************
  154:  *									*
  155:  *		Huge document generator					*
  156:  *									*
  157:  ************************************************************************/
  158: 
  159: #include <libxml/xmlIO.h>
  160: 
  161: 
  162: static const char *start = "<!DOCTYPE foo [\
  163: <!ENTITY f 'some internal data'> \
  164: <!ENTITY e '&f;&f;'> \
  165: <!ENTITY d '&e;&e;'> \
  166: ]> \
  167: <foo>";
  168: 
  169: static const char *segment = "  <bar>&e; &f; &d;</bar>\n";
  170: static const char *finish = "</foo>";
  171: 
  172: static int curseg = 0;
  173: static const char *current;
  174: static int rlen;
  175: 
  176: /**
  177:  * hugeMatch:
  178:  * @URI: an URI to test
  179:  *
  180:  * Check for an huge: query
  181:  *
  182:  * Returns 1 if yes and 0 if another Input module should be used
  183:  */
  184: static int
  185: hugeMatch(const char * URI) {
  186:     if ((URI != NULL) && (!strncmp(URI, "huge:", 4)))
  187:         return(1);
  188:     return(0);
  189: }
  190: 
  191: /**
  192:  * hugeOpen:
  193:  * @URI: an URI to test
  194:  *
  195:  * Return a pointer to the huge: query handler, in this example simply
  196:  * the current pointer...
  197:  *
  198:  * Returns an Input context or NULL in case or error
  199:  */
  200: static void *
  201: hugeOpen(const char * URI) {
  202:     if ((URI == NULL) || (strncmp(URI, "huge:", 4)))
  203:         return(NULL);
  204:     rlen = strlen(start);
  205:     current = start;
  206:     return((void *) current);
  207: }
  208: 
  209: /**
  210:  * hugeClose:
  211:  * @context: the read context
  212:  *
  213:  * Close the huge: query handler
  214:  *
  215:  * Returns 0 or -1 in case of error
  216:  */
  217: static int
  218: hugeClose(void * context) {
  219:     if (context == NULL) return(-1);
  220:     return(0);
  221: }
  222: 
  223: #define MAX_NODES 1000000
  224: 
  225: /**
  226:  * hugeRead:
  227:  * @context: the read context
  228:  * @buffer: where to store data
  229:  * @len: number of bytes to read
  230:  *
  231:  * Implement an huge: query read.
  232:  *
  233:  * Returns the number of bytes read or -1 in case of error
  234:  */
  235: static int
  236: hugeRead(void *context, char *buffer, int len)
  237: {
  238:     if ((context == NULL) || (buffer == NULL) || (len < 0))
  239:         return (-1);
  240: 
  241:     if (len >= rlen) {
  242:         if (curseg >= MAX_NODES + 1) {
  243:             rlen = 0;
  244:             return(0);
  245:         }
  246:         len = rlen;
  247:         rlen = 0;
  248: 	memcpy(buffer, current, len);
  249:         curseg ++;
  250:         if (curseg == MAX_NODES) {
  251: 	    fprintf(stderr, "\n");
  252:             rlen = strlen(finish);
  253:             current = finish;
  254: 	} else {
  255: 	    if (curseg % (MAX_NODES / 10) == 0)
  256: 	        fprintf(stderr, ".");
  257:             rlen = strlen(segment);
  258:             current = segment;
  259: 	}
  260:     } else {
  261: 	memcpy(buffer, current, len);
  262: 	rlen -= len;
  263:         current += len;
  264:     }
  265:     return (len);
  266: }
  267: 
  268: /************************************************************************
  269:  *									*
  270:  *		Libxml2 specific routines				*
  271:  *									*
  272:  ************************************************************************/
  273: 
  274: static int nb_tests = 0;
  275: static int nb_errors = 0;
  276: static int nb_leaks = 0;
  277: static int extraMemoryFromResolver = 0;
  278: 
  279: static int
  280: fatalError(void) {
  281:     fprintf(stderr, "Exitting tests on fatal error\n");
  282:     exit(1);
  283: }
  284: 
  285: /*
  286:  * We need to trap calls to the resolver to not account memory for the catalog
  287:  * which is shared to the current running test. We also don't want to have
  288:  * network downloads modifying tests.
  289:  */
  290: static xmlParserInputPtr
  291: testExternalEntityLoader(const char *URL, const char *ID,
  292: 			 xmlParserCtxtPtr ctxt) {
  293:     xmlParserInputPtr ret;
  294: 
  295:     if (checkTestFile(URL)) {
  296: 	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
  297:     } else {
  298: 	int memused = xmlMemUsed();
  299: 	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
  300: 	extraMemoryFromResolver += xmlMemUsed() - memused;
  301:     }
  302: 
  303:     return(ret);
  304: }
  305: 
  306: /*
  307:  * Trapping the error messages at the generic level to grab the equivalent of
  308:  * stderr messages on CLI tools.
  309:  */
  310: static char testErrors[32769];
  311: static int testErrorsSize = 0;
  312: 
  313: static void XMLCDECL
  314: channel(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
  315:     va_list args;
  316:     int res;
  317: 
  318:     if (testErrorsSize >= 32768)
  319:         return;
  320:     va_start(args, msg);
  321:     res = vsnprintf(&testErrors[testErrorsSize],
  322:                     32768 - testErrorsSize,
  323: 		    msg, args);
  324:     va_end(args);
  325:     if (testErrorsSize + res >= 32768) {
  326:         /* buffer is full */
  327: 	testErrorsSize = 32768;
  328: 	testErrors[testErrorsSize] = 0;
  329:     } else {
  330:         testErrorsSize += res;
  331:     }
  332:     testErrors[testErrorsSize] = 0;
  333: }
  334: 
  335: /**
  336:  * xmlParserPrintFileContext:
  337:  * @input:  an xmlParserInputPtr input
  338:  *
  339:  * Displays current context within the input content for error tracking
  340:  */
  341: 
  342: static void
  343: xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
  344: 		xmlGenericErrorFunc chanl, void *data ) {
  345:     const xmlChar *cur, *base;
  346:     unsigned int n, col;	/* GCC warns if signed, because compared with sizeof() */
  347:     xmlChar  content[81]; /* space for 80 chars + line terminator */
  348:     xmlChar *ctnt;
  349: 
  350:     if (input == NULL) return;
  351:     cur = input->cur;
  352:     base = input->base;
  353:     /* skip backwards over any end-of-lines */
  354:     while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
  355: 	cur--;
  356:     }
  357:     n = 0;
  358:     /* search backwards for beginning-of-line (to max buff size) */
  359:     while ((n++ < (sizeof(content)-1)) && (cur > base) &&
  360:    (*(cur) != '\n') && (*(cur) != '\r'))
  361:         cur--;
  362:     if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
  363:     /* calculate the error position in terms of the current position */
  364:     col = input->cur - cur;
  365:     /* search forward for end-of-line (to max buff size) */
  366:     n = 0;
  367:     ctnt = content;
  368:     /* copy selected text to our buffer */
  369:     while ((*cur != 0) && (*(cur) != '\n') &&
  370:    (*(cur) != '\r') && (n < sizeof(content)-1)) {
  371: 		*ctnt++ = *cur++;
  372: 	n++;
  373:     }
  374:     *ctnt = 0;
  375:     /* print out the selected text */
  376:     chanl(data ,"%s\n", content);
  377:     /* create blank line with problem pointer */
  378:     n = 0;
  379:     ctnt = content;
  380:     /* (leave buffer space for pointer + line terminator) */
  381:     while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
  382: 	if (*(ctnt) != '\t')
  383: 	    *(ctnt) = ' ';
  384: 	ctnt++;
  385:     }
  386:     *ctnt++ = '^';
  387:     *ctnt = 0;
  388:     chanl(data ,"%s\n", content);
  389: }
  390: 
  391: static void
  392: testStructuredErrorHandler(void *ctx  ATTRIBUTE_UNUSED, xmlErrorPtr err) {
  393:     char *file = NULL;
  394:     int line = 0;
  395:     int code = -1;
  396:     int domain;
  397:     void *data = NULL;
  398:     const char *str;
  399:     const xmlChar *name = NULL;
  400:     xmlNodePtr node;
  401:     xmlErrorLevel level;
  402:     xmlParserInputPtr input = NULL;
  403:     xmlParserInputPtr cur = NULL;
  404:     xmlParserCtxtPtr ctxt = NULL;
  405: 
  406:     if (err == NULL)
  407:         return;
  408: 
  409:     file = err->file;
  410:     line = err->line;
  411:     code = err->code;
  412:     domain = err->domain;
  413:     level = err->level;
  414:     node = err->node;
  415:     if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
  416:         (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
  417: 	(domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
  418: 	ctxt = err->ctxt;
  419:     }
  420:     str = err->message;
  421: 
  422:     if (code == XML_ERR_OK)
  423:         return;
  424: 
  425:     if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
  426:         name = node->name;
  427: 
  428:     /*
  429:      * Maintain the compatibility with the legacy error handling
  430:      */
  431:     if (ctxt != NULL) {
  432:         input = ctxt->input;
  433:         if ((input != NULL) && (input->filename == NULL) &&
  434:             (ctxt->inputNr > 1)) {
  435:             cur = input;
  436:             input = ctxt->inputTab[ctxt->inputNr - 2];
  437:         }
  438:         if (input != NULL) {
  439:             if (input->filename)
  440:                 channel(data, "%s:%d: ", input->filename, input->line);
  441:             else if ((line != 0) && (domain == XML_FROM_PARSER))
  442:                 channel(data, "Entity: line %d: ", input->line);
  443:         }
  444:     } else {
  445:         if (file != NULL)
  446:             channel(data, "%s:%d: ", file, line);
  447:         else if ((line != 0) && (domain == XML_FROM_PARSER))
  448:             channel(data, "Entity: line %d: ", line);
  449:     }
  450:     if (name != NULL) {
  451:         channel(data, "element %s: ", name);
  452:     }
  453:     if (code == XML_ERR_OK)
  454:         return;
  455:     switch (domain) {
  456:         case XML_FROM_PARSER:
  457:             channel(data, "parser ");
  458:             break;
  459:         case XML_FROM_NAMESPACE:
  460:             channel(data, "namespace ");
  461:             break;
  462:         case XML_FROM_DTD:
  463:         case XML_FROM_VALID:
  464:             channel(data, "validity ");
  465:             break;
  466:         case XML_FROM_HTML:
  467:             channel(data, "HTML parser ");
  468:             break;
  469:         case XML_FROM_MEMORY:
  470:             channel(data, "memory ");
  471:             break;
  472:         case XML_FROM_OUTPUT:
  473:             channel(data, "output ");
  474:             break;
  475:         case XML_FROM_IO:
  476:             channel(data, "I/O ");
  477:             break;
  478:         case XML_FROM_XINCLUDE:
  479:             channel(data, "XInclude ");
  480:             break;
  481:         case XML_FROM_XPATH:
  482:             channel(data, "XPath ");
  483:             break;
  484:         case XML_FROM_XPOINTER:
  485:             channel(data, "parser ");
  486:             break;
  487:         case XML_FROM_REGEXP:
  488:             channel(data, "regexp ");
  489:             break;
  490:         case XML_FROM_MODULE:
  491:             channel(data, "module ");
  492:             break;
  493:         case XML_FROM_SCHEMASV:
  494:             channel(data, "Schemas validity ");
  495:             break;
  496:         case XML_FROM_SCHEMASP:
  497:             channel(data, "Schemas parser ");
  498:             break;
  499:         case XML_FROM_RELAXNGP:
  500:             channel(data, "Relax-NG parser ");
  501:             break;
  502:         case XML_FROM_RELAXNGV:
  503:             channel(data, "Relax-NG validity ");
  504:             break;
  505:         case XML_FROM_CATALOG:
  506:             channel(data, "Catalog ");
  507:             break;
  508:         case XML_FROM_C14N:
  509:             channel(data, "C14N ");
  510:             break;
  511:         case XML_FROM_XSLT:
  512:             channel(data, "XSLT ");
  513:             break;
  514:         default:
  515:             break;
  516:     }
  517:     if (code == XML_ERR_OK)
  518:         return;
  519:     switch (level) {
  520:         case XML_ERR_NONE:
  521:             channel(data, ": ");
  522:             break;
  523:         case XML_ERR_WARNING:
  524:             channel(data, "warning : ");
  525:             break;
  526:         case XML_ERR_ERROR:
  527:             channel(data, "error : ");
  528:             break;
  529:         case XML_ERR_FATAL:
  530:             channel(data, "error : ");
  531:             break;
  532:     }
  533:     if (code == XML_ERR_OK)
  534:         return;
  535:     if (str != NULL) {
  536:         int len;
  537: 	len = xmlStrlen((const xmlChar *)str);
  538: 	if ((len > 0) && (str[len - 1] != '\n'))
  539: 	    channel(data, "%s\n", str);
  540: 	else
  541: 	    channel(data, "%s", str);
  542:     } else {
  543:         channel(data, "%s\n", "out of memory error");
  544:     }
  545:     if (code == XML_ERR_OK)
  546:         return;
  547: 
  548:     if (ctxt != NULL) {
  549:         xmlParserPrintFileContextInternal(input, channel, data);
  550:         if (cur != NULL) {
  551:             if (cur->filename)
  552:                 channel(data, "%s:%d: \n", cur->filename, cur->line);
  553:             else if ((line != 0) && (domain == XML_FROM_PARSER))
  554:                 channel(data, "Entity: line %d: \n", cur->line);
  555:             xmlParserPrintFileContextInternal(cur, channel, data);
  556:         }
  557:     }
  558:     if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
  559:         (err->int1 < 100) &&
  560: 	(err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
  561: 	xmlChar buf[150];
  562: 	int i;
  563: 
  564: 	channel(data, "%s\n", err->str1);
  565: 	for (i=0;i < err->int1;i++)
  566: 	     buf[i] = ' ';
  567: 	buf[i++] = '^';
  568: 	buf[i] = 0;
  569: 	channel(data, "%s\n", buf);
  570:     }
  571: }
  572: 
  573: static void
  574: initializeLibxml2(void) {
  575:     xmlGetWarningsDefaultValue = 0;
  576:     xmlPedanticParserDefault(0);
  577: 
  578:     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
  579:     xmlInitParser();
  580:     xmlSetExternalEntityLoader(testExternalEntityLoader);
  581:     xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
  582:     /*
  583:      * register the new I/O handlers
  584:      */
  585:     if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
  586:                                   hugeRead, hugeClose) < 0) {
  587:         fprintf(stderr, "failed to register Huge handler\n");
  588: 	exit(1);
  589:     }
  590: }
  591: 
  592: /************************************************************************
  593:  *									*
  594:  *		File name and path utilities				*
  595:  *									*
  596:  ************************************************************************/
  597: 
  598: static const char *baseFilename(const char *filename) {
  599:     const char *cur;
  600:     if (filename == NULL)
  601:         return(NULL);
  602:     cur = &filename[strlen(filename)];
  603:     while ((cur > filename) && (*cur != '/'))
  604:         cur--;
  605:     if (*cur == '/')
  606:         return(cur + 1);
  607:     return(cur);
  608: }
  609: 
  610: static char *resultFilename(const char *filename, const char *out,
  611:                             const char *suffix) {
  612:     const char *base;
  613:     char res[500];
  614:     char suffixbuff[500];
  615: 
  616: /*************
  617:     if ((filename[0] == 't') && (filename[1] == 'e') &&
  618:         (filename[2] == 's') && (filename[3] == 't') &&
  619: 	(filename[4] == '/'))
  620: 	filename = &filename[5];
  621:  *************/
  622: 
  623:     base = baseFilename(filename);
  624:     if (suffix == NULL)
  625:         suffix = ".tmp";
  626:     if (out == NULL)
  627:         out = "";
  628: 
  629:     strncpy(suffixbuff,suffix,499);
  630: #ifdef VMS
  631:     if(strstr(base,".") && suffixbuff[0]=='.')
  632:       suffixbuff[0]='_';
  633: #endif
  634: 
  635:     snprintf(res, 499, "%s%s%s", out, base, suffixbuff);
  636:     res[499] = 0;
  637:     return(strdup(res));
  638: }
  639: 
  640: static int checkTestFile(const char *filename) {
  641:     struct stat buf;
  642: 
  643:     if (stat(filename, &buf) == -1)
  644:         return(0);
  645: 
  646: #if defined(_WIN32) && !defined(__CYGWIN__)
  647:     if (!(buf.st_mode & _S_IFREG))
  648:         return(0);
  649: #else
  650:     if (!S_ISREG(buf.st_mode))
  651:         return(0);
  652: #endif
  653: 
  654:     return(1);
  655: }
  656: 
  657: 
  658: 
  659: /************************************************************************
  660:  *									*
  661:  *		Test to detect or not recursive entities		*
  662:  *									*
  663:  ************************************************************************/
  664: /**
  665:  * recursiveDetectTest:
  666:  * @filename: the file to parse
  667:  * @result: the file with expected result
  668:  * @err: the file with error messages: unused
  669:  *
  670:  * Parse a file loading DTD and replacing entities check it fails for
  671:  * lol cases
  672:  *
  673:  * Returns 0 in case of success, an error code otherwise
  674:  */
  675: static int
  676: recursiveDetectTest(const char *filename,
  677:              const char *result ATTRIBUTE_UNUSED,
  678:              const char *err ATTRIBUTE_UNUSED,
  679: 	     int options ATTRIBUTE_UNUSED) {
  680:     xmlDocPtr doc;
  681:     xmlParserCtxtPtr ctxt;
  682:     int res = 0;
  683: 
  684:     nb_tests++;
  685: 
  686:     ctxt = xmlNewParserCtxt();
  687:     /*
  688:      * base of the test, parse with the old API
  689:      */
  690:     doc = xmlCtxtReadFile(ctxt, filename, NULL,
  691:                           XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
  692:     if ((doc != NULL) || (ctxt->lastError.code != XML_ERR_ENTITY_LOOP)) {
  693:         fprintf(stderr, "Failed to detect recursion in %s\n", filename);
  694: 	xmlFreeParserCtxt(ctxt);
  695: 	xmlFreeDoc(doc);
  696:         return(1);
  697:     }
  698:     xmlFreeParserCtxt(ctxt);
  699: 
  700:     return(res);
  701: }
  702: 
  703: /**
  704:  * notRecursiveDetectTest:
  705:  * @filename: the file to parse
  706:  * @result: the file with expected result
  707:  * @err: the file with error messages: unused
  708:  *
  709:  * Parse a file loading DTD and replacing entities check it works for
  710:  * good cases
  711:  *
  712:  * Returns 0 in case of success, an error code otherwise
  713:  */
  714: static int
  715: notRecursiveDetectTest(const char *filename,
  716:              const char *result ATTRIBUTE_UNUSED,
  717:              const char *err ATTRIBUTE_UNUSED,
  718: 	     int options ATTRIBUTE_UNUSED) {
  719:     xmlDocPtr doc;
  720:     xmlParserCtxtPtr ctxt;
  721:     int res = 0;
  722: 
  723:     nb_tests++;
  724: 
  725:     ctxt = xmlNewParserCtxt();
  726:     /*
  727:      * base of the test, parse with the old API
  728:      */
  729:     doc = xmlCtxtReadFile(ctxt, filename, NULL,
  730:                           XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
  731:     if (doc == NULL) {
  732:         fprintf(stderr, "Failed to parse correct file %s\n", filename);
  733: 	xmlFreeParserCtxt(ctxt);
  734:         return(1);
  735:     }
  736:     xmlFreeDoc(doc);
  737:     xmlFreeParserCtxt(ctxt);
  738: 
  739:     return(res);
  740: }
  741: 
  742: #ifdef LIBXML_READER_ENABLED
  743: /**
  744:  * notRecursiveHugeTest:
  745:  * @filename: the file to parse
  746:  * @result: the file with expected result
  747:  * @err: the file with error messages: unused
  748:  *
  749:  * Parse a memory generated file
  750:  * good cases
  751:  *
  752:  * Returns 0 in case of success, an error code otherwise
  753:  */
  754: static int
  755: notRecursiveHugeTest(const char *filename ATTRIBUTE_UNUSED,
  756:              const char *result ATTRIBUTE_UNUSED,
  757:              const char *err ATTRIBUTE_UNUSED,
  758: 	     int options ATTRIBUTE_UNUSED) {
  759:     xmlTextReaderPtr reader;
  760:     int res = 0;
  761:     int ret;
  762: 
  763:     nb_tests++;
  764: 
  765:     reader = xmlReaderForFile("huge:test" , NULL,
  766:                               XML_PARSE_NOENT | XML_PARSE_DTDLOAD);
  767:     if (reader == NULL) {
  768:         fprintf(stderr, "Failed to open huge:test\n");
  769: 	return(1);
  770:     }
  771:     ret = xmlTextReaderRead(reader);
  772:     while (ret == 1) {
  773:         ret = xmlTextReaderRead(reader);
  774:     }
  775:     if (ret != 0) {
  776:         fprintf(stderr, "Failed to parser huge:test with entities\n");
  777: 	res = 1;
  778:     }
  779:     xmlFreeTextReader(reader);
  780: 
  781:     return(res);
  782: }
  783: #endif
  784: 
  785: /************************************************************************
  786:  *									*
  787:  *			Tests Descriptions				*
  788:  *									*
  789:  ************************************************************************/
  790: 
  791: static
  792: testDesc testDescriptions[] = {
  793:     { "Parsing recursive test cases" ,
  794:       recursiveDetectTest, "./test/recurse/lol*.xml", NULL, NULL, NULL,
  795:       0 },
  796:     { "Parsing non-recursive test cases" ,
  797:       notRecursiveDetectTest, "./test/recurse/good*.xml", NULL, NULL, NULL,
  798:       0 },
  799: #ifdef LIBXML_READER_ENABLED
  800:     { "Parsing non-recursive huge case" ,
  801:       notRecursiveHugeTest, NULL, NULL, NULL, NULL,
  802:       0 },
  803: #endif
  804:     {NULL, NULL, NULL, NULL, NULL, NULL, 0}
  805: };
  806: 
  807: /************************************************************************
  808:  *									*
  809:  *		The main code driving the tests				*
  810:  *									*
  811:  ************************************************************************/
  812: 
  813: static int
  814: launchTests(testDescPtr tst) {
  815:     int res = 0, err = 0;
  816:     size_t i;
  817:     char *result;
  818:     char *error;
  819:     int mem;
  820: 
  821:     if (tst == NULL) return(-1);
  822:     if (tst->in != NULL) {
  823: 	glob_t globbuf;
  824: 
  825: 	globbuf.gl_offs = 0;
  826: 	glob(tst->in, GLOB_DOOFFS, NULL, &globbuf);
  827: 	for (i = 0;i < globbuf.gl_pathc;i++) {
  828: 	    if (!checkTestFile(globbuf.gl_pathv[i]))
  829: 	        continue;
  830: 	    if (tst->suffix != NULL) {
  831: 		result = resultFilename(globbuf.gl_pathv[i], tst->out,
  832: 					tst->suffix);
  833: 		if (result == NULL) {
  834: 		    fprintf(stderr, "Out of memory !\n");
  835: 		    fatalError();
  836: 		}
  837: 	    } else {
  838: 	        result = NULL;
  839: 	    }
  840: 	    if (tst->err != NULL) {
  841: 		error = resultFilename(globbuf.gl_pathv[i], tst->out,
  842: 		                        tst->err);
  843: 		if (error == NULL) {
  844: 		    fprintf(stderr, "Out of memory !\n");
  845: 		    fatalError();
  846: 		}
  847: 	    } else {
  848: 	        error = NULL;
  849: 	    }
  850: 	    if ((result) &&(!checkTestFile(result))) {
  851: 	        fprintf(stderr, "Missing result file %s\n", result);
  852: 	    } else if ((error) &&(!checkTestFile(error))) {
  853: 	        fprintf(stderr, "Missing error file %s\n", error);
  854: 	    } else {
  855: 		mem = xmlMemUsed();
  856: 		extraMemoryFromResolver = 0;
  857: 		testErrorsSize = 0;
  858: 		testErrors[0] = 0;
  859: 		res = tst->func(globbuf.gl_pathv[i], result, error,
  860: 		                tst->options | XML_PARSE_COMPACT);
  861: 		xmlResetLastError();
  862: 		if (res != 0) {
  863: 		    fprintf(stderr, "File %s generated an error\n",
  864: 		            globbuf.gl_pathv[i]);
  865: 		    nb_errors++;
  866: 		    err++;
  867: 		}
  868: 		else if (xmlMemUsed() != mem) {
  869: 		    if ((xmlMemUsed() != mem) &&
  870: 		        (extraMemoryFromResolver == 0)) {
  871: 			fprintf(stderr, "File %s leaked %d bytes\n",
  872: 				globbuf.gl_pathv[i], xmlMemUsed() - mem);
  873: 			nb_leaks++;
  874: 			err++;
  875: 		    }
  876: 		}
  877: 		testErrorsSize = 0;
  878: 	    }
  879: 	    if (result)
  880: 		free(result);
  881: 	    if (error)
  882: 		free(error);
  883: 	}
  884: 	globfree(&globbuf);
  885:     } else {
  886:         testErrorsSize = 0;
  887: 	testErrors[0] = 0;
  888: 	extraMemoryFromResolver = 0;
  889:         res = tst->func(NULL, NULL, NULL, tst->options);
  890: 	if (res != 0) {
  891: 	    nb_errors++;
  892: 	    err++;
  893: 	}
  894:     }
  895:     return(err);
  896: }
  897: 
  898: static int verbose = 0;
  899: static int tests_quiet = 0;
  900: 
  901: static int
  902: runtest(int i) {
  903:     int ret = 0, res;
  904:     int old_errors, old_tests, old_leaks;
  905: 
  906:     old_errors = nb_errors;
  907:     old_tests = nb_tests;
  908:     old_leaks = nb_leaks;
  909:     if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
  910: 	printf("## %s\n", testDescriptions[i].desc);
  911:     res = launchTests(&testDescriptions[i]);
  912:     if (res != 0)
  913: 	ret++;
  914:     if (verbose) {
  915: 	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  916: 	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
  917: 	else
  918: 	    printf("Ran %d tests, %d errors, %d leaks\n",
  919: 		   nb_tests - old_tests,
  920: 		   nb_errors - old_errors,
  921: 		   nb_leaks - old_leaks);
  922:     }
  923:     return(ret);
  924: }
  925: 
  926: int
  927: main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
  928:     int i, a, ret = 0;
  929:     int subset = 0;
  930: 
  931:     initializeLibxml2();
  932: 
  933:     for (a = 1; a < argc;a++) {
  934:         if (!strcmp(argv[a], "-v"))
  935: 	    verbose = 1;
  936:         else if (!strcmp(argv[a], "-quiet"))
  937: 	    tests_quiet = 1;
  938: 	else {
  939: 	    for (i = 0; testDescriptions[i].func != NULL; i++) {
  940: 	        if (strstr(testDescriptions[i].desc, argv[a])) {
  941: 		    ret += runtest(i);
  942: 		    subset++;
  943: 		}
  944: 	    }
  945: 	}
  946:     }
  947:     if (subset == 0) {
  948: 	for (i = 0; testDescriptions[i].func != NULL; i++) {
  949: 	    ret += runtest(i);
  950: 	}
  951:     }
  952:     if ((nb_errors == 0) && (nb_leaks == 0)) {
  953:         ret = 0;
  954: 	printf("Total %d tests, no errors\n",
  955: 	       nb_tests);
  956:     } else {
  957:         ret = 1;
  958: 	printf("Total %d tests, %d errors, %d leaks\n",
  959: 	       nb_tests, nb_errors, nb_leaks);
  960:     }
  961:     xmlCleanupParser();
  962:     xmlMemoryDump();
  963: 
  964:     return(ret);
  965: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>