diff --git a/ChangeLog b/ChangeLog index d0d3cfbd..0a5fba78 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,6 +20,15 @@ CVS code - do_gotolinecolumn()), do_gotoline_void() (renamed do_gotolinecolumn_void()), nano.1, and nano.texi. (DLR, suggested by PFTank) + - Overhaul the history code to work more consistently, and clean + up various parts of it. Note that history tab completion has + been removed. New function history_has_changed(); changes to + load_history(), writehist(), thanks_for_all_the_fish(), + history_init(), find_node() (renamed find_history()), + update_history(), get_history_older(), get_history_newer(), + do_search(), do_replace(), nanogetstr(), and statusq(); + removal of remove_node(), insert_node(), and + get_history_completion(). (DLR) - cut.c: cut_line() - Set placewewant properly after cutting a line, to avoid a diff --git a/src/files.c b/src/files.c index bd5ebb88..c7a1502e 100644 --- a/src/files.c +++ b/src/files.c @@ -2866,7 +2866,12 @@ void load_history(void) ; } } else { - historyheadtype *history = &search_history; + /* Load a history (first the search history, then the + * replace history) from oldest to newest. Assume the last + * history entry is a blank line. */ + filestruct **history = &search_history; + filestruct **historyage = &searchage; + filestruct **historybot = &searchbot; char *line = NULL; size_t buflen = 0; ssize_t read; @@ -2878,31 +2883,38 @@ void load_history(void) } if (read > 0) { unsunder(line, read); - update_history(history, line); - } else + update_history(history, historyage, historybot, + line); + } else { history = &replace_history; + historyage = &replaceage; + historybot = &replacebot; + } } + fclose(hist); free(line); - UNSET(HISTORY_CHANGED); } free(nanohist); } } -bool writehist(FILE *hist, historyheadtype *histhead) +bool writehist(FILE *hist, filestruct *h) { - historytype *p; + filestruct *p; - /* Write oldest history first. */ - for (p = histhead->tail; p->prev != NULL; p = p->prev) { + /* Write history from oldest to newest. Assume the last history + * entry is a blank line. */ + for (p = h; p != NULL; p = p->next) { size_t p_len = strlen(p->data); sunder(p->data); + if (fwrite(p->data, sizeof(char), p_len, hist) < p_len || putc('\n', hist) == EOF) return FALSE; } + return TRUE; } @@ -2912,8 +2924,8 @@ void save_history(void) char *nanohist; /* Don't save unchanged or empty histories. */ - if (!ISSET(HISTORY_CHANGED) || (search_history.count == 0 && - replace_history.count == 0)) + if (!history_has_changed() || (searchbot->lineno == 1 && + replacebot->lineno == 1)) return; nanohist = histfilename(); @@ -2929,13 +2941,14 @@ void save_history(void) * history file. */ chmod(nanohist, S_IRUSR | S_IWUSR); - if (!writehist(hist, &search_history) || - putc('\n', hist) == EOF || - !writehist(hist, &replace_history)) + if (!writehist(hist, searchage) || !writehist(hist, + replaceage)) rcfile_error(N_("Error writing %s: %s"), nanohist, strerror(errno)); + fclose(hist); } + free(nanohist); } } diff --git a/src/global.c b/src/global.c index e71183a8..2134881d 100644 --- a/src/global.c +++ b/src/global.c @@ -168,8 +168,12 @@ toggle *toggles = NULL; #endif #ifndef NANO_SMALL -historyheadtype search_history; -historyheadtype replace_history; +filestruct *search_history = NULL; +filestruct *searchage = NULL; +filestruct *searchbot = NULL; +filestruct *replace_history = NULL; +filestruct *replaceage = NULL; +filestruct *replacebot = NULL; #endif /* Regular expressions */ @@ -1260,8 +1264,8 @@ void thanks_for_all_the_fish(void) #endif /* ENABLE_COLOR */ #ifndef NANO_SMALL /* Free the history lists. */ - free_history(&search_history); - free_history(&replace_history); + free_filestruct(searchage); + free_filestruct(replaceage); #endif #ifdef ENABLE_NANORC free(homedir); diff --git a/src/nano.h b/src/nano.h index d86a41de..e6efdcbf 100644 --- a/src/nano.h +++ b/src/nano.h @@ -265,25 +265,6 @@ typedef struct syntaxtype { } syntaxtype; #endif -#ifndef NANO_SMALL -typedef struct historytype { - struct historytype *next; - struct historytype *prev; - char *data; -} historytype; - -typedef struct historyheadtype { - struct historytype *next; /* Keep *next and *prev members - * together. */ - struct historytype *prev; /* And in the same order as in - * historytype. */ - struct historytype *tail; - struct historytype *current; - int count; - int len; -} historyheadtype; -#endif - /* Bitwise flags so that we can save space (or, more correctly, not * waste it). */ #define MODIFIED (1<<0) @@ -310,13 +291,12 @@ typedef struct historyheadtype { #define NO_RCFILE (1<<21) #define NO_COLOR_SYNTAX (1<<22) #define PRESERVE (1<<23) -#define HISTORY_CHANGED (1<<24) -#define HISTORYLOG (1<<25) -#define RESTRICTED (1<<26) -#define SMART_HOME (1<<27) -#define WHITESPACE_DISPLAY (1<<28) -#define MORE_SPACE (1<<29) -#define NO_UTF8 (1<<30) +#define HISTORYLOG (1<<24) +#define RESTRICTED (1<<25) +#define SMART_HOME (1<<26) +#define WHITESPACE_DISPLAY (1<<27) +#define MORE_SPACE (1<<28) +#define NO_UTF8 (1<<29) /* Control key sequences. Changing these would be very, very bad. */ #define NANO_CONTROL_SPACE 0 @@ -518,7 +498,8 @@ typedef struct historyheadtype { /* Default width of a tab. */ #define WIDTH_OF_TAB 8 -/* Maximum number of search/replace history strings saved. */ +/* Maximum number of search/replace history strings saved, not counting + * the blank lines at their ends. */ #define MAX_SEARCH_HISTORY 100 /* Maximum number of bytes we read from a file at one time. */ diff --git a/src/proto.h b/src/proto.h index 775a6c0e..abdb3502 100644 --- a/src/proto.h +++ b/src/proto.h @@ -144,8 +144,12 @@ extern toggle *toggles; #endif #ifndef NANO_SMALL -extern historyheadtype search_history; -extern historyheadtype replace_history; +extern filestruct *search_history; +extern filestruct *searchage; +extern filestruct *searchbot; +extern filestruct *replace_history; +extern filestruct *replaceage; +extern filestruct *replacebot; #endif extern bool curses_ended; @@ -316,7 +320,7 @@ char *do_browse_from(const char *inpath); #if !defined(NANO_SMALL) && defined(ENABLE_NANORC) char *histfilename(void); void load_history(void); -bool writehist(FILE *hist, historyheadtype *histhead); +bool writehist(FILE *hist, filestruct *histhead); void save_history(void); #endif @@ -524,17 +528,13 @@ void do_gotopos(int line, size_t pos_x, int pos_y, size_t pos_pww); #endif void do_find_bracket(void); #ifndef NANO_SMALL +bool history_has_changed(void); void history_init(void); -historytype *find_node(historytype *h, const char *s); -void remove_node(historytype *r); -void insert_node(historytype *h, const char *s); -void update_history(historyheadtype *h, const char *s); -char *get_history_older(historyheadtype *h); -char *get_history_newer(historyheadtype *h); -char *get_history_completion(historyheadtype *h, char *s); -#ifdef DEBUG -void free_history(historyheadtype *h); -#endif +filestruct *find_history(filestruct *h, const char *s); +void update_history(filestruct **h, filestruct **hage, filestruct + **hbot, const char *s); +char *get_history_older(filestruct **h); +char *get_history_newer(filestruct **h); #endif /* Public functions in utils.c. */ @@ -652,7 +652,7 @@ char *display_string(const char *buf, size_t start_col, size_t len, bool void nanoget_repaint(const char *buf, const char *inputbuf, size_t x); int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer, #ifndef NANO_SMALL - historyheadtype *history_list, + filestruct *history_list, #endif const shortcut *s #ifndef DISABLE_TABCOMP @@ -661,7 +661,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer, ); int statusq(bool allow_tabs, const shortcut *s, const char *curranswer, #ifndef NANO_SMALL - historyheadtype *history_list, + filestruct *history_list, #endif const char *msg, ...); void statusq_abort(void); diff --git a/src/search.c b/src/search.c index 9b907d0b..bb798cfc 100644 --- a/src/search.c +++ b/src/search.c @@ -35,6 +35,10 @@ static bool search_last_line = FALSE; /* Have we gone past the last line while searching? */ +#ifndef NANO_SMALL +static bool history_changed = FALSE; + /* Have any of the history lists changed? */ +#endif #ifdef HAVE_REGEX_H static bool regexp_compiled = FALSE; /* Have we compiled any regular expressions? */ @@ -145,10 +149,6 @@ int search_init(bool replacing, bool use_answer) search_init_globals(); -#ifndef NANO_SMALL - search_history.current = (historytype *)&search_history.next; -#endif - if (last_search[0] != '\0') { char *disp = display_string(last_search, 0, COLS / 3, FALSE); @@ -165,7 +165,7 @@ int search_init(bool replacing, bool use_answer) i = statusq(FALSE, replacing ? replace_list : whereis_list, backupstring, #ifndef NANO_SMALL - &search_history, + search_history, #endif "%s%s%s%s%s%s", _("Search"), @@ -212,9 +212,6 @@ int search_init(bool replacing, bool use_answer) if (i == -1 || (i < 0 && last_search[0] == '\0') || (!replacing && i == 0 && answer[0] == '\0')) { statusbar(_("Cancelled")); -#ifndef NANO_SMALL - search_history.current = search_history.next; -#endif return -1; } else { switch (i) { @@ -252,9 +249,6 @@ int search_init(bool replacing, bool use_answer) backupstring = mallocstrcpy(backupstring, answer); return -2; /* Call the opposite search function. */ case NANO_TOGOTOLINE_KEY: -#ifndef NANO_SMALL - search_history.current = search_history.next; -#endif do_gotolinecolumn(current->lineno, placewewant, TRUE, TRUE, FALSE); /* Put answer up on the statusbar and @@ -481,7 +475,7 @@ void do_search(void) /* If answer is not "", add this search string to the search history * list. */ if (answer[0] != '\0') - update_history(&search_history, answer); + update_history(&search_history, &searchage, &searchbot, answer); #endif findnextstr_wrap_reset(); @@ -910,19 +904,14 @@ void do_replace(void) * copy answer into last_search. */ if (answer[0] != '\0') { #ifndef NANO_SMALL - update_history(&search_history, answer); + update_history(&search_history, &searchage, &searchbot, answer); #endif last_search = mallocstrcpy(last_search, answer); } -#ifndef NANO_SMALL - replace_history.current = (historytype *)&replace_history.next; - last_replace = mallocstrcpy(last_replace, ""); -#endif - i = statusq(FALSE, replace_list_2, last_replace, #ifndef NANO_SMALL - &replace_history, + replace_history, #endif _("Replace with")); @@ -930,7 +919,8 @@ void do_replace(void) /* Add this replace string to the replace history list. i == 0 * means that the string is not "". */ if (i == 0) - update_history(&replace_history, answer); + update_history(&replace_history, &replaceage, &replacebot, + answer); #endif if (i != 0 && i != -2) { @@ -1126,129 +1116,123 @@ void do_find_bracket(void) #endif #ifndef NANO_SMALL -/* - * search and replace history list support functions - */ - -/* initialize search and replace history lists */ -void history_init(void) +/* Indicate whether any of the history lists have changed. */ +bool history_has_changed(void) { - search_history.next = (historytype *)&search_history.prev; - search_history.prev = NULL; - search_history.tail = (historytype *)&search_history.next; - search_history.current = search_history.next; - search_history.count = 0; - search_history.len = 0; - - replace_history.next = (historytype *)&replace_history.prev; - replace_history.prev = NULL; - replace_history.tail = (historytype *)&replace_history.next; - replace_history.current = replace_history.next; - replace_history.count = 0; - replace_history.len = 0; + return history_changed; } -/* find first node containing string *s in history list *h */ -historytype *find_node(historytype *h, const char *s) +/* Initialize the search and replace history lists. */ +void history_init(void) { + search_history = make_new_node(NULL); + search_history->data = mallocstrcpy(NULL, ""); + searchage = search_history; + searchbot = search_history; + + replace_history = make_new_node(NULL); + replace_history->data = mallocstrcpy(NULL, ""); + replaceage = replace_history; + replacebot = replace_history; +} + +/* Return the first node containing the string s in the history list, + * starting at h, or NULL if there isn't one. */ +filestruct *find_history(filestruct *h, const char *s) +{ + assert(h != NULL); + for (; h->next != NULL; h = h->next) { if (strcmp(s, h->data) == 0) return h; } + return NULL; } -/* remove node *r */ -void remove_node(historytype *r) +/* Update a history list. h should be the current position in the list, + * hage should be the top of the list, and hbot should be the bottom of + * the list. */ +void update_history(filestruct **h, filestruct **hage, filestruct + **hbot, const char *s) { - r->prev->next = r->next; - r->next->prev = r->prev; - free(r->data); - free(r); -} + filestruct *p; -/* add a node after node *h */ -void insert_node(historytype *h, const char *s) -{ - historytype *a; + assert(h != NULL && hage != NULL && hbot != NULL && s != NULL); - a = (historytype *)nmalloc(sizeof(historytype)); - a->next = h->next; - a->prev = h; - h->next->prev = a; - h->next = a; - a->data = mallocstrcpy(NULL, s); -} + /* If this string is already in the history, delete it. */ + p = find_history(*hage, s); -/* update history list */ -void update_history(historyheadtype *h, const char *s) -{ - historytype *p; + if (p != NULL) { + filestruct *foo, *bar; - if ((p = find_node(h->next, s)) != NULL) { - if (p == h->next) /* catch delete and re-insert of - same string in 1st node */ - goto up_hs; - remove_node(p); /* delete identical older string */ - h->count--; + /* If the string is at the current position, don't do + * anything. */ + if (p == *h) + return; + + /* If the string is at the beginning, move the beginning down to + * the next string. */ + if (p == *hage) + *hage = (*hage)->next; + + /* Delete the string. */ + foo = p; + bar = p->next; + unlink_node(foo); + delete_node(foo); + renumber(bar); } - if (h->count == MAX_SEARCH_HISTORY) { /* list 'full', delete oldest */ - remove_node(h->tail); - h->count--; + + /* If the history is full, delete the beginning entry to make room + * for the new entry at the end. */ + if ((*hbot)->lineno == MAX_SEARCH_HISTORY + 1) { + filestruct *foo = *hage; + + *hage = (*hage)->next; + unlink_node(foo); + delete_node(foo); + renumber(*hage); } - insert_node((historytype *)h, s); - h->count++; - SET(HISTORY_CHANGED); - up_hs: - h->current = h->next; + + /* Add the new entry to the end. */ + (*hbot)->data = mallocstrcpy(NULL, s); + splice_node(*hbot, make_new_node(*hbot), (*hbot)->next); + *hbot = (*hbot)->next; + (*hbot)->data = mallocstrcpy(NULL, ""); + + /* Indicate that the history's been changed. */ + history_changed = TRUE; + + /* Set the current position in the list to the bottom. */ + *h = *hbot; } -/* return a pointer to either the next older history or NULL if no more */ -char *get_history_older(historyheadtype *h) +/* Return the string in the history list just before h, or NULL if there + * isn't one. */ +char *get_history_older(filestruct **h) { - if (h->current->next != NULL) { /* any older entries? */ - h->current = h->current->next; /* yes */ - return h->current->data; /* return it */ - } - return NULL; /* end of list */ + assert(h != NULL); + + if ((*h)->prev == NULL) + return NULL; + + *h = (*h)->prev; + + return (*h)->data; } -char *get_history_newer(historyheadtype *h) +/* Return the string in the history list just after h, or NULL if there + * isn't one. */ +char *get_history_newer(filestruct **h) { - if (h->current->prev != NULL) { - h->current = h->current->prev; - if (h->current->prev != NULL) - return h->current->data; - } - return NULL; + assert(h != NULL); + + if ((*h)->next == NULL) + return NULL; + + *h = (*h)->next; + + return (*h)->data; } - -/* get a completion */ -char *get_history_completion(historyheadtype *h, char *s) -{ - historytype *p; - - for (p = h->current->next; p->next != NULL; p = p->next) { - if (strncmp(s, p->data, h->len) == 0 && strlen(p->data) != h->len) { - h->current = p; - return p->data; - } - } - h->current = (historytype *)h; - null_at(&s, h->len); - return s; -} - -#ifdef DEBUG -/* free a history list */ -void free_history(historyheadtype *h) -{ - historytype *p; - - for (p = h->next; p->next != NULL; p = p->next) - remove_node(p); -} -#endif - -/* end of history support functions */ #endif /* !NANO_SMALL */ diff --git a/src/winio.c b/src/winio.c index c3850883..96f56cff 100644 --- a/src/winio.c +++ b/src/winio.c @@ -2430,7 +2430,7 @@ void nanoget_repaint(const char *buf, const char *inputbuf, size_t x) * statusq(). */ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer, #ifndef NANO_SMALL - historyheadtype *history_list, + filestruct *history_list, #endif const shortcut *s #ifndef DISABLE_TABCOMP @@ -2443,23 +2443,15 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer, size_t answer_len = strlen(curranswer); #ifndef DISABLE_TABCOMP bool tabbed = FALSE; - /* Used by input_tab(). */ + /* Whether we've pressed Tab more than once consecutively. */ #endif #ifndef NANO_SMALL - /* For history. */ char *history = NULL; - char *currentbuf = NULL; - char *complete = NULL; - int last_kbinput = 0; - - /* This variable is used in the search history code. use_cb == 0 - * means that we're using the existing history and ignoring - * currentbuf. use_cb == 1 means that the entry in answer should be - * moved to currentbuf or restored from currentbuf to answer. - * use_cb == 2 means that the entry in currentbuf should be moved to - * answer or restored from answer to currentbuf. */ - int use_cb = 0; + /* The current history string. */ + char *magichistory = NULL; + /* The temporary string typed at the bottom of the history, if + * any. */ #endif /* Only put statusbar_x at the end of the string if it's @@ -2500,28 +2492,6 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer, switch (kbinput) { case NANO_TAB_KEY: -#ifndef NANO_SMALL - /* Tab history completion. */ - if (history_list != NULL) { - if (complete == NULL || - last_kbinput != NANO_TAB_KEY) { - history_list->current = - (historytype *)history_list; - history_list->len = strlen(answer); - } - - if (history_list->len > 0) { - complete = get_history_completion(history_list, - answer); - answer = mallocstrcpy(answer, complete); - answer_len = strlen(answer); - statusbar_x = answer_len; - } - } -#ifndef DISABLE_TABCOMP - else -#endif -#endif #ifndef DISABLE_TABCOMP if (allow_tabs) { answer = input_tab(answer, &statusbar_x, &tabbed, @@ -2533,43 +2503,23 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer, case NANO_PREVLINE_KEY: #ifndef NANO_SMALL if (history_list != NULL) { - /* If currentbuf is NULL, or if use_cb is 1 and - * currentbuf is different from answer, it means - * that we're scrolling up at the top of the search - * history, and we need to save the current answer - * in currentbuf. Do this and reset use_cb to 0. */ - if (currentbuf == NULL || (use_cb == 1 && - strcmp(currentbuf, answer) != 0)) { - currentbuf = mallocstrcpy(currentbuf, answer); - use_cb = 0; - } + /* If we're scrolling up at the bottom of the + * history list, answer isn't blank, and + * magichistory isn't set, save answer in + * magichistory. */ + if (history_list->next == NULL && + answer[0] != '\0' && magichistory == NULL) + magichistory = mallocstrcpy(NULL, answer); - /* If currentbuf isn't NULL, use_cb is 2, and - * currentbuf is different from answer, it means - * that we're scrolling up at the bottom of the - * search history, and we need to restore the - * current answer from currentbuf. Do this, blow - * away currentbuf since we don't need it anymore, - * and reset use_cb to 0. */ - if (currentbuf != NULL && use_cb == 2 && - strcmp(currentbuf, answer) != 0) { - answer = mallocstrcpy(answer, currentbuf); - answer_len = strlen(answer); - free(currentbuf); - currentbuf = NULL; - use_cb = 0; - /* Otherwise, get the older search from the history - * list and save it in answer. If there is no older - * search, blank out answer. */ - } else if ((history = - get_history_older(history_list)) != NULL) { + /* Get the older search from the history list and + * save it in answer. If there is no older search, + * don't do anything. */ + if ((history = + get_history_older(&history_list)) != NULL) { answer = mallocstrcpy(answer, history); - answer_len = strlen(history); - } else { - answer = mallocstrcpy(answer, ""); - answer_len = 0; + answer_len = strlen(answer); + statusbar_x = answer_len; } - statusbar_x = answer_len; /* This key has a shortcut list entry when it's used * to move to an older search, which means that @@ -2584,39 +2534,25 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer, #ifndef NANO_SMALL if (history_list != NULL) { /* Get the newer search from the history list and - * save it in answer. */ + * save it in answer. If there is no newer search, + * don't do anything. */ if ((history = - get_history_newer(history_list)) != NULL) { + get_history_newer(&history_list)) != NULL) { answer = mallocstrcpy(answer, history); - answer_len = strlen(history); - /* If currentbuf isn't NULL and use_cb isn't 2, it - * means that we're scrolling down at the bottom of - * the search history and we need to restore the - * current answer from currentbuf. Do this, blow - * away currentbuf since we don't need it anymore, - * and set use_cb to 1. */ - } else if (currentbuf != NULL && use_cb != 2) { - answer = mallocstrcpy(answer, currentbuf); answer_len = strlen(answer); - free(currentbuf); - currentbuf = NULL; - use_cb = 1; - /* Otherwise, if currentbuf is NULL and use_cb isn't - * 2, it means that we're scrolling down at the - * bottom of the search history and we need to save - * the current answer (if it's not blank) in - * currentbuf. Do this, blank out answer, and set - * use_cb to 2. */ - } else if (use_cb != 2) { - if (answer[0] != '\0') { - currentbuf = mallocstrcpy(currentbuf, - answer); - answer = mallocstrcpy(answer, ""); - } - answer_len = 0; - use_cb = 2; + statusbar_x = answer_len; + } + + /* If, after scrolling down, we're at the bottom of + * the history list, answer is blank, and + * magichistory is set, save magichistory in + * answer. */ + if (history_list->next == NULL && + answer[0] == '\0' && magichistory != NULL) { + answer = mallocstrcpy(answer, magichistory); + answer_len = strlen(answer); + statusbar_x = answer_len; } - statusbar_x = answer_len; } #endif break; @@ -2628,9 +2564,6 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer, if (finished) break; -#ifndef NANO_SMALL - last_kbinput = kbinput; -#endif nanoget_repaint(buf, answer, statusbar_x); wrefresh(bottomwin); } @@ -2653,7 +2586,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer, * completion. */ int statusq(bool allow_tabs, const shortcut *s, const char *curranswer, #ifndef NANO_SMALL - historyheadtype *history_list, + filestruct *history_list, #endif const char *msg, ...) {