Whitespace handling issues.

Unicode output issues.

Comment/declaration handling fixes.

Add mxmldoc to build.
web
Michael R Sweet 22 years ago
parent bfc6a08cde
commit 438ded6568
  1. 13
      CHANGES
  2. 56
      Makefile.in
  3. 34
      README
  4. 221
      index.html
  5. 189
      mxml-file.c
  6. 125
      mxml-search.c
  7. 21
      mxml.h
  8. 257
      mxmldoc.c
  9. 13
      testmxml.c

@ -1,4 +1,4 @@
README - 06/03/2003 README - 06/04/2003
------------------- -------------------
@ -9,6 +9,17 @@ CHANGES IN Mini-XML 0.93
character entities. character entities.
- mxmlSaveFile() now uses newlines as whitespace - mxmlSaveFile() now uses newlines as whitespace
when valid to do so. when valid to do so.
- mxmlFindElement() now also takes attribute name and
attribute value string arguments to limit the search
to specific elements with attributes and/or values.
NULL pointers can be used as "wildcards".
- Added uninstall target to makefile, and auto-reconfig
if Makefile.in or configure.in are changed.
- mxmlFindElement(), mxmlWalkNext(), and mxmlWalkPrev()
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().
CHANGES IN Mini-XML 0.92 CHANGES IN Mini-XML 0.92

@ -1,5 +1,5 @@
# #
# "$Id: Makefile.in,v 1.3 2003/06/04 00:25:59 mike Exp $" # "$Id: Makefile.in,v 1.4 2003/06/04 16:30:39 mike Exp $"
# #
# Makefile for mini-XML, a small XML-like file parsing library. # Makefile for mini-XML, a small XML-like file parsing library.
# #
@ -53,15 +53,15 @@ libdir = @libdir@
# #
LIBOBJS = mxml-attr.o mxml-file.o mxml-node.o mxml-search.o LIBOBJS = mxml-attr.o mxml-file.o mxml-node.o mxml-search.o
OBJS = testmxml.o $(LIBOBJS) OBJS = mxmldoc.o testmxml.o $(LIBOBJS)
TARGETS = libmxml.a testmxml TARGETS = libmxml.a mxmldoc testmxml
# #
# Make everything... # Make everything...
# #
all: $(TARGETS) all: Makefile configure $(TARGETS)
# #
@ -86,6 +86,37 @@ install: $(TARGETS)
cp mxml.h $(includedir) cp mxml.h $(includedir)
#
# Uninstall everything...
#
uninstall:
rm -f $(libdir)/libmxml.a
rm -f $(includedir)/mxml.h
#
# autoconf stuff...
#
Makefile: configure Makefile.in
if test -f config.status; then \
./config.status --recheck; \
./config.status; \
else \
./configure; \
fi
configure: configure.in
autoconf
if test -f config.status; then \
./config.status --recheck; \
./config.status; \
else \
./configure; \
fi
# #
# libmxml.a # libmxml.a
# #
@ -98,6 +129,16 @@ libmxml.a: $(LIBOBJS)
$(LIBOBJS): mxml.h $(LIBOBJS): mxml.h
#
# mxmldoc
#
mxmldoc: libmxml.a mxmldoc.o
$(CC) $(LDFLAGS) -o $@ mxmldoc.o libmxml.a
mxmldoc.o: mxml.h
# #
# testmxml # testmxml
# #
@ -116,9 +157,14 @@ testmxml: libmxml.a testmxml.o
testmxml.o: mxml.h testmxml.o: mxml.h
#
# All object files depend on the makefile...
#
$(OBJS): Makefile $(OBJS): Makefile
# #
# End of "$Id: Makefile.in,v 1.3 2003/06/04 00:25:59 mike Exp $". # End of "$Id: Makefile.in,v 1.4 2003/06/04 16:30:39 mike Exp $".
# #

@ -1,11 +1,11 @@
README - 06/03/2003 README - 06/04/2003
------------------- -------------------
INTRODUCTION INTRODUCTION
This README file describes the Mini-XML library version This README file describes the Mini-XML library version
0.91. 0.93.
Mini-XML is a small XML parsing library that you can use to Mini-XML is a small XML parsing library that you can use to
read XML and XML-like data files in your application without read XML and XML-like data files in your application without
@ -92,15 +92,39 @@ DOCUMENTATION
You can find a named element/node using the You can find a named element/node using the
"mxmlFindElement()" function: "mxmlFindElement()" function:
mxml_node_t *node = mxmlFindElement(tree, tree, "name"); mxml_node_t *node = mxmlFindElement(tree, tree, "name", "attr",
"value", MXML_DESCEND);
The "name", "attr", and "value" arguments can be passed as
NULL to act as wildcards, e.g.:
/* Find the first "a" element */
node = mxmlFindElement(tree, tree, "a", NULL, NULL, MXML_DESCEND);
/* Find the first "a" element with "href" attribute */
node = mxmlFindElement(tree, tree, "a", "href", NULL, MXML_DESCEND);
/* Find the first "a" element with "href" to a URL */
node = mxmlFindElement(tree, tree, "a", "href",
"http://www.easysw.com/~mike/mxml/",
MXML_DESCEND);
/* Find the first element with a "src" attribute*/
node = mxmlFindElement(tree, tree, NULL, "src", NULL, MXML_DESCEND);
/* Find the first element with a "src" = "foo.jpg" */
node = mxmlFindElement(tree, tree, NULL, "src", "foo.jpg",
MXML_DESCEND);
You can also iterate with the same function: You can also iterate with the same function:
mxml_node_t *node; mxml_node_t *node;
for (node = mxmlFindElement(tree, tree, "name"); for (node = mxmlFindElement(tree, tree, "name", NULL, NULL,
MXML_DESCEND);
node != NULL; node != NULL;
node = mxmlFindElement(node, tree, "name")) node = mxmlFindElement(node, tree, "name", NULL, NULL,
MXML_DESCEND))
{ {
... do something ... ... do something ...
} }

@ -1,174 +1,199 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <!DOCTYPE html
<HTML> PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
<HEAD> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<TITLE>Mini-XML Home Page</TITLE> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<STYLE><!-- <head>
H1, H2, H3, P { font-family: sans-serif; text-align: justify; } <title>Mini-XML Home Page</title>
H1.title, P.title { font-family: sans-serif; text-align: center; } <style><!--
TT, PRE, PRE A:link, PRE A:visited { font-weight: bold; color: #7f0000; } h1, h2, h3, p { font-family: sans-serif; text-align: justify; }
--></STYLE> h1.title, p.title { font-family: sans-serif; text-align: center; }
</HEAD> tt, pre, pre a:link, pre a:visited, tt a:link, tt a:visited { font-weight: bold; color: #7f0000; }
<BODY> --></style>
</head>
<P CLASS="title" ALIGN="CENTER">[&nbsp;<A <body>
HREF="../index.html">Back to Home Page</A>&nbsp;]</P>
<p class="title" align="center">[&nbsp;<a
<H1 CLASS="title" ALIGN="CENTER">Mini-XML Home Page</H1> href="../index.html">Back to Home Page</a>&nbsp;]</p>
<P CLASS="title" ALIGN="CENTER">Current Release: v0.93 [&nbsp;<A <h1 class="title" align="center">Mini-XML Home Page</h1>
HREF="mxml-0.93.tar.gz">Download Source (.tar.gz 40k)</A> |
<A HREF="CHANGES">View Change Log</A>&nbsp;]</P> <p class="title" align="center">Current Release: v0.93 [&nbsp;<a
href="mxml-0.93.tar.gz">Download Source (.tar.gz 40k)</a> |
<H2>Introduction</H2> <a href="CHANGES">View Change Log</a>&nbsp;]</p>
<P>Mini-XML is a small XML parsing library that you can use to <h2>Introduction</h2>
<p>Mini-XML is a small XML parsing library that you can use to
read XML and XML-like data files in your application without read XML and XML-like data files in your application without
requiring large non-standard libraries. Mini-XML only requires requiring large non-standard libraries. Mini-XML only requires
an ANSI C compatible compiler (GCC works, as do most vendors' an ANSI C compatible compiler (GCC works, as do most vendors'
ANSI C compilers) and a "make" program.</P> ANSI C compilers) and a "make" program.</p>
<P>Mini-XML was created to support the basic hierarchy provided <p>Mini-XML was created to support the basic hierarchy provided
by XML and some simple data types, but doesn't do validation or by XML and some simple data types, but doesn't do validation or
other types of processing on the data.</P> other types of processing on the data.</p>
<H2>Building Mini-XML</H2> <h2>Building Mini-XML</h2>
<P>Mini-XML comes with an autoconf-based configure script; just <p>Mini-XML comes with an autoconf-based configure script; just
type the following command to get things going:</P> type the following command to get things going:</p>
<PRE> <pre>
./configure ./configure
</PRE> </pre>
<P>The default install prefix is /usr/local, which can be <p>The default install prefix is /usr/local, which can be
overridden using the --prefix option:</P> overridden using the --prefix option:</p>
<PRE> <pre>
./configure --prefix=/foo ./configure --prefix=/foo
</PRE> </pre>
<P>Once you have configured the software, type "make" to do the <p>Once you have configured the software, type "make" to do the
build and then run the test program to verify that things are build and then run the test program to verify that things are
working, as follows:</P> working, as follows:</p>
<PRE> <pre>
make make
./testmxml test.xml ./testmxml test.xml
</PRE> </pre>
<H2>Installing Mini-XML</H2> <h2>Installing Mini-XML</h2>
<P>The "install" target will install Mini-XML in the lib and <p>The "install" target will install Mini-XML in the lib and
include directories:</P> include directories:</p>
<PRE> <pre>
make install make install
</PRE> </pre>
<P>Once you have installed it, use the "-lmxml" option to link <p>Once you have installed it, use the "-lmxml" option to link
your application against it.</P> your application against it.</p>
<H2>Documentation</H2> <h2>Documentation</h2>
<P>The documentation is currently just in this page. At some <p>The documentation is currently just in this page. At some
point I'll probably do some proper documentation, but for now point I'll probably do some proper documentation, but for now
just read here and look at the <A just read here and look at the <tt><a
HREF="testmxml.c"><TT>testmxml.c</TT></A> source file for an href="testmxml.c">testmxml.c</a></tt> source file for an
example of reading and printing the contents of an XML file to example of reading and printing the contents of an XML file to
stdout.</P> stdout.</p>
<P>Mini-XML provides a single header file which you include:</P> <p>Mini-XML provides a single header file which you include:</p>
<PRE> <pre>
<A HREF="mxml.h">#include &lt;mxml.h></A> <a href="mxml.h">#include &lt;mxml.h></a>
</PRE> </pre>
<P>Nodes are defined by the <TT>mxml_node_t</TT> structure; the <p>Nodes are defined by the <tt>mxml_node_t</tt> structure; the
<TT>type</TT> member defines the node type (element, integer, <tt>type</tt> member defines the node type (element, integer,
opaque, real, or text) which determines which value you want to opaque, real, or text) which determines which value you want to
look at in the <TT>value</TT> union. New nodes can be created look at in the <tt>value</tt> union. New nodes can be created
using the <TT>mxmlNewElement()</TT>, <TT>mxmlNewInteger()</TT>, using the <tt>mxmlNewElement()</tt>, <tt>mxmlNewInteger()</tt>,
<TT>mxmlNewOpaque()</TT>, <TT>mxmlNewReal()</TT>, and <tt>mxmlNewOpaque()</tt>, <tt>mxmlNewReal()</tt>, and
<TT>mxmlNewText()</TT> functions. Only elements can have child <tt>mxmlNewText()</tt> functions. Only elements can have child
nodes, and the top node must be an element, usually "?xml".</P> nodes, and the top node must be an element, usually "?xml".</p>
<P>You load an XML file using the <TT>mxmlLoadFile()</TT> function:</P> <p>You load an XML file using the <tt>mxmlLoadFile()</tt> function:</p>
<PRE> <pre>
FILE *fp; FILE *fp;
mxml_node_t *tree; mxml_node_t *tree;
fp = fopen("filename.xml", "r"); fp = fopen("filename.xml", "r");
tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK); tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);
fclose(fp); fclose(fp);
</PRE> </pre>
<P>Similarly, you save an XML file using the <TT>mxmlSaveFile()</TT> <p>Similarly, you save an XML file using the <tt>mxmlSaveFile()</tt>
function:</P> function:</p>
<PRE> <pre>
FILE *fp; FILE *fp;
mxml_node_t *tree; mxml_node_t *tree;
fp = fopen("filename.xml", "w"); fp = fopen("filename.xml", "w");
mxmlSaveFile(tree, fp); mxmlSaveFile(tree, fp);
fclose(fp); fclose(fp);
</PRE> </pre>
<P>You can find a named element/node using the <p>You can find a named element/node using the
<TT>mxmlFindElement()</TT> function:</P> <tt>mxmlFindElement()</tt> function:</p>
<PRE> <pre>
mxml_node_t *node = mxmlFindElement(tree, tree, "name"); mxml_node_t *node = mxmlFindElement(tree, tree, "name", "attr",
</PRE> "value", MXML_DESCEND);
</pre>
<P>You can also iterate with the same function: <p>The <tt>name</tt>, <tt>attr</tt>, and <tt>value</tt>
arguments can be passed as <tt>NULL</tt> to act as wildcards,
e.g.:</p>
<PRE> <pre>
/* Find the first "a" element */
node = mxmlFindElement(tree, tree, "a", NULL, NULL, MXML_DESCEND);
/* Find the first "a" element with "href" attribute */
node = mxmlFindElement(tree, tree, "a", "href", NULL, MXML_DESCEND);
/* Find the first "a" element with "href" to a URL */
node = mxmlFindElement(tree, tree, "a", "href",
"http://www.easysw.com/~mike/mxml/", MXML_DESCEND);
/* Find the first element with a "src" attribute*/
node = mxmlFindElement(tree, tree, NULL, "src", NULL, MXML_DESCEND);
/* Find the first element with a "src" = "foo.jpg" */
node = mxmlFindElement(tree, tree, NULL, "src", "foo.jpg", MXML_DESCEND);
</pre>
<p>You can also iterate with the same function:</p>
<pre>
mxml_node_t *node; mxml_node_t *node;
for (node = mxmlFindElement(tree, tree, "name"); for (node = mxmlFindElement(tree, tree, "name", NULL, NULL, MXML_DESCEND);
node != NULL; node != NULL;
node = mxmlFindElement(node, tree, "name")) node = mxmlFindElement(node, tree, "name", NULL, NULL, MXML_DESCEND))
{ {
... do something ... ... do something ...
} }
</PRE> </pre>
<P>Finally, once you are done with the XML data, use the <p>Finally, once you are done with the XML data, use the
<TT>mxmlDelete()</TT> function to recursively free the memory <tt>mxmlDelete()</tt> function to recursively free the memory
that is used for a particular node or the entire tree:</P> that is used for a particular node or the entire tree:</p>
<PRE> <pre>
mxmlDelete(tree); mxmlDelete(tree);
</PRE> </pre>
<H2>Getting Help and Reporting Problems</H2> <h2>Getting Help and Reporting Problems</h2>
<P>You can email me at "mxml <I>at</I> easysw <I>dot</I> com" to <p>You can email me at "mxml <i>at</i> easysw <i>dot</i> com" to
report problems and/or ask for help. Just don't expect an report problems and/or ask for help. Just don't expect an
instant response, as I get a <I>lot</I> of email...</P> instant response, as I get a <i>lot</i> of email...</p>
<H2>Legal Stuff</H2> <h2>Legal Stuff</h2>
<P>The Mini-XML library is Copyright 2003 by Michael Sweet.</P> <p>The Mini-XML library is Copyright 2003 by Michael Sweet.</p>
<P>This library is free software; you can redistribute it <p>This library is free software; you can redistribute it
and/or modify it under the terms of the GNU Library General and/or modify it under the terms of the GNU Library General
Public License as published by the Free Software Foundation; Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any either version 2 of the License, or (at your option) any
later version.</P> later version.</p>
<P>This library is distributed in the hope that it will be <p>This library is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU Library General Public License for PURPOSE. See the GNU Library General Public License for
more details.</P> more details.</p>
<P>You should have received a copy of the GNU Library General <p>You should have received a copy of the GNU Library General
Public License along with this library; if not, write to the Public License along with this library; if not, write to the
Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
02139, USA.</P> 02139, USA.</p>
</BODY> </body>
</HTML> </html>

@ -1,5 +1,5 @@
/* /*
* "$Id: mxml-file.c,v 1.5 2003/06/04 02:34:29 mike Exp $" * "$Id: mxml-file.c,v 1.6 2003/06/04 16:30:40 mike Exp $"
* *
* File loading code for mini-XML, a small XML-like file parsing library. * File loading code for mini-XML, a small XML-like file parsing library.
* *
@ -131,6 +131,32 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */
break; break;
} }
} }
else if (isspace(ch) && type == MXML_TEXT)
whitespace = 1;
/*
* Add lone whitespace node if we are starting a new element and have
* existing whitespace...
*/
if (ch == '<' && whitespace && type == MXML_TEXT)
{
/*
* Peek at the next character and only do this if we are starting
* an open tag...
*/
ch = getc(fp);
ungetc(ch, fp);
if (ch != '/')
{
mxmlNewText(parent, whitespace, "");
whitespace = 0;
}
ch = '<';
}
if (ch == '<') if (ch == '<')
{ {
@ -144,40 +170,114 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */
if (isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) if (isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer))
break; break;
else if (bufptr < (buffer + sizeof(buffer) - 1)) else if (bufptr < (buffer + sizeof(buffer) - 1))
{
*bufptr++ = ch; *bufptr++ = ch;
if ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3))
break;
}
*bufptr = '\0'; *bufptr = '\0';
bufptr = buffer;
if (!strcmp(buffer, "!--")) if (!strcmp(buffer, "!--"))
{ {
/* /*
* Skip comment... * Gather rest of comment...
*/ */
buffer[3] = '\0';
while ((ch = getc(fp)) != EOF) while ((ch = getc(fp)) != EOF)
{ {
*bufptr++ = ch; if (ch == '>' && bufptr > (buffer + 4) &&
!strncmp(bufptr - 2, "--", 2))
if ((bufptr - buffer) == 3) break;
else if (bufptr < (buffer + sizeof(buffer) - 1))
*bufptr++ = ch;
else
{ {
if (!strcmp(buffer, "-->")) fprintf(stderr, "Comment too long in file under parent <%s>!\n",
break; parent ? parent->value.element.name : "null");
break;
}
}
buffer[0] = buffer[1]; /*
buffer[1] = buffer[2]; * Error out if we didn't get the whole comment...
bufptr --; */
if (ch != '>')
break;
/*
* Otherwise add this as an element under the current parent...
*/
*bufptr = '\0';
if (!mxmlNewElement(parent, buffer))
{
/*
* Just print error for now...
*/
fprintf(stderr, "Unable to add comment node to parent <%s>!\n",
parent ? parent->value.element.name : "null");
break;
}
}
else if (buffer[0] == '!')
{
/*
* Gather rest of declaration...
*/
do
{
if (ch == '>')
break;
else if (bufptr < (buffer + sizeof(buffer) - 1))
*bufptr++ = ch;
else
{
fprintf(stderr, "Declaration too long in file under parent <%s>!\n",
parent ? parent->value.element.name : "null");
break;
} }
} }
while ((ch = getc(fp)) != EOF);
bufptr = buffer; /*
* Error out if we didn't get the whole declaration...
*/
if (ch == EOF) if (ch != '>')
break; break;
else
continue; /*
* Otherwise add this as an element under the current parent...
*/
*bufptr = '\0';
node = mxmlNewElement(parent, buffer);
if (!node)
{
/*
* Just print error for now...
*/
fprintf(stderr, "Unable to add declaration node to parent <%s>!\n",
parent ? parent->value.element.name : "null");
break;
}
/*
* Descend into this node, setting the value type as needed...
*/
parent = node;
if (cb)
type = (*cb)(parent);
} }
else if (buffer[0] == '/') else if (buffer[0] == '/')
{ {
@ -260,6 +360,8 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */
type = (*cb)(parent); type = (*cb)(parent);
} }
} }
bufptr = buffer;
} }
else if (ch == '&') else if (ch == '&')
{ {
@ -635,7 +737,7 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */
if (node->child) if (node->child)
{ {
/* /*
* The ?xml element is a special-case and has no end tag... * The ? and ! elements are special-cases and have no end tags...
*/ */
if (node->value.element.name[0] == '?') if (node->value.element.name[0] == '?')
@ -653,7 +755,8 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */
if ((col = mxml_write_node(node->child, fp, col)) < 0) if ((col = mxml_write_node(node->child, fp, col)) < 0)
return (-1); return (-1);
if (node->value.element.name[0] != '?') if (node->value.element.name[0] != '?' &&
node->value.element.name[0] != '!')
{ {
if ((n = fprintf(fp, "</%s>", node->value.element.name)) < 0) if ((n = fprintf(fp, "</%s>", node->value.element.name)) < 0)
return (-1); return (-1);
@ -661,6 +764,13 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */
col += n; col += n;
} }
} }
else if (node->value.element.name[0] == '!')
{
if (putc('>', fp) < 0)
return (-1);
else
col ++;
}
else if (fputs("/>", fp) < 0) else if (fputs("/>", fp) < 0)
return (-1); return (-1);
else else
@ -772,6 +882,45 @@ mxml_write_string(const char *s, /* I - String to write */
if (fputs("&lt;", fp) < 0) if (fputs("&lt;", fp) < 0)
return (-1); return (-1);
} }
else if (*s == '>')
{
if (fputs("&gt;", fp) < 0)
return (-1);
}
else if (*s & 128)
{
/*
* Convert UTF-8 to Unicode constant...
*/
int ch; /* Unicode character */
ch = *s & 255;
if ((ch & 0xe0) == 0xc0)
{
ch = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
s ++;
}
else if ((ch & 0xf0) == 0xe0)
{
ch = ((((ch * 0x0f) << 6) | (s[1] & 0x3f)) << 6) | (s[2] & 0x3f);
s += 2;
}
if (ch == 0xa0)
{
/*
* Handle non-breaking space as-is...
*/
if (fputs("&nbsp;", fp) < 0)
return (-1);
}
else if (fprintf(fp, "&#x%x;", ch) < 0)
return (-1);
}
else if (putc(*s, fp) < 0) else if (putc(*s, fp) < 0)
return (-1); return (-1);
@ -784,5 +933,5 @@ mxml_write_string(const char *s, /* I - String to write */
/* /*
* End of "$Id: mxml-file.c,v 1.5 2003/06/04 02:34:29 mike Exp $". * End of "$Id: mxml-file.c,v 1.6 2003/06/04 16:30:40 mike Exp $".
*/ */

@ -1,5 +1,5 @@
/* /*
* "$Id: mxml-search.c,v 1.2 2003/06/03 20:40:01 mike Exp $" * "$Id: mxml-search.c,v 1.3 2003/06/04 16:30:40 mike Exp $"
* *
* Search/navigation functions for mini-XML, a small XML-like file * Search/navigation functions for mini-XML, a small XML-like file
* parsing library. * parsing library.
@ -21,7 +21,6 @@
* mxmlFindElement() - Find the named element. * mxmlFindElement() - Find the named element.
* mxmlWalkNext() - Walk to the next logical node in the tree. * mxmlWalkNext() - Walk to the next logical node in the tree.
* mxmlWalkPrev() - Walk to the previous logical node in the tree. * mxmlWalkPrev() - Walk to the previous logical node in the tree.
* mxml_walk_next() - Walk to the next logical node in the tree.
*/ */
/* /*
@ -31,14 +30,6 @@
#include "mxml.h" #include "mxml.h"
/*
* Local functions...
*/
mxml_node_t *mxml_walk_next(mxml_node_t *node, mxml_node_t *top,
int descend);
/* /*
* 'mxmlFindElement()' - Find the named element. * 'mxmlFindElement()' - Find the named element.
*/ */
@ -46,13 +37,26 @@ mxml_node_t *mxml_walk_next(mxml_node_t *node, mxml_node_t *top,
mxml_node_t * /* O - Element node or NULL */ mxml_node_t * /* O - Element node or NULL */
mxmlFindElement(mxml_node_t *node, /* I - Current node */ mxmlFindElement(mxml_node_t *node, /* I - Current node */
mxml_node_t *top, /* I - Top node */ mxml_node_t *top, /* I - Top node */
const char *name) /* I - Element name */ const char *name, /* I - Element name or NULL for any */
const char *attr, /* I - Attribute name, or NULL for none */
const char *value, /* I - Attribute value, or NULL for any */
int descend) /* I - Descend into tree? */
{ {
const char *temp; /* Current attribute value */
/*
* Range check input...
*/
if (!node || !top || (!attr && value))
return (NULL);
/* /*
* Start with the next node... * Start with the next node...
*/ */
node = mxmlWalkNext(node, top); node = mxmlWalkNext(node, top, descend);
/* /*
* Loop until we find a matching element... * Loop until we find a matching element...
@ -66,14 +70,38 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */
if (node->type == MXML_ELEMENT && if (node->type == MXML_ELEMENT &&
node->value.element.name && node->value.element.name &&
!strcmp(node->value.element.name, name)) (!name || !strcmp(node->value.element.name, name)))
return (node); {
/*
* See if we need to check for an attribute...
*/
if (!attr)
return (node); /* No attribute search, return it... */
/*
* Check for the attribute...
*/
if ((temp = mxmlElementGetAttr(node, attr)) != NULL)
{
/*
* OK, we have the attribute, does it match?
*/
if (!value || !strcmp(value, temp))
return (node); /* Yes, return it... */
}
}
/* /*
* Nope, move on to the next... * No match, move on to the next node...
*/ */
node = mxmlWalkNext(node, top); if (descend == MXML_DESCEND)
node = mxmlWalkNext(node, top, MXML_DESCEND);
else
node = node->next;
} }
return (NULL); return (NULL);
@ -86,9 +114,29 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */
mxml_node_t * /* O - Next node or NULL */ mxml_node_t * /* O - Next node or NULL */
mxmlWalkNext(mxml_node_t *node, /* I - Current node */ mxmlWalkNext(mxml_node_t *node, /* I - Current node */
mxml_node_t *top) /* I - Top node */ mxml_node_t *top, /* I - Top node */
int descend) /* I - Descend into tree? */
{ {
return (mxml_walk_next(node, top, 1)); if (!node)
return (NULL);
else if (node->child && descend)
return (node->child);
else if (node->next)
return (node->next);
else if (node->parent != top)
{
node = node->parent;
while (!node->next)
if (node->parent == top)
return (NULL);
else
node = node->parent;
return (node->next);
}
else
return (NULL);
} }
@ -98,41 +146,36 @@ mxmlWalkNext(mxml_node_t *node, /* I - Current node */
mxml_node_t * /* O - Previous node or NULL */ mxml_node_t * /* O - Previous node or NULL */
mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ mxmlWalkPrev(mxml_node_t *node, /* I - Current node */
mxml_node_t *top) /* I - Top node */ mxml_node_t *top, /* I - Top node */
int descend) /* I - Descend into tree? */
{ {
if (!node) if (!node)
return (NULL); return (NULL);
else if (node->prev) else if (node->prev)
return (node->prev); {
else if (node->parent != top) if (node->prev->last_child && descend)
return (node->parent); {
else /*
return (NULL); * Find the last child under the previous node...
} */
node = node->prev->last_child;
/* while (node->last_child)
* 'mxml_walk_next()' - Walk to the next logical node in the tree. node = node->last_child;
*/
mxml_node_t * /* O - Next node or NULL */ return (node);
mxml_walk_next(mxml_node_t *node, /* I - Current node */ }
mxml_node_t *top, /* I - Top node */ else
int descend) /* I - 1 = descend, 0 = don't */ return (node->prev);
{ }
if (!node)
return (NULL);
else if (node->child && descend)
return (node->child);
else if (node->next)
return (node->next);
else if (node->parent != top) else if (node->parent != top)
return (mxml_walk_next(node->parent, top, 0)); return (node->parent);
else else
return (NULL); return (NULL);
} }
/* /*
* End of "$Id: mxml-search.c,v 1.2 2003/06/03 20:40:01 mike Exp $". * End of "$Id: mxml-search.c,v 1.3 2003/06/04 16:30:40 mike Exp $".
*/ */

@ -1,5 +1,5 @@
/* /*
* "$Id: mxml.h,v 1.4 2003/06/04 02:34:30 mike Exp $" * "$Id: mxml.h,v 1.5 2003/06/04 16:30:40 mike Exp $"
* *
* Header file for mini-XML, a small XML-like file parsing library. * Header file for mini-XML, a small XML-like file parsing library.
* *
@ -31,6 +31,7 @@
# include <stdlib.h> # include <stdlib.h>
# include <string.h> # include <string.h>
# include <ctype.h> # include <ctype.h>
# include <errno.h>
/* /*
@ -38,7 +39,12 @@
*/ */
# define MXML_NO_CALLBACK (mxml_type_t (*)(mxml_node_t *))0 # define MXML_NO_CALLBACK (mxml_type_t (*)(mxml_node_t *))0
# define MXML_WRAP 72 /* 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 */
/* /*
@ -109,7 +115,8 @@ extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name);
extern void mxmlElementSetAttr(mxml_node_t *node, const char *name, extern void mxmlElementSetAttr(mxml_node_t *node, const char *name,
const char *value); const char *value);
extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top, extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top,
const char *name); const char *name, const char *attr,
const char *value, int descend);
extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp, extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp,
mxml_type_t (*cb)(mxml_node_t *)); mxml_type_t (*cb)(mxml_node_t *));
extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name); extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name);
@ -119,8 +126,10 @@ extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real);
extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace, extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace,
const char *string); const char *string);
extern int mxmlSaveFile(mxml_node_t *node, FILE *fp); extern int mxmlSaveFile(mxml_node_t *node, FILE *fp);
extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top); extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top,
extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top); int descend);
extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top,
int descend);
/* /*
@ -134,5 +143,5 @@ extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top);
/* /*
* End of "$Id: mxml.h,v 1.4 2003/06/04 02:34:30 mike Exp $". * End of "$Id: mxml.h,v 1.5 2003/06/04 16:30:40 mike Exp $".
*/ */

@ -1,7 +1,7 @@
/* /*
* "$Id: mxmldoc.c,v 1.1 2003/06/04 03:07:47 mike Exp $" * "$Id: mxmldoc.c,v 1.2 2003/06/04 16:30:40 mike Exp $"
* *
* Documentation generator for mini-XML, a small XML-like file parsing * Documentation generator using mini-XML, a small XML-like file parsing
* library. * library.
* *
* Copyright 2003 by Michael Sweet. * Copyright 2003 by Michael Sweet.
@ -28,12 +28,83 @@
#include "mxml.h" #include "mxml.h"
/*
* This program scans source and header files and produces public API
* documentation for code that conforms to the CUPS Configuration
* Management Plan (CMP) coding standards. Please see the following web
* page for details:
*
* http://www.cups.org/cmp.html
*
* Using Mini-XML, this program creates and maintains an XML representation
* of the public API code documentation which can then be converted to HTML
* as desired. The following is a poor-man's schema:
*
* <namespace name=""> [name is usually "std"...]
* <constant name="">
* <description>descriptive text</description>
* </constant>
*
* <enumeration name="">
* <constant name="">...</constant>
* </enumeration>
*
* <typedef name="">
* <description>descriptive text</description>
* <type>type string</type>
* </typedef>
*
* <function name="">
* <description>descriptive text</description>
* <iargument name="">
* <description>descriptive text</description>
* <type>type string</type>
* </iargument>
* <oargument name="">
* <description>descriptive text</description>
* <type>type string</type>
* </oargument>
* <ioargument name="">
* <description>descriptive text</description>
* <type>type string</type>
* </ioargument>
* <returnvalue>
* <description>descriptive text</description>
* <type>type string</type>
* </returnvalue>
* <seealso>function names separated by spaces</seealso>
* </function>
*
* <variable name="">
* <description>descriptive text</description>
* <type>type string</type>
* </variable>
*
* <struct name="">
* <description>descriptive text</description>
* <variable name="">...</variable>
* <function name="">...</function>
* </struct>
*
* <class name="" parent="">
* <description>descriptive text</description>
* <class name="">...</class>
* <enumeration name="">...</enumeration>
* <function name="">...</function>
* <struct name="">...</struct>
* <variable name="">...</variable>
* </class>
* </namespace>
*/
/* /*
* Local functions... * Local functions...
*/ */
static void insert_func(mxml_node_t *tree, mxml_node_t *func); static void insert_node(mxml_node_t *tree, mxml_node_t *func);
static int scan_file(const char *filename, mxml_node_t *tree); static int scan_file(const char *filename, FILE *fp,
mxml_node_t *doc);
/* /*
@ -44,151 +115,184 @@ int /* O - Exit status */
main(int argc, /* I - Number of command-line args */ main(int argc, /* I - Number of command-line args */
char *argv[]) /* I - Command-line args */ char *argv[]) /* I - Command-line args */
{ {
int i; /* Looping var */
FILE *fp; /* File to read */ FILE *fp; /* File to read */
mxml_node_t *tree, /* XML tree */ mxml_node_t *doc; /* XML documentation tree */
*node; /* Node which should be in test.xml */
/* /*
* Check arguments... * Check arguments...
*/ */
if (argc != 2) if (argc < 2)
{ {
fputs("Usage: testmxml filename.xml\n", stderr); fputs("Usage: mxmldoc filename.xml [source files] >filename.html\n", stderr);
return (1); return (1);
} }
/* /*
* Open the file... * Read the XML documentation file, if it exists...
*/ */
if ((fp = fopen(argv[1], "r")) == NULL) if ((fp = fopen(argv[1], "r")) != NULL)
{ {
perror(argv[1]); /*
return (1); * Read the existing XML file...
} */
/*
* Read the file...
*/
tree = mxmlLoadFile(NULL, fp, type_cb); doc = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);
fclose(fp); fclose(fp);
if (!tree) if (!doc)
{ {
fputs("Unable to read XML file!\n", stderr); fprintf(stderr, "Unable to read the XML documentation file \"%s\"!\n",
return (1); argv[1]);
return (1);
}
} }
else
if (!strcmp(argv[1], "test.xml"))
{ {
/* /*
* Verify that mxmlFindElement() and indirectly mxmlWalkNext() work * Create an empty XML documentation file...
* properly...
*/ */
if ((node = mxmlFindElement(tree, tree, "choice")) == NULL) doc = mxmlNewElement(NULL, "namespace");
mxmlElementSetAttr(doc, "name", "std");
}
/*
* Loop through all of the source files...
*/
for (i = 2; i < argc; i ++)
if ((fp = fopen(argv[i], "r")) == NULL)
{ {
fputs("Unable to find first <choice> element in XML tree!\n", stderr); fprintf(stderr, "Unable to open source file \"%s\": %s\n", argv[i],
mxmlDelete(tree); strerror(errno));
mxmlDelete(doc);
return (1); return (1);
} }
else if (scan_file(argv[i], fp, doc))
if ((node = mxmlFindElement(node, tree, "choice")) == NULL)
{ {
fputs("Unable to find second <choice> element in XML tree!\n", stderr); fclose(fp);
mxmlDelete(tree); mxmlDelete(doc);
return (1); return (1);
} }
} else
fclose(fp);
/* /*
* Print the XML tree... * Save the updated XML documentation file...
*/ */
mxmlSaveFile(tree, stdout); if ((fp = fopen(argv[1], "w")) != NULL)
puts(""); {
/*
* Write over the existing XML file...
*/
if (mxmlSaveFile(doc, fp))
{
fprintf(stderr, "Unable to write the XML documentation file \"%s\": %s!\n",
argv[1], strerror(errno));
fclose(fp);
mxmlDelete(doc);
return (1);
}
fclose(fp);
}
else
{
fprintf(stderr, "Unable to create the XML documentation file \"%s\": %s!\n",
argv[1], strerror(errno));
mxmlDelete(doc);
return (1);
}
/* /*
* Delete the tree and return... * Delete the tree and return...
*/ */
mxmlDelete(tree); mxmlDelete(doc);
return (0); return (0);
} }
/* /*
* 'insert_func()' - Insert a function into a tree. * 'insert_node()' - Insert a node into a tree.
*/ */
static void static void
insert_func(mxml_node_t *tree, /* I - Tree to insert into */ insert_node(mxml_node_t *tree, /* I - Tree to insert into */
mxml_node_t *func) /* I - Function to add */ mxml_node_t *node) /* I - Node to add */
{ {
mxml_node_t *node; /* Current node */ mxml_node_t *temp; /* Current node */
const char *funcname, /* Name of function */ const char *tempname, /* Name of current node */
*nodename; /* Name of current node */ *nodename; /* Name of node */
int diff; /* Different between names */
funcname = mxmlElementGetAttr(func, "name"); /*
* Get the node name...
*/
for (node = tree->child; node; node = node->next) nodename = mxmlElementGetAttr(node, "name");
{
if (node->type != MXML_ELEMENT ||
strcmp(node->value.element.name, "function"))
continue;
if ((nodename = mxmlElementGetAttr(node, "name")) == NULL) /*
continue; * Delete an existing definition, if one exists...
*/
if ((diff = strcmp(funcname, nodename)) == 0) if ((temp = mxmlFindElement(tree, tree, node->value.element.name,
{ "name", nodename, MXML_DESCEND_FIRST)) != NULL)
mxmlDelete(node); mxmlDelete(temp);
insert_func(tree, func);
return; /*
} * Insert the node into the tree...
*/
for (temp = tree->child; temp; temp = temp->next)
{
if ((tempname = mxmlElementGetAttr(temp, "name")) == NULL)
continue;
if (diff > 0) if (strcmp(nodename, tempname) > 0)
break; break;
} }
if (node) if (temp)
{ {
/* /*
* Insert function before this node... * Insert node before this temp...
*/ */
func->next = node; node->next = temp;
func->prev = node->prev; node->prev = temp->prev;
if (node->prev) if (temp->prev)
node->prev->next = func; temp->prev->next = node;
else else
tree->child = func; tree->child = node;
node->prev = func; temp->prev = node;
} }
else else
{ {
/* /*
* Append function to the end... * Append node to the end...
*/ */
func->prev = tree->last_child; node->prev = tree->last_child;
if (tree->last_child) if (tree->last_child)
tree->last_child->next = func; tree->last_child->next = node;
else else
tree->last_child = func; tree->last_child = node;
if (!tree->child) if (!tree->child)
tree->child = func; tree->child = node;
} }
} }
@ -198,12 +302,13 @@ insert_func(mxml_node_t *tree, /* I - Tree to insert into */
*/ */
static int /* O - 0 on success, -1 on error */ static int /* O - 0 on success, -1 on error */
scan_file(const char *filename, /* I - File to scan */ scan_file(const char *filename, /* I - Filename */
FILE *fp, /* I - File to scan */
mxml_node_t *tree) /* I - Function tree */ mxml_node_t *tree) /* I - Function tree */
{ {
} }
/* /*
* End of "$Id: mxmldoc.c,v 1.1 2003/06/04 03:07:47 mike Exp $". * End of "$Id: mxmldoc.c,v 1.2 2003/06/04 16:30:40 mike Exp $".
*/ */

@ -1,5 +1,5 @@
/* /*
* "$Id: testmxml.c,v 1.4 2003/06/04 01:23:21 mike Exp $" * "$Id: testmxml.c,v 1.5 2003/06/04 16:30:40 mike Exp $"
* *
* Test program for mini-XML, a small XML-like file parsing library. * Test program for mini-XML, a small XML-like file parsing library.
* *
@ -89,14 +89,16 @@ main(int argc, /* I - Number of command-line args */
* properly... * properly...
*/ */
if ((node = mxmlFindElement(tree, tree, "choice")) == NULL) if ((node = mxmlFindElement(tree, tree, "choice", NULL, NULL,
MXML_DESCEND)) == NULL)
{ {
fputs("Unable to find first <choice> element in XML tree!\n", stderr); fputs("Unable to find first <choice> element in XML tree!\n", stderr);
mxmlDelete(tree); mxmlDelete(tree);
return (1); return (1);
} }
if ((node = mxmlFindElement(node, tree, "choice")) == NULL) if ((node = mxmlFindElement(node, tree, "choice", NULL, NULL,
MXML_NO_DESCEND)) == NULL)
{ {
fputs("Unable to find second <choice> element in XML tree!\n", stderr); fputs("Unable to find second <choice> element in XML tree!\n", stderr);
mxmlDelete(tree); mxmlDelete(tree);
@ -140,7 +142,8 @@ type_cb(mxml_node_t *node) /* I - Element node */
if (!strcmp(type, "integer")) if (!strcmp(type, "integer"))
return (MXML_INTEGER); return (MXML_INTEGER);
else if (!strcmp(type, "opaque")) else if (!strcmp(type, "opaque") ||
!strcmp(type, "pre") || !strcmp(type, "PRE"))
return (MXML_OPAQUE); return (MXML_OPAQUE);
else if (!strcmp(type, "real")) else if (!strcmp(type, "real"))
return (MXML_REAL); return (MXML_REAL);
@ -150,5 +153,5 @@ type_cb(mxml_node_t *node) /* I - Element node */
/* /*
* End of "$Id: testmxml.c,v 1.4 2003/06/04 01:23:21 mike Exp $". * End of "$Id: testmxml.c,v 1.5 2003/06/04 16:30:40 mike Exp $".
*/ */

Loading…
Cancel
Save