📄 mxmldoc.c
字号:
/* * "$Id: mxmldoc.c 275 2007-04-27 00:49:03Z mike $" * * Documentation generator using Mini-XML, a small XML-like file parsing * library. * * Copyright 2003-2007 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. * load_cb() - Set the type of child nodes. * 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_element() - Write an elements text nodes. * write_html() - Write HTML documentation. * write_man() - Write manpage documentation. * 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"#include <time.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: * * <?xml version="1.0"?> * <mxmldoc xmlns="http://www.easysw.com" * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" * xsi:schemaLocation="http://www.easysw.com/~mike/mxml/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_MAN 2 /* Output nroff/man *//* * 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 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, int mode);static void write_element(mxml_node_t *doc, mxml_node_t *element, int mode);static void write_html(const char *section, const char *title, const char *intro, mxml_node_t *doc);static void write_man(const char *man_name, const char *section, const char *title, const char *intro, mxml_node_t *doc);static void write_string(const char *s, int mode);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 */ const char *name; /* Name of manpage */ int update; /* Updated XML file */ int mode; /* Output mode */ /* * Check arguments... */ name = NULL; section = NULL; title = NULL; introfile = NULL; xmlfile = NULL; update = 0; doc = NULL; mxmldoc = NULL; mode = OUTPUT_HTML; for (i = 1; i < argc; i ++) if (!strcmp(argv[i], "--help")) { /* * Show help... */ 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 (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, 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... */ 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", introfile, mxmldoc); break; case OUTPUT_MAN : /* * Write manpage documentation... */ write_man(name, section, title, introfile, mxmldoc); 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -