4898794edd81be5285ea6e544acbedeaeb31bf78
max
  Tue Nov 23 08:10:57 2021 -0800
Fixing pointers to README file for license in all source code files. refs #27614

diff --git src/hg/near/hgNear/customColumn.c src/hg/near/hgNear/customColumn.c
index 7d35b6a..79b171f 100644
--- src/hg/near/hgNear/customColumn.c
+++ src/hg/near/hgNear/customColumn.c
@@ -1,765 +1,765 @@
 /* customColumn - handle columns put in by users. */
 
 /* Copyright (C) 2012 The Regents of the University of California 
- * See README in this or parent directory for licensing information. */
+ * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "linefile.h"
 #include "hash.h"
 #include "localmem.h"
 #include "cheapcgi.h"
 #include "obscure.h"
 #include "portable.h"
 #include "net.h"
 #include "cart.h"
 #include "hgNear.h"
 
 static char *customFileName()
 /* Return file name associated with custom column if any.
  * Delete cart var if it's grown stale. */
 {
 char *fileName = NULL;
 if (cartVarExists(cart, customFileVarName))
     {
     fileName = cartString(cart, customFileVarName);
     if (!fileExists(fileName))
 	{
         cartRemove(cart, customFileVarName);
 	fileName = NULL;
 	}
     }
 return fileName;
 }
 
 static char *newCustomFileName()
 /* Create new custom file name. */
 {
 struct tempName tn;
 makeTempName(&tn, "near", ".col");
 return cloneString(tn.forCgi);
 }
 
 void doCustomPage(struct sqlConnection *conn, struct column *colList)
 /* Put up page to input custom columns. */
 {
 makeTitle("Setup Custom Columns for Gene Sorter", "hgNearHelp.html#Custom");
 hPrintf("<FORM ACTION=\"../cgi-bin/hgNear\">\n");
 cartSaveSession(cart);
 
 /* Put up descriptive text. */
 controlPanelStart();
 hPrintf(
 "Add your own custom columns to the Gene Sorter using the "
 "<A HREF=\"../goldenPath/help/customColumn.html\">"
 "format described here</A>.");
 controlPanelEnd();
 
 /* Put up various buttons. */
 controlPanelStart();
 hPrintf("<TABLE><TR><TD>");
 hPrintf("Custom column action: ");
 hPrintf("</TD><TD>");
 cgiMakeOptionalButton(customClearDoName, "clear", 
 	customFileName() == NULL);
 hPrintf("</TD><TD>");
 cgiMakeButton(customPasteDoName, "paste in");
 hPrintf("</TD><TD>");
 cgiMakeButton(customUploadDoName, "from file");
 hPrintf("</TD><TD>");
 cgiMakeButton(customFromUrlDoName, "from URL");
 hPrintf("</TD><TD>");
 cgiMakeButton(confVarName, "cancel");
 hPrintf("</TD></TR></TABLE>");
 controlPanelEnd();
 
 hPrintf("</FORM>");
 }
 
 static void submitCancel()
 /* Put up little table with submit and cancle buttons. */
 {
 hPrintf("<TABLE><TR><TD>");
 cgiMakeButton(customSubmitDoName, "submit");
 hPrintf("</TD><TD>");
 hPrintf(" ");
 hPrintf("</TD><TD>");
 cgiMakeButton(customPageDoName, "cancel");
 hPrintf("</TD></TR></TABLE>");
 }
 
 void doCustomUpload(struct sqlConnection *conn, struct column *colList)
 /* Put up page to upload custom columns. */
 {
 makeTitle("Upload Custom Columns", "hgNearHelp.html#Custom");
 hPrintf("<FORM ACTION=\"../cgi-bin/hgNear\" METHOD=\"POST\" ENCTYPE=\"multipart/form-data\">\n");
 cartSaveSession(cart);
 
 controlPanelStart();
 hPrintf(
 "Upload a file with custom columns in the "
 "<A HREF=\"../goldenPath/help/customColumn.html\">"
 "format described here</A>.");
 hPrintf("<BR>");
 hPrintf("Column File: <INPUT TYPE=FILE NAME=%s> ", customUploadVarName);
 hPrintf("<BR>");
 submitCancel();
 controlPanelEnd();
 
 hPrintf("</FORM>");
 }
 
 void doCustomPaste(struct sqlConnection *conn, struct column *colList)
 /* Put up page to paste custom columns. */
 {
 makeTitle("Paste in Custom Columns", "hgNearHelp.html#Custom");
 hPrintf("<FORM ACTION=\"../cgi-bin/hgNear\" METHOD=\"POST\">\n");
 cartSaveSession(cart);
 
 controlPanelStart();
 hPrintf(
 "Paste in custom columns in the "
 "<A HREF=\"../goldenPath/help/customColumn.html\">"
 "format described here</A>.");
 hPrintf("<BR>");
 cgiMakeTextArea(customPasteVarName, "", 10, 70);
 submitCancel();
 controlPanelEnd();
 
 hPrintf("</FORM>");
 }
 
 void doCustomFromUrl(struct sqlConnection *conn, struct column *colList)
 /* Put up page to paste in URLS with custom columns. */
 {
 makeTitle("Paste in Custom Columns", "hgNearHelp.html#Custom");
 hPrintf("<FORM ACTION=\"../cgi-bin/hgNear\" METHOD=\"POST\">\n");
 cartSaveSession(cart);
 
 controlPanelStart();
 hPrintf(
 "Paste in URL or list of URLs that refer to files in the "
 "<A HREF=\"../goldenPath/help/customColumn.html\">"
 "format described here</A>.");
 hPrintf("<BR>");
 cgiMakeTextArea(customFromUrlVarName, "", 10, 70);
 submitCancel();
 controlPanelEnd();
 
 hPrintf("</FORM>");
 }
 
 /* ---- Parse custom columns. ---- */
 
 static void addIfMissing(struct hash *settings, char *var, char *val)
 /* Add var to hash if it's not there already. */
 {
 if (!hashLookup(settings, var))
     hashAdd(settings, var, cloneString(val));
 }
 
 static void outputSetting(FILE *f, char *name, char *val)
 /* Output name=val pair,  adding quotes if val has spaces. */
 {
 fprintf(f, "%s=", name);
 if (hasWhiteSpace(val))
     {
     if (strchr(val, '"') != NULL)
 	{
 	char *s = makeEscapedString(val, '"');
 	fprintf(f, "\"%s\"", s);
 	freez(&s);
 	}
     else
 	{
 	fprintf(f, "\"%s\"", val);
 	}
     }
 else
     fprintf(f, "%s", val);
 }
 
 static void outputSettings(FILE *f, struct hash *settings)
 /* Output settings hash as a var=val set. */
 {
 struct hashEl *list, *el;
 list = hashElListHash(settings);
 for (el = list; el != NULL; el = el->next)
     {
     fprintf(f, " ");
     outputSetting(f, el->name, el->val);
     }
 hashElFreeList(&list);
 }
 
 struct column *parseColumnLine(struct lineFile *lf, char *line)
 /* Parse out column header line and return a column based on it. */
 {
 struct hash *settings;
 struct column *col;
 AllocVar(col);
 settings = col->settings = hashVarLine(line, lf->lineIx);
 addIfMissing(settings, "name", "custom");
 addIfMissing(settings, "shortLabel", "User Column");
 addIfMissing(settings, "longLabel", "User custom column");
 addIfMissing(settings, "visibility", "on");
 addIfMissing(settings, "priority", "2.01");
 addIfMissing(settings, "org", genome);
 addIfMissing(settings, "db", database);
 hashAdd(settings, "type", cloneString("custom"));
 columnVarsFromSettings(col, lf->fileName);
 return col;
 }
 
 struct column *verifyCopyColumn(struct sqlConnection *conn, 
 	struct lineFile *lf, FILE *output, char *line,
 	struct column *builtInList, struct hash *uniqHash)
 /* Parse out column from line file.  If output is non-NULL
  * then also save it to output. */
 {
 char *word, *name;
 struct column *col = parseColumnLine(lf, line);
 struct hash *lookupHash = NULL, *gpHash = newHash(16);
 char *lookup = NULL;
 int hitCount = 0, missCount = 0, badGeneCount = 0;
 struct genePos *gpList, *gp;
 char *oneMiss = NULL, *oneBadGene = NULL;
 
 /* Build up hash & list of all known genes. */
 gpList = knownPosAll(conn);
 for (gp = gpList; gp != NULL; gp = gp->next)
     hashAdd(gpHash, gp->name, gp);
 
 /* Deal with non-uniq names. */
 if (hashLookup(uniqHash, col->name))
     {
     int ix = 1;
     char buf[256];
     for (;;)
 	{
 	safef(buf, sizeof(buf), "%s%d", col->name, ++ix);
 	if (!hashLookup(uniqHash, buf))
 	    {
 	    col->name = cloneString(buf);
 	    break;
 	    }
 	}
     hashRemove(col->settings, "name");
     hashAdd(col->settings, "name", col->name);
     }
 hashAdd(uniqHash, col->name, col);
 
 /* If there's a idLookup setting then make lookup hash. */
 if ((lookup = hashFindVal(col->settings, "idLookup")) != NULL)
     {
     struct column *lookupCol = findNamedColumn(lookup);
     if (lookupCol == NULL)
         errAbort("Couldn't find column %s for idLookup line %d of %s",
 		lookup, lf->lineIx, lf->fileName);
     lookupHash = hashNew(16);
     for (gp = gpList; gp != NULL; gp = gp->next)
         {
 	char *cell = lookupCol->cellVal(lookupCol, gp, conn);
 	if (cell != NULL)
 	    {
 	    hashAdd(lookupHash, cell, gp->name);
 	    freez(&cell);
 	    }
 	}
     }
 
 /* Allocate structure and fill in from header. */
 fprintf(output, "column");
 outputSettings(output, col->settings);
 fprintf(output, "\n");
 
 /* Make hash of lines. */
 while (lineFileNext(lf, &line, NULL))
     {
     line = trimSpaces(line);
     if (line[0] == '#')
         continue;
     if (line[0] == 0)
         break;
     if (startsWith("column ", line))
         {
 	lineFileReuse(lf);
 	break;
 	}
     name = word = nextWord(&line);
     line = skipLeadingSpaces(line);
     if (line == NULL || line[0] == 0)
         {
 	errAbort("Expecting at least two words in line %d of %s",
 	    lf->lineIx, lf->fileName);
 	}
     if (lookupHash != NULL)
 	{
 	name = hashFindVal(lookupHash, word);
 	if (name == NULL)
 	    {
 	    ++missCount;
 	    if (oneMiss == NULL)
 	        oneMiss = cloneString(word);
 	    continue;
 	    }
 	}
     else
         {
 	if (!hashLookup(gpHash, name))
 	    {
 	    ++badGeneCount;
 	    if (oneBadGene == NULL)
 	        {
 		oneBadGene = cloneString(name);
 		continue;
 		}
 	    }
 	}
     ++hitCount;
     fprintf(output, "%s %s\n", name, line);
     }
 fprintf(output, "\n");
 if (missCount > 0)
    {
    warn("%d items including '%s' not found (and %d found) in %s", 
    	missCount, oneMiss, hitCount, lookup);
    }
 if (badGeneCount > 0)
    {
    warn("%d gene ID's including '%s' not found (and %d found)", 
    	badGeneCount, oneBadGene, hitCount);
    }
 hashFree(&lookupHash);
 return col;
 }
 
 static struct hash *hashColumns(struct column *colList)
 /* Return a hash of columns keyed by name. */
 {
 struct column *col;
 struct hash *hash = hashNew(9);
 for (col = colList; col != NULL; col = col->next)
     hashAdd(hash, col->name, col);
 return hash;
 }
 
 static struct column *verifyCopyColumns(struct sqlConnection *conn,
 	struct lineFile *lf, FILE *output, 
 	struct column *builtInList, struct hash *uniqHash)
 /* Read in custom columns.  Verify that they are ok.  Copy 
  * version after lookup if any to output. */
 {
 char *line, *word;
 struct column *colList = NULL;
 while (lineFileNextReal(lf, &line))
     {
     word = nextWord(&line);
     if (sameString("column", word))
         {
 	struct column *col = verifyCopyColumn(conn, lf, output, line, builtInList, uniqHash);
 	if (col != NULL)
 	    {
 	    slAddHead(&colList, col);
 	    }
 	}
     else
         {
 	errAbort("Unrecognized word '%s' line %d of %s", word, 
 		lf->lineIx, lf->fileName);
 	break;
 	}
     }
 return colList;
 }
 
 static void removeOldCustomColumns(struct column **pColList)
 /* Remove any existing custom columns. */
 {
 struct column *list = NULL, *col, *next;
 for (col = *pColList; col != NULL; col = next)
     {
     next = col->next;
     if (!startsWith("custom", col->type))
         {
 	slAddHead(&list, col);
 	}
     }
 slReverse(&list);
 *pColList = list;
 }
 
 static void customFromText(struct sqlConnection *conn, char *text, 
 	struct column **pColList)
 /* Convert custom column text to lineFile, and call custom column
  * updater. */
 {
 struct lineFile *lf = lineFileOnString("custom column", TRUE, text);
 char *outFileName = newCustomFileName();
 struct column *newList;
 FILE *f;
 struct hash *uniqHash = NULL;
 
 /* Remove file name from cart for better error recovery. */
 cartRemove(cart, customFileVarName);
 
 /* Copy and verify output. */
 f = mustOpen(outFileName, "w");
 removeOldCustomColumns(pColList);
 uniqHash = hashColumns(*pColList);
 newList = verifyCopyColumns(conn, lf, f, *pColList, uniqHash);
 *pColList = slCat(*pColList, newList);
 lf->buf = NULL;		/* Don't let lineFileClose free this */
 lineFileClose(&lf);
 carefulClose(&f);
 
 /* Put back file name in cart , looks like we must have succeeded. */
 cartSetString(cart, customFileVarName, outFileName);
 freez(&outFileName);
 freeHash(&uniqHash);
 }
 
 static void customFromUrls(struct sqlConnection *conn, char *urlList, 
 	struct column **pColList)
 /* Grab custom columns from a list of URLs. */
 {
 char *outFileName = newCustomFileName();
 struct column *customCols = NULL;
 FILE *f;
 char *url;
 struct hash *uniqHash = NULL;
 
 /* Remove file name from cart for better error recovery. */
 cartRemove(cart, customFileVarName);
 
 /* Copy and verify output. */
 f = mustOpen(outFileName, "w");
 removeOldCustomColumns(pColList);
 uniqHash = hashColumns(*pColList);
 
 /* Loop through and add columns from each URL. */
 while ((url = nextWord(&urlList)) != NULL)
     {
     if (!(startsWith("http://" , url)
        || startsWith("https://", url)
        || startsWith("ftp://"  , url)))
 	errAbort("Invalid url [%s]. URLs must start with http://, https://, or ftp://", url);
     struct lineFile *lf = netLineFileOpen(url);
     struct column *newList = verifyCopyColumns(conn, lf, f, *pColList, uniqHash);
     customCols = slCat(newList, customCols);
     lineFileClose(&lf);
     }
 
 *pColList = slCat(customCols, *pColList);
 carefulClose(&f);
 
 /* Put back file name in cart , looks like we must have succeeded. */
 cartSetString(cart, customFileVarName, outFileName);
 freez(&outFileName);
 hashFree(&uniqHash);
 }
 
 void doCustomSubmit(struct sqlConnection *conn, struct column *colList)
 /* Put up page to submit custom columns. */
 {
 if (cartVarExists(cart, customUploadVarName))
     {
     customFromText(conn, cartString(cart, customUploadVarName), &colList);
     cartRemove(cart, customUploadVarName);
     }
 else if (cartVarExists(cart, customPasteVarName))
     {
     customFromText(conn, cartString(cart, customPasteVarName), &colList);
     cartRemove(cart, customPasteVarName);
     }
 else if (cartVarExists(cart, customFromUrlVarName))
     {
     customFromUrls(conn, cartString(cart, customFromUrlVarName), &colList);
     cartRemove(cart, customFromUrlVarName);
     }
 else
     {
     warn("doCustomSubmit without recognized custom variable");
     }
 slSort(&colList, columnCmpPriority);
 doConfigure(conn, colList, NULL);
 }
 
 void doCustomClear(struct sqlConnection *conn, struct column *colList)
 /* Put up page to clear custom columns. */
 {
 removeOldCustomColumns(&colList);
 cartRemove(cart, customFileVarName);
 doConfigure(conn, colList, NULL);
 }
 
 struct column *quickParseColumn(struct lineFile *lf, char *colLine)
 /* Given column line, parse column until blank line with
  * minimal error checking. */
 {
 struct column *col = parseColumnLine(lf, colLine);
 struct hash *idHash;
 char *line, *word;
 if (col == NULL)
     return NULL;
 col->customIdHash = idHash = hashNew(16);
 while (lineFileNext(lf, &line, NULL))
     {
     if (line[0] == 0)
         break;
     word = nextWord(&line);
     line = skipLeadingSpaces(line);
     hashAdd(idHash, word, lmCloneString(idHash->lm, line));
     }
 return col;
 }
 
 struct column *quickParseColumns(char *fileName)
 /* Parse column that's been validated and put into temp file. */
 {
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 struct column *colList = NULL;
 char *line, *word;
 while (lineFileNextReal(lf, &line))
     {
     word = nextWord(&line);
     if (sameString("column", word))
 	{
 	struct column *col = quickParseColumn(lf, line);
 	if (col != NULL)
 	    {
 	    slAddHead(&colList, col);
 	    }
 	}
     else
 	{
 	warn("Unrecognized word '%s' line %d of %s", word, 
 		lf->lineIx, lf->fileName);
 	break;
 	}
     }
 lineFileClose(&lf);
 return colList;
 }
 
 struct column *customColumnsRead(struct sqlConnection *conn, char *org, char *db)
 /* Read in data for custom columns. */
 {
 char *fileName = customFileName();
 struct column *colList = NULL;
 
 if (fileName != NULL)
     colList = quickParseColumns(fileName);
 return colList;
 }
 
 /* ---- Implement column methods for custom types ---- */
 
 static boolean colSettingOn(struct column *col, char *name)
 /* Return TRUE if setting exists and has value "on" */
 {
 char *val = hashFindVal(col->settings, name);
 return val != NULL && sameString(val, "on");
 }
 
 static boolean customExists(struct column *col, struct sqlConnection *conn)
 /* Custom column exists if we are in right database. */
 {
 char *db = hashFindVal(col->settings, "db");
 return db == NULL || sameString(db, database);
 }
 
 char *customCellVal(struct column *col, struct genePos *gp, 
 	struct sqlConnection *conn)
 /* Return value of custom field. */
 {
 struct dyString *dy = NULL;
 char *retVal = NULL;
 struct hashEl *hel;
 for (hel = hashLookup(col->customIdHash, gp->name); hel != NULL; hel = hashLookupNext(hel))
     {
     if (dy == NULL)
         dy = dyStringNew(256);
     else
         dyStringAppendC(dy, ',');
     dyStringAppend(dy, hel->val);
     }
 if (dy != NULL)
     retVal = dyStringCannibalize(&dy);
 return retVal;
 }
 
 static void customCellPrint(struct column *col, struct genePos *gp, 
 	struct sqlConnection *conn)
 /* Return value of custom field. */
 {
 boolean gotAny = FALSE;
 struct hashEl *hel;
 hPrintf("<TD>");
 for (hel = hashLookup(col->customIdHash, gp->name); hel != NULL; hel = hashLookupNext(hel))
     {
     if (gotAny)
 	hPrintf(",&nbsp;");
     else
 	gotAny = TRUE;
     if (col->itemUrl)
 	{
 	char *encoded = cgiEncode(hel->val);
         hPrintf("<A HREF=\"");
 	hPrintf(col->itemUrl, encoded);
 	hPrintf("\">");
 	hPrintEncodedNonBreak(hel->val);
 	hPrintf("</A>");
 	freeMem(encoded);
 	}
     else
 	hPrintEncodedNonBreak(hel->val);
     }
 if (!gotAny)
     {
     hPrintf("n/a");
     }
 hPrintf("</TD>");
 }
 
 static void addSearchResult(struct searchResult **pList, struct hashEl *hel)
 /* Add given name to search result. */
 {
 struct searchResult *res;
 AllocVar(res);
 res->gp.name = cloneString(hel->name);
 res->matchingId = cloneString(hel->val);
 slAddHead(pList, res);
 }
 
 struct searchResult *customSearch(struct column *col, 
     struct sqlConnection *conn, char *search)
 /* Search lookup type column. */
 {
 struct searchResult *resList = NULL;
 char *type = columnSetting(col, "search", "exact");
 struct hashEl *helList, *hel;
 
 helList = hashElListHash(col->customIdHash);
 if (sameString(type, "prefix"))
     {
     for (hel = helList; hel != NULL; hel = hel->next)
         {
 	if (startsWith(search, hel->val))
 	    addSearchResult(&resList, hel);
 	}
     }
 else if (sameString(type, "fuzzy"))
     {
     for (hel = helList; hel != NULL; hel = hel->next)
         {
 	if (stringIn(search, hel->val) != NULL)
 	    addSearchResult(&resList, hel);
 	}
     }
 else 
     {
     for (hel = helList; hel != NULL; hel = hel->next)
         {
 	if (sameString(search, hel->val))
 	    addSearchResult(&resList, hel);
 	}
     }
 hashElFreeList(&helList);
 return resList;
 }
 
 static void customFilterControls(struct column *col, 
 	struct sqlConnection *conn)
 /* Print out controls for filter. */
 {
 if (colSettingOn(col, "isNumber"))
     minMaxAdvFilterControls(col, conn);
 else
     lookupAdvFilterControls(col, conn);
 }
 
 static struct genePos *customFilterText(struct column *col, 
 	struct sqlConnection *conn, struct genePos *list)
 /* Do advanced text filter on custom track.  This works much like 
  * lookup custom filter. */
 {
 char *wild = advFilterVal(col, "wild");
 struct hash *keyHash = keyFileHash(col);
 if (wild != NULL || keyHash != NULL)
     {
     boolean orLogic = advFilterOrLogic(col, "logic", TRUE);
     struct hash *hash = newHash(17);
     struct hashEl *hel, *helList = hashElListHash(col->customIdHash);
     struct slName *wildList = stringToSlNames(wild);
     for (hel = helList; hel != NULL; hel = hel->next)
         {
 	if (keyHash == NULL || hashLookupUpperCase(keyHash, hel->val))
 	    {
 	    if (wildList == NULL || wildMatchList(hel->val, wildList, orLogic))
 		hashAdd(hash, hel->name, NULL);
 	    }
 	}
     list = weedUnlessInHash(list, hash);
     hashElFreeList(&helList);
     hashFree(&hash);
     }
 hashFree(&keyHash);
 return list;
 }
 
 static struct genePos *customFilterNum(struct column *col, 
 	struct sqlConnection *conn, struct genePos *list)
 /* Do advanced numerical filter on custom track.  This 
  * works much like floatAdvFilter. */
 {
 char *minString = advFilterVal(col, "min");
 char *maxString = advFilterVal(col, "max");
 char *valString;
 if (minString != NULL || maxString != NULL)
     {
     struct genePos *gp, *next, *newList = NULL;
     double minVal = 0, maxVal = 0, val;
     if (minString != NULL)
         minVal = atof(minString);
     if (maxString != NULL)
         maxVal = atof(maxString);
     for (gp = list; gp != NULL; gp = next)
         {
 	next = gp->next;
 	valString = hashFindVal(col->customIdHash, gp->name);
 	if (valString != NULL)
 	    {
 	    val = atof(valString);
 	    if (minString == NULL || val >= minVal)
 	       if (maxString == NULL || val <= maxVal)
 	           {
 		   slAddHead(&newList, gp);
 		   }
 	    }
 	}
     slReverse(&newList);
     list = newList;
     }
 return list;
 }
 
 static struct genePos *customFilter(struct column *col, 
 	struct sqlConnection *conn, struct genePos *list)
 {
 if (colSettingOn(col, "isNumber"))
     return customFilterNum(col, conn, list); 
 else
     return customFilterText(col, conn, list);
 }
 
 void setupColumnCustom(struct column *col, char *parameters)
 /* Set up custom column. */
 {
 col->exists = customExists;
 col->cellVal = customCellVal;
 col->cellPrint = customCellPrint;
 if (columnSetting(col, "search", NULL))
     col->simpleSearch = customSearch;
 col->filterControls = customFilterControls;
 col->advFilter = customFilter;
 }