libxml2/xmlIO.c
Daniel Veillard 354edd89c8 It's hard to estimate how much I hated to do this, but it's done ...
The best way to solve the I18N problen is unfortunately to fix libxml1
parser to at least deal correctly with UTF8 and ISO-Latin-1 encodings.
I have plugged in the 2.3.5 (or what will be it's not released yet) core
XML parser in the libxml1 framework. No changes API wise. The only changes
will be backward binary compatible extensions of some of the parser
structures and the number of parser states.

- configure.in: version will be 1.8.12
- Makefile.am: added .memdump tests
- SAX.c entities.[ch] parser.[ch] tree.c valid.c xml-error.h
  xmlIO.[ch]: plugged the 2.3.5 libxml2 XML parser in, while
  preserving binary compatibility
- uri.[ch]: the parser code really requires URI manipulation
  add this from 2.3.5 too
- results/* : of course this changed the output of a number of tests
- test/dtd12 : this test was actually not wellformed, the new
  parser pukes at it, fixed ...
Daniel
2001-03-21 12:53:50 +00:00

565 lines
14 KiB
C

/*
* 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 "xmlmemory.h"
#include "parser.h"
#include "parserInternals.h"
#include "xmlIO.h"
#include "nanohttp.h"
#include "nanoftp.h"
/* #define DEBUG_INPUT */
/* #define VERBOSE_FAILURE */
/* #define DEBUG_EXTERNAL_ENTITIES */
#ifdef DEBUG_INPUT
#define MINLEN 40
#else
#define MINLEN 4000
#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);
ret->fd = -1;
ret->httpIO = NULL;
ret->ftpIO = NULL;
/* 2.3.5 */
ret->raw = NULL;
return(ret);
}
/**
* xmlFreeParserInputBuffer:
* @in: a buffered parser input
*
* Free up the memory used by a buffered parser input
*/
void
xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) {
if (in->buffer != NULL) {
xmlBufferFree(in->buffer);
in->buffer = NULL;
}
#ifdef HAVE_ZLIB_H
if (in->gzfile != NULL)
gzclose(in->gzfile);
#endif
if (in->httpIO != NULL)
xmlNanoHTTPClose(in->httpIO);
if (in->ftpIO != NULL)
xmlNanoFTPClose(in->ftpIO);
if (in->fd >= 0)
close(in->fd);
/* 2.3.5 */
if (in->raw) {
xmlBufferFree(in->raw);
in->raw = NULL;
}
memset(in, 0xbe, (size_t) sizeof(xmlParserInputBuffer));
xmlFree(in);
}
/**
* xmlParserInputBufferCreateFilename:
* @filename: a C string containing the 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.
*
* Returns the new parser input or NULL
*/
xmlParserInputBufferPtr
xmlParserInputBufferCreateFilename(const char *filename, xmlCharEncoding enc) {
xmlParserInputBufferPtr ret;
#ifdef HAVE_ZLIB_H
gzFile input = 0;
#else
int input = -1;
#endif
void *httpIO = NULL;
void *ftpIO = NULL;
if (filename == NULL) return(NULL);
if (!strncmp(filename, "http://", 7)) {
httpIO = xmlNanoHTTPOpen(filename, NULL);
if (httpIO == NULL) {
#ifdef VERBOSE_FAILURE
fprintf (stderr, "Cannot read URL %s\n", filename);
perror ("xmlNanoHTTPOpen failed");
#endif
return(NULL);
}
} else if (!strncmp(filename, "ftp://", 6)) {
ftpIO = xmlNanoFTPOpen(filename);
if (ftpIO == NULL) {
#ifdef VERBOSE_FAILURE
fprintf (stderr, "Cannot read URL %s\n", filename);
perror ("xmlNanoFTPOpen failed");
#endif
return(NULL);
}
} else if (!strcmp(filename, "-")) {
#ifdef HAVE_ZLIB_H
input = gzdopen (fileno(stdin), "r");
if (input == NULL) {
#ifdef VERBOSE_FAILURE
fprintf (stderr, "Cannot read from stdin\n");
perror ("gzdopen failed");
#endif
return(NULL);
}
#else
#ifdef WIN32
input = -1;
#else
input = fileno(stdin);
#endif
if (input < 0) {
#ifdef VERBOSE_FAILURE
fprintf (stderr, "Cannot read from stdin\n");
perror ("open failed");
#endif
return(NULL);
}
#endif
} else {
#ifdef HAVE_ZLIB_H
input = gzopen (filename, "r");
if (input == NULL) {
#ifdef VERBOSE_FAILURE
fprintf (stderr, "Cannot read file %s :\n", filename);
perror ("gzopen failed");
#endif
return(NULL);
}
#else
#ifdef WIN32
input = _open (filename, O_RDONLY | _O_BINARY);
#else
input = open (filename, O_RDONLY);
#endif
if (input < 0) {
#ifdef VERBOSE_FAILURE
fprintf (stderr, "Cannot read file %s :\n", filename);
perror ("open failed");
#endif
return(NULL);
}
#endif
}
/*
* TODO : get the 4 first bytes and decode the charset
* if enc == XML_CHAR_ENCODING_NONE
* plug some encoding conversion routines here. !!!
* enc = xmlDetectCharEncoding(buffer);
*/
ret = xmlAllocParserInputBuffer(enc);
if (ret != NULL) {
#ifdef HAVE_ZLIB_H
ret->gzfile = input;
#else
ret->fd = input;
#endif
ret->httpIO = httpIO;
ret->ftpIO = ftpIO;
}
xmlParserInputBufferRead(ret, 4);
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 (file == NULL) return(NULL);
ret = xmlAllocParserInputBuffer(enc);
if (ret != NULL)
ret->file = file;
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->fd = fd;
return(ret);
}
/**
* xmlParserInputBufferPush:
* @in: a buffered parser input
* @buf: an char array
* @len: the size in bytes of the 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) {
xmlChar *buffer;
buffer = (xmlChar *) xmlMalloc((len + 1) * 2 * sizeof(xmlChar));
if (buffer == NULL) {
fprintf(stderr, "xmlParserInputBufferGrow : out of memory !\n");
xmlFree(buffer);
return(-1);
}
nbchars = in->encoder->input(buffer, (len + 1) * 2 * sizeof(xmlChar),
(xmlChar *) buf, len);
/*
* TODO : we really need to have something atomic or the
* encoder must report the number of bytes read
*/
buffer[nbchars] = 0;
xmlBufferAdd(in->buffer, (xmlChar *) buffer, nbchars);
xmlFree(buffer);
} 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
*
* 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;
#ifdef HAVE_ZLIB_H
gzFile input = (gzFile) in->gzfile;
#endif
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);
}
if (in->httpIO != NULL) {
res = xmlNanoHTTPRead(in->httpIO, &buffer[0], len);
} else if (in->ftpIO != NULL) {
res = xmlNanoFTPRead(in->ftpIO, &buffer[0], len);
} else if (in->file != NULL) {
res = fread(&buffer[0], 1, len, in->file);
#ifdef HAVE_ZLIB_H
} else if (in->gzfile != NULL) {
res = gzread(input, &buffer[0], len);
#endif
} else if (in->fd >= 0) {
res = read(in->fd, &buffer[0], len);
} else {
fprintf(stderr, "xmlParserInputBufferGrow : no input !\n");
xmlFree(buffer);
return(-1);
}
if (res == 0) {
xmlFree(buffer);
return(0);
}
if (res < 0) {
perror ("read error");
xmlFree(buffer);
return(-1);
}
if (in->encoder != NULL) {
xmlChar *buf;
buf = (xmlChar *) xmlMalloc((res + 1) * 2 * sizeof(xmlChar));
if (buf == NULL) {
fprintf(stderr, "xmlParserInputBufferGrow : out of memory !\n");
xmlFree(buffer);
return(-1);
}
nbchars = in->encoder->input(buf, (res + 1) * 2 * sizeof(xmlChar),
BAD_CAST buffer, res);
buf[nbchars] = 0;
xmlBufferAdd(in->buffer, (xmlChar *) buf, nbchars);
xmlFree(buf);
} else {
nbchars = res;
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->httpIO != NULL) || (in->ftpIO != NULL) || (in->file != NULL) ||
#ifdef HAVE_ZLIB_H
(in->gzfile != NULL) ||
#endif
(in->fd >= 0))
return(xmlParserInputBufferGrow(in, len));
else
return(0);
}
/*
* 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 (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));
}