search.c 22.4 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
79
80
#ifndef NANO_SMALL
    int j;
#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
129
130
    /* This is now one simple call.  It just does a lot */
    i = statusq(0, replacing ? replace_list : whereis_list,
	replacing ? REPLACE_LIST_LEN : WHEREIS_LIST_LEN, backupstring,
131
	"%s%s%s%s%s%s", 
Chris Allegretta's avatar
Chris Allegretta committed
132
	_("Search"),
133
134
135
136
137
138
139
140
141
142
143
144
145

	/* 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
146
147
	replacing ? _(" (to replace)") : "",
	buf);
Chris Allegretta's avatar
Chris Allegretta committed
148
149
150
151
152

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

    case -2:	/* Same string */
160
#ifdef HAVE_REGEX_H
161
162
163
164
165
166
167
168
	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);
	}
169
#endif
170
171
	break;
    case 0:		/* They entered something new */
172
#ifdef HAVE_REGEX_H
173
174
	if (ISSET(USE_REGEXP))
	    regexp_init(answer);
175
#endif
176
177
	free(backupstring);
	backupstring = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
178
	last_replace[0] = '\0';
179
180
181
182
183
184
	break;
    case TOGGLE_CASE_KEY:
    case TOGGLE_BACKWARDS_KEY:
#ifdef HAVE_REGEX_H
    case TOGGLE_REGEXP_KEY:
#endif
185
186
187
188
	free(backupstring);
	backupstring = NULL;
	backupstring = mallocstrcpy(backupstring, answer);

189
#ifndef NANO_SMALL
190
191
192
	for (j = 0; j <= TOGGLE_LEN - 1; j++)
	    if (i == toggles[j].val)
		TOGGLE(toggles[j].flag);
193
#endif
Chris Allegretta's avatar
Chris Allegretta committed
194
195

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

    return 0;
}

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

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

	free(foo);
226
227
228
    }
}

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

243
244
245
int past_editbuff;	/* search is now looking through lines not displayed */

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

252
    fileptr = current;
Chris Allegretta's avatar
Chris Allegretta committed
253

254
255
    past_editbuff = 0;

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

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

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

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

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

Chris Allegretta's avatar
Chris Allegretta committed
280
	    fileptr = fileptr->next;
281

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

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

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

	    searchstr = fileptr->data;
	}
303

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

	current_x_find = current_x - 1;

320
	/* Are we now back to the place where the search started) */
Chris Allegretta's avatar
Chris Allegretta committed
321
	if ((fileptr == begin) && (current_x_find > beginx))
322
323
	    search_last_line = 1;

Chris Allegretta's avatar
Chris Allegretta committed
324
	/* Make sure we haven't passed the begining of the string */
325
#if 1	/* Is this required here ? */
Chris Allegretta's avatar
Chris Allegretta committed
326
327
328
329
	if (!(&fileptr->data[current_x_find] - fileptr->data))      
	    current_x_find++;
#endif
	rev_start = &fileptr->data[current_x_find];
330
331
	searchstr = fileptr->data;

Chris Allegretta's avatar
Chris Allegretta committed
332
333
334
335
336
337
338
339
340
341
342
343
	/* 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;

344
	    if (fileptr == edittop->prev)
345
		past_editbuff = 1;
346

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

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

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

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

390
391
392
	placewewant = xplustabs();
	reset_cursor();
    }
393

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

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

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

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

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

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

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

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

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

Chris Allegretta's avatar
Chris Allegretta committed
461
462
463
464
465
466
    return 1;
}

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

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

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

    new_size -= search_match_count;

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

    c = last_replace;
    while (*c) {
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
532
533
	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++;
	    }
	}
534
535
536
    }

    if (create_flag)
537
	*string = 0;
538
539
540

    return new_size;
}
541
#endif
542

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

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

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

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

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

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

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

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

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

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

627
	/* Sweet optimization by Rocco here */
628
	fileptr = findnextstr(replaceall, FALSE, begin, *beginx, prevanswer);
Chris Allegretta's avatar
Chris Allegretta committed
629
630

	/* No more matches.  Done! */
631
	if (!fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
632
633
	    break;

634
	/* Make sure only whole words are found */
635
636
	if ((wholewords) && (!is_whole_word(current_x, fileptr, prevanswer)))
	    continue;
Chris Allegretta's avatar
Chris Allegretta committed
637

Chris Allegretta's avatar
Chris Allegretta committed
638
	/* If we're here, we've found the search string */
639
640
641
642
643
	if (!replaceall) {

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

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

646
647
648
649
	    do_replace_highlight(FALSE, prevanswer);
	    curs_set(1);
	}

Chris Allegretta's avatar
Chris Allegretta committed
650
651
	if (*i > 0 || replaceall) {	/* Yes, replace it!!!! */
	    if (*i == 2)
Chris Allegretta's avatar
Chris Allegretta committed
652
653
		replaceall = 1;

654
655
	    copy = replace_line();
	    if (!copy) {
Jordi Mallach's avatar
   
Jordi Mallach committed
656
		statusbar(_("Replace failed: unknown subexpression!"));
657
		replace_abort();
Chris Allegretta's avatar
Chris Allegretta committed
658
		return 0;
659
	    }
Chris Allegretta's avatar
Chris Allegretta committed
660
661

	    /* Cleanup */
662
	    totsize -= strlen(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
663
664
	    free(current->data);
	    current->data = copy;
665
	    totsize += strlen(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
666
667

	    /* Stop bug where we replace a substring of the replacement text */
668
	    current_x += strlen(last_replace) - 1;
Chris Allegretta's avatar
Chris Allegretta committed
669

670
671
	    /* Adjust the original cursor position - COULD BE IMPROVED */
	    if (search_last_line) {
Chris Allegretta's avatar
Chris Allegretta committed
672
		*beginx += strlen(last_replace) - strlen(last_search);
673
674
675

		/* For strings that cross the search start/end boundary */
		/* Don't go outside of allocated memory */
Chris Allegretta's avatar
Chris Allegretta committed
676
677
		if (*beginx < 1)
		    *beginx = 1;
678
679
	    }

Chris Allegretta's avatar
Chris Allegretta committed
680
681
682
	    edit_refresh();
	    set_modified();
	    numreplaced++;
Chris Allegretta's avatar
Chris Allegretta committed
683
	} else if (*i == -1)	/* Abort, else do nothing and continue loop */
Chris Allegretta's avatar
Chris Allegretta committed
684
685
686
	    break;
    }

Chris Allegretta's avatar
Chris Allegretta committed
687
688
689
690
691
692
693
694
695
696
    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
697
698
699
700
701
702
    if (ISSET(VIEW_MODE)) {
	print_view_warning();
	replace_abort();
	return 0;
    }

Chris Allegretta's avatar
Chris Allegretta committed
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
    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;
    }

720
    /* Again, there was a previous string, but they deleted it and hit enter */
721
    if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Chris Allegretta's avatar
Chris Allegretta committed
722
723
724
725
726
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
    }

727
     /* If answer is now == "", then PICO_MODE is set.  So, copy
728
729
730
731
732
733
734
735
	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
736

737
    if (ISSET(PICO_MODE)) {
738
	buf = charalloc(strlen(last_replace) + 5);
Chris Allegretta's avatar
Chris Allegretta committed
739
740
741
742
743
744
745
	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);

746
	    i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta's avatar
Chris Allegretta committed
747
748
749
			_("Replace with [%s]"), buf);
	}
	else
750
	    i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta's avatar
Chris Allegretta committed
751
752
753
			_("Replace with"));
    }
    else
754
	i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, last_replace, 
Chris Allegretta's avatar
Chris Allegretta committed
755
756
757
758
759
760
761
762
763
764
765
			_("Replace with"));

    /* save where we are */
    begin = current;
    beginx = current_x + 1;

    search_last_line = 0;

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

    /* restore where we were */
Chris Allegretta's avatar
Chris Allegretta committed
766
    current = begin;
767
    current_x = beginx - 1;
Chris Allegretta's avatar
Chris Allegretta committed
768
    renumber_all();
769
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
770
771
772
773
774
775
776
777
778
779
780
    print_replaced(numreplaced);
    replace_abort();
    return 1;
}

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

781
int do_gotoline(int line, int save_pos)
Chris Allegretta's avatar
Chris Allegretta committed
782
{
783
    int i = 1;
784
785

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

787
	int j = 0;
Chris Allegretta's avatar
Chris Allegretta committed
788

789
	j = statusq(0, goto_list, GOTO_LIST_LEN, "", _("Enter line number"));
790
	if (j != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
791
792
793
794
	    statusbar(_("Aborted"));
	    goto_abort();
	    return 0;
	}
795
796
797
798
799
800

	line = atoi(answer);

	/* Bounds check */
	if (line <= 0) {
	    statusbar(_("Come on, be reasonable"));
Chris Allegretta's avatar
Chris Allegretta committed
801
	    goto_abort();
802
	    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
803
804
805
	}
    }

806
807
    for (current = fileage; ((current->next != NULL) && (i < line)); i++)
	current = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
808

809
    current_x = 0;
810
811
812
813
814
815
816

    /* 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
817
818
819
820
821
822
823

    goto_abort();
    return 1;
}

int do_gotoline_void(void)
{
824
    return do_gotoline(0, 0);
Chris Allegretta's avatar
Chris Allegretta committed
825
}
826

827
#if (defined ENABLE_MULTIBUFFER || !defined DISABLE_SPELLER)
828
void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant)
829
830
831
832
833
834
{

    /* 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);
835
836
837
838
839
840
841
842

    /* 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 */
843
844
845
846
847
    current_x = pos_x;
    placewewant = pos_placewewant;
    update_line(current, pos_x);
}
#endif
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
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

#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