9a685f7b0fde1b55268aadea67906197c9bacb19
max
  Tue Nov 8 10:51:16 2016 -0800
allowing hub+genomes+trackDb all in the hub.txt file for track hubs. I am just me, but BrianR and Hiram have approved this message. No redmine.

diff --git src/hg/lib/trackHub.c src/hg/lib/trackHub.c
index 8e36d62..848f409 100644
--- src/hg/lib/trackHub.c
+++ src/hg/lib/trackHub.c
@@ -479,60 +479,63 @@
 else if (b->orderKey < a->orderKey) return 1;
 else return 0;
 }
 
 static struct trackHubGenome *trackHubGenomeReadRa(char *url, struct trackHub *hub)
 /* Read in a genome.ra format url and return it as a list of trackHubGenomes. 
  * Also add it to hash, which is keyed by genome. */
 {
 struct lineFile *lf = udcWrapShortLineFile(url, NULL, 64*1024*1024);
 struct trackHubGenome *list = NULL, *el;
 struct hash *hash = hub->genomeHash;
 
 struct hash *ra;
 while ((ra = raNextRecord(lf)) != NULL)
     {
+    // allow that trackDb+hub+genome is in one single file
+    if (hashFindVal(ra, "track"))
+        break;
+
     char *twoBitPath = hashFindVal(ra, "twoBitPath");
     char *genome;
     if (twoBitPath != NULL)
 	genome = addHubName(hashFindVal(ra, "genome"), hub->name);
     else
 	genome = hashFindVal(ra, "genome");
     if (hub->defaultDb == NULL)
 	hub->defaultDb = genome;
     if (genome == NULL)
         badGenomeStanza(lf);
     if (hashLookup(hash, genome) != NULL)
         errAbort("Duplicate genome %s in stanza ending line %d of %s",
 		genome, lf->lineIx, lf->fileName);
     char *trackDb = hashFindVal(ra, "trackDb");
     if (trackDb == NULL)
         badGenomeStanza(lf);
     AllocVar(el);
     el->name = cloneString(genome);
     el->trackDbFile = trackHubRelativeUrl(url, trackDb);
     el->trackHub = hub;
     hashAdd(hash, el->name, el);
     slAddHead(&list, el);
     char *orderKey = hashFindVal(ra, "orderKey");
     if (orderKey != NULL)
 	el->orderKey = sqlUnsigned(orderKey);
 
     char *groups = hashFindVal(ra, "groups");
     if (twoBitPath != NULL)
 	{
-	//printf("reading genome %s twoBitPath %s\n", genome, el->twoBitPath);
 	el->description  = hashFindVal(ra, "description");
 	char *organism = hashFindVal(ra, "organism");
 	if (organism == NULL)
 	    errAbort("must have 'organism' set in assembly hub in stanza ending line %d of %s",
 		     lf->lineIx, lf->fileName);
 	el->organism  = addHubName(organism, hub->name);
 	hashReplace(ra, "organism", el->organism);
 	el->defaultPos  = hashFindVal(ra, "defaultPos");
 	if (el->defaultPos == NULL)
 	    errAbort("must have 'defaultPos' set in assembly hub in stanza ending line %d of %s",
 		     lf->lineIx, lf->fileName);
 	el->twoBitPath = trackHubRelativeUrl(url, twoBitPath);
 
 	char *htmlPath = hashFindVal(ra, "htmlPath");
 	if (htmlPath != NULL)
@@ -595,32 +598,32 @@
 }
 
 struct trackHub *trackHubOpen(char *url, char *hubName)
 /* Open up a track hub from url.  Reads and parses hub.txt and the genomesFile. 
  * The hubName is generally just the asciified ID number. */
 {
 struct trackHub *hub = grabHashedHub(hubName);
 
 if (hub != NULL)
     return hub;
 
 struct lineFile *lf = udcWrapShortLineFile(url, NULL, 256*1024);
 struct hash *hubRa = raNextRecord(lf);
 if (hubRa == NULL)
     errAbort("empty %s in trackHubOpen", url);
-if (raNextRecord(lf) != NULL)
-    errAbort("multiple records in %s", url);
+// no errAbort when more records in hub.txt file: user can stuff
+// trackDb into it
 
 /* Allocate hub and fill in settings field and url. */
 AllocVar(hub);
 hub->url = cloneString(url);
 hub->name = cloneString(hubName);
 hub->settings = hubRa;
 
 /* Fill in required fields from settings. */
 trackHubRequiredSetting(hub, "hub");
 trackHubRequiredSetting(hub, "email");
 hub->shortLabel = trackHubRequiredSetting(hub, "shortLabel");
 hub->longLabel = trackHubRequiredSetting(hub, "longLabel");
 hub->genomesFile = trackHubRequiredSetting(hub, "genomesFile");
 hub->email =  trackHubSetting(hub, "email");
 hub->version = trackHubSetting(hub, "version"); // default to current version