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