98f954e330c488c884f0c0d32cac466fe74612aa
angie
  Wed Mar 9 15:49:57 2011 -0800
While working on something else, I realized that when cartRemove iscalled on a list variable, it should remove the list's multShadow
variable in addition to all values, because the list variable is no
longer defined in the cart.  If the multShadow is left around, it will
look like the variable is in the cart, set to empty.

diff --git src/hg/lib/cart.c src/hg/lib/cart.c
index 9b2c375..15080fe 100644
--- src/hg/lib/cart.c
+++ src/hg/lib/cart.c
@@ -11,32 +11,30 @@
 #include "cart.h"
 #include "net.h"
 #include "web.h"
 #include "hdb.h"
 #include "jksql.h"
 #include "jsHelper.h"
 #include "trashDir.h"
 #ifndef GBROWSE
 #include "customFactory.h"
 #include "googleAnalytics.h"
 #include "wikiLink.h"
 #endif /* GBROWSE */
 #include "hgMaf.h"
 #include "hui.h"
 
-static char const rcsid[] = "$Id: cart.c,v 1.120 2010/05/20 03:15:51 kent Exp $";
-
 static char *sessionVar = "hgsid";	/* Name of cgi variable session is stored in. */
 static char *positionCgiName = "position";
 
 DbConnector cartDefaultConnector = hConnectCart;
 DbDisconnect cartDefaultDisconnector = hDisconnectCart;
 
 static void hashUpdateDynamicVal(struct hash *hash, char *name, void *val)
 /* Val is a dynamically allocated (freeMem-able) entity to put
  * in hash.  Override existing hash item with that name if any.
  * Otherwise make new hash item. */
 {
 struct hashEl *hel = hashLookup(hash, name);
 if (hel == NULL)
     hashAdd(hash, name, val);
 else
@@ -356,30 +354,31 @@
     // Support for 2 boolean CBs: checked/unchecked (1/0) and enabled/disabled:(-1/-2)
 	char *val = (cgiVarExists(booVar) ? "1" : cv->val);
 	storeInOldVars(cart, oldVars, booVar);
 	cartRemove(cart, booVar);
         hashAdd(cgiHash, booVar, val);
 	hashAdd(booHash, booVar, NULL);
 	}
     else if (startsWith(multShadow, cv->name))
 	{
 	/* This shadow variable enables us to detect when all inputs in
 	 * the multi-select box have been deselected. */
 	char *multVar = cv->name + multSize;
 	if (! cgiVarExists(multVar))
 	    {
 	    storeInOldVars(cart, oldVars, multVar);
+	    storeInOldVars(cart, oldVars, cv->name);
 	    cartRemove(cart, multVar);
 	    }
 	}
     }
 
 /* Handle non-boolean vars. */
 for (cv = cgiVarList(); cv != NULL; cv = cv->next)
     {
     if (! (startsWith(booShadow, cv->name) || hashLookup(booHash, cv->name)))
 	{
 	storeInOldVars(cart, oldVars, cv->name);
 	cartRemove(cart, cv->name);
         if (differentString(cv->val, CART_VAR_EMPTY))  // NOTE: CART_VAR_EMPTY logic not implemented for boolShad
             hashAdd(cgiHash, cv->name, cv->val);
 	}
@@ -510,39 +509,30 @@
 	} /* not hgsid */
     } /* each line */
 if (oldVars)
     hashEmpty(oldVars);
 /* Overload settings explicitly passed in via CGI (except for the
  * command that sent us here): */
 loadCgiOverHash(cart, oldVars);
 #ifndef GBROWSE
 cartCopyCustomTracks(cart, oldVars);
 #endif /* GBROWSE */
 
 if (isNotEmpty(actionVar))
     cartRemove(cart, actionVar);
 }
 
-char *_cartVarDbName(char *db, 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;
-}
-
 static char *now()
 /* Return a mysql-formatted time like "2008-05-19 15:33:34". */
 {
 char nowBuf[256];
 time_t seconds = clock1();
 struct tm *theTime = localtime(&seconds);
 strftime(nowBuf, sizeof nowBuf, "%Y-%m-%d %H:%M:%S", theTime);
 return cloneString(nowBuf);
 }
 
 static struct cartDb *emptyCartDb()
 /* Create a new empty placeholder cartDb. */
 {
 struct cartDb *cdb;
 AllocVar(cdb);
@@ -758,57 +748,67 @@
 
 char *cartSidUrlString(struct cart *cart)
 /* Return session id string as in hgsid=N . */
 {
 static char buf[64];
 safef(buf, sizeof(buf), "%s=%u", cartSessionVarName(), cartSessionId(cart));
 return buf;
 }
 
 unsigned int cartUserId(struct cart *cart)
 /* Return session id. */
 {
 return cart->userInfo->id;
 }
 
-static int cartRemoveAndCount(struct cart *cart, char *var)
+static char *cartMultShadowVar(struct cart *cart, char *var)
+/* Return a pointer to the list variable shadow variable name for var.
+ * Don't modify or free result. */
+{
+static char multShadowVar[PATH_LEN];
+safef(multShadowVar, sizeof(multShadowVar), "%s%s", cgiMultListShadowPrefix(), var);
+return multShadowVar;
+}
+
+static int cartRemoveAndCountNoShadow(struct cart *cart, char *var)
 /* Remove variable from cart, returning count of removed vars. */
 {
 int removed = 0;
 struct hashEl *hel = hashLookup(cart->hash, var);
 while (hel != NULL)
     {
     struct hashEl *nextHel = hashLookupNext(hel);
     freez(&hel->val);
     hashRemove(cart->hash, var);
     removed++;
     hel = nextHel;
     }
 return removed;
 }
 
+static int cartRemoveAndCount(struct cart *cart, char *var)
+/* Remove variable from cart, returning count of removed vars. */
+{
+int removed = cartRemoveAndCountNoShadow(cart, var);
+(void)cartRemoveAndCountNoShadow(cart, cartMultShadowVar(cart, var));
+return removed;
+}
+
 void cartRemove(struct cart *cart, char *var)
 /* Remove variable from cart. */
 {
-struct hashEl *hel = hashLookup(cart->hash, var);
-while (hel != NULL)
-    {
-    struct hashEl *nextHel = hashLookupNext(hel);
-    freez(&hel->val);
-    hashRemove(cart->hash, var);
-    hel = nextHel;
-    }
+(void)cartRemoveAndCount(cart, var);
 }
 
 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 hash *exceptHash = newHash(10);
 struct hashEl *list = NULL, *el;
 char *s;
 
 /* Build up hash of things to exclude. */
 if (except != NULL)
     {
     while ((s = *except++) != NULL)
@@ -879,41 +879,31 @@
     struct slPair *cartVar = slPopHead(&cartVars);
     cartRemove(cart, cartVar->name);
     freeMem(cartVar);
     }
 }
 
 boolean cartVarExists(struct cart *cart, char *var)
 /* Return TRUE if variable is in cart. */
 {
 return hashFindVal(cart->hash, var) != NULL;
 }
 
 boolean cartListVarExists(struct cart *cart, char *var)
 /* Return TRUE if a list variable is in cart (list may still be empty). */
 {
-static int bufSize = 0;
-static char *buf = NULL;
-char *multShadow = cgiMultListShadowPrefix();
-int len = strlen(multShadow) + strlen(var) + 1;
-if (bufSize < len)
-    {
-    buf = needMoreMem(buf, 0, len*2);
-    bufSize = len*2;
-    }
-safef(buf, bufSize, "%s%s", multShadow, var);
-return cartVarExists(cart, buf);
+return cartVarExists(cart, cartMultShadowVar(cart, var));
 }
 
 char *cartString(struct cart *cart, char *var)
 /* Return string valued cart variable. */
 {
 return hashMustFindVal(cart->hash, var);
 }
 
 char *cartOptionalString(struct cart *cart, char *var)
 /* Return string valued cart variable or NULL if it doesn't exist. */
 {
 return hashFindVal(cart->hash, var);
 }
 
 char *cartNonemptyString(struct cart *cart, char *name)