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
;
}
} 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;
}
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;
}
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();
* 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);
}
}
#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 */
#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);
} 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)
#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
/* 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. */
#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;
#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
#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. */
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
);
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);
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? */
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);
i = statusq(FALSE, replacing ? replace_list : whereis_list,
backupstring,
#ifndef NANO_SMALL
- &search_history,
+ search_history,
#endif
"%s%s%s%s%s%s", _("Search"),
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) {
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
/* 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();
* 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"));
/* 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) {
#endif
#ifndef NANO_SMALL
-/*
- * search and replace history list support functions
- */
+/* Indicate whether any of the history lists have changed. */
+bool history_has_changed(void)
+{
+ return history_changed;
+}
-/* initialize search and replace history lists */
+/* Initialize the search and replace history lists. */
void history_init(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;
+ 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;
}
-/* find first node containing string *s in history list *h */
-historytype *find_node(historytype *h, const char *s)
+/* 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;
-
- a = (historytype *)nmalloc(sizeof(historytype));
- a->next = h->next;
- a->prev = h;
- h->next->prev = a;
- h->next = a;
- a->data = mallocstrcpy(NULL, s);
-}
+ assert(h != NULL && hage != NULL && hbot != NULL && s != NULL);
-/* update history list */
-void update_history(historyheadtype *h, const char *s)
-{
- historytype *p;
-
- 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 (h->count == MAX_SEARCH_HISTORY) { /* list 'full', delete oldest */
- remove_node(h->tail);
- h->count--;
- }
- insert_node((historytype *)h, s);
- h->count++;
- SET(HISTORY_CHANGED);
- up_hs:
- h->current = h->next;
-}
+ /* If this string is already in the history, delete it. */
+ p = find_history(*hage, s);
-/* return a pointer to either the next older history or NULL if no more */
-char *get_history_older(historyheadtype *h)
-{
- if (h->current->next != NULL) { /* any older entries? */
- h->current = h->current->next; /* yes */
- return h->current->data; /* return it */
+ if (p != NULL) {
+ filestruct *foo, *bar;
+
+ /* 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);
}
- return NULL; /* end of list */
-}
-char *get_history_newer(historyheadtype *h)
-{
- if (h->current->prev != NULL) {
- h->current = h->current->prev;
- if (h->current->prev != NULL)
- return h->current->data;
+ /* 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);
}
- return NULL;
+
+ /* 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;
}
-/* get a completion */
-char *get_history_completion(historyheadtype *h, char *s)
+/* Return the string in the history list just before h, or NULL if there
+ * isn't one. */
+char *get_history_older(filestruct **h)
{
- historytype *p;
+ assert(h != NULL);
- 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;
+ if ((*h)->prev == NULL)
+ return NULL;
+
+ *h = (*h)->prev;
+
+ return (*h)->data;
}
-#ifdef DEBUG
-/* free a history list */
-void free_history(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)
{
- historytype *p;
+ assert(h != NULL);
- for (p = h->next; p->next != NULL; p = p->next)
- remove_node(p);
-}
-#endif
+ if ((*h)->next == NULL)
+ return NULL;
-/* end of history support functions */
+ *h = (*h)->next;
+
+ return (*h)->data;
+}
#endif /* !NANO_SMALL */
* 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
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
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,
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 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) {
+ /* 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);
+
+ /* 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
#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;
if (finished)
break;
-#ifndef NANO_SMALL
- last_kbinput = kbinput;
-#endif
nanoget_repaint(buf, answer, statusbar_x);
wrefresh(bottomwin);
}
* 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, ...)
{