BRANCH release for 2.5.x critical patches
[baserock-morphs:libxml2.git] / xmlIO.c
1 /*
2  * xmlIO.c : implementation of the I/O interfaces used by the parser
3  *
4  * See Copyright for the status of this software.
5  *
6  * daniel@veillard.com
7  *
8  * 14 Nov 2000 ht - for VMS, truncated name of long functions to under 32 char
9  */
10
11 #define IN_LIBXML
12 #include "libxml.h"
13
14 #include <string.h>
15 #ifdef HAVE_ERRNO_H
16 #include <errno.h>
17 #endif
18
19
20 #ifdef HAVE_SYS_TYPES_H
21 #include <sys/types.h>
22 #endif
23 #ifdef HAVE_SYS_STAT_H
24 #include <sys/stat.h>
25 #endif
26 #ifdef HAVE_FCNTL_H
27 #include <fcntl.h>
28 #endif
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifdef HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35 #ifdef HAVE_ZLIB_H
36 #include <zlib.h>
37 #endif
38
39 /* Figure a portable way to know if a file is a directory. */
40 #ifndef HAVE_STAT
41 #  ifdef HAVE__STAT
42      /* MS C library seems to define stat and _stat. The definition
43         is identical. Still, mapping them to each other causes a warning. */
44 #    ifndef _MSC_VER
45 #      define stat(x,y) _stat(x,y)
46 #    endif
47 #    define HAVE_STAT
48 #  endif
49 #endif
50 #ifdef HAVE_STAT
51 #  ifndef S_ISDIR
52 #    ifdef _S_ISDIR
53 #      define S_ISDIR(x) _S_ISDIR(x)
54 #    else
55 #      ifdef S_IFDIR
56 #        ifndef S_IFMT
57 #          ifdef _S_IFMT
58 #            define S_IFMT _S_IFMT
59 #          endif
60 #        endif
61 #        ifdef S_IFMT
62 #          define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
63 #        endif
64 #      endif
65 #    endif
66 #  endif
67 #endif
68
69 #include <libxml/xmlmemory.h>
70 #include <libxml/parser.h>
71 #include <libxml/parserInternals.h>
72 #include <libxml/xmlIO.h>
73 #include <libxml/uri.h>
74 #include <libxml/nanohttp.h>
75 #include <libxml/nanoftp.h>
76 #include <libxml/xmlerror.h>
77 #ifdef LIBXML_CATALOG_ENABLED
78 #include <libxml/catalog.h>
79 #endif
80 #include <libxml/globals.h>
81
82 /* #define VERBOSE_FAILURE */
83 /* #define DEBUG_EXTERNAL_ENTITIES */
84 /* #define DEBUG_INPUT */
85
86 #ifdef DEBUG_INPUT
87 #define MINLEN 40
88 #else
89 #define MINLEN 4000
90 #endif
91
92 /*
93  * Input I/O callback sets
94  */
95 typedef struct _xmlInputCallback {
96     xmlInputMatchCallback matchcallback;
97     xmlInputOpenCallback opencallback;
98     xmlInputReadCallback readcallback;
99     xmlInputCloseCallback closecallback;
100 } xmlInputCallback;
101
102 #define MAX_INPUT_CALLBACK 15
103
104 static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK];
105 static int xmlInputCallbackNr = 0;
106 static int xmlInputCallbackInitialized = 0;
107
108 /*
109  * Output I/O callback sets
110  */
111 typedef struct _xmlOutputCallback {
112     xmlOutputMatchCallback matchcallback;
113     xmlOutputOpenCallback opencallback;
114     xmlOutputWriteCallback writecallback;
115     xmlOutputCloseCallback closecallback;
116 } xmlOutputCallback;
117
118 #define MAX_OUTPUT_CALLBACK 15
119
120 static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK];
121 static int xmlOutputCallbackNr = 0;
122 static int xmlOutputCallbackInitialized = 0;
123
124
125 /**
126  * xmlNormalizeWindowsPath:
127  * @path: the input file path
128  *
129  * This function is obsolete. Please see xmlURIFromPath in uri.c for
130  * a better solution.
131  *
132  * Returns a canonicalized version of the path
133  */
134 xmlChar *
135 xmlNormalizeWindowsPath(const xmlChar *path)
136 {
137     return xmlCanonicPath(path);
138 }
139
140 /**
141  * xmlCleanupInputCallbacks:
142  *
143  * clears the entire input callback table. this includes the
144  * compiled-in I/O. 
145  */
146 void
147 xmlCleanupInputCallbacks(void)
148 {
149     int i;
150
151     if (!xmlInputCallbackInitialized)
152         return;
153
154     for (i = xmlInputCallbackNr - 1; i >= 0; i--) {
155         xmlInputCallbackTable[i].matchcallback = NULL;
156         xmlInputCallbackTable[i].opencallback = NULL;
157         xmlInputCallbackTable[i].readcallback = NULL;
158         xmlInputCallbackTable[i].closecallback = NULL;
159     }
160     xmlInputCallbackInitialized = 0;
161
162     xmlInputCallbackNr = 0;
163     xmlInputCallbackInitialized = 0;
164 }
165
166 /**
167  * xmlCleanupOutputCallbacks:
168  *
169  * clears the entire output callback table. this includes the
170  * compiled-in I/O callbacks. 
171  */
172 void
173 xmlCleanupOutputCallbacks(void)
174 {
175     int i;
176
177     if (!xmlOutputCallbackInitialized)
178         return;
179
180     for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
181         xmlOutputCallbackTable[i].matchcallback = NULL;
182         xmlOutputCallbackTable[i].opencallback = NULL;
183         xmlOutputCallbackTable[i].writecallback = NULL;
184         xmlOutputCallbackTable[i].closecallback = NULL;
185     }
186     xmlOutputCallbackInitialized = 0;
187
188     xmlOutputCallbackNr = 0;
189     xmlOutputCallbackInitialized = 0;
190 }
191
192 /************************************************************************
193  *                                                                      *
194  *              Standard I/O for file accesses                          *
195  *                                                                      *
196  ************************************************************************/
197
198 /**
199  * xmlCheckFilename:
200  * @path:  the path to check
201  *
202  * function checks to see if @path is a valid source
203  * (file, socket...) for XML.
204  *
205  * if stat is not available on the target machine,
206  * returns 1.  if stat fails, returns 0 (if calling
207  * stat on the filename fails, it can't be right).
208  * if stat succeeds and the file is a directory,
209  * returns 2.  otherwise returns 1.
210  */
211
212 int
213 xmlCheckFilename (const char *path)
214 {
215 #ifdef HAVE_STAT
216     struct stat stat_buffer;
217
218     if (stat(path, &stat_buffer) == -1)
219         return 0;
220
221 #ifdef S_ISDIR
222     if (S_ISDIR(stat_buffer.st_mode)) {
223         return 2;
224     }
225 #endif
226 #endif
227     return 1;
228 }
229
230 static int
231 xmlNop(void) {
232     return(0);
233 }
234
235 /**
236  * xmlFdRead:
237  * @context:  the I/O context
238  * @buffer:  where to drop data
239  * @len:  number of bytes to read
240  *
241  * Read @len bytes to @buffer from the I/O channel.
242  *
243  * Returns the number of bytes written
244  */
245 static int
246 xmlFdRead (void * context, char * buffer, int len) {
247     return(read((int) (long) context, &buffer[0], len));
248 }
249
250 /**
251  * xmlFdWrite:
252  * @context:  the I/O context
253  * @buffer:  where to get data
254  * @len:  number of bytes to write
255  *
256  * Write @len bytes from @buffer to the I/O channel.
257  *
258  * Returns the number of bytes written
259  */
260 static int
261 xmlFdWrite (void * context, const char * buffer, int len) {
262     return(write((int) (long) context, &buffer[0], len));
263 }
264
265 /**
266  * xmlFdClose:
267  * @context:  the I/O context
268  *
269  * Close an I/O channel
270  *
271  * Returns 0 in case of success and error code otherwise
272  */
273 static int
274 xmlFdClose (void * context) {
275     return ( close((int) (long) context) );
276 }
277
278 /**
279  * xmlFileMatch:
280  * @filename:  the URI for matching
281  *
282  * input from FILE *
283  *
284  * Returns 1 if matches, 0 otherwise
285  */
286 int
287 xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
288     return(1);
289 }
290
291 /**
292  * xmlFileOpen_real:
293  * @filename:  the URI for matching
294  *
295  * input from FILE *, supports compressed input
296  * if @filename is " " then the standard input is used
297  *
298  * Returns an I/O context or NULL in case of error
299  */
300 static void *
301 xmlFileOpen_real (const char *filename) {
302     const char *path = NULL;
303     FILE *fd;
304
305     if (!strcmp(filename, "-")) {
306         fd = stdin;
307         return((void *) fd);
308     }
309
310     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
311 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
312         path = &filename[17];
313 #else
314         path = &filename[16];
315 #endif
316     else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
317 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
318         path = &filename[8];
319 #else
320         path = &filename[7];
321 #endif
322     } else 
323         path = filename;
324
325     if (path == NULL)
326         return(NULL);
327     if (!xmlCheckFilename(path))
328         return(NULL);
329
330 #if defined(WIN32) || defined (__DJGPP__) && !defined (__CYGWIN__)
331     fd = fopen(path, "rb");
332 #else
333     fd = fopen(path, "r");
334 #endif /* WIN32 */
335     return((void *) fd);
336 }
337
338 /**
339  * xmlFileOpen:
340  * @filename:  the URI for matching
341  *
342  * Wrapper around xmlFileOpen_real that try it with an unescaped
343  * version of @filename, if this fails fallback to @filename
344  *
345  * Returns a handler or NULL in case or failure
346  */
347 void *
348 xmlFileOpen (const char *filename) {
349     char *unescaped;
350     void *retval;
351     unescaped = xmlURIUnescapeString(filename, 0, NULL);
352     if (unescaped != NULL) {
353         retval = xmlFileOpen_real(unescaped);
354     } else {
355         retval = xmlFileOpen_real(filename);
356     }
357     xmlFree(unescaped);
358     return retval;
359 }
360
361 /**
362  * xmlFileOpenW:
363  * @filename:  the URI for matching
364  *
365  * output to from FILE *,
366  * if @filename is "-" then the standard output is used
367  *
368  * Returns an I/O context or NULL in case of error
369  */
370 static void *
371 xmlFileOpenW (const char *filename) {
372     const char *path = NULL;
373     FILE *fd;
374
375     if (!strcmp(filename, "-")) {
376         fd = stdout;
377         return((void *) fd);
378     }
379
380     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
381 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
382         path = &filename[17];
383 #else
384         path = &filename[16];
385 #endif
386     else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
387 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
388         path = &filename[8];
389 #else
390         path = &filename[7];
391 #endif
392     } else 
393         path = filename;
394
395     if (path == NULL)
396         return(NULL);
397
398     fd = fopen(path, "wb");
399     return((void *) fd);
400 }
401
402 /**
403  * xmlFileRead:
404  * @context:  the I/O context
405  * @buffer:  where to drop data
406  * @len:  number of bytes to write
407  *
408  * Read @len bytes to @buffer from the I/O channel.
409  *
410  * Returns the number of bytes written
411  */
412 int
413 xmlFileRead (void * context, char * buffer, int len) {
414     return(fread(&buffer[0], 1,  len, (FILE *) context));
415 }
416
417 /**
418  * xmlFileWrite:
419  * @context:  the I/O context
420  * @buffer:  where to drop data
421  * @len:  number of bytes to write
422  *
423  * Write @len bytes from @buffer to the I/O channel.
424  *
425  * Returns the number of bytes written
426  */
427 static int
428 xmlFileWrite (void * context, const char * buffer, int len) {
429     int items;
430
431     items = fwrite(&buffer[0], len, 1, (FILE *) context);
432
433     return(items * len);
434 }
435
436 /**
437  * xmlFileClose:
438  * @context:  the I/O context
439  *
440  * Close an I/O channel
441  *
442  * Returns 0 or -1 in case of error
443  */
444 int
445 xmlFileClose (void * context) {
446     FILE *fil;
447
448     fil = (FILE *) context;
449     if (fil == stdin)
450         return(0);
451     if (fil == stdout)
452         return(0);
453     if (fil == stderr)
454         return(0);
455     return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 );
456 }
457
458 /**
459  * xmlFileFlush:
460  * @context:  the I/O context
461  *
462  * Flush an I/O channel
463  */
464 static int
465 xmlFileFlush (void * context) {
466     return ( ( fflush((FILE *) context) == EOF ) ? -1 : 0 );
467 }
468
469 #ifdef HAVE_ZLIB_H
470 /************************************************************************
471  *                                                                      *
472  *              I/O for compressed file accesses                        *
473  *                                                                      *
474  ************************************************************************/
475 /**
476  * xmlGzfileMatch:
477  * @filename:  the URI for matching
478  *
479  * input from compressed file test
480  *
481  * Returns 1 if matches, 0 otherwise
482  */
483 static int
484 xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
485     return(1);
486 }
487
488 /**
489  * xmlGzfileOpen_real:
490  * @filename:  the URI for matching
491  *
492  * input from compressed file open
493  * if @filename is " " then the standard input is used
494  *
495  * Returns an I/O context or NULL in case of error
496  */
497 static void *
498 xmlGzfileOpen_real (const char *filename) {
499     const char *path = NULL;
500     gzFile fd;
501
502     if (!strcmp(filename, "-")) {
503         fd = gzdopen(dup(0), "rb");
504         return((void *) fd);
505     }
506
507     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
508 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
509         path = &filename[17];
510 #else
511         path = &filename[16];
512 #endif
513     else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
514 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
515         path = &filename[8];
516 #else
517         path = &filename[7];
518 #endif
519     } else 
520         path = filename;
521
522     if (path == NULL)
523         return(NULL);
524     if (!xmlCheckFilename(path))
525         return(NULL);
526
527     fd = gzopen(path, "rb");
528     return((void *) fd);
529 }
530
531 /**
532  * xmlGzfileOpen:
533  * @filename:  the URI for matching
534  *
535  * Wrapper around xmlGzfileOpen if the open fais, it will
536  * try to unescape @filename
537  */
538 static void *
539 xmlGzfileOpen (const char *filename) {
540     char *unescaped;
541     void *retval;
542
543     retval = xmlGzfileOpen_real(filename);
544     if (retval == NULL) {
545         unescaped = xmlURIUnescapeString(filename, 0, NULL);
546         if (unescaped != NULL) {
547             retval = xmlGzfileOpen_real(unescaped);
548         }
549         xmlFree(unescaped);
550     }
551     return retval;
552 }
553
554 /**
555  * xmlGzfileOpenW:
556  * @filename:  the URI for matching
557  * @compression:  the compression factor (0 - 9 included)
558  *
559  * input from compressed file open
560  * if @filename is " " then the standard input is used
561  *
562  * Returns an I/O context or NULL in case of error
563  */
564 static void *
565 xmlGzfileOpenW (const char *filename, int compression) {
566     const char *path = NULL;
567     char mode[15];
568     gzFile fd;
569
570     snprintf(mode, sizeof(mode), "wb%d", compression);
571     if (!strcmp(filename, "-")) {
572         fd = gzdopen(dup(1), mode);
573         return((void *) fd);
574     }
575
576     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
577 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
578         path = &filename[17];
579 #else
580         path = &filename[16];
581 #endif
582     else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
583 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
584         path = &filename[8];
585 #else
586         path = &filename[7];
587 #endif
588     } else 
589         path = filename;
590
591     if (path == NULL)
592         return(NULL);
593
594     fd = gzopen(path, mode);
595     return((void *) fd);
596 }
597
598 /**
599  * xmlGzfileRead:
600  * @context:  the I/O context
601  * @buffer:  where to drop data
602  * @len:  number of bytes to write
603  *
604  * Read @len bytes to @buffer from the compressed I/O channel.
605  *
606  * Returns the number of bytes written
607  */
608 static int
609 xmlGzfileRead (void * context, char * buffer, int len) {
610     return(gzread((gzFile) context, &buffer[0], len));
611 }
612
613 /**
614  * xmlGzfileWrite:
615  * @context:  the I/O context
616  * @buffer:  where to drop data
617  * @len:  number of bytes to write
618  *
619  * Write @len bytes from @buffer to the compressed I/O channel.
620  *
621  * Returns the number of bytes written
622  */
623 static int
624 xmlGzfileWrite (void * context, const char * buffer, int len) {
625     return(gzwrite((gzFile) context, (char *) &buffer[0], len));
626 }
627
628 /**
629  * xmlGzfileClose:
630  * @context:  the I/O context
631  *
632  * Close a compressed I/O channel
633  */
634 static int
635 xmlGzfileClose (void * context) {
636     return ( ( gzclose((gzFile) context) == Z_OK ) ? 0 : -1 );
637 }
638 #endif /* HAVE_ZLIB_H */
639
640 #ifdef LIBXML_HTTP_ENABLED
641 /************************************************************************
642  *                                                                      *
643  *                      I/O for HTTP file accesses                      *
644  *                                                                      *
645  ************************************************************************/
646
647 typedef struct xmlIOHTTPWriteCtxt_
648 {
649     int                 compression;
650
651     char *              uri;
652
653     void *              doc_buff;
654
655 } xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr;
656
657 #ifdef HAVE_ZLIB_H
658
659 #define DFLT_WBITS              ( -15 )
660 #define DFLT_MEM_LVL            ( 8 )
661 #define GZ_MAGIC1               ( 0x1f )
662 #define GZ_MAGIC2               ( 0x8b )
663 #define LXML_ZLIB_OS_CODE       ( 0x03 )
664 #define INIT_HTTP_BUFF_SIZE     ( 32768 )
665 #define DFLT_ZLIB_RATIO         ( 5 )
666
667 /*
668 **  Data structure and functions to work with sending compressed data
669 **  via HTTP.
670 */
671
672 typedef struct xmlZMemBuff_
673 {
674    unsigned long        size;
675    unsigned long        crc;
676
677    unsigned char *      zbuff;
678    z_stream             zctrl;
679
680 } xmlZMemBuff, *xmlZMemBuffPtr;
681
682 /**
683  * append_reverse_ulong
684  * @buff:  Compressed memory buffer
685  * @data:  Unsigned long to append
686  *
687  * Append a unsigned long in reverse byte order to the end of the
688  * memory buffer.
689  */
690 static void
691 append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) {
692
693     int         idx;
694
695     if ( buff == NULL )
696         return;
697
698     /*
699     **  This is plagiarized from putLong in gzio.c (zlib source) where
700     **  the number "4" is hardcoded.  If zlib is ever patched to 
701     **  support 64 bit file sizes, this code would need to be patched
702     **  as well.
703     */
704
705     for ( idx = 0; idx < 4; idx++ ) {
706         *buff->zctrl.next_out = ( data & 0xff );
707         data >>= 8;
708         buff->zctrl.next_out++;
709     }
710
711     return;
712 }
713
714 /**
715  *
716  * xmlFreeZMemBuff
717  * @buff:  The memory buffer context to clear
718  *
719  * Release all the resources associated with the compressed memory buffer.
720  */
721 static void
722 xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
723
724 #ifdef DEBUG_HTTP
725     int z_err;
726 #endif
727
728     if ( buff == NULL )
729         return;
730
731     xmlFree( buff->zbuff );
732 #ifdef DEBUG_HTTP
733     z_err = deflateEnd( &buff->zctrl );
734     if ( z_err != Z_OK )
735         xmlGenericError( xmlGenericErrorContext,
736                         "xmlFreeZMemBuff:  Error releasing zlib context:  %d\n",
737                         z_err );
738 #else
739     deflateEnd( &buff->zctrl );
740 #endif
741
742     xmlFree( buff );
743     return;
744 }
745
746 /**
747  * xmlCreateZMemBuff
748  *@compression: Compression value to use
749  *
750  * Create a memory buffer to hold the compressed XML document.  The
751  * compressed document in memory will end up being identical to what
752  * would be created if gzopen/gzwrite/gzclose were being used to 
753  * write the document to disk.  The code for the header/trailer data to
754  * the compression is plagiarized from the zlib source files.
755  */
756 static void *
757 xmlCreateZMemBuff( int compression ) {
758
759     int                 z_err;
760     int                 hdr_lgth;
761     xmlZMemBuffPtr      buff = NULL;
762
763     if ( ( compression < 1 ) || ( compression > 9 ) )
764         return ( NULL );
765
766     /*  Create the control and data areas  */
767
768     buff = xmlMalloc( sizeof( xmlZMemBuff ) );
769     if ( buff == NULL ) {
770         xmlGenericError( xmlGenericErrorContext,
771                         "xmlCreateZMemBuff:  %s\n",
772                         "Failure allocating buffer context." );
773         return ( NULL );
774     }
775
776     (void)memset( buff, 0, sizeof( xmlZMemBuff ) );
777     buff->size = INIT_HTTP_BUFF_SIZE;
778     buff->zbuff = xmlMalloc( buff->size );
779     if ( buff->zbuff == NULL ) {
780         xmlFreeZMemBuff( buff );
781         xmlGenericError( xmlGenericErrorContext,
782                         "xmlCreateZMemBuff:  %s\n",
783                         "Failure allocating data buffer." );
784         return ( NULL );
785     }
786
787     z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
788                             DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
789     if ( z_err != Z_OK ) {
790         xmlFreeZMemBuff( buff );
791         buff = NULL;
792         xmlGenericError( xmlGenericErrorContext,
793                         "xmlCreateZMemBuff:  %s %d\n",
794                         "Error initializing compression context.  ZLIB error:",
795                         z_err );
796         return ( NULL );
797     }
798
799     /*  Set the header data.  The CRC will be needed for the trailer  */
800     buff->crc = crc32( 0L, Z_NULL, 0 );
801     hdr_lgth = snprintf( (char *)buff->zbuff, buff->size,
802                         "%c%c%c%c%c%c%c%c%c%c",
803                         GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED, 
804                         0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE );
805     buff->zctrl.next_out  = buff->zbuff + hdr_lgth;
806     buff->zctrl.avail_out = buff->size - hdr_lgth;
807
808     return ( buff );
809 }
810
811 /**
812  * xmlZMemBuffExtend
813  * @buff:  Buffer used to compress and consolidate data.
814  * @ext_amt:   Number of bytes to extend the buffer.
815  *
816  * Extend the internal buffer used to store the compressed data by the
817  * specified amount.
818  *
819  * Returns 0 on success or -1 on failure to extend the buffer.  On failure
820  * the original buffer still exists at the original size.
821  */
822 static int
823 xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) {
824
825     int                 rc = -1;
826     size_t              new_size;
827     size_t              cur_used;
828
829     unsigned char *     tmp_ptr = NULL;
830
831     if ( buff == NULL )
832         return ( -1 );
833
834     else if ( ext_amt == 0 )
835         return ( 0 );
836
837     cur_used = buff->zctrl.next_out - buff->zbuff;
838     new_size = buff->size + ext_amt;
839
840 #ifdef DEBUG_HTTP
841     if ( cur_used > new_size ) 
842         xmlGenericError( xmlGenericErrorContext,
843                         "xmlZMemBuffExtend:  %s\n%s %d bytes.\n",
844                         "Buffer overwrite detected during compressed memory",
845                         "buffer extension.  Overflowed by", 
846                         (cur_used - new_size ) );
847 #endif
848
849     tmp_ptr = xmlRealloc( buff->zbuff, new_size );
850     if ( tmp_ptr != NULL ) {
851         rc = 0;
852         buff->size  = new_size;
853         buff->zbuff = tmp_ptr;
854         buff->zctrl.next_out  = tmp_ptr + cur_used;
855         buff->zctrl.avail_out = new_size - cur_used;
856     }
857     else {
858         xmlGenericError( xmlGenericErrorContext,
859                         "xmlZMemBuffExtend:  %s %lu bytes.\n",
860                         "Allocation failure extending output buffer to",
861                         new_size );
862     }
863
864     return ( rc );
865 }
866
867 /**
868  * xmlZMemBuffAppend
869  * @buff:  Buffer used to compress and consolidate data
870  * @src:   Uncompressed source content to append to buffer
871  * @len:   Length of source data to append to buffer
872  *
873  * Compress and append data to the internal buffer.  The data buffer
874  * will be expanded if needed to store the additional data.
875  *
876  * Returns the number of bytes appended to the buffer or -1 on error.
877  */
878 static int
879 xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) {
880
881     int         z_err;
882     size_t      min_accept;
883
884     if ( ( buff == NULL ) || ( src == NULL ) )
885         return ( -1 );
886
887     buff->zctrl.avail_in = len;
888     buff->zctrl.next_in  = (unsigned char *)src;
889     while ( buff->zctrl.avail_in > 0 ) {
890         /*
891         **  Extend the buffer prior to deflate call if a reasonable amount
892         **  of output buffer space is not available.
893         */
894         min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO;
895         if ( buff->zctrl.avail_out <= min_accept ) {
896             if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
897                 return ( -1 );
898         }
899
900         z_err = deflate( &buff->zctrl, Z_NO_FLUSH );
901         if ( z_err != Z_OK ) {
902             xmlGenericError( xmlGenericErrorContext,
903                         "xmlZMemBuffAppend:  %s %d %s - %d",
904                         "Compression error while appending",
905                         len, "bytes to buffer.  ZLIB error", z_err );
906             return ( -1 );
907         }
908     }
909
910     buff->crc = crc32( buff->crc, (unsigned char *)src, len );
911
912     return ( len );
913 }
914
915 /**
916  * xmlZMemBuffGetContent
917  * @buff:  Compressed memory content buffer
918  * @data_ref:  Pointer reference to point to compressed content
919  *
920  * Flushes the compression buffers, appends gzip file trailers and
921  * returns the compressed content and length of the compressed data.
922  * NOTE:  The gzip trailer code here is plagiarized from zlib source.
923  *
924  * Returns the length of the compressed data or -1 on error.
925  */
926 static int
927 xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) {
928
929     int         zlgth = -1;
930     int         z_err;
931
932     if ( ( buff == NULL ) || ( data_ref == NULL ) )
933         return ( -1 );
934
935     /*  Need to loop until compression output buffers are flushed  */
936
937     do
938     {
939         z_err = deflate( &buff->zctrl, Z_FINISH );
940         if ( z_err == Z_OK ) {
941             /*  In this case Z_OK means more buffer space needed  */
942
943             if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
944                 return ( -1 );
945         }
946     }
947     while ( z_err == Z_OK );
948
949     /*  If the compression state is not Z_STREAM_END, some error occurred  */
950
951     if ( z_err == Z_STREAM_END ) {
952
953         /*  Need to append the gzip data trailer  */
954
955         if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) {
956             if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 )
957                 return ( -1 );
958         }
959
960         /*
961         **  For whatever reason, the CRC and length data are pushed out
962         **  in reverse byte order.  So a memcpy can't be used here.
963         */
964
965         append_reverse_ulong( buff, buff->crc );
966         append_reverse_ulong( buff, buff->zctrl.total_in );
967
968         zlgth = buff->zctrl.next_out - buff->zbuff;
969         *data_ref = (char *)buff->zbuff;
970     }
971
972     else
973         xmlGenericError( xmlGenericErrorContext,
974                         "xmlZMemBuffGetContent:  %s - %d\n",
975                         "Error flushing zlib buffers.  Error code", z_err );
976     
977     return ( zlgth );
978 }
979 #endif  /*  HAVE_ZLIB_H  */
980
981 /**
982  * xmlFreeHTTPWriteCtxt
983  * @ctxt:  Context to cleanup
984  *
985  * Free allocated memory and reclaim system resources.
986  *
987  * No return value.
988  */
989 static void
990 xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
991 {
992     if ( ctxt->uri != NULL )
993         xmlFree( ctxt->uri );
994
995     if ( ctxt->doc_buff != NULL ) {
996
997 #ifdef HAVE_ZLIB_H
998         if ( ctxt->compression > 0 ) {
999             xmlFreeZMemBuff( ctxt->doc_buff );
1000         }
1001         else
1002 #endif
1003         {
1004             xmlOutputBufferClose( ctxt->doc_buff );
1005         }
1006     }
1007
1008     xmlFree( ctxt );
1009     return;
1010 }
1011
1012
1013 /**
1014  * xmlIOHTTPMatch:
1015  * @filename:  the URI for matching
1016  *
1017  * check if the URI matches an HTTP one
1018  *
1019  * Returns 1 if matches, 0 otherwise
1020  */
1021 int
1022 xmlIOHTTPMatch (const char *filename) {
1023     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7))
1024         return(1);
1025     return(0);
1026 }
1027
1028 /**
1029  * xmlIOHTTPOpen:
1030  * @filename:  the URI for matching
1031  *
1032  * open an HTTP I/O channel
1033  *
1034  * Returns an I/O context or NULL in case of error
1035  */
1036 void *
1037 xmlIOHTTPOpen (const char *filename) {
1038     return(xmlNanoHTTPOpen(filename, NULL));
1039 }
1040
1041 /**
1042  * xmlIOHTTPOpenW:
1043  * @post_uri:  The destination URI for the document
1044  * @compression:  The compression desired for the document.
1045  *
1046  * Open a temporary buffer to collect the document for a subsequent HTTP POST
1047  * request.  Non-static as is called from the output buffer creation routine.
1048  *
1049  * Returns an I/O context or NULL in case of error.
1050  */
1051
1052 void *
1053 xmlIOHTTPOpenW(const char *post_uri, int compression)
1054 {
1055
1056     xmlIOHTTPWriteCtxtPtr ctxt = NULL;
1057
1058     if (post_uri == NULL)
1059         return (NULL);
1060
1061     ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt));
1062     if (ctxt == NULL) {
1063         xmlGenericError(xmlGenericErrorContext,
1064                     "xmlIOHTTPOpenW:  Failed to create output HTTP context.\n");
1065         return (NULL);
1066     }
1067
1068     (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt));
1069
1070     ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri);
1071     if (ctxt->uri == NULL) {
1072         xmlGenericError(xmlGenericErrorContext,
1073                     "xmlIOHTTPOpenW:  Failed to duplicate destination URI.\n");
1074         xmlFreeHTTPWriteCtxt(ctxt);
1075         return (NULL);
1076     }
1077
1078     /*
1079      * **  Since the document length is required for an HTTP post,
1080      * **  need to put the document into a buffer.  A memory buffer
1081      * **  is being used to avoid pushing the data to disk and back.
1082      */
1083
1084 #ifdef HAVE_ZLIB_H
1085     if ((compression > 0) && (compression <= 9)) {
1086
1087         ctxt->compression = compression;
1088         ctxt->doc_buff = xmlCreateZMemBuff(compression);
1089     } else
1090 #endif
1091     {
1092         /*  Any character conversions should have been done before this  */
1093
1094         ctxt->doc_buff = xmlAllocOutputBuffer(NULL);
1095     }
1096
1097     if (ctxt->doc_buff == NULL) {
1098         xmlFreeHTTPWriteCtxt(ctxt);
1099         ctxt = NULL;
1100     }
1101
1102     return (ctxt);
1103 }
1104                                 
1105 /**
1106  * xmlIOHTTPDfltOpenW
1107  * @post_uri:  The destination URI for this document.
1108  *
1109  * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent
1110  * HTTP post command.  This function should generally not be used as
1111  * the open callback is short circuited in xmlOutputBufferCreateFile.
1112  *
1113  * Returns a pointer to the new IO context.
1114  */
1115 static void *
1116 xmlIOHTTPDfltOpenW( const char * post_uri ) {
1117     return ( xmlIOHTTPOpenW( post_uri, 0 ) );
1118 }
1119
1120 /**
1121  * xmlIOHTTPRead:
1122  * @context:  the I/O context
1123  * @buffer:  where to drop data
1124  * @len:  number of bytes to write
1125  *
1126  * Read @len bytes to @buffer from the I/O channel.
1127  *
1128  * Returns the number of bytes written
1129  */
1130 int 
1131 xmlIOHTTPRead(void * context, char * buffer, int len) {
1132     return(xmlNanoHTTPRead(context, &buffer[0], len));
1133 }
1134
1135 /**
1136  * xmlIOHTTPWrite
1137  * @context:  previously opened writing context
1138  * @buffer:   data to output to temporary buffer
1139  * @len:      bytes to output
1140  *
1141  * Collect data from memory buffer into a temporary file for later
1142  * processing.
1143  *
1144  * Returns number of bytes written.
1145  */
1146
1147 static int
1148 xmlIOHTTPWrite( void * context, const char * buffer, int len ) { 
1149
1150     xmlIOHTTPWriteCtxtPtr       ctxt = context;
1151
1152     if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) )
1153         return ( -1 );
1154
1155     if ( len > 0 ) {
1156
1157         /*  Use gzwrite or fwrite as previously setup in the open call  */
1158
1159 #ifdef HAVE_ZLIB_H
1160         if ( ctxt->compression > 0 ) 
1161             len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len );
1162
1163         else
1164 #endif
1165             len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer );
1166
1167         if ( len < 0 ) {
1168             xmlGenericError( xmlGenericErrorContext,
1169                         "xmlIOHTTPWrite:  %s\n%s '%s'.\n",
1170                         "Error appending to internal buffer.",
1171                         "Error sending document to URI",
1172                         ctxt->uri );
1173         }
1174     }
1175
1176     return ( len );
1177 }
1178
1179
1180 /**
1181  * xmlIOHTTPClose:
1182  * @context:  the I/O context
1183  *
1184  * Close an HTTP I/O channel
1185  *
1186  * Returns 0
1187  */
1188 int
1189 xmlIOHTTPClose (void * context) {
1190     xmlNanoHTTPClose(context);
1191     return 0;
1192 }
1193
1194 /**
1195  * xmlIOHTTCloseWrite
1196  * @context:  The I/O context
1197  * @http_mthd: The HTTP method to be used when sending the data
1198  *
1199  * Close the transmit HTTP I/O channel and actually send the data.
1200  */
1201 static int
1202 xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) {
1203
1204     int                         close_rc = -1;
1205     int                         http_rtn = 0;
1206     int                         content_lgth = 0;
1207     xmlIOHTTPWriteCtxtPtr       ctxt = context;
1208
1209     char *                      http_content = NULL;
1210     char *                      content_encoding = NULL;
1211     char *                      content_type = (char *) "text/xml";
1212     void *                      http_ctxt = NULL;
1213
1214     if ( ( ctxt == NULL ) || ( http_mthd == NULL ) )
1215         return ( -1 );
1216
1217     /*  Retrieve the content from the appropriate buffer  */
1218
1219 #ifdef HAVE_ZLIB_H
1220
1221     if ( ctxt->compression > 0 ) {
1222         content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content );
1223         content_encoding = (char *) "Content-Encoding: gzip";
1224     }
1225     else
1226 #endif
1227     {
1228         /*  Pull the data out of the memory output buffer  */
1229
1230         xmlOutputBufferPtr      dctxt = ctxt->doc_buff;
1231         http_content = (char *)dctxt->buffer->content;
1232         content_lgth = dctxt->buffer->use;
1233     }
1234
1235     if ( http_content == NULL ) {
1236         xmlGenericError( xmlGenericErrorContext,
1237                         "xmlIOHTTPCloseWrite:  %s '%s' %s '%s'.\n",
1238                         "Error retrieving content.\nUnable to",
1239                         http_mthd, "data to URI", ctxt->uri );
1240     }
1241
1242     else {
1243
1244         http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content,
1245                                         &content_type, content_encoding, 
1246                                         content_lgth );
1247
1248         if ( http_ctxt != NULL ) {
1249 #ifdef DEBUG_HTTP
1250             /*  If testing/debugging - dump reply with request content  */
1251
1252             FILE *      tst_file = NULL;
1253             char        buffer[ 4096 ];
1254             char *      dump_name = NULL;
1255             int         avail;
1256
1257             xmlGenericError( xmlGenericErrorContext,
1258                         "xmlNanoHTTPCloseWrite:  HTTP %s to\n%s returned %d.\n",
1259                         http_mthd, ctxt->uri,
1260                         xmlNanoHTTPReturnCode( http_ctxt ) );
1261
1262             /*
1263             **  Since either content or reply may be gzipped,
1264             **  dump them to separate files instead of the 
1265             **  standard error context.
1266             */
1267
1268             dump_name = tempnam( NULL, "lxml" );
1269             if ( dump_name != NULL ) {
1270                 (void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name );
1271
1272                 tst_file = fopen( buffer, "wb" );
1273                 if ( tst_file != NULL ) {
1274                     xmlGenericError( xmlGenericErrorContext,
1275                         "Transmitted content saved in file:  %s\n", buffer );
1276
1277                     fwrite( http_content, sizeof( char ),
1278                                         content_lgth, tst_file );
1279                     fclose( tst_file );
1280                 }
1281
1282                 (void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name );
1283                 tst_file = fopen( buffer, "wb" );
1284                 if ( tst_file != NULL ) {
1285                     xmlGenericError( xmlGenericErrorContext,
1286                         "Reply content saved in file:  %s\n", buffer );
1287
1288
1289                     while ( (avail = xmlNanoHTTPRead( http_ctxt,
1290                                         buffer, sizeof( buffer ) )) > 0 ) {
1291
1292                         fwrite( buffer, sizeof( char ), avail, tst_file );
1293                     }
1294
1295                     fclose( tst_file );
1296                 }
1297
1298                 free( dump_name );
1299             }
1300 #endif  /*  DEBUG_HTTP  */
1301
1302             http_rtn = xmlNanoHTTPReturnCode( http_ctxt );
1303             if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) )
1304                 close_rc = 0;
1305             else
1306                 xmlGenericError( xmlGenericErrorContext,
1307                             "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
1308                             http_mthd, content_lgth,
1309                             "bytes to URI", ctxt->uri,
1310                             "failed.  HTTP return code:", http_rtn );
1311
1312             xmlNanoHTTPClose( http_ctxt );
1313             xmlFree( content_type );
1314         }
1315     }
1316
1317     /*  Final cleanups  */
1318
1319     xmlFreeHTTPWriteCtxt( ctxt );
1320
1321     return ( close_rc );
1322 }
1323
1324 /**
1325  * xmlIOHTTPClosePut
1326  *
1327  * @context:  The I/O context
1328  *
1329  * Close the transmit HTTP I/O channel and actually send data using a PUT
1330  * HTTP method.
1331  */
1332 static int
1333 xmlIOHTTPClosePut( void * ctxt ) {
1334     return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) );
1335 }
1336
1337
1338 /**
1339  * xmlIOHTTPClosePost
1340  *
1341  * @context:  The I/O context
1342  *
1343  * Close the transmit HTTP I/O channel and actually send data using a POST
1344  * HTTP method.
1345  */
1346 static int
1347 xmlIOHTTPClosePost( void * ctxt ) {
1348     return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) );
1349 }
1350
1351 #endif /* LIBXML_HTTP_ENABLED */
1352
1353 #ifdef LIBXML_FTP_ENABLED
1354 /************************************************************************
1355  *                                                                      *
1356  *                      I/O for FTP file accesses                       *
1357  *                                                                      *
1358  ************************************************************************/
1359 /**
1360  * xmlIOFTPMatch:
1361  * @filename:  the URI for matching
1362  *
1363  * check if the URI matches an FTP one
1364  *
1365  * Returns 1 if matches, 0 otherwise
1366  */
1367 int
1368 xmlIOFTPMatch (const char *filename) {
1369     if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6))
1370         return(1);
1371     return(0);
1372 }
1373
1374 /**
1375  * xmlIOFTPOpen:
1376  * @filename:  the URI for matching
1377  *
1378  * open an FTP I/O channel
1379  *
1380  * Returns an I/O context or NULL in case of error
1381  */
1382 void *
1383 xmlIOFTPOpen (const char *filename) {
1384     return(xmlNanoFTPOpen(filename));
1385 }
1386
1387 /**
1388  * xmlIOFTPRead:
1389  * @context:  the I/O context
1390  * @buffer:  where to drop data
1391  * @len:  number of bytes to write
1392  *
1393  * Read @len bytes to @buffer from the I/O channel.
1394  *
1395  * Returns the number of bytes written
1396  */
1397 int 
1398 xmlIOFTPRead(void * context, char * buffer, int len) {
1399     return(xmlNanoFTPRead(context, &buffer[0], len));
1400 }
1401
1402 /**
1403  * xmlIOFTPClose:
1404  * @context:  the I/O context
1405  *
1406  * Close an FTP I/O channel
1407  *
1408  * Returns 0
1409  */
1410 int
1411 xmlIOFTPClose (void * context) {
1412     return ( xmlNanoFTPClose(context) );
1413 }
1414 #endif /* LIBXML_FTP_ENABLED */
1415
1416
1417 /**
1418  * xmlRegisterInputCallbacks:
1419  * @matchFunc:  the xmlInputMatchCallback
1420  * @openFunc:  the xmlInputOpenCallback
1421  * @readFunc:  the xmlInputReadCallback
1422  * @closeFunc:  the xmlInputCloseCallback
1423  *
1424  * Register a new set of I/O callback for handling parser input.
1425  *
1426  * Returns the registered handler number or -1 in case of error
1427  */
1428 int
1429 xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc,
1430         xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
1431         xmlInputCloseCallback closeFunc) {
1432     if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) {
1433         return(-1);
1434     }
1435     xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc;
1436     xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc;
1437     xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc;
1438     xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc;
1439     return(xmlInputCallbackNr++);
1440 }
1441
1442 /**
1443  * xmlRegisterOutputCallbacks:
1444  * @matchFunc:  the xmlOutputMatchCallback
1445  * @openFunc:  the xmlOutputOpenCallback
1446  * @writeFunc:  the xmlOutputWriteCallback
1447  * @closeFunc:  the xmlOutputCloseCallback
1448  *
1449  * Register a new set of I/O callback for handling output.
1450  *
1451  * Returns the registered handler number or -1 in case of error
1452  */
1453 int
1454 xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc,
1455         xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc,
1456         xmlOutputCloseCallback closeFunc) {
1457     if (xmlOutputCallbackNr >= MAX_INPUT_CALLBACK) {
1458         return(-1);
1459     }
1460     xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc;
1461     xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc;
1462     xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc;
1463     xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc;
1464     return(xmlOutputCallbackNr++);
1465 }
1466
1467 /**
1468  * xmlRegisterDefaultInputCallbacks:
1469  *
1470  * Registers the default compiled-in I/O handlers.
1471  */
1472 void
1473 xmlRegisterDefaultInputCallbacks
1474 (void) {
1475     if (xmlInputCallbackInitialized)
1476         return;
1477
1478     xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
1479                               xmlFileRead, xmlFileClose);
1480 #ifdef HAVE_ZLIB_H
1481     xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1482                               xmlGzfileRead, xmlGzfileClose);
1483 #endif /* HAVE_ZLIB_H */
1484
1485 #ifdef LIBXML_HTTP_ENABLED
1486     xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
1487                               xmlIOHTTPRead, xmlIOHTTPClose);
1488 #endif /* LIBXML_HTTP_ENABLED */
1489
1490 #ifdef LIBXML_FTP_ENABLED
1491     xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1492                               xmlIOFTPRead, xmlIOFTPClose);
1493 #endif /* LIBXML_FTP_ENABLED */
1494     xmlInputCallbackInitialized = 1;
1495 }
1496
1497 /**
1498  * xmlRegisterDefaultOutputCallbacks:
1499  *
1500  * Registers the default compiled-in I/O handlers.
1501  */
1502 void
1503 xmlRegisterDefaultOutputCallbacks
1504 (void) {
1505     if (xmlOutputCallbackInitialized)
1506         return;
1507
1508     xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
1509                               xmlFileWrite, xmlFileClose);
1510
1511 #ifdef LIBXML_HTTP_ENABLED
1512     xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1513                                xmlIOHTTPWrite, xmlIOHTTPClosePut);
1514 #endif
1515
1516 /*********************************
1517  No way a-priori to distinguish between gzipped files from
1518  uncompressed ones except opening if existing then closing
1519  and saving with same compression ratio ... a pain.
1520
1521 #ifdef HAVE_ZLIB_H
1522     xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1523                                xmlGzfileWrite, xmlGzfileClose);
1524 #endif
1525
1526  Nor FTP PUT ....
1527 #ifdef LIBXML_FTP_ENABLED
1528     xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1529                                xmlIOFTPWrite, xmlIOFTPClose);
1530 #endif
1531  **********************************/
1532     xmlOutputCallbackInitialized = 1;
1533 }
1534
1535 #ifdef LIBXML_HTTP_ENABLED
1536 /**
1537  * xmlRegisterHTTPPostCallbacks:
1538  *
1539  * By default, libxml submits HTTP output requests using the "PUT" method.
1540  * Calling this method changes the HTTP output method to use the "POST"
1541  * method instead.
1542  *
1543  */
1544 void
1545 xmlRegisterHTTPPostCallbacks( void ) {
1546
1547     /*  Register defaults if not done previously  */
1548
1549     if ( xmlOutputCallbackInitialized == 0 )
1550         xmlRegisterDefaultOutputCallbacks( );
1551
1552     xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1553                                xmlIOHTTPWrite, xmlIOHTTPClosePost);
1554     return;
1555 }
1556 #endif
1557
1558 /**
1559  * xmlAllocParserInputBuffer:
1560  * @enc:  the charset encoding if known
1561  *
1562  * Create a buffered parser input for progressive parsing
1563  *
1564  * Returns the new parser input or NULL
1565  */
1566 xmlParserInputBufferPtr
1567 xmlAllocParserInputBuffer(xmlCharEncoding enc) {
1568     xmlParserInputBufferPtr ret;
1569
1570     ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
1571     if (ret == NULL) {
1572         xmlGenericError(xmlGenericErrorContext,
1573                 "xmlAllocParserInputBuffer : out of memory!\n");
1574         return(NULL);
1575     }
1576     memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
1577     ret->buffer = xmlBufferCreate();
1578     if (ret->buffer == NULL) {
1579         xmlFree(ret);
1580         return(NULL);
1581     }
1582     ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1583     ret->encoder = xmlGetCharEncodingHandler(enc);
1584     if (ret->encoder != NULL)
1585         ret->raw = xmlBufferCreate();
1586     else
1587         ret->raw = NULL;
1588     ret->readcallback = NULL;
1589     ret->closecallback = NULL;
1590     ret->context = NULL;
1591
1592     return(ret);
1593 }
1594
1595 /**
1596  * xmlAllocOutputBuffer:
1597  * @encoder:  the encoding converter or NULL
1598  *
1599  * Create a buffered parser output
1600  *
1601  * Returns the new parser output or NULL
1602  */
1603 xmlOutputBufferPtr
1604 xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) {
1605     xmlOutputBufferPtr ret;
1606
1607     ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1608     if (ret == NULL) {
1609         xmlGenericError(xmlGenericErrorContext,
1610                 "xmlAllocOutputBuffer : out of memory!\n");
1611         return(NULL);
1612     }
1613     memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
1614     ret->buffer = xmlBufferCreate();
1615     if (ret->buffer == NULL) {
1616         xmlFree(ret);
1617         return(NULL);
1618     }
1619     ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1620     ret->encoder = encoder;
1621     if (encoder != NULL) {
1622         ret->conv = xmlBufferCreateSize(4000);
1623         /*
1624          * This call is designed to initiate the encoder state
1625          */
1626         xmlCharEncOutFunc(encoder, ret->conv, NULL); 
1627     } else
1628         ret->conv = NULL;
1629     ret->writecallback = NULL;
1630     ret->closecallback = NULL;
1631     ret->context = NULL;
1632     ret->written = 0;
1633
1634     return(ret);
1635 }
1636
1637 /**
1638  * xmlFreeParserInputBuffer:
1639  * @in:  a buffered parser input
1640  *
1641  * Free up the memory used by a buffered parser input
1642  */
1643 void
1644 xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
1645     if (in->raw) {
1646         xmlBufferFree(in->raw);
1647         in->raw = NULL;
1648     }
1649     if (in->encoder != NULL) {
1650         xmlCharEncCloseFunc(in->encoder);
1651     }
1652     if (in->closecallback != NULL) {
1653         in->closecallback(in->context);
1654     }
1655     if (in->buffer != NULL) {
1656         xmlBufferFree(in->buffer);
1657         in->buffer = NULL;
1658     }
1659
1660     xmlFree(in);
1661 }
1662
1663 /**
1664  * xmlOutputBufferClose:
1665  * @out:  a buffered output
1666  *
1667  * flushes and close the output I/O channel
1668  * and free up all the associated resources
1669  *
1670  * Returns the number of byte written or -1 in case of error.
1671  */
1672 int
1673 xmlOutputBufferClose(xmlOutputBufferPtr out) {
1674     int written;
1675     int err_rc = 0;
1676
1677     if (out == NULL)
1678         return(-1);
1679     if (out->writecallback != NULL)
1680         xmlOutputBufferFlush(out);
1681     if (out->closecallback != NULL) {
1682         err_rc = out->closecallback(out->context);
1683     }
1684     written = out->written;
1685     if (out->conv) {
1686         xmlBufferFree(out->conv);
1687         out->conv = NULL;
1688     }
1689     if (out->encoder != NULL) {
1690         xmlCharEncCloseFunc(out->encoder);
1691     }
1692     if (out->buffer != NULL) {
1693         xmlBufferFree(out->buffer);
1694         out->buffer = NULL;
1695     }
1696
1697     xmlFree(out);
1698     return( ( err_rc == 0 ) ? written : err_rc );
1699 }
1700
1701 /**
1702  * xmlParserInputBufferCreateFname:
1703  * @URI:  a C string containing the URI or filename
1704  * @enc:  the charset encoding if known
1705  *
1706  * Returns the new parser input or NULL
1707  */
1708 /**
1709  * xmlParserInputBufferCreateFilename:
1710  * @URI:  a C string containing the URI or filename
1711  * @enc:  the charset encoding if known
1712  *
1713  * Create a buffered parser input for the progressive parsing of a file
1714  * If filename is "-' then we use stdin as the input.
1715  * Automatic support for ZLIB/Compress compressed document is provided
1716  * by default if found at compile-time.
1717  * Do an encoding check if enc == XML_CHAR_ENCODING_NONE
1718  *
1719  * Returns the new parser input or NULL
1720  */
1721 xmlParserInputBufferPtr
1722 xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) {
1723     xmlParserInputBufferPtr ret;
1724     int i = 0;
1725     void *context = NULL;
1726
1727     if (xmlInputCallbackInitialized == 0)
1728         xmlRegisterDefaultInputCallbacks();
1729
1730     if (URI == NULL) return(NULL);
1731
1732 #ifdef LIBXML_CATALOG_ENABLED
1733 #endif
1734
1735     /*
1736      * Try to find one of the input accept method accepting that scheme
1737      * Go in reverse to give precedence to user defined handlers.
1738      */
1739     if (context == NULL) {
1740         for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1741             if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1742                 (xmlInputCallbackTable[i].matchcallback(URI) != 0)) {
1743                 context = xmlInputCallbackTable[i].opencallback(URI);
1744                 if (context != NULL)
1745                     break;
1746             }
1747         }
1748     }
1749     if (context == NULL) {
1750         return(NULL);
1751     }
1752
1753     /*
1754      * Allocate the Input buffer front-end.
1755      */
1756     ret = xmlAllocParserInputBuffer(enc);
1757     if (ret != NULL) {
1758         ret->context = context;
1759         ret->readcallback = xmlInputCallbackTable[i].readcallback;
1760         ret->closecallback = xmlInputCallbackTable[i].closecallback;
1761     }
1762     return(ret);
1763 }
1764
1765 /**
1766  * xmlOutputBufferCreateFilename:
1767  * @URI:  a C string containing the URI or filename
1768  * @encoder:  the encoding converter or NULL
1769  * @compression:  the compression ration (0 none, 9 max).
1770  *
1771  * Create a buffered  output for the progressive saving of a file
1772  * If filename is "-' then we use stdout as the output.
1773  * Automatic support for ZLIB/Compress compressed document is provided
1774  * by default if found at compile-time.
1775  * TODO: currently if compression is set, the library only support
1776  *       writing to a local file.
1777  *
1778  * Returns the new output or NULL
1779  */
1780 xmlOutputBufferPtr
1781 xmlOutputBufferCreateFilename(const char *URI,
1782                               xmlCharEncodingHandlerPtr encoder,
1783                               int compression) {
1784     xmlOutputBufferPtr ret;
1785     int i = 0;
1786     void *context = NULL;
1787     char *unescaped;
1788
1789     int is_http_uri = 0;        /*   Can't change if HTTP disabled  */
1790
1791     if (xmlOutputCallbackInitialized == 0)
1792         xmlRegisterDefaultOutputCallbacks();
1793
1794     if (URI == NULL) return(NULL);
1795
1796 #ifdef LIBXML_HTTP_ENABLED
1797     /*  Need to prevent HTTP URI's from falling into zlib short circuit  */
1798
1799     is_http_uri = xmlIOHTTPMatch( URI );
1800 #endif
1801
1802
1803     /*
1804      * Try to find one of the output accept method accepting that scheme
1805      * Go in reverse to give precedence to user defined handlers.
1806      * try with an unescaped version of the URI
1807      */
1808     unescaped = xmlURIUnescapeString(URI, 0, NULL);
1809     if (unescaped != NULL) {
1810 #ifdef HAVE_ZLIB_H
1811         if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
1812             context = xmlGzfileOpenW(unescaped, compression);
1813             if (context != NULL) {
1814                 ret = xmlAllocOutputBuffer(encoder);
1815                 if (ret != NULL) {
1816                     ret->context = context;
1817                     ret->writecallback = xmlGzfileWrite;
1818                     ret->closecallback = xmlGzfileClose;
1819                 }
1820                 xmlFree(unescaped);
1821                 return(ret);
1822             }
1823         }
1824 #endif
1825         for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1826             if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
1827                 (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) {
1828 #if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1829                 /*  Need to pass compression parameter into HTTP open calls  */
1830                 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1831                     context = xmlIOHTTPOpenW(unescaped, compression);
1832                 else
1833 #endif
1834                     context = xmlOutputCallbackTable[i].opencallback(unescaped);
1835                 if (context != NULL)
1836                     break;
1837             }
1838         }
1839         xmlFree(unescaped);
1840     }
1841
1842     /*
1843      * If this failed try with a non-escaped URI this may be a strange
1844      * filename
1845      */
1846     if (context == NULL) {
1847 #ifdef HAVE_ZLIB_H
1848         if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
1849             context = xmlGzfileOpenW(URI, compression);
1850             if (context != NULL) {
1851                 ret = xmlAllocOutputBuffer(encoder);
1852                 if (ret != NULL) {
1853                     ret->context = context;
1854                     ret->writecallback = xmlGzfileWrite;
1855                     ret->closecallback = xmlGzfileClose;
1856                 }
1857                 return(ret);
1858             }
1859         }
1860 #endif
1861         for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1862             if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
1863                 (xmlOutputCallbackTable[i].matchcallback(URI) != 0)) {
1864 #if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1865                 /*  Need to pass compression parameter into HTTP open calls  */
1866                 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1867                     context = xmlIOHTTPOpenW(URI, compression);
1868                 else
1869 #endif
1870                     context = xmlOutputCallbackTable[i].opencallback(URI);
1871                 if (context != NULL)
1872                     break;
1873             }
1874         }
1875     }
1876
1877     if (context == NULL) {
1878         return(NULL);
1879     }
1880
1881     /*
1882      * Allocate the Output buffer front-end.
1883      */
1884     ret = xmlAllocOutputBuffer(encoder);
1885     if (ret != NULL) {
1886         ret->context = context;
1887         ret->writecallback = xmlOutputCallbackTable[i].writecallback;
1888         ret->closecallback = xmlOutputCallbackTable[i].closecallback;
1889     }
1890     return(ret);
1891 }
1892
1893 /**
1894  * xmlParserInputBufferCreateFile:
1895  * @file:  a FILE* 
1896  * @enc:  the charset encoding if known
1897  *
1898  * Create a buffered parser input for the progressive parsing of a FILE *
1899  * buffered C I/O
1900  *
1901  * Returns the new parser input or NULL
1902  */
1903 xmlParserInputBufferPtr
1904 xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) {
1905     xmlParserInputBufferPtr ret;
1906
1907     if (xmlInputCallbackInitialized == 0)
1908         xmlRegisterDefaultInputCallbacks();
1909
1910     if (file == NULL) return(NULL);
1911
1912     ret = xmlAllocParserInputBuffer(enc);
1913     if (ret != NULL) {
1914         ret->context = file;
1915         ret->readcallback = xmlFileRead;
1916         ret->closecallback = xmlFileFlush;
1917     }
1918
1919     return(ret);
1920 }
1921
1922 /**
1923  * xmlOutputBufferCreateFile:
1924  * @file:  a FILE* 
1925  * @encoder:  the encoding converter or NULL
1926  *
1927  * Create a buffered output for the progressive saving to a FILE *
1928  * buffered C I/O
1929  *
1930  * Returns the new parser output or NULL
1931  */
1932 xmlOutputBufferPtr
1933 xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) {
1934     xmlOutputBufferPtr ret;
1935
1936     if (xmlOutputCallbackInitialized == 0)
1937         xmlRegisterDefaultOutputCallbacks();
1938
1939     if (file == NULL) return(NULL);
1940
1941     ret = xmlAllocOutputBuffer(encoder);
1942     if (ret != NULL) {
1943         ret->context = file;
1944         ret->writecallback = xmlFileWrite;
1945         ret->closecallback = xmlFileFlush;
1946     }
1947
1948     return(ret);
1949 }
1950
1951 /**
1952  * xmlParserInputBufferCreateFd:
1953  * @fd:  a file descriptor number
1954  * @enc:  the charset encoding if known
1955  *
1956  * Create a buffered parser input for the progressive parsing for the input
1957  * from a file descriptor
1958  *
1959  * Returns the new parser input or NULL
1960  */
1961 xmlParserInputBufferPtr
1962 xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) {
1963     xmlParserInputBufferPtr ret;
1964
1965     if (fd < 0) return(NULL);
1966
1967     ret = xmlAllocParserInputBuffer(enc);
1968     if (ret != NULL) {
1969         ret->context = (void *) (long) fd;
1970         ret->readcallback = xmlFdRead;
1971         ret->closecallback = xmlFdClose;
1972     }
1973
1974     return(ret);
1975 }
1976
1977 /**
1978  * xmlParserInputBufferCreateMem:
1979  * @mem:  the memory input
1980  * @size:  the length of the memory block
1981  * @enc:  the charset encoding if known
1982  *
1983  * Create a buffered parser input for the progressive parsing for the input
1984  * from a memory area.
1985  *
1986  * Returns the new parser input or NULL
1987  */
1988 xmlParserInputBufferPtr
1989 xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
1990     xmlParserInputBufferPtr ret;
1991
1992     if (size <= 0) return(NULL);
1993     if (mem == NULL) return(NULL);
1994
1995     ret = xmlAllocParserInputBuffer(enc);
1996     if (ret != NULL) {
1997         ret->context = (void *) mem;
1998         ret->readcallback = (xmlInputReadCallback) xmlNop;
1999         ret->closecallback = NULL;
2000         xmlBufferAdd(ret->buffer, (const xmlChar *) mem, size);
2001     }
2002
2003     return(ret);
2004 }
2005
2006 /**
2007  * xmlOutputBufferCreateFd:
2008  * @fd:  a file descriptor number
2009  * @encoder:  the encoding converter or NULL
2010  *
2011  * Create a buffered output for the progressive saving 
2012  * to a file descriptor
2013  *
2014  * Returns the new parser output or NULL
2015  */
2016 xmlOutputBufferPtr
2017 xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) {
2018     xmlOutputBufferPtr ret;
2019
2020     if (fd < 0) return(NULL);
2021
2022     ret = xmlAllocOutputBuffer(encoder);
2023     if (ret != NULL) {
2024         ret->context = (void *) (long) fd;
2025         ret->writecallback = xmlFdWrite;
2026         ret->closecallback = NULL;
2027     }
2028
2029     return(ret);
2030 }
2031
2032 /**
2033  * xmlParserInputBufferCreateIO:
2034  * @ioread:  an I/O read function
2035  * @ioclose:  an I/O close function
2036  * @ioctx:  an I/O handler
2037  * @enc:  the charset encoding if known
2038  *
2039  * Create a buffered parser input for the progressive parsing for the input
2040  * from an I/O handler
2041  *
2042  * Returns the new parser input or NULL
2043  */
2044 xmlParserInputBufferPtr
2045 xmlParserInputBufferCreateIO(xmlInputReadCallback   ioread,
2046          xmlInputCloseCallback  ioclose, void *ioctx, xmlCharEncoding enc) {
2047     xmlParserInputBufferPtr ret;
2048
2049     if (ioread == NULL) return(NULL);
2050
2051     ret = xmlAllocParserInputBuffer(enc);
2052     if (ret != NULL) {
2053         ret->context = (void *) ioctx;
2054         ret->readcallback = ioread;
2055         ret->closecallback = ioclose;
2056     }
2057
2058     return(ret);
2059 }
2060
2061 /**
2062  * xmlOutputBufferCreateIO:
2063  * @iowrite:  an I/O write function
2064  * @ioclose:  an I/O close function
2065  * @ioctx:  an I/O handler
2066  * @encoder:  the charset encoding if known
2067  *
2068  * Create a buffered output for the progressive saving
2069  * to an I/O handler
2070  *
2071  * Returns the new parser output or NULL
2072  */
2073 xmlOutputBufferPtr
2074 xmlOutputBufferCreateIO(xmlOutputWriteCallback   iowrite,
2075          xmlOutputCloseCallback  ioclose, void *ioctx,
2076          xmlCharEncodingHandlerPtr encoder) {
2077     xmlOutputBufferPtr ret;
2078
2079     if (iowrite == NULL) return(NULL);
2080
2081     ret = xmlAllocOutputBuffer(encoder);
2082     if (ret != NULL) {
2083         ret->context = (void *) ioctx;
2084         ret->writecallback = iowrite;
2085         ret->closecallback = ioclose;
2086     }
2087
2088     return(ret);
2089 }
2090
2091 /**
2092  * xmlParserInputBufferPush:
2093  * @in:  a buffered parser input
2094  * @len:  the size in bytes of the array.
2095  * @buf:  an char array
2096  *
2097  * Push the content of the arry in the input buffer
2098  * This routine handle the I18N transcoding to internal UTF-8
2099  * This is used when operating the parser in progressive (push) mode.
2100  *
2101  * Returns the number of chars read and stored in the buffer, or -1
2102  *         in case of error.
2103  */
2104 int
2105 xmlParserInputBufferPush(xmlParserInputBufferPtr in,
2106                          int len, const char *buf) {
2107     int nbchars = 0;
2108
2109     if (len < 0) return(0);
2110     if (in->encoder != NULL) {
2111         /*
2112          * Store the data in the incoming raw buffer
2113          */
2114         if (in->raw == NULL) {
2115             in->raw = xmlBufferCreate();
2116         }
2117         xmlBufferAdd(in->raw, (const xmlChar *) buf, len);
2118
2119         /*
2120          * convert as much as possible to the parser reading buffer.
2121          */
2122         nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2123         if (nbchars < 0) {
2124             xmlGenericError(xmlGenericErrorContext,
2125                     "xmlParserInputBufferPush: encoder error\n");
2126             return(-1);
2127         }
2128     } else {
2129         nbchars = len;
2130         xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
2131     }
2132 #ifdef DEBUG_INPUT
2133     xmlGenericError(xmlGenericErrorContext,
2134             "I/O: pushed %d chars, buffer %d/%d\n",
2135             nbchars, in->buffer->use, in->buffer->size);
2136 #endif
2137     return(nbchars);
2138 }
2139
2140 /**
2141  * endOfInput:
2142  *
2143  * When reading from an Input channel indicated end of file or error
2144  * don't reread from it again.
2145  */
2146 static int
2147 endOfInput (void * context ATTRIBUTE_UNUSED,
2148             char * buffer ATTRIBUTE_UNUSED,
2149             int len ATTRIBUTE_UNUSED) {
2150     return(0);
2151 }
2152
2153 /**
2154  * xmlParserInputBufferGrow:
2155  * @in:  a buffered parser input
2156  * @len:  indicative value of the amount of chars to read
2157  *
2158  * Grow up the content of the input buffer, the old data are preserved
2159  * This routine handle the I18N transcoding to internal UTF-8
2160  * This routine is used when operating the parser in normal (pull) mode
2161  *
2162  * TODO: one should be able to remove one extra copy by copying directly
2163  *       onto in->buffer or in->raw
2164  *
2165  * Returns the number of chars read and stored in the buffer, or -1
2166  *         in case of error.
2167  */
2168 int
2169 xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) {
2170     char *buffer = NULL;
2171     int res = 0;
2172     int nbchars = 0;
2173     int buffree;
2174     unsigned int needSize;
2175
2176     if ((len <= MINLEN) && (len != 4)) 
2177         len = MINLEN;
2178     buffree = in->buffer->size - in->buffer->use;
2179     if (buffree <= 0) {
2180         xmlGenericError(xmlGenericErrorContext,
2181                 "xmlParserInputBufferGrow : buffer full !\n");
2182         return(0);
2183     }
2184     if (len > buffree) 
2185         len = buffree;
2186
2187     needSize = in->buffer->use + len + 1;
2188     if (needSize > in->buffer->size){
2189         if (!xmlBufferResize(in->buffer, needSize)){
2190             xmlGenericError(xmlGenericErrorContext,
2191                     "xmlParserInputBufferGrow : out of memory!\n");
2192             return(0);
2193         }
2194     }
2195     buffer = (char *)&in->buffer->content[in->buffer->use];
2196
2197     /*
2198      * Call the read method for this I/O type.
2199      */
2200     if (in->readcallback != NULL) {
2201         res = in->readcallback(in->context, &buffer[0], len);
2202         if (res <= 0)
2203             in->readcallback = endOfInput;
2204     } else {
2205         xmlGenericError(xmlGenericErrorContext,
2206                 "xmlParserInputBufferGrow : no input !\n");
2207         return(-1);
2208     }
2209     if (res < 0) {
2210         return(-1);
2211     }
2212     len = res;
2213     if (in->encoder != NULL) {
2214         /*
2215          * Store the data in the incoming raw buffer
2216          */
2217         if (in->raw == NULL) {
2218             in->raw = xmlBufferCreate();
2219         }
2220         xmlBufferAdd(in->raw, (const xmlChar *) buffer, len);
2221
2222         /*
2223          * convert as much as possible to the parser reading buffer.
2224          */
2225         nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2226         if (nbchars < 0) {
2227             xmlGenericError(xmlGenericErrorContext,
2228                     "xmlParserInputBufferGrow: encoder error\n");
2229             return(-1);
2230         }
2231     } else {
2232         nbchars = len;
2233         in->buffer->use += nbchars;
2234         buffer[nbchars] = 0;
2235     }
2236 #ifdef DEBUG_INPUT
2237     xmlGenericError(xmlGenericErrorContext,
2238             "I/O: read %d chars, buffer %d/%d\n",
2239             nbchars, in->buffer->use, in->buffer->size);
2240 #endif
2241     return(nbchars);
2242 }
2243
2244 /**
2245  * xmlParserInputBufferRead:
2246  * @in:  a buffered parser input
2247  * @len:  indicative value of the amount of chars to read
2248  *
2249  * Refresh the content of the input buffer, the old data are considered
2250  * consumed
2251  * This routine handle the I18N transcoding to internal UTF-8
2252  *
2253  * Returns the number of chars read and stored in the buffer, or -1
2254  *         in case of error.
2255  */
2256 int
2257 xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) {
2258     /* xmlBufferEmpty(in->buffer); */
2259     if (in->readcallback != NULL)
2260         return(xmlParserInputBufferGrow(in, len));
2261     else
2262         return(-1);
2263 }
2264
2265 /**
2266  * xmlOutputBufferWrite:
2267  * @out:  a buffered parser output
2268  * @len:  the size in bytes of the array.
2269  * @buf:  an char array
2270  *
2271  * Write the content of the array in the output I/O buffer
2272  * This routine handle the I18N transcoding from internal UTF-8
2273  * The buffer is lossless, i.e. will store in case of partial
2274  * or delayed writes.
2275  *
2276  * Returns the number of chars immediately written, or -1
2277  *         in case of error.
2278  */
2279 int
2280 xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) {
2281     int nbchars = 0; /* number of chars to output to I/O */
2282     int ret;         /* return from function call */
2283     int written = 0; /* number of char written to I/O so far */
2284     int chunk;       /* number of byte curreent processed from buf */
2285
2286     if (len < 0) return(0);
2287
2288     do {
2289         chunk = len;
2290         if (chunk > 4 * MINLEN)
2291             chunk = 4 * MINLEN;
2292
2293         /*
2294          * first handle encoding stuff.
2295          */
2296         if (out->encoder != NULL) {
2297             /*
2298              * Store the data in the incoming raw buffer
2299              */
2300             if (out->conv == NULL) {
2301                 out->conv = xmlBufferCreate();
2302             }
2303             xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2304
2305             if ((out->buffer->use < MINLEN) && (chunk == len))
2306                 goto done;
2307
2308             /*
2309              * convert as much as possible to the parser reading buffer.
2310              */
2311             ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2312             if ((ret < 0) && (ret != -3)) {
2313                 xmlGenericError(xmlGenericErrorContext,
2314                         "xmlOutputBufferWrite: encoder error\n");
2315                 return(-1);
2316             }
2317             nbchars = out->conv->use;
2318         } else {
2319             xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2320             nbchars = out->buffer->use;
2321         }
2322         buf += chunk;
2323         len -= chunk;
2324
2325         if ((nbchars < MINLEN) && (len <= 0))
2326             goto done;
2327
2328         if (out->writecallback) {
2329             /*
2330              * second write the stuff to the I/O channel
2331              */
2332             if (out->encoder != NULL) {
2333                 ret = out->writecallback(out->context, 
2334                                  (const char *)out->conv->content, nbchars);
2335                 if (ret >= 0)
2336                     xmlBufferShrink(out->conv, ret);
2337             } else {
2338                 ret = out->writecallback(out->context, 
2339                                  (const char *)out->buffer->content, nbchars);
2340                 if (ret >= 0)
2341                     xmlBufferShrink(out->buffer, ret);
2342             }
2343             if (ret < 0) {
2344                 xmlGenericError(xmlGenericErrorContext,
2345                         "I/O: error %d writing %d bytes\n", ret, nbchars);
2346                 return(ret);
2347             }
2348             out->written += ret;
2349         }
2350         written += nbchars;
2351     } while (len > 0);
2352
2353 done:
2354 #ifdef DEBUG_INPUT
2355     xmlGenericError(xmlGenericErrorContext,
2356             "I/O: wrote %d chars\n", written);
2357 #endif
2358     return(written);
2359 }
2360
2361 /**
2362  * xmlOutputBufferWriteString:
2363  * @out:  a buffered parser output
2364  * @str:  a zero terminated C string
2365  *
2366  * Write the content of the string in the output I/O buffer
2367  * This routine handle the I18N transcoding from internal UTF-8
2368  * The buffer is lossless, i.e. will store in case of partial
2369  * or delayed writes.
2370  *
2371  * Returns the number of chars immediately written, or -1
2372  *         in case of error.
2373  */
2374 int
2375 xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) {
2376     int len;
2377     
2378     if (str == NULL)
2379         return(-1);
2380     len = strlen(str);
2381
2382     if (len > 0)
2383         return(xmlOutputBufferWrite(out, len, str));
2384     return(len);
2385 }
2386
2387 /**
2388  * xmlOutputBufferFlush:
2389  * @out:  a buffered output
2390  *
2391  * flushes the output I/O channel
2392  *
2393  * Returns the number of byte written or -1 in case of error.
2394  */
2395 int
2396 xmlOutputBufferFlush(xmlOutputBufferPtr out) {
2397     int nbchars = 0, ret = 0;
2398
2399     /*
2400      * first handle encoding stuff.
2401      */
2402     if ((out->conv != NULL) && (out->encoder != NULL)) {
2403         /*
2404          * convert as much as possible to the parser reading buffer.
2405          */
2406         nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2407         if (nbchars < 0) {
2408             xmlGenericError(xmlGenericErrorContext,
2409                     "xmlOutputBufferFlush: encoder error\n");
2410             return(-1);
2411         }
2412     }
2413
2414     /*
2415      * second flush the stuff to the I/O channel
2416      */
2417     if ((out->conv != NULL) && (out->encoder != NULL) &&
2418         (out->writecallback != NULL)) {
2419         ret = out->writecallback(out->context,
2420                    (const char *)out->conv->content, out->conv->use);
2421         if (ret >= 0)
2422             xmlBufferShrink(out->conv, ret);
2423     } else if (out->writecallback != NULL) {
2424         ret = out->writecallback(out->context,
2425                    (const char *)out->buffer->content, out->buffer->use);
2426         if (ret >= 0)
2427             xmlBufferShrink(out->buffer, ret);
2428     }
2429     if (ret < 0) {
2430         xmlGenericError(xmlGenericErrorContext,
2431                 "I/O: error %d flushing %d bytes\n", ret, nbchars);
2432         return(ret);
2433     }
2434     out->written += ret;
2435
2436 #ifdef DEBUG_INPUT
2437     xmlGenericError(xmlGenericErrorContext,
2438             "I/O: flushed %d chars\n", ret);
2439 #endif
2440     return(ret);
2441 }
2442
2443 /**
2444  * xmlParserGetDirectory:
2445  * @filename:  the path to a file
2446  *
2447  * lookup the directory for that file
2448  *
2449  * Returns a new allocated string containing the directory, or NULL.
2450  */
2451 char *
2452 xmlParserGetDirectory(const char *filename) {
2453     char *ret = NULL;
2454     char dir[1024];
2455     char *cur;
2456     char sep = '/';
2457
2458 #ifdef _WIN32_WCE  /* easy way by now ... wince does not have dirs! */
2459     return NULL;
2460 #endif
2461
2462     if (xmlInputCallbackInitialized == 0)
2463         xmlRegisterDefaultInputCallbacks();
2464
2465     if (filename == NULL) return(NULL);
2466 #if defined(WIN32) && !defined(__CYGWIN__)
2467     sep = '\\';
2468 #endif
2469
2470     strncpy(dir, filename, 1023);
2471     dir[1023] = 0;
2472     cur = &dir[strlen(dir)];
2473     while (cur > dir) {
2474          if (*cur == sep) break;
2475          cur --;
2476     }
2477     if (*cur == sep) {
2478         if (cur == dir) dir[1] = 0;
2479         else *cur = 0;
2480         ret = xmlMemStrdup(dir);
2481     } else {
2482         if (getcwd(dir, 1024) != NULL) {
2483             dir[1023] = 0;
2484             ret = xmlMemStrdup(dir);
2485         }
2486     }
2487     return(ret);
2488 }
2489
2490 /****************************************************************
2491  *                                                              *
2492  *              External entities loading                       *
2493  *                                                              *
2494  ****************************************************************/
2495
2496 static int xmlSysIDExists(const char *URL) {
2497 #ifdef HAVE_STAT
2498     int ret;
2499     struct stat info;
2500     const char *path;
2501
2502     if (URL == NULL)
2503         return(0);
2504
2505     if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2506 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
2507         path = &URL[17];
2508 #else
2509         path = &URL[16];
2510 #endif
2511     else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
2512 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
2513         path = &URL[8];
2514 #else
2515         path = &URL[7];
2516 #endif
2517     } else 
2518         path = URL;
2519     ret = stat(path, &info);
2520     if (ret == 0)
2521         return(1);
2522 #endif
2523     return(0);
2524 }
2525
2526 /**
2527  * xmlDefaultExternalEntityLoader:
2528  * @URL:  the URL for the entity to load
2529  * @ID:  the System ID for the entity to load
2530  * @ctxt:  the context in which the entity is called or NULL
2531  *
2532  * By default we don't load external entitites, yet.
2533  *
2534  * Returns a new allocated xmlParserInputPtr, or NULL.
2535  */
2536 static
2537 xmlParserInputPtr
2538 xmlDefaultExternalEntityLoader(const char *URL, const char *ID,
2539                                xmlParserCtxtPtr ctxt) {
2540     xmlParserInputPtr ret = NULL;
2541     xmlChar *resource = NULL;
2542 #ifdef LIBXML_CATALOG_ENABLED
2543     xmlCatalogAllow pref;
2544 #endif
2545
2546 #ifdef DEBUG_EXTERNAL_ENTITIES
2547     xmlGenericError(xmlGenericErrorContext,
2548             "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
2549 #endif
2550 #ifdef LIBXML_CATALOG_ENABLED
2551     /*
2552      * If the resource doesn't exists as a file,
2553      * try to load it from the resource pointed in the catalogs
2554      */
2555     pref = xmlCatalogGetDefaults();
2556
2557     if ((pref != XML_CATA_ALLOW_NONE) && (!xmlSysIDExists(URL))) {
2558         /*
2559          * Do a local lookup
2560          */
2561         if ((ctxt->catalogs != NULL) &&
2562             ((pref == XML_CATA_ALLOW_ALL) ||
2563              (pref == XML_CATA_ALLOW_DOCUMENT))) {
2564             resource = xmlCatalogLocalResolve(ctxt->catalogs,
2565                                               (const xmlChar *)ID,
2566                                               (const xmlChar *)URL);
2567         }
2568         /*
2569          * Try a global lookup
2570          */
2571         if ((resource == NULL) &&
2572             ((pref == XML_CATA_ALLOW_ALL) ||
2573              (pref == XML_CATA_ALLOW_GLOBAL))) {
2574             resource = xmlCatalogResolve((const xmlChar *)ID,
2575                                          (const xmlChar *)URL);
2576         }
2577         if ((resource == NULL) && (URL != NULL))
2578             resource = xmlStrdup((const xmlChar *) URL);
2579
2580         /*
2581          * TODO: do an URI lookup on the reference
2582          */
2583         if ((resource != NULL) && (!xmlSysIDExists((const char *)resource))) {
2584             xmlChar *tmp = NULL;
2585
2586             if ((ctxt->catalogs != NULL) &&
2587                 ((pref == XML_CATA_ALLOW_ALL) ||
2588                  (pref == XML_CATA_ALLOW_DOCUMENT))) {
2589                 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2590             }
2591             if ((tmp == NULL) &&
2592                 ((pref == XML_CATA_ALLOW_ALL) ||
2593                  (pref == XML_CATA_ALLOW_GLOBAL))) {
2594                 tmp = xmlCatalogResolveURI(resource);
2595             }
2596
2597             if (tmp != NULL) {
2598                 xmlFree(resource);
2599                 resource = tmp;
2600             }
2601         }
2602     }
2603 #endif
2604
2605     if (resource == NULL)
2606         resource = (xmlChar *) URL;
2607
2608     if (resource == NULL) {
2609         if (ID == NULL)
2610             ID = "NULL";
2611         if ((ctxt->validate) && (ctxt->sax != NULL) && 
2612             (ctxt->sax->error != NULL))
2613             ctxt->sax->error(ctxt,
2614                     "failed to load external entity \"%s\"\n", ID);
2615         else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
2616             ctxt->sax->warning(ctxt,
2617                     "failed to load external entity \"%s\"\n", ID);
2618         return(NULL);
2619     }
2620     ret = xmlNewInputFromFile(ctxt, (const char *)resource);
2621     if (ret == NULL) {
2622         if ((ctxt->validate) && (ctxt->sax != NULL) && 
2623             (ctxt->sax->error != NULL))
2624             ctxt->sax->error(ctxt,
2625                     "failed to load external entity \"%s\"\n", resource);
2626         else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
2627             ctxt->sax->warning(ctxt,
2628                     "failed to load external entity \"%s\"\n", resource);
2629     }
2630     if ((resource != NULL) && (resource != (xmlChar *) URL))
2631         xmlFree(resource);
2632     return(ret);
2633 }
2634
2635 static xmlExternalEntityLoader xmlCurrentExternalEntityLoader =
2636        xmlDefaultExternalEntityLoader;
2637
2638 /**
2639  * xmlSetExternalEntityLoader:
2640  * @f:  the new entity resolver function
2641  *
2642  * Changes the defaultexternal entity resolver function for the application
2643  */
2644 void
2645 xmlSetExternalEntityLoader(xmlExternalEntityLoader f) {
2646     xmlCurrentExternalEntityLoader = f;
2647 }
2648
2649 /**
2650  * xmlGetExternalEntityLoader:
2651  *
2652  * Get the default external entity resolver function for the application
2653  *
2654  * Returns the xmlExternalEntityLoader function pointer
2655  */
2656 xmlExternalEntityLoader
2657 xmlGetExternalEntityLoader(void) {
2658     return(xmlCurrentExternalEntityLoader);
2659 }
2660
2661 /**
2662  * xmlLoadExternalEntity:
2663  * @URL:  the URL for the entity to load
2664  * @ID:  the Public ID for the entity to load
2665  * @ctxt:  the context in which the entity is called or NULL
2666  *
2667  * Load an external entity, note that the use of this function for
2668  * unparsed entities may generate problems
2669  * TODO: a more generic External entity API must be designed
2670  *
2671  * Returns the xmlParserInputPtr or NULL
2672  */
2673 xmlParserInputPtr
2674 xmlLoadExternalEntity(const char *URL, const char *ID,
2675                       xmlParserCtxtPtr ctxt) {
2676     if ((URL != NULL) && (xmlSysIDExists(URL) == 0)) {
2677         char *canonicFilename;
2678         xmlParserInputPtr ret;
2679
2680         canonicFilename = (char *) xmlCanonicPath((const xmlChar *) URL);
2681         if (canonicFilename == NULL) {
2682             if (xmlDefaultSAXHandler.error != NULL) {
2683                 xmlDefaultSAXHandler.error(NULL, "out of memory\n");
2684             }
2685             return(NULL);
2686         }
2687
2688         ret = xmlCurrentExternalEntityLoader(canonicFilename, ID, ctxt);
2689         xmlFree(canonicFilename);
2690         return(ret);
2691     }
2692     return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
2693 }
2694
2695 /************************************************************************
2696  *                                                                      *
2697  *              Disabling Network access                                *
2698  *                                                                      *
2699  ************************************************************************/
2700
2701 #ifdef LIBXML_CATALOG_ENABLED
2702 static int
2703 xmlNoNetExists(const char *URL)
2704 {
2705 #ifdef HAVE_STAT
2706     int ret;
2707     struct stat info;
2708     const char *path;
2709
2710     if (URL == NULL)
2711         return (0);
2712
2713     if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2714 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
2715         path = &URL[17];
2716 #else
2717         path = &URL[16];
2718 #endif
2719     else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
2720 #if defined (_WIN32) || defined (__DJGPP__) && !defined(__CYGWIN__)
2721         path = &URL[8];
2722 #else
2723         path = &URL[7];
2724 #endif
2725     } else
2726         path = URL;
2727     ret = stat(path, &info);
2728     if (ret == 0)
2729         return (1);
2730 #endif
2731     return (0);
2732 }
2733 #endif
2734
2735 /**
2736  * xmlNoNetExternalEntityLoader:
2737  * @URL:  the URL for the entity to load
2738  * @ID:  the System ID for the entity to load
2739  * @ctxt:  the context in which the entity is called or NULL
2740  *
2741  * A specific entity loader disabling network accesses, though still
2742  * allowing local catalog accesses for resolution.
2743  *
2744  * Returns a new allocated xmlParserInputPtr, or NULL.
2745  */
2746 xmlParserInputPtr
2747 xmlNoNetExternalEntityLoader(const char *URL, const char *ID,
2748                              xmlParserCtxtPtr ctxt) {
2749     xmlParserInputPtr input = NULL;
2750     xmlChar *resource = NULL;
2751
2752 #ifdef LIBXML_CATALOG_ENABLED
2753     xmlCatalogAllow pref;
2754
2755     /*
2756      * If the resource doesn't exists as a file,
2757      * try to load it from the resource pointed in the catalogs
2758      */
2759     pref = xmlCatalogGetDefaults();
2760
2761     if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) {
2762         /*
2763          * Do a local lookup
2764          */
2765         if ((ctxt->catalogs != NULL) &&
2766             ((pref == XML_CATA_ALLOW_ALL) ||
2767              (pref == XML_CATA_ALLOW_DOCUMENT))) {
2768             resource = xmlCatalogLocalResolve(ctxt->catalogs,
2769                                               (const xmlChar *)ID,
2770                                               (const xmlChar *)URL);
2771         }
2772         /*
2773          * Try a global lookup
2774          */
2775         if ((resource == NULL) &&
2776             ((pref == XML_CATA_ALLOW_ALL) ||
2777              (pref == XML_CATA_ALLOW_GLOBAL))) {
2778             resource = xmlCatalogResolve((const xmlChar *)ID,
2779                                          (const xmlChar *)URL);
2780         }
2781         if ((resource == NULL) && (URL != NULL))
2782             resource = xmlStrdup((const xmlChar *) URL);
2783
2784         /*
2785          * TODO: do an URI lookup on the reference
2786          */
2787         if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) {
2788             xmlChar *tmp = NULL;
2789
2790             if ((ctxt->catalogs != NULL) &&
2791                 ((pref == XML_CATA_ALLOW_ALL) ||
2792                  (pref == XML_CATA_ALLOW_DOCUMENT))) {
2793                 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2794             }
2795             if ((tmp == NULL) &&
2796                 ((pref == XML_CATA_ALLOW_ALL) ||
2797                  (pref == XML_CATA_ALLOW_GLOBAL))) {
2798                 tmp = xmlCatalogResolveURI(resource);
2799             }
2800
2801             if (tmp != NULL) {
2802                 xmlFree(resource);
2803                 resource = tmp;
2804             }
2805         }
2806     }
2807 #endif
2808     if (resource == NULL)
2809         resource = (xmlChar *) URL;
2810
2811     if (resource != NULL) {
2812         if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) ||
2813             (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) {
2814             xmlGenericError(xmlGenericErrorContext,
2815                     "Attempt to load network entity %s \n", resource);
2816
2817             if (resource != (xmlChar *) URL)
2818                 xmlFree(resource);
2819             return(NULL);
2820         }
2821     }
2822     input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt);
2823     if (resource != (xmlChar *) URL)
2824         xmlFree(resource);
2825     return(input);
2826 }
2827