diff --git a/options-table.c b/options-table.c index 4ad45d37..7f75d22c 100644 --- a/options-table.c +++ b/options-table.c @@ -83,6 +83,11 @@ const struct options_table_entry server_options_table[] = { .default_num = 0 }, + { .name = "history-file", + .type = OPTIONS_TABLE_STRING, + .default_str = NULL + }, + { .name = "message-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, diff --git a/server.c b/server.c index 001a404c..d1c0bb3b 100644 --- a/server.c +++ b/server.c @@ -240,6 +240,7 @@ server_start(int lockfd, char *lockfile) cfg_add_cause("%s: %s", cfg_file, cause); } cmdq_continue(cfg_cmd_q); + status_prompt_load_history(); server_add_accept(0); @@ -250,6 +251,7 @@ server_start(int lockfd, char *lockfile) set_signals(server_signal_callback); server_loop(); + status_prompt_save_history(); exit(0); } diff --git a/status.c b/status.c index 0ffc0427..96ff8994 100644 --- a/status.c +++ b/status.c @@ -47,11 +47,102 @@ const char **status_prompt_complete_list(u_int *, const char *); char *status_prompt_complete_prefix(const char **, u_int); char *status_prompt_complete(struct session *, const char *); +char *status_prompt_find_history_file(void); + /* Status prompt history. */ #define PROMPT_HISTORY 100 char **status_prompt_hlist; u_int status_prompt_hsize; +/* Find the history file to load/save from/to. */ +char * +status_prompt_find_history_file(void) +{ + const char *home, *history_file; + char *path; + + history_file = options_get_string(&global_options, "history-file"); + if (*history_file == '\0') + return (NULL); + if (*history_file == '/') + return (xstrdup(history_file)); + + if (history_file[0] != '~' || history_file[1] != '/') + return (NULL); + if ((home = find_home()) == NULL) + return (NULL); + xasprintf(&path, "%s%s", home, history_file + 1); + return (path); +} + +/* Load status prompt history from file. */ +void +status_prompt_load_history(void) +{ + FILE *f; + char *history_file, *line, *tmp; + size_t length; + + if ((history_file = status_prompt_find_history_file()) == NULL) + return; + log_debug("loading history from %s", history_file); + + f = fopen(history_file, "r"); + if (f == NULL) { + log_debug("%s: %s", history_file, strerror(errno)); + free(history_file); + return; + } + free(history_file); + + for (;;) { + if ((line = fgetln(f, &length)) == NULL) + break; + + if (length > 0) { + if (line[length - 1] == '\n') { + line[length - 1] = '\0'; + status_prompt_add_history(line); + } else { + tmp = xmalloc(length + 1); + memcpy(tmp, line, length); + tmp[length] = '\0'; + status_prompt_add_history(tmp); + free(tmp); + } + } + } + fclose(f); +} + +/* Save status prompt history to file. */ +void +status_prompt_save_history(void) +{ + FILE *f; + u_int i; + char *history_file; + + if ((history_file = status_prompt_find_history_file()) == NULL) + return; + log_debug("saving history to %s", history_file); + + f = fopen(history_file, "w"); + if (f == NULL) { + log_debug("%s: %s", history_file, strerror(errno)); + free(history_file); + return; + } + free(history_file); + + for (i = 0; i < status_prompt_hsize; i++) { + fputs(status_prompt_hlist[i], f); + fputc('\n', f); + } + fclose(f); + +} + /* Status output tree. */ RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); diff --git a/tmux.1 b/tmux.1 index 69bde40a..7320df4f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2396,6 +2396,10 @@ passed through to applications running in .Nm . Attached clients should be detached and attached again after changing this option. +.It Ic history-file Ar path +If not empty, a file to which +.Nm +will write command prompt history on exit and load it from on start. .It Ic message-limit Ar number Set the number of error or information messages to save in the message log for each client. diff --git a/tmux.c b/tmux.c index 878180a7..a3481461 100644 --- a/tmux.c +++ b/tmux.c @@ -198,10 +198,27 @@ shell_exec(const char *shell, const char *shellcmd) fatal("execl failed"); } +const char* +find_home(void) +{ + struct passwd *pw; + const char *home; + + home = getenv("HOME"); + if (home == NULL || *home == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + home = pw->pw_dir; + else + home = NULL; + } + + return home; +} + int main(int argc, char **argv) { - struct passwd *pw; char *s, *path, *label, **var, tmp[PATH_MAX]; char in[256]; const char *home; @@ -320,14 +337,7 @@ main(int argc, char **argv) /* Locate the configuration file. */ if (cfg_file == NULL) { - home = getenv("HOME"); - if (home == NULL || *home == '\0') { - pw = getpwuid(getuid()); - if (pw != NULL) - home = pw->pw_dir; - else - home = NULL; - } + home = find_home(); if (home != NULL) { xasprintf(&cfg_file, "%s/.tmux.conf", home); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { diff --git a/tmux.h b/tmux.h index 3aceff9d..7edc4636 100644 --- a/tmux.h +++ b/tmux.h @@ -1469,6 +1469,7 @@ int checkshell(const char *); int areshell(const char *); void setblocking(int, int); __dead void shell_exec(const char *, const char *); +const char *find_home(void); /* cfg.c */ extern struct cmd_q *cfg_cmd_q; @@ -1943,6 +1944,8 @@ void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); void status_prompt_key(struct client *, int); void status_prompt_update(struct client *, const char *, const char *); +void status_prompt_load_history(void); +void status_prompt_save_history(void); /* resize.c */ void recalculate_sizes(void);