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