4a030195664cbdd1d3c1a4b81b1ed41dbf6d9bc5
angie
  Fri Feb 15 14:02:58 2019 -0800
Prevent hgFind lib (specifically genomePos -> hgPositionsHtml) from opening a web page; instead, pass up results and warning messages to the calling CGI so it can open the page its own way.  refs #22945
hgTracks, hgTables and hgVai used to call findGenomePos{,Web} to resolve positions; hgTables and hgVai had to detect after the fact whether an HTML page had been started (with warnings and/or multiple results).
In fact, hgPositionsHtml called webEnd which could cause conflicts with what the CGI was doing afterwards.
Now, instead of findGenomePos{,Web} there is hgFindSearch which returns hgp and also warning messages, if any, via a dyString parameter.
The calling CGI decides how to open the page if necessary (for hgTracks, it's already open) and displays warnings/multiple results -- or just proceeds as usual with the single position result.

diff --git src/hg/hgTracks/hgTracks.c src/hg/hgTracks/hgTracks.c
index 275ab9c..40a8a0d 100644
--- src/hg/hgTracks/hgTracks.c
+++ src/hg/hgTracks/hgTracks.c
@@ -132,31 +132,31 @@
 char *protDbName;               /* Name of proteome database for this genome. */
 #define MAX_CONTROL_COLUMNS 6
 #define LOW 1
 #define MEDIUM 2
 #define BRIGHT 3
 #define MAXCHAINS 50000000
 boolean hgDebug = FALSE;      /* Activate debugging code. Set to true by hgDebug=on in command line*/
 int imagePixelHeight = 0;
 struct hash *oldVars = NULL;
 struct jsonElement *jsonForClient = NULL;
 
 boolean hideControls = FALSE;           /* Hide all controls? */
 boolean trackImgOnly = FALSE;           /* caller wants just the track image and track table html */
 boolean ideogramToo =  FALSE;           /* caller wants the ideoGram (when requesting just one track) */
 
-/* Structure returned from findGenomePos.
+/* Structure returned from resolvePosition.
  * We use this to to expand any tracks to full
  * that were found to contain the searched-upon
  * position string */
 struct hgPositions *hgp = NULL;
 
 /* Other global variables. */
 struct group *groupList = NULL;    /* List of all tracks. */
 char *browserName;              /* Test, preview, or public browser */
 char *organization;             /* UCSC */
 
 struct hash *trackHash = NULL; /* Hash of the tracks by their name. */
 
 #ifdef DEBUG
 void uglySnoopTrackList(int depth, struct track *trackList)
 /* Print out some info on track list. */
@@ -8910,37 +8910,30 @@
         "<A HREF=\"hgTracks?hgTracksConfigPage=configure\">image configuration menu</A>.");
     printf("<LI>In the image configuration menu, change the size of the image,\n"
             "to make it look more square.\n");
     printf("</UL>\n");
     printf("</div>\n");
 
 
     }
 else
     printf("<BR><BR>PDF format not available");
 
 printf("<a href='%s?%s=%s'><input type='button' VALUE='Return to Browser'></a>\n",
            hgTracksName(), cartSessionVarName(), cartSessionId(cart));
 }
 
-boolean isGenome(char *pos)
-/* Return TRUE if pos is genome. */
-{
-pos = trimSpaces(pos);
-return(sameWord(pos, "genome") || sameWord(pos, "hgBatch"));
-}
-
 void setRulerMode()
 /* Set the rulerMode variable from cart. */
 {
 char *s = cartUsualString(cart, RULER_TRACK_NAME, "dense");
 if (sameWord(s, "full") || sameWord(s, "on"))
     rulerMode = tvFull;
 else if (sameWord(s, "dense"))
     rulerMode = tvDense;
 else
     rulerMode = tvHide;
 }
 
 void setLayoutGlobals()
 /* Figure out basic dimensions of display.  */
 {
@@ -8954,39 +8947,69 @@
     withNextExonArrows = cartUsualBoolean(cart, "nextExonArrows", TRUE);
     }
 withExonNumbers = cartUsualBoolean(cart, "exonNumbers", TRUE);
 emAltHighlight = cartUsualBoolean(cart, "emAltHighlight", FALSE);
 if (!hIsGsidServer())
     {
     revCmplDisp = cartUsualBooleanDb(cart, database, REV_CMPL_DISP, FALSE);
     }
 emPadding = cartUsualInt(cart, "emPadding", emPadding);
 gmPadding = cartUsualInt(cart, "gmPadding", gmPadding);
 withPriorityOverride = cartUsualBoolean(cart, configPriorityOverride, FALSE);
 fullInsideX = trackOffsetX();
 fullInsideWidth = tl.picWidth-gfxBorder-fullInsideX;
 }
 
+static boolean resolvePosition(char **pPosition)
+/* Position may be an already-resolved chr:start-end, or a search term.
+ * If it is a search term:
+ * 1 match ==> set globals chromName, winStart, winEnd, return TRUE.
+ * 0 matches ==> switch back to lastPosition, hopefully get 1 match from that;
+ * set globals chromName, winStart, winEnd, return TRUE.  If no lastPosition, try w/hDefaultPos().
+ * multiple matches ==> Display a page with links to match positions, return FALSE. */
+{
+boolean resolved = TRUE;
+struct dyString *dyWarn = dyStringNew(0);
+hgp = hgFindSearch(cart, pPosition, &chromName, &winStart, &winEnd, hgTracksName(), dyWarn);
+if (isNotEmpty(dyWarn->string))
+    warn("%s", dyWarn->string);
+if (hgp->singlePos)
+    {
+    createHgFindMatchHash();
+    }
+else
+    {
+    char *menuStr = menuBar(cart, database);
+    if (menuStr)
+        puts(menuStr);
+    hgPositionsHtml(database, hgp, hgTracksName(), cart);
+    resolved = FALSE;
+    }
+cartSetString(cart, "position", *pPosition);
+return resolved;
+}
+
 void parseVirtPosition(char *position)
 /* parse virtual position
  *  TODO this is just temporary */
 {
 if (!position)
     {
     errAbort("position NULL");
     }
 char *vPos = cloneString(position);
+stripChar(vPos, ',');
 char *colon = strchr(vPos, ':');
 if (!colon)
     errAbort("position has no colon");
 char *dash = strchr(vPos, '-');
 if (!dash)
     errAbort("position has no dash");
 *colon = 0;
 *dash = 0;
 virtWinStart = atol(colon+1) - 1;
 virtWinEnd = atol(dash+1);
 }
 
 void parseNonVirtPosition(char *position)
 /* parse non-virtual position */
 {
@@ -9064,94 +9087,63 @@
 	cartSetString(cart, "highlight", cartVar);
 	}
     else
 	{
 	// erase the highlight cartvar if it has no overlap with the new virt chrom
 	cartRemove(cart, "highlight");
 	}
     }
 }
 
 
 void tracksDisplay()
 /* Put up main tracks display. This routine handles zooming and
  * scrolling. */
 {
-char *defaultPosition = hDefaultPos(database);
 char titleVar[256];
 char *oldPosition = cartUsualString(cart, "oldPosition", "");
 boolean findNearest = cartUsualBoolean(cart, "findNearest", FALSE);
 cartRemove(cart, "findNearest");
 
 boolean positionIsVirt = FALSE;
 position = getPositionFromCustomTracks();
 if (NULL == position)
     {
     position = cartGetPosition(cart, database, &lastDbPosCart);
     if (sameOk(cgiOptionalString("position"), "lastDbPos"))
 	{
         restoreSavedVirtPosition();
 	}
     if (startsWith("virt:", position))
 	{
 	position = stripCommas(position); // sometimes the position string arrives with commas in it.
 	positionIsVirt = TRUE;
 	}
     }
 
 
 if (sameString(position, ""))
     {
     hUserAbort("Please go back and enter a coordinate range or a search term in the \"search term\" field.<br>For example: chr22:20100000-20200000.\n");
     }
 
 if (!positionIsVirt)
     {
-    chromName = NULL;
-    winStart = 0;
-    if (isGenome(position) || NULL ==
-	(hgp = findGenomePos(database, position, &chromName, &winStart, &winEnd, cart)))
-	{
-	if (winStart == 0)  /* number of positions found */
-	    {
-	    freeMem(position);
-	    position = cloneString(cartUsualString(cart, "lastPosition", defaultPosition));
-	    hgp = findGenomePos(database, position, &chromName, &winStart, &winEnd,cart);
-	    if (hgp != NULL && differentString(position, defaultPosition))
-		cartSetString(cart, "position", position);
-	    }
-	}
-
-    /* After position is found set up hash of matches that should
-       be drawn with names highlighted for easy identification. */
-    createHgFindMatchHash();
-
-    /* This means that no single result was found
-    I.e., multiple results may have been found and are printed out prior to this code*/
-    if (NULL == chromName)
-	{
-	// In case user manually edits the browser location as described in #13009,
-	// revert the position.  If they instead choose from the list as we expect,
-	// that will set the position to their choice.
-	// lastPosition gets set in cart.c
-	char *lastPosition = cartUsualString(cart, "lastPosition", hDefaultPos(database));
-	cartSetString(cart, "position", lastPosition);
+    if (! resolvePosition(&position))
         return;
     }
 
-    }
-
 virtMode = cartUsualBoolean(cart, "virtMode", FALSE);
 
 /* Figure out basic dimensions of display.  This
  * needs to be done early for the sake of the
  * zooming and dinking routines. */
 setLayoutGlobals();
 
 virtModeType = cartUsualString(cart, "virtModeType", virtModeType);
 
 if (positionIsVirt && virtualSingleChrom())
     {
     // we need chromName to be set before initRegionList() gets called.
     position = cartUsualString(cart, "nonVirtPosition", "");
     if (!sameString(position,""))
 	parseNonVirtPosition(position);
@@ -9185,32 +9177,31 @@
 	if (nvh)
 	    cartSetString(cart, "highlight", nvh);
 	if (!sameString(position,""))
 	    parseNonVirtPosition(position);
 	}
 
     if (initRegionList())   // initialize the region list, sets virtModeExtraState
 	{
 	break;
 	}
     else
 	{ // virt mode failed, forced to return to default
 	virtModeType = "default";
 	cartSetString(cart, "virtModeType", virtModeType);
 	position = cloneString(hDefaultPos(database));
-	hgp = findGenomePos(database, position, &chromName, &winStart, &winEnd, cart);
-	cartSetString(cart, "position", position);
+        resolvePosition(&position);
 	positionIsVirt=FALSE;
 	virtMode=FALSE;
 	}
     }
 
 // PAD padding of exon regions is now being done inside the fetch/merge.
 //if (emPadding > 0)
     //padVirtRegions(emPadding); // this old routine does not handle multiple chroms yet
 
 //testRegionList(); // check if it is ascending non-overlapping regions. (this is not the case with custom user-defined-regions)
 
 makeVirtChrom();
 
 //testVirtChromBinarySearch();
 
@@ -10243,31 +10234,31 @@
     configPageSetTrackVis(-2);
     }
 else
     {
     tracksDisplay();
     }
 
 if (cartVarExists(cart, "hgt.convertChromToVirtChrom"))
     {
     cartRemove(cart, "hgt.convertChromToVirtChrom");
     return;
     }
 
 jsonObjectAdd(jsonForClient, "measureTiming", newJsonBoolean(measureTiming));
 // js code needs to know if a highlightRegion is defined for this db
-checkAddHighlight(); // call again in case tracksDisplay's call to findGenomePos changed vars
+checkAddHighlight(); // call again in case tracksDisplay's call to resolvePosition changed vars
 char *highlightDef = cartOptionalString(cart, "highlight");
 if (highlightDef && startsWith(database,highlightDef) && highlightDef[strlen(database)] == '.')
     jsonObjectAdd(jsonForClient, "highlight", newJsonString(highlightDef));
 jsonObjectAdd(jsonForClient, "enableHighlightingDialog",
 	      newJsonBoolean(cartUsualBoolean(cart, "enableHighlightingDialog", TRUE)));
 
 struct dyString *dy = dyStringNew(1024);
 jsonDyStringPrint(dy, (struct jsonElement *) jsonForClient, "hgTracks", 0);
 jsInline(dy->string);
 dyStringFree(&dy);
 
 if (measureTiming)
     measureTime("Time at end of doMiddle, next up cart write");
 
 if (cartOptionalString(cart, "udcTimeout"))