/* * "$Id$" * * Documentation generator using Mini-XML, a small XML-like file parsing * library. * * Copyright 2003-2006 by Michael Sweet. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Contents: * * main() - Main entry for test program. * add_variable() - Add a variable or argument. * get_comment_info() - Get info from comment. * get_text() - Get the text for a node. * new_documentation() - Create a new documentation tree. * 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_documentation() - Write HTML documentation. * write_element() - Write an elements text nodes. * write_string() - Write a string, quoting XHTML special chars * as needed... * ws_cb() - Whitespace callback for saving. */ /* * Include necessary headers... */ #include "config.h" #include "mxml.h" /* * This program scans source and header files and produces public API * documentation for code that conforms to the CUPS Configuration * Management Plan (CMP) coding standards. Please see the following web * page for details: * * http://www.cups.org/cmp.html * * Using Mini-XML, this program creates and maintains an XML representation * of the public API code documentation which can then be converted to HTML * as desired. The following is a poor-man's schema: * * * * * [optional...] * * descriptive text * * * * descriptive text * ... * * * * descriptive text * type string * * * * descriptive text * * descriptive text * type string * * * descriptive text * type string * * function names separated by spaces * * * * descriptive text * type string * * * * descriptive text * ... * ... * * * * descriptive text * ... * * * * descriptive text * ... * ... * ... * ... * ... * * * */ /* * 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 */ /* * 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_node_t *new_documentation(mxml_node_t **mxmldoc); 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(mxml_node_t *description); static void write_documentation(const char *section, const char *title, const char *intro, mxml_node_t *doc); static void write_element(mxml_node_t *doc, mxml_node_t *element); static void write_string(const char *s); 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 *section; /* Section/keywords of documentation */ const char *title; /* Title of documentation */ const char *introfile; /* Introduction file */ const char *xmlfile; /* XML file */ int update; /* Updated XML file */ /* * Check arguments... */ section = NULL; title = NULL; introfile = NULL; xmlfile = NULL; update = 0; doc = NULL; mxmldoc = NULL; for (i = 1; i < argc; i ++) if (!strcmp(argv[i], "--intro") && !introfile) { /* * Set intro file... */ i ++; if (i < argc) introfile = argv[i]; else usage(NULL); } 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 (argv[i][0] == '-') { /* * Unknown/bad option... */ usage(argv[i]); } else { /* * Process XML or source file... */ len = 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, MXML_NO_CALLBACK); 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 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... */ 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); } } /* * Write HTML documentation... */ write_documentation(section, title ? title : "Documentation", introfile, mxmldoc); /* * 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 || !description->child) continue; /* * Look for @private@ in the comment text... */ for (comment = description->child; comment; comment = comment->next) if (strstr(comment->value.text.string, "@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 (" DEPRECATED "); 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), " %s ", 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->value.text.whitespace) *ptr++ = ' '; len = strlen(current->value.text.string); if (len > (end - ptr)) len = end - ptr; memcpy(ptr, current->value.text.string, len); ptr += len; } *ptr = '\0'; return (buffer); } /* * '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 = mxmlNewElement(NULL, "?xml version=\"1.0\"?"); *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.easysw.com/~mike/mxml/mxmldoc.xsd"); return (doc); } /* * '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, /* node */ *constant, /* node */ *enumeration, /* node */ *function, /* node */ *fstructclass, /* function struct/class node */ *structclass, /* or node */ *typedefnode, /* node */ *variable, /* or node */ *returnvalue, /* node */ *type, /* node */ *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 fputs(" duplicating comment for typedef...\n", stderr); #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 to %s...\n", 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 fputs(" duplicating comment for typedef...\n", stderr); #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 fputs(" adding comment to enumeration...\n", stderr); #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; 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, " function=%p, type=%p\n", function, type); #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 function 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; for (node = node->next; node; node = node->next) if (strcmp(node->value.text.string, "*")) break; if (node) { mxmlElementSetAttr(typedefnode, "name", node->value.text.string); sort_node(tree, typedefnode); 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; } } 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 */ ch = type->last_child->value.text.string[0]; 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, last comment %p...\n", comment->child, comment->last_child); #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) { description = mxmlNewElement(variable, "description"); #ifdef DEBUG fputs(" adding comment to variable...\n", stderr); #endif /* DEBUG */ update_comment(variable, mxmlNewText(description, 0, buffer)); variable = NULL; } else if (constant) { description = mxmlNewElement(constant, "description"); #ifdef DEBUG fputs(" adding comment to constant...\n", stderr); #endif /* DEBUG */ update_comment(constant, mxmlNewText(description, 0, buffer)); constant = NULL; } else if (typedefnode) { description = mxmlNewElement(typedefnode, "description"); #ifdef DEBUG fprintf(stderr, " adding comment to typedef %s...\n", mxmlElementGetAttr(typedefnode, "name")); #endif /* DEBUG */ update_comment(typedefnode, 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 fputs(" adding comment to parent...\n", stderr); #endif /* DEBUG */ 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, last comment %p...\n", comment->child, comment->last_child); #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) { description = mxmlNewElement(variable, "description"); #ifdef DEBUG fputs(" adding comment to variable...\n", stderr); #endif /* DEBUG */ update_comment(variable, mxmlNewText(description, 0, buffer)); variable = NULL; } else if (constant) { description = mxmlNewElement(constant, "description"); #ifdef DEBUG fputs(" adding comment to constant...\n", stderr); #endif /* DEBUG */ update_comment(constant, mxmlNewText(description, 0, buffer)); constant = NULL; } else if (typedefnode) { description = mxmlNewElement(typedefnode, "description"); #ifdef DEBUG fprintf(stderr, " adding comment to typedef %s...\n", mxmlElementGetAttr(typedefnode, "name")); #endif /* DEBUG */ update_comment(typedefnode, 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 fputs(" adding comment to parent...\n", stderr); #endif /* DEBUG */ 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, last comment %p...\n", comment->child, comment->last_child); #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) { description = mxmlNewElement(variable, "description"); #ifdef DEBUG fputs(" adding comment to variable...\n", stderr); #endif /* DEBUG */ update_comment(variable, mxmlNewText(description, 0, buffer)); variable = NULL; } else if (constant) { description = mxmlNewElement(constant, "description"); #ifdef DEBUG fputs(" adding comment to constant...\n", stderr); #endif /* DEBUG */ update_comment(constant, mxmlNewText(description, 0, buffer)); constant = NULL; } else if (typedefnode) { description = mxmlNewElement(typedefnode, "description"); #ifdef DEBUG fprintf(stderr, " adding comment to typedef %s...\n", mxmlElementGetAttr(typedefnode, "name")); #endif /* DEBUG */ update_comment(typedefnode, 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 fputs(" adding comment to parent...\n", stderr); #endif /* DEBUG */ 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 fputs(" adding comment to returnvalue...\n", stderr); #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 fputs(" adding comment to function...\n", stderr); #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; /* * 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 + directory 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(" --title title Set documentation title"); puts(" --intro introfile.html Set introduction file"); exit(1); } /* * 'write_description()' - Write the description text. */ static void write_description( mxml_node_t *description) /* I - Description node */ { char text[10240], /* Text for description */ *ptr; /* Pointer into text */ if (!description) return; get_text(description, text, sizeof(text)); for (ptr = text; *ptr; ptr ++) { if (*ptr == '@' && (!strncmp(ptr + 1, "deprecated@", 11) || !strncmp(ptr + 1, "since ", 6))) { ptr ++; while (*ptr && *ptr != '@') ptr ++; if (!*ptr) return; } else if (*ptr == '&') fputs("&", stdout); else if (*ptr == '<') fputs("<", stdout); else if (*ptr == '>') fputs(">", stdout); else if (*ptr == '\"') fputs(""", stdout); 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(" ", stdout); } else printf("&#x%x;", ch); } else putchar(*ptr); } } /* * 'write_documentation()' - Write HTML documentation. */ static void write_documentation( const char *section, /* I - Section */ const char *title, /* I - Title */ const char *introfile, /* I - Intro file */ mxml_node_t *doc) /* I - XML documentation */ { int i; /* Looping var */ FILE *fp; /* File */ char line[8192]; /* Line from 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 */ *cname, /* Class name */ *defval, /* Default value */ *parent; /* Parent class */ int inscope; /* Variable/method scope */ char prefix; /* Prefix character */ static const char * const scopes[] = /* Scope strings */ { "private", "protected", "public" }; /* * Standard header... */ puts("\n" ""); if (section) printf("\n", section); printf("\n" "\t%s\n", title); if (section) printf("\t\n", section); puts("\t\n" "\t\n" "\n" ""); /* * Intro... */ if (introfile && (fp = fopen(introfile, "r")) != NULL) { /* * Insert intro file before contents... */ while (fgets(line, sizeof(line), fp)) fputs(line, stdout); fclose(fp); } /* * Table of contents... */ puts("

Contents

"); puts("
    "); if (find_public(doc, doc, "class")) puts("\t
  • Classes
  • "); if (find_public(doc, doc, "enumeration")) puts("\t
  • Enumerations
  • "); if (find_public(doc, doc, "function")) puts("\t
  • Functions
  • "); if (find_public(doc, doc, "struct")) puts("\t
  • Structures
  • "); if (find_public(doc, doc, "typedef")) puts("\t
  • Types
  • "); if (find_public(doc, doc, "union")) puts("\t
  • Unions
  • "); if (find_public(doc, doc, "variable")) puts("\t
  • Variables
  • "); puts("
"); /* * List of classes... */ if (find_public(doc, doc, "class")) { puts("\n" "

Classes

\n" "
    "); for (scut = find_public(doc, doc, "class"); scut; scut = find_public(scut, doc, "class")) { name = mxmlElementGetAttr(scut, "name"); printf("\t
  • %s %s
  • \n", name, name, get_comment_info(mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST))); } puts("
"); 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("\n" "

%s%s

\n", get_comment_info(description), cname, cname); if (description) { fputs("

Description

\n" "

", stdout); write_description(description); puts("

"); } printf("

Definition

\n" "
\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(doc, mxmlFindElement(arg, arg, "type", NULL,
                                             NULL, MXML_DESCEND_FIRST));
	  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(doc, mxmlFindElement(arg, arg, "type", NULL,
                                               NULL, MXML_DESCEND_FIRST));
	    putchar(' ');
	  }
	  else if (strcmp(cname, name) && strcmp(cname, name + 1))
	    fputs("void ", stdout);

	  printf("%s", 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);

	    putchar(prefix);
	    if (prefix == ',')
	      putchar(' ');

	    if (type->child)
	    {
	      write_element(doc, type);
	      putchar(' ');
	    }
	    fputs(mxmlElementGetAttr(arg, "name"), stdout);
            if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
	      printf(" %s", defval);
	  }

	  if (prefix == '(')
	    puts("(void);");
	  else
	    puts(");");
	}
      }

      puts("};\n
\n" "

Members

\n" "
\n" "\n" ""); 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); printf(""); } 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"); description = mxmlFindElement(function, function, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(""); } puts("
NameDescription
%s %s", mxmlElementGetAttr(arg, "name"), get_comment_info(description)); write_description(description); puts("
%s() %s", cname, name, name, get_comment_info(description)); write_description(description); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) { fputs("\nReturns: ", stdout); write_element(NULL, mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST)); } puts("
"); } } /* * List of enumerations... */ if (find_public(doc, doc, "enumeration")) { puts("\n" "

Enumerations

\n" "
    "); for (scut = find_public(doc, doc, "enumeration"); scut; scut = find_public(scut, doc, "enumeration")) { name = mxmlElementGetAttr(scut, "name"); printf("\t
  • %s %s
  • \n", name, name, get_comment_info(mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST))); } puts("
"); 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("\n" "

%s%s

\n", get_comment_info(description), name, name); if (description) { fputs("

Description

\n" "

", stdout); write_description(description); puts("

"); } puts("

Values

\n" "
\n" "\n" ""); 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(""); } puts("
NameDescription
%s %s", mxmlElementGetAttr(arg, "name"), get_comment_info(description)); write_description(description); puts("
"); } } /* * List of functions... */ if (find_public(doc, doc, "function")) { puts("\n" "

Functions

\n" "
    "); for (function = find_public(doc, doc, "function"); function; function = find_public(function, doc, "function")) { name = mxmlElementGetAttr(function, "name"); printf("\t
  • %s() %s
  • \n", name, name, get_comment_info(mxmlFindElement(function, function, "description", NULL, NULL, MXML_DESCEND_FIRST))); } puts("
"); 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("\n" "

%s%s()

\n", get_comment_info(description), name, name); if (description) { fputs("

Description

\n" "

", stdout); write_description(description); puts("

"); } puts("

Syntax

\n" "
");

      arg = mxmlFindElement(function, function, "returnvalue", NULL,
                            NULL, MXML_DESCEND_FIRST);

      if (arg)
	write_element(doc, mxmlFindElement(arg, arg, "type", NULL,
                                           NULL, MXML_DESCEND_FIRST));
      else
	fputs("void", stdout);

      printf("\n%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(doc, type);
	  putchar(' ');
	}
	fputs(mxmlElementGetAttr(arg, "name"), stdout);
        if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
	  printf(" %s", defval);
      }

      if (prefix == '(')
	puts("(void);\n
"); else puts(");\n"); puts("

Arguments

"); if (prefix == '(') puts("

None.

"); else { puts("
\n" "\n" ""); for (arg = mxmlFindElement(function, function, "argument", NULL, NULL, MXML_DESCEND_FIRST); arg; arg = mxmlFindElement(arg, function, "argument", NULL, NULL, MXML_NO_DESCEND)) { printf(""); } puts("
NameDescription
%s", mxmlElementGetAttr(arg, "name")); write_element(NULL, mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST)); puts("
"); } puts("

Returns

"); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (!arg) puts("

Nothing.

"); else { fputs("

", stdout); write_element(NULL, mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST)); puts("

"); } } } /* * List of structures... */ if (find_public(doc, doc, "struct")) { puts("\n" "

Structures

\n" "
    "); for (scut = find_public(doc, doc, "struct"); scut; scut = find_public(scut, doc, "struct")) { name = mxmlElementGetAttr(scut, "name"); printf("\t
  • %s %s
  • \n", name, name, get_comment_info(mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST))); } puts("
"); 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("\n" "

%s%s

\n", get_comment_info(description), cname, cname); if (description) { fputs("

Description

\n" "

", stdout); write_description(description); puts("

"); } printf("

Definition

\n" "
\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(doc, mxmlFindElement(arg, arg, "type", NULL,
                                           NULL, MXML_DESCEND_FIRST));
	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(doc, mxmlFindElement(arg, arg, "type", NULL,
                                             NULL, MXML_DESCEND_FIRST));
	  putchar(' ');
	}
	else if (strcmp(cname, name) && strcmp(cname, name + 1))
	  fputs("void ", stdout);

	printf("%s", 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);

	  putchar(prefix);
	  if (prefix == ',')
	    putchar(' ');

	  if (type->child)
	  {
	    write_element(doc, type);
	    putchar(' ');
	  }
	  fputs(mxmlElementGetAttr(arg, "name"), stdout);
          if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
	    printf(" %s", defval);
	}

	if (prefix == '(')
	  puts("(void);");
	else
	  puts(");");
      }

      puts("};\n
\n" "

Members

\n" "
\n" "\n" ""); 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); printf(""); } 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"); description = mxmlFindElement(function, function, "description", NULL, NULL, MXML_DESCEND_FIRST); printf(""); } puts("
NameDescription
%s %s", mxmlElementGetAttr(arg, "name"), get_comment_info(description)); write_description(description); puts("
%s() %s", cname, name, name, get_comment_info(description)); write_description(description); arg = mxmlFindElement(function, function, "returnvalue", NULL, NULL, MXML_DESCEND_FIRST); if (arg) { fputs("\nReturns: ", stdout); write_element(NULL, mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST)); } puts("
"); } } /* * List of types... */ if (find_public(doc, doc, "typedef")) { puts("\n" "

Types

\n" "
    "); for (scut = find_public(doc, doc, "typedef"); scut; scut = find_public(scut, doc, "typedef")) { name = mxmlElementGetAttr(scut, "name"); printf("\t
  • %s %s
  • \n", name, name, get_comment_info(mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST))); } puts("
"); 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("\n" "

%s%s

\n", get_comment_info(description), name, name); if (description) { fputs("

Description

\n" "

", stdout); write_description(description); puts("

"); } fputs("

Definition

\n" "
\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(' ');

	  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))
	  {
            printf("");
            write_string(type->value.text.string);
	    printf("");
	  }
	  else
            write_string(type->value.text.string);
        }

      if (type)
      {
       /*
        * Output function type...
	*/

        printf(" (*%s", name);

	for (type = type->next->next; type; type = type->next)
	{
	  if (type->value.text.whitespace)
	    putchar(' ');

	  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))
	  {
            printf("");
            write_string(type->value.text.string);
	    printf("");
	  }
	  else
            write_string(type->value.text.string);
        }

        puts(";");
      }
      else
	printf(" %s;\n", name);

      puts("
"); } } /* * List of unions... */ if (find_public(doc, doc, "union")) { puts("\n" "

Unions

\n" "
    "); for (scut = find_public(doc, doc, "union"); scut; scut = find_public(scut, doc, "union")) { name = mxmlElementGetAttr(scut, "name"); printf("\t
  • %s %s
  • \n", name, name, get_comment_info(mxmlFindElement(scut, scut, "description", NULL, NULL, MXML_DESCEND_FIRST))); } puts("
"); 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("\n" "

%s%s

\n", get_comment_info(description), name, name); if (description) { fputs("

Description

\n" "

", stdout); write_description(description); puts("

"); } printf("

Definition

\n" "
\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(doc, mxmlFindElement(arg, arg, "type", NULL,
                                           NULL, MXML_DESCEND_FIRST));
	printf(" %s;\n", mxmlElementGetAttr(arg, "name"));
      }

      puts("};\n
\n" "

Members

\n" "
\n" "\n" ""); 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); printf(""); } puts("
NameDescription
%s %s", mxmlElementGetAttr(arg, "name"), get_comment_info(description)); write_description(description); puts("
"); } } /* * Variables... */ if (find_public(doc, doc, "variable")) { puts("\n" "

Variables

\n" "
    "); for (arg = find_public(doc, doc, "variable"); arg; arg = find_public(arg, doc, "variable")) { name = mxmlElementGetAttr(arg, "name"); printf("\t
  • %s %s
  • \n", name, name, get_comment_info(mxmlFindElement(arg, arg, "description", NULL, NULL, MXML_DESCEND_FIRST))); } puts("
"); 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("\n" "

%s%s

\n", get_comment_info(description), name, name); if (description) { fputs("

Description

\n" "

", stdout); write_description(description); puts("

"); } puts("

Definition

\n" "
");

      write_element(doc, mxmlFindElement(arg, arg, "type", NULL,
                                         NULL, MXML_DESCEND_FIRST));
      printf(" %s", mxmlElementGetAttr(arg, "name"));
      if ((defval = mxmlElementGetAttr(arg, "default")) != NULL)
	printf(" %s", defval);
      puts(";\n
"); } } /* * Standard footer... */ puts("\n" ""); } /* * 'write_element()' - Write an element's text nodes. */ static void write_element(mxml_node_t *doc, /* I - Document tree */ mxml_node_t *element) /* I - Element to write */ { 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) putchar(' '); if (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)) { printf(""); write_string(node->value.text.string); printf(""); } else write_string(node->value.text.string); } } /* * 'write_string()' - Write a string, quoting XHTML special chars as needed... */ static void write_string(const char *s) /* I - String to write */ { while (*s) { if (*s == '&') fputs("&", stdout); else if (*s == '<') fputs("<", stdout); else if (*s == '>') fputs(">", stdout); else if (*s == '\"') fputs(""", stdout); 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(" ", stdout); } else printf("&#x%x;", ch); } else putchar(*s); s ++; } } /* * '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")) return (NULL); else return ("\n"); } } /* * End of "$Id$". */