Annotation of XML/xmlmemory.c, revision 1.19
1.1 daniel 1: /*
2: * memory.c: libxml memory allocator wrapper.
3: *
4: * Daniel.Veillard@w3.org
5: */
6:
1.3 daniel 7: #ifdef WIN32
1.9 daniel 8: #include "win32config.h"
1.3 daniel 9: #else
1.4 daniel 10: #include "config.h"
1.3 daniel 11: #endif
12:
13: #include <stdio.h>
14: #include <string.h>
15:
16: #ifdef HAVE_SYS_TYPES_H
1.1 daniel 17: #include <sys/types.h>
1.3 daniel 18: #endif
1.4 daniel 19: #ifdef HAVE_TIME_H
20: #include <time.h>
21: #endif
1.3 daniel 22: #ifdef HAVE_MALLOC_H
1.1 daniel 23: #include <malloc.h>
1.3 daniel 24: #endif
1.7 daniel 25: #ifdef HAVE_STDLIB_H
26: #include <stdlib.h>
27: #endif
1.11 daniel 28: #ifdef HAVE_CTYPE_H
29: #include <ctype.h>
30: #endif
1.7 daniel 31:
1.3 daniel 32:
1.14 daniel 33: #include <libxml/xmlmemory.h>
1.19 ! veillard 34: #include <libxml/xmlerror.h>
1.1 daniel 35:
36: #ifdef xmlMalloc
37: #undef xmlMalloc
38: #endif
39: #ifdef xmlRealloc
40: #undef xmlRealloc
41: #endif
42: #ifdef xmlMemStrdup
43: #undef xmlMemStrdup
44: #endif
1.6 daniel 45:
1.1 daniel 46:
47: /*
48: * Each of the blocks allocated begin with a header containing informations
49: */
50:
51: #define MEMTAG 0x5aa5
52:
53: #define MALLOC_TYPE 1
54: #define REALLOC_TYPE 2
55: #define STRDUP_TYPE 3
56:
57: typedef struct memnod {
58: unsigned int mh_tag;
59: unsigned int mh_type;
60: unsigned long mh_number;
61: size_t mh_size;
62: #ifdef MEM_LIST
63: struct memnod *mh_next;
64: struct memnod *mh_prev;
65: #endif
66: const char *mh_file;
67: unsigned int mh_line;
68: } MEMHDR;
69:
70:
71: #ifdef SUN4
72: #define ALIGN_SIZE 16
73: #else
74: #define ALIGN_SIZE sizeof(double)
75: #endif
76: #define HDR_SIZE sizeof(MEMHDR)
77: #define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \
78: / ALIGN_SIZE ) * ALIGN_SIZE)
79:
80:
81: #define CLIENT_2_HDR(a) ((MEMHDR *) (((char *) (a)) - RESERVE_SIZE))
82: #define HDR_2_CLIENT(a) ((void *) (((char *) (a)) + RESERVE_SIZE))
83:
84:
85: static unsigned long debugMemSize = 0;
1.7 daniel 86: static unsigned long debugMaxMemSize = 0;
1.1 daniel 87: static int block=0;
1.7 daniel 88: int xmlMemStopAtBlock = 0;
89: int xmlMemInitialized = 0;
1.1 daniel 90: #ifdef MEM_LIST
91: static MEMHDR *memlist = NULL;
92: #endif
93:
94: void debugmem_tag_error(void *addr);
95: #ifdef MEM_LIST
96: void debugmem_list_add(MEMHDR *);
97: void debugmem_list_delete(MEMHDR *);
98: #endif
99: #define Mem_Tag_Err(a) debugmem_tag_error(a);
100:
101: #ifndef TEST_POINT
102: #define TEST_POINT
103: #endif
104:
105: /**
1.7 daniel 106: * xmlMallocBreakpoint:
107: *
108: * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block
109: * number reaches the specified value this function is called. One need to add a breakpoint
110: * to it to get the context in which the given block is allocated.
111: */
112:
113: void
114: xmlMallocBreakpoint(void) {
1.19 ! veillard 115: xmlGenericError(xmlGenericErrorContext,
! 116: "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock);
1.7 daniel 117: }
118:
119: /**
1.1 daniel 120: * xmlMallocLoc:
121: * @size: an int specifying the size in byte to allocate.
122: * @file: the file name or NULL
1.15 daniel 123: * @line: the line number
1.1 daniel 124: *
125: * a malloc() equivalent, with logging of the allocation info.
126: *
127: * Returns a pointer to the allocated area or NULL in case of lack of memory.
128: */
129:
130: void *
131: xmlMallocLoc(int size, const char * file, int line)
132: {
133: MEMHDR *p;
134:
1.7 daniel 135: if (!xmlMemInitialized) xmlInitMemory();
1.1 daniel 136: #ifdef DEBUG_MEMORY
1.19 ! veillard 137: xmlGenericError(xmlGenericErrorContext,
! 138: "Malloc(%d)\n",size);
1.1 daniel 139: #endif
140:
141: TEST_POINT
142:
143: p = (MEMHDR *) malloc(RESERVE_SIZE+size);
144:
145: if (!p) {
1.19 ! veillard 146: xmlGenericError(xmlGenericErrorContext,
! 147: "xmlMalloc : Out of free space\n");
1.8 daniel 148: xmlMemoryDump();
149: return(NULL);
1.1 daniel 150: }
151: p->mh_tag = MEMTAG;
152: p->mh_number = ++block;
153: p->mh_size = size;
154: p->mh_type = MALLOC_TYPE;
155: p->mh_file = file;
156: p->mh_line = line;
157: debugMemSize += size;
1.7 daniel 158: if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
1.1 daniel 159: #ifdef MEM_LIST
160: debugmem_list_add(p);
161: #endif
162:
163: #ifdef DEBUG_MEMORY
1.19 ! veillard 164: xmlGenericError(xmlGenericErrorContext,
! 165: "Malloc(%d) Ok\n",size);
1.1 daniel 166: #endif
167:
1.7 daniel 168: if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
1.1 daniel 169:
170: TEST_POINT
171:
172: return(HDR_2_CLIENT(p));
173: }
174:
175: /**
1.12 daniel 176: * xmlMemMalloc:
1.1 daniel 177: * @size: an int specifying the size in byte to allocate.
178: *
179: * a malloc() equivalent, with logging of the allocation info.
180: *
181: * Returns a pointer to the allocated area or NULL in case of lack of memory.
182: */
183:
184: void *
1.12 daniel 185: xmlMemMalloc(int size)
1.1 daniel 186: {
187: return(xmlMallocLoc(size, "none", 0));
188: }
189:
190: /**
191: * xmlReallocLoc:
192: * @ptr: the initial memory block pointer
193: * @size: an int specifying the size in byte to allocate.
194: * @file: the file name or NULL
1.15 daniel 195: * @line: the line number
1.1 daniel 196: *
197: * a realloc() equivalent, with logging of the allocation info.
198: *
199: * Returns a pointer to the allocated area or NULL in case of lack of memory.
200: */
201:
202: void *
203: xmlReallocLoc(void *ptr,int size, const char * file, int line)
204: {
205: MEMHDR *p;
206: unsigned long number;
207:
1.7 daniel 208: if (!xmlMemInitialized) xmlInitMemory();
1.1 daniel 209: TEST_POINT
210:
211: p = CLIENT_2_HDR(ptr);
212: number = p->mh_number;
213: if (p->mh_tag != MEMTAG) {
1.4 daniel 214: Mem_Tag_Err(p);
1.1 daniel 215: goto error;
216: }
217: p->mh_tag = ~MEMTAG;
218: debugMemSize -= p->mh_size;
219: #ifdef MEM_LIST
220: debugmem_list_delete(p);
221: #endif
222:
223: p = (MEMHDR *) realloc(p,RESERVE_SIZE+size);
224: if (!p) {
225: goto error;
226: }
227: p->mh_tag = MEMTAG;
228: p->mh_number = number;
229: p->mh_type = REALLOC_TYPE;
230: p->mh_size = size;
231: p->mh_file = file;
232: p->mh_line = line;
233: debugMemSize += size;
1.7 daniel 234: if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
1.1 daniel 235: #ifdef MEM_LIST
236: debugmem_list_add(p);
237: #endif
238:
239: TEST_POINT
240:
241: return(HDR_2_CLIENT(p));
242:
243: error:
244: return(NULL);
245: }
246:
247: /**
1.12 daniel 248: * xmlMemRealloc:
1.1 daniel 249: * @ptr: the initial memory block pointer
250: * @size: an int specifying the size in byte to allocate.
251: *
252: * a realloc() equivalent, with logging of the allocation info.
253: *
254: * Returns a pointer to the allocated area or NULL in case of lack of memory.
255: */
256:
257: void *
1.12 daniel 258: xmlMemRealloc(void *ptr,int size) {
1.1 daniel 259: return(xmlReallocLoc(ptr, size, "none", 0));
260: }
261:
262: /**
1.12 daniel 263: * xmlMemFree:
1.1 daniel 264: * @ptr: the memory block pointer
265: *
266: * a free() equivalent, with error checking.
267: */
268: void
1.12 daniel 269: xmlMemFree(void *ptr)
1.1 daniel 270: {
271: MEMHDR *p;
272:
273: TEST_POINT
274:
275: p = CLIENT_2_HDR(ptr);
276: if (p->mh_tag != MEMTAG) {
1.4 daniel 277: Mem_Tag_Err(p);
278: goto error;
1.1 daniel 279: }
280: p->mh_tag = ~MEMTAG;
281: debugMemSize -= p->mh_size;
282:
283: #ifdef MEM_LIST
284: debugmem_list_delete(p);
285: #endif
286: free(p);
287:
288: TEST_POINT
289:
290: return;
291:
292: error:
1.19 ! veillard 293: xmlGenericError(xmlGenericErrorContext,
! 294: "xmlFree(%X) error\n", (unsigned int) ptr);
1.1 daniel 295: return;
296: }
297:
298: /**
299: * xmlMemStrdupLoc:
300: * @ptr: the initial string pointer
301: * @file: the file name or NULL
1.15 daniel 302: * @line: the line number
1.1 daniel 303: *
304: * a strdup() equivalent, with logging of the allocation info.
305: *
306: * Returns a pointer to the new string or NULL if allocation error occured.
307: */
308:
309: char *
310: xmlMemStrdupLoc(const char *str, const char *file, int line)
311: {
312: char *s;
313: size_t size = strlen(str) + 1;
314: MEMHDR *p;
315:
1.7 daniel 316: if (!xmlMemInitialized) xmlInitMemory();
1.1 daniel 317: TEST_POINT
318:
319: p = (MEMHDR *) malloc(RESERVE_SIZE+size);
320: if (!p) {
1.4 daniel 321: goto error;
1.1 daniel 322: }
323: p->mh_tag = MEMTAG;
324: p->mh_number = ++block;
325: p->mh_size = size;
326: p->mh_type = STRDUP_TYPE;
327: p->mh_file = file;
328: p->mh_line = line;
329: debugMemSize += size;
1.7 daniel 330: if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize;
1.1 daniel 331: #ifdef MEM_LIST
332: debugmem_list_add(p);
333: #endif
1.16 veillard 334: s = (char *) HDR_2_CLIENT(p);
1.1 daniel 335:
1.7 daniel 336: if (xmlMemStopAtBlock == block) xmlMallocBreakpoint();
337:
1.1 daniel 338: if (s != NULL)
1.4 daniel 339: strcpy(s,str);
1.1 daniel 340: else
1.4 daniel 341: goto error;
1.1 daniel 342:
343: TEST_POINT
344:
345: return(s);
346:
347: error:
348: return(NULL);
349: }
350:
351: /**
1.12 daniel 352: * xmlMemoryStrdup:
1.1 daniel 353: * @ptr: the initial string pointer
354: *
355: * a strdup() equivalent, with logging of the allocation info.
356: *
357: * Returns a pointer to the new string or NULL if allocation error occured.
358: */
359:
360: char *
1.12 daniel 361: xmlMemoryStrdup(const char *str) {
1.1 daniel 362: return(xmlMemStrdupLoc(str, "none", 0));
363: }
364:
365: /**
366: * xmlMemUsed:
367: *
368: * returns the amount of memory currenly allocated
369: *
370: * Returns an int representing the amount of memory allocated.
371: */
372:
373: int
374: xmlMemUsed(void) {
375: return(debugMemSize);
376: }
377:
1.11 daniel 378: #ifdef MEM_LIST
379: /**
380: * xmlMemContentShow:
381: * @fp: a FILE descriptor used as the output file
382: * @p: a memory block header
383: *
384: * tries to show some content from the memory block
385: */
386:
387: void
388: xmlMemContentShow(FILE *fp, MEMHDR *p)
389: {
390: int i,j,len = p->mh_size;
1.16 veillard 391: const char *buf = (const char *) HDR_2_CLIENT(p);
1.11 daniel 392:
1.18 veillard 393: if (p == NULL) {
394: fprintf(fp, " NULL");
395: return;
396: }
397:
1.11 daniel 398: for (i = 0;i < len;i++) {
399: if (buf[i] == 0) break;
400: if (!isprint(buf[i])) break;
401: }
402: if ((i < 4) && ((buf[i] != 0) || (i == 0))) {
403: if (len >= 4) {
404: MEMHDR *q;
405: void *cur;
406:
407: for (j = 0;j < len -3;j += 4) {
408: cur = *((void **) &buf[j]);
409: q = CLIENT_2_HDR(cur);
410: p = memlist;
411: while (p != NULL) {
412: if (p == q) break;
413: p = p->mh_next;
414: }
1.18 veillard 415: if ((p != NULL) && (p == q)) {
1.11 daniel 416: fprintf(fp, " pointer to #%lu at index %d",
417: p->mh_number, j);
418: return;
419: }
420: }
421: }
422: } else if ((i == 0) && (buf[i] == 0)) {
423: fprintf(fp," null");
424: } else {
425: if (buf[i] == 0) fprintf(fp," \"%.25s\"", buf);
426: else {
427: fprintf(fp," [");
428: for (j = 0;j < i;j++)
429: fprintf(fp,"%c", buf[j]);
430: fprintf(fp,"]");
431: }
432: }
433: }
434: #endif
435:
1.1 daniel 436: /**
1.10 daniel 437: * xmlMemShow:
438: * @fp: a FILE descriptor used as the output file
439: * @nr: number of entries to dump
440: *
441: * show a show display of the memory allocated, and dump
442: * the @nr last allocated areas which were not freed
443: */
444:
445: void
446: xmlMemShow(FILE *fp, int nr)
447: {
448: #ifdef MEM_LIST
449: MEMHDR *p;
450: #endif
451:
452: if (fp != NULL)
453: fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
454: debugMemSize, debugMaxMemSize);
455: #ifdef MEM_LIST
456: if (nr > 0) {
457: fprintf(fp,"NUMBER SIZE TYPE WHERE\n");
458: p = memlist;
459: while ((p) && nr > 0) {
460: fprintf(fp,"%6lu %6u ",p->mh_number,p->mh_size);
461: switch (p->mh_type) {
462: case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
463: case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
464: case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
465: default:fprintf(fp," ??? in ");break;
466: }
467: if (p->mh_file != NULL)
468: fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
469: if (p->mh_tag != MEMTAG)
470: fprintf(fp," INVALID");
1.11 daniel 471: xmlMemContentShow(fp, p);
1.10 daniel 472: fprintf(fp,"\n");
473: nr--;
474: p = p->mh_next;
475: }
476: }
477: #endif /* MEM_LIST */
478: }
479:
480: /**
1.1 daniel 481: * xmlMemDisplay:
482: * @fp: a FILE descriptor used as the output file, if NULL, the result is
1.10 daniel 483: * written to the file .memorylist
1.1 daniel 484: *
485: * show in-extenso the memory blocks allocated
486: */
487:
488: void
489: xmlMemDisplay(FILE *fp)
490: {
491: #ifdef MEM_LIST
1.4 daniel 492: MEMHDR *p;
493: int idx;
494: #if defined(HAVE_LOCALTIME) && defined(HAVE_STRFTIME)
1.5 daniel 495: time_t currentTime;
1.4 daniel 496: char buf[500];
497: struct tm * tstruct;
498:
1.5 daniel 499: currentTime = time(NULL);
500: tstruct = localtime(¤tTime);
1.4 daniel 501: strftime(buf, sizeof(buf) - 1, "%c", tstruct);
502: fprintf(fp," %s\n\n", buf);
503: #endif
504:
1.1 daniel 505:
1.7 daniel 506: fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n",
507: debugMemSize, debugMaxMemSize);
1.4 daniel 508: fprintf(fp,"BLOCK NUMBER SIZE TYPE\n");
509: idx = 0;
510: p = memlist;
511: while (p) {
1.1 daniel 512: fprintf(fp,"%-5u %6lu %6u ",idx++,p->mh_number,p->mh_size);
1.4 daniel 513: switch (p->mh_type) {
514: case STRDUP_TYPE:fprintf(fp,"strdup() in ");break;
515: case MALLOC_TYPE:fprintf(fp,"malloc() in ");break;
516: case REALLOC_TYPE:fprintf(fp,"realloc() in ");break;
517: default:fprintf(fp," ??? in ");break;
518: }
1.1 daniel 519: if (p->mh_file != NULL) fprintf(fp,"%s(%d)", p->mh_file, p->mh_line);
1.4 daniel 520: if (p->mh_tag != MEMTAG)
1.1 daniel 521: fprintf(fp," INVALID");
1.11 daniel 522: xmlMemContentShow(fp, p);
1.4 daniel 523: fprintf(fp,"\n");
524: p = p->mh_next;
525: }
1.1 daniel 526: #else
1.4 daniel 527: fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n");
1.1 daniel 528: #endif
529: }
530:
531: #ifdef MEM_LIST
532:
533: void debugmem_list_add(MEMHDR *p)
534: {
1.4 daniel 535: p->mh_next = memlist;
536: p->mh_prev = NULL;
537: if (memlist) memlist->mh_prev = p;
538: memlist = p;
1.1 daniel 539: #ifdef MEM_LIST_DEBUG
1.4 daniel 540: if (stderr)
541: Mem_Display(stderr);
1.1 daniel 542: #endif
543: }
544:
545: void debugmem_list_delete(MEMHDR *p)
546: {
1.4 daniel 547: if (p->mh_next)
548: p->mh_next->mh_prev = p->mh_prev;
549: if (p->mh_prev)
550: p->mh_prev->mh_next = p->mh_next;
551: else memlist = p->mh_next;
1.1 daniel 552: #ifdef MEM_LIST_DEBUG
1.4 daniel 553: if (stderr)
554: Mem_Display(stderr);
1.1 daniel 555: #endif
556: }
557:
558: #endif
559:
560: /*
561: * debugmem_tag_error : internal error function.
562: */
563:
564: void debugmem_tag_error(void *p)
565: {
1.19 ! veillard 566: xmlGenericError(xmlGenericErrorContext,
! 567: "Memory tag error occurs :%p \n\t bye\n", p);
1.1 daniel 568: #ifdef MEM_LIST
1.4 daniel 569: if (stderr)
570: xmlMemDisplay(stderr);
1.1 daniel 571: #endif
572: }
573:
574: FILE *xmlMemoryDumpFile = NULL;
575:
576:
577: /**
578: * xmlMemoryDump:
579: *
580: * Dump in-extenso the memory blocks allocated to the file .memorylist
581: */
582:
583: void
584: xmlMemoryDump(void)
585: {
1.13 daniel 586: #if defined(DEBUG_MEMORY_LOCATION) | defined(DEBUG_MEMORY)
1.1 daniel 587: FILE *dump;
588:
589: dump = fopen(".memdump", "w");
590: if (dump == NULL) xmlMemoryDumpFile = stdout;
591: else xmlMemoryDumpFile = dump;
592:
593: xmlMemDisplay(xmlMemoryDumpFile);
594:
595: if (dump != NULL) fclose(dump);
1.13 daniel 596: #endif
1.1 daniel 597: }
598:
599:
600: /****************************************************************
601: * *
602: * Initialization Routines *
603: * *
604: ****************************************************************/
605:
1.12 daniel 606: #if defined(DEBUG_MEMORY_LOCATION) | defined(DEBUG_MEMORY)
1.13 daniel 607: xmlFreeFunc xmlFree = (xmlFreeFunc) xmlMemFree;
608: xmlMallocFunc xmlMalloc = (xmlMallocFunc) xmlMemMalloc;
609: xmlReallocFunc xmlRealloc = (xmlReallocFunc) xmlMemRealloc;
610: xmlStrdupFunc xmlMemStrdup = (xmlStrdupFunc) xmlMemoryStrdup;
1.12 daniel 611: #else
1.13 daniel 612: xmlFreeFunc xmlFree = (xmlFreeFunc) free;
613: xmlMallocFunc xmlMalloc = (xmlMallocFunc) malloc;
614: xmlReallocFunc xmlRealloc = (xmlReallocFunc) realloc;
615: xmlStrdupFunc xmlMemStrdup = (xmlStrdupFunc) strdup;
1.12 daniel 616: #endif
617:
1.1 daniel 618: /**
619: * xmlInitMemory:
620: *
621: * Initialize the memory layer.
1.6 daniel 622: *
623: * Returns 0 on success
1.1 daniel 624: */
625:
1.12 daniel 626: static int xmlInitMemoryDone = 0;
1.1 daniel 627:
628: int
629: xmlInitMemory(void)
630: {
631: int ret;
1.7 daniel 632:
633: #ifdef HAVE_STDLIB_H
634: char *breakpoint;
1.12 daniel 635: #endif
636:
637: if (xmlInitMemoryDone) return(-1);
1.7 daniel 638:
1.12 daniel 639: #ifdef HAVE_STDLIB_H
1.7 daniel 640: breakpoint = getenv("XML_MEM_BREAKPOINT");
641: if (breakpoint != NULL) {
642: sscanf(breakpoint, "%d", &xmlMemStopAtBlock);
643: }
644: #endif
1.1 daniel 645:
646: #ifdef DEBUG_MEMORY
1.19 ! veillard 647: xmlGenericError(xmlGenericErrorContext,
! 648: "xmlInitMemory() Ok\n");
1.1 daniel 649: #endif
650: ret = 0;
651: return(ret);
1.12 daniel 652: }
653:
654: /**
655: * xmlMemSetup:
656: * @freeFunc: the free() function to use
657: * @mallocFunc: the malloc() function to use
658: * @reallocFunc: the realloc() function to use
659: * @strdupFunc: the strdup() function to use
660: *
661: * Override the default memory access functions with a new set
1.13 daniel 662: * This has to be called before any other libxml routines !
663: *
664: * Should this be blocked if there was already some allocations
665: * done ?
1.12 daniel 666: *
667: * Returns 0 on success
668: */
669: int
670: xmlMemSetup(xmlFreeFunc freeFunc, xmlMallocFunc mallocFunc,
671: xmlReallocFunc reallocFunc, xmlStrdupFunc strdupFunc) {
1.17 veillard 672: if (freeFunc == NULL)
1.12 daniel 673: return(-1);
1.17 veillard 674: if (mallocFunc == NULL)
1.12 daniel 675: return(-1);
1.17 veillard 676: if (reallocFunc == NULL)
1.12 daniel 677: return(-1);
1.17 veillard 678: if (strdupFunc == NULL)
1.12 daniel 679: return(-1);
680: xmlFree = freeFunc;
681: xmlMalloc = mallocFunc;
682: xmlRealloc = reallocFunc;
683: xmlMemStrdup = strdupFunc;
684: return(0);
685: }
686:
687: /**
688: * xmlMemGet:
689: * @freeFunc: the free() function in use
690: * @mallocFunc: the malloc() function in use
691: * @reallocFunc: the realloc() function in use
692: * @strdupFunc: the strdup() function in use
693: *
694: * Return the memory access functions set currently in use
695: *
696: * Returns 0 on success
697: */
698: int
699: xmlMemGet(xmlFreeFunc *freeFunc, xmlMallocFunc *mallocFunc,
700: xmlReallocFunc *reallocFunc, xmlStrdupFunc *strdupFunc) {
1.13 daniel 701: if (freeFunc != NULL) *freeFunc = xmlFree;
702: if (mallocFunc != NULL) *mallocFunc = xmlMalloc;
703: if (reallocFunc != NULL) *reallocFunc = xmlRealloc;
704: if (strdupFunc != NULL) *strdupFunc = xmlMemStrdup;
1.12 daniel 705: return(0);
1.1 daniel 706: }
707:
Webmaster