Imported Upstream version 2.9.6
[platform/upstream/libxml2.git] / xmlIO.c
1 /*
2  * xmlIO.c : implementation of the I/O interfaces used by the parser
3  *
4  * See Copyright for the status of this software.
5  *
6  * daniel@veillard.com
7  *
8  * 14 Nov 2000 ht - for VMS, truncated name of long functions to under 32 char
9  */
10
11 #define IN_LIBXML
12 #include "libxml.h"
13
14 #include <string.h>
15 #ifdef HAVE_ERRNO_H
16 #include <errno.h>
17 #endif
18
19
20 #ifdef HAVE_SYS_TYPES_H
21 #include <sys/types.h>
22 #endif
23 #ifdef HAVE_SYS_STAT_H
24 #include <sys/stat.h>
25 #endif
26 #ifdef HAVE_FCNTL_H
27 #include <fcntl.h>
28 #endif
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifdef HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35 #ifdef HAVE_ZLIB_H
36 #include <zlib.h>
37 #endif
38 #ifdef HAVE_LZMA_H
39 #include <lzma.h>
40 #endif
41
42 #if defined(WIN32) || defined(_WIN32)
43 #include <windows.h>
44 #endif
45
46 #if defined(_WIN32_WCE)
47 #include <winnls.h> /* for CP_UTF8 */
48 #endif
49
50 /* Figure a portable way to know if a file is a directory. */
51 #ifndef HAVE_STAT
52 #  ifdef HAVE__STAT
53      /* MS C library seems to define stat and _stat. The definition
54         is identical. Still, mapping them to each other causes a warning. */
55 #    ifndef _MSC_VER
56 #      define stat(x,y) _stat(x,y)
57 #    endif
58 #    define HAVE_STAT
59 #  endif
60 #else
61 #  ifdef HAVE__STAT
62 #    if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
63 #      define stat _stat
64 #    endif
65 #  endif
66 #endif
67 #ifdef HAVE_STAT
68 #  ifndef S_ISDIR
69 #    ifdef _S_ISDIR
70 #      define S_ISDIR(x) _S_ISDIR(x)
71 #    else
72 #      ifdef S_IFDIR
73 #        ifndef S_IFMT
74 #          ifdef _S_IFMT
75 #            define S_IFMT _S_IFMT
76 #          endif
77 #        endif
78 #        ifdef S_IFMT
79 #          define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
80 #        endif
81 #      endif
82 #    endif
83 #  endif
84 #endif
85
86 #include <libxml/xmlmemory.h>
87 #include <libxml/parser.h>
88 #include <libxml/parserInternals.h>
89 #include <libxml/xmlIO.h>
90 #include <libxml/uri.h>
91 #include <libxml/nanohttp.h>
92 #include <libxml/nanoftp.h>
93 #include <libxml/xmlerror.h>
94 #ifdef LIBXML_CATALOG_ENABLED
95 #include <libxml/catalog.h>
96 #endif
97 #include <libxml/globals.h>
98
99 #include "buf.h"
100 #include "enc.h"
101
102 /* #define VERBOSE_FAILURE */
103 /* #define DEBUG_EXTERNAL_ENTITIES */
104 /* #define DEBUG_INPUT */
105
106 #ifdef DEBUG_INPUT
107 #define MINLEN 40
108 #else
109 #define MINLEN 4000
110 #endif
111
112 /*
113  * Input I/O callback sets
114  */
115 typedef struct _xmlInputCallback {
116     xmlInputMatchCallback matchcallback;
117     xmlInputOpenCallback opencallback;
118     xmlInputReadCallback readcallback;
119     xmlInputCloseCallback closecallback;
120 } xmlInputCallback;
121
122 #define MAX_INPUT_CALLBACK 15
123
124 static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK];
125 static int xmlInputCallbackNr = 0;
126 static int xmlInputCallbackInitialized = 0;
127
128 #ifdef LIBXML_OUTPUT_ENABLED
129 /*
130  * Output I/O callback sets
131  */
132 typedef struct _xmlOutputCallback {
133     xmlOutputMatchCallback matchcallback;
134     xmlOutputOpenCallback opencallback;
135     xmlOutputWriteCallback writecallback;
136     xmlOutputCloseCallback closecallback;
137 } xmlOutputCallback;
138
139 #define MAX_OUTPUT_CALLBACK 15
140
141 static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK];
142 static int xmlOutputCallbackNr = 0;
143 static int xmlOutputCallbackInitialized = 0;
144
145 xmlOutputBufferPtr
146 xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder);
147 #endif /* LIBXML_OUTPUT_ENABLED */
148
149 /************************************************************************
150  *                                                                      *
151  *              Tree memory error handler                               *
152  *                                                                      *
153  ************************************************************************/
154
155 static const char *IOerr[] = {
156     "Unknown IO error",         /* UNKNOWN */
157     "Permission denied",        /* EACCES */
158     "Resource temporarily unavailable",/* EAGAIN */
159     "Bad file descriptor",      /* EBADF */
160     "Bad message",              /* EBADMSG */
161     "Resource busy",            /* EBUSY */
162     "Operation canceled",       /* ECANCELED */
163     "No child processes",       /* ECHILD */
164     "Resource deadlock avoided",/* EDEADLK */
165     "Domain error",             /* EDOM */
166     "File exists",              /* EEXIST */
167     "Bad address",              /* EFAULT */
168     "File too large",           /* EFBIG */
169     "Operation in progress",    /* EINPROGRESS */
170     "Interrupted function call",/* EINTR */
171     "Invalid argument",         /* EINVAL */
172     "Input/output error",       /* EIO */
173     "Is a directory",           /* EISDIR */
174     "Too many open files",      /* EMFILE */
175     "Too many links",           /* EMLINK */
176     "Inappropriate message buffer length",/* EMSGSIZE */
177     "Filename too long",        /* ENAMETOOLONG */
178     "Too many open files in system",/* ENFILE */
179     "No such device",           /* ENODEV */
180     "No such file or directory",/* ENOENT */
181     "Exec format error",        /* ENOEXEC */
182     "No locks available",       /* ENOLCK */
183     "Not enough space",         /* ENOMEM */
184     "No space left on device",  /* ENOSPC */
185     "Function not implemented", /* ENOSYS */
186     "Not a directory",          /* ENOTDIR */
187     "Directory not empty",      /* ENOTEMPTY */
188     "Not supported",            /* ENOTSUP */
189     "Inappropriate I/O control operation",/* ENOTTY */
190     "No such device or address",/* ENXIO */
191     "Operation not permitted",  /* EPERM */
192     "Broken pipe",              /* EPIPE */
193     "Result too large",         /* ERANGE */
194     "Read-only file system",    /* EROFS */
195     "Invalid seek",             /* ESPIPE */
196     "No such process",          /* ESRCH */
197     "Operation timed out",      /* ETIMEDOUT */
198     "Improper link",            /* EXDEV */
199     "Attempt to load network entity %s", /* XML_IO_NETWORK_ATTEMPT */
200     "encoder error",            /* XML_IO_ENCODER */
201     "flush error",
202     "write error",
203     "no input",
204     "buffer full",
205     "loading error",
206     "not a socket",             /* ENOTSOCK */
207     "already connected",        /* EISCONN */
208     "connection refused",       /* ECONNREFUSED */
209     "unreachable network",      /* ENETUNREACH */
210     "adddress in use",          /* EADDRINUSE */
211     "already in use",           /* EALREADY */
212     "unknown address familly",  /* EAFNOSUPPORT */
213 };
214
215 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
216 /**
217  * __xmlIOWin32UTF8ToWChar:
218  * @u8String:  uft-8 string
219  *
220  * Convert a string from utf-8 to wchar (WINDOWS ONLY!)
221  */
222 static wchar_t *
223 __xmlIOWin32UTF8ToWChar(const char *u8String)
224 {
225     wchar_t *wString = NULL;
226
227     if (u8String) {
228         int wLen =
229             MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u8String,
230                                 -1, NULL, 0);
231         if (wLen) {
232             wString = xmlMalloc(wLen * sizeof(wchar_t));
233             if (wString) {
234                 if (MultiByteToWideChar
235                     (CP_UTF8, 0, u8String, -1, wString, wLen) == 0) {
236                     xmlFree(wString);
237                     wString = NULL;
238                 }
239             }
240         }
241     }
242
243     return wString;
244 }
245 #endif
246
247 /**
248  * xmlIOErrMemory:
249  * @extra:  extra informations
250  *
251  * Handle an out of memory condition
252  */
253 static void
254 xmlIOErrMemory(const char *extra)
255 {
256     __xmlSimpleError(XML_FROM_IO, XML_ERR_NO_MEMORY, NULL, NULL, extra);
257 }
258
259 /**
260  * __xmlIOErr:
261  * @code:  the error number
262  * @
263  * @extra:  extra informations
264  *
265  * Handle an I/O error
266  */
267 void
268 __xmlIOErr(int domain, int code, const char *extra)
269 {
270     unsigned int idx;
271
272     if (code == 0) {
273 #ifdef HAVE_ERRNO_H
274         if (errno == 0) code = 0;
275 #ifdef EACCES
276         else if (errno == EACCES) code = XML_IO_EACCES;
277 #endif
278 #ifdef EAGAIN
279         else if (errno == EAGAIN) code = XML_IO_EAGAIN;
280 #endif
281 #ifdef EBADF
282         else if (errno == EBADF) code = XML_IO_EBADF;
283 #endif
284 #ifdef EBADMSG
285         else if (errno == EBADMSG) code = XML_IO_EBADMSG;
286 #endif
287 #ifdef EBUSY
288         else if (errno == EBUSY) code = XML_IO_EBUSY;
289 #endif
290 #ifdef ECANCELED
291         else if (errno == ECANCELED) code = XML_IO_ECANCELED;
292 #endif
293 #ifdef ECHILD
294         else if (errno == ECHILD) code = XML_IO_ECHILD;
295 #endif
296 #ifdef EDEADLK
297         else if (errno == EDEADLK) code = XML_IO_EDEADLK;
298 #endif
299 #ifdef EDOM
300         else if (errno == EDOM) code = XML_IO_EDOM;
301 #endif
302 #ifdef EEXIST
303         else if (errno == EEXIST) code = XML_IO_EEXIST;
304 #endif
305 #ifdef EFAULT
306         else if (errno == EFAULT) code = XML_IO_EFAULT;
307 #endif
308 #ifdef EFBIG
309         else if (errno == EFBIG) code = XML_IO_EFBIG;
310 #endif
311 #ifdef EINPROGRESS
312         else if (errno == EINPROGRESS) code = XML_IO_EINPROGRESS;
313 #endif
314 #ifdef EINTR
315         else if (errno == EINTR) code = XML_IO_EINTR;
316 #endif
317 #ifdef EINVAL
318         else if (errno == EINVAL) code = XML_IO_EINVAL;
319 #endif
320 #ifdef EIO
321         else if (errno == EIO) code = XML_IO_EIO;
322 #endif
323 #ifdef EISDIR
324         else if (errno == EISDIR) code = XML_IO_EISDIR;
325 #endif
326 #ifdef EMFILE
327         else if (errno == EMFILE) code = XML_IO_EMFILE;
328 #endif
329 #ifdef EMLINK
330         else if (errno == EMLINK) code = XML_IO_EMLINK;
331 #endif
332 #ifdef EMSGSIZE
333         else if (errno == EMSGSIZE) code = XML_IO_EMSGSIZE;
334 #endif
335 #ifdef ENAMETOOLONG
336         else if (errno == ENAMETOOLONG) code = XML_IO_ENAMETOOLONG;
337 #endif
338 #ifdef ENFILE
339         else if (errno == ENFILE) code = XML_IO_ENFILE;
340 #endif
341 #ifdef ENODEV
342         else if (errno == ENODEV) code = XML_IO_ENODEV;
343 #endif
344 #ifdef ENOENT
345         else if (errno == ENOENT) code = XML_IO_ENOENT;
346 #endif
347 #ifdef ENOEXEC
348         else if (errno == ENOEXEC) code = XML_IO_ENOEXEC;
349 #endif
350 #ifdef ENOLCK
351         else if (errno == ENOLCK) code = XML_IO_ENOLCK;
352 #endif
353 #ifdef ENOMEM
354         else if (errno == ENOMEM) code = XML_IO_ENOMEM;
355 #endif
356 #ifdef ENOSPC
357         else if (errno == ENOSPC) code = XML_IO_ENOSPC;
358 #endif
359 #ifdef ENOSYS
360         else if (errno == ENOSYS) code = XML_IO_ENOSYS;
361 #endif
362 #ifdef ENOTDIR
363         else if (errno == ENOTDIR) code = XML_IO_ENOTDIR;
364 #endif
365 #ifdef ENOTEMPTY
366         else if (errno == ENOTEMPTY) code = XML_IO_ENOTEMPTY;
367 #endif
368 #ifdef ENOTSUP
369         else if (errno == ENOTSUP) code = XML_IO_ENOTSUP;
370 #endif
371 #ifdef ENOTTY
372         else if (errno == ENOTTY) code = XML_IO_ENOTTY;
373 #endif
374 #ifdef ENXIO
375         else if (errno == ENXIO) code = XML_IO_ENXIO;
376 #endif
377 #ifdef EPERM
378         else if (errno == EPERM) code = XML_IO_EPERM;
379 #endif
380 #ifdef EPIPE
381         else if (errno == EPIPE) code = XML_IO_EPIPE;
382 #endif
383 #ifdef ERANGE
384         else if (errno == ERANGE) code = XML_IO_ERANGE;
385 #endif
386 #ifdef EROFS
387         else if (errno == EROFS) code = XML_IO_EROFS;
388 #endif
389 #ifdef ESPIPE
390         else if (errno == ESPIPE) code = XML_IO_ESPIPE;
391 #endif
392 #ifdef ESRCH
393         else if (errno == ESRCH) code = XML_IO_ESRCH;
394 #endif
395 #ifdef ETIMEDOUT
396         else if (errno == ETIMEDOUT) code = XML_IO_ETIMEDOUT;
397 #endif
398 #ifdef EXDEV
399         else if (errno == EXDEV) code = XML_IO_EXDEV;
400 #endif
401 #ifdef ENOTSOCK
402         else if (errno == ENOTSOCK) code = XML_IO_ENOTSOCK;
403 #endif
404 #ifdef EISCONN
405         else if (errno == EISCONN) code = XML_IO_EISCONN;
406 #endif
407 #ifdef ECONNREFUSED
408         else if (errno == ECONNREFUSED) code = XML_IO_ECONNREFUSED;
409 #endif
410 #ifdef ETIMEDOUT
411         else if (errno == ETIMEDOUT) code = XML_IO_ETIMEDOUT;
412 #endif
413 #ifdef ENETUNREACH
414         else if (errno == ENETUNREACH) code = XML_IO_ENETUNREACH;
415 #endif
416 #ifdef EADDRINUSE
417         else if (errno == EADDRINUSE) code = XML_IO_EADDRINUSE;
418 #endif
419 #ifdef EINPROGRESS
420         else if (errno == EINPROGRESS) code = XML_IO_EINPROGRESS;
421 #endif
422 #ifdef EALREADY
423         else if (errno == EALREADY) code = XML_IO_EALREADY;
424 #endif
425 #ifdef EAFNOSUPPORT
426         else if (errno == EAFNOSUPPORT) code = XML_IO_EAFNOSUPPORT;
427 #endif
428         else code = XML_IO_UNKNOWN;
429 #endif /* HAVE_ERRNO_H */
430     }
431     idx = 0;
432     if (code >= XML_IO_UNKNOWN) idx = code - XML_IO_UNKNOWN;
433     if (idx >= (sizeof(IOerr) / sizeof(IOerr[0]))) idx = 0;
434
435     __xmlSimpleError(domain, code, NULL, IOerr[idx], extra);
436 }
437
438 /**
439  * xmlIOErr:
440  * @code:  the error number
441  * @extra:  extra informations
442  *
443  * Handle an I/O error
444  */
445 static void
446 xmlIOErr(int code, const char *extra)
447 {
448     __xmlIOErr(XML_FROM_IO, code, extra);
449 }
450
451 /**
452  * __xmlLoaderErr:
453  * @ctx: the parser context
454  * @extra:  extra informations
455  *
456  * Handle a resource access error
457  */
458 void
459 __xmlLoaderErr(void *ctx, const char *msg, const char *filename)
460 {
461     xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
462     xmlStructuredErrorFunc schannel = NULL;
463     xmlGenericErrorFunc channel = NULL;
464     void *data = NULL;
465     xmlErrorLevel level = XML_ERR_ERROR;
466
467     if ((ctxt != NULL) && (ctxt->disableSAX != 0) &&
468         (ctxt->instate == XML_PARSER_EOF))
469         return;
470     if ((ctxt != NULL) && (ctxt->sax != NULL)) {
471         if (ctxt->validate) {
472             channel = ctxt->sax->error;
473             level = XML_ERR_ERROR;
474         } else {
475             channel = ctxt->sax->warning;
476             level = XML_ERR_WARNING;
477         }
478         if (ctxt->sax->initialized == XML_SAX2_MAGIC)
479             schannel = ctxt->sax->serror;
480         data = ctxt->userData;
481     }
482     __xmlRaiseError(schannel, channel, data, ctxt, NULL, XML_FROM_IO,
483                     XML_IO_LOAD_ERROR, level, NULL, 0,
484                     filename, NULL, NULL, 0, 0,
485                     msg, filename);
486
487 }
488
489 /************************************************************************
490  *                                                                      *
491  *              Tree memory error handler                               *
492  *                                                                      *
493  ************************************************************************/
494 /**
495  * xmlNormalizeWindowsPath:
496  * @path: the input file path
497  *
498  * This function is obsolete. Please see xmlURIFromPath in uri.c for
499  * a better solution.
500  *
501  * Returns a canonicalized version of the path
502  */
503 xmlChar *
504 xmlNormalizeWindowsPath(const xmlChar *path)
505 {
506     return xmlCanonicPath(path);
507 }
508
509 /**
510  * xmlCleanupInputCallbacks:
511  *
512  * clears the entire input callback table. this includes the
513  * compiled-in I/O.
514  */
515 void
516 xmlCleanupInputCallbacks(void)
517 {
518     int i;
519
520     if (!xmlInputCallbackInitialized)
521         return;
522
523     for (i = xmlInputCallbackNr - 1; i >= 0; i--) {
524         xmlInputCallbackTable[i].matchcallback = NULL;
525         xmlInputCallbackTable[i].opencallback = NULL;
526         xmlInputCallbackTable[i].readcallback = NULL;
527         xmlInputCallbackTable[i].closecallback = NULL;
528     }
529
530     xmlInputCallbackNr = 0;
531     xmlInputCallbackInitialized = 0;
532 }
533
534 /**
535  * xmlPopInputCallbacks:
536  *
537  * Clear the top input callback from the input stack. this includes the
538  * compiled-in I/O.
539  *
540  * Returns the number of input callback registered or -1 in case of error.
541  */
542 int
543 xmlPopInputCallbacks(void)
544 {
545     if (!xmlInputCallbackInitialized)
546         return(-1);
547
548     if (xmlInputCallbackNr <= 0)
549         return(-1);
550
551     xmlInputCallbackNr--;
552     xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = NULL;
553     xmlInputCallbackTable[xmlInputCallbackNr].opencallback = NULL;
554     xmlInputCallbackTable[xmlInputCallbackNr].readcallback = NULL;
555     xmlInputCallbackTable[xmlInputCallbackNr].closecallback = NULL;
556
557     return(xmlInputCallbackNr);
558 }
559
560 #ifdef LIBXML_OUTPUT_ENABLED
561 /**
562  * xmlCleanupOutputCallbacks:
563  *
564  * clears the entire output callback table. this includes the
565  * compiled-in I/O callbacks.
566  */
567 void
568 xmlCleanupOutputCallbacks(void)
569 {
570     int i;
571
572     if (!xmlOutputCallbackInitialized)
573         return;
574
575     for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
576         xmlOutputCallbackTable[i].matchcallback = NULL;
577         xmlOutputCallbackTable[i].opencallback = NULL;
578         xmlOutputCallbackTable[i].writecallback = NULL;
579         xmlOutputCallbackTable[i].closecallback = NULL;
580     }
581
582     xmlOutputCallbackNr = 0;
583     xmlOutputCallbackInitialized = 0;
584 }
585 #endif /* LIBXML_OUTPUT_ENABLED */
586
587 /************************************************************************
588  *                                                                      *
589  *              Standard I/O for file accesses                          *
590  *                                                                      *
591  ************************************************************************/
592
593 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
594
595 /**
596  *  xmlWrapOpenUtf8:
597  * @path:  the path in utf-8 encoding
598  * @mode:  type of access (0 - read, 1 - write)
599  *
600  * function opens the file specified by @path
601  *
602  */
603 static FILE*
604 xmlWrapOpenUtf8(const char *path,int mode)
605 {
606     FILE *fd = NULL;
607     wchar_t *wPath;
608
609     wPath = __xmlIOWin32UTF8ToWChar(path);
610     if(wPath)
611     {
612        fd = _wfopen(wPath, mode ? L"wb" : L"rb");
613        xmlFree(wPath);
614     }
615     /* maybe path in native encoding */
616     if(fd == NULL)
617        fd = fopen(path, mode ? "wb" : "rb");
618
619     return fd;
620 }
621
622 #ifdef HAVE_ZLIB_H
623 static gzFile
624 xmlWrapGzOpenUtf8(const char *path, const char *mode)
625 {
626     gzFile fd;
627     wchar_t *wPath;
628
629     fd = gzopen (path, mode);
630     if (fd)
631         return fd;
632
633     wPath = __xmlIOWin32UTF8ToWChar(path);
634     if(wPath)
635     {
636         int d, m = (strstr(mode, "r") ? O_RDONLY : O_RDWR);
637 #ifdef _O_BINARY
638         m |= (strstr(mode, "b") ? _O_BINARY : 0);
639 #endif
640         d = _wopen(wPath, m);
641         if (d >= 0)
642             fd = gzdopen(d, mode);
643         xmlFree(wPath);
644     }
645
646     return fd;
647 }
648 #endif
649
650 /**
651  *  xmlWrapStatUtf8:
652  * @path:  the path in utf-8 encoding
653  * @info:  structure that stores results
654  *
655  * function obtains information about the file or directory
656  *
657  */
658 static int
659 xmlWrapStatUtf8(const char *path,struct stat *info)
660 {
661 #ifdef HAVE_STAT
662     int retval = -1;
663     wchar_t *wPath;
664
665     wPath = __xmlIOWin32UTF8ToWChar(path);
666     if (wPath)
667     {
668        retval = _wstat(wPath,info);
669        xmlFree(wPath);
670     }
671     /* maybe path in native encoding */
672     if(retval < 0)
673        retval = stat(path,info);
674     return retval;
675 #else
676     return -1;
677 #endif
678 }
679
680 /**
681  *  xmlWrapOpenNative:
682  * @path:  the path
683  * @mode:  type of access (0 - read, 1 - write)
684  *
685  * function opens the file specified by @path
686  *
687  */
688 static FILE*
689 xmlWrapOpenNative(const char *path,int mode)
690 {
691     return fopen(path,mode ? "wb" : "rb");
692 }
693
694 /**
695  *  xmlWrapStatNative:
696  * @path:  the path
697  * @info:  structure that stores results
698  *
699  * function obtains information about the file or directory
700  *
701  */
702 static int
703 xmlWrapStatNative(const char *path,struct stat *info)
704 {
705 #ifdef HAVE_STAT
706     return stat(path,info);
707 #else
708     return -1;
709 #endif
710 }
711
712 typedef int (* xmlWrapStatFunc) (const char *f, struct stat *s);
713 static xmlWrapStatFunc xmlWrapStat = xmlWrapStatNative;
714 typedef FILE* (* xmlWrapOpenFunc)(const char *f,int mode);
715 static xmlWrapOpenFunc xmlWrapOpen = xmlWrapOpenNative;
716 #ifdef HAVE_ZLIB_H
717 typedef gzFile (* xmlWrapGzOpenFunc) (const char *f, const char *mode);
718 static xmlWrapGzOpenFunc xmlWrapGzOpen = gzopen;
719 #endif
720 /**
721  * xmlInitPlatformSpecificIo:
722  *
723  * Initialize platform specific features.
724  */
725 static void
726 xmlInitPlatformSpecificIo(void)
727 {
728     static int xmlPlatformIoInitialized = 0;
729     OSVERSIONINFO osvi;
730
731     if(xmlPlatformIoInitialized)
732       return;
733
734     osvi.dwOSVersionInfoSize = sizeof(osvi);
735
736     if(GetVersionEx(&osvi) && (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)) {
737       xmlWrapStat = xmlWrapStatUtf8;
738       xmlWrapOpen = xmlWrapOpenUtf8;
739 #ifdef HAVE_ZLIB_H
740       xmlWrapGzOpen = xmlWrapGzOpenUtf8;
741 #endif
742     } else {
743       xmlWrapStat = xmlWrapStatNative;
744       xmlWrapOpen = xmlWrapOpenNative;
745 #ifdef HAVE_ZLIB_H
746       xmlWrapGzOpen = gzopen;
747 #endif
748     }
749
750     xmlPlatformIoInitialized = 1;
751     return;
752 }
753
754 #endif
755
756 /**
757  * xmlCheckFilename:
758  * @path:  the path to check
759  *
760  * function checks to see if @path is a valid source
761  * (file, socket...) for XML.
762  *
763  * if stat is not available on the target machine,
764  * returns 1.  if stat fails, returns 0 (if calling
765  * stat on the filename fails, it can't be right).
766  * if stat succeeds and the file is a directory,
767  * returns 2.  otherwise returns 1.
768  */
769
770 int
771 xmlCheckFilename (const char *path)
772 {
773 #ifdef HAVE_STAT
774     struct stat stat_buffer;
775 #endif
776     if (path == NULL)
777         return(0);
778
779 #ifdef HAVE_STAT
780 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
781     /*
782      * On Windows stat and wstat do not work with long pathname,
783      * which start with '\\?\'
784      */
785     if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') &&
786         (path[3] == '\\') )
787             return 1;
788
789     if (xmlWrapStat(path, &stat_buffer) == -1)
790         return 0;
791 #else
792     if (stat(path, &stat_buffer) == -1)
793         return 0;
794 #endif
795 #ifdef S_ISDIR
796     if (S_ISDIR(stat_buffer.st_mode))
797         return 2;
798 #endif
799 #endif /* HAVE_STAT */
800     return 1;
801 }
802
803 /**
804  * xmlNop:
805  *
806  * No Operation function, does nothing, no input
807  *
808  * Returns zero
809  */
810 int
811 xmlNop(void) {
812     return(0);
813 }
814
815 /**
816  * xmlFdRead:
817  * @context:  the I/O context
818  * @buffer:  where to drop data
819  * @len:  number of bytes to read
820  *
821  * Read @len bytes to @buffer from the I/O channel.
822  *
823  * Returns the number of bytes written
824  */
825 static int
826 xmlFdRead (void * context, char * buffer, int len) {
827     int ret;
828
829     ret = read((int) (long) context, &buffer[0], len);
830     if (ret < 0) xmlIOErr(0, "read()");
831     return(ret);
832 }
833
834 #ifdef LIBXML_OUTPUT_ENABLED
835 /**
836  * xmlFdWrite:
837  * @context:  the I/O context
838  * @buffer:  where to get data
839  * @len:  number of bytes to write
840  *
841  * Write @len bytes from @buffer to the I/O channel.
842  *
843  * Returns the number of bytes written
844  */
845 static int
846 xmlFdWrite (void * context, const char * buffer, int len) {
847     int ret = 0;
848
849     if (len > 0) {
850         ret = write((int) (long) context, &buffer[0], len);
851         if (ret < 0) xmlIOErr(0, "write()");
852     }
853     return(ret);
854 }
855 #endif /* LIBXML_OUTPUT_ENABLED */
856
857 /**
858  * xmlFdClose:
859  * @context:  the I/O context
860  *
861  * Close an I/O channel
862  *
863  * Returns 0 in case of success and error code otherwise
864  */
865 static int
866 xmlFdClose (void * context) {
867     int ret;
868     ret = close((int) (long) context);
869     if (ret < 0) xmlIOErr(0, "close()");
870     return(ret);
871 }
872
873 /**
874  * xmlFileMatch:
875  * @filename:  the URI for matching
876  *
877  * input from FILE *
878  *
879  * Returns 1 if matches, 0 otherwise
880  */
881 int
882 xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
883     return(1);
884 }
885
886 /**
887  * xmlFileOpen_real:
888  * @filename:  the URI for matching
889  *
890  * input from FILE *, supports compressed input
891  * if @filename is " " then the standard input is used
892  *
893  * Returns an I/O context or NULL in case of error
894  */
895 static void *
896 xmlFileOpen_real (const char *filename) {
897     const char *path = filename;
898     FILE *fd;
899
900     if (filename == NULL)
901         return(NULL);
902
903     if (!strcmp(filename, "-")) {
904         fd = stdin;
905         return((void *) fd);
906     }
907
908     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) {
909 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
910         path = &filename[17];
911 #else
912         path = &filename[16];
913 #endif
914     } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
915 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
916         path = &filename[8];
917 #else
918         path = &filename[7];
919 #endif
920     } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:/", 6)) {
921         /* lots of generators seems to lazy to read RFC 1738 */
922 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
923         path = &filename[6];
924 #else
925         path = &filename[5];
926 #endif
927     }
928
929     /* Do not check DDNAME on zOS ! */
930 #if !defined(__MVS__)
931     if (!xmlCheckFilename(path))
932         return(NULL);
933 #endif
934
935 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
936     fd = xmlWrapOpen(path, 0);
937 #else
938     fd = fopen(path, "r");
939 #endif /* WIN32 */
940     if (fd == NULL) xmlIOErr(0, path);
941     return((void *) fd);
942 }
943
944 /**
945  * xmlFileOpen:
946  * @filename:  the URI for matching
947  *
948  * Wrapper around xmlFileOpen_real that try it with an unescaped
949  * version of @filename, if this fails fallback to @filename
950  *
951  * Returns a handler or NULL in case or failure
952  */
953 void *
954 xmlFileOpen (const char *filename) {
955     char *unescaped;
956     void *retval;
957
958     retval = xmlFileOpen_real(filename);
959     if (retval == NULL) {
960         unescaped = xmlURIUnescapeString(filename, 0, NULL);
961         if (unescaped != NULL) {
962             retval = xmlFileOpen_real(unescaped);
963             xmlFree(unescaped);
964         }
965     }
966
967     return retval;
968 }
969
970 #ifdef LIBXML_OUTPUT_ENABLED
971 /**
972  * xmlFileOpenW:
973  * @filename:  the URI for matching
974  *
975  * output to from FILE *,
976  * if @filename is "-" then the standard output is used
977  *
978  * Returns an I/O context or NULL in case of error
979  */
980 static void *
981 xmlFileOpenW (const char *filename) {
982     const char *path = NULL;
983     FILE *fd;
984
985     if (!strcmp(filename, "-")) {
986         fd = stdout;
987         return((void *) fd);
988     }
989
990     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
991 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
992         path = &filename[17];
993 #else
994         path = &filename[16];
995 #endif
996     else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
997 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
998         path = &filename[8];
999 #else
1000         path = &filename[7];
1001 #endif
1002     } else
1003         path = filename;
1004
1005     if (path == NULL)
1006         return(NULL);
1007
1008 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
1009     fd = xmlWrapOpen(path, 1);
1010 #elif(__MVS__)
1011     fd = fopen(path, "w");
1012 #else
1013     fd = fopen(path, "wb");
1014 #endif /* WIN32 */
1015
1016     if (fd == NULL) xmlIOErr(0, path);
1017     return((void *) fd);
1018 }
1019 #endif /* LIBXML_OUTPUT_ENABLED */
1020
1021 /**
1022  * xmlFileRead:
1023  * @context:  the I/O context
1024  * @buffer:  where to drop data
1025  * @len:  number of bytes to write
1026  *
1027  * Read @len bytes to @buffer from the I/O channel.
1028  *
1029  * Returns the number of bytes written or < 0 in case of failure
1030  */
1031 int
1032 xmlFileRead (void * context, char * buffer, int len) {
1033     int ret;
1034     if ((context == NULL) || (buffer == NULL))
1035         return(-1);
1036     ret = fread(&buffer[0], 1,  len, (FILE *) context);
1037     if (ret < 0) xmlIOErr(0, "fread()");
1038     return(ret);
1039 }
1040
1041 #ifdef LIBXML_OUTPUT_ENABLED
1042 /**
1043  * xmlFileWrite:
1044  * @context:  the I/O context
1045  * @buffer:  where to drop data
1046  * @len:  number of bytes to write
1047  *
1048  * Write @len bytes from @buffer to the I/O channel.
1049  *
1050  * Returns the number of bytes written
1051  */
1052 static int
1053 xmlFileWrite (void * context, const char * buffer, int len) {
1054     int items;
1055
1056     if ((context == NULL) || (buffer == NULL))
1057         return(-1);
1058     items = fwrite(&buffer[0], len, 1, (FILE *) context);
1059     if ((items == 0) && (ferror((FILE *) context))) {
1060         xmlIOErr(0, "fwrite()");
1061         return(-1);
1062     }
1063     return(items * len);
1064 }
1065 #endif /* LIBXML_OUTPUT_ENABLED */
1066
1067 /**
1068  * xmlFileClose:
1069  * @context:  the I/O context
1070  *
1071  * Close an I/O channel
1072  *
1073  * Returns 0 or -1 in case of error
1074  */
1075 int
1076 xmlFileClose (void * context) {
1077     FILE *fil;
1078     int ret;
1079
1080     if (context == NULL)
1081         return(-1);
1082     fil = (FILE *) context;
1083     if ((fil == stdout) || (fil == stderr)) {
1084         ret = fflush(fil);
1085         if (ret < 0)
1086             xmlIOErr(0, "fflush()");
1087         return(0);
1088     }
1089     if (fil == stdin)
1090         return(0);
1091     ret = ( fclose((FILE *) context) == EOF ) ? -1 : 0;
1092     if (ret < 0)
1093         xmlIOErr(0, "fclose()");
1094     return(ret);
1095 }
1096
1097 /**
1098  * xmlFileFlush:
1099  * @context:  the I/O context
1100  *
1101  * Flush an I/O channel
1102  */
1103 static int
1104 xmlFileFlush (void * context) {
1105     int ret;
1106
1107     if (context == NULL)
1108         return(-1);
1109     ret = ( fflush((FILE *) context) == EOF ) ? -1 : 0;
1110     if (ret < 0)
1111         xmlIOErr(0, "fflush()");
1112     return(ret);
1113 }
1114
1115 #ifdef LIBXML_OUTPUT_ENABLED
1116 /**
1117  * xmlBufferWrite:
1118  * @context:  the xmlBuffer
1119  * @buffer:  the data to write
1120  * @len:  number of bytes to write
1121  *
1122  * Write @len bytes from @buffer to the xml buffer
1123  *
1124  * Returns the number of bytes written
1125  */
1126 static int
1127 xmlBufferWrite (void * context, const char * buffer, int len) {
1128     int ret;
1129
1130     ret = xmlBufferAdd((xmlBufferPtr) context, (const xmlChar *) buffer, len);
1131     if (ret != 0)
1132         return(-1);
1133     return(len);
1134 }
1135 #endif
1136
1137 #ifdef HAVE_ZLIB_H
1138 /************************************************************************
1139  *                                                                      *
1140  *              I/O for compressed file accesses                        *
1141  *                                                                      *
1142  ************************************************************************/
1143 /**
1144  * xmlGzfileMatch:
1145  * @filename:  the URI for matching
1146  *
1147  * input from compressed file test
1148  *
1149  * Returns 1 if matches, 0 otherwise
1150  */
1151 static int
1152 xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
1153     return(1);
1154 }
1155
1156 /**
1157  * xmlGzfileOpen_real:
1158  * @filename:  the URI for matching
1159  *
1160  * input from compressed file open
1161  * if @filename is " " then the standard input is used
1162  *
1163  * Returns an I/O context or NULL in case of error
1164  */
1165 static void *
1166 xmlGzfileOpen_real (const char *filename) {
1167     const char *path = NULL;
1168     gzFile fd;
1169
1170     if (!strcmp(filename, "-")) {
1171         int duped_fd = dup(fileno(stdin));
1172         fd = gzdopen(duped_fd, "rb");
1173         if (fd == Z_NULL && duped_fd >= 0) {
1174             close(duped_fd);  /* gzdOpen() does not close on failure */
1175         }
1176
1177         return((void *) fd);
1178     }
1179
1180     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
1181 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
1182         path = &filename[17];
1183 #else
1184         path = &filename[16];
1185 #endif
1186     else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
1187 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
1188         path = &filename[8];
1189 #else
1190         path = &filename[7];
1191 #endif
1192     } else
1193         path = filename;
1194
1195     if (path == NULL)
1196         return(NULL);
1197     if (!xmlCheckFilename(path))
1198         return(NULL);
1199
1200 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
1201     fd = xmlWrapGzOpen(path, "rb");
1202 #else
1203     fd = gzopen(path, "rb");
1204 #endif
1205     return((void *) fd);
1206 }
1207
1208 /**
1209  * xmlGzfileOpen:
1210  * @filename:  the URI for matching
1211  *
1212  * Wrapper around xmlGzfileOpen if the open fais, it will
1213  * try to unescape @filename
1214  */
1215 static void *
1216 xmlGzfileOpen (const char *filename) {
1217     char *unescaped;
1218     void *retval;
1219
1220     retval = xmlGzfileOpen_real(filename);
1221     if (retval == NULL) {
1222         unescaped = xmlURIUnescapeString(filename, 0, NULL);
1223         if (unescaped != NULL) {
1224             retval = xmlGzfileOpen_real(unescaped);
1225         }
1226         xmlFree(unescaped);
1227     }
1228     return retval;
1229 }
1230
1231 #ifdef LIBXML_OUTPUT_ENABLED
1232 /**
1233  * xmlGzfileOpenW:
1234  * @filename:  the URI for matching
1235  * @compression:  the compression factor (0 - 9 included)
1236  *
1237  * input from compressed file open
1238  * if @filename is " " then the standard input is used
1239  *
1240  * Returns an I/O context or NULL in case of error
1241  */
1242 static void *
1243 xmlGzfileOpenW (const char *filename, int compression) {
1244     const char *path = NULL;
1245     char mode[15];
1246     gzFile fd;
1247
1248     snprintf(mode, sizeof(mode), "wb%d", compression);
1249     if (!strcmp(filename, "-")) {
1250         int duped_fd = dup(fileno(stdout));
1251         fd = gzdopen(duped_fd, "rb");
1252         if (fd == Z_NULL && duped_fd >= 0) {
1253             close(duped_fd);  /* gzdOpen() does not close on failure */
1254         }
1255
1256         return((void *) fd);
1257     }
1258
1259     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
1260 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
1261         path = &filename[17];
1262 #else
1263         path = &filename[16];
1264 #endif
1265     else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
1266 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
1267         path = &filename[8];
1268 #else
1269         path = &filename[7];
1270 #endif
1271     } else
1272         path = filename;
1273
1274     if (path == NULL)
1275         return(NULL);
1276
1277 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
1278     fd = xmlWrapGzOpen(path, mode);
1279 #else
1280     fd = gzopen(path, mode);
1281 #endif
1282     return((void *) fd);
1283 }
1284 #endif /* LIBXML_OUTPUT_ENABLED */
1285
1286 /**
1287  * xmlGzfileRead:
1288  * @context:  the I/O context
1289  * @buffer:  where to drop data
1290  * @len:  number of bytes to write
1291  *
1292  * Read @len bytes to @buffer from the compressed I/O channel.
1293  *
1294  * Returns the number of bytes read.
1295  */
1296 static int
1297 xmlGzfileRead (void * context, char * buffer, int len) {
1298     int ret;
1299
1300     ret = gzread((gzFile) context, &buffer[0], len);
1301     if (ret < 0) xmlIOErr(0, "gzread()");
1302     return(ret);
1303 }
1304
1305 #ifdef LIBXML_OUTPUT_ENABLED
1306 /**
1307  * xmlGzfileWrite:
1308  * @context:  the I/O context
1309  * @buffer:  where to drop data
1310  * @len:  number of bytes to write
1311  *
1312  * Write @len bytes from @buffer to the compressed I/O channel.
1313  *
1314  * Returns the number of bytes written
1315  */
1316 static int
1317 xmlGzfileWrite (void * context, const char * buffer, int len) {
1318     int ret;
1319
1320     ret = gzwrite((gzFile) context, (char *) &buffer[0], len);
1321     if (ret < 0) xmlIOErr(0, "gzwrite()");
1322     return(ret);
1323 }
1324 #endif /* LIBXML_OUTPUT_ENABLED */
1325
1326 /**
1327  * xmlGzfileClose:
1328  * @context:  the I/O context
1329  *
1330  * Close a compressed I/O channel
1331  */
1332 static int
1333 xmlGzfileClose (void * context) {
1334     int ret;
1335
1336     ret =  (gzclose((gzFile) context) == Z_OK ) ? 0 : -1;
1337     if (ret < 0) xmlIOErr(0, "gzclose()");
1338     return(ret);
1339 }
1340 #endif /* HAVE_ZLIB_H */
1341
1342 #ifdef LIBXML_LZMA_ENABLED
1343 /************************************************************************
1344  *                                                                      *
1345  *              I/O for compressed file accesses                        *
1346  *                                                                      *
1347  ************************************************************************/
1348 #include "xzlib.h"
1349 /**
1350  * xmlXzfileMatch:
1351  * @filename:  the URI for matching
1352  *
1353  * input from compressed file test
1354  *
1355  * Returns 1 if matches, 0 otherwise
1356  */
1357 static int
1358 xmlXzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
1359     return(1);
1360 }
1361
1362 /**
1363  * xmlXzFileOpen_real:
1364  * @filename:  the URI for matching
1365  *
1366  * input from compressed file open
1367  * if @filename is " " then the standard input is used
1368  *
1369  * Returns an I/O context or NULL in case of error
1370  */
1371 static void *
1372 xmlXzfileOpen_real (const char *filename) {
1373     const char *path = NULL;
1374     xzFile fd;
1375
1376     if (!strcmp(filename, "-")) {
1377         fd = __libxml2_xzdopen(dup(fileno(stdin)), "rb");
1378         return((void *) fd);
1379     }
1380
1381     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) {
1382         path = &filename[16];
1383     } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
1384         path = &filename[7];
1385     } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:/", 6)) {
1386         /* lots of generators seems to lazy to read RFC 1738 */
1387         path = &filename[5];
1388     } else
1389         path = filename;
1390
1391     if (path == NULL)
1392         return(NULL);
1393     if (!xmlCheckFilename(path))
1394         return(NULL);
1395
1396     fd = __libxml2_xzopen(path, "rb");
1397     return((void *) fd);
1398 }
1399
1400 /**
1401  * xmlXzfileOpen:
1402  * @filename:  the URI for matching
1403  *
1404  * Wrapper around xmlXzfileOpen_real that try it with an unescaped
1405  * version of @filename, if this fails fallback to @filename
1406  *
1407  * Returns a handler or NULL in case or failure
1408  */
1409 static void *
1410 xmlXzfileOpen (const char *filename) {
1411     char *unescaped;
1412     void *retval;
1413
1414     retval = xmlXzfileOpen_real(filename);
1415     if (retval == NULL) {
1416         unescaped = xmlURIUnescapeString(filename, 0, NULL);
1417         if (unescaped != NULL) {
1418             retval = xmlXzfileOpen_real(unescaped);
1419         }
1420         xmlFree(unescaped);
1421     }
1422
1423     return retval;
1424 }
1425
1426 /**
1427  * xmlXzfileRead:
1428  * @context:  the I/O context
1429  * @buffer:  where to drop data
1430  * @len:  number of bytes to write
1431  *
1432  * Read @len bytes to @buffer from the compressed I/O channel.
1433  *
1434  * Returns the number of bytes written
1435  */
1436 static int
1437 xmlXzfileRead (void * context, char * buffer, int len) {
1438     int ret;
1439
1440     ret = __libxml2_xzread((xzFile) context, &buffer[0], len);
1441     if (ret < 0) xmlIOErr(0, "xzread()");
1442     return(ret);
1443 }
1444
1445 /**
1446  * xmlXzfileClose:
1447  * @context:  the I/O context
1448  *
1449  * Close a compressed I/O channel
1450  */
1451 static int
1452 xmlXzfileClose (void * context) {
1453     int ret;
1454
1455     ret =  (__libxml2_xzclose((xzFile) context) == LZMA_OK ) ? 0 : -1;
1456     if (ret < 0) xmlIOErr(0, "xzclose()");
1457     return(ret);
1458 }
1459 #endif /* LIBXML_LZMA_ENABLED */
1460
1461 #ifdef LIBXML_HTTP_ENABLED
1462 /************************************************************************
1463  *                                                                      *
1464  *                      I/O for HTTP file accesses                      *
1465  *                                                                      *
1466  ************************************************************************/
1467
1468 #ifdef LIBXML_OUTPUT_ENABLED
1469 typedef struct xmlIOHTTPWriteCtxt_
1470 {
1471     int                 compression;
1472
1473     char *              uri;
1474
1475     void *              doc_buff;
1476
1477 } xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr;
1478
1479 #ifdef HAVE_ZLIB_H
1480
1481 #define DFLT_WBITS              ( -15 )
1482 #define DFLT_MEM_LVL            ( 8 )
1483 #define GZ_MAGIC1               ( 0x1f )
1484 #define GZ_MAGIC2               ( 0x8b )
1485 #define LXML_ZLIB_OS_CODE       ( 0x03 )
1486 #define INIT_HTTP_BUFF_SIZE     ( 32768 )
1487 #define DFLT_ZLIB_RATIO         ( 5 )
1488
1489 /*
1490 **  Data structure and functions to work with sending compressed data
1491 **  via HTTP.
1492 */
1493
1494 typedef struct xmlZMemBuff_
1495 {
1496    unsigned long        size;
1497    unsigned long        crc;
1498
1499    unsigned char *      zbuff;
1500    z_stream             zctrl;
1501
1502 } xmlZMemBuff, *xmlZMemBuffPtr;
1503
1504 /**
1505  * append_reverse_ulong
1506  * @buff:  Compressed memory buffer
1507  * @data:  Unsigned long to append
1508  *
1509  * Append a unsigned long in reverse byte order to the end of the
1510  * memory buffer.
1511  */
1512 static void
1513 append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) {
1514
1515     int         idx;
1516
1517     if ( buff == NULL )
1518         return;
1519
1520     /*
1521     **  This is plagiarized from putLong in gzio.c (zlib source) where
1522     **  the number "4" is hardcoded.  If zlib is ever patched to
1523     **  support 64 bit file sizes, this code would need to be patched
1524     **  as well.
1525     */
1526
1527     for ( idx = 0; idx < 4; idx++ ) {
1528         *buff->zctrl.next_out = ( data & 0xff );
1529         data >>= 8;
1530         buff->zctrl.next_out++;
1531     }
1532
1533     return;
1534 }
1535
1536 /**
1537  *
1538  * xmlFreeZMemBuff
1539  * @buff:  The memory buffer context to clear
1540  *
1541  * Release all the resources associated with the compressed memory buffer.
1542  */
1543 static void
1544 xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
1545
1546 #ifdef DEBUG_HTTP
1547     int z_err;
1548 #endif
1549
1550     if ( buff == NULL )
1551         return;
1552
1553     xmlFree( buff->zbuff );
1554 #ifdef DEBUG_HTTP
1555     z_err = deflateEnd( &buff->zctrl );
1556     if ( z_err != Z_OK )
1557         xmlGenericError( xmlGenericErrorContext,
1558                         "xmlFreeZMemBuff:  Error releasing zlib context:  %d\n",
1559                         z_err );
1560 #else
1561     deflateEnd( &buff->zctrl );
1562 #endif
1563
1564     xmlFree( buff );
1565     return;
1566 }
1567
1568 /**
1569  * xmlCreateZMemBuff
1570  *@compression: Compression value to use
1571  *
1572  * Create a memory buffer to hold the compressed XML document.  The
1573  * compressed document in memory will end up being identical to what
1574  * would be created if gzopen/gzwrite/gzclose were being used to
1575  * write the document to disk.  The code for the header/trailer data to
1576  * the compression is plagiarized from the zlib source files.
1577  */
1578 static void *
1579 xmlCreateZMemBuff( int compression ) {
1580
1581     int                 z_err;
1582     int                 hdr_lgth;
1583     xmlZMemBuffPtr      buff = NULL;
1584
1585     if ( ( compression < 1 ) || ( compression > 9 ) )
1586         return ( NULL );
1587
1588     /*  Create the control and data areas  */
1589
1590     buff = xmlMalloc( sizeof( xmlZMemBuff ) );
1591     if ( buff == NULL ) {
1592         xmlIOErrMemory("creating buffer context");
1593         return ( NULL );
1594     }
1595
1596     (void)memset( buff, 0, sizeof( xmlZMemBuff ) );
1597     buff->size = INIT_HTTP_BUFF_SIZE;
1598     buff->zbuff = xmlMalloc( buff->size );
1599     if ( buff->zbuff == NULL ) {
1600         xmlFreeZMemBuff( buff );
1601         xmlIOErrMemory("creating buffer");
1602         return ( NULL );
1603     }
1604
1605     z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
1606                             DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
1607     if ( z_err != Z_OK ) {
1608         xmlChar msg[500];
1609         xmlFreeZMemBuff( buff );
1610         buff = NULL;
1611         xmlStrPrintf(msg, 500,
1612                     "xmlCreateZMemBuff:  %s %d\n",
1613                     "Error initializing compression context.  ZLIB error:",
1614                     z_err );
1615         xmlIOErr(XML_IO_WRITE, (const char *) msg);
1616         return ( NULL );
1617     }
1618
1619     /*  Set the header data.  The CRC will be needed for the trailer  */
1620     buff->crc = crc32( 0L, NULL, 0 );
1621     hdr_lgth = snprintf( (char *)buff->zbuff, buff->size,
1622                         "%c%c%c%c%c%c%c%c%c%c",
1623                         GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED,
1624                         0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE );
1625     buff->zctrl.next_out  = buff->zbuff + hdr_lgth;
1626     buff->zctrl.avail_out = buff->size - hdr_lgth;
1627
1628     return ( buff );
1629 }
1630
1631 /**
1632  * xmlZMemBuffExtend
1633  * @buff:  Buffer used to compress and consolidate data.
1634  * @ext_amt:   Number of bytes to extend the buffer.
1635  *
1636  * Extend the internal buffer used to store the compressed data by the
1637  * specified amount.
1638  *
1639  * Returns 0 on success or -1 on failure to extend the buffer.  On failure
1640  * the original buffer still exists at the original size.
1641  */
1642 static int
1643 xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) {
1644
1645     int                 rc = -1;
1646     size_t              new_size;
1647     size_t              cur_used;
1648
1649     unsigned char *     tmp_ptr = NULL;
1650
1651     if ( buff == NULL )
1652         return ( -1 );
1653
1654     else if ( ext_amt == 0 )
1655         return ( 0 );
1656
1657     cur_used = buff->zctrl.next_out - buff->zbuff;
1658     new_size = buff->size + ext_amt;
1659
1660 #ifdef DEBUG_HTTP
1661     if ( cur_used > new_size )
1662         xmlGenericError( xmlGenericErrorContext,
1663                         "xmlZMemBuffExtend:  %s\n%s %d bytes.\n",
1664                         "Buffer overwrite detected during compressed memory",
1665                         "buffer extension.  Overflowed by",
1666                         (cur_used - new_size ) );
1667 #endif
1668
1669     tmp_ptr = xmlRealloc( buff->zbuff, new_size );
1670     if ( tmp_ptr != NULL ) {
1671         rc = 0;
1672         buff->size  = new_size;
1673         buff->zbuff = tmp_ptr;
1674         buff->zctrl.next_out  = tmp_ptr + cur_used;
1675         buff->zctrl.avail_out = new_size - cur_used;
1676     }
1677     else {
1678         xmlChar msg[500];
1679         xmlStrPrintf(msg, 500,
1680                     "xmlZMemBuffExtend:  %s %lu bytes.\n",
1681                     "Allocation failure extending output buffer to",
1682                     (unsigned long) new_size );
1683         xmlIOErr(XML_IO_WRITE, (const char *) msg);
1684     }
1685
1686     return ( rc );
1687 }
1688
1689 /**
1690  * xmlZMemBuffAppend
1691  * @buff:  Buffer used to compress and consolidate data
1692  * @src:   Uncompressed source content to append to buffer
1693  * @len:   Length of source data to append to buffer
1694  *
1695  * Compress and append data to the internal buffer.  The data buffer
1696  * will be expanded if needed to store the additional data.
1697  *
1698  * Returns the number of bytes appended to the buffer or -1 on error.
1699  */
1700 static int
1701 xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) {
1702
1703     int         z_err;
1704     size_t      min_accept;
1705
1706     if ( ( buff == NULL ) || ( src == NULL ) )
1707         return ( -1 );
1708
1709     buff->zctrl.avail_in = len;
1710     buff->zctrl.next_in  = (unsigned char *)src;
1711     while ( buff->zctrl.avail_in > 0 ) {
1712         /*
1713         **  Extend the buffer prior to deflate call if a reasonable amount
1714         **  of output buffer space is not available.
1715         */
1716         min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO;
1717         if ( buff->zctrl.avail_out <= min_accept ) {
1718             if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
1719                 return ( -1 );
1720         }
1721
1722         z_err = deflate( &buff->zctrl, Z_NO_FLUSH );
1723         if ( z_err != Z_OK ) {
1724             xmlChar msg[500];
1725             xmlStrPrintf(msg, 500,
1726                         "xmlZMemBuffAppend:  %s %d %s - %d",
1727                         "Compression error while appending",
1728                         len, "bytes to buffer.  ZLIB error", z_err );
1729             xmlIOErr(XML_IO_WRITE, (const char *) msg);
1730             return ( -1 );
1731         }
1732     }
1733
1734     buff->crc = crc32( buff->crc, (unsigned char *)src, len );
1735
1736     return ( len );
1737 }
1738
1739 /**
1740  * xmlZMemBuffGetContent
1741  * @buff:  Compressed memory content buffer
1742  * @data_ref:  Pointer reference to point to compressed content
1743  *
1744  * Flushes the compression buffers, appends gzip file trailers and
1745  * returns the compressed content and length of the compressed data.
1746  * NOTE:  The gzip trailer code here is plagiarized from zlib source.
1747  *
1748  * Returns the length of the compressed data or -1 on error.
1749  */
1750 static int
1751 xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) {
1752
1753     int         zlgth = -1;
1754     int         z_err;
1755
1756     if ( ( buff == NULL ) || ( data_ref == NULL ) )
1757         return ( -1 );
1758
1759     /*  Need to loop until compression output buffers are flushed  */
1760
1761     do
1762     {
1763         z_err = deflate( &buff->zctrl, Z_FINISH );
1764         if ( z_err == Z_OK ) {
1765             /*  In this case Z_OK means more buffer space needed  */
1766
1767             if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
1768                 return ( -1 );
1769         }
1770     }
1771     while ( z_err == Z_OK );
1772
1773     /*  If the compression state is not Z_STREAM_END, some error occurred  */
1774
1775     if ( z_err == Z_STREAM_END ) {
1776
1777         /*  Need to append the gzip data trailer  */
1778
1779         if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) {
1780             if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 )
1781                 return ( -1 );
1782         }
1783
1784         /*
1785         **  For whatever reason, the CRC and length data are pushed out
1786         **  in reverse byte order.  So a memcpy can't be used here.
1787         */
1788
1789         append_reverse_ulong( buff, buff->crc );
1790         append_reverse_ulong( buff, buff->zctrl.total_in );
1791
1792         zlgth = buff->zctrl.next_out - buff->zbuff;
1793         *data_ref = (char *)buff->zbuff;
1794     }
1795
1796     else {
1797         xmlChar msg[500];
1798         xmlStrPrintf(msg, 500,
1799                     "xmlZMemBuffGetContent:  %s - %d\n",
1800                     "Error flushing zlib buffers.  Error code", z_err );
1801         xmlIOErr(XML_IO_WRITE, (const char *) msg);
1802     }
1803
1804     return ( zlgth );
1805 }
1806 #endif /* LIBXML_OUTPUT_ENABLED */
1807 #endif  /*  HAVE_ZLIB_H  */
1808
1809 #ifdef LIBXML_OUTPUT_ENABLED
1810 /**
1811  * xmlFreeHTTPWriteCtxt
1812  * @ctxt:  Context to cleanup
1813  *
1814  * Free allocated memory and reclaim system resources.
1815  *
1816  * No return value.
1817  */
1818 static void
1819 xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
1820 {
1821     if ( ctxt->uri != NULL )
1822         xmlFree( ctxt->uri );
1823
1824     if ( ctxt->doc_buff != NULL ) {
1825
1826 #ifdef HAVE_ZLIB_H
1827         if ( ctxt->compression > 0 ) {
1828             xmlFreeZMemBuff( ctxt->doc_buff );
1829         }
1830         else
1831 #endif
1832         {
1833             xmlOutputBufferClose( ctxt->doc_buff );
1834         }
1835     }
1836
1837     xmlFree( ctxt );
1838     return;
1839 }
1840 #endif /* LIBXML_OUTPUT_ENABLED */
1841
1842
1843 /**
1844  * xmlIOHTTPMatch:
1845  * @filename:  the URI for matching
1846  *
1847  * check if the URI matches an HTTP one
1848  *
1849  * Returns 1 if matches, 0 otherwise
1850  */
1851 int
1852 xmlIOHTTPMatch (const char *filename) {
1853     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7))
1854         return(1);
1855     return(0);
1856 }
1857
1858 /**
1859  * xmlIOHTTPOpen:
1860  * @filename:  the URI for matching
1861  *
1862  * open an HTTP I/O channel
1863  *
1864  * Returns an I/O context or NULL in case of error
1865  */
1866 void *
1867 xmlIOHTTPOpen (const char *filename) {
1868     return(xmlNanoHTTPOpen(filename, NULL));
1869 }
1870
1871 #ifdef LIBXML_OUTPUT_ENABLED
1872 /**
1873  * xmlIOHTTPOpenW:
1874  * @post_uri:  The destination URI for the document
1875  * @compression:  The compression desired for the document.
1876  *
1877  * Open a temporary buffer to collect the document for a subsequent HTTP POST
1878  * request.  Non-static as is called from the output buffer creation routine.
1879  *
1880  * Returns an I/O context or NULL in case of error.
1881  */
1882
1883 void *
1884 xmlIOHTTPOpenW(const char *post_uri, int compression ATTRIBUTE_UNUSED)
1885 {
1886
1887     xmlIOHTTPWriteCtxtPtr ctxt = NULL;
1888
1889     if (post_uri == NULL)
1890         return (NULL);
1891
1892     ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt));
1893     if (ctxt == NULL) {
1894         xmlIOErrMemory("creating HTTP output context");
1895         return (NULL);
1896     }
1897
1898     (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt));
1899
1900     ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri);
1901     if (ctxt->uri == NULL) {
1902         xmlIOErrMemory("copying URI");
1903         xmlFreeHTTPWriteCtxt(ctxt);
1904         return (NULL);
1905     }
1906
1907     /*
1908      * **  Since the document length is required for an HTTP post,
1909      * **  need to put the document into a buffer.  A memory buffer
1910      * **  is being used to avoid pushing the data to disk and back.
1911      */
1912
1913 #ifdef HAVE_ZLIB_H
1914     if ((compression > 0) && (compression <= 9)) {
1915
1916         ctxt->compression = compression;
1917         ctxt->doc_buff = xmlCreateZMemBuff(compression);
1918     } else
1919 #endif
1920     {
1921         /*  Any character conversions should have been done before this  */
1922
1923         ctxt->doc_buff = xmlAllocOutputBufferInternal(NULL);
1924     }
1925
1926     if (ctxt->doc_buff == NULL) {
1927         xmlFreeHTTPWriteCtxt(ctxt);
1928         ctxt = NULL;
1929     }
1930
1931     return (ctxt);
1932 }
1933 #endif /* LIBXML_OUTPUT_ENABLED */
1934
1935 #ifdef LIBXML_OUTPUT_ENABLED
1936 /**
1937  * xmlIOHTTPDfltOpenW
1938  * @post_uri:  The destination URI for this document.
1939  *
1940  * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent
1941  * HTTP post command.  This function should generally not be used as
1942  * the open callback is short circuited in xmlOutputBufferCreateFile.
1943  *
1944  * Returns a pointer to the new IO context.
1945  */
1946 static void *
1947 xmlIOHTTPDfltOpenW( const char * post_uri ) {
1948     return ( xmlIOHTTPOpenW( post_uri, 0 ) );
1949 }
1950 #endif /* LIBXML_OUTPUT_ENABLED */
1951
1952 /**
1953  * xmlIOHTTPRead:
1954  * @context:  the I/O context
1955  * @buffer:  where to drop data
1956  * @len:  number of bytes to write
1957  *
1958  * Read @len bytes to @buffer from the I/O channel.
1959  *
1960  * Returns the number of bytes written
1961  */
1962 int
1963 xmlIOHTTPRead(void * context, char * buffer, int len) {
1964     if ((buffer == NULL) || (len < 0)) return(-1);
1965     return(xmlNanoHTTPRead(context, &buffer[0], len));
1966 }
1967
1968 #ifdef LIBXML_OUTPUT_ENABLED
1969 /**
1970  * xmlIOHTTPWrite
1971  * @context:  previously opened writing context
1972  * @buffer:   data to output to temporary buffer
1973  * @len:      bytes to output
1974  *
1975  * Collect data from memory buffer into a temporary file for later
1976  * processing.
1977  *
1978  * Returns number of bytes written.
1979  */
1980
1981 static int
1982 xmlIOHTTPWrite( void * context, const char * buffer, int len ) {
1983
1984     xmlIOHTTPWriteCtxtPtr       ctxt = context;
1985
1986     if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) )
1987         return ( -1 );
1988
1989     if ( len > 0 ) {
1990
1991         /*  Use gzwrite or fwrite as previously setup in the open call  */
1992
1993 #ifdef HAVE_ZLIB_H
1994         if ( ctxt->compression > 0 )
1995             len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len );
1996
1997         else
1998 #endif
1999             len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer );
2000
2001         if ( len < 0 ) {
2002             xmlChar msg[500];
2003             xmlStrPrintf(msg, 500,
2004                         "xmlIOHTTPWrite:  %s\n%s '%s'.\n",
2005                         "Error appending to internal buffer.",
2006                         "Error sending document to URI",
2007                         ctxt->uri );
2008             xmlIOErr(XML_IO_WRITE, (const char *) msg);
2009         }
2010     }
2011
2012     return ( len );
2013 }
2014 #endif /* LIBXML_OUTPUT_ENABLED */
2015
2016
2017 /**
2018  * xmlIOHTTPClose:
2019  * @context:  the I/O context
2020  *
2021  * Close an HTTP I/O channel
2022  *
2023  * Returns 0
2024  */
2025 int
2026 xmlIOHTTPClose (void * context) {
2027     xmlNanoHTTPClose(context);
2028     return 0;
2029 }
2030
2031 #ifdef LIBXML_OUTPUT_ENABLED
2032 /**
2033  * xmlIOHTTCloseWrite
2034  * @context:  The I/O context
2035  * @http_mthd: The HTTP method to be used when sending the data
2036  *
2037  * Close the transmit HTTP I/O channel and actually send the data.
2038  */
2039 static int
2040 xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) {
2041
2042     int                         close_rc = -1;
2043     int                         http_rtn = 0;
2044     int                         content_lgth = 0;
2045     xmlIOHTTPWriteCtxtPtr       ctxt = context;
2046
2047     char *                      http_content = NULL;
2048     char *                      content_encoding = NULL;
2049     char *                      content_type = (char *) "text/xml";
2050     void *                      http_ctxt = NULL;
2051
2052     if ( ( ctxt == NULL ) || ( http_mthd == NULL ) )
2053         return ( -1 );
2054
2055     /*  Retrieve the content from the appropriate buffer  */
2056
2057 #ifdef HAVE_ZLIB_H
2058
2059     if ( ctxt->compression > 0 ) {
2060         content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content );
2061         content_encoding = (char *) "Content-Encoding: gzip";
2062     }
2063     else
2064 #endif
2065     {
2066         /*  Pull the data out of the memory output buffer  */
2067
2068         xmlOutputBufferPtr      dctxt = ctxt->doc_buff;
2069         http_content = (char *) xmlBufContent(dctxt->buffer);
2070         content_lgth = xmlBufUse(dctxt->buffer);
2071     }
2072
2073     if ( http_content == NULL ) {
2074         xmlChar msg[500];
2075         xmlStrPrintf(msg, 500,
2076                      "xmlIOHTTPCloseWrite:  %s '%s' %s '%s'.\n",
2077                      "Error retrieving content.\nUnable to",
2078                      http_mthd, "data to URI", ctxt->uri );
2079         xmlIOErr(XML_IO_WRITE, (const char *) msg);
2080     }
2081
2082     else {
2083
2084         http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content,
2085                                         &content_type, content_encoding,
2086                                         content_lgth );
2087
2088         if ( http_ctxt != NULL ) {
2089 #ifdef DEBUG_HTTP
2090             /*  If testing/debugging - dump reply with request content  */
2091
2092             FILE *      tst_file = NULL;
2093             char        buffer[ 4096 ];
2094             char *      dump_name = NULL;
2095             int         avail;
2096
2097             xmlGenericError( xmlGenericErrorContext,
2098                         "xmlNanoHTTPCloseWrite:  HTTP %s to\n%s returned %d.\n",
2099                         http_mthd, ctxt->uri,
2100                         xmlNanoHTTPReturnCode( http_ctxt ) );
2101
2102             /*
2103             **  Since either content or reply may be gzipped,
2104             **  dump them to separate files instead of the
2105             **  standard error context.
2106             */
2107
2108             dump_name = tempnam( NULL, "lxml" );
2109             if ( dump_name != NULL ) {
2110                 (void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name );
2111
2112                 tst_file = fopen( buffer, "wb" );
2113                 if ( tst_file != NULL ) {
2114                     xmlGenericError( xmlGenericErrorContext,
2115                         "Transmitted content saved in file:  %s\n", buffer );
2116
2117                     fwrite( http_content, sizeof( char ),
2118                                         content_lgth, tst_file );
2119                     fclose( tst_file );
2120                 }
2121
2122                 (void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name );
2123                 tst_file = fopen( buffer, "wb" );
2124                 if ( tst_file != NULL ) {
2125                     xmlGenericError( xmlGenericErrorContext,
2126                         "Reply content saved in file:  %s\n", buffer );
2127
2128
2129                     while ( (avail = xmlNanoHTTPRead( http_ctxt,
2130                                         buffer, sizeof( buffer ) )) > 0 ) {
2131
2132                         fwrite( buffer, sizeof( char ), avail, tst_file );
2133                     }
2134
2135                     fclose( tst_file );
2136                 }
2137
2138                 free( dump_name );
2139             }
2140 #endif  /*  DEBUG_HTTP  */
2141
2142             http_rtn = xmlNanoHTTPReturnCode( http_ctxt );
2143             if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) )
2144                 close_rc = 0;
2145             else {
2146                 xmlChar msg[500];
2147                 xmlStrPrintf(msg, 500,
2148                       "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
2149                             http_mthd, content_lgth,
2150                             "bytes to URI", ctxt->uri,
2151                             "failed.  HTTP return code:", http_rtn );
2152                 xmlIOErr(XML_IO_WRITE, (const char *) msg);
2153             }
2154
2155             xmlNanoHTTPClose( http_ctxt );
2156             xmlFree( content_type );
2157         }
2158     }
2159
2160     /*  Final cleanups  */
2161
2162     xmlFreeHTTPWriteCtxt( ctxt );
2163
2164     return ( close_rc );
2165 }
2166
2167 /**
2168  * xmlIOHTTPClosePut
2169  *
2170  * @context:  The I/O context
2171  *
2172  * Close the transmit HTTP I/O channel and actually send data using a PUT
2173  * HTTP method.
2174  */
2175 static int
2176 xmlIOHTTPClosePut( void * ctxt ) {
2177     return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) );
2178 }
2179
2180
2181 /**
2182  * xmlIOHTTPClosePost
2183  *
2184  * @context:  The I/O context
2185  *
2186  * Close the transmit HTTP I/O channel and actually send data using a POST
2187  * HTTP method.
2188  */
2189 static int
2190 xmlIOHTTPClosePost( void * ctxt ) {
2191     return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) );
2192 }
2193 #endif /* LIBXML_OUTPUT_ENABLED */
2194
2195 #endif /* LIBXML_HTTP_ENABLED */
2196
2197 #ifdef LIBXML_FTP_ENABLED
2198 /************************************************************************
2199  *                                                                      *
2200  *                      I/O for FTP file accesses                       *
2201  *                                                                      *
2202  ************************************************************************/
2203 /**
2204  * xmlIOFTPMatch:
2205  * @filename:  the URI for matching
2206  *
2207  * check if the URI matches an FTP one
2208  *
2209  * Returns 1 if matches, 0 otherwise
2210  */
2211 int
2212 xmlIOFTPMatch (const char *filename) {
2213     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6))
2214         return(1);
2215     return(0);
2216 }
2217
2218 /**
2219  * xmlIOFTPOpen:
2220  * @filename:  the URI for matching
2221  *
2222  * open an FTP I/O channel
2223  *
2224  * Returns an I/O context or NULL in case of error
2225  */
2226 void *
2227 xmlIOFTPOpen (const char *filename) {
2228     return(xmlNanoFTPOpen(filename));
2229 }
2230
2231 /**
2232  * xmlIOFTPRead:
2233  * @context:  the I/O context
2234  * @buffer:  where to drop data
2235  * @len:  number of bytes to write
2236  *
2237  * Read @len bytes to @buffer from the I/O channel.
2238  *
2239  * Returns the number of bytes written
2240  */
2241 int
2242 xmlIOFTPRead(void * context, char * buffer, int len) {
2243     if ((buffer == NULL) || (len < 0)) return(-1);
2244     return(xmlNanoFTPRead(context, &buffer[0], len));
2245 }
2246
2247 /**
2248  * xmlIOFTPClose:
2249  * @context:  the I/O context
2250  *
2251  * Close an FTP I/O channel
2252  *
2253  * Returns 0
2254  */
2255 int
2256 xmlIOFTPClose (void * context) {
2257     return ( xmlNanoFTPClose(context) );
2258 }
2259 #endif /* LIBXML_FTP_ENABLED */
2260
2261
2262 /**
2263  * xmlRegisterInputCallbacks:
2264  * @matchFunc:  the xmlInputMatchCallback
2265  * @openFunc:  the xmlInputOpenCallback
2266  * @readFunc:  the xmlInputReadCallback
2267  * @closeFunc:  the xmlInputCloseCallback
2268  *
2269  * Register a new set of I/O callback for handling parser input.
2270  *
2271  * Returns the registered handler number or -1 in case of error
2272  */
2273 int
2274 xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc,
2275         xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
2276         xmlInputCloseCallback closeFunc) {
2277     if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) {
2278         return(-1);
2279     }
2280     xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc;
2281     xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc;
2282     xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc;
2283     xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc;
2284     xmlInputCallbackInitialized = 1;
2285     return(xmlInputCallbackNr++);
2286 }
2287
2288 #ifdef LIBXML_OUTPUT_ENABLED
2289 /**
2290  * xmlRegisterOutputCallbacks:
2291  * @matchFunc:  the xmlOutputMatchCallback
2292  * @openFunc:  the xmlOutputOpenCallback
2293  * @writeFunc:  the xmlOutputWriteCallback
2294  * @closeFunc:  the xmlOutputCloseCallback
2295  *
2296  * Register a new set of I/O callback for handling output.
2297  *
2298  * Returns the registered handler number or -1 in case of error
2299  */
2300 int
2301 xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc,
2302         xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc,
2303         xmlOutputCloseCallback closeFunc) {
2304     if (xmlOutputCallbackNr >= MAX_OUTPUT_CALLBACK) {
2305         return(-1);
2306     }
2307     xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc;
2308     xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc;
2309     xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc;
2310     xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc;
2311     xmlOutputCallbackInitialized = 1;
2312     return(xmlOutputCallbackNr++);
2313 }
2314 #endif /* LIBXML_OUTPUT_ENABLED */
2315
2316 /**
2317  * xmlRegisterDefaultInputCallbacks:
2318  *
2319  * Registers the default compiled-in I/O handlers.
2320  */
2321 void
2322 xmlRegisterDefaultInputCallbacks(void) {
2323     if (xmlInputCallbackInitialized)
2324         return;
2325
2326 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
2327     xmlInitPlatformSpecificIo();
2328 #endif
2329
2330     xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
2331                               xmlFileRead, xmlFileClose);
2332 #ifdef HAVE_ZLIB_H
2333     xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
2334                               xmlGzfileRead, xmlGzfileClose);
2335 #endif /* HAVE_ZLIB_H */
2336 #ifdef LIBXML_LZMA_ENABLED
2337     xmlRegisterInputCallbacks(xmlXzfileMatch, xmlXzfileOpen,
2338                               xmlXzfileRead, xmlXzfileClose);
2339 #endif /* LIBXML_LZMA_ENABLED */
2340
2341 #ifdef LIBXML_HTTP_ENABLED
2342     xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
2343                               xmlIOHTTPRead, xmlIOHTTPClose);
2344 #endif /* LIBXML_HTTP_ENABLED */
2345
2346 #ifdef LIBXML_FTP_ENABLED
2347     xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
2348                               xmlIOFTPRead, xmlIOFTPClose);
2349 #endif /* LIBXML_FTP_ENABLED */
2350     xmlInputCallbackInitialized = 1;
2351 }
2352
2353 #ifdef LIBXML_OUTPUT_ENABLED
2354 /**
2355  * xmlRegisterDefaultOutputCallbacks:
2356  *
2357  * Registers the default compiled-in I/O handlers.
2358  */
2359 void
2360 xmlRegisterDefaultOutputCallbacks (void) {
2361     if (xmlOutputCallbackInitialized)
2362         return;
2363
2364 #if defined(_WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
2365     xmlInitPlatformSpecificIo();
2366 #endif
2367
2368     xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
2369                               xmlFileWrite, xmlFileClose);
2370
2371 #ifdef LIBXML_HTTP_ENABLED
2372     xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
2373                                xmlIOHTTPWrite, xmlIOHTTPClosePut);
2374 #endif
2375
2376 /*********************************
2377  No way a-priori to distinguish between gzipped files from
2378  uncompressed ones except opening if existing then closing
2379  and saving with same compression ratio ... a pain.
2380
2381 #ifdef HAVE_ZLIB_H
2382     xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
2383                                xmlGzfileWrite, xmlGzfileClose);
2384 #endif
2385
2386  Nor FTP PUT ....
2387 #ifdef LIBXML_FTP_ENABLED
2388     xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
2389                                xmlIOFTPWrite, xmlIOFTPClose);
2390 #endif
2391  **********************************/
2392     xmlOutputCallbackInitialized = 1;
2393 }
2394
2395 #ifdef LIBXML_HTTP_ENABLED
2396 /**
2397  * xmlRegisterHTTPPostCallbacks:
2398  *
2399  * By default, libxml submits HTTP output requests using the "PUT" method.
2400  * Calling this method changes the HTTP output method to use the "POST"
2401  * method instead.
2402  *
2403  */
2404 void
2405 xmlRegisterHTTPPostCallbacks( void ) {
2406
2407     /*  Register defaults if not done previously  */
2408
2409     if ( xmlOutputCallbackInitialized == 0 )
2410         xmlRegisterDefaultOutputCallbacks( );
2411
2412     xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
2413                                xmlIOHTTPWrite, xmlIOHTTPClosePost);
2414     return;
2415 }
2416 #endif
2417 #endif /* LIBXML_OUTPUT_ENABLED */
2418
2419 /**
2420  * xmlAllocParserInputBuffer:
2421  * @enc:  the charset encoding if known
2422  *
2423  * Create a buffered parser input for progressive parsing
2424  *
2425  * Returns the new parser input or NULL
2426  */
2427 xmlParserInputBufferPtr
2428 xmlAllocParserInputBuffer(xmlCharEncoding enc) {
2429     xmlParserInputBufferPtr ret;
2430
2431     ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
2432     if (ret == NULL) {
2433         xmlIOErrMemory("creating input buffer");
2434         return(NULL);
2435     }
2436     memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
2437     ret->buffer = xmlBufCreateSize(2 * xmlDefaultBufferSize);
2438     if (ret->buffer == NULL) {
2439         xmlFree(ret);
2440         return(NULL);
2441     }
2442     xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_DOUBLEIT);
2443     ret->encoder = xmlGetCharEncodingHandler(enc);
2444     if (ret->encoder != NULL)
2445         ret->raw = xmlBufCreateSize(2 * xmlDefaultBufferSize);
2446     else
2447         ret->raw = NULL;
2448     ret->readcallback = NULL;
2449     ret->closecallback = NULL;
2450     ret->context = NULL;
2451     ret->compressed = -1;
2452     ret->rawconsumed = 0;
2453
2454     return(ret);
2455 }
2456
2457 #ifdef LIBXML_OUTPUT_ENABLED
2458 /**
2459  * xmlAllocOutputBuffer:
2460  * @encoder:  the encoding converter or NULL
2461  *
2462  * Create a buffered parser output
2463  *
2464  * Returns the new parser output or NULL
2465  */
2466 xmlOutputBufferPtr
2467 xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) {
2468     xmlOutputBufferPtr ret;
2469
2470     ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2471     if (ret == NULL) {
2472         xmlIOErrMemory("creating output buffer");
2473         return(NULL);
2474     }
2475     memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
2476     ret->buffer = xmlBufCreate();
2477     if (ret->buffer == NULL) {
2478         xmlFree(ret);
2479         return(NULL);
2480     }
2481
2482     /* try to avoid a performance problem with Windows realloc() */
2483     if (xmlBufGetAllocationScheme(ret->buffer) == XML_BUFFER_ALLOC_EXACT)
2484         xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_DOUBLEIT);
2485
2486     ret->encoder = encoder;
2487     if (encoder != NULL) {
2488         ret->conv = xmlBufCreateSize(4000);
2489         if (ret->conv == NULL) {
2490             xmlFree(ret);
2491             return(NULL);
2492         }
2493
2494         /*
2495          * This call is designed to initiate the encoder state
2496          */
2497         xmlCharEncOutput(ret, 1);
2498     } else
2499         ret->conv = NULL;
2500     ret->writecallback = NULL;
2501     ret->closecallback = NULL;
2502     ret->context = NULL;
2503     ret->written = 0;
2504
2505     return(ret);
2506 }
2507
2508 /**
2509  * xmlAllocOutputBufferInternal:
2510  * @encoder:  the encoding converter or NULL
2511  *
2512  * Create a buffered parser output
2513  *
2514  * Returns the new parser output or NULL
2515  */
2516 xmlOutputBufferPtr
2517 xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder) {
2518     xmlOutputBufferPtr ret;
2519
2520     ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
2521     if (ret == NULL) {
2522         xmlIOErrMemory("creating output buffer");
2523         return(NULL);
2524     }
2525     memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
2526     ret->buffer = xmlBufCreate();
2527     if (ret->buffer == NULL) {
2528         xmlFree(ret);
2529         return(NULL);
2530     }
2531
2532
2533     /*
2534      * For conversion buffers we use the special IO handling
2535      */
2536     xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_IO);
2537
2538     ret->encoder = encoder;
2539     if (encoder != NULL) {
2540         ret->conv = xmlBufCreateSize(4000);
2541         if (ret->conv == NULL) {
2542             xmlFree(ret);
2543             return(NULL);
2544         }
2545
2546         /*
2547          * This call is designed to initiate the encoder state
2548          */
2549         xmlCharEncOutput(ret, 1);
2550     } else
2551         ret->conv = NULL;
2552     ret->writecallback = NULL;
2553     ret->closecallback = NULL;
2554     ret->context = NULL;
2555     ret->written = 0;
2556
2557     return(ret);
2558 }
2559
2560 #endif /* LIBXML_OUTPUT_ENABLED */
2561
2562 /**
2563  * xmlFreeParserInputBuffer:
2564  * @in:  a buffered parser input
2565  *
2566  * Free up the memory used by a buffered parser input
2567  */
2568 void
2569 xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
2570     if (in == NULL) return;
2571
2572     if (in->raw) {
2573         xmlBufFree(in->raw);
2574         in->raw = NULL;
2575     }
2576     if (in->encoder != NULL) {
2577         xmlCharEncCloseFunc(in->encoder);
2578     }
2579     if (in->closecallback != NULL) {
2580         in->closecallback(in->context);
2581     }
2582     if (in->buffer != NULL) {
2583         xmlBufFree(in->buffer);
2584         in->buffer = NULL;
2585     }
2586
2587     xmlFree(in);
2588 }
2589
2590 #ifdef LIBXML_OUTPUT_ENABLED
2591 /**
2592  * xmlOutputBufferClose:
2593  * @out:  a buffered output
2594  *
2595  * flushes and close the output I/O channel
2596  * and free up all the associated resources
2597  *
2598  * Returns the number of byte written or -1 in case of error.
2599  */
2600 int
2601 xmlOutputBufferClose(xmlOutputBufferPtr out)
2602 {
2603     int written;
2604     int err_rc = 0;
2605
2606     if (out == NULL)
2607         return (-1);
2608     if (out->writecallback != NULL)
2609         xmlOutputBufferFlush(out);
2610     if (out->closecallback != NULL) {
2611         err_rc = out->closecallback(out->context);
2612     }
2613     written = out->written;
2614     if (out->conv) {
2615         xmlBufFree(out->conv);
2616         out->conv = NULL;
2617     }
2618     if (out->encoder != NULL) {
2619         xmlCharEncCloseFunc(out->encoder);
2620     }
2621     if (out->buffer != NULL) {
2622         xmlBufFree(out->buffer);
2623         out->buffer = NULL;
2624     }
2625
2626     if (out->error)
2627         err_rc = -1;
2628     xmlFree(out);
2629     return ((err_rc == 0) ? written : err_rc);
2630 }
2631 #endif /* LIBXML_OUTPUT_ENABLED */
2632
2633 xmlParserInputBufferPtr
2634 __xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) {
2635     xmlParserInputBufferPtr ret;
2636     int i = 0;
2637     void *context = NULL;
2638
2639     if (xmlInputCallbackInitialized == 0)
2640         xmlRegisterDefaultInputCallbacks();
2641
2642     if (URI == NULL) return(NULL);
2643
2644     /*
2645      * Try to find one of the input accept method accepting that scheme
2646      * Go in reverse to give precedence to user defined handlers.
2647      */
2648     if (context == NULL) {
2649         for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
2650             if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
2651                 (xmlInputCallbackTable[i].matchcallback(URI) != 0)) {
2652                 context = xmlInputCallbackTable[i].opencallback(URI);
2653                 if (context != NULL) {
2654                     break;
2655                 }
2656             }
2657         }
2658     }
2659     if (context == NULL) {
2660         return(NULL);
2661     }
2662
2663     /*
2664      * Allocate the Input buffer front-end.
2665      */
2666     ret = xmlAllocParserInputBuffer(enc);
2667     if (ret != NULL) {
2668         ret->context = context;
2669         ret->readcallback = xmlInputCallbackTable[i].readcallback;
2670         ret->closecallback = xmlInputCallbackTable[i].closecallback;
2671 #ifdef HAVE_ZLIB_H
2672         if ((xmlInputCallbackTable[i].opencallback == xmlGzfileOpen) &&
2673                 (strcmp(URI, "-") != 0)) {
2674 #if defined(ZLIB_VERNUM) && ZLIB_VERNUM >= 0x1230
2675             ret->compressed = !gzdirect(context);
2676 #else
2677             if (((z_stream *)context)->avail_in > 4) {
2678                 char *cptr, buff4[4];
2679                 cptr = (char *) ((z_stream *)context)->next_in;
2680                 if (gzread(context, buff4, 4) == 4) {
2681                     if (strncmp(buff4, cptr, 4) == 0)
2682                         ret->compressed = 0;
2683                     else
2684                         ret->compressed = 1;
2685                     gzrewind(context);
2686                 }
2687             }
2688 #endif
2689         }
2690 #endif
2691 #ifdef LIBXML_LZMA_ENABLED
2692         if ((xmlInputCallbackTable[i].opencallback == xmlXzfileOpen) &&
2693                 (strcmp(URI, "-") != 0)) {
2694             ret->compressed = __libxml2_xzcompressed(context);
2695         }
2696 #endif
2697     }
2698     else
2699       xmlInputCallbackTable[i].closecallback (context);
2700
2701     return(ret);
2702 }
2703
2704 /**
2705  * xmlParserInputBufferCreateFilename:
2706  * @URI:  a C string containing the URI or filename
2707  * @enc:  the charset encoding if known
2708  *
2709  * Create a buffered parser input for the progressive parsing of a file
2710  * If filename is "-' then we use stdin as the input.
2711  * Automatic support for ZLIB/Compress compressed document is provided
2712  * by default if found at compile-time.
2713  * Do an encoding check if enc == XML_CHAR_ENCODING_NONE
2714  *
2715  * Returns the new parser input or NULL
2716  */
2717 xmlParserInputBufferPtr
2718 xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) {
2719     if ((xmlParserInputBufferCreateFilenameValue)) {
2720                 return xmlParserInputBufferCreateFilenameValue(URI, enc);
2721         }
2722         return __xmlParserInputBufferCreateFilename(URI, enc);
2723 }
2724
2725 #ifdef LIBXML_OUTPUT_ENABLED
2726 xmlOutputBufferPtr
2727 __xmlOutputBufferCreateFilename(const char *URI,
2728                               xmlCharEncodingHandlerPtr encoder,
2729                               int compression ATTRIBUTE_UNUSED) {
2730     xmlOutputBufferPtr ret;
2731     xmlURIPtr puri;
2732     int i = 0;
2733     void *context = NULL;
2734     char *unescaped = NULL;
2735 #ifdef HAVE_ZLIB_H
2736     int is_file_uri = 1;
2737 #endif
2738
2739     if (xmlOutputCallbackInitialized == 0)
2740         xmlRegisterDefaultOutputCallbacks();
2741
2742     if (URI == NULL) return(NULL);
2743
2744     puri = xmlParseURI(URI);
2745     if (puri != NULL) {
2746 #ifdef HAVE_ZLIB_H
2747         if ((puri->scheme != NULL) &&
2748             (!xmlStrEqual(BAD_CAST puri->scheme, BAD_CAST "file")))
2749             is_file_uri = 0;
2750 #endif
2751         /*
2752          * try to limit the damages of the URI unescaping code.
2753          */
2754         if ((puri->scheme == NULL) ||
2755             (xmlStrEqual(BAD_CAST puri->scheme, BAD_CAST "file")))
2756             unescaped = xmlURIUnescapeString(URI, 0, NULL);
2757         xmlFreeURI(puri);
2758     }
2759
2760     /*
2761      * Try to find one of the output accept method accepting that scheme
2762      * Go in reverse to give precedence to user defined handlers.
2763      * try with an unescaped version of the URI
2764      */
2765     if (unescaped != NULL) {
2766 #ifdef HAVE_ZLIB_H
2767         if ((compression > 0) && (compression <= 9) && (is_file_uri == 1)) {
2768             context = xmlGzfileOpenW(unescaped, compression);
2769             if (context != NULL) {
2770                 ret = xmlAllocOutputBufferInternal(encoder);
2771                 if (ret != NULL) {
2772                     ret->context = context;
2773                     ret->writecallback = xmlGzfileWrite;
2774                     ret->closecallback = xmlGzfileClose;
2775                 }
2776                 xmlFree(unescaped);
2777                 return(ret);
2778             }
2779         }
2780 #endif
2781         for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
2782             if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
2783                 (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) {
2784 #if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
2785                 /*  Need to pass compression parameter into HTTP open calls  */
2786                 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
2787                     context = xmlIOHTTPOpenW(unescaped, compression);
2788                 else
2789 #endif
2790                     context = xmlOutputCallbackTable[i].opencallback(unescaped);
2791                 if (context != NULL)
2792                     break;
2793             }
2794         }
2795         xmlFree(unescaped);
2796     }
2797
2798     /*
2799      * If this failed try with a non-escaped URI this may be a strange
2800      * filename
2801      */
2802     if (context == NULL) {
2803 #ifdef HAVE_ZLIB_H
2804         if ((compression > 0) && (compression <= 9) && (is_file_uri == 1)) {
2805             context = xmlGzfileOpenW(URI, compression);
2806             if (context != NULL) {
2807                 ret = xmlAllocOutputBufferInternal(encoder);
2808                 if (ret != NULL) {
2809                     ret->context = context;
2810                     ret->writecallback = xmlGzfileWrite;
2811                     ret->closecallback = xmlGzfileClose;
2812                 }
2813                 return(ret);
2814             }
2815         }
2816 #endif
2817         for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
2818             if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
2819                 (xmlOutputCallbackTable[i].matchcallback(URI) != 0)) {
2820 #if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
2821                 /*  Need to pass compression parameter into HTTP open calls  */
2822                 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
2823                     context = xmlIOHTTPOpenW(URI, compression);
2824                 else
2825 #endif
2826                     context = xmlOutputCallbackTable[i].opencallback(URI);
2827                 if (context != NULL)
2828                     break;
2829             }
2830         }
2831     }
2832
2833     if (context == NULL) {
2834         return(NULL);
2835     }
2836
2837     /*
2838      * Allocate the Output buffer front-end.
2839      */
2840     ret = xmlAllocOutputBufferInternal(encoder);
2841     if (ret != NULL) {
2842         ret->context = context;
2843         ret->writecallback = xmlOutputCallbackTable[i].writecallback;
2844         ret->closecallback = xmlOutputCallbackTable[i].closecallback;
2845     }
2846     return(ret);
2847 }
2848
2849 /**
2850  * xmlOutputBufferCreateFilename:
2851  * @URI:  a C string containing the URI or filename
2852  * @encoder:  the encoding converter or NULL
2853  * @compression:  the compression ration (0 none, 9 max).
2854  *
2855  * Create a buffered  output for the progressive saving of a file
2856  * If filename is "-' then we use stdout as the output.
2857  * Automatic support for ZLIB/Compress compressed document is provided
2858  * by default if found at compile-time.
2859  * TODO: currently if compression is set, the library only support
2860  *       writing to a local file.
2861  *
2862  * Returns the new output or NULL
2863  */
2864 xmlOutputBufferPtr
2865 xmlOutputBufferCreateFilename(const char *URI,
2866                               xmlCharEncodingHandlerPtr encoder,
2867                               int compression ATTRIBUTE_UNUSED) {
2868     if ((xmlOutputBufferCreateFilenameValue)) {
2869                 return xmlOutputBufferCreateFilenameValue(URI, encoder, compression);
2870         }
2871         return __xmlOutputBufferCreateFilename(URI, encoder, compression);
2872 }
2873 #endif /* LIBXML_OUTPUT_ENABLED */
2874
2875 /**
2876  * xmlParserInputBufferCreateFile:
2877  * @file:  a FILE*
2878  * @enc:  the charset encoding if known
2879  *
2880  * Create a buffered parser input for the progressive parsing of a FILE *
2881  * buffered C I/O
2882  *
2883  * Returns the new parser input or NULL
2884  */
2885 xmlParserInputBufferPtr
2886 xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) {
2887     xmlParserInputBufferPtr ret;
2888
2889     if (xmlInputCallbackInitialized == 0)
2890         xmlRegisterDefaultInputCallbacks();
2891
2892     if (file == NULL) return(NULL);
2893
2894     ret = xmlAllocParserInputBuffer(enc);
2895     if (ret != NULL) {
2896         ret->context = file;
2897         ret->readcallback = xmlFileRead;
2898         ret->closecallback = xmlFileFlush;
2899     }
2900
2901     return(ret);
2902 }
2903
2904 #ifdef LIBXML_OUTPUT_ENABLED
2905 /**
2906  * xmlOutputBufferCreateFile:
2907  * @file:  a FILE*
2908  * @encoder:  the encoding converter or NULL
2909  *
2910  * Create a buffered output for the progressive saving to a FILE *
2911  * buffered C I/O
2912  *
2913  * Returns the new parser output or NULL
2914  */
2915 xmlOutputBufferPtr
2916 xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) {
2917     xmlOutputBufferPtr ret;
2918
2919     if (xmlOutputCallbackInitialized == 0)
2920         xmlRegisterDefaultOutputCallbacks();
2921
2922     if (file == NULL) return(NULL);
2923
2924     ret = xmlAllocOutputBufferInternal(encoder);
2925     if (ret != NULL) {
2926         ret->context = file;
2927         ret->writecallback = xmlFileWrite;
2928         ret->closecallback = xmlFileFlush;
2929     }
2930
2931     return(ret);
2932 }
2933
2934 /**
2935  * xmlOutputBufferCreateBuffer:
2936  * @buffer:  a xmlBufferPtr
2937  * @encoder:  the encoding converter or NULL
2938  *
2939  * Create a buffered output for the progressive saving to a xmlBuffer
2940  *
2941  * Returns the new parser output or NULL
2942  */
2943 xmlOutputBufferPtr
2944 xmlOutputBufferCreateBuffer(xmlBufferPtr buffer,
2945                             xmlCharEncodingHandlerPtr encoder) {
2946     xmlOutputBufferPtr ret;
2947
2948     if (buffer == NULL) return(NULL);
2949
2950     ret = xmlOutputBufferCreateIO((xmlOutputWriteCallback)
2951                                   xmlBufferWrite,
2952                                   (xmlOutputCloseCallback)
2953                                   NULL, (void *) buffer, encoder);
2954
2955     return(ret);
2956 }
2957
2958 /**
2959  * xmlOutputBufferGetContent:
2960  * @out:  an xmlOutputBufferPtr
2961  *
2962  * Gives a pointer to the data currently held in the output buffer
2963  *
2964  * Returns a pointer to the data or NULL in case of error
2965  */
2966 const xmlChar *
2967 xmlOutputBufferGetContent(xmlOutputBufferPtr out) {
2968     if ((out == NULL) || (out->buffer == NULL))
2969         return(NULL);
2970
2971     return(xmlBufContent(out->buffer));
2972 }
2973
2974 /**
2975  * xmlOutputBufferGetSize:
2976  * @out:  an xmlOutputBufferPtr
2977  *
2978  * Gives the length of the data currently held in the output buffer
2979  *
2980  * Returns 0 in case or error or no data is held, the size otherwise
2981  */
2982 size_t
2983 xmlOutputBufferGetSize(xmlOutputBufferPtr out) {
2984     if ((out == NULL) || (out->buffer == NULL))
2985         return(0);
2986
2987     return(xmlBufUse(out->buffer));
2988 }
2989
2990
2991 #endif /* LIBXML_OUTPUT_ENABLED */
2992
2993 /**
2994  * xmlParserInputBufferCreateFd:
2995  * @fd:  a file descriptor number
2996  * @enc:  the charset encoding if known
2997  *
2998  * Create a buffered parser input for the progressive parsing for the input
2999  * from a file descriptor
3000  *
3001  * Returns the new parser input or NULL
3002  */
3003 xmlParserInputBufferPtr
3004 xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) {
3005     xmlParserInputBufferPtr ret;
3006
3007     if (fd < 0) return(NULL);
3008
3009     ret = xmlAllocParserInputBuffer(enc);
3010     if (ret != NULL) {
3011         ret->context = (void *) (long) fd;
3012         ret->readcallback = xmlFdRead;
3013         ret->closecallback = xmlFdClose;
3014     }
3015
3016     return(ret);
3017 }
3018
3019 /**
3020  * xmlParserInputBufferCreateMem:
3021  * @mem:  the memory input
3022  * @size:  the length of the memory block
3023  * @enc:  the charset encoding if known
3024  *
3025  * Create a buffered parser input for the progressive parsing for the input
3026  * from a memory area.
3027  *
3028  * Returns the new parser input or NULL
3029  */
3030 xmlParserInputBufferPtr
3031 xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
3032     xmlParserInputBufferPtr ret;
3033     int errcode;
3034
3035     if (size < 0) return(NULL);
3036     if (mem == NULL) return(NULL);
3037
3038     ret = xmlAllocParserInputBuffer(enc);
3039     if (ret != NULL) {
3040         ret->context = (void *) mem;
3041         ret->readcallback = (xmlInputReadCallback) xmlNop;
3042         ret->closecallback = NULL;
3043         errcode = xmlBufAdd(ret->buffer, (const xmlChar *) mem, size);
3044         if (errcode != 0) {
3045             xmlFree(ret);
3046             return(NULL);
3047         }
3048     }
3049
3050     return(ret);
3051 }
3052
3053 /**
3054  * xmlParserInputBufferCreateStatic:
3055  * @mem:  the memory input
3056  * @size:  the length of the memory block
3057  * @enc:  the charset encoding if known
3058  *
3059  * Create a buffered parser input for the progressive parsing for the input
3060  * from an immutable memory area. This will not copy the memory area to
3061  * the buffer, but the memory is expected to be available until the end of
3062  * the parsing, this is useful for example when using mmap'ed file.
3063  *
3064  * Returns the new parser input or NULL
3065  */
3066 xmlParserInputBufferPtr
3067 xmlParserInputBufferCreateStatic(const char *mem, int size,
3068                                  xmlCharEncoding enc) {
3069     xmlParserInputBufferPtr ret;
3070
3071     if (size < 0) return(NULL);
3072     if (mem == NULL) return(NULL);
3073
3074     ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
3075     if (ret == NULL) {
3076         xmlIOErrMemory("creating input buffer");
3077         return(NULL);
3078     }
3079     memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
3080     ret->buffer = xmlBufCreateStatic((void *)mem, (size_t) size);
3081     if (ret->buffer == NULL) {
3082         xmlFree(ret);
3083         return(NULL);
3084     }
3085     ret->encoder = xmlGetCharEncodingHandler(enc);
3086     if (ret->encoder != NULL)
3087         ret->raw = xmlBufCreateSize(2 * xmlDefaultBufferSize);
3088     else
3089         ret->raw = NULL;
3090     ret->compressed = -1;
3091     ret->context = (void *) mem;
3092     ret->readcallback = NULL;
3093     ret->closecallback = NULL;
3094
3095     return(ret);
3096 }
3097
3098 #ifdef LIBXML_OUTPUT_ENABLED
3099 /**
3100  * xmlOutputBufferCreateFd:
3101  * @fd:  a file descriptor number
3102  * @encoder:  the encoding converter or NULL
3103  *
3104  * Create a buffered output for the progressive saving
3105  * to a file descriptor
3106  *
3107  * Returns the new parser output or NULL
3108  */
3109 xmlOutputBufferPtr
3110 xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) {
3111     xmlOutputBufferPtr ret;
3112
3113     if (fd < 0) return(NULL);
3114
3115     ret = xmlAllocOutputBufferInternal(encoder);
3116     if (ret != NULL) {
3117         ret->context = (void *) (long) fd;
3118         ret->writecallback = xmlFdWrite;
3119         ret->closecallback = NULL;
3120     }
3121
3122     return(ret);
3123 }
3124 #endif /* LIBXML_OUTPUT_ENABLED */
3125
3126 /**
3127  * xmlParserInputBufferCreateIO:
3128  * @ioread:  an I/O read function
3129  * @ioclose:  an I/O close function
3130  * @ioctx:  an I/O handler
3131  * @enc:  the charset encoding if known
3132  *
3133  * Create a buffered parser input for the progressive parsing for the input
3134  * from an I/O handler
3135  *
3136  * Returns the new parser input or NULL
3137  */
3138 xmlParserInputBufferPtr
3139 xmlParserInputBufferCreateIO(xmlInputReadCallback   ioread,
3140          xmlInputCloseCallback  ioclose, void *ioctx, xmlCharEncoding enc) {
3141     xmlParserInputBufferPtr ret;
3142
3143     if (ioread == NULL) return(NULL);
3144
3145     ret = xmlAllocParserInputBuffer(enc);
3146     if (ret != NULL) {
3147         ret->context = (void *) ioctx;
3148         ret->readcallback = ioread;
3149         ret->closecallback = ioclose;
3150     }
3151
3152     return(ret);
3153 }
3154
3155 #ifdef LIBXML_OUTPUT_ENABLED
3156 /**
3157  * xmlOutputBufferCreateIO:
3158  * @iowrite:  an I/O write function
3159  * @ioclose:  an I/O close function
3160  * @ioctx:  an I/O handler
3161  * @encoder:  the charset encoding if known
3162  *
3163  * Create a buffered output for the progressive saving
3164  * to an I/O handler
3165  *
3166  * Returns the new parser output or NULL
3167  */
3168 xmlOutputBufferPtr
3169 xmlOutputBufferCreateIO(xmlOutputWriteCallback   iowrite,
3170          xmlOutputCloseCallback  ioclose, void *ioctx,
3171          xmlCharEncodingHandlerPtr encoder) {
3172     xmlOutputBufferPtr ret;
3173
3174     if (iowrite == NULL) return(NULL);
3175
3176     ret = xmlAllocOutputBufferInternal(encoder);
3177     if (ret != NULL) {
3178         ret->context = (void *) ioctx;
3179         ret->writecallback = iowrite;
3180         ret->closecallback = ioclose;
3181     }
3182
3183     return(ret);
3184 }
3185 #endif /* LIBXML_OUTPUT_ENABLED */
3186
3187 /**
3188  * xmlParserInputBufferCreateFilenameDefault:
3189  * @func: function pointer to the new ParserInputBufferCreateFilenameFunc
3190  *
3191  * Registers a callback for URI input file handling
3192  *
3193  * Returns the old value of the registration function
3194  */
3195 xmlParserInputBufferCreateFilenameFunc
3196 xmlParserInputBufferCreateFilenameDefault(xmlParserInputBufferCreateFilenameFunc func)
3197 {
3198     xmlParserInputBufferCreateFilenameFunc old = xmlParserInputBufferCreateFilenameValue;
3199     if (old == NULL) {
3200                 old = __xmlParserInputBufferCreateFilename;
3201         }
3202
3203     xmlParserInputBufferCreateFilenameValue = func;
3204     return(old);
3205 }
3206
3207 /**
3208  * xmlOutputBufferCreateFilenameDefault:
3209  * @func: function pointer to the new OutputBufferCreateFilenameFunc
3210  *
3211  * Registers a callback for URI output file handling
3212  *
3213  * Returns the old value of the registration function
3214  */
3215 xmlOutputBufferCreateFilenameFunc
3216 xmlOutputBufferCreateFilenameDefault(xmlOutputBufferCreateFilenameFunc func)
3217 {
3218     xmlOutputBufferCreateFilenameFunc old = xmlOutputBufferCreateFilenameValue;
3219 #ifdef LIBXML_OUTPUT_ENABLED
3220     if (old == NULL) {
3221                 old = __xmlOutputBufferCreateFilename;
3222         }
3223 #endif
3224     xmlOutputBufferCreateFilenameValue = func;
3225     return(old);
3226 }
3227
3228 /**
3229  * xmlParserInputBufferPush:
3230  * @in:  a buffered parser input
3231  * @len:  the size in bytes of the array.
3232  * @buf:  an char array
3233  *
3234  * Push the content of the arry in the input buffer
3235  * This routine handle the I18N transcoding to internal UTF-8
3236  * This is used when operating the parser in progressive (push) mode.
3237  *
3238  * Returns the number of chars read and stored in the buffer, or -1
3239  *         in case of error.
3240  */
3241 int
3242 xmlParserInputBufferPush(xmlParserInputBufferPtr in,
3243                          int len, const char *buf) {
3244     int nbchars = 0;
3245     int ret;
3246
3247     if (len < 0) return(0);
3248     if ((in == NULL) || (in->error)) return(-1);
3249     if (in->encoder != NULL) {
3250         unsigned int use;
3251
3252         /*
3253          * Store the data in the incoming raw buffer
3254          */
3255         if (in->raw == NULL) {
3256             in->raw = xmlBufCreate();
3257         }
3258         ret = xmlBufAdd(in->raw, (const xmlChar *) buf, len);
3259         if (ret != 0)
3260             return(-1);
3261
3262         /*
3263          * convert as much as possible to the parser reading buffer.
3264          */
3265         use = xmlBufUse(in->raw);
3266         nbchars = xmlCharEncInput(in, 1);
3267         if (nbchars < 0) {
3268             xmlIOErr(XML_IO_ENCODER, NULL);
3269             in->error = XML_IO_ENCODER;
3270             return(-1);
3271         }
3272         in->rawconsumed += (use - xmlBufUse(in->raw));
3273     } else {
3274         nbchars = len;
3275         ret = xmlBufAdd(in->buffer, (xmlChar *) buf, nbchars);
3276         if (ret != 0)
3277             return(-1);
3278     }
3279 #ifdef DEBUG_INPUT
3280     xmlGenericError(xmlGenericErrorContext,
3281             "I/O: pushed %d chars, buffer %d/%d\n",
3282             nbchars, xmlBufUse(in->buffer), xmlBufLength(in->buffer));
3283 #endif
3284     return(nbchars);
3285 }
3286
3287 /**
3288  * endOfInput:
3289  *
3290  * When reading from an Input channel indicated end of file or error
3291  * don't reread from it again.
3292  */
3293 static int
3294 endOfInput (void * context ATTRIBUTE_UNUSED,
3295             char * buffer ATTRIBUTE_UNUSED,
3296             int len ATTRIBUTE_UNUSED) {
3297     return(0);
3298 }
3299
3300 /**
3301  * xmlParserInputBufferGrow:
3302  * @in:  a buffered parser input
3303  * @len:  indicative value of the amount of chars to read
3304  *
3305  * Grow up the content of the input buffer, the old data are preserved
3306  * This routine handle the I18N transcoding to internal UTF-8
3307  * This routine is used when operating the parser in normal (pull) mode
3308  *
3309  * TODO: one should be able to remove one extra copy by copying directly
3310  *       onto in->buffer or in->raw
3311  *
3312  * Returns the number of chars read and stored in the buffer, or -1
3313  *         in case of error.
3314  */
3315 int
3316 xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) {
3317     char *buffer = NULL;
3318     int res = 0;
3319     int nbchars = 0;
3320
3321     if ((in == NULL) || (in->error)) return(-1);
3322     if ((len <= MINLEN) && (len != 4))
3323         len = MINLEN;
3324
3325     if (xmlBufAvail(in->buffer) <= 0) {
3326         xmlIOErr(XML_IO_BUFFER_FULL, NULL);
3327         in->error = XML_IO_BUFFER_FULL;
3328         return(-1);
3329     }
3330
3331     if (xmlBufGrow(in->buffer, len + 1) < 0) {
3332         xmlIOErrMemory("growing input buffer");
3333         in->error = XML_ERR_NO_MEMORY;
3334         return(-1);
3335     }
3336     buffer = (char *)xmlBufEnd(in->buffer);
3337
3338     /*
3339      * Call the read method for this I/O type.
3340      */
3341     if (in->readcallback != NULL) {
3342         res = in->readcallback(in->context, &buffer[0], len);
3343         if (res <= 0)
3344             in->readcallback = endOfInput;
3345     } else {
3346         xmlIOErr(XML_IO_NO_INPUT, NULL);
3347         in->error = XML_IO_NO_INPUT;
3348         return(-1);
3349     }
3350     if (res < 0) {
3351         return(-1);
3352     }
3353
3354     /*
3355      * try to establish compressed status of input if not done already
3356      */
3357     if (in->compressed == -1) {
3358 #ifdef LIBXML_LZMA_ENABLED
3359         if (in->readcallback == xmlXzfileRead)
3360             in->compressed = __libxml2_xzcompressed(in->context);
3361 #endif
3362     }
3363
3364     len = res;
3365     if (in->encoder != NULL) {
3366         unsigned int use;
3367
3368         /*
3369          * Store the data in the incoming raw buffer
3370          */
3371         if (in->raw == NULL) {
3372             in->raw = xmlBufCreate();
3373         }
3374         res = xmlBufAdd(in->raw, (const xmlChar *) buffer, len);
3375         if (res != 0)
3376             return(-1);
3377
3378         /*
3379          * convert as much as possible to the parser reading buffer.
3380          */
3381         use = xmlBufUse(in->raw);
3382         nbchars = xmlCharEncInput(in, 1);
3383         if (nbchars < 0) {
3384             xmlIOErr(XML_IO_ENCODER, NULL);
3385             in->error = XML_IO_ENCODER;
3386             return(-1);
3387         }
3388         in->rawconsumed += (use - xmlBufUse(in->raw));
3389     } else {
3390         nbchars = len;
3391         xmlBufAddLen(in->buffer, nbchars);
3392     }
3393 #ifdef DEBUG_INPUT
3394     xmlGenericError(xmlGenericErrorContext,
3395             "I/O: read %d chars, buffer %d\n",
3396             nbchars, xmlBufUse(in->buffer));
3397 #endif
3398     return(nbchars);
3399 }
3400
3401 /**
3402  * xmlParserInputBufferRead:
3403  * @in:  a buffered parser input
3404  * @len:  indicative value of the amount of chars to read
3405  *
3406  * Refresh the content of the input buffer, the old data are considered
3407  * consumed
3408  * This routine handle the I18N transcoding to internal UTF-8
3409  *
3410  * Returns the number of chars read and stored in the buffer, or -1
3411  *         in case of error.
3412  */
3413 int
3414 xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) {
3415     if ((in == NULL) || (in->error)) return(-1);
3416     if (in->readcallback != NULL)
3417         return(xmlParserInputBufferGrow(in, len));
3418     else if (xmlBufGetAllocationScheme(in->buffer) == XML_BUFFER_ALLOC_IMMUTABLE)
3419         return(0);
3420     else
3421         return(-1);
3422 }
3423
3424 #ifdef LIBXML_OUTPUT_ENABLED
3425 /**
3426  * xmlOutputBufferWrite:
3427  * @out:  a buffered parser output
3428  * @len:  the size in bytes of the array.
3429  * @buf:  an char array
3430  *
3431  * Write the content of the array in the output I/O buffer
3432  * This routine handle the I18N transcoding from internal UTF-8
3433  * The buffer is lossless, i.e. will store in case of partial
3434  * or delayed writes.
3435  *
3436  * Returns the number of chars immediately written, or -1
3437  *         in case of error.
3438  */
3439 int
3440 xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) {
3441     int nbchars = 0; /* number of chars to output to I/O */
3442     int ret;         /* return from function call */
3443     int written = 0; /* number of char written to I/O so far */
3444     int chunk;       /* number of byte curreent processed from buf */
3445
3446     if ((out == NULL) || (out->error)) return(-1);
3447     if (len < 0) return(0);
3448     if (out->error) return(-1);
3449
3450     do {
3451         chunk = len;
3452         if (chunk > 4 * MINLEN)
3453             chunk = 4 * MINLEN;
3454
3455         /*
3456          * first handle encoding stuff.
3457          */
3458         if (out->encoder != NULL) {
3459             /*
3460              * Store the data in the incoming raw buffer
3461              */
3462             if (out->conv == NULL) {
3463                 out->conv = xmlBufCreate();
3464             }
3465             ret = xmlBufAdd(out->buffer, (const xmlChar *) buf, chunk);
3466             if (ret != 0)
3467                 return(-1);
3468
3469             if ((xmlBufUse(out->buffer) < MINLEN) && (chunk == len))
3470                 goto done;
3471
3472             /*
3473              * convert as much as possible to the parser reading buffer.
3474              */
3475             ret = xmlCharEncOutput(out, 0);
3476             if ((ret < 0) && (ret != -3)) {
3477                 xmlIOErr(XML_IO_ENCODER, NULL);
3478                 out->error = XML_IO_ENCODER;
3479                 return(-1);
3480             }
3481             nbchars = xmlBufUse(out->conv);
3482         } else {
3483             ret = xmlBufAdd(out->buffer, (const xmlChar *) buf, chunk);
3484             if (ret != 0)
3485                 return(-1);
3486             nbchars = xmlBufUse(out->buffer);
3487         }
3488         buf += chunk;
3489         len -= chunk;
3490
3491         if ((nbchars < MINLEN) && (len <= 0))
3492             goto done;
3493
3494         if (out->writecallback) {
3495             /*
3496              * second write the stuff to the I/O channel
3497              */
3498             if (out->encoder != NULL) {
3499                 ret = out->writecallback(out->context,
3500                            (const char *)xmlBufContent(out->conv), nbchars);
3501                 if (ret >= 0)
3502                     xmlBufShrink(out->conv, ret);
3503             } else {
3504                 ret = out->writecallback(out->context,
3505                            (const char *)xmlBufContent(out->buffer), nbchars);
3506                 if (ret >= 0)
3507                     xmlBufShrink(out->buffer, ret);
3508             }
3509             if (ret < 0) {
3510                 xmlIOErr(XML_IO_WRITE, NULL);
3511                 out->error = XML_IO_WRITE;
3512                 return(ret);
3513             }
3514             out->written += ret;
3515         }
3516         written += nbchars;
3517     } while (len > 0);
3518
3519 done:
3520 #ifdef DEBUG_INPUT
3521     xmlGenericError(xmlGenericErrorContext,
3522             "I/O: wrote %d chars\n", written);
3523 #endif
3524     return(written);
3525 }
3526
3527 /**
3528  * xmlEscapeContent:
3529  * @out:  a pointer to an array of bytes to store the result
3530  * @outlen:  the length of @out
3531  * @in:  a pointer to an array of unescaped UTF-8 bytes
3532  * @inlen:  the length of @in
3533  *
3534  * Take a block of UTF-8 chars in and escape them.
3535  * Returns 0 if success, or -1 otherwise
3536  * The value of @inlen after return is the number of octets consumed
3537  *     if the return value is positive, else unpredictable.
3538  * The value of @outlen after return is the number of octets consumed.
3539  */
3540 static int
3541 xmlEscapeContent(unsigned char* out, int *outlen,
3542                  const xmlChar* in, int *inlen) {
3543     unsigned char* outstart = out;
3544     const unsigned char* base = in;
3545     unsigned char* outend = out + *outlen;
3546     const unsigned char* inend;
3547
3548     inend = in + (*inlen);
3549
3550     while ((in < inend) && (out < outend)) {
3551         if (*in == '<') {
3552             if (outend - out < 4) break;
3553             *out++ = '&';
3554             *out++ = 'l';
3555             *out++ = 't';
3556             *out++ = ';';
3557         } else if (*in == '>') {
3558             if (outend - out < 4) break;
3559             *out++ = '&';
3560             *out++ = 'g';
3561             *out++ = 't';
3562             *out++ = ';';
3563         } else if (*in == '&') {
3564             if (outend - out < 5) break;
3565             *out++ = '&';
3566             *out++ = 'a';
3567             *out++ = 'm';
3568             *out++ = 'p';
3569             *out++ = ';';
3570         } else if (*in == '\r') {
3571             if (outend - out < 5) break;
3572             *out++ = '&';
3573             *out++ = '#';
3574             *out++ = '1';
3575             *out++ = '3';
3576             *out++ = ';';
3577         } else {
3578             *out++ = (unsigned char) *in;
3579         }
3580         ++in;
3581     }
3582     *outlen = out - outstart;
3583     *inlen = in - base;
3584     return(0);
3585 }
3586
3587 /**
3588  * xmlOutputBufferWriteEscape:
3589  * @out:  a buffered parser output
3590  * @str:  a zero terminated UTF-8 string
3591  * @escaping:  an optional escaping function (or NULL)
3592  *
3593  * Write the content of the string in the output I/O buffer
3594  * This routine escapes the caracters and then handle the I18N
3595  * transcoding from internal UTF-8
3596  * The buffer is lossless, i.e. will store in case of partial
3597  * or delayed writes.
3598  *
3599  * Returns the number of chars immediately written, or -1
3600  *         in case of error.
3601  */
3602 int
3603 xmlOutputBufferWriteEscape(xmlOutputBufferPtr out, const xmlChar *str,
3604                            xmlCharEncodingOutputFunc escaping) {
3605     int nbchars = 0; /* number of chars to output to I/O */
3606     int ret;         /* return from function call */
3607     int written = 0; /* number of char written to I/O so far */
3608     int oldwritten=0;/* loop guard */
3609     int chunk;       /* number of byte currently processed from str */
3610     int len;         /* number of bytes in str */
3611     int cons;        /* byte from str consumed */
3612
3613     if ((out == NULL) || (out->error) || (str == NULL) ||
3614         (out->buffer == NULL) ||
3615         (xmlBufGetAllocationScheme(out->buffer) == XML_BUFFER_ALLOC_IMMUTABLE))
3616         return(-1);
3617     len = strlen((const char *)str);
3618     if (len < 0) return(0);
3619     if (out->error) return(-1);
3620     if (escaping == NULL) escaping = xmlEscapeContent;
3621
3622     do {
3623         oldwritten = written;
3624
3625         /*
3626          * how many bytes to consume and how many bytes to store.
3627          */
3628         cons = len;
3629         chunk = xmlBufAvail(out->buffer) - 1;
3630
3631         /*
3632          * make sure we have enough room to save first, if this is
3633          * not the case force a flush, but make sure we stay in the loop
3634          */
3635         if (chunk < 40) {
3636             if (xmlBufGrow(out->buffer, 100) < 0)
3637                 return(-1);
3638             oldwritten = -1;
3639             continue;
3640         }
3641
3642         /*
3643          * first handle encoding stuff.
3644          */
3645         if (out->encoder != NULL) {
3646             /*
3647              * Store the data in the incoming raw buffer
3648              */
3649             if (out->conv == NULL) {
3650                 out->conv = xmlBufCreate();
3651             }
3652             ret = escaping(xmlBufEnd(out->buffer) ,
3653                            &chunk, str, &cons);
3654             if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */
3655                 return(-1);
3656             xmlBufAddLen(out->buffer, chunk);
3657
3658             if ((xmlBufUse(out->buffer) < MINLEN) && (cons == len))
3659                 goto done;
3660
3661             /*
3662              * convert as much as possible to the output buffer.
3663              */
3664             ret = xmlCharEncOutput(out, 0);
3665             if ((ret < 0) && (ret != -3)) {
3666                 xmlIOErr(XML_IO_ENCODER, NULL);
3667                 out->error = XML_IO_ENCODER;
3668                 return(-1);
3669             }
3670             nbchars = xmlBufUse(out->conv);
3671         } else {
3672             ret = escaping(xmlBufEnd(out->buffer), &chunk, str, &cons);
3673             if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */
3674                 return(-1);
3675             xmlBufAddLen(out->buffer, chunk);
3676             nbchars = xmlBufUse(out->buffer);
3677         }
3678         str += cons;
3679         len -= cons;
3680
3681         if ((nbchars < MINLEN) && (len <= 0))
3682             goto done;
3683
3684         if (out->writecallback) {
3685             /*
3686              * second write the stuff to the I/O channel
3687              */
3688             if (out->encoder != NULL) {
3689                 ret = out->writecallback(out->context,
3690                            (const char *)xmlBufContent(out->conv), nbchars);
3691                 if (ret >= 0)
3692                     xmlBufShrink(out->conv, ret);
3693             } else {
3694                 ret = out->writecallback(out->context,
3695                            (const char *)xmlBufContent(out->buffer), nbchars);
3696                 if (ret >= 0)
3697                     xmlBufShrink(out->buffer, ret);
3698             }
3699             if (ret < 0) {
3700                 xmlIOErr(XML_IO_WRITE, NULL);
3701                 out->error = XML_IO_WRITE;
3702                 return(ret);
3703             }
3704             out->written += ret;
3705         } else if (xmlBufAvail(out->buffer) < MINLEN) {
3706             xmlBufGrow(out->buffer, MINLEN);
3707         }
3708         written += nbchars;
3709     } while ((len > 0) && (oldwritten != written));
3710
3711 done:
3712 #ifdef DEBUG_INPUT
3713     xmlGenericError(xmlGenericErrorContext,
3714             "I/O: wrote %d chars\n", written);
3715 #endif
3716     return(written);
3717 }
3718
3719 /**
3720  * xmlOutputBufferWriteString:
3721  * @out:  a buffered parser output
3722  * @str:  a zero terminated C string
3723  *
3724  * Write the content of the string in the output I/O buffer
3725  * This routine handle the I18N transcoding from internal UTF-8
3726  * The buffer is lossless, i.e. will store in case of partial
3727  * or delayed writes.
3728  *
3729  * Returns the number of chars immediately written, or -1
3730  *         in case of error.
3731  */
3732 int
3733 xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) {
3734     int len;
3735
3736     if ((out == NULL) || (out->error)) return(-1);
3737     if (str == NULL)
3738         return(-1);
3739     len = strlen(str);
3740
3741     if (len > 0)
3742         return(xmlOutputBufferWrite(out, len, str));
3743     return(len);
3744 }
3745
3746 /**
3747  * xmlOutputBufferFlush:
3748  * @out:  a buffered output
3749  *
3750  * flushes the output I/O channel
3751  *
3752  * Returns the number of byte written or -1 in case of error.
3753  */
3754 int
3755 xmlOutputBufferFlush(xmlOutputBufferPtr out) {
3756     int nbchars = 0, ret = 0;
3757
3758     if ((out == NULL) || (out->error)) return(-1);
3759     /*
3760      * first handle encoding stuff.
3761      */
3762     if ((out->conv != NULL) && (out->encoder != NULL)) {
3763         /*
3764          * convert as much as possible to the parser output buffer.
3765          */
3766         do {
3767             nbchars = xmlCharEncOutput(out, 0);
3768             if (nbchars < 0) {
3769                 xmlIOErr(XML_IO_ENCODER, NULL);
3770                 out->error = XML_IO_ENCODER;
3771                 return(-1);
3772             }
3773         } while (nbchars);
3774     }
3775
3776     /*
3777      * second flush the stuff to the I/O channel
3778      */
3779     if ((out->conv != NULL) && (out->encoder != NULL) &&
3780         (out->writecallback != NULL)) {
3781         ret = out->writecallback(out->context,
3782                                  (const char *)xmlBufContent(out->conv),
3783                                  xmlBufUse(out->conv));
3784         if (ret >= 0)
3785             xmlBufShrink(out->conv, ret);
3786     } else if (out->writecallback != NULL) {
3787         ret = out->writecallback(out->context,
3788                                  (const char *)xmlBufContent(out->buffer),
3789                                  xmlBufUse(out->buffer));
3790         if (ret >= 0)
3791             xmlBufShrink(out->buffer, ret);
3792     }
3793     if (ret < 0) {
3794         xmlIOErr(XML_IO_FLUSH, NULL);
3795         out->error = XML_IO_FLUSH;
3796         return(ret);
3797     }
3798     out->written += ret;
3799
3800 #ifdef DEBUG_INPUT
3801     xmlGenericError(xmlGenericErrorContext,
3802             "I/O: flushed %d chars\n", ret);
3803 #endif
3804     return(ret);
3805 }
3806 #endif /* LIBXML_OUTPUT_ENABLED */
3807
3808 /**
3809  * xmlParserGetDirectory:
3810  * @filename:  the path to a file
3811  *
3812  * lookup the directory for that file
3813  *
3814  * Returns a new allocated string containing the directory, or NULL.
3815  */
3816 char *
3817 xmlParserGetDirectory(const char *filename) {
3818     char *ret = NULL;
3819     char dir[1024];
3820     char *cur;
3821
3822 #ifdef _WIN32_WCE  /* easy way by now ... wince does not have dirs! */
3823     return NULL;
3824 #endif
3825
3826     if (xmlInputCallbackInitialized == 0)
3827         xmlRegisterDefaultInputCallbacks();
3828
3829     if (filename == NULL) return(NULL);
3830
3831 #if defined(WIN32) && !defined(__CYGWIN__)
3832 #   define IS_XMLPGD_SEP(ch) ((ch=='/')||(ch=='\\'))
3833 #else
3834 #   define IS_XMLPGD_SEP(ch) (ch=='/')
3835 #endif
3836
3837     strncpy(dir, filename, 1023);
3838     dir[1023] = 0;
3839     cur = &dir[strlen(dir)];
3840     while (cur > dir) {
3841          if (IS_XMLPGD_SEP(*cur)) break;
3842          cur --;
3843     }
3844     if (IS_XMLPGD_SEP(*cur)) {
3845         if (cur == dir) dir[1] = 0;
3846         else *cur = 0;
3847         ret = xmlMemStrdup(dir);
3848     } else {
3849         if (getcwd(dir, 1024) != NULL) {
3850             dir[1023] = 0;
3851             ret = xmlMemStrdup(dir);
3852         }
3853     }
3854     return(ret);
3855 #undef IS_XMLPGD_SEP
3856 }
3857
3858 /****************************************************************
3859  *                                                              *
3860  *              External entities loading                       *
3861  *                                                              *
3862  ****************************************************************/
3863
3864 /**
3865  * xmlCheckHTTPInput:
3866  * @ctxt: an XML parser context
3867  * @ret: an XML parser input
3868  *
3869  * Check an input in case it was created from an HTTP stream, in that
3870  * case it will handle encoding and update of the base URL in case of
3871  * redirection. It also checks for HTTP errors in which case the input
3872  * is cleanly freed up and an appropriate error is raised in context
3873  *
3874  * Returns the input or NULL in case of HTTP error.
3875  */
3876 xmlParserInputPtr
3877 xmlCheckHTTPInput(xmlParserCtxtPtr ctxt, xmlParserInputPtr ret) {
3878 #ifdef LIBXML_HTTP_ENABLED
3879     if ((ret != NULL) && (ret->buf != NULL) &&
3880         (ret->buf->readcallback == xmlIOHTTPRead) &&
3881         (ret->buf->context != NULL)) {
3882         const char *encoding;
3883         const char *redir;
3884         const char *mime;
3885         int code;
3886
3887         code = xmlNanoHTTPReturnCode(ret->buf->context);
3888         if (code >= 400) {
3889             /* fatal error */
3890             if (ret->filename != NULL)
3891                 __xmlLoaderErr(ctxt, "failed to load HTTP resource \"%s\"\n",
3892                          (const char *) ret->filename);
3893             else
3894                 __xmlLoaderErr(ctxt, "failed to load HTTP resource\n", NULL);
3895             xmlFreeInputStream(ret);
3896             ret = NULL;
3897         } else {
3898
3899             mime = xmlNanoHTTPMimeType(ret->buf->context);
3900             if ((xmlStrstr(BAD_CAST mime, BAD_CAST "/xml")) ||
3901                 (xmlStrstr(BAD_CAST mime, BAD_CAST "+xml"))) {
3902                 encoding = xmlNanoHTTPEncoding(ret->buf->context);
3903                 if (encoding != NULL) {
3904                     xmlCharEncodingHandlerPtr handler;
3905
3906                     handler = xmlFindCharEncodingHandler(encoding);
3907                     if (handler != NULL) {
3908                         xmlSwitchInputEncoding(ctxt, ret, handler);
3909                     } else {
3910                         __xmlErrEncoding(ctxt, XML_ERR_UNKNOWN_ENCODING,
3911                                          "Unknown encoding %s",
3912                                          BAD_CAST encoding, NULL);
3913                     }
3914                     if (ret->encoding == NULL)
3915                         ret->encoding = xmlStrdup(BAD_CAST encoding);
3916                 }
3917 #if 0
3918             } else if (xmlStrstr(BAD_CAST mime, BAD_CAST "html")) {
3919 #endif
3920             }
3921             redir = xmlNanoHTTPRedir(ret->buf->context);
3922             if (redir != NULL) {
3923                 if (ret->filename != NULL)
3924                     xmlFree((xmlChar *) ret->filename);
3925                 if (ret->directory != NULL) {
3926                     xmlFree((xmlChar *) ret->directory);
3927                     ret->directory = NULL;
3928                 }
3929                 ret->filename =
3930                     (char *) xmlStrdup((const xmlChar *) redir);
3931             }
3932         }
3933     }
3934 #endif
3935     return(ret);
3936 }
3937
3938 static int xmlNoNetExists(const char *URL) {
3939     const char *path;
3940
3941     if (URL == NULL)
3942         return(0);
3943
3944     if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
3945 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
3946         path = &URL[17];
3947 #else
3948         path = &URL[16];
3949 #endif
3950     else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
3951 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
3952         path = &URL[8];
3953 #else
3954         path = &URL[7];
3955 #endif
3956     } else
3957         path = URL;
3958
3959     return xmlCheckFilename(path);
3960 }
3961
3962 #ifdef LIBXML_CATALOG_ENABLED
3963
3964 /**
3965  * xmlResolveResourceFromCatalog:
3966  * @URL:  the URL for the entity to load
3967  * @ID:  the System ID for the entity to load
3968  * @ctxt:  the context in which the entity is called or NULL
3969  *
3970  * Resolves the URL and ID against the appropriate catalog.
3971  * This function is used by xmlDefaultExternalEntityLoader and
3972  * xmlNoNetExternalEntityLoader.
3973  *
3974  * Returns a new allocated URL, or NULL.
3975  */
3976 static xmlChar *
3977 xmlResolveResourceFromCatalog(const char *URL, const char *ID,
3978                               xmlParserCtxtPtr ctxt) {
3979     xmlChar *resource = NULL;
3980     xmlCatalogAllow pref;
3981
3982     /*
3983      * If the resource doesn't exists as a file,
3984      * try to load it from the resource pointed in the catalogs
3985      */
3986     pref = xmlCatalogGetDefaults();
3987
3988     if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) {
3989         /*
3990          * Do a local lookup
3991          */
3992         if ((ctxt != NULL) && (ctxt->catalogs != NULL) &&
3993             ((pref == XML_CATA_ALLOW_ALL) ||
3994              (pref == XML_CATA_ALLOW_DOCUMENT))) {
3995             resource = xmlCatalogLocalResolve(ctxt->catalogs,
3996                                               (const xmlChar *)ID,
3997                                               (const xmlChar *)URL);
3998         }
3999         /*
4000          * Try a global lookup
4001          */
4002         if ((resource == NULL) &&
4003             ((pref == XML_CATA_ALLOW_ALL) ||
4004              (pref == XML_CATA_ALLOW_GLOBAL))) {
4005             resource = xmlCatalogResolve((const xmlChar *)ID,
4006                                          (const xmlChar *)URL);
4007         }
4008         if ((resource == NULL) && (URL != NULL))
4009             resource = xmlStrdup((const xmlChar *) URL);
4010
4011         /*
4012          * TODO: do an URI lookup on the reference
4013          */
4014         if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) {
4015             xmlChar *tmp = NULL;
4016
4017             if ((ctxt != NULL) && (ctxt->catalogs != NULL) &&
4018                 ((pref == XML_CATA_ALLOW_ALL) ||
4019                  (pref == XML_CATA_ALLOW_DOCUMENT))) {
4020                 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
4021             }
4022             if ((tmp == NULL) &&
4023                 ((pref == XML_CATA_ALLOW_ALL) ||
4024                  (pref == XML_CATA_ALLOW_GLOBAL))) {
4025                 tmp = xmlCatalogResolveURI(resource);
4026             }
4027
4028             if (tmp != NULL) {
4029                 xmlFree(resource);
4030                 resource = tmp;
4031             }
4032         }
4033     }
4034
4035     return resource;
4036 }
4037
4038 #endif
4039
4040 /**
4041  * xmlDefaultExternalEntityLoader:
4042  * @URL:  the URL for the entity to load
4043  * @ID:  the System ID for the entity to load
4044  * @ctxt:  the context in which the entity is called or NULL
4045  *
4046  * By default we don't load external entitites, yet.
4047  *
4048  * Returns a new allocated xmlParserInputPtr, or NULL.
4049  */
4050 static xmlParserInputPtr
4051 xmlDefaultExternalEntityLoader(const char *URL, const char *ID,
4052                                xmlParserCtxtPtr ctxt)
4053 {
4054     xmlParserInputPtr ret = NULL;
4055     xmlChar *resource = NULL;
4056
4057 #ifdef DEBUG_EXTERNAL_ENTITIES
4058     xmlGenericError(xmlGenericErrorContext,
4059                     "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
4060 #endif
4061     if ((ctxt != NULL) && (ctxt->options & XML_PARSE_NONET)) {
4062         int options = ctxt->options;
4063
4064         ctxt->options -= XML_PARSE_NONET;
4065         ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
4066         ctxt->options = options;
4067         return(ret);
4068     }
4069 #ifdef LIBXML_CATALOG_ENABLED
4070     resource = xmlResolveResourceFromCatalog(URL, ID, ctxt);
4071 #endif
4072
4073     if (resource == NULL)
4074         resource = (xmlChar *) URL;
4075
4076     if (resource == NULL) {
4077         if (ID == NULL)
4078             ID = "NULL";
4079         __xmlLoaderErr(ctxt, "failed to load external entity \"%s\"\n", ID);
4080         return (NULL);
4081     }
4082     ret = xmlNewInputFromFile(ctxt, (const char *) resource);
4083     if ((resource != NULL) && (resource != (xmlChar *) URL))
4084         xmlFree(resource);
4085     return (ret);
4086 }
4087
4088 static xmlExternalEntityLoader xmlCurrentExternalEntityLoader =
4089        xmlDefaultExternalEntityLoader;
4090
4091 /**
4092  * xmlSetExternalEntityLoader:
4093  * @f:  the new entity resolver function
4094  *
4095  * Changes the defaultexternal entity resolver function for the application
4096  */
4097 void
4098 xmlSetExternalEntityLoader(xmlExternalEntityLoader f) {
4099     xmlCurrentExternalEntityLoader = f;
4100 }
4101
4102 /**
4103  * xmlGetExternalEntityLoader:
4104  *
4105  * Get the default external entity resolver function for the application
4106  *
4107  * Returns the xmlExternalEntityLoader function pointer
4108  */
4109 xmlExternalEntityLoader
4110 xmlGetExternalEntityLoader(void) {
4111     return(xmlCurrentExternalEntityLoader);
4112 }
4113
4114 /**
4115  * xmlLoadExternalEntity:
4116  * @URL:  the URL for the entity to load
4117  * @ID:  the Public ID for the entity to load
4118  * @ctxt:  the context in which the entity is called or NULL
4119  *
4120  * Load an external entity, note that the use of this function for
4121  * unparsed entities may generate problems
4122  *
4123  * Returns the xmlParserInputPtr or NULL
4124  */
4125 xmlParserInputPtr
4126 xmlLoadExternalEntity(const char *URL, const char *ID,
4127                       xmlParserCtxtPtr ctxt) {
4128     if ((URL != NULL) && (xmlNoNetExists(URL) == 0)) {
4129         char *canonicFilename;
4130         xmlParserInputPtr ret;
4131
4132         canonicFilename = (char *) xmlCanonicPath((const xmlChar *) URL);
4133         if (canonicFilename == NULL) {
4134             xmlIOErrMemory("building canonical path\n");
4135             return(NULL);
4136         }
4137
4138         ret = xmlCurrentExternalEntityLoader(canonicFilename, ID, ctxt);
4139         xmlFree(canonicFilename);
4140         return(ret);
4141     }
4142     return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
4143 }
4144
4145 /************************************************************************
4146  *                                                                      *
4147  *              Disabling Network access                                *
4148  *                                                                      *
4149  ************************************************************************/
4150
4151 /**
4152  * xmlNoNetExternalEntityLoader:
4153  * @URL:  the URL for the entity to load
4154  * @ID:  the System ID for the entity to load
4155  * @ctxt:  the context in which the entity is called or NULL
4156  *
4157  * A specific entity loader disabling network accesses, though still
4158  * allowing local catalog accesses for resolution.
4159  *
4160  * Returns a new allocated xmlParserInputPtr, or NULL.
4161  */
4162 xmlParserInputPtr
4163 xmlNoNetExternalEntityLoader(const char *URL, const char *ID,
4164                              xmlParserCtxtPtr ctxt) {
4165     xmlParserInputPtr input = NULL;
4166     xmlChar *resource = NULL;
4167
4168 #ifdef LIBXML_CATALOG_ENABLED
4169     resource = xmlResolveResourceFromCatalog(URL, ID, ctxt);
4170 #endif
4171
4172     if (resource == NULL)
4173         resource = (xmlChar *) URL;
4174
4175     if (resource != NULL) {
4176         if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) ||
4177             (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) {
4178             xmlIOErr(XML_IO_NETWORK_ATTEMPT, (const char *) resource);
4179             if (resource != (xmlChar *) URL)
4180                 xmlFree(resource);
4181             return(NULL);
4182         }
4183     }
4184     input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt);
4185     if (resource != (xmlChar *) URL)
4186         xmlFree(resource);
4187     return(input);
4188 }
4189
4190 #define bottom_xmlIO
4191 #include "elfgcchack.h"