diff --git a/CHANGES.md b/CHANGES.md index afab5e9..5dae203 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,8 @@ `MXML_TYPE_DIRECTIVE` node types (Issue #250) - Added `mxmlLoadFilename` and `mxmlSaveFilename` functions (Issue #291) - Added AFL fuzzing support (Issue #306) +- Added `mxmlOptions` APIs to replace the long list of callbacks and options for + each of the load and save functions (Issue #312) - Added string copy/free callbacks to support alternate memory management of strings. - Renamed `mxml_type_t` enumerations to `MXML_TYPE_xxx` (Issue #251) diff --git a/Makefile.in b/Makefile.in index bd62bce..7bbbcb4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -150,8 +150,8 @@ BUILDROOT = $(DSTROOT)$(DESTDIR) DOCFILES = doc/mxml.epub doc/mxml.html doc/mxml-cover.png \ CHANGES.md LICENSE NOTICE README.md -PUBLIBOBJS = mxml-attr.o mxml-entity.o mxml-file.o mxml-get.o \ - mxml-index.o mxml-node.o mxml-search.o mxml-set.o +PUBLIBOBJS = mxml-attr.o mxml-file.o mxml-get.o mxml-index.o \ + mxml-node.o mxml-options.o mxml-search.o mxml-set.o LIBOBJS = $(PUBLIBOBJS) mxml-private.o OBJS = testmxml.o $(LIBOBJS) ALLTARGETS = $(LIBMXML) testmxml diff --git a/doc/body.md b/doc/body.md index 61458d3..79f02a6 100644 --- a/doc/body.md +++ b/doc/body.md @@ -5,6 +5,9 @@ copyright: Copyright © 2003-2024, All Rights Reserved. version: 4.0 ... +> TODO: Update for mxmlOptions APIs! + + Introduction ============ diff --git a/mxml-attr.c b/mxml-attr.c index 7af4c1e..6810029 100644 --- a/mxml-attr.c +++ b/mxml-attr.c @@ -173,10 +173,7 @@ mxmlElementSetAttr(mxml_node_t *node, // I - Element node if (value) { if ((valuec = _mxml_strcopy(value)) == NULL) - { - _mxml_error("Unable to allocate memory for attribute '%s' in element %s.", name, node->value.element.name); return; - } } else { @@ -218,10 +215,11 @@ mxmlElementSetAttrf(mxml_node_t *node, // I - Element node vsnprintf(buffer, sizeof(buffer), format, ap); va_end(ap); - if ((value = _mxml_strcopy(buffer)) == NULL) - _mxml_error("Unable to allocate memory for attribute '%s' in element %s.", name, node->value.element.name); - else if (!mxml_set_attr(node, name, value)) - _mxml_strfree(value); + if ((value = _mxml_strcopy(buffer)) != NULL) + { + if (!mxml_set_attr(node, name, value)) + _mxml_strfree(value); + } } @@ -253,19 +251,13 @@ mxml_set_attr(mxml_node_t *node, // I - Element node // Add a new attribute... if ((attr = realloc(node->value.element.attrs, (node->value.element.num_attrs + 1) * sizeof(_mxml_attr_t))) == NULL) - { - _mxml_error("Unable to allocate memory for attribute '%s' in element %s.", name, node->value.element.name); return (false); - } node->value.element.attrs = attr; attr += node->value.element.num_attrs; if ((attr->name = _mxml_strcopy(name)) == NULL) - { - _mxml_error("Unable to allocate memory for attribute '%s' in element %s.", name, node->value.element.name); return (false); - } attr->value = value; diff --git a/mxml-entity.c b/mxml-entity.c deleted file mode 100644 index 6fb03a1..0000000 --- a/mxml-entity.c +++ /dev/null @@ -1,433 +0,0 @@ -// -// Character entity support 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. -// - -#include "mxml-private.h" - - -// -// 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode. -// -// This function adds a callback to the current thread that converts named -// XML character entities to Unicode characters. The callback function `cb` -// accepts the callback data pointer `cbdata` and the entity name and returns a -// Unicode character value or `-1` if the entity is not known. For example, the -// following entity callback supports the "euro" entity: -// -// ```c -// int my_entity_cb(void *cbdata, const char *name) -// { -// if (!strcmp(name, "euro")) -// return (0x20ac); -// else -// return (-1); -// } -// ``` -// - -bool // O - `true` on success, `false` on failure -mxmlEntityAddCallback( - mxml_entity_cb_t cb, // I - Callback function to add - void *cbdata) // I - Callback data -{ - _mxml_global_t *global = _mxml_global(); - // Global data - - - if (global->num_entity_cbs < (sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0]))) - { - global->entity_cbs[global->num_entity_cbs] = cb; - global->entity_cbdatas[global->num_entity_cbs] = cbdata; - global->num_entity_cbs ++; - - return (true); - } - else - { - _mxml_error("Unable to add entity callback!"); - - return (false); - } -} - - -// -// '_mxml_entity_string()' - Get the entity that corresponds to the character, if any. -// - -const char * // O - Entity or `NULL` for none -_mxml_entity_string(int ch) // I - Character -{ - switch (ch) - { - case '&' : - return ("&"); - - case '<' : - return ("<"); - - case '>' : - return (">"); - - case '\"' : - return ("""); - - default : - return (NULL); - } -} - - -// -// 'mxmlEntityGetValue()' - Get the character corresponding to a named entity. -// -// The entity name can also be a numeric constant. `-1` is returned if the -// name is not known. -// - -int // O - Character value or `-1` on error -mxmlEntityGetValue(const char *name) // I - Entity name -{ - size_t i; // Looping var - int ch; // Character value - _mxml_global_t *global = _mxml_global(); - // Global data - - - for (i = 0; i < global->num_entity_cbs; i ++) - { - if ((ch = (global->entity_cbs[i])(global->entity_cbdatas[i], name)) >= 0) - return (ch); - } - - return (-1); -} - - -// -// 'mxmlEntityRemoveCallback()' - Remove a callback. -// - -void -mxmlEntityRemoveCallback( - mxml_entity_cb_t cb) // I - Callback function to remove -{ - size_t i; // Looping var - _mxml_global_t *global = _mxml_global(); - // Global data - - - for (i = 0; i < global->num_entity_cbs; i ++) - { - if (cb == global->entity_cbs[i]) - { - // Remove the callback... - global->num_entity_cbs --; - - if (i < global->num_entity_cbs) - { - memmove(global->entity_cbs + i, global->entity_cbs + i + 1, (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0])); - memmove(global->entity_cbdatas + i, global->entity_cbdatas + i + 1, (global->num_entity_cbs - i) * sizeof(global->entity_cbdatas[0])); - } - return; - } - } -} - - -// -// '_mxml_entity_cb()' - Lookup standard (X)HTML entities. -// - -int // O - Unicode value or -1 -_mxml_entity_cb(void *cbdata, // I - Callback data (unused) - const char *name) // I - Entity name -{ - size_t i; // Looping var - int diff; // Difference - static const struct - { - const char *name; // Entity name - int val; // Character value - } entities[] = - { - { "AElig", 198 }, - { "Aacute", 193 }, - { "Acirc", 194 }, - { "Agrave", 192 }, - { "Alpha", 913 }, - { "Aring", 197 }, - { "Atilde", 195 }, - { "Auml", 196 }, - { "Beta", 914 }, - { "Ccedil", 199 }, - { "Chi", 935 }, - { "Dagger", 8225 }, - { "Delta", 916 }, - { "Dstrok", 208 }, - { "ETH", 208 }, - { "Eacute", 201 }, - { "Ecirc", 202 }, - { "Egrave", 200 }, - { "Epsilon", 917 }, - { "Eta", 919 }, - { "Euml", 203 }, - { "Gamma", 915 }, - { "Iacute", 205 }, - { "Icirc", 206 }, - { "Igrave", 204 }, - { "Iota", 921 }, - { "Iuml", 207 }, - { "Kappa", 922 }, - { "Lambda", 923 }, - { "Mu", 924 }, - { "Ntilde", 209 }, - { "Nu", 925 }, - { "OElig", 338 }, - { "Oacute", 211 }, - { "Ocirc", 212 }, - { "Ograve", 210 }, - { "Omega", 937 }, - { "Omicron", 927 }, - { "Oslash", 216 }, - { "Otilde", 213 }, - { "Ouml", 214 }, - { "Phi", 934 }, - { "Pi", 928 }, - { "Prime", 8243 }, - { "Psi", 936 }, - { "Rho", 929 }, - { "Scaron", 352 }, - { "Sigma", 931 }, - { "THORN", 222 }, - { "Tau", 932 }, - { "Theta", 920 }, - { "Uacute", 218 }, - { "Ucirc", 219 }, - { "Ugrave", 217 }, - { "Upsilon", 933 }, - { "Uuml", 220 }, - { "Xi", 926 }, - { "Yacute", 221 }, - { "Yuml", 376 }, - { "Zeta", 918 }, - { "aacute", 225 }, - { "acirc", 226 }, - { "acute", 180 }, - { "aelig", 230 }, - { "agrave", 224 }, - { "alefsym", 8501 }, - { "alpha", 945 }, - { "amp", '&' }, - { "and", 8743 }, - { "ang", 8736 }, - { "apos", '\'' }, - { "aring", 229 }, - { "asymp", 8776 }, - { "atilde", 227 }, - { "auml", 228 }, - { "bdquo", 8222 }, - { "beta", 946 }, - { "brkbar", 166 }, - { "brvbar", 166 }, - { "bull", 8226 }, - { "cap", 8745 }, - { "ccedil", 231 }, - { "cedil", 184 }, - { "cent", 162 }, - { "chi", 967 }, - { "circ", 710 }, - { "clubs", 9827 }, - { "cong", 8773 }, - { "copy", 169 }, - { "crarr", 8629 }, - { "cup", 8746 }, - { "curren", 164 }, - { "dArr", 8659 }, - { "dagger", 8224 }, - { "darr", 8595 }, - { "deg", 176 }, - { "delta", 948 }, - { "diams", 9830 }, - { "die", 168 }, - { "divide", 247 }, - { "eacute", 233 }, - { "ecirc", 234 }, - { "egrave", 232 }, - { "empty", 8709 }, - { "emsp", 8195 }, - { "ensp", 8194 }, - { "epsilon", 949 }, - { "equiv", 8801 }, - { "eta", 951 }, - { "eth", 240 }, - { "euml", 235 }, - { "euro", 8364 }, - { "exist", 8707 }, - { "fnof", 402 }, - { "forall", 8704 }, - { "frac12", 189 }, - { "frac14", 188 }, - { "frac34", 190 }, - { "frasl", 8260 }, - { "gamma", 947 }, - { "ge", 8805 }, - { "gt", '>' }, - { "hArr", 8660 }, - { "harr", 8596 }, - { "hearts", 9829 }, - { "hellip", 8230 }, - { "hibar", 175 }, - { "iacute", 237 }, - { "icirc", 238 }, - { "iexcl", 161 }, - { "igrave", 236 }, - { "image", 8465 }, - { "infin", 8734 }, - { "int", 8747 }, - { "iota", 953 }, - { "iquest", 191 }, - { "isin", 8712 }, - { "iuml", 239 }, - { "kappa", 954 }, - { "lArr", 8656 }, - { "lambda", 955 }, - { "lang", 9001 }, - { "laquo", 171 }, - { "larr", 8592 }, - { "lceil", 8968 }, - { "ldquo", 8220 }, - { "le", 8804 }, - { "lfloor", 8970 }, - { "lowast", 8727 }, - { "loz", 9674 }, - { "lrm", 8206 }, - { "lsaquo", 8249 }, - { "lsquo", 8216 }, - { "lt", '<' }, - { "macr", 175 }, - { "mdash", 8212 }, - { "micro", 181 }, - { "middot", 183 }, - { "minus", 8722 }, - { "mu", 956 }, - { "nabla", 8711 }, - { "nbsp", 160 }, - { "ndash", 8211 }, - { "ne", 8800 }, - { "ni", 8715 }, - { "not", 172 }, - { "notin", 8713 }, - { "nsub", 8836 }, - { "ntilde", 241 }, - { "nu", 957 }, - { "oacute", 243 }, - { "ocirc", 244 }, - { "oelig", 339 }, - { "ograve", 242 }, - { "oline", 8254 }, - { "omega", 969 }, - { "omicron", 959 }, - { "oplus", 8853 }, - { "or", 8744 }, - { "ordf", 170 }, - { "ordm", 186 }, - { "oslash", 248 }, - { "otilde", 245 }, - { "otimes", 8855 }, - { "ouml", 246 }, - { "para", 182 }, - { "part", 8706 }, - { "permil", 8240 }, - { "perp", 8869 }, - { "phi", 966 }, - { "pi", 960 }, - { "piv", 982 }, - { "plusmn", 177 }, - { "pound", 163 }, - { "prime", 8242 }, - { "prod", 8719 }, - { "prop", 8733 }, - { "psi", 968 }, - { "quot", '\"' }, - { "rArr", 8658 }, - { "radic", 8730 }, - { "rang", 9002 }, - { "raquo", 187 }, - { "rarr", 8594 }, - { "rceil", 8969 }, - { "rdquo", 8221 }, - { "real", 8476 }, - { "reg", 174 }, - { "rfloor", 8971 }, - { "rho", 961 }, - { "rlm", 8207 }, - { "rsaquo", 8250 }, - { "rsquo", 8217 }, - { "sbquo", 8218 }, - { "scaron", 353 }, - { "sdot", 8901 }, - { "sect", 167 }, - { "shy", 173 }, - { "sigma", 963 }, - { "sigmaf", 962 }, - { "sim", 8764 }, - { "spades", 9824 }, - { "sub", 8834 }, - { "sube", 8838 }, - { "sum", 8721 }, - { "sup", 8835 }, - { "sup1", 185 }, - { "sup2", 178 }, - { "sup3", 179 }, - { "supe", 8839 }, - { "szlig", 223 }, - { "tau", 964 }, - { "there4", 8756 }, - { "theta", 952 }, - { "thetasym", 977 }, - { "thinsp", 8201 }, - { "thorn", 254 }, - { "tilde", 732 }, - { "times", 215 }, - { "trade", 8482 }, - { "uArr", 8657 }, - { "uacute", 250 }, - { "uarr", 8593 }, - { "ucirc", 251 }, - { "ugrave", 249 }, - { "uml", 168 }, - { "upsih", 978 }, - { "upsilon", 965 }, - { "uuml", 252 }, - { "weierp", 8472 }, - { "xi", 958 }, - { "yacute", 253 }, - { "yen", 165 }, - { "yuml", 255 }, - { "zeta", 950 }, - { "zwj", 8205 }, - { "zwnj", 8204 } - }; - - - (void)cbdata; - - // Do a linear search for the named entity... - for (i = 0; i < (sizeof(entities) / sizeof(entities[0])); i ++) - { - if ((diff = strcmp(name, entities[i].name)) == 0) - return (entities[i].val); - else if (diff < 0) - break; - } - - return (-1); -} diff --git a/mxml-file.c b/mxml-file.c index 2c80c93..9bb8f21 100644 --- a/mxml-file.c +++ b/mxml-file.c @@ -46,26 +46,25 @@ typedef struct _mxml_stringbuf_s // String buffer // Local functions... // -static bool mxml_add_char(int ch, char **ptr, char **buffer, size_t *bufsize); -static int mxml_get_entity(mxml_read_cb_t read_cb, void *read_cbdata, _mxml_encoding_t *encoding, mxml_node_t *parent, int *line); -static int mxml_getc(mxml_read_cb_t read_cb, void *read_cbdata, _mxml_encoding_t *encoding); +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); static inline int mxml_isspace(int ch) { return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); } -static mxml_node_t *mxml_load_data(mxml_read_cb_t read_cb, void *read_cbdata, mxml_node_t *top, mxml_load_cb_t load_cb, void *load_cbdata, mxml_sax_cb_t sax_cb, void *sax_data); -static int mxml_parse_element(mxml_read_cb_t read_cb, void *read_cbdata, mxml_node_t *node, _mxml_encoding_t *encoding, int *line); -static ssize_t mxml_read_cb_fd(int *fd, void *buffer, size_t bytes); -static ssize_t mxml_read_cb_file(FILE *fp, void *buffer, size_t bytes); -static ssize_t mxml_read_cb_string(_mxml_stringbuf_t *sb, void *buffer, size_t bytes); -static void mxml_set_loc(_mxml_global_t *global); -static double mxml_strtod(_mxml_global_t *global, const char *buffer, char **bufptr); -static ssize_t mxml_write_cb_fd(int *fd, const void *buffer, size_t bytes); -static ssize_t mxml_write_cb_file(FILE *fp, const void *buffer, size_t bytes); -static ssize_t mxml_write_cb_string(_mxml_stringbuf_t *sb, const void *buffer, size_t bytes); -static int mxml_write_node(mxml_write_cb_t write_cb, void *write_cbdata, mxml_node_t *node, mxml_save_cb_t save_cb, void *save_cbdata, int col, _mxml_global_t *global); -static int mxml_write_string(mxml_write_cb_t write_cb, void *write_cbdata, const char *s, bool use_entities, int col); -static int mxml_write_ws(mxml_write_cb_t write_cb, void *write_cbdata, mxml_node_t *node, mxml_save_cb_t save_cb, void *save_cbdata, mxml_ws_t ws, int col); +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); // @@ -95,18 +94,15 @@ static int mxml_write_ws(mxml_write_cb_t write_cb, void *write_cbdata, mxml_nod mxml_node_t * // O - First node or `NULL` if the file could not be read. mxmlLoadFd( mxml_node_t *top, // I - Top node - int fd, // I - File descriptor to read from - mxml_load_cb_t load_cb, // I - Load callback function or `NULL` - void *load_cbdata, // I - Load callback data - mxml_sax_cb_t sax_cb, // I - SAX callback function or `NULL`` - void *sax_cbdata) // I - SAX callback data + mxml_options_t *options, // I - Options + int fd) // I - File descriptor to read from { // Range check input... if (fd < 0) return (NULL); // Read the XML data... - return (mxml_load_data((mxml_read_cb_t)mxml_read_cb_fd, &fd, top, load_cb, load_cbdata, sax_cb, sax_cbdata)); + return (mxml_load_data(top, options, (mxml_io_cb_t)mxml_read_cb_fd, &fd)); } @@ -137,18 +133,15 @@ mxmlLoadFd( mxml_node_t * // O - First node or `NULL` if the file could not be read. mxmlLoadFile( mxml_node_t *top, // I - Top node - FILE *fp, // I - File to read from - mxml_load_cb_t load_cb, // I - Load callback function or `NULL` - void *load_cbdata, // I - Load callback data - mxml_sax_cb_t sax_cb, // I - SAX callback function or `NULL`` - void *sax_cbdata) // I - SAX callback data + mxml_options_t *options, // I - Options + FILE *fp) // I - File to read from { // Range check input... if (!fp) return (NULL); // Read the XML data... - return (mxml_load_data((mxml_read_cb_t)mxml_read_cb_file, fp, top, load_cb, load_cbdata, sax_cb, sax_cbdata)); + return (mxml_load_data(top, options, (mxml_io_cb_t)mxml_read_cb_file, fp)); } @@ -179,11 +172,8 @@ mxmlLoadFile( mxml_node_t * // O - First node or `NULL` if the file could not be read. mxmlLoadFilename( mxml_node_t *top, // I - Top node - const char *filename, // I - File to read from - mxml_load_cb_t load_cb, // I - Load callback function or `NULL` - void *load_cbdata, // I - Load callback data - mxml_sax_cb_t sax_cb, // I - SAX callback function or `NULL`` - void *sax_cbdata) // I - SAX callback data + mxml_options_t *options, // I - Options + const char *filename) // I - File to read from { FILE *fp; // File pointer mxml_node_t *ret; // Node @@ -198,7 +188,7 @@ mxmlLoadFilename( return (NULL); // Read the XML data... - ret = mxml_load_data((mxml_read_cb_t)mxml_read_cb_file, fp, top, load_cb, load_cbdata, sax_cb, sax_cbdata); + ret = mxml_load_data(top, options, (mxml_io_cb_t)mxml_read_cb_file, fp); // Close the file and return... fclose(fp); @@ -215,13 +205,13 @@ mxmlLoadFilename( // is provided, the XML file MUST be well-formed with a single parent processing // instruction node like `` at the start of the file. // -// The read callback function `read_cb` is called to read a number of bytes from -// the source. The callback data pointer `read_cbdata` is passed to the read +// 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 // callback with a pointer to a buffer and the maximum number of bytes to read, // for example: // // ```c -// ssize_t my_read_cb(void *cbdata, void *buffer, size_t bytes) +// ssize_t my_io_cb(void *cbdata, void *buffer, size_t bytes) // { // ... copy up to "bytes" bytes into buffer ... // ... return the number of bytes "read" or -1 on error ... @@ -247,19 +237,16 @@ mxmlLoadFilename( mxml_node_t * // O - First node or `NULL` if the file could not be read. mxmlLoadIO( mxml_node_t *top, // I - Top node - mxml_read_cb_t read_cb, // I - Read callback function - void *read_cbdata, // I - Read callback data - mxml_load_cb_t load_cb, // I - Load callback function or `NULL` - void *load_cbdata, // I - Load callback data - mxml_sax_cb_t sax_cb, // I - SAX callback function or `NULL`` - void *sax_cbdata) // I - SAX callback data + mxml_options_t *options, // I - Options + mxml_io_cb_t io_cb, // I - Read callback function + void *io_cbdata) // I - Read callback data { // Range check input... - if (!read_cb) + if (!io_cb) return (NULL); // Read the XML data... - return (mxml_load_data(read_cb, read_cbdata, top, load_cb, load_cbdata, sax_cb, sax_cbdata)); + return (mxml_load_data(top, options, io_cb, io_cbdata)); } @@ -290,11 +277,8 @@ mxmlLoadIO( mxml_node_t * // O - First node or `NULL` if the string has errors. mxmlLoadString( mxml_node_t *top, // I - Top node - const char *s, // I - String to load - mxml_load_cb_t load_cb, // I - Load callback function or `NULL` - void *load_cbdata, // I - Load callback data - mxml_sax_cb_t sax_cb, // I - SAX callback function or `NULL`` - void *sax_cbdata) // I - SAX callback data + mxml_options_t *options, // I - Options + const char *s) // I - String to load { _mxml_stringbuf_t sb; // String buffer @@ -310,7 +294,7 @@ mxmlLoadString( sb.bufalloc = false; // Read the XML data... - return (mxml_load_data((mxml_read_cb_t)mxml_read_cb_string, &sb, top, load_cb, load_cbdata, sax_cb, sax_cbdata)); + return (mxml_load_data(top, options, (mxml_io_cb_t)mxml_read_cb_string, &sb)); } @@ -344,12 +328,9 @@ mxmlLoadString( char * // O - Allocated string or `NULL` mxmlSaveAllocString( mxml_node_t *node, // I - Node to write - mxml_save_cb_t save_cb, // I - Whitespace callback function - void *save_cbdata) // I - Whitespace callback data + mxml_options_t *options) // I - Options { _mxml_stringbuf_t sb; // String buffer - _mxml_global_t *global = _mxml_global(); - // Global data // Setup a string buffer @@ -361,7 +342,7 @@ mxmlSaveAllocString( sb.bufalloc = true; // Write the top node... - if (mxml_write_node((mxml_write_cb_t)mxml_write_cb_string, &sb, node, save_cb, save_cbdata, 0, global) < 0) + if (mxml_write_node(node, options, (mxml_io_cb_t)mxml_io_cb_string, &sb, 0) < 0) { free(sb.buffer); return (NULL); @@ -399,17 +380,14 @@ mxmlSaveAllocString( bool // O - `true` on success, `false` on error. mxmlSaveFd(mxml_node_t *node, // I - Node to write - int fd, // I - File descriptor to write to - mxml_save_cb_t save_cb, // I - Whitespace callback function - void *save_cbdata) // I - Whitespace callback data + mxml_options_t *options, // I - Options + int fd) // I - File descriptor to write to { int col; // Final column - _mxml_global_t *global = _mxml_global(); - // Global data // Write the node... - if ((col = mxml_write_node((mxml_write_cb_t)mxml_write_cb_fd, &fd, node, save_cb, save_cbdata, 0, global)) < 0) + if ((col = mxml_write_node(node, options, (mxml_io_cb_t)mxml_io_cb_fd, &fd, 0)) < 0) return (false); // Make sure the file ends with a newline... @@ -448,17 +426,14 @@ mxmlSaveFd(mxml_node_t *node, // I - Node to write bool // O - `true` on success, `false` on error. mxmlSaveFile( mxml_node_t *node, // I - Node to write - FILE *fp, // I - File to write to - mxml_save_cb_t save_cb, // I - Whitespace callback function - void *save_cbdata) // I - Whitespace callback data + mxml_options_t *options, // I - Options + FILE *fp) // I - File to write to { int col; // Final column - _mxml_global_t *global = _mxml_global(); - // Global data // Write the node... - if ((col = mxml_write_node((mxml_write_cb_t)mxml_write_cb_file, fp, node, save_cb, save_cbdata, 0, global)) < 0) + if ((col = mxml_write_node(node, options, (mxml_io_cb_t)mxml_io_cb_file, fp, 0)) < 0) return (false); // Make sure the file ends with a newline... @@ -497,15 +472,12 @@ mxmlSaveFile( bool // O - `true` on success, `false` on error. mxmlSaveFilename( mxml_node_t *node, // I - Node to write - const char *filename, // I - File to write to - mxml_save_cb_t save_cb, // I - Whitespace callback function - void *save_cbdata) // I - Whitespace callback data + mxml_options_t *options, // I - Options + const char *filename) // I - File to write to { bool ret = true; // Return value FILE *fp; // File pointer int col; // Final column - _mxml_global_t *global = _mxml_global(); - // Global data // Open the file... @@ -513,7 +485,7 @@ mxmlSaveFilename( return (false); // Write the node... - if ((col = mxml_write_node((mxml_write_cb_t)mxml_write_cb_file, fp, node, save_cb, save_cbdata, 0, global)) < 0) + if ((col = mxml_write_node(node, options, (mxml_io_cb_t)mxml_io_cb_file, fp, 0)) < 0) { ret = false; } @@ -534,12 +506,12 @@ mxmlSaveFilename( // 'mxmlSaveIO()' - Save an XML tree using a callback. // // This function saves the XML tree `node` using a write callback function -// `write_cb`. The write callback is called with the callback data pointer -// `write_cbdata`, a buffer pointer, and the number of bytes to write, for +// `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 // example: // // ```c -// ssize_t my_write_cb(void *cbdata, const void *buffer, size_t bytes) +// ssize_t my_io_cb(void *cbdata, const void *buffer, size_t bytes) // { // ... write/copy bytes from buffer to the output ... // ... return the number of bytes written/copied or -1 on error ... @@ -565,29 +537,26 @@ mxmlSaveFilename( bool // O - `true` on success, `false` on error. mxmlSaveIO( - mxml_node_t *node, // I - Node to write - mxml_write_cb_t write_cb, // I - Write callback function - void *write_cbdata, // I - Write callback data - mxml_save_cb_t save_cb, // I - Whitespace callback function - void *save_cbdata) // I - Whitespace callback data + 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; // Final column - _mxml_global_t *global = _mxml_global(); - // Global data // Range check input... - if (!node || !write_cb) + if (!node || !io_cb) return (false); // Write the node... - if ((col = mxml_write_node(write_cb, write_cbdata, node, save_cb, save_cbdata, 0, global)) < 0) + if ((col = mxml_write_node(node, options, io_cb, io_cbdata, 0)) < 0) return (false); if (col > 0) { // Make sure the file ends with a newline... - if ((write_cb)(write_cbdata, "\n", 1) < 0) + if ((io_cb)(io_cbdata, "\n", 1) < 0) return (false); } @@ -620,14 +589,11 @@ mxmlSaveIO( size_t // O - Size of string mxmlSaveString( mxml_node_t *node, // I - Node to write + mxml_options_t *options, // I - Options char *buffer, // I - String buffer - size_t bufsize, // I - Size of string buffer - mxml_save_cb_t save_cb, // I - Whitespace callback function - void *save_cbdata) // I - Whitespace callback function + size_t bufsize) // I - Size of string buffer { _mxml_stringbuf_t sb; // String buffer - _mxml_global_t *global = _mxml_global(); - // Global data // Setup the string buffer... @@ -637,7 +603,7 @@ mxmlSaveString( sb.bufalloc = false; // Write the node... - if (mxml_write_node((mxml_write_cb_t)mxml_write_cb_string, &sb, node, save_cb, save_cbdata, 0, global) < 0) + if (mxml_write_node(node, options, (mxml_io_cb_t)mxml_io_cb_string, &sb, 0) < 0) return (false); // Nul-terminate the string... @@ -649,33 +615,16 @@ mxmlSaveString( } -// -// 'mxmlSetWrapMargin()' - Set the wrap margin when saving XML data. -// -// This function sets the wrap margin used when saving XML data for the current -// thread. Wrapping is disabled when `column` is `0`. -// - -void -mxmlSetWrapMargin(int column) // I - Column for wrapping, 0 to disable wrapping -{ - _mxml_global_t *global = _mxml_global(); - // Global data - - - global->wrap = column; -} - - // // 'mxml_add_char()' - Add a character to a buffer, expanding as needed. // static bool // O - `true` on success, `false` on error -mxml_add_char(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 +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 { char *newbuffer; // New buffer value @@ -690,7 +639,7 @@ mxml_add_char(int ch, // I - Character to add if ((newbuffer = realloc(*buffer, *bufsize)) == NULL) { - _mxml_error("Unable to expand string buffer to %lu bytes.", (unsigned long)*bufsize); + _mxml_error(options, "Unable to expand string buffer to %lu bytes.", (unsigned long)*bufsize); return (false); } @@ -736,8 +685,9 @@ mxml_add_char(int ch, // I - Character to add static int // O - Character value or `EOF` on error mxml_get_entity( - mxml_read_cb_t read_cb, // I - Read callback function - void *read_cbdata, // I - Read callback data + mxml_options_t *options, // I - Options + mxml_io_cb_t io_cb, // I - Read callback function + void *io_cbdata, // I - Read callback data _mxml_encoding_t *encoding, // IO - Character encoding mxml_node_t *parent, // I - Parent node int *line) // IO - Current line number @@ -750,7 +700,7 @@ mxml_get_entity( // Read a HTML character entity of the form "&NAME;", "&#NUMBER;", or "&#xHEX"... entptr = entity; - while ((ch = mxml_getc(read_cb, read_cbdata, encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, encoding)) != EOF) { if (ch > 126 || (!isalnum(ch) && ch != '#')) { @@ -762,7 +712,7 @@ mxml_get_entity( } else { - _mxml_error("Entity name too long under parent <%s> on line %d.", mxmlGetElement(parent), *line); + _mxml_error(options, "Entity name too long under parent <%s> on line %d.", mxmlGetElement(parent), *line); break; } } @@ -771,7 +721,7 @@ mxml_get_entity( if (ch != ';') { - _mxml_error("Character entity '%s' not terminated under parent <%s> on line %d.", entity, mxmlGetElement(parent), *line); + _mxml_error(options, "Character entity '%s' not terminated under parent <%s> on line %d.", entity, mxmlGetElement(parent), *line); if (ch == '\n') (*line)++; @@ -779,22 +729,15 @@ mxml_get_entity( return (EOF); } - if (entity[0] == '#') - { - if (entity[1] == 'x') - ch = (int)strtol(entity + 2, NULL, 16); - else - ch = (int)strtol(entity + 1, NULL, 10); - } - else if ((ch = mxmlEntityGetValue(entity)) < 0) + if ((ch = _mxml_entity_value(options, entity)) < 0) { - _mxml_error("Entity '&%s;' not supported under parent <%s> on line %d.", entity, mxmlGetElement(parent), *line); + _mxml_error(options, "Entity '&%s;' not supported under parent <%s> on line %d.", entity, mxmlGetElement(parent), *line); return (EOF); } if (mxml_bad_char(ch)) { - _mxml_error("Bad control character 0x%02x under parent <%s> on line %d not allowed by XML standard.", ch, mxmlGetElement(parent), *line); + _mxml_error(options, "Bad control character 0x%02x under parent <%s> on line %d not allowed by XML standard.", ch, mxmlGetElement(parent), *line); return (EOF); } @@ -807,8 +750,9 @@ mxml_get_entity( // static int // O - Character or `EOF` -mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function - void *read_cbdata,// I - Read callback data +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 _mxml_encoding_t *encoding) // IO - Encoding { int ch; // Current character @@ -818,7 +762,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function // Grab the next character... read_first_byte: - if ((read_cb)(read_cbdata, buffer, 1) != 1) + if ((io_cb)(io_cbdata, buffer, 1) != 1) return (EOF); ch = buffer[0]; @@ -835,7 +779,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function else if (ch == 0xfe) { // UTF-16 big-endian BOM? - if ((read_cb)(read_cbdata, buffer + 1, 1) != 1) + if ((io_cb)(io_cbdata, buffer + 1, 1) != 1) return (EOF); if (buffer[1] != 0xff) @@ -849,7 +793,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function else if (ch == 0xff) { // UTF-16 little-endian BOM? - if ((read_cb)(read_cbdata, buffer + 1, 1) != 1) + if ((io_cb)(io_cbdata, buffer + 1, 1) != 1) return (EOF); if (buffer[1] != 0xfe) @@ -863,7 +807,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function else if ((ch & 0xe0) == 0xc0) { // Two-byte value... - if ((read_cb)(read_cbdata, buffer + 1, 1) != 1) + if ((io_cb)(io_cbdata, buffer + 1, 1) != 1) return (EOF); if ((buffer[1] & 0xc0) != 0x80) @@ -873,14 +817,14 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function if (ch < 0x80) { - _mxml_error("Invalid UTF-8 sequence for character 0x%04x.", ch); + _mxml_error(options, "Invalid UTF-8 sequence for character 0x%04x.", ch); return (EOF); } } else if ((ch & 0xf0) == 0xe0) { // Three-byte value... - if ((read_cb)(read_cbdata, buffer + 1, 2) != 2) + if ((io_cb)(io_cbdata, buffer + 1, 2) != 2) return (EOF); if ((buffer[1] & 0xc0) != 0x80 || (buffer[2] & 0xc0) != 0x80) @@ -890,7 +834,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function if (ch < 0x800) { - _mxml_error("Invalid UTF-8 sequence for character 0x%04x.", ch); + _mxml_error(options, "Invalid UTF-8 sequence for character 0x%04x.", ch); return (EOF); } @@ -901,7 +845,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function else if ((ch & 0xf8) == 0xf0) { // Four-byte value... - if ((read_cb)(read_cbdata, buffer + 1, 3) != 3) + if ((io_cb)(io_cbdata, buffer + 1, 3) != 3) return (EOF); if ((buffer[1] & 0xc0) != 0x80 || (buffer[2] & 0xc0) != 0x80 || (buffer[3] & 0xc0) != 0x80) @@ -911,7 +855,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function if (ch < 0x10000) { - _mxml_error("Invalid UTF-8 sequence for character 0x%04x.", ch); + _mxml_error(options, "Invalid UTF-8 sequence for character 0x%04x.", ch); return (EOF); } } @@ -923,7 +867,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function case _MXML_ENCODING_UTF16BE : // Read UTF-16 big-endian char... - if ((read_cb)(read_cbdata, buffer + 1, 1) != 1) + if ((io_cb)(io_cbdata, buffer + 1, 1) != 1) return (EOF); ch = (ch << 8) | buffer[1]; @@ -933,7 +877,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function // Multi-word UTF-16 char... int lch; // Lower bits - if ((read_cb)(read_cbdata, buffer + 2, 2) != 2) + if ((io_cb)(io_cbdata, buffer + 2, 2) != 2) return (EOF); lch = (buffer[2] << 8) | buffer[3]; @@ -947,7 +891,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function case _MXML_ENCODING_UTF16LE : // Read UTF-16 little-endian char... - if ((read_cb)(read_cbdata, buffer + 1, 1) != 1) + if ((io_cb)(io_cbdata, buffer + 1, 1) != 1) return (EOF); ch |= buffer[1] << 8; @@ -957,7 +901,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function // Multi-word UTF-16 char... int lch; // Lower bits - if ((read_cb)(read_cbdata, buffer + 2, 2) != 2) + if ((io_cb)(io_cbdata, buffer + 2, 2) != 2) return (EOF); lch = (buffer[3] << 8) | buffer[2]; @@ -974,7 +918,7 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function if (mxml_bad_char(ch)) { - _mxml_error("Bad control character 0x%02x not allowed by XML standard.", ch); + _mxml_error(options, "Bad control character 0x%02x not allowed by XML standard.", ch); return (EOF); } @@ -988,13 +932,10 @@ mxml_getc(mxml_read_cb_t read_cb, // I - Read callback function static mxml_node_t * // O - First node or `NULL` if the XML could not be read. mxml_load_data( - mxml_read_cb_t read_cb, // I - Read callback function - void *read_cbdata, // I - Read callback data mxml_node_t *top, // I - Top node - mxml_load_cb_t load_cb, // I - Load callback function - void *load_cbdata, // I - Load callback data - mxml_sax_cb_t sax_cb, // I - SAX callback function - void *sax_cbdata) // I - SAX callback data + mxml_options_t *options, // I - Options + mxml_io_cb_t io_cb, // I - Read callback function + void *io_cbdata) // I - Read callback data { mxml_node_t *node = NULL, // Current node *first = NULL, // First node added @@ -1008,8 +949,6 @@ mxml_load_data( mxml_type_t type; // Current node type _mxml_encoding_t encoding = _MXML_ENCODING_UTF8; // Character encoding - _mxml_global_t *global = _mxml_global(); - // Global data static const char * const types[] = // Type strings... { "MXML_TYPE_CDATA", // CDATA @@ -1028,7 +967,7 @@ mxml_load_data( // Read elements and other nodes from the file... if ((buffer = malloc(64)) == NULL) { - _mxml_error("Unable to allocate string buffer."); + _mxml_error(options, "Unable to allocate string buffer."); return (NULL); } @@ -1037,16 +976,14 @@ mxml_load_data( parent = top; first = NULL; - if (load_cb && parent) - type = (*load_cb)(load_cbdata, parent); - else if (!load_cb && load_cbdata) - type = *((mxml_type_t *)load_cbdata); - else if (parent) - type = MXML_TYPE_TEXT; + if (options && options->type_cb && parent) + type = (options->type_cb)(options->type_cbdata, parent); + else if (options && !options->type_cb) + type = options->type_value; else type = MXML_TYPE_IGNORE; - if ((ch = mxml_getc(read_cb, read_cbdata, &encoding)) == EOF) + if ((ch = mxml_getc(options, io_cb, io_cbdata, &encoding)) == EOF) { free(buffer); return (NULL); @@ -1054,7 +991,7 @@ mxml_load_data( else if (ch != '<' && !top) { free(buffer); - _mxml_error("XML does not start with '<' (saw '%c').", ch); + _mxml_error(options, "XML does not start with '<' (saw '%c').", ch); return (NULL); } @@ -1076,7 +1013,7 @@ mxml_load_data( break; case MXML_TYPE_REAL : - node = mxmlNewReal(parent, mxml_strtod(global, buffer, &bufptr)); + node = mxmlNewReal(parent, mxml_strtod(options, buffer, &bufptr)); break; case MXML_TYPE_TEXT : @@ -1084,14 +1021,14 @@ mxml_load_data( break; case MXML_TYPE_CUSTOM : - if (global->custom_load_cb) + if (options && options->custload_cb) { // Use the callback to fill in the custom data... - node = mxmlNewCustom(parent, NULL, NULL); + node = mxmlNewCustom(parent, /*data*/NULL, /*free_cb*/NULL, /*free_cbdata*/NULL); - if (!(*global->custom_load_cb)(global->custom_cbdata, node, buffer)) + if (!(options->custload_cb)(options->cust_cbdata, node, buffer)) { - _mxml_error("Bad custom value '%s' in parent <%s> on line %d.", buffer, parent ? parent->value.element.name : "null", line); + _mxml_error(options, "Bad custom value '%s' in parent <%s> on line %d.", buffer, parent ? parent->value.element.name : "null", line); mxmlDelete(node); node = NULL; } @@ -1106,7 +1043,7 @@ mxml_load_data( if (*bufptr) { // Bad integer/real number value... - _mxml_error("Bad %s value '%s' in parent <%s> on line %d.", type == MXML_TYPE_INTEGER ? "integer" : "real", buffer, parent ? parent->value.element.name : "null", line); + _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); break; } @@ -1118,13 +1055,13 @@ mxml_load_data( if (!node && type != MXML_TYPE_IGNORE) { // Print error and return... - _mxml_error("Unable to add value node of type %s to parent <%s> on line %d.", types[type], parent ? parent->value.element.name : "null", line); + _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); goto error; } - if (sax_cb) + if (options && options->sax_cb) { - if (!(sax_cb)(sax_cbdata, node, MXML_SAX_EVENT_DATA)) + if (!(options->sax_cb)(options->sax_cbdata, node, MXML_SAX_EVENT_DATA)) goto error; if (!mxmlRelease(node)) @@ -1149,9 +1086,9 @@ mxml_load_data( { node = mxmlNewText(parent, whitespace, ""); - if (sax_cb) + if (options && options->sax_cb) { - if (!(sax_cb)(sax_cbdata, node, MXML_SAX_EVENT_DATA)) + if (!(options->sax_cb)(options->sax_cbdata, node, MXML_SAX_EVENT_DATA)) goto error; if (!mxmlRelease(node)) @@ -1170,7 +1107,7 @@ mxml_load_data( // Start of open/close tag... bufptr = buffer; - while ((ch = mxml_getc(read_cb, read_cbdata, &encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, &encoding)) != EOF) { if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) { @@ -1178,22 +1115,22 @@ mxml_load_data( } else if (ch == '<') { - _mxml_error("Bare < in element."); + _mxml_error(options, "Bare < in element."); goto error; } else if (ch == '&') { - if ((ch = mxml_get_entity(read_cb, read_cbdata, &encoding, parent, &line)) == EOF) + if ((ch = mxml_get_entity(options, io_cb, io_cbdata, &encoding, parent, &line)) == EOF) goto error; - if (!mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + if (!mxml_add_char(options, ch, &bufptr, &buffer, &bufsize)) goto error; } else if (ch < '0' && ch != '!' && ch != '-' && ch != '.' && ch != '/') { goto error; } - else if (!mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + else if (!mxml_add_char(options, ch, &bufptr, &buffer, &bufsize)) { goto error; } @@ -1211,11 +1148,11 @@ mxml_load_data( if (!strcmp(buffer, "!--")) { // Gather rest of comment... - while ((ch = mxml_getc(read_cb, read_cbdata, &encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, &encoding)) != EOF) { if (ch == '>' && bufptr > (buffer + 4) && bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-') break; - else if (!mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + else if (!mxml_add_char(options, ch, &bufptr, &buffer, &bufsize)) goto error; if (ch == '\n') @@ -1226,7 +1163,7 @@ mxml_load_data( if (ch != '>') { // Print error and return... - _mxml_error("Early EOF in comment node on line %d.", line); + _mxml_error(options, "Early EOF in comment node on line %d.", line); goto error; } @@ -1236,22 +1173,22 @@ mxml_load_data( if (!parent && first) { // There can only be one root element! - _mxml_error("<%s--> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); + _mxml_error(options, "<%s--> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; } if ((node = mxmlNewComment(parent, buffer + 3)) == NULL) { // Just print error for now... - _mxml_error("Unable to add comment node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + _mxml_error(options, "Unable to add comment node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); break; } MXML_DEBUG("mxml_load_data: node=%p(<%s-->), parent=%p\n", node, buffer, parent); - if (sax_cb) + if (options && options->sax_cb) { - if (!(sax_cb)(sax_cbdata, node, MXML_SAX_EVENT_COMMENT)) + if (!(options->sax_cb)(options->sax_cbdata, node, MXML_SAX_EVENT_COMMENT)) goto error; if (!mxmlRelease(node)) @@ -1264,7 +1201,7 @@ mxml_load_data( else if (!strcmp(buffer, "![CDATA[")) { // Gather CDATA section... - while ((ch = mxml_getc(read_cb, read_cbdata, &encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, &encoding)) != EOF) { if (ch == '>' && !strncmp(bufptr - 2, "]]", 2)) { @@ -1272,7 +1209,7 @@ mxml_load_data( bufptr[-2] = '\0'; break; } - else if (!mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + else if (!mxml_add_char(options, ch, &bufptr, &buffer, &bufsize)) { goto error; } @@ -1285,7 +1222,7 @@ mxml_load_data( if (ch != '>') { // Print error and return... - _mxml_error("Early EOF in CDATA node on line %d.", line); + _mxml_error(options, "Early EOF in CDATA node on line %d.", line); goto error; } @@ -1295,22 +1232,22 @@ mxml_load_data( if (!parent && first) { // There can only be one root element! - _mxml_error("<%s]]> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); + _mxml_error(options, "<%s]]> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; } if ((node = mxmlNewCDATA(parent, buffer + 8)) == NULL) { // Print error and return... - _mxml_error("Unable to add CDATA node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + _mxml_error(options, "Unable to add CDATA node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); goto error; } MXML_DEBUG("mxml_load_data: node=%p(<%s]]>), parent=%p\n", node, buffer, parent); - if (sax_cb) + if (options && options->sax_cb) { - if (!(sax_cb)(sax_cbdata, node, MXML_SAX_EVENT_CDATA)) + if (!(options->sax_cb)(options->sax_cbdata, node, MXML_SAX_EVENT_CDATA)) goto error; if (!mxmlRelease(node)) @@ -1323,11 +1260,11 @@ mxml_load_data( else if (buffer[0] == '?') { // Gather rest of processing instruction... - while ((ch = mxml_getc(read_cb, read_cbdata, &encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, &encoding)) != EOF) { if (ch == '>' && bufptr > buffer && bufptr[-1] == '?') break; - else if (!mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + else if (!mxml_add_char(options, ch, &bufptr, &buffer, &bufsize)) goto error; if (ch == '\n') @@ -1338,7 +1275,7 @@ mxml_load_data( if (ch != '>') { // Print error and return... - _mxml_error("Early EOF in processing instruction node on line %d.", line); + _mxml_error(options, "Early EOF in processing instruction node on line %d.", line); goto error; } @@ -1348,22 +1285,22 @@ mxml_load_data( if (!parent && first) { // There can only be one root element! - _mxml_error("<%s?> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); + _mxml_error(options, "<%s?> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; } if ((node = mxmlNewDirective(parent, buffer + 1)) == NULL) { // Print error and return... - _mxml_error("Unable to add processing instruction node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + _mxml_error(options, "Unable to add processing instruction node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); goto error; } MXML_DEBUG("mxml_load_data: node=%p(<%s?>), parent=%p\n", node, buffer, parent); - if (sax_cb) + if (options && options->sax_cb) { - if (!(sax_cb)(sax_cbdata, node, MXML_SAX_EVENT_DIRECTIVE)) + if (!(options->sax_cb)(options->sax_cbdata, node, MXML_SAX_EVENT_DIRECTIVE)) goto error; if (strncmp(node->value.directive, "xml ", 4) && !mxmlRelease(node)) @@ -1379,10 +1316,10 @@ mxml_load_data( { parent = node; - if (load_cb) - type = (load_cb)(load_cbdata, parent); - else if (load_cbdata) - type = *((mxml_type_t *)load_cbdata); + if (options && options->type_cb) + type = (options->type_cb)(options->type_cbdata, parent); + else if (options) + type = options->type_value; else type = MXML_TYPE_TEXT; } @@ -1401,24 +1338,24 @@ mxml_load_data( { if (ch == '&') { - if ((ch = mxml_get_entity(read_cb, read_cbdata, &encoding, parent, &line)) == EOF) + if ((ch = mxml_get_entity(options, io_cb, io_cbdata, &encoding, parent, &line)) == EOF) goto error; } - if (!mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + if (!mxml_add_char(options, ch, &bufptr, &buffer, &bufsize)) goto error; } if (ch == '\n') line ++; } - while ((ch = mxml_getc(read_cb, read_cbdata, &encoding)) != EOF); + while ((ch = mxml_getc(options, io_cb, io_cbdata, &encoding)) != EOF); // Error out if we didn't get the whole declaration... if (ch != '>') { // Print error and return... - _mxml_error("Early EOF in declaration node on line %d.", line); + _mxml_error(options, "Early EOF in declaration node on line %d.", line); goto error; } @@ -1428,22 +1365,22 @@ mxml_load_data( if (!parent && first) { // There can only be one root element! - _mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); + _mxml_error(options, "<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; } if ((node = mxmlNewDeclaration(parent, buffer + 1)) == NULL) { // Print error and return... - _mxml_error("Unable to add declaration node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + _mxml_error(options, "Unable to add declaration node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); goto error; } MXML_DEBUG("mxml_load_data: node=%p(<%s>), parent=%p\n", node, buffer, parent); - if (sax_cb) + if (options && options->sax_cb) { - if (!(sax_cb)(sax_cbdata, node, MXML_SAX_EVENT_DECLARATION)) + if (!(options->sax_cb)(options->sax_cbdata, node, MXML_SAX_EVENT_DECLARATION)) goto error; if (!mxmlRelease(node)) @@ -1459,10 +1396,10 @@ mxml_load_data( { parent = node; - if (load_cb) - type = (load_cb)(load_cbdata, parent); - else if (load_cbdata) - type = *((mxml_type_t *)load_cbdata); + if (options && options->type_cb) + type = (options->type_cb)(options->type_cbdata, parent); + else if (options) + type = options->type_value; else type = MXML_TYPE_TEXT; } @@ -1476,20 +1413,20 @@ mxml_load_data( if (!parent || strcmp(buffer + 1, parent->value.element.name)) { // Close tag doesn't match tree; print an error for now... - _mxml_error("Mismatched close tag <%s> under parent <%s> on line %d.", buffer, parent ? parent->value.element.name : "(null)", line); + _mxml_error(options, "Mismatched close tag <%s> under parent <%s> on line %d.", buffer, parent ? parent->value.element.name : "(null)", line); goto error; } // Keep reading until we see >... while (ch != '>' && ch != EOF) - ch = mxml_getc(read_cb, read_cbdata, &encoding); + ch = mxml_getc(options, io_cb, io_cbdata, &encoding); node = parent; parent = parent->parent; - if (sax_cb) + if (options && options->sax_cb) { - if (!(sax_cb)(sax_cbdata, node, MXML_SAX_EVENT_ELEMENT_CLOSE)) + if (!(options->sax_cb)(options->sax_cbdata, node, MXML_SAX_EVENT_ELEMENT_CLOSE)) goto error; if (!mxmlRelease(node)) @@ -1502,10 +1439,10 @@ mxml_load_data( } // Ascend into the parent and set the value type as needed... - if (load_cb && parent) - type = (load_cb)(load_cbdata, parent); - else if (!load_cb && load_cbdata) - type = *((mxml_type_t *)load_cbdata); + if (options && options->type_cb && parent) + type = (options->type_cb)(options->type_cbdata, parent); + else if (options && !options->type_cb) + type = options->type_value; } else { @@ -1513,14 +1450,14 @@ mxml_load_data( if (!parent && first) { // There can only be one root element! - _mxml_error("<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); + _mxml_error(options, "<%s> cannot be a second root node after <%s> on line %d.", buffer, first->value.element.name, line); goto error; } if ((node = mxmlNewElement(parent, buffer)) == NULL) { // Just print error for now... - _mxml_error("Unable to add element node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); + _mxml_error(options, "Unable to add element node to parent <%s> on line %d.", parent ? parent->value.element.name : "null", line); goto error; } @@ -1528,16 +1465,16 @@ mxml_load_data( { MXML_DEBUG("mxml_load_data: node=%p(<%s...>), parent=%p\n", node, buffer, parent); - if ((ch = mxml_parse_element(read_cb, read_cbdata, node, &encoding, &line)) == EOF) + if ((ch = mxml_parse_element(options, io_cb, io_cbdata, node, &encoding, &line)) == EOF) goto error; } else if (ch == '/') { MXML_DEBUG("mxml_load_data: node=%p(<%s/>), parent=%p\n", node, buffer, parent); - if ((ch = mxml_getc(read_cb, read_cbdata, &encoding)) != '>') + if ((ch = mxml_getc(options, io_cb, io_cbdata, &encoding)) != '>') { - _mxml_error("Expected > but got '%c' instead for element <%s/> on line %d.", ch, buffer, line); + _mxml_error(options, "Expected > but got '%c' instead for element <%s/> on line %d.", ch, buffer, line); mxmlDelete(node); node = NULL; goto error; @@ -1546,9 +1483,9 @@ mxml_load_data( ch = '/'; } - if (sax_cb) + if (options && options->sax_cb) { - if (!(sax_cb)(sax_cbdata, node, MXML_SAX_EVENT_ELEMENT_OPEN)) + if (!(options->sax_cb)(options->sax_cbdata, node, MXML_SAX_EVENT_ELEMENT_OPEN)) goto error; } @@ -1563,16 +1500,16 @@ mxml_load_data( // Descend into this node, setting the value type as needed... parent = node; - if (load_cb && parent) - type = (load_cb)(load_cbdata, parent); - else if (!load_cb && load_cbdata) - type = *((mxml_type_t *)load_cbdata); + if (options && options->type_cb && parent) + type = (options->type_cb)(options->type_cbdata, parent); + else if (options && !options->type_cb) + type = options->type_value; else type = MXML_TYPE_TEXT; } - else if (sax_cb) + else if (options && options->sax_cb) { - if (!(sax_cb)(sax_cbdata, node, MXML_SAX_EVENT_ELEMENT_CLOSE)) + if (!(options->sax_cb)(options->sax_cbdata, node, MXML_SAX_EVENT_ELEMENT_CLOSE)) goto error; if (!mxmlRelease(node)) @@ -1590,20 +1527,20 @@ mxml_load_data( else if (ch == '&') { // Add character entity to current buffer... - if ((ch = mxml_get_entity(read_cb, read_cbdata, &encoding, parent, &line)) == EOF) + if ((ch = mxml_get_entity(options, io_cb, io_cbdata, &encoding, parent, &line)) == EOF) goto error; - if (!mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + if (!mxml_add_char(options, ch, &bufptr, &buffer, &bufsize)) goto error; } else if (type == MXML_TYPE_OPAQUE || type == MXML_TYPE_CUSTOM || !mxml_isspace(ch)) { // Add character to current buffer... - if (!mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + if (!mxml_add_char(options, ch, &bufptr, &buffer, &bufsize)) goto error; } } - while ((ch = mxml_getc(read_cb, read_cbdata, &encoding)) != EOF); + while ((ch = mxml_getc(options, io_cb, io_cbdata, &encoding)) != EOF); // Free the string buffer - we don't need it anymore... free(buffer); @@ -1618,7 +1555,7 @@ mxml_load_data( if (node != parent) { - _mxml_error("Missing close tag under parent <%s> on line %d.", mxmlGetElement(node), node->parent ? node->parent->value.element.name : "(null)", line); + _mxml_error(options, "Missing close tag under parent <%s> on line %d.", mxmlGetElement(node), node->parent ? node->parent->value.element.name : "(null)", line); mxmlDelete(first); @@ -1648,8 +1585,9 @@ mxml_load_data( static int // O - Terminating character mxml_parse_element( - mxml_read_cb_t read_cb, // I - Read callback function - void *read_cbdata, // I - Read callback data + mxml_options_t *options, // I - Options + mxml_io_cb_t io_cb, // I - Read callback function + void *io_cbdata, // I - Read callback data mxml_node_t *node, // I - Element node _mxml_encoding_t *encoding, // IO - Encoding int *line) // IO - Current line number @@ -1666,7 +1604,7 @@ mxml_parse_element( // Initialize the name and value buffers... if ((name = malloc(64)) == NULL) { - _mxml_error("Unable to allocate memory for name."); + _mxml_error(options, "Unable to allocate memory for name."); return (EOF); } @@ -1675,14 +1613,14 @@ mxml_parse_element( if ((value = malloc(64)) == NULL) { free(name); - _mxml_error("Unable to allocate memory for value."); + _mxml_error(options, "Unable to allocate memory for value."); return (EOF); } valsize = 64; // Loop until we hit a >, /, ?, or EOF... - while ((ch = mxml_getc(read_cb, read_cbdata, encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, encoding)) != EOF) { MXML_DEBUG("mxml_parse_element: ch='%c'\n", ch); @@ -1699,11 +1637,11 @@ mxml_parse_element( if (ch == '/' || ch == '?') { // Grab the > character and print an error if it isn't there... - quote = mxml_getc(read_cb, read_cbdata, encoding); + quote = mxml_getc(options, io_cb, io_cbdata, encoding); if (quote != '>') { - _mxml_error("Expected '>' after '%c' for element %s, but got '%c' on line %d.", ch, mxmlGetElement(node), quote, *line); + _mxml_error(options, "Expected '>' after '%c' for element %s, but got '%c' on line %d.", ch, mxmlGetElement(node), quote, *line); goto error; } @@ -1711,7 +1649,7 @@ mxml_parse_element( } else if (ch == '<') { - _mxml_error("Bare < in element %s on line %d.", mxmlGetElement(node), *line); + _mxml_error(options, "Bare < in element %s on line %d.", mxmlGetElement(node), *line); goto error; } else if (ch == '>') @@ -1721,7 +1659,7 @@ mxml_parse_element( // Read the attribute name... ptr = name; - if (!mxml_add_char(ch, &ptr, &name, &namesize)) + if (!mxml_add_char(options, ch, &ptr, &name, &namesize)) goto error; if (ch == '\"' || ch == '\'') @@ -1729,11 +1667,11 @@ mxml_parse_element( // Name is in quotes, so get a quoted string... quote = ch; - while ((ch = mxml_getc(read_cb, read_cbdata, encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, encoding)) != EOF) { if (ch == '&') { - if ((ch = mxml_get_entity(read_cb, read_cbdata, encoding, node, line)) == EOF) + if ((ch = mxml_get_entity(options, io_cb, io_cbdata, encoding, node, line)) == EOF) goto error; } else if (ch == '\n') @@ -1741,7 +1679,7 @@ mxml_parse_element( (*line)++; } - if (!mxml_add_char(ch, &ptr, &name, &namesize)) + if (!mxml_add_char(options, ch, &ptr, &name, &namesize)) goto error; if (ch == quote) @@ -1751,7 +1689,7 @@ mxml_parse_element( else { // Grab an normal, non-quoted name... - while ((ch = mxml_getc(read_cb, read_cbdata, encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, encoding)) != EOF) { if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' || ch == '?') { @@ -1763,11 +1701,11 @@ mxml_parse_element( { if (ch == '&') { - if ((ch = mxml_get_entity(read_cb, read_cbdata, encoding, node, line)) == EOF) + if ((ch = mxml_get_entity(options, io_cb, io_cbdata, encoding, node, line)) == EOF) goto error; } - if (!mxml_add_char(ch, &ptr, &name, &namesize)) + if (!mxml_add_char(options, ch, &ptr, &name, &namesize)) goto error; } } @@ -1777,13 +1715,13 @@ mxml_parse_element( if (mxmlElementGetAttr(node, name)) { - _mxml_error("Duplicate attribute '%s' in element %s on line %d.", name, mxmlGetElement(node), *line); + _mxml_error(options, "Duplicate attribute '%s' in element %s on line %d.", name, mxmlGetElement(node), *line); goto error; } while (ch != EOF && mxml_isspace(ch)) { - ch = mxml_getc(read_cb, read_cbdata, encoding); + ch = mxml_getc(options, io_cb, io_cbdata, encoding); if (ch == '\n') (*line)++; @@ -1792,7 +1730,7 @@ mxml_parse_element( if (ch == '=') { // Read the attribute value... - while ((ch = mxml_getc(read_cb, read_cbdata, encoding)) != EOF && mxml_isspace(ch)) + while ((ch = mxml_getc(options, io_cb, io_cbdata, encoding)) != EOF && mxml_isspace(ch)) { if (ch == '\n') (*line)++; @@ -1800,7 +1738,7 @@ mxml_parse_element( if (ch == EOF) { - _mxml_error("Missing value for attribute '%s' in element %s on line %d.", name, mxmlGetElement(node), *line); + _mxml_error(options, "Missing value for attribute '%s' in element %s on line %d.", name, mxmlGetElement(node), *line); goto error; } @@ -1810,7 +1748,7 @@ mxml_parse_element( quote = ch; ptr = value; - while ((ch = mxml_getc(read_cb, read_cbdata, encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, encoding)) != EOF) { if (ch == quote) { @@ -1820,7 +1758,7 @@ mxml_parse_element( { if (ch == '&') { - if ((ch = mxml_get_entity(read_cb, read_cbdata, encoding, node, line)) == EOF) + if ((ch = mxml_get_entity(options, io_cb, io_cbdata, encoding, node, line)) == EOF) goto error; } else if (ch == '\n') @@ -1828,7 +1766,7 @@ mxml_parse_element( (*line)++; } - if (!mxml_add_char(ch, &ptr, &value, &valsize)) + if (!mxml_add_char(options, ch, &ptr, &value, &valsize)) goto error; } } @@ -1839,10 +1777,10 @@ mxml_parse_element( { // Read unquoted value... ptr = value; - if (!mxml_add_char(ch, &ptr, &value, &valsize)) + if (!mxml_add_char(options, ch, &ptr, &value, &valsize)) goto error; - while ((ch = mxml_getc(read_cb, read_cbdata, encoding)) != EOF) + while ((ch = mxml_getc(options, io_cb, io_cbdata, encoding)) != EOF) { if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>') { @@ -1855,11 +1793,11 @@ mxml_parse_element( { if (ch == '&') { - if ((ch = mxml_get_entity(read_cb, read_cbdata, encoding, node, line)) == EOF) + if ((ch = mxml_get_entity(options, io_cb, io_cbdata, encoding, node, line)) == EOF) goto error; } - if (!mxml_add_char(ch, &ptr, &value, &valsize)) + if (!mxml_add_char(options, ch, &ptr, &value, &valsize)) goto error; } } @@ -1873,7 +1811,7 @@ mxml_parse_element( } else { - _mxml_error("Missing value for attribute '%s' in element %s on line %d.", name, mxmlGetElement(node), *line); + _mxml_error(options, "Missing value for attribute '%s' in element %s on line %d.", name, mxmlGetElement(node), *line); goto error; } @@ -1881,11 +1819,11 @@ mxml_parse_element( if (ch == '/' || ch == '?') { // Grab the > character and print an error if it isn't there... - quote = mxml_getc(read_cb, read_cbdata, encoding); + quote = mxml_getc(options, io_cb, io_cbdata, encoding); if (quote != '>') { - _mxml_error("Expected '>' after '%c' for element %s, but got '%c' on line %d.", ch, mxmlGetElement(node), quote, *line); + _mxml_error(options, "Expected '>' after '%c' for element %s, but got '%c' on line %d.", ch, mxmlGetElement(node), quote, *line); ch = EOF; } @@ -1915,13 +1853,28 @@ mxml_parse_element( // 'mxml_read_cb_fd()' - Read bytes from a file descriptor. // -static ssize_t // O - Bytes read +static size_t // O - Bytes read mxml_read_cb_fd(int *fd, // I - File descriptor void *buffer, // I - Buffer size_t bytes) // I - Bytes to read { - // TODO: Handle EAGAIN/EINTR? - return (read(*fd, buffer, bytes)); + ssize_t rbytes; // Bytes read + + +#if _WIN32 + rbytes = read(*fd, buffer, buffer); +#else + 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); } @@ -1929,15 +1882,15 @@ mxml_read_cb_fd(int *fd, // I - File descriptor // 'mxml_read_cb_file()' - Read bytes from a file pointer. // -static ssize_t // O - Bytes read +static size_t // O - Bytes read mxml_read_cb_file(FILE *fp, // I - File pointer void *buffer, // I - Buffer size_t bytes) // I - Bytes to read { if (feof(fp)) - return (-1); + return (0); else - return ((ssize_t)fread(buffer, 1, bytes, fp)); + return (fread(buffer, 1, bytes, fp)); } @@ -1945,7 +1898,7 @@ mxml_read_cb_file(FILE *fp, // I - File pointer // 'mxml_read_cb_string()' - Read bytes from a string. // -static ssize_t // O - Bytes read +static size_t // O - Bytes read mxml_read_cb_string( _mxml_stringbuf_t *sb, // I - String buffer void *buffer, // I - Buffer @@ -1968,31 +1921,12 @@ mxml_read_cb_string( } -// -// 'mxml_set_loc()' - Set the locale values for numbers. -// - -static void -mxml_set_loc(_mxml_global_t *global) // I - Global data -{ - if ((global->loc = localeconv()) != NULL) - { - if (!global->loc->decimal_point || !strcmp(global->loc->decimal_point, ".")) - global->loc = NULL; - else - global->loc_declen = strlen(global->loc->decimal_point); - } - - global->loc_set = true; -} - - // // 'mxml_strtod()' - Convert a string to a double without respect to the locale. // static double // O - Real number -mxml_strtod(_mxml_global_t *global, // I - Global data +mxml_strtod(mxml_options_t *options, // I - Options const char *buffer, // I - String char **bufend) // O - End of number in string { @@ -2002,10 +1936,7 @@ mxml_strtod(_mxml_global_t *global, // I - Global data // See if the locale has a special decimal point string... - if (!global->loc_set) - mxml_set_loc(global); - - if (!global->loc) + if (!options || !options->loc) return (strtod(buffer, bufend)); // Copy leading sign, numbers, period, and then numbers... @@ -2032,13 +1963,13 @@ mxml_strtod(_mxml_global_t *global, // I - Global data if (*bufptr == '.') { // Convert decimal point to locale equivalent... - size_t declen = strlen(global->loc->decimal_point); + size_t declen = strlen(options->loc->decimal_point); // Length of decimal point bufptr ++; if (declen <= (sizeof(temp) - (size_t)(tempptr - temp))) { - memcpy(tempptr, global->loc->decimal_point, declen); + memcpy(tempptr, options->loc->decimal_point, declen); tempptr += declen; } else @@ -2070,40 +2001,55 @@ mxml_strtod(_mxml_global_t *global, // I - Global data // -// 'mxml_write_cb_fd()' - Write bytes to a file descriptor. +// 'mxml_io_cb_fd()' - Write bytes to a file descriptor. // -static ssize_t // O - Bytes written -mxml_write_cb_fd(int *fd, // I - File descriptor - const void *buffer, // I - Buffer - size_t bytes) // I - Bytes to write +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 { - // TODO: Handle EAGAIN/EINTR? - return (write(*fd, buffer, bytes)); + ssize_t wbytes; // Bytes written + + +#if _WIN32 + wbytes = write(*fd, buffer, bytes); +#else + 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); } // -// 'mxml_write_cb_file()' - Write bytes to a file pointer. +// 'mxml_io_cb_file()' - Write bytes to a file pointer. // -static ssize_t // O - Bytes written -mxml_write_cb_file(FILE *fp, // I - File pointer - const void *buffer, // I - Buffer - size_t bytes) // I - Bytes to write +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 { - return ((ssize_t)fwrite(buffer, 1, bytes, fp)); + return (fwrite(buffer, 1, bytes, fp)); } // -// 'mxml_write_cb_string()' - Write bytes to a string buffer. +// 'mxml_io_cb_string()' - Write bytes to a string buffer. // -static ssize_t // O - Bytes written -mxml_write_cb_string( +static size_t // O - Bytes written +mxml_io_cb_string( _mxml_stringbuf_t *sb, // I - String buffer - const void *buffer, // I - Buffer + void *buffer, // I - Buffer size_t bytes) // I - Bytes to write { size_t remaining; // Remaining bytes @@ -2118,10 +2064,7 @@ mxml_write_cb_string( newsize = (size_t)(sb->bufptr - sb->buffer) + bytes + 257; if ((temp = realloc(sb->buffer, newsize)) == NULL) - { - _mxml_error("Unable to expand string buffer - %s", strerror(errno)); - return (-1); - } + return (0); sb->bufptr = temp + (sb->bufptr - sb->buffer); sb->buffer = temp; @@ -2130,7 +2073,7 @@ mxml_write_cb_string( // Copy what we can... if (sb->bufptr >= (sb->buffer + sb->bufsize - 1)) - return (-1); // No more room + return (0); // No more room else if ((remaining = (sb->bufsize - (size_t)(sb->bufptr - sb->buffer) - 1)) < bytes) bytes = remaining; @@ -2147,13 +2090,11 @@ mxml_write_cb_string( static int // O - Column or -1 on error mxml_write_node( - mxml_write_cb_t write_cb, // I - Write callback function - void *write_cbdata, // I - Write callback data - mxml_node_t *node, // I - Node to write - mxml_save_cb_t save_cb, // I - Whitespace callback function - void *save_cbdata, // I - Whitespace callback data - int col, // I - Current column - _mxml_global_t *global) // I - Global data + 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 { mxml_node_t *current, // Current node *next; // Next node @@ -2175,41 +2116,41 @@ mxml_write_node( switch (mxmlGetType(current)) { case MXML_TYPE_CDATA : - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_BEFORE_OPEN, col); - col = mxml_write_string(write_cb, write_cbdata, "", /*use_entities*/false, col); - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_AFTER_OPEN, col); + 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_ws(current, options, io_cb, io_cbdata, MXML_WS_AFTER_OPEN, col); break; case MXML_TYPE_COMMENT : - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_BEFORE_OPEN, col); - col = mxml_write_string(write_cb, write_cbdata, "", /*use_entities*/false, col); - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_AFTER_OPEN, col); + 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_ws(current, options, io_cb, io_cbdata, MXML_WS_AFTER_OPEN, col); break; case MXML_TYPE_DECLARATION : - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_BEFORE_OPEN, col); - col = mxml_write_string(write_cb, write_cbdata, "", /*use_entities*/false, col); - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_AFTER_OPEN, col); + 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_ws(current, options, io_cb, io_cbdata, MXML_WS_AFTER_OPEN, col); break; case MXML_TYPE_DIRECTIVE : - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_BEFORE_OPEN, col); - col = mxml_write_string(write_cb, write_cbdata, "", /*use_entities*/false, col); - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_AFTER_OPEN, col); + 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_ws(current, options, io_cb, io_cbdata, MXML_WS_AFTER_OPEN, col); break; case MXML_TYPE_ELEMENT : - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_BEFORE_OPEN, col); - col = mxml_write_string(write_cb, write_cbdata, "<", /*use_entities*/false, col); - col = mxml_write_string(write_cb, write_cbdata, mxmlGetElement(current), /*use_entities*/true, col); + 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); for (i = current->value.element.num_attrs, attr = current->value.element.attrs; i > 0 && col >= 0; i --, attr ++) { @@ -2218,75 +2159,72 @@ mxml_write_node( if (attr->value) width += strlen(attr->value) + 3; - if (global->wrap > 0 && (col + width) > global->wrap) - col = mxml_write_string(write_cb, write_cbdata, "\n", /*use_entities*/false, col); + if (options && options->wrap > 0 && (col + width) > options->wrap) + col = mxml_write_string("\n", io_cb, io_cbdata, /*use_entities*/false, col); else - col = mxml_write_string(write_cb, write_cbdata, " ", /*use_entities*/false, col); + col = mxml_write_string(" ", io_cb, io_cbdata, /*use_entities*/false, col); - col = mxml_write_string(write_cb, write_cbdata, attr->name, /*use_entities*/true, col); + col = mxml_write_string(attr->name, io_cb, io_cbdata, /*use_entities*/true, col); if (attr->value) { - col = mxml_write_string(write_cb, write_cbdata, "=\"", /*use_entities*/false, col); - col = mxml_write_string(write_cb, write_cbdata, attr->value, /*use_entities*/true, col); - col = mxml_write_string(write_cb, write_cbdata, "\"", /*use_entities*/false, col); + 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); } } - col = mxml_write_string(write_cb, write_cbdata, current->child ? ">" : "/>", /*use_entities*/false, col); - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_AFTER_OPEN, col); + 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); break; case MXML_TYPE_INTEGER : if (current->prev) { // Add whitespace separator... - if (global->wrap > 0 && col > global->wrap) - col = mxml_write_string(write_cb, write_cbdata, "\n", /*use_entities*/false, col); + if (options && options->wrap > 0 && col > options->wrap) + col = mxml_write_string("\n", io_cb, io_cbdata, /*use_entities*/false, col); else - col = mxml_write_string(write_cb, write_cbdata, " ", /*use_entities*/false, col); + col = mxml_write_string(" ", io_cb, io_cbdata, /*use_entities*/false, col); } // Write integer... snprintf(s, sizeof(s), "%ld", current->value.integer); - col = mxml_write_string(write_cb, write_cbdata, s, /*use_entities*/true, col); + col = mxml_write_string(s, io_cb, io_cbdata, /*use_entities*/true, col); break; case MXML_TYPE_OPAQUE : - col = mxml_write_string(write_cb, write_cbdata, mxmlGetOpaque(current), /*use_entities*/true, col); + col = mxml_write_string(mxmlGetOpaque(current), io_cb, io_cbdata, /*use_entities*/true, col); break; case MXML_TYPE_REAL : if (current->prev) { // Add whitespace separator... - if (global->wrap > 0 && col > global->wrap) - col = mxml_write_string(write_cb, write_cbdata, "\n", /*use_entities*/false, col); + if (options && options->wrap > 0 && col > options->wrap) + col = mxml_write_string("\n", io_cb, io_cbdata, /*use_entities*/false, col); else - col = mxml_write_string(write_cb, write_cbdata, " ", /*use_entities*/false, col); + col = mxml_write_string(" ", io_cb, io_cbdata, /*use_entities*/false, col); } // Write real number... snprintf(s, sizeof(s), "%g", current->value.real); - if (!global->loc_set) - mxml_set_loc(global); - - if (global->loc) + if (options && options->loc) { char *sptr; // Pointer into string - if ((sptr = strstr(s, global->loc->decimal_point)) != NULL) + if ((sptr = strstr(s, options->loc->decimal_point)) != NULL) { // Convert locale decimal point to "." - if (global->loc_declen > 1) - memmove(sptr + 1, sptr + global->loc_declen, strlen(sptr + global->loc_declen) + 1); + if (options->loc_declen > 1) + memmove(sptr + 1, sptr + options->loc_declen, strlen(sptr + options->loc_declen) + 1); *sptr = '.'; } } - col = mxml_write_string(write_cb, write_cbdata, s, /*use_entities*/true, col); + col = mxml_write_string(s, io_cb, io_cbdata, /*use_entities*/true, col); break; case MXML_TYPE_TEXT : @@ -2295,23 +2233,23 @@ mxml_write_node( if (whitespace && col > 0) { // Add whitespace separator... - if (global->wrap > 0 && col > global->wrap) - col = mxml_write_string(write_cb, write_cbdata, "\n", /*use_entities*/false, col); + if (options && options->wrap > 0 && col > options->wrap) + col = mxml_write_string("\n", io_cb, io_cbdata, /*use_entities*/false, col); else - col = mxml_write_string(write_cb, write_cbdata, " ", /*use_entities*/false, col); + col = mxml_write_string(" ", io_cb, io_cbdata, /*use_entities*/false, col); } - col = mxml_write_string(write_cb, write_cbdata, text, /*use_entities*/true, col); + col = mxml_write_string(text, io_cb, io_cbdata, /*use_entities*/true, col); break; case MXML_TYPE_CUSTOM : - if (!global->custom_save_cb) + if (!options || !options->custsave_cb) return (-1); - if ((data = (*global->custom_save_cb)(global->custom_cbdata, current)) == NULL) + if ((data = (options->custsave_cb)(options->cust_cbdata, current)) == NULL) return (-1); - col = mxml_write_string(write_cb, write_cbdata, data, /*use_entities*/true, col); + col = mxml_write_string(data, io_cb, io_cbdata, /*use_entities*/true, col); free(data); break; @@ -2341,11 +2279,11 @@ mxml_write_node( if (mxmlGetType(current) == MXML_TYPE_ELEMENT) { - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_BEFORE_CLOSE, col); - col = mxml_write_string(write_cb, write_cbdata, "", /*use_entities*/false, col); - col = mxml_write_ws(write_cb, write_cbdata, current, save_cb, save_cbdata, MXML_WS_AFTER_CLOSE, col); + 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_ws(current, options, io_cb, io_cbdata, MXML_WS_AFTER_CLOSE, col); } if (current == node) @@ -2365,9 +2303,9 @@ mxml_write_node( static int // O - New column or `-1` on error mxml_write_string( - mxml_write_cb_t write_cb, // I - Write callback function - void *write_cbdata, // I - Write callback data const char *s, // I - String to write + mxml_io_cb_t io_cb, // I - Write callback function + void *io_cbdata, // I - Write callback data bool use_entities, // I - Escape special characters? int col) // I - Current column { @@ -2377,7 +2315,7 @@ mxml_write_string( size_t fraglen; // Length of fragment - MXML_DEBUG("mxml_write_string(write_cb=%p, write_cbdata=%p, s=\"%s\", use_entities=%s, col=%d)\n", write_cb, write_cbdata, s, use_entities ? "true" : "false", col); + 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); if (col < 0) return (-1); @@ -2393,14 +2331,14 @@ mxml_write_string( // Write current fragment fraglen = (size_t)(ptr - frag); - if ((write_cb)(write_cbdata, frag, fraglen) != (ssize_t)fraglen) + if ((io_cb)(io_cbdata, (char *)frag, fraglen) != fraglen) return (-1); } frag = ptr + 1; // Write entity - if ((write_cb)(write_cbdata, ent, entlen) != (ssize_t)entlen) + if ((io_cb)(io_cbdata, (char *)ent, entlen) != entlen) return (-1); col ++; @@ -2427,7 +2365,7 @@ mxml_write_string( // Write final fragment fraglen = (size_t)(ptr - frag); - if ((write_cb)(write_cbdata, frag, fraglen) != (ssize_t)fraglen) + if ((io_cb)(io_cbdata, (char *)frag, fraglen) != fraglen) return (-1); } @@ -2441,19 +2379,18 @@ mxml_write_string( static int // O - New column or `-1` on error mxml_write_ws( - mxml_write_cb_t write_cb, // I - Write callback function - void *write_cbdata, // I - Write callback data - mxml_node_t *node, // I - Current node - mxml_save_cb_t save_cb, // I - Whitespace callback function - void *save_cbdata, // I - Whitespace callback data - mxml_ws_t ws, // I - Whitespace value - int col) // I - Current column + 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 { const char *s; // Whitespace string - if (save_cb && (s = (*save_cb)(save_cbdata, node, ws)) != NULL) - col = mxml_write_string(write_cb, write_cbdata, s, /*use_entities*/false, col); + 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); return (col); } diff --git a/mxml-index.c b/mxml-index.c index f5ebe94..9847f65 100644 --- a/mxml-index.c +++ b/mxml-index.c @@ -227,16 +227,12 @@ mxmlIndexNew(mxml_node_t *node, // I - XML node tree // Create a new index... if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL) - { - _mxml_error("Unable to allocate memory for index."); return (NULL); - } if (attr) { if ((ind->attr = _mxml_strcopy(attr)) == NULL) { - _mxml_error("Unable to allocate memory for index attribute."); free(ind); return (NULL); } @@ -254,7 +250,6 @@ mxmlIndexNew(mxml_node_t *node, // I - XML node tree if ((temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *))) == NULL) { // Unable to allocate memory for the index, so abort... - _mxml_error("Unable to allocate memory for index nodes."); mxmlIndexDelete(ind); return (NULL); } diff --git a/mxml-node.c b/mxml-node.c index ea9153d..02d031f 100644 --- a/mxml-node.c +++ b/mxml-node.c @@ -210,7 +210,6 @@ mxmlNewCDATA(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT` { if ((node->value.cdata = _mxml_strcopy(data)) == NULL) { - _mxml_error("Unable to allocate memory for CDATA."); mxmlDelete(node); return (NULL); } @@ -286,7 +285,6 @@ mxmlNewComment(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT` { if ((node->value.comment = _mxml_strcopy(comment)) == NULL) { - _mxml_error("Unable to allocate memory for comment."); mxmlDelete(node); return (NULL); } @@ -339,27 +337,28 @@ mxmlNewCommentf(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT` // 'mxmlNewCustom()' - Create a new custom data node. // // The new custom node is added to the end of the specified parent's child -// list. The constant `MXML_NO_PARENT` can be used to specify that the new -// element node has no parent. `NULL` can be passed when the data in the -// node is not dynamically allocated or is separately managed. +// list. The `free_cb` argument specifies a function to call to free the custom +// data when the node is deleted. // mxml_node_t * // O - New node mxmlNewCustom( - mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT` - void *data, // I - Pointer to data - mxml_custom_destroy_cb_t destroy) // I - Function to destroy data + mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT` + void *data, // I - Pointer to data + mxml_custfree_cb_t free_cb, // I - Free callback function or `NULL` if none needed + void *free_cbdata) // I - Free callback data { mxml_node_t *node; // New node - MXML_DEBUG("mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent, data, destroy); + MXML_DEBUG("mxmlNewCustom(parent=%p, data=%p, free_cb=%p, free_cbdata=%p)\n", parent, data, free_cb, free_cbdata); // Create the node and set the value... if ((node = mxml_new(parent, MXML_TYPE_CUSTOM)) != NULL) { - node->value.custom.data = data; - node->value.custom.destroy = destroy; + node->value.custom.data = data; + node->value.custom.free_cb = free_cb; + node->value.custom.free_cbdata = free_cbdata; } return (node); @@ -394,7 +393,6 @@ mxmlNewDeclaration( { if ((node->value.declaration = _mxml_strcopy(declaration)) == NULL) { - _mxml_error("Unable to allocate memory for declaration."); mxmlDelete(node); return (NULL); } @@ -471,7 +469,6 @@ mxmlNewDirective(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT` { if ((node->value.directive = _mxml_strcopy(directive)) == NULL) { - _mxml_error("Unable to allocate memory for processing instruction."); mxmlDelete(node); return (NULL); } @@ -893,8 +890,8 @@ mxml_free(mxml_node_t *node) // I - Node _mxml_strfree(node->value.text.string); break; case MXML_TYPE_CUSTOM : - if (node->value.custom.data && node->value.custom.destroy) - (*(node->value.custom.destroy))(node->value.custom.data); + if (node->value.custom.data && node->value.custom.free_cb) + (node->value.custom.free_cb)(node->value.custom.free_cbdata, node->value.custom.data); break; default : break; diff --git a/mxml-options.c b/mxml-options.c new file mode 100644 index 0000000..e6b2ac5 --- /dev/null +++ b/mxml-options.c @@ -0,0 +1,519 @@ +// +// Options functions 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. +// + +#include "mxml-private.h" + + +// +// 'mxmlOptionsDelete()' - Free load/save options. +// + +void +mxmlOptionsDelete( + mxml_options_t *options) // I - Options +{ + free(options); +} + + +// +// 'mxmlOptionsNew()' - Allocate load/save options. +// + +mxml_options_t * // O - Options +mxmlOptionsNew(void) +{ + mxml_options_t *options; // Options + + + if ((options = (mxml_options_t *)calloc(1, sizeof(mxml_options_t))) != NULL) + { + // Set default values... + options->type_value = MXML_TYPE_TEXT; + options->wrap = 72; + + if ((options->loc = localeconv()) != NULL) + { + if (!options->loc->decimal_point || !strcmp(options->loc->decimal_point, ".")) + options->loc = NULL; + else + options->loc_declen = strlen(options->loc->decimal_point); + } + } + + return (options); +} + + +// +// 'mxmlOptionsSetCustomCallbacks()' - Set the custom data callbacks. +// +// This function sets the callbacks that are used for loading and saving custom +// data types. The load callback `load_cb` accepts the callback data pointer +// `cbdata`, a node pointer, and a data string and returns `true` on success and +// `false` on error, for example: +// +// ```c +// typedef struct +// { +// unsigned year, /* Year */ +// month, /* Month */ +// day, /* Day */ +// hour, /* Hour */ +// minute, /* Minute */ +// second; /* Second */ +// time_t unix; /* UNIX time */ +// } iso_date_time_t; +// +// void +// my_custom_free_cb(void *cbdata, void *data) +// { +// free(data); +// } +// +// bool +// my_custom_load_cb(void *cbdata, mxml_node_t *node, const char *data) +// { +// iso_date_time_t *dt; +// struct tm tmdata; +// +// /* Allocate custom data structure ... */ +// dt = calloc(1, sizeof(iso_date_time_t)); +// +// /* Parse the data string... */ +// if (sscanf(data, "%u-%u-%uT%u:%u:%uZ", &(dt->year), &(dt->month), +// &(dt->day), &(dt->hour), &(dt->minute), &(dt->second)) != 6) +// { +// /* Unable to parse date and time numbers... */ +// free(dt); +// return (false); +// } +// +// /* Range check values... */ +// if (dt->month < 1 || dt->month > 12 || dt->day < 1 || dt->day > 31 || +// dt->hour < 0 || dt->hour > 23 || dt->minute < 0 || dt->minute > 59 || +// dt->second < 0 || dt->second > 60) +// { +// /* Date information is out of range... */ +// free(dt); +// return (false); +// } +// +// /* Convert ISO time to UNIX time in seconds... */ +// tmdata.tm_year = dt->year - 1900; +// tmdata.tm_mon = dt->month - 1; +// tmdata.tm_day = dt->day; +// tmdata.tm_hour = dt->hour; +// tmdata.tm_min = dt->minute; +// tmdata.tm_sec = dt->second; +// +// dt->unix = gmtime(&tmdata); +// +// /* Set custom data and free function... */ +// mxmlSetCustom(node, data, my_custom_free, /*cbdata*/NULL); +// +// /* Return with no errors... */ +// return (true); +// } +// ``` +// +// The save callback `save_cb` accepts the callback data pointer `cbdata` and a +// node pointer and returns a malloc'd string on success and `NULL` on error, +// for example: +// +// ```c +// char * +// my_custom_save_cb(void *cbdata, mxml_node_t *node) +// { +// char data[255]; +// iso_date_time_t *dt; +// +// /* Get the custom data structure */ +// dt = (iso_date_time_t *)mxmlGetCustom(node); +// +// /* Generate string version of the date/time... */ +// snprintf(data, sizeof(data), "%04u-%02u-%02uT%02u:%02u:%02uZ", +// dt->year, dt->month, dt->day, dt->hour, dt->minute, dt->second); +// +// /* Duplicate the string and return... */ +// return (strdup(data)); +// } +// ``` +// + +void +mxmlOptionsSetCustomCallbacks( + mxml_options_t *options, // I - Options + mxml_custload_cb_t load_cb, // I - Custom load callback function + mxml_custsave_cb_t save_cb, // I - Custom save callback function + void *cbdata) // I - Custom callback data +{ + if (options) + { + options->custload_cb = load_cb; + options->custsave_cb = save_cb; + options->cust_cbdata = cbdata; + } +} + + +// +// 'mxmlOptionsSetEntityCallback()' - Set the entity lookup callback to use when loading XML data. +// +// This function sets the callback that is used to lookup named XML character +// entities when loading XML data. The callback function `cb` accepts the +// callback data pointer `cbdata` and the entity name. The function returns a +// Unicode character value or `-1` if the entity is not known. For example, the +// following entity callback supports the "euro" entity: +// +// ```c +// int my_entity_cb(void *cbdata, const char *name) +// { +// if (!strcmp(name, "euro")) +// return (0x20ac); +// else +// return (-1); +// } +// ``` +// +// Mini-XML supports the "amp", "gt", "lt", and "quot" character entities, which +// are required by the base XML specification. +// + +void +mxmlOptionsSetEntityCallback( + mxml_options_t *options, // I - Options + mxml_entity_cb_t cb, // I - Entity callback function + void *cbdata) // I - Entity callback data +{ + if (options) + { + options->entity_cb = cb; + options->entity_cbdata = cbdata; + } +} + + +// +// 'mxmlOptionsSetErrorCallback()' - Set the error message callback. +// +// This function sets a function to use when reporting errors. The callback +// `cb` accepts the data pointer `cbdata` and a string pointer containing the +// error message: +// +// ```c +// void my_error_cb(void *cbdata, const char *message) +// { +// fprintf(stderr, "myprogram: %s\n", message); +// } +// ``` +// +// The default error callback writes the error message to the `stderr` file. +// + +void +mxmlOptionsSetErrorCallback( + mxml_options_t *options, // I - Options + mxml_error_cb_t cb, // I - Error callback function + void *cbdata) // I - Error callback data +{ + if (options) + { + options->error_cb = cb; + options->error_cbdata = cbdata; + } +} + + +// +// 'mxmlOptionsSetSAXCallback()' - Set the SAX callback to use when reading XML data. +// +// This function sets a SAX callback to use when reading XML data. The SAX +// callback function `cb` and associated callback data `cbdata` are used to +// enable the Simple API for XML streaming mode. The callback is called as the +// XML node tree is parsed and receives the `cbdata` pointer, the `mxml_node_t` +// pointer, and an event code. The function returns `true` to continue +// processing or `false` to stop: +// +// ```c +// bool +// sax_cb(void *cbdata, mxml_node_t *node, +// mxml_sax_event_t event) +// { +// ... do something ... +// +// /* Continue processing... */ +// return (true); +// } +// ``` +// +// The event will be one of the following: +// +// - `MXML_SAX_EVENT_CDATA`: CDATA was just read. +// - `MXML_SAX_EVENT_COMMENT`: A comment was just read. +// - `MXML_SAX_EVENT_DATA`: Data (integer, opaque, real, or text) was just read. +// - `MXML_SAX_EVENT_DECLARATION`: A declaration was just read. +// - `MXML_SAX_EVENT_DIRECTIVE`: A processing directive/instruction was just read. +// - `MXML_SAX_EVENT_ELEMENT_CLOSE` - A close element was just read \(``) +// - `MXML_SAX_EVENT_ELEMENT_OPEN` - An open element was just read \(``) +// +// Elements are *released* after the close element is processed. All other nodes +// are released after they are processed. The SAX callback can *retain* the node +// using the [mxmlRetain](@@) function. +// + +void +mxmlOptionsSetSAXCallback( + mxml_options_t *options, // I - Options + mxml_sax_cb_t cb, // I - SAX callback function + void *cbdata) // I - SAX callback data +{ + if (options) + { + options->sax_cb = cb; + options->sax_cbdata = cbdata; + } +} + + +// +// 'mxmlOptionsSetTypeCallback()' - Set the type callback for child/value nodes. +// +// The load callback function `cb` is called to obtain the node type child/value +// nodes and receives the `cbdata` pointer and the `mxml_node_t` pointer, for +// example: +// +// ```c +// mxml_type_t +// my_type_cb(void *cbdata, mxml_node_t *node) +// { +// const char *type; +// +// /* +// * You can lookup attributes and/or use the element name, +// * hierarchy, etc... +// */ +// +// type = mxmlElementGetAttr(node, "type"); +// if (type == NULL) +// type = mxmlGetElement(node); +// if (type == NULL) +// type = "text"; +// +// if (!strcmp(type, "integer")) +// return (MXML_TYPE_INTEGER); +// else if (!strcmp(type, "opaque")) +// return (MXML_TYPE_OPAQUE); +// else if (!strcmp(type, "real")) +// return (MXML_TYPE_REAL); +// else +// return (MXML_TYPE_TEXT); +// } +// ``` +// + +void +mxmlOptionsSetTypeCallback( + mxml_options_t *options, // I - Options + mxml_type_cb_t cb, // I - Type callback function + void *cbdata) // I - Type callback data +{ + if (options) + { + options->type_cb = cb; + options->type_cbdata = cbdata; + } +} + + +// +// 'mxmlOptionsSetTypeValue()' - Set the type to use for all child/value nodes. +// +// This functions sets a constant node type to use for all child/value nodes. +// + +void +mxmlOptionsSetTypeValue( + mxml_options_t *options, // I - Options + mxml_type_t type) // I - Value node type +{ + if (options) + { + options->type_cb = NULL; + options->type_value = type; + } +} + + +// +// 'mxmlOptionsSetWhitespaceCallback()' - Set the whitespace callback. +// +// This function sets the whitespace callback that is used when saving XML data. +// The callback function `cb` specifies a function that returns a whitespace +// string or `NULL` before and after each element. The function receives the +// callback data pointer `cbdata`, the `mxml_node_t` pointer, and a "when" +// value indicating where the whitespace is being added, for example: +// +// ```c +// const char *my_whitespace_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); +// } +// ``` +// + +void +mxmlOptionsSetWhitespaceCallback( + mxml_options_t *options, // I - Options + mxml_ws_cb_t cb, // I - Whitespace callback function + void *cbdata) // I - Whitespace callback data +{ + if (options) + { + options->ws_cb = cb; + options->ws_cbdata = cbdata; + } +} + + +// +// 'mxmlOptionsSetWrapMargin()' - Set the wrap margin when saving XML data. +// +// This function sets the wrap margin used when saving XML data. Wrapping is +// disabled when `column` is `0`. +// + +void +mxmlOptionsSetWrapMargin( + mxml_options_t *options, // I - Options + int column) // I - Wrap column +{ + if (options) + options->wrap = column; +} + + +// +// '_mxml_entity_string()' - Get the entity that corresponds to the character, if any. +// + +const char * // O - Entity or `NULL` for none +_mxml_entity_string(int ch) // I - Character +{ + switch (ch) + { + case '&' : + return ("&"); + + case '<' : + return ("<"); + + case '>' : + return (">"); + + case '\"' : + return ("""); + + default : + return (NULL); + } +} + + +// +// '_mxml_entity_value()' - Get the character corresponding to a named entity. +// +// The entity name can also be a numeric constant. `-1` is returned if the +// name is not known. +// + +int // O - Unicode character +_mxml_entity_value( + mxml_options_t *options, // I - Options + const char *name) // I - Entity name +{ + int ch = -1; // Unicode character + + + if (!name) + { + // No name... + return (-1); + } + else if (*name == '#') + { + // Numeric entity... + if (name[1] == 'x') + ch = (int)strtol(name + 2, NULL, 16); + else + ch = (int)strtol(name + 1, NULL, 10); + } + else if (!strcmp(name, "amp")) + { + // Ampersand + ch = '&'; + } + else if (!strcmp(name, "gt")) + { + // Greater than + ch = '>'; + } + else if (!strcmp(name, "lt")) + { + // Less than + ch = '<'; + } + else if (!strcmp(name, "quot")) + { + // Double quote + ch = '\"'; + } + else if (options && options->entity_cb) + { + // Use callback + ch = (options->entity_cb)(options->entity_cbdata, name); + } + + return (ch); +} + + +// +// '_mxml_error()' - Display an error message. +// + +void +_mxml_error(mxml_options_t *options, // I - Load/save options + const char *format, // I - Printf-style format string + ...) // I - Additional arguments as needed +{ + va_list ap; // Pointer to arguments + char s[1024]; // Message string + + + // Range check input... + if (!format) + return; + + // Format the error message string... + va_start(ap, format); + vsnprintf(s, sizeof(s), format, ap); + va_end(ap); + + // And then display the error message... + if (options->error_cb) + (options->error_cb)(options->error_cbdata, s); + else + fprintf(stderr, "%s\n", s); +} diff --git a/mxml-private.c b/mxml-private.c index 6878dbc..2c26f10 100644 --- a/mxml-private.c +++ b/mxml-private.c @@ -39,144 +39,6 @@ #endif // __sun -// -// 'mxmlSetCustomHandlers()' - Set the custom data callbacks. -// -// This function sets the callbacks that are used for loading and saving custom -// data types. The load callback `load_cb` accepts the callback data pointer -// `cbdata`, a node pointer, and a data string and returns `true` on success and -// `false` on error, for example: -// -// ```c -// typedef struct -// { -// unsigned year, /* Year */ -// month, /* Month */ -// day, /* Day */ -// hour, /* Hour */ -// minute, /* Minute */ -// second; /* Second */ -// time_t unix; /* UNIX time */ -// } iso_date_time_t; -// -// bool -// my_custom_load_cb(void *cbdata, mxml_node_t *node, const char *data) -// { -// iso_date_time_t *dt; -// struct tm tmdata; -// -// /* Allocate custom data structure ... */ -// dt = calloc(1, sizeof(iso_date_time_t)); -// -// /* Parse the data string... */ -// if (sscanf(data, "%u-%u-%uT%u:%u:%uZ", &(dt->year), &(dt->month), -// &(dt->day), &(dt->hour), &(dt->minute), &(dt->second)) != 6) -// { -// /* Unable to parse date and time numbers... */ -// free(dt); -// return (false); -// } -// -// /* Range check values... */ -// if (dt->month < 1 || dt->month > 12 || dt->day < 1 || dt->day > 31 || -// dt->hour < 0 || dt->hour > 23 || dt->minute < 0 || dt->minute > 59 || -// dt->second < 0 || dt->second > 60) -// { -// /* Date information is out of range... */ -// free(dt); -// return (false); -// } -// -// /* Convert ISO time to UNIX time in seconds... */ -// tmdata.tm_year = dt->year - 1900; -// tmdata.tm_mon = dt->month - 1; -// tmdata.tm_day = dt->day; -// tmdata.tm_hour = dt->hour; -// tmdata.tm_min = dt->minute; -// tmdata.tm_sec = dt->second; -// -// dt->unix = gmtime(&tmdata); -// -// /* Set custom data and free function... */ -// mxmlSetCustom(node, data, free); -// -// /* Return with no errors... */ -// return (true); -// } -// ``` -// -// The save callback `save_cb` accepts the callback data pointer `cbdata` and a -// node pointer and returns a malloc'd string on success and `NULL` on error, -// for example: -// -// ```c -// char * -// my_custom_save_cb(void *cbdata, mxml_node_t *node) -// { -// char data[255]; -// iso_date_time_t *dt; -// -// /* Get the custom data structure */ -// dt = (iso_date_time_t *)mxmlGetCustom(node); -// -// /* Generate string version of the date/time... */ -// snprintf(data, sizeof(data), "%04u-%02u-%02uT%02u:%02u:%02uZ", -// dt->year, dt->month, dt->day, dt->hour, dt->minute, dt->second); -// -// /* Duplicate the string and return... */ -// return (strdup(data)); -// } -// ``` -// - - -void -mxmlSetCustomCallbacks( - mxml_custom_load_cb_t load_cb, // I - Load callback function - mxml_custom_save_cb_t save_cb, // I - Save callback function - void *cbdata) // I - Callback data -{ - _mxml_global_t *global = _mxml_global(); - // Global data - - - global->custom_load_cb = load_cb; - global->custom_save_cb = save_cb; - global->custom_cbdata = cbdata; -} - - -// -// 'mxmlSetErrorCallback()' - Set the error message callback. -// -// This function sets a function to use when reporting errors. The callback -// `cb` accepts the data pointer `cbdata` and a string pointer containing the -// error message: -// -// ```c -// void my_error_cb(void *cbdata, const char *message) -// { -// fprintf(stderr, "myprogram: %s\n", message); -// } -// ``` -// -// The default error callback writes the error message to the `stderr` file. -// - -void -mxmlSetErrorCallback( - mxml_error_cb_t cb, // I - Error callback function - void *cbdata) // I - Error callback data -{ - _mxml_global_t *global = _mxml_global(); - // Global data - - - global->error_cb = cb; - global->error_cbdata = cbdata; -} - - // // 'mxmlSetStringCallbacks()' - Set the string copy/free callback functions. // @@ -217,37 +79,6 @@ mxmlSetStringCallbacks( } -// -// '_mxml_error()' - Display an error message. -// - -void -_mxml_error(const char *format, // I - Printf-style format string - ...) // I - Additional arguments as needed -{ - va_list ap; // Pointer to arguments - char s[1024]; // Message string - _mxml_global_t *global = _mxml_global(); - // Global data - - - // Range check input... - if (!format) - return; - - // Format the error message string... - va_start(ap, format); - vsnprintf(s, sizeof(s), format, ap); - va_end(ap); - - // And then display the error message... - if (global->error_cb) - (*global->error_cb)(global->error_cbdata, s); - else - fprintf(stderr, "%s\n", s); -} - - // // '_mxml_strcopy()' - Copy a string. // @@ -341,10 +172,6 @@ _mxml_global(void) { global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); pthread_setspecific(_mxml_key, global); - - global->num_entity_cbs = 1; - global->entity_cbs[0] = _mxml_entity_cb; - global->wrap = 72; } return (global); @@ -425,10 +252,6 @@ _mxml_global(void) { global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); - global->num_entity_cbs = 1; - global->entity_cbs[0] = _mxml_entity_cb; - global->wrap = 72; - TlsSetValue(_mxml_tls_index, (LPVOID)global); } @@ -446,12 +269,9 @@ _mxml_global(void) { static _mxml_global_t global = // Global data { - NULL, // error_cb - 1, // num_entity_cbs - { _mxml_entity_cb }, // entity_cbs - 72, // wrap - NULL, // custom_load_cb - NULL // custom_save_cb + NULL, // strcopy_cb + NULL, // strfree_cb + NULL, // str_cbdata }; diff --git a/mxml-private.h b/mxml-private.h index a92ee54..75aa7d7 100644 --- a/mxml-private.h +++ b/mxml-private.h @@ -54,7 +54,8 @@ typedef struct _mxml_text_s // An XML text value. typedef struct _mxml_custom_s // An XML custom value. { void *data; // Pointer to (allocated) custom data - mxml_custom_destroy_cb_t destroy; // Pointer to destructor function + mxml_custfree_cb_t free_cb; // Free callback function + void *free_cbdata; // Free callback data } _mxml_custom_t; typedef union _mxml_value_u // An XML node value. @@ -84,7 +85,14 @@ struct _mxml_node_s // An XML node. void *user_data; // User data }; -struct _mxml_index_s // An XML node index. +typedef struct _mxml_global_s // Global, per-thread data +{ + mxml_strcopy_cb_t strcopy_cb; // String copy callback function + mxml_strfree_cb_t strfree_cb; // String free callback function + void *str_cbdata; // String callback data +} _mxml_global_t; + +struct _mxml_index_s // An XML node index. { char *attr; // Attribute used for indexing or NULL size_t num_nodes; // Number of nodes in index @@ -93,25 +101,26 @@ struct _mxml_index_s // An XML node index. mxml_node_t **nodes; // Node array }; -typedef struct _mxml_global_s // Global, per-thread data - +struct _mxml_options_s // XML options { - mxml_custom_load_cb_t custom_load_cb; // Custom load callback function - mxml_custom_save_cb_t custom_save_cb; // Custom save callback function - void *custom_cbdata; // Custom callback data - mxml_error_cb_t error_cb; // Error callback function - void *error_cbdata; // Error callback data - size_t num_entity_cbs; // Number of entity callbacks - mxml_entity_cb_t entity_cbs[100]; // Entity callback functions - void *entity_cbdatas[100]; // Entity callback data - struct lconv *loc; // Locale data - size_t loc_declen; // Length of decimal point string - bool loc_set; // Locale data set? - mxml_strcopy_cb_t strcopy_cb; // String copy callback function - mxml_strfree_cb_t strfree_cb; // String free callback function - void *str_cbdata; // String callback data - int wrap; // Wrap margin -} _mxml_global_t; + struct lconv *loc; // Locale data + size_t loc_declen; // Length of decimal point string + mxml_custload_cb_t custload_cb; // Custom load callback function + mxml_custsave_cb_t custsave_cb; // Custom save callback function + void *cust_cbdata; // Custom callback data + mxml_entity_cb_t entity_cb; // Entity callback function + void *entity_cbdata; // Entity callback data + mxml_error_cb_t error_cb; // Error callback function + void *error_cbdata; // Error callback data + mxml_sax_cb_t sax_cb; // SAX callback function + void *sax_cbdata; // SAX callback data + mxml_type_cb_t type_cb; // Type callback function + void *type_cbdata; // Type callback data + mxml_type_t type_value; // Fixed type value (if no type callback) + int wrap; // Wrap margin + mxml_ws_cb_t ws_cb; // Whitespace callback function + void *ws_cbdata; // Whitespace callback data +}; // @@ -119,9 +128,9 @@ typedef struct _mxml_global_s // Global, per-thread data // extern _mxml_global_t *_mxml_global(void); -extern int _mxml_entity_cb(void *cbdata, const char *name); extern const char *_mxml_entity_string(int ch); -extern void _mxml_error(const char *format, ...) MXML_FORMAT(1,2); +extern int _mxml_entity_value(mxml_options_t *options, const char *name); +extern void _mxml_error(mxml_options_t *options, const char *format, ...) MXML_FORMAT(2,3); extern char *_mxml_strcopy(const char *s); extern void _mxml_strfree(char *s); diff --git a/mxml-set.c b/mxml-set.c index 7fc33e0..95dd532 100644 --- a/mxml-set.c +++ b/mxml-set.c @@ -31,15 +31,9 @@ mxmlSetCDATA(mxml_node_t *node, // I - Node to set node = node->child; if (!node || node->type != MXML_TYPE_CDATA) - { - _mxml_error("Wrong node type."); return (false); - } else if (!data) - { - _mxml_error("NULL string not allowed."); return (false); - } if (data == node->value.cdata) { @@ -49,10 +43,7 @@ mxmlSetCDATA(mxml_node_t *node, // I - Node to set // Allocate the new value, free any old element value, and set the new value... if ((s = _mxml_strcopy(data)) == NULL) - { - _mxml_error("Unable to allocate memory for CDATA."); return (false); - } _mxml_strfree(node->value.cdata); node->value.cdata = s; @@ -83,15 +74,9 @@ mxmlSetCDATAf(mxml_node_t *node, // I - Node node = node->child; if (!node || node->type != MXML_TYPE_CDATA) - { - _mxml_error("Wrong node type."); return (false); - } else if (!format) - { - _mxml_error("NULL string not allowed."); return (false); - } // Format the new string, free any old string value, and set the new value... va_start(ap, format); @@ -99,10 +84,7 @@ mxmlSetCDATAf(mxml_node_t *node, // I - Node va_end(ap); if ((s = _mxml_strcopy(buffer)) == NULL) - { - _mxml_error("Unable to allocate memory for CDATA string."); return (false); - } _mxml_strfree(node->value.cdata); node->value.cdata = s; @@ -129,25 +111,16 @@ mxmlSetComment(mxml_node_t *node, // I - Node node = node->child; if (!node || node->type != MXML_TYPE_COMMENT) - { - _mxml_error("Wrong node type."); return (false); - } else if (!comment) - { - _mxml_error("NULL comment not allowed."); return (false); - } if (comment == node->value.comment) return (true); // Free any old string value and set the new value... if ((s = _mxml_strcopy(comment)) == NULL) - { - _mxml_error("Unable to allocate memory for comment string."); return (false); - } _mxml_strfree(node->value.comment); node->value.comment = s; @@ -177,15 +150,9 @@ mxmlSetCommentf(mxml_node_t *node, // I - Node node = node->child; if (!node || node->type != MXML_TYPE_COMMENT) - { - _mxml_error("Wrong node type."); return (false); - } else if (!format) - { - _mxml_error("NULL string not allowed."); return (false); - } // Format the new string, free any old string value, and set the new value... va_start(ap, format); @@ -193,10 +160,7 @@ mxmlSetCommentf(mxml_node_t *node, // I - Node va_end(ap); if ((s = _mxml_strcopy(buffer)) == NULL) - { - _mxml_error("Unable to allocate memory for comment string."); return (false); - } _mxml_strfree(node->value.comment); node->value.comment = s; @@ -215,32 +179,31 @@ mxmlSetCommentf(mxml_node_t *node, // I - Node bool // O - `true` on success, `false` on failure mxmlSetCustom( - mxml_node_t *node, // I - Node to set - void *data, // I - New data pointer - mxml_custom_destroy_cb_t destroy_cb)// I - New destructor function + mxml_node_t *node, // I - Node to set + void *data, // I - New data pointer + mxml_custfree_cb_t free_cb, // I - Free callback function + void *free_cbdata) // I - Free callback data { // Range check input... if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_CUSTOM) node = node->child; if (!node || node->type != MXML_TYPE_CUSTOM) - { - _mxml_error("Wrong node type."); return (false); - } if (data == node->value.custom.data) - { - node->value.custom.destroy = destroy_cb; - return (true); - } + goto set_free_callback; // Free any old element value and set the new value... - if (node->value.custom.data && node->value.custom.destroy) - (*(node->value.custom.destroy))(node->value.custom.data); + if (node->value.custom.data && node->value.custom.free_cb) + (node->value.custom.free_cb)(node->value.custom.free_cbdata, node->value.custom.data); - node->value.custom.data = data; - node->value.custom.destroy = destroy_cb; + node->value.custom.data = data; + + set_free_callback: + + node->value.custom.free_cb = free_cb; + node->value.custom.free_cbdata = free_cbdata; return (true); } @@ -265,25 +228,16 @@ mxmlSetDeclaration( node = node->child; if (!node || node->type != MXML_TYPE_DECLARATION) - { - _mxml_error("Wrong node type."); return (false); - } else if (!declaration) - { - _mxml_error("NULL declaration not allowed."); return (false); - } if (declaration == node->value.declaration) return (true); // Free any old string value and set the new value... if ((s = _mxml_strcopy(declaration)) == NULL) - { - _mxml_error("Unable to allocate memory for declaration string."); return (false); - } _mxml_strfree(node->value.declaration); node->value.declaration = s; @@ -313,15 +267,9 @@ mxmlSetDeclarationf(mxml_node_t *node, // I - Node node = node->child; if (!node || node->type != MXML_TYPE_COMMENT) - { - _mxml_error("Wrong node type."); return (false); - } else if (!format) - { - _mxml_error("NULL string not allowed."); return (false); - } // Format the new string, free any old string value, and set the new value... va_start(ap, format); @@ -329,10 +277,7 @@ mxmlSetDeclarationf(mxml_node_t *node, // I - Node va_end(ap); if ((s = _mxml_strcopy(buffer)) == NULL) - { - _mxml_error("Unable to allocate memory for declaration string."); return (false); - } _mxml_strfree(node->value.declaration); node->value.declaration = s; @@ -359,25 +304,16 @@ mxmlSetDirective(mxml_node_t *node, // I - Node node = node->child; if (!node || node->type != MXML_TYPE_DIRECTIVE) - { - _mxml_error("Wrong node type."); return (false); - } else if (!directive) - { - _mxml_error("NULL directive not allowed."); return (false); - } if (directive == node->value.directive) return (true); // Free any old string value and set the new value... if ((s = _mxml_strcopy(directive)) == NULL) - { - _mxml_error("Unable to allocate memory for directive string."); return (false); - } _mxml_strfree(node->value.directive); node->value.directive = s; @@ -408,15 +344,9 @@ mxmlSetDirectivef(mxml_node_t *node, // I - Node node = node->child; if (!node || node->type != MXML_TYPE_DIRECTIVE) - { - _mxml_error("Wrong node type."); return (false); - } else if (!format) - { - _mxml_error("NULL string not allowed."); return (false); - } // Format the new string, free any old string value, and set the new value... va_start(ap, format); @@ -424,10 +354,7 @@ mxmlSetDirectivef(mxml_node_t *node, // I - Node va_end(ap); if ((s = _mxml_strcopy(buffer)) == NULL) - { - _mxml_error("Unable to allocate memory for directive string."); return (false); - } _mxml_strfree(node->value.directive); node->value.directive = s; @@ -452,25 +379,16 @@ mxmlSetElement(mxml_node_t *node, // I - Node to set // Range check input... if (!node || node->type != MXML_TYPE_ELEMENT) - { - _mxml_error("Wrong node type."); return (false); - } else if (!name) - { - _mxml_error("NULL string not allowed."); return (false); - } if (name == node->value.element.name) return (true); // Free any old element value and set the new value... if ((s = _mxml_strcopy(name)) == NULL) - { - _mxml_error("Unable to allocate memory for element name."); return (false); - } _mxml_strfree(node->value.element.name); node->value.element.name = s; @@ -495,10 +413,7 @@ mxmlSetInteger(mxml_node_t *node, // I - Node to set node = node->child; if (!node || node->type != MXML_TYPE_INTEGER) - { - _mxml_error("Wrong node type."); return (false); - } // Set the new value and return... node->value.integer = integer; @@ -526,25 +441,16 @@ mxmlSetOpaque(mxml_node_t *node, // I - Node to set node = node->child; if (!node || node->type != MXML_TYPE_OPAQUE) - { - _mxml_error("Wrong node type."); return (false); - } else if (!opaque) - { - _mxml_error("NULL string not allowed."); return (false); - } if (node->value.opaque == opaque) return (true); // Free any old opaque value and set the new value... if ((s = _mxml_strcopy(opaque)) == NULL) - { - _mxml_error("Unable to allocate memory for opaque string."); return (false); - } _mxml_strfree(node->value.opaque); node->value.opaque = s; @@ -575,15 +481,9 @@ mxmlSetOpaquef(mxml_node_t *node, // I - Node to set node = node->child; if (!node || node->type != MXML_TYPE_OPAQUE) - { - _mxml_error("Wrong node type."); return (false); - } else if (!format) - { - _mxml_error("NULL string not allowed."); return (false); - } // Format the new string, free any old string value, and set the new value... va_start(ap, format); @@ -591,10 +491,7 @@ mxmlSetOpaquef(mxml_node_t *node, // I - Node to set va_end(ap); if ((s = _mxml_strcopy(buffer)) == NULL) - { - _mxml_error("Unable to allocate memory for opaque string."); return (false); - } _mxml_strfree(node->value.opaque); node->value.opaque = s; @@ -619,10 +516,7 @@ mxmlSetReal(mxml_node_t *node, // I - Node to set node = node->child; if (!node || node->type != MXML_TYPE_REAL) - { - _mxml_error("Wrong node type."); return (false); - } // Set the new value and return... node->value.real = real; @@ -651,15 +545,9 @@ mxmlSetText(mxml_node_t *node, // I - Node to set node = node->child; if (!node || node->type != MXML_TYPE_TEXT) - { - _mxml_error("Wrong node type."); return (false); - } else if (!string) - { - _mxml_error("NULL string not allowed."); return (false); - } if (string == node->value.text.string) { @@ -669,10 +557,7 @@ mxmlSetText(mxml_node_t *node, // I - Node to set // Free any old string value and set the new value... if ((s = _mxml_strcopy(string)) == NULL) - { - _mxml_error("Unable to allocate memory for text string."); return (false); - } _mxml_strfree(node->value.text.string); @@ -706,15 +591,9 @@ mxmlSetTextf(mxml_node_t *node, // I - Node to set node = node->child; if (!node || node->type != MXML_TYPE_TEXT) - { - _mxml_error("Wrong node type."); return (false); - } else if (!format) - { - _mxml_error("NULL string not allowed."); return (false); - } // Free any old string value and set the new value... va_start(ap, format); @@ -722,10 +601,7 @@ mxmlSetTextf(mxml_node_t *node, // I - Node to set va_end(ap); if ((s = _mxml_strcopy(buffer)) == NULL) - { - _mxml_error("Unable to allocate memory for text string."); return (false); - } _mxml_strfree(node->value.text.string); diff --git a/mxml.h b/mxml.h index 65d2687..a10a5c6 100644 --- a/mxml.h +++ b/mxml.h @@ -19,12 +19,6 @@ # include # include # include -# if defined(_WIN32) && !defined(__CUPS_SSIZE_T_DEFINED) -# define __CUPS_SSIZE_T_DEFINED -// Windows does not provide the ssize_t type, so map it to int64_t... */ -typedef int64_t ssize_t; // @private@ -# define SSIZE_MAX INT64_MAX -# endif // _WIN32 && !__CUPS_SSIZE_T_DEFINED # ifdef __cplusplus extern "C" { # endif // __cplusplus @@ -95,45 +89,46 @@ typedef enum mxml_ws_e // Whitespace periods MXML_WS_AFTER_CLOSE, // Callback for after close tag } mxml_ws_t; -typedef void (*mxml_custom_destroy_cb_t)(void *); - // Custom data destructor - typedef void (*mxml_error_cb_t)(void *cbdata, const char *message); // Error callback function -typedef struct _mxml_node_s mxml_node_t;// An XML node. +typedef struct _mxml_node_s mxml_node_t;// An XML node typedef struct _mxml_index_s mxml_index_t; - // An XML node index. + // An XML node index + +typedef struct _mxml_options_s mxml_options_t; + // XML options -typedef bool (*mxml_custom_load_cb_t)(void *cbdata, mxml_node_t *node, const char *s); +typedef void (*mxml_custfree_cb_t)(void *cbdata, void *custdata); + // Custom data destructor + +typedef bool (*mxml_custload_cb_t)(void *cbdata, mxml_node_t *node, const char *s); // Custom data load callback function -typedef char *(*mxml_custom_save_cb_t)(void *cbdata, mxml_node_t *node); +typedef char *(*mxml_custsave_cb_t)(void *cbdata, mxml_node_t *node); // Custom data save callback function typedef int (*mxml_entity_cb_t)(void *cbdata, const char *name); // Entity callback function -typedef mxml_type_t (*mxml_load_cb_t)(void *cbdata, mxml_node_t *node); - // Load callback function - -typedef ssize_t (*mxml_read_cb_t)(void *cbdata, void *buffer, size_t bytes); - // Read callback function +typedef size_t (*mxml_io_cb_t)(void *cbdata, void *buffer, size_t bytes); + // Read/write callback function -typedef const char *(*mxml_save_cb_t)(void *cbdata, mxml_node_t *node, mxml_ws_t when); - // Save callback function +typedef bool (*mxml_sax_cb_t)(void *cbdata, mxml_node_t *node, mxml_sax_event_t event); + // SAX callback function typedef char *(*mxml_strcopy_cb_t)(void *cbdata, const char *s); // String copy/allocation callback typedef void (*mxml_strfree_cb_t)(void *cbdata, char *s); // String free callback -typedef ssize_t (*mxml_write_cb_t)(void *cbdata, const void *buffer, size_t bytes); - // Write callback function +typedef mxml_type_t (*mxml_type_cb_t)(void *cbdata, mxml_node_t *node); + // Type callback function + +typedef const char *(*mxml_ws_cb_t)(void *cbdata, mxml_node_t *node, mxml_ws_t when); + // Whitespace callback function -typedef bool (*mxml_sax_cb_t)(void *cbdata, mxml_node_t *node, mxml_sax_event_t event); - // SAX callback function // @@ -150,9 +145,6 @@ extern const char *mxmlElementGetAttrByIndex(mxml_node_t *node, int idx, c extern size_t mxmlElementGetAttrCount(mxml_node_t *node); extern void mxmlElementSetAttr(mxml_node_t *node, const char *name, const char *value); extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name, const char *format, ...) MXML_FORMAT(3,4); -extern bool mxmlEntityAddCallback(mxml_entity_cb_t cb, void *cbdata); -extern int mxmlEntityGetValue(const char *name); -extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb); extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top, const char *element, const char *attr, const char *value, mxml_descend_t descend); extern mxml_node_t *mxmlFindPath(mxml_node_t *node, const char *path); @@ -183,17 +175,28 @@ extern size_t mxmlIndexGetCount(mxml_index_t *ind); extern mxml_index_t *mxmlIndexNew(mxml_node_t *node, const char *element, const char *attr); extern mxml_node_t *mxmlIndexReset(mxml_index_t *ind); -extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, int fd, mxml_load_cb_t load_cb, void *load_cbdata, mxml_sax_cb_t sax_cb, void *sax_cbdata); -extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp, mxml_load_cb_t load_cb, void *load_cbdata, mxml_sax_cb_t sax_cb, void *sax_cbdata); -extern mxml_node_t *mxmlLoadFilename(mxml_node_t *top, const char *filename, mxml_load_cb_t load_cb, void *load_cbdata, mxml_sax_cb_t sax_cb, void *sax_cbdata); -extern mxml_node_t *mxmlLoadIO(mxml_node_t *top, mxml_read_cb_t read_cb, void *read_cbdata, mxml_load_cb_t load_cb, void *load_cbdata, mxml_sax_cb_t sax_cb, void *sax_cbdata); -extern mxml_node_t *mxmlLoadString(mxml_node_t *top, const char *s, mxml_load_cb_t load_cb, void *load_cbdata, mxml_sax_cb_t sax_cb, void *sax_cbdata); +extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, mxml_options_t *options, int fd); +extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, mxml_options_t *options, FILE *fp); +extern mxml_node_t *mxmlLoadFilename(mxml_node_t *top, mxml_options_t *options, const char *filename); +extern mxml_node_t *mxmlLoadIO(mxml_node_t *top, mxml_options_t *options, mxml_io_cb_t io_cb, void *io_cbdata); +extern mxml_node_t *mxmlLoadString(mxml_node_t *top, mxml_options_t *options, const char *s); + +extern void mxmlOptionsDelete(mxml_options_t *options); +extern mxml_options_t *mxmlOptionsNew(void); +extern void mxmlOptionsSetCustomCallbacks(mxml_options_t *options, mxml_custload_cb_t load_cb, mxml_custsave_cb_t save_cb, void *cbdata); +extern void mxmlOptionsSetEntityCallback(mxml_options_t *options, mxml_entity_cb_t cb, void *cbdata); +extern void mxmlOptionsSetErrorCallback(mxml_options_t *options, mxml_error_cb_t cb, void *cbdata); +extern void mxmlOptionsSetSAXCallback(mxml_options_t *options, mxml_sax_cb_t cb, void *cbdata); +extern void mxmlOptionsSetTypeCallback(mxml_options_t *options, mxml_type_cb_t cb, void *cbdata); +extern void mxmlOptionsSetTypeValue(mxml_options_t *options, mxml_type_t type); +extern void mxmlOptionsSetWhitespaceCallback(mxml_options_t *options, mxml_ws_cb_t cb, void *cbdata); +extern void mxmlOptionsSetWrapMargin(mxml_options_t *options, int column); extern mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string); extern mxml_node_t *mxmlNewCDATAf(mxml_node_t *parent, const char *format, ...) MXML_FORMAT(2,3); extern mxml_node_t *mxmlNewComment(mxml_node_t *parent, const char *comment); extern mxml_node_t *mxmlNewCommentf(mxml_node_t *parent, const char *format, ...) MXML_FORMAT(2,3); -extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data, mxml_custom_destroy_cb_t destroy); +extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data, mxml_custfree_cb_t free_cb, void *free_cbdata); extern mxml_node_t *mxmlNewDeclaration(mxml_node_t *parent, const char *declaration); extern mxml_node_t *mxmlNewDeclarationf(mxml_node_t *parent, const char *format, ...) MXML_FORMAT(2,3); extern mxml_node_t *mxmlNewDirective(mxml_node_t *parent, const char *directive); @@ -211,12 +214,13 @@ extern int mxmlRelease(mxml_node_t *node); extern void mxmlRemove(mxml_node_t *node); extern int mxmlRetain(mxml_node_t *node); -extern char *mxmlSaveAllocString(mxml_node_t *node, mxml_save_cb_t save_cb, void *save_cbdata); -extern bool mxmlSaveFd(mxml_node_t *node, int fd, mxml_save_cb_t save_cb, void *save_cbdata); -extern bool mxmlSaveFile(mxml_node_t *node, FILE *fp, mxml_save_cb_t save_cb, void *save_cbdata); -extern bool mxmlSaveFilename(mxml_node_t *node, const char *filename, mxml_save_cb_t save_cb, void *save_cbdata); -extern bool mxmlSaveIO(mxml_node_t *node, mxml_write_cb_t write_cb, void *write_cbdata, mxml_save_cb_t save_cb, void *save_cbdata); -extern size_t mxmlSaveString(mxml_node_t *node, char *buffer, size_t bufsize, mxml_save_cb_t save_cb, void *save_cbdata); +extern char *mxmlSaveAllocString(mxml_node_t *node, mxml_options_t *options); +extern bool mxmlSaveFd(mxml_node_t *node, mxml_options_t *options, int fd); +extern bool mxmlSaveFile(mxml_node_t *node, mxml_options_t *options, FILE *fp); +extern bool mxmlSaveFilename(mxml_node_t *node, mxml_options_t *options, const char *filename); +extern bool mxmlSaveIO(mxml_node_t *node, mxml_options_t *options, mxml_io_cb_t io_cb, void *io_cbdata); +extern size_t mxmlSaveString(mxml_node_t *node, mxml_options_t *options, char *buffer, size_t bufsize); + extern bool mxmlSetCDATA(mxml_node_t *node, const char *data); extern bool mxmlSetCDATAf(mxml_node_t *node, const char *format, ...) MXML_FORMAT(2,3); extern bool mxmlSetComment(mxml_node_t *node, const char *comment); @@ -225,10 +229,8 @@ extern bool mxmlSetDeclaration(mxml_node_t *node, const char *declaration); extern bool mxmlSetDeclarationf(mxml_node_t *node, const char *format, ...) MXML_FORMAT(2,3); extern bool mxmlSetDirective(mxml_node_t *node, const char *directive); extern bool mxmlSetDirectivef(mxml_node_t *node, const char *format, ...) MXML_FORMAT(2,3); -extern bool mxmlSetCustom(mxml_node_t *node, void *data, mxml_custom_destroy_cb_t destroy_cb); -extern void mxmlSetCustomCallbacks(mxml_custom_load_cb_t load_cb, mxml_custom_save_cb_t save_cb, void *cbdata); +extern bool mxmlSetCustom(mxml_node_t *node, void *data, mxml_custfree_cb_t free_cb, void *free_cbdata); extern bool mxmlSetElement(mxml_node_t *node, const char *name); -extern void mxmlSetErrorCallback(mxml_error_cb_t cb, void *cbdata); extern bool mxmlSetInteger(mxml_node_t *node, long integer); extern bool mxmlSetOpaque(mxml_node_t *node, const char *opaque); extern bool mxmlSetOpaquef(mxml_node_t *node, const char *format, ...) MXML_FORMAT(2,3); @@ -237,7 +239,6 @@ extern void mxmlSetStringCallbacks(mxml_strcopy_cb_t strcopy_cb, mxml_strfree_c extern bool mxmlSetText(mxml_node_t *node, bool whitespace, const char *string); extern bool mxmlSetTextf(mxml_node_t *node, bool whitespace, const char *format, ...) MXML_FORMAT(3,4); extern bool mxmlSetUserData(mxml_node_t *node, void *data); -extern void mxmlSetWrapMargin(int column); extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top, mxml_descend_t descend); extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top, mxml_descend_t descend); diff --git a/testmxml.c b/testmxml.c index d9b4d82..c54f484 100644 --- a/testmxml.c +++ b/testmxml.c @@ -52,6 +52,7 @@ main(int argc, // I - Number of command-line args int i; // Looping var FILE *fp; // File to read int fd; // File descriptor + mxml_options_t *options; // Load/save options mxml_node_t *xml, // node *tree, // Element tree *node; // Node which should be in test.xml @@ -83,8 +84,9 @@ main(int argc, // I - Number of command-line args } // Test the basic functionality... - xml = mxmlNewXML("1.0"); - tree = mxmlNewElement(xml, "element"); + options = mxmlOptionsNew(); + xml = mxmlNewXML("1.0"); + tree = mxmlNewElement(xml, "element"); if (!tree) { @@ -111,18 +113,18 @@ main(int argc, // I - Number of command-line args mxmlNewReal(tree, 123.4); mxmlNewText(tree, 1, "text"); - type = MXML_TYPE_TEXT; - mxmlLoadString(tree, "string string string", /*load_cb*/NULL, /*load_cbdata*/&type, /*sax_cb*/NULL, /*sax_cbdata*/NULL); + mxmlOptionsSetTypeValue(options, MXML_TYPE_TEXT); + mxmlLoadString(tree, options, "string string string"); - type = MXML_TYPE_INTEGER; - mxmlLoadString(tree, "1 2 3", /*load_cb*/NULL, /*load_cbdata*/&type, /*sax_cb*/NULL, /*sax_cbdata*/NULL); + mxmlOptionsSetTypeValue(options, MXML_TYPE_INTEGER); + mxmlLoadString(tree, options, "1 2 3"); - type = MXML_TYPE_REAL; - mxmlLoadString(tree, "1.0 2.0 3.0", /*load_cb*/NULL, /*load_cbdata*/&type, /*sax_cb*/NULL, /*sax_cbdata*/NULL); + mxmlOptionsSetTypeValue(options, MXML_TYPE_REAL); + mxmlLoadString(tree, options, "1.0 2.0 3.0"); - type = MXML_TYPE_OPAQUE; - mxmlLoadString(tree, "opaque opaque opaque", /*load_cb*/NULL, /*load_cbdata*/&type, /*sax_cb*/NULL, /*sax_cbdata*/NULL); - mxmlLoadString(tree, "valuevalue2", /*load_cb*/NULL, /*load_cbdata*/&type, /*sax_cb*/NULL, /*sax_cbdata*/NULL); + mxmlOptionsSetTypeValue(options, MXML_TYPE_OPAQUE); + mxmlLoadString(tree, options, "opaque opaque opaque"); + mxmlLoadString(tree, options, "valuevalue2"); mxmlNewCDATA(tree, "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n"); mxmlNewCDATA(tree, @@ -434,11 +436,13 @@ main(int argc, // I - Number of command-line args mxmlDelete(xml); - // Open the file/string using the default (MXML_NO_CALLBACK) callback... + // Open the file/string using the default callback... + mxmlOptionsSetTypeValue(options, MXML_TYPE_TEXT); + if (argv[1][0] == '<') - xml = mxmlLoadString(/*top*/NULL, argv[1], /*load_cb*/NULL, /*load_cbdata*/NULL, /*sax_cb*/NULL, /*sax_cbdata*/NULL); + xml = mxmlLoadString(/*top*/NULL, options, argv[1]); else - xml = mxmlLoadFilename(/*top*/NULL, argv[1], /*load_cb*/NULL, /*load_cbdata*/NULL, /*sax_cb*/NULL, /*sax_cbdata*/NULL); + xml = mxmlLoadFilename(/*top*/NULL, options, argv[1]); if (!xml) { @@ -459,7 +463,7 @@ main(int argc, // I - Number of command-line args if (mxmlGetType(node) != MXML_TYPE_TEXT) { fputs("No child node of group/option/keyword.\n", stderr); - mxmlSaveFile(xml, stderr, /*save_cb*/NULL, /*save_cbdata*/NULL); + mxmlSaveFile(xml, options, stderr); mxmlDelete(xml); return (1); } @@ -475,10 +479,12 @@ main(int argc, // I - Number of command-line args mxmlDelete(xml); // Open the file... + mxmlOptionsSetTypeCallback(options, type_cb, /*cbdata*/NULL); + if (argv[1][0] == '<') - xml = mxmlLoadString(/*top*/NULL, argv[1], /*load_cb*/type_cb, /*load_cbdata*/NULL, /*sax_cb*/NULL, /*sax_cbdata*/NULL); + xml = mxmlLoadString(/*top*/NULL, options, argv[1]); else - xml = mxmlLoadFilename(/*top*/NULL, argv[1], /*load_cb*/type_cb, /*load_cbdata*/NULL, /*sax_cb*/NULL, /*sax_cbdata*/NULL); + xml = mxmlLoadFilename(/*top*/NULL, options, argv[1]); if (!xml) { @@ -505,10 +511,11 @@ main(int argc, // I - Number of command-line args } // Print the XML tree... - mxmlSaveFile(xml, stdout, whitespace_cb, /*save_cbdata*/NULL); + mxmlOptionsSetWhitespaceCallback(options, whitespace_cb, /*cbdata*/NULL); + mxmlSaveFile(xml, options, stdout); // Save the XML tree to a string and print it... - if (mxmlSaveString(xml, buffer, sizeof(buffer), whitespace_cb, /*save_cbdata*/NULL) > 0) + if (mxmlSaveString(xml, options, buffer, sizeof(buffer)) > 0) { if (argc == 3) { @@ -532,7 +539,7 @@ main(int argc, // I - Number of command-line args } // Read the file... - xml = mxmlLoadFd(/*top*/NULL, fd, type_cb, /*load_cbdata*/NULL, /*sax_cb*/NULL, /*sax_cbdata*/NULL); + xml = mxmlLoadFd(/*top*/NULL, options, fd); close(fd); @@ -547,7 +554,7 @@ main(int argc, // I - Number of command-line args } // Write the file... - mxmlSaveFd(xml, fd, whitespace_cb, /*save_cbdata*/NULL); + mxmlSaveFd(xml, options, fd); close(fd); @@ -558,10 +565,12 @@ main(int argc, // I - Number of command-line args // Test SAX methods... memset(event_counts, 0, sizeof(event_counts)); + mxmlOptionsSetSAXCallback(options, sax_cb, /*cbdata*/NULL); + if (argv[1][0] == '<') - mxmlRelease(mxmlLoadString(/*top*/NULL, argv[1], type_cb, /*load_cbdata*/NULL, sax_cb, /*sax_cbdata*/NULL)); + mxmlRelease(mxmlLoadString(/*top*/NULL, options, argv[1])); else - mxmlRelease(mxmlLoadFilename(/*top*/NULL, argv[1], type_cb, /*load_cbdata*/NULL, sax_cb, /*sax_cbdata*/NULL)); + mxmlRelease(mxmlLoadFilename(/*top*/NULL, options, argv[1])); if (!strcmp(argv[1], "test.xml")) { diff --git a/vcnet/mxml4.def b/vcnet/mxml4.def index 807a87b..8435dcf 100644 --- a/vcnet/mxml4.def +++ b/vcnet/mxml4.def @@ -8,9 +8,6 @@ EXPORTS mxmlElementGetAttr mxmlElementSetAttr mxmlElementSetAttrf - mxmlEntityAddCallback - mxmlEntityGetValue - mxmlEntityRemoveCallback mxmlFindElement mxmlFindPath mxmlGetCDATA @@ -59,6 +56,16 @@ EXPORTS mxmlNewText mxmlNewTextf mxmlNewXML + mxmlOptionsDelete + mxmlOptionsNew + mxmlOptionsSetCustomCallbacks + mxmlOptionsSetEntityCallback + mxmlOptionsSetErrorCallback + mxmlOptionsSetSAXCallback + mxmlOptionsSetTypeCallback + mxmlOptionsSetTypeValue + mxmlOptionsSetWhitespaceCallback + mxmlOptionsSetWrapMargin mxmlRelease mxmlRemove mxmlRetain @@ -73,20 +80,18 @@ EXPORTS mxmlSetComment mxmlSetCommentf mxmlSetCustom - mxmlSetCustomCallbacks mxmlSetDeclaration mxmlSetDeclarationf mxmlSetDirective mxmlSetDirectivef mxmlSetElement - mxmlSetErrorCallback mxmlSetInteger mxmlSetOpaque mxmlSetOpaquef mxmlSetReal + mxmlSetStringCallbacks mxmlSetText mxmlSetTextf mxmlSetUserData - mxmlSetWrapMargin mxmlWalkNext mxmlWalkPrev diff --git a/vcnet/mxml4.vcxproj b/vcnet/mxml4.vcxproj index 336f0d7..ecf8ade 100644 --- a/vcnet/mxml4.vcxproj +++ b/vcnet/mxml4.vcxproj @@ -191,11 +191,11 @@ - + diff --git a/vcnet/mxmlstat.vcxproj b/vcnet/mxmlstat.vcxproj index dacc032..25b2037 100644 --- a/vcnet/mxmlstat.vcxproj +++ b/vcnet/mxmlstat.vcxproj @@ -24,12 +24,12 @@ - + diff --git a/xcode/mxml.xcodeproj/project.pbxproj b/xcode/mxml.xcodeproj/project.pbxproj index 872ee8b..9845e5d 100644 --- a/xcode/mxml.xcodeproj/project.pbxproj +++ b/xcode/mxml.xcodeproj/project.pbxproj @@ -23,7 +23,6 @@ /* Begin PBXBuildFile section */ 272C00191E8C66C8007EBCAC /* mxml-attr.c in Sources */ = {isa = PBXBuildFile; fileRef = 272C000D1E8C66C8007EBCAC /* mxml-attr.c */; }; - 272C001A1E8C66C8007EBCAC /* mxml-entity.c in Sources */ = {isa = PBXBuildFile; fileRef = 272C000E1E8C66C8007EBCAC /* mxml-entity.c */; }; 272C001B1E8C66C8007EBCAC /* mxml-file.c in Sources */ = {isa = PBXBuildFile; fileRef = 272C000F1E8C66C8007EBCAC /* mxml-file.c */; }; 272C001C1E8C66C8007EBCAC /* mxml-get.c in Sources */ = {isa = PBXBuildFile; fileRef = 272C00101E8C66C8007EBCAC /* mxml-get.c */; }; 272C001D1E8C66C8007EBCAC /* mxml-index.c in Sources */ = {isa = PBXBuildFile; fileRef = 272C00111E8C66C8007EBCAC /* mxml-index.c */; }; @@ -36,6 +35,7 @@ 272C00261E8C66CF007EBCAC /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 272C00251E8C66CF007EBCAC /* config.h */; }; 272C00421E8C6B30007EBCAC /* testmxml.c in Sources */ = {isa = PBXBuildFile; fileRef = 272C00401E8C6B1B007EBCAC /* testmxml.c */; }; 272C00501E8C6B89007EBCAC /* libmxml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 272C00051E8C6664007EBCAC /* libmxml.a */; }; + 27459CD92BA8BAC300EAF97D /* mxml-options.c in Sources */ = {isa = PBXBuildFile; fileRef = 27459CD82BA8BAC300EAF97D /* mxml-options.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -77,7 +77,6 @@ /* Begin PBXFileReference section */ 272C00051E8C6664007EBCAC /* libmxml.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libmxml.a; sourceTree = BUILT_PRODUCTS_DIR; }; 272C000D1E8C66C8007EBCAC /* mxml-attr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mxml-attr.c"; path = "../mxml-attr.c"; sourceTree = ""; }; - 272C000E1E8C66C8007EBCAC /* mxml-entity.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mxml-entity.c"; path = "../mxml-entity.c"; sourceTree = ""; }; 272C000F1E8C66C8007EBCAC /* mxml-file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mxml-file.c"; path = "../mxml-file.c"; sourceTree = ""; }; 272C00101E8C66C8007EBCAC /* mxml-get.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mxml-get.c"; path = "../mxml-get.c"; sourceTree = ""; }; 272C00111E8C66C8007EBCAC /* mxml-index.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mxml-index.c"; path = "../mxml-index.c"; sourceTree = ""; }; @@ -92,6 +91,7 @@ 272C00401E8C6B1B007EBCAC /* testmxml.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testmxml.c; path = ../testmxml.c; sourceTree = ""; }; 272C00551E8EF972007EBCAC /* libarchive.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libarchive.tbd; path = usr/lib/libarchive.tbd; sourceTree = SDKROOT; }; 272C005A1E943423007EBCAC /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 27459CD82BA8BAC300EAF97D /* mxml-options.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "mxml-options.c"; path = "../mxml-options.c"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -128,11 +128,11 @@ 272C00181E8C66C8007EBCAC /* mxml.h */, 272C00141E8C66C8007EBCAC /* mxml-private.h */, 272C000D1E8C66C8007EBCAC /* mxml-attr.c */, - 272C000E1E8C66C8007EBCAC /* mxml-entity.c */, 272C000F1E8C66C8007EBCAC /* mxml-file.c */, 272C00101E8C66C8007EBCAC /* mxml-get.c */, 272C00111E8C66C8007EBCAC /* mxml-index.c */, 272C00121E8C66C8007EBCAC /* mxml-node.c */, + 27459CD82BA8BAC300EAF97D /* mxml-options.c */, 272C00131E8C66C8007EBCAC /* mxml-private.c */, 272C00151E8C66C8007EBCAC /* mxml-search.c */, 272C00161E8C66C8007EBCAC /* mxml-set.c */, @@ -226,7 +226,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1520; + LastUpgradeCheck = 1530; ORGANIZATIONNAME = "Michael R Sweet"; TargetAttributes = { 272C00041E8C6664007EBCAC = { @@ -271,7 +271,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 272C001A1E8C66C8007EBCAC /* mxml-entity.c in Sources */, + 27459CD92BA8BAC300EAF97D /* mxml-options.c in Sources */, 272C001E1E8C66C8007EBCAC /* mxml-node.c in Sources */, 272C001B1E8C66C8007EBCAC /* mxml-file.c in Sources */, 272C001C1E8C66C8007EBCAC /* mxml-get.c in Sources */,