source: trunk/third/libxml2/xmlIO.c @ 17096

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