📄 mxml-file.c
字号:
/* * "$Id: mxml-file.c 22267 2006-04-24 17:11:45Z kpfleming $" * * File loading code for Mini-XML, a small XML-like file parsing library. * * Copyright 2003-2005 by Michael Sweet. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Contents: * * mxmlLoadFd() - Load a file descriptor into an XML node tree. * mxmlLoadFile() - Load a file into an XML node tree. * mxmlLoadString() - Load a string into an XML node tree. * mxmlSaveAllocString() - Save an XML node tree to an allocated string. * mxmlSaveFd() - Save an XML tree to a file descriptor. * mxmlSaveFile() - Save an XML tree to a file. * mxmlSaveString() - Save an XML node tree to a string. * mxmlSetCustomHandlers() - Set the handling functions for custom data. * mxmlSetErrorCallback() - Set the error message callback. * mxml_add_char() - Add a character to a buffer, expanding as needed. * mxml_fd_getc() - Read a character from a file descriptor. * mxml_fd_putc() - Write a character to a file descriptor. * mxml_fd_read() - Read a buffer of data from a file descriptor. * mxml_fd_write() - Write a buffer of data to a file descriptor. * mxml_file_getc() - Get a character from a file. * mxml_file_putc() - Write a character to a file. * mxml_get_entity() - Get the character corresponding to an entity... * mxml_load_data() - Load data into an XML node tree. * mxml_parse_element() - Parse an element for any attributes... * mxml_string_getc() - Get a character from a string. * mxml_string_putc() - Write a character to a string. * mxml_write_name() - Write a name string. * mxml_write_node() - Save an XML node to a file. * mxml_write_string() - Write a string, escaping & and < as needed. * mxml_write_ws() - Do whitespace callback... *//* * Include necessary headers... */#include "config.h"#include "mxml.h"#ifdef WIN32# include <io.h>#else# include <unistd.h>#endif /* WIN32 *//* * Character encoding... */#define ENCODE_UTF8 0 /* UTF-8 */#define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */#define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian *//* * Macro to test for a bad XML character... */#define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t')/* * Structures... */typedef struct mxml_fdbuf_s /**** File descriptor buffer (@private) ****/{ int fd; /* File descriptor */ unsigned char *current, /* Current position in buffer */ *end, /* End of buffer */ buffer[8192]; /* Character buffer */} mxml_fdbuf_t;/* * Global error handler... */extern void (*mxml_error_cb)(const char *);/* * Custom data handlers... */static mxml_custom_load_cb_t mxml_custom_load_cb = NULL;static mxml_custom_save_cb_t mxml_custom_save_cb = NULL;/* * Local functions... */static int mxml_add_char(int ch, char **ptr, char **buffer, int *bufsize);static int mxml_fd_getc(void *p, int *encoding);static int mxml_fd_putc(int ch, void *p);static int mxml_fd_read(mxml_fdbuf_t *buf);static int mxml_fd_write(mxml_fdbuf_t *buf);static int mxml_file_getc(void *p, int *encoding);static int mxml_file_putc(int ch, void *p);static int mxml_get_entity(mxml_node_t *parent, void *p, int *encoding, int (*getc_cb)(void *, int *));static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p, mxml_type_t (*cb)(mxml_node_t *), int (*getc_cb)(void *, int *));static int mxml_parse_element(mxml_node_t *node, void *p, int *encoding, int (*getc_cb)(void *, int *));static int mxml_string_getc(void *p, int *encoding);static int mxml_string_putc(int ch, void *p);static int mxml_write_name(const char *s, void *p, int (*putc_cb)(int, void *));static int mxml_write_node(mxml_node_t *node, void *p, const char *(*cb)(mxml_node_t *, int), int col, int (*putc_cb)(int, void *));static int mxml_write_string(const char *s, void *p, int (*putc_cb)(int, void *));static int mxml_write_ws(mxml_node_t *node, void *p, const char *(*cb)(mxml_node_t *, int), int ws, int col, int (*putc_cb)(int, void *));/* * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree. * * The nodes in the specified file are added to the specified top node. * If no top node is provided, the XML file MUST be well-formed with a * single parent node like <?xml> for the entire file. The callback * function returns the value type that should be used for child nodes. * If MXML_NO_CALLBACK is specified then all child nodes will be either * MXML_ELEMENT or MXML_TEXT nodes. * * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading * child nodes of the specified type. */mxml_node_t * /* O - First node or NULL if the file could not be read. */mxmlLoadFd(mxml_node_t *top, /* I - Top node */ int fd, /* I - File descriptor to read from */ mxml_type_t (*cb)(mxml_node_t *node)) /* I - Callback function or MXML_NO_CALLBACK */{ mxml_fdbuf_t buf; /* File descriptor buffer */ /* * Initialize the file descriptor buffer... */ buf.fd = fd; buf.current = buf.buffer; buf.end = buf.buffer; /* * Read the XML data... */ return (mxml_load_data(top, &buf, cb, mxml_fd_getc));}/* * 'mxmlLoadFile()' - Load a file into an XML node tree. * * The nodes in the specified file are added to the specified top node. * If no top node is provided, the XML file MUST be well-formed with a * single parent node like <?xml> for the entire file. The callback * function returns the value type that should be used for child nodes. * If MXML_NO_CALLBACK is specified then all child nodes will be either * MXML_ELEMENT or MXML_TEXT nodes. * * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading * child nodes of the specified type. */mxml_node_t * /* O - First node or NULL if the file could not be read. */mxmlLoadFile(mxml_node_t *top, /* I - Top node */ FILE *fp, /* I - File to read from */ mxml_type_t (*cb)(mxml_node_t *node)) /* I - Callback function or MXML_NO_CALLBACK */{ /* * Read the XML data... */ return (mxml_load_data(top, fp, cb, mxml_file_getc));}/* * 'mxmlLoadString()' - Load a string into an XML node tree. * * The nodes in the specified string are added to the specified top node. * If no top node is provided, the XML string MUST be well-formed with a * single parent node like <?xml> for the entire string. The callback * function returns the value type that should be used for child nodes. * If MXML_NO_CALLBACK is specified then all child nodes will be either * MXML_ELEMENT or MXML_TEXT nodes. * * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading * child nodes of the specified type. */mxml_node_t * /* O - First node or NULL if the string has errors. */mxmlLoadString(mxml_node_t *top, /* I - Top node */ const char *s, /* I - String to load */ mxml_type_t (*cb)(mxml_node_t *node)) /* I - Callback function or MXML_NO_CALLBACK */{ /* * Read the XML data... */ return (mxml_load_data(top, &s, cb, mxml_string_getc));}/* * 'mxmlSaveAllocString()' - Save an XML node tree to an allocated string. * * This function returns a pointer to a string containing the textual * representation of the XML node tree. The string should be freed * using the free() function when you are done with it. NULL is returned * if the node would produce an empty string or if the string cannot be * allocated. * * The callback argument specifies a function that returns a whitespace * string or NULL before and after each element. If MXML_NO_CALLBACK * is specified, whitespace will only be added before MXML_TEXT nodes * with leading whitespace and before attribute names inside opening * element tags. */char * /* O - Allocated string or NULL */mxmlSaveAllocString(mxml_node_t *node, /* I - Node to write */ const char *(*cb)(mxml_node_t *node, int ws)) /* I - Whitespace callback or MXML_NO_CALLBACK */{ int bytes; /* Required bytes */ char buffer[8192]; /* Temporary buffer */ char *s; /* Allocated string */ /* * Write the node to the temporary buffer... */ bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb); if (bytes <= 0) return (NULL); if (bytes < (int)(sizeof(buffer) - 1)) { /* * Node fit inside the buffer, so just duplicate that string and * return... */ return (strdup(buffer)); } /* * Allocate a buffer of the required size and save the node to the * new buffer... */ if ((s = malloc(bytes + 1)) == NULL) return (NULL); mxmlSaveString(node, s, bytes + 1, cb); /* * Return the allocated string... */ return (s);}/* * 'mxmlSaveFd()' - Save an XML tree to a file descriptor. * * The callback argument specifies a function that returns a whitespace * string or NULL before and after each element. If MXML_NO_CALLBACK * is specified, whitespace will only be added before MXML_TEXT nodes * with leading whitespace and before attribute names inside opening * element tags. */int /* O - 0 on success, -1 on error. */mxmlSaveFd(mxml_node_t *node, /* I - Node to write */ int fd, /* I - File descriptor to write to */ const char *(*cb)(mxml_node_t *node, int ws)) /* I - Whitespace callback or MXML_NO_CALLBACK */{ int col; /* Final column */ mxml_fdbuf_t buf; /* File descriptor buffer */ /* * Initialize the file descriptor buffer... */ buf.fd = fd; buf.current = buf.buffer; buf.end = buf.buffer + sizeof(buf.buffer) - 4; /* * Write the node... */ if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc)) < 0) return (-1); if (col > 0) if (mxml_fd_putc('\n', &buf) < 0) return (-1); /* * Flush and return... */ return (mxml_fd_write(&buf));}/* * 'mxmlSaveFile()' - Save an XML tree to a file. * * The callback argument specifies a function that returns a whitespace * string or NULL before and after each element. If MXML_NO_CALLBACK * is specified, whitespace will only be added before MXML_TEXT nodes * with leading whitespace and before attribute names inside opening * element tags. */int /* O - 0 on success, -1 on error. */mxmlSaveFile(mxml_node_t *node, /* I - Node to write */ FILE *fp, /* I - File to write to */ const char *(*cb)(mxml_node_t *node, int ws)) /* I - Whitespace callback or MXML_NO_CALLBACK */{ int col; /* Final column */ /* * Write the node... */ if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc)) < 0) return (-1); if (col > 0) if (putc('\n', fp) < 0) return (-1); /* * Return 0 (success)... */ return (0);}/* * 'mxmlSaveString()' - Save an XML node tree to a string. * * This function returns the total number of bytes that would be * required for the string but only copies (bufsize - 1) characters * into the specified buffer. * * The callback argument specifies a function that returns a whitespace * string or NULL before and after each element. If MXML_NO_CALLBACK * is specified, whitespace will only be added before MXML_TEXT nodes * with leading whitespace and before attribute names inside opening * element tags. */int /* O - Size of string */mxmlSaveString(mxml_node_t *node, /* I - Node to write */ char *buffer, /* I - String buffer */ int bufsize, /* I - Size of string buffer */ const char *(*cb)(mxml_node_t *node, int ws)) /* I - Whitespace callback or MXML_NO_CALLBACK */{ int col; /* Final column */ char *ptr[2]; /* Pointers for putc_cb */ /* * Write the node... */ ptr[0] = buffer; ptr[1] = buffer + bufsize; if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc)) < 0) return (-1); if (col > 0) mxml_string_putc('\n', ptr); /* * Nul-terminate the buffer... */ if (ptr[0] >= ptr[1]) buffer[bufsize - 1] = '\0'; else ptr[0][0] = '\0'; /* * Return the number of characters... */ return (ptr[0] - buffer);}/* * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data. * * The load function accepts a node pointer and a data string and must * return 0 on success and non-zero on error. * * The save function accepts a node pointer and must return a malloc'd * string on success and NULL on error. * */voidmxmlSetCustomHandlers(mxml_custom_load_cb_t load, /* I - Load function */ mxml_custom_save_cb_t save) /* I - Save function */{ mxml_custom_load_cb = load; mxml_custom_save_cb = save;}/* * 'mxmlSetErrorCallback()' - Set the error message callback. */voidmxmlSetErrorCallback(void (*cb)(const char *)) /* I - Error callback function */{ mxml_error_cb = cb;}/* * 'mxml_add_char()' - Add a character to a buffer, expanding as needed. */static int /* O - 0 on success, -1 on error */mxml_add_char(int ch, /* I - Character to add */ char **bufptr, /* IO - Current position in buffer */ char **buffer, /* IO - Current buffer */ int *bufsize) /* IO - Current buffer size */{ char *newbuffer; /* New buffer value */ if (*bufptr >= (*buffer + *bufsize - 4)) { /* * Increase the size of the buffer... */ if (*bufsize < 1024) (*bufsize) *= 2; else (*bufsize) += 1024; if ((newbuffer = realloc(*buffer, *bufsize)) == NULL) { free(*buffer); mxml_error("Unable to expand string buffer to %d bytes!", *bufsize); return (-1); } *bufptr = newbuffer + (*bufptr - *buffer); *buffer = newbuffer; } if (ch < 0x80) { /* * Single byte ASCII... */ *(*bufptr)++ = ch; } else if (ch < 0x800) { /* * Two-byte UTF-8... */ *(*bufptr)++ = 0xc0 | (ch >> 6); *(*bufptr)++ = 0x80 | (ch & 0x3f); } else if (ch < 0x10000) { /* * Three-byte UTF-8... */ *(*bufptr)++ = 0xe0 | (ch >> 12); *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); *(*bufptr)++ = 0x80 | (ch & 0x3f); } else { /* * Four-byte UTF-8... */ *(*bufptr)++ = 0xf0 | (ch >> 18); *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f); *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); *(*bufptr)++ = 0x80 | (ch & 0x3f); } return (0);}/* * 'mxml_fd_getc()' - Read a character from a file descriptor. */static int /* O - Character or EOF */mxml_fd_getc(void *p, /* I - File descriptor buffer */ int *encoding) /* IO - Encoding */{ mxml_fdbuf_t *buf; /* File descriptor buffer */ int ch, /* Current character */ temp; /* Temporary character */ /* * Grab the next character in the buffer... */ buf = (mxml_fdbuf_t *)p; if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); ch = *(buf->current)++; switch (*encoding) { case ENCODE_UTF8 : /* * Got a UTF-8 character; convert UTF-8 to Unicode and return... */ if (!(ch & 0x80)) {#if DEBUG > 1 printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);#endif /* DEBUG > 1 */ if (mxml_bad_char(ch)) { mxml_error("Bad control character 0x%02x not allowed by XML standard!", ch); return (EOF); } return (ch); } else if (ch == 0xfe) { /* * UTF-16 big-endian BOM? */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); ch = *(buf->current)++; if (ch != 0xff) return (EOF); *encoding = ENCODE_UTF16BE; return (mxml_fd_getc(p, encoding)); } else if (ch == 0xff) { /* * UTF-16 little-endian BOM? */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); ch = *(buf->current)++; if (ch != 0xfe) return (EOF); *encoding = ENCODE_UTF16LE; return (mxml_fd_getc(p, encoding)); } else if ((ch & 0xe0) == 0xc0) { /* * Two-byte value... */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x1f) << 6) | (temp & 0x3f); if (ch < 0x80) return (EOF); } else if ((ch & 0xf0) == 0xe0) { /* * Three-byte value... */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x0f) << 6) | (temp & 0x3f); if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = (ch << 6) | (temp & 0x3f); if (ch < 0x800) return (EOF); } else if ((ch & 0xf8) == 0xf0) { /* * Four-byte value... */ if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF); temp = *(buf->current)++; if ((temp & 0xc0) != 0x80) return (EOF); ch = ((ch & 0x07) << 6) | (temp & 0x3f); if (buf->current >= buf->end) if (mxml_fd_read(buf) < 0) return (EOF);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -