From c1d787dc5f370dae91564daa511e2865f180934b Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Wed, 13 Mar 2024 09:59:47 -0400 Subject: [PATCH] Update docos some more. --- README.md | 3 +- doc/body.md | 241 +++++++++++++++++++++++++++----------------------- doc/mxml.3 | 6 +- doc/mxml.epub | Bin 720417 -> 720900 bytes doc/mxml.html | 213 ++++++++++++++++++++++++-------------------- mxml-attr.c | 10 +-- mxml.h | 2 +- 7 files changed, 259 insertions(+), 216 deletions(-) diff --git a/README.md b/README.md index 936a5e3..928114c 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,8 @@ Mini-XML provides the following functionality: - SAX (streamed) reading of XML files and strings to minimize memory usage. - Supports arbitrary element names, attributes, and attribute values with no preset limits, just available memory. -- Supports integer, real, opaque ("cdata"), and text data types in "leaf" nodes. +- Supports integer, real, opaque ("cdata"), text, and custom data types in + "leaf" nodes. - Functions for creating and managing trees of data. - "Find" and "walk" functions for easily locating and navigating trees of data. diff --git a/doc/body.md b/doc/body.md index 284e58c..61458d3 100644 --- a/doc/body.md +++ b/doc/body.md @@ -19,8 +19,8 @@ Mini-XML provides the following functionality: - SAX (streamed) reading of XML files and strings to minimize memory usage. - Supports arbitrary element names, attributes, and attribute values with no preset limits, just available memory. -- Supports integer, real, opaque ("CDATA"), and text data types in "leaf" - nodes. +- Supports integer, real, opaque ("CDATA"), text, and custom data types in + "leaf" nodes. - Functions for creating and managing trees of data. - "Find" and "walk" functions for easily locating and navigating trees of data. @@ -299,12 +299,12 @@ mxmlGetUserData(mxml_node_t *node); Creating XML Documents ---------------------- -You can create and update XML documents in memory using the various `mxmlNew` +You can create and update XML documents in memory using the various mxmlNewXxx functions. The following code will create the XML document described in the previous section: ```c -mxml_node_t *xml; /* */ +mxml_node_t *xml; /* */ mxml_node_t *data; /* */ mxml_node_t *node; /* */ mxml_node_t *group; /* */ @@ -336,25 +336,25 @@ data = mxmlNewElement(xml, "data"); ``` We start by creating the declaration node common to all XML files using the -`mxmlNewXML` function: +[mxmlNewXML](@@) function: ```c xml = mxmlNewXML("1.0"); ``` We then create the `` node used for this document using the -`mxmlNewElement` function. The first argument specifies the parent node +[mxmlNewElement](@@) function. The first argument specifies the parent node \(`xml`) while the second specifies the element name \(`data`): ```c data = mxmlNewElement(xml, "data"); ``` -Each `...` in the file is created using the `mxmlNewElement` and -`mxmlNewText` functions. The first argument of `mxmlNewText` specifies the -parent node \(`node`). The second argument specifies whether whitespace appears -before the text - 0 or false in this case. The last argument specifies the -actual text to add: +Each `...` in the file is created using the [mxmlNewElement](@@) +and [mxmlNewText](@@) functions. The first argument of [mxmlNewText](@@) +specifies the parent node \(`node`). The second argument specifies whether +whitespace appears before the text - `false` in this case. The last argument +specifies the actual text to add: ```c node = mxmlNewElement(data, "node"); @@ -368,47 +368,51 @@ one loaded from disk or a string. Saving an XML File ------------------ -You save an XML file using the `mxmlSaveFile` function: +You save an XML file using the [mxmlSaveFilename](@@) function: ```c bool -mxmlSaveFile(mxml_node_t *node, FILE *fp, - mxml_save_cb_t cb); +mxmlSaveFilename(mxml_node_t *node, const char *filename, + mxml_save_cb_t cb, void *cbdata); ``` -The `cb` argument specifies a function that returns the whitespace (if any) that -is inserted before and after each element node. The `MXML_NO_CALLBACK` constant -tells Mini-XML to not include any extra whitespace. For example, so save an XML -file to the file "filename.xml" with no extra whitespace: +The `cb` and `cbdata` arguments specify a function and data pointer that is +called to determine what whitespace (if any) is inserted before and after each +element node. A `NULL` value tells Mini-XML to not include any extra +whitespace. For example, so save an XML file to the file "filename.xml" with +no extra whitespace: ```c -FILE *fp; - -fp = fopen("filename.xml", "w"); -mxmlSaveFile(xml, fp, MXML_NO_CALLBACK); -fclose(fp); +mxmlSaveFile(xml, "filename.xml", /*cb*/NULL, /*cbdata*/NULL); ``` -Mini-XML also provides functions to save to a file descriptor or strings: +Mini-XML also provides functions to save to a file descriptor, `FILE` pointer, +or strings: ```c char * -mxmlSaveAllocString(mxml_node_t *node, mxml_save_cb_t cb); +mxmlSaveAllocString(mxml_node_t *node, mxml_save_cb_t cb, + void *cbdata); bool -mxmlSaveFd(mxml_node_t *node, int fd, mxml_save_cb_t cb); +mxmlSaveFd(mxml_node_t *node, int fd, mxml_save_cb_t cb, + void *cbdata); + +bool +mxmlSaveFile(mxml_node_t *node, FILE *fp, mxml_save_cb_t cb, + void *cbdata); size_t mxmlSaveString(mxml_node_t *node, char *buffer, size_t bufsize, - mxml_save_cb_t cb); + mxml_save_cb_t cb, void *cbdata); ``` ### Controlling Line Wrapping When saving XML documents, Mini-XML normally wraps output lines at column 75 so -that the text is readable in terminal windows. The `mxmlSetWrapMargin` function -overrides the default wrap margin for the current thread: +that the text is readable in terminal windows. The [mxmlSetWrapMargin](@@) +function overrides the default wrap margin for the current thread: ```c void mxmlSetWrapMargin(int column); @@ -429,20 +433,19 @@ mxmlSetWrapMargin(0); ### Save Callbacks -The last argument to the `mxmlSave` functions is a callback function which is -used to automatically insert whitespace in an XML document. Your callback -function will be called up to four times for each element node with a pointer to -the node and a "where" value of `MXML_WS_BEFORE_OPEN`, `MXML_WS_AFTER_OPEN`, -`MXML_WS_BEFORE_CLOSE`, or `MXML_WS_AFTER_CLOSE`. The callback function should -return `NULL` if no whitespace should be added or the string to insert (spaces, -tabs, carriage returns, and newlines) otherwise. +The last arguments to the mxmlSaveXxx functions are a callback function and data +pointer which is used to automatically insert whitespace in an XML document. +Your callback function will be called up to four times for each element node +with a pointer to the node and a `where` value of `MXML_WS_BEFORE_OPEN`, `MXML_WS_AFTER_OPEN`, `MXML_WS_BEFORE_CLOSE`, or `MXML_WS_AFTER_CLOSE`. The +callback function should return `NULL` if no whitespace should be added or the +string to insert (spaces, tabs, carriage returns, and newlines) otherwise. The following whitespace callback can be used to add whitespace to XHTML output to make it more readable in a standard text editor: ```c const char * -whitespace_cb(mxml_node_t *node, int where) +whitespace_cb(void *cbdata, mxml_node_t *node, mxml_ws_t where) { const char *element; @@ -514,7 +517,7 @@ FILE *fp; mxml_node_t *tree; fp = fopen("filename.xml", "w"); -mxmlSaveFile(tree, fp, whitespace_cb); +mxmlSaveFile(tree, fp, whitespace_cb, /*cbdata*/NULL); fclose(fp); ``` @@ -522,20 +525,19 @@ fclose(fp); Memory Management ----------------- -Once you are done with the XML data, use the `mxmlDelete` function to -recursively free the memory that is used for a particular node or the entire -tree: +Once you are done with the XML data, use the [mxmlDelete](@@) function to +free the memory that is used for a particular node and its children: ```c void mxmlDelete(mxml_node_t *tree); ``` -You can also use reference counting to manage memory usage. The `mxmlRetain` -and `mxmlRelease` functions increment and decrement a node's use count, -respectively. When the use count goes to zero, `mxmlRelease` automatically -calls `mxmlDelete` to actually free the memory used by the node tree. New nodes -start with a use count of 1. +You can also use reference counting to manage memory usage. The +[mxmlRetain](@@) and [mxmlRelease](@@) functions increment and decrement a +node's use count, respectively. When the use count goes to zero, +[mxmlRelease](@@) calls [mxmlDelete](@@) to actually free the memory used by the +node tree. New nodes start with a use count of `1`. More About Nodes @@ -544,17 +546,17 @@ More About Nodes Element Nodes ------------- -Element \(`MXML_TYPE_ELEMENT`) nodes are created using the `mxmlNewElement` -function. Element attributes are set using the `mxmlElementSetAttr` and -`mxmlElementSetAttrf` functions and cleared using the `mxmlElementDeleteAttr` -function: +Element \(`MXML_TYPE_ELEMENT`) nodes are created using the [mxmlNewElement](@@) +function. Element attributes are set using the [mxmlElementSetAttr](@@) and +[mxmlElementSetAttrf](@@) functions and cleared using the +[mxmlElementClearAttr](@@) function: ```c mxml_node_t * mxmlNewElement(mxml_node_t *parent, const char *name); void -mxmlElementDeleteAttr(mxml_node_t *node, const char *name); +mxmlElementClearAttr(mxml_node_t *node, const char *name); void mxmlElementSetAttr(mxml_node_t *node, const char *name, @@ -565,18 +567,10 @@ mxmlElementSetAttrf(mxml_node_t *node, const char *name, const char *format, ...); ``` -Child nodes are added using the various `mxmlNew` functions. The top (root) -node must be an element, usually created by the `mxmlNewXML` function: - -```c -mxml_node_t * -mxmlNewXML(const char *version); -``` - -The `mxmlGetElement` function retrieves the element name, the -`mxmlElementGetAttr` function retrieves the value string for a named attribute -associated with the element. The `mxmlElementGetAttrByIndex` and -`mxmlElementGetAttrCount` functions retrieve attributes by index: +The [mxmlGetElement](@@) function retrieves the element name while the +[mxmlElementGetAttr](@@) function retrieves the value string for a named +attribute associated with the element. The [mxmlElementGetAttrByIndex](@@) and +[mxmlElementGetAttrCount](@@) functions retrieve attributes by index: ```c const char * @@ -597,27 +591,39 @@ mxmlElementGetAttrCount(mxml_node_t *node); CDATA Nodes ----------- -CDATA \(`MXML_TYPE_CDATA`) nodes are created using the `mxmlNewCDATA` function: +CDATA \(`MXML_TYPE_CDATA`) nodes are created using the [mxmlNewCDATA](@@) +and [mxmlNewCDATAf](@@) functions: - mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string); +```c +mxml_node_t * +mxmlNewCDATA(mxml_node_t *parent, const char *string); -The `mxmlGetCDATA` function retrieves the CDATA string pointer for a node: +mxml_node_t * +mxmlNewCDATAf(mxml_node_t *parent, const char *format, ...); +``` - const char *mxmlGetCDATA(mxml_node_t *node); +The [mxmlGetCDATA](@@) function retrieves the CDATA string pointer for a node: + +```c +const char * +mxmlGetCDATA(mxml_node_t *node); +``` Comment Nodes ------------- -Comment \(`MXML_TYPE_COMMENT`) nodes are created using the `mxmlNewComment` -function, for example: +Comment \(`MXML_TYPE_COMMENT`) nodes are created using the [mxmlNewComment](@@) +and [mxmlNewCommentf](@@) functions, for example: ```c mxml_node_t *node = mxmlNewComment(" This is a comment "); + +mxml_node_t *node = mxmlNewCommentf(" This is comment %d ", 42); ``` -Similarly, the `mxmlGetComment` function retrieves the comment string pointer -for a node: +Similarly, the [mxmlGetComment](@@) function retrieves the comment string +pointer for a node: ```c const char *comment = mxmlGetComment(node); @@ -629,25 +635,35 @@ Processing Instruction Nodes ---------------------------- Processing instruction \(`MXML_TYPE_DIRECTIVE`) nodes are created using the -`mxmlNewDirective` function: +[mxmlNewDirective](@@) and [mxmlNewDirectivef](@@) functions: ```c mxml_node_t *node = mxmlNewDirective("xml-stylesheet type=\"text/css\" href=\"style.css\""); + +mxml_node_t *node = mxmlNewDirectivef("xml version=\"%s\"", version); ``` -The `mxmlGetDirective` function retrieves the processing instruction string for a -node: +The [mxmlGetDirective](@@) function retrieves the processing instruction string +for a node: ```c const char *instr = mxmlGetElement(node); /* returns "xml-stylesheet type=\"text/css\" href=\"style.css\"" */ ``` +The [mxmlNewXML](@@) function can be used to create the top-level "xml" +processing instruction with an associated version number: + +```c +mxml_node_t * +mxmlNewXML(const char *version); +``` + Integer Nodes ------------- -Integer \(`MXML_TYPE_INTEGER`) nodes are created using the `mxmlNewInteger` +Integer \(`MXML_TYPE_INTEGER`) nodes are created using the [mxmlNewInteger](@@) function: ```c @@ -655,7 +671,7 @@ mxml_node_t * mxmlNewInteger(mxml_node_t *parent, long integer); ``` -The `mxmlGetInteger` function retrieves the integer value for a node: +The [mxmlGetInteger](@@) function retrieves the integer value for a node: ```c @@ -667,15 +683,18 @@ mxmlGetInteger(mxml_node_t *node); Opaque String Nodes ------------------- -Opaque string \(`MXML_TYPE_OPAQUE`) nodes are created using the `mxmlNewOpaque` -function: +Opaque string \(`MXML_TYPE_OPAQUE`) nodes are created using the +[mxmlNewOpaque](@@) and [mxmlNewOpaquef](@@) functions: ```c mxml_node_t * mxmlNewOpaque(mxml_node_t *parent, const char *opaque); + +mxml_node_t * +mxmlNewOpaquef(mxml_node_t *parent, const char *format, ...); ``` -The `mxmlGetOpaque` function retrieves the opaque string pointer for a node: +The [mxmlGetOpaque](@@) function retrieves the opaque string pointer for a node: ```c const char * @@ -687,8 +706,8 @@ Text Nodes ---------- Whitespace-delimited text string \(`MXML_TYPE_TEXT`) nodes are created using the -`mxmlNewText` and `mxmlNewTextf` functions. Each text node consists of a text -string and (leading) whitespace boolean value. +[mxmlNewText](@@) and [mxmlNewTextf](@@) functions. Each text node consists of +a text string and (leading) whitespace boolean value. ```c mxml_node_t * @@ -700,7 +719,7 @@ mxmlNewTextf(mxml_node_t *parent, bool whitespace, const char *format, ...); ``` -The `mxmlGetText` function retrieves the text string pointer and whitespace +The [mxmlGetText](@@) function retrieves the text string pointer and whitespace boolean value for a node: ```c @@ -712,7 +731,7 @@ mxmlGetText(mxml_node_t *node, bool *whitespace); Real Number Nodes -------------------- -Real number \(`MXML_TYPE_REAL`) nodes are created using the `mxmlNewReal` +Real number \(`MXML_TYPE_REAL`) nodes are created using the [mxmlNewReal](@@) function: ```c @@ -720,7 +739,7 @@ mxml_node_t * mxmlNewReal(mxml_node_t *parent, double real); ``` -The `mxmlGetReal` function retrieves the real number for a node: +The [mxmlGetReal](@@) function retrieves the real number for a node: ```c double @@ -738,7 +757,7 @@ documents. Finding Nodes ------------- -The `mxmlFindPath` function finds the (first) value node under a specific +The [mxmlFindPath](@@) function finds the (first) value node under a specific element using a "path": ```c @@ -757,8 +776,8 @@ mxml_node_t *value = mxmlFindPath(xml, "data/group/node"); mxml_node_t *value = mxmlFindPath(xml, "data/*/node"); ``` -The `mxmlFindElement` function can be used to find a named element, optionally -matching an attribute and value: +The [mxmlFindElement](@@) function can be used to find a named element, +optionally matching an attribute and value: ```c mxml_node_t * @@ -781,7 +800,7 @@ node = mxmlFindElement(tree, tree, "a", "href", NULL, /* Find the first "a" element with "href" to a URL */ node = mxmlFindElement(tree, tree, "a", "href", - "http://michaelrsweet.github.io/", + "http://msweet.org/", MXML_DESCEND_ALL); /* Find the first element with a "src" attribute*/ @@ -827,10 +846,10 @@ three constants: Iterating Nodes --------------- -While the `mxmlFindNode` and `mxmlFindPath` functions will find a particular -element node, sometimes you need to iterate over all nodes. The `mxmlWalkNext` -and `mxmlWalkPrev` functions can be used to iterate through the XML node -tree: +While the [mxmlFindNode](@@) and [mxmlFindPath](@@) functions will find a +particular element node, sometimes you need to iterate over all nodes. The +[mxmlWalkNext](@@) and [mxmlWalkPrev](@@) functions can be used to iterate +through the XML node tree: ```c mxml_node_t * @@ -886,8 +905,8 @@ val8 Indexing -------- -The `mxmlIndexNew` function allows you to create an index of nodes for faster -searching and enumeration: +The [mxmlIndexNew](@@) function allows you to create an index of nodes for +faster searching and enumeration: ```c mxml_index_t * @@ -908,7 +927,7 @@ document: mxml_index_t *ind = mxmlIndexNew(xml, NULL, "id"); ``` -Once the index is created, the `mxmlIndexFind` function can be used to find a +Once the index is created, the [mxmlIndexFind](@@) function can be used to find a matching node: ```c @@ -923,7 +942,7 @@ For example, the following code will find the element whose "id" string is "42": mxml_node_t *node = mxmlIndexFind(ind, NULL, "42"); ``` -Alternately, the `mxmlIndexReset` and `mxmlIndexEnum` functions can be used to +Alternately, the [mxmlIndexReset](@@) and [mxmlIndexEnum](@@) functions can be used to enumerate the nodes in the index: ```c @@ -947,14 +966,14 @@ for (node = mxmlIndexReset(ind); } ``` -The `mxmlIndexCount` function returns the number of nodes in the index: +The [mxmlIndexCount](@@) function returns the number of nodes in the index: ```c size_t mxmlIndexGetCount(mxml_index_t *ind); ``` -Finally, the `mxmlIndexDelete` function frees all memory associated with the +Finally, the [mxmlIndexDelete](@@) function frees all memory associated with the index: ```c @@ -972,16 +991,16 @@ however your callbacks can store additional information in order to support multiple custom data types as needed. The `MXML_TYPE_CUSTOM` node type identifies custom data nodes. -The `mxmlGetCustom` function retrieves the custom value pointer for a node. +The [mxmlGetCustom](@@) function retrieves the custom value pointer for a node. ```c const void * mxmlGetCustom(mxml_node_t *node); ``` -Custom \(`MXML_TYPE_CUSTOM`) nodes are created using the `mxmlNewCustom` +Custom \(`MXML_TYPE_CUSTOM`) nodes are created using the [mxmlNewCustom](@@) function or using a custom per-thread load callbacks specified using the -`mxmlSetCustomHandlers` function: +[mxmlSetCustomHandlers](@@) function: ```c typedef void (*mxml_custom_destroy_cb_t)(void *); @@ -1126,10 +1145,11 @@ save_custom(mxml_node_t *node) } ``` -You register the callback functions using the `mxmlSetCustomHandlers` function: +You register the callback functions using the [mxmlSetCustomCallbacks](@@) +function: ```c -mxmlSetCustomHandlers(load_custom, save_custom); +mxmlSetCustomCallbacks(load_custom, save_custom); ``` @@ -1142,8 +1162,7 @@ allowing you to process XML documents of any size, the Mini-XML implementation also allows you to retain portions of the document in memory for later processing. -The `mxmlLoadFd`, `mxmlLoadFile`, `mxmlLoadFilename`, `mxmlLoadIO`, and -`mxmlLoadString` functions support a SAX callback and associated data. The +The mxmlLoadXxx functions support a SAX callback and associated data. The callback function receives the data pointer you supplied, the node, and an event code and returns `true` to continue processing or `false` to stop: @@ -1171,8 +1190,8 @@ The event will be one of the following: 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. For example, the following SAX callback will -retain all nodes, effectively simulating a normal in-memory load: +using the [mxmlRetain](@@) function. For example, the following SAX callback +will retain all nodes, effectively simulating a normal in-memory load: ```c bool @@ -1301,8 +1320,8 @@ The following incompatible API changes were made in Mini-XML v4.0: - SAX events are now named `MXML_SAX_EVENT_foo` instead of `MXML_SAX_foo`. - SAX callbacks now return a boolean value. -- The `mxmlSAXLoadXxx` functions have been removed in favor of passing the SAX - callback function and data pointers to the `mxmlLoadXxx` functions. +- The mxmlSAXLoadXxx functions have been removed in favor of passing the SAX + callback function and data pointers to the mxmlLoadXxx functions. - Node types are now named `MXML_TYPE_foo` instead of `MXML_foo`. - Descend values are now normalized to `MXML_DESCEND_ALL`, `MXML_DESCEND_FIRST`, and `MXML_DESCEND_NONE`. @@ -1311,8 +1330,12 @@ The following incompatible API changes were made in Mini-XML v4.0: - CDATA nodes ("``") now have their own type (`MXML_TYPE_CDATA`). - Comment nodes ("``") now have their own type (`MXML_TYPE_COMMENT`). +- Custom node callbacks are now set using the [mxmlSetCustomCallbacks](@@) + function instead of mxmlSetCustomHandlers. - Declaration nodes ("``") now have their own type (`MXML_TYPE_DECLARATION`). +- Element attributes are now cleared with the [mxmlElementClearAttr](@@) + function instead of mxmlElementDeleteAttr. - Processing instruction/directive nodes ("``") now have their own type (`MXML_TYPE_DIRECTIVE`). - Integer nodes (`MXML_TYPE_INTEGER`) now use the `long` type. diff --git a/doc/mxml.3 b/doc/mxml.3 index a9f5525..8578aa1 100644 --- a/doc/mxml.3 +++ b/doc/mxml.3 @@ -1,4 +1,4 @@ -.TH mxml 3 "Mini-XML API" "2024-03-07" "Mini-XML API" +.TH mxml 3 "Mini-XML API" "2024-03-13" "Mini-XML API" .SH NAME mxml \- Mini-XML API .SH INCLUDE FILE @@ -330,11 +330,11 @@ void mxmlDelete ( .PP If the specified node has a parent, this function first removes the node from its parent using the \fImxmlRemove\fR function. -.SS mxmlElementDeleteAttr +.SS mxmlElementClearAttr Delete an attribute. .PP .nf -void mxmlElementDeleteAttr ( +void mxmlElementClearAttr ( mxml_node_t *node, const char *name ); diff --git a/doc/mxml.epub b/doc/mxml.epub index cae7f734262466cade52ec78752a32d1031fb761..6101673b001962245daf4ec4770192c887ab2c1b 100644 GIT binary patch delta 22917 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 z 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 delta 22451 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
  • mxmlAdd
  • mxmlDelete
  • -
  • mxmlElementDeleteAttr
  • +
  • mxmlElementClearAttr
  • mxmlElementGetAttr
  • mxmlElementGetAttrByIndex
  • mxmlElementGetAttrCount
  • @@ -414,7 +414,7 @@ span.string {
  • Supports arbitrary element names, attributes, and attribute values with no preset limits, just available memory.

  • -
  • Supports integer, real, opaque ("CDATA"), and text data types in "leaf" nodes.

    +
  • Supports integer, real, opaque ("CDATA"), text, and custom data types in "leaf" nodes.

  • Functions for creating and managing trees of data.

  • @@ -431,10 +431,11 @@ span.string {

    Given the limited scope of what you use in XML, it should be trivial to code a mini-XML API in a few hundred lines of code.

    -

    I took my own challenge and coded furiously for two days to produced the initial public release of Mini-XML, total lines of code: 696. Robert promptly integrated Mini-XML into Gutenprint and removed libxml2.

    -

    Thanks to lots of feedback and support from various developers, Mini-XML has evolved since then to provide a more complete XML implementation and now stands at a whopping 3,839 lines of code, compared to 175,808 lines of code for libxml2 version 2.11.7.

    +

    I took my own challenge and coded furiously for two days to produce the initial public release of Mini-XML, total lines of code: 696. Robert promptly integrated Mini-XML into Gutenprint and removed libxml2.

    +

    Thanks to lots of feedback and support from various developers, Mini-XML has evolved since then to provide a more complete XML implementation and now stands at a whopping 3,875 lines of code, compared to 175,808 lines of code for libxml2 version 2.11.7.

    Resources

    The Mini-XML home page can be found at https://www.msweet.org/mxml. From there you can download the current version of Mini-XML, access the issue tracker, and find other resources.

    +

    Mini-XML v4 has a slightly different API than prior releases. See the Migrating from Mini-XML v3.x chapter for details.

    The Mini-XML library is copyright © 2003-2024 by Michael R Sweet and is provided under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2-only software. See the files "LICENSE" and "NOTICE" for more information.

    Using Mini-XML

    @@ -447,33 +448,34 @@ span.string {

    If you have the pkg-config software installed, you can use it to determine the proper compiler and linker options for your installation:

    gcc `pkg-config --cflags mxml4` -o myprogram myprogram.c `pkg-config --libs mxml4`
     
    +
    +

    Note: The library name "mxml4" is a configure-time option. If you use the --disable-libmxml4-prefix configure option the library is named "mxml".

    +

    Loading an XML File

    -

    You load an XML file using the mxmlLoadFile function:

    +

    You load an XML file using the mxmlLoadFile function:

    mxml_node_t *
    -mxmlLoadFile(mxml_node_t *top, FILE *fp,
    -             mxml_load_cb_t load_cb, void *load_cbdata,
    -             mxml_sax_cb_t sax_cb, void *sax_cbdata);
    +mxmlLoadFilename(mxml_node_t *top, const char *filename,
    +                 mxml_load_cb_t load_cb, void *load_cbdata,
    +                 mxml_sax_cb_t sax_cb, void *sax_cbdata);
     

    The load_cb argument specifies a function that assigns child (value) node types for each element in the document. The default callback (NULL) supports passing a pointer to an mxml_type_t variable containing the type of value nodes. For example, to load the XML file "filename.xml" containing literal strings you can use:

    -
    FILE *fp;
    -mxml_node_t *tree;
    +
    mxml_node_t *tree;
     mxml_type_t type = MXML_TYPE_OPAQUE;
     
    -fp = fopen("filename.xml", "r");
    -tree = mxmlLoadFile(/*top*/NULL, fp, /*load_cb*/NULL, &type,
    -                    /*sax_cb*/NULL, /*sax_cbdata*/NULL);
    -fclose(fp);
    +tree = mxmlLoadFilename(/*top*/NULL, "filename.xml",
    +                        /*load_cb*/NULL, /*load_cbdata*/&type,
    +                        /*sax_cb*/NULL, /*sax_cbdata*/NULL);
     
    -

    Mini-XML also provides functions to load from a named file, a file descriptor, or string:

    +

    Mini-XML also provides functions to load from a FILE pointer, a file descriptor, or string:

    mxml_node_t *
     mxmlLoadFd(mxml_node_t *top, int fd,
                mxml_load_cb_t load_cb, void *load_cbdata,
                mxml_sax_cb_t sax_cb, void *sax_cbdata);
     
     mxml_node_t *
    -mxmlLoadFilename(mxml_node_t *top, const char *filename,
    -                 mxml_load_cb_t load_cb, void *load_cbdata,
    -                 mxml_sax_cb_t sax_cb, void *sax_cbdata);
    +mxmlLoadFile(mxml_node_t *top, FILE *fp,
    +             mxml_load_cb_t load_cb, void *load_cbdata,
    +             mxml_sax_cb_t sax_cb, void *sax_cbdata);
     
     mxml_node_t *
     mxmlLoadString(mxml_node_t *top, const char *s,
    @@ -481,11 +483,11 @@ mxmlLoadString(mxml_node_t *top, const void *sax_cbdata);
     

    Load Callbacks

    -

    The load_xxx arguments to the mxmlLoad functions are a callback function and a data pointer which are used to determine the value type of each data node in an XML document. The default (NULL) callback expects the load_cbdata argument to be a pointer to a mxml_type_t variable - if NULL it returns the MXML_TYPE_TEXT type.

    -

    You can provide your own callback functions for more complex XML documents. Your callback function will receive a pointer to the current element node and must return the value type of the immediate children for that element node: MXML_TYPE_CUSTOM, MXML_TYPE_INTEGER, MXML_TYPE_OPAQUE, MXML_TYPE_REAL, or MXML_TYPE_TEXT. The function is called after the element and its attributes have been read, so you can look at the element name, attributes, and attribute values to determine the proper value type to return.

    +

    The load_xxx arguments to the mxmlLoadXxx functions are a callback function and a data pointer which are used to determine the value type of each data node in an XML document. The default (NULL) callback expects the load_cbdata argument to be a pointer to a mxml_type_t variable that contains the desired value node type - if NULL, it uses the MXML_TYPE_TEXT (whitespace-separated text) type.

    +

    You can provide your own callback function for more complex XML documents. Your callback function will receive a pointer to the current element node and must return the value type of the immediate children for that element node: MXML_TYPE_CUSTOM, MXML_TYPE_INTEGER, MXML_TYPE_OPAQUE, MXML_TYPE_REAL, or MXML_TYPE_TEXT. The function is called after the element and its attributes have been read so you can look at the element name, attributes, and attribute values to determine the proper value type to return.

    The following callback function looks for an attribute named "type" or the element name to determine the value type for its child nodes:

    mxml_type_t
    -type_cb(void *cbdata, mxml_node_t *node)
    +my_load_cb(void *cbdata, mxml_node_t *node)
     {
       const char *type;
     
    @@ -497,6 +499,8 @@ type_cb(void *cbdata, mxml_node_t *node)
       type = mxmlElementGetAttr(node, "type");
       if (type == NULL)
         type = mxmlGetElement(node);
    +  if (type == NULL)
    +    type = "text";
     
       if (!strcmp(type, "integer"))
         return (MXML_TYPE_INTEGER);
    @@ -508,15 +512,12 @@ type_cb(void *cbdata, mxml_node_t *node)
         return (MXML_TYPE_TEXT);
     }
     
    -

    To use this callback function, simply use the name when you call any of the load functions:

    -
    FILE *fp;
    -mxml_node_t *tree;
    +

    To use this callback function, simply specify it when you call any of the load functions:

    +
    mxml_node_t *tree;
     
    -fp = fopen("filename.xml", "r");
    -tree = mxmlLoadFile(/*top*/NULL, fp,
    -                    type_cb, /*load_cbdata*/NULL,
    -                    /*sax_cb*/NULL, /*sax_cbata*/NULL);
    -fclose(fp);
    +tree = mxmlLoadFilename(/*top*/NULL, "filename.xml",
    +                        my_load_cb, /*load_cbdata*/NULL,
    +                        /*sax_cb*/NULL, /*sax_cbdata*/NULL);
     

    Nodes

    Every piece of information in an XML file is stored in memory in "nodes". Nodes are defined by the mxml_node_t structure. Each node has a typed value, optional user data, a parent node, sibling nodes (previous and next), and potentially child nodes.

    @@ -549,7 +550,7 @@ val1 val2 val3 | val7 val8 val4 val5 val6

    where "-" is a pointer to the sibling node and "|" is a pointer to the first child or parent node.

    -

    The mxmlGetType function gets the type of a node:

    +

    The mxmlGetType function gets the type of a node:

    mxml_type_t
     mxmlGetType(mxml_node_t *node);
     
    @@ -575,7 +576,7 @@ mxmlGetType(mxml_node_t *node);
  • MXML_TYPE_TEXT : A whitespace-delimited text (fragment) value.

-

The parent and sibling nodes are accessed using the mxmlGetParent, mxmlGetNextSibling, and mxmlGetPreviousSibling functions, while the children of an element node are accessed using the mxmlGetFirstChild or mxmlGetLastChild functions:

+

The parent and sibling nodes are accessed using the mxmlGetParent, mxmlGetNextSibling, and mxmlGetPreviousSibling functions, while the children of an element node are accessed using the mxmlGetFirstChild or mxmlGetLastChild functions:

mxml_node_t *
 mxmlGetFirstChild(mxml_node_t *node);
 
@@ -591,13 +592,13 @@ mxmlGetParent(mxml_node_t *node);
 mxml_node_t *
 mxmlGetPrevSibling(mxml_node_t *node);
 
-

The mxmlGetUserData function gets any user (application) data associated with the node:

+

The mxmlGetUserData function gets any user (application) data associated with the node:

void *
 mxmlGetUserData(mxml_node_t *node);
 

Creating XML Documents

-

You can create and update XML documents in memory using the various mxmlNew functions. The following code will create the XML document described in the previous section:

-
mxml_node_t *xml;    /* <?xml ... ?> */
+

You can create and update XML documents in memory using the various mxmlNewXxx functions. The following code will create the XML document described in the previous section:

+
mxml_node_t *xml;    /* <?xml version="1.0"?> */
 mxml_node_t *data;   /* <data> */
 mxml_node_t *node;   /* <node> */
 mxml_node_t *group;  /* <group> */
@@ -627,43 +628,45 @@ data = mxmlNewElement(xml, "data");
     node = mxmlNewElement(data, "node");
     mxmlNewText(node, false, "val8");
 
-

We start by creating the declaration node common to all XML files using the mxmlNewXML function:

+

We start by creating the declaration node common to all XML files using the mxmlNewXML function:

xml = mxmlNewXML("1.0");
 
-

We then create the <data> node used for this document using the mxmlNewElement function. The first argument specifies the parent node (xml) while the second specifies the element name (data):

+

We then create the <data> node used for this document using the mxmlNewElement function. The first argument specifies the parent node (xml) while the second specifies the element name (data):

data = mxmlNewElement(xml, "data");
 
-

Each <node>...</node> in the file is created using the mxmlNewElement and mxmlNewText functions. The first argument of mxmlNewText specifies the parent node (node). The second argument specifies whether whitespace appears before the text - 0 or false in this case. The last argument specifies the actual text to add:

+

Each <node>...</node> in the file is created using the mxmlNewElement and mxmlNewText functions. The first argument of mxmlNewText specifies the parent node (node). The second argument specifies whether whitespace appears before the text - false in this case. The last argument specifies the actual text to add:

node = mxmlNewElement(data, "node");
 mxmlNewText(node, false, "val1");
 

The resulting in-memory XML document can then be saved or processed just like one loaded from disk or a string.

Saving an XML File

-

You save an XML file using the mxmlSaveFile function:

+

You save an XML file using the mxmlSaveFilename function:

bool
-mxmlSaveFile(mxml_node_t *node, FILE *fp,
-             mxml_save_cb_t cb);
+mxmlSaveFilename(mxml_node_t *node, const char *filename,
+                 mxml_save_cb_t cb, void *cbdata);
 
-

The cb argument specifies a function that returns the whitespace (if any) that is inserted before and after each element node. The MXML_NO_CALLBACK constant tells Mini-XML to not include any extra whitespace. For example, so save an XML file to the file "filename.xml" with no extra whitespace:

-
FILE *fp;
-
-fp = fopen("filename.xml", "w");
-mxmlSaveFile(xml, fp, MXML_NO_CALLBACK);
-fclose(fp);
+

The cb and cbdata arguments specify a function and data pointer that is called to determine what whitespace (if any) is inserted before and after each element node. A NULL value tells Mini-XML to not include any extra whitespace. For example, so save an XML file to the file "filename.xml" with no extra whitespace:

+
mxmlSaveFile(xml, "filename.xml", /*cb*/NULL, /*cbdata*/NULL);
 
-

Mini-XML also provides functions to save to a file descriptor or strings:

+

Mini-XML also provides functions to save to a file descriptor, FILE pointer, or strings:

char *
-mxmlSaveAllocString(mxml_node_t *node, mxml_save_cb_t cb);
+mxmlSaveAllocString(mxml_node_t *node, mxml_save_cb_t cb,
+                    void *cbdata);
 
 bool
-mxmlSaveFd(mxml_node_t *node, int fd, mxml_save_cb_t cb);
+mxmlSaveFd(mxml_node_t *node, int fd, mxml_save_cb_t cb,
+           void *cbdata);
+
+bool
+mxmlSaveFile(mxml_node_t *node, FILE *fp, mxml_save_cb_t cb,
+             void *cbdata);
 
 size_t
 mxmlSaveString(mxml_node_t *node, char *buffer, size_t bufsize,
-               mxml_save_cb_t cb);
+               mxml_save_cb_t cb, void *cbdata);
 

Controlling Line Wrapping

-

When saving XML documents, Mini-XML normally wraps output lines at column 75 so that the text is readable in terminal windows. The mxmlSetWrapMargin function overrides the default wrap margin for the current thread:

+

When saving XML documents, Mini-XML normally wraps output lines at column 75 so that the text is readable in terminal windows. The mxmlSetWrapMargin function overrides the default wrap margin for the current thread:

void mxmlSetWrapMargin(int column);
 

For example, the following code sets the margin to 132 columns:

@@ -673,10 +676,10 @@ mxmlSaveString(mxml_node_t *node, char *buffer, si
mxmlSetWrapMargin(0);
 

Save Callbacks

-

The last argument to the mxmlSave functions is a callback function which is used to automatically insert whitespace in an XML document. Your callback function will be called up to four times for each element node with a pointer to the node and a "where" value of MXML_WS_BEFORE_OPEN, MXML_WS_AFTER_OPEN, MXML_WS_BEFORE_CLOSE, or MXML_WS_AFTER_CLOSE. The callback function should return NULL if no whitespace should be added or the string to insert (spaces, tabs, carriage returns, and newlines) otherwise.

+

The last arguments to the mxmlSaveXxx functions are a callback function and data pointer which is used to automatically insert whitespace in an XML document. Your callback function will be called up to four times for each element node with a pointer to the node and a where value of MXML_WS_BEFORE_OPEN, MXML_WS_AFTER_OPEN, MXML_WS_BEFORE_CLOSE, or MXML_WS_AFTER_CLOSE. The callback function should return NULL if no whitespace should be added or the string to insert (spaces, tabs, carriage returns, and newlines) otherwise.

The following whitespace callback can be used to add whitespace to XHTML output to make it more readable in a standard text editor:

const char *
-whitespace_cb(mxml_node_t *node, int where)
+whitespace_cb(void *cbdata, mxml_node_t *node, mxml_ws_t where)
 {
   const char *element;
 
@@ -744,23 +747,23 @@ whitespace_cb(mxml_node_t *node, int where)
 mxml_node_t *tree;
 
 fp = fopen("filename.xml", "w");
-mxmlSaveFile(tree, fp, whitespace_cb);
+mxmlSaveFile(tree, fp, whitespace_cb, /*cbdata*/NULL);
 fclose(fp);
 

Memory Management

-

Once you are done with the XML data, use the mxmlDelete function to recursively free the memory that is used for a particular node or the entire tree:

+

Once you are done with the XML data, use the mxmlDelete function to free the memory that is used for a particular node and its children:

void
 mxmlDelete(mxml_node_t *tree);
 
-

You can also use reference counting to manage memory usage. The mxmlRetain and mxmlRelease functions increment and decrement a node's use count, respectively. When the use count goes to zero, mxmlRelease automatically calls mxmlDelete to actually free the memory used by the node tree. New nodes start with a use count of 1.

+

You can also use reference counting to manage memory usage. The mxmlRetain and mxmlRelease functions increment and decrement a node's use count, respectively. When the use count goes to zero, mxmlRelease calls mxmlDelete to actually free the memory used by the node tree. New nodes start with a use count of 1.

More About Nodes

Element Nodes

-

Element (MXML_TYPE_ELEMENT) nodes are created using the mxmlNewElement function. Element attributes are set using the mxmlElementSetAttr and mxmlElementSetAttrf functions and cleared using the mxmlElementDeleteAttr function:

+

Element (MXML_TYPE_ELEMENT) nodes are created using the mxmlNewElement function. Element attributes are set using the mxmlElementSetAttr and mxmlElementSetAttrf functions and cleared using the mxmlElementClearAttr function:

mxml_node_t *
 mxmlNewElement(mxml_node_t *parent, const char *name);
 
 void
-mxmlElementDeleteAttr(mxml_node_t *node, const char *name);
+mxmlElementClearAttr(mxml_node_t *node, const char *name);
 
 void
 mxmlElementSetAttr(mxml_node_t *node, const char *name,
@@ -770,11 +773,7 @@ mxmlElementSetAttr(mxml_node_t *node, const const char *name,
                     const char *format, ...);
 
-

Child nodes are added using the various mxmlNew functions. The top (root) node must be an element, usually created by the mxmlNewXML function:

-
mxml_node_t *
-mxmlNewXML(const char *version);
-
-

The mxmlGetElement function retrieves the element name, the mxmlElementGetAttr function retrieves the value string for a named attribute associated with the element. The mxmlElementGetAttrByIndex and mxmlElementGetAttrCount functions retrieve attributes by index:

+

The mxmlGetElement function retrieves the element name while the mxmlElementGetAttr function retrieves the value string for a named attribute associated with the element. The mxmlElementGetAttrByIndex and mxmlElementGetAttrCount functions retrieve attributes by index:

const char *
 mxmlGetElement(mxml_node_t *node);
 
@@ -789,48 +788,64 @@ size_t
 mxmlElementGetAttrCount(mxml_node_t *node);
 

CDATA Nodes

-

CDATA (MXML_TYPE_CDATA) nodes are created using the mxmlNewCDATA function:

-
mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string);
+

CDATA (MXML_TYPE_CDATA) nodes are created using the mxmlNewCDATA and mxmlNewCDATAf functions:

+
mxml_node_t *
+mxmlNewCDATA(mxml_node_t *parent, const char *string);
+
+mxml_node_t *
+mxmlNewCDATAf(mxml_node_t *parent, const char *format, ...);
 
-

The mxmlGetCDATA function retrieves the CDATA string pointer for a node:

-
const char *mxmlGetCDATA(mxml_node_t *node);
+

The mxmlGetCDATA function retrieves the CDATA string pointer for a node:

+
const char *
+mxmlGetCDATA(mxml_node_t *node);
 

Comment Nodes

-

Comment (MXML_TYPE_COMMENT) nodes are created using the mxmlNewComment function, for example:

+

Comment (MXML_TYPE_COMMENT) nodes are created using the mxmlNewComment and mxmlNewCommentf functions, for example:

mxml_node_t *node = mxmlNewComment(" This is a comment ");
+
+mxml_node_t *node = mxmlNewCommentf(" This is comment %d ", 42);
 
-

Similarly, the mxmlGetComment function retrieves the comment string pointer for a node:

+

Similarly, the mxmlGetComment function retrieves the comment string pointer for a node:

const char *comment = mxmlGetComment(node);
 /* returns " This is a comment " */
 

Processing Instruction Nodes

-

Processing instruction (MXML_TYPE_DIRECTIVE) nodes are created using the mxmlNewDirective function:

+

Processing instruction (MXML_TYPE_DIRECTIVE) nodes are created using the mxmlNewDirective and mxmlNewDirectivef functions:

mxml_node_t *node = mxmlNewDirective("xml-stylesheet type=\"text/css\" href=\"style.css\"");
+
+mxml_node_t *node = mxmlNewDirectivef("xml version=\"%s\"", version);
 
-

The mxmlGetDirective function retrieves the processing instruction string for a node:

+

The mxmlGetDirective function retrieves the processing instruction string for a node:

const char *instr = mxmlGetElement(node);
 /* returns "xml-stylesheet type=\"text/css\" href=\"style.css\"" */
 
+

The mxmlNewXML function can be used to create the top-level "xml" processing instruction with an associated version number:

+
mxml_node_t *
+mxmlNewXML(const char *version);
+

Integer Nodes

-

Integer (MXML_TYPE_INTEGER) nodes are created using the mxmlNewInteger function:

+

Integer (MXML_TYPE_INTEGER) nodes are created using the mxmlNewInteger function:

mxml_node_t *
 mxmlNewInteger(mxml_node_t *parent, long integer);
 
-

The mxmlGetInteger function retrieves the integer value for a node:

+

The mxmlGetInteger function retrieves the integer value for a node:

long
 mxmlGetInteger(mxml_node_t *node);
 

Opaque String Nodes

-

Opaque string (MXML_TYPE_OPAQUE) nodes are created using the mxmlNewOpaque function:

+

Opaque string (MXML_TYPE_OPAQUE) nodes are created using the mxmlNewOpaque and mxmlNewOpaquef functions:

mxml_node_t *
 mxmlNewOpaque(mxml_node_t *parent, const char *opaque);
+
+mxml_node_t *
+mxmlNewOpaquef(mxml_node_t *parent, const char *format, ...);
 
-

The mxmlGetOpaque function retrieves the opaque string pointer for a node:

+

The mxmlGetOpaque function retrieves the opaque string pointer for a node:

const char *
 mxmlGetOpaque(mxml_node_t *node);
 

Text Nodes

-

Whitespace-delimited text string (MXML_TYPE_TEXT) nodes are created using the mxmlNewText and mxmlNewTextf functions. Each text node consists of a text string and (leading) whitespace boolean value.

+

Whitespace-delimited text string (MXML_TYPE_TEXT) nodes are created using the mxmlNewText and mxmlNewTextf functions. Each text node consists of a text string and (leading) whitespace boolean value.

mxml_node_t *
 mxmlNewText(mxml_node_t *parent, bool whitespace,
             const char *string);
@@ -839,23 +854,23 @@ mxml_node_t *
 mxmlNewTextf(mxml_node_t *parent, bool whitespace,
              const char *format, ...);
 
-

The mxmlGetText function retrieves the text string pointer and whitespace boolean value for a node:

+

The mxmlGetText function retrieves the text string pointer and whitespace boolean value for a node:

const char *
 mxmlGetText(mxml_node_t *node, bool *whitespace);
 

Real Number Nodes

-

Real number (MXML_TYPE_REAL) nodes are created using the mxmlNewReal function:

+

Real number (MXML_TYPE_REAL) nodes are created using the mxmlNewReal function:

mxml_node_t *
 mxmlNewReal(mxml_node_t *parent, double real);
 
-

The mxmlGetReal function retrieves the real number for a node:

+

The mxmlGetReal function retrieves the real number for a node:

double
 mxmlGetReal(mxml_node_t *node);
 

Locating Data in an XML Document

Mini-XML provides many functions for enumerating, searching, and indexing XML documents.

Finding Nodes

-

The mxmlFindPath function finds the (first) value node under a specific element using a "path":

+

The mxmlFindPath function finds the (first) value node under a specific element using a "path":

mxml_node_t *
 mxmlFindPath(mxml_node_t *node, const char *path);
 
@@ -864,7 +879,7 @@ mxmlFindPath(mxml_node_t *node, const "data/*/node");
-

The mxmlFindElement function can be used to find a named element, optionally matching an attribute and value:

+

The mxmlFindElement function can be used to find a named element, optionally matching an attribute and value:

mxml_node_t *
 mxmlFindElement(mxml_node_t *node, mxml_node_t *top,
                 const char *element, const char *attr,
@@ -881,7 +896,7 @@ node = mxmlFindElement(tree, tree, "a", /* Find the first "a" element with "href" to a URL */
 node = mxmlFindElement(tree, tree, "a", "href",
-                       "http://michaelrsweet.github.io/",
+                       "http://msweet.org/",
                        MXML_DESCEND_ALL);
 
 /* Find the first element with a "src" attribute*/
@@ -914,7 +929,7 @@ node = mxmlFindElement(tree, tree, NULL, "src"
 
 

Iterating Nodes

-

While the mxmlFindNode and mxmlFindPath functions will find a particular element node, sometimes you need to iterate over all nodes. The mxmlWalkNext and mxmlWalkPrev functions can be used to iterate through the XML node tree:

+

While the mxmlFindNode and mxmlFindPath functions will find a particular element node, sometimes you need to iterate over all nodes. The mxmlWalkNext and mxmlWalkPrev functions can be used to iterate through the XML node tree:

mxml_node_t *
 mxmlWalkNext(mxml_node_t *node, mxml_node_t *top,
              int descend);
@@ -955,7 +970,7 @@ val7
 val8
 

Indexing

-

The mxmlIndexNew function allows you to create an index of nodes for faster searching and enumeration:

+

The mxmlIndexNew function allows you to create an index of nodes for faster searching and enumeration:

mxml_index_t *
 mxmlIndexNew(mxml_node_t *node, const char *element,
              const char *attr);
@@ -964,7 +979,7 @@ mxmlIndexNew(mxml_node_t *node, const For example, the following code creates an index of all "id" values in an XML document:

mxml_index_t *ind = mxmlIndexNew(xml, NULL, "id");
 
-

Once the index is created, the mxmlIndexFind function can be used to find a matching node:

+

Once the index is created, the mxmlIndexFind function can be used to find a matching node:

mxml_node_t *
 mxmlIndexFind(mxml_index_t *ind, const char *element,
               const char *value);
@@ -972,7 +987,7 @@ mxmlIndexFind(mxml_index_t *ind, const For example, the following code will find the element whose "id" string is "42":

mxml_node_t *node = mxmlIndexFind(ind, NULL, "42");
 
-

Alternately, the mxmlIndexReset and mxmlIndexEnum functions can be used to enumerate the nodes in the index:

+

Alternately, the mxmlIndexReset and mxmlIndexEnum functions can be used to enumerate the nodes in the index:

mxml_node_t *
 mxmlIndexReset(mxml_index_t *ind);
 
@@ -989,21 +1004,21 @@ mxmlIndexEnum(mxml_index_t *ind);
   ... do something ...
 }
 
-

The mxmlIndexCount function returns the number of nodes in the index:

+

The mxmlIndexCount function returns the number of nodes in the index:

size_t
 mxmlIndexGetCount(mxml_index_t *ind);
 
-

Finally, the mxmlIndexDelete function frees all memory associated with the index:

+

Finally, the mxmlIndexDelete function frees all memory associated with the index:

void
 mxmlIndexDelete(mxml_index_t *ind);
 

Custom Data Types

Mini-XML supports custom data types via per-thread load and save callbacks. Only a single set of callbacks can be active at any time for the current thread, however your callbacks can store additional information in order to support multiple custom data types as needed. The MXML_TYPE_CUSTOM node type identifies custom data nodes.

-

The mxmlGetCustom function retrieves the custom value pointer for a node.

+

The mxmlGetCustom function retrieves the custom value pointer for a node.

const void *
 mxmlGetCustom(mxml_node_t *node);
 
-

Custom (MXML_TYPE_CUSTOM) nodes are created using the mxmlNewCustom function or using a custom per-thread load callbacks specified using the mxmlSetCustomHandlers function:

+

Custom (MXML_TYPE_CUSTOM) nodes are created using the mxmlNewCustom function or using a custom per-thread load callbacks specified using the mxmlSetCustomHandlers function:

typedef void (*mxml_custom_destroy_cb_t)(void *);
 typedef bool (*mxml_custom_load_cb_t)(mxml_node_t *, const char *);
 typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *);
@@ -1127,12 +1142,12 @@ save_custom(mxml_node_t *node)
   return (strdup(data));
 }
 
-

You register the callback functions using the mxmlSetCustomHandlers function:

-
mxmlSetCustomHandlers(load_custom, save_custom);
+

You register the callback functions using the mxmlSetCustomCallbacks function:

+
mxmlSetCustomCallbacks(load_custom, save_custom);
 

SAX (Stream) Loading of Documents

Mini-XML supports an implementation of the Simple API for XML (SAX) which allows you to load and process an XML document as a stream of nodes. Aside from allowing you to process XML documents of any size, the Mini-XML implementation also allows you to retain portions of the document in memory for later processing.

-

The mxmlLoadFd, mxmlLoadFile, mxmlLoadFilename, mxmlLoadIO, and mxmlLoadString functions support a SAX callback and associated data. The callback function receives the data pointer you supplied, the node, and an event code and returns true to continue processing or false to stop:

+

The mxmlLoadXxx functions support a SAX callback and associated data. The callback function receives the data pointer you supplied, the node, and an event code and returns true to continue processing or false to stop:

bool
 sax_cb(void *cbdata, mxml_node_t *node,
        mxml_sax_event_t event)
@@ -1160,7 +1175,7 @@ sax_cb(void *cbdata, mxml_node_t *node,
 
  • 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. For example, the following SAX callback will retain all nodes, effectively simulating a normal in-memory load:

    +

    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. For example, the following SAX callback will retain all nodes, effectively simulating a normal in-memory load:

    bool
     sax_cb(void *cbdata, mxml_node_t *node, mxml_sax_event_t event)
     {
    @@ -1269,7 +1284,7 @@ print_children(mxml_node_t *parent)
     
     
  • SAX callbacks now return a boolean value.

  • -
  • The mxmlSAXLoadXxx functions have been removed in favor of passing the SAX callback function and data pointers to the mxmlLoadXxx functions.

    +
  • The mxmlSAXLoadXxx functions have been removed in favor of passing the SAX callback function and data pointers to the mxmlLoadXxx functions.

  • Node types are now named MXML_TYPE_foo instead of MXML_foo.

  • @@ -1281,8 +1296,12 @@ print_children(mxml_node_t *parent)
  • Comment nodes ("<!-- ... -->") now have their own type (MXML_TYPE_COMMENT).

  • +
  • Custom node callbacks are now set using the mxmlSetCustomCallbacks function instead of mxmlSetCustomHandlers.

    +
  • Declaration nodes ("<!...>") now have their own type (MXML_TYPE_DECLARATION).

  • +
  • Element attributes are now cleared with the mxmlElementClearAttr function instead of mxmlElementDeleteAttr.

    +
  • Processing instruction/directive nodes ("<?...?>") now have their own type (MXML_TYPE_DIRECTIVE).

  • Integer nodes (MXML_TYPE_INTEGER) now use the long type.

    @@ -1324,10 +1343,10 @@ void mxmlDelete(mxml_node_t *node);

    Discussion

    If the specified node has a parent, this function first removes the node from its parent using the mxmlRemove function.

    -

    mxmlElementDeleteAttr

    +

    mxmlElementClearAttr

    Delete an attribute.

    -void mxmlElementDeleteAttr(mxml_node_t *node, const char *name);

    +void mxmlElementClearAttr(mxml_node_t *node, const char *name);

    Parameters

    diff --git a/mxml-attr.c b/mxml-attr.c index a3246e0..79d98e2 100644 --- a/mxml-attr.c +++ b/mxml-attr.c @@ -20,18 +20,18 @@ static bool mxml_set_attr(mxml_node_t *node, const char *name, char *value); // -// 'mxmlElementDeleteAttr()' - Delete an attribute. +// 'mxmlElementClearAttr()' - Delete an attribute. // void -mxmlElementDeleteAttr(mxml_node_t *node,// I - Element - const char *name)// I - Attribute name +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("mxmlElementDeleteAttr(node=%p, name=\"%s\")\n", node, name ? name : "(null)"); + MXML_DEBUG("mxmlElementClearAttr(node=%p, name=\"%s\")\n", node, name ? name : "(null)"); // Range check input... if (!node || node->type != MXML_TYPE_ELEMENT || !name) @@ -40,7 +40,7 @@ mxmlElementDeleteAttr(mxml_node_t *node,// I - Element // Look for the attribute... for (i = node->value.element.num_attrs, attr = node->value.element.attrs; i > 0; i --, attr ++) { - MXML_DEBUG("mxmlElementDeleteAttr: %s=\"%s\"\n", attr->name, attr->value); + MXML_DEBUG("mxmlElementClearAttr: %s=\"%s\"\n", attr->name, attr->value); if (!strcmp(attr->name, name)) { diff --git a/mxml.h b/mxml.h index cc69bbf..65d2687 100644 --- a/mxml.h +++ b/mxml.h @@ -144,7 +144,7 @@ extern void mxmlAdd(mxml_node_t *parent, mxml_add_t add, mxml_node_t *child, mx extern void mxmlDelete(mxml_node_t *node); -extern void mxmlElementDeleteAttr(mxml_node_t *node, const char *name); +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, int idx, const char **name); extern size_t mxmlElementGetAttrCount(mxml_node_t *node);
    node