Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: zshaddhistoryhook (was Re: history-beginning-local function)



On Mon, 14 Jul 2008 18:42:20 +0100
Peter Stephenson <pws@xxxxxxx> wrote:
> The main problem is that you have to create and update the local history
> file yourself since commands aren't saved to it.  This is tricky since
> it happens in the main shell after zle returns.  We'd need a hook
> function of some sort to be able to do the effect of fc -p around the
> point the history is saved, unless there's a smarter way of doing it.

(Switched to zsh-workers.)

I think I've found quite a neat way of doing this, which kills another
bird (that's been perching on a nearby rooftop for a long time
occasionally looking hungrily into the fishpond) with the same stone and
very little new, er, fish.  However, although this does seem to work,
I'm not 100% sure this doesn't have odd effects in the history.  Wayne
may have some notion.

This adds the hook zshaddhistory at the point an interactive history
line is saved.  It has two bits of magic.

Firstly, you can return non-zero from one of the hook functions (i.e the
zshaddhistory function itself or one of the functions listed
in zshaddhistory_functions) to ignore the line in exactly the same way
as the existing mechanism for ignoring history entries.  This kills the
other bird.

Secondly, if you call "fc -p" within the function it switches the
history context for the current line handling, and the calling function
will obligingly pop the context back the way it was before.  So we can
nicely reuse this mechanism to do all the dirty work for us.  This sets
things up so we can handle history-beginning-local (no code for that
yet).  Note I haven't tried this without INC_APPEND_HISTORY set.

Index: Doc/Zsh/func.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/func.yo,v
retrieving revision 1.18
diff -u -r1.18 func.yo
--- Doc/Zsh/func.yo	30 Jun 2008 10:37:13 -0000	1.18
+++ Doc/Zsh/func.yo	14 Jul 2008 21:21:03 -0000
@@ -241,6 +241,34 @@
 elided); the third argument contains the full text that is being
 executed.
 )
+findex(zshaddhistory)
+vindex(zshaddhistory_functions)
+item(tt(zshaddhistory))(
+cindex(history, hook when line is saved)
+Executed when a history line has been read interactively, but
+before it is executed.  The sole argument is the complete history
+line (so that any terminating newline will still be present).
+
+If any of the hook functions return a non-zero value the history
+line will not be saved, although it lingers in the history until the
+next line is executed allow you to reuse or edit it immediately.
+
+A hook function may call `tt(fc -p) var(...)' to switch the history
+context so that the history is saved in a different file from the
+that in the global tt(HISTFILE) parameter.  This is handled specially:
+the history context is automatically restored after the processing
+of the history line is finished.
+
+The following example function first adds the history line to the normal
+history with the newline stripped,  which is usually the correct behaviour.
+Then it switches the history context so that the line will
+be written to a history file in the current directory.
+
+example(zshaddhistory() {
+  print -s ${1%%$'\n'}
+  fc -p .zsh_local_history
+})
+)
 findex(zshexit)
 vindex(zshexit_functions)
 item(tt(zshexit))(
Index: Src/builtin.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/builtin.c,v
retrieving revision 1.196
diff -u -r1.196 builtin.c
--- Src/builtin.c	10 Jun 2008 08:50:51 -0000	1.196
+++ Src/builtin.c	14 Jul 2008 21:21:05 -0000
@@ -1139,7 +1139,7 @@
     fflush(stdout);
     fflush(stderr);
     if (!quiet)
-	callhookfunc("chpwd", NULL, 1);
+	callhookfunc("chpwd", NULL, 1, NULL);
 
     dirstacksize = getiparam("DIRSTACKSIZE");
     /* handle directory stack sizes out of range */
@@ -4578,7 +4578,7 @@
     lastval = val;
     if (sigtrapped[SIGEXIT])
 	dotrap(SIGEXIT);
-    callhookfunc("zshexit", NULL, 1);
+    callhookfunc("zshexit", NULL, 1, NULL);
     runhookdef(EXITHOOK, NULL);
     if (opts[MONITOR] && interact && (SHTTY != -1)) {
        release_pgrp();
Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.133
diff -u -r1.133 exec.c
--- Src/exec.c	30 Jun 2008 10:37:14 -0000	1.133
+++ Src/exec.c	14 Jul 2008 21:21:06 -0000
@@ -4093,6 +4093,16 @@
 /*
  * execute a shell function
  *
+ * name is the name of the function
+ *
+ * prog is the code to execute
+ *
+ * doshargs, if set, are parameters to pass to the function,
+ * in which the first element is the function name (even if
+ * FUNCTIONARGZERO is set as this is handled inside this function).
+ *
+ * flags are a set of the PM_ flags associated with the function.
+ *
  * If noreturnval is nonzero, then reset the current return
  * value (lastval) to its value before the shell function
  * was executed.  However, in any case return the status value
@@ -4160,6 +4170,7 @@
 	    oargv0 = argzero;
 	    argzero = ztrdup(getdata(node));
 	}
+	/* first node contains name regardless of option */
 	node = node->next;
 	for (; node; node = node->next, x++)
 	    *x = ztrdup(getdata(node));
Index: Src/hist.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/hist.c,v
retrieving revision 1.79
diff -u -r1.79 hist.c
--- Src/hist.c	5 May 2008 14:29:03 -0000	1.79
+++ Src/hist.c	14 Jul 2008 21:21:07 -0000
@@ -130,8 +130,7 @@
 
 /* Bits of histactive variable */
 #define HA_ACTIVE	(1<<0)	/* History mechanism is active */
-#define HA_NOSTORE	(1<<1)	/* Don't store the line when finished */
-#define HA_NOINC	(1<<2)	/* Don't store, curhist not incremented */
+#define HA_NOINC	(1<<1)	/* Don't store, curhist not incremented */
 
 /* Array of word beginnings and endings in current history line. */
 
@@ -180,6 +179,30 @@
  
 static zlong defev;
 
+/* Remember the last line in the history file so we can find it again. */
+static struct histfile_stats {
+    char *text;
+    time_t stim, mtim;
+    off_t fpos, fsiz;
+    zlong next_write_ev;
+} lasthist;
+
+static struct histsave {
+    struct histfile_stats lasthist;
+    char *histfile;
+    HashTable histtab;
+    Histent hist_ring;
+    zlong curhist;
+    zlong histlinect;
+    zlong histsiz;
+    zlong savehistsiz;
+    int locallevel;
+} *histsave_stack;
+static int histsave_stack_size = 0;
+static int histsave_stack_pos = 0;
+
+static zlong histfile_linect;
+
 /* add a character to the current history word */
 
 static void
@@ -1082,7 +1105,8 @@
 mod_export int
 hend(Eprog prog)
 {
-    int flag, save = 1;
+    LinkList hookargs = newlinklist();
+    int flag, save = 1, hookret, stack_pos = histsave_stack_pos;
     char *hf;
 
     DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline,
@@ -1092,7 +1116,7 @@
 	settyinfo(&shttyinfo);
     if (!(histactive & HA_NOINC))
 	unlinkcurline();
-    if (histactive & (HA_NOSTORE|HA_NOINC)) {
+    if (histactive & HA_NOINC) {
 	zfree(chline, hlinesz);
 	zfree(chwords, chwordlen*sizeof(short));
 	chline = NULL;
@@ -1103,6 +1127,10 @@
     if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS)
      && (hist_ignore_all_dups = isset(HISTIGNOREALLDUPS)) != 0)
 	histremovedups();
+    
+    addlinknode(hookargs, "zshaddhistory");
+    addlinknode(hookargs, chline);
+    callhookfunc("zshaddhistory", hookargs, 1, &hookret);
     /* For history sharing, lock history file once for both read and write */
     hf = getsparam("HISTFILE");
     if (isset(SHAREHISTORY) && lockhistfile(hf, 0)) {
@@ -1123,7 +1151,7 @@
 	}
 	if (chwordpos <= 2)
 	    save = 0;
-	else if (should_ignore_line(prog))
+	else if (hookret || should_ignore_line(prog))
 	    save = -1;
     }
     if (flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) {
@@ -1203,6 +1231,12 @@
     if (isset(SHAREHISTORY)? histfileIsLocked() : isset(INCAPPENDHISTORY))
 	savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
     unlockhistfile(hf); /* It's OK to call this even if we aren't locked */
+    /*
+     * No good reason for the user to push the history more than once, but
+     * it's easy to be tidy...
+     */
+    while (histsave_stack_pos > stack_pos)
+	pophiststack();
     unqueue_signals();
     return !(flag & HISTFLAG_NOEXEC || errflag);
 }
@@ -1942,30 +1976,6 @@
     }
 }
 
-/* Remember the last line in the history file so we can find it again. */
-static struct histfile_stats {
-    char *text;
-    time_t stim, mtim;
-    off_t fpos, fsiz;
-    zlong next_write_ev;
-} lasthist;
-
-static struct histsave {
-    struct histfile_stats lasthist;
-    char *histfile;
-    HashTable histtab;
-    Histent hist_ring;
-    zlong curhist;
-    zlong histlinect;
-    zlong histsiz;
-    zlong savehistsiz;
-    int locallevel;
-} *histsave_stack;
-static int histsave_stack_size = 0;
-static int histsave_stack_pos = 0;
-
-static zlong histfile_linect;
-
 static int
 readhistline(int start, char **bufp, int *bufsiz, FILE *in)
 {
Index: Src/init.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/init.c,v
retrieving revision 1.85
diff -u -r1.85 init.c
--- Src/init.c	12 May 2008 13:50:42 -0000	1.85
+++ Src/init.c	14 Jul 2008 21:21:07 -0000
@@ -175,7 +175,7 @@
 		addlinknode(args, dupstring(getjobtext(prog, NULL)));
 		addlinknode(args, cmdstr = getpermtext(prog, NULL));
 
-		callhookfunc("preexec", args, 1);
+		callhookfunc("preexec", args, 1, NULL);
 
 		/* The only permanent storage is from getpermtext() */
 		zsfree(cmdstr);
Index: Src/utils.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/utils.c,v
retrieving revision 1.195
diff -u -r1.195 utils.c
--- Src/utils.c	1 Jul 2008 08:36:02 -0000	1.195
+++ Src/utils.c	14 Jul 2008 21:21:09 -0000
@@ -1117,25 +1117,31 @@
 
 /*
  * Call a function given by "name" with optional arguments
- * "lnklist".  If "arrayp" is not zero, we also look through
+ * "lnklist".  If these are present the first argument is the function name.
+ *
+ * If "arrayp" is not zero, we also look through
  * the array "name"_functions and execute functions found there.
+ *
+ * If "retval" is not NULL, the return value of the first hook function to
+ * return non-zero is stored in *"retval".  The return value is not otherwise
+ * available as the calling context is restored.
  */
 
 /**/
 mod_export int
-callhookfunc(char *name, LinkList lnklst, int arrayp)
+callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
 {
     Eprog prog;
 	/*
 	 * Save stopmsg, since user doesn't get a chance to respond
 	 * to a list of jobs generated in a hook.
 	 */
-    int osc = sfcontext, osm = stopmsg, stat = 1;
+    int osc = sfcontext, osm = stopmsg, stat = 1, ret = 0;
 
     sfcontext = SFC_HOOK;
 
     if ((prog = getshfunc(name)) != &dummy_eprog) {
-	doshfunc(name, prog, lnklst, 0, 1);
+	ret = doshfunc(name, prog, lnklst, 0, 1);
 	stat = 0;
     }
 
@@ -1151,7 +1157,9 @@
 	if ((arrptr = getaparam(arrnam))) {
 	    for (; *arrptr; arrptr++) {
 		if ((prog = getshfunc(*arrptr)) != &dummy_eprog) {
-		    doshfunc(arrnam, prog, lnklst, 0, 1);
+		    int newret = doshfunc(arrnam, prog, lnklst, 0, 1);
+		    if (!ret)
+			ret = newret;
 		    stat = 0;
 		}
 	    }
@@ -1161,6 +1169,8 @@
     sfcontext = osc;
     stopmsg = osm;
 
+    if (retval)
+	*retval = ret;
     return stat;
 }
 
@@ -1200,7 +1210,7 @@
 
     /* If a shell function named "precmd" exists, *
      * then execute it.                           */
-    callhookfunc("precmd", NULL, 1);
+    callhookfunc("precmd", NULL, 1, NULL);
     if (errflag)
 	return;
 
@@ -1208,7 +1218,7 @@
      * "periodic" exists, 3) it's been greater than PERIOD since we *
      * executed any such hook, then execute it now.                 */
     if (period && (time(NULL) > lastperiodic + period) &&
-	!callhookfunc("periodic", NULL, 1))
+	!callhookfunc("periodic", NULL, 1, NULL))
 	lastperiodic = time(NULL);
     if (errflag)
 	return;
Index: Src/Zle/zle_main.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_main.c,v
retrieving revision 1.112
diff -u -r1.112 zle_main.c
--- Src/Zle/zle_main.c	12 May 2008 13:50:43 -0000	1.112
+++ Src/Zle/zle_main.c	14 Jul 2008 21:21:10 -0000
@@ -736,7 +736,7 @@
 # endif
 
 
-			callhookfunc(lwatch_funcs[i], funcargs, 0);
+			callhookfunc(lwatch_funcs[i], funcargs, 0, NULL);
 			if (errflag) {
 			    /* No sensible way of handling errors here */
 			    errflag = 0;


-- 
Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/



Messages sorted by: Reverse Date, Date, Thread, Author