blob: 3bd691a5f0c548b26e1e457856a135fc6adbf3e1 [file] [log] [blame]
Chris Allegretta11b00112000-08-06 21:13:45 +00001/* $Id$ */
Chris Allegrettabceb1b22000-06-19 04:22:15 +00002/**************************************************************************
3 * search.c *
4 * *
5 * Copyright (C) 2000 Chris Allegretta *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 1, or (at your option) *
9 * any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
19 * *
20 **************************************************************************/
21
22#include <stdlib.h>
23#include <string.h>
Chris Allegretta47805612000-07-07 02:35:34 +000024#include <unistd.h>
Chris Allegrettabceb1b22000-06-19 04:22:15 +000025#include <stdio.h>
Chris Allegretta27eb13f2000-11-05 16:52:21 +000026#include <ctype.h>
Chris Allegrettabceb1b22000-06-19 04:22:15 +000027#include "config.h"
28#include "proto.h"
29#include "nano.h"
Chris Allegretta4da1fc62000-06-21 03:00:43 +000030
Chris Allegrettabceb1b22000-06-19 04:22:15 +000031#ifndef NANO_SMALL
32#include <libintl.h>
33#define _(string) gettext(string)
34#else
35#define _(string) (string)
36#endif
37
Chris Allegretta9fc8d432000-07-07 01:49:52 +000038/* Regular expression helper functions */
39
Chris Allegretta805c26d2000-09-06 13:39:17 +000040#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +000041void regexp_init(const char *regexp)
42{
43 regcomp(&search_regexp, regexp, ISSET(CASE_SENSITIVE) ? 0 : REG_ICASE);
44 SET(REGEXP_COMPILED);
45}
46
47void regexp_cleanup()
48{
49 UNSET(REGEXP_COMPILED);
50 regfree(&search_regexp);
51}
Chris Allegretta47805612000-07-07 02:35:34 +000052#endif
Chris Allegretta9fc8d432000-07-07 01:49:52 +000053
Chris Allegretta27eb13f2000-11-05 16:52:21 +000054void search_init_globals(void)
55{
56 if (last_search == NULL) {
57 last_search = nmalloc(1);
58 last_search[0] = 0;
59 }
60 if (last_replace == NULL) {
61 last_replace = nmalloc(1);
62 last_replace[0] = 0;
63 }
64}
65
Chris Allegrettabceb1b22000-06-19 04:22:15 +000066/* Set up the system variables for a search or replace. Returns -1 on
67 abort, 0 on success, and 1 on rerun calling program
68 Return -2 to run opposite program (searchg -> replace, replace -> search)
69
70 replacing = 1 if we call from do_replace, 0 if called from do_search func.
71*/
72int search_init(int replacing)
73{
Chris Allegretta71844ba2000-11-03 14:23:00 +000074 int i = 0;
75 char *buf;
Chris Allegrettaa4d21622000-07-08 23:57:03 +000076 char *prompt, *reprompt = "";
Chris Allegretta5bf51d32000-11-16 06:01:10 +000077 static char *backupstring = NULL;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +000078
Chris Allegretta27eb13f2000-11-05 16:52:21 +000079 search_init_globals();
Chris Allegretta31925e42000-11-02 04:40:39 +000080
Chris Allegretta27eb13f2000-11-05 16:52:21 +000081 buf = nmalloc(strlen(last_search) + 5);
82 buf[0] = 0;
Chris Allegretta71844ba2000-11-03 14:23:00 +000083
Chris Allegretta5bf51d32000-11-16 06:01:10 +000084
85 /* Okay, fun time. backupstring is our holder for what is being
86 returned from the statusq call. Using answer for this would be tricky.
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +000087 Here, if we're using PICO_MODE, we only want nano to put the
Chris Allegretta5bf51d32000-11-16 06:01:10 +000088 old string back up as editable if it's not the same as last_search.
89
90 Otherwise, if we don't already have a backupstring, set it to
91 last_search. */
92
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +000093 if (ISSET(PICO_MODE)) {
Chris Allegretta5bf51d32000-11-16 06:01:10 +000094 if (backupstring == NULL || !strcmp(backupstring, last_search))
95 backupstring = mallocstrcpy(backupstring, "");
96 }
97 else if (backupstring == NULL)
98 backupstring = mallocstrcpy(backupstring, last_search);
99
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000100 /* If using Pico messages, we do things the old fashioned way... */
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000101 if (ISSET(PICO_MODE)) {
Chris Allegretta71844ba2000-11-03 14:23:00 +0000102 if (last_search[0]) {
103
104 /* We use COLS / 3 here because we need to see more on the line */
105 if (strlen(last_search) > COLS / 3) {
106 snprintf(buf, COLS / 3 + 3, " [%s", last_search);
107 sprintf(&buf[COLS / 3 + 2], "...]");
108 } else
109 sprintf(buf, " [%s]", last_search);
110 } else {
111 buf[0] = '\0';
112 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000113 }
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000114 else
115 strcpy(buf, "");
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000116
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000117 if (ISSET(USE_REGEXP) && ISSET(CASE_SENSITIVE))
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000118 prompt = _("Case Sensitive Regexp Search%s%s");
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000119 else if (ISSET(USE_REGEXP))
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000120 prompt = _("Regexp Search%s%s");
121 else if (ISSET(CASE_SENSITIVE))
122 prompt = _("Case Sensitive Search%s%s");
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000123 else
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000124 prompt = _("Search%s%s");
Chris Allegrettaa4d21622000-07-08 23:57:03 +0000125
126 if (replacing)
127 reprompt = _(" (to replace)");
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000128
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000129 /* This is now one simple call. It just does a lot */
130 i = statusq(0, replacing ? replace_list : whereis_list,
131 replacing ? REPLACE_LIST_LEN : WHEREIS_LIST_LEN, backupstring,
Chris Allegrettaef601bb2000-11-19 19:15:03 +0000132 prompt, reprompt, buf);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000133
134 /* Cancel any search, or just return with no previous search */
135 if ((i == -1) || (i < 0 && !last_search[0])) {
136 statusbar(_("Search Cancelled"));
137 reset_cursor();
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000138 free(backupstring);
139 backupstring = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000140 return -1;
141 } else if (i == -2) { /* Same string */
Chris Allegretta805c26d2000-09-06 13:39:17 +0000142#ifdef HAVE_REGEX_H
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000143 if (ISSET(USE_REGEXP))
144 regexp_init(answer);
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000145#else
146 ;
Chris Allegretta47805612000-07-07 02:35:34 +0000147#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000148 } else if (i == 0) { /* They entered something new */
Chris Allegretta805c26d2000-09-06 13:39:17 +0000149#ifdef HAVE_REGEX_H
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000150 if (ISSET(USE_REGEXP))
151 regexp_init(answer);
Chris Allegretta47805612000-07-07 02:35:34 +0000152#endif
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000153 free(backupstring);
154 backupstring = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000155 last_replace[0] = '\0';
156 } else if (i == NANO_CASE_KEY) { /* They want it case sensitive */
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000157 free(backupstring);
158 backupstring = NULL;
159 backupstring = mallocstrcpy(backupstring, answer);
160
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000161 if (ISSET(CASE_SENSITIVE))
162 UNSET(CASE_SENSITIVE);
163 else
164 SET(CASE_SENSITIVE);
165
166 return 1;
167 } else if (i == NANO_OTHERSEARCH_KEY) {
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000168 backupstring = mallocstrcpy(backupstring, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000169 return -2; /* Call the opposite search function */
Chris Allegretta8c2b40f2000-06-29 01:30:04 +0000170 } else if (i == NANO_FROMSEARCHTOGOTO_KEY) {
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000171 free(backupstring);
172 backupstring = NULL;
Chris Allegretta8c2b40f2000-06-29 01:30:04 +0000173 do_gotoline_void();
174 return -3;
Chris Allegretta105da332000-10-31 05:10:10 +0000175 } else { /* First line key, etc. */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000176 do_early_abort();
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000177 free(backupstring);
178 backupstring = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000179 return -3;
180 }
181
182 return 0;
183}
184
Chris Allegretta31925e42000-11-02 04:40:39 +0000185void not_found_msg(char *str)
186{
Chris Allegrettacbb0f8b2000-11-15 03:50:02 +0000187 if (strlen(str) <= COLS / 2)
Chris Allegretta31925e42000-11-02 04:40:39 +0000188 statusbar(_("\"%s\" not found"), str);
189 else {
Chris Allegrettacbb0f8b2000-11-15 03:50:02 +0000190 char *foo = NULL;
191
192 foo = mallocstrcpy(foo, str);
Chris Allegretta31925e42000-11-02 04:40:39 +0000193 foo[COLS / 2] = 0;
194 statusbar(_("\"%s...\" not found"), foo);
Chris Allegrettacbb0f8b2000-11-15 03:50:02 +0000195
196 free(foo);
Chris Allegretta31925e42000-11-02 04:40:39 +0000197 }
198}
199
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000200filestruct *findnextstr(int quiet, filestruct * begin, int beginx,
201 char *needle)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000202{
203 filestruct *fileptr;
204 char *searchstr, *found = NULL, *tmp;
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000205 int past_editbot = 0, current_x_find;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000206
Chris Allegrettab9bfc9b2000-09-10 05:06:09 +0000207 fileptr = current;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000208
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000209 current_x_find = current_x + 1;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000210
211 /* Are we searching the last line? (i.e. the line where search started) */
Chris Allegretta1cc29f62000-11-20 01:29:37 +0000212 if ((fileptr == begin) && (current_x_find < beginx))
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000213 search_last_line = 1;
214
215 /* Make sure we haven't passed the end of the string */
Chris Allegretta1cc29f62000-11-20 01:29:37 +0000216 if (strlen(fileptr->data) < current_x_find)
217 current_x_find--;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000218
Chris Allegretta1cc29f62000-11-20 01:29:37 +0000219 searchstr = &fileptr->data[current_x_find];
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000220
221 /* Look for needle in searchstr */
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000222 while ((found = strstrwrapper(searchstr, needle)) == NULL) {
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000223
224 /* finished processing file, get out */
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000225 if (search_last_line) {
Chris Allegretta1cc29f62000-11-20 01:29:37 +0000226 if (!quiet)
Chris Allegretta31925e42000-11-02 04:40:39 +0000227 not_found_msg(needle);
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000228 return NULL;
229 }
Adam Rogoyski2a4ef922000-07-09 00:15:11 +0000230
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000231 fileptr = fileptr->next;
232
Adam Rogoyskia966d992000-07-09 18:42:53 +0000233 if (!past_editbot && (fileptr == editbot))
234 past_editbot = 1;
Adam Rogoyski2a4ef922000-07-09 00:15:11 +0000235
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000236 /* EOF reached, wrap around once */
237 if (fileptr == NULL) {
238 fileptr = fileage;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000239
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000240 past_editbot = 1;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000241
242 if (!quiet)
243 statusbar(_("Search Wrapped"));
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000244 }
245
246 /* Original start line reached */
247 if (fileptr == begin)
248 search_last_line = 1;
249
250 searchstr = fileptr->data;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000251 }
252
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000253 /* We found an instance */
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000254 current_x_find = 0;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000255 for (tmp = fileptr->data; tmp != found; tmp++)
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000256 current_x_find++;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000257
258 /* Ensure we haven't wrap around again! */
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000259 if ((search_last_line) && (current_x_find >= beginx)) {
Chris Allegretta1cc29f62000-11-20 01:29:37 +0000260 if (!quiet)
Chris Allegretta31925e42000-11-02 04:40:39 +0000261 not_found_msg(needle);
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000262 return NULL;
263 }
264
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000265 /* Set globals now that we are sure we found something */
266 current = fileptr;
267 current_x = current_x_find;
268
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000269 if (past_editbot)
270 edit_update(fileptr, CENTER);
271 else
272 update_line(current, current_x);
273
274 placewewant = xplustabs();
275 reset_cursor();
276
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000277 return fileptr;
278}
279
280void search_abort(void)
281{
282 UNSET(KEEP_CUTBUFFER);
283 display_main_list();
284 wrefresh(bottomwin);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000285 if (ISSET(MARK_ISSET))
Chris Allegrettaf1d33d32000-08-19 03:53:39 +0000286 edit_refresh_clearok();
Chris Allegretta47805612000-07-07 02:35:34 +0000287
Chris Allegretta805c26d2000-09-06 13:39:17 +0000288#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000289 if (ISSET(REGEXP_COMPILED))
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000290 regexp_cleanup();
Chris Allegretta47805612000-07-07 02:35:34 +0000291#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000292}
293
294/* Search for a string */
295int do_search(void)
296{
297 int i;
298 filestruct *fileptr = current;
299
300 wrap_reset();
Chris Allegretta9c371a22000-10-27 05:48:05 +0000301 i = search_init(0);
302 switch (i) {
303 case -1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000304 current = fileptr;
305 search_abort();
306 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000307 case -3:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000308 search_abort();
309 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000310 case -2:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000311 do_replace();
312 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000313 case 1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000314 do_search();
315 search_abort();
316 return 1;
317 }
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000318
319 /* The sneaky user deleted the previous search string */
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000320 if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Jordi Mallache7a647c2000-11-01 18:43:21 +0000321 statusbar(_("Search Cancelled"));
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000322 search_abort();
323 return 0;
324 }
325
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000326 /* If answer is now == "", then PICO_MODE is set. So, copy
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000327 last_search into answer... */
328
329 if (!strcmp(answer, ""))
330 answer = mallocstrcpy(answer, last_search);
331 else
332 last_search = mallocstrcpy(last_search, answer);
333
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000334 search_last_line = 0;
335 findnextstr(0, current, current_x, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000336 search_abort();
337 return 1;
338}
339
340void print_replaced(int num)
341{
342 if (num > 1)
343 statusbar(_("Replaced %d occurences"), num);
344 else if (num == 1)
345 statusbar(_("Replaced 1 occurence"));
346}
347
348void replace_abort(void)
349{
Chris Allegretta18bd0292000-07-28 01:18:10 +0000350 /* Identicle to search_abort, so we'll call it here. If it
351 does something different later, we can change it back. For now
352 it's just a waste to duplicat code */
353 search_abort();
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000354 placewewant = xplustabs();
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000355}
356
Chris Allegretta805c26d2000-09-06 13:39:17 +0000357#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000358int replace_regexp(char *string, int create_flag)
359{
360 /* split personality here - if create_flag is null, just calculate
361 * the size of the replacement line (necessary because of
362 * subexpressions like \1 \2 \3 in the replaced text) */
363
364 char *c;
365 int new_size = strlen(current->data) + 1;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000366 int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000367
368 new_size -= search_match_count;
369
370 /* Iterate through the replacement text to handle
371 * subexpression replacement using \1, \2, \3, etc */
372
373 c = last_replace;
374 while (*c) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000375 if (*c != '\\') {
376 if (create_flag)
377 *string++ = *c;
378 c++;
379 new_size++;
380 } else {
381 int num = (int) *(c + 1) - (int) '0';
382 if (num >= 1 && num <= 9) {
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000383
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000384 int i = regmatches[num].rm_so;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000385
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000386 if (num > search_regexp.re_nsub) {
387 /* Ugh, they specified a subexpression that doesn't
388 exist. */
389 return -1;
390 }
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000391
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000392 /* Skip over the replacement expression */
393 c += 2;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000394
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000395 /* But add the length of the subexpression to new_size */
396 new_size += regmatches[num].rm_eo - regmatches[num].rm_so;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000397
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000398 /* And if create_flag is set, append the result of the
399 * subexpression match to the new line */
400 while (create_flag && i < regmatches[num].rm_eo)
401 *string++ = *(current->data + i++);
402
403 } else {
404 if (create_flag)
405 *string++ = *c;
406 c++;
407 new_size++;
408 }
409 }
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000410 }
411
412 if (create_flag)
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000413 *string = 0;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000414
415 return new_size;
416}
Chris Allegretta47805612000-07-07 02:35:34 +0000417#endif
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000418
Chris Allegrettab46df992000-11-01 02:12:13 +0000419char *replace_line(void)
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000420{
421 char *copy, *tmp;
422 int new_line_size;
423 int search_match_count;
424
425 /* Calculate size of new line */
Chris Allegretta805c26d2000-09-06 13:39:17 +0000426#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000427 if (ISSET(USE_REGEXP)) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000428 search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
429 new_line_size = replace_regexp(NULL, 0);
430 /* If they specified an invalid subexpression in the replace
431 * text, return NULL indicating an error */
432 if (new_line_size < 0)
433 return NULL;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000434 } else {
Chris Allegretta47805612000-07-07 02:35:34 +0000435#else
436 {
437#endif
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000438 search_match_count = strlen(last_search);
439 new_line_size = strlen(current->data) - strlen(last_search) +
440 strlen(last_replace) + 1;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000441 }
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000442
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000443 /* Create buffer */
444 copy = nmalloc(new_line_size);
445
446 /* Head of Original Line */
447 strncpy(copy, current->data, current_x);
448 copy[current_x] = 0;
449
450 /* Replacement Text */
451 if (!ISSET(USE_REGEXP))
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000452 strcat(copy, last_replace);
Chris Allegretta805c26d2000-09-06 13:39:17 +0000453#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000454 else
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000455 (void) replace_regexp(copy + current_x, 1);
Chris Allegretta47805612000-07-07 02:35:34 +0000456#endif
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000457
458 /* The tail of the original line */
459 /* This may expose other bugs, because it no longer
460 goes through each character on the string
461 and tests for string goodness. But because
462 we can assume the invariant that current->data
463 is less than current_x + strlen(last_search) long,
464 this should be safe. Or it will expose bugs ;-) */
465 tmp = current->data + current_x + search_match_count;
466 strcat(copy, tmp);
467
468 return copy;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000469}
470
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000471/* step through each replace word and prompt user before replacing word */
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000472int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx,
473 int wholewords, int *i)
474{
475 int replaceall = 0, numreplaced = 0;
476 filestruct *fileptr;
477 char *copy;
478
479 switch (*i) {
480 case -1: /* Aborted enter */
481 if (strcmp(last_replace, ""))
482 answer = mallocstrcpy(answer, last_replace);
483 statusbar(_("Replace Cancelled"));
484 replace_abort();
485 return 0;
486 case 0: /* They actually entered something */
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000487 break;
488 default:
489 if (*i != -2) { /* First page, last page, for example
490 could get here */
491 do_early_abort();
492 replace_abort();
493 return 0;
494 }
495 }
496
Chris Allegrettac793c432000-11-21 12:52:55 +0000497 if (ISSET(PICO_MODE) && !strcmp(answer, ""))
498 answer = mallocstrcpy(answer, last_replace);
499
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000500 last_replace = mallocstrcpy(last_replace, answer);
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000501 while (1) {
502
503 /* Sweet optimization by Rocco here */
504 fileptr = findnextstr(replaceall, begin, *beginx, prevanswer);
505
506 /* No more matches. Done! */
Chris Allegretta022b96f2000-11-14 17:47:58 +0000507 if (!fileptr)
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000508 break;
509
510 /* Make sure only wholewords are found */
511 if (wholewords)
512 {
513 /* start of line or previous character not a letter */
Chris Allegretta63a89d32000-11-18 03:05:50 +0000514 if ((current_x == 0) || (!isalpha((int) fileptr->data[current_x-1])))
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000515 {
516 /* end of line or next character not a letter */
517 if (((current_x + strlen(prevanswer)) == strlen(fileptr->data))
Chris Allegretta63a89d32000-11-18 03:05:50 +0000518 || (!isalpha((int) fileptr->data[current_x + strlen(prevanswer)])))
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000519 ;
520 else
521 continue;
522 }
523 else
524 continue;
525 }
526
527 /* If we're here, we've found the search string */
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000528 if (!replaceall) {
529
530 curs_set(0);
531 do_replace_highlight(TRUE, prevanswer);
532
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000533 *i = do_yesno(1, 1, _("Replace this instance?"));
534
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000535 do_replace_highlight(FALSE, prevanswer);
536 curs_set(1);
537 }
538
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000539 if (*i > 0 || replaceall) { /* Yes, replace it!!!! */
540 if (*i == 2)
541 replaceall = 1;
542
543 copy = replace_line();
544 if (!copy) {
545 statusbar(_("Replace failed: unknown subexpression!"));
546 replace_abort();
547 return 0;
548 }
549
550 /* Cleanup */
551 free(current->data);
552 current->data = copy;
553
554 /* Stop bug where we replace a substring of the replacement text */
555 current_x += strlen(last_replace) - 1;
556
557 /* Adjust the original cursor position - COULD BE IMPROVED */
558 if (search_last_line) {
559 *beginx += strlen(last_replace) - strlen(last_search);
560
561 /* For strings that cross the search start/end boundary */
562 /* Don't go outside of allocated memory */
563 if (*beginx < 1)
564 *beginx = 1;
565 }
566
567 edit_refresh();
568 set_modified();
569 numreplaced++;
570 } else if (*i == -1) /* Abort, else do nothing and continue loop */
571 break;
572 }
573
574 return numreplaced;
575}
576
Chris Allegretta47805612000-07-07 02:35:34 +0000577/* Replace a string */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000578int do_replace(void)
579{
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000580 int i, numreplaced, beginx;
581 filestruct *begin;
582 char *prevanswer = NULL, *buf = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000583
Chris Allegretta9c371a22000-10-27 05:48:05 +0000584 i = search_init(1);
585 switch (i) {
586 case -1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000587 statusbar(_("Replace Cancelled"));
588 replace_abort();
589 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000590 case 1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000591 do_replace();
592 return 1;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000593 case -2:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000594 do_search();
595 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000596 case -3:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000597 replace_abort();
598 return 0;
599 }
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000600
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000601 /* Again, there was a previous string but they deleted it and hit enter */
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000602 if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000603 statusbar(_("Replace Cancelled"));
604 replace_abort();
605 return 0;
606 }
607
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000608 /* If answer is now == "", then PICO_MODE is set. So, copy
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000609 last_search into answer (and prevanswer)... */
610 if (!strcmp(answer, "")) {
611 answer = mallocstrcpy(answer, last_search);
612 prevanswer = mallocstrcpy(prevanswer, last_search);
613 } else {
614 last_search = mallocstrcpy(last_search, answer);
615 prevanswer = mallocstrcpy(prevanswer, answer);
616 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000617
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000618 if (ISSET(PICO_MODE)) {
Chris Allegretta71844ba2000-11-03 14:23:00 +0000619 buf = nmalloc(strlen(last_replace) + 5);
620 if (strcmp(last_replace, "")) {
621 if (strlen(last_replace) > (COLS / 3)) {
622 strncpy(buf, last_replace, COLS / 3);
623 sprintf(&buf[COLS / 3 - 1], "...");
624 } else
625 sprintf(buf, "%s", last_replace);
626
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000627 i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta71844ba2000-11-03 14:23:00 +0000628 _("Replace with [%s]"), buf);
629 }
630 else
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000631 i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta71844ba2000-11-03 14:23:00 +0000632 _("Replace with"));
633 }
634 else
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000635 i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, last_replace,
Chris Allegretta105da332000-10-31 05:10:10 +0000636 _("Replace with"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000637
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000638 /* save where we are */
639 begin = current;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000640 beginx = current_x + 1;
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000641
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000642 search_last_line = 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000643
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000644 numreplaced = do_replace_loop(prevanswer, begin, &beginx, FALSE, &i);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000645
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000646 /* restore where we were */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000647 current = begin;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000648 current_x = beginx - 1;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000649 renumber_all();
Chris Allegretta234a34d2000-07-29 04:33:38 +0000650 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000651 print_replaced(numreplaced);
652 replace_abort();
653 return 1;
654}
655
656void goto_abort(void)
657{
658 UNSET(KEEP_CUTBUFFER);
659 display_main_list();
660}
661
662int do_gotoline(long defline)
663{
664 long line, i = 1, j = 0;
665 filestruct *fileptr;
666
667 if (defline > 0) /* We already know what line we want to go to */
668 line = defline;
669 else { /* Ask for it */
670
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000671 j = statusq(0, goto_list, GOTO_LIST_LEN, "", _("Enter line number"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000672 if (j == -1) {
673 statusbar(_("Aborted"));
674 goto_abort();
675 return 0;
676 } else if (j != 0) {
677 do_early_abort();
678 goto_abort();
679 return 0;
680 }
681 if (!strcmp(answer, "$")) {
682 current = filebot;
683 current_x = 0;
Chris Allegretta234a34d2000-07-29 04:33:38 +0000684 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000685 goto_abort();
686 return 1;
687 }
688 line = atoi(answer);
689 }
690
691 /* Bounds check */
692 if (line <= 0) {
693 statusbar(_("Come on, be reasonable"));
694 goto_abort();
695 return 0;
696 }
697 if (line > totlines) {
698 statusbar(_("Only %d lines available, skipping to last line"),
699 filebot->lineno);
700 current = filebot;
701 current_x = 0;
Chris Allegretta234a34d2000-07-29 04:33:38 +0000702 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000703 } else {
704 for (fileptr = fileage; fileptr != NULL && i < line; i++)
705 fileptr = fileptr->next;
706
707 current = fileptr;
708 current_x = 0;
Chris Allegretta234a34d2000-07-29 04:33:38 +0000709 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000710 }
711
712 goto_abort();
713 return 1;
714}
715
716int do_gotoline_void(void)
717{
718 return do_gotoline(0);
719}