From 1343ef0979ea9c549b822fb56f3e86ea9fd47c89 Mon Sep 17 00:00:00 2001 From: Calvin Morrison Date: Fri, 3 Apr 2026 22:15:00 -0400 Subject: patch --- 0001-sqlite-history.patch | 1064 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1064 insertions(+) create mode 100644 0001-sqlite-history.patch diff --git a/0001-sqlite-history.patch b/0001-sqlite-history.patch new file mode 100644 index 0000000..d4be939 --- /dev/null +++ b/0001-sqlite-history.patch @@ -0,0 +1,1064 @@ +diff --git a/bashhist.c b/bashhist.c +index 9979f99a..d411eb12 100644 +--- a/bashhist.c ++++ b/bashhist.c +@@ -22,6 +22,10 @@ + + #if defined (HISTORY) + ++#if defined (HAVE_SQLITE3) && defined (HAVE_SQLITE3_H) ++# include "bashhist_sqlite.c" ++#else ++ + #if defined (HAVE_UNISTD_H) + # ifdef _MINIX + # include +@@ -975,4 +979,5 @@ history_should_ignore (line) + + return match; + } ++#endif /* !HAVE_SQLITE3 */ + #endif /* HISTORY */ +diff --git a/config.h.in b/config.h.in +index a5ad9e72..656ad16d 100644 +--- a/config.h.in ++++ b/config.h.in +@@ -703,6 +703,9 @@ + /* Define if you have the killpg function. */ + #undef HAVE_KILLPG + ++/* Define if sqlite3 library is available. */ ++#undef HAVE_SQLITE3 ++ + /* Define if you have the lstat function. */ + #undef HAVE_LSTAT + +@@ -985,6 +988,9 @@ + /* Define if you have the header file. */ + #undef HAVE_REGEX_H + ++/* Define if you have the header file. */ ++#undef HAVE_SQLITE3_H ++ + /* Define if you have the header file. */ + #undef HAVE_STDLIB_H + +diff --git a/configure.ac b/configure.ac +index ce4e9b60..7140edf9 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -862,6 +862,13 @@ AC_CHECK_LIB(dl, dlopen) + AC_CHECK_FUNCS(dlopen dlclose dlsym) + fi + ++dnl check for sqlite3 (used for history storage) ++AC_CHECK_LIB([sqlite3], [sqlite3_open_v2], [ ++ AC_DEFINE([HAVE_SQLITE3], [1], [Define if sqlite3 library is available]) ++ LIBS="-lsqlite3 $LIBS" ++]) ++AC_CHECK_HEADERS([sqlite3.h]) ++ + dnl this defines HAVE_DECL_SYS_SIGLIST + AC_DECL_SYS_SIGLIST + +--- /dev/null 2026-04-01 12:42:23.952423856 -0400 ++++ bashhist_sqlite.c 2026-04-01 22:30:04.382338607 -0400 +@@ -0,0 +1,998 @@ ++/* bashhist_sqlite.c -- SQLite-backed history for bash. ++ * ++ * Drop-in replacement for the body of bashhist.c. Included (not compiled ++ * separately) when HAVE_SQLITE3 && HAVE_SQLITE3_H are both defined. ++ * ++ * Differences from the stock file: ++ * - History is stored in $HISTFILE.db (SQLite, WAL mode) instead of the ++ * flat $HISTFILE text file. ++ * - Every command is written to the DB immediately on add, so history ++ * survives shell crashes without needing HISTAPPEND / HISTFILESIZE tricks. ++ * - HISTSIZE still limits the readline in-memory list; the DB keeps everything. ++ * - Timestamps, CWD, hostname, and PID are stored per-entry. ++ * - erasedups (HISTCONTROL=erasedups) removes duplicates from the DB too, ++ * across all sessions. ++ * - history -c clears the DB. ++ * - history -a / exit are no-ops for file I/O (already persisted). ++ * - history -r / -w / -n still operate on the plain text file for ++ * compatibility / export use. ++ */ ++ ++#include ++#include ++ ++#if defined (HAVE_UNISTD_H) ++# ifdef _MINIX ++# include ++# endif ++# include ++#endif ++ ++#include "bashtypes.h" ++#include ++#include ++#include "bashansi.h" ++#include "posixstat.h" ++#include "filecntl.h" ++ ++#include "bashintl.h" ++ ++#if defined (SYSLOG_HISTORY) ++# include ++#endif ++ ++#include "shell.h" ++#include "flags.h" ++#include "input.h" ++#include "parser.h" ++#include "pathexp.h" ++#include "bashhist.h" ++#include "builtins/common.h" ++ ++#include ++#include ++#include ++ ++#if defined (READLINE) ++# include "bashline.h" ++extern int rl_done, rl_dispatching; ++#endif ++ ++#if !defined (errno) ++extern int errno; ++#endif ++ ++/* ------------------------------------------------------------------ */ ++/* SQLite state */ ++/* ------------------------------------------------------------------ */ ++ ++static sqlite3 *hist_db = NULL; ++static int sqlite_pending_erasedups = 0; ++ ++/* Forward declaration — sqlite_history_add calls this. */ ++static void sqlite_history_erasedups __P((const char *)); ++ ++static char * ++sqlite_history_dbpath (const char *histfile) ++{ ++ char *p; ++ size_t len; ++ ++ if (histfile == NULL || *histfile == '\0') ++ { ++ const char *home = get_string_value ("HOME"); ++ if (home == NULL) return NULL; ++ len = strlen (home) + 20; ++ p = xmalloc (len); ++ snprintf (p, len, "%s/.bash_history.db", home); ++ return p; ++ } ++ ++ len = strlen (histfile) + 4; ++ p = xmalloc (len); ++ snprintf (p, len, "%s.db", histfile); ++ return p; ++} ++ ++static int ++sqlite_history_open (const char *histfile) ++{ ++ char *dbpath; ++ int rc; ++ ++ if (hist_db != NULL) ++ return 0; ++ ++ dbpath = sqlite_history_dbpath (histfile); ++ if (dbpath == NULL) ++ return -1; ++ ++ rc = sqlite3_open (dbpath, &hist_db); ++ xfree (dbpath); ++ ++ if (rc != SQLITE_OK) ++ { ++ hist_db = NULL; ++ return -1; ++ } ++ ++ rc = sqlite3_exec (hist_db, ++ "PRAGMA journal_mode=WAL;" ++ "PRAGMA synchronous=NORMAL;" ++ "CREATE TABLE IF NOT EXISTS history (" ++ " id INTEGER PRIMARY KEY AUTOINCREMENT," ++ " command TEXT NOT NULL," ++ " timestamp INTEGER NOT NULL DEFAULT (strftime('%s','now'))," ++ " session INTEGER," ++ " hostname TEXT," ++ " cwd TEXT" ++ ");" ++ "CREATE INDEX IF NOT EXISTS idx_hist_cmd ON history(command);", ++ NULL, NULL, NULL); ++ ++ if (rc != SQLITE_OK) ++ { ++ sqlite3_close (hist_db); ++ hist_db = NULL; ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Load the most-recent MAX_ENTRIES rows into readline's in-memory list. ++ Pass max_entries <= 0 to load everything. */ ++static int ++sqlite_history_load (int max_entries) ++{ ++ sqlite3_stmt *stmt; ++ int rc, count; ++ char sql[512]; ++ ++ if (hist_db == NULL) ++ return -1; ++ ++ if (max_entries > 0) ++ snprintf (sql, sizeof (sql), ++ "SELECT id, command, timestamp FROM (" ++ " SELECT id, command, timestamp FROM history ORDER BY id DESC LIMIT %d" ++ ") ORDER BY id ASC", ++ max_entries); ++ else ++ snprintf (sql, sizeof (sql), ++ "SELECT id, command, timestamp FROM history ORDER BY id ASC"); ++ ++ rc = sqlite3_prepare_v2 (hist_db, sql, -1, &stmt, NULL); ++ if (rc != SQLITE_OK) ++ return -1; ++ ++ count = 0; ++ while (sqlite3_step (stmt) == SQLITE_ROW) ++ { ++ const char *cmd = (const char *) sqlite3_column_text (stmt, 1); ++ sqlite3_int64 ts = sqlite3_column_int64 (stmt, 2); ++ ++ if (cmd && *cmd) ++ { ++ add_history (cmd); ++ if (ts > 0) ++ { ++ char tsbuf[32]; ++ snprintf (tsbuf, sizeof (tsbuf), "#%lld", (long long) ts); ++ add_history_time (tsbuf); ++ } ++ count++; ++ } ++ } ++ ++ sqlite3_finalize (stmt); ++ history_lines_read_from_file = count; ++ return count; ++} ++ ++static void ++sqlite_history_add (const char *line) ++{ ++ sqlite3_stmt *stmt; ++ time_t now; ++ char cwd[4096]; ++ const char *hostname; ++ ++ if (hist_db == NULL) ++ return; ++ ++ time (&now); ++ hostname = get_string_value ("HOSTNAME"); ++ if (getcwd (cwd, sizeof (cwd)) == NULL) ++ cwd[0] = '\0'; ++ ++ if (sqlite3_prepare_v2 (hist_db, ++ "INSERT INTO history (command, timestamp, session, hostname, cwd)" ++ " VALUES (?, ?, ?, ?, ?)", ++ -1, &stmt, NULL) != SQLITE_OK) ++ return; ++ ++ sqlite3_bind_text (stmt, 1, line, -1, SQLITE_STATIC); ++ sqlite3_bind_int64 (stmt, 2, (sqlite3_int64) now); ++ sqlite3_bind_int (stmt, 3, (int) getpid ()); ++ sqlite3_bind_text (stmt, 4, hostname ? hostname : "", -1, SQLITE_STATIC); ++ sqlite3_bind_text (stmt, 5, cwd, -1, SQLITE_STATIC); ++ ++ sqlite3_step (stmt); ++ sqlite3_finalize (stmt); ++ ++ /* hc_erasedups() already removed in-memory duplicates and set the flag; ++ now remove DB duplicates after the insert so the new row has the MAX id ++ and the DELETE keeps it. */ ++ if (sqlite_pending_erasedups) ++ { ++ sqlite_pending_erasedups = 0; ++ sqlite_history_erasedups (line); ++ } ++} ++ ++/* Keep only the most recent occurrence of LINE in the DB. */ ++static void ++sqlite_history_erasedups (const char *line) ++{ ++ sqlite3_stmt *stmt; ++ ++ if (hist_db == NULL) ++ return; ++ ++ if (sqlite3_prepare_v2 (hist_db, ++ "DELETE FROM history WHERE command = ?" ++ " AND id != (SELECT MAX(id) FROM history WHERE command = ?)", ++ -1, &stmt, NULL) != SQLITE_OK) ++ return; ++ ++ sqlite3_bind_text (stmt, 1, line, -1, SQLITE_STATIC); ++ sqlite3_bind_text (stmt, 2, line, -1, SQLITE_STATIC); ++ sqlite3_step (stmt); ++ sqlite3_finalize (stmt); ++} ++ ++static void ++sqlite_history_clear () ++{ ++ if (hist_db == NULL) ++ return; ++ sqlite3_exec (hist_db, "DELETE FROM history", NULL, NULL, NULL); ++} ++ ++/* ------------------------------------------------------------------ */ ++/* Boilerplate identical to bashhist.c */ ++/* ------------------------------------------------------------------ */ ++ ++static int histignore_item_func __P((struct ign *)); ++static int check_history_control __P((char *)); ++static void hc_erasedups __P((char *)); ++static void really_add_history __P((char *)); ++ ++static struct ignorevar histignore = ++{ ++ "HISTIGNORE", ++ (struct ign *)0, ++ 0, ++ (char *)0, ++ (sh_iv_item_func_t *)histignore_item_func, ++}; ++ ++#define HIGN_EXPAND 0x01 ++ ++int remember_on_history = 0; ++int enable_history_list = 0; ++int history_lines_this_session; ++int history_lines_in_file; ++ ++#if defined (BANG_HISTORY) ++int history_expansion_inhibited; ++int double_quotes_inhibit_history_expansion = 0; ++#endif ++ ++int command_oriented_history = 1; ++int current_command_first_line_saved = 0; ++int literal_history; ++int force_append_history; ++int history_control; ++int hist_last_line_added; ++int hist_last_line_pushed; ++ ++#if defined (READLINE) ++int history_reediting; ++int hist_verify; ++#endif ++ ++int dont_save_function_defs; ++ ++extern int current_command_line_count; ++extern struct dstack dstack; ++extern int parser_state; ++ ++#if defined (BANG_HISTORY) ++static int bash_history_inhibit_expansion __P((char *, int)); ++#endif ++#if defined (READLINE) ++static void re_edit __P((char *)); ++#endif ++static int history_expansion_p __P((char *)); ++static int shell_comment __P((char *)); ++static int should_expand __P((char *)); ++static HIST_ENTRY *last_history_entry __P((void)); ++static char *expand_histignore_pattern __P((char *)); ++static int history_should_ignore __P((char *)); ++ ++#if defined (BANG_HISTORY) ++static int ++bash_history_inhibit_expansion (string, i) ++ char *string; ++ int i; ++{ ++ int t; ++ char hx[2]; ++ ++ hx[0] = history_expansion_char; ++ hx[1] = '\0'; ++ ++ if (i > 0 && (string[i - 1] == '[') && member (']', string + i + 1)) ++ return (1); ++ else if (i > 1 && string[i - 1] == '{' && string[i - 2] == '$' && ++ member ('}', string + i + 1)) ++ return (1); ++ else if (i > 1 && string[i - 1] == '$' && string[i] == '!') ++ return (1); ++#if defined (EXTENDED_GLOB) ++ else if (extended_glob && i > 1 && string[i+1] == '(' && member (')', string + i + 2)) ++ return (1); ++#endif ++ else if ((t = skip_to_histexp (string, 0, hx, SD_NOJMP|SD_HISTEXP)) > 0) ++ { ++ while (t < i) ++ { ++ t = skip_to_histexp (string, t+1, hx, SD_NOJMP|SD_HISTEXP); ++ if (t <= 0) ++ return 0; ++ } ++ return (t > i); ++ } ++ else ++ return (0); ++} ++#endif ++ ++void ++bash_initialize_history () ++{ ++ history_quotes_inhibit_expansion = 1; ++ history_search_delimiter_chars = ";&()|<>"; ++#if defined (BANG_HISTORY) ++ history_inhibit_expansion_function = bash_history_inhibit_expansion; ++ sv_histchars ("histchars"); ++#endif ++} ++ ++void ++bash_history_reinit (interact) ++ int interact; ++{ ++#if defined (BANG_HISTORY) ++ history_expansion = interact != 0; ++ history_expansion_inhibited = 1; ++ history_inhibit_expansion_function = bash_history_inhibit_expansion; ++#endif ++ remember_on_history = enable_history_list; ++} ++ ++void ++bash_history_disable () ++{ ++ remember_on_history = 0; ++#if defined (BANG_HISTORY) ++ history_expansion_inhibited = 1; ++#endif ++} ++ ++void ++bash_history_enable () ++{ ++ remember_on_history = enable_history_list = 1; ++#if defined (BANG_HISTORY) ++ history_expansion_inhibited = 0; ++ history_inhibit_expansion_function = bash_history_inhibit_expansion; ++#endif ++ sv_history_control ("HISTCONTROL"); ++ sv_histignore ("HISTIGNORE"); ++} ++ ++/* ------------------------------------------------------------------ */ ++/* Modified functions */ ++/* ------------------------------------------------------------------ */ ++ ++void ++load_history () ++{ ++ char *hf; ++ int hsval, nread; ++ ++ set_if_not ("HISTSIZE", "500"); ++ sv_histsize ("HISTSIZE"); ++ ++ hf = get_string_value ("HISTFILE"); ++ ++ if (sqlite_history_open (hf) == 0) ++ { ++ char *hs = get_string_value ("HISTSIZE"); ++ hsval = (hs && *hs) ? atoi (hs) : 500; ++ if (hsval <= 0) hsval = 500; ++ ++ nread = sqlite_history_load (hsval); ++ if (nread >= 0) ++ { ++ history_lines_in_file = nread; ++ using_history (); ++ return; ++ } ++ /* SQLite open succeeded but load failed; fall through to plain text. */ ++ } ++ ++ set_if_not ("HISTFILESIZE", get_string_value ("HISTSIZE")); ++ sv_histsize ("HISTFILESIZE"); ++ ++ if (hf && *hf && file_exists (hf)) ++ { ++ read_history (hf); ++ history_lines_in_file = history_lines_read_from_file; ++ using_history (); ++ } ++} ++ ++void ++bash_clear_history () ++{ ++ clear_history (); ++ history_lines_this_session = 0; ++ sqlite_history_clear (); ++} ++ ++int ++bash_delete_histent (i) ++ int i; ++{ ++ HIST_ENTRY *discard; ++ ++ discard = remove_history (i); ++ if (discard) ++ free_history_entry (discard); ++ history_lines_this_session--; ++ ++ return 1; ++} ++ ++int ++bash_delete_last_history () ++{ ++ register int i; ++ HIST_ENTRY **hlist, *histent; ++ int r; ++ ++ hlist = history_list (); ++ if (hlist == NULL) ++ return 0; ++ ++ for (i = 0; hlist[i]; i++) ++ ; ++ i--; ++ ++ histent = history_get (history_base + i); ++ if (histent == NULL) ++ return 0; ++ ++ r = bash_delete_histent (i); ++ ++ if (where_history () > history_length) ++ history_set_pos (history_length); ++ ++ return r; ++} ++ ++#ifdef INCLUDE_UNUSED ++void ++save_history () ++{ ++ char *hf; ++ int r; ++ ++ hf = get_string_value ("HISTFILE"); ++ if (hf && *hf && file_exists (hf)) ++ { ++ using_history (); ++ if (history_lines_this_session <= where_history () || force_append_history) ++ r = append_history (history_lines_this_session, hf); ++ else ++ r = write_history (hf); ++ sv_histsize ("HISTFILESIZE"); ++ } ++} ++#endif ++ ++int ++maybe_append_history (filename) ++ char *filename; ++{ ++ /* Commands are written to the DB on every add; nothing to append. */ ++ if (hist_db != NULL) ++ { ++ history_lines_this_session = 0; ++ return EXECUTION_SUCCESS; ++ } ++ ++ /* Fallback: plain-text path (SQLite unavailable). */ ++ int fd, result; ++ struct stat buf; ++ ++ result = EXECUTION_SUCCESS; ++ if (history_lines_this_session > 0 && (history_lines_this_session <= where_history ())) ++ { ++ if (stat (filename, &buf) == -1 && errno == ENOENT) ++ { ++ fd = open (filename, O_WRONLY|O_CREAT, 0600); ++ if (fd < 0) ++ { ++ builtin_error (_("%s: cannot create: %s"), filename, strerror (errno)); ++ return (EXECUTION_FAILURE); ++ } ++ close (fd); ++ } ++ result = append_history (history_lines_this_session, filename); ++ history_lines_in_file += history_lines_this_session; ++ history_lines_this_session = 0; ++ } ++ else ++ history_lines_this_session = 0; ++ ++ return (result); ++} ++ ++int ++maybe_save_shell_history () ++{ ++ int result; ++ char *hf; ++ ++ /* Commands are written to the DB on every add; nothing to do at exit. */ ++ if (hist_db != NULL) ++ { ++ history_lines_this_session = 0; ++ return 0; ++ } ++ ++ /* Fallback: plain-text path (SQLite unavailable). */ ++ result = 0; ++ if (history_lines_this_session > 0) ++ { ++ hf = get_string_value ("HISTFILE"); ++ if (hf && *hf) ++ { ++ if (file_exists (hf) == 0) ++ { ++ int file; ++ file = open (hf, O_CREAT | O_TRUNC | O_WRONLY, 0600); ++ if (file != -1) ++ close (file); ++ } ++ using_history (); ++ if (history_lines_this_session <= where_history () || force_append_history) ++ { ++ result = append_history (history_lines_this_session, hf); ++ history_lines_in_file += history_lines_this_session; ++ } ++ else ++ { ++ result = write_history (hf); ++ history_lines_in_file = history_lines_written_to_file; ++ } ++ history_lines_this_session = 0; ++ sv_histsize ("HISTFILESIZE"); ++ } ++ } ++ return (result); ++} ++ ++#if defined (READLINE) ++static void ++re_edit (text) ++ char *text; ++{ ++ if (bash_input.type == st_stdin) ++ bash_re_edit (text); ++} ++#endif ++ ++static int ++history_expansion_p (line) ++ char *line; ++{ ++ register char *s; ++ ++ for (s = line; *s; s++) ++ if (*s == history_expansion_char || *s == history_subst_char) ++ return 1; ++ return 0; ++} ++ ++char * ++pre_process_line (line, print_changes, addit) ++ char *line; ++ int print_changes, addit; ++{ ++ char *history_value; ++ char *return_value; ++ int expanded; ++ ++ return_value = line; ++ expanded = 0; ++ ++# if defined (BANG_HISTORY) ++ if (!history_expansion_inhibited && history_expansion && history_expansion_p (line)) ++ { ++ expanded = history_expand (line, &history_value); ++ ++ if (expanded) ++ { ++ if (print_changes) ++ { ++ if (expanded < 0) ++ internal_error ("%s", history_value); ++#if defined (READLINE) ++ else if (hist_verify == 0 || expanded == 2) ++#else ++ else ++#endif ++ fprintf (stderr, "%s\n", history_value); ++ } ++ ++ if (expanded < 0 || expanded == 2) ++ { ++# if defined (READLINE) ++ if (expanded == 2 && rl_dispatching == 0 && *history_value) ++# else ++ if (expanded == 2 && *history_value) ++# endif ++ maybe_add_history (history_value); ++ ++ free (history_value); ++ ++# if defined (READLINE) ++ if (history_reediting && expanded < 0 && rl_done) ++ re_edit (line); ++# endif ++ return ((char *)NULL); ++ } ++ ++# if defined (READLINE) ++ if (hist_verify && expanded == 1) ++ { ++ re_edit (history_value); ++ free (history_value); ++ return ((char *)NULL); ++ } ++# endif ++ } ++ ++ expanded = 1; ++ return_value = history_value; ++ } ++# endif /* BANG_HISTORY */ ++ ++ if (addit && remember_on_history && *return_value) ++ maybe_add_history (return_value); ++ ++ return (return_value); ++} ++ ++static int ++shell_comment (line) ++ char *line; ++{ ++ char *p; ++ ++ for (p = line; p && *p && whitespace (*p); p++) ++ ; ++ return (p && *p == '#'); ++} ++ ++static int ++check_history_control (line) ++ char *line; ++{ ++ HIST_ENTRY *temp; ++ int r; ++ ++ if (history_control == 0) ++ return 1; ++ ++ if ((history_control & HC_IGNSPACE) && *line == ' ') ++ return 0; ++ ++ if (history_control & HC_IGNDUPS) ++ { ++ using_history (); ++ temp = previous_history (); ++ r = (temp == 0 || STREQ (temp->line, line) == 0); ++ using_history (); ++ if (r == 0) ++ return r; ++ } ++ ++ return 1; ++} ++ ++static void ++hc_erasedups (line) ++ char *line; ++{ ++ HIST_ENTRY *temp; ++ int r; ++ ++ using_history (); ++ while (temp = previous_history ()) ++ { ++ if (STREQ (temp->line, line)) ++ { ++ r = where_history (); ++ temp = remove_history (r); ++ if (temp) ++ free_history_entry (temp); ++ } ++ } ++ using_history (); ++ /* Signal sqlite_history_add to erase DB duplicates after the insert. */ ++ sqlite_pending_erasedups = 1; ++} ++ ++void ++maybe_add_history (line) ++ char *line; ++{ ++ hist_last_line_added = 0; ++ ++ if (current_command_line_count > 1) ++ { ++ if (current_command_first_line_saved && ++ ((parser_state & PST_HEREDOC) || literal_history || dstack.delimiter_depth != 0 || shell_comment (line) == 0)) ++ bash_add_history (line); ++ return; ++ } ++ ++ current_command_first_line_saved = check_add_history (line, 0); ++} ++ ++int ++check_add_history (line, force) ++ char *line; ++ int force; ++{ ++ if (check_history_control (line) && history_should_ignore (line) == 0) ++ { ++ if (history_control & HC_ERASEDUPS) ++ hc_erasedups (line); ++ ++ if (force) ++ { ++ really_add_history (line); ++ using_history (); ++ } ++ else ++ bash_add_history (line); ++ return 1; ++ } ++ return 0; ++} ++ ++#if defined (SYSLOG_HISTORY) ++#define SYSLOG_MAXLEN 600 ++ ++extern char *shell_name; ++ ++#ifndef OPENLOG_OPTS ++#define OPENLOG_OPTS 0 ++#endif ++ ++void ++bash_syslog_history (line) ++ const char *line; ++{ ++ char trunc[SYSLOG_MAXLEN]; ++ static int first = 1; ++ ++ if (first) ++ { ++ openlog (shell_name, OPENLOG_OPTS, SYSLOG_FACILITY); ++ first = 0; ++ } ++ ++ if (strlen(line) < SYSLOG_MAXLEN) ++ syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d UID=%d %s", getpid(), current_user.uid, line); ++ else ++ { ++ strncpy (trunc, line, SYSLOG_MAXLEN); ++ trunc[SYSLOG_MAXLEN - 1] = '\0'; ++ syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d UID=%d %s", getpid(), current_user.uid, trunc); ++ } ++} ++#endif ++ ++void ++bash_add_history (line) ++ char *line; ++{ ++ int add_it, offset, curlen; ++ HIST_ENTRY *current, *old; ++ char *chars_to_add, *new_line; ++ ++ add_it = 1; ++ if (command_oriented_history && current_command_line_count > 1) ++ { ++ if ((parser_state & PST_HEREDOC) && literal_history && current_command_line_count > 2 && line[strlen (line) - 1] == '\n') ++ chars_to_add = ""; ++ else ++ chars_to_add = literal_history ? "\n" : history_delimiting_chars (line); ++ ++ using_history (); ++ current = previous_history (); ++ ++ if (current) ++ { ++ curlen = strlen (current->line); ++ ++ if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\\' && ++ current->line[curlen - 2] != '\\') ++ { ++ current->line[curlen - 1] = '\0'; ++ curlen--; ++ chars_to_add = ""; ++ } ++ ++ if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\n' && *chars_to_add == ';') ++ chars_to_add++; ++ ++ new_line = (char *)xmalloc (1 + curlen + strlen (line) + strlen (chars_to_add)); ++ sprintf (new_line, "%s%s%s", current->line, chars_to_add, line); ++ offset = where_history (); ++ old = replace_history_entry (offset, new_line, current->data); ++ free (new_line); ++ ++ if (old) ++ free_history_entry (old); ++ ++ add_it = 0; ++ } ++ } ++ ++ if (add_it) ++ really_add_history (line); ++ ++#if defined (SYSLOG_HISTORY) ++ bash_syslog_history (line); ++#endif ++ ++ using_history (); ++} ++ ++static void ++really_add_history (line) ++ char *line; ++{ ++ hist_last_line_added = 1; ++ hist_last_line_pushed = 0; ++ add_history (line); ++ history_lines_this_session++; ++ sqlite_history_add (line); ++} ++ ++int ++history_number () ++{ ++ using_history (); ++ return (remember_on_history ? history_base + where_history () : 1); ++} ++ ++static int ++should_expand (s) ++ char *s; ++{ ++ char *p; ++ ++ for (p = s; p && *p; p++) ++ { ++ if (*p == '\\') ++ p++; ++ else if (*p == '&') ++ return 1; ++ } ++ return 0; ++} ++ ++static int ++histignore_item_func (ign) ++ struct ign *ign; ++{ ++ if (should_expand (ign->val)) ++ ign->flags |= HIGN_EXPAND; ++ return (0); ++} ++ ++void ++setup_history_ignore (varname) ++ char *varname; ++{ ++ setup_ignore_patterns (&histignore); ++} ++ ++static HIST_ENTRY * ++last_history_entry () ++{ ++ HIST_ENTRY *he; ++ ++ using_history (); ++ he = previous_history (); ++ using_history (); ++ return he; ++} ++ ++char * ++last_history_line () ++{ ++ HIST_ENTRY *he; ++ ++ he = last_history_entry (); ++ if (he == 0) ++ return ((char *)NULL); ++ return he->line; ++} ++ ++static char * ++expand_histignore_pattern (pat) ++ char *pat; ++{ ++ HIST_ENTRY *phe; ++ char *ret; ++ ++ phe = last_history_entry (); ++ ++ if (phe == (HIST_ENTRY *)0) ++ return (savestring (pat)); ++ ++ ret = strcreplace (pat, '&', phe->line, 1); ++ ++ return ret; ++} ++ ++static int ++history_should_ignore (line) ++ char *line; ++{ ++ register int i, match; ++ char *npat; ++ ++ if (histignore.num_ignores == 0) ++ return 0; ++ ++ for (i = match = 0; i < histignore.num_ignores; i++) ++ { ++ if (histignore.ignores[i].flags & HIGN_EXPAND) ++ npat = expand_histignore_pattern (histignore.ignores[i].val); ++ else ++ npat = histignore.ignores[i].val; ++ ++ match = strmatch (npat, line, FNMATCH_EXTFLAG) != FNM_NOMATCH; ++ ++ if (histignore.ignores[i].flags & HIGN_EXPAND) ++ free (npat); ++ ++ if (match) ++ break; ++ } ++ ++ return match; ++} -- cgit v1.2.3