search.c 21.6 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**************************************************************************
 *   search.c                                                             *
 *                                                                        *
 *   Copyright (C) 2000 Chris Allegretta                                  *
 *   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 *
 *   the Free Software Foundation; either version 1, or (at your option)  *
 *   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;
Chris Allegretta's avatar
Chris Allegretta committed
77
    char *prompt;
78
    static char *backupstring = NULL;
79
80
81
#ifndef NANO_SMALL
    int j;
#endif
82

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

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

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

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

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

Chris Allegretta's avatar
Chris Allegretta committed
129
130
131
132
     /* Instead of having a million if statements here to determine
	the prompt, we instead just have a hundred "? :" calls in
	the statusq call.  I hope no one ever has to modify this :-) */
    prompt = "%s%s%s%s%s%s";
133

134
135
136
    /* 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,
Chris Allegretta's avatar
Chris Allegretta committed
137
138
139
140
141
142
143
	prompt, 
	ISSET(CASE_SENSITIVE) ? _("Case Sensitive ") : "",
	ISSET(USE_REGEXP) ? _("Regexp ") : "",
	_("Search"),
	ISSET(REVERSE_SEARCH) ? _(" Backwards") : "",
	replacing ? _(" (to replace)") : "",
	buf);
Chris Allegretta's avatar
Chris Allegretta committed
144
145
146
147
148

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

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

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

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

    return 0;
}

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

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

	free(foo);
222
223
224
    }
}

225
226
227
int past_editbuff;	/* search is now looking through lines not displayed */

filestruct *findnextstr(int quiet, int bracket_mode, filestruct * begin, int beginx,
228
			char *needle)
Chris Allegretta's avatar
Chris Allegretta committed
229
230
{
    filestruct *fileptr;
Chris Allegretta's avatar
Chris Allegretta committed
231
    char *searchstr, *rev_start = NULL, *found = NULL;
232
    int current_x_find;
Chris Allegretta's avatar
Chris Allegretta committed
233

234
    fileptr = current;
Chris Allegretta's avatar
Chris Allegretta committed
235

236
237
    past_editbuff = 0;

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

Chris Allegretta's avatar
Chris Allegretta committed
240
	current_x_find = current_x + 1;
Chris Allegretta's avatar
Chris Allegretta committed
241

Chris Allegretta's avatar
Chris Allegretta committed
242
	/* Are we now back to the line where the search started) */
243
	if ((fileptr == begin) && (beginx > current_x_find))
Chris Allegretta's avatar
Chris Allegretta committed
244
	    search_last_line = 1;
Chris Allegretta's avatar
Chris Allegretta committed
245

Chris Allegretta's avatar
Chris Allegretta committed
246
247
248
	/* 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
249

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

Chris Allegretta's avatar
Chris Allegretta committed
252
253
	/* Look for needle in searchstr */
	while ((found = strstrwrapper(searchstr, needle, rev_start)) == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
254

Chris Allegretta's avatar
Chris Allegretta committed
255
256
257
258
259
260
	    /* finished processing file, get out */
	    if (search_last_line) {
		if (!quiet)
		    not_found_msg(needle);
	        return NULL;
	    }
261

Chris Allegretta's avatar
Chris Allegretta committed
262
	    fileptr = fileptr->next;
263

264
265
	    if (!past_editbuff && (fileptr == editbot))
		past_editbuff = 1;
Chris Allegretta's avatar
Chris Allegretta committed
266
267
268

	    /* EOF reached ?, wrap around once */
	    if (fileptr == NULL) {
269
270
		if (bracket_mode)		/* don't wrap if looking for bracket match */
		   return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
271
		fileptr = fileage;
272
		past_editbuff = 1;
Chris Allegretta's avatar
Chris Allegretta committed
273
		if (!quiet)
274
		   statusbar(_("Search Wrapped"));
Chris Allegretta's avatar
Chris Allegretta committed
275
276
277
278
279
280
281
282
	    }

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

	    searchstr = fileptr->data;
	}
283

Chris Allegretta's avatar
Chris Allegretta committed
284
285
	/* We found an instance */
	current_x_find = found - fileptr->data;
Chris Allegretta's avatar
Chris Allegretta committed
286

Chris Allegretta's avatar
Chris Allegretta committed
287
288
	/* Ensure we haven't wrapped around again! */
	if ((search_last_line) && (current_x_find >= beginx)) {
Chris Allegretta's avatar
Chris Allegretta committed
289
	    if (!quiet)
Chris Allegretta's avatar
Chris Allegretta committed
290
291
		not_found_msg(needle);
	    return NULL;
292
293
	}

Chris Allegretta's avatar
Chris Allegretta committed
294
295
296
297
298
299
    } else {	/* reverse search */

	current_x_find = current_x - 1;

	/* Are we now back to the line where the search started) */
	if ((fileptr == begin) && (current_x_find > beginx))
300
301
	    search_last_line = 1;

Chris Allegretta's avatar
Chris Allegretta committed
302
303
304
305
306
307
	/* Make sure we haven't passed the begining of the string */
#if 0	/* Is this required here ? */
	if (!(&fileptr->data[current_x_find] - fileptr->data))      
	    current_x_find++;
#endif
	rev_start = &fileptr->data[current_x_find];
308
309
	searchstr = fileptr->data;

Chris Allegretta's avatar
Chris Allegretta committed
310
311
312
313
314
315
316
317
318
319
320
321
	/* 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;

322
323
/* ? */	    if (!past_editbuff && (fileptr == edittop->prev))
		past_editbuff = 1;
324

Chris Allegretta's avatar
Chris Allegretta committed
325
326
	    /* SOF reached ?, wrap around once */
/* ? */	    if (fileptr == NULL) {
327
328
		if (bracket_mode)
		   return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
329
		fileptr = filebot;
330
		past_editbuff = 1;
Chris Allegretta's avatar
Chris Allegretta committed
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
		if (!quiet)
		    statusbar(_("Search Wrapped"));
	    }

	    /* 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;

	/* Ensure we haven't wrapped around again! */
	if ((search_last_line) && (current_x_find < beginx)) {
	    if (!quiet)
		not_found_msg(needle);
	    return NULL;
	}
Chris Allegretta's avatar
Chris Allegretta committed
352
353
    }

Chris Allegretta's avatar
Chris Allegretta committed
354
    /* Set globals now that we are sure we found something */
355
356
357
    current = fileptr;
    current_x = current_x_find;

358
359
360
361
362
    if (!bracket_mode) {
	if (past_editbuff)
	   edit_update(fileptr, CENTER);
	else
	   update_line(current, current_x);
363

364
365
366
	placewewant = xplustabs();
	reset_cursor();
    }
367

Chris Allegretta's avatar
Chris Allegretta committed
368
369
370
371
372
373
374
375
    return fileptr;
}

void search_abort(void)
{
    UNSET(KEEP_CUTBUFFER);
    display_main_list();
    wrefresh(bottomwin);
376
    if (ISSET(MARK_ISSET))
377
	edit_refresh_clearok();
378

379
#ifdef HAVE_REGEX_H
380
    if (ISSET(REGEXP_COMPILED))
381
	regexp_cleanup();
382
#endif
Chris Allegretta's avatar
Chris Allegretta committed
383
384
385
386
387
388
389
390
391
}

/* Search for a string */
int do_search(void)
{
    int i;
    filestruct *fileptr = current;

    wrap_reset();
392
393
394
    i = search_init(0);
    switch (i) {
    case -1:
Chris Allegretta's avatar
Chris Allegretta committed
395
396
397
	current = fileptr;
	search_abort();
	return 0;
398
    case -3:
Chris Allegretta's avatar
Chris Allegretta committed
399
400
	search_abort();
	return 0;
401
    case -2:
Chris Allegretta's avatar
Chris Allegretta committed
402
403
	do_replace();
	return 0;
404
    case 1:
Chris Allegretta's avatar
Chris Allegretta committed
405
406
407
408
	do_search();
	search_abort();
	return 1;
    }
409
410

    /* The sneaky user deleted the previous search string */
411
    if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Jordi Mallach's avatar
   
Jordi Mallach committed
412
	statusbar(_("Search Cancelled"));
413
414
415
416
	search_abort();
	return 0;
    }

417
     /* If answer is now == "", then PICO_MODE is set.  So, copy
418
419
420
421
422
423
424
	last_search into answer... */

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

425
    search_last_line = 0;
426
    findnextstr(FALSE, FALSE, current, current_x, answer);
Chris Allegretta's avatar
Chris Allegretta committed
427
428
429
430
431
432
433
    search_abort();
    return 1;
}

void print_replaced(int num)
{
    if (num > 1)
Jordi Mallach's avatar
   
Jordi Mallach committed
434
	statusbar(_("Replaced %d occurrences"), num);
Chris Allegretta's avatar
Chris Allegretta committed
435
    else if (num == 1)
Jordi Mallach's avatar
   
Jordi Mallach committed
436
	statusbar(_("Replaced 1 occurrence"));
Chris Allegretta's avatar
Chris Allegretta committed
437
438
439
440
}

void replace_abort(void)
{
441
    /* Identical to search_abort, so we'll call it here.  If it
442
       does something different later, we can change it back.  For now
443
       it's just a waste to duplicate code */
444
    search_abort();
445
    placewewant = xplustabs();
446
447
}

448
#ifdef HAVE_REGEX_H
449
450
451
452
453
454
455
456
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;
457
    int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
458
459
460
461
462
463
464
465

    new_size -= search_match_count;

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

    c = last_replace;
    while (*c) {
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
	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++;
	    }
	}
501
502
503
    }

    if (create_flag)
504
	*string = 0;
505
506
507

    return new_size;
}
508
#endif
509

Chris Allegretta's avatar
Chris Allegretta committed
510
char *replace_line(void)
511
512
513
514
515
516
{
    char *copy, *tmp;
    int new_line_size;
    int search_match_count;

    /* Calculate size of new line */
517
#ifdef HAVE_REGEX_H
518
    if (ISSET(USE_REGEXP)) {
519
520
521
	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
522
	 * text, return NULL, indicating an error */
523
524
	if (new_line_size < 0)
	    return NULL;
525
    } else {
526
527
528
#else
    {
#endif
529
530
531
	search_match_count = strlen(last_search);
	new_line_size = strlen(current->data) - strlen(last_search) +
	    strlen(last_replace) + 1;
532
    }
533

534
    /* Create buffer */
535
    copy = charalloc(new_line_size);
536
537
538
539
540
541
542

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

    /* Replacement Text */
    if (!ISSET(USE_REGEXP))
543
	strcat(copy, last_replace);
544
#ifdef HAVE_REGEX_H
545
    else
546
	(void) replace_regexp(copy + current_x, 1);
547
#endif
548
549
550

    /* The tail of the original line */
    /* This may expose other bugs, because it no longer
551
       goes through each character in the string
552
553
554
555
556
557
558
559
       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
560
561
}

562
/* step through each replace word and prompt user before replacing word */
Chris Allegretta's avatar
Chris Allegretta committed
563
564
int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx,
			int wholewords, int *i)
Chris Allegretta's avatar
Chris Allegretta committed
565
{
Chris Allegretta's avatar
Chris Allegretta committed
566
567
568
    int replaceall = 0, numreplaced = 0;
    filestruct *fileptr;
    char *copy;
Chris Allegretta's avatar
Chris Allegretta committed
569

Chris Allegretta's avatar
Chris Allegretta committed
570
    switch (*i) {
571
    case -1:				/* Aborted enter */
572
	if (strcmp(last_replace, ""))
573
	    answer = mallocstrcpy(answer, last_replace);
574
575
576
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
577
578
579
    case 0:		/* They actually entered something */
	break;
    default:
Chris Allegretta's avatar
Chris Allegretta committed
580
        if (*i != -2) {	/* First page, last page, for example 
581
				   could get here */
Chris Allegretta's avatar
Chris Allegretta committed
582
583
584
	    do_early_abort();
	    replace_abort();
	    return 0;
585
        }
Chris Allegretta's avatar
Chris Allegretta committed
586
587
    }

588
589
590
    if (ISSET(PICO_MODE) && !strcmp(answer, ""))
	answer = mallocstrcpy(answer, last_replace);

591
    last_replace = mallocstrcpy(last_replace, answer);
Chris Allegretta's avatar
Chris Allegretta committed
592
593
    while (1) {

594
	/* Sweet optimization by Rocco here */
595
	fileptr = findnextstr(replaceall, FALSE, begin, *beginx, prevanswer);
Chris Allegretta's avatar
Chris Allegretta committed
596
597

	/* No more matches.  Done! */
598
	if (!fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
599
600
	    break;

601
	/* Make sure only whole words are found */
Chris Allegretta's avatar
Chris Allegretta committed
602
603
604
	if (wholewords)
	{
	    /* start of line or previous character not a letter */
605
	    if ((current_x == 0) || (!isalpha((int) fileptr->data[current_x-1])))
Chris Allegretta's avatar
Chris Allegretta committed
606
607
608
	    {
		/* end of line or next character not a letter */
		if (((current_x + strlen(prevanswer)) == strlen(fileptr->data))
609
			|| (!isalpha((int) fileptr->data[current_x + strlen(prevanswer)])))
Chris Allegretta's avatar
Chris Allegretta committed
610
611
612
613
614
615
616
617
		    ;
		else
		    continue;
	    }
	    else
		continue;
	}

Chris Allegretta's avatar
Chris Allegretta committed
618
	/* If we're here, we've found the search string */
619
620
621
622
623
	if (!replaceall) {

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

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

626
627
628
629
	    do_replace_highlight(FALSE, prevanswer);
	    curs_set(1);
	}

Chris Allegretta's avatar
Chris Allegretta committed
630
631
	if (*i > 0 || replaceall) {	/* Yes, replace it!!!! */
	    if (*i == 2)
Chris Allegretta's avatar
Chris Allegretta committed
632
633
		replaceall = 1;

634
635
	    copy = replace_line();
	    if (!copy) {
Jordi Mallach's avatar
   
Jordi Mallach committed
636
		statusbar(_("Replace failed: unknown subexpression!"));
637
		replace_abort();
Chris Allegretta's avatar
Chris Allegretta committed
638
		return 0;
639
	    }
Chris Allegretta's avatar
Chris Allegretta committed
640
641
642
643
644
645

	    /* Cleanup */
	    free(current->data);
	    current->data = copy;

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

648
649
	    /* Adjust the original cursor position - COULD BE IMPROVED */
	    if (search_last_line) {
Chris Allegretta's avatar
Chris Allegretta committed
650
		*beginx += strlen(last_replace) - strlen(last_search);
651
652
653

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

Chris Allegretta's avatar
Chris Allegretta committed
658
659
660
	    edit_refresh();
	    set_modified();
	    numreplaced++;
Chris Allegretta's avatar
Chris Allegretta committed
661
	} else if (*i == -1)	/* Abort, else do nothing and continue loop */
Chris Allegretta's avatar
Chris Allegretta committed
662
663
664
	    break;
    }

Chris Allegretta's avatar
Chris Allegretta committed
665
666
667
668
669
670
671
672
673
674
    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
675
676
677
678
679
680
    if (ISSET(VIEW_MODE)) {
	print_view_warning();
	replace_abort();
	return 0;
    }

Chris Allegretta's avatar
Chris Allegretta committed
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
    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;
    }

698
    /* Again, there was a previous string, but they deleted it and hit enter */
699
    if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Chris Allegretta's avatar
Chris Allegretta committed
700
701
702
703
704
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
    }

705
     /* If answer is now == "", then PICO_MODE is set.  So, copy
706
707
708
709
710
711
712
713
	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
714

715
    if (ISSET(PICO_MODE)) {
716
	buf = charalloc(strlen(last_replace) + 5);
Chris Allegretta's avatar
Chris Allegretta committed
717
718
719
720
721
722
723
	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);

724
	    i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta's avatar
Chris Allegretta committed
725
726
727
			_("Replace with [%s]"), buf);
	}
	else
728
	    i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta's avatar
Chris Allegretta committed
729
730
731
			_("Replace with"));
    }
    else
732
	i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, last_replace, 
Chris Allegretta's avatar
Chris Allegretta committed
733
734
735
736
737
738
739
740
741
742
743
			_("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
744
    current = begin;
745
    current_x = beginx - 1;
Chris Allegretta's avatar
Chris Allegretta committed
746
    renumber_all();
747
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
748
749
750
751
752
753
754
755
756
757
758
    print_replaced(numreplaced);
    replace_abort();
    return 1;
}

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

759
int do_gotoline(long line, int save_pos)
Chris Allegretta's avatar
Chris Allegretta committed
760
{
761
762
763
    long i = 1;

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

765
	long j = 0;
Chris Allegretta's avatar
Chris Allegretta committed
766

767
	j = statusq(0, goto_list, GOTO_LIST_LEN, "", _("Enter line number"));
768
	if (j != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
769
770
771
772
	    statusbar(_("Aborted"));
	    goto_abort();
	    return 0;
	}
773
774
775
776
777
778

	line = atoi(answer);

	/* Bounds check */
	if (line <= 0) {
	    statusbar(_("Come on, be reasonable"));
Chris Allegretta's avatar
Chris Allegretta committed
779
	    goto_abort();
780
	    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
781
782
783
	}
    }

784
785
    for (current = fileage; ((current->next != NULL) && (i < line)); i++)
	current = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
786

787
    current_x = 0;
788
789
790
791
792
793
794

    /* 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
795
796
797
798
799
800
801

    goto_abort();
    return 1;
}

int do_gotoline_void(void)
{
802
    return do_gotoline(0, 0);
Chris Allegretta's avatar
Chris Allegretta committed
803
}
804
805
806
807
808
809
810
811
812
813
814
815
816
817

#ifdef ENABLE_MULTIBUFFER
void do_gotopos(long line, int pos_x, int pos_y, int pos_placewewant)
{

    /* 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);
    current_x = pos_x;
    placewewant = pos_placewewant;
    update_line(current, pos_x);
}
#endif
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
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

#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