Bug fixes in mxmlWalkNext().

Break whitespace processing into separate function and add "where" values
for before/after the open/close tags.

Major progress on mxmldoc example program.

Update test program to use new whitespace callback.

Clean up header file.

Add rating link to home page.
web
Michael R Sweet 22 years ago
parent f830ffd6a5
commit 8cfa3005e5
  1. 3
      index.html
  2. 73
      mxml-file.c
  3. 18
      mxml-node.c
  4. 6
      mxml-search.c
  5. 16
      mxml.h
  6. 362
      mxmldoc.c
  7. 35
      testmxml.c

@ -19,7 +19,8 @@ href="../index.html">Back to Home Page</a>&nbsp;]</p>
<p class="title" align="center">Current Release: v0.93 [&nbsp;<a
href="mxml-0.93.tar.gz">Download Source (.tar.gz 40k)</a> |
<a href="CHANGES">View Change Log</a>&nbsp;]</p>
<a href="CHANGES">View Change Log</a> |
<a href="http://freshmeat.net/projects/mxml">Rate/Make Comments</A>&nbsp;]</p>
<h2>Introduction</h2>

@ -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, "</%s>", 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 $".
*/

@ -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 $".
*/

@ -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 $".
*/

@ -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 $".
*/

@ -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 @@
*
* <function name="">
* <description>descriptive text</description>
* <iargument name="">
* <argument name="" direction="I|O|IO">
* <description>descriptive text</description>
* <type>type string</type>
* </iargument>
* <oargument name="">
* <description>descriptive text</description>
* <type>type string</type>
* </oargument>
* <ioargument name="">
* <description>descriptive text</description>
* <type>type string</type>
* </ioargument>
* </argument>
* <returnvalue>
* <description>descriptive text</description>
* <type>type string</type>
@ -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, /* <comment> node */
*function, /* <function> node */
*variable, /* <variable> or <argument> node */
*returnvalue, /* <returnvalue> node */
*type, /* <type> node */
*description; /* <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 $".
*/

@ -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 <li>'s and a newline after </li>'s...
* Put a tab before <li>'s, <dd>'s, and <dt>'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 $".
*/

Loading…
Cancel
Save