about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/hist.c151
1 files changed, 135 insertions, 16 deletions
diff --git a/Src/hist.c b/Src/hist.c
index e0be6e030..7f8bc7cc9 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -1791,13 +1791,26 @@ resizehistents(void)
 }
 
 /* Remember the last line in the history file so we can find it again. */
-static struct {
+static struct histfile_stats {
     char *text;
     time_t stim, mtim;
     off_t fpos, fsiz;
     int next_write_ev;
 } lasthist;
 
+static struct histsave {
+    struct histfile_stats lasthist;
+    char *histfile;
+    HashTable histtab;
+    Histent hist_ring;
+    int curhist;
+    int histlinect;
+    int histsiz;
+    int savehistsiz;
+} *histsave_stack;
+static int histsave_stack_size = 0;
+static int histsave_stack_pos = 0;
+
 static int histfile_linect;
 
 static int
@@ -2078,31 +2091,20 @@ savehistfile(char *fn, int err, int writeflags)
 	fclose(out);
 
 	if ((writeflags & (HFILE_SKIPOLD | HFILE_FAST)) == HFILE_SKIPOLD) {
-	    HashTable remember_histtab = histtab;
-	    Histent remember_hist_ring = hist_ring;
-	    int remember_histlinect = histlinect;
-	    int remember_curhist = curhist;
-	    int remember_histsiz = histsiz;
 	    int remember_histactive = histactive;
 
-	    hist_ring = NULL;
-	    curhist = histlinect = 0;
-	    histsiz = savehistsiz;
+	    /* Zeroing histactive avoids unnecessary munging of curline. */
 	    histactive = 0;
-	    createhisttable(); /* sets histtab */
+	    /* The NULL leaves HISTFILE alone, preserving fn's value. */
+	    pushhiststack(NULL, savehistsiz, savehistsiz);
 
 	    hist_ignore_all_dups |= isset(HISTSAVENODUPS);
 	    readhistfile(fn, err, 0);
 	    hist_ignore_all_dups = isset(HISTIGNOREALLDUPS);
 	    if (histlinect)
 		savehistfile(fn, err, 0);
-	    deletehashtable(histtab);
 
-	    curhist = remember_curhist;
-	    histlinect = remember_histlinect;
-	    hist_ring = remember_hist_ring;
-	    histtab = remember_histtab;
-	    histsiz = remember_histsiz;
+	    pophiststack();
 	    histactive = remember_histactive;
 	}
     } else if (err)
@@ -2331,3 +2333,120 @@ bufferwords(LinkList list, char *buf, int *index)
 
     return list;
 }
+
+/* Move the current history list out of the way and prepare a fresh history
+ * list using hf for HISTFILE, hs for HISTSIZE, and shs for SAVEHIST.  If
+ * the hf value is an empty string, HISTFILE will be unset from the new
+ * environment; if it is NULL, HISTFILE will not be changed, not even by the
+ * pop function (this functionality is used internally to rewrite the current
+ * history file without affecting pointers into the environment).
+ */
+
+/**/
+int
+pushhiststack(char *hf, int hs, int shs)
+{
+    struct histsave *h;
+    int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
+
+    if (histsave_stack_pos == histsave_stack_size) {
+	histsave_stack_size += 5;
+	histsave_stack = zrealloc(histsave_stack,
+			    histsave_stack_size * sizeof (struct histsave));
+    }
+
+    if (curline_in_ring)
+	unlinkcurline();
+
+    h = &histsave_stack[histsave_stack_pos++];
+
+    h->lasthist = lasthist;
+    if (hf) {
+	if ((h->histfile = getsparam("HISTFILE")) != NULL && *h->histfile)
+	    h->histfile = ztrdup(h->histfile);
+	else
+	    h->histfile = "";
+    } else
+	h->histfile = NULL;
+    h->histtab = histtab;
+    h->hist_ring = hist_ring;
+    h->curhist = curhist;
+    h->histlinect = histlinect;
+    h->histsiz = histsiz;
+    h->savehistsiz = savehistsiz;
+
+    memset(&lasthist, 0, sizeof lasthist);
+    if (hf) {
+	if (*hf)
+	    setsparam("HISTFILE", ztrdup(hf));
+	else
+	    unsetparam("HISTFILE");
+    }
+    hist_ring = NULL;
+    curhist = histlinect = 0;
+    histsiz = hs;
+    savehistsiz = shs;
+    inithist(); /* sets histtab */
+
+    if (curline_in_ring)
+	linkcurline();
+
+    return histsave_stack_pos;
+}
+
+
+/**/
+int
+pophiststack(void)
+{
+    struct histsave *h;
+    int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
+
+    if (histsave_stack_pos == 0)
+	return 0;
+
+    if (curline_in_ring)
+	unlinkcurline();
+
+    deletehashtable(histtab);
+    zsfree(lasthist.text);
+
+    h = &histsave_stack[--histsave_stack_pos];
+
+    lasthist = h->lasthist;
+    if (h->histfile) {
+	if (*h->histfile)
+	    setsparam("HISTFILE", h->histfile);
+	else
+	    unsetparam("HISTFILE");
+    }
+    histtab = h->histtab;
+    hist_ring = h->hist_ring;
+    curhist = h->curhist;
+    histlinect = h->histlinect;
+    histsiz = h->histsiz;
+    savehistsiz = h->savehistsiz;
+
+    if (curline_in_ring)
+	linkcurline();
+
+    return histsave_stack_pos + 1;
+}
+
+/**/
+int
+saveandpophiststack(int down_through)
+{
+    if (down_through < 0)
+	down_through += histsave_stack_pos + 1;
+    if (down_through <= 0)
+	down_through = 1;
+    if (histsave_stack_pos < down_through)
+	return 0;
+    do {
+	if (!nohistsave)
+	    savehistfile(NULL, 1, HFILE_USE_OPTIONS);
+	pophiststack();
+    } while (histsave_stack_pos >= down_through);
+    return 1;
+}