blob: 74cb74c2cf1b7058fe007999b00f40b9ff765900 [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 Allegretta658399a2001-06-14 02:54:22 +000075 int i = 0, j;
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 Allegrettabd9e7c32000-10-26 01:44:42 +000079
Chris Allegretta27eb13f2000-11-05 16:52:21 +000080 search_init_globals();
Chris Allegretta31925e42000-11-02 04:40:39 +000081
Chris Allegretta88b09152001-05-17 11:35:43 +000082 buf = charalloc(strlen(last_search) + 5);
Chris Allegretta27eb13f2000-11-05 16:52:21 +000083 buf[0] = 0;
Chris Allegretta71844ba2000-11-03 14:23:00 +000084
Chris Allegretta5bf51d32000-11-16 06:01:10 +000085 /* 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 Allegrettae4933a32001-06-13 02:35:44 +0000117 /* Instead of having a million if statements here to determine
118 the prompt, we instead just have a hundred "? :" calls in
119 the statusq call. I hope no one ever has to modify this :-) */
120 prompt = "%s%s%s%s%s%s";
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000121
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000122 /* This is now one simple call. It just does a lot */
123 i = statusq(0, replacing ? replace_list : whereis_list,
124 replacing ? REPLACE_LIST_LEN : WHEREIS_LIST_LEN, backupstring,
Chris Allegrettae4933a32001-06-13 02:35:44 +0000125 prompt,
126 ISSET(CASE_SENSITIVE) ? _("Case Sensitive ") : "",
127 ISSET(USE_REGEXP) ? _("Regexp ") : "",
128 _("Search"),
129 ISSET(REVERSE_SEARCH) ? _(" Backwards") : "",
130 replacing ? _(" (to replace)") : "",
131 buf);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000132
133 /* Cancel any search, or just return with no previous search */
134 if ((i == -1) || (i < 0 && !last_search[0])) {
135 statusbar(_("Search Cancelled"));
136 reset_cursor();
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000137 free(backupstring);
138 backupstring = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000139 return -1;
Chris Allegretta658399a2001-06-14 02:54:22 +0000140 } else
141 switch (i) {
142
143 case -2: /* Same string */
Chris Allegretta805c26d2000-09-06 13:39:17 +0000144#ifdef HAVE_REGEX_H
Chris Allegretta74bb31b2001-03-14 09:08:14 +0000145 if (ISSET(USE_REGEXP)) {
146
147 /* If we're in pico mode, answer is "", use last_search! */
148 if (ISSET(PICO_MODE))
149 regexp_init(last_search);
150 else
151 regexp_init(answer);
152 }
Chris Allegretta47805612000-07-07 02:35:34 +0000153#endif
Chris Allegretta658399a2001-06-14 02:54:22 +0000154 break;
155 case 0: /* They entered something new */
Chris Allegretta805c26d2000-09-06 13:39:17 +0000156#ifdef HAVE_REGEX_H
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000157 if (ISSET(USE_REGEXP))
158 regexp_init(answer);
Chris Allegretta47805612000-07-07 02:35:34 +0000159#endif
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000160 free(backupstring);
161 backupstring = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000162 last_replace[0] = '\0';
Chris Allegretta658399a2001-06-14 02:54:22 +0000163 break;
164 case TOGGLE_CASE_KEY:
165 case TOGGLE_BACKWARDS_KEY:
166#ifdef HAVE_REGEX_H
167 case TOGGLE_REGEXP_KEY:
168#endif
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000169 free(backupstring);
170 backupstring = NULL;
171 backupstring = mallocstrcpy(backupstring, answer);
172
Chris Allegretta658399a2001-06-14 02:54:22 +0000173 for (j = 0; j <= TOGGLE_LEN - 1; j++)
174 if (i == toggles[j].val)
175 TOGGLE(toggles[j].flag);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000176
177 return 1;
Chris Allegretta658399a2001-06-14 02:54:22 +0000178 case NANO_OTHERSEARCH_KEY:
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000179 backupstring = mallocstrcpy(backupstring, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000180 return -2; /* Call the opposite search function */
Chris Allegretta658399a2001-06-14 02:54:22 +0000181/*
Chris Allegrettae4933a32001-06-13 02:35:44 +0000182 } else if (i == NANO_REVERSESEARCH_KEY) {
183 free(backupstring);
184 backupstring = NULL;
185 backupstring = mallocstrcpy(backupstring, answer);
186
Chris Allegretta658399a2001-06-14 02:54:22 +0000187 TOGGLE(REVERSE_SEARCH);
Chris Allegrettae4933a32001-06-13 02:35:44 +0000188
189 return 1;
Chris Allegretta8c2b40f2000-06-29 01:30:04 +0000190 } else if (i == NANO_FROMSEARCHTOGOTO_KEY) {
Chris Allegretta658399a2001-06-14 02:54:22 +0000191*/
192 case NANO_FROMSEARCHTOGOTO_KEY:
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000193 free(backupstring);
194 backupstring = NULL;
Chris Allegretta8c2b40f2000-06-29 01:30:04 +0000195 do_gotoline_void();
196 return -3;
Chris Allegretta658399a2001-06-14 02:54:22 +0000197 default:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000198 do_early_abort();
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000199 free(backupstring);
200 backupstring = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000201 return -3;
202 }
203
204 return 0;
205}
206
Chris Allegretta31925e42000-11-02 04:40:39 +0000207void not_found_msg(char *str)
208{
Chris Allegrettacbb0f8b2000-11-15 03:50:02 +0000209 if (strlen(str) <= COLS / 2)
Chris Allegretta31925e42000-11-02 04:40:39 +0000210 statusbar(_("\"%s\" not found"), str);
211 else {
Chris Allegrettacbb0f8b2000-11-15 03:50:02 +0000212 char *foo = NULL;
213
214 foo = mallocstrcpy(foo, str);
Chris Allegretta31925e42000-11-02 04:40:39 +0000215 foo[COLS / 2] = 0;
216 statusbar(_("\"%s...\" not found"), foo);
Chris Allegrettacbb0f8b2000-11-15 03:50:02 +0000217
218 free(foo);
Chris Allegretta31925e42000-11-02 04:40:39 +0000219 }
220}
221
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000222filestruct *findnextstr(int quiet, filestruct * begin, int beginx,
223 char *needle)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000224{
225 filestruct *fileptr;
Chris Allegrettae4933a32001-06-13 02:35:44 +0000226 char *searchstr, *rev_start = NULL, *found = NULL;
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000227 int past_editbot = 0, current_x_find;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000228
Chris Allegrettab9bfc9b2000-09-10 05:06:09 +0000229 fileptr = current;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000230
Chris Allegrettae4933a32001-06-13 02:35:44 +0000231 if (!ISSET(REVERSE_SEARCH)) { /* forward search */
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000232
Chris Allegrettae4933a32001-06-13 02:35:44 +0000233 current_x_find = current_x + 1;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000234
Chris Allegrettae4933a32001-06-13 02:35:44 +0000235 /* Are we now back to the line where the search started) */
236 if ((fileptr == begin) && (current_x_find < beginx))
237 search_last_line = 1;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000238
Chris Allegrettae4933a32001-06-13 02:35:44 +0000239 /* Make sure we haven't passed the end of the string */
240 if (strlen(fileptr->data) < current_x_find)
241 current_x_find--;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000242
Chris Allegrettae4933a32001-06-13 02:35:44 +0000243 searchstr = &fileptr->data[current_x_find];
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000244
Chris Allegrettae4933a32001-06-13 02:35:44 +0000245 /* Look for needle in searchstr */
246 while ((found = strstrwrapper(searchstr, needle, rev_start)) == NULL) {
247
248 /* finished processing file, get out */
249 if (search_last_line) {
250 if (!quiet)
251 not_found_msg(needle);
252 return NULL;
253 }
254
255 fileptr = fileptr->next;
256
257 if (!past_editbot && (fileptr == editbot))
258 past_editbot = 1;
259
260 /* EOF reached ?, wrap around once */
261 if (fileptr == NULL) {
262 fileptr = fileage;
263 past_editbot = 1;
264 if (!quiet)
265 statusbar(_("Search Wrapped"));
266 }
267
268 /* Original start line reached */
269 if (fileptr == begin)
270 search_last_line = 1;
271
272 searchstr = fileptr->data;
273 }
274
275 /* We found an instance */
276 current_x_find = found - fileptr->data;
277
278 /* Ensure we haven't wrapped around again! */
279 if ((search_last_line) && (current_x_find >= beginx)) {
Chris Allegretta1cc29f62000-11-20 01:29:37 +0000280 if (!quiet)
Chris Allegretta31925e42000-11-02 04:40:39 +0000281 not_found_msg(needle);
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000282 return NULL;
283 }
Adam Rogoyski2a4ef922000-07-09 00:15:11 +0000284
Chris Allegrettae4933a32001-06-13 02:35:44 +0000285 } else { /* reverse search */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000286
Chris Allegrettae4933a32001-06-13 02:35:44 +0000287 current_x_find = current_x - 1;
Adam Rogoyski2a4ef922000-07-09 00:15:11 +0000288
Chris Allegrettae4933a32001-06-13 02:35:44 +0000289 /* Are we now back to the line where the search started) */
290 if ((fileptr == begin) && (current_x_find > beginx))
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000291 search_last_line = 1;
292
Chris Allegrettae4933a32001-06-13 02:35:44 +0000293 /* Make sure we haven't passed the begining of the string */
294#if 0 /* Is this required here ? */
295 if (!(&fileptr->data[current_x_find] - fileptr->data))
296 current_x_find++;
297#endif
298 rev_start = &fileptr->data[current_x_find];
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000299 searchstr = fileptr->data;
Chris Allegrettae4933a32001-06-13 02:35:44 +0000300
301 /* Look for needle in searchstr */
302 while ((found = strstrwrapper(searchstr, needle, rev_start)) == NULL) {
303
304 /* finished processing file, get out */
305 if (search_last_line) {
306 if (!quiet)
307 not_found_msg(needle);
308 return NULL;
309 }
310
311 fileptr = fileptr->prev;
312
313/* ? */ if (!past_editbot && (fileptr == edittop->prev))
314 past_editbot = 1;
315
316 /* SOF reached ?, wrap around once */
317/* ? */ if (fileptr == NULL) {
318 fileptr = filebot;
319 past_editbot = 1;
320 if (!quiet)
321 statusbar(_("Search Wrapped"));
322 }
323
324 /* Original start line reached */
325 if (fileptr == begin)
326 search_last_line = 1;
327
328 searchstr = fileptr->data;
329 rev_start = fileptr->data + strlen(fileptr->data);
330 }
331
332 /* We found an instance */
333 current_x_find = found - fileptr->data;
334
335 /* Ensure we haven't wrapped around again! */
336 if ((search_last_line) && (current_x_find < beginx)) {
337 if (!quiet)
338 not_found_msg(needle);
339 return NULL;
340 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000341 }
342
Chris Allegrettae4933a32001-06-13 02:35:44 +0000343 /* Set globals now that we are sure we found something */
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000344 current = fileptr;
345 current_x = current_x_find;
346
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000347 if (past_editbot)
348 edit_update(fileptr, CENTER);
349 else
350 update_line(current, current_x);
351
352 placewewant = xplustabs();
353 reset_cursor();
354
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000355 return fileptr;
356}
357
358void search_abort(void)
359{
360 UNSET(KEEP_CUTBUFFER);
361 display_main_list();
362 wrefresh(bottomwin);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000363 if (ISSET(MARK_ISSET))
Chris Allegrettaf1d33d32000-08-19 03:53:39 +0000364 edit_refresh_clearok();
Chris Allegretta47805612000-07-07 02:35:34 +0000365
Chris Allegretta805c26d2000-09-06 13:39:17 +0000366#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000367 if (ISSET(REGEXP_COMPILED))
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000368 regexp_cleanup();
Chris Allegretta47805612000-07-07 02:35:34 +0000369#endif
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000370}
371
372/* Search for a string */
373int do_search(void)
374{
375 int i;
376 filestruct *fileptr = current;
377
378 wrap_reset();
Chris Allegretta9c371a22000-10-27 05:48:05 +0000379 i = search_init(0);
380 switch (i) {
381 case -1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000382 current = fileptr;
383 search_abort();
384 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000385 case -3:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000386 search_abort();
387 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000388 case -2:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000389 do_replace();
390 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000391 case 1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000392 do_search();
393 search_abort();
394 return 1;
395 }
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000396
397 /* The sneaky user deleted the previous search string */
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000398 if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Jordi Mallache7a647c2000-11-01 18:43:21 +0000399 statusbar(_("Search Cancelled"));
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000400 search_abort();
401 return 0;
402 }
403
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000404 /* If answer is now == "", then PICO_MODE is set. So, copy
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000405 last_search into answer... */
406
407 if (!strcmp(answer, ""))
408 answer = mallocstrcpy(answer, last_search);
409 else
410 last_search = mallocstrcpy(last_search, answer);
411
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000412 search_last_line = 0;
413 findnextstr(0, current, current_x, answer);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000414 search_abort();
415 return 1;
416}
417
418void print_replaced(int num)
419{
420 if (num > 1)
Jordi Mallach5b387d72001-02-20 12:45:47 +0000421 statusbar(_("Replaced %d occurrences"), num);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000422 else if (num == 1)
Jordi Mallach5b387d72001-02-20 12:45:47 +0000423 statusbar(_("Replaced 1 occurrence"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000424}
425
426void replace_abort(void)
427{
Chris Allegretta88520c92001-05-05 17:45:54 +0000428 /* Identical to search_abort, so we'll call it here. If it
Chris Allegretta18bd0292000-07-28 01:18:10 +0000429 does something different later, we can change it back. For now
Chris Allegretta88520c92001-05-05 17:45:54 +0000430 it's just a waste to duplicate code */
Chris Allegretta18bd0292000-07-28 01:18:10 +0000431 search_abort();
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000432 placewewant = xplustabs();
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000433}
434
Chris Allegretta805c26d2000-09-06 13:39:17 +0000435#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000436int replace_regexp(char *string, int create_flag)
437{
438 /* split personality here - if create_flag is null, just calculate
439 * the size of the replacement line (necessary because of
440 * subexpressions like \1 \2 \3 in the replaced text) */
441
442 char *c;
443 int new_size = strlen(current->data) + 1;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000444 int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000445
446 new_size -= search_match_count;
447
448 /* Iterate through the replacement text to handle
449 * subexpression replacement using \1, \2, \3, etc */
450
451 c = last_replace;
452 while (*c) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000453 if (*c != '\\') {
454 if (create_flag)
455 *string++ = *c;
456 c++;
457 new_size++;
458 } else {
459 int num = (int) *(c + 1) - (int) '0';
460 if (num >= 1 && num <= 9) {
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000461
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000462 int i = regmatches[num].rm_so;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000463
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000464 if (num > search_regexp.re_nsub) {
465 /* Ugh, they specified a subexpression that doesn't
466 exist. */
467 return -1;
468 }
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000469
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000470 /* Skip over the replacement expression */
471 c += 2;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000472
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000473 /* But add the length of the subexpression to new_size */
474 new_size += regmatches[num].rm_eo - regmatches[num].rm_so;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000475
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000476 /* And if create_flag is set, append the result of the
477 * subexpression match to the new line */
478 while (create_flag && i < regmatches[num].rm_eo)
479 *string++ = *(current->data + i++);
480
481 } else {
482 if (create_flag)
483 *string++ = *c;
484 c++;
485 new_size++;
486 }
487 }
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000488 }
489
490 if (create_flag)
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000491 *string = 0;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000492
493 return new_size;
494}
Chris Allegretta47805612000-07-07 02:35:34 +0000495#endif
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000496
Chris Allegrettab46df992000-11-01 02:12:13 +0000497char *replace_line(void)
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000498{
499 char *copy, *tmp;
500 int new_line_size;
501 int search_match_count;
502
503 /* Calculate size of new line */
Chris Allegretta805c26d2000-09-06 13:39:17 +0000504#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000505 if (ISSET(USE_REGEXP)) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000506 search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
507 new_line_size = replace_regexp(NULL, 0);
508 /* If they specified an invalid subexpression in the replace
Chris Allegretta88520c92001-05-05 17:45:54 +0000509 * text, return NULL, indicating an error */
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000510 if (new_line_size < 0)
511 return NULL;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000512 } else {
Chris Allegretta47805612000-07-07 02:35:34 +0000513#else
514 {
515#endif
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000516 search_match_count = strlen(last_search);
517 new_line_size = strlen(current->data) - strlen(last_search) +
518 strlen(last_replace) + 1;
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000519 }
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000520
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000521 /* Create buffer */
Chris Allegretta88b09152001-05-17 11:35:43 +0000522 copy = charalloc(new_line_size);
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000523
524 /* Head of Original Line */
525 strncpy(copy, current->data, current_x);
526 copy[current_x] = 0;
527
528 /* Replacement Text */
529 if (!ISSET(USE_REGEXP))
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000530 strcat(copy, last_replace);
Chris Allegretta805c26d2000-09-06 13:39:17 +0000531#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000532 else
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000533 (void) replace_regexp(copy + current_x, 1);
Chris Allegretta47805612000-07-07 02:35:34 +0000534#endif
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000535
536 /* The tail of the original line */
537 /* This may expose other bugs, because it no longer
Chris Allegretta88520c92001-05-05 17:45:54 +0000538 goes through each character in the string
Chris Allegretta9fc8d432000-07-07 01:49:52 +0000539 and tests for string goodness. But because
540 we can assume the invariant that current->data
541 is less than current_x + strlen(last_search) long,
542 this should be safe. Or it will expose bugs ;-) */
543 tmp = current->data + current_x + search_match_count;
544 strcat(copy, tmp);
545
546 return copy;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000547}
548
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000549/* step through each replace word and prompt user before replacing word */
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000550int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx,
551 int wholewords, int *i)
552{
553 int replaceall = 0, numreplaced = 0;
554 filestruct *fileptr;
555 char *copy;
556
557 switch (*i) {
558 case -1: /* Aborted enter */
559 if (strcmp(last_replace, ""))
560 answer = mallocstrcpy(answer, last_replace);
561 statusbar(_("Replace Cancelled"));
562 replace_abort();
563 return 0;
564 case 0: /* They actually entered something */
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000565 break;
566 default:
567 if (*i != -2) { /* First page, last page, for example
568 could get here */
569 do_early_abort();
570 replace_abort();
571 return 0;
572 }
573 }
574
Chris Allegrettac793c432000-11-21 12:52:55 +0000575 if (ISSET(PICO_MODE) && !strcmp(answer, ""))
576 answer = mallocstrcpy(answer, last_replace);
577
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000578 last_replace = mallocstrcpy(last_replace, answer);
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000579 while (1) {
580
581 /* Sweet optimization by Rocco here */
582 fileptr = findnextstr(replaceall, begin, *beginx, prevanswer);
583
584 /* No more matches. Done! */
Chris Allegretta022b96f2000-11-14 17:47:58 +0000585 if (!fileptr)
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000586 break;
587
Chris Allegretta88520c92001-05-05 17:45:54 +0000588 /* Make sure only whole words are found */
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000589 if (wholewords)
590 {
591 /* start of line or previous character not a letter */
Chris Allegretta63a89d32000-11-18 03:05:50 +0000592 if ((current_x == 0) || (!isalpha((int) fileptr->data[current_x-1])))
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000593 {
594 /* end of line or next character not a letter */
595 if (((current_x + strlen(prevanswer)) == strlen(fileptr->data))
Chris Allegretta63a89d32000-11-18 03:05:50 +0000596 || (!isalpha((int) fileptr->data[current_x + strlen(prevanswer)])))
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000597 ;
598 else
599 continue;
600 }
601 else
602 continue;
603 }
604
605 /* If we're here, we've found the search string */
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000606 if (!replaceall) {
607
608 curs_set(0);
609 do_replace_highlight(TRUE, prevanswer);
610
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000611 *i = do_yesno(1, 1, _("Replace this instance?"));
612
Chris Allegrettad00e6df2000-11-29 04:33:26 +0000613 do_replace_highlight(FALSE, prevanswer);
614 curs_set(1);
615 }
616
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000617 if (*i > 0 || replaceall) { /* Yes, replace it!!!! */
618 if (*i == 2)
619 replaceall = 1;
620
621 copy = replace_line();
622 if (!copy) {
623 statusbar(_("Replace failed: unknown subexpression!"));
624 replace_abort();
625 return 0;
626 }
627
628 /* Cleanup */
629 free(current->data);
630 current->data = copy;
631
632 /* Stop bug where we replace a substring of the replacement text */
633 current_x += strlen(last_replace) - 1;
634
635 /* Adjust the original cursor position - COULD BE IMPROVED */
636 if (search_last_line) {
637 *beginx += strlen(last_replace) - strlen(last_search);
638
639 /* For strings that cross the search start/end boundary */
640 /* Don't go outside of allocated memory */
641 if (*beginx < 1)
642 *beginx = 1;
643 }
644
645 edit_refresh();
646 set_modified();
647 numreplaced++;
648 } else if (*i == -1) /* Abort, else do nothing and continue loop */
649 break;
650 }
651
652 return numreplaced;
653}
654
Chris Allegretta47805612000-07-07 02:35:34 +0000655/* Replace a string */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000656int do_replace(void)
657{
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000658 int i, numreplaced, beginx;
659 filestruct *begin;
660 char *prevanswer = NULL, *buf = NULL;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000661
Chris Allegretta5050aa62001-04-22 07:10:21 +0000662 if (ISSET(VIEW_MODE)) {
663 print_view_warning();
664 replace_abort();
665 return 0;
666 }
667
Chris Allegretta9c371a22000-10-27 05:48:05 +0000668 i = search_init(1);
669 switch (i) {
670 case -1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000671 statusbar(_("Replace Cancelled"));
672 replace_abort();
673 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000674 case 1:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000675 do_replace();
676 return 1;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000677 case -2:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000678 do_search();
679 return 0;
Chris Allegretta9c371a22000-10-27 05:48:05 +0000680 case -3:
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000681 replace_abort();
682 return 0;
683 }
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000684
Chris Allegretta88520c92001-05-05 17:45:54 +0000685 /* Again, there was a previous string, but they deleted it and hit enter */
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000686 if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Chris Allegretta86f8b3a2000-10-31 05:28:33 +0000687 statusbar(_("Replace Cancelled"));
688 replace_abort();
689 return 0;
690 }
691
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000692 /* If answer is now == "", then PICO_MODE is set. So, copy
Chris Allegretta5bf51d32000-11-16 06:01:10 +0000693 last_search into answer (and prevanswer)... */
694 if (!strcmp(answer, "")) {
695 answer = mallocstrcpy(answer, last_search);
696 prevanswer = mallocstrcpy(prevanswer, last_search);
697 } else {
698 last_search = mallocstrcpy(last_search, answer);
699 prevanswer = mallocstrcpy(prevanswer, answer);
700 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000701
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +0000702 if (ISSET(PICO_MODE)) {
Chris Allegretta88b09152001-05-17 11:35:43 +0000703 buf = charalloc(strlen(last_replace) + 5);
Chris Allegretta71844ba2000-11-03 14:23:00 +0000704 if (strcmp(last_replace, "")) {
705 if (strlen(last_replace) > (COLS / 3)) {
706 strncpy(buf, last_replace, COLS / 3);
707 sprintf(&buf[COLS / 3 - 1], "...");
708 } else
709 sprintf(buf, "%s", last_replace);
710
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000711 i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta71844ba2000-11-03 14:23:00 +0000712 _("Replace with [%s]"), buf);
713 }
714 else
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000715 i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta71844ba2000-11-03 14:23:00 +0000716 _("Replace with"));
717 }
718 else
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000719 i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, last_replace,
Chris Allegretta105da332000-10-31 05:10:10 +0000720 _("Replace with"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000721
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000722 /* save where we are */
723 begin = current;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000724 beginx = current_x + 1;
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000725
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000726 search_last_line = 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000727
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000728 numreplaced = do_replace_loop(prevanswer, begin, &beginx, FALSE, &i);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000729
Chris Allegretta27eb13f2000-11-05 16:52:21 +0000730 /* restore where we were */
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000731 current = begin;
Chris Allegrettaa0e957b2000-10-24 22:25:36 +0000732 current_x = beginx - 1;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000733 renumber_all();
Chris Allegretta234a34d2000-07-29 04:33:38 +0000734 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000735 print_replaced(numreplaced);
736 replace_abort();
737 return 1;
738}
739
740void goto_abort(void)
741{
742 UNSET(KEEP_CUTBUFFER);
743 display_main_list();
744}
745
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000746int do_gotoline(long line)
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000747{
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000748 long i = 1;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000749
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000750 if (line <= 0) { /* Ask for it */
751
752 long j = 0;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000753
Chris Allegretta7da4e9f2000-11-06 02:57:22 +0000754 j = statusq(0, goto_list, GOTO_LIST_LEN, "", _("Enter line number"));
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000755 if (j != 0) {
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000756 statusbar(_("Aborted"));
757 goto_abort();
758 return 0;
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000759 }
760
761 line = atoi(answer);
762
763 /* Bounds check */
764 if (line <= 0) {
765 statusbar(_("Come on, be reasonable"));
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000766 goto_abort();
767 return 0;
768 }
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000769 }
770
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000771 for (current = fileage; ((current->next != NULL) && (i < line)); i++)
772 current = current->next;
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000773
Rocco Corsi4dfaf932001-04-20 01:59:55 +0000774 current_x = 0;
775 edit_update(current, CENTER);
Chris Allegrettabceb1b22000-06-19 04:22:15 +0000776
777 goto_abort();
778 return 1;
779}
780
781int do_gotoline_void(void)
782{
783 return do_gotoline(0);
784}