diff --git a/ChangeLog b/ChangeLog index fb137a8a..edeadaf4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -35,6 +35,11 @@ Cvs code - insert and write routines can't share shortcut lists anymore), new args to do_writeout and write_file called append, and of source code changes to those functions. + - Allow backwards searching. Drastic rewrite of the search prompt + string by Chris. All other code by Ken Tyler. New globals + nano_reverse_msg, new functions revstrstr and revstrcasestr, + many changes to search functions. Not too big a code size + increase! - configure.in: - New option, --enable-nanorc, which allows people to have a .nanorc initialization file and set options normally used on the command diff --git a/global.c b/global.c index 01794f87..7dea333e 100644 --- a/global.c +++ b/global.c @@ -194,7 +194,7 @@ void shortcut_init(int unjustify) "", *nano_backspace_msg = "", *nano_tab_msg = "", *nano_enter_msg = "", *nano_case_msg = "", *nano_cancel_msg = "", *nano_unjustify_msg = - "", *nano_append_msg = ""; + "", *nano_append_msg = "", *nano_reverse_msg = ""; #ifndef NANO_SMALL char *nano_tofiles_msg = ""; @@ -236,6 +236,7 @@ void shortcut_init(int unjustify) nano_gotodir_msg = _("Goto Directory"); nano_cancel_msg = _("Cancel the current function"); nano_append_msg = _("Append to the current file"); + nano_reverse_msg = _("Search Backwards"); #endif sc_init_one(&main_list[0], NANO_HELP_KEY, _("Get Help"), @@ -369,9 +370,11 @@ void shortcut_init(int unjustify) _("Goto Line"), nano_goto_msg, 0, 0, 0, VIEW, do_gotoline_void); - sc_init_one(&whereis_list[5], NANO_CANCEL_KEY, _("Cancel"), - nano_cancel_msg, 0, 0, 0, VIEW, 0); + sc_init_one(&whereis_list[5], NANO_REVERSESEARCH_KEY, _("Backward"), + nano_reverse_msg, 0, 0, 0, VIEW, 0); + sc_init_one(&whereis_list[6], NANO_CANCEL_KEY, _("Cancel"), + nano_cancel_msg, 0, 0, 0, VIEW, 0); sc_init_one(&replace_list[0], NANO_FIRSTLINE_KEY, _("First Line"), nano_firstline_msg, 0, 0, 0, VIEW, do_first_line); @@ -389,9 +392,11 @@ void shortcut_init(int unjustify) _("Goto Line"), nano_goto_msg, 0, 0, 0, VIEW, do_gotoline_void); - sc_init_one(&replace_list[5], NANO_CANCEL_KEY, _("Cancel"), - nano_cancel_msg, 0, 0, 0, VIEW, 0); + sc_init_one(&replace_list[5], NANO_REVERSESEARCH_KEY, _("Backward"), + nano_reverse_msg, 0, 0, 0, VIEW, 0); + sc_init_one(&replace_list[6], NANO_CANCEL_KEY, _("Cancel"), + nano_cancel_msg, 0, 0, 0, VIEW, 0); sc_init_one(&replace_list_2[0], NANO_FIRSTLINE_KEY, _("First Line"), diff --git a/nano.h b/nano.h index ca07b242..23c928fe 100644 --- a/nano.h +++ b/nano.h @@ -123,6 +123,7 @@ typedef struct rcoption { #define TEMP_OPT (1<<16) #define CUT_TO_END (1<<17) #define DISABLE_CURPOS (1<<18) +#define REVERSE_SEARCH (1<<19) /* Control key sequences, changing these would be very very bad */ @@ -205,6 +206,7 @@ know what you're doing */ #define NANO_REPLACE_FKEY KEY_F(14) #define NANO_ALT_REPLACE_KEY NANO_ALT_R #define NANO_OTHERSEARCH_KEY NANO_CONTROL_R +#define NANO_REVERSESEARCH_KEY NANO_CONTROL_B #define NANO_PREVPAGE_KEY NANO_CONTROL_Y #define NANO_PREVPAGE_FKEY KEY_F(7) #define NANO_NEXTPAGE_KEY NANO_CONTROL_V @@ -253,8 +255,8 @@ know what you're doing */ #define MAIN_LIST_LEN 26 #define MAIN_VISIBLE 12 -#define WHEREIS_LIST_LEN 6 -#define REPLACE_LIST_LEN 6 +#define WHEREIS_LIST_LEN 7 +#define REPLACE_LIST_LEN 7 #define REPLACE_LIST_2_LEN 3 #define GOTO_LIST_LEN 3 #define GOTODIR_LIST_LEN 1 diff --git a/proto.h b/proto.h index 39e86d38..766d8725 100644 --- a/proto.h +++ b/proto.h @@ -71,8 +71,10 @@ extern toggle toggles[TOGGLE_LEN]; /* Programs we want available */ +char *revstrstr(char *haystack, char *needle, char *rev_start); char *strcasestr(char *haystack, char *needle); -char *strstrwrapper(char *haystack, char *needle); +char *revstrcasestr(char *haystack, char *needle, char *rev_start); +char *strstrwrapper(char *haystack, char *needle, char *rev_start); int search_init(int replacing); int renumber(filestruct * fileptr); int free_filestruct(filestruct * src); diff --git a/search.c b/search.c index 4b1eef93..584c1383 100644 --- a/search.c +++ b/search.c @@ -74,7 +74,7 @@ int search_init(int replacing) { int i = 0; char *buf; - char *prompt, *reprompt = ""; + char *prompt; static char *backupstring = NULL; search_init_globals(); @@ -114,22 +114,21 @@ int search_init(int replacing) else strcpy(buf, ""); - if (ISSET(USE_REGEXP) && ISSET(CASE_SENSITIVE)) - prompt = _("Case Sensitive Regexp Search%s%s"); - else if (ISSET(USE_REGEXP)) - prompt = _("Regexp Search%s%s"); - else if (ISSET(CASE_SENSITIVE)) - prompt = _("Case Sensitive Search%s%s"); - else - prompt = _("Search%s%s"); - - if (replacing) - reprompt = _(" (to replace)"); + /* Instead of having a million if statements here to determine + the prompt, we instead just have a hundred "? :" calls in + the statusq call. I hope no one ever has to modify this :-) */ + prompt = "%s%s%s%s%s%s"; /* This is now one simple call. It just does a lot */ i = statusq(0, replacing ? replace_list : whereis_list, replacing ? REPLACE_LIST_LEN : WHEREIS_LIST_LEN, backupstring, - prompt, reprompt, buf); + prompt, + ISSET(CASE_SENSITIVE) ? _("Case Sensitive ") : "", + ISSET(USE_REGEXP) ? _("Regexp ") : "", + _("Search"), + ISSET(REVERSE_SEARCH) ? _(" Backwards") : "", + replacing ? _(" (to replace)") : "", + buf); /* Cancel any search, or just return with no previous search */ if ((i == -1) || (i < 0 && !last_search[0])) { @@ -173,6 +172,17 @@ int search_init(int replacing) } else if (i == NANO_OTHERSEARCH_KEY) { backupstring = mallocstrcpy(backupstring, answer); return -2; /* Call the opposite search function */ + } else if (i == NANO_REVERSESEARCH_KEY) { + free(backupstring); + backupstring = NULL; + backupstring = mallocstrcpy(backupstring, answer); + + if (ISSET(REVERSE_SEARCH)) + UNSET(REVERSE_SEARCH); + else + SET(REVERSE_SEARCH); + + return 1; } else if (i == NANO_FROMSEARCHTOGOTO_KEY) { free(backupstring); backupstring = NULL; @@ -207,68 +217,124 @@ filestruct *findnextstr(int quiet, filestruct * begin, int beginx, char *needle) { filestruct *fileptr; - char *searchstr, *found = NULL, *tmp; + char *searchstr, *rev_start = NULL, *found = NULL; int past_editbot = 0, current_x_find; fileptr = current; - current_x_find = current_x + 1; + if (!ISSET(REVERSE_SEARCH)) { /* forward search */ - /* Are we searching the last line? (i.e. the line where search started) */ - if ((fileptr == begin) && (current_x_find < beginx)) - search_last_line = 1; + current_x_find = current_x + 1; - /* Make sure we haven't passed the end of the string */ - if (strlen(fileptr->data) < current_x_find) - current_x_find--; + /* Are we now back to the line where the search started) */ + if ((fileptr == begin) && (current_x_find < beginx)) + search_last_line = 1; - searchstr = &fileptr->data[current_x_find]; + /* Make sure we haven't passed the end of the string */ + if (strlen(fileptr->data) < current_x_find) + current_x_find--; - /* Look for needle in searchstr */ - while ((found = strstrwrapper(searchstr, needle)) == NULL) { + searchstr = &fileptr->data[current_x_find]; - /* finished processing file, get out */ - if (search_last_line) { + /* Look for needle in searchstr */ + while ((found = strstrwrapper(searchstr, needle, rev_start)) == NULL) { + + /* finished processing file, get out */ + if (search_last_line) { + if (!quiet) + not_found_msg(needle); + return NULL; + } + + fileptr = fileptr->next; + + if (!past_editbot && (fileptr == editbot)) + past_editbot = 1; + + /* EOF reached ?, wrap around once */ + if (fileptr == NULL) { + fileptr = fileage; + past_editbot = 1; + if (!quiet) + statusbar(_("Search Wrapped")); + } + + /* Original start line reached */ + if (fileptr == begin) + search_last_line = 1; + + searchstr = fileptr->data; + } + + /* We found an instance */ + current_x_find = found - fileptr->data; + + /* Ensure we haven't wrapped around again! */ + if ((search_last_line) && (current_x_find >= beginx)) { if (!quiet) not_found_msg(needle); return NULL; } - fileptr = fileptr->next; + } else { /* reverse search */ - if (!past_editbot && (fileptr == editbot)) - past_editbot = 1; + current_x_find = current_x - 1; - /* EOF reached, wrap around once */ - if (fileptr == NULL) { - fileptr = fileage; - - past_editbot = 1; - - if (!quiet) - statusbar(_("Search Wrapped")); - } - - /* Original start line reached */ - if (fileptr == begin) + /* Are we now back to the line where the search started) */ + if ((fileptr == begin) && (current_x_find > beginx)) search_last_line = 1; + /* Make sure we haven't passed the begining of the string */ +#if 0 /* Is this required here ? */ + if (!(&fileptr->data[current_x_find] - fileptr->data)) + current_x_find++; +#endif + rev_start = &fileptr->data[current_x_find]; searchstr = fileptr->data; + + /* Look for needle in searchstr */ + while ((found = strstrwrapper(searchstr, needle, rev_start)) == NULL) { + + /* finished processing file, get out */ + if (search_last_line) { + if (!quiet) + not_found_msg(needle); + return NULL; + } + + fileptr = fileptr->prev; + +/* ? */ if (!past_editbot && (fileptr == edittop->prev)) + past_editbot = 1; + + /* SOF reached ?, wrap around once */ +/* ? */ if (fileptr == NULL) { + fileptr = filebot; + past_editbot = 1; + if (!quiet) + statusbar(_("Search Wrapped")); + } + + /* Original start line reached */ + if (fileptr == begin) + search_last_line = 1; + + searchstr = fileptr->data; + rev_start = fileptr->data + strlen(fileptr->data); + } + + /* We found an instance */ + current_x_find = found - fileptr->data; + + /* Ensure we haven't wrapped around again! */ + if ((search_last_line) && (current_x_find < beginx)) { + if (!quiet) + not_found_msg(needle); + return NULL; + } } - /* We found an instance */ - current_x_find = 0; - for (tmp = fileptr->data; tmp != found; tmp++) - current_x_find++; - - /* Ensure we haven't wrapped around again! */ - if ((search_last_line) && (current_x_find >= beginx)) { - if (!quiet) - not_found_msg(needle); - return NULL; - } - - /* Set globals, now that we are sure we found something */ + /* Set globals now that we are sure we found something */ current = fileptr; current_x = current_x_find; diff --git a/utils.c b/utils.c index d97b773e..1e6c2518 100644 --- a/utils.c +++ b/utils.c @@ -47,6 +47,31 @@ void lowercase(char *src) } } +char *revstrstr(char *haystack, char *needle, char *rev_start) +{ + char *p, *q, *r; + + for(p = rev_start ; p >= haystack ; --p) { + for (r = p, q = needle ; (*q == *r) && (*q != '\0') ; r++, q++) + ; + if (*q == '\0') + return p; + } + return 0; +} + +char *revstrcasestr(char *haystack, char *needle, char *rev_start) +{ + char *p, *q, *r; + + for(p = rev_start ; p >= haystack ; --p) { + for (r = p, q = needle ; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++) + ; + if (*q == '\0') + return p; + } + return 0; +} /* This is now mutt's version (called mutt_stristr) because it doesn't use memory allocation to do a simple search (yuck). */ @@ -59,31 +84,55 @@ char *strcasestr(char *haystack, char *needle) if (!needle) return (haystack); - while (*(p = haystack)) - { + while (*(p = haystack)) { for (q = needle; *p && *q && tolower (*p) == tolower (*q); p++, q++) ; if (!*q) return (haystack); - haystack++; + haystack++; } return NULL; } -char *strstrwrapper(char *haystack, char *needle) +char *strstrwrapper(char *haystack, char *needle, char *rev_start) { + #ifdef HAVE_REGEX_H + + int result; + char *i, *j; + if (ISSET(USE_REGEXP)) { - int result = regexec(&search_regexp, haystack, 10, regmatches, 0); - if (!result) - return haystack + regmatches[0].rm_so; + if (!ISSET(REVERSE_SEARCH)) { + result = regexec(&search_regexp, haystack, 10, regmatches, 0); + if (!result) + return haystack + regmatches[0].rm_so; + } else { + /* do quick check first */ + if (!(regexec(&search_regexp, haystack, 10, regmatches, 0))) { + /* there is a match */ + for(i = rev_start ; i >= haystack ; --i) + if (!(result = regexec(&search_regexp, i, 10, regmatches, 0))) { + j = i + regmatches[0].rm_so; + if (j <= rev_start) + return j; + } + } + } return 0; } #endif - if (ISSET(CASE_SENSITIVE)) - return strstr(haystack, needle); - else - return strcasestr(haystack, needle); + if (ISSET(CASE_SENSITIVE)) { + if (!ISSET(REVERSE_SEARCH)) + return strstr(haystack,needle); + else + return revstrstr(haystack, needle, rev_start); + } else { + if (!ISSET(REVERSE_SEARCH)) + return strcasestr(haystack, needle); + else + return revstrcasestr(haystack, needle, rev_start); + } } /* Thanks BG, many ppl have been asking for this... */