Index: src/hg/lib/metaTbl.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/lib/metaTbl.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -b -B -U 1000000 -r1.10 -r1.11
--- src/hg/lib/metaTbl.c	2 Apr 2010 21:14:04 -0000	1.10
+++ src/hg/lib/metaTbl.c	12 Apr 2010 21:56:28 -0000	1.11
@@ -1,1836 +1,1849 @@
 /* metaTbl.c was originally generated by the autoSql program, which also
  * generated metaTbl.h and metaTbl.sql.  This module links the database and
  * the RAM representation of objects. */
 
 #include "common.h"
 #include "linefile.h"
 #include "dystring.h"
 #include "jksql.h"
 #include "metaTbl.h"
 
 static char const rcsid[] = "$Id$";
 
 void metaTblStaticLoad(char **row, struct metaTbl *ret)
 /* Load a row from metaTbl table into ret.  The contents of ret will
  * be replaced at the next call to this function. */
 {
 
 ret->objName = row[0];
 ret->objType = row[1];
 ret->var = row[2];
 ret->varType = row[3];
 ret->val = row[4];
 }
 
 struct metaTbl *metaTblLoadByQuery(struct sqlConnection *conn, char *query)
 /* Load all metaTbl from table that satisfy the query given.
  * Where query is of the form 'select * from example where something=something'
  * or 'select example.* from example, anotherTable where example.something =
  * anotherTable.something'.
  * Dispose of this with metaTblFreeList(). */
 {
 struct metaTbl *list = NULL, *el;
 struct sqlResult *sr;
 char **row;
 
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     el = metaTblLoad(row);
     slAddHead(&list, el);
     }
 slReverse(&list);
 sqlFreeResult(&sr);
 return list;
 }
 
 void metaTblSaveToDb(struct sqlConnection *conn, struct metaTbl *el, char *tableName, int updateSize)
 /* Save metaTbl as a row to the table specified by tableName.
  * As blob fields may be arbitrary size updateSize specifies the approx size
  * of a string that would contain the entire query. Arrays of native types are
  * converted to comma separated strings and loaded as such, User defined types are
  * inserted as NULL. Note that strings must be escaped to allow insertion into the database.
  * For example "autosql's features include" --> "autosql\'s features include"
  * If worried about this use metaTblSaveToDbEscaped() */
 {
 struct dyString *update = newDyString(updateSize);
 dyStringPrintf(update, "insert into %s values ( '%s','%s','%s','%s',%s)",
 	tableName,  el->objName,  el->objType,  el->var,  el->varType,  el->val);
 sqlUpdate(conn, update->string);
 freeDyString(&update);
 }
 
 void metaTblSaveToDbEscaped(struct sqlConnection *conn, struct metaTbl *el, char *tableName, int updateSize)
 /* Save metaTbl as a row to the table specified by tableName.
  * As blob fields may be arbitrary size updateSize specifies the approx size.
  * of a string that would contain the entire query. Automatically
  * escapes all simple strings (not arrays of string) but may be slower than metaTblSaveToDb().
  * For example automatically copies and converts:
  * "autosql's features include" --> "autosql\'s features include"
  * before inserting into database. */
 {
 struct dyString *update = newDyString(updateSize);
 char  *objName, *objType, *var, *varType, *val;
 objName = sqlEscapeString(el->objName);
 objType = sqlEscapeString(el->objType);
 var = sqlEscapeString(el->var);
 varType = sqlEscapeString(el->varType);
 val = sqlEscapeString(el->val);
 
 dyStringPrintf(update, "insert into %s values ( '%s','%s','%s','%s','%s')",
 	tableName,  objName,  objType,  var,  varType,  val);
 sqlUpdate(conn, update->string);
 freeDyString(&update);
 freez(&objName);
 freez(&objType);
 freez(&var);
 freez(&varType);
 freez(&val);
 }
 
 struct metaTbl *metaTblLoad(char **row)
 /* Load a metaTbl from row fetched with select * from metaTbl
  * from database.  Dispose of this with metaTblFree(). */
 {
 struct metaTbl *ret;
 
 AllocVar(ret);
 ret->objName = cloneString(row[0]);
 ret->objType = cloneString(row[1]);
 ret->var = cloneString(row[2]);
 ret->varType = cloneString(row[3]);
 ret->val = cloneString(row[4]);
 return ret;
 }
 
 struct metaTbl *metaTblLoadAll(char *fileName)
 /* Load all metaTbl from a whitespace-separated file.
  * Dispose of this with metaTblFreeList(). */
 {
 struct metaTbl *list = NULL, *el;
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 char *row[5];
 
 while (lineFileRow(lf, row))
     {
     el = metaTblLoad(row);
     slAddHead(&list, el);
     }
 lineFileClose(&lf);
 slReverse(&list);
 return list;
 }
 
 struct metaTbl *metaTblLoadAllByChar(char *fileName, char chopper)
 /* Load all metaTbl from a chopper separated file.
  * Dispose of this with metaTblFreeList(). */
 {
 struct metaTbl *list = NULL, *el;
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 char *row[5];
 
 while (lineFileNextCharRow(lf, chopper, row, ArraySize(row)))
     {
     el = metaTblLoad(row);
     slAddHead(&list, el);
     }
 lineFileClose(&lf);
 slReverse(&list);
 return list;
 }
 
 struct metaTbl *metaTblCommaIn(char **pS, struct metaTbl *ret)
 /* Create a metaTbl out of a comma separated string.
  * This will fill in ret if non-null, otherwise will
  * return a new metaTbl */
 {
 char *s = *pS;
 
 if (ret == NULL)
     AllocVar(ret);
 ret->objName = sqlStringComma(&s);
 ret->objType = sqlStringComma(&s);
 ret->var = sqlStringComma(&s);
 ret->varType = sqlStringComma(&s);
 ret->val = sqlStringComma(&s);
 *pS = s;
 return ret;
 }
 
 void metaTblFree(struct metaTbl **pEl)
 /* Free a single dynamically allocated metaTbl such as created
  * with metaTblLoad(). */
 {
 struct metaTbl *el;
 
 if ((el = *pEl) == NULL) return;
 freeMem(el->objName);
 freeMem(el->objType);
 freeMem(el->var);
 freeMem(el->varType);
 freeMem(el->val);
 freez(pEl);
 }
 
 void metaTblFreeList(struct metaTbl **pList)
 /* Free a list of dynamically allocated metaTbl's */
 {
 struct metaTbl *el, *next;
 
 for (el = *pList; el != NULL; el = next)
     {
     next = el->next;
     metaTblFree(&el);
     }
 *pList = NULL;
 }
 
 void metaTblOutput(struct metaTbl *el, FILE *f, char sep, char lastSep)
 /* Print out metaTbl.  Separate fields with sep. Follow last field with lastSep. */
 {
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->objName);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->objType);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->var);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->varType);
 if (sep == ',') fputc('"',f);
 fputc(sep,f);
 if (sep == ',') fputc('"',f);
 fprintf(f, "%s", el->val);
 if (sep == ',') fputc('"',f);
 fputc(lastSep,f);
 }
 
 /* -------------------------------- End autoSql Generated Code -------------------------------- */
 
 #include "ra.h"
 #include "hgConfig.h"
 #include "obscure.h"
 
 // ------- (static) convert from autoSql -------
 static void metaVarFree(struct metaVar **metaVarPtr)
 // Frees a single metaVar struct
 {
     freeMem((*metaVarPtr)->val);
     freeMem((*metaVarPtr)->var);
     freez(metaVarPtr);
 }
 
 static void metaLeafObjFree(struct metaLeafObj **leafObjPtr)
 // Frees a single metaVar struct
 {
     freeMem((*leafObjPtr)->obj);
     freez(leafObjPtr);
 }
 
 static void metaLimbValFree(struct metaLimbVal **limbValPtr)
 // Frees a single metaVar struct
 {
 struct metaLimbVal *limbVal = *limbValPtr;
 
     // Free hash first (shared memory)
     hashFree(&(limbVal->objHash));
 
     struct metaLeafObj *leafObj = NULL;
     while((leafObj = slPopHead(&(limbVal->objs))) != NULL)
         metaLeafObjFree(&leafObj);
 
     freeMem(limbVal->val);
     freez(limbValPtr);
 }
 
 static struct metaObj *metaObjsLoadFromMemory(struct metaTbl **metaTblPtr,boolean buildHashes)
 // Load all metaObjs from in memory metaTbl struct, cannibalize strings.  Expects sorted order.
 {
 struct metaObj *metaObj  = NULL;
 struct metaObj *metaObjs = NULL;
 struct metaVar *metaVar;
 struct metaTbl *thisRow;
 while((thisRow = slPopHead(metaTblPtr)) != NULL)
     {
     if (metaObj == NULL || differentString(thisRow->objName,metaObj->obj) )
         {
         // Finish last object before starting next!
         if(metaObj!= NULL)
             slReverse(&(metaObjs->vars));
         // Start new object
         AllocVar(metaObj);
         metaObj->obj     = thisRow->objName;
         metaObj->objType = metaObjTypeStringToEnum(thisRow->objType);
         freeMem(thisRow->objType);
         if ( buildHashes )
             metaObj->varHash = hashNew(0);
         slAddHead(&metaObjs,metaObj);
         }
     else
         {
         freeMem(thisRow->objName);  // Already got this from prev row
         freeMem(thisRow->objType);
         }
     AllocVar(metaVar);
     metaVar->var     = thisRow->var;
     metaVar->varType = metaVarTypeStringToEnum(thisRow->varType);
     metaVar->val     = thisRow->val;
     slAddHead(&(metaObj->vars),metaVar);
     if ( buildHashes )
         hashAddUnique(metaObj->varHash, metaVar->var, metaVar); // pointer to struct to resolve type
 
     freeMem(thisRow);
     }
 
 // Finish very last object
 if(metaObjs && metaObjs->vars)
     slReverse(&(metaObjs->vars));
 if(metaObjs)
     slReverse(&metaObjs);
 
 return metaObjs;
 }
 
 static struct metaByVar *metaByVarsLoadFromMemory(struct metaTbl **metaTblPtr,boolean buildHashes)
 // Load all metaVars from in memorys metaTbl struct, cannibalize strings.  Expects sorted order.
 {
 struct metaByVar *rootVars = NULL;
 struct metaByVar *rootVar  = NULL;
 struct metaLimbVal *limbVal  = NULL;
 struct metaLeafObj *leafObj;
 struct metaTbl *thisRow;
 while((thisRow = slPopHead(metaTblPtr)) != NULL)
     {
     // Start at root
     if (rootVar == NULL || differentString(thisRow->var,rootVar->var) )
         {
         // Finish last var before starting next!
         if(rootVars && rootVars->vals && rootVars->vals->objs)
             slReverse(&(rootVars->vals->objs));
         if(rootVars && rootVars->vals)
             slReverse(&(rootVars->vals));
         // Start new var
         AllocVar(rootVar);
         limbVal = NULL;  // Very important!
         rootVar->var     = thisRow->var;
         rootVar->varType = metaVarTypeStringToEnum(thisRow->varType);
         freeMem(thisRow->varType);
         if ( buildHashes )
             rootVar->valHash = hashNew(0);
         slAddHead(&rootVars,rootVar);
         }
     else
         {
         freeMem(thisRow->var);  // Already got this from prev row
         freeMem(thisRow->varType);
         }
 
     // Continue with limb
     if (limbVal == NULL || differentString(thisRow->val,limbVal->val) )
         {
         // Finish last val before starting next!
         if(limbVal != NULL && limbVal->objs != NULL)
             slReverse(&(limbVal->objs));
         // Start new val
         AllocVar(limbVal);
         limbVal->val     = thisRow->val;     // FIXME: binary?
         if ( buildHashes )
             {
             hashAddUnique(rootVar->valHash, limbVal->val, limbVal); // Pointer to struct to get to objHash
             limbVal->objHash = hashNew(0);
             }
         slAddHead(&(rootVar->vals),limbVal);
         }
     else
         freeMem(thisRow->val);  // Already got this from prev row
 
     // End with leaf
     AllocVar(leafObj);
     leafObj->obj     = thisRow->objName;
     leafObj->objType = metaObjTypeStringToEnum(thisRow->objType);
     freeMem(thisRow->objType);
     if ( buildHashes )
         hashAddUnique(limbVal->objHash, leafObj->obj, leafObj); // Pointer to struct to resolve type!
     slAddHead(&(limbVal->objs),leafObj);
 
     freeMem(thisRow);
     }
 
 // Finish very last object
 if(rootVars && rootVars->vals && rootVars->vals->objs)
     slReverse(&(rootVars->vals->objs));
 if(rootVars && rootVars->vals)
     slReverse(&(rootVars->vals));
 if(rootVars && rootVars->vals)
     slReverse(&rootVars);
 
 return rootVars;
 }
 
 
 static int metaObjCRC(struct metaObj *metaObjs)
 // returns a summ of all individual CRC values of all metObj strings
 {
 int crc = 0;
 struct metaObj *metaObj = NULL;
 for(metaObj=metaObjs;metaObj!=NULL;metaObj=metaObj->next)
     {
     if(metaObj->obj != NULL)
         crc += hashCrc(metaObj->obj);
 
     struct metaVar *metaVar = NULL;
     for(metaVar=metaObj->vars;metaVar!=NULL;metaVar=metaVar->next)
         {
         if(metaVar->var != NULL)
             crc += hashCrc(metaVar->var);
         if(metaVar->varType == vtTxt && metaVar->val != NULL)
             crc += hashCrc(metaVar->val);
         }
     }
 
 return crc;
 }
 
 // -------------- Sort primitives --------------
 int metaObjCmp(const void *va, const void *vb)
 /* Compare to sort on label. */
 {
 const struct metaObj *a = *((struct metaObj **)va);
 const struct metaObj *b = *((struct metaObj **)vb);
 return strcasecmp(a->obj, b->obj);
 }
 
 int metaVarCmp(const void *va, const void *vb)
 /* Compare to sort on label. */
 {
 const struct metaVar *a = *((struct metaVar **)va);
 const struct metaVar *b = *((struct metaVar **)vb);
 return strcasecmp(a->var, b->var);
 }
 
 
 // -------------- Enum to Strings --------------
 enum metaObjType metaObjTypeStringToEnum(char *objType)
 // Convert metadata objType string to enum
 {
 if(sameWord(objType,"table"))
     return otTable;
 if(sameWord(objType,"file"))
     return otFile;
 return otUnknown;
 }
 
 char *metaObjTypeEnumToString(enum metaObjType objType)
 // Convert metadata objType enum string
 {
 switch (objType)
     {
     case otTable:  return "table";
     case otFile:   return "file";
     default:       return "unknown";
     }
 }
 
 enum metaVarType metaVarTypeStringToEnum(char *varType)
 // Convert metadata varType string to enum
 {
 if(sameWord(varType,"txt"))
     return vtTxt;
 if(sameWord(varType,"binary"))
     return vtBinary;
 return vtUnknown;
 }
 
 char *metaVarTypeEnumToString(enum metaVarType varType)
 // Convert metadata varType enum string
 {
 switch (varType)
     {
     case vtTxt:    return "txt";
     case vtBinary: return "binary";
     default:       return "unknown";
     }
 }
 
 // ------ Parsing lines ------
 
 struct metaObj *metaObjAddVarPairs(struct metaObj *oldObj,char *varPairs)
 // Parses line of var=val pairs adding to a metaObj.  Creates metaObj if NULL
 {
 struct metaObj *metaObj = oldObj;
 struct metaVar *metaVar;
 char *cloneVars = cloneString(varPairs);
 
     // initial chop and determine if this looks like metadata
     int count = chopByWhiteRespectDoubleQuotes(cloneVars,NULL,0);
     char **words = needMem(sizeof(char *) * count);
     count = chopByWhiteRespectDoubleQuotes(cloneVars,words,count);
     if(count < 1 || words[0] == NULL)
         {
         errAbort("This is not formatted var=val pairs:\n\t%s\n",varPairs);
         }
 
     verbose(3, "metaObjAddVarPairs() word count:%d\n\t%s\n",count,varPairs);
 
     if(metaObj == NULL)
         AllocVar(metaObj);
     if(metaObj->varHash == NULL)
         metaObj->varHash = hashNew(0);
 
     int ix;
     for(ix = 0;ix<count;ix++)
         {
         if(strchr(words[ix], '=') == NULL)
             errAbort("This is not formatted var=val pairs: '%s'\n\t%s\n",words[ix],varPairs);
 
         AllocVar(metaVar);
         metaVar->var = cloneNextWordByDelimiter(&(words[ix]),'=');
         metaVar->varType = vtTxt;                               // FIXME: binary?
         metaVar->val = cloneString(words[ix]);
         verbose(3, "metaObjAddVarPairs() var=val: %s=%s\n",metaVar->var,metaVar->val);
         struct metaVar *oldVar = (struct metaVar *)hashFindVal(metaObj->varHash, metaVar->var);
         if(oldVar)
             {
             verbose(1, "The same variable appears twice: %s=%s and %s=%s.  Ignoring second value.\n\t%s\n",
                 oldVar->var,oldVar->val,metaVar->var,metaVar->val,varPairs);
             metaVarFree(&metaVar);
             }
         else
             {
             hashAdd(metaObj->varHash, metaVar->var, metaVar); // pointer to struct to resolve type
             slAddHead(&(metaObj->vars),metaVar);
             }
         }
     freeMem(words);
     freeMem(cloneVars);
 
     // Special for old style ENCODE metadata
+#define ENCODE_ALN  "Alignments"
+#define ENCODE_RSIG "RawSignal"
     if(metaObj->obj == NULL)
         {
         char * tableName = NULL;
         char * fileName = NULL;
         for(metaVar  = metaObj->vars;
             metaVar != NULL && (tableName == NULL || fileName == NULL);
             metaVar  = metaVar->next)
             {
             if(sameString(metaVar->var,"tableName"))
                 tableName = metaVar->val;
             else if(sameString(metaVar->var,"fileName"))
                 fileName = metaVar->val;
             }
         if(tableName != NULL)
             {
             verbose(3, "tableName:%s\n",tableName);
             if(fileName == NULL || startsWithWordByDelimiter(tableName,'.',fileName))
                 {
                 metaObj->obj     = cloneString(tableName);
                 metaObj->objType = otTable;
                 }
+            else if(stringIn(ENCODE_ALN,fileName) && stringIn(ENCODE_RSIG,tableName))// Messier case where the file has "Alignment" but the table has "RawSignal"
+                {
+                char *tmpFilName = cloneString(fileName);
+                strSwapStrs(tmpFilName, strlen(tmpFilName),ENCODE_ALN, ENCODE_RSIG);
+                if(startsWithWordByDelimiter(tableName,'.',tmpFilName))
+                    {
+                    metaObj->obj     = cloneString(tableName);
+                    metaObj->objType = otTable;
+                    }
+                freeMem(tmpFilName);
+                }
             }
         else if(fileName != NULL)
             {
             verbose(3, "fileName:%s\n",fileName);
             // NOTE: that the file object is the root of the name, so both file.fastq.gz and file.fastq are same obj!
             metaObj->obj     = cloneFirstWordByDelimiter(fileName,'.');
             metaObj->objType = otFile;
             }
         }
 
 if(metaObj->obj == NULL) // NOTE: Should this be a hard error!
     errAbort("No obj found. This is not properly formatted metadata:\n\t%s\n",varPairs);
 
 if(metaObj->objType == otUnknown) // NOTE: defaulting to table
     metaObj->objType = otTable;
 
     //slReverse(&(metaObj->vars)); Could have added vars so sort instead
     slSort(&(metaObj->vars),&metaVarCmp); // Should be in determined order
     verbose(3, "metaObjAddVarPairs() obj=%s(%s) %s(%s)=%s\n",
     metaObj->obj, metaObjTypeEnumToString(metaObj->objType),metaObj->vars->var,metaVarTypeEnumToString(metaObj->vars->varType),metaObj->vars->val);
 return metaObj;
 }
 
 struct metaObj *metadataLineParse(char *line)
 /* Parses a single formatted metadata line into metaObj for updates or queries. */
 {
 char *fromTheTop = line;
 char*nibbledWord = cloneNextWordByDelimiter(&line,' ');
 if(nibbledWord == NULL || differentWord(nibbledWord,"metadata"))
     errAbort("This is not formatted metadata:\n\t%s\n",fromTheTop);
 freeMem(nibbledWord);
 
 struct metaObj *metaObj = NULL;
 char*varPairs = line;
 nibbledWord = cloneNextWordByDelimiter(&line,' ');;
 if(nibbledWord == NULL)
     errAbort("This is not formatted metadata:\n\t%s\n",fromTheTop);
 if(strchr(nibbledWord, '=') == NULL) // If this is not a var=val then it should be objName
     {
     AllocVar(metaObj);
     metaObj->obj = nibbledWord;
     verbose(3, "metadataLineParse() obj=%s\n",metaObj->obj);
     varPairs = line;
     for(;;)
         {
         nibbledWord = cloneNextWordByDelimiter(&line,' ');;
         if(nibbledWord == NULL)
             errAbort("This is not formatted metadata:\n\t%s\n",fromTheTop);
         if(strchr(nibbledWord, '=') != NULL) // If this is start of var=val pairs
             break;
 
         if(sameWord(nibbledWord,"delete"))
             metaObj->deleteThis = TRUE;
         else
             metaObj->objType = metaObjTypeStringToEnum(nibbledWord);
         varPairs = line;
         freeMem(nibbledWord);
         }
     }
 metaObj = metaObjAddVarPairs(metaObj,varPairs);
 return metaObj;
 }
 
 struct metaByVar *metaByVarsLineParse(char *line)
 /* Parses a line of "var1=val1 var2=val2 into a metaByVar object for queries. */
 {
 int thisWord = 0;
 struct metaByVar   *metaByVars = NULL;
 struct metaByVar   *rootVar = NULL;
 struct metaLimbVal *limbVal = NULL;
 char *cloneLine = cloneString(line);
 struct hash* varHash;     // There must not be multiple occurrances of the same var
 
     // initial chop and determine if this looks like metadata
     int count = chopByWhiteRespectDoubleQuotes(cloneLine,NULL,0);
     char **words = needMem(sizeof(char *) * count);
     count = chopByWhiteRespectDoubleQuotes(cloneLine,words,count);
 
     verbose(3, "metaByVarsLineParse() word count:%d\n\t%s\n",count,line);
     // Get obj and figure out if this is a delete line
     varHash = hashNew(0);
 
     // All words are expected to be var=val pairs!
     for(thisWord=0;thisWord<count;thisWord++)
         {
         if(strchr(words[thisWord], '=') == NULL)
             {
             errAbort("Expected 'var=val' but found '%s'.  This is not properly formatted metadata:\n\t%s\n",words[thisWord],line);
             //metaObjsFree(&metaObj);
             //return NULL;
             }
         AllocVar(rootVar);
         rootVar->var = cloneNextWordByDelimiter(&(words[thisWord]),'=');
         rootVar->notEqual = (rootVar->var[strlen(rootVar->var)-1] == '!'); // requested not equal
         if(rootVar->notEqual)
             rootVar->var[strlen(rootVar->var)-1] = 0;
         char *val = cloneString(words[thisWord]);
         if(sameWord(val,"?"))  // "var=?" or "var=" will query by var name only
             freez(&val);
 
         struct metaByVar *oldVar = (struct metaByVar *)hashFindVal(varHash, rootVar->var);
         if(oldVar)
             {  // FIXME: Could build this for 'or' queries!
             verbose(1, "The same variable appears twice: %s=%s and %s=%s.  Ignoring second value.\n",
                 oldVar->var,oldVar->vals->val,rootVar->var,val);
             freeMem(rootVar->var);
             freeMem(rootVar);
             freeMem(val);
             }
         else
             {
             AllocVar(limbVal);
             limbVal->val = val;
             rootVar->vals = limbVal;
             hashAdd(varHash, rootVar->var, rootVar);
             slAddHead(&metaByVars,rootVar);
             }
         }
     freeMem(words);
     slReverse(&metaByVars);
     verbose(3, "metaByVarsLineParse() parsed:%d first: %s=%s.\n",
         slCount(metaByVars->vals),metaByVars->var,metaByVars->vals->val);
 return metaByVars;
 }
 
 // ------ Loading from args, hashes and tdb ------
 struct metaByVar*metaByVarCreate(char *var, char *varType,char *val)
 /* Creates a singular var=val pair struct for metadata queries. */
 {
 struct metaByVar *metaByVar = NULL;
 
     if(var == NULL)
         errAbort("Need variable to create metaByVar query object.\n");
 
     AllocVar(metaByVar);
     metaByVar->var = cloneString(var);
     metaByVar->varType = (varType==NULL?vtUnknown:metaVarTypeStringToEnum(varType));
 
     if(val != NULL)
         {
         struct metaLimbVal * limbVal;
         AllocVar(limbVal);
 
         limbVal->val    = cloneString(val);
         metaByVar->vals = limbVal; // Only one
         }
 
 return metaByVar;
 }
 
 struct metaObj *metaObjCreate(char *obj,char *type,char *var, char *varType,char *val)
 /* Creates a singular metaObj query object based on obj and all other optional params. */
 {
 struct metaObj *metaObj = NULL;
 
     if(obj == NULL)
         errAbort("Need obj to create metaObj query object.\n");
 
     AllocVar(metaObj);
     metaObj->obj     = cloneString(obj);
     metaObj->objType = (type==NULL?otUnknown:metaObjTypeStringToEnum(type));
 
     if(var != NULL)
         {
         struct metaVar * metaVar;
         AllocVar(metaVar);
 
         metaVar->var     = cloneString(var);
         metaVar->varType = (varType==NULL?vtUnknown:metaVarTypeStringToEnum(varType));
         if(val != NULL)
             metaVar->val     = cloneString(val);
         metaObj->vars = metaVar; // Only one
         }
 return metaObj;
 }
 
 struct metaObj *metaObjsLoadFromHashes(struct hash *objsHash)
 // Load all metaObjs from a file containing metadata formatted lines
 {
 struct metaObj *metaObjs = NULL;
 struct hashEl* objEl = NULL;
 
 struct hashCookie objCookie = hashFirst(objsHash);
 while((objEl = hashNext(&objCookie)) != NULL)
     {
     struct metaObj *metaObj;
     AllocVar(metaObj);
     metaObj->obj     = cloneString(objEl->name);
     metaObj->varHash = hashNew(0);
     struct hash *hashedVars = objEl->val;
     struct hashCookie varCookie = hashFirst(hashedVars);
     struct hashEl* varEl = NULL;
     while((varEl = hashNext(&varCookie)) != NULL)
         {
         if(sameString(varEl->name,"metaObject"))
             continue;
         if(sameString(varEl->name,"objType"))
             metaObj->objType = metaObjTypeStringToEnum(varEl->val);
         else
             {
             struct metaVar * metaVar;
             AllocVar(metaVar);
             metaVar->var     = cloneString(varEl->name);
             metaVar->varType = vtTxt;                    // FIXME: binary?
             metaVar->val     = cloneString(varEl->val);
             hashAdd(metaObj->varHash, metaVar->var, metaVar); // pointer to struct to resolve type
             slAddHead(&(metaObj->vars),metaVar);
             }
 
         }
         slSort(&(metaObj->vars),&metaVarCmp); // Should be in determined order
         slAddHead(&metaObjs,metaObj);
     }
     slSort(&metaObjs,&metaObjCmp); // Should be in determined order
     return metaObjs;
 }
 
 // ------ Loading from files ------
 struct metaObj *metaObjsLoadFromFormattedFile(char *fileName,boolean *validated)
 // Load all metaObjs from a file containing metadata formatted lines
 {
 struct metaObj *metaObjs = NULL;
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 char *line;
 
 while (lineFileNext(lf, &line,NULL))
     {
     if(startsWithWord("metaObject",line))
         {
         // This is the RA style file!!
         lineFileClose(&lf);
         return metaObjsLoadFromRAFile(fileName,validated);
         }
     struct metaObj *metaObj = metadataLineParse(line);
     if(metaObj == NULL)
         {
         metaObjsFree(&metaObjs);
         return NULL;
         }
     slAddHead(&metaObjs,metaObj);
     }
     lineFileClose(&lf);
     slReverse(&metaObjs);  // Go ahead and keep this in file order
     if(validated)
         *validated = FALSE;
     return metaObjs;
 }
 
 #define METATBL_MAGIC_PREFIX "# MAGIC: "
 struct metaObj *metaObjsLoadFromRAFile(char *fileName,boolean *validated)
 // Load all metaObjs from a file containing RA formatted 'metaObjects'
 {
 struct hash *mdHash = raReadAll(fileName, "metaObject");
 if(mdHash == NULL)
     {
     verbose(1,"Missing, empty or badly formated RA file:%s\n",fileName);
     return NULL;
     }
 struct metaObj *metaObjs = metaObjsLoadFromHashes(mdHash);
 hashFree(&mdHash);
 
 // Try to validate file
 if(validated)
     {
     *validated = FALSE;
     struct lineFile *lf = lineFileOpen(fileName, TRUE);
     char *line = lineFileSkipToLineStartingWith(lf,METATBL_MAGIC_PREFIX,1000000);
     if(line != NULL)
         {
         int fileMagic = atoi(line+strlen(METATBL_MAGIC_PREFIX));
         int objsMagic = metaObjCRC(metaObjs);
         verbose(3,"Objects magic: %d  Files magic: %d (%s)\n",objsMagic,fileMagic,line+strlen(METATBL_MAGIC_PREFIX));
         *validated = (fileMagic == objsMagic);
         }
     else
         verbose(3,"Can't find magic number on this file.\n");
     }
 return metaObjs;
 }
 
 // ------ Table name and creation ------
 #define METATBL_SPEC_LOCATION "/cluster/bin/sqlCreate/metaTbl.sql"
 
 void metaTblReCreate(struct sqlConnection *conn,char *tblName,boolean testOnly)
 // Creates ore Recreates the named metaTbl.
 {
 if(sqlTableExists(conn,tblName))
     verbose(2, "Table '%s' already exists.  It will be recreated.\n",tblName);
 
 char *sql = NULL;
 readInGulp(METATBL_SPEC_LOCATION, &sql, NULL);
 char *pos = strchr(sql, ';');
 if ( pos != NULL)
     *pos = 0;
 char *oldSql = cloneString(sql);
 pos = stringIn("CREATE TABLE ", oldSql);
 if (pos == NULL)
     errAbort("Can't find CREATE TABLE in %s\n", METATBL_SPEC_LOCATION);
 nextWord(&pos);
 nextWord(&pos);
 char *oldName = nextWord(&pos);
 if(differentWord(oldName, tblName))
     {
     char *saveSql = cloneString(sql);
     freeMem(sql);
     sql = replaceChars(saveSql, oldName, tblName);
     freeMem(saveSql);
     }
 freeMem(oldSql);
 verbose(2, "Requesting table creation:\n\t%s;\n", sql);
 if(!testOnly)
     sqlRemakeTable(conn,tblName, sql);
 freeMem(sql);
 }
 
 char*metaTblName(struct sqlConnection *conn,boolean mySandBox)
 // returns the metaTbl name or NULL if conn supplied but the table doesn't exist
 {
 char *tblName = NULL;
 char *root = NULL;
 char *sand = NULL;
 char *name = cfgOption("db.metaTbl");
 if(name == NULL)
     {
     name = cfgOption("db.trackDb");
     if(name == NULL)
         root = cloneString(METATBL_DEFAULT_NAME);
     }
 
 // Divide name into root and sand
 if(root == NULL)
     {
     char delimit = '_';
     if((sand = strchr(name,delimit)) == NULL)
         {
         delimit = '-';
         if((sand = strchr(name,delimit)) == NULL)
             root = cloneString(name);  // No sandBox portion
         }
 
     if(root == NULL)  // There should be a sandbox portion
         {
         root = cloneNextWordByDelimiter(&name,delimit);
         if(mySandBox && *name != 0)
             sand = name;
         }
     }
 
 // Since db.trackDb was used, make sure to swap it
 if(sameWord("trackDb",root))
     {
     freeMem(root);
     root = cloneString(METATBL_DEFAULT_NAME);
     }
 
 if(!mySandBox || sand == NULL)
     tblName = root;
 else
     {
     int size = strlen(root) + strlen(sand) + 2;
     tblName = needMem(size);
     safef(tblName,size,"%s_%s",root,sand);
     }
 
 // Test for table
 if(conn != NULL && !sqlTableExists(conn,tblName))
     {
     if(sand == NULL || sameWord(tblName,root)) // Then try the root
         return NULL;
     freeMem(tblName);
     tblName = root;
     if(!sqlTableExists(conn,tblName))
         return NULL;
     }
 
 return tblName;
 }
 
 // -------------- Updating the DB --------------
 int metaObjsSetToDb(struct sqlConnection *conn,char *tableName,struct metaObj *metaObjs,boolean replace,boolean testOnly)
 // Adds or updates metadata obj/var pairs into the named table.  Returns total rows affected
 {
 char query[8192];
 struct metaObj *metaObj;
 struct metaVar *metaVar;
 int count = 0;
 
 if(tableName == NULL)
     tableName = METATBL_DEFAULT_NAME;
 
 if(!sqlTableExists(conn,tableName))
     errAbort("metaObjsSetToDb attempting to update non-existent table named '%s'.\n",tableName);
 
 for(metaObj = metaObjs;metaObj != NULL; metaObj = metaObj->next)
     {
     // Handle delete requests first
     if(metaObj->deleteThis)
         {
         if(metaObj->vars == NULL) // deletes all
             {
             safef(query, sizeof(query),"%s where objName = '%s'",tableName,metaObj->obj);
             int delCnt = sqlRowCount(conn,query);
 
             if(delCnt>0)
                 {
                 safef(query, sizeof(query),
                     "delete from %s where objName = '%s'",tableName,metaObj->obj);
                 verbose(2, "Requesting delete of %d rows:\n\t%s;\n",delCnt, query);
                 if(!testOnly)
                     sqlUpdate(conn, query);
                 count += delCnt;
                 }
             }
         else  // deletes selected vars
             {
             for(metaVar = metaObj->vars;metaVar != NULL; metaVar = metaVar->next)
                 {
                 safef(query, sizeof(query),
                     "select objName from %s where objName = '%s' and var = '%s'",
                     tableName,metaObj->obj,metaVar->var);
                 if(sqlExists(conn,query))
                     {
                     safef(query, sizeof(query),
                         "delete from %s where objName = '%s' and var = '%s'",
                         tableName,metaObj->obj,metaVar->var);
                     verbose(2, "Requesting delete of 1 row:\n\t%s;\n",query);
                     if(!testOnly)
                         sqlUpdate(conn, query);
                     count++;
                     }
                 }
             }
         continue;  // Done with this metaObj
         }
     else if (replace)  // If replace then clear out deadwood before inserting new vars
         {
         safef(query, sizeof(query),"%s where objName = '%s'",tableName,metaObj->obj);
         int delCnt = sqlRowCount(conn,query);
 
         if(delCnt>0)
             {
             safef(query, sizeof(query),
                 "delete from %s where objName = '%s'",tableName,metaObj->obj);
             verbose(2, "Requesting replacement of %d rows:\n\t%s;\n",delCnt, query);
             if(!testOnly)
                 sqlUpdate(conn, query);
             count += delCnt;
             }
         }
 
     // Now it is time for update or add!
     for(metaVar = metaObj->vars;metaVar != NULL; metaVar = metaVar->next)
         {
         // Be sure to check for var existence first, then update
         if (!replace)
             {
             struct metaObj *objExists = metaObjQueryByObj(conn,tableName,metaObj->obj,metaVar->var);
             if(objExists)
                 {
                 if(differentString(metaVar->val,objExists->vars->val)
                 || metaVar->varType != objExists->vars->varType)
                     {
                     safef(query, sizeof(query),
                         "update %s set varType = '%s', val = '%s' where objName = '%s' and var = '%s'",
                             tableName,
                             metaVarTypeEnumToString(metaVar->varType),sqlEscapeString(metaVar->val), // FIXME: binary val?
                             metaObj->obj,metaVar->var);
                     verbose(2, "Requesting update of 1 row:\n\t%s;\n",query);
                     if(!testOnly)
                         sqlUpdate(conn, query);
                     count++;
                     }
                 metaObjsFree(&objExists);
                 continue;  // The object was found/updated so done with it
                 }
             }
         // Finally ready to insert new vars
         safef(query, sizeof(query),
             "insert into %s values ( '%s','%s','%s','%s','%s')",
                 tableName,metaObj->obj,metaObjTypeEnumToString(metaObj->objType),
                           metaVar->var,metaVarTypeEnumToString(metaVar->varType),
                           sqlEscapeString(metaVar->val)); // FIXME: binary val?
         verbose(2, "Requesting insert of one row:\n\t%s;\n",query);
         if(!testOnly)
             sqlUpdate(conn, query);
         count++;
         }
     }
 return count;
 }
 
 // ------------------ Querys -------------------
 struct metaObj *metaObjQuery(struct sqlConnection *conn,char *table,struct metaObj *metaObj)
 // Query the metadata table by obj and optional vars and vals in metaObj struct.  If metaObj is NULL query all.
 // Returns new metaObj struct fully populated and sorted in obj,var order.
 {
 //  select objName,var,val where (var= [and val=]) or ([var= and] val=) order by objName,var
     boolean buildHash = TRUE;
 
     if(table == NULL)
         table = METATBL_DEFAULT_NAME;
 
     if(!sqlTableExists(conn,table))
         return NULL;
 
     struct dyString *dy = newDyString(4096);
     dyStringPrintf(dy, "select objName,objType,var,varType,val from %s", table);
     if(metaObj != NULL && metaObj->obj != NULL)
         {
         dyStringPrintf(dy, " where objName %s '%s'",
             (strchr(metaObj->obj,'%')?"like":"="),metaObj->obj);
 
         struct metaVar *metaVar;
         for(metaVar=metaObj->vars;metaVar!=NULL;metaVar=metaVar->next)
             {
             if(metaVar==metaObj->vars)
                 dyStringPrintf(dy, " and (");
             else
                 dyStringPrintf(dy, " or ");
             if(metaVar->var != NULL)
                 {
                 if(metaVar->val != NULL)
                     dyStringPrintf(dy, "(");
                 dyStringPrintf(dy, "var %s '%s'",
                     (strchr(metaVar->var,'%')?"like":"="),metaVar->var);
                 }
             if(metaVar->val != NULL)
                 {
                 if(metaVar->var != NULL)
                     dyStringPrintf(dy, " and ");
                 dyStringPrintf(dy, "val %s '%s'",
                     (strchr(metaVar->val,'%')?"like":"="), sqlEscapeString(metaVar->val));
                 if(metaVar->var != NULL)
                     dyStringPrintf(dy, ")");
                 }
             if(metaVar->var == NULL && metaVar->val)
                 errAbort("metaObjQuery has empty metaVar struct.\n");
             buildHash = FALSE;  // too few variables
             }
         if(metaObj->vars != NULL)
             dyStringPrintf(dy, ")");
         }
     dyStringPrintf(dy, " order by objName, var");
     verbose(2, "Requesting query:\n\t%s;\n",dyStringContents(dy));
 
     struct metaTbl *metaTbl = metaTblLoadByQuery(conn, dyStringCannibalize(&dy));
     struct metaObj *metaObjs = metaObjsLoadFromMemory(&metaTbl,buildHash);
     verbose(3, "Returned %d object(s) with %d var(s).\n",
         metaObjCount(metaObjs,TRUE),metaObjCount(metaObjs,FALSE));
     return metaObjs;
 }
 
 struct metaObj *metaObjQueryByObj(struct sqlConnection *conn,char *table,char *obj,char *var)
 // Query a single metadata object and optional var from a table (default metaTbl).
 {
 if(obj == NULL)
     return metaObjQuery(conn,table,NULL);
 
 struct metaObj *queryObj  = metaObjCreate(obj,NULL,var,NULL,NULL);
 struct metaObj *resultObj = metaObjQuery(conn,table,queryObj);
 metaObjsFree(&queryObj);
 return resultObj;
 }
 
 struct metaByVar *metaByVarsQuery(struct sqlConnection *conn,char *table,struct metaByVar *metaByVars)
 // Query the metadata table by one or more var=val pairs to find the distinct set of objs that satisfy ANY conditions.
 // Returns new metaByVar struct fully populated and sorted in var,val,obj order.
 {
 //  select objName,var,val where (var= [and val in (val1,val2)]) or (var= [and val in (val1,val2)]) order by var,val,objName
 
     if(table == NULL)
         table = METATBL_DEFAULT_NAME;
     if(!sqlTableExists(conn,table))
         return NULL;
 
     struct dyString *dy = newDyString(4096);
     dyStringPrintf(dy, "select distinct objName,objType,var,varType,val from %s", table);
 
     struct metaByVar *rootVar;
     for(rootVar=metaByVars;rootVar!=NULL;rootVar=rootVar->next)
         {
         if(rootVar==metaByVars)
             dyStringPrintf(dy, " where (var ");
         else
             dyStringPrintf(dy, " OR (var ");
         if(rootVar->notEqual && rootVar->vals == NULL)
             dyStringPrintf(dy, "NOT ");
 
         dyStringPrintf(dy, "%s '%s'",
             (strchr(rootVar->var,'%')?"like":"="), rootVar->var);
 
         struct metaLimbVal *limbVal;
         boolean multiVals = FALSE;
         for(limbVal=rootVar->vals;limbVal!=NULL;limbVal=limbVal->next)
             {
             if(limbVal->val == NULL || strlen(limbVal->val) < 1)
                 continue;
 
             if(!multiVals)
                 {
                 dyStringPrintf(dy, " and val ");
                 if(rootVar->notEqual)
                     dyStringPrintf(dy, "NOT ");
                 if(limbVal->next == NULL) // only one val
                     {
                     dyStringPrintf(dy, "%s '%s'",
                         (strchr(limbVal->val,'%')?"like":"="), sqlEscapeString(limbVal->val));
                     break;
                     }
                 else
                     dyStringPrintf(dy, "in (");
                 multiVals=TRUE;
                 }
             else
                 dyStringPrintf(dy, ",");
             dyStringPrintf(dy, "'%s'", sqlEscapeString(limbVal->val));
             }
         if(multiVals)
             dyStringPrintf(dy, ")");
         dyStringPrintf(dy, ")");
         }
     dyStringPrintf(dy, " order by var, val, objName");
     verbose(2, "Requesting query:\n\t%s;\n",dyStringContents(dy));
 
     struct metaTbl *metaTbl = metaTblLoadByQuery(conn, dyStringCannibalize(&dy));
     verbose(3, "rows (vars) returned: %d\n",slCount(metaTbl));
     struct metaByVar *metaByVarsFromMem = metaByVarsLoadFromMemory(&metaTbl,TRUE);
     verbose(3, "Returned %d vars(s) with %d val(s) with %d object(s).\n",
         metaByVarCount(metaByVarsFromMem,TRUE ,FALSE),
         metaByVarCount(metaByVarsFromMem,FALSE,TRUE ),
         metaByVarCount(metaByVarsFromMem,FALSE,FALSE));
     return metaByVarsFromMem;
 }
 
 struct metaByVar *metaByVarQueryByVar(struct sqlConnection *conn,char *table,char *varName,char *val)
 // Query a single metadata variable and optional val from a table (default metaTbl) for searching val->obj.
 {
 if(varName == NULL)
     return metaByVarsQuery(conn,table,NULL);
 
 struct metaByVar *queryVar  = metaByVarCreate(varName,NULL,val);
 struct metaByVar *resultVar = metaByVarsQuery(conn,table,queryVar);
 metaByVarsFree(&queryVar);
 return resultVar;
 }
 
 struct metaObj *metaObjsQueryByVars(struct sqlConnection *conn,char *table,struct metaByVar *metaByVars)
 // Query the metadata table by one or more var=val pairs to find the distinct set of objs that satisfy ALL conditions.
 // Returns new metaObj struct fully populated and sorted in obj,var order.
 {
 //  select objName,var,val where (var= [and val in (val1,val2)]) or (var= [and val in (val1,val2)]) order by objName,var
 
     if(table == NULL)
         table = METATBL_DEFAULT_NAME;
     if(!sqlTableExists(conn,table))
         return NULL;
 
     struct dyString *dy = newDyString(4096);
     dyStringPrintf(dy, "select distinct objName,objType,var,varType,val from %s", table);
 
     struct metaByVar *rootVar;
     boolean gotVar = FALSE;
     for(rootVar=metaByVars;rootVar!=NULL;rootVar=rootVar->next)
         {
         if(!gotVar)
             {
             dyStringPrintf(dy, " where objName in ");
             gotVar=TRUE;
             }
         else
             dyStringPrintf(dy, " AND objName in ");
         dyStringPrintf(dy, "(select objName from %s where var ",table);
 
         if(rootVar->notEqual && rootVar->vals == NULL)
             dyStringPrintf(dy, "NOT ");
 
         dyStringPrintf(dy, "%s '%s'",
             (strchr(rootVar->var,'%')?"like":"="), rootVar->var);
 
         struct metaLimbVal *limbVal;
         boolean multiVals = FALSE;
         for(limbVal=rootVar->vals;limbVal!=NULL;limbVal=limbVal->next)
             {
             if(limbVal->val == NULL || strlen(limbVal->val) < 1)
                 continue;
 
             if(!multiVals)
                 {
                 dyStringPrintf(dy, " and val ");
                 if(rootVar->notEqual)
                     dyStringPrintf(dy, "NOT ");
                 if(limbVal->next == NULL) // only one val
                     {
                     dyStringPrintf(dy, "%s '%s'",
                         (strchr(limbVal->val,'%')?"like":"="), sqlEscapeString(limbVal->val));
                     break;
                     }
                 else
                     dyStringPrintf(dy, "in (");
                 multiVals=TRUE;
                 }
             else
                 dyStringPrintf(dy, ",");
             dyStringPrintf(dy, "'%s'", sqlEscapeString(limbVal->val));
             }
         if(multiVals)
             dyStringPrintf(dy, ")");
         dyStringPrintf(dy, ")");
         }
     dyStringPrintf(dy, " order by objName, var");
     verbose(2, "Requesting query:\n\t%s;\n",dyStringContents(dy));
 
     struct metaTbl *metaTbl = metaTblLoadByQuery(conn, dyStringCannibalize(&dy));
     verbose(3, "rows (vars) returned: %d\n",slCount(metaTbl));
     struct metaObj *metaObjs = metaObjsLoadFromMemory(&metaTbl,TRUE);
     verbose(3, "Returned %d object(s) with %d var(s).\n",
         metaObjCount(metaObjs,TRUE),metaObjCount(metaObjs,FALSE));
     return metaObjs;
 }
 
 
 // ----------- Printing and Counting -----------
 void metaObjPrint(struct metaObj *metaObjs,boolean raStyle)
 // prints objs and var=val pairs as formatted metadata lines or ra style
 {
 // Single line:
 //   metadata iLoveLucy table lucy=ricky ethyl=fred
 // ra style
 //   metadata iLoveLucy table
 //       lucy ricky
 //       ethy fred
 // TODO: Expand for mutilple var types; strip quotes from vals on ra style
 struct metaObj *metaObj = NULL;
 for(metaObj=metaObjs;metaObj!=NULL;metaObj=metaObj->next)
     {
     if(metaObj->obj == NULL)
         continue;
 
     printf("%s %s",(raStyle?"metaObject":"metadata"),metaObj->obj);
     if(metaObj->deleteThis)
         printf(" delete");
     else
         {
         if(raStyle)
             printf("\n    objType %s",metaObjTypeEnumToString(metaObj->objType));
         else
             printf(" %s",metaObjTypeEnumToString(metaObj->objType));
         }
 
     struct metaVar *metaVar = NULL;
     for(metaVar=metaObj->vars;metaVar!=NULL;metaVar=metaVar->next)
         {
         if(metaVar->var != NULL)
             {
             if(raStyle)
                 printf("\n    %s ",metaVar->var);
             else
                 printf(" %s=",metaVar->var);
             if(metaVar->val != NULL)
                 {
                 if(metaVar->varType == vtBinary)
                     printf("binary");
                 else
                     printf("%s",metaVar->val);
                 }
             }
         }
     printf("%s",(raStyle?"\n\n":"\n"));
     }
 if(raStyle) // NOTE: currently only supporting validation of RA files
     printf("%s%d\n",METATBL_MAGIC_PREFIX,metaObjCRC(metaObjs));
 }
 
 void metaByVarPrint(struct metaByVar *metaByVars,boolean raStyle)
 // prints var=val pairs and objs that go with them single lines or ra style
 {
 // Single line:
 //   metaVariable lucy=ethyl bestFriends lifePartners
 //   metaVariable lucy=ricky iLoveLucy divorces
 // NOT QUITE ra style
 //   metaVariable lucy
 //       ethyl
 //           bestFriends
 //           lifePartners
 //       ricky
 //           iLoveLucy
 //           divorces
 // TODO: Expand for mutilple var types; strip quotes from vals on ra style
 struct metaByVar *rootVar = NULL;
 for(rootVar=metaByVars;rootVar!=NULL;rootVar=rootVar->next)
     {
     if(rootVar->var == NULL)
         continue;
 
     boolean first = TRUE;
     struct metaLimbVal *limbVal = NULL;
     for(limbVal=rootVar->vals;limbVal!=NULL;limbVal=limbVal->next)
         {
         if(limbVal->val == NULL)
             continue;
 
         if(first) // first val for this var
             {
             printf("metaVariable %s",rootVar->var);
             first = FALSE;
             }
 
         if(rootVar->varType == vtBinary)
             printf("%sbinary",(raStyle ? "\n    ":"="));
         else
             printf("%s%s",(raStyle ? "\n    ":"="),limbVal->val);
 
         struct metaLeafObj *leafObj = NULL;
         for(leafObj=limbVal->objs;leafObj!=NULL;leafObj=leafObj->next)
             {
             if(leafObj->obj == NULL)
                 continue;
 
             printf("%s%s",(raStyle?"\n        ":" "),leafObj->obj);
             }
         printf("\n");
         }
     }
 }
 
 
 int metaObjCount(struct metaObj *metaObjs,boolean objs)
 // returns the count of vars belonging to this obj or objs;
 {
 int count = 0;
 struct metaObj *metaObj = NULL;
 for(metaObj=metaObjs;metaObj!=NULL;metaObj=metaObj->next)
     {
     if(metaObj->obj == NULL)
         continue;
     if(objs)
         count++;
     else
         {
         struct metaVar *metaVar = NULL;
         for(metaVar=metaObj->vars;metaVar!=NULL;metaVar=metaVar->next)
             {
             if(metaVar->var != NULL && metaVar->val != NULL)
                 count++;
             }
         }
     }
 
 return count;
 }
 
 int metaByVarCount(struct metaByVar *metaByVars,boolean vars, boolean vals)
 // returns the count of objs belonging to this set of vars;
 {
 int count = 0;
 struct metaByVar *rootVar = NULL;
 for(rootVar=metaByVars;rootVar!=NULL;rootVar=rootVar->next)
     {
     if(rootVar->var == NULL)
         continue;
     if(vars)
         count++;
     else
         {
         struct metaLimbVal *limbVal = NULL;
         for(limbVal=rootVar->vals;limbVal!=NULL;limbVal=limbVal->next)
             {
             if(limbVal->val == NULL)
                 continue;
             if(vals)
                 count++;
             else
                 {
                 struct metaLeafObj *leafObj = NULL;
                 for(leafObj=limbVal->objs;leafObj!=NULL;leafObj=leafObj->next)
                     {
                     if(leafObj->obj != NULL)
                         count++;
                     }
                 }
             }
         }
     }
 return count;
 }
 
 // ----------------- Utilities -----------------
 
 char *metaObjFindValue(struct metaObj *metaObj, char *var)
 // Finds the val associated with the var or retruns NULL
 {
 if (metaObj == NULL)
     return NULL;
 
 struct metaVar *metaVar = NULL;
 if(metaObj->varHash != NULL)
     metaVar = hashFindVal(metaObj->varHash,var);
 else
     {
     for(metaVar=metaObj->vars;metaVar!=NULL;metaVar=metaVar->next)
         {
         if(sameOk(var,metaVar->var))
             break;
         }
     }
 if(metaVar == NULL)
     return NULL;
 
 return metaVar->val;
 }
 
 boolean metaObjContains(struct metaObj *metaObj, char *var, char *val)
 // Returns TRUE if object contains var, val or both
 {
 if (metaObj == NULL)
     return FALSE;
 
 if(var != NULL)
     {
     char *foundVal = metaObjFindValue(metaObj,var);
     if(foundVal == NULL)
         return FALSE;
     if(val == NULL)
         return TRUE;
     return sameOk(foundVal,val);
     }
 struct metaVar *metaVar = NULL;
 for(metaVar=metaObj->vars;metaVar!=NULL;metaVar=metaVar->next)
     {
     if(differentStringNullOk(var,metaVar->var) != 0)
         continue;
     if(differentStringNullOk(val,metaVar->val) != 0)
         continue;
     return TRUE;
     }
 
 return FALSE;
 }
 
 boolean metaByVarContains(struct metaByVar *metaByVar, char *val, char *obj)
 // Returns TRUE if var contains val, obj or both
 {
 if (metaByVar != NULL)
     {
     struct metaLimbVal *limbVal = NULL;
     struct metaLeafObj *leafObj = NULL;
     if(metaByVar->valHash != NULL && val != NULL)
         {
         limbVal = hashFindVal(metaByVar->valHash,val);
         if(limbVal == NULL || limbVal->val == NULL)
             return FALSE;
         if(limbVal->objHash != NULL && obj != NULL)
             {
             leafObj = hashFindVal(limbVal->objHash,obj);
             if(leafObj == NULL)
                 return FALSE;
             return sameOk(leafObj->obj,obj);
             }
         }
     for(limbVal=metaByVar->vals;limbVal!=NULL;limbVal=limbVal->next)
         {
         if(differentStringNullOk(val,limbVal->val) != 0)
             continue;
 
         for(leafObj=limbVal->objs;leafObj!=NULL;leafObj=leafObj->next)
             {
             if(differentStringNullOk(obj,leafObj->obj) != 0)
                 continue;
             return TRUE;
             }
         }
     }
 return FALSE;
 }
 
 void metaObjReorderVars(struct metaObj *metaObjs, char *vars,boolean back)
 // Reorders vars list based upon list of vars "cell antibody treatment".  Send to front or back.
 {
 //char *words[48];
 char *cloneLine = cloneString(vars);
 int count = chopByWhite(cloneLine,NULL,0);
 char **words = needMem(sizeof(char *) * count);
 count = chopByWhite(cloneLine,words,count);
 //int count = chopLine(cloneLine,words);
 
 struct metaObj *metaObj = NULL;
 for( metaObj=metaObjs; metaObj!=NULL; metaObj=metaObj->next )
     {
     int ix;
     struct metaVar *orderedVars = NULL;
     struct metaVar **varsToReorder = needMem(sizeof(struct metaVar *) * count);
 
     struct metaVar *metaVar = NULL;
     while((metaVar = slPopHead(&(metaObj->vars))) != NULL)
         {
         ix = stringArrayIx(metaVar->var,words,count);
         if(ix < 0)
             slAddHead(&orderedVars,metaVar);
         else
             varsToReorder[ix] = metaVar;
         }
 
     if(back) // add to front of backward list
         {
         for( ix=0; ix<count; ix++ )
             {
             if(varsToReorder[ix] != NULL)
                 slAddHead(&orderedVars,varsToReorder[ix]);
             }
         }
     slReverse(&orderedVars);
 
     if(!back)  // Add to front of forward list
         {
         for( ix=count-1; ix>=0; ix-- )
             {
             if(varsToReorder[ix] != NULL)
                 slAddHead(&orderedVars,varsToReorder[ix]);
             }
         }
 
     metaObj->vars = orderedVars;
     freeMem(varsToReorder);
     }
     freeMem(words);
 }
 
 void metaObjRemoveVars(struct metaObj *metaObjs, char *vars)
 // Prunes list of vars for an object, freeing the memory.  Doesn't touch DB.
 {
 char *cloneLine = NULL;
 int count = 0;
 char **words = NULL;
 if(vars != NULL)
     {
     cloneLine = cloneString(vars);
     count = chopByWhite(cloneLine,NULL,0);
     words = needMem(sizeof(char *) * count);
     count = chopByWhite(cloneLine,words,count);
     }
 struct metaObj *metaObj = NULL;
 for( metaObj=metaObjs; metaObj!=NULL; metaObj=metaObj->next )
     {
     int ix;
     struct metaVar *keepTheseVars = NULL;
 
     if(count == 0 && metaObj->varHash != NULL)
         hashFree(&metaObj->varHash);
 
     struct metaVar *metaVar = NULL;
     while((metaVar = slPopHead(&(metaObj->vars))) != NULL)
         {
         if(count == 0)
             ix = 1;
         else
             ix = stringArrayIx(metaVar->var,words,count);
         if(ix < 0)
             slAddHead(&keepTheseVars,metaVar);
         else
             {
             if(count != 0 && metaObj->varHash != NULL)
                 hashRemove(metaObj->varHash, metaVar->var);
 
             metaVarFree(&metaVar);
             }
         }
 
     if(keepTheseVars != NULL)
         slReverse(&keepTheseVars);
     metaObj->vars = keepTheseVars;
     }
     if(words != NULL)
         freeMem(words);
 }
 
 void metaObjSwapVars(struct metaObj *metaObjs, char *vars,boolean deleteThis)
 // Replaces objs' vars with var=vap pairs provided, preparing for DB update.
 {
 struct metaObj *metaObj = NULL;
 for( metaObj=metaObjs; metaObj!=NULL; metaObj=metaObj->next )
     {
     metaObj->deleteThis = deleteThis;
 
     if(metaObj->varHash != NULL)
         hashFree(&metaObj->varHash);
 
     struct metaVar *metaVar = NULL;
     while((metaVar = slPopHead(&(metaObj->vars))) != NULL)
         metaVarFree(&metaVar);
 
     metaObjAddVarPairs(metaObj,vars);
     }
 }
 
 void metaObjTransformToUpdate(struct metaObj *metaObjs, char *var, char *varType,char *val,boolean deleteThis)
 // Turns one or more metaObjs into the stucture needed to add/update or delete.
 {
 struct metaObj *metaObj = NULL;
 for( metaObj=metaObjs; metaObj!=NULL; metaObj=metaObj->next )
     {
     metaObj->deleteThis = deleteThis;
 
     if(metaObj->varHash != NULL)
         hashFree(&metaObj->varHash);
 
     struct metaVar *metaVar = NULL;
     while((metaVar = slPopHead(&(metaObj->vars))) != NULL)
         metaVarFree(&metaVar);
 
     if(var != NULL)
         {
         AllocVar(metaVar);
 
         metaVar->var     = cloneString(var);
         metaVar->varType = (varType==NULL?vtUnknown:metaVarTypeStringToEnum(varType));
         if(val != NULL)
             metaVar->val     = cloneString(val);
         metaObj->vars = metaVar; // Only one
         }
     }
 }
 
 struct metaObj *metaObjClone(const struct metaObj *metaObj)
 // Clones a single metaObj, including hash and maintining order
 {
 if(metaObj == NULL)
     return NULL;
 
 struct metaObj *newObj;
 AllocVar(newObj);
 
 if(metaObj->obj != NULL)
     newObj->obj    = cloneString(metaObj->obj);
 newObj->objType    = metaObj->objType;
 newObj->deleteThis = metaObj->deleteThis;
 if(metaObj->vars != NULL)
     {
     if(metaObj->varHash != NULL)
         newObj->varHash = hashNew(0);
 
     struct metaVar *metaVar = NULL;
     for(metaVar = metaObj->vars; metaVar != NULL; metaVar = metaVar->next )
         {
         struct metaVar *newVar = NULL;
         AllocVar(newVar);
         if(metaVar->var != NULL)
             newVar->var = cloneString(metaVar->var);
         if(metaVar->val != NULL)
             newVar->val = cloneString(metaVar->val);
         newVar->varType    = metaVar->varType;
         if(newVar->var != NULL && newVar->val != NULL)
             hashAdd(newObj->varHash, newVar->var, newVar); // pointer to struct to resolve type
         slAddHead(&(newObj->vars),newVar);
         }
     slReverse(&(newObj->vars));
     }
 return newObj;
 }
 
 // --------------- Free at last ----------------
 void metaObjsFree(struct metaObj **metaObjsPtr)
 // Frees one or more metadata objects and any contained metaVars.  Will free any hashes as well.
 {
 
 if(metaObjsPtr != NULL && *metaObjsPtr != NULL)
     {
     // free all roots
     struct metaObj *metaObj = NULL;
     while((metaObj = slPopHead(metaObjsPtr)) != NULL)
         {
         // Free hash first (shared memory)
         hashFree(&(metaObj->varHash));
 
         // free all leaves
         struct metaVar *metaVar = NULL;
         while((metaVar = slPopHead(&(metaObj->vars))) != NULL)
             metaVarFree(&metaVar);
 
         // The rest of root
         freeMem(metaObj->obj);
         freeMem(metaObj);
         }
     freez(metaObjsPtr);
     }
 }
 
 void metaByVarsFree(struct metaByVar **metaByVarsPtr)
 // Frees one or more metadata vars and any contained vals and objs.  Will free any hashes as well.
 {
 if(metaByVarsPtr != NULL && *metaByVarsPtr != NULL)
     {
     // free all roots
     struct metaByVar *rootVar = NULL;
     while((rootVar = slPopHead(metaByVarsPtr)) != NULL)
         {
         // Free hash first (shared memory)
         hashFree(&(rootVar->valHash));
 
         // free all limbs
         struct metaLimbVal *limbVal = NULL;
         while((limbVal = slPopHead(&(rootVar->vals))) != NULL)
             metaLimbValFree(&limbVal);
 
         // The rest of root
         if(rootVar->var)
             freeMem(rootVar->var);
         freeMem(rootVar);
         }
     freez(metaByVarsPtr);
     }
 }
 
 // ----------------- CGI specific routines for use with tdb -----------------
 #define METATBL_NOT_FOUND ((struct metaObj *)-666)
 #define METADATA_NOT_FOUND ((struct metaObj *)-999)
 #define METATBL_OBJ_KEY "metaObj"
 static struct metaObj *metadataForTableFromTdb(struct trackDb *tdb)
 // Returns the metadata for a table from a tdb setting.
 {
 char *setting = trackDbSetting(tdb, "metadata");
 if(setting == NULL)
     return NULL;
 struct metaObj *metaObj;
 AllocVar(metaObj);
 metaObj->obj     = cloneString(tdb->tableName);
 metaObj->objType = otTable;
 return metaObjAddVarPairs(metaObj,setting);
 }
 
 const struct metaObj *metadataForTable(char *db,struct trackDb *tdb,char *table)
 // Returns the metadata for a table.  NEVER FREE THIS STRUCT!
 {
 struct metaObj *metaObj = NULL;
 
 // See of the metaObj was already built
 if(tdb != NULL)
     {
     metaObj = tdbExtrasGetOrDefault(tdb, METATBL_OBJ_KEY,NULL);
     if(metaObj == METADATA_NOT_FOUND) // NOT in mtatbl, not in tdb metadata setting!
         return NULL;
     else if(metaObj == METATBL_NOT_FOUND) // looked metaTbl already and not found!
         return metadataForTableFromTdb(tdb);
     else if(metaObj != NULL)
         {
         return metaObj;  // No reason to query the table again!
         }
     }
 
 struct sqlConnection *conn = sqlConnect(db);
 char *metaTbl = metaTblName(conn,TRUE);  // Look for sandbox name first
 if(tdb != NULL && tdb->tableName != NULL)
     table = tdb->tableName;
 if(metaTbl != NULL)
     metaObj = metaObjQueryByObj(conn,metaTbl,table,NULL);
 sqlDisconnect(&conn);
 
 // save the metaObj for next time
 if(tdb)
     {
     if(metaObj != NULL)
         tdbExtrasAddOrUpdate(tdb,METATBL_OBJ_KEY,metaObj);
     else
         {
         tdbExtrasAddOrUpdate(tdb,METATBL_OBJ_KEY,METATBL_NOT_FOUND);
         return metadataForTableFromTdb(tdb);  // FIXME: metadata setting in TDB is soon to be obsolete
         }
     }
 
 // FIXME: Temporary to distinguish metaTbl metadata from trackDb metadata:
 metaObjRemoveVars(metaObj,"tableName");
 
 
 return metaObj;
 }
 
 const char *metadataFindValue(struct trackDb *tdb, char *var)
 // Finds the val associated with the var or retruns NULL
 {
 struct metaObj *metaObj = tdbExtrasGetOrDefault(tdb, METATBL_OBJ_KEY,NULL);
 if(metaObj == METATBL_NOT_FOUND) // Note, only we if already looked for metaTbl (which requires db)
     metaObj = metadataForTableFromTdb(tdb);
 if (metaObj == NULL || metaObj == METADATA_NOT_FOUND)
     return NULL;
 
 return metaObjFindValue(metaObj,var);
 
 struct metaVar *metaVar = NULL;
 if(metaObj->varHash != NULL)
     metaVar = hashFindVal(metaObj->varHash,var);
 else
     {
     for(metaVar=metaObj->vars;metaVar!=NULL;metaVar=metaVar->next)
         {
         if(sameOk(var,metaVar->var))
             break;
         }
     }
 if(metaVar == NULL)
     return NULL;
 
 return metaVar->val;
 }
 

