diff --git a/index.html b/index.html index 690a1f6..ef05ac5 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,8 @@ href="../index.html">Back to Home Page ]

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

+View Change Log | +Rate/Make Comments ]

Introduction

diff --git a/mxml-file.c b/mxml-file.c index 74907ae..108d309 100644 --- a/mxml-file.c +++ b/mxml-file.c @@ -1,5 +1,5 @@ /* - * "$Id: mxml-file.c,v 1.7 2003/06/04 17:37:23 mike Exp $" + * "$Id: mxml-file.c,v 1.8 2003/06/04 21:18:59 mike Exp $" * * File loading code for mini-XML, a small XML-like file parsing library. * @@ -22,6 +22,7 @@ * mxml_parse_element() - Parse an element for any attributes... * mxml_write_node() - Save an XML node to a file. * mxml_write_string() - Write a string, escaping & and < as needed. + * mxml_write_ws() - Do whitespace callback... */ /* @@ -39,6 +40,8 @@ static int mxml_parse_element(mxml_node_t *node, FILE *fp); static int mxml_write_node(mxml_node_t *node, FILE *fp, int (*cb)(mxml_node_t *, int), int col); static int mxml_write_string(const char *s, FILE *fp); +static int mxml_write_ws(mxml_node_t *node, FILE *fp, + int (*cb)(mxml_node_t *, int), int ws, int col); /* @@ -702,7 +705,6 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ int col) /* I - Current column */ { int i; /* Looping var */ - int ch; /* Whitespace character */ int n; /* Chars written */ mxml_attr_t *attr; /* Current attribute */ @@ -716,17 +718,7 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ switch (node->type) { case MXML_ELEMENT : - if (cb && (ch = (*cb)(node, MXML_SAVE_OPEN_TAG)) != 0) - { - if (putc(ch, fp) < 0) - return (-1); - else if (ch == '\n') - col = 0; - else if (ch == '\t') - col += 8; - else - col ++; - } + col = mxml_write_ws(node, fp, cb, MXML_WS_BEFORE_OPEN, col); if ((n = fprintf(fp, "<%s", node->value.element.name)) < 0) return (-1); @@ -776,16 +768,22 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ else col ++; + col = mxml_write_ws(node, fp, cb, MXML_WS_AFTER_OPEN, col); + if ((col = mxml_write_node(node->child, fp, cb, col)) < 0) return (-1); if (node->value.element.name[0] != '?' && node->value.element.name[0] != '!') { + col = mxml_write_ws(node, fp, cb, MXML_WS_BEFORE_CLOSE, col); + if ((n = fprintf(fp, "", node->value.element.name)) < 0) return (-1); col += n; + + col = mxml_write_ws(node, fp, cb, MXML_WS_AFTER_CLOSE, col); } } else if (node->value.element.name[0] == '!') @@ -794,22 +792,16 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ return (-1); else col ++; + + col = mxml_write_ws(node, fp, cb, MXML_WS_AFTER_OPEN, col); } else if (fputs("/>", fp) < 0) return (-1); else + { col += 2; - if (cb && (ch = (*cb)(node, MXML_SAVE_CLOSE_TAG)) != 0) - { - if (putc(ch, fp) < 0) - return (-1); - else if (ch == '\n') - col = 0; - else if (ch == '\t') - col += 8; - else - col ++; + col = mxml_write_ws(node, fp, cb, MXML_WS_AFTER_OPEN, col); } break; @@ -972,7 +964,40 @@ mxml_write_string(const char *s, /* I - String to write */ } +/* + * 'mxml_write_ws()' - Do whitespace callback... + */ + +static int /* O - New column */ +mxml_write_ws(mxml_node_t *node, /* I - Current node */ + FILE *fp, /* I - File to write to */ + int (*cb)(mxml_node_t *, int), + /* I - Callback function */ + int ws, /* I - Where value */ + int col) /* I - Current column */ +{ + int ch; /* Whitespace character */ + + + if (cb && (ch = (*cb)(node, MXML_WS_BEFORE_OPEN)) != 0) + { + if (putc(ch, fp) < 0) + return (-1); + else if (ch == '\n') + col = 0; + else if (ch == '\t') + { + col += MXML_TAB; + col = col - (col % MXML_TAB); + } + else + col ++; + } + + return (col); +} + /* - * End of "$Id: mxml-file.c,v 1.7 2003/06/04 17:37:23 mike Exp $". + * End of "$Id: mxml-file.c,v 1.8 2003/06/04 21:18:59 mike Exp $". */ diff --git a/mxml-node.c b/mxml-node.c index 3e9849c..935cd28 100644 --- a/mxml-node.c +++ b/mxml-node.c @@ -1,5 +1,5 @@ /* - * "$Id: mxml-node.c,v 1.2 2003/06/04 17:37:23 mike Exp $" + * "$Id: mxml-node.c,v 1.3 2003/06/04 21:19:00 mike Exp $" * * Node support code for mini-XML, a small XML-like file parsing library. * @@ -52,12 +52,24 @@ mxmlAdd(mxml_node_t *parent, /* I - Parent node */ mxml_node_t *child, /* I - Child node for where */ mxml_node_t *node) /* I - Node to add */ { - if (!parent) + /* + * Range check input... + */ + + if (!parent || !node) return; + /* + * Remove the node from any existing parent... + */ + if (node->parent) mxmlRemove(node); + /* + * Reset pointers... + */ + node->parent = parent; switch (where) @@ -430,5 +442,5 @@ mxml_new(mxml_node_t *parent, /* I - Parent node */ /* - * End of "$Id: mxml-node.c,v 1.2 2003/06/04 17:37:23 mike Exp $". + * End of "$Id: mxml-node.c,v 1.3 2003/06/04 21:19:00 mike Exp $". */ diff --git a/mxml-search.c b/mxml-search.c index 857472a..1372d72 100644 --- a/mxml-search.c +++ b/mxml-search.c @@ -1,5 +1,5 @@ /* - * "$Id: mxml-search.c,v 1.3 2003/06/04 16:30:40 mike Exp $" + * "$Id: mxml-search.c,v 1.4 2003/06/04 21:19:00 mike Exp $" * * Search/navigation functions for mini-XML, a small XML-like file * parsing library. @@ -123,7 +123,7 @@ mxmlWalkNext(mxml_node_t *node, /* I - Current node */ return (node->child); else if (node->next) return (node->next); - else if (node->parent != top) + else if (node->parent && node->parent != top) { node = node->parent; @@ -177,5 +177,5 @@ mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ /* - * End of "$Id: mxml-search.c,v 1.3 2003/06/04 16:30:40 mike Exp $". + * End of "$Id: mxml-search.c,v 1.4 2003/06/04 21:19:00 mike Exp $". */ diff --git a/mxml.h b/mxml.h index 8eef0fe..ba7c762 100644 --- a/mxml.h +++ b/mxml.h @@ -1,5 +1,5 @@ /* - * "$Id: mxml.h,v 1.6 2003/06/04 17:37:23 mike Exp $" + * "$Id: mxml.h,v 1.7 2003/06/04 21:19:00 mike Exp $" * * Header file for mini-XML, a small XML-like file parsing library. * @@ -38,16 +38,20 @@ * Constants... */ -# define MXML_NO_CALLBACK 0 /* Don't use a type callback */ # define MXML_WRAP 72 /* Wrap XML output at this column position */ +# define MXML_TAB 8 /* Tabs every N columns */ + +# define MXML_NO_CALLBACK 0 /* Don't use a type callback */ +# define MXML_NO_PARENT 0 /* No parent for the node */ # define MXML_DESCEND 1 /* Descend when finding/walking */ # define MXML_NO_DESCEND 0 /* Don't descend when finding/walking */ # define MXML_DESCEND_FIRST -1 /* Descend for first find */ -# define MXML_SAVE_OPEN_TAG 0 /* Callback for open tag */ -# define MXML_SAVE_CLOSE_TAG 1 /* Callback for close tag */ - +# define MXML_WS_BEFORE_OPEN 0 /* Callback for before open tag */ +# define MXML_WS_AFTER_OPEN 1 /* Callback for after open tag */ +# define MXML_WS_BEFORE_CLOSE 2 /* Callback for before close tag */ +# define MXML_WS_AFTER_CLOSE 3 /* Callback for after close tag */ # define MXML_ADD_BEFORE 0 /* Add node before specified node */ # define MXML_ADD_AFTER 1 /* Add node after specified node */ @@ -154,5 +158,5 @@ extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top, /* - * End of "$Id: mxml.h,v 1.6 2003/06/04 17:37:23 mike Exp $". + * End of "$Id: mxml.h,v 1.7 2003/06/04 21:19:00 mike Exp $". */ diff --git a/mxmldoc.c b/mxmldoc.c index 7651a5f..b75946a 100644 --- a/mxmldoc.c +++ b/mxmldoc.c @@ -1,5 +1,5 @@ /* - * "$Id: mxmldoc.c,v 1.3 2003/06/04 17:37:23 mike Exp $" + * "$Id: mxmldoc.c,v 1.4 2003/06/04 21:19:00 mike Exp $" * * Documentation generator using mini-XML, a small XML-like file parsing * library. @@ -56,18 +56,10 @@ * * * descriptive text - * + * * descriptive text * type string - * - * - * descriptive text - * type string - * - * - * descriptive text - * type string - * + * * * descriptive text * type string @@ -222,6 +214,19 @@ main(int argc, /* I - Number of command-line args */ } +/* + * 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 */ + + /* * 'scan_file()' - Scan a source file. */ @@ -231,6 +236,339 @@ 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 */ + oldstate, /* Previous state */ + oldch, /* Old character */ + braces, /* Number of braces active */ + parens; /* Number of active parenthesis */ + int ch; + char buffer[16384], + *bufptr; + mxml_node_t *comment, /* node */ + *function, /* node */ + *variable, /* or node */ + *returnvalue, /* node */ + *type, /* node */ + *description; /* node */ + static const char *states[] = + { + "STATE_NONE", + "STATE_PREPROCESSOR", + "STATE_C_COMMENT", + "STATE_CXX_COMMENT", + "STATE_STRING", + "STATE_CHARACTER", + "STATE_IDENTIFIER" + }; + + + /* + * Initialize the finite state machine... + */ + + state = STATE_NONE; + braces = 0; + parens = 0; + bufptr = buffer; + + comment = NULL; + function = NULL; + variable = NULL; + returnvalue = NULL; + type = NULL; + description = NULL; + + /* + * Read until end-of-file... + */ + + while ((ch = getc(fp)) != EOF) + { + oldstate = state; + oldch = ch; + + 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); + break; + + case '#' : /* Preprocessor */ + state = STATE_PREPROCESSOR; + break; + + case '\'' : /* Character constant */ + state = STATE_CHARACTER; + break; + + case '\"' : /* String constant */ + state = STATE_STRING; + break; + + case '{' : + braces ++; + break; + + case '}' : + if (braces > 0) + braces --; + break; + + case '(' : + parens ++; + break; + + case ')' : + if (parens > 0) + parens --; + break; + + default : /* Other */ + if (isalpha(ch) || ch == '_') + { + state = STATE_IDENTIFIER; + bufptr = buffer; + *bufptr++ = ch; + } + else if (ch == '*') + { + puts("Identifier: <<< * >>>"); + + if (type) + mxmlNewText(type, 1, "*"); + } + 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) + { + mxmlDelete(comment); + comment = NULL; + } + + if (variable) + { + description = mxmlNewElement(variable, "description"); + mxmlNewText(description, 0, buffer); + } + else + comment = mxmlNewText(MXML_NO_PARENT, 0, buffer); + + printf("C comment: <<< %s >>>\n", buffer); + + state = STATE_NONE; + break; + } + else + ungetc(ch, fp); + } + else if (ch == '\n' && bufptr < (buffer + sizeof(buffer) - 1)) + *bufptr++ = ch; + else if (!isspace(ch)) + break; + + if (ch != EOF) + ungetc(ch, fp); + + if (bufptr < (buffer + sizeof(buffer) - 1)) + *bufptr++ = '\n'; + break; + + case '/' : + if (ch == '/' && bufptr > buffer && bufptr[-1] == '*') + { + while (bufptr > buffer && + (bufptr[-1] == '*' || isspace(bufptr[-1]))) + bufptr --; + *bufptr = '\0'; + + if (comment) + { + mxmlDelete(comment); + comment = NULL; + } + + if (variable) + { + description = mxmlNewElement(variable, "description"); + mxmlNewText(description, 0, buffer); + } + else + comment = mxmlNewText(MXML_NO_PARENT, 0, buffer); + + printf("C comment: <<< %s >>>\n", buffer); + + 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') + { + *bufptr = '\0'; + + if (comment) + { + mxmlDelete(comment); + comment = NULL; + } + + if (variable) + { + description = mxmlNewElement(variable, "description"); + mxmlNewText(description, 0, buffer); + } + else + comment = mxmlNewText(MXML_NO_PARENT, 0, buffer); + + printf("C++ comment: <<< %s >>>\n", buffer); + } + else if (ch == ' ' && bufptr == buffer) + break; + else if (bufptr < (buffer + sizeof(buffer) - 1)) + *bufptr++ = ch; + break; + + case STATE_STRING : /* Inside a string constant */ + if (ch == '\\') + getc(fp); + else if (ch == '\"') + state = STATE_NONE; + break; + + case STATE_CHARACTER : /* Inside a character constant */ + if (ch == '\\') + getc(fp); + else if (ch == '\'') + state = STATE_NONE; + break; + + case STATE_IDENTIFIER : /* Inside a keyword or identifier */ + if (isalnum(ch) || ch == '_' || ch == '[' || ch == ']') + { + if (bufptr < (buffer + sizeof(buffer) - 1)) + *bufptr++ = ch; + } + else + { + ungetc(ch, fp); + *bufptr = '\0'; + printf("Identifier: <<< %s >>>\n", buffer); + + if (!braces) + { + if (!type) + type = mxmlNewElement(MXML_NO_PARENT, "type"); + + if (!function && ch == '(') + { + function = mxmlNewElement(MXML_NO_PARENT, "function"); + mxmlElementSetAttr(function, "name", buffer); + + sort_node(tree, function); + + returnvalue = mxmlNewElement(function, "returnvalue"); + + mxmlAdd(returnvalue, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, comment); + mxmlAdd(returnvalue, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type); + + comment = NULL; + type = NULL; + } + else if (function && (ch == ')' || ch == ',')) + { + /* + * Argument definition... + */ + + variable = mxmlNewElement(function, "argument"); + mxmlElementSetAttr(variable, "name", buffer); + + mxmlAdd(variable, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type); + type = NULL; + } + else if (!function && (ch == ';' || ch == ',')) + { + /* + * Variable definition... + */ + + variable = mxmlNewElement(MXML_NO_PARENT, "variable"); + mxmlElementSetAttr(variable, "name", buffer); + + sort_node(tree, variable); + + mxmlAdd(variable, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, type); + type = NULL; + } + else + mxmlNewText(type, type->child != NULL, buffer); + } + else if (type) + { + mxmlDelete(type); + type = NULL; + } + + state = STATE_NONE; + } + break; + } + +#if 0 + if (state != oldstate) + printf("changed states from %s to %s on receipt of character '%c'...\n", + states[oldstate], states[state], oldch); +#endif /* 0 */ + } + + /* + * All done, return with no errors... + */ + + return (0); } @@ -279,5 +617,5 @@ sort_node(mxml_node_t *tree, /* I - Tree to sort into */ /* - * End of "$Id: mxmldoc.c,v 1.3 2003/06/04 17:37:23 mike Exp $". + * End of "$Id: mxmldoc.c,v 1.4 2003/06/04 21:19:00 mike Exp $". */ diff --git a/testmxml.c b/testmxml.c index ef15702..9a46e6b 100644 --- a/testmxml.c +++ b/testmxml.c @@ -1,5 +1,5 @@ /* - * "$Id: testmxml.c,v 1.6 2003/06/04 17:37:23 mike Exp $" + * "$Id: testmxml.c,v 1.7 2003/06/04 21:19:00 mike Exp $" * * Test program for mini-XML, a small XML-like file parsing library. * @@ -175,23 +175,42 @@ whitespace_cb(mxml_node_t *node, /* I - Element node */ !strcmp(name, "pre") || !strcmp(name, "p") || !strcmp(name, "h1") || !strcmp(name, "h2") || !strcmp(name, "h3") || !strcmp(name, "h4") || !strcmp(name, "h5") || !strcmp(name, "h6")) + { + /* + * Newlines before open and after close... + */ + + if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_AFTER_CLOSE) + return ('\n'); + } + else if (!strcmp(name, "dl") || !strcmp(name, "ol") || !strcmp(name, "ul")) + { + /* + * Put a newline before and after list elements... + */ + return ('\n'); - else if (!strcmp(name, "li")) + } + else if (!strcmp(name, "dd") || !strcmp(name, "dt") || !strcmp(name, "li")) { /* - * Put a tab before
  • 's and a newline after
  • 's... + * Put a tab before
  • 's,
    's, and
    's, and a newline after them... */ - if (where == MXML_SAVE_OPEN_TAG) + if (where == MXML_WS_BEFORE_OPEN) return ('\t'); - else + else if (where == MXML_WS_AFTER_CLOSE) return ('\n'); } - else - return (0); + + /* + * Return 0 for no added whitespace... + */ + + return (0); } /* - * End of "$Id: testmxml.c,v 1.6 2003/06/04 17:37:23 mike Exp $". + * End of "$Id: testmxml.c,v 1.7 2003/06/04 21:19:00 mike Exp $". */