8c908f948b09826c6cb4452ee5b282aca41be85e
galt
  Tue Dec 8 21:52:59 2015 -0800
Multi-region (exonMostly). This work allows people to look at virtual chromosomes from a list of regions and then navigate and perform all of the usual functions on it.

diff --git src/hg/lib/hdb.c src/hg/lib/hdb.c
index 66af474..f466337 100644
--- src/hg/lib/hdb.c
+++ src/hg/lib/hdb.c
@@ -4015,39 +4015,39 @@
 {
 struct trackDb *cTdb = NULL;
 if (sTdb != NULL)
     {
     char *subTrackSetting = cloneString(trackDbLocalSetting(sTdb, "parent"));
     if (subTrackSetting != NULL)
 	{
 	char *compositeName = firstWordInLine(subTrackSetting);
 	cTdb = hTrackDbForTrack(db, compositeName);
 	freez(&subTrackSetting);
 	}
     }
 return cTdb;
 }
 
-boolean hgParseChromRange(char *db, char *spec, char **retChromName,
-	int *retWinStart, int *retWinEnd)
+boolean hgParseChromRangeLong(char *db, char *spec, char **retChromName,
+	long *retWinStart, long *retWinEnd)
 /* Parse something of form chrom:start-end into pieces.
  * if db != NULL then check with chromInfo for names */
 {
 boolean haveDb = (db != NULL);
 char *chrom, *start, *end;
 char buf[256];
-int iStart, iEnd;
+long iStart, iEnd;
 
 safecpy(buf, sizeof(buf), spec);
 stripChar(buf, ',');
 chrom = buf;
 start = strchr(chrom, ':');
 
 if (start == NULL)
     {
     /* If just chromosome name cover all of it. */
     if (!haveDb || ((chrom = hgOfficialChromName(db, chrom)) == NULL))
 	return FALSE;
     else
        {
        iStart = 0;
        iEnd = hChromSize(db, chrom);
@@ -4058,42 +4058,59 @@
     *start++ = 0;
     end = strchr(start, '-');
     if (end == NULL)
 	return FALSE;
     else
     *end++ = 0;
     chrom = trimSpaces(chrom);
     start = trimSpaces(start);
     end = trimSpaces(end);
     if (!isdigit(start[0]))
 	return FALSE;
     if (!isdigit(end[0]))
 	return FALSE;
     if (haveDb && ((chrom = hgOfficialChromName(db, chrom)) == NULL))
 	return FALSE;
-    iStart = atoi(start)-1;
-    iEnd = atoi(end);
+    iStart = atol(start)-1;
+    iEnd = atol(end);
     }
 if (retChromName != NULL)
     *retChromName = (haveDb)? chrom : cloneString(chrom);
 if (retWinStart != NULL)
     *retWinStart = iStart;
 if (retWinEnd != NULL)
     *retWinEnd = iEnd;
 return TRUE;
 }
 
+boolean hgParseChromRange(char *db, char *spec, char **retChromName,
+	int *retWinStart, int *retWinEnd)
+/* Parse something of form chrom:start-end into pieces.
+ * if db != NULL then check with chromInfo for names */
+{
+long winStart, winEnd;
+boolean result = hgParseChromRangeLong(db, spec, retChromName, &winStart, &winEnd);
+if (result)
+    {
+    if (retWinStart != NULL)
+	*retWinStart = winStart;
+    if (retWinEnd != NULL)
+	*retWinEnd = winEnd;
+    }
+return result;
+}
+
 boolean hgIsChromRange(char *db, char *spec)
 /* Returns TRUE if spec is chrom:N-M for some human
  * chromosome chrom and some N and M. */
 {
 return hgParseChromRange(db, spec, NULL, NULL, NULL);
 }
 
 struct trackDb *hMaybeTrackInfo(struct sqlConnection *conn, char *trackName)
 /* Load trackDb object for a track.  Actually the whole trackDb gets loaded
  * and just the one track returned. This is mostly just so parent and subtracks
  * fields can be correct. Don't die if conn has no trackDb table.  Return NULL
  * if trackName is not found. */
 {
 struct slName *tdb, *tdbs = hTrackDbList();
 for (tdb = tdbs; tdb != NULL; tdb = tdb->next)
@@ -4778,47 +4795,45 @@
 hashElFreeList(&helList);
 
 if (retHashOfHash)
     *retHashOfHash = hashOfHash;
 else
     hashFree(&hashOfHash);
 
 return raList;
 }
 
 char *addCommasToPos(char *db, char *position)
 /* add commas to the numbers in a position
  * returns pointer to static */
 {
 static char buffer[256];
-int winStart, winEnd;
+long winStart, winEnd; // long to support virtual chrom
 char *chromName;
 char num1Buf[64], num2Buf[64]; /* big enough for 2^64 (and then some) */
 
 if (position == NULL)
     return NULL;
 
-buffer[sizeof(buffer) - 1] = 0;
-if (!hgParseChromRange(NULL, position, &chromName, &winStart, &winEnd))
-    strncpy(buffer, position, sizeof(buffer) - 1);
-else
+if (hgParseChromRangeLong(NULL, position, &chromName, &winStart, &winEnd))
     {
     sprintLongWithCommas(num1Buf, winStart + 1);
     sprintLongWithCommas(num2Buf, winEnd);
-    safef(buffer, sizeof(buffer) - 1, "%s:%s-%s",chromName, num1Buf,  num2Buf);
+    safef(buffer, sizeof(buffer), "%s:%s-%s",chromName, num1Buf,  num2Buf);
     }
-
+else
+    safecpy(buffer, sizeof(buffer), position);
 return buffer;
 }
 
 INLINE boolean isAllDigits(char *str)
 /* Return TRUE if every character in str is a digit. */
 {
 char *p = str;
 while (*p != '\0')
     if (!isdigit(*p++))
 	return FALSE;
 return TRUE;
 }
 
 boolean parsePosition(char *position, char **retChrom, uint *retStart, uint *retEnd)
 /* If position is word:number-number (possibly with commas & whitespace),