2000-08-06 21:13:45 +00:00
|
|
|
/* $Id$ */
|
2000-06-19 04:22:15 +00:00
|
|
|
/**************************************************************************
|
|
|
|
* search.c *
|
|
|
|
* *
|
2004-01-09 23:04:55 +00:00
|
|
|
* Copyright (C) 2000-2004 Chris Allegretta *
|
2000-06-19 04:22:15 +00:00
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
2001-10-24 11:33:54 +00:00
|
|
|
* the Free Software Foundation; either version 2, or (at your option) *
|
2000-06-19 04:22:15 +00:00
|
|
|
* any later version. *
|
|
|
|
* *
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
* *
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
* along with this program; if not, write to the Free Software *
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
|
|
|
|
* *
|
|
|
|
**************************************************************************/
|
|
|
|
|
2001-04-28 18:03:52 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2000-06-19 04:22:15 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
2004-02-24 20:41:39 +00:00
|
|
|
#include <unistd.h>
|
2000-11-05 16:52:21 +00:00
|
|
|
#include <ctype.h>
|
2003-01-05 20:41:21 +00:00
|
|
|
#include <errno.h>
|
2002-07-19 01:08:59 +00:00
|
|
|
#include <assert.h>
|
2000-06-19 04:22:15 +00:00
|
|
|
#include "proto.h"
|
|
|
|
#include "nano.h"
|
2000-06-21 03:00:43 +00:00
|
|
|
|
2000-07-07 01:49:52 +00:00
|
|
|
|
2000-09-06 13:39:17 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2004-04-30 19:40:03 +00:00
|
|
|
static int regexp_compiled = FALSE;
|
|
|
|
|
|
|
|
/* Regular expression helper functions. */
|
|
|
|
|
|
|
|
/* Compile the given regular expression. Return value 0 means the
|
|
|
|
* expression was invalid, and we wrote an error message on the status
|
|
|
|
* bar. Return value 1 means success. */
|
2003-01-29 04:18:37 +00:00
|
|
|
int regexp_init(const char *regexp)
|
2000-07-07 01:49:52 +00:00
|
|
|
{
|
2004-04-30 19:40:03 +00:00
|
|
|
int rc = regcomp(&search_regexp, regexp, REG_EXTENDED |
|
|
|
|
(ISSET(CASE_SENSITIVE) ? 0 : REG_ICASE));
|
|
|
|
|
|
|
|
assert(!regexp_compiled);
|
|
|
|
if (rc != 0) {
|
|
|
|
size_t len = regerror(rc, &search_regexp, NULL, 0);
|
|
|
|
char *str = charalloc(len);
|
|
|
|
|
|
|
|
regerror(rc, &search_regexp, str, len);
|
|
|
|
statusbar(_("Bad regex \"%s\": %s"), regexp, str);
|
|
|
|
free(str);
|
2003-01-29 04:18:37 +00:00
|
|
|
return 0;
|
2004-04-30 19:40:03 +00:00
|
|
|
}
|
2003-01-29 04:18:37 +00:00
|
|
|
|
2004-04-30 19:40:03 +00:00
|
|
|
regexp_compiled = TRUE;
|
2003-01-29 04:18:37 +00:00
|
|
|
return 1;
|
2000-07-07 01:49:52 +00:00
|
|
|
}
|
|
|
|
|
2001-03-18 16:59:34 +00:00
|
|
|
void regexp_cleanup(void)
|
2000-07-07 01:49:52 +00:00
|
|
|
{
|
2004-04-30 19:40:03 +00:00
|
|
|
if (regexp_compiled) {
|
|
|
|
regexp_compiled = FALSE;
|
2004-02-24 20:41:39 +00:00
|
|
|
regfree(&search_regexp);
|
|
|
|
}
|
2000-07-07 01:49:52 +00:00
|
|
|
}
|
2000-07-07 02:35:34 +00:00
|
|
|
#endif
|
2000-07-07 01:49:52 +00:00
|
|
|
|
2002-09-06 20:35:28 +00:00
|
|
|
void not_found_msg(const char *str)
|
|
|
|
{
|
2004-05-29 16:25:30 +00:00
|
|
|
char *disp;
|
|
|
|
int numchars;
|
|
|
|
|
2004-04-05 01:08:14 +00:00
|
|
|
assert(str != NULL);
|
2004-05-29 16:25:30 +00:00
|
|
|
|
|
|
|
disp = display_string(str, 0, (COLS / 2) + 1);
|
|
|
|
numchars = strnlen(disp, COLS / 2);
|
|
|
|
|
|
|
|
statusbar(_("\"%.*s%s\" not found"), numchars, disp,
|
|
|
|
disp[numchars] == '\0' ? "" : "...");
|
|
|
|
|
|
|
|
free(disp);
|
2002-09-06 20:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void search_abort(void)
|
|
|
|
{
|
|
|
|
display_main_list();
|
2004-02-24 20:41:39 +00:00
|
|
|
#ifndef NANO_SMALL
|
2002-09-06 20:35:28 +00:00
|
|
|
if (ISSET(MARK_ISSET))
|
2004-02-24 20:41:39 +00:00
|
|
|
edit_refresh();
|
|
|
|
#endif
|
2002-09-06 20:35:28 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2004-02-24 20:41:39 +00:00
|
|
|
regexp_cleanup();
|
2002-09-06 20:35:28 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2000-11-05 16:52:21 +00:00
|
|
|
void search_init_globals(void)
|
|
|
|
{
|
|
|
|
if (last_search == NULL) {
|
2001-05-17 11:35:43 +00:00
|
|
|
last_search = charalloc(1);
|
2002-05-11 03:04:44 +00:00
|
|
|
last_search[0] = '\0';
|
2000-11-05 16:52:21 +00:00
|
|
|
}
|
|
|
|
if (last_replace == NULL) {
|
2001-05-17 11:35:43 +00:00
|
|
|
last_replace = charalloc(1);
|
2002-05-11 03:04:44 +00:00
|
|
|
last_replace[0] = '\0';
|
2000-11-05 16:52:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Set up the system variables for a search or replace. Return -1 if
|
|
|
|
* the search should be cancelled (due to Cancel, Go to Line, or a
|
|
|
|
* failed regcomp()). Return 0 on success, and 1 on rerun calling
|
|
|
|
* program. Return -2 to run opposite program (search -> replace,
|
|
|
|
* replace -> search).
|
2003-01-13 01:35:15 +00:00
|
|
|
*
|
2003-01-14 23:36:11 +00:00
|
|
|
* replacing = 1 if we call from do_replace(), 0 if called from
|
|
|
|
* do_search(). */
|
2002-08-21 16:10:37 +00:00
|
|
|
int search_init(int replacing)
|
2000-06-19 04:22:15 +00:00
|
|
|
{
|
2001-07-15 22:24:24 +00:00
|
|
|
int i = 0;
|
2000-11-03 14:23:00 +00:00
|
|
|
char *buf;
|
2000-11-16 06:01:10 +00:00
|
|
|
static char *backupstring = NULL;
|
2004-02-24 20:41:39 +00:00
|
|
|
|
|
|
|
/* We display the search prompt below. If the user types a partial
|
|
|
|
* search string and then Replace or a toggle, we will return to
|
|
|
|
* do_search() or do_replace() and be called again. In that case,
|
|
|
|
* we should put the same search string back up. backupstring holds
|
|
|
|
* this string. */
|
2000-10-26 01:44:42 +00:00
|
|
|
|
2000-11-05 16:52:21 +00:00
|
|
|
search_init_globals();
|
2000-11-02 04:40:39 +00:00
|
|
|
|
2003-09-16 01:16:49 +00:00
|
|
|
/* If we don't already have a backupstring, set it. */
|
2003-01-05 20:41:21 +00:00
|
|
|
if (backupstring == NULL)
|
2003-09-16 01:16:49 +00:00
|
|
|
backupstring = mallocstrcpy(NULL, "");
|
2000-11-16 06:01:10 +00:00
|
|
|
|
2003-01-05 20:57:07 +00:00
|
|
|
#ifndef NANO_SMALL
|
2003-01-05 21:47:06 +00:00
|
|
|
search_history.current = (historytype *)&search_history.next;
|
2003-01-05 20:57:07 +00:00
|
|
|
#endif
|
2003-01-05 21:47:06 +00:00
|
|
|
|
|
|
|
if (last_search[0] != '\0') {
|
2003-09-16 01:16:49 +00:00
|
|
|
char *disp = display_string(last_search, 0, COLS / 3);
|
|
|
|
|
2002-07-20 13:57:41 +00:00
|
|
|
buf = charalloc(COLS / 3 + 7);
|
2004-02-24 20:41:39 +00:00
|
|
|
/* We use COLS / 3 here because we need to see more on the
|
|
|
|
* line. */
|
2003-09-16 01:16:49 +00:00
|
|
|
sprintf(buf, " [%s%s]", disp,
|
|
|
|
strlenpt(last_search) > COLS / 3 ? "..." : "");
|
|
|
|
free(disp);
|
2002-07-19 01:08:59 +00:00
|
|
|
} else {
|
|
|
|
buf = charalloc(1);
|
2002-05-11 03:04:44 +00:00
|
|
|
buf[0] = '\0';
|
2002-07-19 01:08:59 +00:00
|
|
|
}
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* This is now one simple call. It just does a lot. */
|
2004-03-15 20:26:30 +00:00
|
|
|
i = statusq(FALSE, replacing ? replace_list : whereis_list,
|
|
|
|
backupstring,
|
2003-01-05 20:41:21 +00:00
|
|
|
#ifndef NANO_SMALL
|
|
|
|
&search_history,
|
|
|
|
#endif
|
2004-03-15 20:26:30 +00:00
|
|
|
"%s%s%s%s%s%s", _("Search"),
|
2001-09-27 13:04:10 +00:00
|
|
|
|
2003-09-16 01:16:49 +00:00
|
|
|
#ifndef NANO_SMALL
|
2004-02-24 20:41:39 +00:00
|
|
|
/* This string is just a modifier for the search prompt; no
|
|
|
|
* grammar is implied. */
|
2003-09-16 01:16:49 +00:00
|
|
|
ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") :
|
|
|
|
#endif
|
|
|
|
"",
|
2001-09-27 13:04:10 +00:00
|
|
|
|
2004-04-30 19:40:03 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2004-02-24 20:41:39 +00:00
|
|
|
/* This string is just a modifier for the search prompt; no
|
|
|
|
* grammar is implied. */
|
2004-04-30 19:40:03 +00:00
|
|
|
ISSET(USE_REGEXP) ? _(" [Regexp]") :
|
|
|
|
#endif
|
|
|
|
"",
|
2001-09-27 13:04:10 +00:00
|
|
|
|
2003-09-16 01:16:49 +00:00
|
|
|
#ifndef NANO_SMALL
|
2004-02-24 20:41:39 +00:00
|
|
|
/* This string is just a modifier for the search prompt; no
|
|
|
|
* grammar is implied. */
|
2003-09-16 01:16:49 +00:00
|
|
|
ISSET(REVERSE_SEARCH) ? _(" [Backwards]") :
|
|
|
|
#endif
|
|
|
|
"",
|
2001-09-27 13:04:10 +00:00
|
|
|
|
2001-06-13 02:35:44 +00:00
|
|
|
replacing ? _(" (to replace)") : "",
|
|
|
|
buf);
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Release buf now that we don't need it anymore. */
|
2002-03-10 01:22:21 +00:00
|
|
|
free(buf);
|
|
|
|
|
2003-09-16 01:16:49 +00:00
|
|
|
free(backupstring);
|
|
|
|
backupstring = NULL;
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Cancel any search, or just return with no previous search. */
|
|
|
|
if (i == -1 || (i < 0 && last_search[0] == '\0') ||
|
|
|
|
(!replacing && i == 0 && answer[0] == '\0')) {
|
2000-06-19 04:22:15 +00:00
|
|
|
statusbar(_("Search Cancelled"));
|
2003-01-05 20:41:21 +00:00
|
|
|
#ifndef NANO_SMALL
|
|
|
|
search_history.current = search_history.next;
|
|
|
|
#endif
|
2000-06-19 04:22:15 +00:00
|
|
|
return -1;
|
2002-07-19 01:08:59 +00:00
|
|
|
} else {
|
|
|
|
switch (i) {
|
2004-02-24 20:41:39 +00:00
|
|
|
case -2: /* It's the same string. */
|
2000-09-06 13:39:17 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Since answer is "", use last_search! */
|
2004-04-30 19:40:03 +00:00
|
|
|
if (ISSET(USE_REGEXP) && regexp_init(last_search) == 0)
|
2004-02-24 20:41:39 +00:00
|
|
|
return -1;
|
2000-07-07 02:35:34 +00:00
|
|
|
#endif
|
2002-07-19 01:08:59 +00:00
|
|
|
break;
|
2004-02-24 20:41:39 +00:00
|
|
|
case 0: /* They entered something new. */
|
2003-09-16 01:16:49 +00:00
|
|
|
last_replace[0] = '\0';
|
2000-09-06 13:39:17 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2004-04-30 19:40:03 +00:00
|
|
|
if (ISSET(USE_REGEXP) && regexp_init(answer) == 0)
|
2004-02-24 20:41:39 +00:00
|
|
|
return -1;
|
2000-07-07 02:35:34 +00:00
|
|
|
#endif
|
2002-07-19 01:08:59 +00:00
|
|
|
break;
|
|
|
|
#ifndef NANO_SMALL
|
|
|
|
case TOGGLE_CASE_KEY:
|
|
|
|
TOGGLE(CASE_SENSITIVE);
|
|
|
|
backupstring = mallocstrcpy(backupstring, answer);
|
|
|
|
return 1;
|
|
|
|
case TOGGLE_BACKWARDS_KEY:
|
|
|
|
TOGGLE(REVERSE_SEARCH);
|
|
|
|
backupstring = mallocstrcpy(backupstring, answer);
|
|
|
|
return 1;
|
2001-06-14 02:54:22 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2002-07-19 01:08:59 +00:00
|
|
|
case TOGGLE_REGEXP_KEY:
|
|
|
|
TOGGLE(USE_REGEXP);
|
|
|
|
backupstring = mallocstrcpy(backupstring, answer);
|
|
|
|
return 1;
|
2001-06-14 02:54:22 +00:00
|
|
|
#endif
|
2002-07-19 01:08:59 +00:00
|
|
|
#endif /* !NANO_SMALL */
|
|
|
|
case NANO_OTHERSEARCH_KEY:
|
|
|
|
backupstring = mallocstrcpy(backupstring, answer);
|
2004-02-24 20:41:39 +00:00
|
|
|
return -2; /* Call the opposite search function. */
|
2002-07-19 01:08:59 +00:00
|
|
|
case NANO_FROMSEARCHTOGOTO_KEY:
|
2003-01-05 20:41:21 +00:00
|
|
|
#ifndef NANO_SMALL
|
|
|
|
search_history.current = search_history.next;
|
|
|
|
#endif
|
2004-07-12 16:07:14 +00:00
|
|
|
/* If answer parses as an integer, put it up on the
|
|
|
|
* statusbar. */
|
|
|
|
do_gotoline(parse_num(answer, NULL), FALSE);
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Fall through. */
|
2002-07-19 01:08:59 +00:00
|
|
|
default:
|
2004-02-24 20:41:39 +00:00
|
|
|
return -1;
|
2002-07-19 01:08:59 +00:00
|
|
|
}
|
2000-06-19 04:22:15 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
int is_whole_word(int curr_pos, const char *datastr, const char
|
|
|
|
*searchword)
|
2002-01-08 15:00:24 +00:00
|
|
|
{
|
2002-07-19 01:08:59 +00:00
|
|
|
size_t sln = curr_pos + strlen(searchword);
|
2002-01-08 15:00:24 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Start of line or previous character is not a letter and end of
|
|
|
|
* line or next character is not a letter. */
|
2003-01-13 01:35:15 +00:00
|
|
|
return (curr_pos < 1 || !isalpha((int)datastr[curr_pos - 1])) &&
|
|
|
|
(sln == strlen(datastr) || !isalpha((int)datastr[sln]));
|
2002-01-08 15:00:24 +00:00
|
|
|
}
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Look for needle, starting at current, column current_x. If
|
|
|
|
* no_sameline is nonzero, skip over begin when looking for needle.
|
|
|
|
* begin is the line where we first started searching, at column beginx.
|
|
|
|
* If can_display_wrap is nonzero, we put messages on the statusbar, and
|
|
|
|
* wrap around the file boundaries. The return value specifies whether
|
|
|
|
* we found anything. */
|
|
|
|
int findnextstr(int can_display_wrap, int wholeword, const filestruct
|
|
|
|
*begin, size_t beginx, const char *needle, int no_sameline)
|
2000-06-19 04:22:15 +00:00
|
|
|
{
|
2002-05-11 03:04:44 +00:00
|
|
|
filestruct *fileptr = current;
|
2004-02-24 20:41:39 +00:00
|
|
|
const char *rev_start = NULL, *found = NULL;
|
|
|
|
size_t current_x_find = 0;
|
|
|
|
/* Where needle was found. */
|
|
|
|
|
|
|
|
/* rev_start might end up 1 character before the start or after the
|
|
|
|
* end of the line. This won't be a problem because strstrwrapper()
|
|
|
|
* will return immediately and say that no match was found, and
|
|
|
|
* rev_start will be properly set when the search continues on the
|
|
|
|
* previous or next line. */
|
|
|
|
#ifndef NANO_SMALL
|
|
|
|
if (ISSET(REVERSE_SEARCH))
|
|
|
|
rev_start = fileptr->data + (current_x - 1);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
rev_start = fileptr->data + (current_x + 1);
|
2001-06-13 02:35:44 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Look for needle in searchstr. */
|
2004-03-15 20:26:30 +00:00
|
|
|
while (TRUE) {
|
2004-02-24 20:41:39 +00:00
|
|
|
found = strstrwrapper(fileptr->data, needle, rev_start);
|
2001-06-13 02:35:44 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
if (found != NULL && (!wholeword || is_whole_word(found -
|
|
|
|
fileptr->data, fileptr->data, needle))) {
|
|
|
|
if (!no_sameline || fileptr != current)
|
|
|
|
break;
|
2001-06-13 02:35:44 +00:00
|
|
|
}
|
2000-10-24 22:25:36 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Finished processing file, get out. */
|
|
|
|
if (search_last_line) {
|
|
|
|
if (can_display_wrap)
|
2001-06-13 02:35:44 +00:00
|
|
|
not_found_msg(needle);
|
2004-02-24 20:41:39 +00:00
|
|
|
return 0;
|
2000-10-24 22:25:36 +00:00
|
|
|
}
|
2004-02-24 20:41:39 +00:00
|
|
|
fileptr =
|
2001-10-02 03:54:40 +00:00
|
|
|
#ifndef NANO_SMALL
|
2004-02-24 20:41:39 +00:00
|
|
|
ISSET(REVERSE_SEARCH) ? fileptr->prev :
|
|
|
|
#endif
|
|
|
|
fileptr->next;
|
2002-06-21 03:20:06 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Start or end of buffer reached; wrap around. */
|
|
|
|
if (fileptr == NULL) {
|
|
|
|
if (!can_display_wrap)
|
|
|
|
return 0;
|
|
|
|
fileptr =
|
|
|
|
#ifndef NANO_SMALL
|
|
|
|
ISSET(REVERSE_SEARCH) ? filebot :
|
|
|
|
#endif
|
|
|
|
fileage;
|
|
|
|
if (can_display_wrap)
|
|
|
|
statusbar(_("Search Wrapped"));
|
|
|
|
}
|
2001-06-13 02:35:44 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Original start line reached. */
|
|
|
|
if (fileptr == begin)
|
2004-05-27 18:39:16 +00:00
|
|
|
search_last_line = TRUE;
|
2004-02-24 20:41:39 +00:00
|
|
|
rev_start = fileptr->data;
|
|
|
|
#ifndef NANO_SMALL
|
|
|
|
if (ISSET(REVERSE_SEARCH))
|
|
|
|
rev_start += strlen(fileptr->data);
|
|
|
|
#endif
|
|
|
|
}
|
2000-10-24 22:25:36 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* We found an instance. */
|
|
|
|
current_x_find = found - fileptr->data;
|
2001-06-13 02:35:44 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Ensure we haven't wrapped around again! */
|
|
|
|
if (search_last_line &&
|
|
|
|
#ifndef NANO_SMALL
|
|
|
|
((!ISSET(REVERSE_SEARCH) && current_x_find > beginx) ||
|
|
|
|
(ISSET(REVERSE_SEARCH) && current_x_find < beginx))
|
|
|
|
#else
|
|
|
|
current_x_find > beginx
|
|
|
|
#endif
|
|
|
|
) {
|
2001-06-13 02:35:44 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
if (can_display_wrap)
|
|
|
|
not_found_msg(needle);
|
|
|
|
return 0;
|
2000-06-19 04:22:15 +00:00
|
|
|
}
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Set globals now that we are sure we found something. */
|
2000-11-29 04:33:26 +00:00
|
|
|
current = fileptr;
|
|
|
|
current_x = current_x_find;
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
return 1;
|
2000-06-19 04:22:15 +00:00
|
|
|
}
|
|
|
|
|
2002-07-19 01:08:59 +00:00
|
|
|
/* Search for a string. */
|
2004-07-02 14:31:03 +00:00
|
|
|
void do_search(void)
|
2000-06-19 04:22:15 +00:00
|
|
|
{
|
2004-07-12 03:10:30 +00:00
|
|
|
size_t old_pww = placewewant, i, fileptr_x = current_x;
|
|
|
|
int didfind;
|
2004-02-24 20:41:39 +00:00
|
|
|
filestruct *fileptr = current;
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2003-09-10 20:08:00 +00:00
|
|
|
#ifndef DISABLE_WRAPPING
|
2000-06-19 04:22:15 +00:00
|
|
|
wrap_reset();
|
2003-09-10 20:08:00 +00:00
|
|
|
#endif
|
2000-10-27 05:48:05 +00:00
|
|
|
i = search_init(0);
|
2004-02-24 20:41:39 +00:00
|
|
|
if (i == -1) /* Cancel, Go to Line, blank search string, or
|
|
|
|
* regcomp() failed. */
|
2000-06-19 04:22:15 +00:00
|
|
|
search_abort();
|
2004-02-24 20:41:39 +00:00
|
|
|
else if (i == -2) /* Replace. */
|
2000-06-19 04:22:15 +00:00
|
|
|
do_replace();
|
2004-02-24 20:41:39 +00:00
|
|
|
#ifndef NANO_SMALL
|
|
|
|
else if (i == 1) /* Case Sensitive, Backwards, or Regexp search
|
|
|
|
* toggle. */
|
2000-06-19 04:22:15 +00:00
|
|
|
do_search();
|
2004-02-24 20:41:39 +00:00
|
|
|
#endif
|
2000-10-31 05:28:33 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
if (i != 0)
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2004-02-24 20:41:39 +00:00
|
|
|
|
|
|
|
/* If answer is now "", copy last_search into answer. */
|
2002-05-11 03:04:44 +00:00
|
|
|
if (answer[0] == '\0')
|
2000-11-16 06:01:10 +00:00
|
|
|
answer = mallocstrcpy(answer, last_search);
|
|
|
|
else
|
|
|
|
last_search = mallocstrcpy(last_search, answer);
|
|
|
|
|
2003-01-05 20:41:21 +00:00
|
|
|
#ifndef NANO_SMALL
|
2004-02-24 20:41:39 +00:00
|
|
|
/* If answer is not "", add this search string to the search history
|
|
|
|
* list. */
|
2003-01-14 23:36:11 +00:00
|
|
|
if (answer[0] != '\0')
|
2003-01-05 20:43:49 +00:00
|
|
|
update_history(&search_history, answer);
|
2004-02-24 20:41:39 +00:00
|
|
|
#endif
|
2003-01-05 20:41:21 +00:00
|
|
|
|
2004-05-27 18:39:16 +00:00
|
|
|
search_last_line = FALSE;
|
2004-02-24 20:41:39 +00:00
|
|
|
didfind = findnextstr(TRUE, FALSE, current, current_x, answer, FALSE);
|
2002-01-21 20:40:14 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Check to see if there's only one occurrence of the string and
|
|
|
|
* we're on it now. */
|
|
|
|
if (fileptr == current && fileptr_x == current_x && didfind) {
|
|
|
|
#ifdef HAVE_REGEX_H
|
|
|
|
/* Do the search again, skipping over the current line, if we're
|
|
|
|
* doing a bol and/or eol regex search ("^", "$", or "^$"), so
|
|
|
|
* that we find one only once per line. We should only end up
|
|
|
|
* back at the same position if the string isn't found again, in
|
|
|
|
* which case it's the only occurrence. */
|
2004-07-17 19:49:12 +00:00
|
|
|
if (ISSET(USE_REGEXP) && regexp_bol_or_eol(&search_regexp,
|
|
|
|
last_search)) {
|
|
|
|
didfind = findnextstr(TRUE, FALSE, current, current_x,
|
|
|
|
answer, TRUE);
|
2004-02-24 20:41:39 +00:00
|
|
|
if (fileptr == current && fileptr_x == current_x && !didfind)
|
|
|
|
statusbar(_("This is the only occurrence"));
|
|
|
|
} else {
|
|
|
|
#endif
|
|
|
|
statusbar(_("This is the only occurrence"));
|
|
|
|
#ifdef HAVE_REGEX_H
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2002-01-21 20:40:14 +00:00
|
|
|
|
2004-05-28 20:44:09 +00:00
|
|
|
placewewant = xplustabs();
|
2004-05-30 03:19:52 +00:00
|
|
|
edit_redraw(fileptr, old_pww);
|
2000-06-19 04:22:15 +00:00
|
|
|
search_abort();
|
|
|
|
}
|
|
|
|
|
2004-05-05 21:36:50 +00:00
|
|
|
#ifndef NANO_SMALL
|
2003-08-23 21:11:06 +00:00
|
|
|
/* Search for the next string without prompting. */
|
2004-07-02 14:31:03 +00:00
|
|
|
void do_research(void)
|
2003-08-23 21:11:06 +00:00
|
|
|
{
|
2004-07-12 03:10:30 +00:00
|
|
|
size_t old_pww = placewewant, fileptr_x = current_x;
|
|
|
|
int didfind;
|
2004-05-28 20:44:09 +00:00
|
|
|
filestruct *fileptr = current;
|
2003-08-23 21:11:06 +00:00
|
|
|
|
2003-09-10 20:08:00 +00:00
|
|
|
#ifndef DISABLE_WRAPPING
|
2003-08-23 21:11:06 +00:00
|
|
|
wrap_reset();
|
2003-09-10 20:08:00 +00:00
|
|
|
#endif
|
2003-08-23 21:11:06 +00:00
|
|
|
search_init_globals();
|
|
|
|
|
|
|
|
if (last_search[0] != '\0') {
|
|
|
|
|
|
|
|
#ifdef HAVE_REGEX_H
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Since answer is "", use last_search! */
|
2004-04-30 19:40:03 +00:00
|
|
|
if (ISSET(USE_REGEXP) && regexp_init(last_search) == 0)
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2003-08-23 21:11:06 +00:00
|
|
|
#endif
|
|
|
|
|
2004-05-27 18:39:16 +00:00
|
|
|
search_last_line = FALSE;
|
2004-07-17 19:49:12 +00:00
|
|
|
didfind = findnextstr(TRUE, FALSE, current, current_x,
|
|
|
|
last_search, FALSE);
|
2003-08-23 21:11:06 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Check to see if there's only one occurrence of the string and
|
|
|
|
* we're on it now. */
|
|
|
|
if (fileptr == current && fileptr_x == current_x && didfind) {
|
|
|
|
#ifdef HAVE_REGEX_H
|
|
|
|
/* Do the search again, skipping over the current line, if
|
|
|
|
* we're doing a bol and/or eol regex search ("^", "$", or
|
|
|
|
* "^$"), so that we find one only once per line. We should
|
|
|
|
* only end up back at the same position if the string isn't
|
|
|
|
* found again, in which case it's the only occurrence. */
|
2004-07-17 19:49:12 +00:00
|
|
|
if (ISSET(USE_REGEXP) && regexp_bol_or_eol(&search_regexp,
|
|
|
|
last_search)) {
|
|
|
|
didfind = findnextstr(TRUE, FALSE, current, current_x,
|
|
|
|
answer, TRUE);
|
2004-02-24 20:41:39 +00:00
|
|
|
if (fileptr == current && fileptr_x == current_x && !didfind)
|
|
|
|
statusbar(_("This is the only occurrence"));
|
|
|
|
} else {
|
|
|
|
#endif
|
|
|
|
statusbar(_("This is the only occurrence"));
|
|
|
|
#ifdef HAVE_REGEX_H
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2003-08-23 21:11:06 +00:00
|
|
|
} else
|
|
|
|
statusbar(_("No current search pattern"));
|
|
|
|
|
2004-05-28 20:44:09 +00:00
|
|
|
placewewant = xplustabs();
|
2004-05-30 03:19:52 +00:00
|
|
|
edit_redraw(fileptr, old_pww);
|
2003-08-23 21:11:06 +00:00
|
|
|
search_abort();
|
|
|
|
}
|
2004-05-05 21:36:50 +00:00
|
|
|
#endif
|
2003-08-23 21:11:06 +00:00
|
|
|
|
2000-06-19 04:22:15 +00:00
|
|
|
void replace_abort(void)
|
|
|
|
{
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Identical to search_abort(), so we'll call it here. If it does
|
|
|
|
* something different later, we can change it back. For now, it's
|
|
|
|
* just a waste to duplicate code. */
|
2000-07-28 01:18:10 +00:00
|
|
|
search_abort();
|
2000-10-24 22:25:36 +00:00
|
|
|
placewewant = xplustabs();
|
2000-07-07 01:49:52 +00:00
|
|
|
}
|
|
|
|
|
2000-09-06 13:39:17 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2000-07-07 01:49:52 +00:00
|
|
|
int replace_regexp(char *string, int create_flag)
|
|
|
|
{
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Split personality here - if create_flag is zero, just calculate
|
2000-07-07 01:49:52 +00:00
|
|
|
* the size of the replacement line (necessary because of
|
2004-02-24 20:41:39 +00:00
|
|
|
* subexpressions \1 to \9 in the replaced text). */
|
2000-07-07 01:49:52 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
const char *c = last_replace;
|
2000-10-26 01:44:42 +00:00
|
|
|
int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
|
2004-02-24 20:41:39 +00:00
|
|
|
int new_size = strlen(current->data) + 1 - search_match_count;
|
2000-07-07 01:49:52 +00:00
|
|
|
|
2002-07-19 01:08:59 +00:00
|
|
|
/* Iterate through the replacement text to handle subexpression
|
|
|
|
* replacement using \1, \2, \3, etc. */
|
2002-12-22 16:30:00 +00:00
|
|
|
while (*c != '\0') {
|
2004-02-24 20:41:39 +00:00
|
|
|
int num = (int)(*(c + 1) - '0');
|
|
|
|
|
|
|
|
if (*c != '\\' || num < 1 || num > 9 || num > search_regexp.re_nsub) {
|
2000-10-26 01:44:42 +00:00
|
|
|
if (create_flag)
|
|
|
|
*string++ = *c;
|
|
|
|
c++;
|
|
|
|
new_size++;
|
|
|
|
} else {
|
2004-02-24 20:41:39 +00:00
|
|
|
int i = regmatches[num].rm_eo - regmatches[num].rm_so;
|
2000-10-26 01:44:42 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Skip over the replacement expression. */
|
|
|
|
c += 2;
|
2000-10-26 01:44:42 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* But add the length of the subexpression to new_size. */
|
|
|
|
new_size += i;
|
2000-10-26 01:44:42 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* And if create_flag is nonzero, append the result of the
|
|
|
|
* subexpression match to the new line. */
|
|
|
|
if (create_flag) {
|
|
|
|
strncpy(string, current->data + current_x +
|
|
|
|
regmatches[num].rm_so, i);
|
|
|
|
string += i;
|
2000-10-26 01:44:42 +00:00
|
|
|
}
|
|
|
|
}
|
2000-07-07 01:49:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (create_flag)
|
2003-01-13 01:35:15 +00:00
|
|
|
*string = '\0';
|
2000-07-07 01:49:52 +00:00
|
|
|
|
|
|
|
return new_size;
|
|
|
|
}
|
2000-07-07 02:35:34 +00:00
|
|
|
#endif
|
2000-10-26 01:44:42 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
char *replace_line(const char *needle)
|
2000-07-07 01:49:52 +00:00
|
|
|
{
|
2004-02-24 20:41:39 +00:00
|
|
|
char *copy;
|
2000-07-07 01:49:52 +00:00
|
|
|
int new_line_size;
|
|
|
|
int search_match_count;
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Calculate the size of the new line. */
|
2000-09-06 13:39:17 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2000-07-07 01:49:52 +00:00
|
|
|
if (ISSET(USE_REGEXP)) {
|
2004-05-23 21:11:14 +00:00
|
|
|
search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
|
2000-10-26 01:44:42 +00:00
|
|
|
new_line_size = replace_regexp(NULL, 0);
|
2000-07-07 01:49:52 +00:00
|
|
|
} else {
|
2000-07-07 02:35:34 +00:00
|
|
|
#endif
|
2004-02-24 20:41:39 +00:00
|
|
|
search_match_count = strlen(needle);
|
|
|
|
new_line_size = strlen(current->data) - search_match_count +
|
|
|
|
strlen(answer) + 1;
|
|
|
|
#ifdef HAVE_REGEX_H
|
2000-07-07 01:49:52 +00:00
|
|
|
}
|
2004-02-24 20:41:39 +00:00
|
|
|
#endif
|
2000-10-26 01:44:42 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Create the buffer. */
|
2001-05-17 11:35:43 +00:00
|
|
|
copy = charalloc(new_line_size);
|
2000-07-07 01:49:52 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* The head of the original line. */
|
2000-07-07 01:49:52 +00:00
|
|
|
strncpy(copy, current->data, current_x);
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* The replacement text. */
|
2000-09-06 13:39:17 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2004-02-24 20:41:39 +00:00
|
|
|
if (ISSET(USE_REGEXP))
|
|
|
|
replace_regexp(copy + current_x, TRUE);
|
2000-07-07 01:49:52 +00:00
|
|
|
else
|
2000-07-07 02:35:34 +00:00
|
|
|
#endif
|
2004-02-24 20:41:39 +00:00
|
|
|
strcpy(copy + current_x, answer);
|
2000-07-07 01:49:52 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* The tail of the original line. */
|
|
|
|
assert(current_x + search_match_count <= strlen(current->data));
|
|
|
|
strcat(copy, current->data + current_x + search_match_count);
|
2000-07-07 01:49:52 +00:00
|
|
|
|
|
|
|
return copy;
|
2000-06-19 04:22:15 +00:00
|
|
|
}
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Step through each replace word and prompt user before replacing.
|
|
|
|
* Parameters real_current and real_current_x are needed by the internal
|
|
|
|
* speller, to allow the cursor position to be updated when a word
|
|
|
|
* before the cursor is replaced by a shorter word.
|
|
|
|
*
|
|
|
|
* needle is the string to seek. We replace it with answer. Return -1
|
|
|
|
* if needle isn't found, else the number of replacements performed. */
|
|
|
|
int do_replace_loop(const char *needle, const filestruct *real_current,
|
|
|
|
size_t *real_current_x, int wholewords)
|
2000-06-19 04:22:15 +00:00
|
|
|
{
|
2004-07-12 03:10:30 +00:00
|
|
|
int old_pww = placewewant, replaceall = FALSE, numreplaced = -1;
|
2004-02-24 20:41:39 +00:00
|
|
|
size_t current_x_save = current_x;
|
2004-05-28 20:44:09 +00:00
|
|
|
const filestruct *current_save = current;
|
2003-10-31 17:53:38 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2004-01-03 21:42:25 +00:00
|
|
|
/* The starting-line match and bol/eol regex flags. */
|
2004-04-05 01:08:14 +00:00
|
|
|
int begin_line = FALSE, bol_or_eol = FALSE;
|
2003-10-31 17:53:38 +00:00
|
|
|
#endif
|
2004-02-24 20:41:39 +00:00
|
|
|
#ifndef NANO_SMALL
|
|
|
|
int mark_set = ISSET(MARK_ISSET);
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
UNSET(MARK_ISSET);
|
|
|
|
edit_refresh();
|
|
|
|
#endif
|
2003-09-16 01:16:49 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
while (findnextstr(TRUE, wholewords, current_save, current_x_save,
|
|
|
|
needle,
|
2003-11-28 16:04:24 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2004-02-24 20:41:39 +00:00
|
|
|
/* We should find a bol and/or eol regex only once per line. If
|
|
|
|
* the bol_or_eol flag is set, it means that the last search
|
|
|
|
* found one on the beginning line, so we should skip over the
|
|
|
|
* beginning line when doing this search. */
|
|
|
|
bol_or_eol
|
2003-11-28 16:04:24 +00:00
|
|
|
#else
|
2004-02-24 20:41:39 +00:00
|
|
|
FALSE
|
2003-11-28 16:04:24 +00:00
|
|
|
#endif
|
2004-02-24 20:41:39 +00:00
|
|
|
) != 0) {
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
size_t match_len;
|
2003-11-28 16:04:24 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_REGEX_H
|
2004-02-24 20:41:39 +00:00
|
|
|
/* If the bol_or_eol flag is set, we've found a match on the
|
2003-12-24 03:13:44 +00:00
|
|
|
* beginning line already, and we're still on the beginning line
|
|
|
|
* after the search, it means that we've wrapped around, so
|
2003-11-28 16:04:24 +00:00
|
|
|
* we're done. */
|
2004-02-24 20:41:39 +00:00
|
|
|
if (bol_or_eol && begin_line && current == real_current)
|
|
|
|
break;
|
|
|
|
/* Otherwise, set the begin_line flag if we've found a match on
|
|
|
|
* the beginning line, reset the bol_or_eol flag, and
|
|
|
|
* continue. */
|
2003-11-28 16:04:24 +00:00
|
|
|
else {
|
2004-02-24 20:41:39 +00:00
|
|
|
if (current == real_current)
|
2004-04-05 01:08:14 +00:00
|
|
|
begin_line = TRUE;
|
|
|
|
bol_or_eol = FALSE;
|
2003-11-28 16:04:24 +00:00
|
|
|
}
|
|
|
|
#endif
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2004-05-28 20:44:09 +00:00
|
|
|
if (!replaceall)
|
2004-05-30 03:19:52 +00:00
|
|
|
edit_redraw(current_save, old_pww);
|
2003-01-13 01:35:15 +00:00
|
|
|
|
2003-09-16 01:16:49 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
|
|
|
if (ISSET(USE_REGEXP))
|
|
|
|
match_len = regmatches[0].rm_eo - regmatches[0].rm_so;
|
|
|
|
else
|
|
|
|
#endif
|
2004-02-24 20:41:39 +00:00
|
|
|
match_len = strlen(needle);
|
|
|
|
|
|
|
|
/* Record for the return value that we found the search string. */
|
|
|
|
if (numreplaced == -1)
|
|
|
|
numreplaced = 0;
|
2003-09-16 01:16:49 +00:00
|
|
|
|
2000-11-29 04:33:26 +00:00
|
|
|
if (!replaceall) {
|
2003-09-16 01:16:49 +00:00
|
|
|
char *exp_word;
|
|
|
|
size_t xpt = xplustabs();
|
|
|
|
|
|
|
|
exp_word = display_string(current->data, xpt,
|
|
|
|
strnlenpt(current->data, match_len + current_x) - xpt);
|
|
|
|
|
2000-11-29 04:33:26 +00:00
|
|
|
curs_set(0);
|
2003-09-16 01:16:49 +00:00
|
|
|
do_replace_highlight(TRUE, exp_word);
|
2000-11-29 04:33:26 +00:00
|
|
|
|
2004-03-15 20:26:30 +00:00
|
|
|
i = do_yesno(TRUE, _("Replace this instance?"));
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2003-09-16 01:16:49 +00:00
|
|
|
do_replace_highlight(FALSE, exp_word);
|
|
|
|
free(exp_word);
|
2000-11-29 04:33:26 +00:00
|
|
|
curs_set(1);
|
2003-12-29 02:15:23 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
if (i == -1) /* We canceled the replace. */
|
2003-12-29 02:15:23 +00:00
|
|
|
break;
|
2000-11-29 04:33:26 +00:00
|
|
|
}
|
|
|
|
|
2003-12-24 03:13:44 +00:00
|
|
|
#ifdef HAVE_REGEX_H
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Set the bol_or_eol flag if we're doing a bol and/or eol regex
|
2004-01-03 21:42:25 +00:00
|
|
|
* replace ("^", "$", or "^$"). */
|
2004-02-24 20:41:39 +00:00
|
|
|
if (ISSET(USE_REGEXP) && regexp_bol_or_eol(&search_regexp,
|
|
|
|
needle))
|
2004-04-05 01:08:14 +00:00
|
|
|
bol_or_eol = TRUE;
|
2003-12-24 03:13:44 +00:00
|
|
|
#endif
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
if (i > 0 || replaceall) { /* Yes, replace it!!!! */
|
2003-12-29 02:15:23 +00:00
|
|
|
char *copy;
|
|
|
|
int length_change;
|
2003-01-26 04:26:25 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
if (i == 2)
|
2004-07-12 03:10:30 +00:00
|
|
|
replaceall = TRUE;
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
copy = replace_line(needle);
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2003-01-26 04:26:25 +00:00
|
|
|
length_change = strlen(copy) - strlen(current->data);
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2003-01-26 04:26:25 +00:00
|
|
|
#ifndef NANO_SMALL
|
|
|
|
if (current == mark_beginbuf && mark_beginx > current_x) {
|
|
|
|
if (mark_beginx < current_x + match_len)
|
|
|
|
mark_beginx = current_x;
|
|
|
|
else
|
|
|
|
mark_beginx += length_change;
|
|
|
|
}
|
|
|
|
#endif
|
2002-07-19 01:08:59 +00:00
|
|
|
|
2003-01-26 04:26:25 +00:00
|
|
|
assert(0 <= match_len + length_change);
|
2004-02-24 20:41:39 +00:00
|
|
|
if (current == real_current && current_x <= *real_current_x) {
|
|
|
|
if (*real_current_x < current_x + match_len)
|
|
|
|
*real_current_x = current_x + match_len;
|
|
|
|
*real_current_x += length_change;
|
2003-01-26 04:26:25 +00:00
|
|
|
}
|
2002-02-16 20:03:44 +00:00
|
|
|
|
2003-01-26 04:26:25 +00:00
|
|
|
/* Set the cursor at the last character of the replacement
|
2003-11-28 16:04:24 +00:00
|
|
|
* text, so searching will resume after the replacement
|
|
|
|
* text. Note that current_x might be set to -1 here. */
|
2003-01-26 04:26:25 +00:00
|
|
|
#ifndef NANO_SMALL
|
2003-12-24 03:13:44 +00:00
|
|
|
if (!ISSET(REVERSE_SEARCH))
|
2003-01-26 04:26:25 +00:00
|
|
|
#endif
|
|
|
|
current_x += match_len + length_change - 1;
|
2002-02-16 20:03:44 +00:00
|
|
|
|
2003-10-31 17:53:38 +00:00
|
|
|
/* Cleanup. */
|
2003-01-26 04:26:25 +00:00
|
|
|
totsize += length_change;
|
|
|
|
free(current->data);
|
|
|
|
current->data = copy;
|
2000-10-24 22:25:36 +00:00
|
|
|
|
2004-05-28 20:44:09 +00:00
|
|
|
if (!replaceall) {
|
|
|
|
#ifdef ENABLE_COLOR
|
|
|
|
if (ISSET(COLOR_SYNTAX))
|
|
|
|
edit_refresh();
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
update_line(current, current_x);
|
|
|
|
}
|
|
|
|
|
2000-06-19 04:22:15 +00:00
|
|
|
set_modified();
|
|
|
|
numreplaced++;
|
2003-12-29 02:15:23 +00:00
|
|
|
}
|
2000-06-19 04:22:15 +00:00
|
|
|
}
|
|
|
|
|
2003-01-13 01:35:15 +00:00
|
|
|
/* If text has been added to the magicline, make a new magicline. */
|
|
|
|
if (filebot->data[0] != '\0')
|
|
|
|
new_magicline();
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
#ifndef NANO_SMALL
|
|
|
|
if (mark_set)
|
|
|
|
SET(MARK_ISSET);
|
|
|
|
#endif
|
|
|
|
|
2000-11-05 16:52:21 +00:00
|
|
|
return numreplaced;
|
|
|
|
}
|
|
|
|
|
2003-01-13 01:35:15 +00:00
|
|
|
/* Replace a string. */
|
2004-07-02 14:31:03 +00:00
|
|
|
void do_replace(void)
|
2000-11-05 16:52:21 +00:00
|
|
|
{
|
2004-02-24 20:41:39 +00:00
|
|
|
int i, numreplaced;
|
|
|
|
filestruct *edittop_save, *begin;
|
|
|
|
size_t beginx;
|
2000-11-05 16:52:21 +00:00
|
|
|
|
2001-04-22 07:10:21 +00:00
|
|
|
if (ISSET(VIEW_MODE)) {
|
|
|
|
print_view_warning();
|
|
|
|
replace_abort();
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2001-04-22 07:10:21 +00:00
|
|
|
}
|
|
|
|
|
2000-11-05 16:52:21 +00:00
|
|
|
i = search_init(1);
|
2004-02-24 20:41:39 +00:00
|
|
|
if (i == -1) { /* Cancel, Go to Line, blank search
|
|
|
|
* string, or regcomp() failed. */
|
2000-11-05 16:52:21 +00:00
|
|
|
replace_abort();
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2004-02-24 20:41:39 +00:00
|
|
|
} else if (i == -2) { /* No Replace. */
|
2000-11-05 16:52:21 +00:00
|
|
|
do_search();
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2004-02-24 20:41:39 +00:00
|
|
|
} else if (i == 1) /* Case Sensitive, Backwards, or Regexp
|
|
|
|
* search toggle. */
|
|
|
|
do_replace();
|
|
|
|
|
|
|
|
if (i != 0)
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2000-11-05 16:52:21 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* If answer is not "", add answer to the search history list and
|
|
|
|
* copy answer into last_search. */
|
|
|
|
if (answer[0] != '\0') {
|
2003-01-05 20:41:21 +00:00
|
|
|
#ifndef NANO_SMALL
|
2003-01-05 20:43:49 +00:00
|
|
|
update_history(&search_history, answer);
|
2004-02-24 20:41:39 +00:00
|
|
|
#endif
|
2002-07-19 01:08:59 +00:00
|
|
|
last_search = mallocstrcpy(last_search, answer);
|
2004-02-24 20:41:39 +00:00
|
|
|
}
|
2002-07-19 01:08:59 +00:00
|
|
|
|
2003-01-05 20:41:21 +00:00
|
|
|
#ifndef NANO_SMALL
|
2003-01-05 21:47:06 +00:00
|
|
|
replace_history.current = (historytype *)&replace_history.next;
|
|
|
|
last_replace = mallocstrcpy(last_replace, "");
|
2003-01-05 20:41:21 +00:00
|
|
|
#endif
|
2003-01-05 21:47:06 +00:00
|
|
|
|
2004-03-15 20:26:30 +00:00
|
|
|
i = statusq(FALSE, replace_list_2, last_replace,
|
2003-01-05 20:41:21 +00:00
|
|
|
#ifndef NANO_SMALL
|
2004-03-15 20:26:30 +00:00
|
|
|
&replace_history,
|
2003-01-05 20:41:21 +00:00
|
|
|
#endif
|
2004-03-15 20:26:30 +00:00
|
|
|
_("Replace with"));
|
2003-01-05 21:47:06 +00:00
|
|
|
|
2003-01-05 20:41:21 +00:00
|
|
|
#ifndef NANO_SMALL
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Add this replace string to the replace history list. i == 0
|
|
|
|
* means that the string is not "". */
|
|
|
|
if (i == 0)
|
2003-01-05 20:41:21 +00:00
|
|
|
update_history(&replace_history, answer);
|
2004-02-24 20:41:39 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (i != 0 && i != -2) {
|
|
|
|
if (i == -1) { /* Cancel. */
|
|
|
|
if (last_replace[0] != '\0')
|
|
|
|
answer = mallocstrcpy(answer, last_replace);
|
|
|
|
statusbar(_("Replace Cancelled"));
|
|
|
|
}
|
|
|
|
replace_abort();
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2004-02-24 20:41:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
last_replace = mallocstrcpy(last_replace, answer);
|
2000-11-05 16:52:21 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Save where we are. */
|
2000-11-05 16:52:21 +00:00
|
|
|
begin = current;
|
2002-02-16 20:03:44 +00:00
|
|
|
beginx = current_x;
|
2004-02-24 20:41:39 +00:00
|
|
|
edittop_save = edittop;
|
|
|
|
search_last_line = FALSE;
|
2000-11-05 16:52:21 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
numreplaced = do_replace_loop(last_search, begin, &beginx, FALSE);
|
2000-11-05 16:52:21 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Restore where we were. */
|
2000-06-19 04:22:15 +00:00
|
|
|
current = begin;
|
2002-02-16 20:03:44 +00:00
|
|
|
current_x = beginx;
|
2000-06-19 04:22:15 +00:00
|
|
|
renumber_all();
|
2004-02-24 20:41:39 +00:00
|
|
|
edit_update(edittop_save, TOP);
|
2003-01-13 01:35:15 +00:00
|
|
|
|
|
|
|
if (numreplaced >= 0)
|
2003-02-10 11:30:11 +00:00
|
|
|
statusbar(P_("Replaced %d occurrence", "Replaced %d occurrences",
|
2003-01-13 01:35:15 +00:00
|
|
|
numreplaced), numreplaced);
|
|
|
|
|
2000-06-19 04:22:15 +00:00
|
|
|
replace_abort();
|
|
|
|
}
|
|
|
|
|
2004-07-12 16:07:14 +00:00
|
|
|
void do_gotoline(ssize_t line, int save_pos)
|
2000-06-19 04:22:15 +00:00
|
|
|
{
|
2001-04-20 01:59:55 +00:00
|
|
|
if (line <= 0) { /* Ask for it */
|
2004-03-15 20:26:30 +00:00
|
|
|
char *ans = mallocstrcpy(NULL, answer);
|
|
|
|
int st = statusq(FALSE, goto_list, line != 0 ? ans : "",
|
2003-01-13 01:35:15 +00:00
|
|
|
#ifndef NANO_SMALL
|
2004-03-15 20:26:30 +00:00
|
|
|
NULL,
|
2003-01-13 01:35:15 +00:00
|
|
|
#endif
|
2004-03-15 20:26:30 +00:00
|
|
|
_("Enter line number"));
|
|
|
|
|
|
|
|
free(ans);
|
2003-02-10 02:55:03 +00:00
|
|
|
|
|
|
|
/* Cancel, or Enter with blank string. */
|
|
|
|
if (st == -1 || st == -2)
|
2000-06-19 04:22:15 +00:00
|
|
|
statusbar(_("Aborted"));
|
2003-02-10 02:55:03 +00:00
|
|
|
if (st != 0) {
|
2003-04-15 01:15:09 +00:00
|
|
|
display_main_list();
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2000-06-19 04:22:15 +00:00
|
|
|
}
|
2001-04-20 01:59:55 +00:00
|
|
|
|
2004-07-12 16:26:55 +00:00
|
|
|
line = (ssize_t)atol(answer);
|
2001-04-20 01:59:55 +00:00
|
|
|
|
2004-03-11 02:20:25 +00:00
|
|
|
/* Bounds check. */
|
2001-04-20 01:59:55 +00:00
|
|
|
if (line <= 0) {
|
|
|
|
statusbar(_("Come on, be reasonable"));
|
2003-04-15 01:15:09 +00:00
|
|
|
display_main_list();
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2000-06-19 04:22:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-06-21 03:20:06 +00:00
|
|
|
for (current = fileage; current->next != NULL && line > 1; line--)
|
2001-04-20 01:59:55 +00:00
|
|
|
current = current->next;
|
2000-06-19 04:22:15 +00:00
|
|
|
|
2001-04-20 01:59:55 +00:00
|
|
|
current_x = 0;
|
2001-07-11 02:08:33 +00:00
|
|
|
|
2004-07-02 14:31:03 +00:00
|
|
|
/* If save_pos is TRUE, don't change the cursor position when
|
2004-03-11 02:20:25 +00:00
|
|
|
* updating the edit window. */
|
|
|
|
edit_update(current, save_pos ? NONE : CENTER);
|
|
|
|
|
2002-06-21 03:20:06 +00:00
|
|
|
placewewant = 0;
|
2003-04-15 01:15:09 +00:00
|
|
|
display_main_list();
|
2000-06-19 04:22:15 +00:00
|
|
|
}
|
|
|
|
|
2004-07-02 14:31:03 +00:00
|
|
|
void do_gotoline_void(void)
|
2000-06-19 04:22:15 +00:00
|
|
|
{
|
2004-07-02 14:31:03 +00:00
|
|
|
do_gotoline(0, FALSE);
|
2000-06-19 04:22:15 +00:00
|
|
|
}
|
2001-09-19 03:19:43 +00:00
|
|
|
|
2002-06-28 22:45:14 +00:00
|
|
|
#if defined(ENABLE_MULTIBUFFER) || !defined(DISABLE_SPELLER)
|
2001-11-29 03:43:08 +00:00
|
|
|
void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant)
|
2001-09-19 03:19:43 +00:00
|
|
|
{
|
|
|
|
/* since do_gotoline() resets the x-coordinate but not the
|
|
|
|
y-coordinate, set the coordinates up this way */
|
|
|
|
current_y = pos_y;
|
2004-07-02 14:31:03 +00:00
|
|
|
do_gotoline(line, TRUE);
|
2001-11-29 03:43:08 +00:00
|
|
|
|
2002-06-13 00:40:19 +00:00
|
|
|
/* make sure that the x-coordinate is sane here */
|
|
|
|
if (pos_x > strlen(current->data))
|
|
|
|
pos_x = strlen(current->data);
|
2001-11-29 03:43:08 +00:00
|
|
|
|
|
|
|
/* set the rest of the coordinates up */
|
2001-09-19 03:19:43 +00:00
|
|
|
current_x = pos_x;
|
|
|
|
placewewant = pos_placewewant;
|
|
|
|
update_line(current, pos_x);
|
|
|
|
}
|
|
|
|
#endif
|
2001-09-22 22:14:25 +00:00
|
|
|
|
|
|
|
#if !defined(NANO_SMALL) && defined(HAVE_REGEX_H)
|
2004-07-02 14:31:03 +00:00
|
|
|
void do_find_bracket(void)
|
2001-09-22 22:14:25 +00:00
|
|
|
{
|
|
|
|
char ch_under_cursor, wanted_ch;
|
2002-09-13 18:14:04 +00:00
|
|
|
const char *pos, *brackets = "([{<>}])";
|
2001-09-22 22:14:25 +00:00
|
|
|
char regexp_pat[] = "[ ]";
|
2004-07-03 03:09:12 +00:00
|
|
|
int old_pww = placewewant, current_x_save, count = 1;
|
|
|
|
long flags_save;
|
2001-09-22 22:14:25 +00:00
|
|
|
filestruct *current_save;
|
|
|
|
|
|
|
|
ch_under_cursor = current->data[current_x];
|
2003-01-05 20:41:21 +00:00
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
pos = strchr(brackets, ch_under_cursor);
|
|
|
|
if (ch_under_cursor == '\0' || pos == NULL) {
|
2001-09-22 22:14:25 +00:00
|
|
|
statusbar(_("Not a bracket"));
|
2004-07-02 14:31:03 +00:00
|
|
|
return;
|
2001-09-22 22:14:25 +00:00
|
|
|
}
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
assert(strlen(brackets) % 2 == 0);
|
|
|
|
wanted_ch = brackets[(strlen(brackets) - 1) - (pos - brackets)];
|
2001-09-22 22:14:25 +00:00
|
|
|
|
|
|
|
current_x_save = current_x;
|
|
|
|
current_save = current;
|
2004-07-03 03:09:12 +00:00
|
|
|
flags_save = flags;
|
2001-09-22 22:14:25 +00:00
|
|
|
SET(USE_REGEXP);
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Apparent near redundancy with regexp_pat[] here is needed.
|
|
|
|
* "[][]" works, "[[]]" doesn't. */
|
2001-09-22 22:14:25 +00:00
|
|
|
|
2004-07-17 19:49:12 +00:00
|
|
|
if (pos < brackets + (strlen(brackets) / 2)) {
|
|
|
|
/* On a left bracket. */
|
2001-09-22 22:14:25 +00:00
|
|
|
regexp_pat[1] = wanted_ch;
|
|
|
|
regexp_pat[2] = ch_under_cursor;
|
|
|
|
UNSET(REVERSE_SEARCH);
|
2004-07-17 19:49:12 +00:00
|
|
|
} else {
|
|
|
|
/* On a right bracket. */
|
2001-09-22 22:14:25 +00:00
|
|
|
regexp_pat[1] = ch_under_cursor;
|
|
|
|
regexp_pat[2] = wanted_ch;
|
|
|
|
SET(REVERSE_SEARCH);
|
|
|
|
}
|
|
|
|
|
|
|
|
regexp_init(regexp_pat);
|
2004-02-24 20:41:39 +00:00
|
|
|
/* We constructed regexp_pat to be a valid expression. */
|
2004-04-30 19:40:03 +00:00
|
|
|
assert(regexp_compiled);
|
2001-09-22 22:14:25 +00:00
|
|
|
|
2004-05-27 18:39:16 +00:00
|
|
|
search_last_line = FALSE;
|
2004-03-15 20:26:30 +00:00
|
|
|
while (TRUE) {
|
2004-07-17 19:49:12 +00:00
|
|
|
if (findnextstr(FALSE, FALSE, current, current_x, regexp_pat,
|
|
|
|
FALSE) != 0) {
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Found identical bracket. */
|
2003-01-13 01:35:15 +00:00
|
|
|
if (current->data[current_x] == ch_under_cursor)
|
2001-09-22 22:14:25 +00:00
|
|
|
count++;
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Found complementary bracket. */
|
|
|
|
else if (--count == 0) {
|
|
|
|
placewewant = xplustabs();
|
2004-05-30 03:19:52 +00:00
|
|
|
edit_redraw(current_save, old_pww);
|
2004-02-24 20:41:39 +00:00
|
|
|
break;
|
2001-09-22 22:14:25 +00:00
|
|
|
}
|
2003-01-13 01:35:15 +00:00
|
|
|
} else {
|
2004-02-24 20:41:39 +00:00
|
|
|
/* Didn't find either a left or right bracket. */
|
2001-09-22 22:14:25 +00:00
|
|
|
statusbar(_("No matching bracket"));
|
|
|
|
current_x = current_x_save;
|
|
|
|
current = current_save;
|
2003-04-15 01:15:09 +00:00
|
|
|
update_line(current, current_x);
|
2001-09-22 22:14:25 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-24 20:41:39 +00:00
|
|
|
regexp_cleanup();
|
2004-07-03 03:09:12 +00:00
|
|
|
flags = flags_save;
|
2001-09-22 22:14:25 +00:00
|
|
|
}
|
|
|
|
#endif
|
2003-01-05 20:41:21 +00:00
|
|
|
|
|
|
|
#ifndef NANO_SMALL
|
|
|
|
/*
|
|
|
|
* search and replace history list support functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* initialize 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find first node containing string *s in history list *h */
|
|
|
|
historytype *find_node(historytype *h, char *s)
|
|
|
|
{
|
2003-01-16 22:16:38 +00:00
|
|
|
for (; h->next != NULL; h = h->next)
|
2003-01-05 20:41:21 +00:00
|
|
|
if (strcmp(s, h->data) == 0)
|
|
|
|
return h;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove node *r */
|
|
|
|
void remove_node(historytype *r)
|
|
|
|
{
|
|
|
|
r->prev->next = r->next;
|
|
|
|
r->next->prev = r->prev;
|
|
|
|
free(r->data);
|
|
|
|
free(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add a node after node *h */
|
|
|
|
void insert_node(historytype *h, const char *s)
|
|
|
|
{
|
|
|
|
historytype *a;
|
|
|
|
|
2003-06-14 20:41:34 +00:00
|
|
|
a = (historytype *)nmalloc(sizeof(historytype));
|
2003-01-05 20:41:21 +00:00
|
|
|
a->next = h->next;
|
|
|
|
a->prev = h->next->prev;
|
|
|
|
h->next->prev = a;
|
|
|
|
h->next = a;
|
|
|
|
a->data = mallocstrcpy(NULL, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update history list */
|
|
|
|
void update_history(historyheadtype *h, char *s)
|
|
|
|
{
|
|
|
|
historytype *p;
|
|
|
|
|
2003-01-16 22:16:38 +00:00
|
|
|
if ((p = find_node(h->next, s)) != NULL) {
|
|
|
|
if (p == h->next) /* catch delete and re-insert of
|
|
|
|
same string in 1st node */
|
2003-01-05 20:41:21 +00:00
|
|
|
goto up_hs;
|
2003-01-16 22:16:38 +00:00
|
|
|
remove_node(p); /* delete identical older string */
|
2003-01-05 20:41:21 +00:00
|
|
|
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++;
|
2003-01-16 23:44:46 +00:00
|
|
|
SET(HISTORY_CHANGED);
|
2003-01-16 22:16:38 +00:00
|
|
|
up_hs:
|
2003-01-05 20:41:21 +00:00
|
|
|
h->current = h->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return a pointer to either the next older history or NULL if no more */
|
|
|
|
char *get_history_older(historyheadtype *h)
|
|
|
|
{
|
2003-01-16 22:16:38 +00:00
|
|
|
if (h->current->next != NULL) { /* any older entries? */
|
2003-01-05 20:41:21 +00:00
|
|
|
h->current = h->current->next; /* yes */
|
|
|
|
return h->current->data; /* return it */
|
|
|
|
}
|
|
|
|
return NULL; /* end of list */
|
|
|
|
}
|
|
|
|
|
|
|
|
char *get_history_newer(historyheadtype *h)
|
|
|
|
{
|
2003-01-16 22:16:38 +00:00
|
|
|
if (h->current->prev != NULL) {
|
2003-01-05 20:41:21 +00:00
|
|
|
h->current = h->current->prev;
|
2003-01-16 22:16:38 +00:00
|
|
|
if (h->current->prev != NULL)
|
2003-01-05 20:41:21 +00:00
|
|
|
return h->current->data;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get a completion */
|
|
|
|
char *get_history_completion(historyheadtype *h, char *s)
|
|
|
|
{
|
|
|
|
historytype *p;
|
|
|
|
|
2003-01-16 22:16:38 +00:00
|
|
|
for (p = h->current->next; p->next != NULL; p = p->next) {
|
|
|
|
if (strncmp(s, p->data, h->len) == 0 && strlen(p->data) != h->len) {
|
2003-01-05 20:41:21 +00:00
|
|
|
h->current = p;
|
|
|
|
return p->data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
h->current = (historytype*)h;
|
|
|
|
null_at(&s, h->len);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free a history list */
|
|
|
|
void free_history(historyheadtype *h)
|
|
|
|
{
|
|
|
|
historytype *p, *n;
|
|
|
|
|
2003-01-16 22:16:38 +00:00
|
|
|
for (p = h->next; (n = p->next); p = n)
|
2003-01-05 20:41:21 +00:00
|
|
|
remove_node(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* end of history support functions */
|
|
|
|
#endif /* !NANO_SMALL */
|