search.c 23 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   search.c                                                             *
 *                                                                        *
5
 *   Copyright (C) 2000-2002 Chris Allegretta                             *
Chris Allegretta's avatar
Chris Allegretta committed
6
7
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
8
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
11
12
13
14
15
16
17
18
19
20
21
 *   any later version.                                                   *
 *                                                                        *
 *   This program is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 *   GNU General Public License for more details.                         *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

22
23
#include "config.h"

Chris Allegretta's avatar
Chris Allegretta committed
24
25
#include <stdlib.h>
#include <string.h>
26
#include <unistd.h>
Chris Allegretta's avatar
Chris Allegretta committed
27
#include <stdio.h>
Chris Allegretta's avatar
Chris Allegretta committed
28
#include <ctype.h>
Chris Allegretta's avatar
Chris Allegretta committed
29
30
#include "proto.h"
#include "nano.h"
Chris Allegretta's avatar
Chris Allegretta committed
31

32
#ifdef ENABLE_NLS
Chris Allegretta's avatar
Chris Allegretta committed
33
34
35
36
37
38
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif

39
40
/* Regular expression helper functions */

41
#ifdef HAVE_REGEX_H
42
43
44
45
46
47
void regexp_init(const char *regexp)
{
    regcomp(&search_regexp, regexp, ISSET(CASE_SENSITIVE) ? 0 : REG_ICASE);
    SET(REGEXP_COMPILED);
}

48
void regexp_cleanup(void)
49
50
51
52
{
    UNSET(REGEXP_COMPILED);
    regfree(&search_regexp);
}
53
#endif
54

Chris Allegretta's avatar
Chris Allegretta committed
55
56
57
void search_init_globals(void)
{
    if (last_search == NULL) {
58
	last_search = charalloc(1);
Chris Allegretta's avatar
Chris Allegretta committed
59
	last_search[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
60
61
    }
    if (last_replace == NULL) {
62
	last_replace = charalloc(1);
Chris Allegretta's avatar
Chris Allegretta committed
63
	last_replace[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
64
65
66
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
67
68
/* Set up the system variables for a search or replace.  Returns -1 on
   abort, 0 on success, and 1 on rerun calling program 
69
   Return -2 to run opposite program (search -> replace, replace -> search)
Chris Allegretta's avatar
Chris Allegretta committed
70
71
72
73
74

   replacing = 1 if we call from do_replace, 0 if called from do_search func.
*/
int search_init(int replacing)
{
75
    int i = 0;
76
    char *buf;
77
    static char *backupstring = NULL;
78

Chris Allegretta's avatar
Chris Allegretta committed
79
    search_init_globals();
80

81
    buf = charalloc(strlen(last_search) + 5);
Chris Allegretta's avatar
Chris Allegretta committed
82
    buf[0] = '\0';
83

Chris Allegretta's avatar
Chris Allegretta committed
84
85
86
87
88
89

    /* Clear the backupstring if we've changed from Pico mode to regular
	mode */
    if (ISSET(CLEAR_BACKUPSTRING)) {
	free(backupstring);
	backupstring = NULL;
90
	UNSET(CLEAR_BACKUPSTRING);
Chris Allegretta's avatar
Chris Allegretta committed
91
92
    }
	
93
94
     /* Okay, fun time.  backupstring is our holder for what is being 
	returned from the statusq call.  Using answer for this would be tricky.
95
	Here, if we're using PICO_MODE, we only want nano to put the
96
97
98
99
100
	old string back up as editable if it's not the same as last_search.

	Otherwise, if we don't already have a backupstring, set it to
	last_search.  */

101
    if (ISSET(PICO_MODE)) {
102
103
104
105
106
107
	if (backupstring == NULL || !strcmp(backupstring, last_search))
	    backupstring = mallocstrcpy(backupstring, "");
    }
    else if (backupstring == NULL)
	backupstring = mallocstrcpy(backupstring, last_search);

Chris Allegretta's avatar
Chris Allegretta committed
108
    /* If using Pico messages, we do things the old fashioned way... */
109
    if (ISSET(PICO_MODE)) {
110
111
112
113
114
115
116
117
118
119
120
	if (last_search[0]) {

	    /* We use COLS / 3 here because we need to see more on the line */
	    if (strlen(last_search) > COLS / 3) {
		snprintf(buf, COLS / 3 + 3, " [%s", last_search);
		sprintf(&buf[COLS / 3 + 2], "...]");
	    } else
		sprintf(buf, " [%s]", last_search);
	} else {
	    buf[0] = '\0';
	}
Chris Allegretta's avatar
Chris Allegretta committed
121
    }
122
    else
Chris Allegretta's avatar
Chris Allegretta committed
123
	buf[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
124

125
    /* This is now one simple call.  It just does a lot */
126
    i = statusq(0, replacing ? replace_list : whereis_list, backupstring,
127
	"%s%s%s%s%s%s", 
Chris Allegretta's avatar
Chris Allegretta committed
128
	_("Search"),
129
130
131
132
133
134
135
136
137
138
139
140
141

	/* This string is just a modifier for the search prompt,
	   no grammar is implied */
	ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") : "",

	/* This string is just a modifier for the search prompt,
	   no grammar is implied */
	ISSET(USE_REGEXP) ? _(" [Regexp]") : "",

	/* This string is just a modifier for the search prompt,
	   no grammar is implied */
	ISSET(REVERSE_SEARCH) ? _(" [Backwards]") : "",

Chris Allegretta's avatar
Chris Allegretta committed
142
143
	replacing ? _(" (to replace)") : "",
	buf);
Chris Allegretta's avatar
Chris Allegretta committed
144

Chris Allegretta's avatar
Chris Allegretta committed
145
146
147
    /* Release buf now that we don't need it anymore */
    free(buf);

Chris Allegretta's avatar
Chris Allegretta committed
148
149
150
151
    /* Cancel any search, or just return with no previous search */
    if ((i == -1) || (i < 0 && !last_search[0])) {
	statusbar(_("Search Cancelled"));
	reset_cursor();
152
153
	free(backupstring);
	backupstring = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
154
	return -1;
155
156
157
158
    } else 
    switch (i) {

    case -2:	/* Same string */
159
#ifdef HAVE_REGEX_H
160
161
162
163
164
165
166
167
	if (ISSET(USE_REGEXP)) {

	    /* If we're in pico mode, answer is "", use last_search! */
	    if (ISSET(PICO_MODE))
		regexp_init(last_search);
	    else
		regexp_init(answer);
	}
168
#endif
169
170
	break;
    case 0:		/* They entered something new */
171
#ifdef HAVE_REGEX_H
172
173
	if (ISSET(USE_REGEXP))
	    regexp_init(answer);
174
#endif
175
176
	free(backupstring);
	backupstring = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
177
	last_replace[0] = '\0';
178
179
	break;
    case TOGGLE_CASE_KEY:
180
181
	TOGGLE(CASE_SENSITIVE);
	goto string_reinit;
182
    case TOGGLE_BACKWARDS_KEY:
183
184
	TOGGLE(REVERSE_SEARCH);
	goto string_reinit;
185
186
#ifdef HAVE_REGEX_H
    case TOGGLE_REGEXP_KEY:
187
	TOGGLE(USE_REGEXP);
188
#endif
189
  string_reinit:
190
191
192
	free(backupstring);
	backupstring = NULL;
	backupstring = mallocstrcpy(backupstring, answer);
Chris Allegretta's avatar
Chris Allegretta committed
193
	return 1;
194
    case NANO_OTHERSEARCH_KEY:
195
	backupstring = mallocstrcpy(backupstring, answer);
Chris Allegretta's avatar
Chris Allegretta committed
196
	return -2;		/* Call the opposite search function */
197
    case NANO_FROMSEARCHTOGOTO_KEY:
198
199
	free(backupstring);
	backupstring = NULL;
200
201
	do_gotoline_void();
	return -3;
202
    default:
Chris Allegretta's avatar
Chris Allegretta committed
203
	do_early_abort();
204
205
	free(backupstring);
	backupstring = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
206
207
208
209
210
211
	return -3;
    }

    return 0;
}

212
213
void not_found_msg(char *str)
{
214
    if (strlen(str) <= COLS / 2)
215
216
	statusbar(_("\"%s\" not found"), str);
    else {
217
218
219
	char *foo = NULL;

	foo = mallocstrcpy(foo, str);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
220
	foo[COLS / 2] = '\0';
221
	statusbar(_("\"%s...\" not found"), foo);
222
223

	free(foo);
224
225
226
    }
}

227
228
229
230
231
232
233
234
235
236
237
238
239
240
int is_whole_word(int curr_pos, filestruct *fileptr, char *searchword)
{
    /* start of line or previous character not a letter */
    if ((curr_pos < 1) || (!isalpha((int) fileptr->data[curr_pos-1])))

	/* end of line or next character not a letter */
	if (((curr_pos + strlen(searchword)) == strlen(fileptr->data))
	    || (!isalpha((int) fileptr->data[curr_pos + strlen(searchword)])))

	    return TRUE;

    return FALSE;
}

241
int past_editbuff;	/* findnextstr() is now searching lines not displayed */
242
243

filestruct *findnextstr(int quiet, int bracket_mode, filestruct * begin, int beginx,
244
			char *needle)
Chris Allegretta's avatar
Chris Allegretta committed
245
{
Chris Allegretta's avatar
Chris Allegretta committed
246
    filestruct *fileptr = current;
Chris Allegretta's avatar
Chris Allegretta committed
247
    char *searchstr, *rev_start = NULL, *found = NULL;
248
    int current_x_find = 0;
Chris Allegretta's avatar
Chris Allegretta committed
249

250
251
    past_editbuff = 0;

Chris Allegretta's avatar
Chris Allegretta committed
252
    if (!ISSET(REVERSE_SEARCH)) {		/* forward search */
Chris Allegretta's avatar
Chris Allegretta committed
253

Chris Allegretta's avatar
Chris Allegretta committed
254
	current_x_find = current_x + 1;
255
256
#if 0
	/* Are we now back to the place where the search started) */
257
	if ((fileptr == begin) && (beginx > current_x_find))
Chris Allegretta's avatar
Chris Allegretta committed
258
	    search_last_line = 1;
259
#endif
Chris Allegretta's avatar
Chris Allegretta committed
260
261
262
	/* Make sure we haven't passed the end of the string */
	if (strlen(fileptr->data) < current_x_find)
	    current_x_find--;
Chris Allegretta's avatar
Chris Allegretta committed
263

Chris Allegretta's avatar
Chris Allegretta committed
264
	searchstr = &fileptr->data[current_x_find];
Chris Allegretta's avatar
Chris Allegretta committed
265

Chris Allegretta's avatar
Chris Allegretta committed
266
	/* Look for needle in searchstr */
267
	while ((found = strstrwrapper(searchstr, needle, rev_start, current_x_find)) == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
268

Chris Allegretta's avatar
Chris Allegretta committed
269
270
271
272
	    /* finished processing file, get out */
	    if (search_last_line) {
		if (!quiet)
		    not_found_msg(needle);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
273
		update_line(fileptr, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
274
275
	        return NULL;
	    }
276

Chris Allegretta's avatar
Chris Allegretta committed
277
	    update_line(fileptr, 0);
278
279
280
281

	    /* reset current_x_find between lines */
	    current_x_find = 0;

Chris Allegretta's avatar
Chris Allegretta committed
282
	    fileptr = fileptr->next;
283

284
	    if (fileptr == editbot)
285
		past_editbuff = 1;
Chris Allegretta's avatar
Chris Allegretta committed
286
287
288

	    /* EOF reached ?, wrap around once */
	    if (fileptr == NULL) {
289
290
		/* don't wrap if looking for bracket match */
		if (bracket_mode)
Chris Allegretta's avatar
Chris Allegretta committed
291
		    return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
292
		fileptr = fileage;
293
		past_editbuff = 1;
294
		if (!quiet) {
Chris Allegretta's avatar
Chris Allegretta committed
295
296
		    statusbar(_("Search Wrapped"));
		    SET(DISABLE_CURPOS);
297
		}
Chris Allegretta's avatar
Chris Allegretta committed
298
299
300
301
302
303
304
305
	    }

	    /* Original start line reached */
	    if (fileptr == begin)
		search_last_line = 1;

	    searchstr = fileptr->data;
	}
306

Chris Allegretta's avatar
Chris Allegretta committed
307
308
	/* We found an instance */
	current_x_find = found - fileptr->data;
309
#if 1
Chris Allegretta's avatar
Chris Allegretta committed
310
	/* Ensure we haven't wrapped around again! */
311
	if ((search_last_line) && (current_x_find > beginx)) {
Chris Allegretta's avatar
Chris Allegretta committed
312
	    if (!quiet)
Chris Allegretta's avatar
Chris Allegretta committed
313
314
		not_found_msg(needle);
	    return NULL;
315
	}
316
#endif
317
318
319
    } 
#ifndef NANO_SMALL
    else {	/* reverse search */
Chris Allegretta's avatar
Chris Allegretta committed
320
321

	current_x_find = current_x - 1;
322
#if 0
323
	/* Are we now back to the place where the search started) */
Chris Allegretta's avatar
Chris Allegretta committed
324
	if ((fileptr == begin) && (current_x_find > beginx))
325
	    search_last_line = 1;
326
#endif
Chris Allegretta's avatar
Chris Allegretta committed
327
	/* Make sure we haven't passed the begining of the string */
328
#if 0	/* Is this required here ? */
Chris Allegretta's avatar
Chris Allegretta committed
329
330
331
332
	if (!(&fileptr->data[current_x_find] - fileptr->data))      
	    current_x_find++;
#endif
	rev_start = &fileptr->data[current_x_find];
333
334
	searchstr = fileptr->data;

Chris Allegretta's avatar
Chris Allegretta committed
335
	/* Look for needle in searchstr */
336
	while ((found = strstrwrapper(searchstr, needle, rev_start, current_x_find)) == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
337
338
339
340
341
342
343
344

	    /* finished processing file, get out */
	    if (search_last_line) {
		if (!quiet)
		    not_found_msg(needle);
		return NULL;
	    }

Chris Allegretta's avatar
Chris Allegretta committed
345
	    update_line(fileptr, 0);
346
347
348
349

	    /* reset current_x_find between lines */
	    current_x_find = 0;

Chris Allegretta's avatar
Chris Allegretta committed
350
351
	    fileptr = fileptr->prev;

352
	    if (fileptr == edittop->prev)
353
		past_editbuff = 1;
354

Chris Allegretta's avatar
Chris Allegretta committed
355
356
	    /* SOF reached ?, wrap around once */
/* ? */	    if (fileptr == NULL) {
357
358
		if (bracket_mode)
		   return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
359
		fileptr = filebot;
360
		past_editbuff = 1;
361
		if (!quiet) {
Chris Allegretta's avatar
Chris Allegretta committed
362
		    statusbar(_("Search Wrapped"));
363
364
		    SET(DISABLE_CURPOS);
		}
Chris Allegretta's avatar
Chris Allegretta committed
365
366
367
368
369
370
371
372
373
374
375
376
	    }

	    /* Original start line reached */
	    if (fileptr == begin)
		search_last_line = 1;

	    searchstr = fileptr->data;
	    rev_start = fileptr->data + strlen(fileptr->data);
	}

	/* We found an instance */
	current_x_find = found - fileptr->data;
377
#if 1
Chris Allegretta's avatar
Chris Allegretta committed
378
379
380
381
382
383
	/* Ensure we haven't wrapped around again! */
	if ((search_last_line) && (current_x_find < beginx)) {
	    if (!quiet)
		not_found_msg(needle);
	    return NULL;
	}
384
#endif
Chris Allegretta's avatar
Chris Allegretta committed
385
    }
386
#endif /* NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
387

Chris Allegretta's avatar
Chris Allegretta committed
388
    /* Set globals now that we are sure we found something */
389
390
391
    current = fileptr;
    current_x = current_x_find;

392
393
394
395
396
    if (!bracket_mode) {
	if (past_editbuff)
	   edit_update(fileptr, CENTER);
	else
	   update_line(current, current_x);
397

398
399
400
	placewewant = xplustabs();
	reset_cursor();
    }
401

Chris Allegretta's avatar
Chris Allegretta committed
402
403
404
405
406
407
408
409
    return fileptr;
}

void search_abort(void)
{
    UNSET(KEEP_CUTBUFFER);
    display_main_list();
    wrefresh(bottomwin);
410
    if (ISSET(MARK_ISSET))
411
	edit_refresh_clearok();
412

413
#ifdef HAVE_REGEX_H
414
    if (ISSET(REGEXP_COMPILED))
415
	regexp_cleanup();
416
#endif
Chris Allegretta's avatar
Chris Allegretta committed
417
418
419
420
421
422
}

/* Search for a string */
int do_search(void)
{
    int i;
423
424
    filestruct *fileptr = current, *didfind;
    int fileptr_x = current_x;
Chris Allegretta's avatar
Chris Allegretta committed
425
426

    wrap_reset();
427
428
429
    i = search_init(0);
    switch (i) {
    case -1:
Chris Allegretta's avatar
Chris Allegretta committed
430
431
432
	current = fileptr;
	search_abort();
	return 0;
433
    case -3:
Chris Allegretta's avatar
Chris Allegretta committed
434
435
	search_abort();
	return 0;
436
    case -2:
Chris Allegretta's avatar
Chris Allegretta committed
437
438
	do_replace();
	return 0;
439
    case 1:
Chris Allegretta's avatar
Chris Allegretta committed
440
441
442
443
	do_search();
	search_abort();
	return 1;
    }
444
445

    /* The sneaky user deleted the previous search string */
Chris Allegretta's avatar
Chris Allegretta committed
446
    if (!ISSET(PICO_MODE) && answer[0] == '\0') {
Jordi Mallach's avatar
   
Jordi Mallach committed
447
	statusbar(_("Search Cancelled"));
448
449
450
451
	search_abort();
	return 0;
    }

452
     /* If answer is now == "", then PICO_MODE is set.  So, copy
453
454
	last_search into answer... */

Chris Allegretta's avatar
Chris Allegretta committed
455
    if (answer[0] == '\0')
456
457
458
459
	answer = mallocstrcpy(answer, last_search);
    else
	last_search = mallocstrcpy(last_search, answer);

460
    search_last_line = 0;
461
462
463
464
465
466
    didfind = findnextstr(FALSE, FALSE, current, current_x, answer);

    if ((fileptr == current) && (fileptr_x == current_x) &&
	didfind != NULL)
	statusbar(_("This is the only occurrence"));

Chris Allegretta's avatar
Chris Allegretta committed
467
    search_abort();
468

Chris Allegretta's avatar
Chris Allegretta committed
469
470
471
472
473
474
    return 1;
}

void print_replaced(int num)
{
    if (num > 1)
Jordi Mallach's avatar
   
Jordi Mallach committed
475
	statusbar(_("Replaced %d occurrences"), num);
Chris Allegretta's avatar
Chris Allegretta committed
476
    else if (num == 1)
Jordi Mallach's avatar
   
Jordi Mallach committed
477
	statusbar(_("Replaced 1 occurrence"));
Chris Allegretta's avatar
Chris Allegretta committed
478
479
480
481
}

void replace_abort(void)
{
482
    /* Identical to search_abort, so we'll call it here.  If it
483
       does something different later, we can change it back.  For now
484
       it's just a waste to duplicate code */
485
    search_abort();
486
    placewewant = xplustabs();
487
488
}

489
#ifdef HAVE_REGEX_H
490
491
492
493
494
495
496
497
int replace_regexp(char *string, int create_flag)
{
    /* split personality here - if create_flag is null, just calculate
     * the size of the replacement line (necessary because of
     * subexpressions like \1 \2 \3 in the replaced text) */

    char *c;
    int new_size = strlen(current->data) + 1;
498
    int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
499
500
501
502
503
504
505
506

    new_size -= search_match_count;

    /* Iterate through the replacement text to handle
     * subexpression replacement using \1, \2, \3, etc */

    c = last_replace;
    while (*c) {
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
	if (*c != '\\') {
	    if (create_flag)
		*string++ = *c;
	    c++;
	    new_size++;
	} else {
	    int num = (int) *(c + 1) - (int) '0';
	    if (num >= 1 && num <= 9) {

		int i = regmatches[num].rm_so;

		if (num > search_regexp.re_nsub) {
		    /* Ugh, they specified a subexpression that doesn't
		       exist.  */
		    return -1;
		}

		/* Skip over the replacement expression */
		c += 2;

		/* But add the length of the subexpression to new_size */
		new_size += regmatches[num].rm_eo - regmatches[num].rm_so;

		/* And if create_flag is set, append the result of the
		 * subexpression match to the new line */
		while (create_flag && i < regmatches[num].rm_eo)
		    *string++ = *(current->data + i++);

	    } else {
		if (create_flag)
		    *string++ = *c;
		c++;
		new_size++;
	    }
	}
542
543
544
    }

    if (create_flag)
545
	*string = 0;
546
547
548

    return new_size;
}
549
#endif
550

Chris Allegretta's avatar
Chris Allegretta committed
551
char *replace_line(void)
552
553
554
555
556
557
{
    char *copy, *tmp;
    int new_line_size;
    int search_match_count;

    /* Calculate size of new line */
558
#ifdef HAVE_REGEX_H
559
    if (ISSET(USE_REGEXP)) {
560
561
562
	search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
	new_line_size = replace_regexp(NULL, 0);
	/* If they specified an invalid subexpression in the replace
563
	 * text, return NULL, indicating an error */
564
565
	if (new_line_size < 0)
	    return NULL;
566
    } else {
567
568
569
#else
    {
#endif
570
571
572
	search_match_count = strlen(last_search);
	new_line_size = strlen(current->data) - strlen(last_search) +
	    strlen(last_replace) + 1;
573
    }
574

575
    /* Create buffer */
576
    copy = charalloc(new_line_size);
577
578
579

    /* Head of Original Line */
    strncpy(copy, current->data, current_x);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
580
    copy[current_x] = '\0';
581
582
583

    /* Replacement Text */
    if (!ISSET(USE_REGEXP))
584
	strcat(copy, last_replace);
585
#ifdef HAVE_REGEX_H
586
    else
587
	(void) replace_regexp(copy + current_x, 1);
588
#endif
589
590
591

    /* The tail of the original line */
    /* This may expose other bugs, because it no longer
592
       goes through each character in the string
593
594
595
596
597
598
599
600
       and tests for string goodness.  But because
       we can assume the invariant that current->data
       is less than current_x + strlen(last_search) long,
       this should be safe.  Or it will expose bugs ;-) */
    tmp = current->data + current_x + search_match_count;
    strcat(copy, tmp);

    return copy;
Chris Allegretta's avatar
Chris Allegretta committed
601
602
}

603
/* step through each replace word and prompt user before replacing word */
Chris Allegretta's avatar
Chris Allegretta committed
604
605
int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx,
			int wholewords, int *i)
Chris Allegretta's avatar
Chris Allegretta committed
606
{
Chris Allegretta's avatar
Chris Allegretta committed
607
    int replaceall = 0, numreplaced = 0;
608
609

    filestruct *fileptr = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
610
    char *copy;
Chris Allegretta's avatar
Chris Allegretta committed
611

Chris Allegretta's avatar
Chris Allegretta committed
612
    switch (*i) {
613
    case -1:				/* Aborted enter */
Chris Allegretta's avatar
Chris Allegretta committed
614
	if (last_replace[0] != '\0')
615
	    answer = mallocstrcpy(answer, last_replace);
616
617
618
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
619
620
621
    case 0:		/* They actually entered something */
	break;
    default:
Chris Allegretta's avatar
Chris Allegretta committed
622
        if (*i != -2) {	/* First page, last page, for example 
623
				   could get here */
Chris Allegretta's avatar
Chris Allegretta committed
624
625
626
	    do_early_abort();
	    replace_abort();
	    return 0;
627
        }
Chris Allegretta's avatar
Chris Allegretta committed
628
629
    }

Chris Allegretta's avatar
Chris Allegretta committed
630
    if (ISSET(PICO_MODE) && answer[0] == '\0')
631
632
	answer = mallocstrcpy(answer, last_replace);

633
    last_replace = mallocstrcpy(last_replace, answer);
Chris Allegretta's avatar
Chris Allegretta committed
634
635
    while (1) {

636
	/* Sweet optimization by Rocco here */
637
#if 0
638
	fileptr = findnextstr(replaceall, FALSE, begin, *beginx, prevanswer);
639
640
641
642
643
644
#else
	if (fileptr != 0)
	    fileptr = findnextstr(1, FALSE, begin, *beginx, prevanswer);
	else
	    fileptr = findnextstr(replaceall || (search_last_line ? 1 : 0), FALSE, begin, *beginx, prevanswer);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
645
646

	/* No more matches.  Done! */
647
	if (!fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
648
649
	    break;

650
	/* Make sure only whole words are found */
651
652
	if ((wholewords) && (!is_whole_word(current_x, fileptr, prevanswer)))
	    continue;
Chris Allegretta's avatar
Chris Allegretta committed
653

Chris Allegretta's avatar
Chris Allegretta committed
654
	/* If we're here, we've found the search string */
655
656
657
658
659
	if (!replaceall) {

	    curs_set(0);
	    do_replace_highlight(TRUE, prevanswer);

Chris Allegretta's avatar
Chris Allegretta committed
660
	    *i = do_yesno(1, 1, _("Replace this instance?"));
Chris Allegretta's avatar
Chris Allegretta committed
661

662
663
664
665
	    do_replace_highlight(FALSE, prevanswer);
	    curs_set(1);
	}

Chris Allegretta's avatar
Chris Allegretta committed
666
667
	if (*i > 0 || replaceall) {	/* Yes, replace it!!!! */
	    if (*i == 2)
Chris Allegretta's avatar
Chris Allegretta committed
668
669
		replaceall = 1;

670
671
	    copy = replace_line();
	    if (!copy) {
Jordi Mallach's avatar
   
Jordi Mallach committed
672
		statusbar(_("Replace failed: unknown subexpression!"));
673
		replace_abort();
Chris Allegretta's avatar
Chris Allegretta committed
674
		return 0;
675
	    }
Chris Allegretta's avatar
Chris Allegretta committed
676
677

	    /* Cleanup */
678
	    totsize -= strlen(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
679
680
	    free(current->data);
	    current->data = copy;
681
	    totsize += strlen(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
682

683
684
685
	    if (!ISSET(REVERSE_SEARCH)) {
		/* Stop bug where we replace a substring of the replacement text */
		current_x += strlen(last_replace) - 1;
Chris Allegretta's avatar
Chris Allegretta committed
686

687
688
689
		/* Adjust the original cursor position - COULD BE IMPROVED */
		if (search_last_line) {
		    *beginx += strlen(last_replace) - strlen(last_search);
690

691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
		    /* For strings that cross the search start/end boundary */
		    /* Don't go outside of allocated memory */
		    if (*beginx < 1)
			*beginx = 1;
		}
	    } else {
		if (current_x > 1)
		    current_x--;

		if (search_last_line) {
		    *beginx += strlen(last_replace) - strlen(last_search);

		    if (*beginx > strlen(current->data))
			*beginx = strlen(current->data);
		}
706
707
	    }

Chris Allegretta's avatar
Chris Allegretta committed
708
709
710
	    edit_refresh();
	    set_modified();
	    numreplaced++;
Chris Allegretta's avatar
Chris Allegretta committed
711
	} else if (*i == -1)	/* Abort, else do nothing and continue loop */
Chris Allegretta's avatar
Chris Allegretta committed
712
713
714
	    break;
    }

Chris Allegretta's avatar
Chris Allegretta committed
715
716
717
718
719
720
721
722
723
724
    return numreplaced;
}

/* Replace a string */
int do_replace(void)
{
    int i, numreplaced, beginx;
    filestruct *begin;
    char *prevanswer = NULL, *buf = NULL;

Chris Allegretta's avatar
Chris Allegretta committed
725
726
727
728
729
730
    if (ISSET(VIEW_MODE)) {
	print_view_warning();
	replace_abort();
	return 0;
    }

Chris Allegretta's avatar
Chris Allegretta committed
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
    i = search_init(1);
    switch (i) {
    case -1:
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
    case 1:
	do_replace();
	return 1;
    case -2:
	do_search();
	return 0;
    case -3:
	replace_abort();
	return 0;
    }

748
    /* Again, there was a previous string, but they deleted it and hit enter */
Chris Allegretta's avatar
Chris Allegretta committed
749
    if (!ISSET(PICO_MODE) && answer[0] == '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
750
751
752
753
754
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
    }

755
     /* If answer is now == "", then PICO_MODE is set.  So, copy
756
	last_search into answer (and prevanswer)... */
Chris Allegretta's avatar
Chris Allegretta committed
757
    if (answer[0] == '\0') {
758
759
760
761
762
763
	answer = mallocstrcpy(answer, last_search);
	prevanswer = mallocstrcpy(prevanswer, last_search);
    } else {
	last_search = mallocstrcpy(last_search, answer);
	prevanswer = mallocstrcpy(prevanswer, answer);
    }
Chris Allegretta's avatar
Chris Allegretta committed
764

765
    if (ISSET(PICO_MODE)) {
766
	buf = charalloc(strlen(last_replace) + 5);
Chris Allegretta's avatar
Chris Allegretta committed
767
	if (last_replace[0] != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
768
769
770
771
772
773
	    if (strlen(last_replace) > (COLS / 3)) {
		strncpy(buf, last_replace, COLS / 3);
		sprintf(&buf[COLS / 3 - 1], "...");
	    } else
		sprintf(buf, "%s", last_replace);

774
	    i = statusq(0, replace_list_2, "",
Chris Allegretta's avatar
Chris Allegretta committed
775
776
777
			_("Replace with [%s]"), buf);
	}
	else
778
	    i = statusq(0, replace_list_2, "",
Chris Allegretta's avatar
Chris Allegretta committed
779
780
781
			_("Replace with"));
    }
    else
782
	i = statusq(0, replace_list_2, last_replace, 
Chris Allegretta's avatar
Chris Allegretta committed
783
784
785
786
			_("Replace with"));

    /* save where we are */
    begin = current;
787
788
#if 0
    /* why + 1  ? isn't this taken care of in findnextstr() ? */
Chris Allegretta's avatar
Chris Allegretta committed
789
    beginx = current_x + 1;
790
791
792
#else
    beginx = current_x;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
793
794
795
796
797
    search_last_line = 0;

    numreplaced = do_replace_loop(prevanswer, begin, &beginx, FALSE, &i);

    /* restore where we were */
Chris Allegretta's avatar
Chris Allegretta committed
798
    current = begin;
799
#if 0
800
    current_x = beginx - 1;
801
802
803
#else
    current_x = beginx;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
804
    renumber_all();
805
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
806
807
808
809
810
811
812
813
814
815
816
    print_replaced(numreplaced);
    replace_abort();
    return 1;
}

void goto_abort(void)
{
    UNSET(KEEP_CUTBUFFER);
    display_main_list();
}

817
int do_gotoline(int line, int save_pos)
Chris Allegretta's avatar
Chris Allegretta committed
818
{
819
    if (line <= 0) {		/* Ask for it */
820
	if (statusq(0, goto_list, "", _("Enter line number"))) {
Chris Allegretta's avatar
Chris Allegretta committed
821
822
823
824
	    statusbar(_("Aborted"));
	    goto_abort();
	    return 0;
	}
825
826
827
828
829
830

	line = atoi(answer);

	/* Bounds check */
	if (line <= 0) {
	    statusbar(_("Come on, be reasonable"));
Chris Allegretta's avatar
Chris Allegretta committed
831
	    goto_abort();
832
	    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
833
834
835
	}
    }

836
    for (current = fileage; current->next != NULL && line > 1; line--)
837
	current = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
838

839
    current_x = 0;
840
841
842
843
844
845
846

    /* if save_pos is non-zero, don't change the cursor position when
       updating the edit window */
    if (save_pos)
    	edit_update(current, NONE);
    else
	edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
847

848
    placewewant = 0;
Chris Allegretta's avatar
Chris Allegretta committed
849
850
851
852
853
854
    goto_abort();
    return 1;
}

int do_gotoline_void(void)
{
855
    return do_gotoline(0, 0);
Chris Allegretta's avatar
Chris Allegretta committed
856
}
857

858
#if defined(ENABLE_MULTIBUFFER) || !defined(DISABLE_SPELLER)
859
void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant)
860
861
862
863
864
{
    /* since do_gotoline() resets the x-coordinate but not the
       y-coordinate, set the coordinates up this way */
    current_y = pos_y;
    do_gotoline(line, 1);
865

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
866
867
868
    /* make sure that the x-coordinate is sane here */
    if (pos_x > strlen(current->data))
	pos_x = strlen(current->data);
869
870

    /* set the rest of the coordinates up */
871
872
873
874
875
    current_x = pos_x;
    placewewant = pos_placewewant;
    update_line(current, pos_x);
}
#endif
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946

#if !defined(NANO_SMALL) && defined(HAVE_REGEX_H)
int do_find_bracket(void)
{
    char ch_under_cursor, wanted_ch;
    char *pos, *brackets = "([{<>}])";
    char regexp_pat[] = "[  ]";
    int offset, have_past_editbuff = 0, flagsave, current_x_save, count = 1;
    filestruct *current_save;

    ch_under_cursor = current->data[current_x];
 
    if ((!(pos = strchr(brackets, ch_under_cursor))) || (!((offset = pos - brackets) < 8))) {
	statusbar(_("Not a bracket"));
	return 1;
    }

    blank_statusbar_refresh();

    wanted_ch = *(brackets + ((strlen(brackets) - (offset + 1))));

    current_x_save = current_x;
    current_save = current;
    flagsave = flags;
    SET(USE_REGEXP);

/* apparent near redundancy with regexp_pat[] here is needed, [][] works, [[]] doesn't */ 

    if (offset < (strlen(brackets) / 2)) {			/* on a left bracket */
	regexp_pat[1] = wanted_ch;
	regexp_pat[2] = ch_under_cursor;
	UNSET(REVERSE_SEARCH);
    } else {							/* on a right bracket */
	regexp_pat[1] = ch_under_cursor;
	regexp_pat[2] = wanted_ch;
	SET(REVERSE_SEARCH);
    }

    regexp_init(regexp_pat);

    while (1) {
	search_last_line = 0;
	if (findnextstr(1, 1, current, current_x, regexp_pat)) {
	    have_past_editbuff |= past_editbuff;
	    if (current->data[current_x] == ch_under_cursor)	/* found identical bracket  */
		count++;
	    else {						/* found complementary bracket */
		if (!(--count)) {
		    if (have_past_editbuff)
			edit_update(current, CENTER);
		    else
			update_line(current, current_x);
		    placewewant = xplustabs();
		    reset_cursor();
		    break ;
		}
	    }
	} else {						/* didn't find either left or right bracket */
	    statusbar(_("No matching bracket"));
	    current_x = current_x_save;
	    current = current_save;
	    break;
	}
    }

    if (ISSET(REGEXP_COMPILED))
	regexp_cleanup();
    flags = flagsave;
    return 0;
}
#endif