4a3f3df9cab079354e8a9f4cf6c56f616f1ea0ab
kent
  Fri Apr 6 16:29:22 2012 -0700
Removing MySQL linking dependency.
diff --git src/utils/autoXml/autoXml.c src/utils/autoXml/autoXml.c
new file mode 100644
index 0000000..cf05e8b
--- /dev/null
+++ src/utils/autoXml/autoXml.c
@@ -0,0 +1,724 @@
+/* autoXml - Generate structures code and parser for XML file from DTD-like spec. */
+#include "common.h"
+#include "linefile.h"
+#include "hash.h"
+#include "dystring.h"
+#include "cheapcgi.h"
+#include "obscure.h"
+#include "portable.h"
+#include "dtdParse.h"
+
+
+/* Variables that can be over-ridden from command line. */
+char *textField = "text";
+char *fileComment = "autoXml generated file";
+boolean picky;	/* Generate a parser that is afraid of the unknown. */
+boolean makeMain;	/* Generate main() routine as test shell. */
+boolean positiveOnly;	/* Don't write out negative numbers. */
+char prefix[128];	/* Added to start of output file and structure names. */
+
+void usage()
+/* Explain usage and exit. */
+{
+errAbort(
+  "autoXml - Generate structures code and parser for XML file from DTD-like spec\n"
+  "usage:\n"
+  "   autoXml file.dtdx root\n"
+  "This will generate root.c, root.h\n"
+  "options:\n"
+  "   -textField=xxx what to name text between start/end tags. Default 'text'\n"
+  "   -comment=xxx Comment to appear at top of generated code files\n"
+  "   -picky  Generate parser that rejects stuff it doesn't understand\n"
+  "   -main   Put in a main routine that's a test harness\n"
+  "   -prefix=xxx Prefix to add to structure names. By default same as root\n"
+  "   -positive Don't write out optional attributes with negative values\n"
+  );
+}
+
+char *cAttType(char *type)
+/* Return C declaration corresponding to type. */
+{
+if (sameWord(type, "INT"))
+    return "int ";
+else if (sameWord(type, "FLOAT"))
+    return "double ";
+else
+    return "char *";
+}
+
+char fAttType(char *type)
+/* Return printf %code to type. */
+{
+if (sameWord(type, "INT"))
+    return 'd';
+else if (sameWord(type, "FLOAT"))
+    return 'f';
+else
+    return 's';
+}
+
+boolean childMatch(struct dtdElChild *children, struct dtdElement *el)
+/* Return TRUE if any of children are el. */
+{
+struct dtdElChild *ec;
+for (ec = children; ec != NULL; ec = ec->next)
+    {
+    if (ec->el == el)
+	return TRUE;
+    }
+return FALSE;
+}
+
+boolean anyParent(struct dtdElement *elList, struct dtdElement *child)
+/* Return TRUE if anybody in elList could be a parent to child. */
+{
+struct dtdElement *el;
+for (el = elList; el != NULL; el = el->next)
+    {
+    if (childMatch(el->children, child))
+        return TRUE;
+    }
+return FALSE;
+}
+
+void freeFunctionPrototype(struct dtdElement *el, FILE *f, char *addSemi)
+/* Put up function prototype for elFree function. */
+{
+char *name = el->mixedCaseName;
+fprintf(f, "void %sFree(struct %s **pObj)%s", name, name, addSemi);
+fprintf(f, "\n");
+fprintf(f, "/* Free up %s. */\n",  name);
+}
+
+void freeListFunctionPrototype(struct dtdElement *el, FILE *f, char *addSemi)
+/* Put up function prototype for elFreeList function. */
+{
+char *name = el->mixedCaseName;
+fprintf(f, "void %sFreeList(struct %s **pList)%s", name, name, addSemi);
+fprintf(f, "\n");
+fprintf(f, "/* Free up list of %s. */\n",  name);
+}
+
+
+void saveFunctionPrototype(struct dtdElement *el, FILE *f, char *addSemi)
+/* Put up function prototype for elSave function. */
+{
+char *name = el->mixedCaseName;
+fprintf(f, "void %sSave(struct %s *obj, int indent, FILE *f)%s", name, name, addSemi);
+fprintf(f, "\n");
+fprintf(f, "/* Save %s to file. */\n",  name);
+}
+
+void loadFunctionPrototype(struct dtdElement *el, FILE *f, char *addSemi)
+/* Put up function prototype for elLoad function. */
+{
+char *name = el->mixedCaseName;
+fprintf(f, "struct %s *%sLoad(char *fileName)%s\n", name, name, addSemi);
+fprintf(f, "/* Load %s from XML file where it is root element. */\n", name);
+}
+
+void loadFunctionBody(struct dtdElement *el, FILE *f)
+/* Write body of elLoad function. */
+{
+fprintf(f, "{\n");
+fprintf(f, "struct %s *obj;\n", el->mixedCaseName);
+fprintf(f, "xapParseAny(fileName, \"%s\", %sStartHandler, %sEndHandler, NULL, &obj);\n", 
+	el->name, prefix, prefix);
+fprintf(f, "return obj;\n");
+fprintf(f, "}\n");
+fprintf(f, "\n");
+}
+
+void loadNextFunctionPrototype(struct dtdElement *el, FILE *f, char *addSemi)
+/* Put up function prototype for elLoad function. */
+{
+char *name = el->mixedCaseName;
+fprintf(f, "struct %s *%sLoadNext(struct xap *xap)%s\n", name, name, addSemi);
+fprintf(f, "/* Load next %s element.  Use xapOpen to get xap. */\n", name);
+}
+
+void loadNextFunctionBody(struct dtdElement *el, FILE *f)
+/* Write body of elLoad function. */
+{
+fprintf(f, "{\n");
+fprintf(f, "return xapNext(xap, \"%s\");\n", el->name);
+fprintf(f, "}\n");
+fprintf(f, "\n");
+}
+
+
+void startHandlerPrototype(FILE *f, char *addSemi)
+/* Print function prototype for startHandler. */
+{
+fprintf(f, "void *%sStartHandler(struct xap *xp, char *name, char **atts)%s\n", prefix, addSemi);
+fprintf(f, "/* Called by xap with start tag.  Does most of the parsing work. */\n");
+}
+
+void makeStartHandler(struct dtdElement *elList, FILE *f)
+/* Create function that gets called by expat at start of tag. */
+{
+struct dtdElement *el;
+struct dtdAttribute *att;
+
+startHandlerPrototype(f, "");
+fprintf(f, "{\n");
+fprintf(f, "struct xapStack *st = xp->stack+1;\n");
+fprintf(f, "int depth = xp->stackDepth;\n");
+fprintf(f, "int i;\n");
+fprintf(f, "\n");
+for (el = elList; el != NULL; el = el->next)
+    {
+    fprintf(f, "%sif (sameString(name, \"%s\"))\n", (el == elList ? "" : "else "), el->name);
+    fprintf(f, "    {\n");
+    fprintf(f, "    struct %s *obj;\n", el->mixedCaseName);
+    fprintf(f, "    AllocVar(obj);\n");
+    if (el->attributes != NULL)
+        {
+	boolean first = TRUE;
+	for (att = el->attributes; att != NULL; att = att->next)
+	    {
+	    if (att->usual != NULL)
+		{
+		char *quote = "\"";
+		if (sameString(att->type, "INT") || sameString(att->type, "FLOAT"))
+		    quote = "";
+	        fprintf(f, "    obj->%s = %s%s%s;\n", att->name, quote, att->usual, quote);
+		}
+	    }
+	fprintf(f, "    for (i=0; atts[i] != NULL; i += 2)\n");
+	fprintf(f, "        {\n");
+	fprintf(f, "        char *name = atts[i], *val = atts[i+1];\n");
+	for (att = el->attributes; att != NULL; att = att->next)
+	    {
+	    fprintf(f, "        %s (sameString(name, \"%s\"))\n",
+		    (first ? "if " : "else if"), att->name);
+	    if (sameWord(att->type, "INT"))
+		fprintf(f, "            obj->%s = atoi(val);\n", att->name);
+	    else if (sameWord(att->type, "FLOAT"))
+		fprintf(f, "            obj->%s = atof(val);\n", att->name);
+	    else
+	        fprintf(f, "            obj->%s = cloneString(val);\n", att->name);
+	    first = FALSE;
+	    }
+	if (picky)
+	    {
+	    fprintf(f, "        else\n");
+	    fprintf(f, "            xapError(xp, \"Unknown attribute %%s\", name);\n");
+	    }
+	fprintf(f, "        }\n");
+	for (att = el->attributes; att != NULL; att = att->next)
+	    {
+	    if (att->required)
+	        {
+		if (sameWord(att->type, "INT") || sameWord(att->type, "FLOAT"))
+		    {
+		    /* For the moment can't check these. */
+		    }
+		else
+		    {
+		    fprintf(f, "    if (obj->%s == NULL)\n", att->name);
+		    fprintf(f, "        xapError(xp, \"missing %s\");\n", att->name);
+		    }
+		}
+	    }
+	}
+    if (anyParent(elList, el))
+        {
+	struct dtdElement *parent;
+	boolean first = TRUE;
+	fprintf(f, "    if (depth > 1)\n");
+	fprintf(f, "        {\n");
+	for (parent = elList; parent != NULL; parent = parent->next)
+	    {
+	    if (childMatch(parent->children, el))
+	        {
+		fprintf(f, "        %s (sameString(st->elName, \"%s\"))\n", 
+			(first ? "if " : "else if"), parent->name);
+		fprintf(f, "            {\n");
+		fprintf(f, "            struct %s *parent = st->object;\n", parent->mixedCaseName);
+		fprintf(f, "            slAddHead(&parent->%s, obj);\n", el->mixedCaseName);
+		fprintf(f, "            }\n");
+		first = FALSE;
+		}
+	    }
+	if (picky)
+	    {
+	    fprintf(f, "        else\n");
+	    fprintf(f, "            xapError(xp, \"%%s misplaced\", name);\n");
+	    }
+	fprintf(f, "        }\n");
+	}
+    fprintf(f, "    return obj;\n");
+    fprintf(f, "    }\n");
+    }
+fprintf(f, "else\n");
+fprintf(f, "    {\n");
+if (picky)
+    fprintf(f, "    xapError(xp, \"Unknown element %%s\", name);\n");
+else
+    fprintf(f, "    xapSkip(xp);\n");
+fprintf(f, "    return NULL;\n");
+fprintf(f, "    }\n");
+fprintf(f, "}\n");
+fprintf(f, "\n");
+}
+
+
+void endHandlerPrototype(FILE *f, char *addSemi)
+/* Print function prototype for endHandler. */
+{
+fprintf(f, "void %sEndHandler(struct xap *xp, char *name)%s\n", prefix, addSemi);
+fprintf(f, "/* Called by xap with end tag.  Checks all required children are loaded. */\n");
+}
+
+void makeEndHandler(struct dtdElement *elList, FILE *f)
+/* Create function that gets called by expat at end of tag. */
+{
+struct dtdElement *el;
+struct dtdElChild *ec;
+boolean first = TRUE;
+
+endHandlerPrototype(f, "");
+fprintf(f, "{\n");
+fprintf(f, "struct xapStack *stack = xp->stack;\n");
+for (el = elList; el != NULL; el = el->next)
+    {
+    if (el->children || el->textType)
+        {
+	fprintf(f, "%sif (sameString(name, \"%s\"))\n", 
+	   (first ? "" : "else "), el->name);
+	fprintf(f, "    {\n");
+	fprintf(f, "    struct %s *obj = stack->object;\n", el->mixedCaseName);
+	for (ec = el->children; ec != NULL; ec = ec->next)
+	    {
+	    char *cBIG = ec->el->name;
+	    char *cSmall = ec->el->mixedCaseName;
+	    if (ec->copyCode == '1')
+	        {
+		fprintf(f, "    if (obj->%s == NULL)\n", cSmall);
+		fprintf(f, "        xapError(xp, \"Missing %s\");\n", cBIG);
+		fprintf(f, "    if (obj->%s->next != NULL)\n", cSmall);
+		fprintf(f, "        xapError(xp, \"Multiple %s\");\n", cBIG);
+		}
+	    else if (ec->copyCode == '+')
+	        {
+		if (! ec->isOr)
+		    {
+		    /* bypassing this is not the Right thing to do -- 
+		     * really we should make sure that somebody in the Or list
+		     * was present */
+		    fprintf(f, "    if (obj->%s == NULL)\n", cSmall);
+		    fprintf(f, "        xapError(xp, \"Missing %s\");\n",cBIG);
+		    }
+		fprintf(f, "    slReverse(&obj->%s);\n", cSmall);
+		}
+	    else if (ec->copyCode == '*')
+	        {
+		fprintf(f, "    slReverse(&obj->%s);\n", cSmall);
+		}
+	    else if (ec->copyCode == '?')
+	        {
+		fprintf(f, "    if (obj->%s != NULL && obj->%s->next != NULL)\n", cSmall, cSmall);
+		fprintf(f, "        xapError(xp, \"Multiple %s\");\n", cBIG);
+		}
+	    }
+	if (el->textType)
+	    {
+	    if (sameString(el->textType, "#INT"))
+		fprintf(f, "    obj->%s = atoi(stack->%s->string);\n", textField, textField);
+	    else if (sameString(el->textType, "#FLOAT"))
+		fprintf(f, "    obj->%s = atof(stack->%s->string);\n", textField, textField);
+	    else
+		fprintf(f, "    obj->%s = cloneString(stack->%s->string);\n", textField, textField);
+	    }
+	fprintf(f, "    }\n");
+	first = FALSE;
+	}
+    }
+fprintf(f, "}\n");
+fprintf(f, "\n");
+}
+
+void makeH(struct dtdElement *elList, char *fileName)
+/* Produce C header file. */
+{
+FILE *f = mustOpen(fileName, "w");
+struct dtdElement *el;
+struct dtdAttribute *att;
+struct dtdElChild *ec;
+char upcPrefix[128];
+
+fprintf(f, "/* %s.h %s */\n", prefix, fileComment);
+strcpy(upcPrefix, prefix);
+touppers(upcPrefix);
+fprintf(f, "#ifndef %s_H\n", upcPrefix);
+fprintf(f, "#define %s_H\n", upcPrefix);
+fprintf(f, "\n");
+fprintf(f, "#ifndef XAP_H\n");
+fprintf(f, "#include \"xap.h\"\n");
+fprintf(f, "#endif\n");
+fprintf(f, "\n");
+
+fprintf(f, 
+ "/* The start and end handlers here are used with routines defined in xap.h.\n"
+ " * In particular if you want to read just parts of the XML file into memory\n"
+ " * call xapOpen() with these, and then xapNext() with the name of the tag\n"
+ " * you want to load. */\n\n");
+startHandlerPrototype(f, ";");
+fprintf(f, "\n");
+endHandlerPrototype(f, ";");
+fprintf(f, "\n");
+fprintf(f, "\n");
+
+for (el = elList; el != NULL; el = el->next)
+    {
+    fprintf(f, "struct %s\n", el->mixedCaseName);
+    fprintf(f, "    {\n");
+    fprintf(f, "    struct %s *next;\n", el->mixedCaseName);
+    if (el->textType != NULL)
+	 {
+	 if (sameString(el->textType, "#INT"))
+	     fprintf(f, "    int %s;\n", textField);
+	 else if (sameString(el->textType, "#FLOAT"))
+	     fprintf(f, "    float %s;\n", textField);
+	 else
+	     fprintf(f, "    char *%s;\n", textField);
+	 }
+    for (att = el->attributes; att != NULL; att = att->next)
+	{
+	fprintf(f, "    %s%s;", cAttType(att->type), att->name);
+	if (att->required)
+	    fprintf(f, "\t/* Required */");
+	else 
+	    {
+	    if (att->usual != NULL)
+	       fprintf(f, "\t/* Defaults to %s */", att->usual);
+	    else
+	       fprintf(f, "\t/* Optional */");
+	    }
+	fprintf(f, "\n");
+	}
+    for (ec = el->children; ec != NULL; ec = ec->next)
+	{
+	fprintf(f, "    struct %s *%s;", ec->el->mixedCaseName, ec->el->mixedCaseName);
+	if (ec->copyCode == '1')
+	    fprintf(f, "\t/** Single instance required. **/");
+	else if (ec->copyCode == '+')
+	    fprintf(f, "\t/** Non-empty list required. **/");
+	else if (ec->copyCode == '*')
+	    fprintf(f, "\t/** Possibly empty list. **/");
+	else if (ec->copyCode == '?')
+	    fprintf(f, "\t/** Optional (may be NULL). **/");
+	fprintf(f, "\n");
+	}
+    fprintf(f, "    };\n");
+    fprintf(f, "\n");
+    freeFunctionPrototype(el, f, ";");
+    fprintf(f, "\n");
+    freeListFunctionPrototype(el, f, ";");
+    fprintf(f, "\n");
+    saveFunctionPrototype(el, f, ";");
+    fprintf(f, "\n");
+    loadFunctionPrototype(el, f, ";");
+    fprintf(f, "\n");
+    loadNextFunctionPrototype(el, f, ";");
+    fprintf(f, "\n");
+    }
+
+fprintf(f, "#endif /* %s_H */\n", upcPrefix);
+fprintf(f, "\n");
+}
+
+boolean optionalChildren(struct dtdElement *el)
+/* Return TRUE if not sure whether you have children. */
+{
+struct dtdElChild *ec;
+boolean required = FALSE, optional = FALSE;
+for (ec = el->children; ec != NULL; ec = ec->next)
+    {
+    if (ec->copyCode == '*' || ec->copyCode == '?')
+	optional = TRUE;
+    else
+        required = TRUE;
+    }
+return !required && optional;
+}
+
+boolean isAtomic(struct dtdElement *el)
+/* Return TRUE if by definition no children. */
+{
+return el->textType == NULL && el->children == NULL;
+}
+
+void saveFunctionBody(struct dtdElement *el, FILE *f)
+/* Write out save function body. */
+{
+struct dtdElChild *ec;
+struct dtdAttribute *att;
+boolean optKids = optionalChildren(el);
+boolean isAtom = isAtomic(el);
+
+fprintf(f, "{\n");
+
+/* Declare variables to walk through list if need be. */
+for (ec = el->children; ec != NULL; ec = ec->next)
+    {
+    if (ec->copyCode == '*' || ec->copyCode == '+')
+	{
+	char *name = ec->el->mixedCaseName;
+        fprintf(f, "struct %s *%s;\n", name, name);
+	}
+    }
+if (optKids)
+    fprintf(f, "boolean isNode = TRUE;\n");
+
+fprintf(f, "if (obj == NULL) return;\n");
+fprintf(f, "xapIndent(indent, f);\n");
+if (el->attributes == NULL)
+    {
+    if (isAtom)
+	fprintf(f, "fprintf(f, \" ->\\n\");\n");
+    else
+	fprintf(f, "fprintf(f, \"<%s>\");\n", el->name);
+    }
+else 
+    {
+    fprintf(f, "fprintf(f, \"<%s\");\n", el->name);
+    for (att = el->attributes; att != NULL; att = att->next)
+        {
+	if (att->required )
+	    fprintf(f, "fprintf(f, \" %s=\\\"%%%c\\\"\", obj->%s);\n", att->name, fAttType(att->type), att->name);
+	else
+	    {
+	    if (sameString(att->type, "INT") || sameString(att->type, "FLOAT"))
+	        {
+		if (positiveOnly)
+		    {
+		    fprintf(f, "if (obj->%s >= 0)\n    ", att->name);
+		    }
+	        fprintf(f, "fprintf(f, \" %s=\\\"%%%c\\\"\", obj->%s);\n", att->name, fAttType(att->type), att->name);
+		}
+	    else
+		{
+		fprintf(f, "if (obj->%s != NULL)\n", att->name);
+		fprintf(f, "    fprintf(f, \" %s=\\\"%%%c\\\"\", obj->%s);\n", att->name, fAttType(att->type), att->name);
+		}
+	    }
+	}
+    if (isAtom)
+	fprintf(f, "fprintf(f, \"/>\\n\");\n");
+    else
+	fprintf(f, "fprintf(f, \">\");\n");
+    }
+if (el->textType != NULL)
+    {
+    if (sameString(el->textType, "#INT"))
+	fprintf(f, "fprintf(f, \"%%d\", obj->%s);\n", textField);
+    else if (sameString(el->textType, "#FLOAT"))
+	fprintf(f, "fprintf(f, \"%%f\", obj->%s);\n", textField);
+    else
+	fprintf(f, "fprintf(f, \"%%s\", obj->%s);\n", textField);
+    }
+if (isAtom)
+    {
+    }
+else if (el->children == NULL)
+    {
+    fprintf(f, "fprintf(f, \"</%s>\\n\");\n", el->name);
+    }
+else
+    {
+    if (!optKids)
+	fprintf(f, "fprintf(f, \"\\n\");\n");
+    for (ec = el->children; ec != NULL; ec = ec->next)
+	{
+	char *name = ec->el->mixedCaseName;
+	if (ec->copyCode == '*' || ec->copyCode == '+')
+	    {
+	    fprintf(f, "for (%s = obj->%s; %s != NULL; %s = %s->next)\n", name, name, name, name, name);
+	    fprintf(f, "   {\n");
+	    if (optKids)
+		{
+		fprintf(f, "   if (isNode)\n");
+		fprintf(f, "       {\n");
+		fprintf(f, "       fprintf(f, \"\\n\");\n");
+		fprintf(f, "       isNode = FALSE;\n");
+		fprintf(f, "       }\n");
+		}
+	    fprintf(f, "   %sSave(%s, indent+2, f);\n", name, name);
+	    fprintf(f, "   }\n");
+	    }
+	else
+	    {
+	    if (optKids)
+	        {
+		fprintf(f, "if (obj->%s != NULL)\n", name);
+		fprintf(f, "    {\n");
+		fprintf(f, "    if (isNode)\n");
+		fprintf(f, "       {\n");
+		fprintf(f, "       fprintf(f, \"\\n\");\n");
+		fprintf(f, "       isNode = FALSE;\n");
+		fprintf(f, "       }\n");
+		fprintf(f, "    %sSave(obj->%s, indent+2, f);\n", name, name);
+		fprintf(f, "    }\n");
+		}
+	    else
+		fprintf(f, "%sSave(obj->%s, indent+2, f);\n", name, name);
+	    }
+	}
+    if (optKids)
+        {
+	fprintf(f, "if (!isNode)\n");
+	fprintf(f, "    xapIndent(indent, f);\n");
+	}
+    else
+	{
+	fprintf(f, "xapIndent(indent, f);\n");
+	}
+    fprintf(f, "fprintf(f, \"</%s>\\n\");\n", el->name);
+    }
+fprintf(f, "}\n");
+fprintf(f, "\n");
+}
+
+void freeFunctionBody(struct dtdElement *el, FILE *f)
+/* Write out free function body. */
+{
+struct dtdElChild *ec;
+struct dtdAttribute *att;
+
+fprintf(f, "{\n");
+fprintf(f, "struct %s *obj = *pObj;\n",  el->mixedCaseName);
+
+/* Declare variables to walk through list if need be. */
+fprintf(f, "if (obj == NULL) return;\n");
+for (att = el->attributes; att != NULL; att = att->next)
+    {
+    if (!sameString(att->type, "INT") && !sameString(att->type, "FLOAT"))
+	fprintf(f, "freeMem(obj->%s);\n", att->name);
+    }
+if (el->textType != NULL)
+    {
+    if (!sameString(el->textType, "#INT") && 
+    	!sameString(el->textType, "#FLOAT"))
+	fprintf(f, "freeMem(obj->text);\n");
+    }
+for (ec = el->children; ec != NULL; ec = ec->next)
+    {
+    char *name = ec->el->mixedCaseName;
+    if (ec->copyCode == '*' || ec->copyCode == '+')
+	fprintf(f, "%sFreeList(&obj->%s);\n", name, name);
+    else
+	fprintf(f, "%sFree(&obj->%s);\n", name, name);
+    }
+fprintf(f, "freez(pObj);\n");
+fprintf(f, "}\n");
+fprintf(f, "\n");
+}
+
+void freeListFunctionBody(struct dtdElement *el, FILE *f)
+/* Write out free function body. */
+{
+fprintf(f, "{\n");
+fprintf(f, "struct %s *el, *next;\n",  el->mixedCaseName);
+fprintf(f, "for (el = *pList; el != NULL; el = next)\n");
+fprintf(f, "    {\n");
+fprintf(f, "    next = el->next;\n");
+fprintf(f, "    %sFree(&el);\n", el->mixedCaseName);
+fprintf(f, "    el = next;\n");
+fprintf(f, "    }\n");
+fprintf(f, "}\n");
+fprintf(f, "\n");
+}
+
+
+void makeTestDriver(struct dtdElement *rootEl, FILE *f)
+/* Make main routine. */
+{
+char *symName = rootEl->mixedCaseName;
+fprintf(f, "int main(int argc, char *argv[])\n");
+fprintf(f, "/* Test driver for %s routines */\n", prefix);
+fprintf(f, "{\n");
+fprintf(f, "struct %s *obj;\n", symName);
+fprintf(f, "if (argc != 2)\n");
+fprintf(f, "    errAbort(\"Please run again with a xml filename.\");\n");
+fprintf(f, "obj = %sLoad(argv[1]);\n", symName);
+fprintf(f, "%sSave(obj, 0, stdout);\n", symName);
+fprintf(f, "%sFree(&obj);\n", symName);
+fprintf(f, "return 0;\n");
+fprintf(f, "}\n");
+}
+
+
+void makeC(struct dtdElement *elList, char *fileName, char *incName)
+/* Produce C code file. */
+{
+FILE *f = mustOpen(fileName, "w");
+struct dtdElement *el;
+char incFile[128], incExt[64];
+
+splitPath(incName, NULL, incFile, incExt);
+
+fprintf(f, "/* %s.c %s */\n", prefix, fileComment);
+fprintf(f, "\n");
+fprintf(f, "#include \"common.h\"\n");
+fprintf(f, "#include \"xap.h\"\n");
+fprintf(f, "#include \"%s%s\"\n", incFile, incExt);
+fprintf(f, "\n");
+
+fprintf(f, "\n");
+for (el = elList; el != NULL; el = el->next)
+    {
+    freeFunctionPrototype(el, f, "");
+    freeFunctionBody(el, f);
+    freeListFunctionPrototype(el, f, "");
+    freeListFunctionBody(el, f);
+    saveFunctionPrototype(el, f, "");
+    saveFunctionBody(el, f);
+    loadFunctionPrototype(el, f, "");
+    loadFunctionBody(el, f);
+    loadNextFunctionPrototype(el, f, "");
+    loadNextFunctionBody(el, f);
+    }
+makeStartHandler(elList, f);
+makeEndHandler(elList, f);
+if (makeMain)
+   makeTestDriver(elList, f);
+}
+
+void autoXml(char *dtdxFile, char *outRoot)
+/* autoXml - Generate structures code and parser for XML file from DTD-like spec. */
+{
+struct dtdElement *elList = NULL;
+struct hash *elHash = NULL;
+char hName[512], cName[512];
+char outDir[256];
+
+splitPath(outRoot, outDir, prefix, NULL);
+if (cgiVarExists("prefix"))
+    strcpy(prefix, cgiString("prefix"));
+if (outDir[0] != 0)
+    makeDir(outDir);
+dtdParse(dtdxFile, prefix, textField, &elList, &elHash);
+printf("Parsed %d elements in %s\n", slCount(elList), dtdxFile);
+sprintf(hName, "%s.h", outRoot);
+makeH(elList, hName);
+sprintf(cName, "%s.c", outRoot);
+makeC(elList, cName, hName);
+printf("Generated code in %s\n", cName);
+}
+
+int main(int argc, char *argv[])
+/* Process command line. */
+{
+cgiSpoof(&argc, argv);
+textField = cgiUsualString("textField", textField);
+fileComment = cgiUsualString("comment", fileComment);
+picky = cgiBoolean("picky");
+makeMain = cgiBoolean("main");
+positiveOnly = cgiBoolean("positive");
+if (argc != 3)
+    usage();
+autoXml(argv[1], argv[2]);
+return 0;
+}