Add separate node types for comments, CDATA, directives, and declarations (Issue #250)

This commit is contained in:
Michael R Sweet 2024-03-02 18:47:57 -05:00
parent 933dd8716d
commit 0cd995c772
No known key found for this signature in database
GPG Key ID: BE67C75EC81F3244
13 changed files with 1039 additions and 227 deletions

View File

@ -1,7 +1,10 @@
# Changes in Mini-XML 4.0.0
- Now require C99 support.
- Added new `MXML_TYPE_CDATA`, `MXML_TYPE_COMMENT`, `MXML_TYPE_DECLARATION`, and
`MXML_TYPE_DIRECTIVE` node types (Issue #250)
- Renamed `mxml_type_t` enumerations to `MXML_TYPE_xxx` (Issue #251)
- Updated APIs to use bool type instead of an int representing a boolean value.
# Changes in Mini-XML 3.3.2

View File

@ -281,18 +281,21 @@ test: testmxml
$(RM) temp2.xml temp2s.xml; \
else \
echo Stdio file test failed!; \
exit 1; \
fi
@if cmp temp1.xml temp1s.xml; then \
echo String test passed!; \
$(RM) temp1.xml temp1s.xml; \
else \
echo String test failed!; \
exit 1; \
fi
@if cmp test.xml test.xmlfd; then \
echo File descriptor test passed!; \
$(RM) test.xmlfd temp1.xmlfd; \
else \
echo File descriptor test failed!; \
exit 1; \
fi

View File

@ -16,7 +16,7 @@
// Local functions...
//
static int mxml_set_attr(mxml_node_t *node, const char *name, char *value);
static bool mxml_set_attr(mxml_node_t *node, const char *name, char *value);
//
@ -27,7 +27,7 @@ void
mxmlElementDeleteAttr(mxml_node_t *node,// I - Element
const char *name)// I - Attribute name
{
int i; // Looping var
size_t i; // Looping var
_mxml_attr_t *attr; // Cirrent attribute
@ -73,7 +73,7 @@ const char * // O - Attribute value or @code NULL@
mxmlElementGetAttr(mxml_node_t *node, // I - Element node
const char *name) // I - Name of attribute
{
int i; // Looping var
size_t i; // Looping var
_mxml_attr_t *attr; // Cirrent attribute
@ -129,9 +129,9 @@ mxmlElementGetAttrByIndex(
// 'mxmlElementGetAttrCount()' - Get the number of element attributes.
//
int // O - Number of attributes
size_t // O - Number of attributes
mxmlElementGetAttrCount(
mxml_node_t *node) // I - Node
mxml_node_t *node) // I - Node
{
if (node && node->type == MXML_TYPE_ELEMENT)
return (node->value.element.num_attrs);
@ -176,7 +176,7 @@ mxmlElementSetAttr(mxml_node_t *node, // I - Element node
valuec = NULL;
}
if (mxml_set_attr(node, name, valuec))
if (!mxml_set_attr(node, name, valuec))
free(valuec);
}
@ -214,7 +214,7 @@ mxmlElementSetAttrf(mxml_node_t *node, // I - Element node
if ((value = strdup(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))
else if (!mxml_set_attr(node, name, value))
free(value);
}
@ -223,7 +223,7 @@ mxmlElementSetAttrf(mxml_node_t *node, // I - Element node
// 'mxml_set_attr()' - Set or add an attribute name/value pair.
//
static int // O - 0 on success, -1 on failure
static bool // O - `true` on success, `false` on failure
mxml_set_attr(mxml_node_t *node, // I - Element node
const char *name, // I - Attribute name
char *value) // I - Attribute value
@ -241,7 +241,7 @@ mxml_set_attr(mxml_node_t *node, // I - Element node
free(attr->value);
attr->value = value;
return (0);
return (true);
}
}
@ -251,7 +251,7 @@ mxml_set_attr(mxml_node_t *node, // I - Element node
if (!attr)
{
mxml_error("Unable to allocate memory for attribute '%s' in element %s.", name, node->value.element.name);
return (-1);
return (false);
}
node->value.element.attrs = attr;
@ -260,12 +260,12 @@ mxml_set_attr(mxml_node_t *node, // I - Element node
if ((attr->name = strdup(name)) == NULL)
{
mxml_error("Unable to allocate memory for attribute '%s' in element %s.", name, node->value.element.name);
return (-1);
return (false);
}
attr->value = value;
node->value.element.num_attrs ++;
return (0);
return (true);
}

View File

@ -16,7 +16,7 @@
// 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlEntityAddCallback(
mxml_entity_cb_t cb) // I - Callback function to add
{
@ -24,18 +24,18 @@ mxmlEntityAddCallback(
// Global data
if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0])))
if (global->num_entity_cbs < (sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0])))
{
global->entity_cbs[global->num_entity_cbs] = cb;
global->num_entity_cbs ++;
return (0);
return (true);
}
else
{
mxml_error("Unable to add entity callback!");
return (-1);
return (false);
}
}
@ -72,14 +72,14 @@ mxmlEntityGetName(int val) // I - Character value
//
// 'mxmlEntityGetValue()' - Get the character corresponding to a named entity.
//
// The entity name can also be a numeric constant. -1 is returned if the
// 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
int // O - Character value or `-1` on error
mxmlEntityGetValue(const char *name) // I - Entity name
{
int i; // Looping var
size_t i; // Looping var
int ch; // Character value
_mxml_global_t *global = _mxml_global();
// Global data
@ -103,7 +103,7 @@ void
mxmlEntityRemoveCallback(
mxml_entity_cb_t cb) // I - Callback function to remove
{
int i; // Looping var
size_t i; // Looping var
_mxml_global_t *global = _mxml_global();
// Global data
@ -131,10 +131,10 @@ mxmlEntityRemoveCallback(
int // O - Unicode value or -1
_mxml_entity_cb(const char *name) // I - Entity name
{
int diff, // Difference between names
current, // Current entity in search
first, // First entity in search
last; // Last entity in search
int diff; // Difference between names
size_t current, // Current entity in search
first, // First entity in search
last; // Last entity in search
static const struct
{
const char *name; // Entity name
@ -403,7 +403,7 @@ _mxml_entity_cb(const char *name) // I - Entity name
// Do a binary search for the named entity...
first = 0;
last = (int)(sizeof(entities) / sizeof(entities[0]) - 1);
last = sizeof(entities) / sizeof(entities[0]) - 1;
while ((last - first) > 1)
{

View File

@ -1220,8 +1220,8 @@ mxml_load_data(
*first = NULL, // First node added
*parent = NULL; // Current parent node
int line = 1, // Current line number
ch, // Character from file
whitespace; // Non-zero if whitespace seen
ch; // Character from file
bool whitespace; // Whitespace seen?
char *buffer, // String buffer
*bufptr; // Pointer into buffer
int bufsize; // Size of buffer
@ -1231,6 +1231,10 @@ mxml_load_data(
// Global data
static const char * const types[] = // Type strings...
{
"MXML_TYPE_CDATA", // CDATA
"MXML_TYPE_COMMENT", // Comment
"MXML_TYPE_DECLARATION",// Declaration
"MXML_TYPE_DIRECTIVE",// Processing instruction/directive
"MXML_TYPE_ELEMENT", // XML element with attributes
"MXML_TYPE_INTEGER", // Integer value
"MXML_TYPE_OPAQUE", // Opaque string
@ -1251,7 +1255,7 @@ mxml_load_data(
bufptr = buffer;
parent = top;
first = NULL;
whitespace = 0;
whitespace = false;
encoding = ENCODE_UTF8;
if (cb && parent)
@ -1275,9 +1279,7 @@ mxml_load_data(
do
{
if ((ch == '<' ||
(mxml_isspace(ch) && type != MXML_TYPE_OPAQUE && type != MXML_TYPE_CUSTOM)) &&
bufptr > buffer)
if ((ch == '<' || (mxml_isspace(ch) && type != MXML_TYPE_OPAQUE && type != MXML_TYPE_CUSTOM)) && bufptr > buffer)
{
// Add a new value node...
*bufptr = '\0';
@ -1285,7 +1287,7 @@ mxml_load_data(
switch (type)
{
case MXML_TYPE_INTEGER :
node = mxmlNewInteger(parent, (int)strtol(buffer, &bufptr, 0));
node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0));
break;
case MXML_TYPE_OPAQUE :
@ -1350,7 +1352,7 @@ mxml_load_data(
}
else if (mxml_isspace(ch) && type == MXML_TYPE_TEXT)
{
whitespace = 1;
whitespace = true;
}
if (ch == '\n')
@ -1375,7 +1377,7 @@ mxml_load_data(
first = node;
}
whitespace = 0;
whitespace = false;
}
if (ch == '<')
@ -1444,16 +1446,16 @@ mxml_load_data(
}
// Otherwise add this as an element under the current parent...
*bufptr = '\0';
bufptr[-2] = '\0';
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("<%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)
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);
@ -1500,16 +1502,16 @@ mxml_load_data(
}
// Otherwise add this as an element under the current parent...
*bufptr = '\0';
bufptr[-2] = '\0';
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("<%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)
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);
@ -1550,16 +1552,16 @@ mxml_load_data(
}
// Otherwise add this as an element under the current parent...
*bufptr = '\0';
bufptr[-1] = '\0';
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("<%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)
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);
@ -1570,7 +1572,7 @@ mxml_load_data(
{
(*sax_cb)(node, MXML_SAX_EVENT_DIRECTIVE, sax_data);
if (strncmp(node->value.element.name, "?xml ", 5) && !mxmlRelease(node))
if (strncmp(node->value.directive, "xml ", 4) && !mxmlRelease(node))
node = NULL;
}
@ -1634,7 +1636,7 @@ mxml_load_data(
goto error;
}
if ((node = mxmlNewElement(parent, buffer)) == NULL)
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);
@ -1643,7 +1645,7 @@ mxml_load_data(
if (sax_cb)
{
(*sax_cb)(node, MXML_SAX_EVENT_DIRECTIVE, sax_data);
(*sax_cb)(node, MXML_SAX_EVENT_DECLARATION, sax_data);
if (!mxmlRelease(node))
node = NULL;
@ -2394,6 +2396,7 @@ mxml_write_node(mxml_node_t *node, // I - Node to write
width; // Width of attr + value
_mxml_attr_t *attr; // Current attribute
char s[255]; // Temporary string
const char *ptr; // Pointer into string
// Loop through this node and all of its children...
@ -2402,39 +2405,181 @@ mxml_write_node(mxml_node_t *node, // I - Node to write
// Print the node value...
switch (current->type)
{
case MXML_TYPE_CDATA :
col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
if ((*putc_cb)('<', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('!', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('[', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('C', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('D', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('A', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('T', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('A', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('[', p) < 0)
return (-1);
else
col ++;
for (ptr = current->value.cdata; *ptr; ptr ++)
{
if ((*putc_cb)(*ptr, p) < 0)
return (-1);
else if (*ptr == '\n')
col = 0;
else
col ++;
}
if ((*putc_cb)(']', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)(']', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('>', p) < 0)
return (-1);
else
col ++;
col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
break;
case MXML_TYPE_COMMENT :
col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
if ((*putc_cb)('<', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('!', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('-', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('-', p) < 0)
return (-1);
else
col ++;
for (ptr = current->value.comment; *ptr; ptr ++)
{
if ((*putc_cb)(*ptr, p) < 0)
return (-1);
else if (*ptr == '\n')
col = 0;
else
col ++;
}
if ((*putc_cb)('-', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('-', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('>', p) < 0)
return (-1);
else
col ++;
col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
break;
case MXML_TYPE_DECLARATION :
col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
if ((*putc_cb)('<', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('!', p) < 0)
return (-1);
else
col ++;
for (ptr = current->value.declaration; *ptr; ptr ++)
{
if ((*putc_cb)(*ptr, p) < 0)
return (-1);
else if (*ptr == '\n')
col = 0;
else
col ++;
}
if ((*putc_cb)('>', p) < 0)
return (-1);
else
col ++;
col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
break;
case MXML_TYPE_DIRECTIVE :
col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
if ((*putc_cb)('<', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('?', p) < 0)
return (-1);
else
col ++;
for (ptr = current->value.directive; *ptr; ptr ++)
{
if ((*putc_cb)(*ptr, p) < 0)
return (-1);
else if (*ptr == '\n')
col = 0;
else
col ++;
}
if ((*putc_cb)('?', p) < 0)
return (-1);
else
col ++;
if ((*putc_cb)('>', p) < 0)
return (-1);
else
col ++;
col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
break;
case MXML_TYPE_ELEMENT :
col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb);
if ((*putc_cb)('<', p) < 0)
return (-1);
if (current->value.element.name[0] == '?' || !strncmp(current->value.element.name, "!--", 3))
{
// Comments and processing instructions do not use character entities.
const char *ptr; // Pointer into name
for (ptr = current->value.element.name; *ptr; ptr ++)
if ((*putc_cb)(*ptr, p) < 0)
return (-1);
}
else if (!strncmp(current->value.element.name, "![CDATA[", 8))
{
// CDATA elements do not use character entities, but also need the
// "]]" terminator added at the end.
const char *ptr; // Pointer into name
for (ptr = current->value.element.name; *ptr; ptr ++)
if ((*putc_cb)(*ptr, p) < 0)
return (-1);
if ((*putc_cb)(']', p) < 0)
return (-1);
if ((*putc_cb)(']', p) < 0)
return (-1);
}
else if (mxml_write_name(current->value.element.name, p, putc_cb) < 0)
{
return (-1);
}
col += strlen(current->value.element.name) + 1;
@ -2488,27 +2633,14 @@ mxml_write_node(mxml_node_t *node, // I - Node to write
col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
}
else if (current->value.element.name[0] == '!' || current->value.element.name[0] == '?')
{
// The ? and ! elements are special-cases...
if ((*putc_cb)('>', p) < 0)
return (-1);
else
col ++;
col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
}
else
{
if ((*putc_cb)(' ', p) < 0)
return (-1);
if ((*putc_cb)('/', p) < 0)
return (-1);
if ((*putc_cb)('>', p) < 0)
return (-1);
col += 3;
col += 2;
col = mxml_write_ws(current, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb);
}
break;
@ -2533,7 +2665,7 @@ mxml_write_node(mxml_node_t *node, // I - Node to write
}
}
snprintf(s, sizeof(s), "%d", current->value.integer);
snprintf(s, sizeof(s), "%ld", current->value.integer);
if (mxml_write_string(s, p, putc_cb) < 0)
return (-1);
@ -2643,10 +2775,10 @@ mxml_write_node(mxml_node_t *node, // I - Node to write
if (current == node || !current->parent)
break;
// The ? and ! elements are special-cases and have no end tags...
// Declarations and directives have no end tags...
current = current->parent;
if (current->value.element.name[0] != '!' && current->value.element.name[0] != '?')
if (current->type == MXML_TYPE_ELEMENT)
{
col = mxml_write_ws(current, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb);

View File

@ -22,11 +22,29 @@ const char * // O - CDATA value or `NULL`
mxmlGetCDATA(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node || node->type != MXML_TYPE_ELEMENT || strncmp(node->value.element.name, "![CDATA[", 8))
if (!node || node->type != MXML_TYPE_CDATA)
return (NULL);
// Return the text following the CDATA declaration...
return (node->value.element.name + 8);
// Return the CDATA string...
return (node->value.cdata);
}
//
// 'mxmlGetComment()' - Get the value for a comment node.
//
// `NULL` is returned if the node is not a comment.
//
const char * // O - Comment value or `NULL`
mxmlGetComment(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node || node->type != MXML_TYPE_COMMENT)
return (NULL);
// Return the comment string...
return (node->value.comment);
}
@ -54,6 +72,42 @@ mxmlGetCustom(mxml_node_t *node) // I - Node to get
}
//
// 'mxmlGetDeclaration()' - Get the value for a declaration node.
//
// `NULL` is returned if the node is not a declaration.
//
const char * // O - Declaraction value or `NULL`
mxmlGetDeclaration(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node || node->type != MXML_TYPE_DECLARATION)
return (NULL);
// Return the comment string...
return (node->value.declaration);
}
//
// 'mxmlGetDirective()' - Get the value for a processing instruction node.
//
// `NULL` is returned if the node is not a processing instruction.
//
const char * // O - Comment value or `NULL`
mxmlGetDirective(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node || node->type != MXML_TYPE_DIRECTIVE)
return (NULL);
// Return the comment string...
return (node->value.directive);
}
//
// 'mxmlGetElement()' - Get the name for an element node.
//
@ -95,10 +149,10 @@ mxmlGetFirstChild(mxml_node_t *node) // I - Node to get
// 'mxmlGetInteger()' - Get the integer value from the specified node or its
// first child.
//
// 0 is returned if the node (or its first child) is not an integer value node.
// `0` is returned if the node (or its first child) is not an integer value node.
//
int // O - Integer value or 0
long // O - Integer value or `0`
mxmlGetInteger(mxml_node_t *node) // I - Node to get
{
// Range check input...
@ -250,13 +304,13 @@ mxmlGetReal(mxml_node_t *node) // I - Node to get
const char * // O - Text string or `NULL`
mxmlGetText(mxml_node_t *node, // I - Node to get
int *whitespace) // O - 1 if string is preceded by whitespace, 0 otherwise
bool *whitespace) // O - `true` if string is preceded by whitespace, `false` otherwise
{
// Range check input...
if (!node)
{
if (whitespace)
*whitespace = 0;
*whitespace = false;
return (NULL);
}
@ -279,7 +333,7 @@ mxmlGetText(mxml_node_t *node, // I - Node to get
else
{
if (whitespace)
*whitespace = 0;
*whitespace = false;
return (NULL);
}

View File

@ -109,7 +109,7 @@ mxmlIndexFind(mxml_index_t *ind, // I - Index to search
first = 0;
last = ind->num_nodes - 1;
MXML_DEBUG("mxmlIndexFind: Find first time, num_nodes=%d...\n", ind->num_nodes);
MXML_DEBUG("mxmlIndexFind: Find first time, num_nodes=%lu...\n", (unsigned long)ind->num_nodes);
while ((last - first) > 1)
{
@ -165,7 +165,7 @@ mxmlIndexFind(mxml_index_t *ind, // I - Index to search
else if (ind->cur_node < ind->num_nodes && !index_find(ind, element, value, ind->nodes[ind->cur_node]))
{
// Return the next matching node...
MXML_DEBUG("mxmlIndexFind: Returning next match %d...\n", ind->cur_node);
MXML_DEBUG("mxmlIndexFind: Returning next match %lu...\n", (unsigned long)ind->cur_node);
return (ind->nodes[ind->cur_node ++]);
}

View File

@ -170,7 +170,7 @@ mxmlDelete(mxml_node_t *node) // I - Node to delete
// reference count.
//
int // O - Reference count
size_t // O - Reference count
mxmlGetRefCount(mxml_node_t *node) // I - Node
{
// Range check input...
@ -206,18 +206,129 @@ mxmlNewCDATA(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
return (NULL);
// Create the node and set the name value...
if ((node = mxml_new(parent, MXML_TYPE_ELEMENT)) != NULL)
if ((node = mxml_new(parent, MXML_TYPE_CDATA)) != NULL)
{
size_t datalen = strlen(data); // Length of data
if ((node->value.element.name = malloc(datalen + 9)) == NULL)
if ((node->value.cdata = strdup(data)) == NULL)
{
mxml_error("Unable to allocate memory for CDATA.");
mxmlDelete(node);
return (NULL);
}
}
snprintf(node->value.element.name, datalen + 9, "![CDATA[%s", data);
return (node);
}
//
// 'mxmlNewCDATAf()' - Create a new formatted CDATA node.
//
// The new CDATA 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 opaque string node has no parent. The format string must be
// nul-terminated and is formatted into the new node.
//
mxml_node_t * // O - New node
mxmlNewCDATAf(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *format, // I - Printf-style format string
...) // I - Additional args as needed
{
mxml_node_t *node; // New node
va_list ap; // Pointer to arguments
char buffer[16384]; // Format buffer
MXML_DEBUG("mxmlNewCDATAf(parent=%p, format=\"%s\", ...)\n", parent, format ? format : "(null)");
// Range check input...
if (!format)
return (NULL);
// Create the node and set the text value...
if ((node = mxml_new(parent, MXML_TYPE_CDATA)) != NULL)
{
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
node->value.cdata = strdup(buffer);
}
return (node);
}
//
// 'mxmlNewComment()' - Create a new comment node.
//
// The new comment 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
// comment node has no parent. The comment string must be nul-terminated and
// is copied into the new node.
//
mxml_node_t * // O - New node
mxmlNewComment(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *comment) // I - Comment string
{
mxml_node_t *node; // New node
MXML_DEBUG("mxmlNewComment(parent=%p, comment=\"%s\")\n", parent, comment ? comment : "(null)");
// Range check input...
if (!comment)
return (NULL);
// Create the node and set the name value...
if ((node = mxml_new(parent, MXML_TYPE_COMMENT)) != NULL)
{
if ((node->value.comment = strdup(comment)) == NULL)
{
mxml_error("Unable to allocate memory for comment.");
mxmlDelete(node);
return (NULL);
}
}
return (node);
}
//
// 'mxmlNewCommentf()' - Create a new formatted comment string node.
//
// The new comment string 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 opaque string node has no parent. The format string must be
// nul-terminated and is formatted into the new node.
//
mxml_node_t * // O - New node
mxmlNewCommentf(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *format, // I - Printf-style format string
...) // I - Additional args as needed
{
mxml_node_t *node; // New node
va_list ap; // Pointer to arguments
char buffer[16384]; // Format buffer
MXML_DEBUG("mxmlNewCommentf(parent=%p, format=\"%s\", ...)\n", parent, format ? format : "(null)");
// Range check input...
if (!format)
return (NULL);
// Create the node and set the text value...
if ((node = mxml_new(parent, MXML_TYPE_COMMENT)) != NULL)
{
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
node->value.comment = strdup(buffer);
}
return (node);
@ -255,6 +366,160 @@ mxmlNewCustom(
}
//
// 'mxmlNewDeclaration()' - Create a new declaraction node.
//
// The new declaration 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
// declaration node has no parent. The declaration string must be nul-
// terminated and is copied into the new node.
//
mxml_node_t * // O - New node
mxmlNewDeclaration(
mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *declaration) // I - Declaration string
{
mxml_node_t *node; // New node
MXML_DEBUG("mxmlNewDeclaration(parent=%p, declaration=\"%s\")\n", parent, declaration ? declaration : "(null)");
// Range check input...
if (!declaration)
return (NULL);
// Create the node and set the name value...
if ((node = mxml_new(parent, MXML_TYPE_DECLARATION)) != NULL)
{
if ((node->value.declaration = strdup(declaration)) == NULL)
{
mxml_error("Unable to allocate memory for declaration.");
mxmlDelete(node);
return (NULL);
}
}
return (node);
}
//
// 'mxmlNewDeclarationf()' - Create a new formatted declaration node.
//
// The new declaration 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 opaque string node has no parent. The format string must be
// nul-terminated and is formatted into the new node.
//
mxml_node_t * // O - New node
mxmlNewDeclarationf(
mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *format, // I - Printf-style format string
...) // I - Additional args as needed
{
mxml_node_t *node; // New node
va_list ap; // Pointer to arguments
char buffer[16384]; // Format buffer
MXML_DEBUG("mxmlNewDeclarationf(parent=%p, format=\"%s\", ...)\n", parent, format ? format : "(null)");
// Range check input...
if (!format)
return (NULL);
// Create the node and set the text value...
if ((node = mxml_new(parent, MXML_TYPE_DECLARATION)) != NULL)
{
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
node->value.declaration = strdup(buffer);
}
return (node);
}
//
// 'mxmlNewDirective()' - Create a new processing instruction node.
//
// The new processing instruction 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 processing instruction node has no parent. The data string must
// be nul-terminated and is copied into the new node.
//
mxml_node_t * // O - New node
mxmlNewDirective(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *directive)// I - Directive string
{
mxml_node_t *node; // New node
MXML_DEBUG("mxmlNewDirective(parent=%p, directive=\"%s\")\n", parent, directive ? directive : "(null)");
// Range check input...
if (!directive)
return (NULL);
// Create the node and set the name value...
if ((node = mxml_new(parent, MXML_TYPE_DIRECTIVE)) != NULL)
{
if ((node->value.directive = strdup(directive)) == NULL)
{
mxml_error("Unable to allocate memory for processing instruction.");
mxmlDelete(node);
return (NULL);
}
}
return (node);
}
//
// 'mxmlNewDirectivef()' - Create a new formatted processing instruction node.
//
// The new processing instruction 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 opaque string node has no parent. The format string must be
// nul-terminated and is formatted into the new node.
//
mxml_node_t * // O - New node
mxmlNewDirectivef(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *format, // I - Printf-style format string
...) // I - Additional args as needed
{
mxml_node_t *node; // New node
va_list ap; // Pointer to arguments
char buffer[16384]; // Format buffer
MXML_DEBUG("mxmlNewDirectivef(parent=%p, format=\"%s\", ...)\n", parent, format ? format : "(null)");
// Range check input...
if (!format)
return (NULL);
// Create the node and set the text value...
if ((node = mxml_new(parent, MXML_TYPE_DIRECTIVE)) != NULL)
{
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
node->value.directive = strdup(buffer);
}
return (node);
}
//
// 'mxmlNewElement()' - Create a new element node.
//
@ -294,12 +559,12 @@ mxmlNewElement(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
mxml_node_t * // O - New node
mxmlNewInteger(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
int integer) // I - Integer value
long integer) // I - Integer value
{
mxml_node_t *node; // New node
MXML_DEBUG("mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer);
MXML_DEBUG("mxmlNewInteger(parent=%p, integer=%ld)\n", parent, integer);
// Create the node and set the element name...
if ((node = mxml_new(parent, MXML_TYPE_INTEGER)) != NULL)
@ -415,13 +680,13 @@ mxmlNewReal(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
mxml_node_t * // O - New node
mxmlNewText(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
int whitespace, // I - 1 = leading whitespace, 0 = no whitespace
bool whitespace, // I - `true` = leading whitespace, `false` = no whitespace
const char *string) // I - String
{
mxml_node_t *node; // New node
MXML_DEBUG("mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n", parent, whitespace, string ? string : "(null)");
MXML_DEBUG("mxmlNewText(parent=%p, whitespace=%s, string=\"%s\")\n", parent, whitespace ? "true" : "false", string ? string : "(null)");
// Range check input...
if (!string)
@ -450,7 +715,7 @@ mxmlNewText(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
mxml_node_t * // O - New node
mxmlNewTextf(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
int whitespace, // I - 1 = leading whitespace, 0 = no whitespace
bool whitespace, // I - `true` = leading whitespace, `false` = no whitespace
const char *format, // I - Printf-style format string
...) // I - Additional args as needed
{
@ -459,7 +724,7 @@ mxmlNewTextf(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
char buffer[16384]; // Format buffer
MXML_DEBUG("mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n", parent, whitespace, format ? format : "(null)");
MXML_DEBUG("mxmlNewTextf(parent=%p, whitespace=%s, format=\"%s\", ...)\n", parent, whitespace ? "true" : "false", format ? format : "(null)");
// Range check input...
if (!format)
@ -517,18 +782,18 @@ mxmlRemove(mxml_node_t *node) // I - Node to remove
// 'mxmlNewXML()' - Create a new XML document tree.
//
// The "version" argument specifies the version number to put in the
// ?xml element node. If `NULL`, version "1.0" is assumed.
// ?xml directive node. If `NULL`, version "1.0" is assumed.
//
mxml_node_t * // O - New ?xml node
mxmlNewXML(const char *version) // I - Version number to use
{
char element[1024]; // Element text
char directive[1024]; // Directive text
snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?", version ? version : "1.0");
snprintf(directive, sizeof(directive), "xml version=\"%s\" encoding=\"utf-8\"", version ? version : "1.0");
return (mxmlNewElement(NULL, element));
return (mxmlNewDirective(NULL, directive));
}
@ -589,6 +854,18 @@ mxml_free(mxml_node_t *node) // I - Node
switch (node->type)
{
case MXML_TYPE_CDATA :
free(node->value.cdata);
break;
case MXML_TYPE_COMMENT :
free(node->value.comment);
break;
case MXML_TYPE_DECLARATION :
free(node->value.declaration);
break;
case MXML_TYPE_DIRECTIVE :
free(node->value.directive);
break;
case MXML_TYPE_ELEMENT :
free(node->value.element.name);

View File

@ -39,13 +39,13 @@ typedef struct _mxml_attr_s // An XML element attribute value.
typedef struct _mxml_element_s // An XML element value.
{
char *name; // Name of element
int num_attrs; // Number of attributes
size_t num_attrs; // Number of attributes
_mxml_attr_t *attrs; // Attributes
} _mxml_element_t;
typedef struct _mxml_text_s // An XML text value.
{
int whitespace; // Leading whitespace?
bool whitespace; // Leading whitespace?
char *string; // Fragment string
} _mxml_text_t;
@ -57,12 +57,16 @@ typedef struct _mxml_custom_s // An XML custom value.
typedef union _mxml_value_u // An XML node value.
{
char *cdata; // CDATA string
char *comment; // Common string
char *declaration; // Declaration string
char *directive; // Processing instruction string
_mxml_element_t element; // Element
int integer; // Integer number
long integer; // Integer number
char *opaque; // Opaque string
double real; // Real number
_mxml_text_t text; // Text fragment
_mxml_custom_t custom; // Custom data @since Mini-XML 2.1@
_mxml_custom_t custom; // Custom data
} _mxml_value_t;
struct _mxml_node_s // An XML node.
@ -74,26 +78,26 @@ struct _mxml_node_s // An XML node.
struct _mxml_node_s *child; // First child node
struct _mxml_node_s *last_child; // Last child node
_mxml_value_t value; // Node value
int ref_count; // Use count
size_t ref_count; // Use count
void *user_data; // User data
};
struct _mxml_index_s // An XML node index.
{
char *attr; // Attribute used for indexing or NULL
int num_nodes; // Number of nodes in index
int alloc_nodes; // Allocated nodes in index
int cur_node; // Current node
size_t num_nodes; // Number of nodes in index
size_t alloc_nodes; // Allocated nodes in index
size_t cur_node; // Current node
mxml_node_t **nodes; // Node array
};
typedef struct _mxml_global_s // Global, per-thread data
{
void (*error_cb)(const char *);
int num_entity_cbs;
int (*entity_cbs[100])(const char *name);
int wrap;
void (*error_cb)(const char *);
size_t num_entity_cbs;
int (*entity_cbs[100])(const char *name);
int wrap;
mxml_custom_load_cb_t custom_load_cb;
mxml_custom_save_cb_t custom_save_cb;
} _mxml_global_t;

View File

@ -13,54 +13,187 @@
//
// 'mxmlSetCDATA()' - Set the element name of a CDATA node.
// 'mxmlSetCDATA()' - Set the data for a CDATA node.
//
// The node is not changed if it (or its first child) is not a CDATA element node.
// The node is not changed if it (or its first child) is not a CDATA node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetCDATA(mxml_node_t *node, // I - Node to set
const char *data) // I - New data string
{
size_t datalen; // Length of data string
char *s; // New element name
char *s; // New element name
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && strncmp(node->value.element.name, "![CDATA[", 8) && node->child && node->child->type == MXML_TYPE_ELEMENT && !strncmp(node->child->value.element.name, "![CDATA[", 8))
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_CDATA)
node = node->child;
if (!node || node->type != MXML_TYPE_ELEMENT || strncmp(node->value.element.name, "![CDATA[", 8))
if (!node || node->type != MXML_TYPE_CDATA)
{
mxml_error("Wrong node type.");
return (-1);
return (false);
}
else if (!data)
{
mxml_error("NULL string not allowed.");
return (-1);
return (false);
}
if (data == (node->value.element.name + 8))
if (data == node->value.cdata)
{
// Don't change the value...
return (0);
return (true);
}
// Allocate the new value, free any old element value, and set the new value...
datalen = strlen(data);
if ((s = malloc(datalen + 9)) == NULL)
if ((s = strdup(data)) == NULL)
{
mxml_error("Unable to allocate memory for CDATA.");
return (-1);
return (false);
}
free(node->value.element.name);
node->value.element.name = s;
snprintf(node->value.element.name, datalen + 9, "![CDATA[%s", data);
free(node->value.cdata);
node->value.cdata = s;
return (0);
return (true);
}
//
// 'mxmlSetCDATAf()' - Set the data for a CDATA to a formatted string.
//
bool // O - `true` on success, `false` on failure
mxmlSetCDATAf(mxml_node_t *node, // I - Node
const char *format, // I - `printf`-style format string
...) // I - Additional arguments as needed
{
va_list ap; // Pointer to arguments
char buffer[16384]; // Format buffer
char *s; // Temporary string
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_CDATA)
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);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
if ((s = strdup(buffer)) == NULL)
{
mxml_error("Unable to allocate memory for CDATA string.");
return (false);
}
free(node->value.cdata);
node->value.cdata = s;
return (true);
}
//
// 'mxmlSetComment()' - Set a comment to a literal string.
//
bool // O - `true` on success, `false` on failure
mxmlSetComment(mxml_node_t *node, // I - Node
const char *comment) // I - Literal string
{
char *s; // New string
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_COMMENT)
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 = strdup(comment)) == NULL)
{
mxml_error("Unable to allocate memory for comment string.");
return (false);
}
free(node->value.comment);
node->value.comment = s;
return (true);
}
//
// 'mxmlSetCommentf()' - Set a comment to a formatted string.
//
bool // O - `true` on success, `false` on failure
mxmlSetCommentf(mxml_node_t *node, // I - Node
const char *format, // I - `printf`-style format string
...) // I - Additional arguments as needed
{
va_list ap; // Pointer to arguments
char buffer[16384]; // Format buffer
char *s; // Temporary string
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_COMMENT)
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);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
if ((s = strdup(buffer)) == NULL)
{
mxml_error("Unable to allocate memory for comment string.");
return (false);
}
free(node->value.comment);
node->value.comment = s;
return (true);
}
@ -70,7 +203,7 @@ mxmlSetCDATA(mxml_node_t *node, // I - Node to set
// The node is not changed if it (or its first child) is not a custom node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetCustom(
mxml_node_t *node, // I - Node to set
void *data, // I - New data pointer
@ -83,13 +216,13 @@ mxmlSetCustom(
if (!node || node->type != MXML_TYPE_CUSTOM)
{
mxml_error("Wrong node type.");
return (-1);
return (false);
}
if (data == node->value.custom.data)
{
node->value.custom.destroy = destroy;
return (0);
return (true);
}
// Free any old element value and set the new value...
@ -99,7 +232,188 @@ mxmlSetCustom(
node->value.custom.data = data;
node->value.custom.destroy = destroy;
return (0);
return (true);
}
//
// 'mxmlSetDeclaration()' - Set a comment to a literal string.
//
bool // O - `true` on success, `false` on failure
mxmlSetDeclaration(
mxml_node_t *node, // I - Node
const char *declaration) // I - Literal string
{
char *s; // New string
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_DECLARATION)
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 = strdup(declaration)) == NULL)
{
mxml_error("Unable to allocate memory for declaration string.");
return (false);
}
free(node->value.declaration);
node->value.declaration = s;
return (true);
}
//
// 'mxmlSetDeclarationf()' - Set a comment to a formatted string.
//
bool // O - `true` on success, `false` on failure
mxmlSetDeclarationf(mxml_node_t *node, // I - Node
const char *format, // I - `printf`-style format string
...) // I - Additional arguments as needed
{
va_list ap; // Pointer to arguments
char buffer[16384]; // Format buffer
char *s; // Temporary string
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_COMMENT)
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);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
if ((s = strdup(buffer)) == NULL)
{
mxml_error("Unable to allocate memory for declaration string.");
return (false);
}
free(node->value.declaration);
node->value.declaration = s;
return (true);
}
//
// 'mxmlSetDirective()' - Set a directive to a literal string.
//
bool // O - `true` on success, `false` on failure
mxmlSetDirective(mxml_node_t *node, // I - Node
const char *directive)// I - Literal string
{
char *s; // New string
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_DIRECTIVE)
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 = strdup(directive)) == NULL)
{
mxml_error("Unable to allocate memory for directive string.");
return (false);
}
free(node->value.directive);
node->value.directive = s;
return (true);
}
//
// 'mxmlSetDirectivef()' - Set a directive to a formatted string.
//
bool // O - `true` on success, `false` on failure
mxmlSetDirectivef(mxml_node_t *node, // I - Node
const char *format, // I - `printf`-style format string
...) // I - Additional arguments as needed
{
va_list ap; // Pointer to arguments
char buffer[16384]; // Format buffer
char *s; // Temporary string
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_DIRECTIVE)
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);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
if ((s = strdup(buffer)) == NULL)
{
mxml_error("Unable to allocate memory for directive string.");
return (false);
}
free(node->value.directive);
node->value.directive = s;
return (true);
}
@ -109,7 +423,7 @@ mxmlSetCustom(
// The node is not changed if it is not an element node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetElement(mxml_node_t *node, // I - Node to set
const char *name) // I - New name string
{
@ -120,28 +434,28 @@ mxmlSetElement(mxml_node_t *node, // I - Node to set
if (!node || node->type != MXML_TYPE_ELEMENT)
{
mxml_error("Wrong node type.");
return (-1);
return (false);
}
else if (!name)
{
mxml_error("NULL string not allowed.");
return (-1);
return (false);
}
if (name == node->value.element.name)
return (0);
return (true);
// Free any old element value and set the new value...
if ((s = strdup(name)) == NULL)
{
mxml_error("Unable to allocate memory for element name.");
return (-1);
return (false);
}
free(node->value.element.name);
node->value.element.name = s;
return (0);
return (true);
}
@ -151,9 +465,9 @@ mxmlSetElement(mxml_node_t *node, // I - Node to set
// The node is not changed if it (or its first child) is not an integer node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetInteger(mxml_node_t *node, // I - Node to set
int integer) // I - Integer value
long integer) // I - Integer value
{
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_INTEGER)
@ -162,13 +476,13 @@ mxmlSetInteger(mxml_node_t *node, // I - Node to set
if (!node || node->type != MXML_TYPE_INTEGER)
{
mxml_error("Wrong node type.");
return (-1);
return (false);
}
// Set the new value and return...
node->value.integer = integer;
return (0);
return (true);
}
@ -178,7 +492,7 @@ mxmlSetInteger(mxml_node_t *node, // I - Node to set
// The node is not changed if it (or its first child) is not an opaque node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetOpaque(mxml_node_t *node, // I - Node to set
const char *opaque) // I - Opaque string
{
@ -192,28 +506,28 @@ mxmlSetOpaque(mxml_node_t *node, // I - Node to set
if (!node || node->type != MXML_TYPE_OPAQUE)
{
mxml_error("Wrong node type.");
return (-1);
return (false);
}
else if (!opaque)
{
mxml_error("NULL string not allowed.");
return (-1);
return (false);
}
if (node->value.opaque == opaque)
return (0);
return (true);
// Free any old opaque value and set the new value...
if ((s = strdup(opaque)) == NULL)
{
mxml_error("Unable to allocate memory for opaque string.");
return (-1);
return (false);
}
free(node->value.opaque);
node->value.opaque = s;
return (0);
return (true);
}
@ -223,7 +537,7 @@ mxmlSetOpaque(mxml_node_t *node, // I - Node to set
// The node is not changed if it (or its first child) is not an opaque node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetOpaquef(mxml_node_t *node, // I - Node to set
const char *format, // I - Printf-style format string
...) // I - Additional arguments as needed
@ -240,12 +554,12 @@ mxmlSetOpaquef(mxml_node_t *node, // I - Node to set
if (!node || node->type != MXML_TYPE_OPAQUE)
{
mxml_error("Wrong node type.");
return (-1);
return (false);
}
else if (!format)
{
mxml_error("NULL string not allowed.");
return (-1);
return (false);
}
// Format the new string, free any old string value, and set the new value...
@ -256,13 +570,13 @@ mxmlSetOpaquef(mxml_node_t *node, // I - Node to set
if ((s = strdup(buffer)) == NULL)
{
mxml_error("Unable to allocate memory for opaque string.");
return (-1);
return (false);
}
free(node->value.opaque);
node->value.opaque = s;
return (0);
return (true);
}
@ -272,7 +586,7 @@ mxmlSetOpaquef(mxml_node_t *node, // I - Node to set
// The node is not changed if it (or its first child) is not a real number node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetReal(mxml_node_t *node, // I - Node to set
double real) // I - Real number value
{
@ -286,13 +600,13 @@ mxmlSetReal(mxml_node_t *node, // I - Node to set
if (!node || node->type != MXML_TYPE_REAL)
{
mxml_error("Wrong node type.");
return (-1);
return (false);
}
// Set the new value and return...
node->value.real = real;
return (0);
return (true);
}
@ -302,9 +616,9 @@ mxmlSetReal(mxml_node_t *node, // I - Node to set
// The node is not changed if it (or its first child) is not a text node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetText(mxml_node_t *node, // I - Node to set
int whitespace, // I - 1 = leading whitespace, 0 = no whitespace
bool whitespace, // I - `true` = leading whitespace, `false` = no whitespace
const char *string) // I - String
{
char *s; // New string
@ -317,25 +631,25 @@ mxmlSetText(mxml_node_t *node, // I - Node to set
if (!node || node->type != MXML_TYPE_TEXT)
{
mxml_error("Wrong node type.");
return (-1);
return (false);
}
else if (!string)
{
mxml_error("NULL string not allowed.");
return (-1);
return (false);
}
if (string == node->value.text.string)
{
node->value.text.whitespace = whitespace;
return (0);
return (true);
}
// Free any old string value and set the new value...
if ((s = strdup(string)) == NULL)
{
mxml_error("Unable to allocate memory for text string.");
return (-1);
return (false);
}
free(node->value.text.string);
@ -343,7 +657,7 @@ mxmlSetText(mxml_node_t *node, // I - Node to set
node->value.text.whitespace = whitespace;
node->value.text.string = s;
return (0);
return (true);
}
@ -353,9 +667,9 @@ mxmlSetText(mxml_node_t *node, // I - Node to set
// The node is not changed if it (or its first child) is not a text node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetTextf(mxml_node_t *node, // I - Node to set
int whitespace, // I - 1 = leading whitespace, 0 = no whitespace
bool whitespace, // I - `true` = leading whitespace, `false` = no whitespace
const char *format, // I - Printf-style format string
...) // I - Additional arguments as needed
{
@ -371,18 +685,15 @@ mxmlSetTextf(mxml_node_t *node, // I - Node to set
if (!node || node->type != MXML_TYPE_TEXT)
{
mxml_error("Wrong node type.");
return (-1);
return (false);
}
else if (!format)
{
mxml_error("NULL string not allowed.");
return (-1);
return (false);
}
/*
* Free any old string value and set the new value...
*/
// Free any old string value and set the new value...
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
@ -390,7 +701,7 @@ mxmlSetTextf(mxml_node_t *node, // I - Node to set
if ((s = strdup(buffer)) == NULL)
{
mxml_error("Unable to allocate memory for text string.");
return (-1);
return (false);
}
free(node->value.text.string);
@ -398,7 +709,7 @@ mxmlSetTextf(mxml_node_t *node, // I - Node to set
node->value.text.whitespace = whitespace;
node->value.text.string = s;
return (0);
return (true);
}
@ -406,15 +717,15 @@ mxmlSetTextf(mxml_node_t *node, // I - Node to set
// 'mxmlSetUserData()' - Set the user data pointer for a node.
//
int // O - 0 on success, -1 on failure
bool // O - `true` on success, `false` on failure
mxmlSetUserData(mxml_node_t *node, // I - Node to set
void *data) // I - User data pointer
{
// Range check input...
if (!node)
return (-1);
return (false);
// Set the user data pointer and return...
node->user_data = data;
return (0);
return (true);
}

58
mxml.h
View File

@ -13,6 +13,7 @@
# define MXML_H
# include <stdio.h>
# include <stdlib.h>
# include <stdbool.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
@ -72,7 +73,8 @@ typedef enum mxml_sax_event_e // SAX event type.
MXML_SAX_EVENT_CDATA, // CDATA node
MXML_SAX_EVENT_COMMENT, // Comment node
MXML_SAX_EVENT_DATA, // Data node
MXML_SAX_EVENT_DIRECTIVE, // Processing directive node
MXML_SAX_EVENT_DECLARATION, // Declaration node
MXML_SAX_EVENT_DIRECTIVE, // Processing instruction node
MXML_SAX_EVENT_ELEMENT_CLOSE, // Element closed
MXML_SAX_EVENT_ELEMENT_OPEN // Element opened
} mxml_sax_event_t;
@ -80,6 +82,10 @@ typedef enum mxml_sax_event_e // SAX event type.
typedef enum mxml_type_e // The XML node type.
{
MXML_TYPE_IGNORE = -1, // Ignore/throw away node
MXML_TYPE_CDATA, // CDATA value ("<[CDATA[...]]>")
MXML_TYPE_COMMENT, // Comment ("<!--...-->")
MXML_TYPE_DECLARATION, // Declaration ("<!...>")
MXML_TYPE_DIRECTIVE, // Processing instruction ("<?...?>")
MXML_TYPE_ELEMENT, // XML element with attributes
MXML_TYPE_INTEGER, // Integer value
MXML_TYPE_OPAQUE, // Opaque string
@ -127,10 +133,10 @@ extern void mxmlDelete(mxml_node_t *node);
extern void mxmlElementDeleteAttr(mxml_node_t *node, const char *name);
extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name);
extern const char *mxmlElementGetAttrByIndex(mxml_node_t *node, int idx, const char **name);
extern int mxmlElementGetAttrCount(mxml_node_t *node);
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 int mxmlEntityAddCallback(mxml_entity_cb_t cb);
extern bool mxmlEntityAddCallback(mxml_entity_cb_t cb);
extern const char *mxmlEntityGetName(int val);
extern int mxmlEntityGetValue(const char *name);
extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb);
@ -140,15 +146,15 @@ extern const char *mxmlGetCDATA(mxml_node_t *node);
extern const void *mxmlGetCustom(mxml_node_t *node);
extern const char *mxmlGetElement(mxml_node_t *node);
extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node);
extern int mxmlGetInteger(mxml_node_t *node);
extern long mxmlGetInteger(mxml_node_t *node);
extern mxml_node_t *mxmlGetLastChild(mxml_node_t *node);
extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node);
extern const char *mxmlGetOpaque(mxml_node_t *node);
extern mxml_node_t *mxmlGetParent(mxml_node_t *node);
extern mxml_node_t *mxmlGetPrevSibling(mxml_node_t *node);
extern double mxmlGetReal(mxml_node_t *node);
extern int mxmlGetRefCount(mxml_node_t *node);
extern const char *mxmlGetText(mxml_node_t *node, int *whitespace);
extern size_t mxmlGetRefCount(mxml_node_t *node);
extern const char *mxmlGetText(mxml_node_t *node, bool *whitespace);
extern mxml_type_t mxmlGetType(mxml_node_t *node);
extern void *mxmlGetUserData(mxml_node_t *node);
extern void mxmlIndexDelete(mxml_index_t *ind);
@ -160,14 +166,21 @@ extern mxml_node_t *mxmlIndexReset(mxml_index_t *ind);
extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, int fd, mxml_load_cb_t cb); extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp, mxml_load_cb_t cb);
extern mxml_node_t *mxmlLoadString(mxml_node_t *top, const char *s, mxml_load_cb_t cb);
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 *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);
extern mxml_node_t *mxmlNewDirectivef(mxml_node_t *parent, const char *format, ...) MXML_FORMAT(2,3);
extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name);
extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, int integer);
extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, long integer);
extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque);
extern mxml_node_t *mxmlNewOpaquef(mxml_node_t *parent, const char *format, ...) MXML_FORMAT(2,3);
extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real);
extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace, const char *string);
extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace, const char *format, ...) MXML_FORMAT(3,4);
extern mxml_node_t *mxmlNewText(mxml_node_t *parent, bool whitespace, const char *string);
extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, bool whitespace, const char *format, ...) MXML_FORMAT(3,4);
extern mxml_node_t *mxmlNewXML(const char *version);
extern int mxmlRelease(mxml_node_t *node);
extern void mxmlRemove(mxml_node_t *node);
@ -179,18 +192,25 @@ extern int mxmlSaveString(mxml_node_t *node, char *buffer, int bufsize, mxml_sa
extern mxml_node_t *mxmlSAXLoadFd(mxml_node_t *top, int fd, mxml_load_cb_t cb, mxml_sax_cb_t sax, void *sax_data);
extern mxml_node_t *mxmlSAXLoadFile(mxml_node_t *top, FILE *fp, mxml_load_cb_t cb, mxml_sax_cb_t sax, void *sax_data);
extern mxml_node_t *mxmlSAXLoadString(mxml_node_t *top, const char *s, mxml_load_cb_t cb, mxml_sax_cb_t sax, void *sax_data);
extern int mxmlSetCDATA(mxml_node_t *node, const char *data);
extern int mxmlSetCustom(mxml_node_t *node, void *data, mxml_custom_destroy_cb_t destroy);
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);
extern bool mxmlSetCommentf(mxml_node_t *node, const char *format, ...) MXML_FORMAT(2,3);
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);
extern void mxmlSetCustomHandlers(mxml_custom_load_cb_t load, mxml_custom_save_cb_t save);
extern int mxmlSetElement(mxml_node_t *node, const char *name);
extern bool mxmlSetElement(mxml_node_t *node, const char *name);
extern void mxmlSetErrorCallback(mxml_error_cb_t cb);
extern int mxmlSetInteger(mxml_node_t *node, int integer);
extern int mxmlSetOpaque(mxml_node_t *node, const char *opaque);
extern int mxmlSetOpaquef(mxml_node_t *node, const char *format, ...) MXML_FORMAT(2,3);
extern int mxmlSetReal(mxml_node_t *node, double real);
extern int mxmlSetText(mxml_node_t *node, int whitespace, const char *string);
extern int mxmlSetTextf(mxml_node_t *node, int whitespace, const char *format, ...) MXML_FORMAT(3,4);
extern int mxmlSetUserData(mxml_node_t *node, void *data);
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);
extern bool mxmlSetReal(mxml_node_t *node, double real);
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, int descend);
extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top, int descend);

View File

@ -8,7 +8,7 @@
<choice>
<keyword type="opaque">Auto</keyword>
<text>Auto Tray Selection</text>
<code type="opaque" />
<code type="opaque"/>
</choice>
<choice>
<keyword type="opaque">Upper</keyword>

View File

@ -28,7 +28,7 @@
// Globals...
//
int event_counts[6];
int event_counts[7];
//
@ -99,7 +99,7 @@ main(int argc, // I - Number of command-line args
mxmlNewInteger(tree, 123);
mxmlNewOpaque(tree, "opaque");
mxmlNewReal(tree, 123.4f);
mxmlNewReal(tree, 123.4);
mxmlNewText(tree, 1, "text");
mxmlLoadString(tree, "<group type='string'>string string string</group>", MXML_NO_CALLBACK);
@ -141,7 +141,7 @@ main(int argc, // I - Number of command-line args
if (node->value.integer != 123)
{
fprintf(stderr, "ERROR: First child value is %d, expected 123.\n", node->value.integer);
fprintf(stderr, "ERROR: First child value is %ld, expected 123.\n", node->value.integer);
mxmlDelete(tree);
return (1);
}
@ -185,7 +185,7 @@ main(int argc, // I - Number of command-line args
return (1);
}
if (node->value.real != 123.4f)
if (node->value.real != 123.4)
{
fprintf(stderr, "ERROR: Third child value is %f, expected 123.4.\n", node->value.real);
mxmlDelete(tree);
@ -286,9 +286,9 @@ main(int argc, // I - Number of command-line args
return (1);
}
if (ind->num_nodes != 13)
if (ind->num_nodes != 10)
{
fprintf(stderr, "ERROR: Index of all nodes contains %d nodes; expected 13.\n", ind->num_nodes);
fprintf(stderr, "ERROR: Index of all nodes contains %lu nodes; expected 10.\n", (unsigned long)ind->num_nodes);
mxmlIndexDelete(ind);
mxmlDelete(tree);
return (1);
@ -315,7 +315,7 @@ main(int argc, // I - Number of command-line args
if (ind->num_nodes != 4)
{
fprintf(stderr, "ERROR: Index of groups contains %d nodes; expected 4.\n", ind->num_nodes);
fprintf(stderr, "ERROR: Index of groups contains %lu nodes; expected 4.\n", (unsigned long)ind->num_nodes);
mxmlIndexDelete(ind);
mxmlDelete(tree);
return (1);
@ -342,7 +342,7 @@ main(int argc, // I - Number of command-line args
if (ind->num_nodes != 3)
{
fprintf(stderr, "ERROR: Index of type attributes contains %d nodes; expected 3.\n", ind->num_nodes);
fprintf(stderr, "ERROR: Index of type attributes contains %lu nodes; expected 3.\n", (unsigned long)ind->num_nodes);
mxmlIndexDelete(ind);
mxmlDelete(tree);
return (1);
@ -369,7 +369,7 @@ main(int argc, // I - Number of command-line args
if (ind->num_nodes != 3)
{
fprintf(stderr, "ERROR: Index of elements and attributes contains %d nodes; expected 3.\n", ind->num_nodes);
fprintf(stderr, "ERROR: Index of elements and attributes contains %lu nodes; expected 3.\n", (unsigned long)ind->num_nodes);
mxmlIndexDelete(ind);
mxmlDelete(tree);
return (1);
@ -602,6 +602,12 @@ main(int argc, // I - Number of command-line args
return (1);
}
if (event_counts[MXML_SAX_EVENT_DECLARATION] != 0)
{
fprintf(stderr, "MXML_SAX_EVENT_DECLARATION seen %d times, expected 0 times.\n", event_counts[MXML_SAX_EVENT_DECLARATION]);
return (1);
}
if (event_counts[MXML_SAX_EVENT_DIRECTIVE] != 1)
{
fprintf(stderr, "MXML_SAX_EVENT_DIRECTIVE seen %d times, expected 1 times.\n", event_counts[MXML_SAX_EVENT_DIRECTIVE]);
@ -657,7 +663,8 @@ sax_cb(mxml_node_t *node, // I - Current node
"MXML_SAX_EVENT_CDATA", // CDATA node
"MXML_SAX_EVENT_COMMENT", // Comment node
"MXML_SAX_EVENT_DATA", // Data node
"MXML_SAX_EVENT_DIRECTIVE", // Processing directive node
"MXML_SAX_EVENT_DECLARATION", // Declaration node
"MXML_SAX_EVENT_DIRECTIVE", // Processing directive node
"MXML_SAX_EVENT_ELEMENT_CLOSE", // Element closed
"MXML_SAX_EVENT_ELEMENT_OPEN" // Element opened
};
@ -716,7 +723,8 @@ whitespace_cb(mxml_node_t *node, // I - Element node
// We can conditionally break to a new line before or after any element.
// These are just common HTML elements...
name = node->value.element.name;
if ((name = mxmlGetElement(node)) == NULL)
name = "";
if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") || !strcmp(name, "pre") || !strcmp(name, "p") || !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") || !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6"))
{
@ -737,7 +745,7 @@ whitespace_cb(mxml_node_t *node, // I - Element node
else if (where == MXML_WS_AFTER_CLOSE)
return ("\n");
}
else if (!strncmp(name, "?xml", 4))
else if (mxmlGetType(node) == MXML_TYPE_DIRECTIVE)
{
if (where == MXML_WS_AFTER_OPEN)
return ("\n");
@ -757,7 +765,7 @@ whitespace_cb(mxml_node_t *node, // I - Element node
}
else if (where == MXML_WS_AFTER_CLOSE || ((!strcmp(name, "group") || !strcmp(name, "option") || !strcmp(name, "choice")) && where == MXML_WS_AFTER_OPEN))
return ("\n");
else if (where == MXML_WS_AFTER_OPEN && !node->child)
else if (where == MXML_WS_AFTER_OPEN && !mxmlGetFirstChild(node))
return ("\n");
// Return NULL for no added whitespace...