4ccf33da61239996702db7af0d93018806b2d38e
angie
  Fri Feb 22 14:04:22 2019 -0800
lastPosition is set by cartHtmlShell* -- but not by other cart openers e.g. cartAndCookie or cartEmptyShell.  CGIs that use the latter must set lastPosition after determining db and position.  refs #22945

diff --git src/hg/lib/cart.c src/hg/lib/cart.c
index 9179ddd..a270717 100644
--- src/hg/lib/cart.c
+++ src/hg/lib/cart.c
@@ -2469,52 +2469,60 @@
     char *themeKey = catTwoStrings("browser.theme.", cartTheme);
     styleFile = cfgOption(themeKey);
     freeMem(themeKey);
     if (isEmpty(styleFile))
         return;
 
     char * link = webTimeStampedLinkToResourceOnFirstCall(styleFile, TRUE); // resource file link wrapped in html
     if (link != NULL)
         {
         htmlSetStyleTheme(link); // for htmshell.c, used by hgTracks
         webSetStyle(link);       // for web.c, used by hgc
         }
     }
 }
 
+void cartSetLastPosition(struct cart *cart, char *position, struct hash *oldVars)
+/* If position and oldVars are non-NULL, and oldVars' position is different, add it to the cart
+ * as lastPosition.  This is called by cartHtmlShell{,WithHead} but not other cart openers;
+ * it should be called after cartGetPosition or equivalent. */
+{
+if (position != NULL && oldVars != NULL)
+    {
+    struct hashEl *oldPos = hashLookup(oldVars, positionCgiName);
+    if (oldPos != NULL && differentString(position, oldPos->val))
+        cartSetString(cart, "lastPosition", oldPos->val);
+    }
+}
+
 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. */
 {
 struct cart *cart;
 char *db, *org, *pos;
 char titlePlus[2048];
 pushWarnHandler(cartEarlyWarningHandler);
 cart = cartAndCookie(cookieName, exclude, oldVars);
 getDbAndGenome(cart, &db, &org, oldVars);
 pos = cartGetPosition(cart, db, NULL);
 pos = addCommasToPos(db, stripCommas(pos));
-if (pos != NULL && oldVars != NULL)
-    {
-    struct hashEl *oldPos = hashLookup(oldVars, positionCgiName);
-    if (oldPos != NULL && differentString(pos, oldPos->val))
-        cartSetString(cart, "lastPosition", oldPos->val);
-    }
+cartSetLastPosition(cart, pos, oldVars);
 safef(titlePlus, sizeof(titlePlus), "%s %s %s %s", 
                     org ? trackHubSkipHubName(org) : "", db ? db : "",  pos ? pos : "", title);
 popWarnHandler();
 setThemeFromCart(cart);
 htmStartWithHead(stdout, head, titlePlus);
 cartWarnCatcher(doMiddle, cart, htmlVaWarn);
 cartCheckout(&cart);
 cartFooter();
 }
 
 static void cartEmptyShellMaybeContent(void (*doMiddle)(struct cart *cart), char *cookieName,
                                        char **exclude, struct hash *oldVars, boolean doContentType)
 /* Get cart and cookies and set up error handling.
  * If doContentType, print out Content-type:text/html
  * but don't start writing any html yet.