b0828e726eba3cbd345f94f1ed0ad1b334424f7d
angie
  Thu Mar 14 10:38:31 2013 -0700
Fixing bug reported via MLQ: When repeatedly adding/modifying customtracks and saving sessions without any cartResets or session loads,
the same ctfile was used in both saved sessions and the user's active
cart, so modifications after saving a session were leaking back to
saved sessions.  Fix: copy custom tracks not only when loading a
saved session, but also immediately after saving a session, so that
the user's active cart no longer has the same ctfile (and customTrash
database tables, etc) as the saved session.
fixes #10396, refs #10323

diff --git src/hg/inc/cart.h src/hg/inc/cart.h
index 273443a..4a9261e 100644
--- src/hg/inc/cart.h
+++ src/hg/inc/cart.h
@@ -1,559 +1,563 @@
 /* cart - stuff to manage variables that persist from
  * one invocation of a cgi script to another (variables
  * that are carted around).  */
 
 #ifndef CART_H
 #define CART_H
 
 struct cart;         // forward definition for use in trackDb.h
 
 #include "jksql.h"
 #include "errabort.h"
 #include "dystring.h"
 #include "linefile.h"
 #include "trackDb.h"
 
 // If cgi set as CART_VAR_EMPTY, then removed from cart
 // If If cgi created new and oldVars are stored, then will be CART_VAR_EMPTY in old vars
 #define CART_VAR_EMPTY "[]"
 #define IS_CART_VAR_EMPTY(var) ((var) == NULL || sameString(var,CART_VAR_EMPTY))
 
 typedef struct sqlConnection *(*DbConnector)();
 /* funtion type used to get a connection to database */
 
 typedef void (*DbDisconnect)(struct sqlConnection **pConn);
 /* function type used to cleanup a connection from database */
 
 
 struct cart
 /* A cart of settings that persist. */
    {
    struct cart *next;	/* Next in list. */
    unsigned int userId;	/* User ID in database. */
    unsigned int sessionId;	/* Session ID in database. */
    struct hash *hash;	/* String valued hash. */
    struct hash *exclude;	/* Null valued hash of variables not to save. */
    struct cartDb *userInfo;	/* Info on user. */
    struct cartDb *sessionInfo;	/* Info on session. */
    };
 
 INLINE char *_cartVarDbName(const char *db, const char *var)
 /* generate cart variable name that is local to an assembly database.
  * Only for use inside of cart.h.  WARNING: static return */
 {
 static char buf[PATH_LEN]; // something rather big
 safef(buf, sizeof(buf), "%s_%s", var, db);
 return buf;
 }
 
 boolean cartTablesOk(struct sqlConnection *conn);
 /* Return TRUE if cart tables are accessible (otherwise, the connection
  * doesn't do us any good). */
 
 struct cart *cartNew(unsigned int userId, unsigned int sessionId,
 	char **exclude, struct hash *oldVars);
 /* Load up cart from user & session id's.  Exclude is a null-terminated list of
  * strings to not include. oldVars is an optional hash to put in values
  * that were just overwritten by cgi-variables. */
 
 struct cart *cartOfNothing();
 /* Create a new, empty, cart with no real connection to the database. */
 
 struct cart *cartFromHash(struct hash *hash);
 /* Create a cart from hash */
 
 struct cart *cartFromCgiOnly(unsigned int userId, unsigned int sessionId,
 	char **exclude, struct hash *oldVars);
 /* Create a new cart that contains only CGI variables, nothing from the
  * database, and no way to write back to database either. */
 
 void cartCheckout(struct cart **pCart);
 /* Save cart to database and free it up. */
 
 void cartEncodeState(struct cart *cart, struct dyString *dy);
 /* Add a CGI-encoded var=val&... string of all cart variables to dy. */
 
 char *cartSessionVarName();
 /* Return name of CGI session ID variable. */
 
 unsigned int cartSessionId(struct cart *cart);
 /* Return session id. */
 
 char *cartSidUrlString(struct cart *cart);
 /* Return session id string as in hgsid=N . */
 
 unsigned int cartUserId(struct cart *cart);
 /* Return session id. */
 
 void cartRemove(struct cart *cart, char *var);
 /* Remove variable from cart. */
 
 void cartRemoveExcept(struct cart *cart, char **except);
 /* Remove variables except those in null terminated except array
  * from cart.  Except array may be NULL in which case all
  * are removed. */
 
 struct slPair *cartVarsLike(struct cart *cart, char *wildCard);
 /* Return a slPair list of cart vars that match the wildcard */
 
 struct slPair *cartVarsWithPrefix(struct cart *cart, char *prefix);
 /* Return a slPair list of cart vars that begin with prefix */
 
 struct slPair *cartVarsWithPrefixLm(struct cart *cart, char *prefix, struct lm *lm);
 /* Return list of cart vars that begin with prefix allocated in local memory.
  * Quite a lot faster than cartVarsWithPrefix. */
 
 void cartRemoveLike(struct cart *cart, char *wildCard);
 /* Remove all variable from cart that match wildCard. */
 
 void cartRemovePrefix(struct cart *cart, char *prefix);
 /* Remove variables with given prefix from cart. */
 
 boolean cartVarExists(struct cart *cart, char *var);
 /* Return TRUE if variable is in cart. */
 
 boolean cartListVarExists(struct cart *cart, char *var);
 /* Return TRUE if a list variable is in cart (list may still be empty). */
 
 INLINE boolean cartVarExistsDb(struct cart *cart, char *db, char *var)
 /* Return TRUE if variable_$db is in cart. */
 {
 return cartVarExists(cart, _cartVarDbName(db, var));
 }
 
 char *cartString(struct cart *cart, char *var);
 /* Return string valued cart variable. */
 
 INLINE char *cartStringDb(struct cart *cart, char *db, char *var)
 /* Return string valued cart var_$db. */
 {
 return cartString(cart, _cartVarDbName(db, var));
 }
 
 char *cartOptionalString(struct cart *cart, char *var);
 /* Return string valued cart variable or NULL if it doesn't exist. */
 
 INLINE char *cartOptionalStringDb(struct cart *cart, char *db, char *var)
 /* Return string valued cart variable_$db or NULL if it doesn't exist. */
 {
 return cartOptionalString(cart, _cartVarDbName(db, var));
 }
 
 char *cartNonemptyString(struct cart *cart, char *name);
 /* Return string value associated with name.  Return NULL
  * if value doesn't exist or if it is pure white space. */
 
 INLINE char *cartNonemptyStringDb(struct cart *cart, char *db, char *name)
 /* Return string value associated with name_$db.  Return NULL
  * if value doesn't exist or if it is pure white space. */
 {
 return cartNonemptyString(cart, _cartVarDbName(db, name));
 }
 
 char *cartUsualString(struct cart *cart, char *var, char *usual);
 /* Return variable value if it exists or usual if not. */
 
 INLINE char *cartUsualStringDb(struct cart *cart, char *db, char *var, char *usual)
 /* Return var_$db value if it exists or usual if not. */
 {
 return cartUsualString(cart, _cartVarDbName(db, var), usual);
 }
 
 char *cartCgiUsualString(struct cart *cart, char *var, char *usual);
 /* Look for var in CGI, then in cart, if not found then return usual. */
 
 struct slName *cartOptionalSlNameList(struct cart *cart, char *var);
 /* Return slName list (possibly with multiple values for the same var) or
  * NULL if not found. */
 
 void cartAddString(struct cart *cart, char *var, char *val);
 /* Add string valued cart variable (if called multiple times on same var,
  * will create a list -- retrieve with cartOptionalSlNameList. */
 
 void cartSetString(struct cart *cart, char *var, char *val);
 /* Set string valued cart variable. */
 
 INLINE void cartSetStringDb(struct cart *cart, char *db, char *var, char *val)
 /* Set string valued cart var_$db. */
 {
 cartSetString(cart, _cartVarDbName(db, var), val);
 }
 
 int cartInt(struct cart *cart, char *var);
 /* Return int valued variable. */
 
 INLINE int cartIntDb(struct cart *cart, char *db, char *var)
 /* Return int valued variable_$db. */
 {
 return cartInt(cart, _cartVarDbName(db, var));
 }
 
 
 int cartIntExp(struct cart *cart, char *var);
 /* Return integer valued expression in variable. */
 
 int cartUsualInt(struct cart *cart, char *var, int usual);
 /* Return variable value if it exists or usual if not. */
 
 INLINE int cartUsualIntDb(struct cart *cart, char *db, char *var, int usual)
 /* Return variable_$db value if it exists or usual if not. */
 {
 return cartUsualInt(cart, _cartVarDbName(db, var), usual);
 }
 
 int cartUsualIntClipped(struct cart *cart, char *var, int usual,
 	int minVal, int maxVal);
 /* Return integer variable clipped to lie between minVal/maxVal */
 
 int cartCgiUsualInt(struct cart *cart, char *var, int usual);
 /* Look for var in CGI, then in cart, if not found then return usual. */
 
 void cartSetInt(struct cart *cart, char *var, int val);
 /* Set integer value. */
 
 INLINE void cartSetIntDb(struct cart *cart, char *db, char *var, int val)
 /* Set integer value for var_$db. */
 {
 cartSetInt(cart, _cartVarDbName(db, var), val);
 }
 
 double cartDouble(struct cart *cart, char *var);
 /* Return double valued variable. */
 
 INLINE double cartDoubleDb(struct cart *cart, char *db, char *var)
 /* Return double valued var_$db. */
 {
 return cartDouble(cart, _cartVarDbName(db, var));
 }
 
 double cartUsualDouble(struct cart *cart, char *var, double usual);
 /* Return variable value if it exists or usual if not. */
 
 INLINE double cartUsualDoubleDb(struct cart *cart, char *db, char *var, double usual)
 /* Return var_$db value if it exists or usual if not. */
 {
 return cartUsualDouble(cart, _cartVarDbName(db, var), usual);
 }
 
 double cartCgiUsualDouble(struct cart *cart, char *var, double usual);
 /* Look for var in CGI, then in cart, if not found then return usual. */
 
 void cartSetDouble(struct cart *cart, char *var, double val);
 /* Set double value. */
 
 INLINE void cartSetDoubleDb(struct cart *cart, char *db, char *var, double val)
 /* Set double value for var_$db. */
 {
 cartSetDouble(cart, _cartVarDbName(db, var), val);
 }
 
 boolean cartBoolean(struct cart *cart, char *var);
 /* Retrieve cart boolean. */
 
 INLINE boolean cartBooleanDb(struct cart *cart, char *db, char *var)
 /* Retrieve cart boolean for var_$db. */
 {
 return cartBoolean(cart, _cartVarDbName(db, var));
 }
 
 boolean cartUsualBoolean(struct cart *cart, char *var, boolean usual);
 /* Return variable value if it exists or usual if not.  */
 
 INLINE boolean cartUsualBooleanDb(struct cart *cart, char *db, char *var, boolean usual)
 /* Return var_$db value if it exists or usual if not.  */
 {
 return cartUsualBoolean(cart, _cartVarDbName(db, var), usual);
 }
 
 boolean cartCgiUsualBoolean(struct cart *cart, char *var, boolean usual);
 /* Look for var in CGI, then in cart, if not found then return usual. */
 
 void cartSetBoolean(struct cart *cart, char *var, boolean val);
 /* Set boolean value. */
 
 INLINE void cartSetBooleanDb(struct cart *cart, char *db, char *var, boolean val)
 /* Set boolean value for $var_db. */
 {
 cartSetBoolean(cart, _cartVarDbName(db, var), val);
 }
 
 void cartMakeTextVar(struct cart *cart, char *var, char *defaultVal, int charSize);
 /* Make a text control filled with value from cart if it exists or
  * default value otherwise.  If charSize is zero it's calculated to fit
  * current value.  Default value may be NULL. */
 
 void cartMakeIntVar(struct cart *cart, char *var, int defaultVal, int maxDigits);
 /* Make a text control filled with integer value - from cart if available
  * otherwise default.  */
 
 void cartMakeDoubleVar(struct cart *cart, char *var, double defaultVal, int maxDigits);
 /* Make a text control filled with integer value - from cart if available
  * otherwise default.  */
 
 void cartMakeCheckBox(struct cart *cart, char *var, boolean defaultVal);
 /* Make a check box filled with value from cart if it exists or
  * default value otherwise.  */
 
 void cartMakeRadioButton(struct cart *cart, char *var, char *val, char *defaultVal);
 /* Make a radio button that is selected if cart variable exists and matches
  * value (or value matches default val if cart var doesn't exist). */
 
 void cartSaveSession(struct cart *cart);
 /* Save session in a hidden variable. This needs to be called
  * somewhere inside of form or bad things will happen. */
 
 void cartDump(struct cart *cart);
 /* Dump contents of cart. */
 
 #define CART_DUMP_AS_TABLE "cartDumpAsTable"
 void cartDumpList(struct hashEl *elList,boolean asTable);
 /* Dump list of cart variables optionally as a table with ajax update support. */
 
 void cartDumpPrefix(struct cart *cart, char *prefix);
 /* Dump all cart variables with prefix */
 
 void cartDumpLike(struct cart *cart, char *wildcard);
 /* Dump all cart variables matching wildcard */
 
 struct hashEl *cartFindPrefix(struct cart *cart, char *prefix);
 /* Return list of name/val pairs from cart where name starts with
  * prefix.  Free when done with hashElFreeList. */
 
 struct hashEl *cartFindLike(struct cart *cart, char *wildCard);
 /* Return list of name/val pairs from cart where name matches
  * wildcard.  Free when done with hashElFreeList. */
 
 char *cartFindFirstLike(struct cart *cart, char *wildCard);
 /* Find name of first variable that matches wildCard in cart.
  * Return NULL if none. */
 
 void cartResetInDb(char *cookieName);
 /* Clear cart in database. */
 
 void cartEarlyWarningHandler(char *format, va_list args);
 /* Write an error message so user can see it before page is really started */
 
 void cartWarnCatcher(void (*doMiddle)(struct cart *cart), struct cart *cart, WarnHandler warner);
 /* Wrap error and warning handlers around doMiddl. */
 
 void cartEmptyShell(void (*doMiddle)(struct cart *cart), char *cookieName,
 	char **exclude, struct hash *oldVars);
 /* Get cart and cookies and set up error handling, but don't start writing any
  * html yet. The doMiddleFunction has to call cartHtmlStart(title), and
  * cartHtmlEnd(), as well as writing the body of the HTML.
  * oldVars - those in cart that are overlayed by cgi-vars are
  * put in optional hash oldVars. */
 
 void cartHtmlStart(char *title);
 /* Write HTML header and put in normal error handler. Needed with cartEmptyShell,
  * but not cartHtmlShell. */
 
 void cartFooter(void);
 /* Write out HTML footer, possibly with googleAnalytics too */
 
 void cartHtmlEnd();
 /* Write out HTML footer and get rid or error handler. Needed with cartEmptyShell,
  * but not cartHtmlShell. */
 
 void cartWebStart(struct cart *theCart, char *db, char *format, ...)
 /* Print out pretty wrapper around things when working
  * from cart. Balance this with cartWebEnd. */
 #if defined(__GNUC__)
 __attribute__((format(printf, 3, 4)))
 #endif
 ;
 
 void cartVaWebStart(struct cart *cart, char *db, char *format, va_list args);
 /* Print out pretty wrapper around things when working
  * from cart. */
 
 void cartWebEnd();
 /* End out pretty wrapper around things when working
  * from cart. */
 
 void cartHtmlShellWithHead(char *head, char *title, void (*doMiddle)(struct cart *cart),
         char *cookieName, char **exclude, struct hash *oldVars);
 /* Load cart from cookie and session cgi variable.  Write web-page
  * preamble including head and title, call doMiddle with cart, and write end of web-page.
  * Exclude may be NULL.  If it exists it's a comma-separated list of
  * variables that you don't want to save in the cart between
  * invocations of the cgi-script. */
 
 void cartHtmlShell(char *title, void (*doMiddle)(struct cart *cart),
 	char *cookieName, char **exclude, struct hash *oldVars);
 /* Load cart from cookie and session cgi variable.  Write web-page preamble, call doMiddle
  * with cart, and write end of web-page.   Exclude may be NULL.  If it exists it's a
  * comma-separated list of variables that you don't want to save in the cart between
  * invocations of the cgi-script. oldVars is an optional hash that will get values
  * of things in the cart that were overwritten by cgi-variables. */
 
 void cartWriteCookie(struct cart *cart, char *cookieName);
 /* Write out HTTP Set-Cookie statement for cart. */
 
 struct cart *cartAndCookie(char *cookieName, char **exclude,
 	struct hash *oldVars);
 /* Load cart from cookie and session cgi variable.  Write cookie and
  * content-type part HTTP preamble to web page.  Don't write any HTML though. */
 
 struct cart *cartAndCookieNoContent(char *cookieName, char **exclude,
 	struct hash *oldVars);
 /* Load cart from cookie and session cgi variable. Don't write out
  * content type or any HTML. */
 
 struct cart *cartAndCookieWithHtml(char *cookieName, char **exclude,
 	struct hash *oldVars, boolean doContentType);
 /* Load cart from cookie and session cgi variable.  Write cookie
  * and optionally content-type part HTTP preamble to web page.  Don't
  * write any HTML though. */
 
 struct cart *cartForSession(char *cookieName, char **exclude,
 	struct hash *oldVars);
 /* This gets the cart without writing any HTTP lines at all to stdout. */
 
 void cartSetDbConnector(DbConnector connector);
 /* Set the connector that will be used by the cart to connect to the
  * database. Default connector is hConnectCart */
 
 void cartSetDbDisconnector(DbDisconnect disconnector);
 /* Set the connector that will be used by the cart to disconnect from the
  * database. Default disconnector is hDisconnectCart */
 
 
 /* Libified constants and code that originally belonged only to hgSession
  * (now hgTracks uses them too): */
 #define hgSessionPrefix "hgS_"
 
 #define hgsOtherUserName hgSessionPrefix "otherUserName"
 #define hgsOtherUserSessionName hgSessionPrefix "otherUserSessionName"
 #define hgsDoOtherUser hgSessionPrefix "doOtherUser"
 
 #define hgsLoadUrlName hgSessionPrefix "loadUrlName"
 #define hgsDoLoadUrl hgSessionPrefix "doLoadUrl"
 
 #define namedSessionTable "namedSessionDb"
 
 void sessionTouchLastUse(struct sqlConnection *conn, char *encUserName,
 			 char *encSessionName);
 /* Increment namedSessionDb.useCount and update lastUse for this session. */
 
 void cartLoadUserSession(struct sqlConnection *conn, char *sessionOwner,
 			 char *sessionName, struct cart *cart,
 			 struct hash *oldVars, char *actionVar);
 /* If permitted, load the contents of the given user's session, and then
  * reload the CGI settings (to support override of session settings).
  * If non-NULL, oldVars will contain values overloaded when reloading CGI.
  * If non-NULL, actionVar is a cartRemove wildcard string specifying the
  * CGI action variable that sent us here. */
 
 void cartLoadSettings(struct lineFile *lf, struct cart *cart,
 		      struct hash *oldVars, char *actionVar);
 /* Load settings (cartDump output) into current session, and then
  * reload the CGI settings (to support override of session settings).
  * If non-NULL, oldVars will contain values overloaded when reloading CGI.
  * If non-NULL, actionVar is a cartRemove wildcard string specifying the
  * CGI action variable that sent us here. */
 
 char *cartGetOrderFromFile(char *genomeDb, struct cart *cart, char *speciesUseFile);
 /* Look in a cart variable that holds the filename that has a list of
  * species to show in a maf file */
 
 char *cartGetOrderFromFileAndMsaTable(char *genomeDb, struct cart *cart, char *speciesUseFile, char *msaTable);
 /* Look in a cart variable that holds the filename that has a list of
  * species to show in a maf file */
 
 char *cartLookUpVariableClosestToHome(struct cart *cart, struct trackDb *tdb,
                                       boolean parentLevel, char *suffix,char **pVariable);
 /* Returns value or NULL for a cart variable from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix
    Optionally fills the non NULL pVariable with the actual name of the variable in the cart */
 #define cartOptionalStringClosestToHome(cart,tdb,parentLevel,suffix) \
         cartLookUpVariableClosestToHome((cart),(tdb),(parentLevel),(suffix),NULL)
 
 void cartRemoveVariableClosestToHome(struct cart *cart, struct trackDb *tdb,
                                      boolean parentLevel, char *suffix);
 /* Looks for then removes a cart variable from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix */
 
 char *cartStringClosestToHome(struct cart *cart, struct trackDb *tdb,
                               boolean parentLevel, char *suffix);
 /* Returns value or Aborts for a cart string from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix */
 
 boolean cartVarExistsAnyLevel(struct cart *cart, struct trackDb *tdb,
                               boolean parentLevel, char *suffix);
 /* Returns TRUE if variable exists anywhere, looking from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix */
 
 boolean cartListVarExistsAnyLevel(struct cart *cart, struct trackDb *tdb,
 				  boolean parentLevel, char *suffix);
 /* Return TRUE if a list variable for tdb->track (or tdb->parent->track,
  * or tdb->parent->parent->track, etc.) is in cart (list itself may be NULL). */
 
 char *cartUsualStringClosestToHome(struct cart *cart, struct trackDb *tdb, boolean parentLevel,
                                    char *suffix, char *usual);
 /* Returns value or {usual} for a cart string from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix */
 
 boolean cartBooleanClosestToHome(struct cart *cart, struct trackDb *tdb,
                                  boolean parentLevel, char *suffix);
 /* Returns value or Aborts for a cart boolean ('on' or != 0) from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix */
 
 boolean cartUsualBooleanClosestToHome(struct cart *cart, struct trackDb *tdb,
                                       boolean parentLevel, char *suffix,boolean usual);
 /* Returns value or {usual} for a cart boolean ('on' or != 0) from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix */
 
 int cartUsualIntClosestToHome(struct cart *cart, struct trackDb *tdb,
                               boolean parentLevel, char *suffix, int usual);
 /* Returns value or {usual} for a cart int from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix */
 
 double cartUsualDoubleClosestToHome(struct cart *cart, struct trackDb *tdb,
                                     boolean parentLevel, char *suffix, double usual);
 /* Returns value or {usual} for a cart fp double from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix */
 
 struct slName *cartOptionalSlNameListClosestToHome(struct cart *cart, struct trackDb *tdb,
                                                    boolean parentLevel, char *suffix);
 /* Return slName list (possibly with multiple values for the same var) from lowest level on up:
    subtrackName.suffix, then compositeName.view.suffix, then compositeName.suffix */
 
 void cartRemoveAllForTdb(struct cart *cart, struct trackDb *tdb);
 /* Remove all variables from cart that are associated with this tdb. */
 
 void cartRemoveAllForTdbAndChildren(struct cart *cart, struct trackDb *tdb);
 /* Remove all variables from cart that are associated
    with this tdb and it's children. */
 
 char *cartOrTdbString(struct cart *cart, struct trackDb *tdb, char *var, char *defaultVal);
 /* Look first in cart, then in trackDb for var.  Return defaultVal if not found. */
 
 int cartOrTdbInt(struct cart *cart, struct trackDb *tdb, char *var, int defaultVal);
 /* Look first in cart, then in trackDb for var.  Return defaultVal if not found. */
 
 double cartOrTdbDouble(struct cart *cart, struct trackDb *tdb, char *var, double defaultVal);
 /* Look first in cart, then in trackDb for var.  Return defaultVal if not found. */
 
 boolean cartValueHasChanged(struct cart *newCart,struct hash *oldVars,char *setting,
                             boolean ignoreRemoved,boolean ignoreCreated);
 /* Returns TRUE if new cart setting has changed from old cart setting */
 
 int cartRemoveFromTdbTree(struct cart *cart,struct trackDb *tdb,char *suffix,boolean skipParent);
 /* Removes a 'trackName.suffix' from all tdb descendents (but not parent).
    If suffix NULL then removes 'trackName' which holds visibility */
 
 boolean cartTdbTreeReshapeIfNeeded(struct cart *cart,struct trackDb *tdbComposite);
 /* When subtrack vis is set via findTracks, and composite has no cart settings,
    then fashion composite to match found */
 
 boolean cartTdbTreeCleanupOverrides(struct trackDb *tdb,struct cart *newCart,struct hash *oldVars, struct lm *lm);
 /* When composite/view settings changes, remove subtrack specific settings
    Returns TRUE if any cart vars are removed */
 
+void cartCopyCustomTracks(struct cart *cart, char *db);
+/* If cart contains any live custom tracks, save off a new copy of them,
+ * to prevent clashes by multiple uses of the same session.  */
+
 void cgiExitTime(char *cgiName, long enteredMainTime);
 /* single stderr print out called at end of CGI binaries to record run
  * time in apache error_log */
 
 #endif /* CART_H */