From 0409391c7d08016de3f1ea8b1a74d5856669ffaa Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 17 Jul 2008 11:27:55 +0000 Subject: 25272: add zshaddhistory hook --- ChangeLog | 7 +++++ Doc/Zsh/func.yo | 28 +++++++++++++++++++ Functions/Misc/add-zsh-hook | 20 +++++++------ Src/Zle/zle_main.c | 2 +- Src/builtin.c | 4 +-- Src/exec.c | 11 ++++++++ Src/hist.c | 68 ++++++++++++++++++++++++++------------------- Src/init.c | 2 +- Src/utils.c | 24 +++++++++++----- 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 + + * 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 * 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; -- cgit 1.4.1