1/*
2 * testOOM.c: Test out-of-memory handling
3 *
4 * See Copyright for the status of this software.
5 *
6 * hp@redhat.com
7 */
8
9#include "libxml.h"
10
11#include <string.h>
12#include <stdarg.h>
13
14#ifdef HAVE_SYS_TYPES_H
15#include <sys/types.h>
16#endif
17#ifdef HAVE_UNISTD_H
18#include <unistd.h>
19#endif
20#ifdef HAVE_STDLIB_H
21#include <stdlib.h>
22#endif
23#ifdef HAVE_STRING_H
24#include <string.h>
25#endif
26
27#include <libxml/xmlreader.h>
28
29#include "testOOMlib.h"
30
31#ifndef TRUE
32#define TRUE (1)
33#endif
34#ifndef FALSE
35#define FALSE (0)
36#endif
37
38#define EXIT_OOM 2
39
40int error = FALSE;
41int errcount = 0;
42int noent = 0;
43int count = 0;
44int valid = 0;
45int showErrs = 0;
46
47/*
48 * Since we are using the xmlTextReader functions, we set up
49 * strings for the element types to help in debugging any error
50 * output
51 */
52const char *elementNames[] = {
53    "XML_READER_TYPE_NONE",
54    "XML_READER_TYPE_ELEMENT",
55    "XML_READER_TYPE_ATTRIBUTE",
56    "XML_READER_TYPE_TEXT",
57    "XML_READER_TYPE_CDATA",
58    "XML_READER_TYPE_ENTITY_REFERENCE",
59    "XML_READER_TYPE_ENTITY",
60    "XML_READER_TYPE_PROCESSING_INSTRUCTION",
61    "XML_READER_TYPE_COMMENT",
62    "XML_READER_TYPE_DOCUMENT",
63    "XML_READER_TYPE_DOCUMENT_TYPE",
64    "XML_READER_TYPE_DOCUMENT_FRAGMENT",
65    "XML_READER_TYPE_NOTATION",
66    "XML_READER_TYPE_WHITESPACE",
67    "XML_READER_TYPE_SIGNIFICANT_WHITESPACE",
68    "XML_READER_TYPE_END_ELEMENT",
69    "XML_READER_TYPE_END_ENTITY",
70    "XML_READER_TYPE_XML_DECLARATION"};
71
72/* not using xmlBuff here because I don't want those
73 * mallocs to interfere */
74struct buffer {
75    char *str;
76    size_t len;
77    size_t max;
78};
79
80static struct buffer *buffer_create (size_t init_len)
81{
82    struct buffer *b;
83    b = malloc (sizeof *b);
84    if (b == NULL)
85	exit (EXIT_OOM);
86    if (init_len) {
87	b->str = malloc (init_len);
88	if (b->str == NULL)
89	    exit (EXIT_OOM);
90    }
91    else
92	b->str = NULL;
93    b->len = 0;
94    b->max = init_len;
95    return b;
96}
97
98static void buffer_free (struct buffer *b)
99{
100    free (b->str);
101    free (b);
102}
103
104static size_t buffer_get_length (struct buffer *b)
105{
106    return b->len;
107}
108
109static void buffer_expand (struct buffer *b, size_t min)
110{
111    void *new_str;
112    size_t new_size = b->max ? b->max : 512;
113    while (new_size < b->len + min)
114	new_size *= 2;
115    if (new_size > b->max) {
116	new_str = realloc (b->str, new_size);
117	if (new_str == NULL)
118	    exit (EXIT_OOM);
119	b->str = new_str;
120	b->max = new_size;
121    }
122}
123
124static void buffer_add_char (struct buffer *b, char c)
125{
126    buffer_expand (b, 1);
127    b->str[b->len] = c;
128    b->len += 1;
129}
130
131static void buffer_add_string (struct buffer *b, const char *s)
132{
133    size_t size = strlen(s) + 1;
134    unsigned int ix;
135    for (ix=0; ix<size-1; ix++) {
136        if (s[ix] < 0x20)
137	    printf ("binary data [0x%02x]?\n", (unsigned char)s[ix]);
138    }
139    buffer_expand (b, size);
140    strcpy (b->str + b->len, s);
141    b->str[b->len+size-1] = '\n';	/* replace string term with newline */
142    b->len += size;
143}
144
145static int buffer_equal (struct buffer *b1, struct buffer *b2)
146{
147    return (b1->len == b2->len &&
148	    (b1->len == 0 || (memcmp (b1->str, b2->str, b1->len) == 0)));
149}
150
151static void buffer_dump (struct buffer *b, const char *fname)
152{
153    FILE *f = fopen (fname, "wb");
154    if (f != NULL) {
155	fwrite (b->str, 1, b->len, f);
156	fclose (f);
157    }
158}
159
160
161static void usage(const char *progname) {
162    printf("Usage : %s [options] XMLfiles ...\n", progname);
163    printf("\tParse the XML files using the xmlTextReader API\n");
164    printf("\t --count: count the number of attribute and elements\n");
165    printf("\t --valid: validate the document\n");
166    printf("\t --show:  display the error messages encountered\n");
167    exit(1);
168}
169static unsigned int elem, attrs, chars;
170
171static int processNode (xmlTextReaderPtr reader, void *data)
172{
173    struct buffer *buff = data;
174    int type;
175
176    type = xmlTextReaderNodeType(reader);
177    if (count) {
178	if (type == 1) {
179	    elem++;
180	    attrs += xmlTextReaderAttributeCount(reader);
181	} else if (type == 3) {
182	  const xmlChar *txt;
183	  txt = xmlTextReaderConstValue(reader);
184	  if (txt != NULL)
185	    chars += xmlStrlen (txt);
186	  else
187	    return FALSE;
188	}
189    }
190
191    if (buff != NULL) {
192	int ret;
193	const char *s;
194
195	buffer_add_string (buff, elementNames[type]);
196
197	if (type == 1) {
198	    s = (const char *)xmlTextReaderConstName (reader);
199	    if (s == NULL) return FALSE;
200	    buffer_add_string (buff, s);
201	    while ((ret = xmlTextReaderMoveToNextAttribute (reader)) == 1) {
202		s = (const char *)xmlTextReaderConstName (reader);
203		if (s == NULL) return FALSE;
204		buffer_add_string (buff, s);
205		buffer_add_char (buff, '=');
206		s = (const char *)xmlTextReaderConstValue (reader);
207		if (s == NULL) return FALSE;
208		buffer_add_string (buff, s);
209	    }
210	    if (ret == -1) return FALSE;
211	}
212	else if (type == 3) {
213	    s = (const char *)xmlTextReaderConstValue (reader);
214	    if (s == NULL) return FALSE;
215	    buffer_add_string (buff, s);
216	}
217    }
218
219    return TRUE;
220}
221
222
223struct file_params {
224    const char *filename;
225    struct buffer *verif_buff;
226};
227
228static void
229error_func (void *data ATTRIBUTE_UNUSED, xmlErrorPtr err)
230{
231
232    errcount++;
233    if (err->level == XML_ERR_ERROR ||
234        err->level == XML_ERR_FATAL)
235        error = TRUE;
236    if (showErrs) {
237        printf("%3d line %d: %s\n", error, err->line, err->message);
238    }
239}
240
241static int
242check_load_file_memory_func (void *data)
243{
244     struct file_params *p = data;
245     struct buffer *b;
246     xmlTextReaderPtr reader;
247     int ret, status, first_run;
248
249     if (count) {
250         elem = 0;
251         attrs = 0;
252         chars = 0;
253     }
254
255     first_run = p->verif_buff == NULL;
256     status = TRUE;
257     error = FALSE;
258     if (first_run)
259	 b = buffer_create (0);
260     else
261	 b = buffer_create (buffer_get_length (p->verif_buff));
262
263     reader = xmlNewTextReaderFilename (p->filename);
264     if (reader == NULL)
265       goto out;
266
267     xmlTextReaderSetStructuredErrorHandler (reader, error_func, NULL);
268     xmlSetStructuredErrorFunc(NULL, error_func);
269
270     if (valid) {
271       if (xmlTextReaderSetParserProp(reader, XML_PARSER_VALIDATE, 1) == -1)
272	 goto out;
273     }
274
275     /*
276      * Process all nodes in sequence
277      */
278     while ((ret = xmlTextReaderRead(reader)) == 1) {
279	 if (!processNode(reader, b))
280	 goto out;
281     }
282     if (ret == -1)
283       goto out;
284
285     if (error) {
286	 fprintf (stdout, "error handler was called but parse completed successfully (last error #%d)\n", errcount);
287	 return FALSE;
288     }
289
290     /*
291      * Done, cleanup and status
292      */
293     if (! first_run) {
294	 status = buffer_equal (p->verif_buff, b);
295	 if (! status) {
296	     buffer_dump (p->verif_buff, ".OOM.verif_buff");
297	     buffer_dump (b, ".OOM.buff");
298	 }
299     }
300
301     if (count)
302       {
303	   fprintf (stdout, "# %s: %u elems, %u attrs, %u chars %s\n",
304		    p->filename, elem, attrs, chars,
305		    status ? "ok" : "wrong");
306       }
307
308 out:
309     if (first_run)
310	 p->verif_buff = b;
311     else
312	 buffer_free (b);
313     if (reader)
314	 xmlFreeTextReader (reader);
315     return status;
316}
317
318int main(int argc, char **argv) {
319    int i;
320    int files = 0;
321
322    if (argc <= 1) {
323	usage(argv[0]);
324	return(1);
325    }
326    LIBXML_TEST_VERSION;
327
328    xmlMemSetup (test_free,
329                 test_malloc,
330                 test_realloc,
331                 test_strdup);
332
333    xmlInitParser();
334
335    for (i = 1; i < argc ; i++) {
336        if ((!strcmp(argv[i], "-count")) || (!strcmp(argv[i], "--count")))
337	    count++;
338	else if ((!strcmp(argv[i], "-valid")) || (!strcmp(argv[i], "--valid")))
339	    valid++;
340	else if ((!strcmp(argv[i], "-noent")) ||
341	         (!strcmp(argv[i], "--noent")))
342	    noent++;
343	else if ((!strcmp(argv[i], "-show")) ||
344		 (!strcmp(argv[i], "--show")))
345	    showErrs++;
346    }
347    if (noent != 0)
348      xmlSubstituteEntitiesDefault(1);
349    for (i = 1; i < argc ; i++) {
350	if (argv[i][0] != '-') {
351             struct file_params p;
352	     p.filename = argv[i];
353	     p.verif_buff = NULL;
354
355             if (!test_oom_handling (check_load_file_memory_func,
356                                     &p)) {
357                  fprintf (stdout, "Failed!\n");
358                  return 1;
359             }
360
361	     buffer_free (p.verif_buff);
362             xmlCleanupParser();
363
364             if (test_get_malloc_blocks_outstanding () > 0) {
365                  fprintf (stdout, "%d blocks leaked\n",
366                           test_get_malloc_blocks_outstanding ());
367		  xmlMemoryDump();
368                  return 1;
369             }
370
371	    files ++;
372	}
373    }
374    xmlMemoryDump();
375
376    return 0;
377}
378