summaryrefslogtreecommitdiff
path: root/xml/mxmldoc.c
diff options
context:
space:
mode:
Diffstat (limited to 'xml/mxmldoc.c')
-rw-r--r--xml/mxmldoc.c5809
1 files changed, 5809 insertions, 0 deletions
diff --git a/xml/mxmldoc.c b/xml/mxmldoc.c
new file mode 100644
index 0000000..28316ee
--- /dev/null
+++ b/xml/mxmldoc.c
@@ -0,0 +1,5809 @@
+/*#define DEBUG 1*/
+/*
+ * "$Id: mxmldoc.c 440 2011-08-11 18:51:26Z mike $"
+ *
+ * Documentation generator using Mini-XML, a small XML-like file parsing
+ * library.
+ *
+ * Copyright 2003-2011 by Michael R Sweet.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Michael R Sweet and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "COPYING"
+ * which should have been included with this file. If this file is
+ * missing or damaged, see the license at:
+ *
+ * http://www.minixml.org/
+ *
+ * Contents:
+ *
+ * main() - Main entry for test program.
+ * add_variable() - Add a variable or argument.
+ * find_public() - Find a public function, type, etc.
+ * get_comment_info() - Get info from comment.
+ * get_text() - Get the text for a node.
+ * load_cb() - Set the type of child nodes.
+ * new_documentation() - Create a new documentation tree.
+ * remove_directory() - Remove a directory.
+ * safe_strcpy() - Copy a string allowing for overlapping strings.
+ * scan_file() - Scan a source file.
+ * sort_node() - Insert a node sorted into a tree.
+ * update_comment() - Update a comment node.
+ * usage() - Show program usage...
+ * write_description() - Write the description text.
+ * write_element() - Write an element's text nodes.
+ * write_file() - Copy a file to the output.
+ * write_function() - Write documentation for a function.
+ * write_html() - Write HTML documentation.
+ * write_html_head() - Write the standard HTML header.
+ * write_man() - Write manpage documentation.
+ * write_scu() - Write a structure, class, or union.
+ * write_string() - Write a string, quoting HTML special chars as needed.
+ * write_toc() - Write a table-of-contents.
+ * write_tokens() - Write <Token> nodes for all APIs.
+ * ws_cb() - Whitespace callback for saving.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "config.h"
+#include "mxml.h"
+#include <time.h>
+#include <sys/stat.h>
+#ifndef WIN32
+# include <dirent.h>
+# include <unistd.h>
+#endif /* !WIN32 */
+#ifdef __APPLE__
+# include <spawn.h>
+# include <sys/wait.h>
+extern char **environ;
+#endif /* __APPLE__ */
+
+
+/*
+ * 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:
+ *
+ * <?xml version="1.0"?>
+ * <mxmldoc xmlns="http://www.easysw.com"
+ * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ * xsi:schemaLocation="http://www.minixml.org/mxmldoc.xsd">
+ *
+ * <namespace name=""> [optional...]
+ * <constant name="">
+ * <description>descriptive text</description>
+ * </constant>
+ *
+ * <enumeration name="">
+ * <description>descriptive text</description>
+ * <constant name="">...</constant>
+ * </enumeration>
+ *
+ * <typedef name="">
+ * <description>descriptive text</description>
+ * <type>type string</type>
+ * </typedef>
+ *
+ * <function name="" scope="">
+ * <description>descriptive text</description>
+ * <argument name="" direction="I|O|IO" default="">
+ * <description>descriptive text</description>
+ * <type>type string</type>
+ * </argument>
+ * <returnvalue>
+ * <description>descriptive text</description>
+ * <type>type string</type>
+ * </returnvalue>
+ * <seealso>function names separated by spaces</seealso>
+ * </function>
+ *
+ * <variable name="" scope="">
+ * <description>descriptive text</description>
+ * <type>type string</type>
+ * </variable>
+ *
+ * <struct name="">
+ * <description>descriptive text</description>
+ * <variable name="">...</variable>
+ * <function name="">...</function>
+ * </struct>
+ *
+ * <union name="">
+ * <description>descriptive text</description>
+ * <variable name="">...</variable>
+ * </union>
+ *
+ * <class name="" parent="">
+ * <description>descriptive text</description>
+ * <class name="">...</class>
+ * <enumeration name="">...</enumeration>
+ * <function name="">...</function>
+ * <struct name="">...</struct>
+ * <variable name="">...</variable>
+ * </class>
+ * </namespace>
+ * </mxmldoc>
+ */
+
+
+/*
+ * Basic states for file parser...
+ */
+
+#define STATE_NONE 0 /* No state - whitespace, etc. */
+#define STATE_PREPROCESSOR 1 /* Preprocessor directive */
+#define STATE_C_COMMENT 2 /* Inside a C comment */
+#define STATE_CXX_COMMENT 3 /* Inside a C++ comment */
+#define STATE_STRING 4 /* Inside a string constant */
+#define STATE_CHARACTER 5 /* Inside a character constant */
+#define STATE_IDENTIFIER 6 /* Inside a keyword/identifier */
+
+
+/*
+ * Output modes...
+ */
+
+#define OUTPUT_NONE 0 /* No output */
+#define OUTPUT_HTML 1 /* Output HTML */
+#define OUTPUT_XML 2 /* Output XML */
+#define OUTPUT_MAN 3 /* Output nroff/man */
+#define OUTPUT_TOKENS 4 /* Output docset Tokens.xml file */
+
+
+/*
+ * Local functions...
+ */
+
+static mxml_node_t *add_variable(mxml_node_t *parent, const char *name,
+ mxml_node_t *type);
+static mxml_node_t *find_public(mxml_node_t *node, mxml_node_t *top,
+ const char *name);
+static char *get_comment_info(mxml_node_t *description);
+static char *get_text(mxml_node_t *node, char *buffer, int buflen);
+static mxml_type_t load_cb(mxml_node_t *node);
+static mxml_node_t *new_documentation(mxml_node_t **mxmldoc);
+static int remove_directory(const char *path);
+static void safe_strcpy(char *dst, const char *src);
+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);
+static void update_comment(mxml_node_t *parent,
+ mxml_node_t *comment);
+static void usage(const char *option);
+static void write_description(FILE *out, mxml_node_t *description,
+ const char *element, int summary);
+static void write_element(FILE *out, mxml_node_t *doc,
+ mxml_node_t *element, int mode);
+static void write_file(FILE *out, const char *file);
+static void write_function(FILE *out, mxml_node_t *doc,
+ mxml_node_t *function, int level);
+static void write_html(const char *section, const char *title,
+ const char *footerfile,
+ const char *headerfile,
+ const char *introfile, const char *cssfile,
+ const char *framefile,
+ const char *docset, const char *docversion,
+ const char *feedname, const char *feedurl,
+ mxml_node_t *doc);
+static void write_html_head(FILE *out, const char *section,
+ const char *title, const char *cssfile);
+static void write_man(const char *man_name, const char *section,
+ const char *title, const char *headerfile,
+ const char *footerfile, const char *introfile,
+ mxml_node_t *doc);
+static void write_scu(FILE *out, mxml_node_t *doc,
+ mxml_node_t *scut);
+static void write_string(FILE *out, const char *s, int mode);
+static void write_toc(FILE *out, mxml_node_t *doc,
+ const char *introfile, const char *target,
+ int xml);
+static void write_tokens(FILE *out, mxml_node_t *doc,
+ const char *path);
+static const char *ws_cb(mxml_node_t *node, int where);
+
+
+/*
+ * 'main()' - Main entry for test program.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line args */
+ char *argv[]) /* I - Command-line args */
+{
+ int i; /* Looping var */
+ int len; /* Length of argument */
+ FILE *fp; /* File to read */
+ mxml_node_t *doc; /* XML documentation tree */
+ mxml_node_t *mxmldoc; /* mxmldoc node */
+ const char *cssfile, /* CSS stylesheet file */
+ *docset, /* Documentation set directory */
+ *docversion, /* Documentation set version */
+ *feedname, /* Feed name for documentation set */
+ *feedurl, /* Feed URL for documentation set */
+ *footerfile, /* Footer file */
+ *framefile, /* Framed HTML basename */
+ *headerfile, /* Header file */
+ *introfile, /* Introduction file */
+ *name, /* Name of manpage */
+ *path, /* Path to help file for tokens */
+ *section, /* Section/keywords of documentation */
+ *title, /* Title of documentation */
+ *xmlfile; /* XML file */
+ int mode, /* Output mode */
+ update; /* Updated XML file */
+
+
+ /*
+ * Check arguments...
+ */
+
+ cssfile = NULL;
+ doc = NULL;
+ docset = NULL;
+ docversion = NULL;
+ feedname = NULL;
+ feedurl = NULL;
+ footerfile = NULL;
+ framefile = NULL;
+ headerfile = NULL;
+ introfile = NULL;
+ mode = OUTPUT_HTML;
+ mxmldoc = NULL;
+ name = NULL;
+ path = NULL;
+ section = NULL;
+ title = NULL;
+ update = 0;
+ xmlfile = NULL;
+
+ for (i = 1; i < argc; i ++)
+ if (!strcmp(argv[i], "--help"))
+ {
+ /*
+ * Show help...
+ */
+
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--version"))
+ {
+ /*
+ * Show version...
+ */
+
+ puts(MXML_VERSION + 10);
+ return (0);
+ }
+ else if (!strcmp(argv[i], "--css") && !cssfile)
+ {
+ /*
+ * Set CSS stylesheet file...
+ */
+
+ i ++;
+ if (i < argc)
+ cssfile = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--docset") && !docset)
+ {
+ /*
+ * Set documentation set directory...
+ */
+
+ i ++;
+ if (i < argc)
+ docset = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--docversion") && !docversion)
+ {
+ /*
+ * Set documentation set directory...
+ */
+
+ i ++;
+ if (i < argc)
+ docversion = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--footer") && !footerfile)
+ {
+ /*
+ * Set footer file...
+ */
+
+ i ++;
+ if (i < argc)
+ footerfile = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--feedname") && !feedname)
+ {
+ /*
+ * Set documentation set feed name...
+ */
+
+ i ++;
+ if (i < argc)
+ feedname = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--feedurl") && !feedurl)
+ {
+ /*
+ * Set documentation set feed name...
+ */
+
+ i ++;
+ if (i < argc)
+ feedurl = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--framed") && !framefile)
+ {
+ /*
+ * Set base filename for framed HTML output...
+ */
+
+ i ++;
+ if (i < argc)
+ framefile = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--header") && !headerfile)
+ {
+ /*
+ * Set header file...
+ */
+
+ i ++;
+ if (i < argc)
+ headerfile = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--intro") && !introfile)
+ {
+ /*
+ * Set intro file...
+ */
+
+ i ++;
+ if (i < argc)
+ introfile = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--man") && !name)
+ {
+ /*
+ * Output manpage...
+ */
+
+ i ++;
+ if (i < argc)
+ {
+ mode = OUTPUT_MAN;
+ name = argv[i];
+ }
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--no-output"))
+ mode = OUTPUT_NONE;
+ else if (!strcmp(argv[i], "--section") && !section)
+ {
+ /*
+ * Set section/keywords...
+ */
+
+ i ++;
+ if (i < argc)
+ section = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--title") && !title)
+ {
+ /*
+ * Set title...
+ */
+
+ i ++;
+ if (i < argc)
+ title = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (!strcmp(argv[i], "--tokens"))
+ {
+ /*
+ * Output Tokens.xml file...
+ */
+
+ mode = OUTPUT_TOKENS;
+
+ i ++;
+ if (i < argc)
+ path = argv[i];
+ else
+ usage(NULL);
+ }
+ else if (argv[i][0] == '-')
+ {
+ /*
+ * Unknown/bad option...
+ */
+
+ usage(argv[i]);
+ }
+ else
+ {
+ /*
+ * Process XML or source file...
+ */
+
+ len = (int)strlen(argv[i]);
+ if (len > 4 && !strcmp(argv[i] + len - 4, ".xml"))
+ {
+ /*
+ * Set XML file...
+ */
+
+ if (xmlfile)
+ usage(NULL);
+
+ xmlfile = argv[i];
+
+ if (!doc)
+ {
+ if ((fp = fopen(argv[i], "r")) != NULL)
+ {
+ /*
+ * Read the existing XML file...
+ */
+
+ doc = mxmlLoadFile(NULL, fp, load_cb);
+
+ fclose(fp);
+
+ if (!doc)
+ {
+ mxmldoc = NULL;
+
+ fprintf(stderr,
+ "mxmldoc: Unable to read the XML documentation file "
+ "\"%s\"!\n", argv[i]);
+ }
+ else if ((mxmldoc = mxmlFindElement(doc, doc, "mxmldoc", NULL,
+ NULL, MXML_DESCEND)) == NULL)
+ {
+ fprintf(stderr,
+ "mxmldoc: XML documentation file \"%s\" is missing "
+ "<mxmldoc> node!!\n", argv[i]);
+
+ mxmlDelete(doc);
+ doc = NULL;
+ }
+ }
+ else
+ {
+ doc = NULL;
+ mxmldoc = NULL;
+ }
+
+ if (!doc)
+ doc = new_documentation(&mxmldoc);
+ }
+ }
+ else
+ {
+ /*
+ * Load source file...
+ */
+
+ update = 1;
+
+ if (!doc)
+ doc = new_documentation(&mxmldoc);
+
+ if ((fp = fopen(argv[i], "r")) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to open source file \"%s\": %s\n",
+ argv[i], strerror(errno));
+ mxmlDelete(doc);
+ return (1);
+ }
+ else if (scan_file(argv[i], fp, mxmldoc))
+ {
+ fclose(fp);
+ mxmlDelete(doc);
+ return (1);
+ }
+ else
+ fclose(fp);
+ }
+ }
+
+ if (update && xmlfile)
+ {
+ /*
+ * Save the updated XML documentation file...
+ */
+
+ if ((fp = fopen(xmlfile, "w")) != NULL)
+ {
+ /*
+ * Write over the existing XML file...
+ */
+
+ mxmlSetWrapMargin(0);
+
+ if (mxmlSaveFile(doc, fp, ws_cb))
+ {
+ fprintf(stderr,
+ "mxmldoc: Unable to write the XML documentation file \"%s\": "
+ "%s!\n", xmlfile, strerror(errno));
+ fclose(fp);
+ mxmlDelete(doc);
+ return (1);
+ }
+
+ fclose(fp);
+ }
+ else
+ {
+ fprintf(stderr,
+ "mxmldoc: Unable to create the XML documentation file \"%s\": "
+ "%s!\n", xmlfile, strerror(errno));
+ mxmlDelete(doc);
+ return (1);
+ }
+ }
+
+ switch (mode)
+ {
+ case OUTPUT_HTML :
+ /*
+ * Write HTML documentation...
+ */
+
+ write_html(section, title ? title : "Documentation", footerfile,
+ headerfile, introfile, cssfile, framefile, docset,
+ docversion, feedname, feedurl, mxmldoc);
+ break;
+
+ case OUTPUT_MAN :
+ /*
+ * Write manpage documentation...
+ */
+
+ write_man(name, section, title, footerfile, headerfile, introfile,
+ mxmldoc);
+ break;
+
+ case OUTPUT_TOKENS :
+ fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<Tokens version=\"1.0\">\n", stdout);
+
+ write_tokens(stdout, mxmldoc, path);
+
+ fputs("</Tokens>\n", stdout);
+ break;
+ }
+
+ /*
+ * Delete the tree and return...
+ */
+
+ mxmlDelete(doc);
+
+ return (0);
+}
+
+
+/*
+ * 'add_variable()' - Add a variable or argument.
+ */
+
+static mxml_node_t * /* O - New variable/argument */
+add_variable(mxml_node_t *parent, /* I - Parent node */
+ const char *name, /* I - "argument" or "variable" */
+ mxml_node_t *type) /* I - Type nodes */
+{
+ mxml_node_t *variable, /* New variable */
+ *node, /* Current node */
+ *next; /* Next node */
+ char buffer[16384], /* String buffer */
+ *bufptr; /* Pointer into buffer */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "add_variable(parent=%p, name=\"%s\", type=%p)\n",
+ parent, name, type);
+#endif /* DEBUG */
+
+ /*
+ * Range check input...
+ */
+
+ if (!type || !type->child)
+ return (NULL);
+
+ /*
+ * Create the variable/argument node...
+ */
+
+ variable = mxmlNewElement(parent, name);
+
+ /*
+ * Check for a default value...
+ */
+
+ for (node = type->child; node; node = node->next)
+ if (!strcmp(node->value.text.string, "="))
+ break;
+
+ if (node)
+ {
+ /*
+ * Default value found, copy it and add as a "default" attribute...
+ */
+
+ for (bufptr = buffer; node; bufptr += strlen(bufptr))
+ {
+ if (node->value.text.whitespace && bufptr > buffer)
+ *bufptr++ = ' ';
+
+ strcpy(bufptr, node->value.text.string);
+
+ next = node->next;
+ mxmlDelete(node);
+ node = next;
+ }
+
+ mxmlElementSetAttr(variable, "default", buffer);
+ }
+
+ /*
+ * Extract the argument/variable name...
+ */
+
+ if (type->last_child->value.text.string[0] == ')')
+ {
+ /*
+ * Handle "type (*name)(args)"...
+ */
+
+ for (node = type->child; node; node = node->next)
+ if (node->value.text.string[0] == '(')
+ break;
+
+ for (bufptr = buffer; node; bufptr += strlen(bufptr))
+ {
+ if (node->value.text.whitespace && bufptr > buffer)
+ *bufptr++ = ' ';
+
+ strcpy(bufptr, node->value.text.string);
+
+ next = node->next;
+ mxmlDelete(node);
+ node = next;
+ }
+ }
+ else
+ {
+ /*
+ * Handle "type name"...
+ */
+
+ strcpy(buffer, type->last_child->value.text.string);
+ mxmlDelete(type->last_child);
+ }
+
+ /*
+ * Set the name...
+ */
+
+ mxmlElementSetAttr(variable, "name", buffer);
+
+ /*
+ * Add the remaining type information to the variable node...
+ */
+
+ mxmlAdd(variable, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type);
+
+ /*
+ * Add new new variable node...
+ */
+
+ return (variable);
+}
+
+
+/*
+ * 'find_public()' - Find a public function, type, etc.
+ */
+
+static mxml_node_t * /* I - Found node or NULL */
+find_public(mxml_node_t *node, /* I - Current node */
+ mxml_node_t *top, /* I - Top node */
+ const char *name) /* I - Name of element */
+{
+ mxml_node_t *description, /* Description node */
+ *comment; /* Comment node */
+
+
+ for (node = mxmlFindElement(node, top, name, NULL, NULL,
+ node == top ? MXML_DESCEND_FIRST :
+ MXML_NO_DESCEND);
+ node;
+ node = mxmlFindElement(node, top, name, NULL, NULL, MXML_NO_DESCEND))
+ {
+ /*
+ * Get the description for this node...
+ */
+
+ description = mxmlFindElement(node, node, "description", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ /*
+ * A missing or empty description signals a private node...
+ */
+
+ if (!description)
+ continue;
+
+ /*
+ * Look for @private@ in the comment text...
+ */
+
+ for (comment = description->child; comment; comment = comment->next)
+ if ((comment->type == MXML_TEXT &&
+ strstr(comment->value.text.string, "@private@")) ||
+ (comment->type == MXML_OPAQUE &&
+ strstr(comment->value.opaque, "@private@")))
+ break;
+
+ if (!comment)
+ {
+ /*
+ * No @private@, so return this node...
+ */
+
+ return (node);
+ }
+ }
+
+ /*
+ * If we get here, there are no (more) public nodes...
+ */
+
+ return (NULL);
+}
+
+
+/*
+ * 'get_comment_info()' - Get info from comment.
+ */
+
+static char * /* O - Info from comment */
+get_comment_info(
+ mxml_node_t *description) /* I - Description node */
+{
+ char text[10240], /* Description text */
+ since[255], /* @since value */
+ *ptr; /* Pointer into text */
+ static char info[1024]; /* Info string */
+
+
+ if (!description)
+ return ("");
+
+ get_text(description, text, sizeof(text));
+
+ for (ptr = strchr(text, '@'); ptr; ptr = strchr(ptr + 1, '@'))
+ {
+ if (!strncmp(ptr, "@deprecated@", 12))
+ return ("<span class=\"info\">&nbsp;DEPRECATED&nbsp;</span>");
+ else if (!strncmp(ptr, "@since ", 7))
+ {
+ strncpy(since, ptr + 7, sizeof(since) - 1);
+ since[sizeof(since) - 1] = '\0';
+
+ if ((ptr = strchr(since, '@')) != NULL)
+ *ptr = '\0';
+
+ snprintf(info, sizeof(info), "<span class=\"info\">&nbsp;%s&nbsp;</span>", since);
+ return (info);
+ }
+ }
+
+ return ("");
+}
+
+
+/*
+ * 'get_text()' - Get the text for a node.
+ */
+
+static char * /* O - Text in node */
+get_text(mxml_node_t *node, /* I - Node to get */
+ char *buffer, /* I - Buffer */
+ int buflen) /* I - Size of buffer */
+{
+ char *ptr, /* Pointer into buffer */
+ *end; /* End of buffer */
+ int len; /* Length of node */
+ mxml_node_t *current; /* Current node */
+
+
+ ptr = buffer;
+ end = buffer + buflen - 1;
+
+ for (current = node->child; current && ptr < end; current = current->next)
+ {
+ if (current->type == MXML_TEXT)
+ {
+ if (current->value.text.whitespace)
+ *ptr++ = ' ';
+
+ len = (int)strlen(current->value.text.string);
+ if (len > (int)(end - ptr))
+ len = (int)(end - ptr);
+
+ memcpy(ptr, current->value.text.string, len);
+ ptr += len;
+ }
+ else if (current->type == MXML_OPAQUE)
+ {
+ len = (int)strlen(current->value.opaque);
+ if (len > (int)(end - ptr))
+ len = (int)(end - ptr);
+
+ memcpy(ptr, current->value.opaque, len);
+ ptr += len;
+ }
+ }
+
+ *ptr = '\0';
+
+ return (buffer);
+}
+
+
+/*
+ * 'load_cb()' - Set the type of child nodes.
+ */
+
+static mxml_type_t /* O - Node type */
+load_cb(mxml_node_t *node) /* I - Node */
+{
+ if (!strcmp(node->value.element.name, "description"))
+ return (MXML_OPAQUE);
+ else
+ return (MXML_TEXT);
+}
+
+
+/*
+ * 'new_documentation()' - Create a new documentation tree.
+ */
+
+static mxml_node_t * /* O - New documentation */
+new_documentation(mxml_node_t **mxmldoc)/* O - mxmldoc node */
+{
+ mxml_node_t *doc; /* New documentation */
+
+
+ /*
+ * Create an empty XML documentation file...
+ */
+
+ doc = mxmlNewXML(NULL);
+
+ *mxmldoc = mxmlNewElement(doc, "mxmldoc");
+
+ mxmlElementSetAttr(*mxmldoc, "xmlns", "http://www.easysw.com");
+ mxmlElementSetAttr(*mxmldoc, "xmlns:xsi",
+ "http://www.w3.org/2001/XMLSchema-instance");
+ mxmlElementSetAttr(*mxmldoc, "xsi:schemaLocation",
+ "http://www.minixml.org/mxmldoc.xsd");
+
+ return (doc);
+}
+
+
+/*
+ * 'remove_directory()' - Remove a directory.
+ */
+
+static int /* O - 1 on success, 0 on failure */
+remove_directory(const char *path) /* I - Directory to remove */
+{
+#ifdef WIN32
+ /* TODO: Add Windows directory removal code */
+
+#else
+ DIR *dir; /* Directory */
+ struct dirent *dent; /* Current directory entry */
+ char filename[1024]; /* Current filename */
+ struct stat fileinfo; /* File information */
+
+
+ if ((dir = opendir(path)) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to open directory \"%s\": %s\n", path,
+ strerror(errno));
+ return (0);
+ }
+
+ while ((dent = readdir(dir)) != NULL)
+ {
+ /*
+ * Skip "." and ".."...
+ */
+
+ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
+ continue;
+
+ /*
+ * See if we have a file or directory...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/%s", path, dent->d_name);
+
+ if (stat(filename, &fileinfo))
+ {
+ fprintf(stderr, "mxmldoc: Unable to stat \"%s\": %s\n", filename,
+ strerror(errno));
+ closedir(dir);
+ return (0);
+ }
+
+ if (S_ISDIR(fileinfo.st_mode))
+ {
+ if (!remove_directory(filename))
+ {
+ closedir(dir);
+ return (0);
+ }
+ }
+ else if (unlink(filename))
+ {
+ fprintf(stderr, "mxmldoc: Unable to remove \"%s\": %s\n", filename,
+ strerror(errno));
+ closedir(dir);
+ return (0);
+ }
+ }
+
+ closedir(dir);
+
+ if (rmdir(path))
+ {
+ fprintf(stderr, "mxmldoc: Unable to remove directory \"%s\": %s\n", path,
+ strerror(errno));
+ return (0);
+ }
+#endif /* WIN32 */
+
+ return (1);
+}
+
+
+/*
+ * 'safe_strcpy()' - Copy a string allowing for overlapping strings.
+ */
+
+static void
+safe_strcpy(char *dst, /* I - Destination string */
+ const char *src) /* I - Source string */
+{
+ while (*src)
+ *dst++ = *src++;
+
+ *dst = '\0';
+}
+
+
+/*
+ * '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 */
+{
+ int state, /* Current parser state */
+ braces, /* Number of braces active */
+ parens; /* Number of active parenthesis */
+ int ch; /* Current character */
+ char buffer[65536], /* String buffer */
+ *bufptr; /* Pointer into buffer */
+ const char *scope; /* Current variable/function scope */
+ mxml_node_t *comment, /* <comment> node */
+ *constant, /* <constant> node */
+ *enumeration, /* <enumeration> node */
+ *function, /* <function> node */
+ *fstructclass, /* function struct/class node */
+ *structclass, /* <struct> or <class> node */
+ *typedefnode, /* <typedef> node */
+ *variable, /* <variable> or <argument> node */
+ *returnvalue, /* <returnvalue> node */
+ *type, /* <type> node */
+ *description, /* <description> node */
+ *node, /* Current node */
+ *next; /* Next node */
+#if DEBUG > 1
+ mxml_node_t *temp; /* Temporary node */
+ int oldstate, /* Previous state */
+ oldch; /* Old character */
+ static const char *states[] = /* State strings */
+ {
+ "STATE_NONE",
+ "STATE_PREPROCESSOR",
+ "STATE_C_COMMENT",
+ "STATE_CXX_COMMENT",
+ "STATE_STRING",
+ "STATE_CHARACTER",
+ "STATE_IDENTIFIER"
+ };
+#endif /* DEBUG > 1 */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "scan_file(filename=\"%s\", fp=%p, tree=%p)\n", filename,
+ fp, tree);
+#endif /* DEBUG */
+
+ /*
+ * Initialize the finite state machine...
+ */
+
+ state = STATE_NONE;
+ braces = 0;
+ parens = 0;
+ bufptr = buffer;
+
+ comment = mxmlNewElement(MXML_NO_PARENT, "temp");
+ constant = NULL;
+ enumeration = NULL;
+ function = NULL;
+ variable = NULL;
+ returnvalue = NULL;
+ type = NULL;
+ description = NULL;
+ typedefnode = NULL;
+ structclass = NULL;
+ fstructclass = NULL;
+
+ if (!strcmp(tree->value.element.name, "class"))
+ scope = "private";
+ else
+ scope = NULL;
+
+ /*
+ * Read until end-of-file...
+ */
+
+ while ((ch = getc(fp)) != EOF)
+ {
+#if DEBUG > 1
+ oldstate = state;
+ oldch = ch;
+#endif /* DEBUG > 1 */
+
+ switch (state)
+ {
+ case STATE_NONE : /* No state - whitespace, etc. */
+ switch (ch)
+ {
+ case '/' : /* Possible C/C++ comment */
+ ch = getc(fp);
+ bufptr = buffer;
+
+ if (ch == '*')
+ state = STATE_C_COMMENT;
+ else if (ch == '/')
+ state = STATE_CXX_COMMENT;
+ else
+ {
+ ungetc(ch, fp);
+
+ if (type)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< / >>>\n", stderr);
+#endif /* DEBUG */
+ ch = type->last_child->value.text.string[0];
+ mxmlNewText(type, isalnum(ch) || ch == '_', "/");
+ }
+ }
+ break;
+
+ case '#' : /* Preprocessor */
+#ifdef DEBUG
+ fputs(" #preprocessor...\n", stderr);
+#endif /* DEBUG */
+ state = STATE_PREPROCESSOR;
+ break;
+
+ case '\'' : /* Character constant */
+ state = STATE_CHARACTER;
+ bufptr = buffer;
+ *bufptr++ = ch;
+ break;
+
+ case '\"' : /* String constant */
+ state = STATE_STRING;
+ bufptr = buffer;
+ *bufptr++ = ch;
+ break;
+
+ case '{' :
+#ifdef DEBUG
+ fprintf(stderr, " open brace, function=%p, type=%p...\n",
+ function, type);
+ if (type)
+ fprintf(stderr, " type->child=\"%s\"...\n",
+ type->child->value.text.string);
+#endif /* DEBUG */
+
+ if (function)
+ {
+ if (fstructclass)
+ {
+ sort_node(fstructclass, function);
+ fstructclass = NULL;
+ }
+ else
+ sort_node(tree, function);
+
+ function = NULL;
+ }
+ else if (type && type->child &&
+ ((!strcmp(type->child->value.text.string, "typedef") &&
+ type->child->next &&
+ (!strcmp(type->child->next->value.text.string, "struct") ||
+ !strcmp(type->child->next->value.text.string, "union") ||
+ !strcmp(type->child->next->value.text.string, "class"))) ||
+ !strcmp(type->child->value.text.string, "union") ||
+ !strcmp(type->child->value.text.string, "struct") ||
+ !strcmp(type->child->value.text.string, "class")))
+ {
+ /*
+ * Start of a class or structure...
+ */
+
+ if (!strcmp(type->child->value.text.string, "typedef"))
+ {
+#ifdef DEBUG
+ fputs(" starting typedef...\n", stderr);
+#endif /* DEBUG */
+
+ typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef");
+ mxmlDelete(type->child);
+ }
+ else
+ typedefnode = NULL;
+
+ structclass = mxmlNewElement(MXML_NO_PARENT,
+ type->child->value.text.string);
+
+#ifdef DEBUG
+ fprintf(stderr, "%c%s: <<<< %s >>>\n",
+ toupper(type->child->value.text.string[0]),
+ type->child->value.text.string + 1,
+ type->child->next ?
+ type->child->next->value.text.string : "(noname)");
+
+ fputs(" type =", stderr);
+ for (node = type->child; node; node = node->next)
+ fprintf(stderr, " \"%s\"", node->value.text.string);
+ putc('\n', stderr);
+
+ fprintf(stderr, " scope = %s\n", scope ? scope : "(null)");
+#endif /* DEBUG */
+
+ if (type->child->next)
+ {
+ mxmlElementSetAttr(structclass, "name",
+ type->child->next->value.text.string);
+ sort_node(tree, structclass);
+ }
+
+ if (typedefnode && type->child)
+ type->child->value.text.whitespace = 0;
+ else if (structclass && type->child &&
+ type->child->next && type->child->next->next)
+ {
+ for (bufptr = buffer, node = type->child->next->next;
+ node;
+ bufptr += strlen(bufptr))
+ {
+ if (node->value.text.whitespace && bufptr > buffer)
+ *bufptr++ = ' ';
+
+ strcpy(bufptr, node->value.text.string);
+
+ next = node->next;
+ mxmlDelete(node);
+ node = next;
+ }
+
+ mxmlElementSetAttr(structclass, "parent", buffer);
+
+ mxmlDelete(type);
+ type = NULL;
+ }
+ else
+ {
+ mxmlDelete(type);
+ type = NULL;
+ }
+
+ if (typedefnode && comment->last_child)
+ {
+ /*
+ * Copy comment for typedef as well as class/struct/union...
+ */
+
+ mxmlNewText(comment, 0,
+ comment->last_child->value.text.string);
+ description = mxmlNewElement(typedefnode, "description");
+#ifdef DEBUG
+ fprintf(stderr,
+ " duplicating comment %p/%p for typedef...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ update_comment(typedefnode, comment->last_child);
+ mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
+ comment->last_child);
+ }
+
+ description = mxmlNewElement(structclass, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to %s...\n",
+ comment->last_child, comment->child,
+ structclass->value.element.name);
+#endif /* DEBUG */
+ update_comment(structclass, comment->last_child);
+ mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
+ comment->last_child);
+
+ if (scan_file(filename, fp, structclass))
+ {
+ mxmlDelete(comment);
+ return (-1);
+ }
+
+#ifdef DEBUG
+ fputs(" ended typedef...\n", stderr);
+#endif /* DEBUG */
+ structclass = NULL;
+ break;
+ }
+ else if (type && type->child && type->child->next &&
+ (!strcmp(type->child->value.text.string, "enum") ||
+ (!strcmp(type->child->value.text.string, "typedef") &&
+ !strcmp(type->child->next->value.text.string, "enum"))))
+ {
+ /*
+ * Enumeration type...
+ */
+
+ if (!strcmp(type->child->value.text.string, "typedef"))
+ {
+#ifdef DEBUG
+ fputs(" starting typedef...\n", stderr);
+#endif /* DEBUG */
+
+ typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef");
+ mxmlDelete(type->child);
+ }
+ else
+ typedefnode = NULL;
+
+ enumeration = mxmlNewElement(MXML_NO_PARENT, "enumeration");
+
+#ifdef DEBUG
+ fprintf(stderr, "Enumeration: <<<< %s >>>\n",
+ type->child->next ?
+ type->child->next->value.text.string : "(noname)");
+#endif /* DEBUG */
+
+ if (type->child->next)
+ {
+ mxmlElementSetAttr(enumeration, "name",
+ type->child->next->value.text.string);
+ sort_node(tree, enumeration);
+ }
+
+ if (typedefnode && type->child)
+ type->child->value.text.whitespace = 0;
+ else
+ {
+ mxmlDelete(type);
+ type = NULL;
+ }
+
+ if (typedefnode && comment->last_child)
+ {
+ /*
+ * Copy comment for typedef as well as class/struct/union...
+ */
+
+ mxmlNewText(comment, 0,
+ comment->last_child->value.text.string);
+ description = mxmlNewElement(typedefnode, "description");
+#ifdef DEBUG
+ fprintf(stderr,
+ " duplicating comment %p/%p for typedef...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ update_comment(typedefnode, comment->last_child);
+ mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
+ comment->last_child);
+ }
+
+ description = mxmlNewElement(enumeration, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to enumeration...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ update_comment(enumeration, comment->last_child);
+ mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
+ comment->last_child);
+ }
+ else if (type && type->child &&
+ !strcmp(type->child->value.text.string, "extern"))
+ {
+ if (scan_file(filename, fp, tree))
+ {
+ mxmlDelete(comment);
+ return (-1);
+ }
+ }
+ else if (type)
+ {
+ mxmlDelete(type);
+ type = NULL;
+ }
+
+ braces ++;
+ function = NULL;
+ variable = NULL;
+ break;
+
+ case '}' :
+#ifdef DEBUG
+ fputs(" close brace...\n", stderr);
+#endif /* DEBUG */
+
+ if (structclass)
+ scope = NULL;
+
+ if (!typedefnode)
+ enumeration = NULL;
+
+ constant = NULL;
+ structclass = NULL;
+
+ if (braces > 0)
+ braces --;
+ else
+ {
+ mxmlDelete(comment);
+ return (0);
+ }
+ break;
+
+ case '(' :
+ if (type)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< ( >>>\n", stderr);
+#endif /* DEBUG */
+ mxmlNewText(type, 0, "(");
+ }
+
+ parens ++;
+ break;
+
+ case ')' :
+ if (type && parens)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< ) >>>\n", stderr);
+#endif /* DEBUG */
+ mxmlNewText(type, 0, ")");
+ }
+
+ if (function && type && !parens)
+ {
+ /*
+ * Check for "void" argument...
+ */
+
+ if (type->child && type->child->next)
+ variable = add_variable(function, "argument", type);
+ else
+ mxmlDelete(type);
+
+ type = NULL;
+ }
+
+ if (parens > 0)
+ parens --;
+ break;
+
+ case ';' :
+#ifdef DEBUG
+ fputs("Identifier: <<<< ; >>>\n", stderr);
+ fprintf(stderr, " enumeration=%p, function=%p, type=%p, type->child=%p, typedefnode=%p\n",
+ enumeration, function, type, type ? type->child : NULL, typedefnode);
+#endif /* DEBUG */
+
+ if (function)
+ {
+ if (!strcmp(tree->value.element.name, "class"))
+ {
+#ifdef DEBUG
+ fputs(" ADDING FUNCTION TO CLASS\n", stderr);
+#endif /* DEBUG */
+ sort_node(tree, function);
+ }
+ else
+ mxmlDelete(function);
+
+ function = NULL;
+ variable = NULL;
+ }
+
+ if (type)
+ {
+ /*
+ * See if we have a typedef...
+ */
+
+ if (type->child &&
+ !strcmp(type->child->value.text.string, "typedef"))
+ {
+ /*
+ * Yes, add it!
+ */
+
+ typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef");
+
+ for (node = type->child->next; node; node = node->next)
+ if (!strcmp(node->value.text.string, "("))
+ break;
+
+ if (node)
+ {
+ for (node = node->next; node; node = node->next)
+ if (strcmp(node->value.text.string, "*"))
+ break;
+ }
+
+ if (!node)
+ node = type->last_child;
+
+#ifdef DEBUG
+ fprintf(stderr, " ADDING TYPEDEF FOR %p(%s)...\n",
+ node, node->value.text.string);
+#endif /* DEBUG */
+
+ mxmlElementSetAttr(typedefnode, "name",
+ node->value.text.string);
+ sort_node(tree, typedefnode);
+
+ if (type->child != node)
+ mxmlDelete(type->child);
+
+ mxmlDelete(node);
+
+ if (type->child)
+ type->child->value.text.whitespace = 0;
+
+ mxmlAdd(typedefnode, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
+ type);
+ type = NULL;
+ break;
+ }
+ else if (typedefnode && enumeration)
+ {
+ /*
+ * Add enum typedef...
+ */
+
+ node = type->child;
+
+#ifdef DEBUG
+ fprintf(stderr, " ADDING TYPEDEF FOR %p(%s)...\n",
+ node, node->value.text.string);
+#endif /* DEBUG */
+
+ mxmlElementSetAttr(typedefnode, "name",
+ node->value.text.string);
+ sort_node(tree, typedefnode);
+ mxmlDelete(type);
+
+ type = mxmlNewElement(typedefnode, "type");
+ mxmlNewText(type, 0, "enum");
+ mxmlNewText(type, 1,
+ mxmlElementGetAttr(enumeration, "name"));
+ enumeration = NULL;
+ type = NULL;
+ break;
+ }
+
+ mxmlDelete(type);
+ type = NULL;
+ }
+ break;
+
+ case ':' :
+ if (type)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< : >>>\n", stderr);
+#endif /* DEBUG */
+ mxmlNewText(type, 1, ":");
+ }
+ break;
+
+ case '*' :
+ if (type)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< * >>>\n", stderr);
+#endif /* DEBUG */
+ ch = type->last_child->value.text.string[0];
+ mxmlNewText(type, isalnum(ch) || ch == '_', "*");
+ }
+ break;
+
+ case ',' :
+ if (type && !enumeration)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< , >>>\n", stderr);
+#endif /* DEBUG */
+ mxmlNewText(type, 0, ",");
+ }
+ break;
+
+ case '&' :
+ if (type)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< & >>>\n", stderr);
+#endif /* DEBUG */
+ mxmlNewText(type, 1, "&");
+ }
+ break;
+
+ case '+' :
+ if (type)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< + >>>\n", stderr);
+#endif /* DEBUG */
+ ch = type->last_child->value.text.string[0];
+ mxmlNewText(type, isalnum(ch) || ch == '_', "+");
+ }
+ break;
+
+ case '-' :
+ if (type)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< - >>>\n", stderr);
+#endif /* DEBUG */
+ ch = type->last_child->value.text.string[0];
+ mxmlNewText(type, isalnum(ch) || ch == '_', "-");
+ }
+ break;
+
+ case '=' :
+ if (type)
+ {
+#ifdef DEBUG
+ fputs("Identifier: <<<< = >>>\n", stderr);
+#endif /* DEBUG */
+ ch = type->last_child->value.text.string[0];
+ mxmlNewText(type, isalnum(ch) || ch == '_', "=");
+ }
+ break;
+
+ default : /* Other */
+ if (isalnum(ch) || ch == '_' || ch == '.' || ch == ':' || ch == '~')
+ {
+ state = STATE_IDENTIFIER;
+ bufptr = buffer;
+ *bufptr++ = ch;
+ }
+ break;
+ }
+ break;
+
+ case STATE_PREPROCESSOR : /* Preprocessor directive */
+ if (ch == '\n')
+ state = STATE_NONE;
+ else if (ch == '\\')
+ getc(fp);
+ break;
+
+ case STATE_C_COMMENT : /* Inside a C comment */
+ switch (ch)
+ {
+ case '\n' :
+ while ((ch = getc(fp)) != EOF)
+ if (ch == '*')
+ {
+ ch = getc(fp);
+
+ if (ch == '/')
+ {
+ *bufptr = '\0';
+
+ if (comment->child != comment->last_child)
+ {
+#ifdef DEBUG
+ fprintf(stderr, " removing comment %p(%20.20s), last comment %p(%20.20s)...\n",
+ comment->child,
+ comment->child ? comment->child->value.text.string : "",
+ comment->last_child,
+ comment->last_child ? comment->last_child->value.text.string : "");
+#endif /* DEBUG */
+ mxmlDelete(comment->child);
+#ifdef DEBUG
+ fprintf(stderr, " new comment %p, last comment %p...\n",
+ comment->child, comment->last_child);
+#endif /* DEBUG */
+ }
+
+#ifdef DEBUG
+ fprintf(stderr,
+ " processing comment, variable=%p, "
+ "constant=%p, typedefnode=%p, tree=\"%s\"\n",
+ variable, constant, typedefnode,
+ tree->value.element.name);
+#endif /* DEBUG */
+
+ if (variable)
+ {
+ if (strstr(buffer, "@private@"))
+ {
+ /*
+ * Delete private variables...
+ */
+
+ mxmlDelete(variable);
+ }
+ else
+ {
+ description = mxmlNewElement(variable, "description");
+#ifdef DEBUG
+ fprintf(stderr,
+ " adding comment %p/%p to variable...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(variable,
+ mxmlNewText(description, 0, buffer));
+ }
+
+ variable = NULL;
+ }
+ else if (constant)
+ {
+ if (strstr(buffer, "@private@"))
+ {
+ /*
+ * Delete private constants...
+ */
+
+ mxmlDelete(constant);
+ }
+ else
+ {
+ description = mxmlNewElement(constant, "description");
+#ifdef DEBUG
+ fprintf(stderr,
+ " adding comment %p/%p to constant...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(constant,
+ mxmlNewText(description, 0, buffer));
+ }
+
+ constant = NULL;
+ }
+ else if (typedefnode)
+ {
+ if (strstr(buffer, "@private@"))
+ {
+ /*
+ * Delete private typedefs...
+ */
+
+ mxmlDelete(typedefnode);
+
+ if (structclass)
+ {
+ mxmlDelete(structclass);
+ structclass = NULL;
+ }
+
+ if (enumeration)
+ {
+ mxmlDelete(enumeration);
+ enumeration = NULL;
+ }
+ }
+ else
+ {
+ description = mxmlNewElement(typedefnode, "description");
+#ifdef DEBUG
+ fprintf(stderr,
+ " adding comment %p/%p to typedef %s...\n",
+ comment->last_child, comment->child,
+ mxmlElementGetAttr(typedefnode, "name"));
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(typedefnode,
+ mxmlNewText(description, 0, buffer));
+
+ if (structclass)
+ {
+ description = mxmlNewElement(structclass, "description");
+ update_comment(structclass,
+ mxmlNewText(description, 0, buffer));
+ }
+ else if (enumeration)
+ {
+ description = mxmlNewElement(enumeration, "description");
+ update_comment(enumeration,
+ mxmlNewText(description, 0, buffer));
+ }
+ }
+
+ typedefnode = NULL;
+ }
+ else if (strcmp(tree->value.element.name, "mxmldoc") &&
+ !mxmlFindElement(tree, tree, "description",
+ NULL, NULL, MXML_DESCEND_FIRST))
+ {
+ description = mxmlNewElement(tree, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to parent...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(tree,
+ mxmlNewText(description, 0, buffer));
+ }
+ else
+ {
+#ifdef DEBUG
+ fprintf(stderr, " before adding comment, child=%p, last_child=%p\n",
+ comment->child, comment->last_child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+#ifdef DEBUG
+ fprintf(stderr, " after adding comment, child=%p, last_child=%p\n",
+ comment->child, comment->last_child);
+#endif /* DEBUG */
+ }
+#ifdef DEBUG
+ fprintf(stderr, "C comment: <<<< %s >>>\n", buffer);
+#endif /* DEBUG */
+
+ state = STATE_NONE;
+ break;
+ }
+ else
+ ungetc(ch, fp);
+ }
+ else if (ch == '\n' && bufptr > buffer &&
+ bufptr < (buffer + sizeof(buffer) - 1))
+ *bufptr++ = ch;
+ else if (!isspace(ch))
+ break;
+
+ if (ch != EOF)
+ ungetc(ch, fp);
+
+ if (bufptr > buffer && bufptr < (buffer + sizeof(buffer) - 1))
+ *bufptr++ = '\n';
+ break;
+
+ case '/' :
+ if (ch == '/' && bufptr > buffer && bufptr[-1] == '*')
+ {
+ while (bufptr > buffer &&
+ (bufptr[-1] == '*' || isspace(bufptr[-1] & 255)))
+ bufptr --;
+ *bufptr = '\0';
+
+ if (comment->child != comment->last_child)
+ {
+#ifdef DEBUG
+ fprintf(stderr, " removing comment %p(%20.20s), last comment %p(%20.20s)...\n",
+ comment->child,
+ comment->child ? comment->child->value.text.string : "",
+ comment->last_child,
+ comment->last_child ? comment->last_child->value.text.string : "");
+#endif /* DEBUG */
+ mxmlDelete(comment->child);
+#ifdef DEBUG
+ fprintf(stderr, " new comment %p, last comment %p...\n",
+ comment->child, comment->last_child);
+#endif /* DEBUG */
+ }
+
+#ifdef DEBUG
+ fprintf(stderr,
+ " processing comment, variable=%p, "
+ "constant=%p, typedefnode=%p, tree=\"%s\"\n",
+ variable, constant, typedefnode,
+ tree->value.element.name);
+#endif /* DEBUG */
+
+ if (variable)
+ {
+ if (strstr(buffer, "@private@"))
+ {
+ /*
+ * Delete private variables...
+ */
+
+ mxmlDelete(variable);
+ }
+ else
+ {
+ description = mxmlNewElement(variable, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to variable...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(variable,
+ mxmlNewText(description, 0, buffer));
+ }
+
+ variable = NULL;
+ }
+ else if (constant)
+ {
+ if (strstr(buffer, "@private@"))
+ {
+ /*
+ * Delete private constants...
+ */
+
+ mxmlDelete(constant);
+ }
+ else
+ {
+ description = mxmlNewElement(constant, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to constant...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(constant,
+ mxmlNewText(description, 0, buffer));
+ }
+
+ constant = NULL;
+ }
+ else if (typedefnode)
+ {
+ if (strstr(buffer, "@private@"))
+ {
+ /*
+ * Delete private typedefs...
+ */
+
+ mxmlDelete(typedefnode);
+
+ if (structclass)
+ {
+ mxmlDelete(structclass);
+ structclass = NULL;
+ }
+
+ if (enumeration)
+ {
+ mxmlDelete(enumeration);
+ enumeration = NULL;
+ }
+ }
+ else
+ {
+ description = mxmlNewElement(typedefnode, "description");
+#ifdef DEBUG
+ fprintf(stderr,
+ " adding comment %p/%p to typedef %s...\n",
+ comment->last_child, comment->child,
+ mxmlElementGetAttr(typedefnode, "name"));
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(typedefnode,
+ mxmlNewText(description, 0, buffer));
+
+ if (structclass)
+ {
+ description = mxmlNewElement(structclass, "description");
+ update_comment(structclass,
+ mxmlNewText(description, 0, buffer));
+ }
+ else if (enumeration)
+ {
+ description = mxmlNewElement(enumeration, "description");
+ update_comment(enumeration,
+ mxmlNewText(description, 0, buffer));
+ }
+ }
+
+ typedefnode = NULL;
+ }
+ else if (strcmp(tree->value.element.name, "mxmldoc") &&
+ !mxmlFindElement(tree, tree, "description",
+ NULL, NULL, MXML_DESCEND_FIRST))
+ {
+ description = mxmlNewElement(tree, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to parent...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(tree,
+ mxmlNewText(description, 0, buffer));
+ }
+ else
+ mxmlNewText(comment, 0, buffer);
+
+#ifdef DEBUG
+ fprintf(stderr, "C comment: <<<< %s >>>\n", buffer);
+#endif /* DEBUG */
+
+ state = STATE_NONE;
+ break;
+ }
+
+ default :
+ if (ch == ' ' && bufptr == buffer)
+ break;
+
+ if (bufptr < (buffer + sizeof(buffer) - 1))
+ *bufptr++ = ch;
+ break;
+ }
+ break;
+
+ case STATE_CXX_COMMENT : /* Inside a C++ comment */
+ if (ch == '\n')
+ {
+ state = STATE_NONE;
+ *bufptr = '\0';
+
+ if (comment->child != comment->last_child)
+ {
+#ifdef DEBUG
+ fprintf(stderr, " removing comment %p(%20.20s), last comment %p(%20.20s)...\n",
+ comment->child,
+ comment->child ? comment->child->value.text.string : "",
+ comment->last_child,
+ comment->last_child ? comment->last_child->value.text.string : "");
+#endif /* DEBUG */
+ mxmlDelete(comment->child);
+#ifdef DEBUG
+ fprintf(stderr, " new comment %p, last comment %p...\n",
+ comment->child, comment->last_child);
+#endif /* DEBUG */
+ }
+
+ if (variable)
+ {
+ if (strstr(buffer, "@private@"))
+ {
+ /*
+ * Delete private variables...
+ */
+
+ mxmlDelete(variable);
+ }
+ else
+ {
+ description = mxmlNewElement(variable, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to variable...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(variable,
+ mxmlNewText(description, 0, buffer));
+ }
+
+ variable = NULL;
+ }
+ else if (constant)
+ {
+ if (strstr(buffer, "@private@"))
+ {
+ /*
+ * Delete private constants...
+ */
+
+ mxmlDelete(constant);
+ }
+ else
+ {
+ description = mxmlNewElement(constant, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to constant...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(constant,
+ mxmlNewText(description, 0, buffer));
+ }
+
+ constant = NULL;
+ }
+ else if (typedefnode)
+ {
+ if (strstr(buffer, "@private@"))
+ {
+ /*
+ * Delete private typedefs...
+ */
+
+ mxmlDelete(typedefnode);
+ typedefnode = NULL;
+
+ if (structclass)
+ {
+ mxmlDelete(structclass);
+ structclass = NULL;
+ }
+
+ if (enumeration)
+ {
+ mxmlDelete(enumeration);
+ enumeration = NULL;
+ }
+ }
+ else
+ {
+ description = mxmlNewElement(typedefnode, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to typedef %s...\n",
+ comment->last_child, comment->child,
+ mxmlElementGetAttr(typedefnode, "name"));
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(typedefnode,
+ mxmlNewText(description, 0, buffer));
+
+ if (structclass)
+ {
+ description = mxmlNewElement(structclass, "description");
+ update_comment(structclass,
+ mxmlNewText(description, 0, buffer));
+ }
+ else if (enumeration)
+ {
+ description = mxmlNewElement(enumeration, "description");
+ update_comment(enumeration,
+ mxmlNewText(description, 0, buffer));
+ }
+ }
+ }
+ else if (strcmp(tree->value.element.name, "mxmldoc") &&
+ !mxmlFindElement(tree, tree, "description",
+ NULL, NULL, MXML_DESCEND_FIRST))
+ {
+ description = mxmlNewElement(tree, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to parent...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ mxmlNewText(comment, 0, buffer);
+ update_comment(tree,
+ mxmlNewText(description, 0, buffer));
+ }
+ else
+ mxmlNewText(comment, 0, buffer);
+
+#ifdef DEBUG
+ fprintf(stderr, "C++ comment: <<<< %s >>>\n", buffer);
+#endif /* DEBUG */
+ }
+ else if (ch == ' ' && bufptr == buffer)
+ break;
+ else if (bufptr < (buffer + sizeof(buffer) - 1))
+ *bufptr++ = ch;
+ break;
+
+ case STATE_STRING : /* Inside a string constant */
+ *bufptr++ = ch;
+
+ if (ch == '\\')
+ *bufptr++ = getc(fp);
+ else if (ch == '\"')
+ {
+ *bufptr = '\0';
+
+ if (type)
+ mxmlNewText(type, type->child != NULL, buffer);
+
+ state = STATE_NONE;
+ }
+ break;
+
+ case STATE_CHARACTER : /* Inside a character constant */
+ *bufptr++ = ch;
+
+ if (ch == '\\')
+ *bufptr++ = getc(fp);
+ else if (ch == '\'')
+ {
+ *bufptr = '\0';
+
+ if (type)
+ mxmlNewText(type, type->child != NULL, buffer);
+
+ state = STATE_NONE;
+ }
+ break;
+
+ case STATE_IDENTIFIER : /* Inside a keyword or identifier */
+ if (isalnum(ch) || ch == '_' || ch == '[' || ch == ']' ||
+ (ch == ',' && (parens > 1 || (type && !enumeration && !function))) ||
+ ch == ':' || ch == '.' || ch == '~')
+ {
+ if (bufptr < (buffer + sizeof(buffer) - 1))
+ *bufptr++ = ch;
+ }
+ else
+ {
+ ungetc(ch, fp);
+ *bufptr = '\0';
+ state = STATE_NONE;
+
+#ifdef DEBUG
+ fprintf(stderr, " braces=%d, type=%p, type->child=%p, buffer=\"%s\"\n",
+ braces, type, type ? type->child : NULL, buffer);
+#endif /* DEBUG */
+
+ if (!braces)
+ {
+ if (!type || !type->child)
+ {
+ if (!strcmp(tree->value.element.name, "class"))
+ {
+ if (!strcmp(buffer, "public") ||
+ !strcmp(buffer, "public:"))
+ {
+ scope = "public";
+#ifdef DEBUG
+ fputs(" scope = public\n", stderr);
+#endif /* DEBUG */
+ break;
+ }
+ else if (!strcmp(buffer, "private") ||
+ !strcmp(buffer, "private:"))
+ {
+ scope = "private";
+#ifdef DEBUG
+ fputs(" scope = private\n", stderr);
+#endif /* DEBUG */
+ break;
+ }
+ else if (!strcmp(buffer, "protected") ||
+ !strcmp(buffer, "protected:"))
+ {
+ scope = "protected";
+#ifdef DEBUG
+ fputs(" scope = protected\n", stderr);
+#endif /* DEBUG */
+ break;
+ }
+ }
+ }
+
+ if (!type)
+ type = mxmlNewElement(MXML_NO_PARENT, "type");
+
+#ifdef DEBUG
+ fprintf(stderr, " function=%p (%s), type->child=%p, ch='%c', parens=%d\n",
+ function,
+ function ? mxmlElementGetAttr(function, "name") : "null",
+ type->child, ch, parens);
+#endif /* DEBUG */
+
+ if (!function && ch == '(')
+ {
+ if (type->child &&
+ !strcmp(type->child->value.text.string, "extern"))
+ {
+ /*
+ * Remove external declarations...
+ */
+
+ mxmlDelete(type);
+ type = NULL;
+ break;
+ }
+
+ if (type->child &&
+ !strcmp(type->child->value.text.string, "static") &&
+ !strcmp(tree->value.element.name, "mxmldoc"))
+ {
+ /*
+ * Remove static functions...
+ */
+
+ mxmlDelete(type);
+ type = NULL;
+ break;
+ }
+
+ function = mxmlNewElement(MXML_NO_PARENT, "function");
+ if ((bufptr = strchr(buffer, ':')) != NULL && bufptr[1] == ':')
+ {
+ *bufptr = '\0';
+ bufptr += 2;
+
+ if ((fstructclass =
+ mxmlFindElement(tree, tree, "class", "name", buffer,
+ MXML_DESCEND_FIRST)) == NULL)
+ fstructclass =
+ mxmlFindElement(tree, tree, "struct", "name", buffer,
+ MXML_DESCEND_FIRST);
+ }
+ else
+ bufptr = buffer;
+
+ mxmlElementSetAttr(function, "name", bufptr);
+
+ if (scope)
+ mxmlElementSetAttr(function, "scope", scope);
+
+#ifdef DEBUG
+ fprintf(stderr, "function: %s\n", buffer);
+ fprintf(stderr, " scope = %s\n", scope ? scope : "(null)");
+ fprintf(stderr, " comment = %p\n", comment);
+ fprintf(stderr, " child = (%p) %s\n",
+ comment->child,
+ comment->child ?
+ comment->child->value.text.string : "(null)");
+ fprintf(stderr, " last_child = (%p) %s\n",
+ comment->last_child,
+ comment->last_child ?
+ comment->last_child->value.text.string : "(null)");
+#endif /* DEBUG */
+
+ if (type->last_child &&
+ strcmp(type->last_child->value.text.string, "void"))
+ {
+ returnvalue = mxmlNewElement(function, "returnvalue");
+
+ mxmlAdd(returnvalue, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type);
+
+ description = mxmlNewElement(returnvalue, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to returnvalue...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ update_comment(returnvalue, comment->last_child);
+ mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
+ comment->last_child);
+ }
+ else
+ mxmlDelete(type);
+
+ description = mxmlNewElement(function, "description");
+#ifdef DEBUG
+ fprintf(stderr, " adding comment %p/%p to function...\n",
+ comment->last_child, comment->child);
+#endif /* DEBUG */
+ update_comment(function, comment->last_child);
+ mxmlAdd(description, MXML_ADD_AFTER, MXML_ADD_TO_PARENT,
+ comment->last_child);
+
+ type = NULL;
+ }
+ else if (function && ((ch == ')' && parens == 1) || ch == ','))
+ {
+ /*
+ * Argument definition...
+ */
+
+ if (strcmp(buffer, "void"))
+ {
+ mxmlNewText(type, type->child != NULL &&
+ type->last_child->value.text.string[0] != '(' &&
+ type->last_child->value.text.string[0] != '*',
+ buffer);
+
+#ifdef DEBUG
+ fprintf(stderr, "Argument: <<<< %s >>>\n", buffer);
+#endif /* DEBUG */
+
+ variable = add_variable(function, "argument", type);
+ }
+ else
+ mxmlDelete(type);
+
+ type = NULL;
+ }
+ else if (type->child && !function && (ch == ';' || ch == ','))
+ {
+#ifdef DEBUG
+ fprintf(stderr, " got semicolon, typedefnode=%p, structclass=%p\n",
+ typedefnode, structclass);
+#endif /* DEBUG */
+
+ if (typedefnode || structclass)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Typedef/struct/class: <<<< %s >>>>\n", buffer);
+#endif /* DEBUG */
+
+ if (typedefnode)
+ {
+ mxmlElementSetAttr(typedefnode, "name", buffer);
+
+ sort_node(tree, typedefnode);
+ }
+
+ if (structclass && !mxmlElementGetAttr(structclass, "name"))
+ {
+#ifdef DEBUG
+ fprintf(stderr, "setting struct/class name to %s!\n",
+ type->last_child->value.text.string);
+#endif /* DEBUG */
+ mxmlElementSetAttr(structclass, "name", buffer);
+
+ sort_node(tree, structclass);
+ structclass = NULL;
+ }
+
+ if (typedefnode)
+ mxmlAdd(typedefnode, MXML_ADD_BEFORE, MXML_ADD_TO_PARENT,
+ type);
+ else
+ mxmlDelete(type);
+
+ type = NULL;
+ typedefnode = NULL;
+ }
+ else if (type->child &&
+ !strcmp(type->child->value.text.string, "typedef"))
+ {
+ /*
+ * Simple typedef...
+ */
+
+#ifdef DEBUG
+ fprintf(stderr, "Typedef: <<<< %s >>>\n", buffer);
+#endif /* DEBUG */
+
+ typedefnode = mxmlNewElement(MXML_NO_PARENT, "typedef");
+ mxmlElementSetAttr(typedefnode, "name", buffer);
+ mxmlDelete(type->child);
+
+ sort_node(tree, typedefnode);
+
+ if (type->child)
+ type->child->value.text.whitespace = 0;
+
+ mxmlAdd(typedefnode, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type);
+ type = NULL;
+ }
+ else if (!parens)
+ {
+ /*
+ * Variable definition...
+ */
+
+ if (type->child &&
+ !strcmp(type->child->value.text.string, "static") &&
+ !strcmp(tree->value.element.name, "mxmldoc"))
+ {
+ /*
+ * Remove static functions...
+ */
+
+ mxmlDelete(type);
+ type = NULL;
+ break;
+ }
+
+ mxmlNewText(type, type->child != NULL &&
+ type->last_child->value.text.string[0] != '(' &&
+ type->last_child->value.text.string[0] != '*',
+ buffer);
+
+#ifdef DEBUG
+ fprintf(stderr, "Variable: <<<< %s >>>>\n", buffer);
+ fprintf(stderr, " scope = %s\n", scope ? scope : "(null)");
+#endif /* DEBUG */
+
+ variable = add_variable(MXML_NO_PARENT, "variable", type);
+ type = NULL;
+
+ sort_node(tree, variable);
+
+ if (scope)
+ mxmlElementSetAttr(variable, "scope", scope);
+ }
+ }
+ else
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Identifier: <<<< %s >>>>\n", buffer);
+#endif /* DEBUG */
+
+ mxmlNewText(type, type->child != NULL &&
+ type->last_child->value.text.string[0] != '(' &&
+ type->last_child->value.text.string[0] != '*',
+ buffer);
+ }
+ }
+ else if (enumeration && !isdigit(buffer[0] & 255))
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Constant: <<<< %s >>>\n", buffer);
+#endif /* DEBUG */
+
+ constant = mxmlNewElement(MXML_NO_PARENT, "constant");
+ mxmlElementSetAttr(constant, "name", buffer);
+ sort_node(enumeration, constant);
+ }
+ else if (type)
+ {
+ mxmlDelete(type);
+ type = NULL;
+ }
+ }
+ break;
+ }
+
+#if DEBUG > 1
+ if (state != oldstate)
+ {
+ fprintf(stderr, " changed states from %s to %s on receipt of character '%c'...\n",
+ states[oldstate], states[state], oldch);
+ fprintf(stderr, " variable = %p\n", variable);
+ if (type)
+ {
+ fputs(" type =", stderr);
+ for (temp = type->child; temp; temp = temp->next)
+ fprintf(stderr, " \"%s\"", temp->value.text.string);
+ fputs("\n", stderr);
+ }
+ }
+#endif /* DEBUG > 1 */
+ }
+
+ mxmlDelete(comment);
+
+ /*
+ * All done, return with no errors...
+ */
+
+ return (0);
+}
+
+
+/*
+ * 'sort_node()' - Insert a node sorted into a tree.
+ */
+
+static void
+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 */
+ *nodename, /* Name of node */
+ *scope; /* Scope */
+
+
+#if DEBUG > 1
+ fprintf(stderr, " sort_node(tree=%p, node=%p)\n", tree, node);
+#endif /* DEBUG > 1 */
+
+ /*
+ * Range check input...
+ */
+
+ if (!tree || !node || node->parent == tree)
+ return;
+
+ /*
+ * Get the node name...
+ */
+
+ if ((nodename = mxmlElementGetAttr(node, "name")) == NULL)
+ return;
+
+ if (nodename[0] == '_')
+ return; /* Hide private names */
+
+#if DEBUG > 1
+ fprintf(stderr, " nodename=%p (\"%s\")\n", nodename, nodename);
+#endif /* DEBUG > 1 */
+
+ /*
+ * Delete any existing definition at this level, if one exists...
+ */
+
+ if ((temp = mxmlFindElement(tree, tree, node->value.element.name,
+ "name", nodename, MXML_DESCEND_FIRST)) != NULL)
+ {
+ /*
+ * Copy the scope if needed...
+ */
+
+ if ((scope = mxmlElementGetAttr(temp, "scope")) != NULL &&
+ mxmlElementGetAttr(node, "scope") == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, " copying scope %s for %s\n", scope, nodename);
+#endif /* DEBUG */
+
+ mxmlElementSetAttr(node, "scope", scope);
+ }
+
+ mxmlDelete(temp);
+ }
+
+ /*
+ * Add the node into the tree at the proper place...
+ */
+
+ for (temp = tree->child; temp; temp = temp->next)
+ {
+#if DEBUG > 1
+ fprintf(stderr, " temp=%p\n", temp);
+#endif /* DEBUG > 1 */
+
+ if ((tempname = mxmlElementGetAttr(temp, "name")) == NULL)
+ continue;
+
+#if DEBUG > 1
+ fprintf(stderr, " tempname=%p (\"%s\")\n", tempname, tempname);
+#endif /* DEBUG > 1 */
+
+ if (strcmp(nodename, tempname) < 0)
+ break;
+ }
+
+ if (temp)
+ mxmlAdd(tree, MXML_ADD_BEFORE, temp, node);
+ else
+ mxmlAdd(tree, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
+}
+
+
+/*
+ * 'update_comment()' - Update a comment node.
+ */
+
+static void
+update_comment(mxml_node_t *parent, /* I - Parent node */
+ mxml_node_t *comment) /* I - Comment node */
+{
+ char *ptr; /* Pointer into comment */
+
+
+#ifdef DEBUG
+ fprintf(stderr, "update_comment(parent=%p, comment=%p)\n",
+ parent, comment);
+#endif /* DEBUG */
+
+ /*
+ * Range check the input...
+ */
+
+ if (!parent || !comment)
+ return;
+
+ /*
+ * Convert "\/" to "/"...
+ */
+
+ for (ptr = strstr(comment->value.text.string, "\\/");
+ ptr;
+ ptr = strstr(ptr, "\\/"))
+ safe_strcpy(ptr, ptr + 1);
+
+ /*
+ * Update the comment...
+ */
+
+ ptr = comment->value.text.string;
+
+ if (*ptr == '\'')
+ {
+ /*
+ * Convert "'name()' - description" to "description".
+ */
+
+ for (ptr ++; *ptr && *ptr != '\''; ptr ++);
+
+ if (*ptr == '\'')
+ {
+ ptr ++;
+ while (isspace(*ptr & 255))
+ ptr ++;
+
+ if (*ptr == '-')
+ ptr ++;
+
+ while (isspace(*ptr & 255))
+ ptr ++;
+
+ safe_strcpy(comment->value.text.string, ptr);
+ }
+ }
+ else if (!strncmp(ptr, "I ", 2) || !strncmp(ptr, "O ", 2) ||
+ !strncmp(ptr, "IO ", 3))
+ {
+ /*
+ * 'Convert "I - description", "IO - description", or "O - description"
+ * to description + direction attribute.
+ */
+
+ ptr = strchr(ptr, ' ');
+ *ptr++ = '\0';
+
+ if (!strcmp(parent->value.element.name, "argument"))
+ mxmlElementSetAttr(parent, "direction", comment->value.text.string);
+
+ while (isspace(*ptr & 255))
+ ptr ++;
+
+ if (*ptr == '-')
+ ptr ++;
+
+ while (isspace(*ptr & 255))
+ ptr ++;
+
+ safe_strcpy(comment->value.text.string, ptr);
+ }
+
+ /*
+ * Eliminate leading and trailing *'s...
+ */
+
+ for (ptr = comment->value.text.string; *ptr == '*'; ptr ++);
+ for (; isspace(*ptr & 255); ptr ++);
+ if (ptr > comment->value.text.string)
+ safe_strcpy(comment->value.text.string, ptr);
+
+ for (ptr = comment->value.text.string + strlen(comment->value.text.string) - 1;
+ ptr > comment->value.text.string && *ptr == '*';
+ ptr --)
+ *ptr = '\0';
+ for (; ptr > comment->value.text.string && isspace(*ptr & 255); ptr --)
+ *ptr = '\0';
+
+#ifdef DEBUG
+ fprintf(stderr, " updated comment = %s\n", comment->value.text.string);
+#endif /* DEBUG */
+}
+
+
+/*
+ * 'usage()' - Show program usage...
+ */
+
+static void
+usage(const char *option) /* I - Unknown option */
+{
+ if (option)
+ printf("mxmldoc: Bad option \"%s\"!\n\n", option);
+
+ puts("Usage: mxmldoc [options] [filename.xml] [source files] >filename.html");
+ puts("Options:");
+ puts(" --css filename.css Set CSS stylesheet file");
+ puts(" --docset bundleid.docset Generate documentation set");
+ puts(" --docversion version Set documentation version");
+ puts(" --feedname name Set documentation set feed name");
+ puts(" --feedurl url Set documentation set feed URL");
+ puts(" --footer footerfile Set footer file");
+ puts(" --framed basename Generate framed HTML to basename*.html");
+ puts(" --header headerfile Set header file");
+ puts(" --intro introfile Set introduction file");
+ puts(" --man name Generate man page");
+ puts(" --no-output Do no generate documentation file");
+ puts(" --section section Set section name");
+ puts(" --title title Set documentation title");
+ puts(" --tokens path Generate Xcode docset Tokens.xml file");
+ puts(" --version Show mxmldoc/Mini-XML version");
+
+ exit(1);
+}
+
+
+/*
+ * 'write_description()' - Write the description text.
+ */
+
+static void
+write_description(
+ FILE *out, /* I - Output file */
+ mxml_node_t *description, /* I - Description node */
+ const char *element, /* I - HTML element, if any */
+ int summary) /* I - Show summary */
+{
+ char text[10240], /* Text for description */
+ *start, /* Start of code/link */
+ *ptr; /* Pointer into text */
+ int col; /* Current column */
+
+
+ if (!description)
+ return;
+
+ get_text(description, text, sizeof(text));
+
+ ptr = strstr(text, "\n\n");
+
+ if (summary)
+ {
+ if (ptr)
+ *ptr = '\0';
+
+ ptr = text;
+ }
+ else if (!ptr || !ptr[2])
+ return;
+ else
+ ptr += 2;
+
+ if (element && *element)
+ fprintf(out, "<%s class=\"%s\">", element,
+ summary ? "description" : "discussion");
+ else if (!summary)
+ fputs(".PP\n", out);
+
+ for (col = 0; *ptr; ptr ++)
+ {
+ if (*ptr == '@' &&
+ (!strncmp(ptr + 1, "deprecated@", 11) ||
+ !strncmp(ptr + 1, "since ", 6)))
+ {
+ ptr ++;
+ while (*ptr && *ptr != '@')
+ ptr ++;
+
+ if (!*ptr)
+ return;
+ }
+ else if (!strncmp(ptr, "@code ", 6))
+ {
+ for (ptr += 6; isspace(*ptr & 255); ptr ++);
+
+ for (start = ptr, ptr ++; *ptr && *ptr != '@'; ptr ++);
+
+ if (*ptr)
+ *ptr = '\0';
+ else
+ ptr --;
+
+ if (element && *element)
+ fprintf(out, "<code>%s</code>", start);
+ else if (element)
+ fputs(start, out);
+ else
+ fprintf(out, "\\fB%s\\fR", start);
+ }
+ else if (!strncmp(ptr, "@link ", 6))
+ {
+ for (ptr += 6; isspace(*ptr & 255); ptr ++);
+
+ for (start = ptr, ptr ++; *ptr && *ptr != '@'; ptr ++);
+
+ if (*ptr)
+ *ptr = '\0';
+ else
+ ptr --;
+
+ if (element && *element)
+ fprintf(out, "<a href=\"#%s\"><code>%s</code></a>", start, start);
+ else if (element)
+ fputs(start, out);
+ else
+ fprintf(out, "\\fI%s\\fR", start);
+ }
+ else if (element)
+ {
+ if (*ptr == '&')
+ fputs("&amp;", out);
+ else if (*ptr == '<')
+ fputs("&lt;", out);
+ else if (*ptr == '>')
+ fputs("&gt;", out);
+ else if (*ptr == '\"')
+ fputs("&quot;", out);
+ else if (*ptr & 128)
+ {
+ /*
+ * Convert UTF-8 to Unicode constant...
+ */
+
+ int ch; /* Unicode character */
+
+
+ ch = *ptr & 255;
+
+ if ((ch & 0xe0) == 0xc0)
+ {
+ ch = ((ch & 0x1f) << 6) | (ptr[1] & 0x3f);
+ ptr ++;
+ }
+ else if ((ch & 0xf0) == 0xe0)
+ {
+ ch = ((((ch * 0x0f) << 6) | (ptr[1] & 0x3f)) << 6) | (ptr[2] & 0x3f);
+ ptr += 2;
+ }
+
+ if (ch == 0xa0)
+ {
+ /*
+ * Handle non-breaking space as-is...
+ */
+
+ fputs("&nbsp;", out);
+ }
+ else
+ fprintf(out, "&#x%x;", ch);
+ }
+ else if (*ptr == '\n' && ptr[1] == '\n' && ptr[2] && ptr[2] != '@')
+ {
+ fputs("<br>\n<br>\n", out);
+ ptr ++;
+ }
+ else
+ putc(*ptr, out);
+ }
+ else if (*ptr == '\n' && ptr[1] == '\n' && ptr[2] && ptr[2] != '@')
+ {
+ fputs("\n.PP\n", out);
+ ptr ++;
+ }
+ else
+ {
+ if (*ptr == '\\' || (*ptr == '.' && col == 0))
+ putc('\\', out);
+
+ putc(*ptr, out);
+
+ if (*ptr == '\n')
+ col = 0;
+ else
+ col ++;
+ }
+ }
+
+ if (element && *element)
+ fprintf(out, "</%s>\n", element);
+ else if (!element)
+ putc('\n', out);
+}
+
+
+/*
+ * 'write_element()' - Write an element's text nodes.
+ */
+
+static void
+write_element(FILE *out, /* I - Output file */
+ mxml_node_t *doc, /* I - Document tree */
+ mxml_node_t *element, /* I - Element to write */
+ int mode) /* I - Output mode */
+{
+ mxml_node_t *node; /* Current node */
+
+
+ if (!element)
+ return;
+
+ for (node = element->child;
+ node;
+ node = mxmlWalkNext(node, element, MXML_NO_DESCEND))
+ if (node->type == MXML_TEXT)
+ {
+ if (node->value.text.whitespace)
+ putc(' ', out);
+
+ if (mode == OUTPUT_HTML &&
+ (mxmlFindElement(doc, doc, "class", "name", node->value.text.string,
+ MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "enumeration", "name",
+ node->value.text.string, MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "struct", "name", node->value.text.string,
+ MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "typedef", "name", node->value.text.string,
+ MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "union", "name", node->value.text.string,
+ MXML_DESCEND)))
+ {
+ fputs("<a href=\"#", out);
+ write_string(out, node->value.text.string, mode);
+ fputs("\">", out);
+ write_string(out, node->value.text.string, mode);
+ fputs("</a>", out);
+ }
+ else
+ write_string(out, node->value.text.string, mode);
+ }
+
+ if (!strcmp(element->value.element.name, "type") &&
+ element->last_child->value.text.string[0] != '*')
+ putc(' ', out);
+}
+
+
+/*
+ * 'write_file()' - Copy a file to the output.
+ */
+
+static void
+write_file(FILE *out, /* I - Output file */
+ const char *file) /* I - File to copy */
+{
+ FILE *fp; /* Copy file */
+ char line[8192]; /* Line from file */
+
+
+ if ((fp = fopen(file, "r")) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to open \"%s\": %s\n", file,
+ strerror(errno));
+ return;
+ }
+
+ while (fgets(line, sizeof(line), fp))
+ fputs(line, out);
+
+ fclose(fp);
+}
+
+
+/*
+ * 'write_function()' - Write documentation for a function.
+ */
+
+static void
+write_function(FILE *out, /* I - Output file */
+ mxml_node_t *doc, /* I - Document */
+ mxml_node_t *function, /* I - Function */
+ int level) /* I - Base heading level */
+{
+ mxml_node_t *arg, /* Current argument */
+ *adesc, /* Description of argument */
+ *description, /* Description of function */
+ *type, /* Type for argument */
+ *node; /* Node in description */
+ const char *name, /* Name of function/type */
+ *defval; /* Default value */
+ char prefix; /* Prefix character */
+ char *sep; /* Newline separator */
+
+
+ name = mxmlElementGetAttr(function, "name");
+ description = mxmlFindElement(function, function, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<h%d class=\"%s\">%s<a name=\"%s\">%s</a></h%d>\n",
+ level, level == 3 ? "function" : "method",
+ get_comment_info(description), name, name, level);
+
+ if (description)
+ write_description(out, description, "p", 1);
+
+ fputs("<p class=\"code\">\n", out);
+
+ arg = mxmlFindElement(function, function, "returnvalue", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ if (arg)
+ write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_HTML);
+ else
+ fputs("void ", out);
+
+ fprintf(out, "%s ", name);
+ for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
+ MXML_DESCEND_FIRST), prefix = '(';
+ arg;
+ arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
+ MXML_NO_DESCEND), prefix = ',')
+ {
+ type = mxmlFindElement(arg, arg, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ fprintf(out, "%c<br>\n&nbsp;&nbsp;&nbsp;&nbsp;", prefix);
+ if (type->child)
+ write_element(out, doc, type, OUTPUT_HTML);
+
+ fputs(mxmlElementGetAttr(arg, "name"), out);
+ if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
+ fprintf(out, " %s", defval);
+ }
+
+ if (prefix == '(')
+ fputs("(void);</p>\n", out);
+ else
+ {
+ fprintf(out,
+ "<br>\n);</p>\n"
+ "<h%d class=\"parameters\">Parameters</h%d>\n"
+ "<dl>\n", level + 1, level + 1);
+
+ for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ arg;
+ arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ fprintf(out, "<dt>%s</dt>\n", mxmlElementGetAttr(arg, "name"));
+
+ adesc = mxmlFindElement(arg, arg, "description", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ write_description(out, adesc, "dd", 1);
+ write_description(out, adesc, "dd", 0);
+ }
+
+ fputs("</dl>\n", out);
+ }
+
+ arg = mxmlFindElement(function, function, "returnvalue", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ if (arg)
+ {
+ fprintf(out, "<h%d class=\"returnvalue\">Return Value</h%d>\n", level + 1,
+ level + 1);
+
+ adesc = mxmlFindElement(arg, arg, "description", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ write_description(out, adesc, "p", 1);
+ write_description(out, adesc, "p", 0);
+ }
+
+ if (description)
+ {
+ for (node = description->child; node; node = node->next)
+ if (node->value.text.string &&
+ (sep = strstr(node->value.text.string, "\n\n")) != NULL)
+ {
+ sep += 2;
+ if (*sep && strncmp(sep, "@since ", 7) &&
+ strncmp(sep, "@deprecated@", 12))
+ break;
+ }
+
+ if (node)
+ {
+ fprintf(out, "<h%d class=\"discussion\">Discussion</h%d>\n", level + 1,
+ level + 1);
+ write_description(out, description, "p", 0);
+ }
+ }
+}
+
+
+/*
+ * 'write_html()' - Write HTML documentation.
+ */
+
+static void
+write_html(const char *section, /* I - Section */
+ const char *title, /* I - Title */
+ const char *footerfile, /* I - Footer file */
+ const char *headerfile, /* I - Header file */
+ const char *introfile, /* I - Intro file */
+ const char *cssfile, /* I - Stylesheet file */
+ const char *framefile, /* I - Framed HTML basename */
+ const char *docset, /* I - Documentation set directory */
+ const char *docversion, /* I - Documentation set version */
+ const char *feedname, /* I - Feed name for doc set */
+ const char *feedurl, /* I - Feed URL for doc set */
+ mxml_node_t *doc) /* I - XML documentation */
+{
+ FILE *out; /* Output file */
+ mxml_node_t *function, /* Current function */
+ *scut, /* Struct/class/union/typedef */
+ *arg, /* Current argument */
+ *description, /* Description of function/var */
+ *type; /* Type for argument */
+ const char *name, /* Name of function/type */
+ *defval, /* Default value */
+ *basename; /* Base filename for framed output */
+ char filename[1024]; /* Current output filename */
+
+
+ if (framefile)
+ {
+ /*
+ * Get the basename of the frame file...
+ */
+
+ if ((basename = strrchr(framefile, '/')) != NULL)
+ basename ++;
+ else
+ basename = framefile;
+
+ if (strstr(basename, ".html"))
+ fputs("mxmldoc: Frame base name should not contain .html extension!\n",
+ stderr);
+
+ /*
+ * Create the container HTML file for the frames...
+ */
+
+ snprintf(filename, sizeof(filename), "%s.html", framefile);
+
+ if ((out = fopen(filename, "w")) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+
+ fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" "
+ "\"http://www.w3.org/TR/html4/frameset.dtd\">\n"
+ "<html>\n"
+ "<head>\n"
+ "\t<title>", out);
+ write_string(out, title, OUTPUT_HTML);
+ fputs("</title>\n", out);
+
+ if (section)
+ fprintf(out, "\t<meta name=\"keywords\" content=\"%s\">\n", section);
+
+ fputs("\t<meta http-equiv=\"Content-Type\" "
+ "content=\"text/html;charset=utf-8\">\n"
+ "\t<meta name=\"creator\" content=\"" MXML_VERSION "\">\n"
+ "</head>\n", out);
+
+ fputs("<frameset cols=\"250,*\">\n", out);
+ fprintf(out, "<frame src=\"%s-toc.html\">\n", basename);
+ fprintf(out, "<frame name=\"body\" src=\"%s-body.html\">\n", basename);
+ fputs("</frameset>\n"
+ "<noframes>\n"
+ "<h1>", out);
+ write_string(out, title, OUTPUT_HTML);
+ fprintf(out,
+ "</h1>\n"
+ "<ul>\n"
+ "\t<li><a href=\"%s-toc.html\">Table of Contents</a></li>\n"
+ "\t<li><a href=\"%s-body.html\">Body</a></li>\n"
+ "</ul>\n", basename, basename);
+ fputs("</noframes>\n"
+ "</html>\n", out);
+ fclose(out);
+
+ /*
+ * Write the table-of-contents file...
+ */
+
+ snprintf(filename, sizeof(filename), "%s-toc.html", framefile);
+
+ if ((out = fopen(filename, "w")) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+
+ write_html_head(out, section, title, cssfile);
+
+ snprintf(filename, sizeof(filename), "%s-body.html", basename);
+
+ fputs("<div class=\"contents\">\n", out);
+ fprintf(out, "<h1 class=\"title\"><a href=\"%s\" target=\"body\">",
+ filename);
+ write_string(out, title, OUTPUT_HTML);
+ fputs("</a></h1>\n", out);
+
+ write_toc(out, doc, introfile, filename, 0);
+
+ fputs("</div>\n"
+ "</body>\n"
+ "</html>\n", out);
+ fclose(out);
+
+ /*
+ * Finally, open the body file...
+ */
+
+ snprintf(filename, sizeof(filename), "%s-body.html", framefile);
+
+ if ((out = fopen(filename, "w")) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+ }
+ else if (docset)
+ {
+ /*
+ * Create an Xcode documentation set - start by removing any existing
+ * output directory...
+ */
+
+#ifdef __APPLE__
+ const char *id; /* Identifier */
+
+
+ if (!access(docset, 0) && !remove_directory(docset))
+ return;
+
+ /*
+ * Then make the Apple standard bundle directory structure...
+ */
+
+ if (mkdir(docset, 0755))
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", docset,
+ strerror(errno));
+ return;
+ }
+
+ snprintf(filename, sizeof(filename), "%s/Contents", docset);
+ if (mkdir(filename, 0755))
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+
+ snprintf(filename, sizeof(filename), "%s/Contents/Resources", docset);
+ if (mkdir(filename, 0755))
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+
+ snprintf(filename, sizeof(filename), "%s/Contents/Resources/Documentation",
+ docset);
+ if (mkdir(filename, 0755))
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+
+ /*
+ * The Info.plist file, which describes the documentation set...
+ */
+
+ if ((id = strrchr(docset, '/')) != NULL)
+ id ++;
+ else
+ id = docset;
+
+ snprintf(filename, sizeof(filename), "%s/Contents/Info.plist", docset);
+ if ((out = fopen(filename, "w")) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+
+ fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ "<plist version=\"1.0\">\n"
+ "<dict>\n"
+ "\t<key>CFBundleIdentifier</key>\n"
+ "\t<string>", out);
+ write_string(out, id, OUTPUT_HTML);
+ fputs("</string>\n"
+ "\t<key>CFBundleName</key>\n"
+ "\t<string>", out);
+ write_string(out, title, OUTPUT_HTML);
+ fputs("</string>\n"
+ "\t<key>CFBundleVersion</key>\n"
+ "\t<string>", out);
+ write_string(out, docversion ? docversion : "0.0", OUTPUT_HTML);
+ fputs("</string>\n"
+ "\t<key>CFBundleShortVersionString</key>\n"
+ "\t<string>", out);
+ write_string(out, docversion ? docversion : "0.0", OUTPUT_HTML);
+ fputs("</string>\n", out);
+
+ if (feedname)
+ {
+ fputs("\t<key>DocSetFeedName</key>\n"
+ "\t<string>", out);
+ write_string(out, feedname ? feedname : title, OUTPUT_HTML);
+ fputs("</string>\n", out);
+ }
+
+ if (feedurl)
+ {
+ fputs("\t<key>DocSetFeedURL</key>\n"
+ "\t<string>", out);
+ write_string(out, feedurl, OUTPUT_HTML);
+ fputs("</string>\n", out);
+ }
+
+ fputs("</dict>\n"
+ "</plist>\n", out);
+
+ fclose(out);
+
+ /*
+ * Next the Nodes.xml file...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/Contents/Resources/Nodes.xml",
+ docset);
+ if ((out = fopen(filename, "w")) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+
+ fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<DocSetNodes version=\"1.0\">\n"
+ "<TOC>\n"
+ "<Node id=\"0\">\n"
+ "<Name>", out);
+ write_string(out, title, OUTPUT_HTML);
+ fputs("</Name>\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Subnodes>\n", out);
+
+ write_toc(out, doc, introfile, NULL, 1);
+
+ fputs("</Subnodes>\n"
+ "</Node>\n"
+ "</TOC>\n"
+ "</DocSetNodes>\n", out);
+
+ fclose(out);
+
+ /*
+ * Then the Tokens.xml file...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/Contents/Resources/Tokens.xml",
+ docset);
+ if ((out = fopen(filename, "w")) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+
+ fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<Tokens version=\"1.0\">\n", out);
+
+ write_tokens(out, doc, "index.html");
+
+ fputs("</Tokens>\n", out);
+
+ fclose(out);
+
+ /*
+ * Finally the HTML file...
+ */
+
+ snprintf(filename, sizeof(filename),
+ "%s/Contents/Resources/Documentation/index.html",
+ docset);
+ if ((out = fopen(filename, "w")) == NULL)
+ {
+ fprintf(stderr, "mxmldoc: Unable to create \"%s\": %s\n", filename,
+ strerror(errno));
+ return;
+ }
+
+#else
+ fputs("mxmldoc: Xcode documentation sets can only be created on "
+ "Mac OS X.\n", stderr);
+ return;
+#endif /* __APPLE__ */
+ }
+ else
+ out = stdout;
+
+ /*
+ * Standard header...
+ */
+
+ write_html_head(out, section, title, cssfile);
+
+ fputs("<div class='body'>\n", out);
+
+ /*
+ * Header...
+ */
+
+ if (headerfile)
+ {
+ /*
+ * Use custom header...
+ */
+
+ write_file(out, headerfile);
+ }
+ else
+ {
+ /*
+ * Use standard header...
+ */
+
+ fputs("<h1 class=\"title\">", out);
+ write_string(out, title, OUTPUT_HTML);
+ fputs("</h1>\n", out);
+ }
+
+ /*
+ * Table of contents...
+ */
+
+ if (!framefile)
+ write_toc(out, doc, introfile, NULL, 0);
+
+ /*
+ * Intro...
+ */
+
+ if (introfile)
+ write_file(out, introfile);
+
+ /*
+ * List of classes...
+ */
+
+ if ((scut = find_public(doc, doc, "class")) != NULL)
+ {
+ fputs("<h2 class=\"title\"><a name=\"CLASSES\">Classes</a></h2>\n", out);
+
+ while (scut)
+ {
+ write_scu(out, doc, scut);
+
+ scut = find_public(scut, doc, "class");
+ }
+ }
+
+ /*
+ * List of functions...
+ */
+
+ if ((function = find_public(doc, doc, "function")) != NULL)
+ {
+ fputs("<h2 class=\"title\"><a name=\"FUNCTIONS\">Functions</a></h2>\n", out);
+
+ while (function)
+ {
+ write_function(out, doc, function, 3);
+
+ function = find_public(function, doc, "function");
+ }
+ }
+
+ /*
+ * List of types...
+ */
+
+ if ((scut = find_public(doc, doc, "typedef")) != NULL)
+ {
+ fputs("<h2 class=\"title\"><a name=\"TYPES\">Data Types</a></h2>\n", out);
+
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ fprintf(out, "<h3 class=\"typedef\">%s<a name=\"%s\">%s</a></h3>\n",
+ get_comment_info(description), name, name);
+
+ if (description)
+ write_description(out, description, "p", 1);
+
+ fputs("<p class=\"code\">\n"
+ "typedef ", out);
+
+ type = mxmlFindElement(scut, scut, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ for (type = type->child; type; type = type->next)
+ if (!strcmp(type->value.text.string, "("))
+ break;
+ else
+ {
+ if (type->value.text.whitespace)
+ putc(' ', out);
+
+ if (mxmlFindElement(doc, doc, "class", "name",
+ type->value.text.string, MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "enumeration", "name",
+ type->value.text.string, MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "struct", "name",
+ type->value.text.string, MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "typedef", "name",
+ type->value.text.string, MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "union", "name",
+ type->value.text.string, MXML_DESCEND))
+ {
+ fputs("<a href=\"#", out);
+ write_string(out, type->value.text.string, OUTPUT_HTML);
+ fputs("\">", out);
+ write_string(out, type->value.text.string, OUTPUT_HTML);
+ fputs("</a>", out);
+ }
+ else
+ write_string(out, type->value.text.string, OUTPUT_HTML);
+ }
+
+ if (type)
+ {
+ /*
+ * Output function type...
+ */
+
+ if (type->prev && type->prev->value.text.string[0] != '*')
+ putc(' ', out);
+
+ fprintf(out, "(*%s", name);
+
+ for (type = type->next->next; type; type = type->next)
+ {
+ if (type->value.text.whitespace)
+ putc(' ', out);
+
+ if (mxmlFindElement(doc, doc, "class", "name",
+ type->value.text.string, MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "enumeration", "name",
+ type->value.text.string, MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "struct", "name",
+ type->value.text.string, MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "typedef", "name",
+ type->value.text.string, MXML_DESCEND) ||
+ mxmlFindElement(doc, doc, "union", "name",
+ type->value.text.string, MXML_DESCEND))
+ {
+ fputs("<a href=\"#", out);
+ write_string(out, type->value.text.string, OUTPUT_HTML);
+ fputs("\">", out);
+ write_string(out, type->value.text.string, OUTPUT_HTML);
+ fputs("</a>", out);
+ }
+ else
+ write_string(out, type->value.text.string, OUTPUT_HTML);
+ }
+
+ fputs(";\n", out);
+ }
+ else
+ {
+ type = mxmlFindElement(scut, scut, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ if (type->last_child->value.text.string[0] != '*')
+ putc(' ', out);
+
+ fprintf(out, "%s;\n", name);
+ }
+
+ fputs("</p>\n", out);
+
+ scut = find_public(scut, doc, "typedef");
+ }
+ }
+
+ /*
+ * List of structures...
+ */
+
+ if ((scut = find_public(doc, doc, "struct")) != NULL)
+ {
+ fputs("<h2 class=\"title\"><a name=\"STRUCTURES\">Structures</a></h2>\n",
+ out);
+
+ while (scut)
+ {
+ write_scu(out, doc, scut);
+
+ scut = find_public(scut, doc, "struct");
+ }
+ }
+
+ /*
+ * List of unions...
+ */
+
+ if ((scut = find_public(doc, doc, "union")) != NULL)
+ {
+ fputs("<h2 class=\"title\"><a name=\"UNIONS\">Unions</a></h2>\n", out);
+
+ while (scut)
+ {
+ write_scu(out, doc, scut);
+
+ scut = find_public(scut, doc, "union");
+ }
+ }
+
+ /*
+ * Variables...
+ */
+
+ if ((arg = find_public(doc, doc, "variable")) != NULL)
+ {
+ fputs("<h2 class=\"title\"><a name=\"VARIABLES\">Variables</a></h2>\n",
+ out);
+
+ while (arg)
+ {
+ name = mxmlElementGetAttr(arg, "name");
+ description = mxmlFindElement(arg, arg, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ fprintf(out, "<h3 class=\"variable\">%s<a name=\"%s\">%s</a></h3>\n",
+ get_comment_info(description), name, name);
+
+ if (description)
+ write_description(out, description, "p", 1);
+
+ fputs("<p class=\"code\">", out);
+
+ write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_HTML);
+ fputs(mxmlElementGetAttr(arg, "name"), out);
+ if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
+ fprintf(out, " %s", defval);
+ fputs(";</p>\n", out);
+
+ arg = find_public(arg, doc, "variable");
+ }
+ }
+
+ /*
+ * List of enumerations...
+ */
+
+ if ((scut = find_public(doc, doc, "enumeration")) != NULL)
+ {
+ fputs("<h2 class=\"title\"><a name=\"ENUMERATIONS\">Constants</a></h2>\n",
+ out);
+
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ fprintf(out, "<h3 class=\"enumeration\">%s<a name=\"%s\">%s</a></h3>\n",
+ get_comment_info(description), name, name);
+
+ if (description)
+ write_description(out, description, "p", 1);
+
+ fputs("<h4 class=\"constants\">Constants</h4>\n"
+ "<dl>\n", out);
+
+ for (arg = mxmlFindElement(scut, scut, "constant", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ arg;
+ arg = mxmlFindElement(arg, scut, "constant", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ description = mxmlFindElement(arg, arg, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ fprintf(out, "<dt>%s %s</dt>\n",
+ mxmlElementGetAttr(arg, "name"), get_comment_info(description));
+
+ write_description(out, description, "dd", 1);
+ write_description(out, description, "dd", 0);
+ }
+
+ fputs("</dl>\n", out);
+
+ scut = find_public(scut, doc, "enumeration");
+ }
+ }
+
+ /*
+ * Footer...
+ */
+
+ if (footerfile)
+ {
+ /*
+ * Use custom footer...
+ */
+
+ write_file(out, footerfile);
+ }
+
+ fputs("</div>\n"
+ "</body>\n"
+ "</html>\n", out);
+
+ /*
+ * Close output file as needed...
+ */
+
+ if (out != stdout)
+ fclose(out);
+
+#ifdef __APPLE__
+ /*
+ * When generating document sets, run the docsetutil program to index it...
+ */
+
+ if (docset)
+ {
+ const char *args[4]; /* Argument array */
+ pid_t pid; /* Process ID */
+ int status; /* Exit status */
+
+
+ args[0] = "/Developer/usr/bin/docsetutil";
+ args[1] = "index";
+ args[2] = docset;
+ args[3] = NULL;
+
+ if (posix_spawn(&pid, args[0], NULL, NULL, (char **)args, environ))
+ {
+ fprintf(stderr, "mxmldoc: Unable to index documentation set \"%s\": %s\n",
+ docset, strerror(errno));
+ }
+ else
+ {
+ while (wait(&status) != pid);
+
+ if (status)
+ {
+ if (WIFEXITED(status))
+ fprintf(stderr, "mxmldoc: docsetutil exited with status %d\n",
+ WEXITSTATUS(status));
+ else
+ fprintf(stderr, "mxmldoc: docsetutil crashed with signal %d\n",
+ WTERMSIG(status));
+ }
+ else
+ {
+ /*
+ * Remove unneeded temporary XML files...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/Contents/Resources/Nodes.xml",
+ docset);
+ unlink(filename);
+
+ snprintf(filename, sizeof(filename), "%s/Contents/Resources/Tokens.xml",
+ docset);
+ unlink(filename);
+ }
+ }
+ }
+#endif /* __APPLE__ */
+}
+
+
+/*
+ * 'write_html_head()' - Write the standard HTML header.
+ */
+
+static void
+write_html_head(FILE *out, /* I - Output file */
+ const char *section, /* I - Section */
+ const char *title, /* I - Title */
+ const char *cssfile) /* I - Stylesheet */
+{
+ fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
+ "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
+ "<html>\n", out);
+
+ if (section)
+ fprintf(out, "<!-- SECTION: %s -->\n", section);
+
+ fputs("<head>\n"
+ "\t<title>", out);
+ write_string(out, title, OUTPUT_HTML);
+ fputs("\t</title>\n", out);
+
+ if (section)
+ fprintf(out, "\t<meta name=\"keywords\" content=\"%s\">\n", section);
+
+ fputs("\t<meta http-equiv=\"Content-Type\" "
+ "content=\"text/html;charset=utf-8\">\n"
+ "\t<meta name=\"creator\" content=\"" MXML_VERSION "\">\n"
+ "<style type=\"text/css\"><!--\n", out);
+
+ if (cssfile)
+ {
+ /*
+ * Use custom stylesheet file...
+ */
+
+ write_file(out, cssfile);
+ }
+ else
+ {
+ /*
+ * Use standard stylesheet...
+ */
+
+ fputs("body, p, h1, h2, h3, h4 {\n"
+ " font-family: \"lucida grande\", geneva, helvetica, arial, "
+ "sans-serif;\n"
+ "}\n"
+ "div.body h1 {\n"
+ " font-size: 250%;\n"
+ " font-weight: bold;\n"
+ " margin: 0;\n"
+ "}\n"
+ "div.body h2 {\n"
+ " font-size: 250%;\n"
+ " margin-top: 1.5em;\n"
+ "}\n"
+ "div.body h3 {\n"
+ " font-size: 150%;\n"
+ " margin-bottom: 0.5em;\n"
+ " margin-top: 1.5em;\n"
+ "}\n"
+ "div.body h4 {\n"
+ " font-size: 110%;\n"
+ " margin-bottom: 0.5em;\n"
+ " margin-top: 1.5em;\n"
+ "}\n"
+ "div.body h5 {\n"
+ " font-size: 100%;\n"
+ " margin-bottom: 0.5em;\n"
+ " margin-top: 1.5em;\n"
+ "}\n"
+ "div.contents {\n"
+ " background: #e8e8e8;\n"
+ " border: solid thin black;\n"
+ " padding: 10px;\n"
+ "}\n"
+ "div.contents h1 {\n"
+ " font-size: 110%;\n"
+ "}\n"
+ "div.contents h2 {\n"
+ " font-size: 100%;\n"
+ "}\n"
+ "div.contents ul.contents {\n"
+ " font-size: 80%;\n"
+ "}\n"
+ ".class {\n"
+ " border-bottom: solid 2px gray;\n"
+ "}\n"
+ ".constants {\n"
+ "}\n"
+ ".description {\n"
+ " margin-top: 0.5em;\n"
+ "}\n"
+ ".discussion {\n"
+ "}\n"
+ ".enumeration {\n"
+ " border-bottom: solid 2px gray;\n"
+ "}\n"
+ ".function {\n"
+ " border-bottom: solid 2px gray;\n"
+ " margin-bottom: 0;\n"
+ "}\n"
+ ".members {\n"
+ "}\n"
+ ".method {\n"
+ "}\n"
+ ".parameters {\n"
+ "}\n"
+ ".returnvalue {\n"
+ "}\n"
+ ".struct {\n"
+ " border-bottom: solid 2px gray;\n"
+ "}\n"
+ ".typedef {\n"
+ " border-bottom: solid 2px gray;\n"
+ "}\n"
+ ".union {\n"
+ " border-bottom: solid 2px gray;\n"
+ "}\n"
+ ".variable {\n"
+ "}\n"
+ "code, p.code, pre, ul.code li {\n"
+ " font-family: monaco, courier, monospace;\n"
+ " font-size: 90%;\n"
+ "}\n"
+ "a:link, a:visited {\n"
+ " text-decoration: none;\n"
+ "}\n"
+ "span.info {\n"
+ " background: black;\n"
+ " border: solid thin black;\n"
+ " color: white;\n"
+ " font-size: 80%;\n"
+ " font-style: italic;\n"
+ " font-weight: bold;\n"
+ " white-space: nowrap;\n"
+ "}\n"
+ "h3 span.info, h4 span.info {\n"
+ " float: right;\n"
+ " font-size: 100%;\n"
+ "}\n"
+ "ul.code, ul.contents, ul.subcontents {\n"
+ " list-style-type: none;\n"
+ " margin: 0;\n"
+ " padding-left: 0;\n"
+ "}\n"
+ "ul.code li {\n"
+ " margin: 0;\n"
+ "}\n"
+ "ul.contents > li {\n"
+ " margin-top: 1em;\n"
+ "}\n"
+ "ul.contents li ul.code, ul.contents li ul.subcontents {\n"
+ " padding-left: 2em;\n"
+ "}\n"
+ "div.body dl {\n"
+ " margin-top: 0;\n"
+ "}\n"
+ "div.body dt {\n"
+ " font-style: italic;\n"
+ " margin-top: 0;\n"
+ "}\n"
+ "div.body dd {\n"
+ " margin-bottom: 0.5em;\n"
+ "}\n"
+ "h1.title {\n"
+ "}\n"
+ "h2.title {\n"
+ " border-bottom: solid 2px black;\n"
+ "}\n"
+ "h3.title {\n"
+ " border-bottom: solid 2px black;\n"
+ "}\n", out);
+ }
+
+ fputs("--></style>\n"
+ "</head>\n"
+ "<body>\n", out);
+}
+
+
+/*
+ * 'write_man()' - Write manpage documentation.
+ */
+
+static void
+write_man(const char *man_name, /* I - Name of manpage */
+ const char *section, /* I - Section */
+ const char *title, /* I - Title */
+ const char *footerfile, /* I - Footer file */
+ const char *headerfile, /* I - Header file */
+ const char *introfile, /* I - Intro file */
+ mxml_node_t *doc) /* I - XML documentation */
+{
+ int i; /* Looping var */
+ mxml_node_t *function, /* Current function */
+ *scut, /* Struct/class/union/typedef */
+ *arg, /* Current argument */
+ *description, /* Description of function/var */
+ *type; /* Type for argument */
+ const char *name, /* Name of function/type */
+ *cname, /* Class name */
+ *defval, /* Default value */
+ *parent; /* Parent class */
+ int inscope; /* Variable/method scope */
+ char prefix; /* Prefix character */
+ time_t curtime; /* Current time */
+ struct tm *curdate; /* Current date */
+ char buffer[1024]; /* String buffer */
+ static const char * const scopes[] = /* Scope strings */
+ {
+ "private",
+ "protected",
+ "public"
+ };
+
+
+ /*
+ * Standard man page...
+ */
+
+ curtime = time(NULL);
+ curdate = localtime(&curtime);
+ strftime(buffer, sizeof(buffer), "%x", curdate);
+
+ printf(".TH %s %s \"%s\" \"%s\" \"%s\"\n", man_name, section ? section : "3",
+ title ? title : "", buffer, title ? title : "");
+
+ /*
+ * Header...
+ */
+
+ if (headerfile)
+ {
+ /*
+ * Use custom header...
+ */
+
+ write_file(stdout, headerfile);
+ }
+ else
+ {
+ /*
+ * Use standard header...
+ */
+
+ puts(".SH NAME");
+ printf("%s \\- %s\n", man_name, title ? title : man_name);
+ }
+
+ /*
+ * Intro...
+ */
+
+ if (introfile)
+ write_file(stdout, introfile);
+
+ /*
+ * List of classes...
+ */
+
+ if (find_public(doc, doc, "class"))
+ {
+ puts(".SH CLASSES");
+
+ for (scut = find_public(doc, doc, "class");
+ scut;
+ scut = find_public(scut, doc, "class"))
+ {
+ cname = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ printf(".SS %s\n", cname);
+
+ write_description(stdout, description, NULL, 1);
+
+ printf(".PP\n"
+ ".nf\n"
+ "class %s", cname);
+ if ((parent = mxmlElementGetAttr(scut, "parent")) != NULL)
+ printf(" %s", parent);
+ puts("\n{");
+
+ for (i = 0; i < 3; i ++)
+ {
+ inscope = 0;
+
+ for (arg = mxmlFindElement(scut, scut, "variable", "scope", scopes[i],
+ MXML_DESCEND_FIRST);
+ arg;
+ arg = mxmlFindElement(arg, scut, "variable", "scope", scopes[i],
+ MXML_NO_DESCEND))
+ {
+ if (!inscope)
+ {
+ inscope = 1;
+ printf(" %s:\n", scopes[i]);
+ }
+
+ printf(" ");
+ write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_MAN);
+ printf("%s;\n", mxmlElementGetAttr(arg, "name"));
+ }
+
+ for (function = mxmlFindElement(scut, scut, "function", "scope",
+ scopes[i], MXML_DESCEND_FIRST);
+ function;
+ function = mxmlFindElement(function, scut, "function", "scope",
+ scopes[i], MXML_NO_DESCEND))
+ {
+ if (!inscope)
+ {
+ inscope = 1;
+ printf(" %s:\n", scopes[i]);
+ }
+
+ name = mxmlElementGetAttr(function, "name");
+
+ printf(" ");
+
+ arg = mxmlFindElement(function, function, "returnvalue", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ if (arg)
+ write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_MAN);
+ else if (strcmp(cname, name) && strcmp(cname, name + 1))
+ fputs("void ", stdout);
+
+ printf("%s", name);
+
+ for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
+ MXML_DESCEND_FIRST), prefix = '(';
+ arg;
+ arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
+ MXML_NO_DESCEND), prefix = ',')
+ {
+ type = mxmlFindElement(arg, arg, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ putchar(prefix);
+ if (prefix == ',')
+ putchar(' ');
+
+ if (type->child)
+ write_element(stdout, doc, type, OUTPUT_MAN);
+ fputs(mxmlElementGetAttr(arg, "name"), stdout);
+ if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
+ printf(" %s", defval);
+ }
+
+ if (prefix == '(')
+ puts("(void);");
+ else
+ puts(");");
+ }
+ }
+
+ puts("};\n"
+ ".fi");
+
+ write_description(stdout, description, NULL, 0);
+ }
+ }
+
+ /*
+ * List of enumerations...
+ */
+
+ if (find_public(doc, doc, "enumeration"))
+ {
+ puts(".SH ENUMERATIONS");
+
+ for (scut = find_public(doc, doc, "enumeration");
+ scut;
+ scut = find_public(scut, doc, "enumeration"))
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ printf(".SS %s\n", name);
+
+ write_description(stdout, description, NULL, 1);
+ write_description(stdout, description, NULL, 0);
+
+ for (arg = mxmlFindElement(scut, scut, "constant", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ arg;
+ arg = mxmlFindElement(arg, scut, "constant", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ description = mxmlFindElement(arg, arg, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ printf(".TP 5\n%s\n.br\n", mxmlElementGetAttr(arg, "name"));
+ write_description(stdout, description, NULL, 1);
+ }
+ }
+ }
+
+ /*
+ * List of functions...
+ */
+
+ if (find_public(doc, doc, "function"))
+ {
+ puts(".SH FUNCTIONS");
+
+ for (function = find_public(doc, doc, "function");
+ function;
+ function = find_public(function, doc, "function"))
+ {
+ name = mxmlElementGetAttr(function, "name");
+ description = mxmlFindElement(function, function, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ printf(".SS %s\n", name);
+
+ write_description(stdout, description, NULL, 1);
+
+ puts(".PP\n"
+ ".nf");
+
+ arg = mxmlFindElement(function, function, "returnvalue", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ if (arg)
+ write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_MAN);
+ else
+ fputs("void", stdout);
+
+ printf(" %s ", name);
+ for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
+ MXML_DESCEND_FIRST), prefix = '(';
+ arg;
+ arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
+ MXML_NO_DESCEND), prefix = ',')
+ {
+ type = mxmlFindElement(arg, arg, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ printf("%c\n ", prefix);
+ if (type->child)
+ write_element(stdout, doc, type, OUTPUT_MAN);
+ fputs(mxmlElementGetAttr(arg, "name"), stdout);
+ if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
+ printf(" %s", defval);
+ }
+
+ if (prefix == '(')
+ puts("(void);");
+ else
+ puts("\n);");
+
+ puts(".fi");
+
+ write_description(stdout, description, NULL, 0);
+ }
+ }
+
+ /*
+ * List of structures...
+ */
+
+ if (find_public(doc, doc, "struct"))
+ {
+ puts(".SH STRUCTURES");
+
+ for (scut = find_public(doc, doc, "struct");
+ scut;
+ scut = find_public(scut, doc, "struct"))
+ {
+ cname = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ printf(".SS %s\n", cname);
+
+ write_description(stdout, description, NULL, 1);
+
+ printf(".PP\n"
+ ".nf\n"
+ "struct %s\n{\n", cname);
+ for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ arg;
+ arg = mxmlFindElement(arg, scut, "variable", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ printf(" ");
+ write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_MAN);
+ printf("%s;\n", mxmlElementGetAttr(arg, "name"));
+ }
+
+ for (function = mxmlFindElement(scut, scut, "function", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ function;
+ function = mxmlFindElement(function, scut, "function", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ name = mxmlElementGetAttr(function, "name");
+
+ printf(" ");
+
+ arg = mxmlFindElement(function, function, "returnvalue", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ if (arg)
+ write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_MAN);
+ else if (strcmp(cname, name) && strcmp(cname, name + 1))
+ fputs("void ", stdout);
+
+ fputs(name, stdout);
+
+ for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
+ MXML_DESCEND_FIRST), prefix = '(';
+ arg;
+ arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
+ MXML_NO_DESCEND), prefix = ',')
+ {
+ type = mxmlFindElement(arg, arg, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ putchar(prefix);
+ if (prefix == ',')
+ putchar(' ');
+
+ if (type->child)
+ write_element(stdout, doc, type, OUTPUT_MAN);
+ fputs(mxmlElementGetAttr(arg, "name"), stdout);
+ if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
+ printf(" %s", defval);
+ }
+
+ if (prefix == '(')
+ puts("(void);");
+ else
+ puts(");");
+ }
+
+ puts("};\n"
+ ".fi");
+
+ write_description(stdout, description, NULL, 0);
+ }
+ }
+
+ /*
+ * List of types...
+ */
+
+ if (find_public(doc, doc, "typedef"))
+ {
+ puts(".SH TYPES");
+
+ for (scut = find_public(doc, doc, "typedef");
+ scut;
+ scut = find_public(scut, doc, "typedef"))
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ printf(".SS %s\n", name);
+
+ write_description(stdout, description, NULL, 1);
+
+ fputs(".PP\n"
+ ".nf\n"
+ "typedef ", stdout);
+
+ type = mxmlFindElement(scut, scut, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ for (type = type->child; type; type = type->next)
+ if (!strcmp(type->value.text.string, "("))
+ break;
+ else
+ {
+ if (type->value.text.whitespace)
+ putchar(' ');
+
+ write_string(stdout, type->value.text.string, OUTPUT_MAN);
+ }
+
+ if (type)
+ {
+ /*
+ * Output function type...
+ */
+
+ printf(" (*%s", name);
+
+ for (type = type->next->next; type; type = type->next)
+ {
+ if (type->value.text.whitespace)
+ putchar(' ');
+
+ write_string(stdout, type->value.text.string, OUTPUT_MAN);
+ }
+
+ puts(";");
+ }
+ else
+ printf(" %s;\n", name);
+
+ puts(".fi");
+
+ write_description(stdout, description, NULL, 0);
+ }
+ }
+
+ /*
+ * List of unions...
+ */
+
+ if (find_public(doc, doc, "union"))
+ {
+ puts(".SH UNIONS");
+
+ for (scut = find_public(doc, doc, "union");
+ scut;
+ scut = find_public(scut, doc, "union"))
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ printf(".SS %s\n", name);
+
+ write_description(stdout, description, NULL, 1);
+
+ printf(".PP\n"
+ ".nf\n"
+ "union %s\n{\n", name);
+ for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ arg;
+ arg = mxmlFindElement(arg, scut, "variable", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ printf(" ");
+ write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_MAN);
+ printf("%s;\n", mxmlElementGetAttr(arg, "name"));
+ }
+
+ puts("};\n"
+ ".fi");
+
+ write_description(stdout, description, NULL, 0);
+ }
+ }
+
+ /*
+ * Variables...
+ */
+
+ if (find_public(doc, doc, "variable"))
+ {
+ puts(".SH VARIABLES");
+
+ for (arg = find_public(doc, doc, "variable");
+ arg;
+ arg = find_public(arg, doc, "variable"))
+ {
+ name = mxmlElementGetAttr(arg, "name");
+ description = mxmlFindElement(arg, arg, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ printf(".SS %s\n", name);
+
+ write_description(stdout, description, NULL, 1);
+
+ puts(".PP\n"
+ ".nf");
+
+ write_element(stdout, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_MAN);
+ fputs(mxmlElementGetAttr(arg, "name"), stdout);
+ if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
+ printf(" %s", defval);
+ puts(";\n"
+ ".fi");
+
+ write_description(stdout, description, NULL, 0);
+ }
+ }
+
+ if (footerfile)
+ {
+ /*
+ * Use custom footer...
+ */
+
+ write_file(stdout, footerfile);
+ }
+}
+
+
+/*
+ * 'write_scu()' - Write a structure, class, or union.
+ */
+
+static void
+write_scu(FILE *out, /* I - Output file */
+ mxml_node_t *doc, /* I - Document */
+ mxml_node_t *scut) /* I - Structure, class, or union */
+{
+ int i; /* Looping var */
+ mxml_node_t *function, /* Current function */
+ *arg, /* Current argument */
+ *description, /* Description of function/var */
+ *type; /* Type for argument */
+ const char *name, /* Name of function/type */
+ *cname, /* Class name */
+ *defval, /* Default value */
+ *parent, /* Parent class */
+ *scope; /* Scope for variable/function */
+ int inscope, /* Variable/method scope */
+ maxscope; /* Maximum scope */
+ char prefix; /* Prefix character */
+ static const char * const scopes[] = /* Scope strings */
+ {
+ "private",
+ "protected",
+ "public"
+ };
+
+
+ cname = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<h3 class=\"%s\">%s<a name=\"%s\">%s</a></h3>\n",
+ scut->value.element.name, get_comment_info(description), cname,
+ cname);
+
+ if (description)
+ write_description(out, description, "p", 1);
+
+ fprintf(out, "<p class=\"code\">%s %s", scut->value.element.name, cname);
+ if ((parent = mxmlElementGetAttr(scut, "parent")) != NULL)
+ fprintf(out, " %s", parent);
+ fputs(" {<br>\n", out);
+
+ maxscope = !strcmp(scut->value.element.name, "class") ? 3 : 1;
+
+ for (i = 0; i < maxscope; i ++)
+ {
+ inscope = maxscope == 1;
+
+ for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ arg;
+ arg = mxmlFindElement(arg, scut, "variable", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ if (maxscope > 1 &&
+ ((scope = mxmlElementGetAttr(arg, "scope")) == NULL ||
+ strcmp(scope, scopes[i])))
+ continue;
+
+ if (!inscope)
+ {
+ inscope = 1;
+ fprintf(out, "&nbsp;&nbsp;%s:<br>\n", scopes[i]);
+ }
+
+ fputs("&nbsp;&nbsp;&nbsp;&nbsp;", out);
+ write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_HTML);
+ fprintf(out, "%s;<br>\n", mxmlElementGetAttr(arg, "name"));
+ }
+
+ for (function = mxmlFindElement(scut, scut, "function", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ function;
+ function = mxmlFindElement(function, scut, "function", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ if (maxscope > 1 &&
+ ((scope = mxmlElementGetAttr(arg, "scope")) == NULL ||
+ strcmp(scope, scopes[i])))
+ continue;
+
+ if (!inscope)
+ {
+ inscope = 1;
+ fprintf(out, "&nbsp;&nbsp;%s:<br>\n", scopes[i]);
+ }
+
+ name = mxmlElementGetAttr(function, "name");
+
+ fputs("&nbsp;&nbsp;&nbsp;&nbsp;", out);
+
+ arg = mxmlFindElement(function, function, "returnvalue", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ if (arg)
+ write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_HTML);
+ else if (strcmp(cname, name) && strcmp(cname, name + 1))
+ fputs("void ", out);
+
+ fprintf(out, "<a href=\"#%s.%s\">%s</a>", cname, name, name);
+
+ for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
+ MXML_DESCEND_FIRST), prefix = '(';
+ arg;
+ arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
+ MXML_NO_DESCEND), prefix = ',')
+ {
+ type = mxmlFindElement(arg, arg, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ putc(prefix, out);
+ if (prefix == ',')
+ putc(' ', out);
+
+ if (type->child)
+ write_element(out, doc, type, OUTPUT_HTML);
+
+ fputs(mxmlElementGetAttr(arg, "name"), out);
+ if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
+ fprintf(out, " %s", defval);
+ }
+
+ if (prefix == '(')
+ fputs("(void);<br>\n", out);
+ else
+ fputs(");<br>\n", out);
+ }
+ }
+
+ fputs("};</p>\n"
+ "<h4 class=\"members\">Members</h4>\n"
+ "<dl>\n", out);
+
+ for (arg = mxmlFindElement(scut, scut, "variable", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ arg;
+ arg = mxmlFindElement(arg, scut, "variable", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ description = mxmlFindElement(arg, arg, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<dt>%s %s</dt>\n",
+ mxmlElementGetAttr(arg, "name"), get_comment_info(description));
+
+ write_description(out, description, "dd", 1);
+ write_description(out, description, "dd", 0);
+ }
+
+ fputs("</dl>\n", out);
+
+ for (function = mxmlFindElement(scut, scut, "function", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ function;
+ function = mxmlFindElement(function, scut, "function", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ write_function(out, doc, function, 4);
+ }
+}
+
+
+/*
+ * 'write_string()' - Write a string, quoting HTML special chars as needed.
+ */
+
+static void
+write_string(FILE *out, /* I - Output file */
+ const char *s, /* I - String to write */
+ int mode) /* I - Output mode */
+{
+ switch (mode)
+ {
+ case OUTPUT_HTML :
+ case OUTPUT_XML :
+ while (*s)
+ {
+ if (*s == '&')
+ fputs("&amp;", out);
+ else if (*s == '<')
+ fputs("&lt;", out);
+ else if (*s == '>')
+ fputs("&gt;", out);
+ else if (*s == '\"')
+ fputs("&quot;", out);
+ 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...
+ */
+
+ fputs("&nbsp;", out);
+ }
+ else
+ fprintf(out, "&#x%x;", ch);
+ }
+ else
+ putc(*s, out);
+
+ s ++;
+ }
+ break;
+
+ case OUTPUT_MAN :
+ while (*s)
+ {
+ if (*s == '\\' || *s == '-')
+ putc('\\', out);
+
+ putc(*s++, out);
+ }
+ break;
+ }
+}
+
+
+/*
+ * 'write_toc()' - Write a table-of-contents.
+ */
+
+static void
+write_toc(FILE *out, /* I - Output file */
+ mxml_node_t *doc, /* I - Document */
+ const char *introfile, /* I - Introduction file */
+ const char *target, /* I - Target name */
+ int xml) /* I - Write XML nodes? */
+{
+ FILE *fp; /* Intro file */
+ mxml_node_t *function, /* Current function */
+ *scut, /* Struct/class/union/typedef */
+ *arg, /* Current argument */
+ *description; /* Description of function/var */
+ const char *name, /* Name of function/type */
+ *targetattr; /* Target attribute, if any */
+ int xmlid = 1; /* Current XML node ID */
+
+
+ /*
+ * If target is set, it is the frame file that contains the body.
+ * Otherwise, we are creating a single-file...
+ */
+
+ if (target)
+ targetattr = " target=\"body\"";
+ else
+ targetattr = "";
+
+ /*
+ * The table-of-contents is a nested unordered list. Start by
+ * reading any intro file to see if there are any headings there.
+ */
+
+ if (!xml)
+ fputs("<h2 class=\"title\">Contents</h2>\n"
+ "<ul class=\"contents\">\n", out);
+
+ if (introfile && (fp = fopen(introfile, "r")) != NULL)
+ {
+ char line[8192], /* Line from file */
+ *ptr, /* Pointer in line */
+ *end, /* End of line */
+ *anchor, /* Anchor name */
+ quote, /* Quote character for value */
+ level = '2', /* Current heading level */
+ newlevel; /* New heading level */
+ int inelement; /* In an element? */
+
+
+ while (fgets(line, sizeof(line), fp))
+ {
+ /*
+ * See if this line has a heading...
+ */
+
+ if ((ptr = strstr(line, "<h")) == NULL &&
+ (ptr = strstr(line, "<H")) == NULL)
+ continue;
+
+ if (ptr[2] != '2' && ptr[2] != '3')
+ continue;
+
+ newlevel = ptr[2];
+
+ /*
+ * Make sure we have the whole heading...
+ */
+
+ while (!strstr(line, "</h") && !strstr(line, "</H"))
+ {
+ end = line + strlen(line);
+
+ if (end == (line + sizeof(line) - 1) ||
+ !fgets(end, (int)(sizeof(line) - (end - line)), fp))
+ break;
+ }
+
+ /*
+ * Convert newlines and tabs to spaces...
+ */
+
+ for (ptr = line; *ptr; ptr ++)
+ if (isspace(*ptr & 255))
+ *ptr = ' ';
+
+ /*
+ * Find the anchor and text...
+ */
+
+ for (ptr = strchr(line, '<'); ptr; ptr = strchr(ptr + 1, '<'))
+ if (!strncmp(ptr, "<A NAME=", 8) || !strncmp(ptr, "<a name=", 8))
+ break;
+
+ if (!ptr)
+ continue;
+
+ ptr += 8;
+ inelement = 1;
+
+ if (*ptr == '\'' || *ptr == '\"')
+ {
+ /*
+ * Quoted anchor...
+ */
+
+ quote = *ptr++;
+ anchor = ptr;
+
+ while (*ptr && *ptr != quote)
+ ptr ++;
+
+ if (!*ptr)
+ continue;
+
+ *ptr++ = '\0';
+ }
+ else
+ {
+ /*
+ * Non-quoted anchor...
+ */
+
+ anchor = ptr;
+
+ while (*ptr && *ptr != '>' && !isspace(*ptr & 255))
+ ptr ++;
+
+ if (!*ptr)
+ continue;
+
+ if (*ptr == '>')
+ inelement = 0;
+
+ *ptr++ = '\0';
+ }
+
+ /*
+ * Write text until we see "</A>"...
+ */
+
+ if (xml)
+ {
+ if (newlevel < level)
+ fputs("</Node>\n"
+ "</Subnodes></Node>\n", out);
+ else if (newlevel > level && newlevel == '3')
+ fputs("<Subnodes>\n", out);
+ else if (xmlid > 1)
+ fputs("</Node>\n", out);
+
+ level = newlevel;
+
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<Name>", xmlid ++, anchor);
+
+ quote = 0;
+
+ while (*ptr)
+ {
+ if (inelement)
+ {
+ if (*ptr == quote)
+ quote = 0;
+ else if (*ptr == '>')
+ inelement = 0;
+ else if (*ptr == '\'' || *ptr == '\"')
+ quote = *ptr;
+ }
+ else if (*ptr == '<')
+ {
+ if (!strncmp(ptr, "</A>", 4) || !strncmp(ptr, "</a>", 4))
+ break;
+
+ inelement = 1;
+ }
+ else
+ putc(*ptr, out);
+
+ ptr ++;
+ }
+
+ fputs("</Name>\n", out);
+ }
+ else
+ {
+ if (newlevel < level)
+ fputs("</li>\n"
+ "</ul></li>\n", out);
+ else if (newlevel > level)
+ fputs("<ul class=\"subcontents\">\n", out);
+ else if (xmlid > 1)
+ fputs("</li>\n", out);
+
+ level = newlevel;
+ xmlid ++;
+
+ fprintf(out, "%s<li><a href=\"%s#%s\"%s>", level > '2' ? "\t" : "",
+ target ? target : "", anchor, targetattr);
+
+ quote = 0;
+
+ while (*ptr)
+ {
+ if (inelement)
+ {
+ if (*ptr == quote)
+ quote = 0;
+ else if (*ptr == '>')
+ inelement = 0;
+ else if (*ptr == '\'' || *ptr == '\"')
+ quote = *ptr;
+ }
+ else if (*ptr == '<')
+ {
+ if (!strncmp(ptr, "</A>", 4) || !strncmp(ptr, "</a>", 4))
+ break;
+
+ inelement = 1;
+ }
+ else
+ putc(*ptr, out);
+
+ ptr ++;
+ }
+
+ fputs("</a>", out);
+ }
+ }
+
+ if (level > '1')
+ {
+ if (xml)
+ {
+ fputs("</Node>\n", out);
+
+ if (level == '3')
+ fputs("</Subnodes></Node>\n", out);
+ }
+ else
+ {
+ fputs("</li>\n", out);
+
+ if (level == '3')
+ fputs("</ul></li>\n", out);
+ }
+ }
+
+ fclose(fp);
+ }
+
+ /*
+ * Next the classes...
+ */
+
+ if ((scut = find_public(doc, doc, "class")) != NULL)
+ {
+ if (xml)
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>CLASSES</Anchor>\n"
+ "<Name>Classes</Name>\n"
+ "<Subnodes>\n", xmlid ++);
+ else
+ fprintf(out, "<li><a href=\"%s#CLASSES\"%s>Classes</a>"
+ "<ul class=\"code\">\n",
+ target ? target : "", targetattr);
+
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ if (xml)
+ {
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<Name>%s</Name>\n"
+ "</Node>\n", xmlid ++, name, name);
+ }
+ else
+ {
+ fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
+ target ? target : "", name, targetattr);
+ write_description(out, description, "", 1);
+ fprintf(out, "\">%s</a></li>\n", name);
+ }
+
+ scut = find_public(scut, doc, "class");
+ }
+
+ if (xml)
+ fputs("</Subnodes></Node>\n", out);
+ else
+ fputs("</ul></li>\n", out);
+ }
+
+ /*
+ * Functions...
+ */
+
+ if ((function = find_public(doc, doc, "function")) != NULL)
+ {
+ if (xml)
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>FUNCTIONS</Anchor>\n"
+ "<Name>Functions</Name>\n"
+ "<Subnodes>\n", xmlid ++);
+ else
+ fprintf(out, "<li><a href=\"%s#FUNCTIONS\"%s>Functions</a>"
+ "<ul class=\"code\">\n", target ? target : "", targetattr);
+
+ while (function)
+ {
+ name = mxmlElementGetAttr(function, "name");
+ description = mxmlFindElement(function, function, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ if (xml)
+ {
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<Name>%s</Name>\n"
+ "</Node>\n", xmlid ++, name, name);
+ }
+ else
+ {
+ fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
+ target ? target : "", name, targetattr);
+ write_description(out, description, "", 1);
+ fprintf(out, "\">%s</a></li>\n", name);
+ }
+
+ function = find_public(function, doc, "function");
+ }
+
+ if (xml)
+ fputs("</Subnodes></Node>\n", out);
+ else
+ fputs("</ul></li>\n", out);
+ }
+
+ /*
+ * Data types...
+ */
+
+ if ((scut = find_public(doc, doc, "typedef")) != NULL)
+ {
+ if (xml)
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>TYPES</Anchor>\n"
+ "<Name>Data Types</Name>\n"
+ "<Subnodes>\n", xmlid ++);
+ else
+ fprintf(out, "<li><a href=\"%s#TYPES\"%s>Data Types</a>"
+ "<ul class=\"code\">\n", target ? target : "", targetattr);
+
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ if (xml)
+ {
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<Name>%s</Name>\n"
+ "</Node>\n", xmlid ++, name, name);
+ }
+ else
+ {
+ fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
+ target ? target : "", name, targetattr);
+ write_description(out, description, "", 1);
+ fprintf(out, "\">%s</a></li>\n", name);
+ }
+
+ scut = find_public(scut, doc, "typedef");
+ }
+
+ if (xml)
+ fputs("</Subnodes></Node>\n", out);
+ else
+ fputs("</ul></li>\n", out);
+ }
+
+ /*
+ * Structures...
+ */
+
+ if ((scut = find_public(doc, doc, "struct")) != NULL)
+ {
+ if (xml)
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>STRUCTURES</Anchor>\n"
+ "<Name>Structures</Name>\n"
+ "<Subnodes>\n", xmlid ++);
+ else
+ fprintf(out, "<li><a href=\"%s#STRUCTURES\"%s>Structures</a>"
+ "<ul class=\"code\">\n", target ? target : "", targetattr);
+
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ if (xml)
+ {
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<Name>%s</Name>\n"
+ "</Node>\n", xmlid ++, name, name);
+ }
+ else
+ {
+ fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
+ target ? target : "", name, targetattr);
+ write_description(out, description, "", 1);
+ fprintf(out, "\">%s</a></li>\n", name);
+ }
+
+ scut = find_public(scut, doc, "struct");
+ }
+
+ if (xml)
+ fputs("</Subnodes></Node>\n", out);
+ else
+ fputs("</ul></li>\n", out);
+ }
+
+ /*
+ * Unions...
+ */
+
+ if ((scut = find_public(doc, doc, "union")) != NULL)
+ {
+ if (xml)
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>UNIONS</Anchor>\n"
+ "<Name>Unions</Name>\n"
+ "<Subnodes>\n", xmlid ++);
+ else
+ fprintf(out,
+ "<li><a href=\"%s#UNIONS\"%s>Unions</a><ul class=\"code\">\n",
+ target ? target : "", targetattr);
+
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ if (xml)
+ {
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<Name>%s</Name>\n"
+ "</Node>\n", xmlid ++, name, name);
+ }
+ else
+ {
+ fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
+ target ? target : "", name, targetattr);
+ write_description(out, description, "", 1);
+ fprintf(out, "\">%s</a></li>\n", name);
+ }
+
+ scut = find_public(scut, doc, "union");
+ }
+
+ if (xml)
+ fputs("</Subnodes></Node>\n", out);
+ else
+ fputs("</ul></li>\n", out);
+ }
+
+ /*
+ * Globals variables...
+ */
+
+ if ((arg = find_public(doc, doc, "variable")) != NULL)
+ {
+ if (xml)
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>VARIABLES</Anchor>\n"
+ "<Name>Variables</Name>\n"
+ "<Subnodes>\n", xmlid ++);
+ else
+ fprintf(out, "<li><a href=\"%s#VARIABLES\"%s>Variables</a>"
+ "<ul class=\"code\">\n", target ? target : "", targetattr);
+
+ while (arg)
+ {
+ name = mxmlElementGetAttr(arg, "name");
+ description = mxmlFindElement(arg, arg, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ if (xml)
+ {
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<Name>%s</Name>\n"
+ "</Node>\n", xmlid ++, name, name);
+ }
+ else
+ {
+ fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
+ target ? target : "", name, targetattr);
+ write_description(out, description, "", 1);
+ fprintf(out, "\">%s</a></li>\n", name);
+ }
+
+ arg = find_public(arg, doc, "variable");
+ }
+
+ if (xml)
+ fputs("</Subnodes></Node>\n", out);
+ else
+ fputs("</ul></li>\n", out);
+ }
+
+ /*
+ * Enumerations/constants...
+ */
+
+ if ((scut = find_public(doc, doc, "enumeration")) != NULL)
+ {
+ if (xml)
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>ENUMERATIONS</Anchor>\n"
+ "<Name>Constants</Name>\n"
+ "<Subnodes>\n", xmlid ++);
+ else
+ fprintf(out, "<li><a href=\"%s#ENUMERATIONS\"%s>Constants</a>"
+ "<ul class=\"code\">\n", target ? target : "", targetattr);
+
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ if (xml)
+ {
+ fprintf(out, "<Node id=\"%d\">\n"
+ "<Path>Documentation/index.html</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<Name>%s</Name>\n"
+ "</Node>\n", xmlid ++, name, name);
+ }
+ else
+ {
+ fprintf(out, "\t<li><a href=\"%s#%s\"%s title=\"",
+ target ? target : "", name, targetattr);
+ write_description(out, description, "", 1);
+ fprintf(out, "\">%s</a></li>\n", name);
+ }
+
+ scut = find_public(scut, doc, "enumeration");
+ }
+
+ if (xml)
+ fputs("</Subnodes></Node>\n", out);
+ else
+ fputs("</ul></li>\n", out);
+ }
+
+ /*
+ * Close out the HTML table-of-contents list as needed...
+ */
+
+ if (!xml)
+ fputs("</ul>\n", out);
+}
+
+
+/*
+ * 'write_tokens()' - Write <Token> nodes for all APIs.
+ */
+
+static void
+write_tokens(FILE *out, /* I - Output file */
+ mxml_node_t *doc, /* I - Document */
+ const char *path) /* I - Path to help file */
+{
+ mxml_node_t *function, /* Current function */
+ *scut, /* Struct/class/union/typedef */
+ *arg, /* Current argument */
+ *description, /* Description of function/var */
+ *type, /* Type node */
+ *node; /* Current child node */
+ const char *name, /* Name of function/type */
+ *cename, /* Current class/enum name */
+ *defval; /* Default value for argument */
+ char prefix; /* Prefix for declarations */
+
+
+ /*
+ * Classes...
+ */
+
+ if ((scut = find_public(doc, doc, "class")) != NULL)
+ {
+ while (scut)
+ {
+ cename = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<Token>\n"
+ "<Path>Documentation/%s</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<TokenIdentifier>//apple_ref/cpp/cl/%s</TokenIdentifier>\n"
+ "<Abstract>", path, cename, cename);
+ write_description(out, description, "", 1);
+ fputs("</Abstract>\n"
+ "</Token>\n", out);
+
+ if ((function = find_public(scut, scut, "function")) != NULL)
+ {
+ while (function)
+ {
+ name = mxmlElementGetAttr(function, "name");
+ description = mxmlFindElement(function, function, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<Token>\n"
+ "<Path>Documentation/%s</Path>\n"
+ "<Anchor>%s.%s</Anchor>\n"
+ "<TokenIdentifier>//apple_ref/cpp/clm/%s/%s", path,
+ cename, name, cename, name);
+
+ arg = mxmlFindElement(function, function, "returnvalue", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ if (arg && (type = mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST)) != NULL)
+ {
+ for (node = type->child; node; node = node->next)
+ fputs(node->value.text.string, out);
+ }
+ else if (strcmp(cename, name) && strcmp(cename, name + 1))
+ fputs("void", out);
+
+ fputs("/", out);
+
+ for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
+ MXML_DESCEND_FIRST), prefix = '(';
+ arg;
+ arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
+ MXML_NO_DESCEND), prefix = ',')
+ {
+ type = mxmlFindElement(arg, arg, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ putc(prefix, out);
+
+ for (node = type->child; node; node = node->next)
+ fputs(node->value.text.string, out);
+
+ fputs(mxmlElementGetAttr(arg, "name"), out);
+ }
+
+ if (prefix == '(')
+ fputs("(void", out);
+
+ fputs(")</TokenIdentifier>\n"
+ "<Abstract>", out);
+ write_description(out, description, "", 1);
+ fputs("</Abstract>\n"
+ "<Declaration>", out);
+
+ arg = mxmlFindElement(function, function, "returnvalue", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ if (arg)
+ write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_XML);
+ else if (strcmp(cename, name) && strcmp(cename, name + 1))
+ fputs("void ", out);
+
+ fputs(name, out);
+
+ for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
+ MXML_DESCEND_FIRST), prefix = '(';
+ arg;
+ arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
+ MXML_NO_DESCEND), prefix = ',')
+ {
+ type = mxmlFindElement(arg, arg, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ putc(prefix, out);
+ if (prefix == ',')
+ putc(' ', out);
+
+ if (type->child)
+ write_element(out, doc, type, OUTPUT_XML);
+
+ fputs(mxmlElementGetAttr(arg, "name"), out);
+ if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
+ fprintf(out, " %s", defval);
+ }
+
+ if (prefix == '(')
+ fputs("(void);", out);
+ else
+ fputs(");", out);
+
+ fputs("</Declaration>\n"
+ "</Token>\n", out);
+
+ function = find_public(function, doc, "function");
+ }
+ }
+ scut = find_public(scut, doc, "class");
+ }
+ }
+
+ /*
+ * Functions...
+ */
+
+ if ((function = find_public(doc, doc, "function")) != NULL)
+ {
+ while (function)
+ {
+ name = mxmlElementGetAttr(function, "name");
+ description = mxmlFindElement(function, function, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<Token>\n"
+ "<Path>Documentation/%s</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<TokenIdentifier>//apple_ref/c/func/%s</TokenIdentifier>\n"
+ "<Abstract>", path, name, name);
+ write_description(out, description, "", 1);
+ fputs("</Abstract>\n"
+ "<Declaration>", out);
+
+ arg = mxmlFindElement(function, function, "returnvalue", NULL,
+ NULL, MXML_DESCEND_FIRST);
+
+ if (arg)
+ write_element(out, doc, mxmlFindElement(arg, arg, "type", NULL,
+ NULL, MXML_DESCEND_FIRST),
+ OUTPUT_XML);
+ else // if (strcmp(cname, name) && strcmp(cname, name + 1))
+ fputs("void ", out);
+
+ fputs(name, out);
+
+ for (arg = mxmlFindElement(function, function, "argument", NULL, NULL,
+ MXML_DESCEND_FIRST), prefix = '(';
+ arg;
+ arg = mxmlFindElement(arg, function, "argument", NULL, NULL,
+ MXML_NO_DESCEND), prefix = ',')
+ {
+ type = mxmlFindElement(arg, arg, "type", NULL, NULL,
+ MXML_DESCEND_FIRST);
+
+ putc(prefix, out);
+ if (prefix == ',')
+ putc(' ', out);
+
+ if (type->child)
+ write_element(out, doc, type, OUTPUT_XML);
+
+ fputs(mxmlElementGetAttr(arg, "name"), out);
+ if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
+ fprintf(out, " %s", defval);
+ }
+
+ if (prefix == '(')
+ fputs("(void);", out);
+ else
+ fputs(");", out);
+
+ fputs("</Declaration>\n"
+ "</Token>\n", out);
+
+ function = find_public(function, doc, "function");
+ }
+ }
+
+ /*
+ * Data types...
+ */
+
+ if ((scut = find_public(doc, doc, "typedef")) != NULL)
+ {
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<Token>\n"
+ "<Path>Documentation/%s</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<TokenIdentifier>//apple_ref/c/tdef/%s</TokenIdentifier>\n"
+ "<Abstract>", path, name, name);
+ write_description(out, description, "", 1);
+ fputs("</Abstract>\n"
+ "</Token>\n", out);
+
+ scut = find_public(scut, doc, "typedef");
+ }
+ }
+
+ /*
+ * Structures...
+ */
+
+ if ((scut = find_public(doc, doc, "struct")) != NULL)
+ {
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<Token>\n"
+ "<Path>Documentation/%s</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<TokenIdentifier>//apple_ref/c/tag/%s</TokenIdentifier>\n"
+ "<Abstract>", path, name, name);
+ write_description(out, description, "", 1);
+ fputs("</Abstract>\n"
+ "</Token>\n", out);
+
+ scut = find_public(scut, doc, "struct");
+ }
+ }
+
+ /*
+ * Unions...
+ */
+
+ if ((scut = find_public(doc, doc, "union")) != NULL)
+ {
+ while (scut)
+ {
+ name = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<Token>\n"
+ "<Path>Documentation/%s</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<TokenIdentifier>//apple_ref/c/tag/%s</TokenIdentifier>\n"
+ "<Abstract>", path, name, name);
+ write_description(out, description, "", 1);
+ fputs("</Abstract>\n"
+ "</Token>\n", out);
+
+ scut = find_public(scut, doc, "union");
+ }
+ }
+
+ /*
+ * Globals variables...
+ */
+
+ if ((arg = find_public(doc, doc, "variable")) != NULL)
+ {
+ while (arg)
+ {
+ name = mxmlElementGetAttr(arg, "name");
+ description = mxmlFindElement(arg, arg, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<Token>\n"
+ "<Path>Documentation/%s</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<TokenIdentifier>//apple_ref/c/data/%s</TokenIdentifier>\n"
+ "<Abstract>", path, name, name);
+ write_description(out, description, "", 1);
+ fputs("</Abstract>\n"
+ "</Token>\n", out);
+
+ arg = find_public(arg, doc, "variable");
+ }
+ }
+
+ /*
+ * Enumerations/constants...
+ */
+
+ if ((scut = find_public(doc, doc, "enumeration")) != NULL)
+ {
+ while (scut)
+ {
+ cename = mxmlElementGetAttr(scut, "name");
+ description = mxmlFindElement(scut, scut, "description",
+ NULL, NULL, MXML_DESCEND_FIRST);
+
+ fprintf(out, "<Token>\n"
+ "<Path>Documentation/%s</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<TokenIdentifier>//apple_ref/c/tag/%s</TokenIdentifier>\n"
+ "<Abstract>", path, cename, cename);
+ write_description(out, description, "", 1);
+ fputs("</Abstract>\n"
+ "</Token>\n", out);
+
+ for (arg = mxmlFindElement(scut, scut, "constant", NULL, NULL,
+ MXML_DESCEND_FIRST);
+ arg;
+ arg = mxmlFindElement(arg, scut, "constant", NULL, NULL,
+ MXML_NO_DESCEND))
+ {
+ name = mxmlElementGetAttr(arg, "name");
+ description = mxmlFindElement(arg, arg, "description", NULL,
+ NULL, MXML_DESCEND_FIRST);
+ fprintf(out, "<Token>\n"
+ "<Path>Documentation/%s</Path>\n"
+ "<Anchor>%s</Anchor>\n"
+ "<TokenIdentifier>//apple_ref/c/econst/%s</TokenIdentifier>\n"
+ "<Abstract>", path, cename, name);
+ write_description(out, description, "", 1);
+ fputs("</Abstract>\n"
+ "</Token>\n", out);
+ }
+
+ scut = find_public(scut, doc, "enumeration");
+ }
+ }
+}
+
+
+/*
+ * 'ws_cb()' - Whitespace callback for saving.
+ */
+
+static const char * /* O - Whitespace string or NULL for none */
+ws_cb(mxml_node_t *node, /* I - Element node */
+ int where) /* I - Where value */
+{
+ const char *name; /* Name of element */
+ int depth; /* Depth of node */
+ static const char *spaces = " ";
+ /* Whitespace (40 spaces) for indent */
+
+
+ name = node->value.element.name;
+
+ switch (where)
+ {
+ case MXML_WS_BEFORE_CLOSE :
+ if (strcmp(name, "argument") &&
+ strcmp(name, "class") &&
+ strcmp(name, "constant") &&
+ strcmp(name, "enumeration") &&
+ strcmp(name, "function") &&
+ strcmp(name, "mxmldoc") &&
+ strcmp(name, "namespace") &&
+ strcmp(name, "returnvalue") &&
+ strcmp(name, "struct") &&
+ strcmp(name, "typedef") &&
+ strcmp(name, "union") &&
+ strcmp(name, "variable"))
+ return (NULL);
+
+ for (depth = -4; node; node = node->parent, depth += 2);
+ if (depth > 40)
+ return (spaces);
+ else if (depth < 2)
+ return (NULL);
+ else
+ return (spaces + 40 - depth);
+
+ case MXML_WS_AFTER_CLOSE :
+ return ("\n");
+
+ case MXML_WS_BEFORE_OPEN :
+ for (depth = -4; node; node = node->parent, depth += 2);
+ if (depth > 40)
+ return (spaces);
+ else if (depth < 2)
+ return (NULL);
+ else
+ return (spaces + 40 - depth);
+
+ default :
+ case MXML_WS_AFTER_OPEN :
+ if (strcmp(name, "argument") &&
+ strcmp(name, "class") &&
+ strcmp(name, "constant") &&
+ strcmp(name, "enumeration") &&
+ strcmp(name, "function") &&
+ strcmp(name, "mxmldoc") &&
+ strcmp(name, "namespace") &&
+ strcmp(name, "returnvalue") &&
+ strcmp(name, "struct") &&
+ strcmp(name, "typedef") &&
+ strcmp(name, "union") &&
+ strcmp(name, "variable") &&
+ strncmp(name, "?xml", 4))
+ return (NULL);
+ else
+ return ("\n");
+ }
+}
+
+
+/*
+ * End of "$Id: mxmldoc.c 440 2011-08-11 18:51:26Z mike $".
+ */