about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Doc/Zsh/func.yo28
-rw-r--r--Functions/Misc/add-zsh-hook20
-rw-r--r--Src/Zle/zle_main.c2
-rw-r--r--Src/builtin.c4
-rw-r--r--Src/exec.c11
-rw-r--r--Src/hist.c68
-rw-r--r--Src/init.c2
-rw-r--r--Src/utils.c24
9 files changed, 117 insertions, 49 deletions
diff --git a/ChangeLog b/ChangeLog
index 5d41e6f07..d7e04e206 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2008-07-17  Peter Stephenson  <pws@csr.com>
+
+	* 25279: Doc/Zsh/func.yo, Functions/Misc/add-zsh-hook,
+	Src/builtin.c, Src/exec.c, Src/hist.c, Src/init.c, Src/utils.c,
+	Src/Zle/zle_main.c: add zshaddhistory hook to allow manipulations
+	when history line is saved.
+
 2008-07-15  Peter Stephenson  <pws@csr.com>
 
 	* users/13036: Src/utils.c: "." is a valid character in
diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo
index 65bcd9abc..0d215dde3 100644
--- a/Doc/Zsh/func.yo
+++ b/Doc/Zsh/func.yo
@@ -241,6 +241,34 @@ size-limited version of the command (with things like function bodies
 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 -sr -- ${1%%$'\n'}
+  fc -p .zsh_local_history
+})
+)
 findex(zshexit)
 vindex(zshexit_functions)
 item(tt(zshexit))(
diff --git a/Functions/Misc/add-zsh-hook b/Functions/Misc/add-zsh-hook
index d349ac635..f534980f5 100644
--- a/Functions/Misc/add-zsh-hook
+++ b/Functions/Misc/add-zsh-hook
@@ -1,27 +1,27 @@
 # Add to HOOK the given FUNCTION.
-# HOOK is one of chpwd, precmd, preexec, periodic, zshexit (the
-# _functions subscript is not required).
+# HOOK is one of chpwd, precmd, preexec, periodic, zshaddhistory,
+# zshexit (the _functions subscript is not required).
 #
 # With -d, remove the function from the hook instead; delete the hook
 # variable if it is empty.
-# 
+#
 # -D behaves like -d, but pattern characters are active in the
 # function name, so any matching function will be deleted from the hook.
 #
 # Without -d, the FUNCTION is marked for autoload; -U is passed down to
-# autoload if that is given.  (This is harmless if the function is actually
-# defined inline.)
+# autoload if that is given, as are -z and -k.  (This is harmless if the
+# function is actually defined inline.)
 
 emulate -L zsh
 
 local -a hooktypes
-hooktypes=(chpwd precmd preexec periodic zshexit)
+hooktypes=(chpwd precmd preexec periodic zshaddhistory zshexit)
 
 local opt
 local -a autoopts
 integer del
 
-while getopts "dDU" opt; do
+while getopts "dDUzk" opt; do
   case $opt in
     (d)
     del=1
@@ -31,7 +31,7 @@ while getopts "dDU" opt; do
     del=2
     ;;
 
-    (U)
+    ([Uzk])
     autoopts+=(-$opt)
     ;;
 
@@ -60,7 +60,9 @@ if (( del )); then
     fi
     # unset if no remaining entries --- this can give better
     # performance in some cases
-    (( ${(P)#hook} )) || unset $hook
+    if (( ! ${(P)#hook} )); then
+      unset $hook
+    fi
   fi
 else
   if (( ${(P)+hook} )); then
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 7fb6878da..f564b84ee 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -736,7 +736,7 @@ raw_getbyte(long do_keytmout, char *cptr)
 # 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;
diff --git a/Src/builtin.c b/Src/builtin.c
index af2c24fd6..c7ed283a9 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1139,7 +1139,7 @@ cd_new_pwd(int func, LinkNode dir, int quiet)
     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 @@ zexit(int val, int from_where)
     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();
diff --git a/Src/exec.c b/Src/exec.c
index 7ce657032..437a45c37 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -4093,6 +4093,16 @@ loadautofn(Shfunc shf, int fksh, int autol)
 /*
  * 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 @@ doshfunc(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval)
 	    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));
diff --git a/Src/hist.c b/Src/hist.c
index a1bc73636..f856eedff 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -130,8 +130,7 @@ mod_export int hist_skip_flags;
 
 /* 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 @@ int hlinesz;
  
 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 @@ should_ignore_line(Eprog prog)
 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 @@ hend(Eprog prog)
 	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 @@ hend(Eprog prog)
     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 @@ hend(Eprog prog)
 	}
 	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 @@ hend(Eprog prog)
     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 @@ resizehistents(void)
     }
 }
 
-/* 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)
 {
diff --git a/Src/init.c b/Src/init.c
index e3c89008a..7a64df17e 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -175,7 +175,7 @@ loop(int toplevel, int justonce)
 		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);
diff --git a/Src/utils.c b/Src/utils.c
index 154ca8627..ae0d43876 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -1117,25 +1117,31 @@ time_t lastwatch;
 
 /*
  * 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 @@ callhookfunc(char *name, LinkList lnklst, int arrayp)
 	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 @@ callhookfunc(char *name, LinkList lnklst, int arrayp)
     sfcontext = osc;
     stopmsg = osm;
 
+    if (retval)
+	*retval = ret;
     return stat;
 }
 
@@ -1200,7 +1210,7 @@ preprompt(void)
 
     /* 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 @@ preprompt(void)
      * "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;