Commit f830ffd6 authored by Michael R Sweet's avatar Michael R Sweet

Add mxmlAdd() and mxmlRemove() functions.

Documentation updates.

General cleanup.
parent 438ded65
......@@ -4,9 +4,13 @@ README - 06/04/2003
CHANGES IN Mini-XML 0.93
- Added mxmlAdd() and mxmlRemove() functions to add and
remove nodes from a tree. This provides more
flexibility over where the nodes are inserted and
allows nodes to be moved within the tree as needed.
- mxmlLoadFile() now correctly handles comments.
- mxmlLoadFile() now supports the "gt" and "nbsp"
character entities.
- mxmlLoadFile() now supports the required "gt", "quot",
and "nbsp" character entities.
- mxmlSaveFile() now uses newlines as whitespace
when valid to do so.
- mxmlFindElement() now also takes attribute name and
......@@ -19,7 +23,11 @@ CHANGES IN Mini-XML 0.93
now all provide "descend" arguments to control whether
they descend into child nodes in the tree.
- Fixed some whitespace issues in mxmlLoadFile().
- Fixed Unicode output issues in mxmlSaveFile().
- Fixed Unicode output and whitespace issues in
mxmlSaveFile().
- mxmlSaveFile() now supports a whitespace callback to
provide more human-readable XML output under program
control.
CHANGES IN Mini-XML 0.92
......
......@@ -13,9 +13,27 @@ INTRODUCTION
requires an ANSI C compatible compiler (GCC works, as do
most vendors' ANSI C compilers) and a "make" program.
Mini-XML was created to support the basic hierarchy provided
by XML and some simple data types, but doesn't do validation
or other types of processing on the data.
Mini-XML provides the following functionality:
- Reading and writing of UTF-8 encoded XML files.
- Data is stored in a linked-list tree structure,
preserving the XML data hierarchy.
- 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.
- Functions for creating and managing trees of data.
- "Find" and "walk" functions for easily locating and
navigating trees of data.
Mini-XML doesn't do validation or other types of processing
on the data based upon schema files or other sources of
definition information, nor does it support character
entities other than those required by the XML
specification. Also, since Mini-XML does not support the
UTF-16 encoding, it is technically not a conforming XML
consumer/client.
BUILDING Mini-XML
......@@ -86,7 +104,7 @@ DOCUMENTATION
mxml_node_t *tree;
fp = fopen("filename.xml", "w");
mxmlSaveFile(tree, fp);
mxmlSaveFile(tree, fp, MXML_NO_CALLBACK);
fclose(fp);
You can find a named element/node using the
......
......@@ -29,9 +29,28 @@ requiring large non-standard libraries. Mini-XML only requires
an ANSI C compatible compiler (GCC works, as do most vendors'
ANSI C compilers) and a "make" program.</p>
<p>Mini-XML was created to support the basic hierarchy provided
by XML and some simple data types, but doesn't do validation or
other types of processing on the data.</p>
<p>Mini-XML provides the following functionality:</p>
<ul>
<li>Reading and writing of UTF-8 encoded XML files.</li>
<li>Data is stored in a linked-list tree structure,
preserving the XML data hierarchy.</li>
<li>Supports arbitrary element names, attributes, and
attribute values with no preset limits, just available
memory.</li>
<li>Supports integer, real, opaque ("cdata"), and text
data types in "leaf" nodes.</li>
<li>Functions for creating and managing trees of data.</li>
<li>"Find" and "walk" functions for easily locating and
navigating trees of data.</li>
</ul>
<p>Mini-XML doesn't do validation or other types of processing
on the data based upon schema files or other sources of
definition information, nor does it support character entities
other than those required by the XML specification. Also, since
Mini-XML does not support the UTF-16 encoding, it is technically
not a conforming XML consumer/client.</p>
<h2>Building Mini-XML</h2>
......@@ -113,7 +132,7 @@ function:</p>
mxml_node_t *tree;
fp = fopen("filename.xml", "w");
mxmlSaveFile(tree, fp);
mxmlSaveFile(tree, fp, MXML_NO_CALLBACK);
fclose(fp);
</pre>
......
/*
* "$Id: mxml-file.c,v 1.6 2003/06/04 16:30:40 mike Exp $"
* "$Id: mxml-file.c,v 1.7 2003/06/04 17:37:23 mike Exp $"
*
* File loading code for mini-XML, a small XML-like file parsing library.
*
......@@ -36,7 +36,8 @@
*/
static int mxml_parse_element(mxml_node_t *node, FILE *fp);
static int mxml_write_node(mxml_node_t *node, FILE *fp, int col);
static int mxml_write_node(mxml_node_t *node, FILE *fp,
int (*cb)(mxml_node_t *, int), int col);
static int mxml_write_string(const char *s, FILE *fp);
......@@ -367,7 +368,7 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */
{
/*
* Add character entity to current buffer... Currently we only
* support &lt;, &amp;, &gt;, &nbsp;, &#nnn;, and &#xXXXX;...
* support &lt;, &amp;, &gt;, &nbsp;, &quot;, &#nnn;, and &#xXXXX;...
*/
char entity[64], /* Entity string */
......@@ -413,6 +414,8 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */
ch = '<';
else if (!strcmp(entity, "&nbsp"))
ch = 0xa0;
else if (!strcmp(entity, "&quot"))
ch = '\"';
else
{
fprintf(stderr, "Entity name \"%s;\" not supported under parent <%s>!\n",
......@@ -507,17 +510,23 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */
int /* O - 0 on success, -1 on error */
mxmlSaveFile(mxml_node_t *node, /* I - Node to write */
FILE *fp) /* I - File to write to */
FILE *fp, /* I - File to write to */
int (*cb)(mxml_node_t *, int))
/* I - Whitespace callback */
{
int col; /* Final column */
/*
* Write the node...
*/
if (mxml_write_node(node, fp, 0) < 0)
if ((col = mxml_write_node(node, fp, cb, 0)) < 0)
return (-1);
if (putc('\n', fp) < 0)
return (-1);
if (col > 0)
if (putc('\n', fp) < 0)
return (-1);
/*
* Return 0 (success)...
......@@ -688,9 +697,12 @@ mxml_parse_element(mxml_node_t *node, /* I - Element node */
static int /* O - Column or -1 on error */
mxml_write_node(mxml_node_t *node, /* I - Node to write */
FILE *fp, /* I - File to write to */
int (*cb)(mxml_node_t *, int),
/* I - Whitespace callback */
int col) /* I - Current column */
{
int i; /* Looping var */
int ch; /* Whitespace character */
int n; /* Chars written */
mxml_attr_t *attr; /* Current attribute */
......@@ -704,6 +716,18 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */
switch (node->type)
{
case MXML_ELEMENT :
if (cb && (ch = (*cb)(node, MXML_SAVE_OPEN_TAG)) != 0)
{
if (putc(ch, fp) < 0)
return (-1);
else if (ch == '\n')
col = 0;
else if (ch == '\t')
col += 8;
else
col ++;
}
if ((n = fprintf(fp, "<%s", node->value.element.name)) < 0)
return (-1);
......@@ -752,7 +776,7 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */
else
col ++;
if ((col = mxml_write_node(node->child, fp, col)) < 0)
if ((col = mxml_write_node(node->child, fp, cb, col)) < 0)
return (-1);
if (node->value.element.name[0] != '?' &&
......@@ -775,6 +799,18 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */
return (-1);
else
col += 2;
if (cb && (ch = (*cb)(node, MXML_SAVE_CLOSE_TAG)) != 0)
{
if (putc(ch, fp) < 0)
return (-1);
else if (ch == '\n')
col = 0;
else if (ch == '\t')
col += 8;
else
col ++;
}
break;
case MXML_INTEGER :
......@@ -829,7 +865,7 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */
break;
case MXML_TEXT :
if (node->value.text.whitespace)
if (node->value.text.whitespace && col > 0)
{
if (col > MXML_WRAP)
{
......@@ -887,6 +923,11 @@ mxml_write_string(const char *s, /* I - String to write */
if (fputs("&gt;", fp) < 0)
return (-1);
}
else if (*s == '\"')
{
if (fputs("&quot;", fp) < 0)
return (-1);
}
else if (*s & 128)
{
/*
......@@ -933,5 +974,5 @@ mxml_write_string(const char *s, /* I - String to write */
/*
* End of "$Id: mxml-file.c,v 1.6 2003/06/04 16:30:40 mike Exp $".
* End of "$Id: mxml-file.c,v 1.7 2003/06/04 17:37:23 mike Exp $".
*/
/*
* "$Id: mxml-node.c,v 1.1 2003/06/03 19:46:30 mike Exp $"
* "$Id: mxml-node.c,v 1.2 2003/06/04 17:37:23 mike Exp $"
*
* Node support code for mini-XML, a small XML-like file parsing library.
*
......@@ -17,12 +17,14 @@
*
* Contents:
*
* mxmlAdd() - Add a node to a tree.
* mxmlDelete() - Delete a node and all of its children.
* mxmlNewElement() - Create a new element node.
* mxmlNewInteger() - Create a new integer node.
* mxmlNewOpaque() - Create a new opaque string.
* mxmlNewReal() - Create a new real number node.
* mxmlNewText() - Create a new text fragment node.
* mxmlRemove() - Remove a node from its parent.
* mxml_new() - Create a new node.
*/
......@@ -40,6 +42,98 @@
static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type);
/*
* 'mxmlAdd()' - Add a node to a tree.
*/
void
mxmlAdd(mxml_node_t *parent, /* I - Parent node */
int where, /* I - Where to add */
mxml_node_t *child, /* I - Child node for where */
mxml_node_t *node) /* I - Node to add */
{
if (!parent)
return;
if (node->parent)
mxmlRemove(node);
node->parent = parent;
switch (where)
{
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.
*/
......@@ -58,7 +152,13 @@ mxmlDelete(mxml_node_t *node) /* I - Node */
return;
/*
* Delete children first...
* Remove the node from its parent, if any...
*/
mxmlRemove(node);
/*
* Delete children...
*/
while (node->child)
......@@ -103,23 +203,6 @@ mxmlDelete(mxml_node_t *node) /* I - Node */
break;
}
/*
* Remove from parent, if any...
*/
if (node->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;
}
/*
* Free this node...
*/
......@@ -277,6 +360,36 @@ mxmlNewText(mxml_node_t *parent, /* I - Parent node */
}
/*
* 'mxmlRemove()' - Remove a node from its parent.
*/
void
mxmlRemove(mxml_node_t *node) /* I - Node to remove */
{
/*
* 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;
}
/*
* 'mxml_new()' - Create a new node.
*/
......@@ -306,17 +419,7 @@ mxml_new(mxml_node_t *parent, /* I - Parent node */
*/
if (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;
}
mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
/*
* Return the new node...
......@@ -327,5 +430,5 @@ mxml_new(mxml_node_t *parent, /* I - Parent node */
/*
* End of "$Id: mxml-node.c,v 1.1 2003/06/03 19:46:30 mike Exp $".
* End of "$Id: mxml-node.c,v 1.2 2003/06/04 17:37:23 mike Exp $".
*/
/*
* "$Id: mxml.h,v 1.5 2003/06/04 16:30:40 mike Exp $"
* "$Id: mxml.h,v 1.6 2003/06/04 17:37:23 mike Exp $"
*
* Header file for mini-XML, a small XML-like file parsing library.
*
......@@ -38,14 +38,21 @@
* Constants...
*/
# define MXML_NO_CALLBACK (mxml_type_t (*)(mxml_node_t *))0
/* Don't use a type callback */
# define MXML_NO_CALLBACK 0 /* Don't use a type callback */
# define MXML_WRAP 72 /* Wrap XML output at this column position */
# define MXML_DESCEND 1 /* Descend when finding/walking */
# define MXML_NO_DESCEND 0 /* Don't descend when finding/walking */
# define MXML_DESCEND_FIRST -1 /* Descend for first find */
# define MXML_SAVE_OPEN_TAG 0 /* Callback for open tag */
# define MXML_SAVE_CLOSE_TAG 1 /* Callback for close tag */
# define MXML_ADD_BEFORE 0 /* Add node before specified node */
# define MXML_ADD_AFTER 1 /* Add node after specified node */
# define MXML_ADD_TO_PARENT NULL /* Add node relative to parent */
/*
* Data types...
......@@ -110,6 +117,8 @@ extern "C" {
* Prototypes...
*/
extern void mxmlAdd(mxml_node_t *parent, int where,
mxml_node_t *child, mxml_node_t *node);
extern void mxmlDelete(mxml_node_t *node);
extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name);
extern void mxmlElementSetAttr(mxml_node_t *node, const char *name,
......@@ -125,7 +134,9 @@ extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque);
extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real);
extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace,
const char *string);
extern int mxmlSaveFile(mxml_node_t *node, FILE *fp);
extern void mxmlRemove(mxml_node_t *node);
extern int mxmlSaveFile(mxml_node_t *node, FILE *fp,
int (*cb)(mxml_node_t *, int));
extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top,
int descend);
extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top,
......@@ -143,5 +154,5 @@ extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top,
/*
* End of "$Id: mxml.h,v 1.5 2003/06/04 16:30:40 mike Exp $".
* End of "$Id: mxml.h,v 1.6 2003/06/04 17:37:23 mike Exp $".
*/
/*
* "$Id: mxmldoc.c,v 1.2 2003/06/04 16:30:40 mike Exp $"
* "$Id: mxmldoc.c,v 1.3 2003/06/04 17:37:23 mike Exp $"
*
* Documentation generator using mini-XML, a small XML-like file parsing
* library.
......@@ -102,9 +102,9 @@
* Local functions...
*/
static void insert_node(mxml_node_t *tree, mxml_node_t *func);
static int scan_file(const char *filename, FILE *fp,
mxml_node_t *doc);
static void sort_node(mxml_node_t *tree, mxml_node_t *func);
/*
......@@ -193,7 +193,7 @@ main(int argc, /* I - Number of command-line args */
* Write over the existing XML file...
*/
if (mxmlSaveFile(doc, fp))
if (mxmlSaveFile(doc, fp, MXML_NO_CALLBACK))
{
fprintf(stderr, "Unable to write the XML documentation file \"%s\": %s!\n",
argv[1], strerror(errno));
......@@ -223,12 +223,24 @@ main(int argc, /* I - Number of command-line args */
/*
* 'insert_node()' - Insert a node into a tree.
* 'scan_file()' - Scan a source file.
*/
static int /* O - 0 on success, -1 on error */
scan_file(const char *filename, /* I - Filename */
FILE *fp, /* I - File to scan */
mxml_node_t *tree) /* I - Function tree */
{
}
/*
* 'sort_node()' - Insert a node sorted into a tree.
*/
static void
insert_node(mxml_node_t *tree, /* I - Tree to insert into */
mxml_node_t *node) /* I - Node to add */
sort_node(mxml_node_t *tree, /* I - Tree to sort into */
mxml_node_t *node) /* I - Node to add */
{
mxml_node_t *temp; /* Current node */
const char *tempname, /* Name of current node */
......@@ -242,7 +254,7 @@ insert_node(mxml_node_t *tree, /* I - Tree to insert into */
nodename = mxmlElementGetAttr(node, "name");
/*
* Delete an existing definition, if one exists...
* Delete any existing definition at this level, if one exists...
*/
if ((temp = mxmlFindElement(tree, tree, node->value.element.name,
......@@ -250,7 +262,7 @@ insert_node(mxml_node_t *tree, /* I - Tree to insert into */
mxmlDelete(temp);
/*
* Insert the node into the tree...
* Add the node into the tree at the proper place...
*/
for (temp = tree->child; temp; temp = temp->next)
......@@ -262,53 +274,10 @@ insert_node(mxml_node_t *tree, /* I - Tree to insert into */
break;
}
if (temp)
{
/*
* Insert node before this temp...
*/
node->next = temp;
node->prev = temp->prev;
if (temp->prev)
temp->prev->next = node;
else
tree->child = node;
temp->prev = node;
}
else
{
/*
* Append node to the end...
*/
node->prev = tree->last_child;
if (tree->last_child)
tree->last_child->next = node;
else
tree->last_child = node;
if (!tree->child)
tree->child = node;
}
}
/*
* 'scan_file()' - Scan a source file.
*/
static int /* O - 0 on success, -1 on error */
scan_file(const char *filename, /* I - Filename */
FILE *fp, /* I - File to scan */
mxml_node_t *tree) /* I - Function tree */
{
mxmlAdd(tree, MXML_ADD_AFTER, temp, node);
}
/*
* End of "$Id: mxmldoc.c,v 1.2 2003/06/04 16:30:40 mike Exp $".
* End of "$Id: mxmldoc.c,v 1.3 2003/06/04 17:37:23 mike Exp $".
*/
/*
* "$Id: testmxml.c,v 1.5 2003/06/04 16:30:40 mike Exp $"
* "$Id: testmxml.c,v 1.6 2003/06/04 17:37:23 mike Exp $"
*
* Test program for mini-XML, a small XML-like file parsing library.
*
......@@ -17,8 +17,10 @@
*
* Contents:
*
* main() - Main entry for test program.
* type_cb() - XML data type callback for mxmlLoadFile()...
* main() - Main entry for test program.
* type_cb() - XML data type callback for mxmlLoadFile()...
* whitespace_cb() - Let the mxmlSaveFile() function know when to insert
* newlines and tabs...
*/
/*
......@@ -33,6 +35,7 @@
*/
mxml_type_t type_cb(mxml_node_t *node);
int whitespace_cb(mxml_node_t *node, int where);
/*
......@@ -110,8 +113,7 @@ main(int argc, /* I - Number of command-line args */
* Print the XML tree...
*/
mxmlSaveFile(tree, stdout);
puts("");
mxmlSaveFile(tree, stdout, whitespace_cb);
/*
* Delete the tree and return...
......@@ -142,8 +144,7 @@ type_cb(mxml_node_t *node) /* I - Element node */
if (!strcmp(type, "integer"))
return (MXML_INTEGER);
else if (!strcmp(type, "opaque") ||
!strcmp(type, "pre") || !strcmp(type, "PRE"))
else if (!strcmp(type, "opaque") || !strcmp(type, "pre"))
return (MXML_OPAQUE);
else if (!strcmp(type, "real"))
return (MXML_REAL);
......@@ -153,5 +154,44 @@ type_cb(mxml_node_t *node) /* I - Element node */
/*
* End of "$Id: testmxml.c,v 1.5 2003/06/04 16:30:40 mike Exp $".
* 'whitespace_cb()' - Let the mxmlSaveFile() function know when to insert
* newlines and tabs...
*/
int /* O - Whitespace char or 0 */
whitespace_cb(mxml_node_t *node, /* I - Element node */
int where) /* I - Open or close tag? */
{
const char *name; /* Name of element */
/*
* 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"))
return ('\n');
else if (!strcmp(name, "li"))
{
/*
* Put a tab before <li>'s and a newline after </li>'s...
*/
if (where == MXML_SAVE_OPEN_TAG)
return ('\t');
else
return ('\n');
}
else
return (0);
}
/*
* End of "$Id: testmxml.c,v 1.6 2003/06/04 17:37:23 mike Exp $".
*/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment