blob: 21aa6c0535b5c91c8bee97459c3ce0352e761a1a [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
Chris Allegretta6efda542001-04-28 18:03:52 +000022#include "config.h"
23
Chris Allegrettabceb1b22000-06-19 04:22:15 +000024#include <stdlib.h>
25#include <string.h>
Chris Allegretta47805612000-07-07 02:35:34 +000026#include <unistd.h>
Chris Allegrettabceb1b22000-06-19 04:22:15 +000027#include <stdio.h>
Chris Allegretta27eb13f2000-11-05 16:52:21 +000028#include <ctype.h>
Chris Allegrettabceb1b22000-06-19 04:22:15 +000029#include "proto.h"
30#include "nano.h"
Chris Allegretta4da1fc62000-06-21 03:00:43 +000031
Chris Allegrettabceb1b22000-06-19 04:22:15 +000032#ifndef NANO_SMALL
33#include <libintl.h>
34#define _(string) gettext(string)
35#else
36#define _(string) (string)
37#endif
38
Chris Allegretta9fc8d432000-07-07 01:49:52 +000039/* Regular expression helper functions */
40
Chris Allegretta805c26d2000-09-06 13:39:17 +000041#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +000042void regexp_init(const char *regexp)
43{
44 regcomp(&search_regexp, regexp, ISSET(CASE_SENSITIVE) ? 0 : REG_ICASE);
45 SET(REGEXP_COMPILED);
46}
47
Chris Allegrettae3167732001-03-18 16:59:34 +000048void regexp_cleanup(void)
Chris Allegretta9fc8d432000-07-07 01:49:52 +000049{
50 UNSET(REGEXP_COMPILED);
51 regfree(&search_regexp);
52}
Chris Allegretta47805612000-07-07 02:35:34 +000053#endif
Chris Allegretta9fc8d432000-07-07 01:49:52 +000054
Chris Allegretta27eb13f2000-11-05 16:52:21 +000055void search_init_globals(void)
56{
57 if (last_search == NULL) {
Chris Allegretta88b09152001-05-17 11:35:43 +000058 last_search = charalloc(1);
Chris Allegretta27eb13f2000-11-05 16:52:21 +000059 last_search[0] = 0;
60 }
61 if (last_replace == NULL) {
Chris Allegretta88b09152001-05-17 11:35:43 +000062 last_replace = charalloc(1);
Chris Allegretta27eb13f2000-11-05 16:52:21 +000063 last_replace[0] = 0;
64 }
65}
66
Chris Allegrettabceb1b22000-06-19 04:22:15 +000067/* Set up the system variables for a search or replace. Returns -1 on
68 abort, 0 on success, and 1 on rerun calling program
Chris Allegretta88520c92001-05-05 17:45:54 +000069 Return -2 to run opposite program (search -> replace, replace -> search)
Chris Allegrettabceb1b22000-06-19 04:22:15 +000070
71 replacing = 1 if we call from do_replace, 0 if called from do_search func.
72*/
73int search_init(int replacing)
74{
Chris Allegrettaf372bd92001-07-15 22:24:24 +000075 int i = 0;
Chris Allegretta71844ba2000-11-03 14:23:00 +000076 char *buf;
Chris Allegrettae4933a32001-06-13 02:35:44 +000077 char *prompt;
Chris Allegretta5bf51d32000-11-16 06:01:10 +000078 static char *backupstring = NULL;
Chris Allegrettaf372bd92001-07-15 22:24:24 +000079#ifndef NANO_SMALL
80 int j;
81#endif
Chris Allegrettabd9e7c32000-10-26 01:44:42 +000082
Chris Allegretta27eb13f2000-11-05 16:52:21 +000083 search_init_globals();
Chris Allegretta31925e42000-11-02 04:40:39 +000084
Chris Allegretta88b09152001-05-17 11:35:43 +000085 buf = charalloc(strlen(last_search) + 5);
Chris Allegretta27eb13f2000-11-05 16:52:21 +000086 buf[0] = 0;
Chris Allegretta71844ba2000-11-03 14:23:00 +000087
Chris Allegrettac1049ac2001-08-17 00:03:46 +000088
89 /* Clear the backupstring if we've changed from Pico mode to regular
90 mode */
91 if (ISSET(CLEAR_BACKUPSTRING)) {
92 free(backupstring);
93 backupstring = NULL;
Chris Allegrettac2acb812001-08-17 00:08:44 +000094 UNSET(CLEAR_BACKUPSTRING);
Chris Allegrettac1049ac2001-08-17 00:03:46 +000095 }
96
Chris Allegretta5bf51d32000-11-16 06:01:10 +000097 /* Okay, fun time. backupstring is our holder for what is being
98 returned from the statusq call. Using answer for this would be tricky.
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +000099 Here, if we're using PICO_MODE, we only want nano to put the
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000100 old string back up as editable if it's not the same as last_search.
101
102 Otherwise, if we don't already have a backupstring, set it to
103 last_search. */
104
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000105 if (ISSET(PICO_MODE)) {
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000106 if (backupstring == NULL || !strcmp(backupstring, last_search))
107 backupstring = mallocstrcpy(backupstring, "");
108 }
109 else if (backupstring == NULL)
110 backupstring = mallocstrcpy(backupstring, last_search);
111
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000112 /* If using Pico messages, we do things the old fashioned way... */
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000113 if (ISSET(PICO_MODE)) {
Chris Allegretta71844ba2000-11-03 14:23:00 +0000114 if (last_search[0]) {
115
116 /* We use COLS / 3 here because we need to see more on the line */
117 if (strlen(last_search) > COLS / 3) {
118 snprintf(buf, COLS / 3 + 3, " [%s", last_search);
119 sprintf(&buf[COLS / 3 + 2], "...]");
120 } else
121 sprintf(buf, " [%s]", last_search);
122 } else {
123 buf[0] = '\0';
124 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000125 }
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000126 else
127 strcpy(buf, "");
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000128
Chris Allegrettae4933a32001-06-13 02:35:44 +0000129 /* Instead of having a million if statements here to determine
130 the prompt, we instead just have a hundred "? :" calls in
131 the statusq call. I hope no one ever has to modify this :-) */
132 prompt = "%s%s%s%s%s%s";
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000133
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000134 /* This is now one simple call. It just does a lot */
135 i = statusq(0, replacing ? replace_list : whereis_list,
136 replacing ? REPLACE_LIST_LEN : WHEREIS_LIST_LEN, backupstring,
Chris Allegrettae4933a32001-06-13 02:35:44 +0000137 prompt,
138 ISSET(CASE_SENSITIVE) ? _("Case Sensitive ") : "",
139 ISSET(USE_REGEXP) ? _("Regexp ") : "",
140 _("Search"),
141 ISSET(REVERSE_SEARCH) ? _(" Backwards") : "",
142 replacing ? _(" (to replace)") : "",
143 buf);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000144
145 /* Cancel any search, or just return with no previous search */
146 if ((i == -1) || (i < 0 && !last_search[0])) {
147 statusbar(_("Search Cancelled"));
148 reset_cursor();
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000149 free(backupstring);
150 backupstring = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000151 return -1;
Chris Allegretta658399a2001-06-14 02:54:22 +0000152 } else
153 switch (i) {
154
155 case -2: /* Same string */
Chris Allegretta805c26d2000-09-06 13:39:17 +0000156#ifdef HAVE_REGEX_H
Chris Allegretta74bb31b2001-03-14 09:08:14 +0000157 if (ISSET(USE_REGEXP)) {
158
159 /* If we're in pico mode, answer is "", use last_search! */
160 if (ISSET(PICO_MODE))
161 regexp_init(last_search);
162 else
163 regexp_init(answer);
164 }
Chris Allegretta47805612000-07-07 02:35:34 +0000165#endif
Chris Allegretta658399a2001-06-14 02:54:22 +0000166 break;
167 case 0: /* They entered something new */
Chris Allegretta805c26d2000-09-06 13:39:17 +0000168#ifdef HAVE_REGEX_H
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000169 if (ISSET(USE_REGEXP))
170 regexp_init(answer);
Chris Allegretta47805612000-07-07 02:35:34 +0000171#endif
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000172 free(backupstring);
173 backupstring = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000174 last_replace[0] = '\0';
Chris Allegretta658399a2001-06-14 02:54:22 +0000175 break;
176 case TOGGLE_CASE_KEY:
177 case TOGGLE_BACKWARDS_KEY:
178#ifdef HAVE_REGEX_H
179 case TOGGLE_REGEXP_KEY:
180#endif
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000181 free(backupstring);
182 backupstring = NULL;
183 backupstring = mallocstrcpy(backupstring, answer);
184
Chris Allegrettaf372bd92001-07-15 22:24:24 +0000185#ifndef NANO_SMALL
Chris Allegretta658399a2001-06-14 02:54:22 +0000186 for (j = 0; j <= TOGGLE_LEN - 1; j++)
187 if (i == toggles[j].val)
188 TOGGLE(toggles[j].flag);
Chris Allegrettaf372bd92001-07-15 22:24:24 +0000189#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000190
191 return 1;
Chris Allegretta658399a2001-06-14 02:54:22 +0000192 case NANO_OTHERSEARCH_KEY:
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000193 backupstring = mallocstrcpy(backupstring, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000194 return -2; /* Call the opposite search function */
Chris Allegretta658399a2001-06-14 02:54:22 +0000195 case NANO_FROMSEARCHTOGOTO_KEY:
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000196 free(backupstring);
197 backupstring = NULL;
Chris Allegretta8c2b40f2000-06-29 01:30:04 +0000198 do_gotoline_void();
199 return -3;
Chris Allegretta658399a2001-06-14 02:54:22 +0000200 default:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000201 do_early_abort();
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000202 free(backupstring);
203 backupstring = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000204 return -3;
205 }
206
207 return 0;
208}
209
Chris Allegretta31925e42000-11-02 04:40:39 +0000210void not_found_msg(char *str)
211{
Chris Allegrettacbb0f8b2000-11-15 03:50:02 +0000212 if (strlen(str) <= COLS / 2)
Chris Allegretta31925e42000-11-02 04:40:39 +0000213 statusbar(_("\"%s\" not found"), str);
214 else {
Chris Allegrettacbb0f8b2000-11-15 03:50:02 +0000215 char *foo = NULL;
216
217 foo = mallocstrcpy(foo, str);
Chris Allegretta31925e42000-11-02 04:40:39 +0000218 foo[COLS / 2] = 0;
219 statusbar(_("\"%s...\" not found"), foo);
Chris Allegrettacbb0f8b2000-11-15 03:50:02 +0000220
221 free(foo);
Chris Allegretta31925e42000-11-02 04:40:39 +0000222 }
223}
224
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000225filestruct *findnextstr(int quiet, filestruct * begin, int beginx,
226 char *needle)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000227{
228 filestruct *fileptr;
Chris Allegrettae4933a32001-06-13 02:35:44 +0000229 char *searchstr, *rev_start = NULL, *found = NULL;
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000230 int past_editbot = 0, current_x_find;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000231
Chris Allegrettab9bfc9b2000-09-10 05:06:09 +0000232 fileptr = current;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000233
Chris Allegrettae4933a32001-06-13 02:35:44 +0000234 if (!ISSET(REVERSE_SEARCH)) { /* forward search */
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000235
Chris Allegrettae4933a32001-06-13 02:35:44 +0000236 current_x_find = current_x + 1;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000237
Chris Allegrettae4933a32001-06-13 02:35:44 +0000238 /* Are we now back to the line where the search started) */
239 if ((fileptr == begin) && (current_x_find < beginx))
240 search_last_line = 1;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000241
Chris Allegrettae4933a32001-06-13 02:35:44 +0000242 /* Make sure we haven't passed the end of the string */
243 if (strlen(fileptr->data) < current_x_find)
244 current_x_find--;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000245
Chris Allegrettae4933a32001-06-13 02:35:44 +0000246 searchstr = &fileptr->data[current_x_find];
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000247
Chris Allegrettae4933a32001-06-13 02:35:44 +0000248 /* Look for needle in searchstr */
249 while ((found = strstrwrapper(searchstr, needle, rev_start)) == NULL) {
250
251 /* finished processing file, get out */
252 if (search_last_line) {
253 if (!quiet)
254 not_found_msg(needle);
255 return NULL;
256 }
257
258 fileptr = fileptr->next;
259
260 if (!past_editbot && (fileptr == editbot))
261 past_editbot = 1;
262
263 /* EOF reached ?, wrap around once */
264 if (fileptr == NULL) {
265 fileptr = fileage;
266 past_editbot = 1;
267 if (!quiet)
268 statusbar(_("Search Wrapped"));
269 }
270
271 /* Original start line reached */
272 if (fileptr == begin)
273 search_last_line = 1;
274
275 searchstr = fileptr->data;
276 }
277
278 /* We found an instance */
279 current_x_find = found - fileptr->data;
280
281 /* Ensure we haven't wrapped around again! */
282 if ((search_last_line) && (current_x_find >= beginx)) {
Chris Allegretta1cc29f62000-11-20 01:29:37 +0000283 if (!quiet)
Chris Allegretta31925e42000-11-02 04:40:39 +0000284 not_found_msg(needle);
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000285 return NULL;
286 }
Adam Rogoyski2a4ef922000-07-09 00:15:11 +0000287
Chris Allegrettae4933a32001-06-13 02:35:44 +0000288 } else { /* reverse search */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000289
Chris Allegrettae4933a32001-06-13 02:35:44 +0000290 current_x_find = current_x - 1;
Adam Rogoyski2a4ef922000-07-09 00:15:11 +0000291
Chris Allegrettae4933a32001-06-13 02:35:44 +0000292 /* Are we now back to the line where the search started) */
293 if ((fileptr == begin) && (current_x_find > beginx))
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000294 search_last_line = 1;
295
Chris Allegrettae4933a32001-06-13 02:35:44 +0000296 /* Make sure we haven't passed the begining of the string */
297#if 0 /* Is this required here ? */
298 if (!(&fileptr->data[current_x_find] - fileptr->data))
299 current_x_find++;
300#endif
301 rev_start = &fileptr->data[current_x_find];
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000302 searchstr = fileptr->data;
Chris Allegrettae4933a32001-06-13 02:35:44 +0000303
304 /* Look for needle in searchstr */
305 while ((found = strstrwrapper(searchstr, needle, rev_start)) == NULL) {
306
307 /* finished processing file, get out */
308 if (search_last_line) {
309 if (!quiet)
310 not_found_msg(needle);
311 return NULL;
312 }
313
314 fileptr = fileptr->prev;
315
316/* ? */ if (!past_editbot && (fileptr == edittop->prev))
317 past_editbot = 1;
318
319 /* SOF reached ?, wrap around once */
320/* ? */ if (fileptr == NULL) {
321 fileptr = filebot;
322 past_editbot = 1;
323 if (!quiet)
324 statusbar(_("Search Wrapped"));
325 }
326
327 /* Original start line reached */
328 if (fileptr == begin)
329 search_last_line = 1;
330
331 searchstr = fileptr->data;
332 rev_start = fileptr->data + strlen(fileptr->data);
333 }
334
335 /* We found an instance */
336 current_x_find = found - fileptr->data;
337
338 /* Ensure we haven't wrapped around again! */
339 if ((search_last_line) && (current_x_find < beginx)) {
340 if (!quiet)
341 not_found_msg(needle);
342 return NULL;
343 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000344 }
345
Chris Allegrettae4933a32001-06-13 02:35:44 +0000346 /* Set globals now that we are sure we found something */
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000347 current = fileptr;
348 current_x = current_x_find;
349
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000350 if (past_editbot)
351 edit_update(fileptr, CENTER);
352 else
353 update_line(current, current_x);
354
355 placewewant = xplustabs();
356 reset_cursor();
357
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000358 return fileptr;
359}
360
361void search_abort(void)
362{
363 UNSET(KEEP_CUTBUFFER);
364 display_main_list();
365 wrefresh(bottomwin);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000366 if (ISSET(MARK_ISSET))
Chris Allegrettaf1d33d32000-08-19 03:53:39 +0000367 edit_refresh_clearok();
Chris Allegretta47805612000-07-07 02:35:34 +0000368
Chris Allegretta805c26d2000-09-06 13:39:17 +0000369#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000370 if (ISSET(REGEXP_COMPILED))
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000371 regexp_cleanup();
Chris Allegretta47805612000-07-07 02:35:34 +0000372#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000373}
374
375/* Search for a string */
376int do_search(void)
377{
378 int i;
379 filestruct *fileptr = current;
380
381 wrap_reset();
Chris Allegretta9c371a22000-10-27 05:48:05 +0000382 i = search_init(0);
383 switch (i) {
384 case -1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000385 current = fileptr;
386 search_abort();
387 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000388 case -3:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000389 search_abort();
390 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000391 case -2:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000392 do_replace();
393 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000394 case 1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000395 do_search();
396 search_abort();
397 return 1;
398 }
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000399
400 /* The sneaky user deleted the previous search string */
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000401 if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Jordi Mallache7a647c2000-11-01 18:43:21 +0000402 statusbar(_("Search Cancelled"));
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000403 search_abort();
404 return 0;
405 }
406
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000407 /* If answer is now == "", then PICO_MODE is set. So, copy
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000408 last_search into answer... */
409
410 if (!strcmp(answer, ""))
411 answer = mallocstrcpy(answer, last_search);
412 else
413 last_search = mallocstrcpy(last_search, answer);
414
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000415 search_last_line = 0;
416 findnextstr(0, current, current_x, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000417 search_abort();
418 return 1;
419}
420
421void print_replaced(int num)
422{
423 if (num > 1)
Jordi Mallach5b387d72001-02-20 12:45:47 +0000424 statusbar(_("Replaced %d occurrences"), num);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000425 else if (num == 1)
Jordi Mallach5b387d72001-02-20 12:45:47 +0000426 statusbar(_("Replaced 1 occurrence"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000427}
428
429void replace_abort(void)
430{
Chris Allegretta88520c92001-05-05 17:45:54 +0000431 /* Identical to search_abort, so we'll call it here. If it
Chris Allegretta18bd0292000-07-28 01:18:10 +0000432 does something different later, we can change it back. For now
Chris Allegretta88520c92001-05-05 17:45:54 +0000433 it's just a waste to duplicate code */
Chris Allegretta18bd0292000-07-28 01:18:10 +0000434 search_abort();
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000435 placewewant = xplustabs();
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000436}
437
Chris Allegretta805c26d2000-09-06 13:39:17 +0000438#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000439int replace_regexp(char *string, int create_flag)
440{
441 /* split personality here - if create_flag is null, just calculate
442 * the size of the replacement line (necessary because of
443 * subexpressions like \1 \2 \3 in the replaced text) */
444
445 char *c;
446 int new_size = strlen(current->data) + 1;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000447 int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000448
449 new_size -= search_match_count;
450
451 /* Iterate through the replacement text to handle
452 * subexpression replacement using \1, \2, \3, etc */
453
454 c = last_replace;
455 while (*c) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000456 if (*c != '\\') {
457 if (create_flag)
458 *string++ = *c;
459 c++;
460 new_size++;
461 } else {
462 int num = (int) *(c + 1) - (int) '0';
463 if (num >= 1 && num <= 9) {
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000464
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000465 int i = regmatches[num].rm_so;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000466
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000467 if (num > search_regexp.re_nsub) {
468 /* Ugh, they specified a subexpression that doesn't
469 exist. */
470 return -1;
471 }
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000472
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000473 /* Skip over the replacement expression */
474 c += 2;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000475
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000476 /* But add the length of the subexpression to new_size */
477 new_size += regmatches[num].rm_eo - regmatches[num].rm_so;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000478
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000479 /* And if create_flag is set, append the result of the
480 * subexpression match to the new line */
481 while (create_flag && i < regmatches[num].rm_eo)
482 *string++ = *(current->data + i++);
483
484 } else {
485 if (create_flag)
486 *string++ = *c;
487 c++;
488 new_size++;
489 }
490 }
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000491 }
492
493 if (create_flag)
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000494 *string = 0;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000495
496 return new_size;
497}
Chris Allegretta47805612000-07-07 02:35:34 +0000498#endif
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000499
Chris Allegrettab46df992000-11-01 02:12:13 +0000500char *replace_line(void)
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000501{
502 char *copy, *tmp;
503 int new_line_size;
504 int search_match_count;
505
506 /* Calculate size of new line */
Chris Allegretta805c26d2000-09-06 13:39:17 +0000507#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000508 if (ISSET(USE_REGEXP)) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000509 search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
510 new_line_size = replace_regexp(NULL, 0);
511 /* If they specified an invalid subexpression in the replace
Chris Allegretta88520c92001-05-05 17:45:54 +0000512 * text, return NULL, indicating an error */
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000513 if (new_line_size < 0)
514 return NULL;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000515 } else {
Chris Allegretta47805612000-07-07 02:35:34 +0000516#else
517 {
518#endif
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000519 search_match_count = strlen(last_search);
520 new_line_size = strlen(current->data) - strlen(last_search) +
521 strlen(last_replace) + 1;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000522 }
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000523
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000524 /* Create buffer */
Chris Allegretta88b09152001-05-17 11:35:43 +0000525 copy = charalloc(new_line_size);
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000526
527 /* Head of Original Line */
528 strncpy(copy, current->data, current_x);
529 copy[current_x] = 0;
530
531 /* Replacement Text */
532 if (!ISSET(USE_REGEXP))
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000533 strcat(copy, last_replace);
Chris Allegretta805c26d2000-09-06 13:39:17 +0000534#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000535 else
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000536 (void) replace_regexp(copy + current_x, 1);
Chris Allegretta47805612000-07-07 02:35:34 +0000537#endif
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000538
539 /* The tail of the original line */
540 /* This may expose other bugs, because it no longer
Chris Allegretta88520c92001-05-05 17:45:54 +0000541 goes through each character in the string
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000542 and tests for string goodness. But because
543 we can assume the invariant that current->data
544 is less than current_x + strlen(last_search) long,
545 this should be safe. Or it will expose bugs ;-) */
546 tmp = current->data + current_x + search_match_count;
547 strcat(copy, tmp);
548
549 return copy;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000550}
551
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000552/* step through each replace word and prompt user before replacing word */
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000553int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx,
554 int wholewords, int *i)
555{
556 int replaceall = 0, numreplaced = 0;
557 filestruct *fileptr;
558 char *copy;
559
560 switch (*i) {
561 case -1: /* Aborted enter */
562 if (strcmp(last_replace, ""))
563 answer = mallocstrcpy(answer, last_replace);
564 statusbar(_("Replace Cancelled"));
565 replace_abort();
566 return 0;
567 case 0: /* They actually entered something */
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000568 break;
569 default:
570 if (*i != -2) { /* First page, last page, for example
571 could get here */
572 do_early_abort();
573 replace_abort();
574 return 0;
575 }
576 }
577
Chris Allegrettac793c432000-11-21 12:52:55 +0000578 if (ISSET(PICO_MODE) && !strcmp(answer, ""))
579 answer = mallocstrcpy(answer, last_replace);
580
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000581 last_replace = mallocstrcpy(last_replace, answer);
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000582 while (1) {
583
584 /* Sweet optimization by Rocco here */
585 fileptr = findnextstr(replaceall, begin, *beginx, prevanswer);
586
587 /* No more matches. Done! */
Chris Allegretta022b96f2000-11-14 17:47:58 +0000588 if (!fileptr)
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000589 break;
590
Chris Allegretta88520c92001-05-05 17:45:54 +0000591 /* Make sure only whole words are found */
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000592 if (wholewords)
593 {
594 /* start of line or previous character not a letter */
Chris Allegretta63a89d32000-11-18 03:05:50 +0000595 if ((current_x == 0) || (!isalpha((int) fileptr->data[current_x-1])))
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000596 {
597 /* end of line or next character not a letter */
598 if (((current_x + strlen(prevanswer)) == strlen(fileptr->data))
Chris Allegretta63a89d32000-11-18 03:05:50 +0000599 || (!isalpha((int) fileptr->data[current_x + strlen(prevanswer)])))
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000600 ;
601 else
602 continue;
603 }
604 else
605 continue;
606 }
607
608 /* If we're here, we've found the search string */
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000609 if (!replaceall) {
610
611 curs_set(0);
612 do_replace_highlight(TRUE, prevanswer);
613
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000614 *i = do_yesno(1, 1, _("Replace this instance?"));
615
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000616 do_replace_highlight(FALSE, prevanswer);
617 curs_set(1);
618 }
619
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000620 if (*i > 0 || replaceall) { /* Yes, replace it!!!! */
621 if (*i == 2)
622 replaceall = 1;
623
624 copy = replace_line();
625 if (!copy) {
626 statusbar(_("Replace failed: unknown subexpression!"));
627 replace_abort();
628 return 0;
629 }
630
631 /* Cleanup */
632 free(current->data);
633 current->data = copy;
634
635 /* Stop bug where we replace a substring of the replacement text */
636 current_x += strlen(last_replace) - 1;
637
638 /* Adjust the original cursor position - COULD BE IMPROVED */
639 if (search_last_line) {
640 *beginx += strlen(last_replace) - strlen(last_search);
641
642 /* For strings that cross the search start/end boundary */
643 /* Don't go outside of allocated memory */
644 if (*beginx < 1)
645 *beginx = 1;
646 }
647
648 edit_refresh();
649 set_modified();
650 numreplaced++;
651 } else if (*i == -1) /* Abort, else do nothing and continue loop */
652 break;
653 }
654
655 return numreplaced;
656}
657
Chris Allegretta47805612000-07-07 02:35:34 +0000658/* Replace a string */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000659int do_replace(void)
660{
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000661 int i, numreplaced, beginx;
662 filestruct *begin;
663 char *prevanswer = NULL, *buf = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000664
Chris Allegretta5050aa62001-04-22 07:10:21 +0000665 if (ISSET(VIEW_MODE)) {
666 print_view_warning();
667 replace_abort();
668 return 0;
669 }
670
Chris Allegretta9c371a22000-10-27 05:48:05 +0000671 i = search_init(1);
672 switch (i) {
673 case -1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000674 statusbar(_("Replace Cancelled"));
675 replace_abort();
676 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000677 case 1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000678 do_replace();
679 return 1;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000680 case -2:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000681 do_search();
682 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000683 case -3:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000684 replace_abort();
685 return 0;
686 }
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000687
Chris Allegretta88520c92001-05-05 17:45:54 +0000688 /* Again, there was a previous string, but they deleted it and hit enter */
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000689 if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000690 statusbar(_("Replace Cancelled"));
691 replace_abort();
692 return 0;
693 }
694
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000695 /* If answer is now == "", then PICO_MODE is set. So, copy
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000696 last_search into answer (and prevanswer)... */
697 if (!strcmp(answer, "")) {
698 answer = mallocstrcpy(answer, last_search);
699 prevanswer = mallocstrcpy(prevanswer, last_search);
700 } else {
701 last_search = mallocstrcpy(last_search, answer);
702 prevanswer = mallocstrcpy(prevanswer, answer);
703 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000704
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000705 if (ISSET(PICO_MODE)) {
Chris Allegretta88b09152001-05-17 11:35:43 +0000706 buf = charalloc(strlen(last_replace) + 5);
Chris Allegretta71844ba2000-11-03 14:23:00 +0000707 if (strcmp(last_replace, "")) {
708 if (strlen(last_replace) > (COLS / 3)) {
709 strncpy(buf, last_replace, COLS / 3);
710 sprintf(&buf[COLS / 3 - 1], "...");
711 } else
712 sprintf(buf, "%s", last_replace);
713
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000714 i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta71844ba2000-11-03 14:23:00 +0000715 _("Replace with [%s]"), buf);
716 }
717 else
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000718 i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta71844ba2000-11-03 14:23:00 +0000719 _("Replace with"));
720 }
721 else
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000722 i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, last_replace,
Chris Allegretta105da332000-10-31 05:10:10 +0000723 _("Replace with"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000724
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000725 /* save where we are */
726 begin = current;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000727 beginx = current_x + 1;
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000728
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000729 search_last_line = 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000730
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000731 numreplaced = do_replace_loop(prevanswer, begin, &beginx, FALSE, &i);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000732
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000733 /* restore where we were */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000734 current = begin;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000735 current_x = beginx - 1;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000736 renumber_all();
Chris Allegretta234a34d2000-07-29 04:33:38 +0000737 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000738 print_replaced(numreplaced);
739 replace_abort();
740 return 1;
741}
742
743void goto_abort(void)
744{
745 UNSET(KEEP_CUTBUFFER);
746 display_main_list();
747}
748
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000749int do_gotoline(long line, int save_pos)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000750{
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000751 long i = 1;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000752
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000753 if (line <= 0) { /* Ask for it */
754
755 long j = 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000756
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000757 j = statusq(0, goto_list, GOTO_LIST_LEN, "", _("Enter line number"));
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000758 if (j != 0) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000759 statusbar(_("Aborted"));
760 goto_abort();
761 return 0;
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000762 }
763
764 line = atoi(answer);
765
766 /* Bounds check */
767 if (line <= 0) {
768 statusbar(_("Come on, be reasonable"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000769 goto_abort();
770 return 0;
771 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000772 }
773
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000774 for (current = fileage; ((current->next != NULL) && (i < line)); i++)
775 current = current->next;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000776
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000777 current_x = 0;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000778
779 /* if save_pos is non-zero, don't change the cursor position when
780 updating the edit window */
781 if (save_pos)
782 edit_update(current, NONE);
783 else
784 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000785
786 goto_abort();
787 return 1;
788}
789
790int do_gotoline_void(void)
791{
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000792 return do_gotoline(0, 0);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000793}