File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / testrecurse.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:37:58 2012 UTC (12 years, 4 months ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_7_8, HEAD
libxml2

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

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