4a3f3df9cab079354e8a9f4cf6c56f616f1ea0ab
kent
  Fri Apr 6 16:29:22 2012 -0700
Removing MySQL linking dependency.
diff --git src/utils/autoSql/autoSql.c src/utils/autoSql/autoSql.c
new file mode 100644
index 0000000..88ba3af
--- /dev/null
+++ src/utils/autoSql/autoSql.c
@@ -0,0 +1,1946 @@
+/* autoSql - automatically generate SQL and C code for saving
+ * and loading entities in database. 
+ *
+ * This module is pretty ugly.  It seems to work within some
+ * limitations (described in the autoSql.doc).  Rather than do major
+ * changes from here I'd rewrite it though.  The tokenizer is
+ * pretty sound, the parser isn't bad (though the language it
+ * parses is quirky), but the code generator is u-g-l-y.
+ *
+ * This file is copyright 2002-2005 Jim Kent, but license is hereby
+ * granted for all use - public, private or commercial. */
+
+#include "common.h"
+#include "errabort.h"
+#include "linefile.h"
+#include "obscure.h"
+#include "dystring.h"
+#include "asParse.h"
+#include "options.h"
+
+
+boolean withNull = FALSE;
+boolean makeJson = FALSE;
+boolean makeDjango = FALSE;
+
+void usage()
+/* Explain usage and exit. */
+{
+errAbort("autoSql - create SQL and C code for permanently storing\n"
+         "a structure in database and loading it back into memory\n"
+	 "based on a specification file\n"
+	 "usage:\n"
+	 "    autoSql specFile outRoot {optional: -dbLink -withNull -json} \n"
+	 "This will create outRoot.sql outRoot.c and outRoot.h based\n"
+	 "on the contents of specFile. \n"
+         "\n"
+         "options:\n"
+         "  -dbLink - optionally generates code to execute queries and\n"
+         "            updates of the table.\n"
+	 "  -withNull - optionally generates code and .sql to enable\n"
+         "              applications to accept and load data into objects\n"
+	 "              with potential 'missing data' (NULL in SQL)\n"
+         "              situations.\n"
+         "  -django - generate method to output object as django model Python code\n"
+	 "  -json - generate method to output the object in JSON (JavaScript) format.\n");
+}
+
+static struct optionSpec optionSpecs[] = {
+    {"dbLink", OPTION_BOOLEAN},
+    {"withNull", OPTION_BOOLEAN},
+    {"json", OPTION_BOOLEAN},
+    {"django", OPTION_BOOLEAN},
+    {NULL, 0}
+};
+
+void sqlColumn(struct asColumn *col, FILE *f)
+/* Print out column in SQL. */
+{
+fprintf(f, "    %s ", col->name);
+struct dyString *type = asColumnToSqlType(col);
+fprintf(f, "%s", type->string);
+if (!withNull)
+    fprintf(f, " not null");
+fputc(',', f);
+fprintf(f, "\t# %s\n", col->comment);
+dyStringFree(&type);
+}
+
+void sqlTable(struct asObject *table, FILE *f)
+/* Print out structure of table in SQL. */
+{
+struct asColumn *col;
+
+fprintf(f, "\n#%s\n", table->comment);
+fprintf(f, "CREATE TABLE %s (\n", table->name);
+for (col = table->columnList; col != NULL; col = col->next)
+    sqlColumn(col, f);
+
+fprintf(f,"              #Indices\n");
+fprintf(f, "    PRIMARY KEY(%s)\n", table->columnList->name);
+fprintf(f, ");\n");
+}
+
+void djangoEnumChoices(struct asColumn *col, FILE *f)
+/* Write out a list of choices to use with a django enum. */
+{
+fprintf(f, "    %sChoices = (\n", col->name);
+struct slName *val;
+for (val = col->values; val != NULL; val = val->next)
+    fprintf(f, "        ('%s', '%s'),\n", val->name, val->name);
+fprintf(f, "    )\n");
+}
+
+int longestValue(struct asColumn *col)
+/* Return length of longest value in col->values list */
+{
+int longest = 0;
+struct slName *val;
+for (val = col->values; val != NULL; val = val->next)
+    {
+    int len = strlen(val->name);
+    if (len > longest)
+        longest = len;
+    }
+return longest;
+}
+
+void djangoColumn(struct asColumn *col, FILE *f)
+/* Print out column in Django flavored python. */
+{
+fprintf(f, "    %s = models.", col->name);
+struct asTypeInfo *lt = col->lowType;
+if (lt->type == t_enum)
+    fprintf(f, "CharField(max_length=%d choices=%sChoices)",  longestValue(col), col->name);
+else if (lt->type == t_set)
+    {
+    warn("Set type fields such as '%s' are not tested in Django and are unlikely to work.",
+        col->name);
+    fprintf(f, "TextField() # A set in autoSql");
+    }
+else if (col->isList || col->isArray)
+    fprintf(f, "TextField()");
+else if (lt->type == t_char)
+    fprintf(f, "CharField(max_length=%d)", col->fixedSize ? col->fixedSize : 1);
+else if (lt->type == t_string)
+    fprintf(f, "CharField(max_length=255)");
+else
+    fprintf(f, "%s()", lt->djangoName);
+fprintf(f, "\t# %s\n", col->comment);
+}
+
+void djangoModel(struct asObject *table, FILE *f)
+/* Print out structure of table in Django flavored python */
+{
+fprintf(f, "class %s(models.Model):\n");
+struct asColumn *col;
+for (col = table->columnList; col != NULL; col = col->next)
+    if (col->lowType->type == t_enum)
+        djangoEnumChoices(col, f);
+for (col = table->columnList; col != NULL; col = col->next)
+    djangoColumn(col, f);
+fprintf(f, "\n");
+}
+
+static void cSymTypePrName(struct asObject *dbObj, char *name, FILE *f)
+/* print the C type name, prefixed with the object name */
+{
+char cname[1024];
+safef(cname, sizeof(cname), "%s%c%s", dbObj->name, toupper(name[0]), name+1);
+// Fix name so that it is a valid C identifier.
+char *c;
+for (c = cname; *c != '\0'; c++)
+    {
+    if (!(isalnum(*c) || (*c == '_')))
+        *c = '_';
+    }
+fputs(cname, f);
+}
+
+void cSymTypeDef(struct asObject *dbObj, struct asColumn *col, FILE *f)
+/* print out C enum for enum or set columns.  enum and value names are
+ * prefixed with structure names to prevent collisions */
+{
+boolean isEnum = (col->lowType->type == t_enum);
+fprintf(f, "enum ");
+cSymTypePrName(dbObj, col->name, f);
+fprintf(f, "\n    {\n");
+unsigned value = isEnum ? 0 : 1;
+struct slName *val;
+for (val = col->values; val != NULL; val = val->next)
+    {
+    fprintf(f, "    ");
+    cSymTypePrName(dbObj, val->name, f);
+    if (isEnum)
+        {
+        fprintf(f, " = %d,\n", value);
+        value++;
+        }
+    else
+        {
+        fprintf(f, " = 0x%.4x,\n", value);
+        value = value << 1;
+        }
+    }
+fprintf(f, "    };\n");
+}
+
+void cObjColumn(struct asColumn *col, FILE *f)
+/* Print out an object column in C. */
+{
+if (col->lowType->type == t_object)
+    fprintf(f, "    struct %s *%s", col->obType->name, col->name);
+else if (col->lowType->type == t_simple)
+    {
+    if (col->isArray)
+        {
+        if (col->fixedSize)
+            fprintf(f, "    struct %s %s[%d]", 
+                col->obType->name, col->name, col->fixedSize);
+        else
+            fprintf(f, "    struct %s *%s", 
+                col->obType->name, col->name);
+        }
+    else
+        fprintf(f, "    struct %s %s", col->obType->name, col->name);
+    }
+else
+    assert(FALSE);
+}
+
+void cCharColumn(struct asColumn *col, FILE *f)
+/* Print out a char column in C. */
+{
+fprintf(f, "    %s", col->lowType->cName);
+if (col->fixedSize > 0)
+    fprintf(f, " %s[%d]", col->name, col->fixedSize+1); 
+else if (col->isList)
+    fprintf(f, " *%s", col->name);
+else
+    fprintf(f, " %s", col->name); 
+}
+
+void cEnumColumn(struct asObject *dbObj, struct asColumn *col, FILE *f)
+/* print out enum column def in C */
+{
+fprintf(f, "    enum ");
+cSymTypePrName(dbObj, col->name, f);
+fprintf(f, " %s", col->name);
+}
+
+void cOtherColumn(struct asColumn *col, FILE *f)
+/* Print out other types of column in C. */
+{
+fprintf(f, "    %s", col->lowType->cName);
+if (!withNull)
+    {
+    if (!col->lowType->stringy)
+    	fputc(' ',f);
+    }
+else
+    {
+    if (!col->lowType->stringy)
+	{
+    	fputc(' ',f);
+    	fputc('*',f);
+	}
+    }
+
+if (col->isList && !col->fixedSize)
+    fputc('*', f);
+fprintf(f, "%s", col->name);
+if (col->isList && col->fixedSize)
+    fprintf(f, "[%d]", col->fixedSize);
+}
+
+void cColumn(struct asObject *dbObj, struct asColumn *col, FILE *f)
+/* Print out a column in C. */
+{
+if (col->obType != NULL)
+    cObjColumn(col, f);
+else if (col->lowType->type == t_char)
+    cCharColumn(col, f);
+else if (col->lowType->type == t_enum)
+    cEnumColumn(dbObj, col, f);
+else
+    cOtherColumn(col, f);
+fprintf(f, ";\t/* %s */\n", col->comment);
+}
+
+void cTable(struct asObject *dbObj, FILE *f)
+/* Print out structure of dbObj in C. */
+{
+struct asColumn *col;
+char defineName[256];
+
+splitPath(dbObj->name, NULL, defineName, NULL);
+touppers(defineName);
+fprintf(f, "#define %s_NUM_COLS %d\n\n", defineName,
+        slCount(dbObj->columnList));
+
+for (col = dbObj->columnList; col != NULL; col = col->next)
+    {
+    if ((col->lowType->type == t_enum) || (col->lowType->type == t_set))
+        cSymTypeDef(dbObj, col, f);
+    }
+
+fprintf(f, "struct %s\n", dbObj->name);
+fprintf(f, "/* %s */\n", dbObj->comment);
+fprintf(f, "    {\n");
+if (!dbObj->isSimple)
+    fprintf(f, "    struct %s *next;  /* Next in singly linked list. */\n", dbObj->name);
+for (col = dbObj->columnList; col != NULL; col = col->next)
+    cColumn(dbObj, col, f);
+fprintf(f, "    };\n\n");
+}
+
+static boolean trueFalse[] = {TRUE, FALSE};
+
+void makeCommaInColumn(char *indent, struct asColumn *col,  FILE *f, bool isArray)
+/* Make code to read in one column from a comma separated set. */
+{
+struct asObject *obType = col->obType;
+struct asTypeInfo *lt = col->lowType;
+char *arrayRef = (isArray ? "[i]" : "");
+    
+if (obType != NULL)
+    {
+    if (lt->type == t_object)
+	{
+	fprintf(f, "%ss = sqlEatChar(s, '{');\n", indent);
+	fprintf(f, "%sif(s[0] != '}')",indent);
+	fprintf(f, "%s    slSafeAddHead(&ret->%s, %sCommaIn(&s,NULL));\n", indent,
+	     col->name, obType->name);
+	fprintf(f, "%ss = sqlEatChar(s, '}');\n", indent);
+	fprintf(f, "%ss = sqlEatChar(s, ',');\n", indent);
+	}
+    else if (lt->type == t_simple)
+	{
+	fprintf(f, "%ss = sqlEatChar(s, '{');\n", indent);
+	fprintf(f, "%sif(s[0] != '}')",indent);
+	fprintf(f, "%s    %sCommaIn(&s, &ret->%s%s);\n", indent,
+	    obType->name, col->name, arrayRef);
+	fprintf(f, "%ss = sqlEatChar(s, '}');\n", indent);
+	fprintf(f, "%ss = sqlEatChar(s, ',');\n", indent);
+	}
+    else
+	{
+	assert(FALSE);
+	}
+    }
+else if ((lt->type == t_enum) || (lt->type == t_set))
+    {
+    if (!withNull)
+	{
+    	fprintf(f, "%sret->%s = sql%sComma(&s, values_%s, &valhash_%s);\n", indent,
+            col->name, lt->nummyName,  col->name,  col->name);
+    	}
+    else
+	{
+    	fprintf(f, "%sret->%s = needMem(sizeof(*(ret->%s)));\n", indent, col->name, col->name);
+    	fprintf(f, "%s*(ret->%s) = sql%sComma(&s, values_%s, &valhash_%s);\n", indent,
+            col->name, lt->nummyName,  col->name,  col->name);
+    	}
+    }
+else if (lt->stringy)
+    fprintf(f, "%sret->%s%s = sqlStringComma(&s);\n", indent, col->name, arrayRef);
+else if (lt->isUnsigned)
+    {
+    if (!withNull)
+	{
+    	fprintf(f, "%sret->%s%s = sqlUnsignedComma(&s);\n", indent, col->name, arrayRef);
+    	}
+    else
+	{
+    	fprintf(f, "%sret->%s = needMem(sizeof(unsigned));\n", indent, col->name);
+    	fprintf(f, "%s*(ret->%s%s) = sqlUnsignedComma(&s);\n", indent, col->name, arrayRef);
+    	}
+    }
+else if (lt->type == t_char)
+    {
+    if (col->fixedSize > 0)
+	fprintf(f, "%ssqlFixedStringComma(&s, ret->%s%s, sizeof(ret->%s%s));\n",
+		indent, col->name, arrayRef, col->name, arrayRef);
+    else if (col->isList)
+	fprintf(f, "%sret->%s%s = sqlCharComma(&s);\n",
+		indent, col->name, arrayRef);
+    else
+	fprintf(f, "%ssqlFixedStringComma(&s, &(ret->%s), sizeof(ret->%s));\n",
+		indent, col->name, col->name);
+    }
+else
+    {
+    if (!withNull)
+	{
+    	fprintf(f, "%sret->%s%s = sql%sComma(&s);\n", indent, col->name, arrayRef, lt->nummyName);
+    	}
+    else
+	{
+    	fprintf(f, "%sret->%s = needMem(sizeof(*(ret->%s)));\n", indent, col->name, col->name);
+    	fprintf(f, "%s*(ret->%s%s) = sql%sComma(&s);\n", indent, col->name, arrayRef, lt->nummyName);
+	}
+    }
+}
+
+
+void loadColumn(struct asColumn *col, int colIx, boolean isDynamic, boolean isSizeLink,
+	FILE *f)
+/* Print statement to load column. */
+{
+char *staDyn = (isDynamic ? "Dynamic" : "Static");
+
+if (col->isSizeLink == isSizeLink)
+    {
+    struct asTypeInfo *lt = col->lowType;
+    enum asTypes type = lt->type;
+    struct asObject *obType = col->obType;
+    if (col->isList || col->isArray)
+	{
+	char *lName;
+	if ((lName = lt->listyName) == NULL)
+	    errAbort("Sorry, lists of %s not implemented.", lt->name);
+	if (obType)
+	    {
+            fprintf(f, "{\n");
+            fprintf(f, "int i;\n");
+	    fprintf(f, "char *s = row[%d];\n", colIx);
+	    if (col->fixedSize)
+		{
+		fprintf(f, "for (i=0; i<%d; ++i)\n", col->fixedSize);
+		}
+	    else
+		{
+		if (type == t_simple)
+		    {
+		    fprintf(f, "AllocArray(ret->%s, ret->%s);\n", col->name, 
+		    	col->linkedSize->name);
+		    }
+		fprintf(f, "for (i=0; i<ret->%s; ++i)\n", col->linkedSize->name);
+		}
+	    fprintf(f, "    {\n");
+	    fprintf(f, "    s = sqlEatChar(s, '{');\n");
+	    if (type == t_object)
+		{
+		fprintf(f, "    slSafeAddHead(&ret->%s, %sCommaIn(&s, NULL));\n",
+		    col->name, obType->name);
+		}
+	    else if (type == t_simple)
+		{
+		fprintf(f, "    %sCommaIn(&s, &ret->%s[i]);\n", obType->name, col->name);
+		}
+	    else
+		assert(FALSE);
+	    fprintf(f, "    s = sqlEatChar(s, '}');\n");
+	    fprintf(f, "    s = sqlEatChar(s, ',');\n");
+	    fprintf(f, "    }\n");
+	    if (type == t_object)
+		fprintf(f, "slReverse(&ret->%s);\n", col->name);
+            fprintf(f, "}\n");
+	    }
+	else
+	    {
+	    if (col->fixedSize)
+		{
+		if (isDynamic && lt->stringy)
+		    {
+                    fprintf(f, "{\n");
+		    fprintf(f, "char *s = cloneString(row[%d]);\n", colIx);
+		    fprintf(f, "sql%sArray(s, ret->%s, %d);\n",
+			       lName, col->name, col->fixedSize);
+                    fprintf(f, "}\n");
+		    }
+		else
+		    {
+		    fprintf(f, "sql%sArray(row[%d], ret->%s, %d);\n",
+			       lName, colIx, col->name, col->fixedSize);
+		    }
+		}
+	    else
+		{
+		struct asColumn *ls;
+		fprintf(f, "{\n");
+		fprintf(f, "int sizeOne;\n");
+		fprintf(f, "sql%s%sArray(row[%d], &ret->%s, &sizeOne);\n",
+			   lName, staDyn, colIx, col->name); 
+		if ((ls = col->linkedSize) != NULL)
+		    fprintf(f, "assert(sizeOne == ret->%s);\n", ls->name);
+		fprintf(f, "}\n");
+		}
+	    }
+	}
+    else
+	{
+	switch (type)
+	    {
+	    case t_float:
+		if (!withNull)
+		    {
+		    fprintf(f, "ret->%s = sqlFloat(row[%d]);\n", col->name, colIx);
+		    }
+		else
+		    {
+		    fprintf(f, "if (row[%d] != NULL)\n", colIx);
+		    fprintf(f, "    {\n");
+		    fprintf(f, "    ret->%s = needMem(sizeof(float));\n", col->name);
+		    fprintf(f, "    *(ret->%s) = sqlFloat(row[%d]);\n", col->name, colIx);
+		    fprintf(f, "    }\n");
+		    }
+
+		break;
+	    case t_double:
+		fprintf(f, "ret->%s = sqlDouble(row[%d]);\n", col->name, colIx);
+		break;
+	    case t_string:
+	    case t_lstring:
+		if (isDynamic)
+		    fprintf(f, "ret->%s = cloneString(row[%d]);\n", col->name, colIx);
+		else
+		    fprintf(f, "ret->%s = row[%d];\n", col->name, colIx);
+		break;
+	    case t_char:
+		if (col->fixedSize > 0)
+		    fprintf(f, "safecpy(ret->%s, sizeof(ret->%s), row[%d]);\n", col->name, col->name, colIx);
+		else
+		    fprintf(f, "ret->%s = row[%d][0];\n", col->name, colIx);
+		break;
+	    case t_object:
+		{
+		struct asObject *obj = col->obType;
+		fprintf(f, "{\n");
+		fprintf(f, "char *s = row[%d];\n", colIx);
+		fprintf(f, "if(s != NULL && differentString(s, \"\"))\n");
+		fprintf(f, "   ret->%s = %sCommaIn(&s, NULL);\n", col->name, obj->name);
+		fprintf(f, "}\n");
+		break;
+		}
+	    case t_simple:
+		{
+		struct asObject *obj = col->obType;
+		fprintf(f, "{\n");
+		fprintf(f, "char *s = row[%d];\n", colIx);
+		fprintf(f, "if(s != NULL && differentString(s, \"\"))\n");
+		fprintf(f, "   %sCommaIn(&s, &ret->%s);\n", obj->name, col->name);
+		fprintf(f, "}\n");
+		break;
+		}
+	    case t_enum:
+	    case t_set:
+		{
+		fprintf(f, "ret->%s = sql%sParse(row[%d], values_%s, &valhash_%s);\n", col->name,
+                        col->lowType->nummyName, colIx, col->name, col->name);
+		break;
+		}
+	    default:
+	        {
+		if (!withNull)
+		    {
+		    fprintf(f, "ret->%s = sql%s(row[%d]);\n", 
+		    	col->name, lt->nummyName, colIx);
+		    }
+		else
+		    {
+		    fprintf(f, "if (row[%d] != NULL)\n", colIx);
+		    fprintf(f, "    {\n");
+		    fprintf(f, "    ret->%s = needMem(sizeof(*(ret->%s)));\n", col->name, col->name);
+		    fprintf(f, "    *(ret->%s) = sql%s(row[%d]);\n", 
+		    	col->name, lt->nummyName, colIx);
+		    fprintf(f, "    }\n");
+		    fprintf(f, "else\n");
+		    fprintf(f, "    {\n");
+		    fprintf(f, "    ret->%s = NULL;\n", col->name);
+		    fprintf(f, "    }\n");
+		    }
+
+		break;
+		}
+	    }
+	}
+    }
+}
+
+void makeCommaIn(struct asObject *table, FILE *f, FILE *hFile)
+/* Make routine that loads object from comma separated file. */
+{
+char *tableName = table->name;
+struct asColumn *col;
+
+fprintf(hFile, "struct %s *%sCommaIn(char **pS, struct %s *ret);\n", 
+	tableName, tableName, tableName);
+fprintf(hFile, "/* Create a %s out of a comma separated string. \n", tableName);
+fprintf(hFile, " * This will fill in ret if non-null, otherwise will\n");
+fprintf(hFile, " * return a new %s */\n\n", tableName);
+
+fprintf(f, "struct %s *%sCommaIn(char **pS, struct %s *ret)\n", 
+	tableName, tableName, tableName);
+fprintf(f, "/* Create a %s out of a comma separated string. \n", tableName);
+fprintf(f, " * This will fill in ret if non-null, otherwise will\n");
+fprintf(f, " * return a new %s */\n", tableName);
+fprintf(f, "{\n");
+fprintf(f, "char *s = *pS;\n");
+fprintf(f, "\n");
+fprintf(f, "if (ret == NULL)\n");
+fprintf(f, "    AllocVar(ret);\n");
+
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    if (col->isList)
+	{
+        fprintf(f, "{\n");
+        fprintf(f, "int i;\n");
+	fprintf(f, "s = sqlEatChar(s, '{');\n");
+	if (col->fixedSize)
+	    {
+	    fprintf(f, "for (i=0; i<%d; ++i)\n", col->fixedSize);
+	    }
+	else
+	    {
+	    if (!col->obType)
+		fprintf(f, "AllocArray(ret->%s, ret->%s);\n", col->name, col->linkedSizeName);
+	    fprintf(f, "for (i=0; i<ret->%s; ++i)\n", col->linkedSizeName);
+	    }
+	fprintf(f, "    {\n");
+	makeCommaInColumn("    ", col, f, col->obType == NULL);
+	fprintf(f, "    }\n");
+	if (col->obType)
+	    fprintf(f, "slReverse(&ret->%s);\n", col->name);
+	fprintf(f, "s = sqlEatChar(s, '}');\n");
+	fprintf(f, "s = sqlEatChar(s, ',');\n");
+        fprintf(f, "}\n");
+	}
+    else if (col->isArray)
+	{
+        fprintf(f, "{\n");
+        fprintf(f, "int i;\n");
+	fprintf(f, "s = sqlEatChar(s, '{');\n");
+	if (col->fixedSize)
+	    {
+	    fprintf(f, "for (i=0; i<%d; ++i)\n", col->fixedSize);
+	    }
+	else
+	    {
+	    fprintf(f, "AllocArray(ret->%s, ret->%s);\n", col->name, col->linkedSizeName);
+	    fprintf(f, "for (i=0; i<ret->%s; ++i)\n", col->linkedSizeName);
+	    }
+	fprintf(f, "    {\n");
+	makeCommaInColumn("    ", col, f, TRUE);
+	fprintf(f, "    }\n");
+	fprintf(f, "s = sqlEatChar(s, '}');\n");
+	fprintf(f, "s = sqlEatChar(s, ',');\n");
+        fprintf(f, "}\n");
+	}
+    else
+	{
+	makeCommaInColumn("",col, f, FALSE);
+	}
+    }
+fprintf(f, "*pS = s;\n");
+fprintf(f, "return ret;\n");
+fprintf(f, "}\n\n");
+}
+
+
+boolean objectHasVariableLists(struct asObject *table)
+/* Returns TRUE if object has any list members. */
+{
+struct asColumn *col;
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    if ((col->isList || col->isArray) && !col->fixedSize)
+	return TRUE;
+    }
+return FALSE;
+}
+
+boolean objectHasSubObjects(struct asObject *table)
+/* Returns TRUE if object has any object members. */
+{
+struct asColumn *col;
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    if (col->lowType->type == t_object)
+	return TRUE;
+    }
+return FALSE;
+}
+
+void staticLoadRow(struct asObject *table, FILE *f, FILE *hFile)
+/* Create C code to load a static instance from a row.
+ * Only generated if no lists... */
+{
+int i;
+char *tableName = table->name;
+struct asColumn *col;
+boolean isSizeLink;
+int tfIx;
+
+if (!withNull)
+    {
+    fprintf(hFile, "void %sStaticLoad(char **row, struct %s *ret);\n", tableName, tableName);
+    }
+else
+    {
+    fprintf(hFile, "void %sStaticLoadWithNull(char **row, struct %s *ret);\n", tableName, tableName);
+    }
+
+fprintf(hFile, "/* Load a row from %s table into ret.  The contents of ret will\n", tableName);
+fprintf(hFile, " * be replaced at the next call to this function. */\n\n");
+
+if (!withNull)
+    {
+    fprintf(f, "void %sStaticLoad(char **row, struct %s *ret)\n", tableName, tableName);
+    }
+else
+    {
+    fprintf(f, "void %sStaticLoadWithNull(char **row, struct %s *ret)\n", tableName, tableName);
+    }
+fprintf(f, "/* Load a row from %s table into ret.  The contents of ret will\n", tableName);
+fprintf(f, " * be replaced at the next call to this function. */\n");
+fprintf(f, "{\n");
+fprintf(f, "\n");
+for (tfIx = 0; tfIx < 2; ++tfIx)
+    {
+    isSizeLink = trueFalse[tfIx];
+    for (i=0,col = table->columnList; col != NULL; col = col->next, ++i)
+	{
+	loadColumn(col, i, FALSE, isSizeLink, f);
+	}
+    }
+fprintf(f, "}\n\n");
+}
+
+void dynamicLoadRow(struct asObject *table, FILE *f, FILE *hFile)
+/* Create C code to load an instance from a row into dynamically
+ * allocated memory. */
+{
+int i;
+char *tableName = table->name;
+struct asColumn *col;
+boolean isSizeLink;
+int tfIx;
+
+if (!withNull)
+    {
+    fprintf(hFile, "struct %s *%sLoad(char **row);\n", tableName, tableName);
+    }
+else
+    {
+    fprintf(hFile, "struct %s *%sLoadWithNull(char **row);\n", tableName, tableName);
+    }
+
+fprintf(hFile, "/* Load a %s from row fetched with select * from %s\n", tableName, tableName);
+fprintf(hFile, " * from database.  Dispose of this with %sFree(). */\n\n", tableName);
+
+if (!withNull)
+    {
+    fprintf(f, "struct %s *%sLoad(char **row)\n", tableName, tableName);
+    }
+else
+    {
+    fprintf(f, "struct %s *%sLoadWithNull(char **row)\n", tableName, tableName);
+    }
+
+fprintf(f, "/* Load a %s from row fetched with select * from %s\n", tableName, tableName);
+fprintf(f, " * from database.  Dispose of this with %sFree(). */\n", tableName);
+fprintf(f, "{\n");
+fprintf(f, "struct %s *ret;\n", tableName);
+fprintf(f, "\n");
+fprintf(f, "AllocVar(ret);\n");
+for (tfIx = 0; tfIx < 2; ++tfIx)
+    {
+    isSizeLink = trueFalse[tfIx];
+    for (i=0,col = table->columnList; col != NULL; col = col->next, ++i)
+	{
+	loadColumn(col, i, TRUE, isSizeLink, f);
+	}
+    }
+fprintf(f, "return ret;\n");
+fprintf(f, "}\n\n");
+}
+
+void dynamicLoadAll(struct asObject *table, FILE *f, FILE *hFile)
+/* Create C code to load a all objects from a tab separated file. */
+{
+char *tableName = table->name;
+
+fprintf(hFile, "struct %s *%sLoadAll(char *fileName);\n", tableName, tableName);
+fprintf(hFile, "/* Load all %s from whitespace-separated file.\n", tableName);
+fprintf(hFile, " * Dispose of this with %sFreeList(). */\n\n", tableName);
+
+fprintf(f, "struct %s *%sLoadAll(char *fileName) \n", tableName, tableName);
+fprintf(f, "/* Load all %s from a whitespace-separated file.\n", tableName);
+fprintf(f, " * Dispose of this with %sFreeList(). */\n", tableName);
+fprintf(f, "{\n");
+fprintf(f, "struct %s *list = NULL, *el;\n", tableName);
+fprintf(f, "struct lineFile *lf = lineFileOpen(fileName, TRUE);\n");
+fprintf(f, "char *row[%d];\n", slCount(table->columnList));
+fprintf(f, "\n");
+fprintf(f, "while (lineFileRow(lf, row))\n");
+fprintf(f, "    {\n");
+if (!withNull)
+    {
+    fprintf(f, "    el = %sLoad(row);\n", tableName);
+    }
+else
+    {
+    fprintf(f, "    el = %sLoadWithNull(row);\n", tableName);
+    }
+
+fprintf(f, "    slAddHead(&list, el);\n");
+fprintf(f, "    }\n");
+fprintf(f, "lineFileClose(&lf);\n");
+fprintf(f, "slReverse(&list);\n");
+fprintf(f, "return list;\n");
+fprintf(f, "}\n\n");
+}
+
+void dynamicLoadAllByChar(struct asObject *table, FILE *f, FILE *hFile)
+/* Create C code to load a all objects from a tab separated file. */
+{
+char *tableName = table->name;
+
+fprintf(hFile, "struct %s *%sLoadAllByChar(char *fileName, char chopper);\n", tableName, tableName);
+fprintf(hFile, "/* Load all %s from chopper separated file.\n", tableName);
+fprintf(hFile, " * Dispose of this with %sFreeList(). */\n\n", tableName);
+
+fprintf(hFile, "#define %sLoadAllByTab(a) %sLoadAllByChar(a, '\\t');\n", tableName, tableName);
+fprintf(hFile, "/* Load all %s from tab separated file.\n", tableName);
+fprintf(hFile, " * Dispose of this with %sFreeList(). */\n\n", tableName);
+
+fprintf(f, "struct %s *%sLoadAllByChar(char *fileName, char chopper) \n", tableName, tableName);
+fprintf(f, "/* Load all %s from a chopper separated file.\n", tableName);
+fprintf(f, " * Dispose of this with %sFreeList(). */\n", tableName);
+fprintf(f, "{\n");
+fprintf(f, "struct %s *list = NULL, *el;\n", tableName);
+fprintf(f, "struct lineFile *lf = lineFileOpen(fileName, TRUE);\n");
+fprintf(f, "char *row[%d];\n", slCount(table->columnList));
+fprintf(f, "\n");
+fprintf(f, "while (lineFileNextCharRow(lf, chopper, row, ArraySize(row)))\n");
+fprintf(f, "    {\n");
+if (!withNull)
+    {
+    fprintf(f, "    el = %sLoad(row);\n", tableName);
+    }
+else
+    {
+    fprintf(f, "    el = %sLoadWithNull(row);\n", tableName);
+    }
+fprintf(f, "    slAddHead(&list, el);\n");
+fprintf(f, "    }\n");
+fprintf(f, "lineFileClose(&lf);\n");
+fprintf(f, "slReverse(&list);\n");
+fprintf(f, "return list;\n");
+fprintf(f, "}\n\n");
+}
+
+void dynamicLoadByQueryPrintPrototype(char *tableName, FILE *f, boolean addSemi)
+/* Print out function prototype and opening comment. */
+{
+fprintf(f, 
+   "struct %s *%sLoadByQuery(struct sqlConnection *conn, char *query)%s\n", 
+    tableName, tableName,
+    (addSemi ? ";" : ""));
+fprintf(f, "/* Load all %s from table that satisfy the query given.  \n", tableName);
+fprintf(f, " * Where query is of the form 'select * from example where something=something'\n");
+fprintf(f, " * or 'select example.* from example, anotherTable where example.something = \n");
+fprintf(f, " * anotherTable.something'.\n");
+fprintf(f, " * Dispose of this with %sFreeList(). */\n", tableName);
+}
+
+void dynamicLoadByQuery(struct asObject *table, FILE *f, FILE *hFile)
+/* Create C code to build a list from a query to database. */
+{
+char *tableName = table->name;
+dynamicLoadByQueryPrintPrototype(tableName, hFile, TRUE);
+fprintf(hFile, "\n");
+dynamicLoadByQueryPrintPrototype(tableName, f, FALSE);
+fprintf(f, "{\n");
+fprintf(f, "struct %s *list = NULL, *el;\n", tableName);
+fprintf(f, "struct sqlResult *sr;\n");
+fprintf(f, "char **row;\n");
+fprintf(f, "\n");
+fprintf(f, "sr = sqlGetResult(conn, query);\n");
+fprintf(f, "while ((row = sqlNextRow(sr)) != NULL)\n");
+fprintf(f, "    {\n");
+if (!withNull)
+    {
+    fprintf(f, "    el = %sLoad(row);\n", tableName);
+    }
+else
+    {
+    fprintf(f, "    el = %sLoadWithNull(row);\n", tableName);
+    }
+
+fprintf(f, "    slAddHead(&list, el);\n");
+fprintf(f, "    }\n");
+fprintf(f, "slReverse(&list);\n");
+fprintf(f, "sqlFreeResult(&sr);\n");
+fprintf(f, "return list;\n");
+fprintf(f, "}\n\n");
+}
+
+void dynamicSaveToDbPrintPrototype(char *tableName, FILE *f, boolean addSemi)
+/* Print out function prototype and opening comment. */
+{
+fprintf(f,
+	"void %sSaveToDb(struct sqlConnection *conn, struct %s *el, char *tableName, int updateSize)%s\n", 
+	tableName, tableName, (addSemi ? ";" : ""));
+fprintf(f,
+	"/* Save %s as a row to the table specified by tableName. \n", tableName);
+fprintf(f, " * As blob fields may be arbitrary size updateSize specifies the approx size\n");
+fprintf(f, " * of a string that would contain the entire query. Arrays of native types are\n");
+fprintf(f, " * converted to comma separated strings and loaded as such, User defined types are\n");
+fprintf(f, " * inserted as NULL. Note that strings must be escaped to allow insertion into the database.\n");
+fprintf(f, " * For example \"autosql's features include\" --> \"autosql\\'s features include\" \n");
+fprintf(f, " * If worried about this use %sSaveToDbEscaped() */\n", tableName);
+}
+
+boolean lastArrayType(struct asColumn *colList)
+/* if there are any more string types returns TRUE else returns false */
+{
+struct asColumn *col;
+for(col = colList; col != NULL; col = col->next)
+    {
+    struct asTypeInfo *lt = col->lowType;
+    enum asTypes type = lt->type;
+    if((col->isArray || col->isList) && type != t_object && type != t_simple)
+	return FALSE;
+    }
+return TRUE;
+}
+
+boolean noMoreColumnsToInsert(struct asColumn *colList)
+{
+struct asColumn *col;
+for(col = colList; col != NULL; col = col->next)
+    {
+    if(col->obType == NULL)
+	return FALSE;
+    }
+return TRUE;
+}
+
+void dynamicSaveToDb(struct asObject *table, FILE *f, FILE *hFile)
+/* create C code that will save a table structure to the database */
+{
+char *tableName = table->name;
+struct asColumn *col;
+struct dyString *colInsert = newDyString(1024);           /* code to associate columns with printf format characters the insert statement */
+struct dyString *stringDeclarations = newDyString(1024);  /* code to declare necessary strings */
+struct dyString *stringFrees = newDyString(1024);         /* code to free necessary strings */
+struct dyString *update = newDyString(1024);              /* code to do the update statement itself */
+struct dyString *stringArrays = newDyString(1024);        /* code to convert arrays to strings */
+boolean hasArray = FALSE;
+dynamicSaveToDbPrintPrototype(tableName, hFile, TRUE);
+fprintf(hFile, "\n");
+dynamicSaveToDbPrintPrototype(tableName, f, FALSE);
+fprintf(f, "{\n");
+fprintf(f, "struct dyString *update = newDyString(updateSize);\n");
+dyStringPrintf(update, "dyStringPrintf(update, \"insert into %%s values ( ");
+dyStringPrintf(stringDeclarations, "char ");
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    char *colName = col->name;
+    char *outString = NULL; /* printf formater for column, i.e. %d for int, '%s' for string */
+    struct asTypeInfo *lt = col->lowType;
+    enum asTypes type = lt->type;
+    char colInsertBuff[256]; /* what variable name  matches up with the printf format character in outString */
+    boolean colInsertFlag = TRUE; /* if column is not a native type insert NULL with no associated variable */
+    switch(type)
+	{
+	case t_char:
+	    outString = (col->fixedSize > 0) ? "'%s'" : "'%c'";
+	    break;
+	case t_string:
+	    outString = "'%s'";
+	    break;
+	default:
+	    outString = lt->outFormat;
+	    break;
+	}
+    
+    switch(type)
+	{
+	case t_char:
+	case t_string:
+	case t_lstring:
+    	    sprintf(colInsertBuff, " el->%s", colName);
+	    break;
+	default:
+    	    if (withNull)
+		sprintf(colInsertBuff, " *(el->%s)", colName);
+	    else
+    	    	sprintf(colInsertBuff, " el->%s", colName);
+	    break;
+	}
+
+    /* it gets pretty ugly here as we have to handle arrays of objects.. */
+    if(col->isArray || col->isList || type == t_object || type == t_simple)
+	{
+	/* if we have a basic array type convert it to a string representation and insert into db */
+	if(type != t_object && type != t_simple )
+	    {
+	    hasArray = TRUE;
+	    outString = "'%s'";
+	    /* if this is the last array put a semi otherwise a comment */
+	    if(lastArrayType(col->next))
+		dyStringPrintf(stringDeclarations, " *%sArray;", colName);
+	    else 
+		dyStringPrintf(stringDeclarations, " *%sArray,", colName);
+	    /* set up call to convert array to char * */
+	    if(col->fixedSize)
+		dyStringPrintf(stringArrays, "%sArray = sql%sArrayToString(el->%s, %d);\n", 
+			       colName, lt->listyName, colName, col->fixedSize);
+	    else
+		dyStringPrintf(stringArrays, "%sArray = sql%sArrayToString(el->%s, el->%s);\n", 
+			       colName, lt->listyName, colName, col->linkedSizeName);
+	    /* code to free allocated strings */
+	    dyStringPrintf(stringFrees, "freez(&%sArray);\n", colName);
+	    sprintf(colInsertBuff, " %sArray ", colName);
+	    }
+	/* if we have an object, or simple data type just insert NULL,
+	 * don't wrap the whole thing up into one string.*/
+	else
+	    {
+	    warn("The user defined type \"%s\" in table \"%s\" will be saved to the database as NULL.", 
+		 col->obType->name, tableName);
+	    outString = " NULL ";
+	    colInsertFlag = FALSE;
+	    }
+	}
+    /* can't have a comma at the end of the list */
+    if(col->next == NULL)
+	dyStringPrintf(update, "%s", outString);
+    else
+	dyStringPrintf(update, "%s,",outString);
+
+    /* if we still have more columns to insert add a comma */
+    if(!noMoreColumnsToInsert(col->next))
+	strcat(colInsertBuff, ", ");
+
+    /* if we have a column to append do so */
+    if(colInsertFlag)
+	dyStringPrintf(colInsert, "%s", colInsertBuff);
+    }
+if(hasArray)
+    {
+    fprintf(f, "%s\n", stringDeclarations->string);
+    }
+fprintf(f, "%s", stringArrays->string);
+fprintf(f, "%s", update->string);
+fprintf(f, ")\", \n\ttableName, ");
+fprintf(f, "%s);\n", colInsert->string);
+fprintf(f, "sqlUpdate(conn, update->string);\n");
+fprintf(f, "freeDyString(&update);\n");
+if(hasArray)
+    {
+    fprintf(f, "%s", stringFrees->string);
+    }
+fprintf(f, "}\n\n");
+dyStringFree(&colInsert);
+dyStringFree(&stringDeclarations);
+dyStringFree(&stringFrees);
+dyStringFree(&update);
+}
+
+void dynamicSaveToDbEscapedPrintPrototype(char *tableName, FILE *f, boolean addSemi)
+/* Print out function prototype and opening comment. */
+{
+fprintf(f,
+	"void %sSaveToDbEscaped(struct sqlConnection *conn, struct %s *el, char *tableName, int updateSize)%s\n", 
+	tableName, tableName, (addSemi ? ";" : ""));
+fprintf(f,
+	"/* Save %s as a row to the table specified by tableName. \n", tableName);
+fprintf(f, " * As blob fields may be arbitrary size updateSize specifies the approx size.\n");
+fprintf(f, " * of a string that would contain the entire query. Automatically \n");
+fprintf(f, " * escapes all simple strings (not arrays of string) but may be slower than %sSaveToDb().\n", tableName);
+fprintf(f, " * For example automatically copies and converts: \n");
+fprintf(f, " * \"autosql's features include\" --> \"autosql\\'s features include\" \n");
+fprintf(f, " * before inserting into database. */ \n");
+}
+
+boolean lastStringType(struct asColumn *colList)
+/* if there are any more string types returns TRUE else returns false */
+{
+struct asColumn *col;
+for(col = colList; col != NULL; col = col->next)
+    {
+    struct asTypeInfo *lt = col->lowType;
+    enum asTypes type = lt->type;
+    if(type == t_char || type == t_string || type == t_lstring || ((col->isArray || col->isList) && type != t_object && type != t_simple))
+	return FALSE;
+    }
+return TRUE;
+}
+
+void dynamicSaveToDbEscaped(struct asObject *table, FILE *f, FILE *hFile)
+/* create C code that will save a table structure to the database with 
+ * all strings escaped. */
+{
+char *tableName = table->name;
+struct asColumn *col;
+/* We need to do a lot of things with the string datatypes use
+ * these buffers to only cycle through columns once */
+struct dyString *colInsert = newDyString(1024);          /* code to associate columns with printf format characters the insert statement */
+struct dyString *stringDeclarations = newDyString(1024); /* code to declare necessary strings */
+struct dyString *stringFrees = newDyString(1024);        /* code to free necessary strings */
+struct dyString *update = newDyString(1024);             /* code to do the update statement itself */
+struct dyString *stringEscapes = newDyString(1024);      /* code to escape strings */
+struct dyString *stringArrays = newDyString(1024);       /* code to convert arrays to strings */
+boolean hasString = FALSE;
+boolean hasArray = FALSE;
+dynamicSaveToDbEscapedPrintPrototype(tableName, hFile, TRUE);
+fprintf(hFile, "\n");
+dynamicSaveToDbEscapedPrintPrototype(tableName, f, FALSE);
+fprintf(f, "{\n");
+fprintf(f, "struct dyString *update = newDyString(updateSize);\n");
+dyStringPrintf(update, "dyStringPrintf(update, \"insert into %%s values ( ");
+dyStringPrintf(stringDeclarations, "char ");
+
+/* loop through each of the columns and add things appropriately */
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    char *colName = col->name;
+    char *outString = NULL; /* printf formater for column, i.e. %d for int, '%s' for string */
+    struct asTypeInfo *lt = col->lowType;
+    enum asTypes type = lt->type;
+    char colInsertBuff[256]; /* what variable name  matches up with the printf format character in outString */
+    boolean colInsertFlag = TRUE; /* if column is not a native type insert NULL with no associated variable */
+    switch(type)
+	{
+	case t_char:
+	case t_string:
+	case t_lstring:
+	    /* if of string type have to do all the work of declaring, escaping,
+	     * and freeing */
+	    if(!col->isArray && !col->isList)
+		{
+		hasString = TRUE;
+		outString = "'%s'";
+		/* code to escape strings */
+		dyStringPrintf(stringEscapes, "%s = sqlEscapeString(el->%s);\n", colName, colName);
+		/* code to free strings */
+		dyStringPrintf(stringFrees, "freez(&%s);\n", colName);
+		sprintf(colInsertBuff, " %s", colName);
+		if(lastStringType(col->next))
+		    dyStringPrintf(stringDeclarations, " *%s;", colName);
+		else 
+		    dyStringPrintf(stringDeclarations, " *%s,", colName);
+		}
+	    break;
+	default:
+	    outString = lt->outFormat;
+    	    if (withNull)
+		sprintf(colInsertBuff, " *(el->%s)", colName);
+	    else
+    	    	sprintf(colInsertBuff, " el->%s", colName);
+	    break;
+	}
+    if(col->isArray || col->isList || type == t_object || type == t_simple)
+	{
+	/* if we have a basic array type convert it to a string representation and insert into db */
+	if(type != t_object && type != t_simple )
+	    {
+	    hasArray = TRUE;
+	    outString = "'%s'";
+	    if(lastStringType(col->next) && lastArrayType(col->next))
+		dyStringPrintf(stringDeclarations, " *%sArray;", colName);
+	    else 
+		dyStringPrintf(stringDeclarations, " *%sArray,", colName);
+	    if(col->fixedSize)
+		dyStringPrintf(stringArrays, "%sArray = sql%sArrayToString(el->%s, %d);\n", colName, lt->listyName, colName, col->fixedSize);
+	    else
+		dyStringPrintf(stringArrays, "%sArray = sql%sArrayToString(el->%s, el->%s);\n", colName, lt->listyName, colName, col->linkedSizeName);
+	    dyStringPrintf(stringFrees, "freez(&%sArray);\n", colName);
+	    sprintf(colInsertBuff, " %sArray ", colName);
+	    }
+	/* if we have an object, or simple data type just insert NULL, don't wrap the whole thing up into one string.*/
+	else
+	    {
+	    warn("The user defined type \"%s\" in table \"%s\" will be saved to the database as NULL.", col->obType->name, tableName);
+	    outString = " NULL ";
+	    colInsertFlag = FALSE;
+	    }
+	}
+    /* can't have comma at the end of the insert */
+    if(col->next == NULL)
+	dyStringPrintf(update, "%s", outString);
+    else
+	dyStringPrintf(update, "%s,",outString);
+
+    /* if we still have more columns to insert add a comma */
+    if(!noMoreColumnsToInsert(col->next))
+	strcat(colInsertBuff, ", ");
+    /* if we have a column to append do so */
+    if(colInsertFlag)
+	dyStringPrintf(colInsert, "%s", colInsertBuff);
+    }
+if(hasString || hasArray)
+    {
+    fprintf(f, "%s\n", stringDeclarations->string);
+    fprintf(f, "%s\n", stringEscapes->string);
+    }
+fprintf(f, "%s", stringArrays->string);
+fprintf(f, "%s", update->string);
+fprintf(f, ")\", \n\ttableName, ");
+fprintf(f, "%s);\n", colInsert->string);
+fprintf(f, "sqlUpdate(conn, update->string);\n");
+fprintf(f, "freeDyString(&update);\n");
+if(hasString)
+    {
+    fprintf(f, "%s", stringFrees->string);
+    }
+fprintf(f, "}\n\n");
+dyStringFree(&colInsert);
+dyStringFree(&stringDeclarations);
+dyStringFree(&stringEscapes);
+dyStringFree(&stringFrees);
+dyStringFree(&update);
+}
+
+boolean internalsNeedFree(struct asObject *table)
+/* Return TRUE if the fields of a table contain strings,
+ * dynamically sized arrays, objects, or anything else that
+ * needs freeing. */
+{
+struct asColumn *col;
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    struct asTypeInfo *lt = col->lowType;
+    enum asTypes type = lt->type;
+    struct asObject *obj;
+
+    if ((obj = col->obType) != NULL)
+	{
+	if (type == t_object)
+	    return TRUE;
+	else if (type == t_simple)
+	    {
+	    if (internalsNeedFree(obj))
+	        return TRUE;
+	    if (col->isArray && !col->fixedSize)
+		return TRUE;
+	    }
+	}
+    else
+	{
+	if (col->isList)
+	    {
+	    if (lt->stringy)
+		return TRUE;
+	    if (!col->fixedSize)
+		return TRUE;
+	    }
+	else
+	    {
+	    if (lt->stringy)
+		return TRUE;
+	    }
+	}
+    }
+return FALSE;
+}
+
+void printIndent(int spaces, FILE *f, char *format, ...)
+/* Write out to file indented by spaces. */
+{
+va_list args;
+va_start(args, format);
+spaceOut(f, spaces);
+vfprintf(f, format, args);
+va_end(args);
+}
+
+void freeFields(struct asObject *table, int spaces, FILE *f)
+/* Write out code to free fields of a table. */
+{
+struct asColumn *col;
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    struct asTypeInfo *lt = col->lowType;
+    enum asTypes type = lt->type;
+    char *colName = col->name;
+    struct asObject *obj;
+
+    if ((obj = col->obType) != NULL)
+	{
+	if (type == t_object)
+	    {
+	    printIndent(spaces, f, "%sFreeList(&el->%s);\n", obj->name, colName);
+	    }
+	else if (type == t_simple)
+	    {
+	    if (internalsNeedFree(obj))
+	        {
+		if (col->isArray)
+		    {
+		    if (col->fixedSize)
+			{
+			printIndent(spaces, f, "%sFreeInternals(el->%s, ArraySize(el->%s));\n",
+				obj->name, colName, colName);
+			}
+		    else
+		        {
+			printIndent(spaces, f, "%sFreeInternals(el->%s, el->%s);\n",
+				obj->name, colName, col->linkedSizeName);
+			printIndent(spaces, f, "freeMem(el->%s);\n", colName);
+			}
+		    }
+		else
+		    {
+		    printIndent(spaces, f, "%sFreeInternals(&el->%s, 1);\n", obj->name, colName);
+		    }
+		}
+	    else
+		{
+		if (col->isArray && !col->fixedSize)
+		    {
+		    printIndent(spaces, f, "freeMem(el->%s);\n", colName);
+		    }
+		}
+	    }
+	}
+    else
+	{
+	if (col->isList)
+	    {
+	    if (lt->stringy)
+		{
+		printIndent(spaces, f, "/* All strings in %s are allocated at once, so only need to free first. */\n", colName);
+		printIndent(spaces, f, "if (el->%s != NULL)\n", colName);
+		printIndent(spaces, f, "    freeMem(el->%s[0]);\n", colName);
+		}
+	    if (!col->fixedSize)
+		{
+		printIndent(spaces, f, "freeMem(el->%s);\n", colName);
+		}
+	    }
+	else
+	    {
+	    if (lt->stringy)
+		{
+		printIndent(spaces, f, "freeMem(el->%s);\n", colName);
+		}
+	    }
+	}
+    }
+}
+
+void makeFreeInternals(struct asObject *table, FILE *f, FILE *hFile)
+/* Make a function that frees the internal parts if any of a simple object 
+ * (or array of simple objects). */
+{
+char *tableName = table->name;
+
+fprintf(hFile, "void %sFreeInternals(struct %s *array, int count);\n", tableName, tableName);
+fprintf(hFile, "/* Free internals of a simple type %s (one not put on a list). */\n\n", tableName);
+
+fprintf(f, "void %sFreeInternals(struct %s *array, int count)\n", tableName, tableName);
+fprintf(f, "/* Free internals of a simple type %s (one not put on a list). */\n", tableName);
+fprintf(f, "{\n");
+fprintf(f, "int i;\n");
+fprintf(f, "for (i=0; i<count; ++i)\n");
+fprintf(f, "    {\n");
+fprintf(f, "    struct %s *el = &array[i];\n", tableName);
+freeFields(table, 4, f);
+fprintf(f, "    }\n");
+fprintf(f, "}\n\n");
+}
+
+
+void makeFree(struct asObject *table, FILE *f, FILE *hFile)
+/* Make function that frees a dynamically allocated table. */
+{
+char *tableName = table->name;
+
+fprintf(hFile, "void %sFree(struct %s **pEl);\n", tableName, tableName);
+fprintf(hFile, "/* Free a single dynamically allocated %s such as created\n", tableName);
+fprintf(hFile, " * with %sLoad(). */\n\n", tableName);
+
+fprintf(f, "void %sFree(struct %s **pEl)\n", tableName, tableName);
+fprintf(f, "/* Free a single dynamically allocated %s such as created\n", tableName);
+fprintf(f, " * with %sLoad(). */\n", tableName);
+fprintf(f, "{\n");
+fprintf(f, "struct %s *el;\n", tableName);
+fprintf(f, "\n");
+fprintf(f, "if ((el = *pEl) == NULL) return;\n");
+freeFields(table, 0, f);
+fprintf(f, "freez(pEl);\n");
+fprintf(f, "}\n\n");
+}
+
+void makeFreeList(struct asObject *table, FILE *f, FILE *hFile)
+/* Make function that frees a list of dynamically table. */
+{
+char *name = table->name;
+
+fprintf(hFile, "void %sFreeList(struct %s **pList);\n", name, name);
+fprintf(hFile, "/* Free a list of dynamically allocated %s's */\n\n", name);
+
+fprintf(f, "void %sFreeList(struct %s **pList)\n", name, name);
+fprintf(f, "/* Free a list of dynamically allocated %s's */\n", name);
+fprintf(f, "{\n");
+fprintf(f, "struct %s *el, *next;\n", name);
+fprintf(f, "\n");
+fprintf(f, "for (el = *pList; el != NULL; el = next)\n");
+fprintf(f, "    {\n");
+fprintf(f, "    next = el->next;\n");
+fprintf(f, "    %sFree(&el);\n", name);
+fprintf(f, "    }\n");
+fprintf(f, "*pList = NULL;\n");
+fprintf(f, "}\n\n");
+}
+
+void makeArrayColOutput(struct asColumn *col, boolean mightNeedQuotes,
+                        char *outString, char *lineEnd, FILE *f)
+/* Make code that prints one array or list column to a tab delimited file. */
+{
+struct asTypeInfo *lt = col->lowType;
+struct asObject *obType = col->obType;
+char *indent = "";
+fprintf(f, "{\n");
+fprintf(f, "int i;\n");
+if (obType != NULL)
+    {
+    fprintf(f, "/* Loading %s list. */\n", obType->name);
+    fprintf(f, "    {\n    struct %s *it = el->%s;\n", 
+        obType->name, col->name);
+    indent = "    ";
+    }
+fprintf(f, "%sif (sep == ',') fputc('{',f);\n", indent);
+if (col->fixedSize)
+    fprintf(f, "%sfor (i=0; i<%d; ++i)\n", indent, col->fixedSize);
+else
+    fprintf(f, "%sfor (i=0; i<el->%s; ++i)\n", indent, col->linkedSize->name);
+fprintf(f, "%s    {\n", indent);
+if (lt->type == t_object)
+    {
+    fprintf(f, "%s    fputc('{',f);\n", indent);
+    fprintf(f, "%s    %sCommaOut(it,f);\n", indent, obType->name);
+    fprintf(f, "%s    it = it->next;\n", indent);
+    fprintf(f, "%s    fputc('}',f);\n", indent);
+    fprintf(f, "%s    fputc(',',f);\n", indent);
+    }
+else if (lt->type == t_simple)
+    {
+    fprintf(f, "%s    fputc('{',f);\n", indent);
+    fprintf(f, "%s    %sCommaOut(&it[i],f);\n", indent, obType->name);
+    fprintf(f, "%s    fputc('}',f);\n", indent);
+    fprintf(f, "%s    fputc(',',f);\n", indent);
+    }
+else
+    {
+    if (mightNeedQuotes)
+        fprintf(f, "%s    if (sep == ',') fputc('\"',f);\n", indent);
+    fprintf(f, "%s    fprintf(f, \"%s\", el->%s[i]);\n", indent, outString, 
+            col->name);
+    if (mightNeedQuotes)
+        fprintf(f, "%s    if (sep == ',') fputc('\"',f);\n", indent);
+    fprintf(f, "%s    fputc(',', f);\n", indent);
+    }
+fprintf(f, "%s    }\n", indent);
+fprintf(f, "%sif (sep == ',') fputc('}',f);\n", indent);
+if (obType != NULL)
+    fprintf(f, "    }\n");
+fprintf(f, "}\n");
+fprintf(f, "fputc(%s,f);\n", lineEnd);
+}
+
+void makeArrayColJsonOutput(struct asColumn *col, boolean mightNeedQuotes,
+                        		char *outString, FILE *f)
+/* Make code that prints one array or list column to a tab delimited file. */
+{
+struct asTypeInfo *lt = col->lowType;
+struct asObject *obType = col->obType;
+char *indent = "";
+fprintf(f, "{\n");
+fprintf(f, "int i;\n");
+if (obType != NULL)
+    {
+    fprintf(f, "/* Loading %s list. */\n", obType->name);
+    fprintf(f, "    {\n    struct %s *it = el->%s;\n",        obType->name, col->name);
+    indent = "    ";
+    }
+fprintf(f, "%sfputc('[',f);\n", indent);
+if (col->fixedSize)
+    fprintf(f, "%sfor (i=0; i<%d; ++i)\n", indent, col->fixedSize);
+else
+    fprintf(f, "%sfor (i=0; i<el->%s; ++i)\n", indent, col->linkedSize->name);
+fprintf(f, "%s    {\n", indent);
+if (lt->type == t_object)
+    {
+    fprintf(f, "%s    %sJsonOutput(it,f);\n", indent, obType->name);
+    fprintf(f, "%s    it = it->next;\n", indent);
+    }
+else if (lt->type == t_simple)
+    {
+    fprintf(f, "%s    %sJsonOutput(&it[i],f);\n", indent, obType->name);
+    }
+else
+    {
+    if (mightNeedQuotes)
+        fprintf(f, "%s    fputc('\"',f);\n", indent);
+    fprintf(f, "%s    fprintf(f, \"%s\", el->%s[i]);\n", indent, outString,
+            col->name);
+    if (mightNeedQuotes)
+        fprintf(f, "%s    fputc('\"',f);\n", indent);
+    }
+if (col->fixedSize)
+    fprintf(f, "%s    if (i<%d)\n", indent, (col->fixedSize)-1);
+else
+    fprintf(f, "%s    if (i<(el->%s)-1)\n", indent, col->linkedSize->name);
+fprintf(f, "%s%s    fputc(',',f);\n", indent, indent);
+fprintf(f, "%s    }\n", indent);
+fprintf(f, "%sfputc(']',f);\n", indent);
+if (obType != NULL)
+    fprintf(f, "    }\n");
+fprintf(f, "}\n");
+}
+
+void makeScalarColOutput(struct asColumn *col, boolean mightNeedQuotes,
+                         char *outString, char *lineEnd, FILE *f)
+/* Make code that prints one scalar column to a tab delimited file. */
+{
+struct asTypeInfo *lt = col->lowType;
+if (lt->type == t_object)
+    {
+    struct asObject *obj = col->obType;
+    fprintf(f, "if (sep == ',') fputc('{',f);\n");
+    fprintf(f, "if(el->%s != NULL)", col->name);
+    fprintf(f, "    %sCommaOut(el->%s,f);\n", obj->name, col->name);
+    fprintf(f, "if (sep == ',') fputc('}',f);\n");
+    fprintf(f, "fputc(%s,f);\n", lineEnd);
+    }
+else if (lt->type == t_simple)
+    {
+    struct asObject *obj = col->obType;
+    fprintf(f, "if (sep == ',') fputc('{',f);\n");
+    fprintf(f, "%sCommaOut(&el->%s,f);\n", obj->name, col->name);
+    fprintf(f, "if (sep == ',') fputc('}',f);\n");
+    fprintf(f, "fputc(%s,f);\n", lineEnd);
+    }
+else
+    {
+    if (mightNeedQuotes)
+        fprintf(f, "if (sep == ',') fputc('\"',f);\n");
+    if (!withNull)
+	{
+    	fprintf(f, "fprintf(f, \"%s\", el->%s);\n", outString, 
+            col->name);
+	}
+    else
+	{
+    	if (! ((lt->type == t_string) || (lt->type == t_lstring)) )
+	    {
+	    fprintf(f, "fprintf(f, \"%s\", *(el->%s));\n", outString, 
+            	col->name);
+	    }
+	else
+	    {
+	    fprintf(f, "fprintf(f, \"%s\", el->%s);\n", outString, 
+            	col->name);
+	    }
+	}
+
+    if (mightNeedQuotes)
+        fprintf(f, "if (sep == ',') fputc('\"',f);\n");
+    fprintf(f, "fputc(%s,f);\n", lineEnd);
+    }
+}
+
+void makeScalarColJsonOutput(struct asColumn *col, boolean mightNeedQuotes,
+                         			char *outString, FILE *f)
+/* Make code that prints one scalar column to a tab delimited file. */
+{
+struct asTypeInfo *lt = col->lowType;
+if (lt->type == t_object)
+    {
+    struct asObject *obj = col->obType;
+    fprintf(f, "if(el->%s != NULL)", col->name);
+    fprintf(f, "    %sJsonOutput(el->%s,f);\n", obj->name, col->name);
+    }
+else if (lt->type == t_simple)
+    {
+    struct asObject *obj = col->obType;
+    fprintf(f, "%sJsonOutput(&el->%s,f);\n", obj->name, col->name);
+    }
+else
+    {
+    if (mightNeedQuotes)
+        fprintf(f, "fputc('\"',f);\n");
+    if (!withNull)
+        {
+        fprintf(f, "fprintf(f, \"%s\", el->%s);\n", outString,
+            col->name);
+        }
+    else
+        {
+        if (! ((lt->type == t_string) || (lt->type == t_lstring)) )
+            {
+            fprintf(f, "fprintf(f, \"%s\", *(el->%s));\n", outString,
+                col->name);
+            }
+        else
+            {
+            fprintf(f, "fprintf(f, \"%s\", el->%s);\n", outString,
+                col->name);
+            }
+        }
+
+    if (mightNeedQuotes)
+        fprintf(f, "fputc('\"',f);\n");
+    }
+}
+
+void makeSymColOutput(struct asColumn *col, char *lineEnd, FILE *f)
+/* Make code that prints one symbolic column to a tab delimited file. */
+{
+fprintf(f, "if (sep == ',') fputc('\"',f);\n");
+fprintf(f, "sql%sPrint(f, el->%s, values_%s);\n",
+        col->lowType->nummyName, col->name, col->name);
+fprintf(f, "if (sep == ',') fputc('\"',f);\n");
+fprintf(f, "fputc(%s,f);\n", lineEnd);
+}
+
+void makeSymColJsonOutput(struct asColumn *col, FILE *f)
+/* Make code that prints one symbolic column to a tab delimited file. */
+{
+fprintf(f, "fputc('\"',f);\n");
+fprintf(f, "sql%sPrint(f, el->%s, values_%s);\n",
+        col->lowType->nummyName, col->name, col->name);
+fprintf(f, "fputc('\"',f);\n");
+}
+
+void makeColOutput(struct asColumn *col, FILE *f)
+/* Make code that prints one column to a tab delimited file. */
+{
+char *outString = NULL;
+struct asTypeInfo *lt = col->lowType;
+enum asTypes type = lt->type;
+char *lineEnd = (col->next != NULL ? "sep" : "lastSep");
+boolean mightNeedQuotes = FALSE;
+
+switch(type)
+    {
+    case t_char:
+        outString = (col->fixedSize > 0) ? "%s" : "%c";
+        mightNeedQuotes = TRUE;
+        break;
+    case t_string:
+    case t_lstring:
+        outString = "%s";
+        mightNeedQuotes = TRUE;
+        break;
+    default:
+        outString = lt->outFormat;
+        break;
+    }
+
+if (col->isList || col->isArray)
+    makeArrayColOutput(col, mightNeedQuotes, outString, lineEnd, f);
+else if ((lt->type == t_enum) || (lt->type == t_set))
+    makeSymColOutput(col, lineEnd, f);
+else
+    makeScalarColOutput(col, mightNeedQuotes, outString, lineEnd, f);
+}
+
+void makeColJsonOutput(struct asColumn *col, FILE *f)
+/* Make code that prints one column to a tab delimited file. */
+{
+char *outString = NULL;
+struct asTypeInfo *lt = col->lowType;
+enum asTypes type = lt->type;
+boolean mightNeedQuotes = FALSE;
+
+switch(type)
+    {
+    case t_char:
+        outString = (col->fixedSize > 0) ? "%s" : "%c";
+        mightNeedQuotes = TRUE;
+        break;
+    case t_string:
+    case t_lstring:
+        outString = "%s";
+        mightNeedQuotes = TRUE;
+        break;
+    default:
+        outString = lt->outFormat;
+        break;
+    }
+
+fprintf(f, "fputc('\"',f);\n");
+fprintf(f, "fprintf(f,\"%s\");\n", col->name);
+fprintf(f, "fputc('\"',f);\n");
+fprintf(f, "fputc(':',f);\n");
+if (col->isList || col->isArray)
+    makeArrayColJsonOutput(col, mightNeedQuotes, outString, f);
+else if ((lt->type == t_enum) || (lt->type == t_set))
+    makeSymColJsonOutput(col, f);
+else
+    makeScalarColJsonOutput(col, mightNeedQuotes, outString, f);
+}
+
+void makeOutput(struct asObject *table, FILE *f, FILE *hFile)
+/* Make function that prints table to tab delimited file. */
+{
+char *tableName = table->name;
+struct asColumn *col;
+
+fprintf(hFile, 
+  "void %sOutput(struct %s *el, FILE *f, char sep, char lastSep);\n", tableName, tableName);
+fprintf(hFile, 
+  "/* Print out %s.  Separate fields with sep. Follow last field with lastSep. */\n\n", 
+  tableName);
+
+fprintf(f, 
+  "void %sOutput(struct %s *el, FILE *f, char sep, char lastSep) \n", tableName, tableName);
+fprintf(f, 
+  "/* Print out %s.  Separate fields with sep. Follow last field with lastSep. */\n", 
+  tableName);
+
+fprintf(hFile, 
+   "#define %sTabOut(el,f) %sOutput(el,f,'\\t','\\n');\n", tableName, tableName);
+fprintf(hFile, 
+   "/* Print out %s as a line in a tab-separated file. */\n\n", tableName);
+
+fprintf(hFile, 
+   "#define %sCommaOut(el,f) %sOutput(el,f,',',',');\n", tableName, tableName);
+fprintf(hFile, 
+   "/* Print out %s as a comma separated list including final comma. */\n\n", 
+   	tableName);
+
+fprintf(f, "{\n");
+for (col = table->columnList; col != NULL; col = col->next)
+    makeColOutput(col, f);
+fprintf(f, "}\n\n");
+}
+
+void makeJsonOutput(struct asObject *table, FILE *f, FILE *hFile)
+/* Make function that prints table in JSON format. */
+{
+char *tableName = table->name;
+struct asColumn *col;
+
+fprintf(hFile,
+  "void %sJsonOutput(struct %s *el, FILE *f);\n", tableName, tableName);
+fprintf(hFile, "/* Print out %s in JSON format. */\n\n", tableName);
+
+fprintf(f,
+  "void %sJsonOutput(struct %s *el, FILE *f) \n", tableName, tableName);
+fprintf(f, "/* Print out %s in JSON format. */\n", tableName);
+
+fprintf(f, "{\n");
+fprintf(f, "fputc('{',f);\n");
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    makeColJsonOutput(col, f);
+    if (col->next != NULL)
+        fprintf(f, "fputc(',',f);\n");
+    }
+fprintf(f, "fputc('}',f);\n");
+fprintf(f, "}\n\n");
+}
+
+void makeDjangoOutput(struct asObject *table, FILE *f, FILE *hFile)
+/* Make function that prints table as django model in Python code */
+{
+char *tableName = table->name;
+struct asColumn *col;
+
+fprintf(hFile,
+  "void %sDjangoOutput(struct %s *el, FILE *f);\n", tableName, tableName);
+fprintf(hFile, "/* Print out %s as Django model in python. */\n\n", tableName);
+
+fprintf(f,
+  "void %sDjangoOutput(struct %s *el, FILE *f) \n", tableName, tableName);
+fprintf(f, "/* Print out %s as Django model in python. */\n\n", tableName);
+
+fprintf(f, "{\n");
+fprintf(f, "fputc('{',f);\n");
+for (col = table->columnList; col != NULL; col = col->next)
+    {
+    fprintf(f, "/* TBD %s */\n", col->name);
+    // makeColJsonOutput(col, f);
+    if (col->next != NULL)
+        fprintf(f, "fputc(',',f);\n");
+    }
+fprintf(f, "fputc('}',f);\n");
+fprintf(f, "}\n\n");
+}
+
+void cSymColumnDef(struct asColumn *col, FILE *cFile)
+/* output definition used for parsing and formating a symbolic column field */
+{
+struct slName *val;
+fprintf(cFile, "/* definitions for %s column */\n", col->name);
+fprintf(cFile, "static char *values_%s[] = {", col->name);
+for (val = col->values; val != NULL; val = val->next)
+    fprintf(cFile, "\"%s\", ", val->name);
+fprintf(cFile, "NULL};\n");
+fprintf(cFile, "static struct hash *valhash_%s = NULL;\n\n", col->name);
+}
+
+void cSymColumnDefs(struct asObject *obj, FILE *cFile)
+/* output definitions used for parsing and formating a symbolic column fields */
+{
+struct asColumn *col;
+for (col = obj->columnList; col != NULL; col = col->next)
+    {
+    if ((col->lowType->type == t_enum) || (col->lowType->type == t_set))
+        cSymColumnDef(col, cFile);
+    }
+}
+
+void genObjectCode(struct asObject *obj, boolean doDbLoadAndSave,
+                   FILE *cFile, FILE *hFile, FILE *sqlFile, FILE *djangoFile)
+/* output code for one object */
+{
+cTable(obj, hFile);
+cSymColumnDefs(obj, cFile);
+
+if (obj->isTable)
+    {
+    sqlTable(obj, sqlFile);
+    if (makeDjango)
+        djangoModel(obj, djangoFile);
+    if (!objectHasVariableLists(obj) && !objectHasSubObjects(obj))
+        staticLoadRow(obj, cFile, hFile);
+    if(doDbLoadAndSave)
+        {
+        dynamicLoadByQuery(obj, cFile, hFile);
+        dynamicSaveToDb(obj, cFile, hFile);
+        dynamicSaveToDbEscaped(obj, cFile, hFile);
+        }
+    }
+dynamicLoadRow(obj, cFile, hFile);
+dynamicLoadAll(obj, cFile, hFile);
+dynamicLoadAllByChar(obj, cFile, hFile);
+makeCommaIn(obj, cFile, hFile);
+/*if (makeJson)
+    makeJsonInput(obj, cFile, hFile);*/
+
+if (obj->isSimple)
+    {
+    if (internalsNeedFree(obj))
+	makeFreeInternals(obj, cFile, hFile);
+    }
+else
+    {
+    makeFree(obj, cFile, hFile);
+    makeFreeList(obj, cFile, hFile);
+    }
+makeOutput(obj, cFile, hFile);
+if (makeJson)
+    makeJsonOutput(obj, cFile, hFile);
+verbose(2, "Made %s object\n", obj->name);
+}
+
+
+int main(int argc, char *argv[])
+{
+struct asObject *objList, *obj;
+char *outRoot, outTail[256];
+char dotC[256];
+char dotH[256];
+char dotSql[256];
+char dotDjango[256];
+FILE *cFile;
+FILE *hFile;
+FILE *sqlFile;
+FILE *djangoFile = NULL;
+char defineName[256];
+boolean doDbLoadAndSave = FALSE;
+
+optionInit(&argc, argv, optionSpecs);
+doDbLoadAndSave = optionExists("dbLink");
+withNull = optionExists("withNull");
+makeJson = optionExists("json");
+makeDjango = optionExists("django");
+
+if (argc != 3)
+    usage();
+
+objList = asParseFile(argv[1]);
+outRoot = argv[2];
+/* don't embed directories in files */
+splitPath(outRoot, NULL, outTail, NULL);
+
+safef(dotC, sizeof(dotC), "%s.c", outRoot);
+cFile = mustOpen(dotC, "w");
+safef(dotH, sizeof(dotH), "%s.h", outRoot);
+hFile = mustOpen(dotH, "w");
+safef(dotSql, sizeof(dotSql), "%s.sql", outRoot);
+sqlFile = mustOpen(dotSql, "w");
+if (makeDjango)
+    {
+    safef(dotDjango, sizeof(dotDjango), "%s.django", outRoot);
+    djangoFile = mustOpen(dotDjango, "w");
+    }
+
+/* Print header comment in all files. */
+fprintf(hFile, 
+   "/* %s.h was originally generated by the autoSql program, which also \n"
+   " * generated %s.c and %s.sql.  This header links the database and\n"
+   " * the RAM representation of objects. */\n\n",
+   outTail, outTail, outTail);
+fprintf(cFile, 
+   "/* %s.c was originally generated by the autoSql program, which also \n"
+   " * generated %s.h and %s.sql.  This module links the database and\n"
+   " * the RAM representation of objects. */\n\n",
+   outTail, outTail, outTail);
+fprintf(sqlFile, 
+   "# %s.sql was originally generated by the autoSql program, which also \n"
+   "# generated %s.c and %s.h.  This creates the database representation of\n"
+   "# an object which can be loaded and saved from RAM in a fairly \n"
+   "# automatic way.\n",
+   outTail, outTail, outTail);
+if (makeDjango)
+     {
+     fprintf(djangoFile, 
+         "# %s.python was originally generated by the autoSql program, which also \n"
+         "# generated %s.sql %s.c and %s.h.  This creates the database representation of\n"
+         "# an object which can be loaded and saved from RAM in a fairly \n"
+         "# automatic way.\n\n",
+         outTail, outTail, outTail, outTail);
+     }
+
+/* Bracket H file with definition that keeps it from being included twice. */
+sprintf(defineName, "%s_H", outTail);
+touppers(defineName);
+fprintf(hFile, "#ifndef %s\n", defineName);
+fprintf(hFile, "#define %s\n\n", defineName);
+if(doDbLoadAndSave)
+    {
+    fprintf(hFile, "#include \"jksql.h\"\n");
+    }
+
+/* Put the usual includes in .c file, and also include .h file we are
+ * generating. */
+fprintf(cFile, "#include \"common.h\"\n");
+fprintf(cFile, "#include \"linefile.h\"\n");
+fprintf(cFile, "#include \"dystring.h\"\n");
+fprintf(cFile, "#include \"jksql.h\"\n");
+fprintf(cFile, "#include \"%s\"\n", dotH);
+fprintf(cFile, "\n");
+fprintf(cFile, "\n");
+
+/* Also generate imports for django. */
+if (makeDjango)
+    {
+    fprintf(djangoFile, "import datetime\n");
+    fprintf(djangoFile, "from django.db import models\n\n");
+    }
+
+/* Process each object in specification file and output to .c, 
+ * .h, and .sql and even Django/Python. */
+for (obj = objList; obj != NULL; obj = obj->next)
+    genObjectCode(obj, doDbLoadAndSave, cFile, hFile, sqlFile, djangoFile);
+
+fprintf(cFile, "/* -------------------------------- End autoSql Generated Code -------------------------------- */\n\n");
+fprintf(hFile, "/* -------------------------------- End autoSql Generated Code -------------------------------- */\n\n");
+if (makeDjango)
+    fprintf(djangoFile, "###################### End autoSql Generated Code ######################\n\n");
+/* Finish off H file bracket. */
+fprintf(hFile, "#endif /* %s */\n\n", defineName);
+return 0;
+}
+