blob: 9e84a3c9a0a0c09838e6d1b16ba0064a2fe4ecae [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
82#ifdef VMS
83#define xmlRegisterDefaultInputCallbacks xmlRegisterDefInputCallbacks
84#define xmlRegisterDefaultOutputCallbacks xmlRegisterDefOutputCallbacks
85#endif
86
Daniel Veillardf012a642001-07-23 19:10:52 +000087/* #define VERBOSE_FAILURE */
Daniel Veillard1fd36d22001-07-04 22:54:28 +000088/* #define DEBUG_EXTERNAL_ENTITIES */
Owen Taylor3473f882001-02-23 17:55:21 +000089/* #define DEBUG_INPUT */
90
91#ifdef DEBUG_INPUT
92#define MINLEN 40
93#else
94#define MINLEN 4000
95#endif
96
97/*
98 * Input I/O callback sets
99 */
100typedef struct _xmlInputCallback {
101 xmlInputMatchCallback matchcallback;
102 xmlInputOpenCallback opencallback;
103 xmlInputReadCallback readcallback;
104 xmlInputCloseCallback closecallback;
105} xmlInputCallback;
106
107#define MAX_INPUT_CALLBACK 15
108
Daniel Veillard22090732001-07-16 00:06:07 +0000109static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK];
110static int xmlInputCallbackNr = 0;
111static int xmlInputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000112
113/*
114 * Output I/O callback sets
115 */
116typedef struct _xmlOutputCallback {
117 xmlOutputMatchCallback matchcallback;
118 xmlOutputOpenCallback opencallback;
119 xmlOutputWriteCallback writecallback;
120 xmlOutputCloseCallback closecallback;
121} xmlOutputCallback;
122
123#define MAX_OUTPUT_CALLBACK 15
124
Daniel Veillard22090732001-07-16 00:06:07 +0000125static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK];
126static int xmlOutputCallbackNr = 0;
127static int xmlOutputCallbackInitialized = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000128
Daniel Veillardf4862f02002-09-10 11:13:43 +0000129/************************************************************************
130 * *
131 * Handling of Windows file paths *
132 * *
133 ************************************************************************/
134
135#define IS_WINDOWS_PATH(p) \
136 ((p != NULL) && \
137 (((p[0] >= 'a') && (p[0] <= 'z')) || \
138 ((p[0] >= 'A') && (p[0] <= 'Z'))) && \
139 (p[1] == ':') && ((p[2] == '/') || (p[2] == '\\')))
140
141
142/**
143 * xmlNormalizeWindowsPath
144 * @path: a windows path like "C:/foo/bar"
145 *
146 * Normalize a Windows path to make an URL from it
147 *
148 * Returns a new URI which must be freed by the caller or NULL
149 * in case of error
150 */
151xmlChar *
152xmlNormalizeWindowsPath(const xmlChar *path)
153{
154 int len, i, j;
155 xmlChar *ret;
156
157 if (path == NULL)
158 return(NULL);
159 if (!IS_WINDOWS_PATH(path))
160 return(xmlStrdup(path));
161
162 len = xmlStrlen(path);
Daniel Veillard607b35c2002-09-10 12:16:19 +0000163 ret = xmlMalloc(len + 10);
Daniel Veillardf4862f02002-09-10 11:13:43 +0000164 if (ret == NULL)
165 return(NULL);
166
167 ret[0] = 'f';
168 ret[1] = 'i';
169 ret[2] = 'l';
170 ret[3] = 'e';
171 ret[4] = ':';
172 ret[5] = '/';
173 ret[6] = '/';
174 ret[7] = '/';
175 for (i = 0,j = 8;i < len;i++,j++) {
176 /* TODO: UTF8 conversion + URI escaping ??? */
177 if (path[i] == '\\')
178 ret[j] = '/';
179 else
180 ret[j] = path[i];
181 }
182 ret[j] = 0;
183 return(ret);
184}
185
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000186/**
187 * xmlCleanupInputCallbacks:
188 *
189 * clears the entire input callback table. this includes the
190 * compiled-in I/O.
191 */
192void
193xmlCleanupInputCallbacks(void)
194{
195 int i;
196
197 if (!xmlInputCallbackInitialized)
198 return;
199
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000200 for (i = xmlInputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000201 xmlInputCallbackTable[i].matchcallback = NULL;
202 xmlInputCallbackTable[i].opencallback = NULL;
203 xmlInputCallbackTable[i].readcallback = NULL;
204 xmlInputCallbackTable[i].closecallback = NULL;
205 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000206 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000207
208 xmlInputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000209 xmlInputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000210}
211
212/**
213 * xmlCleanupOutputCallbacks:
214 *
215 * clears the entire output callback table. this includes the
216 * compiled-in I/O callbacks.
217 */
218void
219xmlCleanupOutputCallbacks(void)
220{
221 int i;
222
223 if (!xmlOutputCallbackInitialized)
224 return;
225
Daniel Veillard107ccaa2001-11-27 16:23:50 +0000226 for (i = xmlOutputCallbackNr - 1; i >= 0; i--) {
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000227 xmlOutputCallbackTable[i].matchcallback = NULL;
228 xmlOutputCallbackTable[i].opencallback = NULL;
229 xmlOutputCallbackTable[i].writecallback = NULL;
230 xmlOutputCallbackTable[i].closecallback = NULL;
231 }
Daniel Veillard9e412302002-06-10 15:59:44 +0000232 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000233
234 xmlOutputCallbackNr = 0;
Aleksey Sanin9c45ba82002-06-06 21:46:13 +0000235 xmlOutputCallbackInitialized = 0;
Daniel Veillardacf7ff02001-10-29 20:21:47 +0000236}
237
Owen Taylor3473f882001-02-23 17:55:21 +0000238/************************************************************************
239 * *
240 * Standard I/O for file accesses *
241 * *
242 ************************************************************************/
243
244/**
245 * xmlCheckFilename
246 * @path: the path to check
247 *
248 * function checks to see if @path is a valid source
249 * (file, socket...) for XML.
250 *
251 * if stat is not available on the target machine,
252 * returns 1. if stat fails, returns 0 (if calling
253 * stat on the filename fails, it can't be right).
254 * if stat succeeds and the file is a directory,
255 * sets errno to EISDIR and returns 0. otherwise
256 * returns 1.
257 */
258
259static int
260xmlCheckFilename (const char *path)
261{
262#ifdef HAVE_STAT
263#ifdef S_ISDIR
264 struct stat stat_buffer;
265
266 if (stat(path, &stat_buffer) == -1)
267 return 0;
268
269 if (S_ISDIR(stat_buffer.st_mode)) {
270 errno = EISDIR;
271 return 0;
272 }
273
274#endif
275#endif
276 return 1;
277}
278
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000279static int
Owen Taylor3473f882001-02-23 17:55:21 +0000280xmlNop(void) {
281 return(0);
282}
283
284/**
Owen Taylor3473f882001-02-23 17:55:21 +0000285 * xmlFdRead:
286 * @context: the I/O context
287 * @buffer: where to drop data
288 * @len: number of bytes to read
289 *
290 * Read @len bytes to @buffer from the I/O channel.
291 *
292 * Returns the number of bytes written
293 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000294static int
Owen Taylor3473f882001-02-23 17:55:21 +0000295xmlFdRead (void * context, char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000296 return(read((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000297}
298
299/**
300 * xmlFdWrite:
301 * @context: the I/O context
302 * @buffer: where to get data
303 * @len: number of bytes to write
304 *
305 * Write @len bytes from @buffer to the I/O channel.
306 *
307 * Returns the number of bytes written
308 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000309static int
Owen Taylor3473f882001-02-23 17:55:21 +0000310xmlFdWrite (void * context, const char * buffer, int len) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +0000311 return(write((int) (long) context, &buffer[0], len));
Owen Taylor3473f882001-02-23 17:55:21 +0000312}
313
314/**
315 * xmlFdClose:
316 * @context: the I/O context
317 *
318 * Close an I/O channel
Daniel Veillardf012a642001-07-23 19:10:52 +0000319 *
320 * Returns 0 in case of success and error code otherwise
Owen Taylor3473f882001-02-23 17:55:21 +0000321 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000322static int
Owen Taylor3473f882001-02-23 17:55:21 +0000323xmlFdClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000324 return ( close((int) (long) context) );
Owen Taylor3473f882001-02-23 17:55:21 +0000325}
326
327/**
328 * xmlFileMatch:
329 * @filename: the URI for matching
330 *
331 * input from FILE *
332 *
333 * Returns 1 if matches, 0 otherwise
334 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000335int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000336xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000337 return(1);
338}
339
340/**
341 * xmlFileOpen:
342 * @filename: the URI for matching
343 *
344 * input from FILE *, supports compressed input
345 * if @filename is " " then the standard input is used
346 *
347 * Returns an I/O context or NULL in case of error
348 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000349void *
Owen Taylor3473f882001-02-23 17:55:21 +0000350xmlFileOpen (const char *filename) {
351 const char *path = NULL;
352 FILE *fd;
353
354 if (!strcmp(filename, "-")) {
355 fd = stdin;
356 return((void *) fd);
357 }
358
Daniel Veillardf4862f02002-09-10 11:13:43 +0000359 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000360#if defined (_WIN32) && !defined(__CYGWIN__)
361 path = &filename[17];
362#else
Owen Taylor3473f882001-02-23 17:55:21 +0000363 path = &filename[16];
Daniel Veillardb212bbb2002-08-25 14:39:16 +0000364#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000365 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000366#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000367 path = &filename[8];
368#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000369 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000370#endif
371 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000372 path = filename;
373
374 if (path == NULL)
375 return(NULL);
376 if (!xmlCheckFilename(path))
377 return(NULL);
378
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000379#if defined(WIN32) || defined (__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +0000380 fd = fopen(path, "rb");
381#else
382 fd = fopen(path, "r");
383#endif /* WIN32 */
384 return((void *) fd);
385}
386
387/**
388 * xmlFileOpenW:
389 * @filename: the URI for matching
390 *
391 * output to from FILE *,
392 * if @filename is "-" then the standard output is used
393 *
394 * Returns an I/O context or NULL in case of error
395 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000396static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000397xmlFileOpenW (const char *filename) {
398 const char *path = NULL;
399 FILE *fd;
400
401 if (!strcmp(filename, "-")) {
402 fd = stdout;
403 return((void *) fd);
404 }
405
Daniel Veillardf4862f02002-09-10 11:13:43 +0000406 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
407#if defined (_WIN32) && !defined(__CYGWIN__)
408 path = &filename[17];
409#else
Owen Taylor3473f882001-02-23 17:55:21 +0000410 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000411#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000412 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000413#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000414 path = &filename[8];
415#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000416 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000417#endif
418 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000419 path = filename;
420
421 if (path == NULL)
422 return(NULL);
423
424 fd = fopen(path, "w");
425 return((void *) fd);
426}
427
428/**
429 * xmlFileRead:
430 * @context: the I/O context
431 * @buffer: where to drop data
432 * @len: number of bytes to write
433 *
434 * Read @len bytes to @buffer from the I/O channel.
435 *
436 * Returns the number of bytes written
437 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000438int
Owen Taylor3473f882001-02-23 17:55:21 +0000439xmlFileRead (void * context, char * buffer, int len) {
440 return(fread(&buffer[0], 1, len, (FILE *) context));
441}
442
443/**
444 * xmlFileWrite:
445 * @context: the I/O context
446 * @buffer: where to drop data
447 * @len: number of bytes to write
448 *
449 * Write @len bytes from @buffer to the I/O channel.
450 *
451 * Returns the number of bytes written
452 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000453static int
Owen Taylor3473f882001-02-23 17:55:21 +0000454xmlFileWrite (void * context, const char * buffer, int len) {
455 return(fwrite(&buffer[0], 1, len, (FILE *) context));
456}
457
458/**
459 * xmlFileClose:
460 * @context: the I/O context
461 *
462 * Close an I/O channel
463 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000464int
Owen Taylor3473f882001-02-23 17:55:21 +0000465xmlFileClose (void * context) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000466 FILE *fil;
467
468 fil = (FILE *) context;
469 if (fil == stdin)
470 return(0);
471 if (fil == stdout)
472 return(0);
Daniel Veillardcd337f02001-11-22 18:20:37 +0000473 if (fil == stderr)
474 return(0);
Daniel Veillardf012a642001-07-23 19:10:52 +0000475 return ( ( fclose((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000476}
477
478/**
479 * xmlFileFlush:
480 * @context: the I/O context
481 *
482 * Flush an I/O channel
483 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000484static int
Owen Taylor3473f882001-02-23 17:55:21 +0000485xmlFileFlush (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000486 return ( ( fflush((FILE *) context) == EOF ) ? -1 : 0 );
Owen Taylor3473f882001-02-23 17:55:21 +0000487}
488
489#ifdef HAVE_ZLIB_H
490/************************************************************************
491 * *
492 * I/O for compressed file accesses *
493 * *
494 ************************************************************************/
495/**
496 * xmlGzfileMatch:
497 * @filename: the URI for matching
498 *
499 * input from compressed file test
500 *
501 * Returns 1 if matches, 0 otherwise
502 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000503static int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +0000504xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000505 return(1);
506}
507
508/**
509 * xmlGzfileOpen:
510 * @filename: the URI for matching
511 *
512 * input from compressed file open
513 * if @filename is " " then the standard input is used
514 *
515 * Returns an I/O context or NULL in case of error
516 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000517static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000518xmlGzfileOpen (const char *filename) {
519 const char *path = NULL;
520 gzFile fd;
521
522 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000523 fd = gzdopen(dup(0), "rb");
Owen Taylor3473f882001-02-23 17:55:21 +0000524 return((void *) fd);
525 }
526
Daniel Veillardf4862f02002-09-10 11:13:43 +0000527 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
528#if defined (_WIN32) && !defined(__CYGWIN__)
529 path = &filename[17];
530#else
Owen Taylor3473f882001-02-23 17:55:21 +0000531 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000532#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000533 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000534#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000535 path = &filename[8];
536#else
Owen Taylor3473f882001-02-23 17:55:21 +0000537 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000538#endif
539 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000540 path = filename;
541
542 if (path == NULL)
543 return(NULL);
544 if (!xmlCheckFilename(path))
545 return(NULL);
546
547 fd = gzopen(path, "rb");
548 return((void *) fd);
549}
550
551/**
552 * xmlGzfileOpenW:
553 * @filename: the URI for matching
554 * @compression: the compression factor (0 - 9 included)
555 *
556 * input from compressed file open
557 * if @filename is " " then the standard input is used
558 *
559 * Returns an I/O context or NULL in case of error
560 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000561static void *
Owen Taylor3473f882001-02-23 17:55:21 +0000562xmlGzfileOpenW (const char *filename, int compression) {
563 const char *path = NULL;
564 char mode[15];
565 gzFile fd;
566
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000567 snprintf(mode, sizeof(mode), "wb%d", compression);
Owen Taylor3473f882001-02-23 17:55:21 +0000568 if (!strcmp(filename, "-")) {
Daniel Veillarda9e65e82001-10-30 10:32:36 +0000569 fd = gzdopen(dup(1), mode);
Owen Taylor3473f882001-02-23 17:55:21 +0000570 return((void *) fd);
571 }
572
Daniel Veillardf4862f02002-09-10 11:13:43 +0000573 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17))
574#if defined (_WIN32) && !defined(__CYGWIN__)
575 path = &filename[17];
576#else
Owen Taylor3473f882001-02-23 17:55:21 +0000577 path = &filename[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +0000578#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +0000579 else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +0000580#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillardfe703322001-08-14 12:18:09 +0000581 path = &filename[8];
582#else
Daniel Veillard3c2758d2001-05-31 18:43:43 +0000583 path = &filename[7];
Daniel Veillardfe703322001-08-14 12:18:09 +0000584#endif
585 } else
Owen Taylor3473f882001-02-23 17:55:21 +0000586 path = filename;
587
588 if (path == NULL)
589 return(NULL);
590
591 fd = gzopen(path, mode);
592 return((void *) fd);
593}
594
595/**
596 * xmlGzfileRead:
597 * @context: the I/O context
598 * @buffer: where to drop data
599 * @len: number of bytes to write
600 *
601 * Read @len bytes to @buffer from the compressed I/O channel.
602 *
603 * Returns the number of bytes written
604 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000605static int
Owen Taylor3473f882001-02-23 17:55:21 +0000606xmlGzfileRead (void * context, char * buffer, int len) {
607 return(gzread((gzFile) context, &buffer[0], len));
608}
609
610/**
611 * xmlGzfileWrite:
612 * @context: the I/O context
613 * @buffer: where to drop data
614 * @len: number of bytes to write
615 *
616 * Write @len bytes from @buffer to the compressed I/O channel.
617 *
618 * Returns the number of bytes written
619 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000620static int
Owen Taylor3473f882001-02-23 17:55:21 +0000621xmlGzfileWrite (void * context, const char * buffer, int len) {
622 return(gzwrite((gzFile) context, (char *) &buffer[0], len));
623}
624
625/**
626 * xmlGzfileClose:
627 * @context: the I/O context
628 *
629 * Close a compressed I/O channel
630 */
Daniel Veillardf012a642001-07-23 19:10:52 +0000631static int
Owen Taylor3473f882001-02-23 17:55:21 +0000632xmlGzfileClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000633 return ( ( gzclose((gzFile) context) == Z_OK ) ? 0 : -1 );
Owen Taylor3473f882001-02-23 17:55:21 +0000634}
635#endif /* HAVE_ZLIB_H */
636
637#ifdef LIBXML_HTTP_ENABLED
638/************************************************************************
639 * *
640 * I/O for HTTP file accesses *
641 * *
642 ************************************************************************/
Daniel Veillardf012a642001-07-23 19:10:52 +0000643
644typedef struct xmlIOHTTPWriteCtxt_
645{
646 int compression;
647
648 char * uri;
649
650 void * doc_buff;
651
652} xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr;
653
654#ifdef HAVE_ZLIB_H
655
656#define DFLT_WBITS ( -15 )
657#define DFLT_MEM_LVL ( 8 )
658#define GZ_MAGIC1 ( 0x1f )
659#define GZ_MAGIC2 ( 0x8b )
660#define LXML_ZLIB_OS_CODE ( 0x03 )
661#define INIT_HTTP_BUFF_SIZE ( 32768 )
662#define DFLT_ZLIB_RATIO ( 5 )
663
664/*
665** Data structure and functions to work with sending compressed data
666** via HTTP.
667*/
668
669typedef struct xmlZMemBuff_
670{
671 unsigned long size;
672 unsigned long crc;
673
674 unsigned char * zbuff;
675 z_stream zctrl;
676
677} xmlZMemBuff, *xmlZMemBuffPtr;
678
679/**
680 * append_reverse_ulong
681 * @buff: Compressed memory buffer
682 * @data: Unsigned long to append
683 *
684 * Append a unsigned long in reverse byte order to the end of the
685 * memory buffer.
686 */
687static void
688append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) {
689
690 int idx;
691
692 if ( buff == NULL )
693 return;
694
695 /*
696 ** This is plagiarized from putLong in gzio.c (zlib source) where
697 ** the number "4" is hardcoded. If zlib is ever patched to
698 ** support 64 bit file sizes, this code would need to be patched
699 ** as well.
700 */
701
702 for ( idx = 0; idx < 4; idx++ ) {
703 *buff->zctrl.next_out = ( data & 0xff );
704 data >>= 8;
705 buff->zctrl.next_out++;
706 }
707
708 return;
709}
710
711/**
712 *
713 * xmlFreeZMemBuff
714 * @buff: The memory buffer context to clear
715 *
716 * Release all the resources associated with the compressed memory buffer.
717 */
718static void
719xmlFreeZMemBuff( xmlZMemBuffPtr buff ) {
720
721 int z_err;
722
723 if ( buff == NULL )
724 return;
725
726 xmlFree( buff->zbuff );
727 z_err = deflateEnd( &buff->zctrl );
728#ifdef DEBUG_HTTP
729 if ( z_err != Z_OK )
730 xmlGenericError( xmlGenericErrorContext,
731 "xmlFreeZMemBuff: Error releasing zlib context: %d\n",
732 z_err );
733#endif
734
735 xmlFree( buff );
736 return;
737}
738
739/**
740 * xmlCreateZMemBuff
741 *@compression: Compression value to use
742 *
743 * Create a memory buffer to hold the compressed XML document. The
744 * compressed document in memory will end up being identical to what
745 * would be created if gzopen/gzwrite/gzclose were being used to
746 * write the document to disk. The code for the header/trailer data to
747 * the compression is plagiarized from the zlib source files.
748 */
749static void *
750xmlCreateZMemBuff( int compression ) {
751
752 int z_err;
753 int hdr_lgth;
754 xmlZMemBuffPtr buff = NULL;
755
756 if ( ( compression < 1 ) || ( compression > 9 ) )
757 return ( NULL );
758
759 /* Create the control and data areas */
760
761 buff = xmlMalloc( sizeof( xmlZMemBuff ) );
762 if ( buff == NULL ) {
763 xmlGenericError( xmlGenericErrorContext,
764 "xmlCreateZMemBuff: %s\n",
765 "Failure allocating buffer context." );
766 return ( NULL );
767 }
768
769 (void)memset( buff, 0, sizeof( xmlZMemBuff ) );
770 buff->size = INIT_HTTP_BUFF_SIZE;
771 buff->zbuff = xmlMalloc( buff->size );
772 if ( buff->zbuff == NULL ) {
773 xmlFreeZMemBuff( buff );
774 xmlGenericError( xmlGenericErrorContext,
775 "xmlCreateZMemBuff: %s\n",
776 "Failure allocating data buffer." );
777 return ( NULL );
778 }
779
780 z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED,
781 DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY );
782 if ( z_err != Z_OK ) {
783 xmlFreeZMemBuff( buff );
784 buff = NULL;
785 xmlGenericError( xmlGenericErrorContext,
786 "xmlCreateZMemBuff: %s %d\n",
787 "Error initializing compression context. ZLIB error:",
788 z_err );
789 return ( NULL );
790 }
791
792 /* Set the header data. The CRC will be needed for the trailer */
Daniel Veillardf012a642001-07-23 19:10:52 +0000793 buff->crc = crc32( 0L, Z_NULL, 0 );
Aleksey Sanin49cc9752002-06-14 17:07:10 +0000794 hdr_lgth = snprintf( (char *)buff->zbuff, buff->size,
795 "%c%c%c%c%c%c%c%c%c%c",
Daniel Veillardf012a642001-07-23 19:10:52 +0000796 GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED,
797 0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE );
798 buff->zctrl.next_out = buff->zbuff + hdr_lgth;
799 buff->zctrl.avail_out = buff->size - hdr_lgth;
800
801 return ( buff );
802}
803
804/**
805 * xmlZMemBuffExtend
806 * @buff: Buffer used to compress and consolidate data.
807 * @ext_amt: Number of bytes to extend the buffer.
808 *
809 * Extend the internal buffer used to store the compressed data by the
810 * specified amount.
811 *
812 * Returns 0 on success or -1 on failure to extend the buffer. On failure
813 * the original buffer still exists at the original size.
814 */
815static int
816xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) {
817
818 int rc = -1;
819 size_t new_size;
820 size_t cur_used;
821
822 unsigned char * tmp_ptr = NULL;
823
824 if ( buff == NULL )
825 return ( -1 );
826
827 else if ( ext_amt == 0 )
828 return ( 0 );
829
830 cur_used = buff->zctrl.next_out - buff->zbuff;
831 new_size = buff->size + ext_amt;
832
833#ifdef DEBUG_HTTP
834 if ( cur_used > new_size )
835 xmlGenericError( xmlGenericErrorContext,
836 "xmlZMemBuffExtend: %s\n%s %d bytes.\n",
837 "Buffer overwrite detected during compressed memory",
838 "buffer extension. Overflowed by",
839 (cur_used - new_size ) );
840#endif
841
842 tmp_ptr = xmlRealloc( buff->zbuff, new_size );
843 if ( tmp_ptr != NULL ) {
844 rc = 0;
845 buff->size = new_size;
846 buff->zbuff = tmp_ptr;
847 buff->zctrl.next_out = tmp_ptr + cur_used;
848 buff->zctrl.avail_out = new_size - cur_used;
849 }
850 else {
851 xmlGenericError( xmlGenericErrorContext,
852 "xmlZMemBuffExtend: %s %lu bytes.\n",
853 "Allocation failure extending output buffer to",
854 new_size );
855 }
856
857 return ( rc );
858}
859
860/**
861 * xmlZMemBuffAppend
862 * @buff: Buffer used to compress and consolidate data
863 * @src: Uncompressed source content to append to buffer
864 * @len: Length of source data to append to buffer
865 *
866 * Compress and append data to the internal buffer. The data buffer
867 * will be expanded if needed to store the additional data.
868 *
869 * Returns the number of bytes appended to the buffer or -1 on error.
870 */
871static int
872xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) {
873
874 int z_err;
875 size_t min_accept;
876
877 if ( ( buff == NULL ) || ( src == NULL ) )
878 return ( -1 );
879
880 buff->zctrl.avail_in = len;
881 buff->zctrl.next_in = (unsigned char *)src;
882 while ( buff->zctrl.avail_in > 0 ) {
883 /*
884 ** Extend the buffer prior to deflate call if a reasonable amount
885 ** of output buffer space is not available.
886 */
887 min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO;
888 if ( buff->zctrl.avail_out <= min_accept ) {
889 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
890 return ( -1 );
891 }
892
893 z_err = deflate( &buff->zctrl, Z_NO_FLUSH );
894 if ( z_err != Z_OK ) {
895 xmlGenericError( xmlGenericErrorContext,
896 "xmlZMemBuffAppend: %s %d %s - %d",
897 "Compression error while appending",
898 len, "bytes to buffer. ZLIB error", z_err );
899 return ( -1 );
900 }
901 }
902
903 buff->crc = crc32( buff->crc, (unsigned char *)src, len );
904
905 return ( len );
906}
907
908/**
909 * xmlZMemBuffGetContent
910 * @buff: Compressed memory content buffer
911 * @data_ref: Pointer reference to point to compressed content
912 *
913 * Flushes the compression buffers, appends gzip file trailers and
914 * returns the compressed content and length of the compressed data.
915 * NOTE: The gzip trailer code here is plagiarized from zlib source.
916 *
917 * Returns the length of the compressed data or -1 on error.
918 */
919static int
920xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) {
921
922 int zlgth = -1;
923 int z_err;
924
925 if ( ( buff == NULL ) || ( data_ref == NULL ) )
926 return ( -1 );
927
928 /* Need to loop until compression output buffers are flushed */
929
930 do
931 {
932 z_err = deflate( &buff->zctrl, Z_FINISH );
933 if ( z_err == Z_OK ) {
934 /* In this case Z_OK means more buffer space needed */
935
936 if ( xmlZMemBuffExtend( buff, buff->size ) == -1 )
937 return ( -1 );
938 }
939 }
940 while ( z_err == Z_OK );
941
942 /* If the compression state is not Z_STREAM_END, some error occurred */
943
944 if ( z_err == Z_STREAM_END ) {
945
946 /* Need to append the gzip data trailer */
947
948 if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) {
949 if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 )
950 return ( -1 );
951 }
952
953 /*
954 ** For whatever reason, the CRC and length data are pushed out
955 ** in reverse byte order. So a memcpy can't be used here.
956 */
957
958 append_reverse_ulong( buff, buff->crc );
959 append_reverse_ulong( buff, buff->zctrl.total_in );
960
961 zlgth = buff->zctrl.next_out - buff->zbuff;
962 *data_ref = (char *)buff->zbuff;
963 }
964
965 else
966 xmlGenericError( xmlGenericErrorContext,
967 "xmlZMemBuffGetContent: %s - %d\n",
968 "Error flushing zlib buffers. Error code", z_err );
969
970 return ( zlgth );
971}
972#endif /* HAVE_ZLIB_H */
973
974/**
975 * xmlFreeHTTPWriteCtxt
976 * @ctxt: Context to cleanup
977 *
978 * Free allocated memory and reclaim system resources.
979 *
980 * No return value.
981 */
982static void
983xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt )
984{
985 if ( ctxt->uri != NULL )
986 free( ctxt->uri );
987
988 if ( ctxt->doc_buff != NULL ) {
989
990#ifdef HAVE_ZLIB_H
991 if ( ctxt->compression > 0 ) {
992 xmlFreeZMemBuff( ctxt->doc_buff );
993 }
994 else
995#endif
996 {
997 xmlOutputBufferClose( ctxt->doc_buff );
998 }
999 }
1000
1001 free( ctxt );
1002 return;
1003}
1004
1005
Owen Taylor3473f882001-02-23 17:55:21 +00001006/**
1007 * xmlIOHTTPMatch:
1008 * @filename: the URI for matching
1009 *
1010 * check if the URI matches an HTTP one
1011 *
1012 * Returns 1 if matches, 0 otherwise
1013 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001014int
Owen Taylor3473f882001-02-23 17:55:21 +00001015xmlIOHTTPMatch (const char *filename) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001016 if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "http://", 7))
Owen Taylor3473f882001-02-23 17:55:21 +00001017 return(1);
1018 return(0);
1019}
1020
1021/**
1022 * xmlIOHTTPOpen:
1023 * @filename: the URI for matching
1024 *
1025 * open an HTTP I/O channel
1026 *
1027 * Returns an I/O context or NULL in case of error
1028 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001029void *
Owen Taylor3473f882001-02-23 17:55:21 +00001030xmlIOHTTPOpen (const char *filename) {
1031 return(xmlNanoHTTPOpen(filename, NULL));
1032}
1033
1034/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001035 * xmlIOHTTPOpenW
1036 * @post_uri: The destination URI for the document
1037 * @compression: The compression desired for the document.
1038 *
1039 * Open a temporary buffer to collect the document for a subsequent HTTP POST
1040 * request. Non-static as is called from the output buffer creation routine.
1041 *
1042 * Returns an I/O context or NULL in case of error.
1043 */
1044
1045void *
Daniel Veillard572577e2002-01-18 16:23:55 +00001046xmlIOHTTPOpenW(const char *post_uri, int compression)
1047{
Daniel Veillardf012a642001-07-23 19:10:52 +00001048
Daniel Veillard572577e2002-01-18 16:23:55 +00001049 xmlIOHTTPWriteCtxtPtr ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001050
Daniel Veillard572577e2002-01-18 16:23:55 +00001051 if (post_uri == NULL)
1052 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001053
Daniel Veillard572577e2002-01-18 16:23:55 +00001054 ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt));
1055 if (ctxt == NULL) {
1056 xmlGenericError(xmlGenericErrorContext,
1057 "xmlIOHTTPOpenW: Failed to create output HTTP context.\n");
1058 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001059 }
1060
Daniel Veillard572577e2002-01-18 16:23:55 +00001061 (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt));
Daniel Veillardf012a642001-07-23 19:10:52 +00001062
Daniel Veillard572577e2002-01-18 16:23:55 +00001063 ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri);
1064 if (ctxt->uri == NULL) {
1065 xmlGenericError(xmlGenericErrorContext,
1066 "xmlIOHTTPOpenW: Failed to duplicate destination URI.\n");
1067 xmlFreeHTTPWriteCtxt(ctxt);
1068 return (NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001069 }
1070
1071 /*
Daniel Veillard572577e2002-01-18 16:23:55 +00001072 * ** Since the document length is required for an HTTP post,
1073 * ** need to put the document into a buffer. A memory buffer
1074 * ** is being used to avoid pushing the data to disk and back.
1075 */
Daniel Veillardf012a642001-07-23 19:10:52 +00001076
1077#ifdef HAVE_ZLIB_H
Daniel Veillard572577e2002-01-18 16:23:55 +00001078 if ((compression > 0) && (compression <= 9)) {
1079
1080 ctxt->compression = compression;
1081 ctxt->doc_buff = xmlCreateZMemBuff(compression);
1082 } else
Daniel Veillardf012a642001-07-23 19:10:52 +00001083#endif
1084 {
Daniel Veillard572577e2002-01-18 16:23:55 +00001085 /* Any character conversions should have been done before this */
Daniel Veillardf012a642001-07-23 19:10:52 +00001086
Daniel Veillard572577e2002-01-18 16:23:55 +00001087 ctxt->doc_buff = xmlAllocOutputBuffer(NULL);
Daniel Veillardf012a642001-07-23 19:10:52 +00001088 }
1089
Daniel Veillard572577e2002-01-18 16:23:55 +00001090 if (ctxt->doc_buff == NULL) {
1091 xmlFreeHTTPWriteCtxt(ctxt);
1092 ctxt = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +00001093 }
1094
Daniel Veillard572577e2002-01-18 16:23:55 +00001095 return (ctxt);
Daniel Veillardf012a642001-07-23 19:10:52 +00001096}
1097
1098/**
1099 * xmlIOHTTPDfltOpenW
1100 * @post_uri: The destination URI for this document.
1101 *
1102 * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent
1103 * HTTP post command. This function should generally not be used as
1104 * the open callback is short circuited in xmlOutputBufferCreateFile.
1105 *
1106 * Returns a pointer to the new IO context.
1107 */
1108static void *
1109xmlIOHTTPDfltOpenW( const char * post_uri ) {
1110 return ( xmlIOHTTPOpenW( post_uri, 0 ) );
1111}
1112
1113/**
Owen Taylor3473f882001-02-23 17:55:21 +00001114 * xmlIOHTTPRead:
1115 * @context: the I/O context
1116 * @buffer: where to drop data
1117 * @len: number of bytes to write
1118 *
1119 * Read @len bytes to @buffer from the I/O channel.
1120 *
1121 * Returns the number of bytes written
1122 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001123int
Owen Taylor3473f882001-02-23 17:55:21 +00001124xmlIOHTTPRead(void * context, char * buffer, int len) {
1125 return(xmlNanoHTTPRead(context, &buffer[0], len));
1126}
1127
1128/**
Daniel Veillardf012a642001-07-23 19:10:52 +00001129 * xmlIOHTTPWrite
1130 * @context: previously opened writing context
1131 * @buffer: data to output to temporary buffer
1132 * @len: bytes to output
1133 *
1134 * Collect data from memory buffer into a temporary file for later
1135 * processing.
1136 *
1137 * Returns number of bytes written.
1138 */
1139
1140static int
1141xmlIOHTTPWrite( void * context, const char * buffer, int len ) {
1142
1143 xmlIOHTTPWriteCtxtPtr ctxt = context;
1144
1145 if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) )
1146 return ( -1 );
1147
1148 if ( len > 0 ) {
1149
1150 /* Use gzwrite or fwrite as previously setup in the open call */
1151
1152#ifdef HAVE_ZLIB_H
1153 if ( ctxt->compression > 0 )
1154 len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len );
1155
1156 else
1157#endif
1158 len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer );
1159
1160 if ( len < 0 ) {
1161 xmlGenericError( xmlGenericErrorContext,
1162 "xmlIOHTTPWrite: %s\n%s '%s'.\n",
1163 "Error appending to internal buffer.",
1164 "Error sending document to URI",
1165 ctxt->uri );
1166 }
1167 }
1168
1169 return ( len );
1170}
1171
1172
1173/**
Owen Taylor3473f882001-02-23 17:55:21 +00001174 * xmlIOHTTPClose:
1175 * @context: the I/O context
1176 *
1177 * Close an HTTP I/O channel
1178 */
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
1398 */
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00001399int
Owen Taylor3473f882001-02-23 17:55:21 +00001400xmlIOFTPClose (void * context) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001401 return ( xmlNanoFTPClose(context) );
Owen Taylor3473f882001-02-23 17:55:21 +00001402}
1403#endif /* LIBXML_FTP_ENABLED */
1404
1405
1406/**
1407 * xmlRegisterInputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001408 * @matchFunc: the xmlInputMatchCallback
1409 * @openFunc: the xmlInputOpenCallback
1410 * @readFunc: the xmlInputReadCallback
1411 * @closeFunc: the xmlInputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001412 *
1413 * Register a new set of I/O callback for handling parser input.
1414 *
1415 * Returns the registered handler number or -1 in case of error
1416 */
1417int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001418xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc,
1419 xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc,
1420 xmlInputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001421 if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) {
1422 return(-1);
1423 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001424 xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc;
1425 xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc;
1426 xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc;
1427 xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001428 return(xmlInputCallbackNr++);
1429}
1430
1431/**
1432 * xmlRegisterOutputCallbacks:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001433 * @matchFunc: the xmlOutputMatchCallback
1434 * @openFunc: the xmlOutputOpenCallback
1435 * @writeFunc: the xmlOutputWriteCallback
1436 * @closeFunc: the xmlOutputCloseCallback
Owen Taylor3473f882001-02-23 17:55:21 +00001437 *
1438 * Register a new set of I/O callback for handling output.
1439 *
1440 * Returns the registered handler number or -1 in case of error
1441 */
1442int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001443xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc,
1444 xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc,
1445 xmlOutputCloseCallback closeFunc) {
Owen Taylor3473f882001-02-23 17:55:21 +00001446 if (xmlOutputCallbackNr >= MAX_INPUT_CALLBACK) {
1447 return(-1);
1448 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001449 xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc;
1450 xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc;
1451 xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc;
1452 xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc;
Owen Taylor3473f882001-02-23 17:55:21 +00001453 return(xmlOutputCallbackNr++);
1454}
1455
1456/**
1457 * xmlRegisterDefaultInputCallbacks:
1458 *
1459 * Registers the default compiled-in I/O handlers.
1460 */
1461void
1462#ifdef VMS
1463xmlRegisterDefInputCallbacks
1464#else
1465xmlRegisterDefaultInputCallbacks
1466#endif
1467(void) {
1468 if (xmlInputCallbackInitialized)
1469 return;
1470
1471 xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen,
1472 xmlFileRead, xmlFileClose);
1473#ifdef HAVE_ZLIB_H
1474 xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1475 xmlGzfileRead, xmlGzfileClose);
1476#endif /* HAVE_ZLIB_H */
1477
1478#ifdef LIBXML_HTTP_ENABLED
1479 xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen,
1480 xmlIOHTTPRead, xmlIOHTTPClose);
1481#endif /* LIBXML_HTTP_ENABLED */
1482
1483#ifdef LIBXML_FTP_ENABLED
1484 xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1485 xmlIOFTPRead, xmlIOFTPClose);
1486#endif /* LIBXML_FTP_ENABLED */
1487 xmlInputCallbackInitialized = 1;
1488}
1489
1490/**
1491 * xmlRegisterDefaultOutputCallbacks:
1492 *
1493 * Registers the default compiled-in I/O handlers.
1494 */
1495void
1496#ifdef VMS
1497xmlRegisterDefOutputCallbacks
1498#else
1499xmlRegisterDefaultOutputCallbacks
1500#endif
1501(void) {
1502 if (xmlOutputCallbackInitialized)
1503 return;
1504
1505 xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW,
1506 xmlFileWrite, xmlFileClose);
Daniel Veillardf012a642001-07-23 19:10:52 +00001507
1508#ifdef LIBXML_HTTP_ENABLED
1509 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1510 xmlIOHTTPWrite, xmlIOHTTPClosePut);
1511#endif
1512
Owen Taylor3473f882001-02-23 17:55:21 +00001513/*********************************
1514 No way a-priori to distinguish between gzipped files from
1515 uncompressed ones except opening if existing then closing
1516 and saving with same compression ratio ... a pain.
1517
1518#ifdef HAVE_ZLIB_H
1519 xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen,
1520 xmlGzfileWrite, xmlGzfileClose);
1521#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001522
1523 Nor FTP PUT ....
1524#ifdef LIBXML_FTP_ENABLED
1525 xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen,
1526 xmlIOFTPWrite, xmlIOFTPClose);
1527#endif
1528 **********************************/
1529 xmlOutputCallbackInitialized = 1;
1530}
1531
Daniel Veillardf012a642001-07-23 19:10:52 +00001532#ifdef LIBXML_HTTP_ENABLED
1533/**
1534 * xmlRegisterHTTPPostCallbacks
1535 *
1536 * By default, libxml submits HTTP output requests using the "PUT" method.
1537 * Calling this method changes the HTTP output method to use the "POST"
1538 * method instead.
1539 *
1540 */
1541void
1542xmlRegisterHTTPPostCallbacks( void ) {
1543
1544 /* Register defaults if not done previously */
1545
1546 if ( xmlOutputCallbackInitialized == 0 )
1547 xmlRegisterDefaultOutputCallbacks( );
1548
1549 xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW,
1550 xmlIOHTTPWrite, xmlIOHTTPClosePost);
1551 return;
1552}
1553#endif
1554
Owen Taylor3473f882001-02-23 17:55:21 +00001555/**
1556 * xmlAllocParserInputBuffer:
1557 * @enc: the charset encoding if known
1558 *
1559 * Create a buffered parser input for progressive parsing
1560 *
1561 * Returns the new parser input or NULL
1562 */
1563xmlParserInputBufferPtr
1564xmlAllocParserInputBuffer(xmlCharEncoding enc) {
1565 xmlParserInputBufferPtr ret;
1566
1567 ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer));
1568 if (ret == NULL) {
1569 xmlGenericError(xmlGenericErrorContext,
1570 "xmlAllocParserInputBuffer : out of memory!\n");
1571 return(NULL);
1572 }
1573 memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer));
1574 ret->buffer = xmlBufferCreate();
1575 if (ret->buffer == NULL) {
1576 xmlFree(ret);
1577 return(NULL);
1578 }
1579 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1580 ret->encoder = xmlGetCharEncodingHandler(enc);
1581 if (ret->encoder != NULL)
1582 ret->raw = xmlBufferCreate();
1583 else
1584 ret->raw = NULL;
1585 ret->readcallback = NULL;
1586 ret->closecallback = NULL;
1587 ret->context = NULL;
1588
1589 return(ret);
1590}
1591
1592/**
1593 * xmlAllocOutputBuffer:
1594 * @encoder: the encoding converter or NULL
1595 *
1596 * Create a buffered parser output
1597 *
1598 * Returns the new parser output or NULL
1599 */
1600xmlOutputBufferPtr
1601xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) {
1602 xmlOutputBufferPtr ret;
1603
1604 ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer));
1605 if (ret == NULL) {
1606 xmlGenericError(xmlGenericErrorContext,
1607 "xmlAllocOutputBuffer : out of memory!\n");
1608 return(NULL);
1609 }
1610 memset(ret, 0, (size_t) sizeof(xmlOutputBuffer));
1611 ret->buffer = xmlBufferCreate();
1612 if (ret->buffer == NULL) {
1613 xmlFree(ret);
1614 return(NULL);
1615 }
1616 ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT;
1617 ret->encoder = encoder;
1618 if (encoder != NULL) {
1619 ret->conv = xmlBufferCreateSize(4000);
1620 /*
1621 * This call is designed to initiate the encoder state
1622 */
1623 xmlCharEncOutFunc(encoder, ret->conv, NULL);
1624 } else
1625 ret->conv = NULL;
1626 ret->writecallback = NULL;
1627 ret->closecallback = NULL;
1628 ret->context = NULL;
1629 ret->written = 0;
1630
1631 return(ret);
1632}
1633
1634/**
1635 * xmlFreeParserInputBuffer:
1636 * @in: a buffered parser input
1637 *
1638 * Free up the memory used by a buffered parser input
1639 */
1640void
1641xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
1642 if (in->raw) {
1643 xmlBufferFree(in->raw);
1644 in->raw = NULL;
1645 }
1646 if (in->encoder != NULL) {
1647 xmlCharEncCloseFunc(in->encoder);
1648 }
1649 if (in->closecallback != NULL) {
1650 in->closecallback(in->context);
1651 }
1652 if (in->buffer != NULL) {
1653 xmlBufferFree(in->buffer);
1654 in->buffer = NULL;
1655 }
1656
Owen Taylor3473f882001-02-23 17:55:21 +00001657 xmlFree(in);
1658}
1659
1660/**
1661 * xmlOutputBufferClose:
1662 * @out: a buffered output
1663 *
1664 * flushes and close the output I/O channel
1665 * and free up all the associated resources
1666 *
1667 * Returns the number of byte written or -1 in case of error.
1668 */
1669int
1670xmlOutputBufferClose(xmlOutputBufferPtr out) {
1671 int written;
Daniel Veillardf012a642001-07-23 19:10:52 +00001672 int err_rc = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001673
1674 if (out == NULL)
1675 return(-1);
1676 if (out->writecallback != NULL)
1677 xmlOutputBufferFlush(out);
1678 if (out->closecallback != NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001679 err_rc = out->closecallback(out->context);
Owen Taylor3473f882001-02-23 17:55:21 +00001680 }
1681 written = out->written;
1682 if (out->conv) {
1683 xmlBufferFree(out->conv);
1684 out->conv = NULL;
1685 }
1686 if (out->encoder != NULL) {
1687 xmlCharEncCloseFunc(out->encoder);
1688 }
1689 if (out->buffer != NULL) {
1690 xmlBufferFree(out->buffer);
1691 out->buffer = NULL;
1692 }
1693
Owen Taylor3473f882001-02-23 17:55:21 +00001694 xmlFree(out);
Daniel Veillardf012a642001-07-23 19:10:52 +00001695 return( ( err_rc == 0 ) ? written : err_rc );
Owen Taylor3473f882001-02-23 17:55:21 +00001696}
1697
1698/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001699 * xmlParserInputBufferCreateFname:
1700 * @URI: a C string containing the URI or filename
1701 * @enc: the charset encoding if known
1702 *
1703 * VMS version of xmlParserInputBufferCreateFilename()
1704 *
1705 * Returns the new parser input or NULL
1706 */
1707/**
Owen Taylor3473f882001-02-23 17:55:21 +00001708 * xmlParserInputBufferCreateFilename:
1709 * @URI: a C string containing the URI or filename
1710 * @enc: the charset encoding if known
1711 *
1712 * Create a buffered parser input for the progressive parsing of a file
1713 * If filename is "-' then we use stdin as the input.
1714 * Automatic support for ZLIB/Compress compressed document is provided
1715 * by default if found at compile-time.
1716 * Do an encoding check if enc == XML_CHAR_ENCODING_NONE
1717 *
1718 * Returns the new parser input or NULL
1719 */
1720xmlParserInputBufferPtr
1721#ifdef VMS
1722xmlParserInputBufferCreateFname
1723#else
1724xmlParserInputBufferCreateFilename
1725#endif
1726(const char *URI, xmlCharEncoding enc) {
1727 xmlParserInputBufferPtr ret;
Daniel Veillard388236f2001-07-08 18:35:48 +00001728 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001729 void *context = NULL;
Daniel Veillard388236f2001-07-08 18:35:48 +00001730 char *unescaped;
Daniel Veillardf4862f02002-09-10 11:13:43 +00001731 char *normalized;
Owen Taylor3473f882001-02-23 17:55:21 +00001732
1733 if (xmlInputCallbackInitialized == 0)
1734 xmlRegisterDefaultInputCallbacks();
1735
1736 if (URI == NULL) return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001737 normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
1738 if (normalized == NULL) return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001739
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00001740#ifdef LIBXML_CATALOG_ENABLED
1741#endif
1742
Owen Taylor3473f882001-02-23 17:55:21 +00001743 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001744 * Try to find one of the input accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001745 * Go in reverse to give precedence to user defined handlers.
Daniel Veillard388236f2001-07-08 18:35:48 +00001746 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001747 */
Daniel Veillardf4862f02002-09-10 11:13:43 +00001748 unescaped = xmlURIUnescapeString((char *) normalized, 0, NULL);
Daniel Veillard388236f2001-07-08 18:35:48 +00001749 if (unescaped != NULL) {
1750 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1751 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1752 (xmlInputCallbackTable[i].matchcallback(unescaped) != 0)) {
1753 context = xmlInputCallbackTable[i].opencallback(unescaped);
1754 if (context != NULL)
1755 break;
1756 }
1757 }
1758 xmlFree(unescaped);
1759 }
1760
1761 /*
1762 * If this failed try with a non-escaped URI this may be a strange
1763 * filename
1764 */
1765 if (context == NULL) {
1766 for (i = xmlInputCallbackNr - 1;i >= 0;i--) {
1767 if ((xmlInputCallbackTable[i].matchcallback != NULL) &&
1768 (xmlInputCallbackTable[i].matchcallback(URI) != 0)) {
Daniel Veillardf4862f02002-09-10 11:13:43 +00001769 context = xmlInputCallbackTable[i].opencallback(normalized);
Daniel Veillard388236f2001-07-08 18:35:48 +00001770 if (context != NULL)
1771 break;
1772 }
Owen Taylor3473f882001-02-23 17:55:21 +00001773 }
1774 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001775 xmlFree(normalized);
Owen Taylor3473f882001-02-23 17:55:21 +00001776 if (context == NULL) {
1777 return(NULL);
1778 }
1779
1780 /*
1781 * Allocate the Input buffer front-end.
1782 */
1783 ret = xmlAllocParserInputBuffer(enc);
1784 if (ret != NULL) {
1785 ret->context = context;
1786 ret->readcallback = xmlInputCallbackTable[i].readcallback;
1787 ret->closecallback = xmlInputCallbackTable[i].closecallback;
1788 }
1789 return(ret);
1790}
1791
1792/**
1793 * xmlOutputBufferCreateFilename:
1794 * @URI: a C string containing the URI or filename
1795 * @encoder: the encoding converter or NULL
1796 * @compression: the compression ration (0 none, 9 max).
1797 *
1798 * Create a buffered output for the progressive saving of a file
1799 * If filename is "-' then we use stdout as the output.
1800 * Automatic support for ZLIB/Compress compressed document is provided
1801 * by default if found at compile-time.
1802 * TODO: currently if compression is set, the library only support
1803 * writing to a local file.
1804 *
1805 * Returns the new output or NULL
1806 */
1807xmlOutputBufferPtr
1808xmlOutputBufferCreateFilename(const char *URI,
1809 xmlCharEncodingHandlerPtr encoder,
1810 int compression) {
1811 xmlOutputBufferPtr ret;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001812 int i = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001813 void *context = NULL;
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001814 char *unescaped;
Daniel Veillardf4862f02002-09-10 11:13:43 +00001815 char *normalized;
Owen Taylor3473f882001-02-23 17:55:21 +00001816
Daniel Veillardf012a642001-07-23 19:10:52 +00001817 int is_http_uri = 0; /* Can't change if HTTP disabled */
1818
Owen Taylor3473f882001-02-23 17:55:21 +00001819 if (xmlOutputCallbackInitialized == 0)
1820 xmlRegisterDefaultOutputCallbacks();
1821
1822 if (URI == NULL) return(NULL);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001823 normalized = (char *) xmlNormalizeWindowsPath((const xmlChar *)URI);
1824 if (normalized == NULL) return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001825
Daniel Veillardf012a642001-07-23 19:10:52 +00001826#ifdef LIBXML_HTTP_ENABLED
1827 /* Need to prevent HTTP URI's from falling into zlib short circuit */
1828
Daniel Veillardf4862f02002-09-10 11:13:43 +00001829 is_http_uri = xmlIOHTTPMatch( normalized );
Daniel Veillardf012a642001-07-23 19:10:52 +00001830#endif
1831
Owen Taylor3473f882001-02-23 17:55:21 +00001832
1833 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001834 * Try to find one of the output accept method accepting that scheme
Owen Taylor3473f882001-02-23 17:55:21 +00001835 * Go in reverse to give precedence to user defined handlers.
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001836 * try with an unescaped version of the URI
Owen Taylor3473f882001-02-23 17:55:21 +00001837 */
Daniel Veillardf4862f02002-09-10 11:13:43 +00001838 unescaped = xmlURIUnescapeString(normalized, 0, NULL);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001839 if (unescaped != NULL) {
1840#ifdef HAVE_ZLIB_H
1841 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
1842 context = xmlGzfileOpenW(unescaped, compression);
1843 if (context != NULL) {
1844 ret = xmlAllocOutputBuffer(encoder);
1845 if (ret != NULL) {
1846 ret->context = context;
1847 ret->writecallback = xmlGzfileWrite;
1848 ret->closecallback = xmlGzfileClose;
1849 }
1850 xmlFree(unescaped);
Daniel Veillardf4862f02002-09-10 11:13:43 +00001851 xmlFree(normalized);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001852 return(ret);
1853 }
1854 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001855#endif
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001856 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1857 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
1858 (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) {
1859#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1860 /* Need to pass compression parameter into HTTP open calls */
1861 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1862 context = xmlIOHTTPOpenW(unescaped, compression);
1863 else
1864#endif
1865 context = xmlOutputCallbackTable[i].opencallback(unescaped);
1866 if (context != NULL)
1867 break;
1868 }
1869 }
1870 xmlFree(unescaped);
1871 }
Daniel Veillardf012a642001-07-23 19:10:52 +00001872
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001873 /*
1874 * If this failed try with a non-escaped URI this may be a strange
1875 * filename
1876 */
1877 if (context == NULL) {
1878#ifdef HAVE_ZLIB_H
1879 if ((compression > 0) && (compression <= 9) && (is_http_uri == 0)) {
Daniel Veillardf4862f02002-09-10 11:13:43 +00001880 context = xmlGzfileOpenW(normalized, compression);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001881 if (context != NULL) {
1882 ret = xmlAllocOutputBuffer(encoder);
1883 if (ret != NULL) {
1884 ret->context = context;
1885 ret->writecallback = xmlGzfileWrite;
1886 ret->closecallback = xmlGzfileClose;
1887 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001888 xmlFree(normalized);
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001889 return(ret);
1890 }
1891 }
1892#endif
1893 for (i = xmlOutputCallbackNr - 1;i >= 0;i--) {
1894 if ((xmlOutputCallbackTable[i].matchcallback != NULL) &&
Daniel Veillardf4862f02002-09-10 11:13:43 +00001895 (xmlOutputCallbackTable[i].matchcallback(normalized) != 0)) {
Daniel Veillardecb6f5b2001-08-15 08:47:42 +00001896#if defined(LIBXML_HTTP_ENABLED) && defined(HAVE_ZLIB_H)
1897 /* Need to pass compression parameter into HTTP open calls */
1898 if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch)
1899 context = xmlIOHTTPOpenW(URI, compression);
1900 else
1901#endif
1902 context = xmlOutputCallbackTable[i].opencallback(URI);
1903 if (context != NULL)
1904 break;
1905 }
Owen Taylor3473f882001-02-23 17:55:21 +00001906 }
1907 }
Daniel Veillardf4862f02002-09-10 11:13:43 +00001908 xmlFree(normalized);
Daniel Veillardf012a642001-07-23 19:10:52 +00001909
Owen Taylor3473f882001-02-23 17:55:21 +00001910 if (context == NULL) {
1911 return(NULL);
1912 }
1913
1914 /*
1915 * Allocate the Output buffer front-end.
1916 */
1917 ret = xmlAllocOutputBuffer(encoder);
1918 if (ret != NULL) {
1919 ret->context = context;
1920 ret->writecallback = xmlOutputCallbackTable[i].writecallback;
1921 ret->closecallback = xmlOutputCallbackTable[i].closecallback;
1922 }
1923 return(ret);
1924}
1925
1926/**
1927 * xmlParserInputBufferCreateFile:
1928 * @file: a FILE*
1929 * @enc: the charset encoding if known
1930 *
1931 * Create a buffered parser input for the progressive parsing of a FILE *
1932 * buffered C I/O
1933 *
1934 * Returns the new parser input or NULL
1935 */
1936xmlParserInputBufferPtr
1937xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) {
1938 xmlParserInputBufferPtr ret;
1939
1940 if (xmlInputCallbackInitialized == 0)
1941 xmlRegisterDefaultInputCallbacks();
1942
1943 if (file == NULL) return(NULL);
1944
1945 ret = xmlAllocParserInputBuffer(enc);
1946 if (ret != NULL) {
1947 ret->context = file;
1948 ret->readcallback = xmlFileRead;
1949 ret->closecallback = xmlFileFlush;
1950 }
1951
1952 return(ret);
1953}
1954
1955/**
1956 * xmlOutputBufferCreateFile:
1957 * @file: a FILE*
1958 * @encoder: the encoding converter or NULL
1959 *
1960 * Create a buffered output for the progressive saving to a FILE *
1961 * buffered C I/O
1962 *
1963 * Returns the new parser output or NULL
1964 */
1965xmlOutputBufferPtr
1966xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) {
1967 xmlOutputBufferPtr ret;
1968
1969 if (xmlOutputCallbackInitialized == 0)
1970 xmlRegisterDefaultOutputCallbacks();
1971
1972 if (file == NULL) return(NULL);
1973
1974 ret = xmlAllocOutputBuffer(encoder);
1975 if (ret != NULL) {
1976 ret->context = file;
1977 ret->writecallback = xmlFileWrite;
1978 ret->closecallback = xmlFileFlush;
1979 }
1980
1981 return(ret);
1982}
1983
1984/**
1985 * xmlParserInputBufferCreateFd:
1986 * @fd: a file descriptor number
1987 * @enc: the charset encoding if known
1988 *
1989 * Create a buffered parser input for the progressive parsing for the input
1990 * from a file descriptor
1991 *
1992 * Returns the new parser input or NULL
1993 */
1994xmlParserInputBufferPtr
1995xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) {
1996 xmlParserInputBufferPtr ret;
1997
1998 if (fd < 0) return(NULL);
1999
2000 ret = xmlAllocParserInputBuffer(enc);
2001 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00002002 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00002003 ret->readcallback = xmlFdRead;
2004 ret->closecallback = xmlFdClose;
2005 }
2006
2007 return(ret);
2008}
2009
2010/**
2011 * xmlParserInputBufferCreateMem:
2012 * @mem: the memory input
2013 * @size: the length of the memory block
2014 * @enc: the charset encoding if known
2015 *
2016 * Create a buffered parser input for the progressive parsing for the input
2017 * from a memory area.
2018 *
2019 * Returns the new parser input or NULL
2020 */
2021xmlParserInputBufferPtr
2022xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) {
2023 xmlParserInputBufferPtr ret;
2024
2025 if (size <= 0) return(NULL);
2026 if (mem == NULL) return(NULL);
2027
2028 ret = xmlAllocParserInputBuffer(enc);
2029 if (ret != NULL) {
2030 ret->context = (void *) mem;
2031 ret->readcallback = (xmlInputReadCallback) xmlNop;
2032 ret->closecallback = NULL;
2033 xmlBufferAdd(ret->buffer, (const xmlChar *) mem, size);
2034 }
2035
2036 return(ret);
2037}
2038
2039/**
2040 * xmlOutputBufferCreateFd:
2041 * @fd: a file descriptor number
2042 * @encoder: the encoding converter or NULL
2043 *
2044 * Create a buffered output for the progressive saving
2045 * to a file descriptor
2046 *
2047 * Returns the new parser output or NULL
2048 */
2049xmlOutputBufferPtr
2050xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) {
2051 xmlOutputBufferPtr ret;
2052
2053 if (fd < 0) return(NULL);
2054
2055 ret = xmlAllocOutputBuffer(encoder);
2056 if (ret != NULL) {
Daniel Veillard8fcc4942001-07-17 20:07:33 +00002057 ret->context = (void *) (long) fd;
Owen Taylor3473f882001-02-23 17:55:21 +00002058 ret->writecallback = xmlFdWrite;
Daniel Veillard7db38712002-02-07 16:39:11 +00002059 ret->closecallback = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00002060 }
2061
2062 return(ret);
2063}
2064
2065/**
2066 * xmlParserInputBufferCreateIO:
2067 * @ioread: an I/O read function
2068 * @ioclose: an I/O close function
2069 * @ioctx: an I/O handler
2070 * @enc: the charset encoding if known
2071 *
2072 * Create a buffered parser input for the progressive parsing for the input
2073 * from an I/O handler
2074 *
2075 * Returns the new parser input or NULL
2076 */
2077xmlParserInputBufferPtr
2078xmlParserInputBufferCreateIO(xmlInputReadCallback ioread,
2079 xmlInputCloseCallback ioclose, void *ioctx, xmlCharEncoding enc) {
2080 xmlParserInputBufferPtr ret;
2081
2082 if (ioread == NULL) return(NULL);
2083
2084 ret = xmlAllocParserInputBuffer(enc);
2085 if (ret != NULL) {
2086 ret->context = (void *) ioctx;
2087 ret->readcallback = ioread;
2088 ret->closecallback = ioclose;
2089 }
2090
2091 return(ret);
2092}
2093
2094/**
2095 * xmlOutputBufferCreateIO:
2096 * @iowrite: an I/O write function
2097 * @ioclose: an I/O close function
2098 * @ioctx: an I/O handler
Daniel Veillard9d06d302002-01-22 18:15:52 +00002099 * @encoder: the charset encoding if known
Owen Taylor3473f882001-02-23 17:55:21 +00002100 *
2101 * Create a buffered output for the progressive saving
2102 * to an I/O handler
2103 *
2104 * Returns the new parser output or NULL
2105 */
2106xmlOutputBufferPtr
2107xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite,
2108 xmlOutputCloseCallback ioclose, void *ioctx,
2109 xmlCharEncodingHandlerPtr encoder) {
2110 xmlOutputBufferPtr ret;
2111
2112 if (iowrite == NULL) return(NULL);
2113
2114 ret = xmlAllocOutputBuffer(encoder);
2115 if (ret != NULL) {
2116 ret->context = (void *) ioctx;
2117 ret->writecallback = iowrite;
2118 ret->closecallback = ioclose;
2119 }
2120
2121 return(ret);
2122}
2123
2124/**
2125 * xmlParserInputBufferPush:
2126 * @in: a buffered parser input
2127 * @len: the size in bytes of the array.
2128 * @buf: an char array
2129 *
2130 * Push the content of the arry in the input buffer
2131 * This routine handle the I18N transcoding to internal UTF-8
2132 * This is used when operating the parser in progressive (push) mode.
2133 *
2134 * Returns the number of chars read and stored in the buffer, or -1
2135 * in case of error.
2136 */
2137int
2138xmlParserInputBufferPush(xmlParserInputBufferPtr in,
2139 int len, const char *buf) {
2140 int nbchars = 0;
2141
2142 if (len < 0) return(0);
2143 if (in->encoder != NULL) {
2144 /*
2145 * Store the data in the incoming raw buffer
2146 */
2147 if (in->raw == NULL) {
2148 in->raw = xmlBufferCreate();
2149 }
2150 xmlBufferAdd(in->raw, (const xmlChar *) buf, len);
2151
2152 /*
2153 * convert as much as possible to the parser reading buffer.
2154 */
2155 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2156 if (nbchars < 0) {
2157 xmlGenericError(xmlGenericErrorContext,
2158 "xmlParserInputBufferPush: encoder error\n");
2159 return(-1);
2160 }
2161 } else {
2162 nbchars = len;
2163 xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
2164 }
2165#ifdef DEBUG_INPUT
2166 xmlGenericError(xmlGenericErrorContext,
2167 "I/O: pushed %d chars, buffer %d/%d\n",
2168 nbchars, in->buffer->use, in->buffer->size);
2169#endif
2170 return(nbchars);
2171}
2172
2173/**
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002174 * endOfInput:
2175 *
2176 * When reading from an Input channel indicated end of file or error
2177 * don't reread from it again.
2178 */
2179static int
2180endOfInput (void * context ATTRIBUTE_UNUSED,
2181 char * buffer ATTRIBUTE_UNUSED,
2182 int len ATTRIBUTE_UNUSED) {
2183 return(0);
2184}
2185
2186/**
Owen Taylor3473f882001-02-23 17:55:21 +00002187 * xmlParserInputBufferGrow:
2188 * @in: a buffered parser input
2189 * @len: indicative value of the amount of chars to read
2190 *
2191 * Grow up the content of the input buffer, the old data are preserved
2192 * This routine handle the I18N transcoding to internal UTF-8
2193 * This routine is used when operating the parser in normal (pull) mode
2194 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002195 * TODO: one should be able to remove one extra copy by copying directly
Owen Taylor3473f882001-02-23 17:55:21 +00002196 * onto in->buffer or in->raw
2197 *
2198 * Returns the number of chars read and stored in the buffer, or -1
2199 * in case of error.
2200 */
2201int
2202xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) {
2203 char *buffer = NULL;
2204 int res = 0;
2205 int nbchars = 0;
2206 int buffree;
Daniel Veillard9e412302002-06-10 15:59:44 +00002207 unsigned int needSize;
Owen Taylor3473f882001-02-23 17:55:21 +00002208
2209 if ((len <= MINLEN) && (len != 4))
2210 len = MINLEN;
2211 buffree = in->buffer->size - in->buffer->use;
2212 if (buffree <= 0) {
2213 xmlGenericError(xmlGenericErrorContext,
2214 "xmlParserInputBufferGrow : buffer full !\n");
2215 return(0);
2216 }
2217 if (len > buffree)
2218 len = buffree;
2219
Daniel Veillarde5354492002-05-16 08:43:22 +00002220 needSize = in->buffer->use + len + 1;
2221 if (needSize > in->buffer->size){
2222 if (!xmlBufferResize(in->buffer, needSize)){
2223 xmlGenericError(xmlGenericErrorContext,
2224 "xmlBufferAdd : out of memory!\n");
2225 return(0);
2226 }
Owen Taylor3473f882001-02-23 17:55:21 +00002227 }
Daniel Veillarde5354492002-05-16 08:43:22 +00002228 buffer = (char *)&in->buffer->content[in->buffer->use];
Owen Taylor3473f882001-02-23 17:55:21 +00002229
2230 /*
2231 * Call the read method for this I/O type.
2232 */
2233 if (in->readcallback != NULL) {
2234 res = in->readcallback(in->context, &buffer[0], len);
Daniel Veillardddffd2a2002-03-05 20:28:20 +00002235 if (res <= 0)
2236 in->readcallback = endOfInput;
Owen Taylor3473f882001-02-23 17:55:21 +00002237 } else {
2238 xmlGenericError(xmlGenericErrorContext,
2239 "xmlParserInputBufferGrow : no input !\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002240 return(-1);
2241 }
2242 if (res < 0) {
Owen Taylor3473f882001-02-23 17:55:21 +00002243 return(-1);
2244 }
2245 len = res;
2246 if (in->encoder != NULL) {
2247 /*
2248 * Store the data in the incoming raw buffer
2249 */
2250 if (in->raw == NULL) {
2251 in->raw = xmlBufferCreate();
2252 }
2253 xmlBufferAdd(in->raw, (const xmlChar *) buffer, len);
2254
2255 /*
2256 * convert as much as possible to the parser reading buffer.
2257 */
2258 nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw);
2259 if (nbchars < 0) {
2260 xmlGenericError(xmlGenericErrorContext,
2261 "xmlParserInputBufferGrow: encoder error\n");
2262 return(-1);
2263 }
2264 } else {
2265 nbchars = len;
Daniel Veillarde5354492002-05-16 08:43:22 +00002266 in->buffer->use += nbchars;
2267 buffer[nbchars] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002268 }
2269#ifdef DEBUG_INPUT
2270 xmlGenericError(xmlGenericErrorContext,
2271 "I/O: read %d chars, buffer %d/%d\n",
2272 nbchars, in->buffer->use, in->buffer->size);
2273#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002274 return(nbchars);
2275}
2276
2277/**
2278 * xmlParserInputBufferRead:
2279 * @in: a buffered parser input
2280 * @len: indicative value of the amount of chars to read
2281 *
2282 * Refresh the content of the input buffer, the old data are considered
2283 * consumed
2284 * This routine handle the I18N transcoding to internal UTF-8
2285 *
2286 * Returns the number of chars read and stored in the buffer, or -1
2287 * in case of error.
2288 */
2289int
2290xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) {
2291 /* xmlBufferEmpty(in->buffer); */
2292 if (in->readcallback != NULL)
2293 return(xmlParserInputBufferGrow(in, len));
2294 else
2295 return(-1);
2296}
2297
2298/**
2299 * xmlOutputBufferWrite:
2300 * @out: a buffered parser output
2301 * @len: the size in bytes of the array.
2302 * @buf: an char array
2303 *
2304 * Write the content of the array in the output I/O buffer
2305 * This routine handle the I18N transcoding from internal UTF-8
2306 * The buffer is lossless, i.e. will store in case of partial
2307 * or delayed writes.
2308 *
2309 * Returns the number of chars immediately written, or -1
2310 * in case of error.
2311 */
2312int
2313xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) {
2314 int nbchars = 0; /* number of chars to output to I/O */
2315 int ret; /* return from function call */
2316 int written = 0; /* number of char written to I/O so far */
2317 int chunk; /* number of byte curreent processed from buf */
2318
2319 if (len < 0) return(0);
2320
2321 do {
2322 chunk = len;
2323 if (chunk > 4 * MINLEN)
2324 chunk = 4 * MINLEN;
2325
2326 /*
2327 * first handle encoding stuff.
2328 */
2329 if (out->encoder != NULL) {
2330 /*
2331 * Store the data in the incoming raw buffer
2332 */
2333 if (out->conv == NULL) {
2334 out->conv = xmlBufferCreate();
2335 }
2336 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2337
2338 if ((out->buffer->use < MINLEN) && (chunk == len))
2339 goto done;
2340
2341 /*
2342 * convert as much as possible to the parser reading buffer.
2343 */
2344 ret = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2345 if (ret < 0) {
2346 xmlGenericError(xmlGenericErrorContext,
2347 "xmlOutputBufferWrite: encoder error\n");
2348 return(-1);
2349 }
2350 nbchars = out->conv->use;
2351 } else {
2352 xmlBufferAdd(out->buffer, (const xmlChar *) buf, chunk);
2353 nbchars = out->buffer->use;
2354 }
2355 buf += chunk;
2356 len -= chunk;
2357
2358 if ((nbchars < MINLEN) && (len <= 0))
2359 goto done;
2360
2361 if (out->writecallback) {
2362 /*
2363 * second write the stuff to the I/O channel
2364 */
2365 if (out->encoder != NULL) {
2366 ret = out->writecallback(out->context,
2367 (const char *)out->conv->content, nbchars);
2368 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002369 xmlBufferShrink(out->conv, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002370 } else {
2371 ret = out->writecallback(out->context,
2372 (const char *)out->buffer->content, nbchars);
2373 if (ret >= 0)
Daniel Veillardedddff92001-05-02 10:58:52 +00002374 xmlBufferShrink(out->buffer, ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002375 }
2376 if (ret < 0) {
2377 xmlGenericError(xmlGenericErrorContext,
2378 "I/O: error %d writing %d bytes\n", ret, nbchars);
2379 return(ret);
2380 }
2381 out->written += ret;
2382 }
2383 written += nbchars;
2384 } while (len > 0);
2385
2386done:
2387#ifdef DEBUG_INPUT
2388 xmlGenericError(xmlGenericErrorContext,
2389 "I/O: wrote %d chars\n", written);
2390#endif
2391 return(written);
2392}
2393
2394/**
2395 * xmlOutputBufferWriteString:
2396 * @out: a buffered parser output
2397 * @str: a zero terminated C string
2398 *
2399 * Write the content of the string in the output I/O buffer
2400 * This routine handle the I18N transcoding from internal UTF-8
2401 * The buffer is lossless, i.e. will store in case of partial
2402 * or delayed writes.
2403 *
2404 * Returns the number of chars immediately written, or -1
2405 * in case of error.
2406 */
2407int
2408xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) {
2409 int len;
2410
2411 if (str == NULL)
2412 return(-1);
2413 len = strlen(str);
2414
2415 if (len > 0)
2416 return(xmlOutputBufferWrite(out, len, str));
2417 return(len);
2418}
2419
2420/**
2421 * xmlOutputBufferFlush:
2422 * @out: a buffered output
2423 *
2424 * flushes the output I/O channel
2425 *
2426 * Returns the number of byte written or -1 in case of error.
2427 */
2428int
2429xmlOutputBufferFlush(xmlOutputBufferPtr out) {
2430 int nbchars = 0, ret = 0;
2431
2432 /*
2433 * first handle encoding stuff.
2434 */
2435 if ((out->conv != NULL) && (out->encoder != NULL)) {
2436 /*
2437 * convert as much as possible to the parser reading buffer.
2438 */
2439 nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer);
2440 if (nbchars < 0) {
2441 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002442 "xmlOutputBufferFlush: encoder error\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002443 return(-1);
2444 }
2445 }
2446
2447 /*
2448 * second flush the stuff to the I/O channel
2449 */
2450 if ((out->conv != NULL) && (out->encoder != NULL) &&
2451 (out->writecallback != NULL)) {
2452 ret = out->writecallback(out->context,
2453 (const char *)out->conv->content, out->conv->use);
2454 if (ret >= 0)
2455 xmlBufferShrink(out->conv, ret);
2456 } else if (out->writecallback != NULL) {
2457 ret = out->writecallback(out->context,
2458 (const char *)out->buffer->content, out->buffer->use);
2459 if (ret >= 0)
2460 xmlBufferShrink(out->buffer, ret);
2461 }
2462 if (ret < 0) {
2463 xmlGenericError(xmlGenericErrorContext,
2464 "I/O: error %d flushing %d bytes\n", ret, nbchars);
2465 return(ret);
2466 }
2467 out->written += ret;
2468
2469#ifdef DEBUG_INPUT
2470 xmlGenericError(xmlGenericErrorContext,
2471 "I/O: flushed %d chars\n", ret);
2472#endif
2473 return(ret);
2474}
2475
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002476/**
Owen Taylor3473f882001-02-23 17:55:21 +00002477 * xmlParserGetDirectory:
2478 * @filename: the path to a file
2479 *
2480 * lookup the directory for that file
2481 *
2482 * Returns a new allocated string containing the directory, or NULL.
2483 */
2484char *
2485xmlParserGetDirectory(const char *filename) {
2486 char *ret = NULL;
2487 char dir[1024];
2488 char *cur;
2489 char sep = '/';
2490
Igor Zlatkovic9181cc02002-09-29 17:51:06 +00002491#ifdef _WIN32_WCE /* easy way by now ... wince does not have dirs! */
2492 return NULL;
2493#endif
2494
Owen Taylor3473f882001-02-23 17:55:21 +00002495 if (xmlInputCallbackInitialized == 0)
2496 xmlRegisterDefaultInputCallbacks();
2497
2498 if (filename == NULL) return(NULL);
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002499#if defined(WIN32) && !defined(__CYGWIN__)
Owen Taylor3473f882001-02-23 17:55:21 +00002500 sep = '\\';
2501#endif
2502
2503 strncpy(dir, filename, 1023);
2504 dir[1023] = 0;
2505 cur = &dir[strlen(dir)];
2506 while (cur > dir) {
2507 if (*cur == sep) break;
2508 cur --;
2509 }
2510 if (*cur == sep) {
2511 if (cur == dir) dir[1] = 0;
2512 else *cur = 0;
2513 ret = xmlMemStrdup(dir);
2514 } else {
2515 if (getcwd(dir, 1024) != NULL) {
2516 dir[1023] = 0;
2517 ret = xmlMemStrdup(dir);
2518 }
2519 }
2520 return(ret);
2521}
2522
2523/****************************************************************
2524 * *
2525 * External entities loading *
2526 * *
2527 ****************************************************************/
2528
Daniel Veillard561b7f82002-03-20 21:55:57 +00002529#ifdef LIBXML_CATALOG_ENABLED
2530static int xmlSysIDExists(const char *URL) {
Daniel Veillard6990bf32001-08-23 21:17:48 +00002531#ifdef HAVE_STAT
2532 int ret;
2533 struct stat info;
2534 const char *path;
2535
2536 if (URL == NULL)
2537 return(0);
2538
Daniel Veillardf4862f02002-09-10 11:13:43 +00002539 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2540#if defined (_WIN32) && !defined(__CYGWIN__)
2541 path = &URL[17];
2542#else
Daniel Veillard6990bf32001-08-23 21:17:48 +00002543 path = &URL[16];
Daniel Veillardf4862f02002-09-10 11:13:43 +00002544#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002545 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002546#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard6990bf32001-08-23 21:17:48 +00002547 path = &URL[8];
2548#else
2549 path = &URL[7];
2550#endif
2551 } else
2552 path = URL;
2553 ret = stat(path, &info);
Daniel Veillard561b7f82002-03-20 21:55:57 +00002554 if (ret == 0)
2555 return(1);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002556#endif
Daniel Veillard561b7f82002-03-20 21:55:57 +00002557 return(0);
Daniel Veillard6990bf32001-08-23 21:17:48 +00002558}
Daniel Veillard561b7f82002-03-20 21:55:57 +00002559#endif
Daniel Veillard6990bf32001-08-23 21:17:48 +00002560
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002561/**
Owen Taylor3473f882001-02-23 17:55:21 +00002562 * xmlDefaultExternalEntityLoader:
2563 * @URL: the URL for the entity to load
2564 * @ID: the System ID for the entity to load
2565 * @ctxt: the context in which the entity is called or NULL
2566 *
2567 * By default we don't load external entitites, yet.
2568 *
2569 * Returns a new allocated xmlParserInputPtr, or NULL.
2570 */
2571static
2572xmlParserInputPtr
2573xmlDefaultExternalEntityLoader(const char *URL, const char *ID,
2574 xmlParserCtxtPtr ctxt) {
2575 xmlParserInputPtr ret = NULL;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002576 xmlChar *resource = NULL;
2577#ifdef LIBXML_CATALOG_ENABLED
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002578 xmlCatalogAllow pref;
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002579#endif
Owen Taylor3473f882001-02-23 17:55:21 +00002580
2581#ifdef DEBUG_EXTERNAL_ENTITIES
2582 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00002583 "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL);
Owen Taylor3473f882001-02-23 17:55:21 +00002584#endif
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002585#ifdef LIBXML_CATALOG_ENABLED
2586 /*
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002587 * If the resource doesn't exists as a file,
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002588 * try to load it from the resource pointed in the catalogs
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002589 */
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002590 pref = xmlCatalogGetDefaults();
2591
Daniel Veillard561b7f82002-03-20 21:55:57 +00002592 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlSysIDExists(URL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002593 /*
2594 * Do a local lookup
2595 */
2596 if ((ctxt->catalogs != NULL) &&
2597 ((pref == XML_CATA_ALLOW_ALL) ||
2598 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2599 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2600 (const xmlChar *)ID,
2601 (const xmlChar *)URL);
2602 }
2603 /*
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002604 * Try a global lookup
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002605 */
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002606 if ((resource == NULL) &&
2607 ((pref == XML_CATA_ALLOW_ALL) ||
2608 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002609 resource = xmlCatalogResolve((const xmlChar *)ID,
2610 (const xmlChar *)URL);
2611 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002612 if ((resource == NULL) && (URL != NULL))
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002613 resource = xmlStrdup((const xmlChar *) URL);
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002614
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002615 /*
2616 * TODO: do an URI lookup on the reference
2617 */
Daniel Veillard561b7f82002-03-20 21:55:57 +00002618 if ((resource != NULL) && (!xmlSysIDExists((const char *)resource))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002619 xmlChar *tmp = NULL;
2620
2621 if ((ctxt->catalogs != NULL) &&
2622 ((pref == XML_CATA_ALLOW_ALL) ||
2623 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2624 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2625 }
2626 if ((tmp == NULL) &&
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002627 ((pref == XML_CATA_ALLOW_ALL) ||
2628 (pref == XML_CATA_ALLOW_GLOBAL))) {
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002629 tmp = xmlCatalogResolveURI(resource);
2630 }
2631
2632 if (tmp != NULL) {
2633 xmlFree(resource);
2634 resource = tmp;
2635 }
2636 }
Daniel Veillard5d90b6c2001-08-22 14:29:45 +00002637 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002638#endif
2639
2640 if (resource == NULL)
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002641 resource = (xmlChar *) URL;
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002642
2643 if (resource == NULL) {
Daniel Veillardc6613042002-03-02 09:34:02 +00002644 if (ID == NULL)
2645 ID = "NULL";
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002646 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2647 (ctxt->sax->error != NULL))
2648 ctxt->sax->error(ctxt,
2649 "failed to load external entity \"%s\"\n", ID);
2650 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002651 ctxt->sax->warning(ctxt,
2652 "failed to load external entity \"%s\"\n", ID);
2653 return(NULL);
2654 }
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002655 ret = xmlNewInputFromFile(ctxt, (const char *)resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002656 if (ret == NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002657 if ((ctxt->validate) && (ctxt->sax != NULL) &&
2658 (ctxt->sax->error != NULL))
2659 ctxt->sax->error(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002660 "failed to load external entity \"%s\"\n", resource);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00002661 else if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00002662 ctxt->sax->warning(ctxt,
Daniel Veillard7d6fd212001-05-10 15:34:11 +00002663 "failed to load external entity \"%s\"\n", resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002664 }
Daniel Veillarddc2cee22001-08-22 16:30:37 +00002665 if ((resource != NULL) && (resource != (xmlChar *) URL))
Daniel Veillarde2940dd2001-08-22 00:06:49 +00002666 xmlFree(resource);
Owen Taylor3473f882001-02-23 17:55:21 +00002667 return(ret);
2668}
2669
2670static xmlExternalEntityLoader xmlCurrentExternalEntityLoader =
2671 xmlDefaultExternalEntityLoader;
2672
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002673/**
Owen Taylor3473f882001-02-23 17:55:21 +00002674 * xmlSetExternalEntityLoader:
2675 * @f: the new entity resolver function
2676 *
2677 * Changes the defaultexternal entity resolver function for the application
2678 */
2679void
2680xmlSetExternalEntityLoader(xmlExternalEntityLoader f) {
2681 xmlCurrentExternalEntityLoader = f;
2682}
2683
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002684/**
Owen Taylor3473f882001-02-23 17:55:21 +00002685 * xmlGetExternalEntityLoader:
2686 *
2687 * Get the default external entity resolver function for the application
2688 *
2689 * Returns the xmlExternalEntityLoader function pointer
2690 */
2691xmlExternalEntityLoader
2692xmlGetExternalEntityLoader(void) {
2693 return(xmlCurrentExternalEntityLoader);
2694}
2695
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002696/**
Owen Taylor3473f882001-02-23 17:55:21 +00002697 * xmlLoadExternalEntity:
2698 * @URL: the URL for the entity to load
Daniel Veillard9f7b84b2001-08-23 15:31:19 +00002699 * @ID: the Public ID for the entity to load
Owen Taylor3473f882001-02-23 17:55:21 +00002700 * @ctxt: the context in which the entity is called or NULL
2701 *
2702 * Load an external entity, note that the use of this function for
2703 * unparsed entities may generate problems
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002704 * TODO: a more generic External entity API must be designed
Owen Taylor3473f882001-02-23 17:55:21 +00002705 *
2706 * Returns the xmlParserInputPtr or NULL
2707 */
2708xmlParserInputPtr
2709xmlLoadExternalEntity(const char *URL, const char *ID,
2710 xmlParserCtxtPtr ctxt) {
2711 return(xmlCurrentExternalEntityLoader(URL, ID, ctxt));
2712}
2713
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002714/************************************************************************
2715 * *
2716 * Disabling Network access *
2717 * *
2718 ************************************************************************/
2719
2720#ifdef LIBXML_CATALOG_ENABLED
2721static int
2722xmlNoNetExists(const char *URL)
2723{
2724#ifdef HAVE_STAT
2725 int ret;
2726 struct stat info;
2727 const char *path;
2728
2729 if (URL == NULL)
2730 return (0);
2731
Daniel Veillardf4862f02002-09-10 11:13:43 +00002732 if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17))
2733#if defined (_WIN32) && !defined(__CYGWIN__)
2734 path = &URL[17];
2735#else
2736 path = &URL[16];
2737#endif
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002738 else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) {
Daniel Veillard3c5ed912002-01-08 10:36:16 +00002739#if defined (_WIN32) && !defined(__CYGWIN__)
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002740 path = &URL[8];
2741#else
2742 path = &URL[7];
2743#endif
2744 } else
2745 path = URL;
2746 ret = stat(path, &info);
2747 if (ret == 0)
2748 return (1);
2749#endif
2750 return (0);
2751}
2752#endif
2753
2754/**
2755 * xmlNoNetExternalEntityLoader:
2756 * @URL: the URL for the entity to load
2757 * @ID: the System ID for the entity to load
2758 * @ctxt: the context in which the entity is called or NULL
2759 *
2760 * A specific entity loader disabling network accesses, though still
2761 * allowing local catalog accesses for resolution.
2762 *
2763 * Returns a new allocated xmlParserInputPtr, or NULL.
2764 */
2765xmlParserInputPtr
2766xmlNoNetExternalEntityLoader(const char *URL, const char *ID,
2767 xmlParserCtxtPtr ctxt) {
2768 xmlParserInputPtr input = NULL;
2769 xmlChar *resource = NULL;
2770
2771#ifdef LIBXML_CATALOG_ENABLED
2772 xmlCatalogAllow pref;
2773
2774 /*
2775 * If the resource doesn't exists as a file,
2776 * try to load it from the resource pointed in the catalogs
2777 */
2778 pref = xmlCatalogGetDefaults();
2779
2780 if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) {
2781 /*
2782 * Do a local lookup
2783 */
2784 if ((ctxt->catalogs != NULL) &&
2785 ((pref == XML_CATA_ALLOW_ALL) ||
2786 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2787 resource = xmlCatalogLocalResolve(ctxt->catalogs,
2788 (const xmlChar *)ID,
2789 (const xmlChar *)URL);
2790 }
2791 /*
2792 * Try a global lookup
2793 */
2794 if ((resource == NULL) &&
2795 ((pref == XML_CATA_ALLOW_ALL) ||
2796 (pref == XML_CATA_ALLOW_GLOBAL))) {
2797 resource = xmlCatalogResolve((const xmlChar *)ID,
2798 (const xmlChar *)URL);
2799 }
2800 if ((resource == NULL) && (URL != NULL))
2801 resource = xmlStrdup((const xmlChar *) URL);
2802
2803 /*
2804 * TODO: do an URI lookup on the reference
2805 */
2806 if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) {
2807 xmlChar *tmp = NULL;
2808
2809 if ((ctxt->catalogs != NULL) &&
2810 ((pref == XML_CATA_ALLOW_ALL) ||
2811 (pref == XML_CATA_ALLOW_DOCUMENT))) {
2812 tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource);
2813 }
2814 if ((tmp == NULL) &&
2815 ((pref == XML_CATA_ALLOW_ALL) ||
2816 (pref == XML_CATA_ALLOW_GLOBAL))) {
2817 tmp = xmlCatalogResolveURI(resource);
2818 }
2819
2820 if (tmp != NULL) {
2821 xmlFree(resource);
2822 resource = tmp;
2823 }
2824 }
2825 }
2826#endif
2827 if (resource == NULL)
2828 resource = (xmlChar *) URL;
2829
2830 if (resource != NULL) {
Aleksey Sanin5aac8b82002-05-01 18:32:28 +00002831 if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) ||
2832 (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) {
Daniel Veillard8bdb91d2001-10-31 17:52:43 +00002833 xmlGenericError(xmlGenericErrorContext,
2834 "Attempt to load network entity %s \n", resource);
2835
2836 if (resource != (xmlChar *) URL)
2837 xmlFree(resource);
2838 return(NULL);
2839 }
2840 }
2841 input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt);
2842 if (resource != (xmlChar *) URL)
2843 xmlFree(resource);
2844 return(input);
2845}
2846