mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-04-24 14:57:09 -05:00
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:
parent
ee8f2a05fe
commit
45d7a29cba
|
|
@ -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
14
src/main/mxml/Module.mk
Normal 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
49
src/main/mxml/config.h
Normal 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
271
src/main/mxml/mxml-attr.c
Normal 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
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
364
src/main/mxml/mxml-get.c
Normal 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
418
src/main/mxml/mxml-index.c
Normal 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
937
src/main/mxml/mxml-node.c
Normal 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);
|
||||
}
|
||||
545
src/main/mxml/mxml-options.c
Normal file
545
src/main/mxml/mxml-options.c
Normal 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 ("&");
|
||||
|
||||
case '<' :
|
||||
return ("<");
|
||||
|
||||
case '>' :
|
||||
return (">");
|
||||
|
||||
case '\"' :
|
||||
return (""");
|
||||
|
||||
default :
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '_mxml_entity_value()' - Get the character corresponding to a named entity.
|
||||
//
|
||||
// The entity name can also be a numeric constant. `-1` is returned if the
|
||||
// name is not known.
|
||||
//
|
||||
|
||||
int // O - Unicode character
|
||||
_mxml_entity_value(
|
||||
mxml_options_t *options, // I - Options
|
||||
const char *name) // I - Entity name
|
||||
{
|
||||
int ch = -1; // Unicode character
|
||||
|
||||
|
||||
if (!name)
|
||||
{
|
||||
// No name...
|
||||
return (-1);
|
||||
}
|
||||
else if (*name == '#')
|
||||
{
|
||||
// Numeric entity...
|
||||
if (name[1] == 'x')
|
||||
ch = (int)strtol(name + 2, NULL, 16);
|
||||
else
|
||||
ch = (int)strtol(name + 1, NULL, 10);
|
||||
}
|
||||
else if (!strcmp(name, "amp"))
|
||||
{
|
||||
// Ampersand
|
||||
ch = '&';
|
||||
}
|
||||
else if (!strcmp(name, "gt"))
|
||||
{
|
||||
// Greater than
|
||||
ch = '>';
|
||||
}
|
||||
else if (!strcmp(name, "lt"))
|
||||
{
|
||||
// Less than
|
||||
ch = '<';
|
||||
}
|
||||
else if (!strcmp(name, "quot"))
|
||||
{
|
||||
// Double quote
|
||||
ch = '\"';
|
||||
}
|
||||
else if (options && options->entity_cb)
|
||||
{
|
||||
// Use callback
|
||||
ch = (options->entity_cb)(options->entity_cbdata, name);
|
||||
}
|
||||
|
||||
return (ch);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '_mxml_error()' - Display an error message.
|
||||
//
|
||||
|
||||
void
|
||||
_mxml_error(mxml_options_t *options, // I - Load/save options
|
||||
const char *format, // I - Printf-style format string
|
||||
...) // I - Additional arguments as needed
|
||||
{
|
||||
va_list ap; // Pointer to arguments
|
||||
char s[1024]; // Message string
|
||||
|
||||
|
||||
// Range check input...
|
||||
if (!format)
|
||||
return;
|
||||
|
||||
// Format the error message string...
|
||||
va_start(ap, format);
|
||||
vsnprintf(s, sizeof(s), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
// And then display the error message...
|
||||
if (options->error_cb)
|
||||
(options->error_cb)(options->error_cbdata, s);
|
||||
else
|
||||
fprintf(stderr, "%s\n", s);
|
||||
}
|
||||
280
src/main/mxml/mxml-private.c
Normal file
280
src/main/mxml/mxml-private.c
Normal 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
|
||||
140
src/main/mxml/mxml-private.h
Normal file
140
src/main/mxml/mxml-private.h
Normal 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
237
src/main/mxml/mxml-search.c
Normal 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
630
src/main/mxml/mxml-set.c
Normal 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
250
src/main/mxml/mxml.h
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user