2024-02-27 20:04:27 +00:00
//
// File loading code for Mini-XML, a small XML file parsing library.
//
// https://www.msweet.org/mxml
//
// Copyright © 2003-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
2003-06-03 19:46:29 +00:00
2019-02-10 15:40:53 +00:00
# ifndef _WIN32
2004-07-11 13:14:07 +00:00
# include <unistd.h>
2024-02-27 20:04:27 +00:00
# endif // !_WIN32
2009-02-05 06:06:11 +00:00
# include "mxml-private.h"
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
// Local types...
2024-02-27 20:04:27 +00:00
//
2004-05-16 21:54:47 +00:00
2024-03-06 19:45:10 +00:00
typedef enum _mxml_encoding_e // Character encoding
{
_MXML_ENCODING_UTF8 , // UTF-8
_MXML_ENCODING_UTF16BE , // UTF-16 Big-Endian
_MXML_ENCODING_UTF16LE // UTF-16 Little-Endian
} _mxml_encoding_t ;
typedef struct _mxml_stringbuf_s // String buffer
{
char * buffer , // Buffer
* bufptr ; // Pointer into buffer
size_t bufsize ; // Size of buffer
bool bufalloc ; // Allocate buffer?
} _mxml_stringbuf_t ;
2004-05-16 21:54:47 +00:00
2024-02-27 20:04:27 +00:00
//
// Macro to test for a bad XML character...
//
2005-01-29 17:03:33 +00:00
# define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t')
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
// Local functions...
2024-02-27 20:04:27 +00:00
//
2004-07-11 13:14:07 +00:00
2024-03-19 01:46:14 +00:00
static bool mxml_add_char ( mxml_options_t * options , int ch , char * * ptr , char * * buffer , size_t * bufsize ) ;
static int mxml_get_entity ( mxml_options_t * options , mxml_io_cb_t io_cb , void * io_cbdata , _mxml_encoding_t * encoding , mxml_node_t * parent , int * line ) ;
static int mxml_getc ( mxml_options_t * options , mxml_io_cb_t io_cb , void * io_cbdata , _mxml_encoding_t * encoding ) ;
2024-03-06 19:45:10 +00:00
static inline int mxml_isspace ( int ch )
{
return ( ch = = ' ' | | ch = = ' \t ' | | ch = = ' \r ' | | ch = = ' \n ' ) ;
}
2024-03-19 01:46:14 +00:00
static mxml_node_t * mxml_load_data ( mxml_node_t * top , mxml_options_t * options , mxml_io_cb_t io_cb , void * io_cbdata ) ;
static int mxml_parse_element ( mxml_options_t * options , mxml_io_cb_t io_cb , void * io_cbdata , mxml_node_t * node , _mxml_encoding_t * encoding , int * line ) ;
static size_t mxml_read_cb_fd ( int * fd , void * buffer , size_t bytes ) ;
static size_t mxml_read_cb_file ( FILE * fp , void * buffer , size_t bytes ) ;
static size_t mxml_read_cb_string ( _mxml_stringbuf_t * sb , void * buffer , size_t bytes ) ;
static double mxml_strtod ( mxml_options_t * options , const char * buffer , char * * bufptr ) ;
static size_t mxml_io_cb_fd ( int * fd , void * buffer , size_t bytes ) ;
static size_t mxml_io_cb_file ( FILE * fp , void * buffer , size_t bytes ) ;
static size_t mxml_io_cb_string ( _mxml_stringbuf_t * sb , void * buffer , size_t bytes ) ;
static int mxml_write_node ( mxml_node_t * node , mxml_options_t * options , mxml_io_cb_t io_cb , void * io_cbdata , int col ) ;
static int mxml_write_string ( const char * s , mxml_io_cb_t io_cb , void * io_cbdata , bool use_entities , int col ) ;
static int mxml_write_ws ( mxml_node_t * node , mxml_options_t * options , mxml_io_cb_t io_cb , void * io_cbdata , mxml_ws_t ws , int col ) ;
2024-03-06 19:45:10 +00:00
//
// 'mxmlLoadFd()' - Load a file descriptor into an XML node tree.
//
2024-03-17 02:20:24 +00:00
// This function loads the file descriptor `fd` into an XML node tree. The
// nodes in the specified file are added to the specified node `top`. If `NULL`
// is provided, the XML file MUST be well-formed with a single parent processing
// instruction node like `<?xml version="1.0"?>` at the start of the file.
//
// The load callback function `load_cb` is called to obtain the node type that
// should be used for child nodes. If `NULL`, the `load_cbdata` argument points
// to a `mmd_type_t` variable that specifies the value type or `MMD_TYPE_TEXT`
// if that argument is also `NULL`.
//
// The SAX callback function `sax_cb` and associated callback data `sax_cbdata`
// are used to enable the Simple API for XML streaming mode. The callback is
// called as the XML node tree is parsed.
2024-03-06 19:45:10 +00:00
//
// Note: The most common programming error when using the Mini-XML library is
2024-03-17 02:20:24 +00:00
// to load an XML file using the `MXML_TYPE_TEXT` node type, which returns
// inline text as a series of whitespace-delimited words, instead of using the
// `MXML_TYPE_OPAQUE` node type which returns the inline text as a single string
2024-03-06 19:45:10 +00:00
// (including whitespace).
//
2007-04-23 21:48:03 +00:00
2024-03-06 19:45:10 +00:00
mxml_node_t * // O - First node or `NULL` if the file could not be read.
mxmlLoadFd (
mxml_node_t * top , // I - Top node
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
int fd ) // I - File descriptor to read from
2004-07-11 13:14:07 +00:00
{
2024-03-06 19:45:10 +00:00
// Range check input...
if ( fd < 0 )
return ( NULL ) ;
// Read the XML data...
2024-03-19 01:46:14 +00:00
return ( mxml_load_data ( top , options , ( mxml_io_cb_t ) mxml_read_cb_fd , & fd ) ) ;
2024-03-06 19:45:10 +00:00
}
2004-07-11 13:14:07 +00:00
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
// 'mxmlLoadFile()' - Load a file into an XML node tree.
//
2024-03-17 02:20:24 +00:00
// This function loads the `FILE` pointer `fp` into an XML node tree. The
// nodes in the specified file are added to the specified node `top`. If `NULL`
// is provided, the XML file MUST be well-formed with a single parent processing
// instruction node like `<?xml version="1.0"?>` at the start of the file.
//
// The load callback function `load_cb` is called to obtain the node type that
// should be used for child nodes. If `NULL`, the `load_cbdata` argument points
// to a `mmd_type_t` variable that specifies the value type or `MMD_TYPE_TEXT`
// if that argument is also `NULL`.
//
// The SAX callback function `sax_cb` and associated callback data `sax_cbdata`
// are used to enable the Simple API for XML streaming mode. The callback is
// called as the XML node tree is parsed.
2024-03-06 19:45:10 +00:00
//
// Note: The most common programming error when using the Mini-XML library is
2024-03-17 02:20:24 +00:00
// to load an XML file using the `MXML_TYPE_TEXT` node type, which returns
// inline text as a series of whitespace-delimited words, instead of using the
// `MXML_TYPE_OPAQUE` node type which returns the inline text as a single string
2024-03-06 19:45:10 +00:00
// (including whitespace).
2024-02-27 20:04:27 +00:00
//
2003-06-03 19:46:29 +00:00
2024-03-06 19:45:10 +00:00
mxml_node_t * // O - First node or `NULL` if the file could not be read.
mxmlLoadFile (
mxml_node_t * top , // I - Top node
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
FILE * fp ) // I - File to read from
2024-03-06 19:45:10 +00:00
{
// Range check input...
if ( ! fp )
return ( NULL ) ;
// Read the XML data...
2024-03-19 01:46:14 +00:00
return ( mxml_load_data ( top , options , ( mxml_io_cb_t ) mxml_read_cb_file , fp ) ) ;
2024-03-06 19:45:10 +00:00
}
2003-06-19 03:20:41 +00:00
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
// 'mxmlLoadFilename()' - Load a file into an XML node tree.
2024-02-27 20:04:27 +00:00
//
2024-03-17 02:20:24 +00:00
// This function loads the named file `filename` into an XML node tree. The
// nodes in the specified file are added to the specified node `top`. If `NULL`
// is provided, the XML file MUST be well-formed with a single parent processing
// instruction node like `<?xml version="1.0"?>` at the start of the file.
//
// The load callback function `load_cb` is called to obtain the node type that
// should be used for child nodes. If `NULL`, the `load_cbdata` argument points
// to a `mmd_type_t` variable that specifies the value type or `MMD_TYPE_TEXT`
// if that argument is also `NULL`.
//
// The SAX callback function `sax_cb` and associated callback data `sax_cbdata`
// are used to enable the Simple API for XML streaming mode. The callback is
// called as the XML node tree is parsed.
2024-02-27 20:04:27 +00:00
//
// Note: The most common programming error when using the Mini-XML library is
2024-03-17 02:20:24 +00:00
// to load an XML file using the `MXML_TYPE_TEXT` node type, which returns
// inline text as a series of whitespace-delimited words, instead of using the
// `MXML_TYPE_OPAQUE` node type which returns the inline text as a single string
2024-02-27 20:04:27 +00:00
// (including whitespace).
//
mxml_node_t * // O - First node or `NULL` if the file could not be read.
2024-03-06 19:45:10 +00:00
mxmlLoadFilename (
mxml_node_t * top , // I - Top node
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
const char * filename ) // I - File to read from
2004-07-11 13:14:07 +00:00
{
2024-03-06 19:45:10 +00:00
FILE * fp ; // File pointer
mxml_node_t * ret ; // Node
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
// Range check input...
if ( ! filename )
return ( NULL ) ;
// Open the file...
if ( ( fp = fopen ( filename , " r " ) ) = = NULL )
return ( NULL ) ;
2004-07-11 13:14:07 +00:00
2024-02-27 20:04:27 +00:00
// Read the XML data...
2024-03-19 01:46:14 +00:00
ret = mxml_load_data ( top , options , ( mxml_io_cb_t ) mxml_read_cb_file , fp ) ;
2024-03-06 19:45:10 +00:00
// Close the file and return...
fclose ( fp ) ;
return ( ret ) ;
2004-07-11 13:14:07 +00:00
}
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
// 'mxmlLoadIO()' - Load an XML node tree using a read callback.
2024-02-27 20:04:27 +00:00
//
2024-03-17 02:20:24 +00:00
// This function loads data into an XML node tree using a read callback. The
// nodes in the specified file are added to the specified node `top`. If `NULL`
// is provided, the XML file MUST be well-formed with a single parent processing
// instruction node like `<?xml version="1.0"?>` at the start of the file.
//
2024-03-19 01:46:14 +00:00
// The read callback function `io_cb` is called to read a number of bytes from
// the source. The callback data pointer `io_cbdata` is passed to the read
2024-03-17 02:20:24 +00:00
// callback with a pointer to a buffer and the maximum number of bytes to read,
// for example:
//
// ```c
2024-03-19 15:22:41 +00:00
// size_t my_io_cb(void *cbdata, void *buffer, size_t bytes)
2024-03-17 02:20:24 +00:00
// {
// ... copy up to "bytes" bytes into buffer ...
2024-03-19 15:22:41 +00:00
// ... return the number of bytes "read" or 0 on error ...
2024-03-17 02:20:24 +00:00
// }
// ```
//
// The load callback function `load_cb` is called to obtain the node type that
// should be used for child nodes. If `NULL`, the `load_cbdata` argument points
// to a `mmd_type_t` variable that specifies the value type or `MMD_TYPE_TEXT`
// if that argument is also `NULL`.
//
// The SAX callback function `sax_cb` and associated callback data `sax_cbdata`
// are used to enable the Simple API for XML streaming mode. The callback is
// called as the XML node tree is parsed.
2024-02-27 20:04:27 +00:00
//
// Note: The most common programming error when using the Mini-XML library is
2024-03-17 02:20:24 +00:00
// to load an XML file using the `MXML_TYPE_TEXT` node type, which returns
// inline text as a series of whitespace-delimited words, instead of using the
// `MXML_TYPE_OPAQUE` node type which returns the inline text as a single string
2024-02-27 20:04:27 +00:00
// (including whitespace).
//
mxml_node_t * // O - First node or `NULL` if the file could not be read.
2024-03-06 19:45:10 +00:00
mxmlLoadIO (
mxml_node_t * top , // I - Top node
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
mxml_io_cb_t io_cb , // I - Read callback function
void * io_cbdata ) // I - Read callback data
2003-06-19 03:20:41 +00:00
{
2024-03-06 19:45:10 +00:00
// Range check input...
2024-03-19 01:46:14 +00:00
if ( ! io_cb )
2024-03-06 19:45:10 +00:00
return ( NULL ) ;
2024-02-27 20:04:27 +00:00
// Read the XML data...
2024-03-19 01:46:14 +00:00
return ( mxml_load_data ( top , options , io_cb , io_cbdata ) ) ;
2003-06-19 03:20:41 +00:00
}
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
//
// 'mxmlLoadString()' - Load a string into an XML node tree.
//
2024-03-17 02:20:24 +00:00
// This function loads the string into an XML node tree. The nodes in the
// specified file are added to the specified node `top`. If `NULL` is provided,
// the XML file MUST be well-formed with a single parent processing instruction
// node like `<?xml version="1.0"?>` at the start of the file.
//
// The load callback function `load_cb` is called to obtain the node type that
// should be used for child nodes. If `NULL`, the `load_cbdata` argument points
// to a `mmd_type_t` variable that specifies the value type or `MMD_TYPE_TEXT`
// if that argument is also `NULL`.
//
// The SAX callback function `sax_cb` and associated callback data `sax_cbdata`
// are used to enable the Simple API for XML streaming mode. The callback is
// called as the XML node tree is parsed.
2024-02-27 20:04:27 +00:00
//
// Note: The most common programming error when using the Mini-XML library is
2024-03-17 02:20:24 +00:00
// to load an XML file using the `MXML_TYPE_TEXT` node type, which returns
// inline text as a series of whitespace-delimited words, instead of using the
// `MXML_TYPE_OPAQUE` node type which returns the inline text as a single string
2024-02-27 20:04:27 +00:00
// (including whitespace).
//
mxml_node_t * // O - First node or `NULL` if the string has errors.
2024-03-06 19:45:10 +00:00
mxmlLoadString (
mxml_node_t * top , // I - Top node
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
const char * s ) // I - String to load
2003-06-19 03:20:41 +00:00
{
2024-03-06 19:45:10 +00:00
_mxml_stringbuf_t sb ; // String buffer
// Range check input...
if ( ! s )
return ( NULL ) ;
// Setup string buffer...
sb . buffer = ( char * ) s ;
sb . bufptr = ( char * ) s ;
sb . bufsize = strlen ( s ) ;
sb . bufalloc = false ;
2024-02-27 20:04:27 +00:00
// Read the XML data...
2024-03-19 01:46:14 +00:00
return ( mxml_load_data ( top , options , ( mxml_io_cb_t ) mxml_read_cb_string , & sb ) ) ;
2003-06-19 03:20:41 +00:00
}
2024-02-27 20:04:27 +00:00
//
// 'mxmlSaveAllocString()' - Save an XML tree to an allocated string.
//
2024-03-17 02:20:24 +00:00
// This function saves the XML tree `node` to an allocated string. The string
// should be freed using `free` (or the string free callback set using
// @link mxmlSetStringCallbacks@) when you are done with it.
//
// `NULL` is returned if the node would produce an empty string or if the string
// cannot be allocated.
2024-02-27 20:04:27 +00:00
//
2024-03-17 02:20:24 +00:00
// The callback function `save_cb` specifies a function that returns a
// whitespace string or `NULL` before and after each element. The function
// receives the callback data pointer `save_cbdata`, the `mxml_node_t` pointer,
// and a "when" value indicating where the whitespace is being added, for
// example:
//
// ```c
// const char *my_save_cb(void *cbdata, mxml_node_t *node, mxml_ws_t when)
// {
// if (when == MXML_WS_BEFORE_OPEN || when == MXML_WS_AFTER_CLOSE)
// return ("\n");
// else
// return (NULL);
// }
// ```
2024-02-27 20:04:27 +00:00
//
char * // O - Allocated string or `NULL`
2007-04-23 21:48:03 +00:00
mxmlSaveAllocString (
2024-02-27 20:04:27 +00:00
mxml_node_t * node , // I - Node to write
2024-03-19 01:46:14 +00:00
mxml_options_t * options ) // I - Options
2003-07-22 10:29:19 +00:00
{
2024-03-06 19:45:10 +00:00
_mxml_stringbuf_t sb ; // String buffer
2003-07-22 10:29:19 +00:00
2024-03-06 19:45:10 +00:00
// Setup a string buffer
if ( ( sb . buffer = malloc ( 1024 ) ) = = NULL )
2003-07-22 10:29:19 +00:00
return ( NULL ) ;
2024-03-06 19:45:10 +00:00
sb . bufptr = sb . buffer ;
sb . bufsize = 1024 ;
sb . bufalloc = true ;
2003-07-22 10:29:19 +00:00
2024-03-06 19:45:10 +00:00
// Write the top node...
2024-03-19 01:46:14 +00:00
if ( mxml_write_node ( node , options , ( mxml_io_cb_t ) mxml_io_cb_string , & sb , 0 ) < 0 )
2024-03-06 19:45:10 +00:00
{
free ( sb . buffer ) ;
2003-07-22 10:29:19 +00:00
return ( NULL ) ;
2024-03-06 19:45:10 +00:00
}
2003-07-22 10:29:19 +00:00
2024-03-06 19:45:10 +00:00
// Nul-terminate the string...
* ( sb . bufptr ) = ' \0 ' ;
2003-07-22 10:29:19 +00:00
2024-02-27 20:04:27 +00:00
// Return the allocated string...
2024-03-06 19:45:10 +00:00
return ( sb . buffer ) ;
2003-07-22 10:29:19 +00:00
}
2024-02-27 20:04:27 +00:00
//
// 'mxmlSaveFd()' - Save an XML tree to a file descriptor.
//
2024-03-17 02:20:24 +00:00
// This function saves the XML tree `node` to a file descriptor.
//
// The callback function `save_cb` specifies a function that returns a
// whitespace string or `NULL` before and after each element. The function
// receives the callback data pointer `save_cbdata`, the `mxml_node_t` pointer,
// and a "when" value indicating where the whitespace is being added, for
// example:
//
// ```c
// const char *my_save_cb(void *cbdata, mxml_node_t *node, mxml_ws_t when)
// {
// if (when == MXML_WS_BEFORE_OPEN || when == MXML_WS_AFTER_CLOSE)
// return ("\n");
// else
// return (NULL);
// }
// ```
2024-02-27 20:04:27 +00:00
//
2024-03-03 01:02:08 +00:00
bool // O - `true` on success, `false` on error.
2024-02-27 20:04:27 +00:00
mxmlSaveFd ( mxml_node_t * node , // I - Node to write
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
int fd ) // I - File descriptor to write to
2004-07-11 13:14:07 +00:00
{
2024-02-27 20:04:27 +00:00
int col ; // Final column
2004-07-11 13:14:07 +00:00
2024-02-27 20:04:27 +00:00
// Write the node...
2024-03-19 01:46:14 +00:00
if ( ( col = mxml_write_node ( node , options , ( mxml_io_cb_t ) mxml_io_cb_fd , & fd , 0 ) ) < 0 )
2024-03-06 19:45:10 +00:00
return ( false ) ;
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
// Make sure the file ends with a newline...
2004-07-11 13:14:07 +00:00
if ( col > 0 )
2024-02-27 20:04:27 +00:00
{
2024-03-06 19:45:10 +00:00
if ( write ( fd , " \n " , 1 ) < 0 )
return ( false ) ;
2024-02-27 20:04:27 +00:00
}
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
return ( true ) ;
2004-07-11 13:14:07 +00:00
}
2024-02-27 20:04:27 +00:00
//
// 'mxmlSaveFile()' - Save an XML tree to a file.
//
2024-03-17 02:20:24 +00:00
// This function saves the XML tree `node` to a stdio `FILE`.
//
// The callback function `save_cb` specifies a function that returns a
// whitespace string or `NULL` before and after each element. The function
// receives the callback data pointer `save_cbdata`, the `mxml_node_t` pointer,
// and a "when" value indicating where the whitespace is being added, for
// example:
//
// ```c
// const char *my_save_cb(void *cbdata, mxml_node_t *node, mxml_ws_t when)
// {
// if (when == MXML_WS_BEFORE_OPEN || when == MXML_WS_AFTER_CLOSE)
// return ("\n");
// else
// return (NULL);
// }
// ```
2024-02-27 20:04:27 +00:00
//
2024-03-03 01:02:08 +00:00
bool // O - `true` on success, `false` on error.
2024-03-06 19:45:10 +00:00
mxmlSaveFile (
mxml_node_t * node , // I - Node to write
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
FILE * fp ) // I - File to write to
2003-06-19 03:20:41 +00:00
{
2024-03-06 19:45:10 +00:00
int col ; // Final column
2003-06-19 03:20:41 +00:00
2024-02-27 20:04:27 +00:00
// Write the node...
2024-03-19 01:46:14 +00:00
if ( ( col = mxml_write_node ( node , options , ( mxml_io_cb_t ) mxml_io_cb_file , fp , 0 ) ) < 0 )
2024-03-06 19:45:10 +00:00
return ( false ) ;
2003-06-19 03:20:41 +00:00
2024-03-06 19:45:10 +00:00
// Make sure the file ends with a newline...
2003-06-19 03:20:41 +00:00
if ( col > 0 )
2024-02-27 20:04:27 +00:00
{
2003-06-19 03:20:41 +00:00
if ( putc ( ' \n ' , fp ) < 0 )
2024-03-06 19:45:10 +00:00
return ( false ) ;
2024-02-27 20:04:27 +00:00
}
2003-06-19 03:20:41 +00:00
2024-03-06 19:45:10 +00:00
return ( true ) ;
2003-06-19 03:20:41 +00:00
}
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
// 'mxmlSaveFilename()' - Save an XML tree to a file.
2024-02-27 20:04:27 +00:00
//
2024-03-17 02:20:24 +00:00
// This function saves the XML tree `node` to a named file.
//
// The callback function `save_cb` specifies a function that returns a
// whitespace string or `NULL` before and after each element. The function
// receives the callback data pointer `save_cbdata`, the `mxml_node_t` pointer,
// and a "when" value indicating where the whitespace is being added, for
// example:
//
// ```c
// const char *my_save_cb(void *cbdata, mxml_node_t *node, mxml_ws_t when)
// {
// if (when == MXML_WS_BEFORE_OPEN || when == MXML_WS_AFTER_CLOSE)
// return ("\n");
// else
// return (NULL);
// }
// ```
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
bool // O - `true` on success, `false` on error.
mxmlSaveFilename (
mxml_node_t * node , // I - Node to write
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
const char * filename ) // I - File to write to
2003-06-19 03:20:41 +00:00
{
2024-03-06 19:45:10 +00:00
bool ret = true ; // Return value
FILE * fp ; // File pointer
int col ; // Final column
2003-06-19 04:25:12 +00:00
2024-03-06 19:45:10 +00:00
// Open the file...
if ( ( fp = fopen ( filename , " w " ) ) = = NULL )
return ( false ) ;
2003-06-19 04:25:12 +00:00
2024-03-06 19:45:10 +00:00
// Write the node...
2024-03-19 01:46:14 +00:00
if ( ( col = mxml_write_node ( node , options , ( mxml_io_cb_t ) mxml_io_cb_file , fp , 0 ) ) < 0 )
2021-10-26 18:44:15 +00:00
{
2024-03-06 19:45:10 +00:00
ret = false ;
2021-10-26 18:44:15 +00:00
}
2024-03-06 19:45:10 +00:00
else if ( col > 0 )
2024-02-27 20:04:27 +00:00
{
2024-03-06 19:45:10 +00:00
// Make sure the file ends with a newline...
if ( putc ( ' \n ' , fp ) < 0 )
ret = false ;
2024-02-27 20:04:27 +00:00
}
2003-06-19 04:25:12 +00:00
2024-03-06 19:45:10 +00:00
fclose ( fp ) ;
return ( ret ) ;
2003-06-19 03:20:41 +00:00
}
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
// 'mxmlSaveIO()' - Save an XML tree using a callback.
2024-02-27 20:04:27 +00:00
//
2024-03-17 02:20:24 +00:00
// This function saves the XML tree `node` using a write callback function
2024-03-19 01:46:14 +00:00
// `io_cb`. The write callback is called with the callback data pointer
// `io_cbdata`, a buffer pointer, and the number of bytes to write, for
2024-03-17 02:20:24 +00:00
// example:
//
// ```c
2024-03-19 15:22:41 +00:00
// size_t my_io_cb(void *cbdata, const void *buffer, size_t bytes)
2024-03-17 02:20:24 +00:00
// {
// ... write/copy bytes from buffer to the output ...
2024-03-19 15:22:41 +00:00
// ... return the number of bytes written/copied or 0 on error ...
2024-03-17 02:20:24 +00:00
// }
// ```
//
// The callback function `save_cb` specifies a function that returns a
// whitespace string or `NULL` before and after each element. The function
// receives the callback data pointer `save_cbdata`, the `mxml_node_t` pointer,
// and a "when" value indicating where the whitespace is being added, for
// example:
//
// ```c
// const char *my_save_cb(void *cbdata, mxml_node_t *node, mxml_ws_t when)
// {
// if (when == MXML_WS_BEFORE_OPEN || when == MXML_WS_AFTER_CLOSE)
// return ("\n");
// else
// return (NULL);
// }
// ```
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
bool // O - `true` on success, `false` on error.
mxmlSaveIO (
2024-03-19 01:46:14 +00:00
mxml_node_t * node , // I - Node to write
mxml_options_t * options , // I - Options
mxml_io_cb_t io_cb , // I - Write callback function
void * io_cbdata ) // I - Write callback data
2007-04-23 21:48:03 +00:00
{
2024-03-06 19:45:10 +00:00
int col ; // Final column
2007-04-23 21:48:03 +00:00
2024-03-06 19:45:10 +00:00
// Range check input...
2024-03-19 01:46:14 +00:00
if ( ! node | | ! io_cb )
2024-03-06 19:45:10 +00:00
return ( false ) ;
2007-04-23 21:48:03 +00:00
2024-03-06 19:45:10 +00:00
// Write the node...
2024-03-19 01:46:14 +00:00
if ( ( col = mxml_write_node ( node , options , io_cb , io_cbdata , 0 ) ) < 0 )
2024-03-06 19:45:10 +00:00
return ( false ) ;
if ( col > 0 )
{
// Make sure the file ends with a newline...
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , " \n " , 1 ) < 0 )
2024-03-06 19:45:10 +00:00
return ( false ) ;
}
return ( true ) ;
2007-04-23 21:48:03 +00:00
}
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
// 'mxmlSaveString()' - Save an XML node tree to a string.
2024-02-27 20:04:27 +00:00
//
2024-03-17 02:20:24 +00:00
// This function saves the XML tree `node` to a string buffer.
//
// The callback function `save_cb` specifies a function that returns a
// whitespace string or `NULL` before and after each element. The function
// receives the callback data pointer `save_cbdata`, the `mxml_node_t` pointer,
// and a "when" value indicating where the whitespace is being added, for
// example:
2024-02-27 20:04:27 +00:00
//
2024-03-17 02:20:24 +00:00
// ```c
// const char *my_save_cb(void *cbdata, mxml_node_t *node, mxml_ws_t when)
// {
// if (when == MXML_WS_BEFORE_OPEN || when == MXML_WS_AFTER_CLOSE)
// return ("\n");
// else
// return (NULL);
// }
// ```
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
size_t // O - Size of string
mxmlSaveString (
mxml_node_t * node , // I - Node to write
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
2024-03-06 19:45:10 +00:00
char * buffer , // I - String buffer
2024-03-19 01:46:14 +00:00
size_t bufsize ) // I - Size of string buffer
2007-04-23 21:48:03 +00:00
{
2024-03-06 19:45:10 +00:00
_mxml_stringbuf_t sb ; // String buffer
2007-04-23 21:48:03 +00:00
2024-03-06 19:45:10 +00:00
// Setup the string buffer...
sb . buffer = buffer ;
sb . bufptr = buffer ;
sb . bufsize = bufsize ;
sb . bufalloc = false ;
2024-02-27 20:04:27 +00:00
2024-03-06 19:45:10 +00:00
// Write the node...
2024-03-19 01:46:14 +00:00
if ( mxml_write_node ( node , options , ( mxml_io_cb_t ) mxml_io_cb_string , & sb , 0 ) < 0 )
2024-03-06 19:45:10 +00:00
return ( false ) ;
// Nul-terminate the string...
if ( sb . bufptr < ( sb . buffer + sb . bufsize ) )
* ( sb . bufptr ) = ' \0 ' ;
// Return the number of characters...
return ( ( size_t ) ( sb . bufptr - sb . buffer ) ) ;
2007-04-23 21:48:03 +00:00
}
2024-02-27 20:04:27 +00:00
//
// 'mxml_add_char()' - Add a character to a buffer, expanding as needed.
//
2003-06-19 03:20:41 +00:00
2024-03-06 19:45:10 +00:00
static bool // O - `true` on success, `false` on error
2024-03-19 01:46:14 +00:00
mxml_add_char ( mxml_options_t * options , // I - Options
int ch , // I - Character to add
char * * bufptr , // IO - Current position in buffer
char * * buffer , // IO - Current buffer
size_t * bufsize ) // IO - Current buffer size
2003-06-19 03:20:41 +00:00
{
2024-02-27 20:04:27 +00:00
char * newbuffer ; // New buffer value
2003-06-19 03:20:41 +00:00
2003-12-21 15:01:15 +00:00
if ( * bufptr > = ( * buffer + * bufsize - 4 ) )
2003-06-19 03:20:41 +00:00
{
2024-02-27 20:04:27 +00:00
// Increase the size of the buffer...
2003-06-19 03:20:41 +00:00
if ( * bufsize < 1024 )
( * bufsize ) * = 2 ;
else
( * bufsize ) + = 1024 ;
if ( ( newbuffer = realloc ( * buffer , * bufsize ) ) = = NULL )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to expand string buffer to %lu bytes. " , ( unsigned long ) * bufsize ) ;
2003-06-19 03:20:41 +00:00
2024-03-06 19:45:10 +00:00
return ( false ) ;
2003-06-19 03:20:41 +00:00
}
* bufptr = newbuffer + ( * bufptr - * buffer ) ;
2003-07-23 14:47:17 +00:00
* buffer = newbuffer ;
2003-06-19 03:20:41 +00:00
}
2004-10-28 01:07:00 +00:00
if ( ch < 0x80 )
2003-12-21 15:01:15 +00:00
{
2024-02-27 20:04:27 +00:00
// Single byte ASCII...
2003-12-21 15:01:15 +00:00
* ( * bufptr ) + + = ch ;
}
2004-10-28 01:07:00 +00:00
else if ( ch < 0x800 )
2003-12-21 15:01:15 +00:00
{
2024-02-27 20:04:27 +00:00
// Two-byte UTF-8...
2003-12-21 15:01:15 +00:00
* ( * bufptr ) + + = 0xc0 | ( ch > > 6 ) ;
* ( * bufptr ) + + = 0x80 | ( ch & 0x3f ) ;
}
2004-10-28 01:07:00 +00:00
else if ( ch < 0x10000 )
2003-12-21 15:01:15 +00:00
{
2024-02-27 20:04:27 +00:00
// Three-byte UTF-8...
2003-12-21 15:01:15 +00:00
* ( * bufptr ) + + = 0xe0 | ( ch > > 12 ) ;
* ( * bufptr ) + + = 0x80 | ( ( ch > > 6 ) & 0x3f ) ;
* ( * bufptr ) + + = 0x80 | ( ch & 0x3f ) ;
}
else
{
2024-02-27 20:04:27 +00:00
// Four-byte UTF-8...
2003-12-21 15:01:15 +00:00
* ( * bufptr ) + + = 0xf0 | ( ch > > 18 ) ;
* ( * bufptr ) + + = 0x80 | ( ( ch > > 12 ) & 0x3f ) ;
* ( * bufptr ) + + = 0x80 | ( ( ch > > 6 ) & 0x3f ) ;
* ( * bufptr ) + + = 0x80 | ( ch & 0x3f ) ;
}
2003-06-19 03:20:41 +00:00
2024-03-06 19:45:10 +00:00
return ( true ) ;
2003-06-19 03:20:41 +00:00
}
2024-02-27 20:04:27 +00:00
//
2024-03-06 19:45:10 +00:00
// 'mxml_get_entity()' - Get the character corresponding to an entity...
2024-02-27 20:04:27 +00:00
//
2003-12-21 15:01:15 +00:00
2024-03-06 19:45:10 +00:00
static int // O - Character value or `EOF` on error
mxml_get_entity (
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
mxml_io_cb_t io_cb , // I - Read callback function
void * io_cbdata , // I - Read callback data
2024-03-06 19:45:10 +00:00
_mxml_encoding_t * encoding , // IO - Character encoding
mxml_node_t * parent , // I - Parent node
int * line ) // IO - Current line number
2003-12-21 15:01:15 +00:00
{
2024-03-06 19:45:10 +00:00
int ch ; // Current character
char entity [ 64 ] , // Entity string
* entptr ; // Pointer into entity
// Read a HTML character entity of the form "&NAME;", "&#NUMBER;", or "&#xHEX"...
entptr = entity ;
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , encoding ) ) ! = EOF )
2024-03-06 19:45:10 +00:00
{
if ( ch > 126 | | ( ! isalnum ( ch ) & & ch ! = ' # ' ) )
{
break ;
}
else if ( entptr < ( entity + sizeof ( entity ) - 1 ) )
{
* entptr + + = ch ;
}
else
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Entity name too long under parent <%s> on line %d. " , mxmlGetElement ( parent ) , * line ) ;
2024-03-06 19:45:10 +00:00
break ;
}
}
* entptr = ' \0 ' ;
if ( ch ! = ' ; ' )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Character entity '%s' not terminated under parent <%s> on line %d. " , entity , mxmlGetElement ( parent ) , * line ) ;
2003-12-21 15:01:15 +00:00
2024-03-06 19:45:10 +00:00
if ( ch = = ' \n ' )
( * line ) + + ;
return ( EOF ) ;
}
2003-12-21 15:01:15 +00:00
2024-03-19 01:46:14 +00:00
if ( ( ch = _mxml_entity_value ( options , entity ) ) < 0 )
2024-03-06 19:45:10 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Entity '&%s;' not supported under parent <%s> on line %d. " , entity , mxmlGetElement ( parent ) , * line ) ;
2024-03-06 23:18:29 +00:00
return ( EOF ) ;
2024-03-06 19:45:10 +00:00
}
2003-12-21 15:01:15 +00:00
2024-03-06 19:45:10 +00:00
if ( mxml_bad_char ( ch ) )
2024-02-27 20:04:27 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Bad control character 0x%02x under parent <%s> on line %d not allowed by XML standard. " , ch , mxmlGetElement ( parent ) , * line ) ;
2024-03-06 19:45:10 +00:00
return ( EOF ) ;
2024-02-27 20:04:27 +00:00
}
2003-12-21 15:01:15 +00:00
2024-03-06 19:45:10 +00:00
return ( ch ) ;
}
//
// 'mxml_getc()' - Read a character from a file descriptor.
//
static int // O - Character or `EOF`
2024-03-19 01:46:14 +00:00
mxml_getc ( mxml_options_t * options , // I - Options
mxml_io_cb_t io_cb , // I - Read callback function
void * io_cbdata , // I - Read callback data
2024-03-06 19:45:10 +00:00
_mxml_encoding_t * encoding ) // IO - Encoding
{
int ch ; // Current character
unsigned char buffer [ 4 ] ; // Read buffer
// Grab the next character...
read_first_byte :
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer , 1 ) ! = 1 )
2024-03-06 19:45:10 +00:00
return ( EOF ) ;
ch = buffer [ 0 ] ;
2004-07-11 13:14:07 +00:00
switch ( * encoding )
2003-12-21 15:01:15 +00:00
{
2024-03-06 19:45:10 +00:00
case _MXML_ENCODING_UTF8 :
2024-02-27 20:04:27 +00:00
// Got a UTF-8 character; convert UTF-8 to Unicode and return...
2004-07-11 13:14:07 +00:00
if ( ! ( ch & 0x80 ) )
2005-01-29 17:03:33 +00:00
{
2024-03-06 19:45:10 +00:00
// ASCII
break ;
2005-01-29 17:03:33 +00:00
}
else if ( ch = = 0xfe )
2004-07-11 13:14:07 +00:00
{
2024-02-27 20:04:27 +00:00
// UTF-16 big-endian BOM?
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer + 1 , 1 ) ! = 1 )
2024-03-06 19:45:10 +00:00
return ( EOF ) ;
2013-11-12 05:03:47 +00:00
2024-03-06 19:45:10 +00:00
if ( buffer [ 1 ] ! = 0xff )
2004-07-11 13:14:07 +00:00
return ( EOF ) ;
2024-03-06 19:45:10 +00:00
// Yes, switch to UTF-16 BE and try reading again...
* encoding = _MXML_ENCODING_UTF16BE ;
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
goto read_first_byte ;
2004-07-11 13:14:07 +00:00
}
else if ( ch = = 0xff )
{
2024-02-27 20:04:27 +00:00
// UTF-16 little-endian BOM?
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer + 1 , 1 ) ! = 1 )
2024-03-06 19:45:10 +00:00
return ( EOF ) ;
2013-11-12 05:03:47 +00:00
2024-03-06 19:45:10 +00:00
if ( buffer [ 1 ] ! = 0xfe )
2004-07-11 13:14:07 +00:00
return ( EOF ) ;
2024-03-06 19:45:10 +00:00
// Yes, switch to UTF-16 LE and try reading again...
* encoding = _MXML_ENCODING_UTF16LE ;
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
goto read_first_byte ;
2004-07-11 13:14:07 +00:00
}
else if ( ( ch & 0xe0 ) = = 0xc0 )
{
2024-02-27 20:04:27 +00:00
// Two-byte value...
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer + 1 , 1 ) ! = 1 )
2024-03-06 19:45:10 +00:00
return ( EOF ) ;
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
if ( ( buffer [ 1 ] & 0xc0 ) ! = 0x80 )
2004-07-11 13:14:07 +00:00
return ( EOF ) ;
2024-03-06 19:45:10 +00:00
ch = ( ( ch & 0x1f ) < < 6 ) | ( buffer [ 1 ] & 0x3f ) ;
2004-10-28 01:07:00 +00:00
if ( ch < 0x80 )
2009-03-19 05:27:26 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Invalid UTF-8 sequence for character 0x%04x. " , ch ) ;
2004-10-28 01:07:00 +00:00
return ( EOF ) ;
2009-03-19 05:27:26 +00:00
}
2004-07-11 13:14:07 +00:00
}
else if ( ( ch & 0xf0 ) = = 0xe0 )
{
2024-02-27 20:04:27 +00:00
// Three-byte value...
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer + 1 , 2 ) ! = 2 )
2024-03-06 19:45:10 +00:00
return ( EOF ) ;
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
if ( ( buffer [ 1 ] & 0xc0 ) ! = 0x80 | | ( buffer [ 2 ] & 0xc0 ) ! = 0x80 )
2004-07-11 13:14:07 +00:00
return ( EOF ) ;
2024-03-06 19:45:10 +00:00
ch = ( ( ch & 0x0f ) < < 12 ) | ( ( buffer [ 1 ] & 0x3f ) < < 6 ) | ( buffer [ 2 ] & 0x3f ) ;
2004-10-28 01:07:00 +00:00
if ( ch < 0x800 )
2009-03-19 05:27:26 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Invalid UTF-8 sequence for character 0x%04x. " , ch ) ;
2009-03-19 05:27:26 +00:00
return ( EOF ) ;
}
2024-02-27 20:04:27 +00:00
// Ignore (strip) Byte Order Mark (BOM)...
2009-05-17 05:20:52 +00:00
if ( ch = = 0xfeff )
2024-03-06 19:45:10 +00:00
goto read_first_byte ;
2004-07-11 13:14:07 +00:00
}
else if ( ( ch & 0xf8 ) = = 0xf0 )
{
2024-02-27 20:04:27 +00:00
// Four-byte value...
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer + 1 , 3 ) ! = 3 )
2004-07-11 13:14:07 +00:00
return ( EOF ) ;
2024-03-06 19:45:10 +00:00
if ( ( buffer [ 1 ] & 0xc0 ) ! = 0x80 | | ( buffer [ 2 ] & 0xc0 ) ! = 0x80 | | ( buffer [ 3 ] & 0xc0 ) ! = 0x80 )
2004-07-11 13:14:07 +00:00
return ( EOF ) ;
2024-03-06 19:45:10 +00:00
ch = ( ( ch & 0x07 ) < < 18 ) | ( ( buffer [ 1 ] & 0x3f ) < < 12 ) | ( ( buffer [ 2 ] & 0x3f ) < < 6 ) | ( buffer [ 3 ] & 0x3f ) ;
2004-10-28 01:07:00 +00:00
if ( ch < 0x10000 )
2009-03-19 05:27:26 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Invalid UTF-8 sequence for character 0x%04x. " , ch ) ;
2004-10-28 01:07:00 +00:00
return ( EOF ) ;
2009-03-19 05:27:26 +00:00
}
2004-07-11 13:14:07 +00:00
}
else
2024-02-27 20:04:27 +00:00
{
2004-07-11 13:14:07 +00:00
return ( EOF ) ;
2024-02-27 20:04:27 +00:00
}
2004-07-11 13:14:07 +00:00
break ;
2024-03-06 19:45:10 +00:00
case _MXML_ENCODING_UTF16BE :
2024-02-27 20:04:27 +00:00
// Read UTF-16 big-endian char...
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer + 1 , 1 ) ! = 1 )
2005-01-29 17:03:33 +00:00
return ( EOF ) ;
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
ch = ( ch < < 8 ) | buffer [ 1 ] ;
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
if ( ch > = 0xd800 & & ch < = 0xdbff )
2004-07-11 13:14:07 +00:00
{
2024-02-27 20:04:27 +00:00
// Multi-word UTF-16 char...
2024-03-06 19:45:10 +00:00
int lch ; // Lower bits
2004-07-11 13:14:07 +00:00
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer + 2 , 2 ) ! = 2 )
2024-03-06 19:45:10 +00:00
return ( EOF ) ;
2004-07-11 13:14:07 +00:00
2024-03-06 19:45:10 +00:00
lch = ( buffer [ 2 ] < < 8 ) | buffer [ 3 ] ;
2004-07-11 13:14:07 +00:00
if ( lch < 0xdc00 | | lch > = 0xdfff )
return ( EOF ) ;
ch = ( ( ( ch & 0x3ff ) < < 10 ) | ( lch & 0x3ff ) ) + 0x10000 ;
}
break ;
2003-12-21 15:01:15 +00:00
2024-03-06 19:45:10 +00:00
case _MXML_ENCODING_UTF16LE :
// Read UTF-16 little-endian char...
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer + 1 , 1 ) ! = 1 )
2004-05-16 21:54:47 +00:00
return ( EOF ) ;
2024-03-06 19:45:10 +00:00
ch | = buffer [ 1 ] < < 8 ;
2003-12-21 15:01:15 +00:00
2024-03-06 19:45:10 +00:00
if ( ch > = 0xd800 & & ch < = 0xdbff )
2004-05-16 21:54:47 +00:00
{
2024-02-27 20:04:27 +00:00
// Multi-word UTF-16 char...
2024-03-06 19:45:10 +00:00
int lch ; // Lower bits
2003-12-21 15:01:15 +00:00
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , buffer + 2 , 2 ) ! = 2 )
2004-05-16 21:54:47 +00:00
return ( EOF ) ;
2003-12-21 15:01:15 +00:00
2024-03-06 19:45:10 +00:00
lch = ( buffer [ 3 ] < < 8 ) | buffer [ 2 ] ;
2004-05-16 21:54:47 +00:00
2004-07-11 13:14:07 +00:00
if ( lch < 0xdc00 | | lch > = 0xdfff )
2004-05-16 21:54:47 +00:00
return ( EOF ) ;
ch = ( ( ( ch & 0x3ff ) < < 10 ) | ( lch & 0x3ff ) ) + 0x10000 ;
}
break ;
2003-12-21 15:01:15 +00:00
}
2024-03-06 19:45:10 +00:00
// MXML_DEBUG("mxml_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch);
2004-07-11 13:14:07 +00:00
2005-01-29 17:03:33 +00:00
if ( mxml_bad_char ( ch ) )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Bad control character 0x%02x not allowed by XML standard. " , ch ) ;
2005-01-29 17:03:33 +00:00
return ( EOF ) ;
}
2004-07-11 13:14:07 +00:00
return ( ch ) ;
}
2024-02-27 20:04:27 +00:00
//
// 'mxml_load_data()' - Load data into an XML node tree.
//
2003-06-19 03:20:41 +00:00
2024-03-06 19:45:10 +00:00
static mxml_node_t * // O - First node or `NULL` if the XML could not be read.
2007-04-23 21:48:03 +00:00
mxml_load_data (
2024-02-27 20:04:27 +00:00
mxml_node_t * top , // I - Top node
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
mxml_io_cb_t io_cb , // I - Read callback function
void * io_cbdata ) // I - Read callback data
2003-06-03 19:46:29 +00:00
{
2024-02-27 20:04:27 +00:00
mxml_node_t * node = NULL , // Current node
* first = NULL , // First node added
* parent = NULL ; // Current parent node
int line = 1 , // Current line number
2024-03-02 23:47:57 +00:00
ch ; // Character from file
2024-03-06 19:45:10 +00:00
bool whitespace = false ; // Whitespace seen?
2024-02-27 20:04:27 +00:00
char * buffer , // String buffer
* bufptr ; // Pointer into buffer
2024-03-06 19:45:10 +00:00
size_t bufsize ; // Size of buffer
2024-02-27 20:04:27 +00:00
mxml_type_t type ; // Current node type
2024-03-06 19:45:10 +00:00
_mxml_encoding_t encoding = _MXML_ENCODING_UTF8 ;
// Character encoding
2024-02-27 20:04:27 +00:00
static const char * const types [ ] = // Type strings...
2005-05-18 01:45:20 +00:00
{
2024-03-02 23:47:57 +00:00
" MXML_TYPE_CDATA " , // CDATA
" MXML_TYPE_COMMENT " , // Comment
" MXML_TYPE_DECLARATION " , // Declaration
" MXML_TYPE_DIRECTIVE " , // Processing instruction/directive
2024-02-27 20:04:27 +00:00
" MXML_TYPE_ELEMENT " , // XML element with attributes
" MXML_TYPE_INTEGER " , // Integer value
" MXML_TYPE_OPAQUE " , // Opaque string
" MXML_TYPE_REAL " , // Real value
" MXML_TYPE_TEXT " , // Text fragment
" MXML_TYPE_CUSTOM " // Custom data
2005-05-18 01:45:20 +00:00
} ;
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
// Read elements and other nodes from the file...
2003-06-15 21:31:45 +00:00
if ( ( buffer = malloc ( 64 ) ) = = NULL )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to allocate string buffer. " ) ;
2003-06-15 21:31:45 +00:00
return ( NULL ) ;
}
bufsize = 64 ;
2003-06-03 19:46:29 +00:00
bufptr = buffer ;
parent = top ;
2005-05-18 01:45:20 +00:00
first = NULL ;
2003-06-03 19:46:29 +00:00
2024-03-19 01:46:14 +00:00
if ( options & & options - > type_cb & & parent )
type = ( options - > type_cb ) ( options - > type_cbdata , parent ) ;
else if ( options & & ! options - > type_cb )
type = options - > type_value ;
2014-01-05 03:21:00 +00:00
else
2024-02-27 20:04:27 +00:00
type = MXML_TYPE_IGNORE ;
2003-06-03 19:46:29 +00:00
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_getc ( options , io_cb , io_cbdata , & encoding ) ) = = EOF )
2019-07-04 02:27:11 +00:00
{
2019-07-08 18:15:19 +00:00
free ( buffer ) ;
2019-07-04 02:27:11 +00:00
return ( NULL ) ;
}
else if ( ch ! = ' < ' & & ! top )
{
2019-07-08 18:15:19 +00:00
free ( buffer ) ;
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " XML does not start with '<' (saw '%c'). " , ch ) ;
2019-07-04 02:27:11 +00:00
return ( NULL ) ;
}
do
2003-06-03 19:46:29 +00:00
{
2024-03-02 23:47:57 +00:00
if ( ( ch = = ' < ' | | ( mxml_isspace ( ch ) & & type ! = MXML_TYPE_OPAQUE & & type ! = MXML_TYPE_CUSTOM ) ) & & bufptr > buffer )
2003-06-03 19:46:29 +00:00
{
2024-02-27 20:04:27 +00:00
// Add a new value node...
2003-06-03 19:46:29 +00:00
* bufptr = ' \0 ' ;
switch ( type )
{
2024-02-27 20:04:27 +00:00
case MXML_TYPE_INTEGER :
2024-03-02 23:47:57 +00:00
node = mxmlNewInteger ( parent , strtol ( buffer , & bufptr , 0 ) ) ;
2003-06-03 19:46:29 +00:00
break ;
2024-02-27 20:04:27 +00:00
case MXML_TYPE_OPAQUE :
2003-06-03 19:46:29 +00:00
node = mxmlNewOpaque ( parent , buffer ) ;
break ;
2024-02-27 20:04:27 +00:00
case MXML_TYPE_REAL :
2024-03-19 01:46:14 +00:00
node = mxmlNewReal ( parent , mxml_strtod ( options , buffer , & bufptr ) ) ;
2003-06-03 19:46:29 +00:00
break ;
2024-02-27 20:04:27 +00:00
case MXML_TYPE_TEXT :
2003-06-03 19:46:29 +00:00
node = mxmlNewText ( parent , whitespace , buffer ) ;
break ;
2024-02-27 20:04:27 +00:00
case MXML_TYPE_CUSTOM :
2024-03-19 01:46:14 +00:00
if ( options & & options - > custload_cb )
2004-10-28 02:58:01 +00:00
{
2024-02-27 20:04:27 +00:00
// Use the callback to fill in the custom data...
2024-03-19 01:46:14 +00:00
node = mxmlNewCustom ( parent , /*data*/ NULL , /*free_cb*/ NULL , /*free_cbdata*/ NULL ) ;
2004-10-28 02:58:01 +00:00
2024-03-19 01:46:14 +00:00
if ( ! ( options - > custload_cb ) ( options - > cust_cbdata , node , buffer ) )
2004-10-28 02:58:01 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Bad custom value '%s' in parent <%s> on line %d. " , buffer , parent ? parent - > value . element . name : " null " , line ) ;
2004-10-28 02:58:01 +00:00
mxmlDelete ( node ) ;
node = NULL ;
}
break ;
}
2024-02-27 20:04:27 +00:00
default : // Ignore...
2003-06-03 19:46:29 +00:00
node = NULL ;
break ;
2013-11-12 05:03:47 +00:00
}
2003-06-03 19:46:29 +00:00
2003-06-04 02:34:30 +00:00
if ( * bufptr )
{
2024-02-27 20:04:27 +00:00
// Bad integer/real number value...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Bad %s value '%s' in parent <%s> on line %d. " , type = = MXML_TYPE_INTEGER ? " integer " : " real " , buffer , parent ? parent - > value . element . name : " null " , line ) ;
2003-06-04 02:34:30 +00:00
break ;
}
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_load_data: node=%p(%s), parent=%p \n " , node , buffer , parent ) ;
2003-06-04 02:34:30 +00:00
bufptr = buffer ;
2024-02-27 20:04:27 +00:00
whitespace = mxml_isspace ( ch ) & & type = = MXML_TYPE_TEXT ;
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
if ( ! node & & type ! = MXML_TYPE_IGNORE )
2003-06-03 19:46:29 +00:00
{
2024-02-27 20:04:27 +00:00
// Print error and return...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to add value node of type %s to parent <%s> on line %d. " , types [ type ] , parent ? parent - > value . element . name : " null " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
2005-05-18 01:45:20 +00:00
2024-03-19 01:46:14 +00:00
if ( options & & options - > sax_cb )
2007-04-23 21:48:03 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ! ( options - > sax_cb ) ( options - > sax_cbdata , node , MXML_SAX_EVENT_DATA ) )
2024-03-04 12:37:33 +00:00
goto error ;
2007-04-23 21:48:03 +00:00
if ( ! mxmlRelease ( node ) )
node = NULL ;
}
2005-06-07 23:43:45 +00:00
if ( ! first & & node )
2005-05-18 01:45:20 +00:00
first = node ;
2003-06-03 19:46:29 +00:00
}
2024-02-27 20:04:27 +00:00
else if ( mxml_isspace ( ch ) & & type = = MXML_TYPE_TEXT )
{
2024-03-02 23:47:57 +00:00
whitespace = true ;
2024-02-27 20:04:27 +00:00
}
2003-06-04 16:30:40 +00:00
2019-01-05 17:23:17 +00:00
if ( ch = = ' \n ' )
line + + ;
2024-02-27 20:04:27 +00:00
// Add lone whitespace node if we have an element and existing whitespace...
if ( ch = = ' < ' & & whitespace & & type = = MXML_TYPE_TEXT )
2003-06-04 16:30:40 +00:00
{
2011-03-24 05:47:51 +00:00
if ( parent )
2007-04-23 21:48:03 +00:00
{
2011-03-24 05:47:51 +00:00
node = mxmlNewText ( parent , whitespace , " " ) ;
2007-04-23 21:48:03 +00:00
2024-03-19 01:46:14 +00:00
if ( options & & options - > sax_cb )
2011-03-24 05:47:51 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ! ( options - > sax_cb ) ( options - > sax_cbdata , node , MXML_SAX_EVENT_DATA ) )
2024-03-04 12:37:33 +00:00
goto error ;
2007-04-23 21:48:03 +00:00
2011-03-24 05:47:51 +00:00
if ( ! mxmlRelease ( node ) )
node = NULL ;
}
if ( ! first & & node )
first = node ;
}
2005-08-16 14:46:18 +00:00
2024-03-02 23:47:57 +00:00
whitespace = false ;
2003-06-04 16:30:40 +00:00
}
2003-06-03 19:46:29 +00:00
if ( ch = = ' < ' )
{
2024-02-27 20:04:27 +00:00
// Start of open/close tag...
2003-06-03 19:46:29 +00:00
bufptr = buffer ;
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , & encoding ) ) ! = EOF )
2019-01-05 17:23:17 +00:00
{
2007-09-09 07:27:08 +00:00
if ( mxml_isspace ( ch ) | | ch = = ' > ' | | ( ch = = ' / ' & & bufptr > buffer ) )
2024-02-27 20:04:27 +00:00
{
2003-06-03 19:46:29 +00:00
break ;
2024-02-27 20:04:27 +00:00
}
2007-04-18 02:45:47 +00:00
else if ( ch = = ' < ' )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Bare < in element. " ) ;
2007-04-18 02:45:47 +00:00
goto error ;
}
2003-12-21 15:01:15 +00:00
else if ( ch = = ' & ' )
2003-06-04 16:30:40 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_get_entity ( options , io_cb , io_cbdata , & encoding , parent , & line ) ) = = EOF )
2003-12-21 15:01:15 +00:00
goto error ;
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & bufptr , & buffer , & bufsize ) )
2003-12-21 15:01:15 +00:00
goto error ;
2003-06-15 21:31:45 +00:00
}
2016-06-13 00:51:16 +00:00
else if ( ch < ' 0 ' & & ch ! = ' ! ' & & ch ! = ' - ' & & ch ! = ' . ' & & ch ! = ' / ' )
2024-02-27 20:04:27 +00:00
{
2016-06-13 00:51:16 +00:00
goto error ;
2024-02-27 20:04:27 +00:00
}
2024-03-19 01:46:14 +00:00
else if ( ! mxml_add_char ( options , ch , & bufptr , & buffer , & bufsize ) )
2024-02-27 20:04:27 +00:00
{
2003-12-21 15:01:15 +00:00
goto error ;
2024-02-27 20:04:27 +00:00
}
else if ( ( ( bufptr - buffer ) = = 1 & & buffer [ 0 ] = = ' ? ' ) | | ( ( bufptr - buffer ) = = 3 & & ! strncmp ( buffer , " !-- " , 3 ) ) | | ( ( bufptr - buffer ) = = 8 & & ! strncmp ( buffer , " ![CDATA[ " , 8 ) ) )
{
2003-06-15 21:31:45 +00:00
break ;
2024-02-27 20:04:27 +00:00
}
2003-06-04 16:30:40 +00:00
2019-01-05 17:23:17 +00:00
if ( ch = = ' \n ' )
line + + ;
}
2003-06-03 19:46:29 +00:00
* bufptr = ' \0 ' ;
2019-07-08 18:15:19 +00:00
if ( ! strcmp ( buffer , " !-- " ) )
2003-06-04 01:23:21 +00:00
{
2024-02-27 20:04:27 +00:00
// Gather rest of comment...
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , & encoding ) ) ! = EOF )
2003-06-04 01:23:21 +00:00
{
2024-02-27 20:04:27 +00:00
if ( ch = = ' > ' & & bufptr > ( buffer + 4 ) & & bufptr [ - 3 ] ! = ' - ' & & bufptr [ - 2 ] = = ' - ' & & bufptr [ - 1 ] = = ' - ' )
2003-06-04 16:30:40 +00:00
break ;
2024-03-19 01:46:14 +00:00
else if ( ! mxml_add_char ( options , ch , & bufptr , & buffer , & bufsize ) )
2005-01-29 07:19:38 +00:00
goto error ;
2019-01-05 17:23:17 +00:00
if ( ch = = ' \n ' )
line + + ;
2005-01-29 07:19:38 +00:00
}
2003-12-21 15:01:15 +00:00
2024-02-27 20:04:27 +00:00
// Error out if we didn't get the whole comment...
2005-01-29 07:19:38 +00:00
if ( ch ! = ' > ' )
2005-05-18 01:45:20 +00:00
{
2024-02-27 20:04:27 +00:00
// Print error and return...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Early EOF in comment node on line %d. " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
}
2024-02-27 20:04:27 +00:00
// Otherwise add this as an element under the current parent...
2024-03-02 23:47:57 +00:00
bufptr [ - 2 ] = ' \0 ' ;
2005-01-29 07:19:38 +00:00
2010-09-19 06:14:36 +00:00
if ( ! parent & & first )
{
2024-02-27 20:04:27 +00:00
// There can only be one root element!
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " <%s--> cannot be a second root node after <%s> on line %d. " , buffer , first - > value . element . name , line ) ;
2013-11-12 05:03:47 +00:00
goto error ;
2010-09-19 06:14:36 +00:00
}
2024-03-02 23:47:57 +00:00
if ( ( node = mxmlNewComment ( parent , buffer + 3 ) ) = = NULL )
2005-01-29 07:19:38 +00:00
{
2024-02-27 20:04:27 +00:00
// Just print error for now...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to add comment node to parent <%s> on line %d. " , parent ? parent - > value . element . name : " null " , line ) ;
2005-01-29 07:19:38 +00:00
break ;
}
2005-08-16 14:46:18 +00:00
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_load_data: node=%p(<%s-->), parent=%p \n " , node , buffer , parent ) ;
2024-03-19 01:46:14 +00:00
if ( options & & options - > sax_cb )
2007-04-23 21:48:03 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ! ( options - > sax_cb ) ( options - > sax_cbdata , node , MXML_SAX_EVENT_COMMENT ) )
2024-03-04 12:37:33 +00:00
goto error ;
2007-04-23 21:48:03 +00:00
if ( ! mxmlRelease ( node ) )
node = NULL ;
}
2007-09-15 20:03:15 +00:00
if ( node & & ! first )
2005-08-16 14:46:18 +00:00
first = node ;
2005-01-29 07:19:38 +00:00
}
else if ( ! strcmp ( buffer , " ![CDATA[ " ) )
{
2024-02-27 20:04:27 +00:00
// Gather CDATA section...
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , & encoding ) ) ! = EOF )
2005-01-29 07:19:38 +00:00
{
if ( ch = = ' > ' & & ! strncmp ( bufptr - 2 , " ]] " , 2 ) )
2017-03-30 01:42:30 +00:00
{
2024-02-27 20:04:27 +00:00
// Drop terminator from CDATA string...
2017-03-30 01:42:30 +00:00
bufptr [ - 2 ] = ' \0 ' ;
2005-01-29 07:19:38 +00:00
break ;
2017-03-30 01:42:30 +00:00
}
2024-03-19 01:46:14 +00:00
else if ( ! mxml_add_char ( options , ch , & bufptr , & buffer , & bufsize ) )
2024-02-27 20:04:27 +00:00
{
2005-01-29 07:19:38 +00:00
goto error ;
2024-02-27 20:04:27 +00:00
}
2019-01-05 17:23:17 +00:00
if ( ch = = ' \n ' )
line + + ;
2003-06-04 16:30:40 +00:00
}
2003-06-04 01:23:21 +00:00
2024-02-27 20:04:27 +00:00
// Error out if we didn't get the whole comment...
2003-06-04 16:30:40 +00:00
if ( ch ! = ' > ' )
2005-05-18 01:45:20 +00:00
{
2024-02-27 20:04:27 +00:00
// Print error and return...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Early EOF in CDATA node on line %d. " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
}
2024-02-27 20:04:27 +00:00
// Otherwise add this as an element under the current parent...
2024-03-02 23:47:57 +00:00
bufptr [ - 2 ] = ' \0 ' ;
2003-06-04 16:30:40 +00:00
2010-09-19 06:14:36 +00:00
if ( ! parent & & first )
{
2024-02-27 20:04:27 +00:00
// There can only be one root element!
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " <%s]]> cannot be a second root node after <%s> on line %d. " , buffer , first - > value . element . name , line ) ;
2013-11-12 05:03:47 +00:00
goto error ;
2010-09-19 06:14:36 +00:00
}
2024-03-02 23:47:57 +00:00
if ( ( node = mxmlNewCDATA ( parent , buffer + 8 ) ) = = NULL )
2003-06-04 16:30:40 +00:00
{
2024-02-27 20:04:27 +00:00
// Print error and return...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to add CDATA node to parent <%s> on line %d. " , parent ? parent - > value . element . name : " null " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
2003-06-04 16:30:40 +00:00
}
2005-08-16 14:46:18 +00:00
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_load_data: node=%p(<%s]]>), parent=%p \n " , node , buffer , parent ) ;
2024-03-19 01:46:14 +00:00
if ( options & & options - > sax_cb )
2007-04-23 21:48:03 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ! ( options - > sax_cb ) ( options - > sax_cbdata , node , MXML_SAX_EVENT_CDATA ) )
2024-03-04 12:37:33 +00:00
goto error ;
2007-04-23 21:48:03 +00:00
if ( ! mxmlRelease ( node ) )
node = NULL ;
}
2007-09-15 20:03:15 +00:00
if ( node & & ! first )
2005-08-16 14:46:18 +00:00
first = node ;
2003-06-04 16:30:40 +00:00
}
2005-01-29 07:19:38 +00:00
else if ( buffer [ 0 ] = = ' ? ' )
{
2024-02-27 20:04:27 +00:00
// Gather rest of processing instruction...
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , & encoding ) ) ! = EOF )
2005-01-29 07:19:38 +00:00
{
if ( ch = = ' > ' & & bufptr > buffer & & bufptr [ - 1 ] = = ' ? ' )
break ;
2024-03-19 01:46:14 +00:00
else if ( ! mxml_add_char ( options , ch , & bufptr , & buffer , & bufsize ) )
2005-01-29 07:19:38 +00:00
goto error ;
2019-01-05 17:23:17 +00:00
if ( ch = = ' \n ' )
line + + ;
2005-01-29 07:19:38 +00:00
}
2024-02-27 20:04:27 +00:00
// Error out if we didn't get the whole processing instruction...
2005-01-29 07:19:38 +00:00
if ( ch ! = ' > ' )
2005-05-18 01:45:20 +00:00
{
2024-02-27 20:04:27 +00:00
// Print error and return...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Early EOF in processing instruction node on line %d. " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
}
2024-02-27 20:04:27 +00:00
// Otherwise add this as an element under the current parent...
2024-03-02 23:47:57 +00:00
bufptr [ - 1 ] = ' \0 ' ;
2005-01-29 07:19:38 +00:00
2019-07-08 18:15:19 +00:00
if ( ! parent & & first )
2010-09-19 06:14:36 +00:00
{
2024-02-27 20:04:27 +00:00
// There can only be one root element!
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " <%s?> cannot be a second root node after <%s> on line %d. " , buffer , first - > value . element . name , line ) ;
2013-11-12 05:03:47 +00:00
goto error ;
2010-09-19 06:14:36 +00:00
}
2024-03-02 23:47:57 +00:00
if ( ( node = mxmlNewDirective ( parent , buffer + 1 ) ) = = NULL )
2005-01-29 07:19:38 +00:00
{
2024-02-27 20:04:27 +00:00
// Print error and return...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to add processing instruction node to parent <%s> on line %d. " , parent ? parent - > value . element . name : " null " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
2005-01-29 07:19:38 +00:00
}
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_load_data: node=%p(<%s?>), parent=%p \n " , node , buffer , parent ) ;
2024-03-19 01:46:14 +00:00
if ( options & & options - > sax_cb )
2007-04-23 21:48:03 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ! ( options - > sax_cb ) ( options - > sax_cbdata , node , MXML_SAX_EVENT_DIRECTIVE ) )
2024-03-04 12:37:33 +00:00
goto error ;
2005-08-16 14:46:18 +00:00
2024-03-02 23:47:57 +00:00
if ( strncmp ( node - > value . directive , " xml " , 4 ) & & ! mxmlRelease ( node ) )
2007-04-23 21:48:03 +00:00
node = NULL ;
}
if ( node )
2007-09-15 20:03:15 +00:00
{
if ( ! first )
2008-01-13 00:42:35 +00:00
first = node ;
2007-04-23 21:48:03 +00:00
2008-01-13 00:42:35 +00:00
if ( ! parent )
{
parent = node ;
2007-04-23 21:48:03 +00:00
2024-03-19 01:46:14 +00:00
if ( options & & options - > type_cb )
type = ( options - > type_cb ) ( options - > type_cbdata , parent ) ;
else if ( options )
type = options - > type_value ;
2014-10-19 17:21:48 +00:00
else
2024-02-27 20:04:27 +00:00
type = MXML_TYPE_TEXT ;
2008-01-13 00:42:35 +00:00
}
}
2005-01-29 07:19:38 +00:00
}
2003-06-04 16:30:40 +00:00
else if ( buffer [ 0 ] = = ' ! ' )
{
2024-02-27 20:04:27 +00:00
// Gather rest of declaration...
2003-06-04 16:30:40 +00:00
do
{
if ( ch = = ' > ' )
2024-02-27 20:04:27 +00:00
{
2003-06-04 16:30:40 +00:00
break ;
2024-02-27 20:04:27 +00:00
}
2003-12-21 15:01:15 +00:00
else
2003-06-04 16:30:40 +00:00
{
2003-12-21 15:01:15 +00:00
if ( ch = = ' & ' )
2019-01-05 17:23:17 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_get_entity ( options , io_cb , io_cbdata , & encoding , parent , & line ) ) = = EOF )
2003-12-21 15:01:15 +00:00
goto error ;
2019-01-05 17:23:17 +00:00
}
2003-12-21 15:01:15 +00:00
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & bufptr , & buffer , & bufsize ) )
2003-12-21 15:01:15 +00:00
goto error ;
2003-06-04 01:23:21 +00:00
}
2019-01-05 17:23:17 +00:00
if ( ch = = ' \n ' )
line + + ;
2003-06-04 01:23:21 +00:00
}
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , & encoding ) ) ! = EOF ) ;
2003-06-04 01:23:21 +00:00
2024-02-27 20:04:27 +00:00
// Error out if we didn't get the whole declaration...
2003-06-04 16:30:40 +00:00
if ( ch ! = ' > ' )
2005-05-18 01:45:20 +00:00
{
2024-02-27 20:04:27 +00:00
// Print error and return...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Early EOF in declaration node on line %d. " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
}
2003-06-04 16:30:40 +00:00
2024-02-27 20:04:27 +00:00
// Otherwise add this as an element under the current parent...
2003-06-04 16:30:40 +00:00
* bufptr = ' \0 ' ;
2010-09-19 06:14:36 +00:00
if ( ! parent & & first )
{
2024-02-27 20:04:27 +00:00
// There can only be one root element!
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " <%s> cannot be a second root node after <%s> on line %d. " , buffer , first - > value . element . name , line ) ;
2013-11-12 05:03:47 +00:00
goto error ;
2010-09-19 06:14:36 +00:00
}
2024-03-02 23:47:57 +00:00
if ( ( node = mxmlNewDeclaration ( parent , buffer + 1 ) ) = = NULL )
2003-06-04 16:30:40 +00:00
{
2024-02-27 20:04:27 +00:00
// Print error and return...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to add declaration node to parent <%s> on line %d. " , parent ? parent - > value . element . name : " null " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
2003-06-04 16:30:40 +00:00
}
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_load_data: node=%p(<%s>), parent=%p \n " , node , buffer , parent ) ;
2024-03-19 01:46:14 +00:00
if ( options & & options - > sax_cb )
2007-04-23 21:48:03 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ! ( options - > sax_cb ) ( options - > sax_cbdata , node , MXML_SAX_EVENT_DECLARATION ) )
2024-03-04 12:37:33 +00:00
goto error ;
2005-08-16 14:46:18 +00:00
2007-04-23 21:48:03 +00:00
if ( ! mxmlRelease ( node ) )
node = NULL ;
}
2003-06-04 16:30:40 +00:00
2008-01-13 00:42:35 +00:00
if ( node )
{
if ( ! first )
first = node ;
if ( ! parent )
{
parent = node ;
2024-03-19 01:46:14 +00:00
if ( options & & options - > type_cb )
type = ( options - > type_cb ) ( options - > type_cbdata , parent ) ;
else if ( options )
type = options - > type_value ;
2014-10-19 17:21:48 +00:00
else
2024-02-27 20:04:27 +00:00
type = MXML_TYPE_TEXT ;
2008-01-13 00:42:35 +00:00
}
}
2003-06-04 01:23:21 +00:00
}
else if ( buffer [ 0 ] = = ' / ' )
2003-06-03 19:46:29 +00:00
{
2024-02-27 20:04:27 +00:00
// Handle close tag...
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_load_data: <%s>, parent=%p \n " , buffer , parent ) ;
2003-06-03 19:46:29 +00:00
if ( ! parent | | strcmp ( buffer + 1 , parent - > value . element . name ) )
{
2024-02-27 20:04:27 +00:00
// Close tag doesn't match tree; print an error for now...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Mismatched close tag <%s> under parent <%s> on line %d. " , buffer , parent ? parent - > value . element . name : " (null) " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
2024-02-27 20:04:27 +00:00
// Keep reading until we see >...
2003-06-03 19:46:29 +00:00
while ( ch ! = ' > ' & & ch ! = EOF )
2024-03-19 01:46:14 +00:00
ch = mxml_getc ( options , io_cb , io_cbdata , & encoding ) ;
2003-06-03 19:46:29 +00:00
2007-04-23 21:48:03 +00:00
node = parent ;
parent = parent - > parent ;
2024-03-19 01:46:14 +00:00
if ( options & & options - > sax_cb )
2007-04-23 21:48:03 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ! ( options - > sax_cb ) ( options - > sax_cbdata , node , MXML_SAX_EVENT_ELEMENT_CLOSE ) )
2024-03-04 12:37:33 +00:00
goto error ;
2007-04-23 21:48:03 +00:00
2021-10-26 18:34:33 +00:00
if ( ! mxmlRelease ( node ) )
{
if ( first = = node )
first = NULL ;
node = NULL ;
}
2007-04-23 21:48:03 +00:00
}
2024-02-27 20:04:27 +00:00
// Ascend into the parent and set the value type as needed...
2024-03-19 01:46:14 +00:00
if ( options & & options - > type_cb & & parent )
type = ( options - > type_cb ) ( options - > type_cbdata , parent ) ;
else if ( options & & ! options - > type_cb )
type = options - > type_value ;
2003-06-03 19:46:29 +00:00
}
else
{
2024-02-27 20:04:27 +00:00
// Handle open tag...
2010-09-19 06:14:36 +00:00
if ( ! parent & & first )
{
2024-02-27 20:04:27 +00:00
// There can only be one root element!
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " <%s> cannot be a second root node after <%s> on line %d. " , buffer , first - > value . element . name , line ) ;
2013-11-12 05:03:47 +00:00
goto error ;
2010-09-19 06:14:36 +00:00
}
2005-08-16 14:46:18 +00:00
if ( ( node = mxmlNewElement ( parent , buffer ) ) = = NULL )
2003-06-03 19:46:29 +00:00
{
2024-02-27 20:04:27 +00:00
// Just print error for now...
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to add element node to parent <%s> on line %d. " , parent ? parent - > value . element . name : " null " , line ) ;
2005-05-18 01:45:20 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
2007-09-09 07:27:08 +00:00
if ( mxml_isspace ( ch ) )
2007-09-09 08:11:25 +00:00
{
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_load_data: node=%p(<%s...>), parent=%p \n " , node , buffer , parent ) ;
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_parse_element ( options , io_cb , io_cbdata , node , & encoding , & line ) ) = = EOF )
2007-09-09 08:11:25 +00:00
goto error ;
}
2003-06-03 19:46:29 +00:00
else if ( ch = = ' / ' )
{
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_load_data: node=%p(<%s/>), parent=%p \n " , node , buffer , parent ) ;
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_getc ( options , io_cb , io_cbdata , & encoding ) ) ! = ' > ' )
2003-06-03 19:46:29 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Expected > but got '%c' instead for element <%s/> on line %d. " , ch , buffer , line ) ;
2005-08-16 14:46:18 +00:00
mxmlDelete ( node ) ;
2021-10-26 18:34:33 +00:00
node = NULL ;
2005-05-18 01:45:20 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
ch = ' / ' ;
}
2024-03-19 01:46:14 +00:00
if ( options & & options - > sax_cb )
2024-03-04 12:37:33 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ! ( options - > sax_cb ) ( options - > sax_cbdata , node , MXML_SAX_EVENT_ELEMENT_OPEN ) )
2024-03-04 12:37:33 +00:00
goto error ;
}
2007-04-23 21:48:03 +00:00
if ( ! first )
first = node ;
2003-06-03 19:46:29 +00:00
if ( ch = = EOF )
break ;
if ( ch ! = ' / ' )
{
2024-02-27 20:04:27 +00:00
// Descend into this node, setting the value type as needed...
2003-06-03 19:46:29 +00:00
parent = node ;
2024-03-19 01:46:14 +00:00
if ( options & & options - > type_cb & & parent )
type = ( options - > type_cb ) ( options - > type_cbdata , parent ) ;
else if ( options & & ! options - > type_cb )
type = options - > type_value ;
2016-06-13 00:27:11 +00:00
else
2024-02-27 20:04:27 +00:00
type = MXML_TYPE_TEXT ;
2003-06-03 19:46:29 +00:00
}
2024-03-19 01:46:14 +00:00
else if ( options & & options - > sax_cb )
2007-04-23 21:48:03 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ! ( options - > sax_cb ) ( options - > sax_cbdata , node , MXML_SAX_EVENT_ELEMENT_CLOSE ) )
2024-03-04 12:37:33 +00:00
goto error ;
2007-04-23 21:48:03 +00:00
2021-10-26 18:34:33 +00:00
if ( ! mxmlRelease ( node ) )
{
if ( first = = node )
first = NULL ;
node = NULL ;
}
2007-04-23 21:48:03 +00:00
}
2003-06-03 19:46:29 +00:00
}
2003-06-04 16:30:40 +00:00
bufptr = buffer ;
2003-06-03 19:46:29 +00:00
}
else if ( ch = = ' & ' )
{
2024-02-27 20:04:27 +00:00
// Add character entity to current buffer...
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_get_entity ( options , io_cb , io_cbdata , & encoding , parent , & line ) ) = = EOF )
2003-12-21 15:01:15 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & bufptr , & buffer , & bufsize ) )
2003-12-21 15:01:15 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
2024-02-27 20:04:27 +00:00
else if ( type = = MXML_TYPE_OPAQUE | | type = = MXML_TYPE_CUSTOM | | ! mxml_isspace ( ch ) )
2003-06-03 19:46:29 +00:00
{
2024-02-27 20:04:27 +00:00
// Add character to current buffer...
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & bufptr , & buffer , & bufsize ) )
2003-12-21 15:01:15 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
}
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , & encoding ) ) ! = EOF ) ;
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
// Free the string buffer - we don't need it anymore...
2003-06-15 21:31:45 +00:00
free ( buffer ) ;
2024-02-27 20:04:27 +00:00
// Find the top element and return it...
2003-06-03 19:46:29 +00:00
if ( parent )
{
2007-04-18 02:37:01 +00:00
node = parent ;
2014-01-05 03:28:03 +00:00
while ( parent ! = top & & parent - > parent )
2003-06-03 19:46:29 +00:00
parent = parent - > parent ;
2007-04-18 02:37:01 +00:00
if ( node ! = parent )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Missing close tag </%s> under parent <%s> on line %d. " , mxmlGetElement ( node ) , node - > parent ? node - > parent - > value . element . name : " (null) " , line ) ;
2007-04-18 02:37:01 +00:00
mxmlDelete ( first ) ;
return ( NULL ) ;
}
2003-06-03 19:46:29 +00:00
}
2007-09-15 20:03:15 +00:00
if ( parent )
return ( parent ) ;
else
return ( first ) ;
2003-12-21 15:01:15 +00:00
2024-02-27 20:04:27 +00:00
// Common error return...
2019-01-05 17:23:17 +00:00
error :
2003-12-21 15:01:15 +00:00
2005-05-18 01:45:20 +00:00
mxmlDelete ( first ) ;
2003-12-21 15:01:15 +00:00
free ( buffer ) ;
return ( NULL ) ;
2003-06-03 19:46:29 +00:00
}
2024-02-27 20:04:27 +00:00
//
// 'mxml_parse_element()' - Parse an element for any attributes...
//
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
static int // O - Terminating character
2007-04-23 21:48:03 +00:00
mxml_parse_element (
2024-03-19 01:46:14 +00:00
mxml_options_t * options , // I - Options
mxml_io_cb_t io_cb , // I - Read callback function
void * io_cbdata , // I - Read callback data
2024-03-06 19:45:10 +00:00
mxml_node_t * node , // I - Element node
_mxml_encoding_t * encoding , // IO - Encoding
int * line ) // IO - Current line number
2003-06-03 19:46:29 +00:00
{
2024-03-06 19:45:10 +00:00
int ch , // Current character in file
quote ; // Quoting character
char * name , // Attribute name
* value , // Attribute value
* ptr ; // Pointer into name/value
size_t namesize , // Size of name string
valsize ; // Size of value string
2003-12-19 02:56:11 +00:00
2024-02-27 20:04:27 +00:00
// Initialize the name and value buffers...
2003-06-15 21:31:45 +00:00
if ( ( name = malloc ( 64 ) ) = = NULL )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to allocate memory for name. " ) ;
2003-06-15 21:31:45 +00:00
return ( EOF ) ;
}
namesize = 64 ;
if ( ( value = malloc ( 64 ) ) = = NULL )
{
free ( name ) ;
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Unable to allocate memory for value. " ) ;
2003-06-15 21:31:45 +00:00
return ( EOF ) ;
}
valsize = 64 ;
2024-02-27 20:04:27 +00:00
// Loop until we hit a >, /, ?, or EOF...
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , encoding ) ) ! = EOF )
2003-06-03 19:46:29 +00:00
{
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_parse_element: ch='%c' \n " , ch ) ;
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
// Skip leading whitespace...
2007-09-09 07:27:08 +00:00
if ( mxml_isspace ( ch ) )
2019-01-05 17:23:17 +00:00
{
if ( ch = = ' \n ' )
( * line ) + + ;
2003-06-03 19:46:29 +00:00
continue ;
2019-01-05 17:23:17 +00:00
}
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
// Stop at /, ?, or >...
2003-06-03 19:46:29 +00:00
if ( ch = = ' / ' | | ch = = ' ? ' )
{
2024-02-27 20:04:27 +00:00
// Grab the > character and print an error if it isn't there...
2024-03-19 01:46:14 +00:00
quote = mxml_getc ( options , io_cb , io_cbdata , encoding ) ;
2003-06-03 19:46:29 +00:00
if ( quote ! = ' > ' )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Expected '>' after '%c' for element %s, but got '%c' on line %d. " , ch , mxmlGetElement ( node ) , quote , * line ) ;
2007-04-18 02:45:47 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
break ;
}
2007-04-18 02:45:47 +00:00
else if ( ch = = ' < ' )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Bare < in element %s on line %d. " , mxmlGetElement ( node ) , * line ) ;
2007-04-18 02:45:47 +00:00
goto error ;
}
2003-06-03 19:46:29 +00:00
else if ( ch = = ' > ' )
2024-02-27 20:04:27 +00:00
{
2003-06-03 19:46:29 +00:00
break ;
2024-02-27 20:04:27 +00:00
}
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
// Read the attribute name...
2020-10-02 20:36:19 +00:00
ptr = name ;
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & ptr , & name , & namesize ) )
2020-10-02 20:36:19 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
2003-12-01 15:27:47 +00:00
if ( ch = = ' \" ' | | ch = = ' \' ' )
{
2024-02-27 20:04:27 +00:00
// Name is in quotes, so get a quoted string...
2003-12-01 15:27:47 +00:00
quote = ch ;
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , encoding ) ) ! = EOF )
2003-06-03 19:46:29 +00:00
{
2003-12-21 15:01:15 +00:00
if ( ch = = ' & ' )
2019-01-05 17:23:17 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_get_entity ( options , io_cb , io_cbdata , encoding , node , line ) ) = = EOF )
2003-12-21 15:01:15 +00:00
goto error ;
2019-01-05 17:23:17 +00:00
}
else if ( ch = = ' \n ' )
2024-02-27 20:04:27 +00:00
{
2019-01-05 17:23:17 +00:00
( * line ) + + ;
2024-02-27 20:04:27 +00:00
}
2003-12-21 15:01:15 +00:00
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & ptr , & name , & namesize ) )
2003-12-21 15:01:15 +00:00
goto error ;
2003-12-01 15:27:47 +00:00
if ( ch = = quote )
break ;
2003-06-03 19:46:29 +00:00
}
2003-12-01 15:27:47 +00:00
}
else
{
2024-02-27 20:04:27 +00:00
// Grab an normal, non-quoted name...
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , encoding ) ) ! = EOF )
2019-01-05 17:23:17 +00:00
{
2024-02-27 20:04:27 +00:00
if ( mxml_isspace ( ch ) | | ch = = ' = ' | | ch = = ' / ' | | ch = = ' > ' | | ch = = ' ? ' )
2019-01-05 17:23:17 +00:00
{
if ( ch = = ' \n ' )
( * line ) + + ;
2003-12-01 15:27:47 +00:00
break ;
2019-01-05 17:23:17 +00:00
}
2003-12-21 15:01:15 +00:00
else
2003-12-01 15:27:47 +00:00
{
2003-12-21 15:01:15 +00:00
if ( ch = = ' & ' )
2019-01-05 17:23:17 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_get_entity ( options , io_cb , io_cbdata , encoding , node , line ) ) = = EOF )
2003-12-21 15:01:15 +00:00
goto error ;
2019-01-05 17:23:17 +00:00
}
2003-12-21 15:01:15 +00:00
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & ptr , & name , & namesize ) )
2003-12-21 15:01:15 +00:00
goto error ;
2003-12-01 15:27:47 +00:00
}
2019-01-05 17:23:17 +00:00
}
2003-12-01 15:27:47 +00:00
}
2003-06-03 19:46:29 +00:00
* ptr = ' \0 ' ;
2005-01-29 17:03:33 +00:00
if ( mxmlElementGetAttr ( node , name ) )
2019-01-05 17:23:17 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Duplicate attribute '%s' in element %s on line %d. " , name , mxmlGetElement ( node ) , * line ) ;
2005-01-29 17:03:33 +00:00
goto error ;
2019-01-05 17:23:17 +00:00
}
2005-01-29 17:03:33 +00:00
2008-01-13 00:42:35 +00:00
while ( ch ! = EOF & & mxml_isspace ( ch ) )
2019-01-05 17:23:17 +00:00
{
2024-03-19 01:46:14 +00:00
ch = mxml_getc ( options , io_cb , io_cbdata , encoding ) ;
2008-01-13 00:42:35 +00:00
2019-01-05 17:23:17 +00:00
if ( ch = = ' \n ' )
( * line ) + + ;
}
2003-06-03 19:46:29 +00:00
if ( ch = = ' = ' )
{
2024-02-27 20:04:27 +00:00
// Read the attribute value...
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , encoding ) ) ! = EOF & & mxml_isspace ( ch ) )
2019-01-05 17:23:17 +00:00
{
if ( ch = = ' \n ' )
( * line ) + + ;
}
2008-01-13 00:42:35 +00:00
if ( ch = = EOF )
2003-06-03 19:46:29 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Missing value for attribute '%s' in element %s on line %d. " , name , mxmlGetElement ( node ) , * line ) ;
2007-09-09 08:22:12 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
if ( ch = = ' \' ' | | ch = = ' \" ' )
{
2024-02-27 20:04:27 +00:00
// Read quoted value...
2003-06-03 19:46:29 +00:00
quote = ch ;
ptr = value ;
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , encoding ) ) ! = EOF )
2019-01-05 17:23:17 +00:00
{
2003-06-03 19:46:29 +00:00
if ( ch = = quote )
2019-01-05 17:23:17 +00:00
{
2003-06-03 19:46:29 +00:00
break ;
2019-01-05 17:23:17 +00:00
}
2003-12-21 15:01:15 +00:00
else
2003-06-03 19:46:29 +00:00
{
2003-12-21 15:01:15 +00:00
if ( ch = = ' & ' )
2019-01-05 17:23:17 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_get_entity ( options , io_cb , io_cbdata , encoding , node , line ) ) = = EOF )
2003-12-21 15:01:15 +00:00
goto error ;
2019-01-05 17:23:17 +00:00
}
else if ( ch = = ' \n ' )
2024-02-27 20:04:27 +00:00
{
2019-01-05 17:23:17 +00:00
( * line ) + + ;
2024-02-27 20:04:27 +00:00
}
2013-11-12 05:03:47 +00:00
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & ptr , & value , & valsize ) )
2003-12-21 15:01:15 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
2019-01-05 17:23:17 +00:00
}
2003-06-03 19:46:29 +00:00
* ptr = ' \0 ' ;
}
else
{
2024-02-27 20:04:27 +00:00
// Read unquoted value...
2020-01-10 19:55:59 +00:00
ptr = value ;
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & ptr , & value , & valsize ) )
2020-01-10 19:55:59 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
2024-03-19 01:46:14 +00:00
while ( ( ch = mxml_getc ( options , io_cb , io_cbdata , encoding ) ) ! = EOF )
2019-01-05 17:23:17 +00:00
{
2007-09-09 07:27:08 +00:00
if ( mxml_isspace ( ch ) | | ch = = ' = ' | | ch = = ' / ' | | ch = = ' > ' )
2019-01-05 17:23:17 +00:00
{
if ( ch = = ' \n ' )
( * line ) + + ;
2003-06-03 19:46:29 +00:00
break ;
2019-01-05 17:23:17 +00:00
}
2003-12-21 15:01:15 +00:00
else
2003-06-03 19:46:29 +00:00
{
2003-12-21 15:01:15 +00:00
if ( ch = = ' & ' )
2019-01-05 17:23:17 +00:00
{
2024-03-19 01:46:14 +00:00
if ( ( ch = mxml_get_entity ( options , io_cb , io_cbdata , encoding , node , line ) ) = = EOF )
2003-12-21 15:01:15 +00:00
goto error ;
2019-01-05 17:23:17 +00:00
}
2013-11-12 05:03:47 +00:00
2024-03-19 01:46:14 +00:00
if ( ! mxml_add_char ( options , ch , & ptr , & value , & valsize ) )
2003-12-21 15:01:15 +00:00
goto error ;
2003-06-03 19:46:29 +00:00
}
2019-01-05 17:23:17 +00:00
}
2003-06-03 19:46:29 +00:00
* ptr = ' \0 ' ;
}
2003-12-01 15:27:47 +00:00
2024-02-27 20:04:27 +00:00
// Set the attribute with the given string value...
2003-12-01 15:27:47 +00:00
mxmlElementSetAttr ( node , name , value ) ;
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_parse_element: %s= \" %s \" \n " , name , value ) ;
2003-06-03 19:46:29 +00:00
}
else
2003-12-01 15:27:47 +00:00
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Missing value for attribute '%s' in element %s on line %d. " , name , mxmlGetElement ( node ) , * line ) ;
2007-09-09 08:22:12 +00:00
goto error ;
2003-12-01 15:27:47 +00:00
}
2003-06-03 19:46:29 +00:00
2024-02-27 20:04:27 +00:00
// Check the end character...
2003-06-19 03:20:41 +00:00
if ( ch = = ' / ' | | ch = = ' ? ' )
{
2024-02-27 20:04:27 +00:00
// Grab the > character and print an error if it isn't there...
2024-03-19 01:46:14 +00:00
quote = mxml_getc ( options , io_cb , io_cbdata , encoding ) ;
2003-06-19 03:20:41 +00:00
if ( quote ! = ' > ' )
{
2024-03-19 01:46:14 +00:00
_mxml_error ( options , " Expected '>' after '%c' for element %s, but got '%c' on line %d. " , ch , mxmlGetElement ( node ) , quote , * line ) ;
2003-06-19 03:20:41 +00:00
ch = EOF ;
}
break ;
}
else if ( ch = = ' > ' )
break ;
2003-06-03 19:46:29 +00:00
}
2024-02-27 20:04:27 +00:00
// Free the name and value buffers and return...
2003-06-15 21:31:45 +00:00
free ( name ) ;
2024-03-06 19:45:10 +00:00
free ( value ) ;
2004-05-16 21:54:47 +00:00
2024-03-06 19:45:10 +00:00
return ( ch ) ;
2009-03-19 05:27:26 +00:00
2024-03-06 19:45:10 +00:00
// Common error return point...
error :
2004-10-28 01:07:00 +00:00
2024-03-06 19:45:10 +00:00
free ( name ) ;
free ( value ) ;
2004-05-16 21:54:47 +00:00
2024-03-06 19:45:10 +00:00
return ( EOF ) ;
}
2004-05-16 21:54:47 +00:00
2004-10-28 01:07:00 +00:00
2024-03-06 19:45:10 +00:00
//
// 'mxml_read_cb_fd()' - Read bytes from a file descriptor.
//
2004-05-16 21:54:47 +00:00
2024-03-19 01:46:14 +00:00
static size_t // O - Bytes read
2024-03-06 19:45:10 +00:00
mxml_read_cb_fd ( int * fd , // I - File descriptor
void * buffer , // I - Buffer
size_t bytes ) // I - Bytes to read
{
2024-03-19 15:22:41 +00:00
# if _WIN32
int rbytes ; // Bytes read
2024-03-19 01:46:14 +00:00
2024-03-19 15:22:41 +00:00
rbytes = read ( * fd , buffer , bytes ) ;
2024-03-19 01:46:14 +00:00
# else
2024-03-19 15:22:41 +00:00
ssize_t rbytes ; // Bytes read
2024-03-19 01:46:14 +00:00
while ( ( rbytes = read ( * fd , buffer , bytes ) ) < 0 )
{
if ( errno ! = EINTR & & errno ! = EAGAIN )
break ;
}
# endif // _WIN32
if ( rbytes < 0 )
return ( 0 ) ;
else
return ( ( size_t ) rbytes ) ;
2024-03-06 19:45:10 +00:00
}
2004-05-16 21:54:47 +00:00
2024-03-06 19:45:10 +00:00
//
// 'mxml_read_cb_file()' - Read bytes from a file pointer.
//
2004-05-16 21:54:47 +00:00
2024-03-19 01:46:14 +00:00
static size_t // O - Bytes read
2024-03-06 19:45:10 +00:00
mxml_read_cb_file ( FILE * fp , // I - File pointer
void * buffer , // I - Buffer
size_t bytes ) // I - Bytes to read
{
if ( feof ( fp ) )
2024-03-19 01:46:14 +00:00
return ( 0 ) ;
2024-03-06 19:45:10 +00:00
else
2024-03-19 01:46:14 +00:00
return ( fread ( buffer , 1 , bytes , fp ) ) ;
2024-03-06 19:45:10 +00:00
}
2004-05-16 21:54:47 +00:00
2003-12-21 15:01:15 +00:00
2024-03-06 19:45:10 +00:00
//
// 'mxml_read_cb_string()' - Read bytes from a string.
//
2003-12-21 15:01:15 +00:00
2024-03-19 01:46:14 +00:00
static size_t // O - Bytes read
2024-03-06 19:45:10 +00:00
mxml_read_cb_string (
_mxml_stringbuf_t * sb , // I - String buffer
void * buffer , // I - Buffer
size_t bytes ) // I - Bytes to read
{
size_t remaining ; // Remaining bytes in buffer
2004-05-16 21:54:47 +00:00
2024-03-06 19:45:10 +00:00
if ( ( remaining = sb - > bufsize - ( size_t ) ( sb - > bufptr - sb - > buffer ) ) < bytes )
bytes = remaining ;
2004-05-16 21:54:47 +00:00
2024-03-06 19:45:10 +00:00
if ( bytes > 0 )
{
// Copy bytes from string...
memcpy ( buffer , sb - > bufptr , bytes ) ;
sb - > bufptr + = bytes ;
2003-06-19 03:20:41 +00:00
}
2004-05-16 21:54:47 +00:00
2024-03-19 15:22:41 +00:00
return ( bytes ) ;
2003-06-19 03:20:41 +00:00
}
2024-03-06 23:18:29 +00:00
//
// 'mxml_strtod()' - Convert a string to a double without respect to the locale.
//
static double // O - Real number
2024-03-19 01:46:14 +00:00
mxml_strtod ( mxml_options_t * options , // I - Options
2024-03-06 23:18:29 +00:00
const char * buffer , // I - String
char * * bufend ) // O - End of number in string
{
const char * bufptr ; // Pointer into buffer
char temp [ 64 ] , // Temporary buffer
* tempptr ; // Pointer into temporary buffer
// See if the locale has a special decimal point string...
2024-03-19 01:46:14 +00:00
if ( ! options | | ! options - > loc )
2024-03-06 23:18:29 +00:00
return ( strtod ( buffer , bufend ) ) ;
// Copy leading sign, numbers, period, and then numbers...
tempptr = temp ;
temp [ sizeof ( temp ) - 1 ] = ' \0 ' ;
2024-03-07 01:03:48 +00:00
bufptr = buffer ;
2024-03-06 23:18:29 +00:00
if ( * bufptr = = ' - ' | | * bufptr = = ' + ' )
* tempptr + + = * bufptr + + ;
2024-03-07 01:03:48 +00:00
while ( * bufptr & & isdigit ( * bufptr & 255 ) )
2024-03-06 23:18:29 +00:00
{
if ( tempptr < ( temp + sizeof ( temp ) - 1 ) )
{
2024-03-07 01:03:48 +00:00
* tempptr + + = * bufptr + + ;
2024-03-06 23:18:29 +00:00
}
else
{
* bufend = ( char * ) bufptr ;
return ( 0.0 ) ;
}
}
if ( * bufptr = = ' . ' )
{
// Convert decimal point to locale equivalent...
2024-03-19 01:46:14 +00:00
size_t declen = strlen ( options - > loc - > decimal_point ) ;
2024-03-06 23:18:29 +00:00
// Length of decimal point
bufptr + + ;
if ( declen < = ( sizeof ( temp ) - ( size_t ) ( tempptr - temp ) ) )
{
2024-03-19 01:46:14 +00:00
memcpy ( tempptr , options - > loc - > decimal_point , declen ) ;
2024-03-06 23:18:29 +00:00
tempptr + = declen ;
}
else
{
* bufend = ( char * ) bufptr ;
return ( 0.0 ) ;
}
}
// Copy any remaining characters...
while ( * bufptr & & isdigit ( * bufptr & 255 ) )
{
if ( tempptr < ( temp + sizeof ( temp ) - 1 ) )
* tempptr + + = * bufptr + + ;
else
break ;
}
* bufend = ( char * ) bufptr ;
if ( * bufptr )
return ( 0.0 ) ;
// Nul-terminate the temporary string and convert the string...
* tempptr = ' \0 ' ;
return ( strtod ( temp , NULL ) ) ;
}
2024-02-27 20:04:27 +00:00
//
2024-03-19 01:46:14 +00:00
// 'mxml_io_cb_fd()' - Write bytes to a file descriptor.
2024-02-27 20:04:27 +00:00
//
2003-06-19 04:25:12 +00:00
2024-03-19 01:46:14 +00:00
static size_t // O - Bytes written
mxml_io_cb_fd ( int * fd , // I - File descriptor
void * buffer , // I - Buffer
size_t bytes ) // I - Bytes to write
2003-06-19 04:25:12 +00:00
{
2024-03-19 15:22:41 +00:00
# if _WIN32
int wbytes ; // Bytes written
2024-03-19 01:46:14 +00:00
wbytes = write ( * fd , buffer , bytes ) ;
2024-03-19 15:22:41 +00:00
2024-03-19 01:46:14 +00:00
# else
2024-03-19 15:22:41 +00:00
ssize_t wbytes ; // Bytes written
2024-03-19 01:46:14 +00:00
while ( ( wbytes = write ( * fd , buffer , bytes ) ) < 0 )
{
if ( errno ! = EINTR & & errno ! = EAGAIN )
break ;
}
# endif // _WIN32
if ( wbytes < 0 )
return ( 0 ) ;
else
return ( ( size_t ) wbytes ) ;
2024-03-06 19:45:10 +00:00
}
2003-06-19 04:25:12 +00:00
2003-12-21 15:01:15 +00:00
2024-03-06 19:45:10 +00:00
//
2024-03-19 01:46:14 +00:00
// 'mxml_io_cb_file()' - Write bytes to a file pointer.
2024-03-06 19:45:10 +00:00
//
2003-06-19 04:25:12 +00:00
2024-03-19 01:46:14 +00:00
static size_t // O - Bytes written
mxml_io_cb_file ( FILE * fp , // I - File pointer
void * buffer , // I - Buffer
size_t bytes ) // I - Bytes to write
2024-03-06 19:45:10 +00:00
{
2024-03-19 01:46:14 +00:00
return ( fwrite ( buffer , 1 , bytes , fp ) ) ;
2003-06-19 04:25:12 +00:00
}
2024-02-27 20:04:27 +00:00
//
2024-03-19 01:46:14 +00:00
// 'mxml_io_cb_string()' - Write bytes to a string buffer.
2024-02-27 20:04:27 +00:00
//
2003-12-01 15:27:47 +00:00
2024-03-19 01:46:14 +00:00
static size_t // O - Bytes written
mxml_io_cb_string (
2024-03-06 19:45:10 +00:00
_mxml_stringbuf_t * sb , // I - String buffer
2024-03-19 01:46:14 +00:00
void * buffer , // I - Buffer
2024-03-06 19:45:10 +00:00
size_t bytes ) // I - Bytes to write
2003-12-01 15:27:47 +00:00
{
2024-03-06 19:45:10 +00:00
size_t remaining ; // Remaining bytes
2003-12-01 15:27:47 +00:00
2024-03-06 19:45:10 +00:00
// Expand buffer as needed...
if ( ( sb - > bufptr + bytes ) > = ( sb - > buffer + sb - > bufsize - 1 ) & & sb - > bufalloc )
2003-12-01 15:27:47 +00:00
{
2024-03-06 19:45:10 +00:00
// Reallocate buffer
char * temp ; // New buffer pointer
size_t newsize ; // New bufsize
2003-12-01 15:27:47 +00:00
2024-03-06 19:45:10 +00:00
newsize = ( size_t ) ( sb - > bufptr - sb - > buffer ) + bytes + 257 ;
if ( ( temp = realloc ( sb - > buffer , newsize ) ) = = NULL )
2024-03-19 01:46:14 +00:00
return ( 0 ) ;
2003-12-01 15:27:47 +00:00
2024-03-06 19:45:10 +00:00
sb - > bufptr = temp + ( sb - > bufptr - sb - > buffer ) ;
sb - > buffer = temp ;
sb - > bufsize = newsize ;
2003-12-01 15:27:47 +00:00
}
2024-03-06 19:45:10 +00:00
// Copy what we can...
if ( sb - > bufptr > = ( sb - > buffer + sb - > bufsize - 1 ) )
2024-03-19 01:46:14 +00:00
return ( 0 ) ; // No more room
2024-03-06 19:45:10 +00:00
else if ( ( remaining = ( sb - > bufsize - ( size_t ) ( sb - > bufptr - sb - > buffer ) - 1 ) ) < bytes )
bytes = remaining ;
2003-12-01 15:27:47 +00:00
2024-03-06 19:45:10 +00:00
memcpy ( sb - > bufptr , buffer , bytes ) ;
sb - > bufptr + = bytes ;
return ( bytes ) ;
2003-12-01 15:27:47 +00:00
}
2024-02-27 20:04:27 +00:00
//
// 'mxml_write_node()' - Save an XML node to a file.
//
2003-06-04 02:34:30 +00:00
2024-02-27 20:04:27 +00:00
static int // O - Column or -1 on error
2024-03-06 19:45:10 +00:00
mxml_write_node (
2024-03-19 01:46:14 +00:00
mxml_node_t * node , // I - Node to write
mxml_options_t * options , // I - Options
mxml_io_cb_t io_cb , // I - Write callback function
void * io_cbdata , // I - Write callback data
int col ) // I - Current column
2003-06-04 02:34:30 +00:00
{
2024-02-27 20:04:27 +00:00
mxml_node_t * current , // Current node
* next ; // Next node
2024-03-06 19:45:10 +00:00
size_t i , // Looping var
2024-02-27 20:04:27 +00:00
width ; // Width of attr + value
_mxml_attr_t * attr ; // Current attribute
2024-03-06 19:45:10 +00:00
char s [ 255 ] , // Temporary string
* data ; // Custom data string
const char * text ; // Text string
bool whitespace ; // Whitespace before text string?
2003-06-04 02:34:30 +00:00
2010-09-19 07:27:48 +00:00
2024-02-27 20:04:27 +00:00
// Loop through this node and all of its children...
2024-03-06 19:45:10 +00:00
for ( current = node ; current & & col > = 0 ; current = next )
2003-06-04 02:34:30 +00:00
{
2024-02-27 20:04:27 +00:00
// Print the node value...
2024-03-06 19:45:10 +00:00
MXML_DEBUG ( " mxml_write_node: current=%p(%d) \n " , current , current - > type ) ;
switch ( mxmlGetType ( current ) )
2016-06-12 21:12:11 +00:00
{
2024-03-02 23:47:57 +00:00
case MXML_TYPE_CDATA :
2024-03-19 01:46:14 +00:00
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_BEFORE_OPEN , col ) ;
col = mxml_write_string ( " <![CDATA[ " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( mxmlGetCDATA ( current ) , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( " ]]> " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_AFTER_OPEN , col ) ;
2024-03-02 23:47:57 +00:00
break ;
case MXML_TYPE_COMMENT :
2024-03-19 01:46:14 +00:00
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_BEFORE_OPEN , col ) ;
col = mxml_write_string ( " <!-- " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( mxmlGetComment ( current ) , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( " --> " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_AFTER_OPEN , col ) ;
2024-03-02 23:47:57 +00:00
break ;
case MXML_TYPE_DECLARATION :
2024-03-19 01:46:14 +00:00
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_BEFORE_OPEN , col ) ;
col = mxml_write_string ( " <! " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( mxmlGetDeclaration ( current ) , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( " > " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_AFTER_OPEN , col ) ;
2024-03-02 23:47:57 +00:00
break ;
2017-03-30 01:42:30 +00:00
2024-03-02 23:47:57 +00:00
case MXML_TYPE_DIRECTIVE :
2024-03-19 01:46:14 +00:00
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_BEFORE_OPEN , col ) ;
col = mxml_write_string ( " <? " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( mxmlGetDirective ( current ) , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( " ?> " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_AFTER_OPEN , col ) ;
2024-03-02 23:47:57 +00:00
break ;
case MXML_TYPE_ELEMENT :
2024-03-19 01:46:14 +00:00
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_BEFORE_OPEN , col ) ;
col = mxml_write_string ( " < " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( mxmlGetElement ( current ) , io_cb , io_cbdata , /*use_entities*/ true , col ) ;
2005-01-29 07:19:38 +00:00
2024-03-06 19:45:10 +00:00
for ( i = current - > value . element . num_attrs , attr = current - > value . element . attrs ; i > 0 & & col > = 0 ; i - - , attr + + )
2016-06-12 21:12:11 +00:00
{
2024-03-06 19:45:10 +00:00
width = strlen ( attr - > name ) ;
2003-06-04 02:34:30 +00:00
2016-06-12 21:12:11 +00:00
if ( attr - > value )
width + = strlen ( attr - > value ) + 3 ;
2003-06-04 02:34:30 +00:00
2024-03-19 15:22:41 +00:00
if ( options & & options - > wrap > 0 & & ( col + ( int ) width ) > options - > wrap )
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( " \n " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2016-06-12 21:12:11 +00:00
else
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( " " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2016-06-12 21:12:11 +00:00
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( attr - > name , io_cb , io_cbdata , /*use_entities*/ true , col ) ;
2003-06-04 02:34:30 +00:00
2016-06-12 21:12:11 +00:00
if ( attr - > value )
{
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( " = \" " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( attr - > value , io_cb , io_cbdata , /*use_entities*/ true , col ) ;
col = mxml_write_string ( " \" " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2016-06-12 21:12:11 +00:00
}
2003-06-04 02:34:30 +00:00
}
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( current - > child ? " > " : " /> " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_AFTER_OPEN , col ) ;
2016-06-12 21:12:11 +00:00
break ;
2003-06-04 02:34:30 +00:00
2024-02-27 20:04:27 +00:00
case MXML_TYPE_INTEGER :
2016-06-12 21:12:11 +00:00
if ( current - > prev )
{
2024-03-06 19:45:10 +00:00
// Add whitespace separator...
2024-03-19 01:46:14 +00:00
if ( options & & options - > wrap > 0 & & col > options - > wrap )
col = mxml_write_string ( " \n " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2016-06-12 21:12:11 +00:00
else
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( " " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2016-06-12 21:12:11 +00:00
}
2003-06-04 21:19:00 +00:00
2024-03-06 19:45:10 +00:00
// Write integer...
2024-03-02 23:47:57 +00:00
snprintf ( s , sizeof ( s ) , " %ld " , current - > value . integer ) ;
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( s , io_cb , io_cbdata , /*use_entities*/ true , col ) ;
2016-06-12 21:12:11 +00:00
break ;
2003-06-04 21:19:00 +00:00
2024-02-27 20:04:27 +00:00
case MXML_TYPE_OPAQUE :
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( mxmlGetOpaque ( current ) , io_cb , io_cbdata , /*use_entities*/ true , col ) ;
2016-06-12 21:12:11 +00:00
break ;
2010-09-19 07:27:48 +00:00
2024-02-27 20:04:27 +00:00
case MXML_TYPE_REAL :
2016-06-12 21:12:11 +00:00
if ( current - > prev )
2003-06-04 21:19:00 +00:00
{
2024-03-06 19:45:10 +00:00
// Add whitespace separator...
2024-03-19 01:46:14 +00:00
if ( options & & options - > wrap > 0 & & col > options - > wrap )
col = mxml_write_string ( " \n " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2016-06-12 21:12:11 +00:00
else
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( " " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2003-06-04 17:37:23 +00:00
}
2003-06-04 02:34:30 +00:00
2024-03-06 19:45:10 +00:00
// Write real number...
2024-03-06 23:18:29 +00:00
snprintf ( s , sizeof ( s ) , " %g " , current - > value . real ) ;
2024-03-19 01:46:14 +00:00
if ( options & & options - > loc )
2024-03-06 23:18:29 +00:00
{
char * sptr ; // Pointer into string
2024-03-19 01:46:14 +00:00
if ( ( sptr = strstr ( s , options - > loc - > decimal_point ) ) ! = NULL )
2024-03-06 23:18:29 +00:00
{
// Convert locale decimal point to "."
2024-03-19 01:46:14 +00:00
if ( options - > loc_declen > 1 )
memmove ( sptr + 1 , sptr + options - > loc_declen , strlen ( sptr + options - > loc_declen ) + 1 ) ;
2024-03-06 23:18:29 +00:00
* sptr = ' . ' ;
}
}
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( s , io_cb , io_cbdata , /*use_entities*/ true , col ) ;
2016-06-12 21:12:11 +00:00
break ;
2010-09-19 07:27:48 +00:00
2024-02-27 20:04:27 +00:00
case MXML_TYPE_TEXT :
2024-03-06 19:45:10 +00:00
text = mxmlGetText ( current , & whitespace ) ;
if ( whitespace & & col > 0 )
2010-09-19 07:27:48 +00:00
{
2024-03-06 19:45:10 +00:00
// Add whitespace separator...
2024-03-19 01:46:14 +00:00
if ( options & & options - > wrap > 0 & & col > options - > wrap )
col = mxml_write_string ( " \n " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2016-06-12 21:12:11 +00:00
else
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( " " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2010-09-19 07:27:48 +00:00
}
2016-06-12 21:12:11 +00:00
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( text , io_cb , io_cbdata , /*use_entities*/ true , col ) ;
2016-06-12 21:12:11 +00:00
break ;
2003-06-04 02:34:30 +00:00
2024-02-27 20:04:27 +00:00
case MXML_TYPE_CUSTOM :
2024-03-19 01:46:14 +00:00
if ( ! options | | ! options - > custsave_cb )
2024-03-06 19:45:10 +00:00
return ( - 1 ) ;
2003-06-04 02:34:30 +00:00
2024-03-19 01:46:14 +00:00
if ( ( data = ( options - > custsave_cb ) ( options - > cust_cbdata , current ) ) = = NULL )
2024-03-06 19:45:10 +00:00
return ( - 1 ) ;
2003-06-04 02:34:30 +00:00
2024-03-19 01:46:14 +00:00
col = mxml_write_string ( data , io_cb , io_cbdata , /*use_entities*/ true , col ) ;
2016-06-12 21:12:11 +00:00
2024-03-06 19:45:10 +00:00
free ( data ) ;
break ;
2003-06-04 02:34:30 +00:00
2024-02-27 20:04:27 +00:00
default : // Should never happen
2010-09-19 07:27:48 +00:00
return ( - 1 ) ;
2016-06-12 21:12:11 +00:00
}
2003-06-04 02:34:30 +00:00
2024-02-27 20:04:27 +00:00
// Figure out the next node...
2024-03-06 19:45:10 +00:00
if ( ( next = mxmlGetFirstChild ( current ) ) = = NULL )
2016-06-12 21:12:11 +00:00
{
2018-10-01 17:15:22 +00:00
if ( current = = node )
2016-06-12 21:12:11 +00:00
{
2024-02-27 20:04:27 +00:00
// Don't traverse to sibling node if we are at the "root" node...
2018-10-01 17:15:22 +00:00
next = NULL ;
}
else
{
2024-02-27 20:04:27 +00:00
// Try the next sibling, and continue traversing upwards as needed...
2024-03-06 19:45:10 +00:00
while ( ( next = mxmlGetNextSibling ( current ) ) = = NULL )
2010-09-19 07:27:48 +00:00
{
2024-03-06 19:45:10 +00:00
if ( current = = node | | ! mxmlGetParent ( current ) )
2018-10-01 17:15:22 +00:00
break ;
2004-10-28 02:58:01 +00:00
2024-03-02 23:47:57 +00:00
// Declarations and directives have no end tags...
2024-03-06 19:45:10 +00:00
current = mxmlGetParent ( current ) ;
2018-10-01 17:15:22 +00:00
2024-03-06 19:45:10 +00:00
if ( mxmlGetType ( current ) = = MXML_TYPE_ELEMENT )
2018-10-01 17:15:22 +00:00
{
2024-03-19 01:46:14 +00:00
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_BEFORE_CLOSE , col ) ;
col = mxml_write_string ( " </ " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_string ( mxmlGetElement ( current ) , io_cb , io_cbdata , /*use_entities*/ true , col ) ;
col = mxml_write_string ( " > " , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
col = mxml_write_ws ( current , options , io_cb , io_cbdata , MXML_WS_AFTER_CLOSE , col ) ;
2018-10-01 17:15:22 +00:00
}
if ( current = = node )
break ;
2010-09-19 07:27:48 +00:00
}
2016-06-12 21:12:11 +00:00
}
}
2003-06-04 02:34:30 +00:00
}
return ( col ) ;
}
2024-02-27 20:04:27 +00:00
//
// 'mxml_write_string()' - Write a string, escaping & and < as needed.
//
2003-06-03 19:46:29 +00:00
2024-03-06 19:45:10 +00:00
static int // O - New column or `-1` on error
2007-04-23 21:48:03 +00:00
mxml_write_string (
2024-02-27 20:04:27 +00:00
const char * s , // I - String to write
2024-03-19 01:46:14 +00:00
mxml_io_cb_t io_cb , // I - Write callback function
void * io_cbdata , // I - Write callback data
2024-03-06 19:45:10 +00:00
bool use_entities , // I - Escape special characters?
int col ) // I - Current column
2003-06-03 19:46:29 +00:00
{
2024-03-06 19:45:10 +00:00
const char * frag , // Start of current string fragment
* ptr , // Pointer into string
* ent ; // Entity, if any
size_t fraglen ; // Length of fragment
2003-06-19 04:25:12 +00:00
2024-03-19 01:46:14 +00:00
MXML_DEBUG ( " mxml_write_string(io_cb=%p, io_cbdata=%p, s= \" %s \" , use_entities=%s, col=%d) \n " , io_cb , io_cbdata , s , use_entities ? " true " : " false " , col ) ;
2003-06-19 04:25:12 +00:00
2024-03-06 19:45:10 +00:00
if ( col < 0 )
return ( - 1 ) ;
for ( frag = ptr = s ; * ptr ; ptr + + )
2003-06-03 19:46:29 +00:00
{
2024-03-06 19:45:10 +00:00
if ( use_entities & & ( ent = _mxml_entity_string ( * ptr ) ) ! = NULL )
2003-06-03 19:46:29 +00:00
{
2024-03-06 19:45:10 +00:00
size_t entlen = strlen ( ent ) ; // Length of entity
2003-06-04 16:30:40 +00:00
2024-03-06 19:45:10 +00:00
if ( ptr > frag )
2003-06-04 16:30:40 +00:00
{
2024-03-06 19:45:10 +00:00
// Write current fragment
fraglen = ( size_t ) ( ptr - frag ) ;
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , ( char * ) frag , fraglen ) ! = fraglen )
2024-03-06 19:45:10 +00:00
return ( - 1 ) ;
2003-06-19 04:25:12 +00:00
}
2024-03-06 19:45:10 +00:00
frag = ptr + 1 ;
// Write entity
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , ( char * ) ent , entlen ) ! = entlen )
2003-12-18 04:16:37 +00:00
return ( - 1 ) ;
2024-03-06 19:45:10 +00:00
col + + ;
2003-06-04 16:30:40 +00:00
}
2024-03-06 19:45:10 +00:00
else if ( * ptr = = ' \r ' | | * ptr = = ' \n ' )
2024-02-27 20:04:27 +00:00
{
2024-03-06 19:45:10 +00:00
// CR or LF resets column
col = 0 ;
2024-02-27 20:04:27 +00:00
}
2024-03-06 19:45:10 +00:00
else if ( * ptr = = ' \t ' )
{
// Tab indents column
col = col - ( col % MXML_TAB ) + MXML_TAB ;
}
else
{
// All other characters are 1 column wide
col + + ;
}
}
if ( ptr > frag )
{
// Write final fragment
fraglen = ( size_t ) ( ptr - frag ) ;
2003-06-03 19:46:29 +00:00
2024-03-19 01:46:14 +00:00
if ( ( io_cb ) ( io_cbdata , ( char * ) frag , fraglen ) ! = fraglen )
2024-03-06 19:45:10 +00:00
return ( - 1 ) ;
2003-06-03 19:46:29 +00:00
}
2024-03-06 19:45:10 +00:00
return ( col ) ;
2003-06-03 19:46:29 +00:00
}
2024-02-27 20:04:27 +00:00
//
// 'mxml_write_ws()' - Do whitespace callback...
//
2003-06-04 21:19:00 +00:00
2024-03-06 19:45:10 +00:00
static int // O - New column or `-1` on error
mxml_write_ws (
2024-03-19 01:46:14 +00:00
mxml_node_t * node , // I - Current node
mxml_options_t * options , // I - Options
mxml_io_cb_t io_cb , // I - Write callback function
void * io_cbdata , // I - Write callback data
mxml_ws_t ws , // I - Whitespace value
int col ) // I - Current column
2003-06-04 21:19:00 +00:00
{
2024-02-27 20:04:27 +00:00
const char * s ; // Whitespace string
2003-06-04 21:19:00 +00:00
2024-03-19 01:46:14 +00:00
if ( options & & options - > ws_cb & & ( s = ( options - > ws_cb ) ( options - > ws_cbdata , node , ws ) ) ! = NULL )
col = mxml_write_string ( s , io_cb , io_cbdata , /*use_entities*/ false , col ) ;
2003-06-04 21:19:00 +00:00
return ( col ) ;
}