| /* |
| * xmlIO.c : implementation of the I/O interfaces used by the parser |
| * |
| * See Copyright for the status of this software. |
| * |
| * Daniel.Veillard@w3.org |
| */ |
| |
| #ifdef WIN32 |
| #include "win32config.h" |
| #else |
| #include "config.h" |
| #endif |
| |
| #include <stdio.h> |
| #include <string.h> |
| |
| #ifdef HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| #ifdef HAVE_SYS_STAT_H |
| #include <sys/stat.h> |
| #endif |
| #ifdef HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #ifdef HAVE_ZLIB_H |
| #include <zlib.h> |
| #endif |
| |
| #include <libxml/xmlmemory.h> |
| #include <libxml/parser.h> |
| #include <libxml/parserInternals.h> |
| #include <libxml/xmlIO.h> |
| #include <libxml/nanohttp.h> |
| #include <libxml/nanoftp.h> |
| |
| /* #define VERBOSE_FAILURE */ |
| /* #define DEBUG_EXTERNAL_ENTITIES */ |
| /* #define DEBUG_INPUT */ |
| |
| #ifdef DEBUG_INPUT |
| #define MINLEN 40 |
| #else |
| #define MINLEN 4000 |
| #endif |
| |
| /* |
| * Input I/O callback sets |
| */ |
| typedef struct _xmlInputCallback { |
| xmlInputMatchCallback matchcallback; |
| xmlInputOpenCallback opencallback; |
| xmlInputReadCallback readcallback; |
| xmlInputCloseCallback closecallback; |
| } xmlInputCallback; |
| |
| #define MAX_INPUT_CALLBACK 15 |
| |
| xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK]; |
| int xmlInputCallbackNr = 0; |
| int xmlInputCallbackInitialized = 0; |
| |
| /* |
| * Output I/O callback sets |
| */ |
| typedef struct _xmlOutputCallback { |
| xmlOutputMatchCallback matchcallback; |
| xmlOutputOpenCallback opencallback; |
| xmlOutputWriteCallback writecallback; |
| xmlOutputCloseCallback closecallback; |
| } xmlOutputCallback; |
| |
| #define MAX_OUTPUT_CALLBACK 15 |
| |
| xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK]; |
| int xmlOutputCallbackNr = 0; |
| int xmlOutputCallbackInitialized = 0; |
| |
| /************************************************************************ |
| * * |
| * Standard I/O for file accesses * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xmlFdMatch: |
| * @filename: the URI for matching |
| * |
| * input from file descriptor |
| * |
| * Returns 1 if matches, 0 otherwise |
| */ |
| int |
| xmlFdMatch (const char *filename) { |
| return(1); |
| } |
| |
| /** |
| * xmlFdOpen: |
| * @filename: the URI for matching |
| * |
| * input from file descriptor, supports compressed input |
| * if @filename is " " then the standard input is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlFdOpen (const char *filename) { |
| const char *path = NULL; |
| int fd; |
| |
| if (!strcmp(filename, "-")) { |
| fd = 0; |
| return((void *) fd); |
| } |
| |
| if (!strncmp(filename, "file://localhost", 16)) |
| path = &filename[16]; |
| else if (!strncmp(filename, "file:///", 8)) |
| path = &filename[8]; |
| else if (filename[0] == '/') |
| path = filename; |
| if (path == NULL) |
| return(NULL); |
| |
| #ifdef WIN32 |
| fd = _open (filename, O_RDONLY | _O_BINARY); |
| #else |
| fd = open (filename, O_RDONLY); |
| #endif |
| |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlFdOpenW: |
| * @filename: the URI for matching |
| * |
| * input from file descriptor, |
| * if @filename is "-" then the standard output is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlFdOpenW (const char *filename) { |
| const char *path = NULL; |
| int fd; |
| |
| if (!strcmp(filename, "-")) { |
| fd = 1; |
| return((void *) fd); |
| } |
| |
| if (!strncmp(filename, "file://localhost", 16)) |
| path = &filename[16]; |
| else if (!strncmp(filename, "file:///", 8)) |
| path = &filename[8]; |
| else if (filename[0] == '/') |
| path = filename; |
| if (path == NULL) |
| return(NULL); |
| |
| fd = open (filename, O_WRONLY); |
| |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlFdRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to read |
| * |
| * Read @len bytes to @buffer from the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlFdRead (void * context, char * buffer, int len) { |
| return(read((int) context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlFdWrite: |
| * @context: the I/O context |
| * @buffer: where to get data |
| * @len: number of bytes to write |
| * |
| * Write @len bytes from @buffer to the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlFdWrite (void * context, const char * buffer, int len) { |
| return(write((int) context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlFdClose: |
| * @context: the I/O context |
| * |
| * Close an I/O channel |
| */ |
| void |
| xmlFdClose (void * context) { |
| close((int) context); |
| } |
| |
| /** |
| * xmlFileMatch: |
| * @filename: the URI for matching |
| * |
| * input from FILE * |
| * |
| * Returns 1 if matches, 0 otherwise |
| */ |
| int |
| xmlFileMatch (const char *filename) { |
| return(1); |
| } |
| |
| /** |
| * xmlFileOpen: |
| * @filename: the URI for matching |
| * |
| * input from FILE *, supports compressed input |
| * if @filename is " " then the standard input is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlFileOpen (const char *filename) { |
| const char *path = NULL; |
| FILE *fd; |
| |
| if (!strcmp(filename, "-")) { |
| fd = stdin; |
| return((void *) fd); |
| } |
| |
| if (!strncmp(filename, "file://localhost", 16)) |
| path = &filename[16]; |
| else if (!strncmp(filename, "file:///", 8)) |
| path = &filename[8]; |
| else |
| path = filename; |
| if (path == NULL) |
| return(NULL); |
| |
| #ifdef WIN32 |
| fd = fopen(path, "rb"); |
| #else |
| fd = fopen(path, "r"); |
| #endif /* WIN32 */ |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlFileOpenW: |
| * @filename: the URI for matching |
| * |
| * output to from FILE *, |
| * if @filename is "-" then the standard output is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlFileOpenW (const char *filename) { |
| const char *path = NULL; |
| FILE *fd; |
| |
| if (!strcmp(filename, "-")) { |
| fd = stdout; |
| return((void *) fd); |
| } |
| |
| if (!strncmp(filename, "file://localhost", 16)) |
| path = &filename[16]; |
| else if (!strncmp(filename, "file:///", 8)) |
| path = &filename[8]; |
| else |
| path = filename; |
| if (path == NULL) |
| return(NULL); |
| |
| fd = fopen(path, "w"); |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlFileRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Read @len bytes to @buffer from the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlFileRead (void * context, char * buffer, int len) { |
| return(fread(&buffer[0], 1, len, (FILE *) context)); |
| } |
| |
| /** |
| * xmlFileWrite: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Write @len bytes from @buffer to the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlFileWrite (void * context, const char * buffer, int len) { |
| return(fwrite(&buffer[0], 1, len, (FILE *) context)); |
| } |
| |
| /** |
| * xmlFileClose: |
| * @context: the I/O context |
| * |
| * Close an I/O channel |
| */ |
| void |
| xmlFileClose (void * context) { |
| fclose((FILE *) context); |
| } |
| |
| #ifdef HAVE_ZLIB_H |
| /************************************************************************ |
| * * |
| * I/O for compressed file accesses * |
| * * |
| ************************************************************************/ |
| /** |
| * xmlGzfileMatch: |
| * @filename: the URI for matching |
| * |
| * input from compressed file test |
| * |
| * Returns 1 if matches, 0 otherwise |
| */ |
| int |
| xmlGzfileMatch (const char *filename) { |
| return(1); |
| } |
| |
| /** |
| * xmlGzfileOpen: |
| * @filename: the URI for matching |
| * |
| * input from compressed file open |
| * if @filename is " " then the standard input is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlGzfileOpen (const char *filename) { |
| const char *path = NULL; |
| gzFile fd; |
| |
| if (!strcmp(filename, "-")) { |
| fd = gzdopen (fileno(stdin), "r"); |
| return((void *) fd); |
| } |
| |
| if (!strncmp(filename, "file://localhost", 16)) |
| path = &filename[16]; |
| else if (!strncmp(filename, "file:///", 8)) |
| path = &filename[8]; |
| else |
| path = filename; |
| |
| fd = gzopen(filename, "r"); |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlGzfileOpenW: |
| * @filename: the URI for matching |
| * @compression: the compression factor (0 - 9 included) |
| * |
| * input from compressed file open |
| * if @filename is " " then the standard input is used |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlGzfileOpenW (const char *filename, int compression) { |
| const char *path = NULL; |
| char mode[15]; |
| gzFile fd; |
| |
| sprintf(mode, "w%d", compression); |
| if (!strcmp(filename, "-")) { |
| fd = gzdopen(1, mode); |
| return((void *) fd); |
| } |
| |
| if (!strncmp(filename, "file://localhost", 16)) |
| path = &filename[16]; |
| else if (!strncmp(filename, "file:///", 8)) |
| path = &filename[8]; |
| else |
| path = filename; |
| |
| fd = gzopen(filename, mode); |
| return((void *) fd); |
| } |
| |
| /** |
| * xmlGzfileRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Read @len bytes to @buffer from the compressed I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlGzfileRead (void * context, char * buffer, int len) { |
| return(gzread((gzFile) context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlGzfileWrite: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Write @len bytes from @buffer to the compressed I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlGzfileWrite (void * context, const char * buffer, int len) { |
| return(gzwrite((gzFile) context, (char *) &buffer[0], len)); |
| } |
| |
| /** |
| * xmlGzfileClose: |
| * @context: the I/O context |
| * |
| * Close a compressed I/O channel |
| */ |
| void |
| xmlGzfileClose (void * context) { |
| gzclose((gzFile) context); |
| } |
| #endif /* HAVE_ZLIB_H */ |
| |
| #ifdef LIBXML_HTTP_ENABLED |
| /************************************************************************ |
| * * |
| * I/O for HTTP file accesses * |
| * * |
| ************************************************************************/ |
| /** |
| * xmlIOHTTPMatch: |
| * @filename: the URI for matching |
| * |
| * check if the URI matches an HTTP one |
| * |
| * Returns 1 if matches, 0 otherwise |
| */ |
| int |
| xmlIOHTTPMatch (const char *filename) { |
| if (!strncmp(filename, "http://", 7)) |
| return(1); |
| return(0); |
| } |
| |
| /** |
| * xmlIOHTTPOpen: |
| * @filename: the URI for matching |
| * |
| * open an HTTP I/O channel |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlIOHTTPOpen (const char *filename) { |
| return(xmlNanoHTTPOpen(filename, NULL)); |
| } |
| |
| /** |
| * xmlIOHTTPRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Read @len bytes to @buffer from the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlIOHTTPRead(void * context, char * buffer, int len) { |
| return(xmlNanoHTTPRead(context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlIOHTTPClose: |
| * @context: the I/O context |
| * |
| * Close an HTTP I/O channel |
| */ |
| void |
| xmlIOHTTPClose (void * context) { |
| xmlNanoHTTPClose(context); |
| } |
| #endif /* LIBXML_HTTP_ENABLED */ |
| |
| #ifdef LIBXML_FTP_ENABLED |
| /************************************************************************ |
| * * |
| * I/O for FTP file accesses * |
| * * |
| ************************************************************************/ |
| /** |
| * xmlIOFTPMatch: |
| * @filename: the URI for matching |
| * |
| * check if the URI matches an FTP one |
| * |
| * Returns 1 if matches, 0 otherwise |
| */ |
| int |
| xmlIOFTPMatch (const char *filename) { |
| if (!strncmp(filename, "ftp://", 6)) |
| return(1); |
| return(0); |
| } |
| |
| /** |
| * xmlIOFTPOpen: |
| * @filename: the URI for matching |
| * |
| * open an FTP I/O channel |
| * |
| * Returns an I/O context or NULL in case of error |
| */ |
| void * |
| xmlIOFTPOpen (const char *filename) { |
| return(xmlNanoFTPOpen(filename)); |
| } |
| |
| /** |
| * xmlIOFTPRead: |
| * @context: the I/O context |
| * @buffer: where to drop data |
| * @len: number of bytes to write |
| * |
| * Read @len bytes to @buffer from the I/O channel. |
| * |
| * Returns the number of bytes written |
| */ |
| int |
| xmlIOFTPRead(void * context, char * buffer, int len) { |
| return(xmlNanoFTPRead(context, &buffer[0], len)); |
| } |
| |
| /** |
| * xmlIOFTPClose: |
| * @context: the I/O context |
| * |
| * Close an FTP I/O channel |
| */ |
| void |
| xmlIOFTPClose (void * context) { |
| xmlNanoFTPClose(context); |
| } |
| #endif /* LIBXML_FTP_ENABLED */ |
| |
| |
| /** |
| * xmlRegisterInputCallbacks: |
| * @match: the xmlInputMatchCallback |
| * @open: the xmlInputOpenCallback |
| * @read: the xmlInputReadCallback |
| * @close: the xmlInputCloseCallback |
| * |
| * Register a new set of I/O callback for handling parser input. |
| * |
| * Returns the registered handler number or -1 in case of error |
| */ |
| int |
| xmlRegisterInputCallbacks(xmlInputMatchCallback match, |
| xmlInputOpenCallback open, xmlInputReadCallback read, |
| xmlInputCloseCallback close) { |
| if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) { |
| return(-1); |
| } |
| xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = match; |
| xmlInputCallbackTable[xmlInputCallbackNr].opencallback = open; |
| xmlInputCallbackTable[xmlInputCallbackNr].readcallback = read; |
| xmlInputCallbackTable[xmlInputCallbackNr].closecallback = close; |
| return(xmlInputCallbackNr++); |
| } |
| |
| /** |
| * xmlRegisterOutputCallbacks: |
| * @match: the xmlOutputMatchCallback |
| * @open: the xmlOutputOpenCallback |
| * @write: the xmlOutputWriteCallback |
| * @close: the xmlOutputCloseCallback |
| * |
| * Register a new set of I/O callback for handling output. |
| * |
| * Returns the registered handler number or -1 in case of error |
| */ |
| int |
| xmlRegisterOutputCallbacks(xmlOutputMatchCallback match, |
| xmlOutputOpenCallback open, xmlOutputWriteCallback write, |
| xmlOutputCloseCallback close) { |
| if (xmlOutputCallbackNr >= MAX_INPUT_CALLBACK) { |
| return(-1); |
| } |
| xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = match; |
| xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = open; |
| xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = write; |
| xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = close; |
| return(xmlOutputCallbackNr++); |
| } |
| |
| /** |
| * xmlRegisterDefaultInputCallbacks: |
| * |
| * Registers the default compiled-in I/O handlers. |
| */ |
| void |
| xmlRegisterDefaultInputCallbacks(void) { |
| xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen, |
| xmlFileRead, xmlFileClose); |
| #ifdef HAVE_ZLIB_H |
| xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen, |
| xmlGzfileRead, xmlGzfileClose); |
| #endif /* HAVE_ZLIB_H */ |
| |
| #ifdef LIBXML_HTTP_ENABLED |
| xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen, |
| xmlIOHTTPRead, xmlIOHTTPClose); |
| #endif /* LIBXML_HTTP_ENABLED */ |
| |
| #ifdef LIBXML_FTP_ENABLED |
| xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen, |
| xmlIOFTPRead, xmlIOFTPClose); |
| #endif /* LIBXML_FTP_ENABLED */ |
| } |
| |
| /** |
| * xmlRegisterDefaultOutputCallbacks: |
| * |
| * Registers the default compiled-in I/O handlers. |
| */ |
| void |
| xmlRegisterDefaultOutputCallbacks(void) { |
| xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW, |
| xmlFileWrite, xmlFileClose); |
| /********************************* |
| No way a-priori to distinguish between gzipped files from |
| uncompressed ones except opening if existing then closing |
| and saving with same compression ratio ... a pain. |
| |
| #ifdef HAVE_ZLIB_H |
| xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen, |
| xmlGzfileWrite, xmlGzfileClose); |
| #endif |
| No HTTP PUT support yet, patches welcome |
| |
| #ifdef LIBXML_HTTP_ENABLED |
| xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen, |
| xmlIOHTTPWrite, xmlIOHTTPClose); |
| #endif |
| |
| Nor FTP PUT .... |
| #ifdef LIBXML_FTP_ENABLED |
| xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen, |
| xmlIOFTPWrite, xmlIOFTPClose); |
| #endif |
| **********************************/ |
| } |
| |
| /** |
| * xmlAllocParserInputBuffer: |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for progressive parsing |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlAllocParserInputBuffer(xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| ret = (xmlParserInputBufferPtr) xmlMalloc(sizeof(xmlParserInputBuffer)); |
| if (ret == NULL) { |
| fprintf(stderr, "xmlAllocParserInputBuffer : out of memory!\n"); |
| return(NULL); |
| } |
| memset(ret, 0, (size_t) sizeof(xmlParserInputBuffer)); |
| ret->buffer = xmlBufferCreate(); |
| if (ret->buffer == NULL) { |
| xmlFree(ret); |
| return(NULL); |
| } |
| ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT; |
| ret->encoder = xmlGetCharEncodingHandler(enc); |
| if (ret->encoder != NULL) |
| ret->raw = xmlBufferCreate(); |
| else |
| ret->raw = NULL; |
| ret->readcallback = NULL; |
| ret->closecallback = NULL; |
| ret->context = NULL; |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlAllocOutputBuffer: |
| * @encoder: the encoding converter or NULL |
| * |
| * Create a buffered parser output |
| * |
| * Returns the new parser output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) { |
| xmlOutputBufferPtr ret; |
| |
| ret = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer)); |
| if (ret == NULL) { |
| fprintf(stderr, "xmlAllocOutputBuffer : out of memory!\n"); |
| return(NULL); |
| } |
| memset(ret, 0, (size_t) sizeof(xmlOutputBuffer)); |
| ret->buffer = xmlBufferCreate(); |
| if (ret->buffer == NULL) { |
| xmlFree(ret); |
| return(NULL); |
| } |
| ret->buffer->alloc = XML_BUFFER_ALLOC_DOUBLEIT; |
| ret->encoder = encoder; |
| if (encoder != NULL) { |
| ret->conv = xmlBufferCreateSize(4000); |
| /* |
| * This call is designed to initiate the encoder state |
| */ |
| xmlCharEncOutFunc(encoder, ret->conv, NULL); |
| } else |
| ret->conv = NULL; |
| ret->writecallback = NULL; |
| ret->closecallback = NULL; |
| ret->context = NULL; |
| ret->written = 0; |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlFreeParserInputBuffer: |
| * @in: a buffered parser input |
| * |
| * Free up the memory used by a buffered parser input |
| */ |
| void |
| xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) { |
| if (in->raw) { |
| xmlBufferFree(in->raw); |
| in->raw = NULL; |
| } |
| if (in->encoder != NULL) { |
| xmlCharEncCloseFunc(in->encoder); |
| } |
| if (in->closecallback != NULL) { |
| in->closecallback(in->context); |
| } |
| if (in->buffer != NULL) { |
| xmlBufferFree(in->buffer); |
| in->buffer = NULL; |
| } |
| |
| memset(in, 0xbe, (size_t) sizeof(xmlParserInputBuffer)); |
| xmlFree(in); |
| } |
| |
| /** |
| * xmlOutputBufferClose: |
| * @out: a buffered output |
| * |
| * flushes and close the output I/O channel |
| * and free up all the associated resources |
| * |
| * Returns the number of byte written or -1 in case of error. |
| */ |
| int |
| xmlOutputBufferClose(xmlOutputBufferPtr out) { |
| int written; |
| |
| if (out == NULL) |
| return(-1); |
| xmlOutputBufferFlush(out); |
| if (out->closecallback != NULL) { |
| out->closecallback(out->context); |
| } |
| written = out->written; |
| if (out->conv) { |
| xmlBufferFree(out->conv); |
| out->conv = NULL; |
| } |
| if (out->encoder != NULL) { |
| xmlCharEncCloseFunc(out->encoder); |
| } |
| if (out->buffer != NULL) { |
| xmlBufferFree(out->buffer); |
| out->buffer = NULL; |
| } |
| |
| memset(out, 0xbe, (size_t) sizeof(xmlOutputBuffer)); |
| xmlFree(out); |
| return(written); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateFilename: |
| * @URI: a C string containing the URI or filename |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing of a file |
| * If filename is "-' then we use stdin as the input. |
| * Automatic support for ZLIB/Compress compressed document is provided |
| * by default if found at compile-time. |
| * Do an encoding check if enc == XML_CHAR_ENCODING_NONE |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| int i; |
| void *context = NULL; |
| |
| if (xmlInputCallbackInitialized == 0) |
| xmlRegisterDefaultInputCallbacks(); |
| |
| if (URI == NULL) return(NULL); |
| |
| /* |
| * Try to find one of the input accept method accepting taht scheme |
| * Go in reverse to give precedence to user defined handlers. |
| */ |
| for (i = xmlInputCallbackNr - 1;i >= 0;i--) { |
| if ((xmlInputCallbackTable[i].matchcallback != NULL) && |
| (xmlInputCallbackTable[i].matchcallback(URI) != 0)) { |
| context = xmlInputCallbackTable[i].opencallback(URI); |
| if (context != NULL) |
| break; |
| } |
| } |
| if (context == NULL) { |
| #ifdef DEBUG_INPUT |
| fprintf(stderr, "No input filter matching \"%s\"\n", URI); |
| #endif |
| return(NULL); |
| } |
| |
| /* |
| * Allocate the Input buffer front-end. |
| */ |
| ret = xmlAllocParserInputBuffer(enc); |
| if (ret != NULL) { |
| ret->context = context; |
| ret->readcallback = xmlInputCallbackTable[i].readcallback; |
| ret->closecallback = xmlInputCallbackTable[i].closecallback; |
| } |
| return(ret); |
| } |
| |
| /** |
| * xmlOutputBufferCreateFilename: |
| * @URI: a C string containing the URI or filename |
| * @encoder: the encoding converter or NULL |
| * @compression: the compression ration (0 none, 9 max). |
| * |
| * Create a buffered output for the progressive saving of a file |
| * If filename is "-' then we use stdout as the output. |
| * Automatic support for ZLIB/Compress compressed document is provided |
| * by default if found at compile-time. |
| * TODO: currently if compression is set, the library only support |
| * writing to a local file. |
| * |
| * Returns the new output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlOutputBufferCreateFilename(const char *URI, |
| xmlCharEncodingHandlerPtr encoder, |
| int compression) { |
| xmlOutputBufferPtr ret; |
| int i; |
| void *context = NULL; |
| |
| if (xmlOutputCallbackInitialized == 0) |
| xmlRegisterDefaultOutputCallbacks(); |
| |
| if (URI == NULL) return(NULL); |
| |
| #ifdef HAVE_ZLIB_H |
| if ((compression > 0) && (compression <= 9)) { |
| context = xmlGzfileOpenW(URI, compression); |
| if (context != NULL) { |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = context; |
| ret->writecallback = xmlGzfileWrite; |
| ret->closecallback = xmlGzfileClose; |
| } |
| return(ret); |
| } |
| } |
| #endif |
| |
| /* |
| * Try to find one of the output accept method accepting taht scheme |
| * Go in reverse to give precedence to user defined handlers. |
| */ |
| for (i = xmlOutputCallbackNr - 1;i >= 0;i--) { |
| if ((xmlOutputCallbackTable[i].matchcallback != NULL) && |
| (xmlOutputCallbackTable[i].matchcallback(URI) != 0)) { |
| context = xmlOutputCallbackTable[i].opencallback(URI); |
| if (context != NULL) |
| break; |
| } |
| } |
| if (context == NULL) { |
| #ifdef DEBUG_INPUT |
| fprintf(stderr, "No output filter matching \"%s\"\n", URI); |
| #endif |
| return(NULL); |
| } |
| |
| /* |
| * Allocate the Output buffer front-end. |
| */ |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = context; |
| ret->writecallback = xmlOutputCallbackTable[i].writecallback; |
| ret->closecallback = xmlOutputCallbackTable[i].closecallback; |
| } |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateFile: |
| * @file: a FILE* |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing of a FILE * |
| * buffered C I/O |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| if (xmlInputCallbackInitialized == 0) |
| xmlRegisterDefaultInputCallbacks(); |
| |
| if (file == NULL) return(NULL); |
| |
| ret = xmlAllocParserInputBuffer(enc); |
| if (ret != NULL) { |
| ret->context = file; |
| ret->readcallback = xmlFileRead; |
| ret->closecallback = xmlFileClose; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlOutputBufferCreateFile: |
| * @file: a FILE* |
| * @encoder: the encoding converter or NULL |
| * |
| * Create a buffered output for the progressive saving to a FILE * |
| * buffered C I/O |
| * |
| * Returns the new parser output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) { |
| xmlOutputBufferPtr ret; |
| |
| if (xmlOutputCallbackInitialized == 0) |
| xmlRegisterDefaultOutputCallbacks(); |
| |
| if (file == NULL) return(NULL); |
| |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = file; |
| ret->writecallback = xmlFileWrite; |
| ret->closecallback = xmlFileClose; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateFd: |
| * @fd: a file descriptor number |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing for the input |
| * from a file descriptor |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| if (fd < 0) return(NULL); |
| |
| ret = xmlAllocParserInputBuffer(enc); |
| if (ret != NULL) { |
| ret->context = (void *) fd; |
| ret->readcallback = xmlFdRead; |
| ret->closecallback = xmlFdClose; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlOutputBufferCreateFd: |
| * @fd: a file descriptor number |
| * @encoder: the encoding converter or NULL |
| * |
| * Create a buffered output for the progressive saving |
| * to a file descriptor |
| * |
| * Returns the new parser output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) { |
| xmlOutputBufferPtr ret; |
| |
| if (fd < 0) return(NULL); |
| |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = (void *) fd; |
| ret->writecallback = xmlFdWrite; |
| ret->closecallback = xmlFdClose; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferCreateIO: |
| * @ioread: an I/O read function |
| * @ioclose: an I/O close function |
| * @ioctx: an I/O handler |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered parser input for the progressive parsing for the input |
| * from an I/O handler |
| * |
| * Returns the new parser input or NULL |
| */ |
| xmlParserInputBufferPtr |
| xmlParserInputBufferCreateIO(xmlInputReadCallback ioread, |
| xmlInputCloseCallback ioclose, void *ioctx, xmlCharEncoding enc) { |
| xmlParserInputBufferPtr ret; |
| |
| if (ioread == NULL) return(NULL); |
| |
| ret = xmlAllocParserInputBuffer(enc); |
| if (ret != NULL) { |
| ret->context = (void *) ioctx; |
| ret->readcallback = ioread; |
| ret->closecallback = ioclose; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlOutputBufferCreateIO: |
| * @iowrite: an I/O write function |
| * @ioclose: an I/O close function |
| * @ioctx: an I/O handler |
| * @enc: the charset encoding if known |
| * |
| * Create a buffered output for the progressive saving |
| * to an I/O handler |
| * |
| * Returns the new parser output or NULL |
| */ |
| xmlOutputBufferPtr |
| xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite, |
| xmlOutputCloseCallback ioclose, void *ioctx, |
| xmlCharEncodingHandlerPtr encoder) { |
| xmlOutputBufferPtr ret; |
| |
| if (iowrite == NULL) return(NULL); |
| |
| ret = xmlAllocOutputBuffer(encoder); |
| if (ret != NULL) { |
| ret->context = (void *) ioctx; |
| ret->writecallback = iowrite; |
| ret->closecallback = ioclose; |
| } |
| |
| return(ret); |
| } |
| |
| /** |
| * xmlParserInputBufferPush: |
| * @in: a buffered parser input |
| * @len: the size in bytes of the array. |
| * @buf: an char array |
| * |
| * Push the content of the arry in the input buffer |
| * This routine handle the I18N transcoding to internal UTF-8 |
| * This is used when operating the parser in progressive (push) mode. |
| * |
| * Returns the number of chars read and stored in the buffer, or -1 |
| * in case of error. |
| */ |
| int |
| xmlParserInputBufferPush(xmlParserInputBufferPtr in, int len, const char *buf) { |
| int nbchars = 0; |
| |
| if (len < 0) return(0); |
| if (in->encoder != NULL) { |
| /* |
| * Store the data in the incoming raw buffer |
| */ |
| if (in->raw == NULL) { |
| in->raw = xmlBufferCreate(); |
| } |
| xmlBufferAdd(in->raw, (const xmlChar *) buf, len); |
| |
| /* |
| * convert as much as possible to the parser reading buffer. |
| */ |
| nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw); |
| if (nbchars < 0) { |
| fprintf(stderr, "xmlParserInputBufferPush: encoder error\n"); |
| return(-1); |
| } |
| } else { |
| nbchars = len; |
| xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars); |
| } |
| #ifdef DEBUG_INPUT |
| fprintf(stderr, "I/O: pushed %d chars, buffer %d/%d\n", |
| nbchars, in->buffer->use, in->buffer->size); |
| #endif |
| return(nbchars); |
| } |
| |
| /** |
| * xmlParserInputBufferGrow: |
| * @in: a buffered parser input |
| * @len: indicative value of the amount of chars to read |
| * |
| * Grow up the content of the input buffer, the old data are preserved |
| * This routine handle the I18N transcoding to internal UTF-8 |
| * This routine is used when operating the parser in normal (pull) mode |
| * |
| * TODO: one should be able to remove one extra copy by copying directy |
| * onto in->buffer or in->raw |
| * |
| * Returns the number of chars read and stored in the buffer, or -1 |
| * in case of error. |
| */ |
| int |
| xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) { |
| char *buffer = NULL; |
| int res = 0; |
| int nbchars = 0; |
| int buffree; |
| |
| if ((len <= MINLEN) && (len != 4)) |
| len = MINLEN; |
| buffree = in->buffer->size - in->buffer->use; |
| if (buffree <= 0) { |
| fprintf(stderr, "xmlParserInputBufferGrow : buffer full !\n"); |
| return(0); |
| } |
| if (len > buffree) |
| len = buffree; |
| |
| buffer = xmlMalloc((len + 1) * sizeof(char)); |
| if (buffer == NULL) { |
| fprintf(stderr, "xmlParserInputBufferGrow : out of memory !\n"); |
| return(-1); |
| } |
| |
| /* |
| * Call the read method for this I/O type. |
| */ |
| if (in->readcallback != NULL) { |
| res = in->readcallback(in->context, &buffer[0], len); |
| } else { |
| fprintf(stderr, "xmlParserInputBufferGrow : no input !\n"); |
| xmlFree(buffer); |
| return(-1); |
| } |
| if (res < 0) { |
| perror ("read error"); |
| xmlFree(buffer); |
| return(-1); |
| } |
| len = res; |
| if (in->encoder != NULL) { |
| /* |
| * Store the data in the incoming raw buffer |
| */ |
| if (in->raw == NULL) { |
| in->raw = xmlBufferCreate(); |
| } |
| xmlBufferAdd(in->raw, (const xmlChar *) buffer, len); |
| |
| /* |
| * convert as much as possible to the parser reading buffer. |
| */ |
| nbchars = xmlCharEncInFunc(in->encoder, in->buffer, in->raw); |
| if (nbchars < 0) { |
| fprintf(stderr, "xmlParserInputBufferGrow: encoder error\n"); |
| return(-1); |
| } |
| } else { |
| nbchars = len; |
| buffer[nbchars] = 0; |
| xmlBufferAdd(in->buffer, (xmlChar *) buffer, nbchars); |
| } |
| #ifdef DEBUG_INPUT |
| fprintf(stderr, "I/O: read %d chars, buffer %d/%d\n", |
| nbchars, in->buffer->use, in->buffer->size); |
| #endif |
| xmlFree(buffer); |
| return(nbchars); |
| } |
| |
| /** |
| * xmlParserInputBufferRead: |
| * @in: a buffered parser input |
| * @len: indicative value of the amount of chars to read |
| * |
| * Refresh the content of the input buffer, the old data are considered |
| * consumed |
| * This routine handle the I18N transcoding to internal UTF-8 |
| * |
| * Returns the number of chars read and stored in the buffer, or -1 |
| * in case of error. |
| */ |
| int |
| xmlParserInputBufferRead(xmlParserInputBufferPtr in, int len) { |
| /* xmlBufferEmpty(in->buffer); */ |
| if (in->readcallback != NULL) |
| return(xmlParserInputBufferGrow(in, len)); |
| else |
| return(-1); |
| } |
| |
| /** |
| * xmlOutputBufferWrite: |
| * @out: a buffered parser output |
| * @len: the size in bytes of the array. |
| * @buf: an char array |
| * |
| * Write the content of the array in the output I/O buffer |
| * This routine handle the I18N transcoding from internal UTF-8 |
| * The buffer is lossless, i.e. will store in case of partial |
| * or delayed writes. |
| * |
| * Returns the number of chars immediately written, or -1 |
| * in case of error. |
| */ |
| int |
| xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) { |
| int nbchars = 0, ret; |
| |
| if (len < 0) return(0); |
| |
| /* |
| * first handle encoding stuff. |
| */ |
| if (out->encoder != NULL) { |
| /* |
| * Store the data in the incoming raw buffer |
| */ |
| if (out->conv == NULL) { |
| out->conv = xmlBufferCreate(); |
| } |
| xmlBufferAdd(out->buffer, (const xmlChar *) buf, len); |
| |
| if (out->buffer->use < MINLEN) |
| return(0); |
| |
| /* |
| * convert as much as possible to the parser reading buffer. |
| */ |
| nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer); |
| if (nbchars < 0) { |
| fprintf(stderr, "xmlOutputBufferWrite: encoder error\n"); |
| return(-1); |
| } |
| nbchars = out->conv->use; |
| } else { |
| xmlBufferAdd(out->buffer, (const xmlChar *) buf, len); |
| nbchars = out->buffer->use; |
| } |
| if (nbchars < MINLEN) |
| return(0); |
| |
| /* |
| * second write the stuff to the I/O channel |
| */ |
| if (out->encoder != NULL) { |
| ret = out->writecallback(out->context, |
| (const char *)out->conv->content, nbchars); |
| if (ret >= 0) |
| xmlBufferShrink(out->conv, nbchars); |
| } else { |
| ret = out->writecallback(out->context, |
| (const char *)out->buffer->content, nbchars); |
| if (ret >= 0) |
| xmlBufferShrink(out->buffer, nbchars); |
| } |
| if (ret < 0) { |
| fprintf(stderr, "I/O: error %d writing %d bytes\n", ret, nbchars); |
| return(ret); |
| } |
| out->written += ret; |
| |
| #ifdef DEBUG_INPUT |
| fprintf(stderr, "I/O: wrote %d chars\n", ret); |
| #endif |
| return(nbchars); |
| } |
| |
| /** |
| * xmlOutputBufferWriteString: |
| * @out: a buffered parser output |
| * @str: a zero terminated C string |
| * |
| * Write the content of the string in the output I/O buffer |
| * This routine handle the I18N transcoding from internal UTF-8 |
| * The buffer is lossless, i.e. will store in case of partial |
| * or delayed writes. |
| * |
| * Returns the number of chars immediately written, or -1 |
| * in case of error. |
| */ |
| int |
| xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) { |
| int len; |
| |
| if (str == NULL) |
| return(-1); |
| len = strlen(str); |
| |
| if (len > 0) |
| return(xmlOutputBufferWrite(out, len, str)); |
| return(len); |
| } |
| |
| /** |
| * xmlOutputBufferFlush: |
| * @out: a buffered output |
| * |
| * flushes the output I/O channel |
| * |
| * Returns the number of byte written or -1 in case of error. |
| */ |
| int |
| xmlOutputBufferFlush(xmlOutputBufferPtr out) { |
| int nbchars = 0, ret; |
| |
| /* |
| * first handle encoding stuff. |
| */ |
| if ((out->conv != NULL) && (out->encoder != NULL)) { |
| /* |
| * convert as much as possible to the parser reading buffer. |
| */ |
| nbchars = xmlCharEncOutFunc(out->encoder, out->conv, out->buffer); |
| if (nbchars < 0) { |
| fprintf(stderr, "xmlOutputBufferWrite: encoder error\n"); |
| return(-1); |
| } |
| } |
| |
| /* |
| * second flush the stuff to the I/O channel |
| */ |
| if ((out->conv != NULL) && (out->encoder != NULL)) { |
| ret = out->writecallback(out->context, |
| (const char *)out->conv->content, out->conv->use); |
| if (ret >= 0) |
| xmlBufferShrink(out->conv, ret); |
| } else { |
| ret = out->writecallback(out->context, |
| (const char *)out->buffer->content, out->buffer->use); |
| if (ret >= 0) |
| xmlBufferShrink(out->buffer, ret); |
| } |
| if (ret < 0) { |
| fprintf(stderr, "I/O: error %d flushing %d bytes\n", ret, nbchars); |
| return(ret); |
| } |
| out->written += ret; |
| |
| #ifdef DEBUG_INPUT |
| fprintf(stderr, "I/O: flushed %d chars\n", ret); |
| #endif |
| return(ret); |
| } |
| |
| /* |
| * xmlParserGetDirectory: |
| * @filename: the path to a file |
| * |
| * lookup the directory for that file |
| * |
| * Returns a new allocated string containing the directory, or NULL. |
| */ |
| char * |
| xmlParserGetDirectory(const char *filename) { |
| char *ret = NULL; |
| char dir[1024]; |
| char *cur; |
| char sep = '/'; |
| |
| if (xmlInputCallbackInitialized == 0) |
| xmlRegisterDefaultInputCallbacks(); |
| |
| if (filename == NULL) return(NULL); |
| #ifdef WIN32 |
| sep = '\\'; |
| #endif |
| |
| strncpy(dir, filename, 1023); |
| dir[1023] = 0; |
| cur = &dir[strlen(dir)]; |
| while (cur > dir) { |
| if (*cur == sep) break; |
| cur --; |
| } |
| if (*cur == sep) { |
| if (cur == dir) dir[1] = 0; |
| else *cur = 0; |
| ret = xmlMemStrdup(dir); |
| } else { |
| if (getcwd(dir, 1024) != NULL) { |
| dir[1023] = 0; |
| ret = xmlMemStrdup(dir); |
| } |
| } |
| return(ret); |
| } |
| |
| /**************************************************************** |
| * * |
| * External entities loading * |
| * * |
| ****************************************************************/ |
| |
| /* |
| * xmlDefaultExternalEntityLoader: |
| * @URL: the URL for the entity to load |
| * @ID: the System ID for the entity to load |
| * @ctxt: the context in which the entity is called or NULL |
| * |
| * By default we don't load external entitites, yet. |
| * |
| * Returns a new allocated xmlParserInputPtr, or NULL. |
| */ |
| static |
| xmlParserInputPtr |
| xmlDefaultExternalEntityLoader(const char *URL, const char *ID, |
| xmlParserCtxtPtr ctxt) { |
| xmlParserInputPtr ret = NULL; |
| #ifdef DEBUG_EXTERNAL_ENTITIES |
| fprintf(stderr, "xmlDefaultExternalEntityLoader(%s, xxx)\n", URL); |
| #endif |
| if (URL == NULL) { |
| if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) |
| ctxt->sax->warning(ctxt, "failed to load external entity \"%s\"\n", |
| ID); |
| return(NULL); |
| } |
| ret = xmlNewInputFromFile(ctxt, URL); |
| if (ret == NULL) { |
| if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) |
| ctxt->sax->warning(ctxt, "failed to load external entity \"%s\"\n", |
| URL); |
| } |
| return(ret); |
| } |
| |
| static xmlExternalEntityLoader xmlCurrentExternalEntityLoader = |
| xmlDefaultExternalEntityLoader; |
| |
| /* |
| * xmlSetExternalEntityLoader: |
| * @f: the new entity resolver function |
| * |
| * Changes the defaultexternal entity resolver function for the application |
| */ |
| void |
| xmlSetExternalEntityLoader(xmlExternalEntityLoader f) { |
| xmlCurrentExternalEntityLoader = f; |
| } |
| |
| /* |
| * xmlGetExternalEntityLoader: |
| * |
| * Get the default external entity resolver function for the application |
| * |
| * Returns the xmlExternalEntityLoader function pointer |
| */ |
| xmlExternalEntityLoader |
| xmlGetExternalEntityLoader(void) { |
| return(xmlCurrentExternalEntityLoader); |
| } |
| |
| /* |
| * xmlLoadExternalEntity: |
| * @URL: the URL for the entity to load |
| * @ID: the System ID for the entity to load |
| * @ctxt: the context in which the entity is called or NULL |
| * |
| * Load an external entity, note that the use of this function for |
| * unparsed entities may generate problems |
| * TODO: a more generic External entitiy API must be designed |
| * |
| * Returns the xmlParserInputPtr or NULL |
| */ |
| xmlParserInputPtr |
| xmlLoadExternalEntity(const char *URL, const char *ID, |
| xmlParserCtxtPtr ctxt) { |
| return(xmlCurrentExternalEntityLoader(URL, ID, ctxt)); |
| } |
| |