blob: 82416f2074eea7ba5744043029198d6ac00b8d57 [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 Veillardf4862f02002-09-10 11:13:43 +0000127 *
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +0000128 * This function is obsolete. Please see xmlURIFromPath in uri.c for
129 * a better solution.
Daniel Veillardf4862f02002-09-10 11:13:43 +0000130 */
131xmlChar *
132xmlNormalizeWindowsPath(const xmlChar *path)
133{
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +0000134 return xmlCanonicPath(path);
Daniel Veillardf4862f02002-09-10 11:13:43 +0000135}
136
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000137/**
138 * xmlCleanupInputCallbacks:
139 *
140 * clears the entire input callback table. this includes the
141 * compiled-in I/O.
142 */
143void
144xmlCleanupInputCallbacks(void)
145{
146 int i;
147
148 if (!xmlInputCallbackInitialized)
149 return;
150
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000151 for (i = xmlInputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000152 xmlInputCallbackTable[i].matchcallback = NULL;
153 xmlInputCallbackTable[i].opencallback = NULL;
154 xmlInputCallbackTable[i].readcallback = NULL;
155 xmlInputCallbackTable[i].closecallback = NULL;
156 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000157 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000158
159 xmlInputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000160 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000161}
162
163/**
164 * xmlCleanupOutputCallbacks:
165 *
166 * clears the entire output callback table. this includes the
167 * compiled-in I/O callbacks.
168 */
169void
170xmlCleanupOutputCallbacks(void)
171{
172 int i;
173
174 if (!xmlOutputCallbackInitialized)
175 return;
176
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000177 for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000178 xmlOutputCallbackTable[i].matchcallback = NULL;
179 xmlOutputCallbackTable[i].opencallback = NULL;
180 xmlOutputCallbackTable[i].writecallback = NULL;
181 xmlOutputCallbackTable[i].closecallback = NULL;
182 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000183 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000184
185 xmlOutputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000186 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000187}
188
Owen Taylor3473f882001-02-23 17:55:21 +0000189/************************************************************************
190 * *
191 * Standard I/O for file accesses *
192 * *
193 ************************************************************************/
194
195/**
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000196 * xmlCheckFilename:
Owen Taylor3473f882001-02-23 17:55:21 +0000197 * @path: the path to check
198 *
199 * function checks to see if @path is a valid source
200 * (file, socket...) for XML.
201 *
202 * if stat is not available on the target machine,
203 * returns 1. if stat fails, returns 0 (if calling
204 * stat on the filename fails, it can't be right).
205 * if stat succeeds and the file is a directory,
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000206 * returns 2. otherwise returns 1.
Owen Taylor3473f882001-02-23 17:55:21 +0000207 */
208
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000209int
Owen Taylor3473f882001-02-23 17:55:21 +0000210xmlCheckFilename (const char *path)
211{
212#ifdef HAVE_STAT
Owen Taylor3473f882001-02-23 17:55:21 +0000213 struct stat stat_buffer;
214
215 if (stat(path, &stat_buffer) == -1)
216 return 0;
217
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000218#ifdef S_ISDIR
Owen Taylor3473f882001-02-23 17:55:21 +0000219 if (S_ISDIR(stat_buffer.st_mode)) {
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000220 return 2;
Owen Taylor3473f882001-02-23 17:55:21 +0000221 }
Owen Taylor3473f882001-02-23 17:55:21 +0000222#endif
223#endif
224 return 1;
225}
226
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000227static int
Owen Taylor3473f882001-02-23 17:55:21 +0000228xmlNop(void) {
229 return(0);
230}
231
232/**
Owen Taylor3473f882001-02-23 17:55:21 +0000233 * xmlFdRead:
234 * @context: the I/O context
235 * @buffer: where to drop data
236 * @len: number of bytes to read
237 *
238 * Read @len bytes to @buffer from the I/O channel.
239 *
240 * Returns the number of bytes written
241 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000242static int
Owen Taylor3473f882001-02-23 17:55:21 +0000243xmlFdRead (void * context, char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000244 return(read((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000245}
246
247/**
248 * xmlFdWrite:
249 * @context: the I/O context
250 * @buffer: where to get data
251 * @len: number of bytes to write
252 *
253 * Write @len bytes from @buffer to the I/O channel.
254 *
255 * Returns the number of bytes written
256 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000257static int
Owen Taylor3473f882001-02-23 17:55:21 +0000258xmlFdWrite (void * context, const char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000259 return(write((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000260}
261
262/**
263 * xmlFdClose:
264 * @context: the I/O context
265 *
266 * Close an I/O channel
Daniel Veillardf012a642001-07-23 19:10:52 +0000267 *
268 * Returns 0 in case of success and error code otherwise
Owen Taylor3473f882001-02-23 17:55:21 +0000269 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000270static int
Owen Taylor3473f882001-02-23 17:55:21 +0000271xmlFdClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000272 return ( close((int) (long) context) );
Owen Taylor3473f882001-02-23 17:55:21 +0000273}
274
275/**
276 * xmlFileMatch:
277 * @filename: the URI for matching
278 *
279 * input from FILE *
280 *
281 * Returns 1 if matches, 0 otherwise
282 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000283int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000284xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000285 return(1);
286}
287
288/**
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000289 * xmlFileOpen_real:
Owen Taylor3473f882001-02-23 17:55:21 +0000290 * @filename: the URI for matching
291 *
292 * input from FILE *, supports compressed input
293 * if @filename is " " then the standard input is used
294 *
295 * Returns an I/O context or NULL in case of error
296 */
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000297static void *
298xmlFileOpen_real (const char *filename) {
Owen Taylor3473f882001-02-23 17:55:21 +0000299 const char *path = NULL;
300 FILE *fd;
301
302 if (!strcmp(filename, "-")) {
303 fd = stdin;
304 return((void *) fd);
305 }
306
Daniel Veillardf4862f02002-09-10 11:13:43 +0000307 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000308#if defined (_WIN32) && !defined(__CYGWIN__)
309 path = &filename[17];
310#else
Owen Taylor3473f882001-02-23 17:55:21 +0000311 path = &filename[16];
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000312#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000313 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000314#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000315 path = &filename[8];
316#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000317 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000318#endif
319 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000320 path = filename;
321
322 if (path == NULL)
323 return(NULL);
324 if (!xmlCheckFilename(path))
325 return(NULL);
326
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000327#if defined(WIN32) || defined (__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +0000328 fd = fopen(path, "rb");
329#else
330 fd = fopen(path, "r");
331#endif /* WIN32 */
332 return((void *) fd);
333}
334
335/**
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000336 * xmlFileOpen:
337 * @filename: the URI for matching
338 *
339 * Wrapper around xmlFileOpen_real that try it with an unescaped
340 * version of @filename, if this fails fallback to @filename
Daniel Veillard71531f32003-02-05 13:19:53 +0000341 *
342 * Returns a handler or NULL in case or failure
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000343 */
344void *
345xmlFileOpen (const char *filename) {
346 char *unescaped;
347 void *retval;
348 unescaped = xmlURIUnescapeString(filename, 0, NULL);
349 if (unescaped != NULL) {
350 retval = xmlFileOpen_real(unescaped);
351 } else {
352 retval = xmlFileOpen_real(filename);
353 }
354 xmlFree(unescaped);
355 return retval;
356}
357
358/**
Owen Taylor3473f882001-02-23 17:55:21 +0000359 * xmlFileOpenW:
360 * @filename: the URI for matching
361 *
362 * output to from FILE *,
363 * if @filename is "-" then the standard output is used
364 *
365 * Returns an I/O context or NULL in case of error
366 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000367static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000368xmlFileOpenW (const char *filename) {
369 const char *path = NULL;
370 FILE *fd;
371
372 if (!strcmp(filename, "-")) {
373 fd = stdout;
374 return((void *) fd);
375 }
376
Daniel Veillardf4862f02002-09-10 11:13:43 +0000377 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
378#if defined (_WIN32) && !defined(__CYGWIN__)
379 path = &filename[17];
380#else
Owen Taylor3473f882001-02-23 17:55:21 +0000381 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000382#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000383 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000384#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000385 path = &filename[8];
386#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000387 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000388#endif
389 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000390 path = filename;
391
392 if (path == NULL)
393 return(NULL);
394
395 fd = fopen(path, "w");
396 return((void *) fd);
397}
398
399/**
400 * xmlFileRead:
401 * @context: the I/O context
402 * @buffer: where to drop data
403 * @len: number of bytes to write
404 *
405 * Read @len bytes to @buffer from the I/O channel.
406 *
407 * Returns the number of bytes written
408 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000409int
Owen Taylor3473f882001-02-23 17:55:21 +0000410xmlFileRead (void * context, char * buffer, int len) {
411 return(fread(&buffer[0], 1, len, (FILE *) context));
412}
413
414/**
415 * xmlFileWrite:
416 * @context: the I/O context
417 * @buffer: where to drop data
418 * @len: number of bytes to write
419 *
420 * Write @len bytes from @buffer to the I/O channel.
421 *
422 * Returns the number of bytes written
423 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000424static int
Owen Taylor3473f882001-02-23 17:55:21 +0000425xmlFileWrite (void * context, const char * buffer, int len) {
Daniel Veillard4a6d39b2002-12-17 18:33:01 +0000426 int items;
427
428 items = fwrite(&buffer[0], len, 1, (FILE *) context);
429
430 return(items * len);
Owen Taylor3473f882001-02-23 17:55:21 +0000431}
432
433/**
434 * xmlFileClose:
435 * @context: the I/O context
436 *
437 * Close an I/O channel
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000438 *
439 * Returns 0 or -1 in case of error
Owen Taylor3473f882001-02-23 17:55:21 +0000440 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000441int
Owen Taylor3473f882001-02-23 17:55:21 +0000442xmlFileClose (void * context) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000443 FILE *fil;
444
445 fil = (FILE *) context;
446 if (fil == stdin)
447 return(0);
448 if (fil == stdout)
449 return(0);
Daniel Veillardcd337f02001-11-22 18:20:37 +0000450 if (fil == stderr)
451 return(0);
Daniel Veillardf012a642001-07-23 19:10:52 +0000452 return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000453}
454
455/**
456 * xmlFileFlush:
457 * @context: the I/O context
458 *
459 * Flush an I/O channel
460 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000461static int
Owen Taylor3473f882001-02-23 17:55:21 +0000462xmlFileFlush (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000463 return ( ( fflush((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000464}
465
466#ifdef HAVE_ZLIB_H
467/************************************************************************
468 * *
469 * I/O for compressed file accesses *
470 * *
471 ************************************************************************/
472/**
473 * xmlGzfileMatch:
474 * @filename: the URI for matching
475 *
476 * input from compressed file test
477 *
478 * Returns 1 if matches, 0 otherwise
479 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000480static int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000481xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000482 return(1);
483}
484
485/**
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000486 * xmlGzfileOpen_real:
Owen Taylor3473f882001-02-23 17:55:21 +0000487 * @filename: the URI for matching
488 *
489 * input from compressed file open
490 * if @filename is " " then the standard input is used
491 *
492 * Returns an I/O context or NULL in case of error
493 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000494static void *
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000495xmlGzfileOpen_real (const char *filename) {
Owen Taylor3473f882001-02-23 17:55:21 +0000496 const char *path = NULL;
497 gzFile fd;
498
499 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000500 fd = gzdopen(dup(0), "rb");
Owen Taylor3473f882001-02-23 17:55:21 +0000501 return((void *) fd);
502 }
503
Daniel Veillardf4862f02002-09-10 11:13:43 +0000504 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
505#if defined (_WIN32) && !defined(__CYGWIN__)
506 path = &filename[17];
507#else
Owen Taylor3473f882001-02-23 17:55:21 +0000508 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000509#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000510 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000511#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000512 path = &filename[8];
513#else
Owen Taylor3473f882001-02-23 17:55:21 +0000514 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000515#endif
516 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000517 path = filename;
518
519 if (path == NULL)
520 return(NULL);
521 if (!xmlCheckFilename(path))
522 return(NULL);
523
524 fd = gzopen(path, "rb");
525 return((void *) fd);
526}
527
528/**
Daniel Veillardcacbe5d2003-01-10 16:09:51 +0000529 * xmlGzfileOpen:
530 * @filename: the URI for matching
531 *
532 * Wrapper around xmlGzfileOpen that try it with an unescaped
533 * version of @filename, if this fails fallback to @filename
534 */
535static void *
536xmlGzfileOpen (const char *filename) {
537 char *unescaped;
538 void *retval;
539 unescaped = xmlURIUnescapeString(filename, 0, NULL);
540 if (unescaped != NULL) {
541 retval = xmlGzfileOpen_real(unescaped);
542 } else {
543 retval = xmlGzfileOpen_real(filename);
544 }
545 xmlFree(unescaped);
546 return retval;
547}
548
549/**
Owen Taylor3473f882001-02-23 17:55:21 +0000550 * xmlGzfileOpenW:
551 * @filename: the URI for matching
552 * @compression: the compression factor (0 - 9 included)
553 *
554 * input from compressed file open
555 * if @filename is " " then the standard input is used
556 *
557 * Returns an I/O context or NULL in case of error
558 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000559static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000560xmlGzfileOpenW (const char *filename, int compression) {
561 const char *path = NULL;
562 char mode[15];
563 gzFile fd;
564
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000565 snprintf(mode, sizeof(mode), "wb%d", compression);
Owen Taylor3473f882001-02-23 17:55:21 +0000566 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000567 fd = gzdopen(dup(1), mode);
Owen Taylor3473f882001-02-23 17:55:21 +0000568 return((void *) fd);
569 }
570
Daniel Veillardf4862f02002-09-10 11:13:43 +0000571 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
572#if defined (_WIN32) && !defined(__CYGWIN__)
573 path = &filename[17];
574#else
Owen Taylor3473f882001-02-23 17:55:21 +0000575 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000576#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000577 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000578#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000579 path = &filename[8];
580#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000581 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000582#endif
583 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000584 path = filename;
585
586 if (path == NULL)
587 return(NULL);
588
589 fd = gzopen(path, mode);
590 return((void *) fd);
591}
592
593/**
594 * xmlGzfileRead:
595 * @context: the I/O context
596 * @buffer: where to drop data
597 * @len: number of bytes to write
598 *
599 * Read @len bytes to @buffer from the compressed I/O channel.
600 *
601 * Returns the number of bytes written
602 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000603static int
Owen Taylor3473f882001-02-23 17:55:21 +0000604xmlGzfileRead (void * context, char * buffer, int len) {
605 return(gzread((gzFile) context, &buffer[0], len));
606}
607
608/**
609 * xmlGzfileWrite:
610 * @context: the I/O context
611 * @buffer: where to drop data
612 * @len: number of bytes to write
613 *
614 * Write @len bytes from @buffer to the compressed I/O channel.
615 *
616 * Returns the number of bytes written
617 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000618static int
Owen Taylor3473f882001-02-23 17:55:21 +0000619xmlGzfileWrite (void * context, const char * buffer, int len) {
620 return(gzwrite((gzFile) context, (char *) &buffer[0], len));
621}
622
623/**
624 * xmlGzfileClose:
625 * @context: the I/O context
626 *
627 * Close a compressed I/O channel
628 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000629static int
Owen Taylor3473f882001-02-23 17:55:21 +0000630xmlGzfileClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000631 return ( ( gzclose((gzFile) context) == Z_OK ) ? 0 : -1 );
Owen Taylor3473f882001-02-23 17:55:21 +0000632}
633#endif /* HAVE_ZLIB_H */
634
635#ifdef LIBXML_HTTP_ENABLED
636/************************************************************************
637 * *
638 * I/O for HTTP file accesses *
639 * *
640 ************************************************************************/
Daniel Veillardf012a642001-07-23 19:10:52 +0000641
642typedef struct xmlIOHTTPWriteCtxt_
643{
644 int compression;
645
646 char * uri;
647
648 void * doc_buff;
649
650} xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr;
651
652#ifdef HAVE_ZLIB_H
653
654#define DFLT_WBITS ( -15 )
655#define DFLT_MEM_LVL ( 8 )
656#define GZ_MAGIC1 ( 0x1f )
657#define GZ_MAGIC2 ( 0x8b )
658#define LXML_ZLIB_OS_CODE ( 0x03 )
659#define INIT_HTTP_BUFF_SIZE ( 32768 )
660#define DFLT_ZLIB_RATIO ( 5 )
661
662/*
663** Data structure and functions to work with sending compressed data
664** via HTTP.
665*/
666
667typedef struct xmlZMemBuff_
668{
669 unsigned long size;
670 unsigned long crc;
671
672 unsigned char * zbuff;
673 z_stream zctrl;
674
675} xmlZMemBuff, *xmlZMemBuffPtr;
676
677/**
678 * append_reverse_ulong
679 * @buff: Compressed memory buffer
680 * @data: Unsigned long to append
681 *
682 * Append a unsigned long in reverse byte order to the end of the
683 * memory buffer.
684 */
685static void
686append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) {
687
688 int idx;
689
690 if ( buff == NULL )
691 return;
692
693 /*
694 ** This is plagiarized from putLong in gzio.c (zlib source) where
695 ** the number "4" is hardcoded. If zlib is ever patched to
696 ** support 64 bit file sizes, this code would need to be patched
697 ** as well.
698 */
699
700 for ( idx = 0; idx < 4; idx++ ) {
701 *buff->zctrl.next_out = ( data & 0xff );
702 data >>= 8;
703 buff->zctrl.next_out++;
704 }
705
706 return;
707}
708
709/**
710 *
711 * xmlFreeZMemBuff
712 * @buff: The memory buffer context to clear
713 *
714 * Release all the resources associated with the compressed memory buffer.
715 */
716static void
717xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
718
719 int z_err;
720
721 if ( buff == NULL )
722 return;
723
724 xmlFree( buff->zbuff );
725 z_err = deflateEnd( &buff->zctrl );
726#ifdef DEBUG_HTTP
727 if ( z_err != Z_OK )
728 xmlGenericError( xmlGenericErrorContext,
729 "xmlFreeZMemBuff: Error releasing zlib context: %d\n",
730 z_err );
731#endif
732
733 xmlFree( buff );
734 return;
735}
736
737/**
738 * xmlCreateZMemBuff
739 *@compression: Compression value to use
740 *
741 * Create a memory buffer to hold the compressed XML document. The
742 * compressed document in memory will end up being identical to what
743 * would be created if gzopen/gzwrite/gzclose were being used to
744 * write the document to disk. The code for the header/trailer data to
745 * the compression is plagiarized from the zlib source files.
746 */
747static void *
748xmlCreateZMemBuff( int compression ) {
749
750 int z_err;
751 int hdr_lgth;
752 xmlZMemBuffPtr buff = NULL;
753
754 if ( ( compression < 1 ) || ( compression > 9 ) )
755 return ( NULL );
756
757 /* Create the control and data areas */
758
759 buff = xmlMalloc( sizeof( xmlZMemBuff ) );
760 if ( buff == NULL ) {
761 xmlGenericError( xmlGenericErrorContext,
762 "xmlCreateZMemBuff: %s\n",
763 "Failure allocating buffer context." );
764 return ( NULL );
765 }
766
767 (void)memset( buff, 0, sizeof( xmlZMemBuff ) );
768 buff->size = INIT_HTTP_BUFF_SIZE;
769 buff->zbuff = xmlMalloc( buff->size );
770 if ( buff->zbuff == NULL ) {
771 xmlFreeZMemBuff( buff );
772 xmlGenericError( xmlGenericErrorContext,
773 "xmlCreateZMemBuff: %s\n",
774 "Failure allocating data buffer." );
775 return ( NULL );
776 }
777
778 z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
779 DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
780 if ( z_err != Z_OK ) {
781 xmlFreeZMemBuff( buff );
782 buff = NULL;
783 xmlGenericError( xmlGenericErrorContext,
784 "xmlCreateZMemBuff: %s %d\n",
785 "Error initializing compression context. ZLIB error:",
786 z_err );
787 return ( NULL );
788 }
789
790 /* Set the header data. The CRC will be needed for the trailer */
Daniel Veillardf012a642001-07-23 19:10:52 +0000791 buff->crc = crc32( 0L, Z_NULL, 0 );
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000792 hdr_lgth = snprintf( (char *)buff->zbuff, buff->size,
793 "%c%c%c%c%c%c%c%c%c%c",
Daniel Veillardf012a642001-07-23 19:10:52 +0000794 GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED,
795 0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE );
796 buff->zctrl.next_out = buff->zbuff + hdr_lgth;
797 buff->zctrl.avail_out = buff->size - hdr_lgth;
798
799 return ( buff );
800}
801
802/**
803 * xmlZMemBuffExtend
804 * @buff: Buffer used to compress and consolidate data.
805 * @ext_amt: Number of bytes to extend the buffer.
806 *
807 * Extend the internal buffer used to store the compressed data by the
808 * specified amount.
809 *
810 * Returns 0 on success or -1 on failure to extend the buffer. On failure
811 * the original buffer still exists at the original size.
812 */
813static int
814xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) {
815
816 int rc = -1;
817 size_t new_size;
818 size_t cur_used;
819
820 unsigned char * tmp_ptr = NULL;
821
822 if ( buff == NULL )
823 return ( -1 );
824
825 else if ( ext_amt == 0 )
826 return ( 0 );
827
828 cur_used = buff->zctrl.next_out - buff->zbuff;
829 new_size = buff->size + ext_amt;
830
831#ifdef DEBUG_HTTP
832 if ( cur_used > new_size )
833 xmlGenericError( xmlGenericErrorContext,
834 "xmlZMemBuffExtend: %s\n%s %d bytes.\n",
835 "Buffer overwrite detected during compressed memory",
836 "buffer extension. Overflowed by",
837 (cur_used - new_size ) );
838#endif
839
840 tmp_ptr = xmlRealloc( buff->zbuff, new_size );
841 if ( tmp_ptr != NULL ) {
842 rc = 0;
843 buff->size = new_size;
844 buff->zbuff = tmp_ptr;
845 buff->zctrl.next_out = tmp_ptr + cur_used;
846 buff->zctrl.avail_out = new_size - cur_used;
847 }
848 else {
849 xmlGenericError( xmlGenericErrorContext,
850 "xmlZMemBuffExtend: %s %lu bytes.\n",
851 "Allocation failure extending output buffer to",
852 new_size );
853 }
854
855 return ( rc );
856}
857
858/**
859 * xmlZMemBuffAppend
860 * @buff: Buffer used to compress and consolidate data
861 * @src: Uncompressed source content to append to buffer
862 * @len: Length of source data to append to buffer
863 *
864 * Compress and append data to the internal buffer. The data buffer
865 * will be expanded if needed to store the additional data.
866 *
867 * Returns the number of bytes appended to the buffer or -1 on error.
868 */
869static int
870xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) {
871
872 int z_err;
873 size_t min_accept;
874
875 if ( ( buff == NULL ) || ( src == NULL ) )
876 return ( -1 );
877
878 buff->zctrl.avail_in = len;
879 buff->zctrl.next_in = (unsigned char *)src;
880 while ( buff->zctrl.avail_in > 0 ) {
881 /*
882 ** Extend the buffer prior to deflate call if a reasonable amount
883 ** of output buffer space is not available.
884 */
885 min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO;
886 if ( buff->zctrl.avail_out <= min_accept ) {
887 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
888 return ( -1 );
889 }
890
891 z_err = deflate( &buff->zctrl, Z_NO_FLUSH );
892 if ( z_err != Z_OK ) {
893 xmlGenericError( xmlGenericErrorContext,
894 "xmlZMemBuffAppend: %s %d %s - %d",
895 "Compression error while appending",
896 len, "bytes to buffer. ZLIB error", z_err );
897 return ( -1 );
898 }
899 }
900
901 buff->crc = crc32( buff->crc, (unsigned char *)src, len );
902
903 return ( len );
904}
905
906/**
907 * xmlZMemBuffGetContent
908 * @buff: Compressed memory content buffer
909 * @data_ref: Pointer reference to point to compressed content
910 *
911 * Flushes the compression buffers, appends gzip file trailers and
912 * returns the compressed content and length of the compressed data.
913 * NOTE: The gzip trailer code here is plagiarized from zlib source.
914 *
915 * Returns the length of the compressed data or -1 on error.
916 */
917static int
918xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) {
919
920 int zlgth = -1;
921 int z_err;
922
923 if ( ( buff == NULL ) || ( data_ref == NULL ) )
924 return ( -1 );
925
926 /* Need to loop until compression output buffers are flushed */
927
928 do
929 {
930 z_err = deflate( &buff->zctrl, Z_FINISH );
931 if ( z_err == Z_OK ) {
932 /* In this case Z_OK means more buffer space needed */
933
934 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
935 return ( -1 );
936 }
937 }
938 while ( z_err == Z_OK );
939
940 /* If the compression state is not Z_STREAM_END, some error occurred */
941
942 if ( z_err == Z_STREAM_END ) {
943
944 /* Need to append the gzip data trailer */
945
946 if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) {
947 if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 )
948 return ( -1 );
949 }
950
951 /*
952 ** For whatever reason, the CRC and length data are pushed out
953 ** in reverse byte order. So a memcpy can't be used here.
954 */
955
956 append_reverse_ulong( buff, buff->crc );
957 append_reverse_ulong( buff, buff->zctrl.total_in );
958
959 zlgth = buff->zctrl.next_out - buff->zbuff;
960 *data_ref = (char *)buff->zbuff;
961 }
962
963 else
964 xmlGenericError( xmlGenericErrorContext,
965 "xmlZMemBuffGetContent: %s - %d\n",
966 "Error flushing zlib buffers. Error code", z_err );
967
968 return ( zlgth );
969}
970#endif /* HAVE_ZLIB_H */
971
972/**
973 * xmlFreeHTTPWriteCtxt
974 * @ctxt: Context to cleanup
975 *
976 * Free allocated memory and reclaim system resources.
977 *
978 * No return value.
979 */
980static void
981xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
982{
983 if ( ctxt->uri != NULL )
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000984 xmlFree( ctxt->uri );
Daniel Veillardf012a642001-07-23 19:10:52 +0000985
986 if ( ctxt->doc_buff != NULL ) {
987
988#ifdef HAVE_ZLIB_H
989 if ( ctxt->compression > 0 ) {
990 xmlFreeZMemBuff( ctxt->doc_buff );
991 }
992 else
993#endif
994 {
995 xmlOutputBufferClose( ctxt->doc_buff );
996 }
997 }
998
Daniel Veillard819d5cb2002-10-14 11:15:18 +0000999 xmlFree( ctxt );
Daniel Veillardf012a642001-07-23 19:10:52 +00001000 return;
1001}
1002
1003
Owen Taylor3473f882001-02-23 17:55:21 +00001004/**
1005 * xmlIOHTTPMatch:
1006 * @filename: the URI for matching
1007 *
1008 * check if the URI matches an HTTP one
1009 *
1010 * Returns 1 if matches, 0 otherwise
1011 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001012int
Owen Taylor3473f882001-02-23 17:55:21 +00001013xmlIOHTTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001014 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7))
Owen Taylor3473f882001-02-23 17:55:21 +00001015 return(1);
1016 return(0);
1017}
1018
1019/**
1020 * xmlIOHTTPOpen:
1021 * @filename: the URI for matching
1022 *
1023 * open an HTTP I/O channel
1024 *
1025 * Returns an I/O context or NULL in case of error
1026 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001027void *
Owen Taylor3473f882001-02-23 17:55:21 +00001028xmlIOHTTPOpen (const char *filename) {
1029 return(xmlNanoHTTPOpen(filename, NULL));
1030}
1031
1032/**
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001033 * xmlIOHTTPOpenW:
Daniel Veillardf012a642001-07-23 19:10:52 +00001034 * @post_uri: The destination URI for the document
1035 * @compression: The compression desired for the document.
1036 *
1037 * Open a temporary buffer to collect the document for a subsequent HTTP POST
1038 * request. Non-static as is called from the output buffer creation routine.
1039 *
1040 * Returns an I/O context or NULL in case of error.
1041 */
1042
1043void *
Daniel Veillard572577e2002-01-18 16:23:55 +00001044xmlIOHTTPOpenW(const char *post_uri, int compression)
1045{
Daniel Veillardf012a642001-07-23 19:10:52 +00001046
Daniel Veillard572577e2002-01-18 16:23:55 +00001047 xmlIOHTTPWriteCtxtPtr ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001048
Daniel Veillard572577e2002-01-18 16:23:55 +00001049 if (post_uri == NULL)
1050 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001051
Daniel Veillard572577e2002-01-18 16:23:55 +00001052 ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt));
1053 if (ctxt == NULL) {
1054 xmlGenericError(xmlGenericErrorContext,
1055 "xmlIOHTTPOpenW: Failed to create output HTTP context.\n");
1056 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001057 }
1058
Daniel Veillard572577e2002-01-18 16:23:55 +00001059 (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt));
Daniel Veillardf012a642001-07-23 19:10:52 +00001060
Daniel Veillard572577e2002-01-18 16:23:55 +00001061 ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri);
1062 if (ctxt->uri == NULL) {
1063 xmlGenericError(xmlGenericErrorContext,
1064 "xmlIOHTTPOpenW: Failed to duplicate destination URI.\n");
1065 xmlFreeHTTPWriteCtxt(ctxt);
1066 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001067 }
1068
1069 /*
Daniel Veillard572577e2002-01-18 16:23:55 +00001070 * ** Since the document length is required for an HTTP post,
1071 * ** need to put the document into a buffer. A memory buffer
1072 * ** is being used to avoid pushing the data to disk and back.
1073 */
Daniel Veillardf012a642001-07-23 19:10:52 +00001074
1075#ifdef HAVE_ZLIB_H
Daniel Veillard572577e2002-01-18 16:23:55 +00001076 if ((compression > 0) && (compression <= 9)) {
1077
1078 ctxt->compression = compression;
1079 ctxt->doc_buff = xmlCreateZMemBuff(compression);
1080 } else
Daniel Veillardf012a642001-07-23 19:10:52 +00001081#endif
1082 {
Daniel Veillard572577e2002-01-18 16:23:55 +00001083 /* Any character conversions should have been done before this */
Daniel Veillardf012a642001-07-23 19:10:52 +00001084
Daniel Veillard572577e2002-01-18 16:23:55 +00001085 ctxt->doc_buff = xmlAllocOutputBuffer(NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001086 }
1087
Daniel Veillard572577e2002-01-18 16:23:55 +00001088 if (ctxt->doc_buff == NULL) {
1089 xmlFreeHTTPWriteCtxt(ctxt);
1090 ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001091 }
1092
Daniel Veillard572577e2002-01-18 16:23:55 +00001093 return (ctxt);
Daniel Veillardf012a642001-07-23 19:10:52 +00001094}
1095
1096/**
1097 * xmlIOHTTPDfltOpenW
1098 * @post_uri: The destination URI for this document.
1099 *
1100 * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent
1101 * HTTP post command. This function should generally not be used as
1102 * the open callback is short circuited in xmlOutputBufferCreateFile.
1103 *
1104 * Returns a pointer to the new IO context.
1105 */
1106static void *
1107xmlIOHTTPDfltOpenW( const char * post_uri ) {
1108 return ( xmlIOHTTPOpenW( post_uri, 0 ) );
1109}
1110
1111/**
Owen Taylor3473f882001-02-23 17:55:21 +00001112 * xmlIOHTTPRead:
1113 * @context: the I/O context
1114 * @buffer: where to drop data
1115 * @len: number of bytes to write
1116 *
1117 * Read @len bytes to @buffer from the I/O channel.
1118 *
1119 * Returns the number of bytes written
1120 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001121int
Owen Taylor3473f882001-02-23 17:55:21 +00001122xmlIOHTTPRead(void * context, char * buffer, int len) {
1123 return(xmlNanoHTTPRead(context, &buffer[0], len));
1124}
1125
1126/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001127 * xmlIOHTTPWrite
1128 * @context: previously opened writing context
1129 * @buffer: data to output to temporary buffer
1130 * @len: bytes to output
1131 *
1132 * Collect data from memory buffer into a temporary file for later
1133 * processing.
1134 *
1135 * Returns number of bytes written.
1136 */
1137
1138static int
1139xmlIOHTTPWrite( void * context, const char * buffer, int len ) {
1140
1141 xmlIOHTTPWriteCtxtPtr ctxt = context;
1142
1143 if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) )
1144 return ( -1 );
1145
1146 if ( len > 0 ) {
1147
1148 /* Use gzwrite or fwrite as previously setup in the open call */
1149
1150#ifdef HAVE_ZLIB_H
1151 if ( ctxt->compression > 0 )
1152 len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len );
1153
1154 else
1155#endif
1156 len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer );
1157
1158 if ( len < 0 ) {
1159 xmlGenericError( xmlGenericErrorContext,
1160 "xmlIOHTTPWrite: %s\n%s '%s'.\n",
1161 "Error appending to internal buffer.",
1162 "Error sending document to URI",
1163 ctxt->uri );
1164 }
1165 }
1166
1167 return ( len );
1168}
1169
1170
1171/**
Owen Taylor3473f882001-02-23 17:55:21 +00001172 * xmlIOHTTPClose:
1173 * @context: the I/O context
1174 *
1175 * Close an HTTP I/O channel
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001176 *
1177 * Returns 0
Owen Taylor3473f882001-02-23 17:55:21 +00001178 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001179int
Owen Taylor3473f882001-02-23 17:55:21 +00001180xmlIOHTTPClose (void * context) {
1181 xmlNanoHTTPClose(context);
Daniel Veillardf012a642001-07-23 19:10:52 +00001182 return 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001183}
Daniel Veillardf012a642001-07-23 19:10:52 +00001184
1185/**
1186 * xmlIOHTTCloseWrite
1187 * @context: The I/O context
1188 * @http_mthd: The HTTP method to be used when sending the data
1189 *
1190 * Close the transmit HTTP I/O channel and actually send the data.
1191 */
1192static int
1193xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) {
1194
1195 int close_rc = -1;
1196 int http_rtn = 0;
1197 int content_lgth = 0;
1198 xmlIOHTTPWriteCtxtPtr ctxt = context;
1199
1200 char * http_content = NULL;
1201 char * content_encoding = NULL;
1202 char * content_type = (char *) "text/xml";
1203 void * http_ctxt = NULL;
1204
1205 if ( ( ctxt == NULL ) || ( http_mthd == NULL ) )
1206 return ( -1 );
1207
1208 /* Retrieve the content from the appropriate buffer */
1209
1210#ifdef HAVE_ZLIB_H
1211
1212 if ( ctxt->compression > 0 ) {
1213 content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content );
1214 content_encoding = (char *) "Content-Encoding: gzip";
1215 }
1216 else
1217#endif
1218 {
1219 /* Pull the data out of the memory output buffer */
1220
1221 xmlOutputBufferPtr dctxt = ctxt->doc_buff;
1222 http_content = (char *)dctxt->buffer->content;
1223 content_lgth = dctxt->buffer->use;
1224 }
1225
1226 if ( http_content == NULL ) {
1227 xmlGenericError( xmlGenericErrorContext,
1228 "xmlIOHTTPCloseWrite: %s '%s' %s '%s'.\n",
1229 "Error retrieving content.\nUnable to",
1230 http_mthd, "data to URI", ctxt->uri );
1231 }
1232
1233 else {
1234
1235 http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content,
1236 &content_type, content_encoding,
1237 content_lgth );
1238
1239 if ( http_ctxt != NULL ) {
1240#ifdef DEBUG_HTTP
1241 /* If testing/debugging - dump reply with request content */
1242
1243 FILE * tst_file = NULL;
1244 char buffer[ 4096 ];
1245 char * dump_name = NULL;
1246 int avail;
1247
1248 xmlGenericError( xmlGenericErrorContext,
1249 "xmlNanoHTTPCloseWrite: HTTP %s to\n%s returned %d.\n",
1250 http_mthd, ctxt->uri,
1251 xmlNanoHTTPReturnCode( http_ctxt ) );
1252
1253 /*
1254 ** Since either content or reply may be gzipped,
1255 ** dump them to separate files instead of the
1256 ** standard error context.
1257 */
1258
1259 dump_name = tempnam( NULL, "lxml" );
1260 if ( dump_name != NULL ) {
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001261 (void)snprintf( buffer, sizeof(buffer), "%s.content", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001262
1263 tst_file = fopen( buffer, "w" );
1264 if ( tst_file != NULL ) {
1265 xmlGenericError( xmlGenericErrorContext,
1266 "Transmitted content saved in file: %s\n", buffer );
1267
1268 fwrite( http_content, sizeof( char ),
1269 content_lgth, tst_file );
1270 fclose( tst_file );
1271 }
1272
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001273 (void)snprintf( buffer, sizeof(buffer), "%s.reply", dump_name );
Daniel Veillardf012a642001-07-23 19:10:52 +00001274 tst_file = fopen( buffer, "w" );
1275 if ( tst_file != NULL ) {
1276 xmlGenericError( xmlGenericErrorContext,
1277 "Reply content saved in file: %s\n", buffer );
1278
1279
1280 while ( (avail = xmlNanoHTTPRead( http_ctxt,
1281 buffer, sizeof( buffer ) )) > 0 ) {
1282
1283 fwrite( buffer, sizeof( char ), avail, tst_file );
1284 }
1285
1286 fclose( tst_file );
1287 }
1288
1289 free( dump_name );
1290 }
1291#endif /* DEBUG_HTTP */
1292
1293 http_rtn = xmlNanoHTTPReturnCode( http_ctxt );
1294 if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) )
1295 close_rc = 0;
1296 else
1297 xmlGenericError( xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001298 "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n",
Daniel Veillardf012a642001-07-23 19:10:52 +00001299 http_mthd, content_lgth,
1300 "bytes to URI", ctxt->uri,
1301 "failed. HTTP return code:", http_rtn );
1302
1303 xmlNanoHTTPClose( http_ctxt );
1304 xmlFree( content_type );
1305 }
1306 }
1307
1308 /* Final cleanups */
1309
1310 xmlFreeHTTPWriteCtxt( ctxt );
1311
1312 return ( close_rc );
1313}
1314
1315/**
1316 * xmlIOHTTPClosePut
1317 *
1318 * @context: The I/O context
1319 *
1320 * Close the transmit HTTP I/O channel and actually send data using a PUT
1321 * HTTP method.
1322 */
1323static int
1324xmlIOHTTPClosePut( void * ctxt ) {
1325 return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) );
1326}
1327
1328
1329/**
1330 * xmlIOHTTPClosePost
1331 *
1332 * @context: The I/O context
1333 *
1334 * Close the transmit HTTP I/O channel and actually send data using a POST
1335 * HTTP method.
1336 */
1337static int
1338xmlIOHTTPClosePost( void * ctxt ) {
1339 return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) );
1340}
1341
Owen Taylor3473f882001-02-23 17:55:21 +00001342#endif /* LIBXML_HTTP_ENABLED */
1343
1344#ifdef LIBXML_FTP_ENABLED
1345/************************************************************************
1346 * *
1347 * I/O for FTP file accesses *
1348 * *
1349 ************************************************************************/
1350/**
1351 * xmlIOFTPMatch:
1352 * @filename: the URI for matching
1353 *
1354 * check if the URI matches an FTP one
1355 *
1356 * Returns 1 if matches, 0 otherwise
1357 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001358int
Owen Taylor3473f882001-02-23 17:55:21 +00001359xmlIOFTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001360 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "ftp://", 6))
Owen Taylor3473f882001-02-23 17:55:21 +00001361 return(1);
1362 return(0);
1363}
1364
1365/**
1366 * xmlIOFTPOpen:
1367 * @filename: the URI for matching
1368 *
1369 * open an FTP I/O channel
1370 *
1371 * Returns an I/O context or NULL in case of error
1372 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001373void *
Owen Taylor3473f882001-02-23 17:55:21 +00001374xmlIOFTPOpen (const char *filename) {
1375 return(xmlNanoFTPOpen(filename));
1376}
1377
1378/**
1379 * xmlIOFTPRead:
1380 * @context: the I/O context
1381 * @buffer: where to drop data
1382 * @len: number of bytes to write
1383 *
1384 * Read @len bytes to @buffer from the I/O channel.
1385 *
1386 * Returns the number of bytes written
1387 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001388int
Owen Taylor3473f882001-02-23 17:55:21 +00001389xmlIOFTPRead(void * context, char * buffer, int len) {
1390 return(xmlNanoFTPRead(context, &buffer[0], len));
1391}
1392
1393/**
1394 * xmlIOFTPClose:
1395 * @context: the I/O context
1396 *
1397 * Close an FTP I/O channel
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001398 *
1399 * Returns 0
Owen Taylor3473f882001-02-23 17:55:21 +00001400 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001401int
Owen Taylor3473f882001-02-23 17:55:21 +00001402xmlIOFTPClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001403 return ( xmlNanoFTPClose(context) );
Owen Taylor3473f882001-02-23 17:55:21 +00001404}
1405#endif /* LIBXML_FTP_ENABLED */
1406
1407
1408/**
1409 * xmlRegisterInputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001410 * @matchFunc: the xmlInputMatchCallback
1411 * @openFunc: the xmlInputOpenCallback
1412 * @readFunc: the xmlInputReadCallback
1413 * @closeFunc: the xmlInputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001414 *
1415 * Register a new set of I/O callback for handling parser input.
1416 *
1417 * Returns the registered handler number or -1 in case of error
1418 */
1419int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001420xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc,
1421 xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
1422 xmlInputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001423 if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) {
1424 return(-1);
1425 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001426 xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc;
1427 xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc;
1428 xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc;
1429 xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001430 return(xmlInputCallbackNr++);
1431}
1432
1433/**
1434 * xmlRegisterOutputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001435 * @matchFunc: the xmlOutputMatchCallback
1436 * @openFunc: the xmlOutputOpenCallback
1437 * @writeFunc: the xmlOutputWriteCallback
1438 * @closeFunc: the xmlOutputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001439 *
1440 * Register a new set of I/O callback for handling output.
1441 *
1442 * Returns the registered handler number or -1 in case of error
1443 */
1444int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001445xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc,
1446 xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc,
1447 xmlOutputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001448 if (xmlOutputCallbackNr >= MAX_INPUT_CALLBACK) {
1449 return(-1);
1450 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001451 xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc;
1452 xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc;
1453 xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc;
1454 xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001455 return(xmlOutputCallbackNr++);
1456}
1457
1458/**
1459 * xmlRegisterDefaultInputCallbacks:
1460 *
1461 * Registers the default compiled-in I/O handlers.
1462 */
1463void
Owen Taylor3473f882001-02-23 17:55:21 +00001464xmlRegisterDefaultInputCallbacks
Owen Taylor3473f882001-02-23 17:55:21 +00001465(void) {
1466 if (xmlInputCallbackInitialized)
1467 return;
1468
1469 xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
1470 xmlFileRead, xmlFileClose);
1471#ifdef HAVE_ZLIB_H
1472 xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1473 xmlGzfileRead, xmlGzfileClose);
1474#endif /* HAVE_ZLIB_H */
1475
1476#ifdef LIBXML_HTTP_ENABLED
1477 xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
1478 xmlIOHTTPRead, xmlIOHTTPClose);
1479#endif /* LIBXML_HTTP_ENABLED */
1480
1481#ifdef LIBXML_FTP_ENABLED
1482 xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1483 xmlIOFTPRead, xmlIOFTPClose);
1484#endif /* LIBXML_FTP_ENABLED */
1485 xmlInputCallbackInitialized = 1;
1486}
1487
1488/**
1489 * xmlRegisterDefaultOutputCallbacks:
1490 *
1491 * Registers the default compiled-in I/O handlers.
1492 */
1493void
Owen Taylor3473f882001-02-23 17:55:21 +00001494xmlRegisterDefaultOutputCallbacks
Owen Taylor3473f882001-02-23 17:55:21 +00001495(void) {
1496 if (xmlOutputCallbackInitialized)
1497 return;
1498
1499 xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
1500 xmlFileWrite, xmlFileClose);
Daniel Veillardf012a642001-07-23 19:10:52 +00001501
1502#ifdef LIBXML_HTTP_ENABLED
1503 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1504 xmlIOHTTPWrite, xmlIOHTTPClosePut);
1505#endif
1506
Owen Taylor3473f882001-02-23 17:55:21 +00001507/*********************************
1508 No way a-priori to distinguish between gzipped files from
1509 uncompressed ones except opening if existing then closing
1510 and saving with same compression ratio ... a pain.
1511
1512#ifdef HAVE_ZLIB_H
1513 xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1514 xmlGzfileWrite, xmlGzfileClose);
1515#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001516
1517 Nor FTP PUT ....
1518#ifdef LIBXML_FTP_ENABLED
1519 xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1520 xmlIOFTPWrite, xmlIOFTPClose);
1521#endif
1522 **********************************/
1523 xmlOutputCallbackInitialized = 1;
1524}
1525
Daniel Veillardf012a642001-07-23 19:10:52 +00001526#ifdef LIBXML_HTTP_ENABLED
1527/**
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001528 * xmlRegisterHTTPPostCallbacks:
Daniel Veillardf012a642001-07-23 19:10:52 +00001529 *
1530 * By default, libxml submits HTTP output requests using the "PUT" method.
1531 * Calling this method changes the HTTP output method to use the "POST"
1532 * method instead.
1533 *
1534 */
1535void
1536xmlRegisterHTTPPostCallbacks( void ) {
1537
1538 /* Register defaults if not done previously */
1539
1540 if ( xmlOutputCallbackInitialized == 0 )
1541 xmlRegisterDefaultOutputCallbacks( );
1542
1543 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1544 xmlIOHTTPWrite, xmlIOHTTPClosePost);
1545 return;
1546}
1547#endif
1548
Owen Taylor3473f882001-02-23 17:55:21 +00001549/**
1550 * xmlAllocParserInputBuffer:
1551 * @enc: the charset encoding if known
1552 *
1553 * Create a buffered parser input for progressive parsing
1554 *
1555 * Returns the new parser input or NULL
1556 */
1557xmlParserInputBufferPtr
1558xmlAllocParserInputBuffer(xmlCharEncoding enc) {
1559 xmlParserInputBufferPtr ret;
1560
1561 ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
1562 if (ret == NULL) {
1563 xmlGenericError(xmlGenericErrorContext,
1564 "xmlAllocParserInputBuffer : out of memory!\n");
1565 return(NULL);
1566 }
1567 memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
1568 ret->buffer = xmlBufferCreate();
1569 if (ret->buffer == NULL) {
1570 xmlFree(ret);
1571 return(NULL);
1572 }
1573 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1574 ret->encoder = xmlGetCharEncodingHandler(enc);
1575 if (ret->encoder != NULL)
1576 ret->raw = xmlBufferCreate();
1577 else
1578 ret->raw = NULL;
1579 ret->readcallback = NULL;
1580 ret->closecallback = NULL;
1581 ret->context = NULL;
1582
1583 return(ret);
1584}
1585
1586/**
1587 * xmlAllocOutputBuffer:
1588 * @encoder: the encoding converter or NULL
1589 *
1590 * Create a buffered parser output
1591 *
1592 * Returns the new parser output or NULL
1593 */
1594xmlOutputBufferPtr
1595xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) {
1596 xmlOutputBufferPtr ret;
1597
1598 ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1599 if (ret == NULL) {
1600 xmlGenericError(xmlGenericErrorContext,
1601 "xmlAllocOutputBuffer : out of memory!\n");
1602 return(NULL);
1603 }
1604 memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
1605 ret->buffer = xmlBufferCreate();
1606 if (ret->buffer == NULL) {
1607 xmlFree(ret);
1608 return(NULL);
1609 }
1610 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1611 ret->encoder = encoder;
1612 if (encoder != NULL) {
1613 ret->conv = xmlBufferCreateSize(4000);
1614 /*
1615 * This call is designed to initiate the encoder state
1616 */
1617 xmlCharEncOutFunc(encoder, ret->conv, NULL);
1618 } else
1619 ret->conv = NULL;
1620 ret->writecallback = NULL;
1621 ret->closecallback = NULL;
1622 ret->context = NULL;
1623 ret->written = 0;
1624
1625 return(ret);
1626}
1627
1628/**
1629 * xmlFreeParserInputBuffer:
1630 * @in: a buffered parser input
1631 *
1632 * Free up the memory used by a buffered parser input
1633 */
1634void
1635xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
1636 if (in->raw) {
1637 xmlBufferFree(in->raw);
1638 in->raw = NULL;
1639 }
1640 if (in->encoder != NULL) {
1641 xmlCharEncCloseFunc(in->encoder);
1642 }
1643 if (in->closecallback != NULL) {
1644 in->closecallback(in->context);
1645 }
1646 if (in->buffer != NULL) {
1647 xmlBufferFree(in->buffer);
1648 in->buffer = NULL;
1649 }
1650
Owen Taylor3473f882001-02-23 17:55:21 +00001651 xmlFree(in);
1652}
1653
1654/**
1655 * xmlOutputBufferClose:
1656 * @out: a buffered output
1657 *
1658 * flushes and close the output I/O channel
1659 * and free up all the associated resources
1660 *
1661 * Returns the number of byte written or -1 in case of error.
1662 */
1663int
1664xmlOutputBufferClose(xmlOutputBufferPtr out) {
1665 int written;
Daniel Veillardf012a642001-07-23 19:10:52 +00001666 int err_rc = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001667
1668 if (out == NULL)
1669 return(-1);
1670 if (out->writecallback != NULL)
1671 xmlOutputBufferFlush(out);
1672 if (out->closecallback != NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001673 err_rc = out->closecallback(out->context);
Owen Taylor3473f882001-02-23 17:55:21 +00001674 }
1675 written = out->written;
1676 if (out->conv) {
1677 xmlBufferFree(out->conv);
1678 out->conv = NULL;
1679 }
1680 if (out->encoder != NULL) {
1681 xmlCharEncCloseFunc(out->encoder);
1682 }
1683 if (out->buffer != NULL) {
1684 xmlBufferFree(out->buffer);
1685 out->buffer = NULL;
1686 }
1687
Owen Taylor3473f882001-02-23 17:55:21 +00001688 xmlFree(out);
Daniel Veillardf012a642001-07-23 19:10:52 +00001689 return( ( err_rc == 0 ) ? written : err_rc );
Owen Taylor3473f882001-02-23 17:55:21 +00001690}
1691
1692/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001693 * xmlParserInputBufferCreateFname:
1694 * @URI: a C string containing the URI or filename
1695 * @enc: the charset encoding if known
1696 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001697 * Returns the new parser input or NULL
1698 */
1699/**
Owen Taylor3473f882001-02-23 17:55:21 +00001700 * xmlParserInputBufferCreateFilename:
1701 * @URI: a C string containing the URI or filename
1702 * @enc: the charset encoding if known
1703 *
1704 * Create a buffered parser input for the progressive parsing of a file
1705 * If filename is "-' then we use stdin as the input.
1706 * Automatic support for ZLIB/Compress compressed document is provided
1707 * by default if found at compile-time.
1708 * Do an encoding check if enc == XML_CHAR_ENCODING_NONE
1709 *
1710 * Returns the new parser input or NULL
1711 */
1712xmlParserInputBufferPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001713xmlParserInputBufferCreateFilename
Owen Taylor3473f882001-02-23 17:55:21 +00001714(const char *URI, xmlCharEncoding enc) {
1715 xmlParserInputBufferPtr ret;
Daniel Veillard388236f2001-07-08 18:35:48 +00001716 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001717 void *context = NULL;
1718
1719 if (xmlInputCallbackInitialized == 0)
1720 xmlRegisterDefaultInputCallbacks();
1721
1722 if (URI == NULL) return(NULL);
1723
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00001724#ifdef LIBXML_CATALOG_ENABLED
1725#endif
1726
Owen Taylor3473f882001-02-23 17:55:21 +00001727 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001728 * Try to find one of the input accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001729 * Go in reverse to give precedence to user defined handlers.
Daniel Veillard388236f2001-07-08 18:35:48 +00001730 */
1731 if (context == NULL) {
1732 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1733 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1734 (xmlInputCallbackTable[i].matchcallback(URI) != 0)) {
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001735 context = xmlInputCallbackTable[i].opencallback(URI);
Daniel Veillard388236f2001-07-08 18:35:48 +00001736 if (context != NULL)
1737 break;
1738 }
Owen Taylor3473f882001-02-23 17:55:21 +00001739 }
1740 }
1741 if (context == NULL) {
1742 return(NULL);
1743 }
1744
1745 /*
1746 * Allocate the Input buffer front-end.
1747 */
1748 ret = xmlAllocParserInputBuffer(enc);
1749 if (ret != NULL) {
1750 ret->context = context;
1751 ret->readcallback = xmlInputCallbackTable[i].readcallback;
1752 ret->closecallback = xmlInputCallbackTable[i].closecallback;
1753 }
1754 return(ret);
1755}
1756
1757/**
1758 * xmlOutputBufferCreateFilename:
1759 * @URI: a C string containing the URI or filename
1760 * @encoder: the encoding converter or NULL
1761 * @compression: the compression ration (0 none, 9 max).
1762 *
1763 * Create a buffered output for the progressive saving of a file
1764 * If filename is "-' then we use stdout as the output.
1765 * Automatic support for ZLIB/Compress compressed document is provided
1766 * by default if found at compile-time.
1767 * TODO: currently if compression is set, the library only support
1768 * writing to a local file.
1769 *
1770 * Returns the new output or NULL
1771 */
1772xmlOutputBufferPtr
1773xmlOutputBufferCreateFilename(const char *URI,
1774 xmlCharEncodingHandlerPtr encoder,
1775 int compression) {
1776 xmlOutputBufferPtr ret;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001777 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001778 void *context = NULL;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001779 char *unescaped;
Owen Taylor3473f882001-02-23 17:55:21 +00001780
Daniel Veillardf012a642001-07-23 19:10:52 +00001781 int is_http_uri = 0; /* Can't change if HTTP disabled */
1782
Owen Taylor3473f882001-02-23 17:55:21 +00001783 if (xmlOutputCallbackInitialized == 0)
1784 xmlRegisterDefaultOutputCallbacks();
1785
1786 if (URI == NULL) return(NULL);
1787
Daniel Veillardf012a642001-07-23 19:10:52 +00001788#ifdef LIBXML_HTTP_ENABLED
1789 /* Need to prevent HTTP URI's from falling into zlib short circuit */
1790
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001791 is_http_uri = xmlIOHTTPMatch( URI );
Daniel Veillardf012a642001-07-23 19:10:52 +00001792#endif
1793
Owen Taylor3473f882001-02-23 17:55:21 +00001794
1795 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001796 * Try to find one of the output accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001797 * Go in reverse to give precedence to user defined handlers.
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001798 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001799 */
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001800 unescaped = xmlURIUnescapeString(URI, 0, NULL);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001801 if (unescaped != NULL) {
1802#ifdef HAVE_ZLIB_H
1803 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
1804 context = xmlGzfileOpenW(unescaped, compression);
1805 if (context != NULL) {
1806 ret = xmlAllocOutputBuffer(encoder);
1807 if (ret != NULL) {
1808 ret->context = context;
1809 ret->writecallback = xmlGzfileWrite;
1810 ret->closecallback = xmlGzfileClose;
1811 }
1812 xmlFree(unescaped);
1813 return(ret);
1814 }
1815 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001816#endif
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001817 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1818 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
1819 (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) {
1820#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1821 /* Need to pass compression parameter into HTTP open calls */
1822 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1823 context = xmlIOHTTPOpenW(unescaped, compression);
1824 else
1825#endif
1826 context = xmlOutputCallbackTable[i].opencallback(unescaped);
1827 if (context != NULL)
1828 break;
1829 }
1830 }
1831 xmlFree(unescaped);
1832 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001833
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001834 /*
1835 * If this failed try with a non-escaped URI this may be a strange
1836 * filename
1837 */
1838 if (context == NULL) {
1839#ifdef HAVE_ZLIB_H
1840 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001841 context = xmlGzfileOpenW(URI, compression);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001842 if (context != NULL) {
1843 ret = xmlAllocOutputBuffer(encoder);
1844 if (ret != NULL) {
1845 ret->context = context;
1846 ret->writecallback = xmlGzfileWrite;
1847 ret->closecallback = xmlGzfileClose;
1848 }
1849 return(ret);
1850 }
1851 }
1852#endif
1853 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1854 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
Igor Zlatkovic5f9fada2003-02-19 14:51:00 +00001855 (xmlOutputCallbackTable[i].matchcallback(URI) != 0)) {
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001856#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1857 /* Need to pass compression parameter into HTTP open calls */
1858 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1859 context = xmlIOHTTPOpenW(URI, compression);
1860 else
1861#endif
1862 context = xmlOutputCallbackTable[i].opencallback(URI);
1863 if (context != NULL)
1864 break;
1865 }
Owen Taylor3473f882001-02-23 17:55:21 +00001866 }
1867 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001868
Owen Taylor3473f882001-02-23 17:55:21 +00001869 if (context == NULL) {
1870 return(NULL);
1871 }
1872
1873 /*
1874 * Allocate the Output buffer front-end.
1875 */
1876 ret = xmlAllocOutputBuffer(encoder);
1877 if (ret != NULL) {
1878 ret->context = context;
1879 ret->writecallback = xmlOutputCallbackTable[i].writecallback;
1880 ret->closecallback = xmlOutputCallbackTable[i].closecallback;
1881 }
1882 return(ret);
1883}
1884
1885/**
1886 * xmlParserInputBufferCreateFile:
1887 * @file: a FILE*
1888 * @enc: the charset encoding if known
1889 *
1890 * Create a buffered parser input for the progressive parsing of a FILE *
1891 * buffered C I/O
1892 *
1893 * Returns the new parser input or NULL
1894 */
1895xmlParserInputBufferPtr
1896xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) {
1897 xmlParserInputBufferPtr ret;
1898
1899 if (xmlInputCallbackInitialized == 0)
1900 xmlRegisterDefaultInputCallbacks();
1901
1902 if (file == NULL) return(NULL);
1903
1904 ret = xmlAllocParserInputBuffer(enc);
1905 if (ret != NULL) {
1906 ret->context = file;
1907 ret->readcallback = xmlFileRead;
1908 ret->closecallback = xmlFileFlush;
1909 }
1910
1911 return(ret);
1912}
1913
1914/**
1915 * xmlOutputBufferCreateFile:
1916 * @file: a FILE*
1917 * @encoder: the encoding converter or NULL
1918 *
1919 * Create a buffered output for the progressive saving to a FILE *
1920 * buffered C I/O
1921 *
1922 * Returns the new parser output or NULL
1923 */
1924xmlOutputBufferPtr
1925xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) {
1926 xmlOutputBufferPtr ret;
1927
1928 if (xmlOutputCallbackInitialized == 0)
1929 xmlRegisterDefaultOutputCallbacks();
1930
1931 if (file == NULL) return(NULL);
1932
1933 ret = xmlAllocOutputBuffer(encoder);
1934 if (ret != NULL) {
1935 ret->context = file;
1936 ret->writecallback = xmlFileWrite;
1937 ret->closecallback = xmlFileFlush;
1938 }
1939
1940 return(ret);
1941}
1942
1943/**
1944 * xmlParserInputBufferCreateFd:
1945 * @fd: a file descriptor number
1946 * @enc: the charset encoding if known
1947 *
1948 * Create a buffered parser input for the progressive parsing for the input
1949 * from a file descriptor
1950 *
1951 * Returns the new parser input or NULL
1952 */
1953xmlParserInputBufferPtr
1954xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) {
1955 xmlParserInputBufferPtr ret;
1956
1957 if (fd < 0) return(NULL);
1958
1959 ret = xmlAllocParserInputBuffer(enc);
1960 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00001961 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00001962 ret->readcallback = xmlFdRead;
1963 ret->closecallback = xmlFdClose;
1964 }
1965
1966 return(ret);
1967}
1968
1969/**
1970 * xmlParserInputBufferCreateMem:
1971 * @mem: the memory input
1972 * @size: the length of the memory block
1973 * @enc: the charset encoding if known
1974 *
1975 * Create a buffered parser input for the progressive parsing for the input
1976 * from a memory area.
1977 *
1978 * Returns the new parser input or NULL
1979 */
1980xmlParserInputBufferPtr
1981xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
1982 xmlParserInputBufferPtr ret;
1983
1984 if (size <= 0) return(NULL);
1985 if (mem == NULL) return(NULL);
1986
1987 ret = xmlAllocParserInputBuffer(enc);
1988 if (ret != NULL) {
1989 ret->context = (void *) mem;
1990 ret->readcallback = (xmlInputReadCallback) xmlNop;
1991 ret->closecallback = NULL;
1992 xmlBufferAdd(ret->buffer, (const xmlChar *) mem, size);
1993 }
1994
1995 return(ret);
1996}
1997
1998/**
1999 * xmlOutputBufferCreateFd:
2000 * @fd: a file descriptor number
2001 * @encoder: the encoding converter or NULL
2002 *
2003 * Create a buffered output for the progressive saving
2004 * to a file descriptor
2005 *
2006 * Returns the new parser output or NULL
2007 */
2008xmlOutputBufferPtr
2009xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) {
2010 xmlOutputBufferPtr ret;
2011
2012 if (fd < 0) return(NULL);
2013
2014 ret = xmlAllocOutputBuffer(encoder);
2015 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00002016 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00002017 ret->writecallback = xmlFdWrite;
Daniel Veillard7db38712002-02-07 16:39:11 +00002018 ret->closecallback = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00002019 }
2020
2021 return(ret);
2022}
2023
2024/**
2025 * xmlParserInputBufferCreateIO:
2026 * @ioread: an I/O read function
2027 * @ioclose: an I/O close function
2028 * @ioctx: an I/O handler
2029 * @enc: the charset encoding if known
2030 *
2031 * Create a buffered parser input for the progressive parsing for the input
2032 * from an I/O handler
2033 *
2034 * Returns the new parser input or NULL
2035 */
2036xmlParserInputBufferPtr
2037xmlParserInputBufferCreateIO(xmlInputReadCallback ioread,
2038 xmlInputCloseCallback ioclose, void *ioctx, xmlCharEncoding enc) {
2039 xmlParserInputBufferPtr ret;
2040
2041 if (ioread == NULL) return(NULL);
2042
2043 ret = xmlAllocParserInputBuffer(enc);
2044 if (ret != NULL) {
2045 ret->context = (void *) ioctx;
2046 ret->readcallback = ioread;
2047 ret->closecallback = ioclose;
2048 }
2049
2050 return(ret);
2051}
2052
2053/**
2054 * xmlOutputBufferCreateIO:
2055 * @iowrite: an I/O write function
2056 * @ioclose: an I/O close function
2057 * @ioctx: an I/O handler
Daniel Veillard9d06d302002-01-22 18:15:52 +00002058 * @encoder: the charset encoding if known
Owen Taylor3473f882001-02-23 17:55:21 +00002059 *
2060 * Create a buffered output for the progressive saving
2061 * to an I/O handler
2062 *
2063 * Returns the new parser output or NULL
2064 */
2065xmlOutputBufferPtr
2066xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite,
2067 xmlOutputCloseCallback ioclose, void *ioctx,
2068 xmlCharEncodingHandlerPtr encoder) {
2069 xmlOutputBufferPtr ret;
2070
2071 if (iowrite == NULL) return(NULL);
2072
2073 ret = xmlAllocOutputBuffer(encoder);
2074 if (ret != NULL) {
2075 ret->context = (void *) ioctx;
2076 ret->writecallback = iowrite;
2077 ret->closecallback = ioclose;
2078 }
2079
2080 return(ret);
2081}
2082
2083/**
2084 * xmlParserInputBufferPush:
2085 * @in: a buffered parser input
2086 * @len: the size in bytes of the array.
2087 * @buf: an char array
2088 *
2089 * Push the content of the arry in the input buffer
2090 * This routine handle the I18N transcoding to internal UTF-8
2091 * This is used when operating the parser in progressive (push) mode.
2092 *
2093 * Returns the number of chars read and stored in the buffer, or -1
2094 * in case of error.
2095 */
2096int
2097xmlParserInputBufferPush(xmlParserInputBufferPtr in,
2098 int len, const char *buf) {
2099 int nbchars = 0;
2100
2101 if (len < 0) return(0);
2102 if (in->encoder != NULL) {
2103 /*
2104 * Store the data in the incoming raw buffer
2105 */
2106 if (in->raw == NULL) {
2107 in->raw = xmlBufferCreate();
2108 }
2109 xmlBufferAdd(in->raw, (const xmlChar *) buf, len);
2110
2111 /*
2112 * convert as much as possible to the parser reading buffer.
2113 */
2114 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2115 if (nbchars < 0) {
2116 xmlGenericError(xmlGenericErrorContext,
2117 "xmlParserInputBufferPush: encoder error\n");
2118 return(-1);
2119 }
2120 } else {
2121 nbchars = len;
2122 xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
2123 }
2124#ifdef DEBUG_INPUT
2125 xmlGenericError(xmlGenericErrorContext,
2126 "I/O: pushed %d chars, buffer %d/%d\n",
2127 nbchars, in->buffer->use, in->buffer->size);
2128#endif
2129 return(nbchars);
2130}
2131
2132/**
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002133 * endOfInput:
2134 *
2135 * When reading from an Input channel indicated end of file or error
2136 * don't reread from it again.
2137 */
2138static int
2139endOfInput (void * context ATTRIBUTE_UNUSED,
2140 char * buffer ATTRIBUTE_UNUSED,
2141 int len ATTRIBUTE_UNUSED) {
2142 return(0);
2143}
2144
2145/**
Owen Taylor3473f882001-02-23 17:55:21 +00002146 * xmlParserInputBufferGrow:
2147 * @in: a buffered parser input
2148 * @len: indicative value of the amount of chars to read
2149 *
2150 * Grow up the content of the input buffer, the old data are preserved
2151 * This routine handle the I18N transcoding to internal UTF-8
2152 * This routine is used when operating the parser in normal (pull) mode
2153 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002154 * TODO: one should be able to remove one extra copy by copying directly
Owen Taylor3473f882001-02-23 17:55:21 +00002155 * onto in->buffer or in->raw
2156 *
2157 * Returns the number of chars read and stored in the buffer, or -1
2158 * in case of error.
2159 */
2160int
2161xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) {
2162 char *buffer = NULL;
2163 int res = 0;
2164 int nbchars = 0;
2165 int buffree;
Daniel Veillard9e412302002-06-10 15:59:44 +00002166 unsigned int needSize;
Owen Taylor3473f882001-02-23 17:55:21 +00002167
2168 if ((len <= MINLEN) && (len != 4))
2169 len = MINLEN;
2170 buffree = in->buffer->size - in->buffer->use;
2171 if (buffree <= 0) {
2172 xmlGenericError(xmlGenericErrorContext,
2173 "xmlParserInputBufferGrow : buffer full !\n");
2174 return(0);
2175 }
2176 if (len > buffree)
2177 len = buffree;
2178
Daniel Veillarde5354492002-05-16 08:43:22 +00002179 needSize = in->buffer->use + len + 1;
2180 if (needSize > in->buffer->size){
2181 if (!xmlBufferResize(in->buffer, needSize)){
2182 xmlGenericError(xmlGenericErrorContext,
2183 "xmlBufferAdd : out of memory!\n");
2184 return(0);
2185 }
Owen Taylor3473f882001-02-23 17:55:21 +00002186 }
Daniel Veillarde5354492002-05-16 08:43:22 +00002187 buffer = (char *)&in->buffer->content[in->buffer->use];
Owen Taylor3473f882001-02-23 17:55:21 +00002188
2189 /*
2190 * Call the read method for this I/O type.
2191 */
2192 if (in->readcallback != NULL) {
2193 res = in->readcallback(in->context, &buffer[0], len);
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002194 if (res <= 0)
2195 in->readcallback = endOfInput;
Owen Taylor3473f882001-02-23 17:55:21 +00002196 } else {
2197 xmlGenericError(xmlGenericErrorContext,
2198 "xmlParserInputBufferGrow : no input !\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002199 return(-1);
2200 }
2201 if (res < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00002202 return(-1);
2203 }
2204 len = res;
2205 if (in->encoder != NULL) {
2206 /*
2207 * Store the data in the incoming raw buffer
2208 */
2209 if (in->raw == NULL) {
2210 in->raw = xmlBufferCreate();
2211 }
2212 xmlBufferAdd(in->raw, (const xmlChar *) buffer, len);
2213
2214 /*
2215 * convert as much as possible to the parser reading buffer.
2216 */
2217 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2218 if (nbchars < 0) {
2219 xmlGenericError(xmlGenericErrorContext,
2220 "xmlParserInputBufferGrow: encoder error\n");
2221 return(-1);
2222 }
2223 } else {
2224 nbchars = len;
Daniel Veillarde5354492002-05-16 08:43:22 +00002225 in->buffer->use += nbchars;
2226 buffer[nbchars] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002227 }
2228#ifdef DEBUG_INPUT
2229 xmlGenericError(xmlGenericErrorContext,
2230 "I/O: read %d chars, buffer %d/%d\n",
2231 nbchars, in->buffer->use, in->buffer->size);
2232#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002233 return(nbchars);
2234}
2235
2236/**
2237 * xmlParserInputBufferRead:
2238 * @in: a buffered parser input
2239 * @len: indicative value of the amount of chars to read
2240 *
2241 * Refresh the content of the input buffer, the old data are considered
2242 * consumed
2243 * This routine handle the I18N transcoding to internal UTF-8
2244 *
2245 * Returns the number of chars read and stored in the buffer, or -1
2246 * in case of error.
2247 */
2248int
2249xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) {
2250 /* xmlBufferEmpty(in->buffer); */
2251 if (in->readcallback != NULL)
2252 return(xmlParserInputBufferGrow(in, len));
2253 else
2254 return(-1);
2255}
2256
2257/**
2258 * xmlOutputBufferWrite:
2259 * @out: a buffered parser output
2260 * @len: the size in bytes of the array.
2261 * @buf: an char array
2262 *
2263 * Write the content of the array in the output I/O buffer
2264 * This routine handle the I18N transcoding from internal UTF-8
2265 * The buffer is lossless, i.e. will store in case of partial
2266 * or delayed writes.
2267 *
2268 * Returns the number of chars immediately written, or -1
2269 * in case of error.
2270 */
2271int
2272xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) {
2273 int nbchars = 0; /* number of chars to output to I/O */
2274 int ret; /* return from function call */
2275 int written = 0; /* number of char written to I/O so far */
2276 int chunk; /* number of byte curreent processed from buf */
2277
2278 if (len < 0) return(0);
2279
2280 do {
2281 chunk = len;
2282 if (chunk > 4 * MINLEN)
2283 chunk = 4 * MINLEN;
2284
2285 /*
2286 * first handle encoding stuff.
2287 */
2288 if (out->encoder != NULL) {
2289 /*
2290 * Store the data in the incoming raw buffer
2291 */
2292 if (out->conv == NULL) {
2293 out->conv = xmlBufferCreate();
2294 }
2295 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2296
2297 if ((out->buffer->use < MINLEN) && (chunk == len))
2298 goto done;
2299
2300 /*
2301 * convert as much as possible to the parser reading buffer.
2302 */
2303 ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
Daniel Veillard809faa52003-02-10 15:43:53 +00002304 if ((ret < 0) && (ret != -3)) {
Owen Taylor3473f882001-02-23 17:55:21 +00002305 xmlGenericError(xmlGenericErrorContext,
2306 "xmlOutputBufferWrite: encoder error\n");
2307 return(-1);
2308 }
2309 nbchars = out->conv->use;
2310 } else {
2311 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2312 nbchars = out->buffer->use;
2313 }
2314 buf += chunk;
2315 len -= chunk;
2316
2317 if ((nbchars < MINLEN) && (len <= 0))
2318 goto done;
2319
2320 if (out->writecallback) {
2321 /*
2322 * second write the stuff to the I/O channel
2323 */
2324 if (out->encoder != NULL) {
2325 ret = out->writecallback(out->context,
2326 (const char *)out->conv->content, nbchars);
2327 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002328 xmlBufferShrink(out->conv, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002329 } else {
2330 ret = out->writecallback(out->context,
2331 (const char *)out->buffer->content, nbchars);
2332 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002333 xmlBufferShrink(out->buffer, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002334 }
2335 if (ret < 0) {
2336 xmlGenericError(xmlGenericErrorContext,
2337 "I/O: error %d writing %d bytes\n", ret, nbchars);
2338 return(ret);
2339 }
2340 out->written += ret;
2341 }
2342 written += nbchars;
2343 } while (len > 0);
2344
2345done:
2346#ifdef DEBUG_INPUT
2347 xmlGenericError(xmlGenericErrorContext,
2348 "I/O: wrote %d chars\n", written);
2349#endif
2350 return(written);
2351}
2352
2353/**
2354 * xmlOutputBufferWriteString:
2355 * @out: a buffered parser output
2356 * @str: a zero terminated C string
2357 *
2358 * Write the content of the string in the output I/O buffer
2359 * This routine handle the I18N transcoding from internal UTF-8
2360 * The buffer is lossless, i.e. will store in case of partial
2361 * or delayed writes.
2362 *
2363 * Returns the number of chars immediately written, or -1
2364 * in case of error.
2365 */
2366int
2367xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) {
2368 int len;
2369
2370 if (str == NULL)
2371 return(-1);
2372 len = strlen(str);
2373
2374 if (len > 0)
2375 return(xmlOutputBufferWrite(out, len, str));
2376 return(len);
2377}
2378
2379/**
2380 * xmlOutputBufferFlush:
2381 * @out: a buffered output
2382 *
2383 * flushes the output I/O channel
2384 *
2385 * Returns the number of byte written or -1 in case of error.
2386 */
2387int
2388xmlOutputBufferFlush(xmlOutputBufferPtr out) {
2389 int nbchars = 0, ret = 0;
2390
2391 /*
2392 * first handle encoding stuff.
2393 */
2394 if ((out->conv != NULL) && (out->encoder != NULL)) {
2395 /*
2396 * convert as much as possible to the parser reading buffer.
2397 */
2398 nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2399 if (nbchars < 0) {
2400 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002401 "xmlOutputBufferFlush: encoder error\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002402 return(-1);
2403 }
2404 }
2405
2406 /*
2407 * second flush the stuff to the I/O channel
2408 */
2409 if ((out->conv != NULL) && (out->encoder != NULL) &&
2410 (out->writecallback != NULL)) {
2411 ret = out->writecallback(out->context,
2412 (const char *)out->conv->content, out->conv->use);
2413 if (ret >= 0)
2414 xmlBufferShrink(out->conv, ret);
2415 } else if (out->writecallback != NULL) {
2416 ret = out->writecallback(out->context,
2417 (const char *)out->buffer->content, out->buffer->use);
2418 if (ret >= 0)
2419 xmlBufferShrink(out->buffer, ret);
2420 }
2421 if (ret < 0) {
2422 xmlGenericError(xmlGenericErrorContext,
2423 "I/O: error %d flushing %d bytes\n", ret, nbchars);
2424 return(ret);
2425 }
2426 out->written += ret;
2427
2428#ifdef DEBUG_INPUT
2429 xmlGenericError(xmlGenericErrorContext,
2430 "I/O: flushed %d chars\n", ret);
2431#endif
2432 return(ret);
2433}
2434
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002435/**
Owen Taylor3473f882001-02-23 17:55:21 +00002436 * xmlParserGetDirectory:
2437 * @filename: the path to a file
2438 *
2439 * lookup the directory for that file
2440 *
2441 * Returns a new allocated string containing the directory, or NULL.
2442 */
2443char *
2444xmlParserGetDirectory(const char *filename) {
2445 char *ret = NULL;
2446 char dir[1024];
2447 char *cur;
2448 char sep = '/';
2449
Igor Zlatkovic9181cc02002-09-29 17:51:06 +00002450#ifdef _WIN32_WCE /* easy way by now ... wince does not have dirs! */
2451 return NULL;
2452#endif
2453
Owen Taylor3473f882001-02-23 17:55:21 +00002454 if (xmlInputCallbackInitialized == 0)
2455 xmlRegisterDefaultInputCallbacks();
2456
2457 if (filename == NULL) return(NULL);
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002458#if defined(WIN32) && !defined(__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +00002459 sep = '\\';
2460#endif
2461
2462 strncpy(dir, filename, 1023);
2463 dir[1023] = 0;
2464 cur = &dir[strlen(dir)];
2465 while (cur > dir) {
2466 if (*cur == sep) break;
2467 cur --;
2468 }
2469 if (*cur == sep) {
2470 if (cur == dir) dir[1] = 0;
2471 else *cur = 0;
2472 ret = xmlMemStrdup(dir);
2473 } else {
2474 if (getcwd(dir, 1024) != NULL) {
2475 dir[1023] = 0;
2476 ret = xmlMemStrdup(dir);
2477 }
2478 }
2479 return(ret);
2480}
2481
2482/****************************************************************
2483 * *
2484 * External entities loading *
2485 * *
2486 ****************************************************************/
2487
Daniel Veillard561b7f82002-03-20 21:55:57 +00002488#ifdef LIBXML_CATALOG_ENABLED
2489static int xmlSysIDExists(const char *URL) {
Daniel Veillard6990bf32001-08-23 21:17:48 +00002490#ifdef HAVE_STAT
2491 int ret;
2492 struct stat info;
2493 const char *path;
2494
2495 if (URL == NULL)
2496 return(0);
2497
Daniel Veillardf4862f02002-09-10 11:13:43 +00002498 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2499#if defined (_WIN32) && !defined(__CYGWIN__)
2500 path = &URL[17];
2501#else
Daniel Veillard6990bf32001-08-23 21:17:48 +00002502 path = &URL[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +00002503#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002504 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002505#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard6990bf32001-08-23 21:17:48 +00002506 path = &URL[8];
2507#else
2508 path = &URL[7];
2509#endif
2510 } else
2511 path = URL;
2512 ret = stat(path, &info);
Daniel Veillard561b7f82002-03-20 21:55:57 +00002513 if (ret == 0)
2514 return(1);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002515#endif
Daniel Veillard561b7f82002-03-20 21:55:57 +00002516 return(0);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002517}
Daniel Veillard561b7f82002-03-20 21:55:57 +00002518#endif
Daniel Veillard6990bf32001-08-23 21:17:48 +00002519
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002520/**
Owen Taylor3473f882001-02-23 17:55:21 +00002521 * xmlDefaultExternalEntityLoader:
2522 * @URL: the URL for the entity to load
2523 * @ID: the System ID for the entity to load
2524 * @ctxt: the context in which the entity is called or NULL
2525 *
2526 * By default we don't load external entitites, yet.
2527 *
2528 * Returns a new allocated xmlParserInputPtr, or NULL.
2529 */
2530static
2531xmlParserInputPtr
2532xmlDefaultExternalEntityLoader(const char *URL, const char *ID,
2533 xmlParserCtxtPtr ctxt) {
2534 xmlParserInputPtr ret = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002535 xmlChar *resource = NULL;
2536#ifdef LIBXML_CATALOG_ENABLED
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002537 xmlCatalogAllow pref;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002538#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002539
2540#ifdef DEBUG_EXTERNAL_ENTITIES
2541 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00002542 "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
Owen Taylor3473f882001-02-23 17:55:21 +00002543#endif
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002544#ifdef LIBXML_CATALOG_ENABLED
2545 /*
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002546 * If the resource doesn't exists as a file,
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002547 * try to load it from the resource pointed in the catalogs
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002548 */
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002549 pref = xmlCatalogGetDefaults();
2550
Daniel Veillard561b7f82002-03-20 21:55:57 +00002551 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlSysIDExists(URL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002552 /*
2553 * Do a local lookup
2554 */
2555 if ((ctxt->catalogs != NULL) &&
2556 ((pref == XML_CATA_ALLOW_ALL) ||
2557 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2558 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2559 (const xmlChar *)ID,
2560 (const xmlChar *)URL);
2561 }
2562 /*
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002563 * Try a global lookup
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002564 */
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002565 if ((resource == NULL) &&
2566 ((pref == XML_CATA_ALLOW_ALL) ||
2567 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002568 resource = xmlCatalogResolve((const xmlChar *)ID,
2569 (const xmlChar *)URL);
2570 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002571 if ((resource == NULL) && (URL != NULL))
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002572 resource = xmlStrdup((const xmlChar *) URL);
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002573
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002574 /*
2575 * TODO: do an URI lookup on the reference
2576 */
Daniel Veillard561b7f82002-03-20 21:55:57 +00002577 if ((resource != NULL) && (!xmlSysIDExists((const char *)resource))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002578 xmlChar *tmp = NULL;
2579
2580 if ((ctxt->catalogs != NULL) &&
2581 ((pref == XML_CATA_ALLOW_ALL) ||
2582 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2583 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2584 }
2585 if ((tmp == NULL) &&
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002586 ((pref == XML_CATA_ALLOW_ALL) ||
2587 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002588 tmp = xmlCatalogResolveURI(resource);
2589 }
2590
2591 if (tmp != NULL) {
2592 xmlFree(resource);
2593 resource = tmp;
2594 }
2595 }
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002596 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002597#endif
2598
2599 if (resource == NULL)
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002600 resource = (xmlChar *) URL;
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002601
2602 if (resource == NULL) {
Daniel Veillardc6613042002-03-02 09:34:02 +00002603 if (ID == NULL)
2604 ID = "NULL";
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002605 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2606 (ctxt->sax->error != NULL))
2607 ctxt->sax->error(ctxt,
2608 "failed to load external entity \"%s\"\n", ID);
2609 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002610 ctxt->sax->warning(ctxt,
2611 "failed to load external entity \"%s\"\n", ID);
2612 return(NULL);
2613 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002614 ret = xmlNewInputFromFile(ctxt, (const char *)resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002615 if (ret == NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002616 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2617 (ctxt->sax->error != NULL))
2618 ctxt->sax->error(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002619 "failed to load external entity \"%s\"\n", resource);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002620 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002621 ctxt->sax->warning(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002622 "failed to load external entity \"%s\"\n", resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002623 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002624 if ((resource != NULL) && (resource != (xmlChar *) URL))
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002625 xmlFree(resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002626 return(ret);
2627}
2628
2629static xmlExternalEntityLoader xmlCurrentExternalEntityLoader =
2630 xmlDefaultExternalEntityLoader;
2631
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002632/**
Owen Taylor3473f882001-02-23 17:55:21 +00002633 * xmlSetExternalEntityLoader:
2634 * @f: the new entity resolver function
2635 *
2636 * Changes the defaultexternal entity resolver function for the application
2637 */
2638void
2639xmlSetExternalEntityLoader(xmlExternalEntityLoader f) {
2640 xmlCurrentExternalEntityLoader = f;
2641}
2642
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002643/**
Owen Taylor3473f882001-02-23 17:55:21 +00002644 * xmlGetExternalEntityLoader:
2645 *
2646 * Get the default external entity resolver function for the application
2647 *
2648 * Returns the xmlExternalEntityLoader function pointer
2649 */
2650xmlExternalEntityLoader
2651xmlGetExternalEntityLoader(void) {
2652 return(xmlCurrentExternalEntityLoader);
2653}
2654
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002655/**
Owen Taylor3473f882001-02-23 17:55:21 +00002656 * xmlLoadExternalEntity:
2657 * @URL: the URL for the entity to load
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002658 * @ID: the Public ID for the entity to load
Owen Taylor3473f882001-02-23 17:55:21 +00002659 * @ctxt: the context in which the entity is called or NULL
2660 *
2661 * Load an external entity, note that the use of this function for
2662 * unparsed entities may generate problems
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002663 * TODO: a more generic External entity API must be designed
Owen Taylor3473f882001-02-23 17:55:21 +00002664 *
2665 * Returns the xmlParserInputPtr or NULL
2666 */
2667xmlParserInputPtr
2668xmlLoadExternalEntity(const char *URL, const char *ID,
2669 xmlParserCtxtPtr ctxt) {
2670 return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
2671}
2672
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002673/************************************************************************
2674 * *
2675 * Disabling Network access *
2676 * *
2677 ************************************************************************/
2678
2679#ifdef LIBXML_CATALOG_ENABLED
2680static int
2681xmlNoNetExists(const char *URL)
2682{
2683#ifdef HAVE_STAT
2684 int ret;
2685 struct stat info;
2686 const char *path;
2687
2688 if (URL == NULL)
2689 return (0);
2690
Daniel Veillardf4862f02002-09-10 11:13:43 +00002691 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2692#if defined (_WIN32) && !defined(__CYGWIN__)
2693 path = &URL[17];
2694#else
2695 path = &URL[16];
2696#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002697 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002698#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002699 path = &URL[8];
2700#else
2701 path = &URL[7];
2702#endif
2703 } else
2704 path = URL;
2705 ret = stat(path, &info);
2706 if (ret == 0)
2707 return (1);
2708#endif
2709 return (0);
2710}
2711#endif
2712
2713/**
2714 * xmlNoNetExternalEntityLoader:
2715 * @URL: the URL for the entity to load
2716 * @ID: the System ID for the entity to load
2717 * @ctxt: the context in which the entity is called or NULL
2718 *
2719 * A specific entity loader disabling network accesses, though still
2720 * allowing local catalog accesses for resolution.
2721 *
2722 * Returns a new allocated xmlParserInputPtr, or NULL.
2723 */
2724xmlParserInputPtr
2725xmlNoNetExternalEntityLoader(const char *URL, const char *ID,
2726 xmlParserCtxtPtr ctxt) {
2727 xmlParserInputPtr input = NULL;
2728 xmlChar *resource = NULL;
2729
2730#ifdef LIBXML_CATALOG_ENABLED
2731 xmlCatalogAllow pref;
2732
2733 /*
2734 * If the resource doesn't exists as a file,
2735 * try to load it from the resource pointed in the catalogs
2736 */
2737 pref = xmlCatalogGetDefaults();
2738
2739 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) {
2740 /*
2741 * Do a local lookup
2742 */
2743 if ((ctxt->catalogs != NULL) &&
2744 ((pref == XML_CATA_ALLOW_ALL) ||
2745 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2746 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2747 (const xmlChar *)ID,
2748 (const xmlChar *)URL);
2749 }
2750 /*
2751 * Try a global lookup
2752 */
2753 if ((resource == NULL) &&
2754 ((pref == XML_CATA_ALLOW_ALL) ||
2755 (pref == XML_CATA_ALLOW_GLOBAL))) {
2756 resource = xmlCatalogResolve((const xmlChar *)ID,
2757 (const xmlChar *)URL);
2758 }
2759 if ((resource == NULL) && (URL != NULL))
2760 resource = xmlStrdup((const xmlChar *) URL);
2761
2762 /*
2763 * TODO: do an URI lookup on the reference
2764 */
2765 if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) {
2766 xmlChar *tmp = NULL;
2767
2768 if ((ctxt->catalogs != NULL) &&
2769 ((pref == XML_CATA_ALLOW_ALL) ||
2770 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2771 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2772 }
2773 if ((tmp == NULL) &&
2774 ((pref == XML_CATA_ALLOW_ALL) ||
2775 (pref == XML_CATA_ALLOW_GLOBAL))) {
2776 tmp = xmlCatalogResolveURI(resource);
2777 }
2778
2779 if (tmp != NULL) {
2780 xmlFree(resource);
2781 resource = tmp;
2782 }
2783 }
2784 }
2785#endif
2786 if (resource == NULL)
2787 resource = (xmlChar *) URL;
2788
2789 if (resource != NULL) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002790 if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) ||
2791 (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) {
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002792 xmlGenericError(xmlGenericErrorContext,
2793 "Attempt to load network entity %s \n", resource);
2794
2795 if (resource != (xmlChar *) URL)
2796 xmlFree(resource);
2797 return(NULL);
2798 }
2799 }
2800 input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt);
2801 if (resource != (xmlChar *) URL)
2802 xmlFree(resource);
2803 return(input);
2804}
2805