testrecurse.c 24 KB

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