feat: Add mxml

I checked out the few xml implementations that
are available as pure C and this one seemed like
the best choice so far. It's fairly small regarding
foot print, similar API to the property (node) API,
and I could get it to work fairly easily with a few
sandbox examples thanks to decent documentation.

This serves as the base for a bemanitools property
(node) implementation for games that do not come
with AVS 2 and the property API.
This commit is contained in:
icex2 2024-08-15 11:34:31 +02:00
parent ee8f2a05fe
commit 45d7a29cba
14 changed files with 6412 additions and 0 deletions

View File

@ -85,6 +85,8 @@ avsvers_64 := 1700 1603 1601 1509 1508
imps += avs avs-ea3
include src/main/mxml/Module.mk
include src/main/aciodrv/Module.mk
include src/main/aciodrv-proc/Module.mk
include src/main/acioemu/Module.mk

14
src/main/mxml/Module.mk Normal file
View File

@ -0,0 +1,14 @@
libs += mxml
libs_mxml := \
src_mxml := \
mxml-get.c \
mxml-options.c \
mxml-search.c \
mxml-attr.c \
mxml-index.c \
mxml-private.c \
mxml-set.c \
mxml-file.c \
mxml-node.c \

49
src/main/mxml/config.h Normal file
View File

@ -0,0 +1,49 @@
/* config.h. Generated from config.h.in by configure. */
//
// Configuration file 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.
//
#ifndef MXML_CONFIG_H
# define MXML_CONFIG_H
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <stdarg.h>
# include <ctype.h>
//
// Version number
//
# define MXML_VERSION "Mini-XML v4.0.3"
//
// Inline function support
//
# define inline
//
// Long long support
//
# define HAVE_LONG_LONG_INT 1
//
// Have <pthread.h>?
//
// # define HAVE_PTHREAD_H 1
#endif // !MXML_CONFIG_H

271
src/main/mxml/mxml-attr.c Normal file
View File

@ -0,0 +1,271 @@
//
// Attribute 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"
//
// Local functions...
//
static bool mxml_set_attr(mxml_node_t *node, const char *name, char *value);
//
// 'mxmlElementClearAttr()' - Remove an attribute from an element.
//
// This function removes the attribute `name` from the element `node`.
//
void
mxmlElementClearAttr(mxml_node_t *node, // I - Element
const char *name) // I - Attribute name
{
size_t i; // Looping var
_mxml_attr_t *attr; // Cirrent attribute
MXML_DEBUG("mxmlElementClearAttr(node=%p, name=\"%s\")\n", node, name ? name : "(null)");
// Range check input...
if (!node || node->type != MXML_TYPE_ELEMENT || !name)
return;
// Look for the attribute...
for (i = node->value.element.num_attrs, attr = node->value.element.attrs; i > 0; i --, attr ++)
{
MXML_DEBUG("mxmlElementClearAttr: %s=\"%s\"\n", attr->name, attr->value);
if (!strcmp(attr->name, name))
{
// Delete this attribute...
_mxml_strfree(attr->name);
_mxml_strfree(attr->value);
i --;
if (i > 0)
memmove(attr, attr + 1, i * sizeof(_mxml_attr_t));
node->value.element.num_attrs --;
if (node->value.element.num_attrs == 0)
free(node->value.element.attrs);
return;
}
}
}
//
// 'mxmlElementGetAttr()' - Get the value of an attribute.
//
// This function gets the value for the attribute `name` from the element
// `node`. `NULL` is returned if the node is not an element or the named
// attribute does not exist.
//
const char * // O - Attribute value or `NULL`
mxmlElementGetAttr(mxml_node_t *node, // I - Element node
const char *name) // I - Name of attribute
{
size_t i; // Looping var
_mxml_attr_t *attr; // Cirrent attribute
MXML_DEBUG("mxmlElementGetAttr(node=%p, name=\"%s\")\n", node, name ? name : "(null)");
// Range check input...
if (!node || node->type != MXML_TYPE_ELEMENT || !name)
return (NULL);
// Look for the attribute...
for (i = node->value.element.num_attrs, attr = node->value.element.attrs; i > 0; i --, attr ++)
{
MXML_DEBUG("mxmlElementGetAttr: %s=\"%s\"\n", attr->name, attr->value);
if (!strcmp(attr->name, name))
{
MXML_DEBUG("mxmlElementGetAttr: Returning \"%s\".\n", attr->value);
return (attr->value);
}
}
// Didn't find attribute, so return NULL...
MXML_DEBUG("mxmlElementGetAttr: Returning NULL.\n");
return (NULL);
}
//
// 'mxmlElementGetAttrByIndex()' - Get an attribute by index.
//
// This function returned the Nth (`idx`) attribute for element `node`. The
// attribute name is optionallly returned in the `name` argument. `NULL` is
// returned if node is not an element or the specified index is out of range.
//
const char * // O - Attribute value
mxmlElementGetAttrByIndex(
mxml_node_t *node, // I - Node
size_t idx, // I - Attribute index, starting at `0`
const char **name) // O - Attribute name or `NULL` to not return it
{
if (!node || node->type != MXML_TYPE_ELEMENT || idx >= node->value.element.num_attrs)
return (NULL);
if (name)
*name = node->value.element.attrs[idx].name;
return (node->value.element.attrs[idx].value);
}
//
// 'mxmlElementGetAttrCount()' - Get the number of element attributes.
//
// This function returns the number of attributes for the element `node`. `0`
// is returned if the node is not an element or there are no attributes for the
// element.
//
size_t // O - Number of attributes
mxmlElementGetAttrCount(
mxml_node_t *node) // I - Node
{
if (node && node->type == MXML_TYPE_ELEMENT)
return (node->value.element.num_attrs);
else
return (0);
}
//
// 'mxmlElementSetAttr()' - Set an attribute for an element.
//
// This function sets attribute `name` to the string `value` for the element
// `node`. If the named attribute already exists, the value of the attribute
// is replaced by the new string value. The string value is copied.
//
void
mxmlElementSetAttr(mxml_node_t *node, // I - Element node
const char *name, // I - Name of attribute
const char *value) // I - Attribute value
{
char *valuec; // Copy of value
MXML_DEBUG("mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n", node, name ? name : "(null)", value ? value : "(null)");
// Range check input...
if (!node || node->type != MXML_TYPE_ELEMENT || !name)
return;
if (value)
{
if ((valuec = _mxml_strcopy(value)) == NULL)
return;
}
else
{
valuec = NULL;
}
if (!mxml_set_attr(node, name, valuec))
_mxml_strfree(valuec);
}
//
// 'mxmlElementSetAttrf()' - Set an attribute with a formatted value.
//
// This function sets attribute `name` to the formatted value of `format` for
// the element `node`. If the named attribute already exists, the value of the
// attribute is replaced by the new formatted string value.
//
void
mxmlElementSetAttrf(mxml_node_t *node, // I - Element node
const char *name, // I - Name of attribute
const char *format,// I - Printf-style attribute value
...) // I - Additional arguments as needed
{
va_list ap; // Argument pointer
char buffer[16384]; // Format buffer
char *value; // Value
MXML_DEBUG("mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n", node, name ? name : "(null)", format ? format : "(null)");
// Range check input...
if (!node || node->type != MXML_TYPE_ELEMENT || !name || !format)
return;
// Format the value...
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
if ((value = _mxml_strcopy(buffer)) != NULL)
{
if (!mxml_set_attr(node, name, value))
_mxml_strfree(value);
}
}
//
// 'mxml_set_attr()' - Set or add an attribute name/value pair.
//
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
{
int i; // Looping var
_mxml_attr_t *attr; // New attribute
// Look for the attribute...
for (i = node->value.element.num_attrs, attr = node->value.element.attrs; i > 0; i --, attr ++)
{
if (!strcmp(attr->name, name))
{
// Free the old value as needed...
_mxml_strfree(attr->value);
attr->value = value;
return (true);
}
}
// Add a new attribute...
if ((node->value.element.num_attrs % MXML_ALLOC_SIZE) == 0)
{
if ((attr = realloc(node->value.element.attrs, (node->value.element.num_attrs + MXML_ALLOC_SIZE) * sizeof(_mxml_attr_t))) == NULL)
return (false);
node->value.element.attrs = attr;
}
attr = node->value.element.attrs + node->value.element.num_attrs;
if ((attr->name = _mxml_strcopy(name)) == NULL)
return (false);
attr->value = value;
node->value.element.num_attrs ++;
return (true);
}

2275
src/main/mxml/mxml-file.c Normal file

File diff suppressed because it is too large Load Diff

364
src/main/mxml/mxml-get.c Normal file
View File

@ -0,0 +1,364 @@
//
// Node get functions for Mini-XML, a small XML file parsing library.
//
// https://www.msweet.org/mxml
//
// Copyright © 2014-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
#include "mxml-private.h"
//
// 'mxmlGetCDATA()' - Get the value for a CDATA node.
//
// This function gets the string value of a CDATA node. `NULL` is returned if
// the node is not a CDATA element.
//
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_CDATA)
return (NULL);
// Return the CDATA string...
return (node->value.cdata);
}
//
// 'mxmlGetComment()' - Get the value for a comment node.
//
// This function gets the string value of 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);
}
//
// 'mxmlGetCustom()' - Get the value for a custom node.
//
// This function gets the binary value of a custom node. `NULL` is returned if
// the node (or its first child) is not a custom value node.
//
const void * // O - Custom value or `NULL`
mxmlGetCustom(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node)
return (NULL);
// Return the custom value...
if (node->type == MXML_TYPE_CUSTOM)
return (node->value.custom.data);
else if (node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_CUSTOM)
return (node->child->value.custom.data);
else
return (NULL);
}
//
// 'mxmlGetDeclaration()' - Get the value for a declaration node.
//
// This function gets the string value of a declaraction 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.
//
// This function gets the string value of a processing instruction. `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.
//
// This function gets the name of an element node. `NULL` is returned if the
// node is not an element node.
//
const char * // O - Element name or `NULL`
mxmlGetElement(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node || node->type != MXML_TYPE_ELEMENT)
return (NULL);
// Return the element name...
return (node->value.element.name);
}
//
// 'mxmlGetFirstChild()' - Get the first child of a node.
//
// This function gets the first child of a node. `NULL` is returned if the node
// has no children.
//
mxml_node_t * // O - First child or `NULL`
mxmlGetFirstChild(mxml_node_t *node) // I - Node to get
{
// Return the first child node...
return (node ? node->child : NULL);
}
//
// 'mxmlGetInteger()' - Get the integer value from the specified node or its
// first child.
//
// This function gets the value of an integer node. `0` is returned if the node
// (or its first child) is not an integer value node.
//
long // O - Integer value or `0`
mxmlGetInteger(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node)
return (0);
// Return the integer value...
if (node->type == MXML_TYPE_INTEGER)
return (node->value.integer);
else if (node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_INTEGER)
return (node->child->value.integer);
else
return (0);
}
//
// 'mxmlGetLastChild()' - Get the last child of a node.
//
// This function gets the last child of a node. `NULL` is returned if the node
// has no children.
//
mxml_node_t * // O - Last child or `NULL`
mxmlGetLastChild(mxml_node_t *node) // I - Node to get
{
return (node ? node->last_child : NULL);
}
//
// 'mxmlGetNextSibling()' - Get the next node for the current parent.
//
// This function gets the next node for the current parent. `NULL` is returned
// if this is the last child for the current parent.
//
mxml_node_t *
mxmlGetNextSibling(mxml_node_t *node) // I - Node to get
{
return (node ? node->next : NULL);
}
//
// 'mxmlGetOpaque()' - Get an opaque string value for a node or its first child.
//
// This function gets the string value of an opaque node. `NULL` is returned if
// the node (or its first child) is not an opaque value node.
//
const char * // O - Opaque string or `NULL`
mxmlGetOpaque(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node)
return (NULL);
// Return the opaque value...
if (node->type == MXML_TYPE_OPAQUE)
return (node->value.opaque);
else if (node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_OPAQUE)
return (node->child->value.opaque);
else
return (NULL);
}
//
// 'mxmlGetParent()' - Get the parent node.
//
// This function gets the parent of a node. `NULL` is returned for a root node.
//
mxml_node_t * // O - Parent node or `NULL`
mxmlGetParent(mxml_node_t *node) // I - Node to get
{
return (node ? node->parent : NULL);
}
//
// 'mxmlGetPrevSibling()' - Get the previous node for the current parent.
//
// This function gets the previous node for the current parent. `NULL` is
// returned if this is the first child for the current parent.
//
mxml_node_t * // O - Previous node or `NULL`
mxmlGetPrevSibling(mxml_node_t *node) // I - Node to get
{
return (node ? node->prev : NULL);
}
//
// 'mxmlGetReal()' - Get the real value for a node or its first child.
//
// This function gets the value of a real value node. `0.0` is returned if the
// node (or its first child) is not a real value node.
//
double // O - Real value or 0.0
mxmlGetReal(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node)
return (0.0);
// Return the real value...
if (node->type == MXML_TYPE_REAL)
return (node->value.real);
else if (node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_REAL)
return (node->child->value.real);
else
return (0.0);
}
//
// 'mxmlGetText()' - Get the text value for a node or its first child.
//
// This function gets the string and whitespace values of a text node. `NULL`
// and `false` are returned if the node (or its first child) is not a text node.
// The `whitespace` argument can be `NULL` if you don't want to know the
// whitespace value.
//
// Note: Text nodes consist of whitespace-delimited words. You will only get
// single words of text when reading an XML file with `MXML_TYPE_TEXT` nodes.
// If you want the entire string between elements in the XML file, you MUST read
// the XML file with `MXML_TYPE_OPAQUE` nodes and get the resulting strings
// using the @link mxmlGetOpaque@ function instead.
//
const char * // O - Text string or `NULL`
mxmlGetText(mxml_node_t *node, // I - Node to get
bool *whitespace) // O - `true` if string is preceded by whitespace, `false` otherwise
{
// Range check input...
if (!node)
{
if (whitespace)
*whitespace = false;
return (NULL);
}
// Return the integer value...
if (node->type == MXML_TYPE_TEXT)
{
if (whitespace)
*whitespace = node->value.text.whitespace;
return (node->value.text.string);
}
else if (node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_TEXT)
{
if (whitespace)
*whitespace = node->child->value.text.whitespace;
return (node->child->value.text.string);
}
else
{
if (whitespace)
*whitespace = false;
return (NULL);
}
}
//
// 'mxmlGetType()' - Get the node type.
//
// This function gets the type of `node`. `MXML_TYPE_IGNORE` is returned if
// `node` is `NULL`.
//
mxml_type_t // O - Type of node
mxmlGetType(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node)
return (MXML_TYPE_IGNORE);
// Return the node type...
return (node->type);
}
//
// 'mxmlGetUserData()' - Get the user data pointer for a node.
//
// This function gets the user data pointer associated with `node`.
//
void * // O - User data pointer
mxmlGetUserData(mxml_node_t *node) // I - Node to get
{
// Range check input...
if (!node)
return (NULL);
// Return the user data pointer...
return (node->user_data);
}

418
src/main/mxml/mxml-index.c Normal file
View File

@ -0,0 +1,418 @@
//
// Index 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"
//
// Local functions...
//
static int index_compare(mxml_index_t *ind, mxml_node_t *first, mxml_node_t *second);
static int index_find(mxml_index_t *ind, const char *element, const char *value, mxml_node_t *node);
static void index_sort(mxml_index_t *ind, int left, int right);
//
// 'mxmlIndexDelete()' - Delete an index.
//
void
mxmlIndexDelete(mxml_index_t *ind) // I - Index to delete
{
// Range check input..
if (!ind)
return;
// Free memory...
_mxml_strfree(ind->attr);
free(ind->nodes);
free(ind);
}
//
// 'mxmlIndexEnum()' - Return the next node in the index.
//
// This function returns the next node in index `ind`.
//
// You should call @link mxmlIndexReset@ prior to using this function to get
// the first node in the index. Nodes are returned in the sorted order of the
// index.
//
mxml_node_t * // O - Next node or `NULL` if there is none
mxmlIndexEnum(mxml_index_t *ind) // I - Index to enumerate
{
// Range check input...
if (!ind)
return (NULL);
// Return the next node...
if (ind->cur_node < ind->num_nodes)
return (ind->nodes[ind->cur_node ++]);
else
return (NULL);
}
//
// 'mxmlIndexFind()' - Find the next matching node.
//
// This function finds the next matching node in index `ind`.
//
// You should call @link mxmlIndexReset@ prior to using this function for
// the first time with a particular set of `element` and `value`
// strings. Passing `NULL` for both `element` and `value` is equivalent
// to calling @link mxmlIndexEnum@.
//
mxml_node_t * // O - Node or `NULL` if none found
mxmlIndexFind(mxml_index_t *ind, // I - Index to search
const char *element, // I - Element name to find, if any
const char *value) // I - Attribute value, if any
{
int diff, // Difference between names
current, // Current entity in search
first, // First entity in search
last; // Last entity in search
MXML_DEBUG("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n", ind, element ? element : "(null)", value ? value : "(null)");
// Range check input...
if (!ind || (!ind->attr && value))
{
MXML_DEBUG("mxmlIndexFind: Returning NULL, ind->attr=\"%s\"...\n", ind && ind->attr ? ind->attr : "(null)");
return (NULL);
}
// If both element and value are NULL, just enumerate the nodes in the index...
if (!element && !value)
return (mxmlIndexEnum(ind));
// If there are no nodes in the index, return NULL...
if (!ind->num_nodes)
{
MXML_DEBUG("mxmlIndexFind: Returning NULL, no nodes...\n");
return (NULL);
}
// If cur_node == 0, then find the first matching node...
if (ind->cur_node == 0)
{
// Find the first node using a modified binary search algorithm...
first = 0;
last = ind->num_nodes - 1;
MXML_DEBUG("mxmlIndexFind: Find first time, num_nodes=%lu...\n", (unsigned long)ind->num_nodes);
while ((last - first) > 1)
{
current = (first + last) / 2;
MXML_DEBUG("mxmlIndexFind: first=%d, last=%d, current=%d\n", first, last, current);
if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0)
{
// Found a match, move back to find the first...
MXML_DEBUG("mxmlIndexFind: match.\n");
while (current > 0 && !index_find(ind, element, value, ind->nodes[current - 1]))
current --;
MXML_DEBUG("mxmlIndexFind: Returning first match=%d\n", current);
// Return the first match and save the index to the next...
ind->cur_node = current + 1;
return (ind->nodes[current]);
}
else if (diff < 0)
{
last = current;
}
else
{
first = current;
}
MXML_DEBUG("mxmlIndexFind: diff=%d\n", diff);
}
// If we get this far, then we found exactly 0 or 1 matches...
for (current = first; current <= last; current ++)
{
if (!index_find(ind, element, value, ind->nodes[current]))
{
// Found exactly one (or possibly two) match...
MXML_DEBUG("mxmlIndexFind: Returning only match %d...\n", current);
ind->cur_node = current + 1;
return (ind->nodes[current]);
}
}
// No matches...
ind->cur_node = ind->num_nodes;
MXML_DEBUG("mxmlIndexFind: Returning NULL...\n");
return (NULL);
}
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 %lu...\n", (unsigned long)ind->cur_node);
return (ind->nodes[ind->cur_node ++]);
}
// If we get this far, then we have no matches...
ind->cur_node = ind->num_nodes;
MXML_DEBUG("mxmlIndexFind: Returning NULL...\n");
return (NULL);
}
//
// 'mxmlIndexGetCount()' - Get the number of nodes in an index.
//
size_t // I - Number of nodes in index
mxmlIndexGetCount(mxml_index_t *ind) // I - Index of nodes
{
// Range check input...
if (!ind)
return (0);
// Return the number of nodes in the index...
return (ind->num_nodes);
}
//
// 'mxmlIndexNew()' - Create a new index.
//
// This function creates a new index for XML tree `node`.
//
// The index will contain all nodes that contain the named element and/or
// attribute. If both `element` and `attr` are `NULL`, then the index will
// contain a sorted list of the elements in the node tree. Nodes are
// sorted by element name and optionally by attribute value if the `attr`
// argument is not `NULL`.
//
mxml_index_t * // O - New index
mxmlIndexNew(mxml_node_t *node, // I - XML node tree
const char *element, // I - Element to index or `NULL` for all
const char *attr) // I - Attribute to index or `NULL` for none
{
mxml_index_t *ind; // New index
mxml_node_t *current, // Current node in index
**temp; // Temporary node pointer array
// Range check input...
MXML_DEBUG("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n", node, element ? element : "(null)", attr ? attr : "(null)");
if (!node)
return (NULL);
// Create a new index...
if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL)
return (NULL);
if (attr)
{
if ((ind->attr = _mxml_strcopy(attr)) == NULL)
{
free(ind);
return (NULL);
}
}
if (!element && !attr)
current = node;
else
current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND_ALL);
while (current)
{
if (ind->num_nodes >= ind->alloc_nodes)
{
if ((temp = realloc(ind->nodes, (ind->alloc_nodes + MXML_ALLOC_SIZE) * sizeof(mxml_node_t *))) == NULL)
{
// Unable to allocate memory for the index, so abort...
mxmlIndexDelete(ind);
return (NULL);
}
ind->nodes = temp;
ind->alloc_nodes += MXML_ALLOC_SIZE;
}
ind->nodes[ind->num_nodes ++] = current;
current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND_ALL);
}
// Sort nodes based upon the search criteria...
if (ind->num_nodes > 1)
index_sort(ind, 0, ind->num_nodes - 1);
// Return the new index...
return (ind);
}
//
// 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and
// return the first node in the index.
//
// This function resets the enumeration/find pointer in index `ind` and should
// be called prior to using @link mxmlIndexEnum@ or @link mxmlIndexFind@ for the
// first time.
//
mxml_node_t * // O - First node or `NULL` if there is none
mxmlIndexReset(mxml_index_t *ind) // I - Index to reset
{
MXML_DEBUG("mxmlIndexReset(ind=%p)\n", ind);
// Range check input...
if (!ind)
return (NULL);
// Set the index to the first element...
ind->cur_node = 0;
// Return the first node...
if (ind->num_nodes)
return (ind->nodes[0]);
else
return (NULL);
}
//
// 'index_compare()' - Compare two nodes.
//
static int // O - Result of comparison
index_compare(mxml_index_t *ind, // I - Index
mxml_node_t *first, // I - First node
mxml_node_t *second) // I - Second node
{
int diff; // Difference
// Check the element name...
if ((diff = strcmp(first->value.element.name, second->value.element.name)) != 0)
return (diff);
// Check the attribute value...
if (ind->attr)
{
if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr), mxmlElementGetAttr(second, ind->attr))) != 0)
return (diff);
}
// No difference, return 0...
return (0);
}
//
// 'index_find()' - Compare a node with index values.
//
static int // O - Result of comparison
index_find(mxml_index_t *ind, // I - Index
const char *element, // I - Element name or `NULL`
const char *value, // I - Attribute value or `NULL`
mxml_node_t *node) // I - Node
{
int diff; // Difference
// Check the element name...
if (element)
{
if ((diff = strcmp(element, node->value.element.name)) != 0)
return (diff);
}
// Check the attribute value...
if (value)
{
if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0)
return (diff);
}
// No difference, return 0...
return (0);
}
//
// 'index_sort()' - Sort the nodes in the index...
//
// This function implements the classic quicksort algorithm...
//
static void
index_sort(mxml_index_t *ind, // I - Index to sort
int left, // I - Left node in partition
int right) // I - Right node in partition
{
mxml_node_t *pivot, // Pivot node
*temp; // Swap node
int templ, // Temporary left node
tempr; // Temporary right node
// Loop until we have sorted all the way to the right...
do
{
// Sort the pivot in the current partition...
pivot = ind->nodes[left];
for (templ = left, tempr = right; templ < tempr;)
{
// Move left while left node <= pivot node...
while ((templ < right) && index_compare(ind, ind->nodes[templ], pivot) <= 0)
templ ++;
// Move right while right node > pivot node...
while ((tempr > left) && index_compare(ind, ind->nodes[tempr], pivot) > 0)
tempr --;
// Swap nodes if needed...
if (templ < tempr)
{
temp = ind->nodes[templ];
ind->nodes[templ] = ind->nodes[tempr];
ind->nodes[tempr] = temp;
}
}
// When we get here, the right (tempr) node is the new position for the pivot node...
if (index_compare(ind, pivot, ind->nodes[tempr]) > 0)
{
ind->nodes[left] = ind->nodes[tempr];
ind->nodes[tempr] = pivot;
}
// Recursively sort the left partition as needed...
if (left < (tempr - 1))
index_sort(ind, left, tempr - 1);
}
while (right > (left = tempr + 1));
}

937
src/main/mxml/mxml-node.c Normal file
View File

@ -0,0 +1,937 @@
//
// Node 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"
//
// Local functions...
//
static void mxml_free(mxml_node_t *node);
static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type);
//
// 'mxmlAdd()' - Add a node to a tree.
//
// This function adds the specified node `node` to the parent. If the `child`
// argument is not `NULL`, the new node is added before or after the specified
// child depending on the value of the `add` argument. If the `child` argument
// is `NULL`, the new node is placed at the beginning of the child list
// (`MXML_ADD_BEFORE`) or at the end of the child list (`MXML_ADD_AFTER`).
//
void
mxmlAdd(mxml_node_t *parent, // I - Parent node
mxml_add_t add, // I - Where to add, `MXML_ADD_BEFORE` or `MXML_ADD_AFTER`
mxml_node_t *child, // I - Child node for where or `MXML_ADD_TO_PARENT`
mxml_node_t *node) // I - Node to add
{
MXML_DEBUG("mxmlAdd(parent=%p, add=%d, child=%p, node=%p)\n", parent, add, child, node);
// Range check input...
if (!parent || !node)
return;
// Remove the node from any existing parent...
if (node->parent)
mxmlRemove(node);
// Reset pointers...
node->parent = parent;
switch (add)
{
case MXML_ADD_BEFORE :
if (!child || child == parent->child || child->parent != parent)
{
// Insert as first node under parent...
node->next = parent->child;
if (parent->child)
parent->child->prev = node;
else
parent->last_child = node;
parent->child = node;
}
else
{
// Insert node before this child...
node->next = child;
node->prev = child->prev;
if (child->prev)
child->prev->next = node;
else
parent->child = node;
child->prev = node;
}
break;
case MXML_ADD_AFTER :
if (!child || child == parent->last_child || child->parent != parent)
{
// Insert as last node under parent...
node->parent = parent;
node->prev = parent->last_child;
if (parent->last_child)
parent->last_child->next = node;
else
parent->child = node;
parent->last_child = node;
}
else
{
// Insert node after this child...
node->prev = child;
node->next = child->next;
if (child->next)
child->next->prev = node;
else
parent->last_child = node;
child->next = node;
}
break;
}
}
//
// 'mxmlDelete()' - Delete a node and all of its children.
//
// This function deletes the node `node` and all of its children. If the
// specified node has a parent, this function first removes the node from its
// parent using the @link mxmlRemove@ function.
//
void
mxmlDelete(mxml_node_t *node) // I - Node to delete
{
mxml_node_t *current, // Current node
*next; // Next node
MXML_DEBUG("mxmlDelete(node=%p)\n", node);
// Range check input...
if (!node)
return;
// Remove the node from its parent, if any...
mxmlRemove(node);
// Delete children...
for (current = node->child; current; current = next)
{
// Get the next node...
if ((next = current->child) != NULL)
{
// Free parent nodes after child nodes have been freed...
current->child = NULL;
continue;
}
if ((next = current->next) == NULL)
{
// Next node is the parent, which we'll free as needed...
if ((next = current->parent) == node)
next = NULL;
}
// Free child...
mxml_free(current);
}
// Then free the memory used by the parent node...
mxml_free(node);
}
//
// 'mxmlGetRefCount()' - Get the current reference (use) count for a node.
//
// The initial reference count of new nodes is 1. Use the @link mxmlRetain@
// and @link mxmlRelease@ functions to increment and decrement a node's
// reference count.
//
size_t // O - Reference count
mxmlGetRefCount(mxml_node_t *node) // I - Node
{
// Range check input...
if (!node)
return (0);
// Return the reference count...
return (node->ref_count);
}
//
// 'mxmlNewCDATA()' - Create a new 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
// CDATA node has no parent. The data string must be nul-terminated and
// is copied into the new node. CDATA nodes currently use the
// `MXML_TYPE_ELEMENT` type.
//
mxml_node_t * // O - New node
mxmlNewCDATA(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *data) // I - Data string
{
mxml_node_t *node; // New node
MXML_DEBUG("mxmlNewCDATA(parent=%p, data=\"%s\")\n", parent, data ? data : "(null)");
// Range check input...
if (!data)
return (NULL);
// Create the node and set the name value...
if ((node = mxml_new(parent, MXML_TYPE_CDATA)) != NULL)
{
if ((node->value.cdata = _mxml_strcopy(data)) == NULL)
{
mxmlDelete(node);
return (NULL);
}
}
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 = _mxml_strcopy(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 = _mxml_strcopy(comment)) == NULL)
{
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 = _mxml_strcopy(buffer);
}
return (node);
}
//
// 'mxmlNewCustom()' - Create a new custom data node.
//
// The new custom node is added to the end of the specified parent's child
// 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_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, 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.free_cb = free_cb;
node->value.custom.free_cbdata = free_cbdata;
}
return (node);
}
//
// '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 = _mxml_strcopy(declaration)) == NULL)
{
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 = _mxml_strcopy(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 = _mxml_strcopy(directive)) == NULL)
{
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 = _mxml_strcopy(buffer);
}
return (node);
}
//
// 'mxmlNewElement()' - Create a new element node.
//
// The new element 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.
//
mxml_node_t * // O - New node
mxmlNewElement(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *name) // I - Name of element
{
mxml_node_t *node; // New node
MXML_DEBUG("mxmlNewElement(parent=%p, name=\"%s\")\n", parent, name ? name : "(null)");
// Range check input...
if (!name)
return (NULL);
// Create the node and set the element name...
if ((node = mxml_new(parent, MXML_TYPE_ELEMENT)) != NULL)
node->value.element.name = _mxml_strcopy(name);
return (node);
}
//
// 'mxmlNewInteger()' - Create a new integer node.
//
// The new integer 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
// integer node has no parent.
//
mxml_node_t * // O - New node
mxmlNewInteger(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
long integer) // I - Integer value
{
mxml_node_t *node; // New node
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)
node->value.integer = integer;
return (node);
}
//
// 'mxmlNewOpaque()' - Create a new opaque string.
//
// The new opaque 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 opaque string must be nul-
// terminated and is copied into the new node.
//
mxml_node_t * // O - New node
mxmlNewOpaque(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
const char *opaque) // I - Opaque string
{
mxml_node_t *node; // New node
MXML_DEBUG("mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent, opaque ? opaque : "(null)");
// Range check input...
if (!opaque)
return (NULL);
// Create the node and set the element name...
if ((node = mxml_new(parent, MXML_TYPE_OPAQUE)) != NULL)
node->value.opaque = _mxml_strcopy(opaque);
return (node);
}
//
// 'mxmlNewOpaquef()' - Create a new formatted opaque string node.
//
// The new opaque 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
mxmlNewOpaquef(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("mxmlNewOpaquef(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_OPAQUE)) != NULL)
{
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
node->value.opaque = _mxml_strcopy(buffer);
}
return (node);
}
//
// 'mxmlNewReal()' - Create a new real number node.
//
// The new real number 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 real number node has no parent.
//
mxml_node_t * // O - New node
mxmlNewReal(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
double real) // I - Real number value
{
mxml_node_t *node; // New node
MXML_DEBUG("mxmlNewReal(parent=%p, real=%g)\n", parent, real);
// Create the node and set the element name...
if ((node = mxml_new(parent, MXML_TYPE_REAL)) != NULL)
node->value.real = real;
return (node);
}
//
// 'mxmlNewText()' - Create a new text fragment node.
//
// The new text 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
// text node has no parent. The whitespace parameter is used to specify
// whether leading whitespace is present before the node. The text
// string must be nul-terminated and is copied into the new node.
//
mxml_node_t * // O - New node
mxmlNewText(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
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=%s, string=\"%s\")\n", parent, whitespace ? "true" : "false", string ? string : "(null)");
// Range check input...
if (!string)
return (NULL);
// Create the node and set the text value...
if ((node = mxml_new(parent, MXML_TYPE_TEXT)) != NULL)
{
node->value.text.whitespace = whitespace;
node->value.text.string = _mxml_strcopy(string);
}
return (node);
}
//
// 'mxmlNewTextf()' - Create a new formatted text fragment node.
//
// The new text 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
// text node has no parent. The whitespace parameter is used to specify
// whether leading whitespace is present before the node. The format
// string must be nul-terminated and is formatted into the new node.
//
mxml_node_t * // O - New node
mxmlNewTextf(mxml_node_t *parent, // I - Parent node or `MXML_NO_PARENT`
bool whitespace, // I - `true` = leading whitespace, `false` = no whitespace
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("mxmlNewTextf(parent=%p, whitespace=%s, format=\"%s\", ...)\n", parent, whitespace ? "true" : "false", 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_TEXT)) != NULL)
{
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
node->value.text.whitespace = whitespace;
node->value.text.string = _mxml_strcopy(buffer);
}
return (node);
}
//
// 'mxmlRemove()' - Remove a node from its parent.
//
// This function does not free memory used by the node - use @link mxmlDelete@
// for that. This function does nothing if the node has no parent.
//
void
mxmlRemove(mxml_node_t *node) // I - Node to remove
{
MXML_DEBUG("mxmlRemove(node=%p)\n", node);
// Range check input...
if (!node || !node->parent)
return;
// Remove from parent...
if (node->prev)
node->prev->next = node->next;
else
node->parent->child = node->next;
if (node->next)
node->next->prev = node->prev;
else
node->parent->last_child = node->prev;
node->parent = NULL;
node->prev = NULL;
node->next = NULL;
}
//
// 'mxmlNewXML()' - Create a new XML document tree.
//
// The "version" argument specifies the version number to put in the
// ?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 directive[1024]; // Directive text
snprintf(directive, sizeof(directive), "xml version=\"%s\" encoding=\"utf-8\"", version ? version : "1.0");
return (mxmlNewDirective(NULL, directive));
}
//
// 'mxmlRelease()' - Release a node.
//
// When the reference count reaches zero, the node (and any children)
// is deleted via @link mxmlDelete@.
//
int // O - New reference count
mxmlRelease(mxml_node_t *node) // I - Node
{
if (node)
{
if ((-- node->ref_count) <= 0)
{
mxmlDelete(node);
return (0);
}
else
{
return (node->ref_count);
}
}
else
{
return (-1);
}
}
//
// 'mxmlRetain()' - Retain a node.
//
int // O - New reference count
mxmlRetain(mxml_node_t *node) // I - Node
{
if (node)
return (++ node->ref_count);
else
return (-1);
}
//
// 'mxml_free()' - Free the memory used by a node.
//
// Note: Does not free child nodes, does not remove from parent.
//
static void
mxml_free(mxml_node_t *node) // I - Node
{
size_t i; // Looping var
switch (node->type)
{
case MXML_TYPE_CDATA :
_mxml_strfree(node->value.cdata);
break;
case MXML_TYPE_COMMENT :
_mxml_strfree(node->value.comment);
break;
case MXML_TYPE_DECLARATION :
_mxml_strfree(node->value.declaration);
break;
case MXML_TYPE_DIRECTIVE :
_mxml_strfree(node->value.directive);
break;
case MXML_TYPE_ELEMENT :
_mxml_strfree(node->value.element.name);
if (node->value.element.num_attrs)
{
for (i = 0; i < node->value.element.num_attrs; i ++)
{
_mxml_strfree(node->value.element.attrs[i].name);
_mxml_strfree(node->value.element.attrs[i].value);
}
free(node->value.element.attrs);
}
break;
case MXML_TYPE_INTEGER :
// Nothing to do
break;
case MXML_TYPE_OPAQUE :
_mxml_strfree(node->value.opaque);
break;
case MXML_TYPE_REAL :
// Nothing to do
break;
case MXML_TYPE_TEXT :
_mxml_strfree(node->value.text.string);
break;
case MXML_TYPE_CUSTOM :
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;
}
// Free this node...
free(node);
}
//
// 'mxml_new()' - Create a new node.
//
static mxml_node_t * // O - New node
mxml_new(mxml_node_t *parent, // I - Parent node
mxml_type_t type) // I - Node type
{
mxml_node_t *node; // New node
MXML_DEBUG("mxml_new(parent=%p, type=%d)\n", parent, type);
// Allocate memory for the node...
if ((node = calloc(1, sizeof(mxml_node_t))) == NULL)
{
MXML_DEBUG("mxml_new: Returning NULL\n");
return (NULL);
}
MXML_DEBUG("mxml_new: Returning %p\n", node);
// Set the node type...
node->type = type;
node->ref_count = 1;
// Add to the parent if present...
if (parent)
mxmlAdd(parent, MXML_ADD_AFTER, /*child*/NULL, node);
// Return the new node...
return (node);
}

View File

@ -0,0 +1,545 @@
//
// 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.
//
// This function creates a new set of load/save options to use with the
// @link mxmlLoadFd@, @link mxmlLoadFile@, @link mxmlLoadFilename@,
// @link mxmlLoadIO@, @link mxmlLoadString@, @link mxmlSaveAllocString@,
// @link mxmlSaveFd@, @link mxmlSaveFile@, @link mxmlSaveFilename@,
// @link mxmlSaveIO@, and @link mxmlSaveString@ functions. Options can be
// reused for multiple calls to these functions and should be freed using the
// @link mxmlOptionsDelete@ function.
//
// The default load/save options load values using the constant type
// `MXML_TYPE_TEXT` and save XML data with a wrap margin of 72 columns.
// The various `mxmlOptionsSet` functions are used to change the defaults,
// for example:
//
// ```c
// mxml_options_t *options = mxmlOptionsNew();
//
// /* Load values as opaque strings */
// mxmlOptionsSetTypeValue(options, MXML_TYPE_OPAQUE);
// ```
//
// Note: The most common programming error when using the Mini-XML library is
// to load an XML file using the `MXML_TYPE_TEXT` node type, which returns
// inline text as a series of whitespace-delimited words, instead of using the
// `MXML_TYPE_OPAQUE` node type which returns the inline text as a single string
// (including whitespace).
//
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 automatically 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 \(`</element>`)
// - `MXML_SAX_EVENT_ELEMENT_OPEN` - An open element was just read \(`<element>`)
//
// 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 ("&amp;");
case '<' :
return ("&lt;");
case '>' :
return ("&gt;");
case '\"' :
return ("&quot;");
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);
}

View File

@ -0,0 +1,280 @@
//
// Private 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"
//
// Some crazy people think that unloading a shared object is a good or safe
// thing to do. Unfortunately, most objects are simply *not* safe to unload
// and bad things *will* happen.
//
// The following mess of conditional code allows us to provide a destructor
// function in Mini-XML for our thread-global storage so that it can possibly
// be unloaded safely, although since there is no standard way to do so I
// can't even provide any guarantees that you can do it safely on all platforms.
//
// This code currently supports AIX, HP-UX, Linux, macOS, Solaris, and
// Windows. It might work on the BSDs and IRIX, but I haven't tested that.
//
#if defined(__sun) || defined(_AIX)
# pragma fini(_mxml_fini)
# define _MXML_FINI _mxml_fini
#elif defined(__hpux)
# pragma FINI _mxml_fini
# define _MXML_FINI _mxml_fini
#elif defined(__GNUC__) // Linux and macOS
# define _MXML_FINI __attribute((destructor)) _mxml_fini
#else
# define _MXML_FINI _fini
#endif // __sun
//
// 'mxmlSetStringCallbacks()' - Set the string copy/free callback functions.
//
// This function sets the string copy/free callback functions for the current
// thread. The `strcopy_cb` function makes a copy of the provided string while
// the `strfree_cb` function frees the copy. Each callback accepts the
// `str_cbdata` pointer along with the pointer to the string:
//
// ```c
// char *my_strcopy_cb(void *cbdata, const char *s)
// {
// ... make a copy of "s" ...
// }
//
// void my_strfree_cb(void *cbdata, char *s)
// {
// ... release the memory used by "s" ...
// }
// ```
//
// The default `strcopy_cb` function calls `strdup` while the default
// `strfree_cb` function calls `free`.
//
void
mxmlSetStringCallbacks(
mxml_strcopy_cb_t strcopy_cb, // I - String copy callback function
mxml_strfree_cb_t strfree_cb, // I - String free callback function
void *str_cbdata) // I - String callback data
{
_mxml_global_t *global = _mxml_global();
// Global data
global->strcopy_cb = strcopy_cb;
global->strfree_cb = strfree_cb;
global->str_cbdata = str_cbdata;
}
//
// '_mxml_strcopy()' - Copy a string.
//
char * // O - Copy of string
_mxml_strcopy(const char *s) // I - String
{
_mxml_global_t *global = _mxml_global();
// Global data
if (!s)
return (NULL);
if (global->strcopy_cb)
return ((global->strcopy_cb)(global->str_cbdata, s));
else
return (strdup(s));
}
//
// '_mxml_strfree()' - Free a string.
//
void
_mxml_strfree(char *s) // I - String
{
_mxml_global_t *global = _mxml_global();
// Global data
if (!s)
return;
if (global->strfree_cb)
(global->strfree_cb)(global->str_cbdata, s);
else
free((void *)s);
}
#ifdef HAVE_PTHREAD_H // POSIX threading
# include <pthread.h>
static int _mxml_initialized = 0;
// Have we been initialized?
static pthread_key_t _mxml_key; // Thread local storage key
static pthread_once_t _mxml_key_once = PTHREAD_ONCE_INIT;
// One-time initialization object
static void _mxml_init(void);
static void _mxml_destructor(void *g);
//
// '_mxml_destructor()' - Free memory used for globals...
//
static void
_mxml_destructor(void *g) // I - Global data
{
free(g);
}
//
// '_mxml_fini()' - Clean up when unloaded.
//
static void
_MXML_FINI(void)
{
if (_mxml_initialized)
pthread_key_delete(_mxml_key);
}
//
// '_mxml_global()' - Get global data.
//
_mxml_global_t * // O - Global data
_mxml_global(void)
{
_mxml_global_t *global; // Global data
pthread_once(&_mxml_key_once, _mxml_init);
if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) == NULL)
{
global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t));
pthread_setspecific(_mxml_key, global);
}
return (global);
}
//
// '_mxml_init()' - Initialize global data...
//
static void
_mxml_init(void)
{
_mxml_initialized = 1;
pthread_key_create(&_mxml_key, _mxml_destructor);
}
#elif defined(_WIN32) && defined(MXML1_EXPORTS) // WIN32 threading
# include <windows.h>
static DWORD _mxml_tls_index; // Index for global storage
//
// 'DllMain()' - Main entry for library.
//
BOOL WINAPI // O - Success/failure
DllMain(HINSTANCE hinst, // I - DLL module handle
DWORD reason, // I - Reason
LPVOID reserved) // I - Unused
{
_mxml_global_t *global; // Global data
(void)hinst;
(void)reserved;
switch (reason)
{
case DLL_PROCESS_ATTACH : // Called on library initialization
if ((_mxml_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES)
return (FALSE);
break;
case DLL_THREAD_DETACH : // Called when a thread terminates
if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL)
free(global);
break;
case DLL_PROCESS_DETACH : // Called when library is unloaded
if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL)
free(global);
TlsFree(_mxml_tls_index);
break;
default:
break;
}
return (TRUE);
}
//
// '_mxml_global()' - Get global data.
//
_mxml_global_t * // O - Global data
_mxml_global(void)
{
_mxml_global_t *global; // Global data
if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) == NULL)
{
global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t));
TlsSetValue(_mxml_tls_index, (LPVOID)global);
}
return (global);
}
#else // No threading
//
// '_mxml_global()' - Get global data.
//
_mxml_global_t * // O - Global data
_mxml_global(void)
{
static _mxml_global_t global = // Global data
{
NULL, // strcopy_cb
NULL, // strfree_cb
NULL, // str_cbdata
};
return (&global);
}
#endif // HAVE_PTHREAD_H

View File

@ -0,0 +1,140 @@
//
// Private definitions 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.
//
#ifndef MXML_PRIVATE_H
# define MXML_PRIVATE_H
# include "config.h"
# include "mxml.h"
# include <locale.h>
//
// Private macros...
//
# ifdef DEBUG
# define MXML_DEBUG(...) fprintf(stderr, __VA_ARGS__)
# else
# define MXML_DEBUG(...)
# endif // DEBUG
# ifndef MXML_ALLOC_SIZE
# define MXML_ALLOC_SIZE 16 // Allocation increment
# endif // !MXML_ALLOC_SIZE
# define MXML_TAB 8 // Tabs every N columns
//
// Private structures...
//
typedef struct _mxml_attr_s // An XML element attribute value.
{
char *name; // Attribute name
char *value; // Attribute value
} _mxml_attr_t;
typedef struct _mxml_element_s // An XML element value.
{
char *name; // Name of element
size_t num_attrs; // Number of attributes
_mxml_attr_t *attrs; // Attributes
} _mxml_element_t;
typedef struct _mxml_text_s // An XML text value.
{
bool whitespace; // Leading whitespace?
char *string; // Fragment string
} _mxml_text_t;
typedef struct _mxml_custom_s // An XML custom value.
{
void *data; // Pointer to (allocated) custom data
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.
{
char *cdata; // CDATA string
char *comment; // Common string
char *declaration; // Declaration string
char *directive; // Processing instruction string
_mxml_element_t element; // Element
long integer; // Integer number
char *opaque; // Opaque string
double real; // Real number
_mxml_text_t text; // Text fragment
_mxml_custom_t custom; // Custom data
} _mxml_value_t;
struct _mxml_node_s // An XML node.
{
mxml_type_t type; // Node type
struct _mxml_node_s *next; // Next node under same parent
struct _mxml_node_s *prev; // Previous node under same parent
struct _mxml_node_s *parent; // Parent node
struct _mxml_node_s *child; // First child node
struct _mxml_node_s *last_child; // Last child node
_mxml_value_t value; // Node value
size_t ref_count; // Use count
void *user_data; // User data
};
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
size_t alloc_nodes; // Allocated nodes in index
size_t cur_node; // Current node
mxml_node_t **nodes; // Node array
};
struct _mxml_options_s // XML options
{
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
};
//
// Private functions...
//
extern _mxml_global_t *_mxml_global(void);
extern const char *_mxml_entity_string(int ch);
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);
#endif // !MXML_PRIVATE_H

237
src/main/mxml/mxml-search.c Normal file
View File

@ -0,0 +1,237 @@
//
// Search/navigation 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"
//
// 'mxmlFindElement()' - Find the named element.
//
// This function finds the named element `element` in XML tree `top` starting at
// node `node`. The search is constrained by element name `element`, attribute
// name `attr`, and attribute value `value` - `NULL` names or values are treated
// as wildcards, so different kinds of searches can be implemented by looking
// for all elements of a given name or all elements with a specific attribute.
//
// The `descend` argument determines whether the search descends into child
// nodes; normally you will use `MXML_DESCEND_FIRST` for the initial search and
// `MXML_DESCEND_NONE` to find additional direct descendents of the node.
//
mxml_node_t * // O - Element node or `NULL`
mxmlFindElement(mxml_node_t *node, // I - Current node
mxml_node_t *top, // I - Top node
const char *element,// I - Element name or `NULL` for any
const char *attr, // I - Attribute name, or `NULL` for none
const char *value, // I - Attribute value, or `NULL` for any
mxml_descend_t descend) // I - Descend into tree - `MXML_DESCEND_ALL`, `MXML_DESCEND_NONE`, or `MXML_DESCEND_FIRST`
{
const char *temp; // Current attribute value
// Range check input...
if (!node || !top || (!attr && value))
return (NULL);
// Start with the next node...
node = mxmlWalkNext(node, top, descend);
// Loop until we find a matching element...
while (node != NULL)
{
// See if this node matches...
if (node->type == MXML_TYPE_ELEMENT && node->value.element.name && (!element || !strcmp(node->value.element.name, element)))
{
// See if we need to check for an attribute...
if (!attr)
return (node); // No attribute search, return it...
// Check for the attribute...
if ((temp = mxmlElementGetAttr(node, attr)) != NULL)
{
// OK, we have the attribute, does it match?
if (!value || !strcmp(value, temp))
return (node); // Yes, return it...
}
}
// No match, move on to the next node...
if (descend == MXML_DESCEND_ALL)
node = mxmlWalkNext(node, top, MXML_DESCEND_ALL);
else
node = node->next;
}
return (NULL);
}
//
// 'mxmlFindPath()' - Find a node with the given path.
//
// This function finds a node in XML tree `top` using a slash-separated list of
// element names in `path`. The name "*" is considered a wildcard for one or
// more levels of elements, for example, "foo/one/two", "bar/two/one", "*\/one",
// and so forth.
//
// The first child node of the found node is returned if the given node has
// children and the first child is a value node.
//
mxml_node_t * // O - Found node or `NULL`
mxmlFindPath(mxml_node_t *top, // I - Top node
const char *path) // I - Path to element
{
mxml_node_t *node; // Current node
char element[256]; // Current element name
const char *pathsep; // Separator in path
mxml_descend_t descend; // mxmlFindElement option
// Range check input...
if (!top || !path || !*path)
return (NULL);
// Search each element in the path...
node = top;
while (*path)
{
// Handle wildcards...
if (!strncmp(path, "*/", 2))
{
path += 2;
descend = MXML_DESCEND_ALL;
}
else
{
descend = MXML_DESCEND_FIRST;
}
// Get the next element in the path...
if ((pathsep = strchr(path, '/')) == NULL)
pathsep = path + strlen(path);
if (pathsep == path || (size_t)(pathsep - path) >= sizeof(element))
return (NULL);
memcpy(element, path, pathsep - path);
element[pathsep - path] = '\0';
if (*pathsep)
path = pathsep + 1;
else
path = pathsep;
// Search for the element...
if ((node = mxmlFindElement(node, node, element, NULL, NULL, descend)) == NULL)
return (NULL);
}
// If we get this far, return the node or its first child...
// if (node->child && node->child->type != MXML_TYPE_ELEMENT)
// return (node->child);
// else
return (node);
}
//
// 'mxmlWalkNext()' - Walk to the next logical node in the tree.
//
// This function walks to the next logical node in the tree. The `descend`
// argument controls whether the first child is considered to be the next node.
// The `top` argument constrains the walk to that node's children.
//
mxml_node_t * // O - Next node or `NULL`
mxmlWalkNext(mxml_node_t *node, // I - Current node
mxml_node_t *top, // I - Top node
mxml_descend_t descend) // I - Descend into tree - `MXML_DESCEND_ALL`, `MXML_DESCEND_NONE`, or `MXML_DESCEND_FIRST`
{
if (!node)
{
return (NULL);
}
else if (node->child && descend != MXML_DESCEND_NONE)
{
return (node->child);
}
else if (node == top)
{
return (NULL);
}
else if (node->next)
{
return (node->next);
}
else if (node->parent && node->parent != top)
{
node = node->parent;
while (!node->next)
{
if (node->parent == top || !node->parent)
return (NULL);
else
node = node->parent;
}
return (node->next);
}
else
{
return (NULL);
}
}
//
// 'mxmlWalkPrev()' - Walk to the previous logical node in the tree.
//
// This function walks to the previous logical node in the tree. The `descend`
// argument controls whether the first child is considered to be the next node.
// The `top` argument constrains the walk to that node's children.
//
mxml_node_t * // O - Previous node or `NULL`
mxmlWalkPrev(mxml_node_t *node, // I - Current node
mxml_node_t *top, // I - Top node
mxml_descend_t descend) // I - Descend into tree - `MXML_DESCEND_ALL`, `MXML_DESCEND_NONE`, or `MXML_DESCEND_FIRST`
{
if (!node || node == top)
{
return (NULL);
}
else if (node->prev)
{
if (node->prev->last_child && descend != MXML_DESCEND_NONE)
{
// Find the last child under the previous node...
node = node->prev->last_child;
while (node->last_child)
node = node->last_child;
return (node);
}
else
{
return (node->prev);
}
}
else if (node->parent != top)
{
return (node->parent);
}
else
{
return (NULL);
}
}

630
src/main/mxml/mxml-set.c Normal file
View File

@ -0,0 +1,630 @@
//
// Node set 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"
//
// 'mxmlSetCDATA()' - Set the data for a CDATA node.
//
// This function sets the value string for a CDATA node. The node is not
// changed if it (or its first child) is not a CDATA node.
//
bool // O - `true` on success, `false` on failure
mxmlSetCDATA(mxml_node_t *node, // I - Node to set
const char *data) // I - New data string
{
char *s; // New element name
// 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)
return (false);
else if (!data)
return (false);
if (data == node->value.cdata)
{
// Don't change the value...
return (true);
}
// Allocate the new value, free any old element value, and set the new value...
if ((s = _mxml_strcopy(data)) == NULL)
return (false);
_mxml_strfree(node->value.cdata);
node->value.cdata = s;
return (true);
}
//
// 'mxmlSetCDATAf()' - Set the data for a CDATA to a formatted string.
//
// This function sets the formatted string value of a CDATA node. The node is
// not changed if it (or its first child) is not a CDATA node.
//
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)
return (false);
else if (!format)
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 = _mxml_strcopy(buffer)) == NULL)
return (false);
_mxml_strfree(node->value.cdata);
node->value.cdata = s;
return (true);
}
//
// 'mxmlSetComment()' - Set a comment to a literal string.
//
// This function sets the string value of a comment node.
//
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)
return (false);
else if (!comment)
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)
return (false);
_mxml_strfree(node->value.comment);
node->value.comment = s;
return (true);
}
//
// 'mxmlSetCommentf()' - Set a comment to a formatted string.
//
// This function sets the formatted string value of a comment node.
//
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)
return (false);
else if (!format)
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 = _mxml_strcopy(buffer)) == NULL)
return (false);
_mxml_strfree(node->value.comment);
node->value.comment = s;
return (true);
}
//
// 'mxmlSetCustom()' - Set the data and destructor of a custom data node.
//
// This function sets the data pointer `data` and destructor callback
// `destroy_cb` of a custom data node. The node is not changed if it (or its
// first child) is not a custom 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_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)
return (false);
if (data == node->value.custom.data)
goto set_free_callback;
// Free any old element value and set the new value...
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;
set_free_callback:
node->value.custom.free_cb = free_cb;
node->value.custom.free_cbdata = free_cbdata;
return (true);
}
//
// 'mxmlSetDeclaration()' - Set a declaration to a literal string.
//
// This function sets the string value of a declaration node.
//
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)
return (false);
else if (!declaration)
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)
return (false);
_mxml_strfree(node->value.declaration);
node->value.declaration = s;
return (true);
}
//
// 'mxmlSetDeclarationf()' - Set a declaration to a formatted string.
//
// This function sets the formatted string value of a declaration node.
//
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_DECLARATION)
node = node->child;
if (!node || node->type != MXML_TYPE_DECLARATION)
return (false);
else if (!format)
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 = _mxml_strcopy(buffer)) == NULL)
return (false);
_mxml_strfree(node->value.declaration);
node->value.declaration = s;
return (true);
}
//
// 'mxmlSetDirective()' - Set a processing instruction to a literal string.
//
// This function sets the string value of a processing instruction node.
//
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)
return (false);
else if (!directive)
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)
return (false);
_mxml_strfree(node->value.directive);
node->value.directive = s;
return (true);
}
//
// 'mxmlSetDirectivef()' - Set a processing instruction to a formatted string.
//
// This function sets the formatted string value of a processing instruction
// node.
//
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)
return (false);
else if (!format)
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 = _mxml_strcopy(buffer)) == NULL)
return (false);
_mxml_strfree(node->value.directive);
node->value.directive = s;
return (true);
}
//
// 'mxmlSetElement()' - Set the name of an element node.
//
// This function sets the name of an element node. The node is not changed if
// it is not an element node.
//
bool // O - `true` on success, `false` on failure
mxmlSetElement(mxml_node_t *node, // I - Node to set
const char *name) // I - New name string
{
char *s; // New name string
// Range check input...
if (!node || node->type != MXML_TYPE_ELEMENT)
return (false);
else if (!name)
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)
return (false);
_mxml_strfree(node->value.element.name);
node->value.element.name = s;
return (true);
}
//
// 'mxmlSetInteger()' - Set the value of an integer node.
//
// This function sets the value of an integer node. The node is not changed if
// it (or its first child) is not an integer node.
//
bool // O - `true` on success, `false` on failure
mxmlSetInteger(mxml_node_t *node, // I - Node to set
long integer) // I - Integer value
{
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_INTEGER)
node = node->child;
if (!node || node->type != MXML_TYPE_INTEGER)
return (false);
// Set the new value and return...
node->value.integer = integer;
return (true);
}
//
// 'mxmlSetOpaque()' - Set the value of an opaque node.
//
// This function sets the string value of an opaque node. The node is not
// changed if it (or its first child) is not an opaque node.
//
bool // O - `true` on success, `false` on failure
mxmlSetOpaque(mxml_node_t *node, // I - Node to set
const char *opaque) // I - Opaque string
{
char *s; // New opaque string
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_OPAQUE)
node = node->child;
if (!node || node->type != MXML_TYPE_OPAQUE)
return (false);
else if (!opaque)
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)
return (false);
_mxml_strfree(node->value.opaque);
node->value.opaque = s;
return (true);
}
//
// 'mxmlSetOpaquef()' - Set the value of an opaque string node to a formatted string.
//
// This function sets the formatted string value of an opaque node. The node is
// not changed if it (or its first child) is not an opaque node.
//
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
{
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_OPAQUE)
node = node->child;
if (!node || node->type != MXML_TYPE_OPAQUE)
return (false);
else if (!format)
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 = _mxml_strcopy(buffer)) == NULL)
return (false);
_mxml_strfree(node->value.opaque);
node->value.opaque = s;
return (true);
}
//
// 'mxmlSetReal()' - Set the value of a real value node.
//
// This function sets the value of a real value node. The node is not changed
// if it (or its first child) is not a real value node.
//
bool // O - `true` on success, `false` on failure
mxmlSetReal(mxml_node_t *node, // I - Node to set
double real) // I - Real number value
{
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_REAL)
node = node->child;
if (!node || node->type != MXML_TYPE_REAL)
return (false);
// Set the new value and return...
node->value.real = real;
return (true);
}
//
// 'mxmlSetText()' - Set the value of a text node.
//
// This function sets the string and whitespace values of a text node. The node
// is not changed if it (or its first child) is not a text node.
//
bool // O - `true` on success, `false` on failure
mxmlSetText(mxml_node_t *node, // I - Node to set
bool whitespace, // I - `true` = leading whitespace, `false` = no whitespace
const char *string) // I - String
{
char *s; // New string
// Range check input...
if (node && node->type == MXML_TYPE_ELEMENT && node->child && node->child->type == MXML_TYPE_TEXT)
node = node->child;
if (!node || node->type != MXML_TYPE_TEXT)
return (false);
else if (!string)
return (false);
if (string == node->value.text.string)
{
node->value.text.whitespace = whitespace;
return (true);
}
// Free any old string value and set the new value...
if ((s = _mxml_strcopy(string)) == NULL)
return (false);
_mxml_strfree(node->value.text.string);
node->value.text.whitespace = whitespace;
node->value.text.string = s;
return (true);
}
//
// 'mxmlSetTextf()' - Set the value of a text node to a formatted string.
//
// This function sets the formatted string and whitespace values of a text node.
// The node is not changed if it (or its first child) is not a text node.
//
bool // O - `true` on success, `false` on failure
mxmlSetTextf(mxml_node_t *node, // I - Node to set
bool whitespace, // I - `true` = leading whitespace, `false` = no whitespace
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_TEXT)
node = node->child;
if (!node || node->type != MXML_TYPE_TEXT)
return (false);
else if (!format)
return (false);
// 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 = _mxml_strcopy(buffer)) == NULL)
return (false);
_mxml_strfree(node->value.text.string);
node->value.text.whitespace = whitespace;
node->value.text.string = s;
return (true);
}
//
// 'mxmlSetUserData()' - Set the user data pointer for a node.
//
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 (false);
// Set the user data pointer and return...
node->user_data = data;
return (true);
}

250
src/main/mxml/mxml.h Normal file
View File

@ -0,0 +1,250 @@
//
// Header file 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.
//
#ifndef MXML_H
# define MXML_H
# include <stdio.h>
# include <stdlib.h>
# include <stdbool.h>
# include <stdint.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
# include <limits.h>
# ifdef __cplusplus
extern "C" {
# endif // __cplusplus
//
// Constants...
//
# define MXML_MAJOR_VERSION 4 // Major version number
# define MXML_MINOR_VERSION 0 // Minor version number
# ifdef __GNUC__
# define MXML_FORMAT(a,b) __attribute__ ((__format__ (__printf__, a, b)))
# else
# define MXML_FORMAT(a,b)
# endif // __GNUC__
//
// Data types...
//
typedef enum mxml_add_e // @link mxmlAdd@ add values
{
MXML_ADD_BEFORE, // Add node before specified node
MXML_ADD_AFTER // Add node after specified node
} mxml_add_t;
typedef enum mxml_descend_e // @link mxmlFindElement@, @link mxmlWalkNext@, and @link mxmlWalkPrev@ descend values
{
MXML_DESCEND_FIRST = -1, // Descend for first find
MXML_DESCEND_NONE = 0, // Don't descend when finding/walking
MXML_DESCEND_ALL = 1 // Descend when finding/walking
} mxml_descend_t;
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_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;
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
MXML_TYPE_REAL, // Real value
MXML_TYPE_TEXT, // Text fragment
MXML_TYPE_CUSTOM // Custom data
} mxml_type_t;
typedef enum mxml_ws_e // Whitespace periods
{
MXML_WS_BEFORE_OPEN, // Callback for before open tag
MXML_WS_AFTER_OPEN, // Callback for after open tag
MXML_WS_BEFORE_CLOSE, // Callback for before close tag
MXML_WS_AFTER_CLOSE, // Callback for after close tag
} mxml_ws_t;
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_index_s mxml_index_t;
// An XML node index
typedef struct _mxml_options_s mxml_options_t;
// XML options
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_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 size_t (*mxml_io_cb_t)(void *cbdata, void *buffer, size_t bytes);
// Read/write 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 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
//
// Prototypes...
//
extern void mxmlAdd(mxml_node_t *parent, mxml_add_t add, mxml_node_t *child, mxml_node_t *node);
extern void mxmlDelete(mxml_node_t *node);
extern void mxmlElementClearAttr(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, size_t idx, const char **name);
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 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);
extern const char *mxmlGetCDATA(mxml_node_t *node);
extern const char *mxmlGetComment(mxml_node_t *node);
extern const void *mxmlGetCustom(mxml_node_t *node);
extern const char *mxmlGetDeclaration(mxml_node_t *node);
extern const char *mxmlGetDirective(mxml_node_t *node);
extern const char *mxmlGetElement(mxml_node_t *node);
extern mxml_node_t *mxmlGetFirstChild(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 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);
extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind);
extern mxml_node_t *mxmlIndexFind(mxml_index_t *ind, const char *element, const char *value);
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, 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_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);
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, 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, 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);
extern int mxmlRetain(mxml_node_t *node);
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);
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_custfree_cb_t free_cb, void *free_cbdata);
extern bool mxmlSetElement(mxml_node_t *node, const char *name);
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 void mxmlSetStringCallbacks(mxml_strcopy_cb_t strcopy_cb, mxml_strfree_cb_t strfree_cb, void *str_cbdata);
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 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);
# ifdef __cplusplus
}
# endif // __cplusplus
#endif // !MXML_H