ff61568e30e9390cd4d261aa73b2e9f058601af7
braney
  Mon Feb 27 10:29:37 2023 -0800
merge cart hashes if we're merging sessions

diff --git src/hg/lib/cart.c src/hg/lib/cart.c
index 2f03f02..47e6f55 100644
--- src/hg/lib/cart.c
+++ src/hg/lib/cart.c
@@ -155,56 +155,69 @@
 if (!sqlTableExists(conn, userDbTable()))
     {
     fprintf(stderr, "cartTablesOk failed on %s.%s  pid=%ld\n",
 	    sqlGetDatabase(conn), userDbTable(),  (long)getpid());
     return FALSE;
     }
 if (!sqlTableExists(conn, sessionDbTable()))
     {
     fprintf(stderr, "cartTablesOk failed on %s.%s  pid=%ld\n",
 	    sqlGetDatabase(conn), sessionDbTable(), (long)getpid());
     return FALSE;
     }
 return TRUE;
 }
 
-void cartParseOverHash(struct cart *cart, char *contents)
-/* Parse cgi-style contents into a hash table.  This will *not*
+void cartParseOverHashExt(struct cart *cart, char *contents, boolean merge)
+/* Parse cgi-style contents into a hash table.  If merge is FALSE, this will *not*
  * replace existing members of hash that have same name, so we can
  * support multi-select form inputs (same var name can have multiple
- * values which will be in separate hashEl's). */
+ * values which will be in separate hashEl's). If merge is TRUE, we
+ * replace existing values with new values */
 {
 struct hash *hash = cart->hash;
 char *namePt, *dataPt, *nextNamePt;
 namePt = contents;
 while (namePt != NULL && namePt[0] != 0)
     {
     dataPt = strchr(namePt, '=');
     if (dataPt == NULL)
 	errAbort("Mangled input string %s", namePt);
     *dataPt++ = 0;
     nextNamePt = strchr(dataPt, '&');
     if (nextNamePt == NULL)
 	nextNamePt = strchr(dataPt, ';');	/* Accomodate DAS. */
     if (nextNamePt != NULL)
          *nextNamePt++ = 0;
     cgiDecode(dataPt,dataPt,strlen(dataPt));
+    if (!merge)
         hashAdd(hash, namePt, cloneString(dataPt));
+    else 
+        hashReplace(hash, namePt, cloneString(dataPt));
     namePt = nextNamePt;
     }
 }
 
+void cartParseOverHash(struct cart *cart, char *contents)
+/* Parse cgi-style contents into a hash table.  This will *not*
+ * replace existing members of hash that have same name, so we can
+ * support multi-select form inputs (same var name can have multiple
+ * values which will be in separate hashEl's). */
+{
+cartParseOverHashExt(cart, contents, FALSE);
+}
+
 static boolean looksCorrupted(struct cartDb *cdb)
 /* Test for db corruption by checking format of firstUse field. */
 {
 if (cdb == NULL)
     return FALSE;
 else
     {
     char *words[3];
     int wordCount = 0;
     boolean isCorr = FALSE;
     char *fu = cloneString(cdb->firstUse);
     wordCount = chopByChar(fu, '-', words, ArraySize(words));
     if (wordCount < 3)
 	isCorr = TRUE;
     else
@@ -577,32 +590,36 @@
 if ((row = sqlNextRow(sr)) != NULL)
     {
     boolean shared = atoi(row[0]);
     if (shared ||
 	(userName && sameString(sessionOwner, userName)))
 	{
 	char *sessionVar = cartSessionVarName();
 	char *hgsid = cartSessionId(cart);
     char *sessionTableString = cartOptionalString(cart, hgSessionTableState);
     sessionTableString = cloneString(sessionTableString);
     char *pubSessionsTableString = cartOptionalString(cart, hgPublicSessionsTableState);
     pubSessionsTableString = cloneString(pubSessionsTableString);
 	struct sqlConnection *conn2 = hConnectCentral();
 	sessionTouchLastUse(conn2, encSessionOwner, encSessionName);
         if (!merge)
+            {
             cartRemoveLike(cart, "*");
             cartParseOverHash(cart, row[1]);
+            }
+        else
+            cartParseOverHashExt(cart, row[1], TRUE);
 	cartSetString(cart, sessionVar, hgsid);
 	if (sessionTableString != NULL)
 	    cartSetString(cart, hgSessionTableState, sessionTableString);
 	if (pubSessionsTableString != NULL)
 	    cartSetString(cart, hgPublicSessionsTableState, pubSessionsTableString);
 	if (oldVars)
 	    hashEmpty(oldVars);
 	/* Overload settings explicitly passed in via CGI (except for the
 	 * command that sent us here): */
 	loadCgiOverHash(cart, oldVars);
 	if (isNotEmpty(actionVar))
 	    cartRemove(cart, actionVar);
 	hDisconnectCentral(&conn2);
 	}
     else