54535926c35c6a44c925a3169773f2506a6e27ca
braney
  Mon Dec 5 11:56:46 2022 -0800
check for NULL before checking for dups

diff --git src/hg/lib/dupTrack.c src/hg/lib/dupTrack.c
index e858793..ce91cd3 100644
--- src/hg/lib/dupTrack.c
+++ src/hg/lib/dupTrack.c
@@ -1,258 +1,259 @@
 /* Stuff to help handle a track that is a duplicate of a another - sharing data
  * and type, but having it's own cart settings. */
 
 #include "common.h"
 #include "hash.h"
 #include "cart.h"
 #include "dupTrack.h"
 #include "ra.h"
 #include "portable.h"
 #include "trashDir.h"
 #include "hgConfig.h"
 
 boolean isDupTrack(char *track)
 /* determine if track name refers to a custom track */
 {
 return (startsWith(DUP_TRACK_PREFIX, track));
 }
 
 char *dupTrackSkipToSourceName(char *dupeTrackName)
 /* If it looks like it's a dupe track then skip over duppy part 
  * in particular skip over dup_N_ form prefix for numerical N. */
 {
 char *name = dupeTrackName;
-if (startsWith(DUP_TRACK_PREFIX, name))
+
+if ((name != NULL ) && startsWith(DUP_TRACK_PREFIX, name))
     {
     char *s = name + strlen(DUP_TRACK_PREFIX);
     if (isdigit(s[0]))
         {
 	s = skipNumeric(s);
 	if (s[0] == '_')
 	    return s+1;
 	}
     }
 return name;
 }
 
 static void makeDupName(char *sourceName, int n, char *buf, int bufSize)
 /* Create name for dupe */
 {
 char *source = dupTrackSkipToSourceName(sourceName);
 safef(buf, bufSize, "%s%d_%s", DUP_TRACK_PREFIX, n, source);
 }
 
 static void findUniqDupName(char *sourceName, struct hash *dupHash, char nameBuf[], int nameBufSize)
 /* Make up a name of format dup_N_sourceTrack where N is a small unique number */
 {
 int i;
 for (i=1; ;++i)
     {
     makeDupName(sourceName, i, nameBuf, nameBufSize);
     if (!hashLookup(dupHash, nameBuf))
 	return;
     }
 }
 
 char *dupTrackInCartAndTrash(char *sourceTrack, struct cart *cart, struct trackDb *sourceTdb)
 /* Update cart vars to reflect existance of duplicate of sourceTrack.
  * Also write out or append to dupe track trash file */
 {
 char *dupFileName = cartUsualString(cart, DUP_TRACKS_VAR, NULL);
 FILE *f = NULL;	    // This will be our output
 
 /* We keep duplicate's name here. */
 int bufSize = strlen(sourceTrack) + 32;
 char dupeTrackName[bufSize];
 makeDupName(sourceTrack, 1, dupeTrackName, sizeof(dupeTrackName));
 
 if (dupFileName != NULL && fileExists(dupFileName))   // Try and read in from old file
     {
     /* If there are already duplicates and trash cleaner hasn't nuked file read it in
      * and make sre we come up with a unique name before we append to existing file */
     struct hash *dupHash = raReadAll(dupFileName, "track");
     findUniqDupName(sourceTrack, dupHash, dupeTrackName, sizeof(dupeTrackName));
     f = mustOpen(dupFileName, "a");	// Append to old file but use new name
     fputc('\n', f);
     }
 else
     {
     /* Otherwise we need to make up a new temp file and recored it in the cart */
     struct tempName dupTn;
     trashDirDateFile(&dupTn, "dup", "dup", ".ra");
     f = mustOpen(dupTn.forCgi, "w");
     cartSetString(cart, DUP_TRACKS_VAR, dupTn.forCgi);
     }
 /* Write out new stanza */
 fprintf(f, "track %s\n", dupeTrackName);
 fprintf(f, "source %s\n", dupTrackSkipToSourceName(sourceTrack));
 int dupNo = atoi(dupeTrackName + strlen(DUP_TRACK_PREFIX)) + 1;
 if (sourceTdb != NULL)
     {
     fprintf(f, "shortLabel %s #%d\n", sourceTdb->shortLabel, dupNo);
     fprintf(f, "longLabel %s copy #%d\n", sourceTdb->longLabel, dupNo);
     }
 else
     {
     fprintf(f, "%s #%d\n", sourceTrack, dupNo);
     fprintf(f, "longLabel %s copy #%d\n", sourceTrack, dupNo);
     }
 carefulClose(&f);
 
 cartCloneVarsWithPrefix(cart, sourceTrack, dupeTrackName);
 return cloneString(dupeTrackName);
 }
 
 static void cartRemoveConfigVarsForTrack(struct cart *cart, char *track)
 /* Remove track, track.* and track_*.  Perhaps this is more cautious than
  * track*? */
 {
 cartRemove(cart, track);
 int nameSize = strlen(track);
 char namePlusBuf[nameSize+2];
 strcpy(namePlusBuf, track);
 namePlusBuf[nameSize] = '.';
 namePlusBuf[nameSize+1] = 0; 
 cartRemovePrefix(cart, namePlusBuf);
 namePlusBuf[nameSize] = '_';
 cartRemovePrefix(cart, namePlusBuf);
 }
 
 static void dupTracksRemoveAllFromCart(struct cart *cart)
 /* Remove all trace of dupe tracks from cart */
 {
 cartRemovePrefix(cart, DUP_TRACK_PREFIX);
 }
 
 void undupTrackInCartAndTrash(char *dupName, struct cart *cart)
 /* Update cart vars to reflect removal of dupTrack.  Also reduce or 
  * remove trash file */
 {
 char *dupFileName = cartUsualString(cart, DUP_TRACKS_VAR, NULL);
 if (dupFileName == NULL)
     return;  // Nothing to do here
 
 /* We'll try and create our new list from info in old file */
 struct dupTrack *newList = NULL;
 if (fileExists(dupFileName))
     {
     struct dupTrack *dupList = dupTrackReadAll(dupFileName);
 
     /* Turn newList into a copy of dupList with our own stanza removed */
     struct dupTrack *dup, *next;
     for (dup = dupList; dup != NULL; dup = next)
         {
 	next = dup->next;
 	if (!sameString(dupName, dup->name))
 	    slAddHead(&newList, dup);
 	}
     slReverse(&newList);
     }
 
 /* If we have any dupes left, write them to file, otherwise remove dup variable from cart */
 if (newList == NULL)
     {
     dupTracksRemoveAllFromCart(cart);
     }
 else
     {
     FILE *f = mustOpen(dupFileName, "w");
     struct dupTrack *dup;
     for (dup = newList; dup != NULL; dup = dup->next)
         {
 	if (dup != newList)
 	    fputc('\n', f);
 	struct slPair *tag;
 	for (tag = dup->tagList; tag != NULL; tag = tag->next)
 	    fprintf(f, "%s\t%s\n", tag->name, (char *)(tag->val));
 	}
     carefulClose(&f);
     cartRemoveConfigVarsForTrack(cart, dupName);
     }
 }
 
 struct dupTrack *dupTrackReadAll(char *fileName)
 /* Read in ra file and return it as a list of dupTracks */
 {
 struct dupTrack *list = NULL;
 struct slPair *tagList;
 struct lineFile *lf = lineFileMayOpen(fileName, TRUE);
 if (lf == NULL)
     return NULL;
 
 while ((tagList = raNextStanzAsPairs(lf)) != NULL)
     {
     char *name = slPairFindVal(tagList, "track");
     if (name == NULL)
 	errAbort("Trackless stanza ending line %d of %s", lf->lineIx, lf->fileName);
     char *source = slPairFindVal(tagList, "source");
     if (source == NULL)
        errAbort("Sourceless stanza ending line %d of %s", lf->lineIx, lf->fileName);
     struct dupTrack *dup;
     AllocVar(dup);
     dup->name = cloneString(name);
     dup->source = cloneString(source);
     dup->tagList = tagList;
     slAddHead(&list, dup);
     }
 lineFileClose(&lf);
 slReverse(&list);
 return list;
 }
 
 static void dupTrackAddInfoToTdb(struct dupTrack *dup, struct trackDb *tdb)
 /* Add tags from dup to tdb */
 {
 struct slPair *tag;
 for (tag = dup->tagList; tag != NULL; tag = tag->next)
     {
     if (!sameString(tag->name, "track"))
 	{
 	if (sameString(tag->name, "longLabel"))
 	    tdb->longLabel = tag->val;
 	else if (sameString(tag->name, "shortLabel"))
 	    tdb->shortLabel = tag->val;
 	hashAdd(tdb->settingsHash, tag->name, tag->val);
 	}
     }
 }
 
 struct dupTrack *dupTrackListFromCart(struct cart *cart)
 /* Consult cart for dup track variable and if it's there return
  * list of dupes */
 {
 char *dupFileName = cartUsualString(cart, DUP_TRACKS_VAR, NULL);
 if (dupFileName == NULL)
     return NULL;
 struct dupTrack *list = dupTrackReadAll(dupFileName);
 if (list == NULL)	// Trash cleaner got it, so clean up cart too
     dupTracksRemoveAllFromCart(cart);
 return list;
 }
 
 struct dupTrack *dupTrackFindInList(struct dupTrack *list, char *name)
 /* Return matching element in list */
 {
 struct dupTrack *dup;
 for (dup = list; dup != NULL; dup = dup->next)
    if (sameString(dup->name, name))
        break;
 return dup;
 }
 
 struct trackDb *dupTdbFrom(struct trackDb *sourceTdb, struct dupTrack *dup)
 /* Generate a duplicate trackDb */
 {
 /* Start with a basic shallow clone */
 struct trackDb *tdb = CloneVar(sourceTdb);
 tdb->next = NULL;
 tdb->track = cloneString(dup->name);
 dupTrackAddInfoToTdb(dup, tdb);
 return tdb;
 }
 
 boolean dupTrackEnabled()
 /* Return true if we allow tracks to be duped. */
 {
 return  cfgOptionBooleanDefault("canDupTracks", FALSE);
 }