diff options
-rw-r--r-- | linenoise/linenoise.c | 25 | ||||
-rw-r--r-- | linenoise/linenoise.h | 3 | ||||
-rw-r--r-- | parser.c | 15 | ||||
-rw-r--r-- | txr.1 | 27 |
4 files changed, 60 insertions, 10 deletions
diff --git a/linenoise/linenoise.c b/linenoise/linenoise.c index bdb0fedc..92a5b0e7 100644 --- a/linenoise/linenoise.c +++ b/linenoise/linenoise.c @@ -106,6 +106,7 @@ struct lino_state { int mlmode; /* Multi line mode. Default is single line. */ int history_max_len; int history_len; + int loaded_lines; /* How many lines come from load. */ wchar_t **history; wchar_t *clip; /* Selection */ wchar_t *result; /* Previous command result. */ @@ -2680,6 +2681,7 @@ int lino_hist_add(lino_t *ls, const wchar_t *line) { ls->history = coerce(wchar_t **, lino_os.alloc_fn(size)); if (ls->history == NULL) return 0; memset(ls->history, 0, size); + ls->loaded_lines = 0; } /* Don't add duplicated lines, unless we are resubmitting historic lines. */ @@ -2695,6 +2697,8 @@ int lino_hist_add(lino_t *ls, const wchar_t *line) { lino_os.free_fn(ls->history[0]); memmove(ls->history,ls->history+1,(ls->history_max_len-1)*sizeof *ls->history); ls->history_len--; + if (ls->loaded_lines > 0) + ls->loaded_lines--; } ls->history[ls->history_len] = linecopy; ls->history_len++; @@ -2723,6 +2727,10 @@ int lino_hist_set_max_len(lino_t *ls, int len) { for (j = 0; j < tocopy-len; j++) lino_os.free_fn(ls->history[j]); tocopy = len; + + ls->loaded_lines -= (tocopy - len); + if (ls->loaded_lines < 0) + ls->loaded_lines = 0; } memset(nsv, 0, sizeof *nsv * len); memcpy(nsv, ls->history+(ls->history_len-tocopy), sizeof *ls->history * tocopy); @@ -2736,8 +2744,10 @@ int lino_hist_set_max_len(lino_t *ls, int len) { /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ -int lino_hist_save(lino_t *ls, const wchar_t *filename) { - mem_t *fp = lino_os.open_fn(filename, lino_overwrite); +int lino_hist_save(lino_t *ls, const wchar_t *filename, int new_only) { + int from = new_only ? ls->loaded_lines : 0; + mem_t *fp = lino_os.open_fn(filename, + new_only ? lino_append : lino_overwrite); int j; if (fp == NULL) { @@ -2745,11 +2755,13 @@ int lino_hist_save(lino_t *ls, const wchar_t *filename) { return -1; } - for (j = 0; j < ls->history_len; j++) { + for (j = from; j < ls->history_len; j++) { lino_os.puts_file_fn(fp, ls->history[j]); lino_os.puts_file_fn(fp, L"\n"); } + ls->loaded_lines = ls->history_len; + lino_os.close_fn(fp); return 0; } @@ -2768,6 +2780,12 @@ int lino_hist_load(lino_t *ls, const wchar_t *filename) { return -1; } + if (ls->history) { + ls->error = lino_error; + lino_os.close_fn(fp); + return -1; + } + while (lino_os.getl_fn(fp, buf, LINENOISE_MAX_LINE) != NULL) { wchar_t *p = wcschr(buf, '\n'); if (p) @@ -2776,6 +2794,7 @@ int lino_hist_load(lino_t *ls, const wchar_t *filename) { } lino_os.close_fn(fp); + ls->loaded_lines = ls->history_len; return 0; } diff --git a/linenoise/linenoise.h b/linenoise/linenoise.h index 178df11c..05f66632 100644 --- a/linenoise/linenoise.h +++ b/linenoise/linenoise.h @@ -58,6 +58,7 @@ typedef unsigned char mem_t; typedef enum lino_file_mode { lino_read, lino_overwrite, + lino_append, } lino_file_mode_t; typedef struct lino_os { @@ -112,7 +113,7 @@ lino_error_t lino_get_error(lino_t *); lino_error_t lino_set_error(lino_t *, lino_error_t); /* returns old */ int lino_hist_add(lino_t *, const wchar_t *line); int lino_hist_set_max_len(lino_t *, int len); -int lino_hist_save(lino_t *, const wchar_t *filename); +int lino_hist_save(lino_t *, const wchar_t *filename, int new_only); int lino_hist_load(lino_t *, const wchar_t *filename); void lino_set_result(lino_t *, wchar_t *); /* takes ownership of malloced mem; modifies it */ int lino_clear_screen(lino_t *); @@ -1505,12 +1505,19 @@ val repl(val bindings, val in_stream, val out_stream, val env) dyn_env = saved_dyn_env; if (histfile_w) { - val histfile_tmp = format(nil, lit("~a/.txr_history.tmp"), home, nao); - if (lino_hist_save(ls, c_str(histfile_tmp)) == 0) + val histfile_tmp = format(nil, lit("~a.tmp"), histfile, nao); + const wchar_t *histfile_tmp_w = c_str(histfile_tmp); + lino_t *ltmp = lino_make(coerce(mem_t *, in_stream), + coerce(mem_t *, out_stream)); + lino_hist_set_max_len(ltmp, c_num(cdr(hist_len_var))); + lino_hist_load(ltmp, histfile_w); + lino_hist_save(ltmp, histfile_tmp_w, 0); + if (lino_hist_save(ls, histfile_tmp_w, 1) == 0) rename_path(histfile_tmp, histfile); else put_line(lit("** unable to save history file"), out_stream); gc_hint(histfile_tmp); + lino_free(ltmp); } free(line_w); @@ -1649,7 +1656,7 @@ static int lino_feof(mem_t *stream_in) } static const wchli_t *lino_mode_str[] = { - wli("r"), wli("w") + wli("r"), wli("w"), wli("a") }; static mem_t *lino_open(const wchar_t *name_in, lino_file_mode_t mode_in) @@ -1660,7 +1667,7 @@ static mem_t *lino_open(const wchar_t *name_in, lino_file_mode_t mode_in) ignerr_begin; ret = open_file(name, mode); #if HAVE_CHMOD - if (mode_in == lino_overwrite) + if (mode_in == lino_overwrite || mode_in == lino_append) (void) fchmod(c_num(stream_fd(ret)), S_IRUSR | S_IWUSR); #endif ignerr_end; @@ -72025,9 +72025,32 @@ option isn't present. The history is maintained in a text file called .code .txr_history in the user's home directory. Whenever the interactive listener terminates, -this file is overwritten with the history contents stored in the listener's +this file is updated with the history contents stored in the listener's memory. The next time the listener starts, it first re-loads the history from -this file, making the commands of a previous session available for recall. +this file, making the most recent +.code *listener-hist-len* +expressions of a previous session available for recall. + +The history file is maintained in a way that is somewhat +robust against the loss of history arising from the situation that a user +manages multiple simultaneous \*(TX sessions. When a session terminates, it +doesn't blindly overwrite the history file, which may have already been updated +with new history produced by another session. Rather, it appends new entries +to the history file. New entries are those that had not been previously read +from the history file, but have been newly entered into the listener. + +An effort is made to keep the history file trimmed to no more than +twice the number of entries specified in +.codn *listener-hist-len* . +The terminating session first makes a temporary copy of the existing +history, which is trimmed to the most recent +.code *listener-hist-len* +entries. New entries are then appended to this temporary file. +Finally, the actual history file is replaced with this temporary file by a +.code rename-path +a rename operation. This algorithm doesn't use locking, and is therefore not +robust against the situation when a two or more multiple interactive \*(TX +sessions belonging to the same user terminate at around the same time. The home directory is determined from the contents of the |