Add mxmlNewXML() function and use it.

More updates to the manual.
This commit is contained in:
Michael R Sweet 2007-04-19 21:12:08 +00:00
parent 94ad843d35
commit 0f8052e766
10 changed files with 392 additions and 221 deletions

25
CHANGES
View File

@ -1,11 +1,12 @@
CHANGES - 2007-04-18
CHANGES - 2007-04-19
--------------------
CHANGES IN Mini-XML 2.3
- Added two exceptions to the LGPL to support static
linking of applications against Mini-XML.
- Added mxmlElementSetAttrf() function (STR #43)
linking of applications against Mini-XML
- Added a mxmlNewXML() function
- Added a mxmlElementSetAttrf() function (STR #43)
- Added snprintf() emulation function for test program (STR
#32)
- Added the _CRT_SECURE_NO_DEPRECATE definition when
@ -15,31 +16,31 @@ CHANGES IN Mini-XML 2.3
- mxmlLoad*() did not detect missing close tags at the end
of an XML document (STR #45)
- Added user_data and ref_count members to mxml_node_t
structure.
structure
- Added mxmlReleaseNode() and mxmlRetainNode() APIs for
reference-counted nodes.
reference-counted nodes
- Added mxmlSetWrapMargin() to control the wrapping of XML
output.
output
- Added conditional check for EINTR error code for
certain Windows compilers that do not define it (STR
#33)
- The mxmldoc program now generates correct HTML 4.0
output (previously it generated invalid XHTML...)
output - previously it generated invalid XHTML
- The mxmldoc program now supports "@deprecated@,
"@private@", and "@since version@" comments.
"@private@", and "@since version@" comments
- Fixed function and enumeration type bugs in mxmldoc.
- Fixed XML schema for mxmldoc.
- Fixed the XML schema for mxmldoc
- The mxmldoc program now supports --intro, --section,
and --title options.
and --title options
- The mxmlLoad*() functions could leak a node on an error
(STR #27)
- The mxml_vsnprintf() function could get in an infinite
loop on a buffer overflow (STR #25)
- Added new mxmlNewCDATA() and mxmlSetCDATA() functions
to create and set CDATA nodes, which are really just
special element nodes.
special element nodes
- Added new MXML_IGNORE type and MXML_IGNORE_CB callback
to ignore non-element nodes (i.e. whitespace)
to ignore non-element nodes, e.g. whitespace
- mxmlLoad*() crashed when reporting an error in some
invalid XML (STR #23)

View File

@ -21,11 +21,11 @@ XML data files:</p>
<ul>
<li><tt>MXML_INTEGER_CALLBACK</tt> - All data nodes contain
whitespace-separated integers.</li>
<li><tt>MXML_INTEGER_CALLBACK</tt> - All data nodes
contain whitespace-separated integers.</li>
<li><tt>MXML_OPAQUE_CALLBACK</tt> - All data nodes contain
opaque strings ("CDATA").</li>
<li><tt>MXML_OPAQUE_CALLBACK</tt> - All data nodes
contain opaque strings ("CDATA").</li>
<li><tt>MXML_REAL_CALLBACK</tt> - All data nodes contain
whitespace-separated floating-point numbers.</li>
@ -51,21 +51,18 @@ type to return.</p>
child nodes:</p>
<pre>
/*
* 'type_cb()' - XML data type callback for mxmlLoadFile()...
*/
mxml_type_t /* O - Data type */
type_cb(mxml_node_t *node) /* I - Element node */
mxml_type_t
type_cb(mxml_node_t *node)
{
const char *type; /* Type string */
const char *type;
/*
* You can lookup attributes and/or use the element name, hierarchy, etc...
* You can lookup attributes and/or use the
* element name, hierarchy, etc...
*/
if ((type = mxmlElementGetAttr(node, "type")) == NULL)
type = mxmlElementGetAttr(node, "type");
if (type == NULL)
type = node->value.element.name;
if (!strcmp(type, "integer"))
@ -84,10 +81,10 @@ call any of the load functions:</p>
<pre>
FILE *fp;
<a href='#mxml_node_t'>mxml_node_t</a> *tree;
mxml_node_t *tree;
fp = fopen("filename.xml", "r");
tree = <a href='#mxmlLoadFile'>mxmlLoadFile</a>(NULL, fp, <b>type_cb</b>);
tree = mxmlLoadFile(NULL, fp, <b>type_cb</b>);
fclose(fp);
</pre>
@ -108,54 +105,66 @@ of <tt>MXML_WS_BEFORE_OPEN</tt>, <tt>MXML_WS_AFTER_OPEN</tt>,
<tt>MXML_WS_BEFORE_CLOSE</tt>, or <tt>MXML_WS_AFTER_CLOSE</tt>.
The callback function should return <tt>NULL</tt> if no
whitespace should be added and 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:</p>
tabs, carriage returns, and newlines) otherwise.</p>
<p>The following whitespace callback can be used to add
whitespace to XHTML output to make it more readable in a standard
text editor:</p>
<!-- NEW PAGE -->
<pre>
/*
* 'whitespace_cb()' - Let the mxmlSaveFile() function know when to insert
* newlines and tabs...
*/
const char * /* O - Whitespace string or NULL */
whitespace_cb(mxml_node_t *node, /* I - Element node */
int where) /* I - Open or close tag? */
const char *
whitespace_cb(mxml_node_t *node,
int where)
{
const char *name; /* Name of element */
const char *name;
/*
* We can conditionally break to a new line before or after any element.
* These are just common HTML elements...
* We can conditionally break to a new line
* before or after any element. These are
* just common HTML elements...
*/
name = node->value.element.name;
if (!strcmp(name, "html") || !strcmp(name, "head") || !strcmp(name, "body") ||
!strcmp(name, "pre") || !strcmp(name, "p") ||
!strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") ||
!strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6"))
if (!strcmp(name, "html") ||
!strcmp(name, "head") ||
!strcmp(name, "body") ||
!strcmp(name, "pre") ||
!strcmp(name, "p") ||
!strcmp(name, "h1") ||
!strcmp(name, "h2") ||
!strcmp(name, "h3") ||
!strcmp(name, "h4") ||
!strcmp(name, "h5") ||
!strcmp(name, "h6"))
{
/*
* Newlines before open and after close...
* Newlines before open and after
* close...
*/
if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE)
if (where == MXML_WS_BEFORE_OPEN ||
where == MXML_WS_AFTER_CLOSE)
return ("\n");
}
else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul"))
else if (!strcmp(name, "dl") ||
!strcmp(name, "ol") ||
!strcmp(name, "ul"))
{
/*
* Put a newline before and after list elements...
* Put a newline before and after list
* elements...
*/
return ("\n");
}
else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li"))
else if (!strcmp(name, "dd") ||
!strcmp(name, "dt") ||
!strcmp(name, "li"))
{
/*
* Put a tab before &lt;li>'s, &lt;dd>'s, and &lt;dt>'s, and a newline after them...
* Put a tab before &lt;li>'s, * &lt;dd>'s,
* and &lt;dt>'s, and a newline after them...
*/
if (where == MXML_WS_BEFORE_OPEN)
@ -172,20 +181,20 @@ output to make it more readable in a standard text editor:</p>
}
</pre>
<!-- NEW PAGE -->
<p>To use this callback function, simply use the name when you
call any of the save functions:</p>
<pre>
FILE *fp;
<a href='#mxml_node_t'>mxml_node_t</a> *tree;
mxml_node_t *tree;
fp = fopen("filename.xml", "w");
<a href='#mxmlSaveFile'>mxmlSaveFile</a>(tree, fp, whitespace_cb);
mxmlSaveFile(tree, fp, <b>whitespace_cb</b>);
fclose(fp);
</pre>
<!-- NEED 10 -->
<h2>Custom Data Types</h2>
<p>Mini-XML supports custom data types via global load and save
@ -205,22 +214,21 @@ following:</p>
<pre>
typedef struct
{
unsigned year, /* Year */
month, /* Month */
day, /* Day */
hour, /* Hour */
minute, /* Minute */
second; /* Second */
time_t unix; /* UNIX time value */
unsigned year, /* Year */
month, /* Month */
day, /* Day */
hour, /* Hour */
minute, /* Minute */
second; /* Second */
time_t unix; /* UNIX time */
} iso_date_time_t;
int /* I - 0 on success, -1 on error */
load_custom(mxml_node_t *node, /* I - Node */
const char *data) /* I - Value */
int
load_custom(mxml_node_t *node,
const char *data)
{
iso_date_time_t *dt; /* Date/time value */
struct tm tmdata; /* UNIX time data */
iso_date_time_t *dt;
struct tm tmdata;
/*
* Allocate data structure...
@ -229,16 +237,19 @@ following:</p>
dt = calloc(1, sizeof(iso_date_time_t));
/*
* Try reading 6 unsigned integers from the data string...
* Try reading 6 unsigned integers from 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)
&(dt->year), &(dt->month),
&(dt->day), &(dt->hour),
&(dt->minute),
&(dt->second)) != 6)
{
/*
* Unable to read numbers, free the data structure and return an
* error...
* Unable to read numbers, free the data
* structure and return an error...
*/
free(dt);
@ -266,7 +277,8 @@ following:</p>
}
/*
* Convert ISO time to UNIX time in seconds...
* Convert ISO time to UNIX time in
* seconds...
*/
tmdata.tm_year = dt->year - 1900;
@ -276,10 +288,11 @@ following:</p>
tmdata.tm_min = dt->minute;
tmdata.tm_sec = dt->second;
dt->unix = gmtime(&tmdata);
dt->unix = gmtime(&amp;tmdata);
/*
* Assign custom node data and destroy function pointers...
* Assign custom node data and destroy
* function pointers...
*/
node->value.custom.data = dt;
@ -300,24 +313,24 @@ allocated custom data for the node and a pointer to a destructor
function which will free the custom data when the node is
deleted.</p>
<!-- NEED 3in -->
<p>The save callback receives the node pointer and returns an
allocated string containing the custom data value. The following
save callback could be used for our ISO date/time type:</p>
<pre>
char * /* I - Allocated string */
save_custom(mxml_node_t *node) /* I - Node */
char *
save_custom(mxml_node_t *node)
{
char data[255]; /* Data string */
iso_date_time_t *dt; /* ISO date/time pointer */
char data[255];
iso_date_time_t *dt;
dt = (iso_date_time_t *)node->custom.data;
snprintf(data, sizeof(data), "%04u-%02u-%02uT%02u:%02u:%02uZ",
dt->year, dt->month, dt->day, dt->hour,
dt->minute, dt->second);
snprintf(data, sizeof(data),
"%04u-%02u-%02uT%02u:%02u:%02uZ",
dt->year, dt->month, dt->day,
dt->hour, dt->minute, dt->second);
return (strdup(data));
}
@ -328,7 +341,8 @@ href='#mxmlSetCustomHandlers'><tt>mxmlSetCustomHandlers()</tt></a>
function:</p>
<pre>
mxmlSetCustomHandlers(load_custom, save_custom);
mxmlSetCustomHandlers(<b>load_custom</b>,
<b>save_custom</b>);
</pre>
@ -344,30 +358,32 @@ without leaking memory.</p>
href='#mxmlSetElement'><tt>mxmlSetElement()</tt></a>, <a
href='#mxmlSetInteger'><tt>mxmlSetInteger()</tt></a>, <a
href='#mxmlSetOpaque'><tt>mxmlSetOpaque()</tt></a>, <a
href='#mxmlSetReal'><tt>mxmlSetReal()</tt></a>, and <a
href='#mxmlSetText'><tt>mxmlSetText()</tt></a> functions. For
href='#mxmlSetReal'><tt>mxmlSetReal()</tt></a>, <a
href='#mxmlSetText'><tt>mxmlSetText()</tt></a>, and <a
href='#mxmlSetTextf'><tt>mxmlSetTextf()</tt></a> functions. For
example, use the following function call to change a text node
to contain the text "new" with leading whitespace:</p>
<pre>
<a href='#mxml_node_t'>mxml_node_t</a> *node;
mxml_node_t *node;
<a href='#mxmlSetText'>mxmlSetText</a>(node, 1, "new");
mxmlSetText(node, 1, "new");
</pre>
<h2>Formatted Text</h2>
<p>The <a href='#mxmlNewTextf'><tt>mxmlNewTextf()</tt></a> and
<a href='#mxmlSetTextf'><tt>mxmlSetTextf()</tt></a> functions
create and change text nodes, respectively, using
<tt>printf</tt>-style format strings and arguments. For example,
use the following function call to create a new text node:</p>
<p>The <a href='#mxmlNewTextf'><tt>mxmlNewTextf()</tt></a> and <a
href='#mxmlSetTextf'><tt>mxmlSetTextf()</tt></a> functions create
and change text nodes, respectively, using <tt>printf</tt>-style
format strings and arguments. For example, use the following
function call to create a new text node containing a constructed
filename:</p>
<pre>
<a href='#mxml_node_t'>mxml_node_t</a> *node;
mxml_node_t</a> *node;
node = <a href='#mxmlNewTextf'>mxmlNewTextf</a>(node, 1, "%s/%s",
node = mxmlNewTextf(node, 1, "%s/%s",
path, filename);
</pre>
@ -376,7 +392,7 @@ use the following function call to create a new text node:</p>
<p>Mini-XML provides functions for managing indices of nodes.
The current implementation provides the same functionality as
the <a href='#mxmlFindElement'><tt>mxmlFindElement()</tt></a>.
<a href='#mxmlFindElement'><tt>mxmlFindElement()</tt></a>.
The advantage of using an index is that searching and
enumeration of elements is significantly faster. The only
disadvantage is that each index is a static snapshot of the XML
@ -392,10 +408,11 @@ href='#mxml_index_t'><tt>mxml_index_t</tt></a> structures. The
creates a new index:</p>
<pre>
<a href='#mxml_node_t'>mxml_node_t</a> *tree;
<a href='#mxml_index_t'>mxml_index_t</a> *ind;
mxml_node_t *tree;
mxml_index_t *ind;
ind = <a href='#mxmlIndexNew'>mxmlIndexNew</a>(tree, "element", "attribute");
ind = mxmlIndexNew(tree, "element",
"attribute");
</pre>
<p>The first argument is the XML node tree to index. Normally this
@ -424,8 +441,7 @@ function enumerates each of the nodes in the index and can be
used in a loop as follows:</p>
<pre>
<a href='#mxml_node_t'>mxml_node_t</a> *node;
<a href='#mxml_index_t'>mxml_index_t</a> *ind;
mxml_node_t *node;
mxmlIndexReset(ind);
@ -441,12 +457,13 @@ attribute value in the index. It can be used to find all
matching elements in an index, as follows:</p>
<pre>
<a href='#mxml_node_t'>mxml_node_t</a> *node;
<a href='#mxml_index_t'>mxml_index_t</a> *ind;
mxml_node_t *node;
mxmlIndexReset(ind);
while ((node = mxmlIndexFind(ind, "element", "attr-value")) != NULL)
while ((node = mxmlIndexFind(ind, "element",
"attr-value"))
!= NULL)
{
// do something with node
}
@ -458,14 +475,11 @@ to return all elements or attributes in the index. Passing
<tt>NULL</tt> for both the element name and attribute value
is equivalent to calling <tt>mxmlIndexEnum</tt>.</p>
<!-- NEED 2in -->
<p>When you are done using the index, delete it using the
<a href='#mxmlIndexDelete()'><tt>mxmlIndexDelete()</tt></a>
function:</p>
<pre>
<a href='#mxml_index_t'>mxml_index_t</a> *ind;
mxmlIndexDelete(ind);
</pre>

View File

@ -44,18 +44,20 @@ which determines which value you want to look at in the <a
href='#mxml_value_t'><tt>value</tt></a> union.</p>
<p>New nodes can be created using the <a
href='#mxmlNewElement'><tt>mxmlNewElement()</tt></a>,
<a
href='#mxmlNewInteger'><tt>mxmlNewInteger()</tt></a>,
<a
href='#mxmlNewOpaque'><tt>mxmlNewOpaque()</tt></a>,
<a href='#mxmlNewReal'><tt>mxmlNewReal()</tt></a>,
and <a
href='#mxmlNewText'><tt>mxmlNewText()</tt></a>
functions. Only elements can have child nodes, and the top node
must be an element, usually "?xml".</p>
href='#mxmlNewElement'><tt>mxmlNewElement()</tt></a>, <a
href='#mxmlNewInteger'><tt>mxmlNewInteger()</tt></a>, <a
href='#mxmlNewOpaque'><tt>mxmlNewOpaque()</tt></a>, <a
href='#mxmlNewReal'><tt>mxmlNewReal()</tt></a>, <a
href='#mxmlNewText'><tt>mxmlNewText()</tt></a> <a
href='#mxmlNewTextf'><tt>mxmlNewTextf()</tt></a> <a
href='#mxmlNewXML'><tt>mxmlNewXML()</tt></a> functions. Only
elements can have child nodes, and the top node must be an
element, usually <tt>&lt;?xml version="1.0"?&gt;</tt>.</p>
<p>Each node has pointers for the node above (<tt>parent</tt>),
<p>Each node has a <tt>user_data</tt> member which allows you to
associate application-specific data with each node as needed.</p>
<p>Node also have pointers for the node above (<tt>parent</tt>),
below (<tt>child</tt>), to the left (<tt>prev</tt>), and to the
right (<tt>next</tt>) of the current node. If you have an XML
file like the following:</p>
@ -73,21 +75,20 @@ file like the following:</p>
&lt;/group&gt;
&lt;node&gt;val7&lt;/node&gt;
&lt;node&gt;val8&lt;/node&gt;
&lt;node&gt;val9&lt;/node&gt;
&lt;/data&gt;
</pre>
<p>the node tree returned by <tt>mxmlLoadFile()</tt> would look
like the following in memory:</p>
<p>the node tree for the file would look like the following in
memory:</p>
<pre>
?xml
|
data
|
node - node - node - group - node - node - node
| | | | | | |
val1 val2 val3 | val7 val8 val9
node - node - node - group - node - node
| | | | | |
val1 val2 val3 | val7 val8
|
node - node - node
| | |
@ -106,7 +107,80 @@ particular node or the entire tree:</p>
mxmlDelete(tree);
</pre>
<!-- NEED 15 -->
<!-- NEW PAGE -->
<h2>Creating XML Documents</h2>
<p>You can create and update XML documents in memory using the
various <tt>mxmlNew</tt> functions. The following code will
create the XML document described in the previous section:</p>
<pre>
mxml_node_t *xml; /* &lt;?xml ... ?&gt; */
mxml_node_t *data; /* &lt;data&gt; */
mxml_node_t *node; /* &lt;node&gt; */
mxml_node_t *group; /* &lt;group&gt; */
xml = mxmlNewXML("1.0");
data = mxmlNewElement(xml, "data");
node = mxmlNewElement(data, "node");
mxmlNewText(node, 0, "val1");
node = mxmlNewElement(data, "node");
mxmlNewText(node, 0, "val2");
node = mxmlNewElement(data, "node");
mxmlNewText(node, 0, "val3");
group = mxmlNewElement(data, "group");
node = mxmlNewElement(group, "node");
mxmlNewText(node, 0, "val4");
node = mxmlNewElement(group, "node");
mxmlNewText(node, 0, "val5");
node = mxmlNewElement(group, "node");
mxmlNewText(node, 0, "val6");
node = mxmlNewElement(data, "node");
mxmlNewText(node, 0, "val7");
node = mxmlNewElement(data, "node");
mxmlNewText(node, 0, "val8");
</pre>
<p>We start by creating the <tt>&lt;?xml version="1.0"?&gt;</tt>
node common to all XML files using the <a
href="#mxmlNewXML"><tt>mxmlNewXML</tt></a> function:</p>
<pre>
xml = mxmlNewXML("1.0");
</pre>
<p>We then create the <tt>&lt;data&gt;</tt> node used for this
document using the <a
href="#mxmlNewElement"><tt>mxmlNewElement</tt></a> function. The
first argument specifies the parent node (<tt>xml</tt>) while the
second specifies the element name (<tt>data</tt>):</p>
<pre>
data = mxmlNewElement(xml, "data");
</pre>
<p>Each <tt>&lt;node&gt;...&lt;/node&gt;</tt> in the file is
created using the <tt>mxmlNewElement</tt> and <a
href="#mxmlNewText"><tt>mxmlNewText</tt></a> functions. The first
argument of <tt>mxmlNewText</tt> specifies the parent node
(<tt>node</tt>). 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:</p>
<pre>
node = mxmlNewElement(data, "node");
mxmlNewText(node, 0, "val1");
</pre>
<p>The resulting in-memory XML document can then be saved or
processed just like one loaded from disk or a string.</p>
<!-- NEW PAGE -->
<h2>Loading XML</h2>
<p>You load an XML file using the <a
@ -115,10 +189,11 @@ function:</p>
<pre>
FILE *fp;
<a href='#mxml_node_t'>mxml_node_t</a> *tree;
mxml_node_t *tree;
fp = fopen("filename.xml", "r");
tree = <a href='#mxmlLoadFile'>mxmlLoadFile</a>(NULL, fp, MXML_NO_CALLBACK);
tree = mxmlLoadFile(NULL, fp,
MXML_TEXT_CALLBACK);
fclose(fp);
</pre>
@ -133,14 +208,18 @@ opened by <tt>fopen()</tt> or <tt>popen()</tt>. You can also use
<tt>stdin</tt> if you are implementing an XML filter
program.</p>
<p>The third argument specifies a callback function which
returns the value type of the immediate children for a new
element node: <tt>MXML_INTEGER</tt>, <tt>MXML_OPAQUE</tt>,
<tt>MXML_REAL</tt>, or <tt>MXML_TEXT</tt>. Load callbacks are
described in detail in <a href='#LOAD_CALLBACKS'>Chapter 3</a>.
The example code uses the <tt>MXML_NO_CALLBACK</tt> constant
which specifies that all data nodes in the document contain
whitespace-separated text values.</p>
<p>The third argument specifies a callback function which returns
the value type of the immediate children for a new element node:
<tt>MXML_CUSTOM</tt>, <tt>MXML_IGNORE</tt>,
<tt>MXML_INTEGER</tt>, <tt>MXML_OPAQUE</tt>, <tt>MXML_REAL</tt>,
or <tt>MXML_TEXT</tt>. Load callbacks are described in detail in
<a href='#LOAD_CALLBACKS'>Chapter 3</a>. The example code uses
the <tt>MXML_TEXT_CALLBACK</tt> constant which specifies that all
data nodes in the document contain whitespace-separated text
values. Other standard callbacks include
<tt>MXML_IGNORE_CALLBACK</tt>, <tt>MXML_INTEGER_CALLBACK</tt>,
<tt>MXML_OPAQUE_CALLBACK</tt>, and
<tt>MXML_REAL_CALLBACK</tt>.</p>
<p>The <a href='#mxmlLoadString'><tt>mxmlLoadString()</tt></a>
function loads XML node trees from a string:</p>
@ -148,11 +227,11 @@ function loads XML node trees from a string:</p>
<!-- NEED 10 -->
<pre>
char buffer[8192];
<a href='#mxml_node_t'>mxml_node_t</a> *tree;
mxml_node_t *tree;
...
tree = <a href='#mxmlLoadString'>mxmlLoadString</a>(NULL, buffer,
MXML_NO_CALLBACK);
tree = mxmlLoadString(NULL, buffer,
MXML_TEXT_CALLBACK);
</pre>
<p>The first and third arguments are the same as used for
@ -162,6 +241,7 @@ document including the <tt>?xml</tt> element if the parent node
is <tt>NULL</tt>.</p>
<!-- NEW PAGE -->
<h2>Saving XML</h2>
<p>You save an XML file using the <a
@ -169,10 +249,10 @@ href='#mxmlSaveFile'><tt>mxmlSaveFile()</tt></a> function:</p>
<pre>
FILE *fp;
<a href='#mxml_node_t'>mxml_node_t</a> *tree;
mxml_node_t *tree;
fp = fopen("filename.xml", "w");
<a href='#mxmlSaveFile'>mxmlSaveFile</a>(tree, fp, MXML_NO_CALLBACK);
mxmlSaveFile(tree, fp, MXML_NO_CALLBACK);
fclose(fp);
</pre>
@ -186,8 +266,8 @@ by <tt>fopen()</tt> or <tt>popen()</tt>. You can also use
program.</p>
<p>The third argument is the whitespace callback to use when
saving the file. Whitespace callbacks are covered in detail in
<a href='SAVE_CALLBACKS'>Chapter 3</a>. The example code above
saving the file. Whitespace callbacks are covered in detail in <a
href='SAVE_CALLBACKS'>Chapter 3</a>. The previous example code
uses the <tt>MXML_NO_CALLBACK</tt> constant to specify that no
special whitespace handling is required.</p>
@ -199,14 +279,14 @@ functions save XML node trees to strings:</p>
<pre>
char buffer[8192];
char *ptr;
<a href='#mxml_node_t'>mxml_node_t</a> *tree;
mxml_node_t *tree;
...
<a href='#mxmlSaveString'>mxmlSaveString</a>(tree, buffer, sizeof(buffer),
mxmlSaveString(tree, buffer, sizeof(buffer),
MXML_NO_CALLBACK);
...
ptr = <a href='#mxmlSaveAllocString'>mxmlSaveAllocString</a>(tree, MXML_NO_CALLBACK);
ptr = mxmlSaveAllocString(tree, MXML_NO_CALLBACK);
</pre>
<p>The first and last arguments are the same as used for
@ -216,8 +296,25 @@ a fixed-size buffer, while <tt>mxmlSaveAllocString()</tt>
returns a string buffer that was allocated using
<tt>malloc()</tt>.</p>
<h3>Controlling Line Wrapping</h3>
<h3>Finding and Iterating Nodes</h3>
<p>When saving XML documents, Mini-XML normally wraps output
lines at column 75 so that the text is readable in terminal
windows. The <a
href='#mxmlSetWrapMargin'><tt>mxmlSetWrapMargin</tt></a> function
overrides the default wrap margin:</p>
<pre>
/* Set the margin to 132 columns */
mxmlSetWrapMargin(132);
/* Disable wrapping */
mxmlSetWrapMargin(0);
</pre>
<!-- NEW PAGE-->
<h2>Finding and Iterating Nodes</h2>
<p>The <a
href='#mxmlWalkPrev'><tt>mxmlWalkPrev()</tt></a>
@ -226,11 +323,13 @@ href='#mxmlWalkNext'><tt>mxmlWalkNext()</tt></a>functions
can be used to iterate through the XML node tree:</p>
<pre>
<a href='#mxml_node_t'>mxml_node_t</a> *node;
mxml_node_t *node;
node = <a href='#mxmlWalkPrev'>mxmlWalkPrev</a>(current, tree, MXML_DESCEND);
node = mxmlWalkPrev(current, tree,
MXML_DESCEND);
node = <a href='#mxmlWalkNext'>mxmlWalkNext</a>(current, tree, MXML_DESCEND);
node = mxmlWalkNext(current, tree,
MXML_DESCEND);
</pre>
<p>In addition, you can find a named element/node using the <a
@ -238,9 +337,9 @@ href='#mxmlFindElement'><tt>mxmlFindElement()</tt></a>
function:</p>
<pre>
<a href='#mxml_node_t'>mxml_node_t</a> *node;
mxml_node_t *node;
node = <a href='#mxmlFindElement'>mxmlFindElement</a>(tree, tree, "name",
node = mxmlFindElement(tree, tree, "name",
"attr", "value",
MXML_DESCEND);
</pre>
@ -249,34 +348,43 @@ function:</p>
arguments can be passed as <tt>NULL</tt> to act as wildcards,
e.g.:</p>
<!-- NEED 4 -->
<pre>
/* Find the first "a" element */
node = <a href='#mxmlFindElement'>mxmlFindElement</a>(tree, tree, "a",
node = mxmlFindElement(tree, tree, "a",
NULL, NULL,
MXML_DESCEND);
</pre>
<!-- NEED 5 -->
<pre>
/* Find the first "a" element with "href"
attribute */
node = <a href='#mxmlFindElement'>mxmlFindElement</a>(tree, tree, "a",
node = mxmlFindElement(tree, tree, "a",
"href", NULL,
MXML_DESCEND);
</pre>
<!-- NEED 6 -->
<pre>
/* Find the first "a" element with "href"
to a URL */
node = <a href='#mxmlFindElement'>mxmlFindElement</a>(tree, tree, "a",
node = mxmlFindElement(tree, tree, "a",
"href",
"http://www.easysw.com/",
MXML_DESCEND);
</pre>
<!-- NEED 5 -->
<pre>
/* Find the first element with a "src"
attribute */
node = <a href='#mxmlFindElement'>mxmlFindElement</a>(tree, tree, NULL,
node = mxmlFindElement(tree, tree, NULL,
"src", NULL,
MXML_DESCEND);
</pre>
<!-- NEED 5 -->
<pre>
/* Find the first element with a "src"
= "foo.jpg" */
node = <a href='#mxmlFindElement'>mxmlFindElement</a>(tree, tree, NULL,
node = mxmlFindElement(tree, tree, NULL,
"src", "foo.jpg",
MXML_DESCEND);
</pre>
@ -284,13 +392,15 @@ e.g.:</p>
<p>You can also iterate with the same function:</p>
<pre>
<a href='#mxml_node_t'>mxml_node_t</a> *node;
mxml_node_t *node;
for (node = <a href='#mxmlFindElement'>mxmlFindElement</a>(tree, tree, "name",
for (node = mxmlFindElement(tree, tree,
"name",
NULL, NULL,
MXML_DESCEND);
node != NULL;
node = <a href='#mxmlFindElement'>mxmlFindElement</a>(node, tree, "name",
node = mxmlFindElement(node, tree,
"name",
NULL, NULL,
MXML_DESCEND))
{
@ -298,6 +408,7 @@ e.g.:</p>
}
</pre>
<!-- NEED 10 -->
<p>The <tt>MXML_DESCEND</tt> argument can actually be one of
three constants:</p>
@ -309,7 +420,7 @@ three constants:</p>
node or top-of-tree is reached. The previous node from
"group" would be the "node" element to the left, while
the next node from "group" would be the "node" element
to the right.</li>
to the right.<br><br></li>
<li><tt>MXML_DESCEND_FIRST</tt> means that it is OK to
descend to the first child of a node, but not to descend
@ -319,40 +430,19 @@ three constants:</p>
in the example above. This mode is only applicable to
the search function; the walk functions treat this as
<tt>MXML_DESCEND</tt> since every call is a first
time.</li>
time.<br><br></li>
<li><tt>MXML_DESCEND</tt> means to keep descending until
you hit the bottom of the tree. The previous node from
"group" would be the "val3" node and the next node would
be the first node element under "group". If you were to
walk from the root node "?xml" to the end of the
tree with <tt>mxmlWalkNext()</tt>, the order would be:
<li><tt>MXML_DESCEND</tt> means to keep descending until
you hit the bottom of the tree. The previous node from
"group" would be the "val3" node and the next node would
be the first node element under "group". If you were to
walk from the root node "?xml" to the end of the tree
with <tt>mxmlWalkNext()</tt>, the order would be:
<pre>
?xml
data
node
val1
node
val2
node
val3
group
node
val4
node
val5
node
val6
node
val7
node
val8
node
val9
</pre>
<p><tt>?xml data node val1 node val2 node val3 group node
val4 node val5 node val6 node val7 node val8</tt></p>
<p>If you started at "val9" and walked using
<p>If you started at "val8" and walked using
<tt>mxmlWalkPrev()</tt>, the order would be reversed,
ending at "?xml".</p></li>

View File

@ -71,6 +71,7 @@
<li><a href='#mxmlNewReal'><tt>mxmlNewReal()</tt></a> </li>
<li><a href='#mxmlNewText'><tt>mxmlNewText()</tt></a> </li>
<li><a href='#mxmlNewTextf'><tt>mxmlNewTextf()</tt></a> </li>
<li><a href='#mxmlNewXML'><tt>mxmlNewXML()</tt></a> <span class='info'>&nbsp;Mini-XML 2.3&nbsp;</span></li>
<li><a href='#mxmlRelease'><tt>mxmlRelease()</tt></a> <span class='info'>&nbsp;Mini-XML 2.3&nbsp;</span></li>
<li><a href='#mxmlRemove'><tt>mxmlRemove()</tt></a> </li>
<li><a href='#mxmlRetain'><tt>mxmlRetain()</tt></a> <span class='info'>&nbsp;Mini-XML 2.3&nbsp;</span></li>
@ -745,6 +746,29 @@ mxmlNewTextf(
<h4>Returns</h4>
<p>New node</p>
<!-- NEW PAGE -->
<h3 class='title'><span class='info'>&nbsp;Mini-XML 2.3&nbsp;</span><a name='mxmlNewXML'>mxmlNewXML()</a></h3>
<h4>Description</h4>
<p>Create a new XML document tree.
The &quot;version&quot; argument specifies the version number to put in the
?xml element node. If NULL, version 1.0 is assumed.
</p>
<h4>Syntax</h4>
<pre>
<a href='#mxml_node_t'>mxml_node_t</a> *
mxmlNewXML(
const char * version);
</pre>
<h4>Arguments</h4>
<div class='table'><table align='center' border='1' width='80%' cellpadding='5' cellspacing='0'>
<thead><tr><th>Name</th><th>Description</th></tr></thead>
<tbody>
<tr><td><tt>version</tt></td><td>Version number to use</td></tr>
</tbody></table></div>
<h4>Returns</h4>
<p>New ?xml node</p>
<!-- NEW PAGE -->
<h3 class='title'><span class='info'>&nbsp;Mini-XML 2.3&nbsp;</span><a name='mxmlRelease'>mxmlRelease()</a></h3>
<h4>Description</h4>
<p>Release a node.

View File

@ -8,12 +8,14 @@
<ul>
<li>Added two exceptions to the LGPL to support static
linking of applications against Mini-XML.</li>
linking of applications against Mini-XML</li>
<li>Added mxmlElementSetAttrf() function (STR #43)</li>
<li>Added a mxmlNewXML() function</li>
<li>Added snprintf() emulation function for test program
(STR #32)</li>
<li>Added a mxmlElementSetAttrf() function (STR #43)</li>
<li>Added a snprintf() emulation function for the test
program (STR #32)</li>
<li>Added the _CRT_SECURE_NO_DEPRECATE definition when
building on VC++ 2005 (STR #36)</li>
@ -21,52 +23,52 @@
<li>mxmlLoad*() did not detect missing > characters in
elements (STR #41)</li>
<li>mxmlLoad*() did not detect missing close tags at the end
of an XML document (STR #45)</li>
<li>mxmlLoad*() did not detect missing close tags at the
end of an XML document (STR #45)</li>
<li>Added user_data and ref_count members to mxml_node_t
structure.</li>
structure</li>
<li>Added mxmlReleaseNode() and mxmlRetainNode() APIs for
reference-counted nodes.</li>
reference-counted nodes</li>
<li>Added mxmlSetWrapMargin() to control the wrapping of XML
output.</li>
<li>Added mxmlSetWrapMargin() to control the wrapping of
XML output</li>
<li>Added conditional check for EINTR error code for
certain Windows compilers that do not define it (STR
#33)</li>
<li>The mxmldoc program now generates correct HTML 4.0
output (previously it generated invalid XHTML...)</li>
output - previously it generated invalid XHTML</li>
<li>The mxmldoc program now supports "@deprecated@,
"@private@", and "@since version@" comments.</li>
"@private@", and "@since version@" comments</li>
<li>Fixed function and enumeraion type bugs in
mxmldoc.</li>
<li>Fixed function and enumeration type bugs in
mxmldoc</li>
<li>Fixed XML schema for mxmldoc.</li>
<li>Fixed the XML schema for mxmldoc</li>
<li>The mxmldoc program now supports --intro, --section,
and --title options.</li>
and --title options</li>
<li>The mxmlLoad*() functions could leak a node on an
error (STR #27)</li>
<li>The mxml_vsnprintf() function could get in an infinite
loop on a buffer overflow (STR #25)</li>
<li>The mxml_vsnprintf() function could get in an
infinite loop on a buffer overflow (STR #25)</li>
<li>Added new mxmlNewCDATA() and mxmlSetCDATA() functions
to create and set CDATA nodes, which are really just
special element nodes.</li>
special element nodes</li>
<li>Added new MXML_IGNORE type and MXML_IGNORE_CB
callback to ignore non-element nodes (i.e.
whitespace)</li>
callback to ignore non-element nodes, e.g.
whitespace</li>
<li>mxmlLoad*() did not treat custom data as opaque, so
whitespace characters would be lost.</li>
whitespace characters would be lost</li>
</ul>

View File

@ -27,6 +27,7 @@
* mxmlNewReal() - Create a new real number node.
* mxmlNewText() - Create a new text fragment node.
* mxmlNewTextf() - Create a new formatted text fragment node.
* mxmlNewXML() - Create a new XML document tree.
* mxmlRelease() - Release a node.
* mxmlRemove() - Remove a node from its parent.
* mxmlRetain() - Retain a node.
@ -655,6 +656,28 @@ mxmlRemove(mxml_node_t *node) /* I - Node to remove */
}
/*
* 'mxmlNewXML()' - Create a new XML document tree.
*
* The "version" argument specifies the version number to put in the
* ?xml element node. If NULL, version 1.0 is assumed.
*
* @since Mini-XML 2.3@
*/
mxml_node_t * /* O - New ?xml node */
mxmlNewXML(const char *version) /* I - Version number to use */
{
char element[1024]; /* Element text */
snprintf(element, sizeof(element), "?xml version=\"%s\"?",
version ? version : "1.0");
return (mxmlNewElement(NULL, element));
}
/*
* 'mxmlRelease()' - Release a node.
*

1
mxml.h
View File

@ -207,6 +207,7 @@ extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace,
__attribute__ ((__format__ (__printf__, 3, 4)))
# endif /* __GNUC__ */
;
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);

View File

@ -512,6 +512,22 @@ string must be nul-terminated and is formatted into the new node.</description>
<type /> <description>Additional args as needed</description>
</argument>
</function>
<function name="mxmlNewXML">
<returnvalue>
<type>mxml_node_t *</type>
<description>New ?xml node</description>
</returnvalue>
<description>Create a new XML document tree.
The &quot;version&quot; argument specifies the version number to put in the
?xml element node. If NULL, version 1.0 is assumed.
@since Mini-XML 2.3@</description>
<argument name="version" direction="I">
<type>const char *</type>
<description>Version number to use</description>
</argument>
</function>
<function name="mxmlRelease">
<returnvalue>
<type>int</type>

View File

@ -645,7 +645,7 @@ new_documentation(mxml_node_t **mxmldoc)/* O - mxmldoc node */
* Create an empty XML documentation file...
*/
doc = mxmlNewElement(NULL, "?xml version=\"1.0\"?");
doc = mxmlNewXML(NULL);
*mxmldoc = mxmlNewElement(doc, "mxmldoc");

View File

@ -613,7 +613,7 @@ whitespace_cb(mxml_node_t *node, /* I - Element node */
else if (where == MXML_WS_AFTER_CLOSE)
return ("\n");
}
else if (!strcmp(name, "?xml"))
else if (!strncmp(name, "?xml", 4))
{
return (NULL);
}