search.c 22.9 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

Chris Allegretta's avatar
Chris Allegretta committed
32
33
34
35
36
37
38
#ifndef NANO_SMALL
#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
60
61
	last_search[0] = 0;
    }
    if (last_replace == NULL) {
62
	last_replace = charalloc(1);
Chris Allegretta's avatar
Chris Allegretta committed
63
64
65
66
	last_replace[0] = 0;
    }
}

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
#ifndef NANO_SMALL
79
    toggle *t;
80
#endif
81

Chris Allegretta's avatar
Chris Allegretta committed
82
    search_init_globals();
83

84
    buf = charalloc(strlen(last_search) + 5);
Chris Allegretta's avatar
Chris Allegretta committed
85
    buf[0] = 0;
86

Chris Allegretta's avatar
Chris Allegretta committed
87
88
89
90
91
92

    /* Clear the backupstring if we've changed from Pico mode to regular
	mode */
    if (ISSET(CLEAR_BACKUPSTRING)) {
	free(backupstring);
	backupstring = NULL;
93
	UNSET(CLEAR_BACKUPSTRING);
Chris Allegretta's avatar
Chris Allegretta committed
94
95
    }
	
96
97
     /* Okay, fun time.  backupstring is our holder for what is being 
	returned from the statusq call.  Using answer for this would be tricky.
98
	Here, if we're using PICO_MODE, we only want nano to put the
99
100
101
102
103
	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.  */

104
    if (ISSET(PICO_MODE)) {
105
106
107
108
109
110
	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
111
    /* If using Pico messages, we do things the old fashioned way... */
112
    if (ISSET(PICO_MODE)) {
113
114
115
116
117
118
119
120
121
122
123
	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
124
    }
125
126
    else
	strcpy(buf, "");
Chris Allegretta's avatar
Chris Allegretta committed
127

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

	/* 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
145
146
	replacing ? _(" (to replace)") : "",
	buf);
Chris Allegretta's avatar
Chris Allegretta committed
147
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
180
181
182
183
	break;
    case TOGGLE_CASE_KEY:
    case TOGGLE_BACKWARDS_KEY:
#ifdef HAVE_REGEX_H
    case TOGGLE_REGEXP_KEY:
#endif
184
185
186
187
	free(backupstring);
	backupstring = NULL;
	backupstring = mallocstrcpy(backupstring, answer);

188
#ifndef NANO_SMALL
189
190
191
	for (t = toggles; t != NULL; t = t->next)
	    if (i == t->val)
		TOGGLE(t->flag);
192
#endif
Chris Allegretta's avatar
Chris Allegretta committed
193
194

	return 1;
195
    case NANO_OTHERSEARCH_KEY:
196
	backupstring = mallocstrcpy(backupstring, answer);
Chris Allegretta's avatar
Chris Allegretta committed
197
	return -2;		/* Call the opposite search function */
198
    case NANO_FROMSEARCHTOGOTO_KEY:
199
200
	free(backupstring);
	backupstring = NULL;
201
202
	do_gotoline_void();
	return -3;
203
    default:
Chris Allegretta's avatar
Chris Allegretta committed
204
	do_early_abort();
205
206
	free(backupstring);
	backupstring = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
207
208
209
210
211
212
	return -3;
    }

    return 0;
}

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

	foo = mallocstrcpy(foo, str);
221
222
	foo[COLS / 2] = 0;
	statusbar(_("\"%s...\" not found"), foo);
223
224

	free(foo);
225
226
227
    }
}

228
229
230
231
232
233
234
235
236
237
238
239
240
241
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;
}

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

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

252
253
    past_editbuff = 0;

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

Chris Allegretta's avatar
Chris Allegretta committed
256
	current_x_find = current_x + 1;
257
258
#if 0
	/* Are we now back to the place where the search started) */
259
	if ((fileptr == begin) && (beginx > current_x_find))
Chris Allegretta's avatar
Chris Allegretta committed
260
	    search_last_line = 1;
261
#endif
Chris Allegretta's avatar
Chris Allegretta committed
262
263
264
	/* 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
265

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

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

Chris Allegretta's avatar
Chris Allegretta committed
271
272
273
274
275
276
	    /* finished processing file, get out */
	    if (search_last_line) {
		if (!quiet)
		    not_found_msg(needle);
	        return NULL;
	    }
277

Chris Allegretta's avatar
Chris Allegretta committed
278
	    fileptr = fileptr->next;
279

280
	    if (fileptr == editbot)
281
		past_editbuff = 1;
Chris Allegretta's avatar
Chris Allegretta committed
282
283
284

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

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

	    searchstr = fileptr->data;
	}
301

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

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

Chris Allegretta's avatar
Chris Allegretta committed
330
331
332
333
334
335
336
337
338
339
340
341
	/* Look for needle in searchstr */
	while ((found = strstrwrapper(searchstr, needle, rev_start)) == NULL) {

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

	    fileptr = fileptr->prev;

342
	    if (fileptr == edittop->prev)
343
		past_editbuff = 1;
344

Chris Allegretta's avatar
Chris Allegretta committed
345
346
	    /* SOF reached ?, wrap around once */
/* ? */	    if (fileptr == NULL) {
347
348
		if (bracket_mode)
		   return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
349
		fileptr = filebot;
350
		past_editbuff = 1;
351
		if (!quiet) {
Chris Allegretta's avatar
Chris Allegretta committed
352
		    statusbar(_("Search Wrapped"));
353
354
		    SET(DISABLE_CURPOS);
		}
Chris Allegretta's avatar
Chris Allegretta committed
355
356
357
358
359
360
361
362
363
364
365
366
	    }

	    /* 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;
367
#if 1
Chris Allegretta's avatar
Chris Allegretta committed
368
369
370
371
372
373
	/* Ensure we haven't wrapped around again! */
	if ((search_last_line) && (current_x_find < beginx)) {
	    if (!quiet)
		not_found_msg(needle);
	    return NULL;
	}
374
#endif
Chris Allegretta's avatar
Chris Allegretta committed
375
    }
376
#endif /* NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
377

Chris Allegretta's avatar
Chris Allegretta committed
378
    /* Set globals now that we are sure we found something */
379
380
381
    current = fileptr;
    current_x = current_x_find;

382
383
384
385
386
    if (!bracket_mode) {
	if (past_editbuff)
	   edit_update(fileptr, CENTER);
	else
	   update_line(current, current_x);
387

388
389
390
	placewewant = xplustabs();
	reset_cursor();
    }
391

Chris Allegretta's avatar
Chris Allegretta committed
392
393
394
395
396
397
398
399
    return fileptr;
}

void search_abort(void)
{
    UNSET(KEEP_CUTBUFFER);
    display_main_list();
    wrefresh(bottomwin);
400
    if (ISSET(MARK_ISSET))
401
	edit_refresh_clearok();
402

403
#ifdef HAVE_REGEX_H
404
    if (ISSET(REGEXP_COMPILED))
405
	regexp_cleanup();
406
#endif
Chris Allegretta's avatar
Chris Allegretta committed
407
408
409
410
411
412
}

/* Search for a string */
int do_search(void)
{
    int i;
413
414
    filestruct *fileptr = current, *didfind;
    int fileptr_x = current_x;
Chris Allegretta's avatar
Chris Allegretta committed
415
416

    wrap_reset();
417
418
419
    i = search_init(0);
    switch (i) {
    case -1:
Chris Allegretta's avatar
Chris Allegretta committed
420
421
422
	current = fileptr;
	search_abort();
	return 0;
423
    case -3:
Chris Allegretta's avatar
Chris Allegretta committed
424
425
	search_abort();
	return 0;
426
    case -2:
Chris Allegretta's avatar
Chris Allegretta committed
427
428
	do_replace();
	return 0;
429
    case 1:
Chris Allegretta's avatar
Chris Allegretta committed
430
431
432
433
	do_search();
	search_abort();
	return 1;
    }
434
435

    /* The sneaky user deleted the previous search string */
436
    if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Jordi Mallach's avatar
   
Jordi Mallach committed
437
	statusbar(_("Search Cancelled"));
438
439
440
441
	search_abort();
	return 0;
    }

442
     /* If answer is now == "", then PICO_MODE is set.  So, copy
443
444
445
446
447
448
449
	last_search into answer... */

    if (!strcmp(answer, ""))
	answer = mallocstrcpy(answer, last_search);
    else
	last_search = mallocstrcpy(last_search, answer);

450
    search_last_line = 0;
451
452
453
454
455
456
    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
457
    search_abort();
458

Chris Allegretta's avatar
Chris Allegretta committed
459
460
461
462
463
464
    return 1;
}

void print_replaced(int num)
{
    if (num > 1)
Jordi Mallach's avatar
   
Jordi Mallach committed
465
	statusbar(_("Replaced %d occurrences"), num);
Chris Allegretta's avatar
Chris Allegretta committed
466
    else if (num == 1)
Jordi Mallach's avatar
   
Jordi Mallach committed
467
	statusbar(_("Replaced 1 occurrence"));
Chris Allegretta's avatar
Chris Allegretta committed
468
469
470
471
}

void replace_abort(void)
{
472
    /* Identical to search_abort, so we'll call it here.  If it
473
       does something different later, we can change it back.  For now
474
       it's just a waste to duplicate code */
475
    search_abort();
476
    placewewant = xplustabs();
477
478
}

479
#ifdef HAVE_REGEX_H
480
481
482
483
484
485
486
487
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;
488
    int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
489
490
491
492
493
494
495
496

    new_size -= search_match_count;

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

    c = last_replace;
    while (*c) {
497
498
499
500
501
502
503
504
505
506
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
	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++;
	    }
	}
532
533
534
    }

    if (create_flag)
535
	*string = 0;
536
537
538

    return new_size;
}
539
#endif
540

Chris Allegretta's avatar
Chris Allegretta committed
541
char *replace_line(void)
542
543
544
545
546
547
{
    char *copy, *tmp;
    int new_line_size;
    int search_match_count;

    /* Calculate size of new line */
548
#ifdef HAVE_REGEX_H
549
    if (ISSET(USE_REGEXP)) {
550
551
552
	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
553
	 * text, return NULL, indicating an error */
554
555
	if (new_line_size < 0)
	    return NULL;
556
    } else {
557
558
559
#else
    {
#endif
560
561
562
	search_match_count = strlen(last_search);
	new_line_size = strlen(current->data) - strlen(last_search) +
	    strlen(last_replace) + 1;
563
    }
564

565
    /* Create buffer */
566
    copy = charalloc(new_line_size);
567
568
569
570
571
572
573

    /* Head of Original Line */
    strncpy(copy, current->data, current_x);
    copy[current_x] = 0;

    /* Replacement Text */
    if (!ISSET(USE_REGEXP))
574
	strcat(copy, last_replace);
575
#ifdef HAVE_REGEX_H
576
    else
577
	(void) replace_regexp(copy + current_x, 1);
578
#endif
579
580
581

    /* The tail of the original line */
    /* This may expose other bugs, because it no longer
582
       goes through each character in the string
583
584
585
586
587
588
589
590
       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
591
592
}

593
/* step through each replace word and prompt user before replacing word */
Chris Allegretta's avatar
Chris Allegretta committed
594
595
int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx,
			int wholewords, int *i)
Chris Allegretta's avatar
Chris Allegretta committed
596
{
Chris Allegretta's avatar
Chris Allegretta committed
597
    int replaceall = 0, numreplaced = 0;
598
599

    filestruct *fileptr = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
600
    char *copy;
Chris Allegretta's avatar
Chris Allegretta committed
601

Chris Allegretta's avatar
Chris Allegretta committed
602
    switch (*i) {
603
    case -1:				/* Aborted enter */
604
	if (strcmp(last_replace, ""))
605
	    answer = mallocstrcpy(answer, last_replace);
606
607
608
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
609
610
611
    case 0:		/* They actually entered something */
	break;
    default:
Chris Allegretta's avatar
Chris Allegretta committed
612
        if (*i != -2) {	/* First page, last page, for example 
613
				   could get here */
Chris Allegretta's avatar
Chris Allegretta committed
614
615
616
	    do_early_abort();
	    replace_abort();
	    return 0;
617
        }
Chris Allegretta's avatar
Chris Allegretta committed
618
619
    }

620
621
622
    if (ISSET(PICO_MODE) && !strcmp(answer, ""))
	answer = mallocstrcpy(answer, last_replace);

623
    last_replace = mallocstrcpy(last_replace, answer);
Chris Allegretta's avatar
Chris Allegretta committed
624
625
    while (1) {

626
	/* Sweet optimization by Rocco here */
627
#if 0
628
	fileptr = findnextstr(replaceall, FALSE, begin, *beginx, prevanswer);
629
630
631
632
633
634
#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
635
636

	/* No more matches.  Done! */
637
	if (!fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
638
639
	    break;

640
	/* Make sure only whole words are found */
641
642
	if ((wholewords) && (!is_whole_word(current_x, fileptr, prevanswer)))
	    continue;
Chris Allegretta's avatar
Chris Allegretta committed
643

Chris Allegretta's avatar
Chris Allegretta committed
644
	/* If we're here, we've found the search string */
645
646
647
648
649
	if (!replaceall) {

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

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

652
653
654
655
	    do_replace_highlight(FALSE, prevanswer);
	    curs_set(1);
	}

Chris Allegretta's avatar
Chris Allegretta committed
656
657
	if (*i > 0 || replaceall) {	/* Yes, replace it!!!! */
	    if (*i == 2)
Chris Allegretta's avatar
Chris Allegretta committed
658
659
		replaceall = 1;

660
661
	    copy = replace_line();
	    if (!copy) {
Jordi Mallach's avatar
   
Jordi Mallach committed
662
		statusbar(_("Replace failed: unknown subexpression!"));
663
		replace_abort();
Chris Allegretta's avatar
Chris Allegretta committed
664
		return 0;
665
	    }
Chris Allegretta's avatar
Chris Allegretta committed
666
667

	    /* Cleanup */
668
	    totsize -= strlen(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
669
670
	    free(current->data);
	    current->data = copy;
671
	    totsize += strlen(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
672

673
674
675
	    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
676

677
678
679
		/* Adjust the original cursor position - COULD BE IMPROVED */
		if (search_last_line) {
		    *beginx += strlen(last_replace) - strlen(last_search);
680

681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
		    /* 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);
		}
696
697
	    }

Chris Allegretta's avatar
Chris Allegretta committed
698
699
700
	    edit_refresh();
	    set_modified();
	    numreplaced++;
Chris Allegretta's avatar
Chris Allegretta committed
701
	} else if (*i == -1)	/* Abort, else do nothing and continue loop */
Chris Allegretta's avatar
Chris Allegretta committed
702
703
704
	    break;
    }

Chris Allegretta's avatar
Chris Allegretta committed
705
706
707
708
709
710
711
712
713
714
    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
715
716
717
718
719
720
    if (ISSET(VIEW_MODE)) {
	print_view_warning();
	replace_abort();
	return 0;
    }

Chris Allegretta's avatar
Chris Allegretta committed
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
    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;
    }

738
    /* Again, there was a previous string, but they deleted it and hit enter */
739
    if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Chris Allegretta's avatar
Chris Allegretta committed
740
741
742
743
744
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
    }

745
     /* If answer is now == "", then PICO_MODE is set.  So, copy
746
747
748
749
750
751
752
753
	last_search into answer (and prevanswer)... */
    if (!strcmp(answer, "")) {
	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
754

755
    if (ISSET(PICO_MODE)) {
756
	buf = charalloc(strlen(last_replace) + 5);
Chris Allegretta's avatar
Chris Allegretta committed
757
758
759
760
761
762
763
	if (strcmp(last_replace, "")) {
	    if (strlen(last_replace) > (COLS / 3)) {
		strncpy(buf, last_replace, COLS / 3);
		sprintf(&buf[COLS / 3 - 1], "...");
	    } else
		sprintf(buf, "%s", last_replace);

764
	    i = statusq(0, replace_list_2, "",
Chris Allegretta's avatar
Chris Allegretta committed
765
766
767
			_("Replace with [%s]"), buf);
	}
	else
768
	    i = statusq(0, replace_list_2, "",
Chris Allegretta's avatar
Chris Allegretta committed
769
770
771
			_("Replace with"));
    }
    else
772
	i = statusq(0, replace_list_2, last_replace, 
Chris Allegretta's avatar
Chris Allegretta committed
773
774
775
776
			_("Replace with"));

    /* save where we are */
    begin = current;
777
778
#if 0
    /* why + 1  ? isn't this taken care of in findnextstr() ? */
Chris Allegretta's avatar
Chris Allegretta committed
779
    beginx = current_x + 1;
780
781
782
#else
    beginx = current_x;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
783
784
785
786
787
    search_last_line = 0;

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

    /* restore where we were */
Chris Allegretta's avatar
Chris Allegretta committed
788
    current = begin;
789
#if 0
790
    current_x = beginx - 1;
791
792
793
#else
    current_x = beginx;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
794
    renumber_all();
795
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
796
797
798
799
800
801
802
803
804
805
806
    print_replaced(numreplaced);
    replace_abort();
    return 1;
}

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

807
int do_gotoline(int line, int save_pos)
Chris Allegretta's avatar
Chris Allegretta committed
808
{
809
    int i = 1;
810
811

    if (line <= 0) {		/* Ask for it */
Chris Allegretta's avatar
Chris Allegretta committed
812

813
	int j = 0;
Chris Allegretta's avatar
Chris Allegretta committed
814

815
	j = statusq(0, goto_list, "", _("Enter line number"));
816
	if (j != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
817
818
819
820
	    statusbar(_("Aborted"));
	    goto_abort();
	    return 0;
	}
821
822
823
824
825
826

	line = atoi(answer);

	/* Bounds check */
	if (line <= 0) {
	    statusbar(_("Come on, be reasonable"));
Chris Allegretta's avatar
Chris Allegretta committed
827
	    goto_abort();
828
	    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
829
830
831
	}
    }

832
833
    for (current = fileage; ((current->next != NULL) && (i < line)); i++)
	current = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
834

835
    current_x = 0;
836
837
838
839
840
841
842

    /* 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
843
844
845
846
847
848
849

    goto_abort();
    return 1;
}

int do_gotoline_void(void)
{
850
    return do_gotoline(0, 0);
Chris Allegretta's avatar
Chris Allegretta committed
851
}
852

853
#if (defined ENABLE_MULTIBUFFER || !defined DISABLE_SPELLER)
854
void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant)
855
856
857
858
859
860
{

    /* 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);
861
862
863
864
865
866
867
868

    /* recalculate the x-coordinate and place we want, just in case their
       values are insane; if they aren't, they won't be changed by this */
    current_x = pos_x;
    pos_placewewant = xplustabs();
    pos_x = actual_x(current, pos_placewewant);

    /* set the rest of the coordinates up */
869
870
871
872
873
    current_x = pos_x;
    placewewant = pos_placewewant;
    update_line(current, pos_x);
}
#endif
874
875
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

#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