From 438ded6568840d3bf738436de8b5dd8e5d4ee24d Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Wed, 4 Jun 2003 16:30:40 +0000 Subject: [PATCH] Whitespace handling issues. Unicode output issues. Comment/declaration handling fixes. Add mxmldoc to build. --- CHANGES | 13 ++- Makefile.in | 56 ++++++++++- README | 34 ++++++- index.html | 221 ++++++++++++++++++++++++------------------- mxml-file.c | 189 +++++++++++++++++++++++++++++++++---- mxml-search.c | 125 ++++++++++++++++-------- mxml.h | 21 +++-- mxmldoc.c | 257 +++++++++++++++++++++++++++++++++++--------------- testmxml.c | 13 ++- 9 files changed, 672 insertions(+), 257 deletions(-) diff --git a/CHANGES b/CHANGES index 3006f21..d603494 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -README - 06/03/2003 +README - 06/04/2003 ------------------- @@ -9,6 +9,17 @@ CHANGES IN Mini-XML 0.93 character entities. - mxmlSaveFile() now uses newlines as whitespace 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 diff --git a/Makefile.in b/Makefile.in index 4ef89f7..0df3680 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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. # @@ -53,15 +53,15 @@ libdir = @libdir@ # LIBOBJS = mxml-attr.o mxml-file.o mxml-node.o mxml-search.o -OBJS = testmxml.o $(LIBOBJS) -TARGETS = libmxml.a testmxml +OBJS = mxmldoc.o testmxml.o $(LIBOBJS) +TARGETS = libmxml.a mxmldoc testmxml # # Make everything... # -all: $(TARGETS) +all: Makefile configure $(TARGETS) # @@ -86,6 +86,37 @@ install: $(TARGETS) 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 # @@ -98,6 +129,16 @@ libmxml.a: $(LIBOBJS) $(LIBOBJS): mxml.h +# +# mxmldoc +# + +mxmldoc: libmxml.a mxmldoc.o + $(CC) $(LDFLAGS) -o $@ mxmldoc.o libmxml.a + +mxmldoc.o: mxml.h + + # # testmxml # @@ -116,9 +157,14 @@ testmxml: libmxml.a testmxml.o testmxml.o: mxml.h + +# +# All object files depend on the 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 $". # diff --git a/README b/README index eef2b03..61a51b1 100644 --- a/README +++ b/README @@ -1,11 +1,11 @@ -README - 06/03/2003 +README - 06/04/2003 ------------------- INTRODUCTION 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 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 "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: mxml_node_t *node; - for (node = mxmlFindElement(tree, tree, "name"); + for (node = mxmlFindElement(tree, tree, "name", NULL, NULL, + MXML_DESCEND); node != NULL; - node = mxmlFindElement(node, tree, "name")) + node = mxmlFindElement(node, tree, "name", NULL, NULL, + MXML_DESCEND)) { ... do something ... } diff --git a/index.html b/index.html index 14add74..c147f4f 100644 --- a/index.html +++ b/index.html @@ -1,174 +1,199 @@ - - - - Mini-XML Home Page - - - - -

Back to Home Page ]

- -

Mini-XML Home Page

- -

Current Release: v0.93 [ Download Source (.tar.gz 40k) | -View Change Log ]

- -

Introduction

- -

Mini-XML is a small XML parsing library that you can use to + + + + Mini-XML Home Page + + + + +

Back to Home Page ]

+ +

Mini-XML Home Page

+ +

Current Release: v0.93 [ Download Source (.tar.gz 40k) | +View Change Log ]

+ +

Introduction

+ +

Mini-XML is a small XML parsing library that you can use to read XML and XML-like data files in your application without 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.

+ANSI C compilers) and a "make" program.

-

Mini-XML was created to support the basic hierarchy provided +

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.

+other types of processing on the data.

-

Building Mini-XML

+

Building Mini-XML

-

Mini-XML comes with an autoconf-based configure script; just -type the following command to get things going:

+

Mini-XML comes with an autoconf-based configure script; just +type the following command to get things going:

-
+
     ./configure
-
+
-

The default install prefix is /usr/local, which can be -overridden using the --prefix option:

+

The default install prefix is /usr/local, which can be +overridden using the --prefix option:

-
+
     ./configure --prefix=/foo
-
+
-

Once you have configured the software, type "make" to do the +

Once you have configured the software, type "make" to do the build and then run the test program to verify that things are -working, as follows:

+working, as follows:

-
+
     make
     ./testmxml test.xml
-
+
-

Installing Mini-XML

+

Installing Mini-XML

-

The "install" target will install Mini-XML in the lib and -include directories:

+

The "install" target will install Mini-XML in the lib and +include directories:

-
+
     make install
-
+
-

Once you have installed it, use the "-lmxml" option to link -your application against it.

+

Once you have installed it, use the "-lmxml" option to link +your application against it.

-

Documentation

+

Documentation

-

The documentation is currently just in this page. At some +

The documentation is currently just in this page. At some point I'll probably do some proper documentation, but for now -just read here and look at the testmxml.c source file for an +just read here and look at the testmxml.c source file for an example of reading and printing the contents of an XML file to -stdout.

+stdout.

-

Mini-XML provides a single header file which you include:

+

Mini-XML provides a single header file which you include:

-
-    #include <mxml.h>
-
+
+    #include <mxml.h>
+
-

Nodes are defined by the mxml_node_t structure; the -type member defines the node type (element, integer, +

Nodes are defined by the mxml_node_t structure; the +type member defines the node type (element, integer, opaque, real, or text) which determines which value you want to -look at in the value union. New nodes can be created -using the mxmlNewElement(), mxmlNewInteger(), -mxmlNewOpaque(), mxmlNewReal(), and -mxmlNewText() functions. Only elements can have child -nodes, and the top node must be an element, usually "?xml".

+look at in the value union. New nodes can be created +using the mxmlNewElement(), mxmlNewInteger(), +mxmlNewOpaque(), mxmlNewReal(), and +mxmlNewText() functions. Only elements can have child +nodes, and the top node must be an element, usually "?xml".

-

You load an XML file using the mxmlLoadFile() function:

+

You load an XML file using the mxmlLoadFile() function:

-
+
     FILE *fp;
     mxml_node_t *tree;
 
     fp = fopen("filename.xml", "r");
     tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);
     fclose(fp);
-
+
-

Similarly, you save an XML file using the mxmlSaveFile() -function:

+

Similarly, you save an XML file using the mxmlSaveFile() +function:

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

You can find a named element/node using the -mxmlFindElement() function:

+

You can find a named element/node using the +mxmlFindElement() function:

-
-    mxml_node_t *node = mxmlFindElement(tree, tree, "name");
-
+
+    mxml_node_t *node = mxmlFindElement(tree, tree, "name", "attr",
+                                        "value", MXML_DESCEND);
+
-

You can also iterate with the same function: +

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:

+ +
     mxml_node_t *node;
 
-    for (node = mxmlFindElement(tree, tree, "name");
+    for (node = mxmlFindElement(tree, tree, "name", NULL, NULL, MXML_DESCEND);
 	 node != NULL;
-	 node = mxmlFindElement(node, tree, "name"))
+	 node = mxmlFindElement(node, tree, "name", NULL, NULL, MXML_DESCEND))
     {
       ... do something ...
     }
-
+
-

Finally, 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:

+

Finally, 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:

-
+
     mxmlDelete(tree);
-
+
-

Getting Help and Reporting Problems

+

Getting Help and Reporting Problems

-

You can email me at "mxml at easysw dot com" to +

You can email me at "mxml at easysw dot com" to report problems and/or ask for help. Just don't expect an -instant response, as I get a lot of email...

+instant response, as I get a lot of email...

-

Legal Stuff

+

Legal Stuff

-

The Mini-XML library is Copyright 2003 by Michael Sweet.

+

The Mini-XML library is Copyright 2003 by Michael Sweet.

-

This library is free software; you can redistribute it +

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any -later version.

+later version.

-

This library is distributed in the hope that it will be +

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for -more details.

+more details.

-

You should have received a copy of the GNU Library General +

You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA -02139, USA.

+02139, USA.

- - + + diff --git a/mxml-file.c b/mxml-file.c index aed38e3..19ab436 100644 --- a/mxml-file.c +++ b/mxml-file.c @@ -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. * @@ -131,6 +131,32 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */ 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 == '<') { @@ -144,40 +170,114 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */ if (isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) break; else if (bufptr < (buffer + sizeof(buffer) - 1)) + { *bufptr++ = ch; + if ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) + break; + } + *bufptr = '\0'; - bufptr = buffer; if (!strcmp(buffer, "!--")) { /* - * Skip comment... + * Gather rest of comment... */ - buffer[3] = '\0'; - while ((ch = getc(fp)) != EOF) { - *bufptr++ = ch; - - if ((bufptr - buffer) == 3) + if (ch == '>' && bufptr > (buffer + 4) && + !strncmp(bufptr - 2, "--", 2)) + break; + else if (bufptr < (buffer + sizeof(buffer) - 1)) + *bufptr++ = ch; + else { - if (!strcmp(buffer, "-->")) - break; + fprintf(stderr, "Comment too long in file under parent <%s>!\n", + parent ? parent->value.element.name : "null"); + break; + } + } - buffer[0] = buffer[1]; - buffer[1] = buffer[2]; - bufptr --; + /* + * Error out if we didn't get the whole comment... + */ + + 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; - 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] == '/') { @@ -260,6 +360,8 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */ type = (*cb)(parent); } } + + bufptr = buffer; } else if (ch == '&') { @@ -635,7 +737,7 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ 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] == '?') @@ -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) return (-1); - if (node->value.element.name[0] != '?') + if (node->value.element.name[0] != '?' && + node->value.element.name[0] != '!') { if ((n = fprintf(fp, "", node->value.element.name)) < 0) return (-1); @@ -661,6 +764,13 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ col += n; } } + else if (node->value.element.name[0] == '!') + { + if (putc('>', fp) < 0) + return (-1); + else + col ++; + } else if (fputs("/>", fp) < 0) return (-1); else @@ -772,6 +882,45 @@ mxml_write_string(const char *s, /* I - String to write */ if (fputs("<", fp) < 0) return (-1); } + else if (*s == '>') + { + if (fputs(">", 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(" ", fp) < 0) + return (-1); + } + else if (fprintf(fp, "&#x%x;", ch) < 0) + return (-1); + } else if (putc(*s, fp) < 0) 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 $". */ diff --git a/mxml-search.c b/mxml-search.c index 0a545df..857472a 100644 --- a/mxml-search.c +++ b/mxml-search.c @@ -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 * parsing library. @@ -21,7 +21,6 @@ * mxmlFindElement() - Find the named element. * mxmlWalkNext() - Walk to the next 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" -/* - * Local functions... - */ - -mxml_node_t *mxml_walk_next(mxml_node_t *node, mxml_node_t *top, - int descend); - - /* * '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 */ mxmlFindElement(mxml_node_t *node, /* I - Current 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... */ - node = mxmlWalkNext(node, top); + node = mxmlWalkNext(node, top, descend); /* * Loop until we find a matching element... @@ -66,14 +70,38 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */ if (node->type == MXML_ELEMENT && node->value.element.name && - !strcmp(node->value.element.name, name)) - return (node); + (!name || !strcmp(node->value.element.name, name))) + { + /* + * 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); @@ -86,9 +114,29 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */ mxml_node_t * /* O - Next node or NULL */ 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 */ 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) return (NULL); else if (node->prev) - return (node->prev); - else if (node->parent != top) - return (node->parent); - else - return (NULL); -} + { + if (node->prev->last_child && descend) + { + /* + * Find the last child under the previous node... + */ + node = node->prev->last_child; -/* - * 'mxml_walk_next()' - Walk to the next logical node in the tree. - */ + while (node->last_child) + node = node->last_child; -mxml_node_t * /* O - Next node or NULL */ -mxml_walk_next(mxml_node_t *node, /* I - Current node */ - mxml_node_t *top, /* I - Top node */ - int descend) /* I - 1 = descend, 0 = don't */ -{ - if (!node) - return (NULL); - else if (node->child && descend) - return (node->child); - else if (node->next) - return (node->next); + return (node); + } + else + return (node->prev); + } else if (node->parent != top) - return (mxml_walk_next(node->parent, top, 0)); + return (node->parent); else 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 $". */ diff --git a/mxml.h b/mxml.h index 0d2d1d4..d18c38a 100644 --- a/mxml.h +++ b/mxml.h @@ -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. * @@ -31,6 +31,7 @@ # include # include # include +# include /* @@ -38,7 +39,12 @@ */ # 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, const char *value); 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, mxml_type_t (*cb)(mxml_node_t *)); 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, const char *string); 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 *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top); +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, + 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 $". */ diff --git a/mxmldoc.c b/mxmldoc.c index 577dae9..bf7ece6 100644 --- a/mxmldoc.c +++ b/mxmldoc.c @@ -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. * * Copyright 2003 by Michael Sweet. @@ -28,12 +28,83 @@ #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: + * + * [name is usually "std"...] + * + * descriptive text + * + * + * + * ... + * + * + * + * descriptive text + * type string + * + * + * + * descriptive text + * + * descriptive text + * type string + * + * + * descriptive text + * type string + * + * + * descriptive text + * type string + * + * + * descriptive text + * type string + * + * function names separated by spaces + * + * + * + * descriptive text + * type string + * + * + * + * descriptive text + * ... + * ... + * + * + * + * descriptive text + * ... + * ... + * ... + * ... + * ... + * + * + */ + + /* * Local functions... */ -static void insert_func(mxml_node_t *tree, mxml_node_t *func); -static int scan_file(const char *filename, mxml_node_t *tree); +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); /* @@ -44,151 +115,184 @@ int /* O - Exit status */ main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line args */ { + int i; /* Looping var */ FILE *fp; /* File to read */ - mxml_node_t *tree, /* XML tree */ - *node; /* Node which should be in test.xml */ + mxml_node_t *doc; /* XML documentation tree */ /* * 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); } /* - * 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 file... - */ + /* + * Read the existing XML file... + */ - tree = mxmlLoadFile(NULL, fp, type_cb); + doc = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK); - fclose(fp); + fclose(fp); - if (!tree) - { - fputs("Unable to read XML file!\n", stderr); - return (1); + if (!doc) + { + fprintf(stderr, "Unable to read the XML documentation file \"%s\"!\n", + argv[1]); + return (1); + } } - - if (!strcmp(argv[1], "test.xml")) + else { /* - * Verify that mxmlFindElement() and indirectly mxmlWalkNext() work - * properly... + * Create an empty XML documentation file... */ - 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 element in XML tree!\n", stderr); - mxmlDelete(tree); + fprintf(stderr, "Unable to open source file \"%s\": %s\n", argv[i], + strerror(errno)); + mxmlDelete(doc); return (1); } - - if ((node = mxmlFindElement(node, tree, "choice")) == NULL) + else if (scan_file(argv[i], fp, doc)) { - fputs("Unable to find second element in XML tree!\n", stderr); - mxmlDelete(tree); + fclose(fp); + mxmlDelete(doc); return (1); } - } + else + fclose(fp); /* - * Print the XML tree... + * Save the updated XML documentation file... */ - mxmlSaveFile(tree, stdout); - puts(""); + if ((fp = fopen(argv[1], "w")) != NULL) + { + /* + * 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... */ - mxmlDelete(tree); + mxmlDelete(doc); return (0); } /* - * 'insert_func()' - Insert a function into a tree. + * 'insert_node()' - Insert a node into a tree. */ static void -insert_func(mxml_node_t *tree, /* I - Tree to insert into */ - mxml_node_t *func) /* I - Function to add */ +insert_node(mxml_node_t *tree, /* I - Tree to insert into */ + mxml_node_t *node) /* I - Node to add */ { - mxml_node_t *node; /* Current node */ - const char *funcname, /* Name of function */ - *nodename; /* Name of current node */ - int diff; /* Different between names */ + mxml_node_t *temp; /* Current node */ + const char *tempname, /* Name of current node */ + *nodename; /* Name of node */ - funcname = mxmlElementGetAttr(func, "name"); + /* + * Get the node name... + */ - for (node = tree->child; node; node = node->next) - { - if (node->type != MXML_ELEMENT || - strcmp(node->value.element.name, "function")) - continue; + nodename = mxmlElementGetAttr(node, "name"); - if ((nodename = mxmlElementGetAttr(node, "name")) == NULL) - continue; + /* + * Delete an existing definition, if one exists... + */ - if ((diff = strcmp(funcname, nodename)) == 0) - { - mxmlDelete(node); - insert_func(tree, func); - return; - } + if ((temp = mxmlFindElement(tree, tree, node->value.element.name, + "name", nodename, MXML_DESCEND_FIRST)) != NULL) + mxmlDelete(temp); + + /* + * 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; } - if (node) + if (temp) { /* - * Insert function before this node... + * Insert node before this temp... */ - func->next = node; - func->prev = node->prev; + node->next = temp; + node->prev = temp->prev; - if (node->prev) - node->prev->next = func; + if (temp->prev) + temp->prev->next = node; else - tree->child = func; + tree->child = node; - node->prev = func; + temp->prev = node; } 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) - tree->last_child->next = func; + tree->last_child->next = node; else - tree->last_child = func; + tree->last_child = node; 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 */ -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 */ { } /* - * 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 $". */ diff --git a/testmxml.c b/testmxml.c index 17cf9bc..02a6554 100644 --- a/testmxml.c +++ b/testmxml.c @@ -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. * @@ -89,14 +89,16 @@ main(int argc, /* I - Number of command-line args */ * properly... */ - if ((node = mxmlFindElement(tree, tree, "choice")) == NULL) + if ((node = mxmlFindElement(tree, tree, "choice", NULL, NULL, + MXML_DESCEND)) == NULL) { fputs("Unable to find first element in XML tree!\n", stderr); mxmlDelete(tree); 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 element in XML tree!\n", stderr); mxmlDelete(tree); @@ -140,7 +142,8 @@ type_cb(mxml_node_t *node) /* I - Element node */ if (!strcmp(type, "integer")) return (MXML_INTEGER); - else if (!strcmp(type, "opaque")) + else if (!strcmp(type, "opaque") || + !strcmp(type, "pre") || !strcmp(type, "PRE")) return (MXML_OPAQUE); else if (!strcmp(type, "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 $". */