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