diff options
Diffstat (limited to 'xml/mxmldoc.c')
-rw-r--r-- | xml/mxmldoc.c | 5809 |
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\"> DEPRECATED </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\"> %s </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("&", out); + else if (*ptr == '<') + fputs("<", out); + else if (*ptr == '>') + fputs(">", out); + else if (*ptr == '\"') + fputs(""", 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(" ", 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 ", 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, " %s:<br>\n", scopes[i]); + } + + fputs(" ", 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, " %s:<br>\n", scopes[i]); + } + + name = mxmlElementGetAttr(function, "name"); + + fputs(" ", 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("&", out); + else if (*s == '<') + fputs("<", out); + else if (*s == '>') + fputs(">", out); + else if (*s == '\"') + fputs(""", 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(" ", 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 $". + */ |