1b20a1774021cdcb07303801d55b7ca3fa6cff09
braney
  Wed Jan 8 05:54:20 2025 -0800
fix up chop functions so they don't call ArraySize with NULL

diff --git src/hg/encode/hgEncodeVocab/hgEncodeVocab.c src/hg/encode/hgEncodeVocab/hgEncodeVocab.c
index 6b23d8b..17761f6 100644
--- src/hg/encode/hgEncodeVocab/hgEncodeVocab.c
+++ src/hg/encode/hgEncodeVocab/hgEncodeVocab.c
@@ -1,922 +1,922 @@
 /* hgEncodeVocab - print table of controlled vocabulary from ENCODE configuration files */
 
 /* Copyright (C) 2014 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "hash.h"
 #include "linefile.h"
 #include "cheapcgi.h"
 #include "hCommon.h"
 #include "htmshell.h"
 #include "ra.h"
 #include "hui.h"
 #include "cv.h"
 #include "web.h"
 #include "jsHelper.h"
 #include "hgConfig.h"
 
 /* hgEncodeVocab - A CGI script to display the different types of encode controlled vocabulary.
  * usage:
  *   hgEncodeVocab type=[Antibody|"Cell Line"|localization|rnaExtract|"Gene Type"] [tier=(1|2|3)]
  * options:\n"
  *    type=TypeName  : Type to display
  *    tier=N         : If type="Cell Line" then this is the tier to display
  *    bgcolor=RRGGBB : Change background color (hex digits)
  *    term=a[,b,c]   : Display row for a single term [or comma delimited set of terms]
  *    tag=a[,b,c]    : Display row for a single term, using tag as identifier [or comma delimited
  *                     set of tags]
  *    target=a[,b,c] : Display all antibodies for a single target.  If 'a'[,b,c] is a term,
  *                     corresponding targets will be looked up and used
  *    label=a[,b,c]  : Display row for a single term with the specific label.
  *                     Must use with 'type' or terms must have same type.
  *    deprectate=y   : Include deprecated terms.  Usually these are excluded unles the term
  *                     is reqested by name.
  * Hint: try  "hgEncodeVocab type=typeOfTerm" for a complete list of types with links to
  *       each specific type.
  */
 
 //options that apply to all vocab types
 
 #define ORGANISM           "organism"
 #define ORG_HUMAN          "human"
 #define ORG_MOUSE          "mouse"
 
 #define MAX_TABLE_COLS     11
 #define TABLE_COLS_AVAILABLE(colsUsed) (MAX_TABLE_COLS - (colsUsed))
 
 static char *termOpt = NULL;
 static char *tagOpt = NULL;
 static char *typeOpt = NULL;
 static char *targetOpt = NULL;
 static char *labelOpt = NULL;
 static char *organismOpt = NULL; // we default to human if nothing else is set
 static char *organismOptLower = NULL; //  version of above used for path names
 
 int documentLink(struct hash *ra, char *term, char *docTerm,char *dir,
                  char *title,boolean genericDoc)
 // Compare controlled vocab based on term value
 {
 boolean docsPrinted = 0;
 char *s;
 if (title == NULL)
     title = docTerm;
 
 //can use hg.conf to direct links back to main UCSC server if a mirror doesn't
 //want all the PDFs
 char *baseUrl = cfgOptionDefault("hgEncodeVocabDocBaseUrl", "");
 // add links to protocol doc if it exists
 char docUrl[PATH_LEN];
 char docFile[PATH_LEN];
 // parse setting
 s = hashFindVal(ra,docTerm);
 if (s != NULL)
     {
     if (sameWord(s,"missing"))
         printf("&nbsp;<em>missing</em>\n");
     else
         {
         char *docSetting = cloneString(s);
         char *settings=docSetting;
         int count=0;
         while ((s = nextWord(&settings)) != NULL)
             {
             char *docTitle = NULL;
             char *fileName = NULL;
             if (strchr(s,':')) // lab Specific setting
                 {
                 docTitle = strSwapChar(s,':',0);
                 fileName = docTitle + strlen(docTitle) + 1;
                 }
             else
                 {
                 docTitle = title;
                 fileName = s;
                 }
             if (count>0)
                 printf("<BR>");
             count++;
             docTitle = htmlEncode(strSwapChar(docTitle,'_',' '));
             if (sameWord(fileName,"missing"))
                 printf("%s<em>missing</em>\n",docTitle);
             else
                 {
                 safef(docUrl,  sizeof(docUrl),  "%s%s%s", baseUrl, dir, fileName);
                 safef(docFile, sizeof(docFile), "%s%s", hDocumentRoot(), docUrl);
                 printf(" <A TARGET=_BLANK HREF=%s>%s</A>\n", docUrl,docTitle);
                 docsPrinted++;
                 }
             freeMem(docTitle);
             }
         freeMem(docSetting);
         }
     }
 else if (genericDoc)
     { // generate a standard name
     safef(docUrl,  sizeof(docUrl),  "%s%s%s_protocol.pdf", baseUrl, dir, term);
     safef(docFile, sizeof(docFile), "%s%s", hDocumentRoot(), docUrl);
     if (fileExists(docFile))
         {
         printf(" <A TARGET=_BLANK HREF=%s>%s</A>\n", docUrl,title);
         docsPrinted++;
         }
     }
 return docsPrinted;
 }
 
 void printDocumentLink(struct hash *ra, char *term, char *docTerm,char *dir,
                        char *title,boolean genericDoc)
 // prints a document link
 {
 printf("  <TD>");
 (void)documentLink(ra,term,docTerm,dir,title,genericDoc);
 printf("  &nbsp;</TD>\n");
 }
 
 int termCmp(const void *va, const void *vb)
 /* Compare controlled vocab based on term value */
 {
 const struct hash *a = *((struct hash **)va);
 const struct hash *b = *((struct hash **)vb);
 char *typeA = hashMustFindVal((struct hash *)a, CV_TYPE);
 char *typeB = hashMustFindVal((struct hash *)b, CV_TYPE);
 int ret = strcasecmp(typeA, typeB);
 if (ret != 0)
     return ret;
 
 char *targA = hashFindVal((struct hash *)a, CV_TARGET);
 char *targB = hashFindVal((struct hash *)b, CV_TARGET);
 if (targA != NULL && targB != NULL)
     {
     ret = strcasecmp(targA, targB);
     if (ret != 0)
         return ret;
     }
 
 char *termA = hashMustFindVal((struct hash *)a, CV_TERM);
 char *termB = hashMustFindVal((struct hash *)b, CV_TERM);
 return (strcasecmp(termA, termB));
 }
 
 char *getDescription(struct hash *ra,char *alternateSetting)
 // Returns allocated string that is description.  Will include DEPRECATED if appropriate
 {
 struct dyString *dyDescription = dyStringNew(256);
 char *deprecated = hashFindVal(ra, "deprecated");
 if (deprecated != NULL)
     dyStringPrintf(dyDescription,"DEPRECATED - %s",deprecated);
 char *description = hashFindVal(ra, CV_DESCRIPTION);
 if (description == NULL && alternateSetting != NULL)
     description = hashFindVal(ra, alternateSetting);
 if (description == NULL)
     description = hashFindVal(ra, CV_TITLE);
 if (description == NULL)
     description = hashFindVal(ra, CV_LABEL);
 if (description != NULL)
     {
     if (dyStringLen(dyDescription) > 0)
         dyStringAppend(dyDescription,"<BR>");
     dyStringAppend(dyDescription,description);
     }
 if (dyStringLen(dyDescription) > 0)
     return dyStringCannibalize(&dyDescription);
 dyStringFree(&dyDescription);
 return NULL;
 }
 
 void printDescription(struct hash *ra,char *alternateSetting,int colsUsed)
 {
 char *description = getDescription(ra,alternateSetting);
 if (colsUsed >= 0)
     printf("  <TD colspan=%d>", TABLE_COLS_AVAILABLE(colsUsed) );
 else
     printf("  <TD>");
 if (description != NULL)
     {
     printf("%s</TD>\n", description );
     freeMem(description);
     }
 else
     puts("&nbsp;</TD>\n");
 }
 
 void printLabel(struct hash *ra,char *term)
 {
 char *label = hashFindVal(ra, CV_LABEL);
 if (label != NULL)
     printf("  <TD><I>%s</I></TD>\n", label );
 else
     printf("  <TD>%s</TD>\n", term );
 }
 
 char *printTerm(struct hash *ra)
 {
 char *term = (char *)hashMustFindVal(ra, CV_TERM);
 printf("  <TD>%s</TD>\n", term);
 return term;
 }
 
 void printSetting(struct hash *ra,char *setting)
 {
 char *val = hashFindVal(ra, setting);
 printf("  <TD>%s</TD>\n", val ? val : "&nbsp;");
 }
 
 void printSettingsWithUrls(struct hash *ra,char *urlSetting,char *nameSetting,char *idSetting)
 // will print one or more urls with name and optional id.  Only Name is required!
 // If more than one, then should add same number of slots to each ("fred;ethyl" & " ;wife")
 {
 char *names = hashFindVal(ra, nameSetting);
 struct slName *nameList = slNameListFromString(names, ';');;
 
 char *urls = NULL;
 struct slName *urlList = NULL;
 char *ids = NULL;
 struct slName *idList = NULL;
 if (idSetting != NULL)
     ids = hashFindVal(ra, idSetting);
 if (ids != NULL)
     {
     idList = slNameListFromString(ids, ';');
     if (slCount(idList) > slCount(nameList))
         {
         if (slCount(nameList) == 1)
             {
             while (slCount(nameList) < slCount(idList))
                 slAddHead(&nameList,slNameNew(nameList->name));
             }
         else
             errAbort("The number of of items in %s and %s must match for term %s",
                      nameSetting,idSetting,(char *)hashMustFindVal(ra,CV_TERM));
         }
     }
 
 if (urlSetting != NULL)
     urls = hashFindVal(ra, urlSetting);
 if (urls != NULL)
     {
     if (slCount(nameList) == 1)
         urlList = slNameNew(urls); // It is the case that singleton URLs sometimes have ';'!
     else
         {
         urlList = slNameListFromString(urls, ';');
         if (slCount(urlList) > slCount(nameList))
             errAbort("The number of of items in %s and %s must match for term %s",
                      nameSetting,urlSetting,(char *)hashMustFindVal(ra,CV_TERM));
         }
     }
 
 printf("  <TD>");
 
 // while there are items in the list of vendorNames, print the vendorName 
 //  and vendorID together with the url if present
 struct slName *curName = NULL;
 struct slName *curId;
 struct slName *curUrl;
 
 for (curName=nameList,curId=idList,curUrl=urlList; curName != NULL; curName=curName->next)
     {
     if (curName!=nameList)  // Break between links
         printf("<BR>\n      ");
 
     // if there is a url, add it as a link
     char *url = NULL;
     if (curUrl != NULL)
         {
         url = trimSpaces(curUrl->name);
         if (isNotEmpty(url))
             printf("<A TARGET=_BLANK HREF=%s>", url);
         curUrl=curUrl->next;
         }
 
     printf("%s", curName->name);
     if (curId != NULL)
         {
         char *id = trimSpaces(curId->name);
         if (isNotEmpty(id))
             printf(" %s", id );
         curId=curId->next;
         }
 
     if (isNotEmpty(url))
         printf("</A>");
     }
 puts("</TD>");
 
 // Free the memory
 slFreeList(&nameList);
 slFreeList(&idList);
 slFreeList(&urlList);
 }
 
 boolean doTypeDefinition(char *type,boolean inTable,boolean showType)
 // Write out description of the type if it is known
 {
 struct hash *typeHash = (struct hash *)cvTermTypeHash();
 
 struct hash *ra = hashFindVal(typeHash,(char *)cvTermNormalized(type)); // Find by term
 if (ra == NULL)
     return FALSE;
 
 char *label = hashFindVal(ra, CV_LABEL);
 
 struct dyString *dyDefinition = dyStringNew(256);
 if (inTable)
     dyStringPrintf(dyDefinition,"<tr><td colspan=%d style='background:%s; color:%s;'>&nbsp;",
                    TABLE_COLS_AVAILABLE(0),COLOR_LTGREEN,COLOR_DARKBLUE);
 else
     dyStringPrintf(dyDefinition,"<div style='max-width:900px;'>");
 if (label != NULL)
     {
     dyStringPrintf(dyDefinition,"<B><em>%s</em></B>",label);
     if (showType)
         dyStringPrintf(dyDefinition,"&nbsp;[%s]",type);
     dyStringAppend(dyDefinition,":&nbsp;");
     }
 else if (showType)
     dyStringPrintf(dyDefinition,"<B>%s</B>:&nbsp;",type);
 
 char *val = getDescription(ra,NULL);
 dyStringPrintf(dyDefinition,"%s",val);
 freeMem(val);
 if (inTable)
     dyStringAppend(dyDefinition,"&nbsp;</td></tr>");
 else
     dyStringPrintf(dyDefinition,"</div>");
 printf("%s\n",dyStringContents(dyDefinition));
 dyStringFree(&dyDefinition);
 
 return TRUE;
 }
 
 void printColHeader(boolean emphasis,char *title,int sortOrder,char *extra,int colSpan)
 {
 printf("<TH");
 if (sortOrder > 0)
     printf(" class='sortable sort%d'",sortOrder);
 if (colSpan > 1)
     printf(" colspan=%d",colSpan);
 if (extra)
     printf(" %s",extra);
 
 printf(">");
 if (emphasis)
     printf("<em>");
 printf("%s",title);
 if (emphasis)
     printf("</em>");
 printf("</TH>");
 }
 
 void doTypeHeader(char *type, char *cellOrg,boolean sortable)
 {
 if ((organismOptLower != NULL) && !sameWord(cellOrg, organismOptLower))
     errAbort("specified organism %s not consistent with cell type which is org %s\n",
         organismOpt, cellOrg);
 
 // NOTE:  All tables must have the same number of columns in order to allow 'control'
 //        to be swapped in  Use colSapn= on description column
 
 printf("<THEAD><TR valign='bottom' style='background:%s;'>\n",COLOR_BG_HEADER_LTBLUE);
 int sortOrder = (sortable ? 1: -999); // hint: -999 will keep sortOrtder++ < 0
 if (sameWord(type,CV_TERM_CELL))
     {
     printf("<!-- Cell Line table: contains links to protocol file and vendor description page -->");
 
     /* Venkat: To differentiate between the print statments of Mouse and Human Cell Lines */
     if (sameWord(cellOrg,ORG_HUMAN))
         {
         printColHeader(FALSE,type,         sortOrder++,NULL,1);
         printColHeader(FALSE,"Tier",       sortOrder++,NULL,1);
         printColHeader(FALSE,"Description",sortOrder++,NULL,1);
         printColHeader(FALSE,"Lineage",    sortOrder++,NULL,1);
         printColHeader(FALSE,"Tissue",     sortOrder++,NULL,1);
         printColHeader(FALSE,"Karyotype",  sortOrder++,NULL,1);
         printColHeader(FALSE,"Sex",        sortOrder++,NULL,1);
         printColHeader(FALSE,"Documents",  sortOrder++,NULL,1);
         printColHeader(FALSE,"Vendor ID",  sortOrder++,NULL,1);
         printColHeader(FALSE,"Term ID",    sortOrder++,NULL,1);
         printColHeader(TRUE ,"Label",      sortOrder++,NULL,1);
         }
     else
         {
         printColHeader(FALSE,"Source",     sortOrder++,NULL,1);
         printColHeader(FALSE,"Description",sortOrder++,NULL,TABLE_COLS_AVAILABLE(8));
         printColHeader(FALSE,"Category",   sortOrder++,NULL,1);
         printColHeader(FALSE,"Tissue",     sortOrder++,NULL,1);
         printColHeader(FALSE,"Sex",        sortOrder++,NULL,1);
         printColHeader(FALSE,"Documents",  sortOrder++,NULL,1);
         printColHeader(FALSE,"Source Lab", sortOrder++,NULL,1);
         printColHeader(FALSE,"Term ID",    sortOrder++,NULL,1);
         printColHeader(TRUE ,"Label",      sortOrder++,NULL,1);
         }
     }
 else if (sameWord(type,CV_TERM_ANTIBODY))
     {
     printColHeader(FALSE,type,                  sortOrder++,NULL,1);
     printColHeader(FALSE,"Antibody Description",sortOrder++,NULL,TABLE_COLS_AVAILABLE(9));
     printColHeader(FALSE,"Target",              sortOrder++,NULL,1);
     printColHeader(FALSE,"Target Description",  sortOrder++,"style='min-width:600px;'",1);
     printColHeader(FALSE,"Vendor ID",           sortOrder++,NULL,1);
     printColHeader(FALSE,"Lab",                 sortOrder++,NULL,1);
     printColHeader(FALSE,"Documents",           sortOrder++,NULL,1);
     printColHeader(FALSE,"Lots",                sortOrder++,NULL,1);
     printColHeader(FALSE,"Target Link",         sortOrder++,NULL,1);
     printColHeader(TRUE ,"Label",               sortOrder++,NULL,1);
     }
 else
     {
     char *caplitalized = NULL;
     if (sameWord(type,CV_TERM_DATA_TYPE))
         caplitalized = cloneString("Data Type");
     else
         {
         caplitalized = cloneString(type);
         toUpperN(caplitalized,1);
         }
 
     printColHeader(FALSE,caplitalized,sortOrder++,NULL,1);
     if (sameWord(type,CV_TERM_LOCALIZATION))
         {
         printColHeader(FALSE,"Description",sortOrder++,NULL,TABLE_COLS_AVAILABLE(3));
         printColHeader(FALSE,"GO ID",      sortOrder++,NULL,1);
         }
     else if (sameWord(type,CV_TERM_LAB))
         {
         printColHeader(FALSE,"Institution",sortOrder++,NULL,TABLE_COLS_AVAILABLE(5));
         printColHeader(FALSE,"Lab PI",     sortOrder++,NULL,1);
         printColHeader(FALSE,"Grant PI",   sortOrder++,NULL,1);
         printColHeader(FALSE,"Organism",   sortOrder++,NULL,1);
         }
     else
         printColHeader(FALSE,"Description",sortOrder++,NULL,TABLE_COLS_AVAILABLE(2));
 
     printColHeader(TRUE ,"Label",sortOrder++,NULL,1);
     freeMem(caplitalized);
     }
 puts("</TR></THEAD><TBODY>");
 }
 
 boolean doCellRow(struct hash *ra, char *org)
 // print one cell row
 {
 char *s;
 
 s = hashFindVal(ra, ORGANISM);
 if (s != NULL)
     {
     char *cellOrg = cloneString(s);
     strLower(cellOrg);
     if (differentString(cellOrg, org))
         return FALSE;
     }
 
 // pathBuffer for new protocols not in human
 char pathBuffer[PATH_LEN];
 safef(pathBuffer, sizeof(pathBuffer), "/ENCODE/protocols/cell/%s/",org);
 
 if (sameWord(org, ORG_HUMAN))
     {
     if (cgiOptionalInt("tier",0))
         {
         if (hashFindVal(ra,"tier") == NULL)
             return FALSE;
         if (atoi(hashFindVal(ra,"tier"))!=cgiOptionalInt("tier",0))
             return FALSE;
         }
     if (cgiOptionalString("tiers"))
         {
         if (hashFindVal(ra,"tier") == NULL)
             return FALSE;
         boolean found=FALSE;
         char *tiers=cloneString(cgiOptionalString("tiers"));
         char *tier;
         (void)strSwapChar(tiers,',',' ');
         while ((tier=nextWord(&tiers)))
             {
             if (atoi(hashFindVal(ra,"tier"))==atoi(tier))
                 {
                 found=TRUE;
                 break;
                 }
             }
         if (!found)
             return FALSE;
         }
     puts("<TR>");
     char *term = printTerm(ra);
 
     printSetting(ra, "tier");
     printDescription(ra,NULL,-1);
     printSetting(ra,"lineage");
     printSetting(ra,"tissue");
     printSetting(ra,"karyotype");
     printSetting(ra,"sex");
     printDocumentLink(ra,term,"protocol",pathBuffer,NULL,TRUE);
     printSettingsWithUrls(ra,"orderUrl","vendorName","vendorId");
     printSettingsWithUrls(ra,"termUrl","termId",NULL);
     printLabel(ra,term);
     puts("</TR>");
     }
 else        // non-human cell type
     {
     puts("<TR>");
     char *term = printTerm(ra);
 
     printDescription(ra,NULL,8);
     printSetting(ra,"category");
     printSetting(ra,"tissue");
     printSetting(ra,"sex");
     //printSetting(ra,"karyotype");
     printDocumentLink(ra,term,"protocol",pathBuffer,NULL,TRUE);
     printSettingsWithUrls(ra,"orderUrl","vendorName","vendorId");
     printSettingsWithUrls(ra,"termUrl","termId",NULL);
     printLabel(ra,term);
     puts("</TR>");
 
     }
 return TRUE;
 }
 
 boolean doAntibodyRow(struct hash *ra, char *org)
 // print one antibody row
 {
 puts("<TR>");
 char *term = printTerm(ra);
 printDescription(ra,"antibodyDescription",9);
 printSetting(ra,"target");                  // target is NOT first but still is major sort order
 printSetting(ra,"targetDescription");
 printSettingsWithUrls(ra,"orderUrl","vendorName","vendorId");
 printSetting(ra,"lab");
 printDocumentLink(ra,term,"validation","/ENCODE/validation/antibodies/",NULL,FALSE);
 printSetting(ra,"lots");
 printSettingsWithUrls(ra,"targetUrl","targetId",NULL);
 printLabel(ra,term);
 puts("</TR>");
 
 return TRUE;
 }
 
 boolean doTypeOfTermRow(struct hash *ra, char *org)
 // print one typeOfTerm row
 {
 char *term = (char *)hashMustFindVal(ra, CV_TERM);
 
 if (sameString(term,cvTypeNormalized(CV_TERM_CELL)))
     term = CV_TERM_CELL;
 else if (sameString(term,cvTypeNormalized(CV_TERM_ANTIBODY)))
     term = CV_TERM_ANTIBODY;
 
 puts("<TR>");
 printf("  <TD><A HREF='hgEncodeVocab?type=%s' title='%s details' "
         "TARGET=ucscVocabChild>%s</a></TD>\n", term, term, term);
 printDescription(ra,NULL,2);
 printLabel(ra,term);
 puts("</TR>");
 if (sameString(term,CV_TERM_CELL))
     {
     puts("<TR>");
     printf("  <TD><A HREF='hgEncodeVocab?type=%s&organism=Mouse' title='Mouse %s details' "
             "TARGET=ucscVocabChild>%s</a> <em>(for mouse)</em></TD>\n", term, term, term);
     char *s = getDescription(ra,NULL);
     printf("  <TD colspan=%d>%s <em>(for mouse)</em></TD>\n",
             TABLE_COLS_AVAILABLE(2), s?s:"&nbsp;");
     freeMem(s);
     printLabel(ra,term);
     puts("</TR>");
     }
 
 return TRUE;
 }
 
 boolean doTypeRow(struct hash *ra, char *org)
 {
 char *type = (char *)cvTermNormalized(hashMustFindVal(ra, CV_TYPE));
 
 if (sameWord(type,CV_TOT))
     return doTypeOfTermRow(ra,org);
 else if (sameWord(type,CV_TERM_CELL))
     return doCellRow(ra,org);
 else if (sameWord(type,CV_TERM_ANTIBODY))
     return doAntibodyRow(ra,org);
 else if (sameWord(type,CV_TERM_LAB))
     {
     puts("<TR>");
     char *term = printTerm(ra);
     printDescription(ra,"labInst",5);
     printSetting(ra,"labPiFull");
     printSetting(ra,"grantPi");
     printSetting(ra,"organism");
     printLabel(ra,term);
     puts("</TR>");
     }
 else if (sameWord(type,CV_TERM_GRANT))
     {
     puts("<TR>");
     char *term = printTerm(ra);
     printDescription(ra,"grantInst",2);
     printLabel(ra,term);
     puts("</TR>");
     }
 else if (sameWord(type,CV_TERM_LOCALIZATION))
     {
     puts("<TR>");
     char *term = printTerm(ra);
     printDescription(ra,NULL,3);
     printSettingsWithUrls(ra,"termUrl","termId",NULL);
     printLabel(ra,term);
     puts("</TR>");
     }
 else  // generic term: term, description, label
     {
     puts("<TR>");
     char *term = printTerm(ra);
     printDescription(ra,NULL,2);
     printLabel(ra,term);
     puts("</TR>");
     }
 return TRUE;
 }
 
 static char **convertAntibodiesToTargets(struct hash *cvHash,char **requested,int requestCount)
 /* Convers requested terms from antibodies to their corresponding targets */
 {
 struct hashCookie hc = hashFirst(cvHash);
 struct hashEl *hEl;
 struct hash *ra;
 char **targets = needMem(sizeof(char *)*requestCount);
 int ix = 0;
 
 assert(requested != NULL);
 
 while ((hEl = hashNext(&hc)) != NULL)
     {
     ra = (struct hash *)hEl->val;
     ix = stringArrayIx(hashMustFindVal(ra, CV_TERM),requested,requestCount);
     if (ix != -1 && targets[ix] == NULL) // but not yet converted to antibody
         {
         // Special case for Inputs that do not have targets but are listed as Antibody
         // It is generalized to cover all cases for missing target
         targets[ix] = cloneString(hashFindVal(ra, CV_TARGET)); // May have a target
         if (targets[ix] == NULL)
             targets[ix] = cloneString(hashMustFindVal(ra, CV_TERM)); // Must have a term
         }
     }
 
 // At this point every term should have been found
 for (ix=0;ix<requestCount;ix++)
     {
     if (targets[ix] == NULL)
         errAbort("Failed to find antibody %s=%s\n",CV_TERM,requested[ix]);
     }
 
 return targets;
 }
 
 static char *normalizeType(char *type)
 /* Strips any quotation marks and converts common synonyms */
 {
 if (type != NULL)
     {
     (void)stripChar(type,'\"');
     if (sameWord(type,"Factor"))
         return cloneString(CV_TERM_ANTIBODY);
 
     char *cleanType = cloneString(cvTermNormalized(type));
     if (differentString(cleanType,type))
         return cleanType;
     freeMem(cleanType);
     /*
     if ((sameWord(type,"Cell Line"))
     ||  (sameWord(type,"cellLine" ))
     ||  (sameWord(type,"Cell Type"))
     ||  (sameWord(type,"Cell Type"))
     ||  (sameWord(type,"Cell" )))  // sameWord is case insensitive
         return cloneString(CV_TERM_CELL);
     else if (sameWord(type,"Factor"))
          ||  (sameString(type,"Antibody")))
         return cloneString(CV_TERM_ANTIBODY);
     */
     }
 return type;
 }
 
 static char *findType(struct hash *cvHash,char **requested,int requestCount,
                       char **queryBy, char **org,boolean silent)
 /* returns the type that was requested or else the type associated with the term requested */
 {
 struct hashCookie hc = hashFirst(cvHash);
 struct hashEl *hEl;
 struct hash *ra;
 char *type = typeOpt;
 
 if (requested != NULL) // if no type, find it from requested terms.  Will validate terms match type
     {              // NOTE: Enter here even if there is a type, to confirm the type
     while ((hEl = hashNext(&hc)) != NULL)  // FIXME: This should be using mdbCv APIs to get hashes.
         {                                  // One per "request[]"
         ra = (struct hash *)hEl->val;
         if (sameWord(hashMustFindVal(ra, CV_TYPE),CV_TOT)) // TOT = typeOfTerm
             continue;
         char *val = hashFindVal(ra, *queryBy);
         if (val != NULL)
             {
             int ix = stringArrayIx(val,requested,requestCount);
             if (ix != -1) // found
                 {
                 char *thisType = hashMustFindVal(ra, CV_TYPE);
                 char *thisOrg = hashFindVal(ra, ORGANISM);
                 if (type == NULL)
                     {
                     if (thisOrg != NULL)
                         {
                         *org = strLower(cloneString(thisOrg));
                         }
                     type = thisType;
                     }
                 else if (differentWord(type,thisType))
                     {
                     if (sameWord(CV_TERM_CONTROL,type))
                         type = thisType;
                     else if (differentWord(CV_TERM_CONTROL,thisType))
                         errAbort("Error: Requested %s of type '%s'.  But '%s' has type '%s'\n",
                                  *queryBy,type,requested[ix],thisType);
                     }
                 }
             }
         }
     }
 if (type == NULL && sameWord(*queryBy,CV_TERM))    // Special case of term becoming target
     {
     char *queryByTarget = CV_TARGET;
     type = findType(cvHash,requested,requestCount,&queryByTarget,org,TRUE); // silent here
     if (type != NULL)
         *queryBy = queryByTarget;
     }
 if (type == NULL && !silent)    // Still not type? abort
     errAbort("Error: Required %s=%s ['%s', '%s', '%s', '%s' or '%s'] argument not found\n",
                     *queryBy,(requested != NULL) ? *requested : "?",
                     CV_TYPE, CV_TERM, CV_TAG, CV_TARGET, CV_LABEL);
 
 return normalizeType(type);
 }
 
 void doMiddle()
 {
 struct hash *cvHash = raReadAll((char *)cvFile(), CV_TERM);
 struct hashCookie hc = hashFirst(cvHash);
 struct hashEl *hEl;
 struct slList *termList = NULL;
 struct hash *ra;
 int totalPrinted = 0;
 boolean excludeDeprecated = (cgiOptionalString("deprecated") == NULL);
 
 // Prepare an array of selected terms (if any)
 int requestCount = 0;
 char **requested = NULL;
 char *requestVal = termOpt;
 char *queryBy = CV_TERM;
 if (tagOpt)
     {
     requestVal = tagOpt;
     queryBy = CV_TAG;
     }
 else if (targetOpt)
     {
     requestVal = targetOpt;
     queryBy = CV_TERM;  // request target is special: lookup term, convert to target, display target
     }
 else if (labelOpt)
     {
     requestVal = labelOpt;
     queryBy = CV_LABEL;
     }
 if (requestVal)
     {
     (void)stripChar(requestVal,'\"');
-    requestCount = chopCommas(requestVal,NULL);
+    requestCount = chopCommasLen(requestVal);
     requested = needMem(requestCount * sizeof(char *));
     chopByChar(requestVal,',',requested,requestCount);
     }
 
 char *org = NULL;
 // if the org is specified in the type (eg. cell line)
 // then use that for the org, otherwise use the command line option,
 // otherwise use human.
 char *type = findType(cvHash,requested,requestCount,&queryBy, &org, FALSE);
 if (org == NULL)
     org = organismOptLower;
 if (org == NULL)
     org = ORG_HUMAN;
 
 // Special logic for requesting antibody by target
 if (targetOpt && requestCount > 0 && sameWord(queryBy,CV_TERM) && sameWord(type,CV_TERM_ANTIBODY))
     {
     // Several antibodies may have same target.
     // requested target={antibody} and found antibody
     // Must now convert each of the requested terms to its target before displaying all targets
     char **targets = convertAntibodiesToTargets(cvHash,requested,requestCount);
     if (targets != NULL)
         {
         freeMem(requested);
         requested = targets;
         queryBy = CV_TARGET;
         }
     }
 //warn("Query by: %s = '%s' type:%s",queryBy,requestVal?requestVal:"all",type);
 
 // Get just the terms that match type and requested, then sort them
 if (differentWord(type,CV_TOT) || typeOpt != NULL ) // If type resolves to typeOfTerm and
     {                                               // typeOfTerm was not requested,
     while ((hEl = hashNext(&hc)) != NULL)           // then just show definition
         {
         ra = (struct hash *)hEl->val;
         char *thisType = (char *)cvTermNormalized(hashMustFindVal(ra,CV_TYPE));
         if (differentWord(thisType,type) && (requested == NULL
         ||  differentWord(thisType,CV_TERM_CONTROL)))
             continue;
         // Skip all rows that do not match queryBy param if specified
         if (requested)
             {
             char *val = hashFindVal(ra, queryBy);
             if (val == NULL)
                 {
                 // Special case for input that has no target
                 if (sameString(queryBy, CV_TARGET))
                     val = hashMustFindVal(ra, CV_TERM);
                 else
                     continue;
                 }
             if (-1 == stringArrayIx(val,requested,requestCount))
                 continue;
             }
         else if (excludeDeprecated)
             {
             if (hashFindVal(ra, "deprecated") != NULL)
                 continue;
             }
         slAddTail(&termList, ra);
         }
     }
 slSort(&termList, termCmp);
 
 boolean described = doTypeDefinition(type,FALSE,(slCount(termList) == 0));
 boolean sortable = (slCount(termList) > 5);
 if (sortable)
     {
     webIncludeResourceFile("HGStyle.css");
     jsIncludeFile("jquery.js",NULL);
     jsIncludeFile("utils.js",NULL);
     printf("<TABLE class='sortable' border=1 CELLSPACING=0 style='border: 2px outset #006600; "
            "background-color:%s;'>\n",COLOR_BG_DEFAULT);
     }
 else
     printf("<TABLE BORDER=1 BGCOLOR=%s CELLSPACING=0 CELLPADDING=2>\n",COLOR_BG_DEFAULT);
 if (slCount(termList) > 0)
     {
     doTypeHeader(type, org,sortable);
 
     // Print out the terms
     while ((ra = slPopHead(&termList)) != NULL)
         {
         if (doTypeRow( ra, org ))
             totalPrinted++;
         }
     }
 puts("</TBODY></TABLE><BR>");
 if (sortable)
     jsInline("{$(document).ready(function() "
          "{sortTable.initialize($('table.sortable')[0],true,true);});}\n");
 if (totalPrinted == 0)
     {
     if (!described)
         warn("Error: Unrecognised type (%s)\n", type);
     }
 else if (totalPrinted > 1)
     printf("Total = %d\n", totalPrinted);
 }
 
 int main(int argc, char *argv[])
 /* Process command line */
 {
 long enteredMainTime = clock1000();
 cgiSpoof(&argc, argv);
 termOpt = cgiOptionalString(CV_TERM);
 tagOpt = cgiOptionalString(CV_TAG);
 typeOpt = cgiOptionalString(CV_TYPE);
 targetOpt = cgiOptionalString(CV_TARGET);
 labelOpt = cgiOptionalString(CV_LABEL);
 organismOpt = cgiUsualString(ORGANISM, organismOpt);
 if (organismOpt != NULL)
     {
     organismOptLower=cloneString(organismOpt);
     strLower(organismOptLower);
     }
 char *bgColor = cgiOptionalString("bgcolor");
 if (bgColor)
     htmlSetBgColor(strtol(bgColor, 0, 16));
 htmlSetStyle(htmlStyleUndecoratedLink);
 htmShell("ENCODE Controlled Vocabulary", doMiddle, "get");
 cgiExitTime("hgEncodeVocab", enteredMainTime);
 return 0;
 }