Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 30 Apr 2022 08:04:22 GMT
From:      Piotr Pawel Stefaniak <pstef@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: d9d0812bc6e9 - stable/13 - sh: implement persistent history storage
Message-ID:  <202204300804.23U84MdW058650@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by pstef:

URL: https://cgit.FreeBSD.org/src/commit/?id=d9d0812bc6e90f6d8f85ef0939357590d34c3a4a

commit d9d0812bc6e90f6d8f85ef0939357590d34c3a4a
Author:     Baptiste Daroussin <bapt@FreeBSD.org>
AuthorDate: 2021-03-30 08:28:08 +0000
Commit:     Piotr Pawel Stefaniak <pstef@FreeBSD.org>
CommitDate: 2022-04-30 07:55:42 +0000

    sh: implement persistent history storage
    
    Implement persistent history storage:
    the strategy is simple at start: loads the existing .sh_history file
    at exit dump it.
    
    The implementation respects the HISTFILE variable and its POSIX
    definition: ~/.sh_history is used if HISTFILE is not set.
    
    to avoid sh to create the history file, set HISTSIZE to 0 or HISTFILE to
    en empty value
    
    (cherry picked from commit 988b1bb0c54e50654112f0bd649aee68307a5a80)
    
    sh: try to avoid overwriting HISTFILE produced by other shells
    
    If an attempt to load history from an existing history file was
    unsuccessful, do not try to save command history to that file on exit.
    
    (cherry picked from commit 1f82fb3834105fd8f1186b1c6d719d8a24738180)
---
 bin/sh/histedit.c   | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 bin/sh/main.c       |  7 ++++++
 bin/sh/myhistedit.h |  3 ++-
 bin/sh/sh.1         | 11 +++++++++-
 bin/sh/trap.c       |  3 +++
 5 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c
index cf6bebad4c1a..2001b80bd8ed 100644
--- a/bin/sh/histedit.c
+++ b/bin/sh/histedit.c
@@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <paths.h>
 #include <stdbool.h>
@@ -70,6 +72,7 @@ __FBSDID("$FreeBSD$");
 History *hist;	/* history cookie */
 EditLine *el;	/* editline cookie */
 int displayhist;
+static int savehist;
 static FILE *el_in, *el_out;
 static bool in_command_completion;
 
@@ -81,6 +84,66 @@ static char **sh_matches(const char *, int, int);
 static const char *append_char_function(const char *);
 static unsigned char sh_complete(EditLine *, int);
 
+static const char *
+get_histfile(void)
+{
+	const char *histfile;
+
+	/* don't try to save if the history size is 0 */
+	if (hist == NULL || histsizeval() == 0)
+		return (NULL);
+	histfile = expandstr("${HISTFILE-${HOME-}/.sh_history}");
+
+	if (histfile[0] == '\0')
+		return (NULL);
+	return (histfile);
+}
+
+void
+histsave(void)
+{
+	HistEvent he;
+	char *histtmpname = NULL;
+	const char *histfile;
+	int fd;
+	FILE *f;
+
+	if (!savehist || (histfile = get_histfile()) == NULL)
+		return;
+	INTOFF;
+	asprintf(&histtmpname, "%s.XXXXXXXXXX", histfile);
+	if (histtmpname == NULL) {
+		INTON;
+		return;
+	}
+	fd = mkstemp(histtmpname);
+	if (fd == -1 || (f = fdopen(fd, "w")) == NULL) {
+		free(histtmpname);
+		INTON;
+		return;
+	}
+	if (history(hist, &he, H_SAVE_FP, f) < 1 ||
+	    rename(histtmpname, histfile) == -1)
+		unlink(histtmpname);
+	fclose(f);
+	free(histtmpname);
+	INTON;
+
+}
+
+void
+histload(void)
+{
+	const char *histfile;
+	HistEvent he;
+
+	if ((histfile = get_histfile()) == NULL)
+		return;
+	errno = 0;
+	if (history(hist, &he, H_LOAD, histfile) != -1 || errno == ENOENT)
+		savehist = 1;
+}
+
 /*
  * Set history and editing status.  Called whenever the status may
  * have changed (figures out what to do).
diff --git a/bin/sh/main.c b/bin/sh/main.c
index cbe026e13640..b0a5fac6fd4e 100644
--- a/bin/sh/main.c
+++ b/bin/sh/main.c
@@ -75,6 +75,9 @@ __FBSDID("$FreeBSD$");
 #include "cd.h"
 #include "redir.h"
 #include "builtins.h"
+#ifndef NO_HISTORY
+#include "myhistedit.h"
+#endif
 
 int rootpid;
 int rootshell;
@@ -157,6 +160,10 @@ state2:
 			read_profile(shinit);
 		}
 	}
+#ifndef NO_HISTORY
+	if (iflag)
+		histload();
+#endif
 state3:
 	state = 4;
 	popstackmark(&smark2);
diff --git a/bin/sh/myhistedit.h b/bin/sh/myhistedit.h
index 968d23c9c1f8..1f513f0ae206 100644
--- a/bin/sh/myhistedit.h
+++ b/bin/sh/myhistedit.h
@@ -43,4 +43,5 @@ extern int displayhist;
 void histedit(void);
 void sethistsize(const char *);
 void setterm(const char *);
-
+void histload(void);
+void histsave(void);
diff --git a/bin/sh/sh.1 b/bin/sh/sh.1
index 76335cfaa2cd..ca3faeff13af 100644
--- a/bin/sh/sh.1
+++ b/bin/sh/sh.1
@@ -32,7 +32,7 @@
 .\"	from: @(#)sh.1	8.6 (Berkeley) 5/4/95
 .\" $FreeBSD$
 .\"
-.Dd July 6, 2020
+.Dd May 10, 2021
 .Dt SH 1
 .Os
 .Sh NAME
@@ -1351,6 +1351,15 @@ If not set, the default editor is
 The default editor used with the
 .Ic fc
 built-in.
+.It Va HISTFILE
+File used for persistent history storage.
+If unset
+.Pa ~/.sh_history
+will be used.
+If set but empty or
+.Va HISTSIZE
+is set to 0
+the shell will not load and save the history.
 .It Va HISTSIZE
 The number of previous commands that are accessible.
 .It Va HOME
diff --git a/bin/sh/trap.c b/bin/sh/trap.c
index d7ef40274270..2dd394035ca4 100644
--- a/bin/sh/trap.c
+++ b/bin/sh/trap.c
@@ -535,6 +535,9 @@ exitshell_savedstatus(void)
 		flushall();
 #if JOBS
 		setjobctl(0);
+#endif
+#ifndef NO_HISTORY
+		histsave();
 #endif
 	}
 	if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN &&



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202204300804.23U84MdW058650>