5c213974bb28e98d1b10ad9a86063101e684e5e3 braney Mon Oct 31 12:16:52 2022 -0700 add Jim's track duplication code diff --git src/hg/lib/dupTrack.c src/hg/lib/dupTrack.c new file mode 100644 index 0000000..a5004e5 --- /dev/null +++ src/hg/lib/dupTrack.c @@ -0,0 +1,251 @@ +/* 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" + +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)) + { + 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; +} +