From bb83575d84ddfc362ba8b421d6efe5d16c2a15dc Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Tue, 24 Dec 2024 14:21:34 +0000 Subject: [PATCH] [v1.1] emacs ^X^E, vi v: avoid executing if command line not saved @pghvlaans reports: > According to 'The New KornShell', the full editor history editing > commands are only meant to execute command lines that have had > saved changes: > > "The command is the ksh line that you were editing with the vi > built-in editor if you omit n, or command n in the history file. > When you exit the vi program, ksh displays and executes the > command if you have changed it. Version: With some early releases > of the 11/16/88 version of ksh, the file was executed even if you > had not changed it." (Bolsky & Korn, pg. 116) > > There is no other evidence that this functionality was ever part > of release ksh93, however. Neither bash nor any existing open > source ksh88 derivative behaves in this manner. Nevertheless, > some distributions of ksh88 (such as the one shipped with > UnixWare 7) did attempt to avoid running unchanged command lines > using timestamps. POSIX does not allow this behaviour for the fc command, but says nothing about the emacs ^X^E and vi v editor commands. I have concluded from the resulting discussion is that it is best to make the timestamp check on the temporary file subject to a new option to the fc/hist command, let's say -E. The emacs and vi commands can then invoke fc/hist with that new option. src/cmd/ksh93/bltins/hist.c, src/cmd/ksh93/data/builtins.c: - Add new -E option to fc/hist, remembered by the checktime flag. - When invoking the editor and checktime is set, use the libast tv(3) library to get the temporary file's timestamp before and after editing (with nanosecond granularity where available), and flag up an error if the timestamp has not changed. This has the effect of not executing the command line if a user quits the editor without saving. src/cmd/ksh93/data/msg.c: - Change e_runvi, the command used by ed_fulledit() (called by ^X^E in emacs and v in vi) to invoke a fullscreen editor, to invoke 'fc' with the new -E option. - 'fc' is the same as 'hist' but is shorter and standard. :) - Add a 'command' prefix to bypass a possible fc shell function. src/cmd/ksh93/sh.1: - Document the changes. - Remove mention of the specific shell command executed by the emacs and vi editor commands; that's an internal implementation detail. Resolves: https://github.com/ksh93/ksh/pull/748 --- ANNOUNCE | 17 +++++++++++++ NEWS | 10 ++++++++ src/cmd/ksh93/Mamfile | 1 + src/cmd/ksh93/bltins/hist.c | 21 +++++++++++++++- src/cmd/ksh93/data/builtins.c | 4 ++- src/cmd/ksh93/data/msg.c | 2 +- src/cmd/ksh93/include/version.h | 2 +- src/cmd/ksh93/sh.1 | 43 ++++++++++++++++++++++----------- 8 files changed, 82 insertions(+), 18 deletions(-) diff --git a/ANNOUNCE b/ANNOUNCE index 58a4dd93dd7a..161e13291990 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -17,6 +17,11 @@ New command line editor features: - In the emacs line editor, pressing ^U (kill) twice no longer causes further ^U presses to start a new line (this was a feature for paper terminals). +- The ^X^E (emacs mode) and v (vi mode) commands will now only execute the + command line that was loaded into the editor if the temporary file is + saved before exiting the editor. This removes the need to empty the file + and save just to cancel execution. + New shell language features: - The appending redirection operator &>>FILE is now available. It is a @@ -44,6 +49,18 @@ New features in built-in commands: command given that was not found on PATH, and return a non-zero exit status if any of the commands given were not found. +- The 'fc'/'hist' command has a new -E option. If given and a full-screen + editor is launched, the command line that was loaded into the editor will + only be executed if the temporary file is saved before exiting the editor. + This removes the need to empty the file and save just to cancel execution. + People who want the new behaviour as a default for the fc/hist command + where available (i.e., for ksh 93u+m/1.1 and later) can add the following + to their ~/.kshrc or other profile script: + case ${KSH_VERSION-} in + *93u+m/1.0.*) ;; + *93u+m/*) alias fc='fc -E' hist='fc -E' ;; + esac + New features in shell options: - A new --arrowkeysearch option, on by default, causes the up and down arrow diff --git a/NEWS b/NEWS index 47f64e55e9c6..5031c8164b5d 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,16 @@ This documents significant changes in the dev branch of ksh 93u+m. For full details, see the git log at: https://github.com/ksh93/ksh Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library. +2024-12-24: + +- [v1.1] The fc/hist command has a new -E option. If this option is given, + the command line that was loaded into the editor will only be executed if + the temp file is saved before exiting the editor. + +- [v1.1] The ^X^E (emacs mode) and v (vi mode) commands will now only + execute the command line that was loaded into the full-screen editor if + the temp file is saved before exiting the editor. + 2024-12-22: - In the emacs/gmacs line editor, the ESC [ macro feature, which is diff --git a/src/cmd/ksh93/Mamfile b/src/cmd/ksh93/Mamfile index a0bff1cf5ae2..6a700b16c5bd 100644 --- a/src/cmd/ksh93/Mamfile +++ b/src/cmd/ksh93/Mamfile @@ -406,6 +406,7 @@ make install virtual prev include/io.h prev include/variables.h prev %{INCLUDE_AST}/error.h + prev %{INCLUDE_AST}/tv.h prev %{INCLUDE_AST}/ls.h prev include/defs.h prev shopt.h diff --git a/src/cmd/ksh93/bltins/hist.c b/src/cmd/ksh93/bltins/hist.c index 8a69ce12c5b5..fb8f439a7e5b 100644 --- a/src/cmd/ksh93/bltins/hist.c +++ b/src/cmd/ksh93/bltins/hist.c @@ -18,6 +18,7 @@ #include "shopt.h" #include "defs.h" #include +#include #include #include "variables.h" #include "io.h" @@ -52,6 +53,7 @@ int b_hist(int argc,char *argv[], Shbltin_t *context) #if SHOPT_HISTEXPAND int pflag = 0; #endif + int checktime = 0; Histloc_t location; NOT_USED(argc); NOT_USED(context); @@ -63,6 +65,9 @@ int b_hist(int argc,char *argv[], Shbltin_t *context) hp = sh.hist_ptr; while((flag = optget(argv,sh_opthist))) switch(flag) { + case 'E': + checktime = 1; + break; case 'e': edit = opt_info.arg; break; @@ -246,11 +251,25 @@ int b_hist(int argc,char *argv[], Shbltin_t *context) } if(*arg != '-') { + int e = 0; /* error flag */ + struct stat statb; + Tv_t before, after; char *com[3]; com[0] = arg; com[1] = fname; com[2] = 0; - error_info.errors = sh_eval(sh_sfeval(com),0); + if (checktime && !(e = stat(fname,&statb)<0)) + tvgetmtime(&before,&statb); + /* invoke the editor */ + if (!e) + e = sh_eval(sh_sfeval(com),0); + if (checktime && !e && !(e = stat(fname,&statb)<0)) + { + /* if the file's timestamp hasn't changed, treat this as an error */ + tvgetmtime(&after,&statb); + e = before.tv_sec==after.tv_sec && before.tv_nsec==after.tv_nsec; + } + error_info.errors = e; } fdo = sh_chkopen(fname); unlink(fname); diff --git a/src/cmd/ksh93/data/builtins.c b/src/cmd/ksh93/data/builtins.c index 71ea688b81b7..ff78aa2caa17 100644 --- a/src/cmd/ksh93/data/builtins.c +++ b/src/cmd/ksh93/data/builtins.c @@ -1017,7 +1017,7 @@ const char sh_opthash[] = #if !SHOPT_SCRIPTONLY const char sh_opthist[] = -"[-1cn?\n@(#)$Id: hist (AT&T Research) 2000-04-02 $\n]" +"[-1cn?\n@(#)$Id: hist (ksh 93u+m) 2024-12-23 $\n]" "[--catalog?" SH_DICT "]" "[+NAME?\f?\f - process command history list]" "[+DESCRIPTION?\b\f?\f\b lists, edits, or re-executes, commands " @@ -1055,6 +1055,8 @@ const char sh_opthist[] = "[+?If no editor is specified, then the editor specified by the \bHISTEDIT\b " "variable will be used if set, or the \bFCEDIT\b variable will be " "used if set, otherwise, \bed\b will be used.]" +"[E?Only execute the edited command line if the file is saved before " + "exiting the editor.]" "[e]:[editor?\aeditor\a specifies the editor to use to edit the history " "command. A value of \b-\b for \aeditor\a is equivalent to " "specifying the \b-s\b option.]" diff --git a/src/cmd/ksh93/data/msg.c b/src/cmd/ksh93/data/msg.c index 3d193e20460c..75dfa39166a1 100644 --- a/src/cmd/ksh93/data/msg.c +++ b/src/cmd/ksh93/data/msg.c @@ -39,7 +39,7 @@ /* error messages */ const char e_timewarn[] = "\r\n\ashell will timeout in 60 seconds due to inactivity"; -const char e_runvi[] = "\\hist -e \"${VISUAL:-${EDITOR:-vi}}\" "; +const char e_runvi[] = "\\command fc -Ee\"${VISUAL:-${EDITOR:-vi}}\" "; const char e_timeout[] = "timed out waiting for input"; const char e_mailmsg[] = "you have mail in $_"; const char e_query[] = "no query process"; diff --git a/src/cmd/ksh93/include/version.h b/src/cmd/ksh93/include/version.h index 9ab358a64860..660dc1f8b770 100644 --- a/src/cmd/ksh93/include/version.h +++ b/src/cmd/ksh93/include/version.h @@ -18,7 +18,7 @@ #include #include "git.h" -#define SH_RELEASE_DATE "2024-12-22" /* must be in this format for $((.sh.version)) */ +#define SH_RELEASE_DATE "2024-12-24" /* must be in this format for $((.sh.version)) */ /* * This comment keeps SH_RELEASE_DATE a few lines away from SH_RELEASE_SVER to avoid * merge conflicts when cherry-picking dev branch commits onto a release branch. diff --git a/src/cmd/ksh93/sh.1 b/src/cmd/ksh93/sh.1 index 8c8110a064fd..b2161945c7c7 100644 --- a/src/cmd/ksh93/sh.1 +++ b/src/cmd/ksh93/sh.1 @@ -5353,11 +5353,16 @@ Kill the entire current line. Restore last item removed from line. (Yank item back to the line.) .TP 10 .BI ^X^E -Return the command -.BI "hist \-e ${\s-1VISUAL\s+1:\-${\s-1EDITOR\s+1:\-vi}}" -in the input buffer to call a full editor \(em -.BI vi -by default \(em on the current command line. +Edit the current command line in a full editor (as set in the +.SM +.B VISUAL +or +.SM +.B EDITOR +variable, or +.IR vi (1), +in that order of preference). +The command line is executed if the file is saved before exiting the editor. .TP 10 .BI ^L Line feed and print current line. @@ -6113,14 +6118,21 @@ Undo the last text modifying command. Undo all the text modifying commands performed on the line. .TP 10 [\f2count\fP]\f3v\fP -Returns the command -.BI "hist \-e ${\s-1VISUAL\s+1:\-${\s-1EDITOR\s+1:\-vi}}" " count" -in the input buffer to call a full editor \(em -.BI vi -by default \(em on a history entry. +Edit command line number +.I count\^ +in a full editor (as set in the +.SM +.B VISUAL +or +.SM +.B EDITOR +variable, or +.IR vi (1), +in that order of preference). If .I count\^ is omitted, then the current line is used. +The command line is executed if the file is saved before exiting the editor. .TP 10 .BI ^L Line feed and print current line. @@ -6783,7 +6795,7 @@ Does nothing, and exits 1. Used with for infinite loops. .TP .PD 0 -\f3fc\fP \*(OK \f3\-e\fP \f2ename\^\fP \ \*(CK \*(OK \f3\-N\fP \f2num\^\fP \*(CK \*(OK \f3\-nlr\^\fP \*(CK \*(OK \f2first\^\fP \*(OK \f2last\^\fP \*(CK \*(CK +\f3fc\fP \*(OK \f3\-e\fP \f2ename\^\fP \ \*(CK \*(OK \f3\-N\fP \f2num\^\fP \*(CK \*(OK \f3\-Enlr\^\fP \*(CK \*(OK \f2first\^\fP \*(OK \f2last\^\fP \*(CK \*(CK .TP \f3fc \-s \fP \*(OK \f2old\fP\f3\=\fP\f2new\^\fP \*(CK \*(OK \f2command\^\fP \*(CK .PD @@ -6952,7 +6964,7 @@ option empties the hash table. This can also be achieved by resetting .BR PATH. .TP .PD 0 -\f3hist\fP \*(OK \f3\-e\fP \f2ename\^\fP \ \*(CK \*(OK \f3\-N\fP \f2num\^\fP \*(CK \*(OK \f3\-nlr\^\fP \*(CK \*(OK \f2first\^\fP \*(OK \f2last\^\fP \*(CK \*(CK +\f3hist\fP \*(OK \f3\-e\fP \f2ename\^\fP \ \*(CK \*(OK \f3\-N\fP \f2num\^\fP \*(CK \*(OK \f3\-Enlr\^\fP \*(CK \*(OK \f2first\^\fP \*(OK \f2last\^\fP \*(CK \*(CK .TP \f3hist \-s\fP \*(OK \f2old\fP\f3\=\fP\f2new\^\fP \*(CK \*(OK \f2command\^\fP \*(CK .PD @@ -6997,8 +7009,11 @@ is not set, then (default .BR /bin/ed\^ ) is used as the editor. -When editing is complete, the edited command(s) -is executed if the changes have been saved. +When the editor exits, the contents of the file become +the new command line and it is either executed +regardless of any change, or if the +.B \-E +option is given, it is executed if the file has been saved. If .I last\^ is not specified,