blob: a3b1dc07288c96c418bc8519e1bd6e92a15fc954 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * xmlIO.c : implementation of the I/O interfaces used by the parser
3 *
4 * See Copyright for the status of this software.
5 *
Daniel Veillard344cee72001-08-20 00:08:40 +00006 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00007 *
8 * 14 Nov 2000 ht - for VMS, truncated name of long functions to under 32 char
9 */
10
Daniel Veillard34ce8be2002-03-18 19:37:11 +000011#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000012#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000013
Owen Taylor3473f882001-02-23 17:55:21 +000014#include <string.h>
Daniel Veillard92727042002-09-17 17:59:20 +000015#ifdef HAVE_ERRNO_H
Owen Taylor3473f882001-02-23 17:55:21 +000016#include <errno.h>
Daniel Veillard92727042002-09-17 17:59:20 +000017#endif
18
Owen Taylor3473f882001-02-23 17:55:21 +000019
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
Daniel Veillard50f34372001-08-03 12:06:36 +000042 /* 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
Owen Taylor3473f882001-02-23 17:55:21 +000047# 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>
Daniel Veillard388236f2001-07-08 18:35:48 +000073#include <libxml/uri.h>
Owen Taylor3473f882001-02-23 17:55:21 +000074#include <libxml/nanohttp.h>
75#include <libxml/nanoftp.h>
76#include <libxml/xmlerror.h>
Daniel Veillard7d6fd212001-05-10 15:34:11 +000077#ifdef LIBXML_CATALOG_ENABLED
78#include <libxml/catalog.h>
79#endif
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000080#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000081
Daniel Veillardf012a642001-07-23 19:10:52 +000082/* #define VERBOSE_FAILURE */
Daniel Veillard1fd36d22001-07-04 22:54:28 +000083/* #define DEBUG_EXTERNAL_ENTITIES */
Owen Taylor3473f882001-02-23 17:55:21 +000084/* #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 */
95typedef struct _xmlInputCallback {
96 xmlInputMatchCallback matchcallback;
97 xmlInputOpenCallback opencallback;
98 xmlInputReadCallback readcallback;
99 xmlInputCloseCallback closecallback;
100} xmlInputCallback;
101
102#define MAX_INPUT_CALLBACK 15
103
Daniel Veillard22090732001-07-16 00:06:07 +0000104static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK];
105static int xmlInputCallbackNr = 0;
106static int xmlInputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000107
108/*
109 * Output I/O callback sets
110 */
111typedef struct _xmlOutputCallback {
112 xmlOutputMatchCallback matchcallback;
113 xmlOutputOpenCallback opencallback;
114 xmlOutputWriteCallback writecallback;
115 xmlOutputCloseCallback closecallback;
116} xmlOutputCallback;
117
118#define MAX_OUTPUT_CALLBACK 15
119
Daniel Veillard22090732001-07-16 00:06:07 +0000120static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK];
121static int xmlOutputCallbackNr = 0;
122static int xmlOutputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000123
Daniel Veillardf4862f02002-09-10 11:13:43 +0000124
125/**
Daniel Veillard01c13b52002-12-10 15:19:08 +0000126 * xmlNormalizeWindowsPath:
Daniel Veillard33300b42003-04-17 09:09:19 +0000127 * @path: the input file path
Daniel Veillardf4862f02002-09-10 11:13:43 +0000128 *
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +0000129 * This function is obsolete. Please see xmlURIFromPath in uri.c for
130 * a better solution.
Daniel Veillard33300b42003-04-17 09:09:19 +0000131 *
132 * Returns a canonicalized version of the path
Daniel Veillardf4862f02002-09-10 11:13:43 +0000133 */
134xmlChar *
135xmlNormalizeWindowsPath(const xmlChar *path)
136{
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +0000137 return xmlCanonicPath(path);
Daniel Veillardf4862f02002-09-10 11:13:43 +0000138}
139
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000140/**
141 * xmlCleanupInputCallbacks:
142 *
143 * clears the entire input callback table. this includes the
144 * compiled-in I/O.
145 */
146void
147xmlCleanupInputCallbacks(void)
148{
149 int i;
150
151 if (!xmlInputCallbackInitialized)
152 return;
153
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000154 for (i = xmlInputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000155 xmlInputCallbackTable[i].matchcallback = NULL;
156 xmlInputCallbackTable[i].opencallback = NULL;
157 xmlInputCallbackTable[i].readcallback = NULL;
158 xmlInputCallbackTable[i].closecallback = NULL;
159 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000160 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000161
162 xmlInputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000163 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000164}
165
166/**
167 * xmlCleanupOutputCallbacks:
168 *
169 * clears the entire output callback table. this includes the
170 * compiled-in I/O callbacks.
171 */
172void
173xmlCleanupOutputCallbacks(void)
174{
175 int i;
176
177 if (!xmlOutputCallbackInitialized)
178 return;
179
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000180 for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000181 xmlOutputCallbackTable[i].matchcallback = NULL;
182 xmlOutputCallbackTable[i].opencallback = NULL;
183 xmlOutputCallbackTable[i].writecallback = NULL;
184 xmlOutputCallbackTable[i].closecallback = NULL;
185 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000186 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000187
188 xmlOutputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000189 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000190}
191
Owen Taylor3473f882001-02-23 17:55:21 +0000192/************************************************************************
193 * *
194 * Standard I/O for file accesses *
195 * *
196 ************************************************************************/
197
198/**
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000199 * xmlCheckFilename:
Owen Taylor3473f882001-02-23 17:55:21 +0000200 * @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,
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000209 * returns 2. otherwise returns 1.
Owen Taylor3473f882001-02-23 17:55:21 +0000210 */
211
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000212int
Owen Taylor3473f882001-02-23 17:55:21 +0000213xmlCheckFilename (const char *path)
214{
215#ifdef HAVE_STAT
Owen Taylor3473f882001-02-23 17:55:21 +0000216 struct stat stat_buffer;
217
218 if (stat(path, &stat_buffer) == -1)
219 return 0;
220
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000221#ifdef S_ISDIR
Owen Taylor3473f882001-02-23 17:55:21 +0000222 if (S_ISDIR(stat_buffer.st_mode)) {
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000223 return 2;
Owen Taylor3473f882001-02-23 17:55:21 +0000224 }
Owen Taylor3473f882001-02-23 17:55:21 +0000225#endif
226#endif
227 return 1;
228}
229
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000230static int
Owen Taylor3473f882001-02-23 17:55:21 +0000231xmlNop(void) {
232 return(0);
233}
234
235/**
Owen Taylor3473f882001-02-23 17:55:21 +0000236 * 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 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000245static int
Owen Taylor3473f882001-02-23 17:55:21 +0000246xmlFdRead (void * context, char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000247 return(read((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000248}
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 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000260static int
Owen Taylor3473f882001-02-23 17:55:21 +0000261xmlFdWrite (void * context, const char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000262 return(write((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000263}
264
265/**
266 * xmlFdClose:
267 * @context: the I/O context
268 *
269 * Close an I/O channel
Daniel Veillardf012a642001-07-23 19:10:52 +0000270 *
271 * Returns 0 in case of success and error code otherwise
Owen Taylor3473f882001-02-23 17:55:21 +0000272 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000273static int
Owen Taylor3473f882001-02-23 17:55:21 +0000274xmlFdClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000275 return ( close((int) (long) context) );
Owen Taylor3473f882001-02-23 17:55:21 +0000276}
277
278/**
279 * xmlFileMatch:
280 * @filename: the URI for matching
281 *
282 * input from FILE *
283 *
284 * Returns 1 if matches, 0 otherwise
285 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000286int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000287xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000288 return(1);
289}
290
291/**
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000292 * xmlFileOpen_real:
Owen Taylor3473f882001-02-23 17:55:21 +0000293 * @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 */
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000300static void *
301xmlFileOpen_real (const char *filename) {
Owen Taylor3473f882001-02-23 17:55:21 +0000302 const char *path = NULL;
303 FILE *fd;
304
305 if (!strcmp(filename, "-")) {
306 fd = stdin;
307 return((void *) fd);
308 }
309
Daniel Veillardf4862f02002-09-10 11:13:43 +0000310 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000311#if defined (_WIN32) && !defined(__CYGWIN__)
312 path = &filename[17];
313#else
Owen Taylor3473f882001-02-23 17:55:21 +0000314 path = &filename[16];
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000315#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000316 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000317#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000318 path = &filename[8];
319#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000320 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000321#endif
322 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000323 path = filename;
324
325 if (path == NULL)
326 return(NULL);
327 if (!xmlCheckFilename(path))
328 return(NULL);
329
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000330#if defined(WIN32) || defined (__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +0000331 fd = fopen(path, "rb");
332#else
333 fd = fopen(path, "r");
334#endif /* WIN32 */
335 return((void *) fd);
336}
337
338/**
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000339 * 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
Daniel Veillard71531f32003-02-05 13:19:53 +0000344 *
345 * Returns a handler or NULL in case or failure
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000346 */
347void *
348xmlFileOpen (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/**
Owen Taylor3473f882001-02-23 17:55:21 +0000362 * 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 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000370static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000371xmlFileOpenW (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
Daniel Veillardf4862f02002-09-10 11:13:43 +0000380 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
381#if defined (_WIN32) && !defined(__CYGWIN__)
382 path = &filename[17];
383#else
Owen Taylor3473f882001-02-23 17:55:21 +0000384 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000385#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000386 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000387#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000388 path = &filename[8];
389#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000390 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000391#endif
392 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000393 path = filename;
394
395 if (path == NULL)
396 return(NULL);
397
398 fd = fopen(path, "w");
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 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000412int
Owen Taylor3473f882001-02-23 17:55:21 +0000413xmlFileRead (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 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000427static int
Owen Taylor3473f882001-02-23 17:55:21 +0000428xmlFileWrite (void * context, const char * buffer, int len) {
Daniel Veillard4a6d39b2002-12-17 18:33:01 +0000429 int items;
430
431 items = fwrite(&buffer[0], len, 1, (FILE *) context);
432
433 return(items * len);
Owen Taylor3473f882001-02-23 17:55:21 +0000434}
435
436/**
437 * xmlFileClose:
438 * @context: the I/O context
439 *
440 * Close an I/O channel
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000441 *
442 * Returns 0 or -1 in case of error
Owen Taylor3473f882001-02-23 17:55:21 +0000443 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000444int
Owen Taylor3473f882001-02-23 17:55:21 +0000445xmlFileClose (void * context) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000446 FILE *fil;
447
448 fil = (FILE *) context;
449 if (fil == stdin)
450 return(0);
451 if (fil == stdout)
452 return(0);
Daniel Veillardcd337f02001-11-22 18:20:37 +0000453 if (fil == stderr)
454 return(0);
Daniel Veillardf012a642001-07-23 19:10:52 +0000455 return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000456}
457
458/**
459 * xmlFileFlush:
460 * @context: the I/O context
461 *
462 * Flush an I/O channel
463 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000464static int
Owen Taylor3473f882001-02-23 17:55:21 +0000465xmlFileFlush (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000466 return ( ( fflush((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000467}
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 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000483static int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000484xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000485 return(1);
486}
487
488/**
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000489 * xmlGzfileOpen_real:
Owen Taylor3473f882001-02-23 17:55:21 +0000490 * @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 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000497static void *
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000498xmlGzfileOpen_real (const char *filename) {
Owen Taylor3473f882001-02-23 17:55:21 +0000499 const char *path = NULL;
500 gzFile fd;
501
502 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000503 fd = gzdopen(dup(0), "rb");
Owen Taylor3473f882001-02-23 17:55:21 +0000504 return((void *) fd);
505 }
506
Daniel Veillardf4862f02002-09-10 11:13:43 +0000507 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
508#if defined (_WIN32) && !defined(__CYGWIN__)
509 path = &filename[17];
510#else
Owen Taylor3473f882001-02-23 17:55:21 +0000511 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000512#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000513 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000514#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000515 path = &filename[8];
516#else
Owen Taylor3473f882001-02-23 17:55:21 +0000517 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000518#endif
519 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000520 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/**
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000532 * xmlGzfileOpen:
533 * @filename: the URI for matching
534 *
535 * Wrapper around xmlGzfileOpen that try it with an unescaped
536 * version of @filename, if this fails fallback to @filename
537 */
538static void *
539xmlGzfileOpen (const char *filename) {
540 char *unescaped;
541 void *retval;
542 unescaped = xmlURIUnescapeString(filename, 0, NULL);
543 if (unescaped != NULL) {
544 retval = xmlGzfileOpen_real(unescaped);
545 } else {
546 retval = xmlGzfileOpen_real(filename);
547 }
548 xmlFree(unescaped);
549 return retval;
550}
551
552/**
Owen Taylor3473f882001-02-23 17:55:21 +0000553 * xmlGzfileOpenW:
554 * @filename: the URI for matching
555 * @compression: the compression factor (0 - 9 included)
556 *
557 * input from compressed file open
558 * if @filename is " " then the standard input is used
559 *
560 * Returns an I/O context or NULL in case of error
561 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000562static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000563xmlGzfileOpenW (const char *filename, int compression) {
564 const char *path = NULL;
565 char mode[15];
566 gzFile fd;
567
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000568 snprintf(mode, sizeof(mode), "wb%d", compression);
Owen Taylor3473f882001-02-23 17:55:21 +0000569 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000570 fd = gzdopen(dup(1), mode);
Owen Taylor3473f882001-02-23 17:55:21 +0000571 return((void *) fd);
572 }
573
Daniel Veillardf4862f02002-09-10 11:13:43 +0000574 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
575#if defined (_WIN32) && !defined(__CYGWIN__)
576 path = &filename[17];
577#else
Owen Taylor3473f882001-02-23 17:55:21 +0000578 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000579#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000580 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000581#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000582 path = &filename[8];
583#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000584 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000585#endif
586 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000587 path = filename;
588
589 if (path == NULL)
590 return(NULL);
591
592 fd = gzopen(path, mode);
593 return((void *) fd);
594}
595
596/**
597 * xmlGzfileRead:
598 * @context: the I/O context
599 * @buffer: where to drop data
600 * @len: number of bytes to write
601 *
602 * Read @len bytes to @buffer from the compressed I/O channel.
603 *
604 * Returns the number of bytes written
605 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000606static int
Owen Taylor3473f882001-02-23 17:55:21 +0000607xmlGzfileRead (void * context, char * buffer, int len) {
608 return(gzread((gzFile) context, &buffer[0], len));
609}
610
611/**
612 * xmlGzfileWrite:
613 * @context: the I/O context
614 * @buffer: where to drop data
615 * @len: number of bytes to write
616 *
617 * Write @len bytes from @buffer to the compressed I/O channel.
618 *
619 * Returns the number of bytes written
620 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000621static int
Owen Taylor3473f882001-02-23 17:55:21 +0000622xmlGzfileWrite (void * context, const char * buffer, int len) {
623 return(gzwrite((gzFile) context, (char *) &buffer[0], len));
624}
625
626/**
627 * xmlGzfileClose:
628 * @context: the I/O context
629 *
630 * Close a compressed I/O channel
631 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000632static int
Owen Taylor3473f882001-02-23 17:55:21 +0000633xmlGzfileClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000634 return ( ( gzclose((gzFile) context) == Z_OK ) ? 0 : -1 );
Owen Taylor3473f882001-02-23 17:55:21 +0000635}
636#endif /* HAVE_ZLIB_H */
637
638#ifdef LIBXML_HTTP_ENABLED
639/************************************************************************
640 * *
641 * I/O for HTTP file accesses *
642 * *
643 ************************************************************************/
Daniel Veillardf012a642001-07-23 19:10:52 +0000644
645typedef struct xmlIOHTTPWriteCtxt_
646{
647 int compression;
648
649 char * uri;
650
651 void * doc_buff;
652
653} xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr;
654
655#ifdef HAVE_ZLIB_H
656
657#define DFLT_WBITS ( -15 )
658#define DFLT_MEM_LVL ( 8 )
659#define GZ_MAGIC1 ( 0x1f )
660#define GZ_MAGIC2 ( 0x8b )
661#define LXML_ZLIB_OS_CODE ( 0x03 )
662#define INIT_HTTP_BUFF_SIZE ( 32768 )
663#define DFLT_ZLIB_RATIO ( 5 )
664
665/*
666** Data structure and functions to work with sending compressed data
667** via HTTP.
668*/
669
670typedef struct xmlZMemBuff_
671{
672 unsigned long size;
673 unsigned long crc;
674
675 unsigned char * zbuff;
676 z_stream zctrl;
677
678} xmlZMemBuff, *xmlZMemBuffPtr;
679
680/**
681 * append_reverse_ulong
682 * @buff: Compressed memory buffer
683 * @data: Unsigned long to append
684 *
685 * Append a unsigned long in reverse byte order to the end of the
686 * memory buffer.
687 */
688static void
689append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) {
690
691 int idx;
692
693 if ( buff == NULL )
694 return;
695
696 /*
697 ** This is plagiarized from putLong in gzio.c (zlib source) where
698 ** the number "4" is hardcoded. If zlib is ever patched to
699 ** support 64 bit file sizes, this code would need to be patched
700 ** as well.
701 */
702
703 for ( idx = 0; idx < 4; idx++ ) {
704 *buff->zctrl.next_out = ( data & 0xff );
705 data >>= 8;
706 buff->zctrl.next_out++;
707 }
708
709 return;
710}
711
712/**
713 *
714 * xmlFreeZMemBuff
715 * @buff: The memory buffer context to clear
716 *
717 * Release all the resources associated with the compressed memory buffer.
718 */
719static void
720xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
721
722 int z_err;
723
724 if ( buff == NULL )
725 return;
726
727 xmlFree( buff->zbuff );
728 z_err = deflateEnd( &buff->zctrl );
729#ifdef DEBUG_HTTP
730 if ( z_err != Z_OK )
731 xmlGenericError( xmlGenericErrorContext,
732 "xmlFreeZMemBuff: Error releasing zlib context: %d\n",
733 z_err );
734#endif
735
736 xmlFree( buff );
737 return;
738}
739
740/**
741 * xmlCreateZMemBuff
742 *@compression: Compression value to use
743 *
744 * Create a memory buffer to hold the compressed XML document. The
745 * compressed document in memory will end up being identical to what
746 * would be created if gzopen/gzwrite/gzclose were being used to
747 * write the document to disk. The code for the header/trailer data to
748 * the compression is plagiarized from the zlib source files.
749 */
750static void *
751xmlCreateZMemBuff( int compression ) {
752
753 int z_err;
754 int hdr_lgth;
755 xmlZMemBuffPtr buff = NULL;
756
757 if ( ( compression < 1 ) || ( compression > 9 ) )
758 return ( NULL );
759
760 /* Create the control and data areas */
761
762 buff = xmlMalloc( sizeof( xmlZMemBuff ) );
763 if ( buff == NULL ) {
764 xmlGenericError( xmlGenericErrorContext,
765 "xmlCreateZMemBuff: %s\n",
766 "Failure allocating buffer context." );
767 return ( NULL );
768 }
769
770 (void)memset( buff, 0, sizeof( xmlZMemBuff ) );
771 buff->size = INIT_HTTP_BUFF_SIZE;
772 buff->zbuff = xmlMalloc( buff->size );
773 if ( buff->zbuff == NULL ) {
774 xmlFreeZMemBuff( buff );
775 xmlGenericError( xmlGenericErrorContext,
776 "xmlCreateZMemBuff: %s\n",
777 "Failure allocating data buffer." );
778 return ( NULL );
779 }
780
781 z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
782 DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
783 if ( z_err != Z_OK ) {
784 xmlFreeZMemBuff( buff );
785 buff = NULL;
786 xmlGenericError( xmlGenericErrorContext,
787 "xmlCreateZMemBuff: %s %d\n",
788 "Error initializing compression context. ZLIB error:",
789 z_err );
790 return ( NULL );
791 }
792
793 /* Set the header data. The CRC will be needed for the trailer */
Daniel Veillardf012a642001-07-23 19:10:52 +0000794 buff->crc = crc32( 0L, Z_NULL, 0 );
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000795 hdr_lgth = snprintf( (char *)buff->zbuff, buff->size,
796 "%c%c%c%c%c%c%c%c%c%c",
Daniel Veillardf012a642001-07-23 19:10:52 +0000797 GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED,
798 0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE );
799 buff->zctrl.next_out = buff->zbuff + hdr_lgth;
800 buff->zctrl.avail_out = buff->size - hdr_lgth;
801
802 return ( buff );
803}
804
805/**
806 * xmlZMemBuffExtend
807 * @buff: Buffer used to compress and consolidate data.
808 * @ext_amt: Number of bytes to extend the buffer.
809 *
810 * Extend the internal buffer used to store the compressed data by the
811 * specified amount.
812 *
813 * Returns 0 on success or -1 on failure to extend the buffer. On failure
814 * the original buffer still exists at the original size.
815 */
816static int
817xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) {
818
819 int rc = -1;
820 size_t new_size;
821 size_t cur_used;
822
823 unsigned char * tmp_ptr = NULL;
824
825 if ( buff == NULL )
826 return ( -1 );
827
828 else if ( ext_amt == 0 )
829 return ( 0 );
830
831 cur_used = buff->zctrl.next_out - buff->zbuff;
832 new_size = buff->size + ext_amt;
833
834#ifdef DEBUG_HTTP
835 if ( cur_used > new_size )
836 xmlGenericError( xmlGenericErrorContext,
837 "xmlZMemBuffExtend: %s\n%s %d bytes.\n",
838 "Buffer overwrite detected during compressed memory",
839 "buffer extension. Overflowed by",
840 (cur_used - new_size ) );
841#endif
842
843 tmp_ptr = xmlRealloc( buff->zbuff, new_size );
844 if ( tmp_ptr != NULL ) {
845 rc = 0;
846 buff->size = new_size;
847 buff->zbuff = tmp_ptr;
848 buff->zctrl.next_out = tmp_ptr + cur_used;
849 buff->zctrl.avail_out = new_size - cur_used;
850 }
851 else {
852 xmlGenericError( xmlGenericErrorContext,
853 "xmlZMemBuffExtend: %s %lu bytes.\n",
854 "Allocation failure extending output buffer to",
855 new_size );
856 }
857
858 return ( rc );
859}
860
861/**
862 * xmlZMemBuffAppend
863 * @buff: Buffer used to compress and consolidate data
864 * @src: Uncompressed source content to append to buffer
865 * @len: Length of source data to append to buffer
866 *
867 * Compress and append data to the internal buffer. The data buffer
868 * will be expanded if needed to store the additional data.
869 *
870 * Returns the number of bytes appended to the buffer or -1 on error.
871 */
872static int
873xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) {
874
875 int z_err;
876 size_t min_accept;
877
878 if ( ( buff == NULL ) || ( src == NULL ) )
879 return ( -1 );
880
881 buff->zctrl.avail_in = len;
882 buff->zctrl.next_in = (unsigned char *)src;
883 while ( buff->zctrl.avail_in > 0 ) {
884 /*
885 ** Extend the buffer prior to deflate call if a reasonable amount
886 ** of output buffer space is not available.
887 */
888 min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO;
889 if ( buff->zctrl.avail_out <= min_accept ) {
890 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
891 return ( -1 );
892 }
893
894 z_err = deflate( &buff->zctrl, Z_NO_FLUSH );
895 if ( z_err != Z_OK ) {
896 xmlGenericError( xmlGenericErrorContext,
897 "xmlZMemBuffAppend: %s %d %s - %d",
898 "Compression error while appending",
899 len, "bytes to buffer. ZLIB error", z_err );
900 return ( -1 );
901 }
902 }
903
904 buff->crc = crc32( buff->crc, (unsigned char *)src, len );
905
906 return ( len );
907}
908
909/**
910 * xmlZMemBuffGetContent
911 * @buff: Compressed memory content buffer
912 * @data_ref: Pointer reference to point to compressed content
913 *
914 * Flushes the compression buffers, appends gzip file trailers and
915 * returns the compressed content and length of the compressed data.
916 * NOTE: The gzip trailer code here is plagiarized from zlib source.
917 *
918 * Returns the length of the compressed data or -1 on error.
919 */
920static int
921xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) {
922
923 int zlgth = -1;
924 int z_err;
925
926 if ( ( buff == NULL ) || ( data_ref == NULL ) )
927 return ( -1 );
928
929 /* Need to loop until compression output buffers are flushed */
930
931 do
932 {
933 z_err = deflate( &buff->zctrl, Z_FINISH );
934 if ( z_err == Z_OK ) {
935 /* In this case Z_OK means more buffer space needed */
936
937 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
938 return ( -1 );
939 }
940 }
941 while ( z_err == Z_OK );
942
943 /* If the compression state is not Z_STREAM_END, some error occurred */
944
945 if ( z_err == Z_STREAM_END ) {
946
947 /* Need to append the gzip data trailer */
948
949 if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) {
950 if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 )
951 return ( -1 );
952 }
953
954 /*
955 ** For whatever reason, the CRC and length data are pushed out
956 ** in reverse byte order. So a memcpy can't be used here.
957 */
958
959 append_reverse_ulong( buff, buff->crc );
960 append_reverse_ulong( buff, buff->zctrl.total_in );
961
962 zlgth = buff->zctrl.next_out - buff->zbuff;
963 *data_ref = (char *)buff->zbuff;
964 }
965
966 else
967 xmlGenericError( xmlGenericErrorContext,
968 "xmlZMemBuffGetContent: %s - %d\n",
969 "Error flushing zlib buffers. Error code", z_err );
970
971 return ( zlgth );
972}
973#endif /* HAVE_ZLIB_H */
974
975/**
976 * xmlFreeHTTPWriteCtxt
977 * @ctxt: Context to cleanup
978 *
979 * Free allocated memory and reclaim system resources.
980 *
981 * No return value.
982 */
983static void
984xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
985{
986 if ( ctxt->uri != NULL )
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000987 xmlFree( ctxt->uri );
Daniel Veillardf012a642001-07-23 19:10:52 +0000988
989 if ( ctxt->doc_buff != NULL ) {
990
991#ifdef HAVE_ZLIB_H
992 if ( ctxt->compression > 0 ) {
993 xmlFreeZMemBuff( ctxt->doc_buff );
994 }
995 else
996#endif
997 {
998 xmlOutputBufferClose( ctxt->doc_buff );
999 }
1000 }
1001
Daniel Veillard819d5cb2002-10-14 11:15:18 +00001002 xmlFree( ctxt );
Daniel Veillardf012a642001-07-23 19:10:52 +00001003 return;
1004}
1005
1006
Owen Taylor3473f882001-02-23 17:55:21 +00001007/**
1008 * xmlIOHTTPMatch:
1009 * @filename: the URI for matching
1010 *
1011 * check if the URI matches an HTTP one
1012 *
1013 * Returns 1 if matches, 0 otherwise
1014 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001015int
Owen Taylor3473f882001-02-23 17:55:21 +00001016xmlIOHTTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001017 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7))
Owen Taylor3473f882001-02-23 17:55:21 +00001018 return(1);
1019 return(0);
1020}
1021
1022/**
1023 * xmlIOHTTPOpen:
1024 * @filename: the URI for matching
1025 *
1026 * open an HTTP I/O channel
1027 *
1028 * Returns an I/O context or NULL in case of error
1029 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001030void *
Owen Taylor3473f882001-02-23 17:55:21 +00001031xmlIOHTTPOpen (const char *filename) {
1032 return(xmlNanoHTTPOpen(filename, NULL));
1033}
1034
1035/**
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001036 * xmlIOHTTPOpenW:
Daniel Veillardf012a642001-07-23 19:10:52 +00001037 * @post_uri: The destination URI for the document
1038 * @compression: The compression desired for the document.
1039 *
1040 * Open a temporary buffer to collect the document for a subsequent HTTP POST
1041 * request. Non-static as is called from the output buffer creation routine.
1042 *
1043 * Returns an I/O context or NULL in case of error.
1044 */
1045
1046void *
Daniel Veillard572577e2002-01-18 16:23:55 +00001047xmlIOHTTPOpenW(const char *post_uri, int compression)
1048{
Daniel Veillardf012a642001-07-23 19:10:52 +00001049
Daniel Veillard572577e2002-01-18 16:23:55 +00001050 xmlIOHTTPWriteCtxtPtr ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001051
Daniel Veillard572577e2002-01-18 16:23:55 +00001052 if (post_uri == NULL)
1053 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001054
Daniel Veillard572577e2002-01-18 16:23:55 +00001055 ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt));
1056 if (ctxt == NULL) {
1057 xmlGenericError(xmlGenericErrorContext,
1058 "xmlIOHTTPOpenW: Failed to create output HTTP context.\n");
1059 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001060 }
1061
Daniel Veillard572577e2002-01-18 16:23:55 +00001062 (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt));
Daniel Veillardf012a642001-07-23 19:10:52 +00001063
Daniel Veillard572577e2002-01-18 16:23:55 +00001064 ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri);
1065 if (ctxt->uri == NULL) {
1066 xmlGenericError(xmlGenericErrorContext,
1067 "xmlIOHTTPOpenW: Failed to duplicate destination URI.\n");
1068 xmlFreeHTTPWriteCtxt(ctxt);
1069 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001070 }
1071
1072 /*
Daniel Veillard572577e2002-01-18 16:23:55 +00001073 * ** Since the document length is required for an HTTP post,
1074 * ** need to put the document into a buffer. A memory buffer
1075 * ** is being used to avoid pushing the data to disk and back.
1076 */
Daniel Veillardf012a642001-07-23 19:10:52 +00001077
1078#ifdef HAVE_ZLIB_H
Daniel Veillard572577e2002-01-18 16:23:55 +00001079 if ((compression > 0) && (compression <= 9)) {
1080
1081 ctxt->compression = compression;
1082 ctxt->doc_buff = xmlCreateZMemBuff(compression);
1083 } else
Daniel Veillardf012a642001-07-23 19:10:52 +00001084#endif
1085 {
Daniel Veillard572577e2002-01-18 16:23:55 +00001086 /* Any character conversions should have been done before this */
Daniel Veillardf012a642001-07-23 19:10:52 +00001087
Daniel Veillard572577e2002-01-18 16:23:55 +00001088 ctxt->doc_buff = xmlAllocOutputBuffer(NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001089 }
1090
Daniel Veillard572577e2002-01-18 16:23:55 +00001091 if (ctxt->doc_buff == NULL) {
1092 xmlFreeHTTPWriteCtxt(ctxt);
1093 ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001094 }
1095
Daniel Veillard572577e2002-01-18 16:23:55 +00001096 return (ctxt);
Daniel Veillardf012a642001-07-23 19:10:52 +00001097}
1098
1099/**
1100 * xmlIOHTTPDfltOpenW
1101 * @post_uri: The destination URI for this document.
1102 *
1103 * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent
1104 * HTTP post command. This function should generally not be used as
1105 * the open callback is short circuited in xmlOutputBufferCreateFile.
1106 *
1107 * Returns a pointer to the new IO context.
1108 */
1109static void *
1110xmlIOHTTPDfltOpenW( const char * post_uri ) {
1111 return ( xmlIOHTTPOpenW( post_uri, 0 ) );
1112}
1113
1114/**
Owen Taylor3473f882001-02-23 17:55:21 +00001115 * xmlIOHTTPRead:
1116 * @context: the I/O context
1117 * @buffer: where to drop data
1118 * @len: number of bytes to write
1119 *
1120 * Read @len bytes to @buffer from the I/O channel.
1121 *
1122 * Returns the number of bytes written
1123 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001124int
Owen Taylor3473f882001-02-23 17:55:21 +00001125xmlIOHTTPRead(void * context, char * buffer, int len) {
1126 return(xmlNanoHTTPRead(context, &buffer[0], len));
1127}
1128
1129/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001130 * xmlIOHTTPWrite
1131 * @context: previously opened writing context
1132 * @buffer: data to output to temporary buffer
1133 * @len: bytes to output
1134 *
1135 * Collect data from memory buffer into a temporary file for later
1136 * processing.
1137 *
1138 * Returns number of bytes written.
1139 */
1140
1141static int
1142xmlIOHTTPWrite( void * context, const char * buffer, int len ) {
1143
1144 xmlIOHTTPWriteCtxtPtr ctxt = context;
1145
1146 if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) )
1147 return ( -1 );
1148
1149 if ( len > 0 ) {
1150
1151 /* Use gzwrite or fwrite as previously setup in the open call */
1152
1153#ifdef HAVE_ZLIB_H
1154 if ( ctxt->compression > 0 )
1155 len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len );
1156
1157 else
1158#endif
1159 len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer );
1160
1161 if ( len < 0 ) {
1162 xmlGenericError( xmlGenericErrorContext,
1163 "xmlIOHTTPWrite: %s\n%s '%s'.\n",
1164 "Error appending to internal buffer.",
1165 "Error sending document to URI",
1166 ctxt->uri );
1167 }
1168 }
1169
1170 return ( len );
1171}
1172
1173
1174/**
Owen Taylor3473f882001-02-23 17:55:21 +00001175 * xmlIOHTTPClose:
1176 * @context: the I/O context
1177 *
1178 * Close an HTTP I/O channel
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001179 *
1180 * Returns 0
Owen Taylor3473f882001-02-23 17:55:21 +00001181 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001182int
Owen Taylor3473f882001-02-23 17:55:21 +00001183xmlIOHTTPClose (void * context) {
1184 xmlNanoHTTPClose(context);
Daniel Veillardf012a642001-07-23 19:10:52 +00001185 return 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001186}
Daniel Veillardf012a642001-07-23 19:10:52 +00001187
1188/**
1189 * xmlIOHTTCloseWrite
1190 * @context: The I/O context
1191 * @http_mthd: The HTTP method to be used when sending the data
1192 *
1193 * Close the transmit HTTP I/O channel and actually send the data.
1194 */
1195static int
1196xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) {
1197
1198 int close_rc = -1;
1199 int http_rtn = 0;
1200 int content_lgth = 0;
1201 xmlIOHTTPWriteCtxtPtr ctxt = context;
1202
1203 char * http_content = NULL;
1204 char * content_encoding = NULL;
1205 char * content_type = (char *) "text/xml";
1206 void * http_ctxt = NULL;
1207
1208 if ( ( ctxt == NULL ) || ( http_mthd == NULL ) )
1209 return ( -1 );
1210
1211 /* Retrieve the content from the appropriate buffer */
1212
1213#ifdef HAVE_ZLIB_H
1214
1215 if ( ctxt->compression > 0 ) {
1216 content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content );
1217 content_encoding = (char *) "Content-Encoding: gzip";
1218 }
1219 else
1220#endif
1221 {
1222 /* Pull the data out of the memory output buffer */
1223
1224 xmlOutputBufferPtr dctxt = ctxt->doc_buff;
1225 http_content = (char *)dctxt->buffer->content;
1226 content_lgth = dctxt->buffer->use;
1227 }
1228
1229 if ( http_content == NULL ) {
1230 xmlGenericError( xmlGenericErrorContext,
1231 "xmlIOHTTPCloseWrite: %s '%s' %s '%s'.\n",
1232 "Error retrieving content.\nUnable to",
1233 http_mthd, "data to URI", ctxt->uri );
1234 }
1235
1236 else {
1237
1238 http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content,
1239 &content_type, content_encoding,
1240 content_lgth );
1241
1242 if ( http_ctxt != NULL ) {
1243#ifdef DEBUG_HTTP
1244 /* If testing/debugging - dump reply with request content */
1245
1246 FILE * tst_file = NULL;
1247 char buffer[ 4096 ];
1248 char * dump_name = NULL;
1249 int avail;
1250
1251 xmlGenericError( xmlGenericErrorContext,
1252 "xmlNanoHTTPCloseWrite: HTTP %s to\n%s returned %d.\n",
1253 http_mthd, ctxt->uri,
1254 xmlNanoHTTPReturnCode( http_ctxt ) );
1255
1256 /*
1257 ** Since either content or reply may be gzipped,
1258 ** dump them to separate files instead of the
1259 ** standard error context.
1260 */
1261
1262 dump_name = tempnam( NULL, "lxml" );
1263 if ( dump_name != NULL ) {
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001264 (void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001265
1266 tst_file = fopen( buffer, "w" );
1267 if ( tst_file != NULL ) {
1268 xmlGenericError( xmlGenericErrorContext,
1269 "Transmitted content saved in file: %s\n", buffer );
1270
1271 fwrite( http_content, sizeof( char ),
1272 content_lgth, tst_file );
1273 fclose( tst_file );
1274 }
1275
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001276 (void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001277 tst_file = fopen( buffer, "w" );
1278 if ( tst_file != NULL ) {
1279 xmlGenericError( xmlGenericErrorContext,
1280 "Reply content saved in file: %s\n", buffer );
1281
1282
1283 while ( (avail = xmlNanoHTTPRead( http_ctxt,
1284 buffer, sizeof( buffer ) )) > 0 ) {
1285
1286 fwrite( buffer, sizeof( char ), avail, tst_file );
1287 }
1288
1289 fclose( tst_file );
1290 }
1291
1292 free( dump_name );
1293 }
1294#endif /* DEBUG_HTTP */
1295
1296 http_rtn = xmlNanoHTTPReturnCode( http_ctxt );
1297 if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) )
1298 close_rc = 0;
1299 else
1300 xmlGenericError( xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001301 "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
Daniel Veillardf012a642001-07-23 19:10:52 +00001302 http_mthd, content_lgth,
1303 "bytes to URI", ctxt->uri,
1304 "failed. HTTP return code:", http_rtn );
1305
1306 xmlNanoHTTPClose( http_ctxt );
1307 xmlFree( content_type );
1308 }
1309 }
1310
1311 /* Final cleanups */
1312
1313 xmlFreeHTTPWriteCtxt( ctxt );
1314
1315 return ( close_rc );
1316}
1317
1318/**
1319 * xmlIOHTTPClosePut
1320 *
1321 * @context: The I/O context
1322 *
1323 * Close the transmit HTTP I/O channel and actually send data using a PUT
1324 * HTTP method.
1325 */
1326static int
1327xmlIOHTTPClosePut( void * ctxt ) {
1328 return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) );
1329}
1330
1331
1332/**
1333 * xmlIOHTTPClosePost
1334 *
1335 * @context: The I/O context
1336 *
1337 * Close the transmit HTTP I/O channel and actually send data using a POST
1338 * HTTP method.
1339 */
1340static int
1341xmlIOHTTPClosePost( void * ctxt ) {
1342 return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) );
1343}
1344
Owen Taylor3473f882001-02-23 17:55:21 +00001345#endif /* LIBXML_HTTP_ENABLED */
1346
1347#ifdef LIBXML_FTP_ENABLED
1348/************************************************************************
1349 * *
1350 * I/O for FTP file accesses *
1351 * *
1352 ************************************************************************/
1353/**
1354 * xmlIOFTPMatch:
1355 * @filename: the URI for matching
1356 *
1357 * check if the URI matches an FTP one
1358 *
1359 * Returns 1 if matches, 0 otherwise
1360 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001361int
Owen Taylor3473f882001-02-23 17:55:21 +00001362xmlIOFTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001363 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6))
Owen Taylor3473f882001-02-23 17:55:21 +00001364 return(1);
1365 return(0);
1366}
1367
1368/**
1369 * xmlIOFTPOpen:
1370 * @filename: the URI for matching
1371 *
1372 * open an FTP I/O channel
1373 *
1374 * Returns an I/O context or NULL in case of error
1375 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001376void *
Owen Taylor3473f882001-02-23 17:55:21 +00001377xmlIOFTPOpen (const char *filename) {
1378 return(xmlNanoFTPOpen(filename));
1379}
1380
1381/**
1382 * xmlIOFTPRead:
1383 * @context: the I/O context
1384 * @buffer: where to drop data
1385 * @len: number of bytes to write
1386 *
1387 * Read @len bytes to @buffer from the I/O channel.
1388 *
1389 * Returns the number of bytes written
1390 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001391int
Owen Taylor3473f882001-02-23 17:55:21 +00001392xmlIOFTPRead(void * context, char * buffer, int len) {
1393 return(xmlNanoFTPRead(context, &buffer[0], len));
1394}
1395
1396/**
1397 * xmlIOFTPClose:
1398 * @context: the I/O context
1399 *
1400 * Close an FTP I/O channel
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001401 *
1402 * Returns 0
Owen Taylor3473f882001-02-23 17:55:21 +00001403 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001404int
Owen Taylor3473f882001-02-23 17:55:21 +00001405xmlIOFTPClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001406 return ( xmlNanoFTPClose(context) );
Owen Taylor3473f882001-02-23 17:55:21 +00001407}
1408#endif /* LIBXML_FTP_ENABLED */
1409
1410
1411/**
1412 * xmlRegisterInputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001413 * @matchFunc: the xmlInputMatchCallback
1414 * @openFunc: the xmlInputOpenCallback
1415 * @readFunc: the xmlInputReadCallback
1416 * @closeFunc: the xmlInputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001417 *
1418 * Register a new set of I/O callback for handling parser input.
1419 *
1420 * Returns the registered handler number or -1 in case of error
1421 */
1422int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001423xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc,
1424 xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
1425 xmlInputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001426 if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) {
1427 return(-1);
1428 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001429 xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc;
1430 xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc;
1431 xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc;
1432 xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001433 return(xmlInputCallbackNr++);
1434}
1435
1436/**
1437 * xmlRegisterOutputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001438 * @matchFunc: the xmlOutputMatchCallback
1439 * @openFunc: the xmlOutputOpenCallback
1440 * @writeFunc: the xmlOutputWriteCallback
1441 * @closeFunc: the xmlOutputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001442 *
1443 * Register a new set of I/O callback for handling output.
1444 *
1445 * Returns the registered handler number or -1 in case of error
1446 */
1447int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001448xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc,
1449 xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc,
1450 xmlOutputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001451 if (xmlOutputCallbackNr >= MAX_INPUT_CALLBACK) {
1452 return(-1);
1453 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001454 xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc;
1455 xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc;
1456 xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc;
1457 xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001458 return(xmlOutputCallbackNr++);
1459}
1460
1461/**
1462 * xmlRegisterDefaultInputCallbacks:
1463 *
1464 * Registers the default compiled-in I/O handlers.
1465 */
1466void
Owen Taylor3473f882001-02-23 17:55:21 +00001467xmlRegisterDefaultInputCallbacks
Owen Taylor3473f882001-02-23 17:55:21 +00001468(void) {
1469 if (xmlInputCallbackInitialized)
1470 return;
1471
1472 xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
1473 xmlFileRead, xmlFileClose);
1474#ifdef HAVE_ZLIB_H
1475 xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1476 xmlGzfileRead, xmlGzfileClose);
1477#endif /* HAVE_ZLIB_H */
1478
1479#ifdef LIBXML_HTTP_ENABLED
1480 xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
1481 xmlIOHTTPRead, xmlIOHTTPClose);
1482#endif /* LIBXML_HTTP_ENABLED */
1483
1484#ifdef LIBXML_FTP_ENABLED
1485 xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1486 xmlIOFTPRead, xmlIOFTPClose);
1487#endif /* LIBXML_FTP_ENABLED */
1488 xmlInputCallbackInitialized = 1;
1489}
1490
1491/**
1492 * xmlRegisterDefaultOutputCallbacks:
1493 *
1494 * Registers the default compiled-in I/O handlers.
1495 */
1496void
Owen Taylor3473f882001-02-23 17:55:21 +00001497xmlRegisterDefaultOutputCallbacks
Owen Taylor3473f882001-02-23 17:55:21 +00001498(void) {
1499 if (xmlOutputCallbackInitialized)
1500 return;
1501
1502 xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
1503 xmlFileWrite, xmlFileClose);
Daniel Veillardf012a642001-07-23 19:10:52 +00001504
1505#ifdef LIBXML_HTTP_ENABLED
1506 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1507 xmlIOHTTPWrite, xmlIOHTTPClosePut);
1508#endif
1509
Owen Taylor3473f882001-02-23 17:55:21 +00001510/*********************************
1511 No way a-priori to distinguish between gzipped files from
1512 uncompressed ones except opening if existing then closing
1513 and saving with same compression ratio ... a pain.
1514
1515#ifdef HAVE_ZLIB_H
1516 xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1517 xmlGzfileWrite, xmlGzfileClose);
1518#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001519
1520 Nor FTP PUT ....
1521#ifdef LIBXML_FTP_ENABLED
1522 xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1523 xmlIOFTPWrite, xmlIOFTPClose);
1524#endif
1525 **********************************/
1526 xmlOutputCallbackInitialized = 1;
1527}
1528
Daniel Veillardf012a642001-07-23 19:10:52 +00001529#ifdef LIBXML_HTTP_ENABLED
1530/**
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001531 * xmlRegisterHTTPPostCallbacks:
Daniel Veillardf012a642001-07-23 19:10:52 +00001532 *
1533 * By default, libxml submits HTTP output requests using the "PUT" method.
1534 * Calling this method changes the HTTP output method to use the "POST"
1535 * method instead.
1536 *
1537 */
1538void
1539xmlRegisterHTTPPostCallbacks( void ) {
1540
1541 /* Register defaults if not done previously */
1542
1543 if ( xmlOutputCallbackInitialized == 0 )
1544 xmlRegisterDefaultOutputCallbacks( );
1545
1546 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1547 xmlIOHTTPWrite, xmlIOHTTPClosePost);
1548 return;
1549}
1550#endif
1551
Owen Taylor3473f882001-02-23 17:55:21 +00001552/**
1553 * xmlAllocParserInputBuffer:
1554 * @enc: the charset encoding if known
1555 *
1556 * Create a buffered parser input for progressive parsing
1557 *
1558 * Returns the new parser input or NULL
1559 */
1560xmlParserInputBufferPtr
1561xmlAllocParserInputBuffer(xmlCharEncoding enc) {
1562 xmlParserInputBufferPtr ret;
1563
1564 ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
1565 if (ret == NULL) {
1566 xmlGenericError(xmlGenericErrorContext,
1567 "xmlAllocParserInputBuffer : out of memory!\n");
1568 return(NULL);
1569 }
1570 memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
1571 ret->buffer = xmlBufferCreate();
1572 if (ret->buffer == NULL) {
1573 xmlFree(ret);
1574 return(NULL);
1575 }
1576 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1577 ret->encoder = xmlGetCharEncodingHandler(enc);
1578 if (ret->encoder != NULL)
1579 ret->raw = xmlBufferCreate();
1580 else
1581 ret->raw = NULL;
1582 ret->readcallback = NULL;
1583 ret->closecallback = NULL;
1584 ret->context = NULL;
1585
1586 return(ret);
1587}
1588
1589/**
1590 * xmlAllocOutputBuffer:
1591 * @encoder: the encoding converter or NULL
1592 *
1593 * Create a buffered parser output
1594 *
1595 * Returns the new parser output or NULL
1596 */
1597xmlOutputBufferPtr
1598xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) {
1599 xmlOutputBufferPtr ret;
1600
1601 ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1602 if (ret == NULL) {
1603 xmlGenericError(xmlGenericErrorContext,
1604 "xmlAllocOutputBuffer : out of memory!\n");
1605 return(NULL);
1606 }
1607 memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
1608 ret->buffer = xmlBufferCreate();
1609 if (ret->buffer == NULL) {
1610 xmlFree(ret);
1611 return(NULL);
1612 }
1613 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1614 ret->encoder = encoder;
1615 if (encoder != NULL) {
1616 ret->conv = xmlBufferCreateSize(4000);
1617 /*
1618 * This call is designed to initiate the encoder state
1619 */
1620 xmlCharEncOutFunc(encoder, ret->conv, NULL);
1621 } else
1622 ret->conv = NULL;
1623 ret->writecallback = NULL;
1624 ret->closecallback = NULL;
1625 ret->context = NULL;
1626 ret->written = 0;
1627
1628 return(ret);
1629}
1630
1631/**
1632 * xmlFreeParserInputBuffer:
1633 * @in: a buffered parser input
1634 *
1635 * Free up the memory used by a buffered parser input
1636 */
1637void
1638xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
1639 if (in->raw) {
1640 xmlBufferFree(in->raw);
1641 in->raw = NULL;
1642 }
1643 if (in->encoder != NULL) {
1644 xmlCharEncCloseFunc(in->encoder);
1645 }
1646 if (in->closecallback != NULL) {
1647 in->closecallback(in->context);
1648 }
1649 if (in->buffer != NULL) {
1650 xmlBufferFree(in->buffer);
1651 in->buffer = NULL;
1652 }
1653
Owen Taylor3473f882001-02-23 17:55:21 +00001654 xmlFree(in);
1655}
1656
1657/**
1658 * xmlOutputBufferClose:
1659 * @out: a buffered output
1660 *
1661 * flushes and close the output I/O channel
1662 * and free up all the associated resources
1663 *
1664 * Returns the number of byte written or -1 in case of error.
1665 */
1666int
1667xmlOutputBufferClose(xmlOutputBufferPtr out) {
1668 int written;
Daniel Veillardf012a642001-07-23 19:10:52 +00001669 int err_rc = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001670
1671 if (out == NULL)
1672 return(-1);
1673 if (out->writecallback != NULL)
1674 xmlOutputBufferFlush(out);
1675 if (out->closecallback != NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001676 err_rc = out->closecallback(out->context);
Owen Taylor3473f882001-02-23 17:55:21 +00001677 }
1678 written = out->written;
1679 if (out->conv) {
1680 xmlBufferFree(out->conv);
1681 out->conv = NULL;
1682 }
1683 if (out->encoder != NULL) {
1684 xmlCharEncCloseFunc(out->encoder);
1685 }
1686 if (out->buffer != NULL) {
1687 xmlBufferFree(out->buffer);
1688 out->buffer = NULL;
1689 }
1690
Owen Taylor3473f882001-02-23 17:55:21 +00001691 xmlFree(out);
Daniel Veillardf012a642001-07-23 19:10:52 +00001692 return( ( err_rc == 0 ) ? written : err_rc );
Owen Taylor3473f882001-02-23 17:55:21 +00001693}
1694
1695/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001696 * xmlParserInputBufferCreateFname:
1697 * @URI: a C string containing the URI or filename
1698 * @enc: the charset encoding if known
1699 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001700 * Returns the new parser input or NULL
1701 */
1702/**
Owen Taylor3473f882001-02-23 17:55:21 +00001703 * xmlParserInputBufferCreateFilename:
1704 * @URI: a C string containing the URI or filename
1705 * @enc: the charset encoding if known
1706 *
1707 * Create a buffered parser input for the progressive parsing of a file
1708 * If filename is "-' then we use stdin as the input.
1709 * Automatic support for ZLIB/Compress compressed document is provided
1710 * by default if found at compile-time.
1711 * Do an encoding check if enc == XML_CHAR_ENCODING_NONE
1712 *
1713 * Returns the new parser input or NULL
1714 */
1715xmlParserInputBufferPtr
Daniel Veillard3e59fc52003-04-18 12:34:58 +00001716xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001717 xmlParserInputBufferPtr ret;
Daniel Veillard388236f2001-07-08 18:35:48 +00001718 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001719 void *context = NULL;
1720
1721 if (xmlInputCallbackInitialized == 0)
1722 xmlRegisterDefaultInputCallbacks();
1723
1724 if (URI == NULL) return(NULL);
1725
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00001726#ifdef LIBXML_CATALOG_ENABLED
1727#endif
1728
Owen Taylor3473f882001-02-23 17:55:21 +00001729 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001730 * Try to find one of the input accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001731 * Go in reverse to give precedence to user defined handlers.
Daniel Veillard388236f2001-07-08 18:35:48 +00001732 */
1733 if (context == NULL) {
1734 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1735 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1736 (xmlInputCallbackTable[i].matchcallback(URI) != 0)) {
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001737 context = xmlInputCallbackTable[i].opencallback(URI);
Daniel Veillard388236f2001-07-08 18:35:48 +00001738 if (context != NULL)
1739 break;
1740 }
Owen Taylor3473f882001-02-23 17:55:21 +00001741 }
1742 }
1743 if (context == NULL) {
1744 return(NULL);
1745 }
1746
1747 /*
1748 * Allocate the Input buffer front-end.
1749 */
1750 ret = xmlAllocParserInputBuffer(enc);
1751 if (ret != NULL) {
1752 ret->context = context;
1753 ret->readcallback = xmlInputCallbackTable[i].readcallback;
1754 ret->closecallback = xmlInputCallbackTable[i].closecallback;
1755 }
1756 return(ret);
1757}
1758
1759/**
1760 * xmlOutputBufferCreateFilename:
1761 * @URI: a C string containing the URI or filename
1762 * @encoder: the encoding converter or NULL
1763 * @compression: the compression ration (0 none, 9 max).
1764 *
1765 * Create a buffered output for the progressive saving of a file
1766 * If filename is "-' then we use stdout as the output.
1767 * Automatic support for ZLIB/Compress compressed document is provided
1768 * by default if found at compile-time.
1769 * TODO: currently if compression is set, the library only support
1770 * writing to a local file.
1771 *
1772 * Returns the new output or NULL
1773 */
1774xmlOutputBufferPtr
1775xmlOutputBufferCreateFilename(const char *URI,
1776 xmlCharEncodingHandlerPtr encoder,
1777 int compression) {
1778 xmlOutputBufferPtr ret;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001779 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001780 void *context = NULL;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001781 char *unescaped;
Owen Taylor3473f882001-02-23 17:55:21 +00001782
Daniel Veillardf012a642001-07-23 19:10:52 +00001783 int is_http_uri = 0; /* Can't change if HTTP disabled */
1784
Owen Taylor3473f882001-02-23 17:55:21 +00001785 if (xmlOutputCallbackInitialized == 0)
1786 xmlRegisterDefaultOutputCallbacks();
1787
1788 if (URI == NULL) return(NULL);
1789
Daniel Veillardf012a642001-07-23 19:10:52 +00001790#ifdef LIBXML_HTTP_ENABLED
1791 /* Need to prevent HTTP URI's from falling into zlib short circuit */
1792
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001793 is_http_uri = xmlIOHTTPMatch( URI );
Daniel Veillardf012a642001-07-23 19:10:52 +00001794#endif
1795
Owen Taylor3473f882001-02-23 17:55:21 +00001796
1797 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001798 * Try to find one of the output accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001799 * Go in reverse to give precedence to user defined handlers.
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001800 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001801 */
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001802 unescaped = xmlURIUnescapeString(URI, 0, NULL);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001803 if (unescaped != NULL) {
1804#ifdef HAVE_ZLIB_H
1805 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
1806 context = xmlGzfileOpenW(unescaped, compression);
1807 if (context != NULL) {
1808 ret = xmlAllocOutputBuffer(encoder);
1809 if (ret != NULL) {
1810 ret->context = context;
1811 ret->writecallback = xmlGzfileWrite;
1812 ret->closecallback = xmlGzfileClose;
1813 }
1814 xmlFree(unescaped);
1815 return(ret);
1816 }
1817 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001818#endif
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001819 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1820 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
1821 (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) {
1822#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1823 /* Need to pass compression parameter into HTTP open calls */
1824 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1825 context = xmlIOHTTPOpenW(unescaped, compression);
1826 else
1827#endif
1828 context = xmlOutputCallbackTable[i].opencallback(unescaped);
1829 if (context != NULL)
1830 break;
1831 }
1832 }
1833 xmlFree(unescaped);
1834 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001835
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001836 /*
1837 * If this failed try with a non-escaped URI this may be a strange
1838 * filename
1839 */
1840 if (context == NULL) {
1841#ifdef HAVE_ZLIB_H
1842 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001843 context = xmlGzfileOpenW(URI, compression);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001844 if (context != NULL) {
1845 ret = xmlAllocOutputBuffer(encoder);
1846 if (ret != NULL) {
1847 ret->context = context;
1848 ret->writecallback = xmlGzfileWrite;
1849 ret->closecallback = xmlGzfileClose;
1850 }
1851 return(ret);
1852 }
1853 }
1854#endif
1855 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1856 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001857 (xmlOutputCallbackTable[i].matchcallback(URI) != 0)) {
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001858#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1859 /* Need to pass compression parameter into HTTP open calls */
1860 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1861 context = xmlIOHTTPOpenW(URI, compression);
1862 else
1863#endif
1864 context = xmlOutputCallbackTable[i].opencallback(URI);
1865 if (context != NULL)
1866 break;
1867 }
Owen Taylor3473f882001-02-23 17:55:21 +00001868 }
1869 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001870
Owen Taylor3473f882001-02-23 17:55:21 +00001871 if (context == NULL) {
1872 return(NULL);
1873 }
1874
1875 /*
1876 * Allocate the Output buffer front-end.
1877 */
1878 ret = xmlAllocOutputBuffer(encoder);
1879 if (ret != NULL) {
1880 ret->context = context;
1881 ret->writecallback = xmlOutputCallbackTable[i].writecallback;
1882 ret->closecallback = xmlOutputCallbackTable[i].closecallback;
1883 }
1884 return(ret);
1885}
1886
1887/**
1888 * xmlParserInputBufferCreateFile:
1889 * @file: a FILE*
1890 * @enc: the charset encoding if known
1891 *
1892 * Create a buffered parser input for the progressive parsing of a FILE *
1893 * buffered C I/O
1894 *
1895 * Returns the new parser input or NULL
1896 */
1897xmlParserInputBufferPtr
1898xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) {
1899 xmlParserInputBufferPtr ret;
1900
1901 if (xmlInputCallbackInitialized == 0)
1902 xmlRegisterDefaultInputCallbacks();
1903
1904 if (file == NULL) return(NULL);
1905
1906 ret = xmlAllocParserInputBuffer(enc);
1907 if (ret != NULL) {
1908 ret->context = file;
1909 ret->readcallback = xmlFileRead;
1910 ret->closecallback = xmlFileFlush;
1911 }
1912
1913 return(ret);
1914}
1915
1916/**
1917 * xmlOutputBufferCreateFile:
1918 * @file: a FILE*
1919 * @encoder: the encoding converter or NULL
1920 *
1921 * Create a buffered output for the progressive saving to a FILE *
1922 * buffered C I/O
1923 *
1924 * Returns the new parser output or NULL
1925 */
1926xmlOutputBufferPtr
1927xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) {
1928 xmlOutputBufferPtr ret;
1929
1930 if (xmlOutputCallbackInitialized == 0)
1931 xmlRegisterDefaultOutputCallbacks();
1932
1933 if (file == NULL) return(NULL);
1934
1935 ret = xmlAllocOutputBuffer(encoder);
1936 if (ret != NULL) {
1937 ret->context = file;
1938 ret->writecallback = xmlFileWrite;
1939 ret->closecallback = xmlFileFlush;
1940 }
1941
1942 return(ret);
1943}
1944
1945/**
1946 * xmlParserInputBufferCreateFd:
1947 * @fd: a file descriptor number
1948 * @enc: the charset encoding if known
1949 *
1950 * Create a buffered parser input for the progressive parsing for the input
1951 * from a file descriptor
1952 *
1953 * Returns the new parser input or NULL
1954 */
1955xmlParserInputBufferPtr
1956xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) {
1957 xmlParserInputBufferPtr ret;
1958
1959 if (fd < 0) return(NULL);
1960
1961 ret = xmlAllocParserInputBuffer(enc);
1962 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00001963 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00001964 ret->readcallback = xmlFdRead;
1965 ret->closecallback = xmlFdClose;
1966 }
1967
1968 return(ret);
1969}
1970
1971/**
1972 * xmlParserInputBufferCreateMem:
1973 * @mem: the memory input
1974 * @size: the length of the memory block
1975 * @enc: the charset encoding if known
1976 *
1977 * Create a buffered parser input for the progressive parsing for the input
1978 * from a memory area.
1979 *
1980 * Returns the new parser input or NULL
1981 */
1982xmlParserInputBufferPtr
1983xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
1984 xmlParserInputBufferPtr ret;
1985
1986 if (size <= 0) return(NULL);
1987 if (mem == NULL) return(NULL);
1988
1989 ret = xmlAllocParserInputBuffer(enc);
1990 if (ret != NULL) {
1991 ret->context = (void *) mem;
1992 ret->readcallback = (xmlInputReadCallback) xmlNop;
1993 ret->closecallback = NULL;
1994 xmlBufferAdd(ret->buffer, (const xmlChar *) mem, size);
1995 }
1996
1997 return(ret);
1998}
1999
2000/**
2001 * xmlOutputBufferCreateFd:
2002 * @fd: a file descriptor number
2003 * @encoder: the encoding converter or NULL
2004 *
2005 * Create a buffered output for the progressive saving
2006 * to a file descriptor
2007 *
2008 * Returns the new parser output or NULL
2009 */
2010xmlOutputBufferPtr
2011xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) {
2012 xmlOutputBufferPtr ret;
2013
2014 if (fd < 0) return(NULL);
2015
2016 ret = xmlAllocOutputBuffer(encoder);
2017 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00002018 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00002019 ret->writecallback = xmlFdWrite;
Daniel Veillard7db38712002-02-07 16:39:11 +00002020 ret->closecallback = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00002021 }
2022
2023 return(ret);
2024}
2025
2026/**
2027 * xmlParserInputBufferCreateIO:
2028 * @ioread: an I/O read function
2029 * @ioclose: an I/O close function
2030 * @ioctx: an I/O handler
2031 * @enc: the charset encoding if known
2032 *
2033 * Create a buffered parser input for the progressive parsing for the input
2034 * from an I/O handler
2035 *
2036 * Returns the new parser input or NULL
2037 */
2038xmlParserInputBufferPtr
2039xmlParserInputBufferCreateIO(xmlInputReadCallback ioread,
2040 xmlInputCloseCallback ioclose, void *ioctx, xmlCharEncoding enc) {
2041 xmlParserInputBufferPtr ret;
2042
2043 if (ioread == NULL) return(NULL);
2044
2045 ret = xmlAllocParserInputBuffer(enc);
2046 if (ret != NULL) {
2047 ret->context = (void *) ioctx;
2048 ret->readcallback = ioread;
2049 ret->closecallback = ioclose;
2050 }
2051
2052 return(ret);
2053}
2054
2055/**
2056 * xmlOutputBufferCreateIO:
2057 * @iowrite: an I/O write function
2058 * @ioclose: an I/O close function
2059 * @ioctx: an I/O handler
Daniel Veillard9d06d302002-01-22 18:15:52 +00002060 * @encoder: the charset encoding if known
Owen Taylor3473f882001-02-23 17:55:21 +00002061 *
2062 * Create a buffered output for the progressive saving
2063 * to an I/O handler
2064 *
2065 * Returns the new parser output or NULL
2066 */
2067xmlOutputBufferPtr
2068xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite,
2069 xmlOutputCloseCallback ioclose, void *ioctx,
2070 xmlCharEncodingHandlerPtr encoder) {
2071 xmlOutputBufferPtr ret;
2072
2073 if (iowrite == NULL) return(NULL);
2074
2075 ret = xmlAllocOutputBuffer(encoder);
2076 if (ret != NULL) {
2077 ret->context = (void *) ioctx;
2078 ret->writecallback = iowrite;
2079 ret->closecallback = ioclose;
2080 }
2081
2082 return(ret);
2083}
2084
2085/**
2086 * xmlParserInputBufferPush:
2087 * @in: a buffered parser input
2088 * @len: the size in bytes of the array.
2089 * @buf: an char array
2090 *
2091 * Push the content of the arry in the input buffer
2092 * This routine handle the I18N transcoding to internal UTF-8
2093 * This is used when operating the parser in progressive (push) mode.
2094 *
2095 * Returns the number of chars read and stored in the buffer, or -1
2096 * in case of error.
2097 */
2098int
2099xmlParserInputBufferPush(xmlParserInputBufferPtr in,
2100 int len, const char *buf) {
2101 int nbchars = 0;
2102
2103 if (len < 0) return(0);
2104 if (in->encoder != NULL) {
2105 /*
2106 * Store the data in the incoming raw buffer
2107 */
2108 if (in->raw == NULL) {
2109 in->raw = xmlBufferCreate();
2110 }
2111 xmlBufferAdd(in->raw, (const xmlChar *) buf, len);
2112
2113 /*
2114 * convert as much as possible to the parser reading buffer.
2115 */
2116 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2117 if (nbchars < 0) {
2118 xmlGenericError(xmlGenericErrorContext,
2119 "xmlParserInputBufferPush: encoder error\n");
2120 return(-1);
2121 }
2122 } else {
2123 nbchars = len;
2124 xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
2125 }
2126#ifdef DEBUG_INPUT
2127 xmlGenericError(xmlGenericErrorContext,
2128 "I/O: pushed %d chars, buffer %d/%d\n",
2129 nbchars, in->buffer->use, in->buffer->size);
2130#endif
2131 return(nbchars);
2132}
2133
2134/**
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002135 * endOfInput:
2136 *
2137 * When reading from an Input channel indicated end of file or error
2138 * don't reread from it again.
2139 */
2140static int
2141endOfInput (void * context ATTRIBUTE_UNUSED,
2142 char * buffer ATTRIBUTE_UNUSED,
2143 int len ATTRIBUTE_UNUSED) {
2144 return(0);
2145}
2146
2147/**
Owen Taylor3473f882001-02-23 17:55:21 +00002148 * xmlParserInputBufferGrow:
2149 * @in: a buffered parser input
2150 * @len: indicative value of the amount of chars to read
2151 *
2152 * Grow up the content of the input buffer, the old data are preserved
2153 * This routine handle the I18N transcoding to internal UTF-8
2154 * This routine is used when operating the parser in normal (pull) mode
2155 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002156 * TODO: one should be able to remove one extra copy by copying directly
Owen Taylor3473f882001-02-23 17:55:21 +00002157 * onto in->buffer or in->raw
2158 *
2159 * Returns the number of chars read and stored in the buffer, or -1
2160 * in case of error.
2161 */
2162int
2163xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) {
2164 char *buffer = NULL;
2165 int res = 0;
2166 int nbchars = 0;
2167 int buffree;
Daniel Veillard9e412302002-06-10 15:59:44 +00002168 unsigned int needSize;
Owen Taylor3473f882001-02-23 17:55:21 +00002169
2170 if ((len <= MINLEN) && (len != 4))
2171 len = MINLEN;
2172 buffree = in->buffer->size - in->buffer->use;
2173 if (buffree <= 0) {
2174 xmlGenericError(xmlGenericErrorContext,
2175 "xmlParserInputBufferGrow : buffer full !\n");
2176 return(0);
2177 }
2178 if (len > buffree)
2179 len = buffree;
2180
Daniel Veillarde5354492002-05-16 08:43:22 +00002181 needSize = in->buffer->use + len + 1;
2182 if (needSize > in->buffer->size){
2183 if (!xmlBufferResize(in->buffer, needSize)){
2184 xmlGenericError(xmlGenericErrorContext,
2185 "xmlBufferAdd : out of memory!\n");
2186 return(0);
2187 }
Owen Taylor3473f882001-02-23 17:55:21 +00002188 }
Daniel Veillarde5354492002-05-16 08:43:22 +00002189 buffer = (char *)&in->buffer->content[in->buffer->use];
Owen Taylor3473f882001-02-23 17:55:21 +00002190
2191 /*
2192 * Call the read method for this I/O type.
2193 */
2194 if (in->readcallback != NULL) {
2195 res = in->readcallback(in->context, &buffer[0], len);
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002196 if (res <= 0)
2197 in->readcallback = endOfInput;
Owen Taylor3473f882001-02-23 17:55:21 +00002198 } else {
2199 xmlGenericError(xmlGenericErrorContext,
2200 "xmlParserInputBufferGrow : no input !\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002201 return(-1);
2202 }
2203 if (res < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00002204 return(-1);
2205 }
2206 len = res;
2207 if (in->encoder != NULL) {
2208 /*
2209 * Store the data in the incoming raw buffer
2210 */
2211 if (in->raw == NULL) {
2212 in->raw = xmlBufferCreate();
2213 }
2214 xmlBufferAdd(in->raw, (const xmlChar *) buffer, len);
2215
2216 /*
2217 * convert as much as possible to the parser reading buffer.
2218 */
2219 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2220 if (nbchars < 0) {
2221 xmlGenericError(xmlGenericErrorContext,
2222 "xmlParserInputBufferGrow: encoder error\n");
2223 return(-1);
2224 }
2225 } else {
2226 nbchars = len;
Daniel Veillarde5354492002-05-16 08:43:22 +00002227 in->buffer->use += nbchars;
2228 buffer[nbchars] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002229 }
2230#ifdef DEBUG_INPUT
2231 xmlGenericError(xmlGenericErrorContext,
2232 "I/O: read %d chars, buffer %d/%d\n",
2233 nbchars, in->buffer->use, in->buffer->size);
2234#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002235 return(nbchars);
2236}
2237
2238/**
2239 * xmlParserInputBufferRead:
2240 * @in: a buffered parser input
2241 * @len: indicative value of the amount of chars to read
2242 *
2243 * Refresh the content of the input buffer, the old data are considered
2244 * consumed
2245 * This routine handle the I18N transcoding to internal UTF-8
2246 *
2247 * Returns the number of chars read and stored in the buffer, or -1
2248 * in case of error.
2249 */
2250int
2251xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) {
2252 /* xmlBufferEmpty(in->buffer); */
2253 if (in->readcallback != NULL)
2254 return(xmlParserInputBufferGrow(in, len));
2255 else
2256 return(-1);
2257}
2258
2259/**
2260 * xmlOutputBufferWrite:
2261 * @out: a buffered parser output
2262 * @len: the size in bytes of the array.
2263 * @buf: an char array
2264 *
2265 * Write the content of the array in the output I/O buffer
2266 * This routine handle the I18N transcoding from internal UTF-8
2267 * The buffer is lossless, i.e. will store in case of partial
2268 * or delayed writes.
2269 *
2270 * Returns the number of chars immediately written, or -1
2271 * in case of error.
2272 */
2273int
2274xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) {
2275 int nbchars = 0; /* number of chars to output to I/O */
2276 int ret; /* return from function call */
2277 int written = 0; /* number of char written to I/O so far */
2278 int chunk; /* number of byte curreent processed from buf */
2279
2280 if (len < 0) return(0);
2281
2282 do {
2283 chunk = len;
2284 if (chunk > 4 * MINLEN)
2285 chunk = 4 * MINLEN;
2286
2287 /*
2288 * first handle encoding stuff.
2289 */
2290 if (out->encoder != NULL) {
2291 /*
2292 * Store the data in the incoming raw buffer
2293 */
2294 if (out->conv == NULL) {
2295 out->conv = xmlBufferCreate();
2296 }
2297 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2298
2299 if ((out->buffer->use < MINLEN) && (chunk == len))
2300 goto done;
2301
2302 /*
2303 * convert as much as possible to the parser reading buffer.
2304 */
2305 ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
Daniel Veillard809faa52003-02-10 15:43:53 +00002306 if ((ret < 0) && (ret != -3)) {
Owen Taylor3473f882001-02-23 17:55:21 +00002307 xmlGenericError(xmlGenericErrorContext,
2308 "xmlOutputBufferWrite: encoder error\n");
2309 return(-1);
2310 }
2311 nbchars = out->conv->use;
2312 } else {
2313 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2314 nbchars = out->buffer->use;
2315 }
2316 buf += chunk;
2317 len -= chunk;
2318
2319 if ((nbchars < MINLEN) && (len <= 0))
2320 goto done;
2321
2322 if (out->writecallback) {
2323 /*
2324 * second write the stuff to the I/O channel
2325 */
2326 if (out->encoder != NULL) {
2327 ret = out->writecallback(out->context,
2328 (const char *)out->conv->content, nbchars);
2329 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002330 xmlBufferShrink(out->conv, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002331 } else {
2332 ret = out->writecallback(out->context,
2333 (const char *)out->buffer->content, nbchars);
2334 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002335 xmlBufferShrink(out->buffer, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002336 }
2337 if (ret < 0) {
2338 xmlGenericError(xmlGenericErrorContext,
2339 "I/O: error %d writing %d bytes\n", ret, nbchars);
2340 return(ret);
2341 }
2342 out->written += ret;
2343 }
2344 written += nbchars;
2345 } while (len > 0);
2346
2347done:
2348#ifdef DEBUG_INPUT
2349 xmlGenericError(xmlGenericErrorContext,
2350 "I/O: wrote %d chars\n", written);
2351#endif
2352 return(written);
2353}
2354
2355/**
2356 * xmlOutputBufferWriteString:
2357 * @out: a buffered parser output
2358 * @str: a zero terminated C string
2359 *
2360 * Write the content of the string in the output I/O buffer
2361 * This routine handle the I18N transcoding from internal UTF-8
2362 * The buffer is lossless, i.e. will store in case of partial
2363 * or delayed writes.
2364 *
2365 * Returns the number of chars immediately written, or -1
2366 * in case of error.
2367 */
2368int
2369xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) {
2370 int len;
2371
2372 if (str == NULL)
2373 return(-1);
2374 len = strlen(str);
2375
2376 if (len > 0)
2377 return(xmlOutputBufferWrite(out, len, str));
2378 return(len);
2379}
2380
2381/**
2382 * xmlOutputBufferFlush:
2383 * @out: a buffered output
2384 *
2385 * flushes the output I/O channel
2386 *
2387 * Returns the number of byte written or -1 in case of error.
2388 */
2389int
2390xmlOutputBufferFlush(xmlOutputBufferPtr out) {
2391 int nbchars = 0, ret = 0;
2392
2393 /*
2394 * first handle encoding stuff.
2395 */
2396 if ((out->conv != NULL) && (out->encoder != NULL)) {
2397 /*
2398 * convert as much as possible to the parser reading buffer.
2399 */
2400 nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2401 if (nbchars < 0) {
2402 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002403 "xmlOutputBufferFlush: encoder error\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002404 return(-1);
2405 }
2406 }
2407
2408 /*
2409 * second flush the stuff to the I/O channel
2410 */
2411 if ((out->conv != NULL) && (out->encoder != NULL) &&
2412 (out->writecallback != NULL)) {
2413 ret = out->writecallback(out->context,
2414 (const char *)out->conv->content, out->conv->use);
2415 if (ret >= 0)
2416 xmlBufferShrink(out->conv, ret);
2417 } else if (out->writecallback != NULL) {
2418 ret = out->writecallback(out->context,
2419 (const char *)out->buffer->content, out->buffer->use);
2420 if (ret >= 0)
2421 xmlBufferShrink(out->buffer, ret);
2422 }
2423 if (ret < 0) {
2424 xmlGenericError(xmlGenericErrorContext,
2425 "I/O: error %d flushing %d bytes\n", ret, nbchars);
2426 return(ret);
2427 }
2428 out->written += ret;
2429
2430#ifdef DEBUG_INPUT
2431 xmlGenericError(xmlGenericErrorContext,
2432 "I/O: flushed %d chars\n", ret);
2433#endif
2434 return(ret);
2435}
2436
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002437/**
Owen Taylor3473f882001-02-23 17:55:21 +00002438 * xmlParserGetDirectory:
2439 * @filename: the path to a file
2440 *
2441 * lookup the directory for that file
2442 *
2443 * Returns a new allocated string containing the directory, or NULL.
2444 */
2445char *
2446xmlParserGetDirectory(const char *filename) {
2447 char *ret = NULL;
2448 char dir[1024];
2449 char *cur;
2450 char sep = '/';
2451
Igor Zlatkovic9181cc02002-09-29 17:51:06 +00002452#ifdef _WIN32_WCE /* easy way by now ... wince does not have dirs! */
2453 return NULL;
2454#endif
2455
Owen Taylor3473f882001-02-23 17:55:21 +00002456 if (xmlInputCallbackInitialized == 0)
2457 xmlRegisterDefaultInputCallbacks();
2458
2459 if (filename == NULL) return(NULL);
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002460#if defined(WIN32) && !defined(__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +00002461 sep = '\\';
2462#endif
2463
2464 strncpy(dir, filename, 1023);
2465 dir[1023] = 0;
2466 cur = &dir[strlen(dir)];
2467 while (cur > dir) {
2468 if (*cur == sep) break;
2469 cur --;
2470 }
2471 if (*cur == sep) {
2472 if (cur == dir) dir[1] = 0;
2473 else *cur = 0;
2474 ret = xmlMemStrdup(dir);
2475 } else {
2476 if (getcwd(dir, 1024) != NULL) {
2477 dir[1023] = 0;
2478 ret = xmlMemStrdup(dir);
2479 }
2480 }
2481 return(ret);
2482}
2483
2484/****************************************************************
2485 * *
2486 * External entities loading *
2487 * *
2488 ****************************************************************/
2489
Daniel Veillard561b7f82002-03-20 21:55:57 +00002490#ifdef LIBXML_CATALOG_ENABLED
2491static int xmlSysIDExists(const char *URL) {
Daniel Veillard6990bf32001-08-23 21:17:48 +00002492#ifdef HAVE_STAT
2493 int ret;
2494 struct stat info;
2495 const char *path;
2496
2497 if (URL == NULL)
2498 return(0);
2499
Daniel Veillardf4862f02002-09-10 11:13:43 +00002500 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2501#if defined (_WIN32) && !defined(__CYGWIN__)
2502 path = &URL[17];
2503#else
Daniel Veillard6990bf32001-08-23 21:17:48 +00002504 path = &URL[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +00002505#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002506 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002507#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard6990bf32001-08-23 21:17:48 +00002508 path = &URL[8];
2509#else
2510 path = &URL[7];
2511#endif
2512 } else
2513 path = URL;
2514 ret = stat(path, &info);
Daniel Veillard561b7f82002-03-20 21:55:57 +00002515 if (ret == 0)
2516 return(1);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002517#endif
Daniel Veillard561b7f82002-03-20 21:55:57 +00002518 return(0);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002519}
Daniel Veillard561b7f82002-03-20 21:55:57 +00002520#endif
Daniel Veillard6990bf32001-08-23 21:17:48 +00002521
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002522/**
Owen Taylor3473f882001-02-23 17:55:21 +00002523 * xmlDefaultExternalEntityLoader:
2524 * @URL: the URL for the entity to load
2525 * @ID: the System ID for the entity to load
2526 * @ctxt: the context in which the entity is called or NULL
2527 *
2528 * By default we don't load external entitites, yet.
2529 *
2530 * Returns a new allocated xmlParserInputPtr, or NULL.
2531 */
2532static
2533xmlParserInputPtr
2534xmlDefaultExternalEntityLoader(const char *URL, const char *ID,
2535 xmlParserCtxtPtr ctxt) {
2536 xmlParserInputPtr ret = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002537 xmlChar *resource = NULL;
2538#ifdef LIBXML_CATALOG_ENABLED
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002539 xmlCatalogAllow pref;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002540#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002541
2542#ifdef DEBUG_EXTERNAL_ENTITIES
2543 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00002544 "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
Owen Taylor3473f882001-02-23 17:55:21 +00002545#endif
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002546#ifdef LIBXML_CATALOG_ENABLED
2547 /*
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002548 * If the resource doesn't exists as a file,
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002549 * try to load it from the resource pointed in the catalogs
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002550 */
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002551 pref = xmlCatalogGetDefaults();
2552
Daniel Veillard561b7f82002-03-20 21:55:57 +00002553 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlSysIDExists(URL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002554 /*
2555 * Do a local lookup
2556 */
2557 if ((ctxt->catalogs != NULL) &&
2558 ((pref == XML_CATA_ALLOW_ALL) ||
2559 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2560 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2561 (const xmlChar *)ID,
2562 (const xmlChar *)URL);
2563 }
2564 /*
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002565 * Try a global lookup
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002566 */
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002567 if ((resource == NULL) &&
2568 ((pref == XML_CATA_ALLOW_ALL) ||
2569 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002570 resource = xmlCatalogResolve((const xmlChar *)ID,
2571 (const xmlChar *)URL);
2572 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002573 if ((resource == NULL) && (URL != NULL))
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002574 resource = xmlStrdup((const xmlChar *) URL);
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002575
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002576 /*
2577 * TODO: do an URI lookup on the reference
2578 */
Daniel Veillard561b7f82002-03-20 21:55:57 +00002579 if ((resource != NULL) && (!xmlSysIDExists((const char *)resource))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002580 xmlChar *tmp = NULL;
2581
2582 if ((ctxt->catalogs != NULL) &&
2583 ((pref == XML_CATA_ALLOW_ALL) ||
2584 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2585 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2586 }
2587 if ((tmp == NULL) &&
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002588 ((pref == XML_CATA_ALLOW_ALL) ||
2589 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002590 tmp = xmlCatalogResolveURI(resource);
2591 }
2592
2593 if (tmp != NULL) {
2594 xmlFree(resource);
2595 resource = tmp;
2596 }
2597 }
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002598 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002599#endif
2600
2601 if (resource == NULL)
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002602 resource = (xmlChar *) URL;
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002603
2604 if (resource == NULL) {
Daniel Veillardc6613042002-03-02 09:34:02 +00002605 if (ID == NULL)
2606 ID = "NULL";
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002607 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2608 (ctxt->sax->error != NULL))
2609 ctxt->sax->error(ctxt,
2610 "failed to load external entity \"%s\"\n", ID);
2611 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002612 ctxt->sax->warning(ctxt,
2613 "failed to load external entity \"%s\"\n", ID);
2614 return(NULL);
2615 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002616 ret = xmlNewInputFromFile(ctxt, (const char *)resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002617 if (ret == NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002618 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2619 (ctxt->sax->error != NULL))
2620 ctxt->sax->error(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002621 "failed to load external entity \"%s\"\n", resource);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002622 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002623 ctxt->sax->warning(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002624 "failed to load external entity \"%s\"\n", resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002625 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002626 if ((resource != NULL) && (resource != (xmlChar *) URL))
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002627 xmlFree(resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002628 return(ret);
2629}
2630
2631static xmlExternalEntityLoader xmlCurrentExternalEntityLoader =
2632 xmlDefaultExternalEntityLoader;
2633
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002634/**
Owen Taylor3473f882001-02-23 17:55:21 +00002635 * xmlSetExternalEntityLoader:
2636 * @f: the new entity resolver function
2637 *
2638 * Changes the defaultexternal entity resolver function for the application
2639 */
2640void
2641xmlSetExternalEntityLoader(xmlExternalEntityLoader f) {
2642 xmlCurrentExternalEntityLoader = f;
2643}
2644
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002645/**
Owen Taylor3473f882001-02-23 17:55:21 +00002646 * xmlGetExternalEntityLoader:
2647 *
2648 * Get the default external entity resolver function for the application
2649 *
2650 * Returns the xmlExternalEntityLoader function pointer
2651 */
2652xmlExternalEntityLoader
2653xmlGetExternalEntityLoader(void) {
2654 return(xmlCurrentExternalEntityLoader);
2655}
2656
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002657/**
Owen Taylor3473f882001-02-23 17:55:21 +00002658 * xmlLoadExternalEntity:
2659 * @URL: the URL for the entity to load
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002660 * @ID: the Public ID for the entity to load
Owen Taylor3473f882001-02-23 17:55:21 +00002661 * @ctxt: the context in which the entity is called or NULL
2662 *
2663 * Load an external entity, note that the use of this function for
2664 * unparsed entities may generate problems
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002665 * TODO: a more generic External entity API must be designed
Owen Taylor3473f882001-02-23 17:55:21 +00002666 *
2667 * Returns the xmlParserInputPtr or NULL
2668 */
2669xmlParserInputPtr
2670xmlLoadExternalEntity(const char *URL, const char *ID,
2671 xmlParserCtxtPtr ctxt) {
2672 return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
2673}
2674
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002675/************************************************************************
2676 * *
2677 * Disabling Network access *
2678 * *
2679 ************************************************************************/
2680
2681#ifdef LIBXML_CATALOG_ENABLED
2682static int
2683xmlNoNetExists(const char *URL)
2684{
2685#ifdef HAVE_STAT
2686 int ret;
2687 struct stat info;
2688 const char *path;
2689
2690 if (URL == NULL)
2691 return (0);
2692
Daniel Veillardf4862f02002-09-10 11:13:43 +00002693 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2694#if defined (_WIN32) && !defined(__CYGWIN__)
2695 path = &URL[17];
2696#else
2697 path = &URL[16];
2698#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002699 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002700#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002701 path = &URL[8];
2702#else
2703 path = &URL[7];
2704#endif
2705 } else
2706 path = URL;
2707 ret = stat(path, &info);
2708 if (ret == 0)
2709 return (1);
2710#endif
2711 return (0);
2712}
2713#endif
2714
2715/**
2716 * xmlNoNetExternalEntityLoader:
2717 * @URL: the URL for the entity to load
2718 * @ID: the System ID for the entity to load
2719 * @ctxt: the context in which the entity is called or NULL
2720 *
2721 * A specific entity loader disabling network accesses, though still
2722 * allowing local catalog accesses for resolution.
2723 *
2724 * Returns a new allocated xmlParserInputPtr, or NULL.
2725 */
2726xmlParserInputPtr
2727xmlNoNetExternalEntityLoader(const char *URL, const char *ID,
2728 xmlParserCtxtPtr ctxt) {
2729 xmlParserInputPtr input = NULL;
2730 xmlChar *resource = NULL;
2731
2732#ifdef LIBXML_CATALOG_ENABLED
2733 xmlCatalogAllow pref;
2734
2735 /*
2736 * If the resource doesn't exists as a file,
2737 * try to load it from the resource pointed in the catalogs
2738 */
2739 pref = xmlCatalogGetDefaults();
2740
2741 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) {
2742 /*
2743 * Do a local lookup
2744 */
2745 if ((ctxt->catalogs != NULL) &&
2746 ((pref == XML_CATA_ALLOW_ALL) ||
2747 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2748 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2749 (const xmlChar *)ID,
2750 (const xmlChar *)URL);
2751 }
2752 /*
2753 * Try a global lookup
2754 */
2755 if ((resource == NULL) &&
2756 ((pref == XML_CATA_ALLOW_ALL) ||
2757 (pref == XML_CATA_ALLOW_GLOBAL))) {
2758 resource = xmlCatalogResolve((const xmlChar *)ID,
2759 (const xmlChar *)URL);
2760 }
2761 if ((resource == NULL) && (URL != NULL))
2762 resource = xmlStrdup((const xmlChar *) URL);
2763
2764 /*
2765 * TODO: do an URI lookup on the reference
2766 */
2767 if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) {
2768 xmlChar *tmp = NULL;
2769
2770 if ((ctxt->catalogs != NULL) &&
2771 ((pref == XML_CATA_ALLOW_ALL) ||
2772 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2773 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2774 }
2775 if ((tmp == NULL) &&
2776 ((pref == XML_CATA_ALLOW_ALL) ||
2777 (pref == XML_CATA_ALLOW_GLOBAL))) {
2778 tmp = xmlCatalogResolveURI(resource);
2779 }
2780
2781 if (tmp != NULL) {
2782 xmlFree(resource);
2783 resource = tmp;
2784 }
2785 }
2786 }
2787#endif
2788 if (resource == NULL)
2789 resource = (xmlChar *) URL;
2790
2791 if (resource != NULL) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002792 if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) ||
2793 (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) {
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002794 xmlGenericError(xmlGenericErrorContext,
2795 "Attempt to load network entity %s \n", resource);
2796
2797 if (resource != (xmlChar *) URL)
2798 xmlFree(resource);
2799 return(NULL);
2800 }
2801 }
2802 input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt);
2803 if (resource != (xmlChar *) URL)
2804 xmlFree(resource);
2805 return(input);
2806}
2807