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/encode/lib/encodePatchTdb.c src/hg/encode/lib/encodePatchTdb.c
index 7b5df91..cedc77c 100644
--- src/hg/encode/lib/encodePatchTdb.c
+++ src/hg/encode/lib/encodePatchTdb.c
@@ -1,898 +1,898 @@
 /* encodePatchTdb - Lay a trackDb.ra file from the pipeline gently on top of the trackDb system. */
 
 /* 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 "options.h"
 #include "obscure.h"
 #include "errAbort.h"
 #include "dystring.h"
 #include "portable.h"
 #include "ra.h"
 #include "../inc/encodePatchTdb.h"
 
 char *clTest = NULL;
 boolean clNoComment = FALSE;
 
 
 boolean glReplace;	// If TRUE then do a replacement operation.
 
 void recordLocationReport(struct raRecord *rec, FILE *out)
 /* Write out where record ends. */
 {
 fprintf(out, "in stanza from lines %d-%d of %s\n",
 	rec->startLineIx, rec->endLineIx, rec->file->name);
 }
 
 #if defined(__GNUC__)
 __attribute__((format(printf, 2, 3)))
 #endif
 void recordWarn(struct raRecord *rec, char *format, ...)
 /* Issue a warning message. */
 {
 va_list args;
 va_start(args, format);
 vaWarn(format, args);
 va_end(args);
 recordLocationReport(rec, stderr);
 }
 
 #if defined(__GNUC__)
 __attribute__((format(printf, 2, 3)))
 #endif
 void recordAbort(struct raRecord *rec, char *format, ...)
 /* Issue a warning message. */
 {
 va_list args;
 va_start(args, format);
 vaWarn(format, args);
 va_end(args);
 recordLocationReport(rec, stderr);
 noWarnAbort();
 }
 
 struct raTag *raRecordFindTag(struct raRecord *r, char *name)
 /* Find tag of given name.  Return NULL if not found. */
 {
 struct raTag *tag;
 for (tag = r->tagList; tag != NULL; tag = tag->next)
     if (sameString(tag->name, name))
         break;
 return tag;
 }
 
 char *raRecordFindTagVal(struct raRecord *r, char *name)
 /* Return value of tag of given name, or NULL if not found. */
 {
 struct raTag *tag = raRecordFindTag(r, name);
 return (tag == NULL ? NULL : tag->val);
 }
 
 struct raTag *raRecordMustFindTag(struct raRecord *r, char *name)
 /* Find given tag, abort with message if not found. */
 {
 struct raTag *tag = raRecordFindTag(r, name);
 if (tag == NULL)
     recordAbort(r, "missing required tag %s", name);
 return tag;
 }
 
 char *raRecordMustFindTagVal(struct raRecord *r, char *name)
 /* Find value of given tag.  Abort if it doesn't exist. */
 {
 struct raTag *tag = raRecordMustFindTag(r, name);
 return tag->val;
 }
 
 static struct raRecord *readRecordsFromFile(struct raFile *file, struct dyString *dy)
 /* Read all the records in a file and return as a list.  The dy parameter returns the
  * last bits of the file (after the last record). */
 {
 char *fileName = file->name;
 struct raRecord *r, *rList = NULL;
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 while (raSkipLeadingEmptyLines(lf, dy))
     {
     /* Create a tag structure in local memory. */
     AllocVar(r);
     r->startLineIx = lf->lineIx;
     char *name, *val;
     while (raNextTagVal(lf, &name, &val, dy))
         {
 	struct raTag *tag;
 	AllocVar(tag);
 	tag->name = cloneString( name);
 	tag->val = cloneString( val);
 	tag->text = cloneString( dy->string);
 	if (sameString(name, "track"))
 	    r->key = cloneFirstWord(tag->val);
 	slAddHead(&r->tagList, tag);
 	dyStringClear(dy);
 	}
     if (dy->stringSize > 0)
         {
 	r->endComments = cloneString( dy->string);
 	}
     slReverse(&r->tagList);
     r->endLineIx = lf->lineIx;
     r->file = file;
     slAddHead(&rList, r);
     }
 lineFileClose(&lf);
 slReverse(&rList);
 return rList;
 }
 
 struct raFile *raFileRead(char *fileName)
 /* Read in file */
 {
 struct dyString *dy = dyStringNew(0);
 struct raFile *raFile;
 AllocVar(raFile);
 raFile->name = cloneString( fileName);
 raFile->recordList = readRecordsFromFile(raFile, dy);
 raFile->endSpace = cloneString( dy->string);
 
 /* Hash up tracks. */
 struct hash *hash = raFile->trackHash = newHash(0);
 struct raRecord *r;
 for (r = raFile->recordList; r != NULL; r = r->next)
     {
     if (r->key != NULL)
 	hashAdd(hash, r->key, r);
     }
 
 /* Clean up and go home. */
 dyStringFree(&dy);
 return raFile;
 }
 
 boolean compositeFirst(struct raRecord *raList)
 /* Return first record if it's composite.  Insure there are no other composites.
  * return NULL if first record is not composite. */
 {
 struct raRecord *r;
 struct raTag *compositeTag = raRecordFindTag(raList, "compositeTrack");
 if (compositeTag != NULL)
     if (!sameString(compositeTag->val, "on"))
         recordAbort(raList, "Expecting 'on' for compositeTrack value, got '%s'", compositeTag->val);
 for (r = raList->next; r != NULL; r = r->next)
     {
     struct raTag *tag = raRecordFindTag(r, "compositeTrack");
     if (tag != NULL)
         recordAbort(r, "The compositeTrack tag is only allowed on the first stanza\n");
     }
 return compositeTag != NULL;
 }
 
 static void checkSubsAreForParent(char *parentName, struct raRecord *subList)
 /* Check that all subtracks in list have parent as subtrack. */
 {
 struct raRecord *sub;
 for (sub = subList; sub != NULL; sub = sub->next)
     {
     char *parent = raRecordFindTagVal(sub, "parent");
     if (parent == NULL)
         parent = raRecordMustFindTagVal(sub, "subTrack");
     char *subParent = cloneFirstWord(parent);
     if (!startsWith(parentName, subParent))
         recordAbort(sub, "%s has parent %s, expecting %s",
 		sub->key, subParent, parentName);
     }
 }
 
 static char *findViewTrackName(struct raRecord *parent, char *view)
 /* Returns the name of the viewInTheMiddle track for a given view tag */
 {
 struct raRecord *mid;
 for (mid = parent->children; mid != NULL; mid = mid->olderSibling)
     {
     char *viewName = raRecordFindTagVal(mid, "view");
     if (viewName != NULL && sameString(viewName,view))
         return mid->key;
     }
 return NULL;
 }
 
 static struct raRecord *rFindView(struct raRecord *r, char *view)
 /* Find view of given name at r or under. */
 {
 char *thisView = raRecordFindTagVal(r, "view");
 if (thisView != NULL && sameString(thisView, view))
     return r;
 struct raRecord *sub;
 for (sub = r->subtracks; sub != NULL; sub = sub->next)
     {
     struct raRecord *viewRecord = rFindView(sub, view);
     if (viewRecord != NULL)
         return viewRecord;
     }
 return NULL;
 }
 
 char *getOneOutOfThisEqualsThat(char *thisEqThat, char *name)
 /* Get value on right side of name= in a name=value list. */
 {
 /* Not an efficient implementation, should be ok though. */
 struct hash *hash = hashThisEqThatLine(thisEqThat, 0, FALSE);
 char *val = cloneString(hashFindVal(hash, name));
 freeHashAndVals(&hash);
 return val;
 }
 
 char *findViewOfSub(struct raRecord *sub)
 /* Find view that subtrack belongs to - parsing it out of subTrack. */
 {
 char *subGroups = raRecordFindTagVal(sub, "subGroups");
 if (subGroups == NULL)
     return NULL;
 char *viewName = getOneOutOfThisEqualsThat(subGroups, "view");
 return viewName;
 }
 
 struct raRecord *findRecordCompatibleWithRelease(struct raFile *file, char *release, char *key)
 /* Look in file for record with given key that is compatible with release.  Compatible means
  * that either the releases are the same, or one or the other is NULL. */
 {
 struct hashEl *hel;
 for (hel = hashLookup(file->trackHash, key); hel != NULL; hel = hashLookupNext(hel))
     {
     struct raRecord *r = hel->val;
     struct raTag *tag = raRecordFindTag(r, "release");
     if (tag == NULL || release == NULL || sameString(tag->val, release))
         return r;
     }
 return NULL;
 }
 
 static void linkUpParents(struct raFile *file)
 /* Link up records according to parent/child relationships. */
 {
 struct raRecord *list = file->recordList;
 
 /* Zero out children, parent, and older sibling fields, since going to recalculate
  * them and need lists to start out empty. */
 struct raRecord *rec;
 for (rec = list; rec != NULL; rec = rec->next)
     rec->parent = rec->olderSibling = rec->children = NULL;
 
 /* Scan through linking up parents. */
 for (rec = list; rec != NULL; rec = rec->next)
     {
     char *subTrack = raRecordFindTagVal(rec, "parent");
     if (subTrack == NULL)
         subTrack = raRecordFindTagVal(rec, "subTrack");
     if (subTrack != NULL)
 	{
 	char *release = raRecordFindTagVal(rec, "release");
 	char *parentName = cloneFirstWord(subTrack);
 	struct raRecord *parent = findRecordCompatibleWithRelease(file, release, parentName);
 	if (parent == NULL)
 	    recordAbort(rec, "Can't find parent record %s release %s", parentName, release);
 	rec->parent = parent;
 	rec->olderSibling = parent->children;
 	parent->children = rec;
 	}
     }
 }
 
 char *nonNullRelease(char *release1, char *release2)
 /* Return whichever of the two releases is non-null. */
 {
 if (release1 != NULL)
     return release1;
 return release2;
 }
 
 
 struct hash *hashOfHashSubGroupNs(struct raRecord *r)
 /* Return a hash keyed by subGroup names (view, cellType, etc).  The hash value is
  * itself a hash, one that contains the name=val pairs on the subGroupN line. */
 {
 struct hash *hashOfHashes = hashNew(0);
 int groupIx;
 for (groupIx=1; ; groupIx++)
     {
     char tagName[16];
     safef(tagName,  sizeof(tagName), "subGroup%d", groupIx);
     struct raTag *tag = raRecordFindTag(r, tagName);
     if (tag == NULL)
         break;
     char *dupe = cloneString(tag->val);
     char *line = dupe;
     char *groupName = nextWord(&line);
     char *groupLabel = nextWord(&line);
     if (groupLabel == NULL)
         recordAbort(r, "malformed %s %s", tag->name, tag->val);
     struct hash *hash = hashThisEqThatLine(line, r->startLineIx, FALSE);
     if (hash->elCount < 1)
         recordAbort(r, "%s with no this=that", tag->name);
     hashAdd(hashOfHashes, groupName, hash);
     freez(&dupe);
     }
 return hashOfHashes;
 }
 
 void validateParentSub(struct raRecord *parent, struct raRecord *sub)
 /* Make sure that it is kosher that sub belongs to parent.  Mostly check
  * that subGroup1, subGroup2, etc in parent work with subGroups of sub. */
 {
 char *subGroups = raRecordMustFindTagVal(sub, "subGroups");
 struct hash *myGroups = hashThisEqThatLine(subGroups, sub->startLineIx, FALSE);
 struct hash *parentGroups = hashOfHashSubGroupNs(parent);
 struct hashEl *myGroupList = hashElListHash(myGroups);
 struct hashEl *myGroupEl;
 for (myGroupEl = myGroupList; myGroupEl != NULL; myGroupEl = myGroupEl->next)
     {
     char *myName = myGroupEl->name;
     char *myVal = myGroupEl->val;
     struct hash *parentGroup = hashFindVal(parentGroups, myName);
     if (parentGroup == NULL)
         recordAbort(sub, "Parent %s doesn't have a subGroup %s", parent->key, myName);
     char *groupName = hashFindVal(parentGroup, myVal);
     if (groupName == NULL)
         recordAbort(sub, "Parent %s doesn't have a %s %s", parent->key, myName, myVal);
     verbose(2, "%s %s %s found in %s\n", sub->key, myName, myVal, parent->key);
     }
 }
 
 void validateParentViewSub(struct raRecord *parent, struct raRecord *view, struct raRecord *sub)
 /* Make sure that it is kosher that sub belongs to parent and view.  */
 {
 validateParentSub(parent, sub);
 }
 
 
 void patchIntoEndOfView(struct raRecord *sub, struct raRecord *view)
 /* Assuming view is in a file,  chase down later records in file until come to
  * first one that does not have view as a parent.  Insert sub right before that. */
 {
 struct raRecord *viewChild = NULL;
 struct raRecord *r;
 for (r = view->next; r != NULL; r = r->next)
     {
     if (r->parent == view)
         viewChild = r;
     else
         break;
     }
 struct raRecord *recordBefore = (viewChild != NULL ? viewChild : view);
 sub->parent = view;
 sub->next = recordBefore->next;
 recordBefore->next = sub;
 }
 
 char *firstTagInText(char *text)
 /* Return the location of tag in text - skipping blank and comment lines and white-space */
 {
 char *s = text;
 for (;;)
     {
     s = skipLeadingSpaces(s);
     if (s[0] == '#')
         {
 	s = strchr(s, '\n');
 	}
     else
         break;
     }
 return s;
 }
 
 void substituteParentText(struct raRecord *parent, struct raRecord *view,
 	struct raRecord *sub)
 /* Convert subtrack parent with subtrack view. */
 {
 struct raTag *t = raRecordFindTag(sub, "parent");
 if (t == NULL)
     t = raRecordMustFindTag(sub, "subTrack");
 struct dyString *dy = dyStringNew(0);
 char *s = firstTagInText(t->text);
 dyStringAppendN(dy, t->text, s - t->text);
 dyStringPrintf(dy, "parent %s", view->key);
 /* Skip over subTrack and name in original text. */
 int i;
 for (i=0; i<2; ++i)
     {
     s = skipLeadingSpaces(s);
     s = skipToSpaces(s);
     }
 if (s != NULL)
      dyStringAppend(dy, s);
 else
     dyStringAppendC(dy, '\n');
 t->text = dyStringCannibalize(&dy);
 }
 
 boolean hasBlankLine(char *text)
 /* Return TRUE if there is an empty line in text. */
 {
 char *s, *e;
 for (s = text; !isEmpty(s); s = e)
     {
     e = strchr(s, '\n');
     if (e == s)
         return TRUE;
     else
         e += 1;
     }
 return FALSE;
 }
 
 void makeSureBlankLineBefore(struct raRecord *r)
 /* Make sure there is a blank line before record. */
 {
 struct raTag *first = r->tagList;
 char *firstText = first->text;
 if (!hasBlankLine(firstText))
     {
     int len = strlen(firstText);
     char *newText = needMem(len+2);
     newText[0] = '\n';
     strcpy(newText+1, firstText);
     first->text = newText;
     }
 }
 
 void addToStartOfTextLines(struct raRecord *r, char c, int charCount)
 /* Add char to start of all text in r. */
 {
 struct raTag *t;
 struct dyString *dy = dyStringNew(0);
 for (t = r->tagList; t != NULL; t = t->next)
     {
     char *s, *e;
     dyStringClear(dy);
     for (s = t->text; !isEmpty(s); s = e)
         {
 	int i;
 	e = strchr(s, '\n');
 	if (e == s)  // empty line, keep empty
 	    {
 	    dyStringAppendC(dy, '\n');
 	    e += 1;
 	    }
 	else
 	    {
 	    // Indent some extra.
 	    for (i=0; i<charCount; ++i)
 		dyStringAppendC(dy, c);
 	    if (e == NULL)
 		{
 		dyStringAppend(dy, s);
 		}
 	    else
 		{
 		dyStringAppendN(dy, s, e-s+1);
 		e += 1;
 		}
 	    }
 	}
     t->text = cloneString(dy->string);
     }
 if (r->endComments != NULL)
     {
     dyStringClear(dy);
     int i;
     for (i=0; i<charCount; ++i)
         dyStringAppendC(dy, c);
     dyStringAppend(dy, r->endComments);
     r->endComments = cloneString(dy->string);
     }
 dyStringFree(&dy);
 }
 
 void indentTdbText(struct raRecord *r, int indentCount)
 /* Add spaces to start of all text in r. */
 {
 addToStartOfTextLines(r, ' ', indentCount);
 }
 
 void commentOutStanza(struct raRecord *r)
 /* Add # to start of all test in r. */
 {
 addToStartOfTextLines(r, '#', 1);
 }
 
 void substituteIntoView(struct raRecord *sub, struct raRecord *oldSub, struct raRecord *view)
 /* Substitute sub for oldSub as a child of view.  Assumes oldSub is in same file and after view.
  * Leaves in oldSub, but "commented out" */
 {
 sub->parent = view;
 sub->next = oldSub->next;
 oldSub->next = sub;
 if (clNoComment)
     oldSub->isRemoved = TRUE;
 else
     commentOutStanza(oldSub);
 }
 
 void patchInSubtrack(struct raRecord *parent, struct raRecord *sub)
 /* Patch sub into the correct view of parent */
 {
 char *viewOfSub = findViewOfSub(sub);
 if (viewOfSub == NULL)
     recordAbort(sub, "Can only handle subtracks with view in subGroups");
 char *viewTrackName = findViewTrackName(parent,viewOfSub);
 if(viewTrackName == NULL)
     recordAbort(sub, "Can't find view track name for %s in subtrack %s and parent %s.",viewOfSub,sub->key,parent->key);
 char *parentRelease = raRecordFindTagVal(parent, "release");
 char *subRelease = raRecordFindTagVal(sub, "release");
 char *release = nonNullRelease(parentRelease, subRelease);
 struct raRecord *view = findRecordCompatibleWithRelease(parent->file, release, viewTrackName);
 validateParentViewSub(parent, view, sub);
 substituteParentText(parent, view, sub);
 makeSureBlankLineBefore(sub);
 if(raRecordFindTag(sub, "subTrack"))
     indentTdbText(sub, 4);
 struct raRecord *oldSub = findRecordCompatibleWithRelease(parent->file, release, sub->key);
 if (glReplace)
     {
     if (oldSub == NULL)
         recordAbort(sub, "%s doesn't exist but using mode=replace\n", sub->key);
     substituteIntoView(sub, oldSub, view);
     }
 else
     {
     if (oldSub != NULL)
         recordAbort(sub, "record %s already exists - use mode=replace", sub->key);
     patchIntoEndOfView(sub, view);
     }
 }
 
 void patchInSubtracks(struct raRecord *tdbParent, struct raRecord *patchList)
 /* Move records in patchList to correct position in tdbParent. */
 {
 struct raRecord *patch, *nextPatch;
 for (patch = patchList; patch != NULL; patch = nextPatch)
     {
     nextPatch = patch->next;	/* Since patchInSubtrack will alter this. */
     patchInSubtrack(tdbParent, patch);
     }
 }
 
 void writeTdbFile(struct raFile *tdbFile, char *outName)
 /* Write it back to a file outName */
 {
 FILE *f = mustOpen(outName, "w");
 struct raRecord *r;
 for (r = tdbFile->recordList; r != NULL; r = r->next)
     {
     if (!r->isRemoved)
 	{
 	struct raTag *tag;
 	for (tag = r->tagList; tag != NULL; tag = tag->next)
 	    {
 	    //fprintf(f, "name=[%s] val=[%s]\n", tag->name, tag->val);  // DEBUG REMOVE
 	    //fputs("text=[", f);  // DEBUG REMOVE
 	    fputs(tag->text, f);
 	    //fputs("]\n", f); // DEBUG REMOVE
 	    }
 	if (r->endComments != NULL)
 	    {
 	    //fputs("endComments={", f);  // DEBUG REMOVE
 	    fputs(r->endComments, f);
 	    //fputs("}\n", f); // DEBUG REMOVE
 	    }
 	}
     }
 fputs(tdbFile->endSpace, f);
 carefulClose(&f);
 }
 
 void splitTagText(struct raTag *tag, char **leadingComment, char **indentation, char **name, char **val)
 {
 // parse tag->text into leading-comment, indentation, line
 char *text = tag->text;
 int l = strlen(text);
 if (text[l-1] != '\n')
     errAbort("unexpected error in splitTagText, tag->text does not end in newline");
 text[l-1] = 0;
 char *indentStart = strrchr(text, '\n');  // MAY NEED ATTENTION FOR LINE-CONTINUATION CHARACTER "\"
 if (indentStart)
     {
     ++indentStart;
     if (leadingComment)
 	*leadingComment = cloneStringZ(text, indentStart - text);
     }
 else
     {
     indentStart = text;
     if (leadingComment)
 	*leadingComment = cloneString("");
     }
 char *nameStart = skipLeadingSpaces(indentStart);
 char *nameEnd = skipToSpaces(nameStart);
 char *valStart = skipLeadingSpaces(nameEnd);
 
 if (indentation)
     *indentation = cloneStringZ(indentStart, nameStart - indentStart);
 if (name)
     *name = cloneStringZ(nameStart, nameEnd - nameStart);
 if (val)
     *val = cloneString(valStart);
 text[l-1] = '\n';
 }
 
 void updateTagText(struct raTag *tag, char *lcOverride, char *iOverride)
 /* Update tag->text because tag->name or tag->val has changed.
  * Use lcOverride or iOverride to override the default leadingComment and indentation */
 {
 char tempTagText[1024];
 char *leadingComment, *indentation;
 splitTagText(tag, &leadingComment, &indentation, NULL, NULL);
 safef(tempTagText, sizeof tempTagText, "%s%s%s %s\n",
       lcOverride ? lcOverride : leadingComment,
       iOverride ? iOverride : indentation,
       tag->name, tag->val);
 tag->text = cloneString(tempTagText);
 freeMem(leadingComment);
 freeMem(indentation);
 }
 
 boolean renameTrack(struct raFile *tdbFile, char *oldTrack, char *newTrack, int pass, char **warnMsg)
 /* Rename track, or just check if it is found
  * pass 0 is checking, pass 1 is renaming*/
 {
 boolean foundOld = FALSE;
 boolean foundNew = FALSE;
 struct raRecord *r;
 for (r = tdbFile->recordList; r != NULL; r = r->next)
     {
     if (!r->isRemoved)
 	{
 	struct raTag *tag;
 	for (tag = r->tagList; tag != NULL; tag = tag->next)
 	    {
 	    if (sameString(tag->name, "track") && sameString(tag->val, oldTrack))
 		{
 		foundOld = TRUE;
 		if (pass == 1)
 		    {
 		    tag->val = cloneString(newTrack);   // rename the track
 		    updateTagText(tag, NULL, NULL);
 		    char *indentation;
 		    splitTagText(tag, NULL, &indentation, NULL, NULL);
 		    char *user = getenv("USER");
 		    time_t now;
 		    time(&now);
 		    char newComment[10*1024];
 		    safef(newComment, sizeof(newComment), "%s%s# was %s %s %s",  // NOTE: ctime() adds its own newline
 			(r->endComments ? r->endComments : ""), indentation, oldTrack, user, ctime(&now));
 		    freeMem(indentation);
 		    r->endComments = cloneString(newComment);
 		    }
 		}
 	    else if (sameString(tag->name, "track") && sameString(tag->val, newTrack))
 		{
 		foundNew = TRUE;
 		char errMsg[1024];
                 safef(errMsg, sizeof errMsg, 
                       "Track renaming collision: newTrack %s already "
                       "exists, unable to rename oldTrack %s to it\n",
 		      newTrack, oldTrack);
 		if (*warnMsg)
 		    *warnMsg = cloneString(errMsg);
 		}
 	    }
 	}
     }
 if (!foundOld)
     {
     char errMsg[1024];
     safef(errMsg, sizeof errMsg, "Unable to find oldTrack %s in renameTrack()\n", oldTrack);
     if (*warnMsg)
 	*warnMsg = cloneString(errMsg);
     }
 return (foundOld && !foundNew);
 }
 
 char *findCompositeInIncluder(struct raFile *includer, char *composite, char *releaseTag, int *numTagsFound, boolean removeAlpha, char **warnMsg)
 /* */
 /* Find <composite>{.other}.ra in includer file trackDb.wgEncode.ra
  * Return compositeName (no path) or NULL if error, give warnings.
  * Must have tag releaseTag (usually "alpha") or no tags.
  * If removeAlpha is true, then the alpha tag is removed,
  * or if no tags then adds tags beta,pubic */
 {
 char *result = NULL;
 boolean found = FALSE;
 struct raRecord *r;
 for (r = includer->recordList; r != NULL; r = r->next)
     {
     if (!r->isRemoved)
 	{
 	struct raTag *tag;
 	for (tag = r->tagList; tag != NULL; tag = tag->next)
 	    {
 	    if (sameString(tag->name, "include") && startsWith(composite, tag->val))
 		{
 		char *raEnd = tag->val+strlen(composite);
 		if (raEnd[0] != '.')
 		    continue;
 		raEnd = strstr(raEnd, ".ra");
 		if (raEnd)
 		    {
 		    raEnd += strlen(".ra");
 		    char *releaseTags = raEnd;
 		    releaseTags = trimSpaces(releaseTags);
                     if (*releaseTags == 0)
 			{
 			found = TRUE;
     			result = cloneStringZ(tag->val, raEnd - tag->val);
 			if (numTagsFound)
 			    *numTagsFound = 0;
 			if (removeAlpha)
 			    {
 			    char tempTagVal[1024];
 			    safef(tempTagVal, sizeof tempTagVal, "%s beta,public", result);
 			    tag->val = cloneString(tempTagVal);
 			    updateTagText(tag, NULL, NULL);
 			    }
 
 			}
 		    else
 			{
     			struct slName *rlTags = slNameListFromString(releaseTags, ',');
     			struct slName *rlTag, *elFound = NULL;
 			int tagCount = slCount(rlTags);
 			for(rlTag=rlTags; rlTag; rlTag = rlTag->next)
 			    {
 			    if (sameString(rlTag->name, releaseTag))
 				{
 				found = TRUE;
 				result = cloneStringZ(tag->val, raEnd - tag->val);
 				if (numTagsFound)
 				    *numTagsFound = tagCount;
 				if (removeAlpha)
 				    elFound = rlTag;
 				}
 			    }
 			if (found && removeAlpha)
 			    {
 			    char tempTagVal[1024] = "";
 			    slRemoveEl(&rlTags, elFound);
 			    char *newTagString = slNameListToString(rlTags, ',');
 			    safef(tempTagVal, sizeof tempTagVal, "%s %s", result, newTagString);
 			    tag->val = cloneString(tempTagVal);
 			    updateTagText(tag, NULL, NULL);
 			    }
 			slFreeList(&rlTags);
 			}
 		    }
 		}
 	    }
 	}
     }
 if (!found)
     {
     char errMsg[1024];
     safef(errMsg, sizeof errMsg, 
           "Unable to find composite{.something}.ra in includer for composite %s "
           "in findCompositeInIncluder()\n", composite);
     if (*warnMsg)
 	*warnMsg = cloneString(errMsg);
     }
 return result;
 }
 
 
 boolean addCompositeToIncluder(struct raFile *includer, char *composite, char *newCompositeRaName, char **warnMsg)
 /* Find <composite>{.other}.ra in includer file trackDb.wgEncode.ra
  * Return compositeName (no path) or NULL if error, give warnings.
  * Split the record, inserting below all the records found, with tag alpha */
 {
 boolean found = FALSE;
 struct raRecord *r;
 struct raTag *tagFound = NULL;
 for (r = includer->recordList; r != NULL; r = r->next)
     {
     if (!r->isRemoved)
 	{
 	struct raTag *tag;
 	for (tag = r->tagList; tag != NULL; tag = tag->next)
 	    {
 	    if (sameString(tag->name, "include") && startsWith(composite, tag->val))
 		{
 		char *raEnd = tag->val+strlen(composite);
 		if (raEnd[0] != '.')
 		    continue;
 		raEnd = strstr(raEnd, ".ra");
 		if (raEnd)
 		    {
 		    raEnd += strlen(".ra");
 		    found = TRUE;
 		    tagFound = tag;
 		    }
 		}
 	    }
 	}
     }
 
 if (found)
     {
     char *indentation;
     splitTagText(tagFound, NULL, &indentation, NULL, NULL);
     struct raTag *tag;
     AllocVar(tag);
     tag->name = cloneString("include");
     char tempTagVal[1024] = "";
     safef(tempTagVal, sizeof tempTagVal, "%s alpha", newCompositeRaName);
     tag->val = cloneString(tempTagVal);
     char tempTagText[1024] = "";
     safef(tempTagText, sizeof tempTagText, "%sinclude %s alpha", indentation, newCompositeRaName);
     tag->text = cloneString(tempTagText);
     tag->next = tagFound->next;
     tagFound->next = tag;
     freeMem(indentation);
     }
 else
     {
     char errMsg[1024];
     safef(errMsg, sizeof errMsg, 
           "Unable to find composite{.something}.ra in includer for composite %s "
           "in findCompositeInIncluder()\n", composite);
     if (*warnMsg)
 	*warnMsg = cloneString(errMsg);
     }
 return found;
 }
 
 
 
 void encodePatchTdb(char *patchFileName, char *tdbFileName)
 /* encodePatchTdb - Lay a trackDb.ra file from the pipeline gently on top of the trackDb system. */
 {
 struct raFile *patchFile = raFileRead(patchFileName);
 struct raRecord *patchList = patchFile->recordList;
 int trackCount = slCount(patchList);
 if (trackCount < 1)
     errAbort("No tracks in %s", patchFileName);
 
 boolean hasTrack = compositeFirst(patchList);
 
 /* Find parent track name. */
 char *parentName;
 struct raRecord *subList;
 if (hasTrack)
     {
     parentName = patchList->key;
     subList = patchList->next;
     }
 else
     {
     char *val = raRecordFindTagVal(patchList, "parent");
     if (val == NULL)
         val = raRecordMustFindTagVal(patchList, "subTrack");
     parentName = cloneFirstWord(val);
     subList = patchList;
     val = strstr(parentName,"View"); // NOTE: Requires viewInTheMiddle follows naming convention {parentTrack}View{ViewName}
     if ( val != NULL)
         *val = '\0';  // Chop off the view portion of the name.
     }
 checkSubsAreForParent(parentName, subList);
 
 /* Load file to patch. */
 struct raFile *tdbFile = raFileRead(tdbFileName);
 int oldTdbCount = slCount(tdbFile->recordList);
 if (oldTdbCount < 50)
     warn("%s only has %d records, I hope you meant to hit a new file\n", tdbFileName,
     	oldTdbCount);
 linkUpParents(tdbFile);
 
 struct raRecord *tdbParent = findRecordCompatibleWithRelease(tdbFile, "alpha", parentName);
 if (!tdbParent)
     errAbort("Can't find composite track %s compatible with alpha mode in %s",
     	parentName, tdbFileName);
 patchInSubtracks(tdbParent, subList);
 
 char *outName = tdbFileName;
 if (clTest != NULL)
     outName = clTest;
 
 writeTdbFile(tdbFile, outName);
 }