search.c 17.1 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
58
59
60
61
62
63
64
65
66
void search_init_globals(void)
{
    if (last_search == NULL) {
	last_search = nmalloc(1);
	last_search[0] = 0;
    }
    if (last_replace == NULL) {
	last_replace = nmalloc(1);
	last_replace[0] = 0;
    }
}

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

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

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

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

85
86
     /* Okay, fun time.  backupstring is our holder for what is being 
	returned from the statusq call.  Using answer for this would be tricky.
87
	Here, if we're using PICO_MODE, we only want nano to put the
88
89
90
91
92
	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.  */

93
    if (ISSET(PICO_MODE)) {
94
95
96
97
98
99
	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
100
    /* If using Pico messages, we do things the old fashioned way... */
101
    if (ISSET(PICO_MODE)) {
102
103
104
105
106
107
108
109
110
111
112
	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
113
    }
114
115
    else
	strcpy(buf, "");
Chris Allegretta's avatar
Chris Allegretta committed
116

117
    if (ISSET(USE_REGEXP) && ISSET(CASE_SENSITIVE))
118
	prompt = _("Case Sensitive Regexp Search%s%s");
119
    else if (ISSET(USE_REGEXP))
120
121
122
	prompt = _("Regexp Search%s%s");
    else if (ISSET(CASE_SENSITIVE))
	prompt = _("Case Sensitive Search%s%s");
123
    else
124
	prompt = _("Search%s%s");
125
126
127

    if (replacing)
	reprompt = _(" (to replace)");
128

129
130
131
    /* 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,
132
	prompt, reprompt, buf);
Chris Allegretta's avatar
Chris Allegretta committed
133
134
135
136
137

    /* Cancel any search, or just return with no previous search */
    if ((i == -1) || (i < 0 && !last_search[0])) {
	statusbar(_("Search Cancelled"));
	reset_cursor();
138
139
	free(backupstring);
	backupstring = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
140
141
	return -1;
    } else if (i == -2) {	/* Same string */
142
#ifdef HAVE_REGEX_H
143
144
145
146
147
148
149
150
	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);
	}
151
152
#else
	;
153
#endif
Chris Allegretta's avatar
Chris Allegretta committed
154
    } else if (i == 0) {	/* They entered something new */
155
#ifdef HAVE_REGEX_H
156
157
	if (ISSET(USE_REGEXP))
	    regexp_init(answer);
158
#endif
159
160
	free(backupstring);
	backupstring = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
161
162
	last_replace[0] = '\0';
    } else if (i == NANO_CASE_KEY) {	/* They want it case sensitive */
163
164
165
166
	free(backupstring);
	backupstring = NULL;
	backupstring = mallocstrcpy(backupstring, answer);

Chris Allegretta's avatar
Chris Allegretta committed
167
168
169
170
171
172
173
	if (ISSET(CASE_SENSITIVE))
	    UNSET(CASE_SENSITIVE);
	else
	    SET(CASE_SENSITIVE);

	return 1;
    } else if (i == NANO_OTHERSEARCH_KEY) {
174
	backupstring = mallocstrcpy(backupstring, answer);
Chris Allegretta's avatar
Chris Allegretta committed
175
	return -2;		/* Call the opposite search function */
176
    } else if (i == NANO_FROMSEARCHTOGOTO_KEY) {
177
178
	free(backupstring);
	backupstring = NULL;
179
180
	do_gotoline_void();
	return -3;
181
    } else {			/* First line key, etc. */
Chris Allegretta's avatar
Chris Allegretta committed
182
	do_early_abort();
183
184
	free(backupstring);
	backupstring = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
185
186
187
188
189
190
	return -3;
    }

    return 0;
}

191
192
void not_found_msg(char *str)
{
193
    if (strlen(str) <= COLS / 2)
194
195
	statusbar(_("\"%s\" not found"), str);
    else {
196
197
198
	char *foo = NULL;

	foo = mallocstrcpy(foo, str);
199
200
	foo[COLS / 2] = 0;
	statusbar(_("\"%s...\" not found"), foo);
201
202

	free(foo);
203
204
205
    }
}

206
207
filestruct *findnextstr(int quiet, filestruct * begin, int beginx,
			char *needle)
Chris Allegretta's avatar
Chris Allegretta committed
208
209
210
{
    filestruct *fileptr;
    char *searchstr, *found = NULL, *tmp;
211
    int past_editbot = 0, current_x_find;
Chris Allegretta's avatar
Chris Allegretta committed
212

213
    fileptr = current;
Chris Allegretta's avatar
Chris Allegretta committed
214

215
    current_x_find = current_x + 1;
Chris Allegretta's avatar
Chris Allegretta committed
216

217
    /* Are we searching the last line? (i.e. the line where search started) */
218
    if ((fileptr == begin) && (current_x_find < beginx))
219
	search_last_line = 1;
Chris Allegretta's avatar
Chris Allegretta committed
220

221
    /* Make sure we haven't passed the end of the string */
222
223
    if (strlen(fileptr->data) < current_x_find)
	current_x_find--;
Chris Allegretta's avatar
Chris Allegretta committed
224

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

227
    /* Look for needle in searchstr */
228
    while ((found = strstrwrapper(searchstr, needle)) == NULL) {
Chris Allegretta's avatar
Chris Allegretta committed
229

230
	/* finished processing file, get out */
231
	if (search_last_line) {
232
	    if (!quiet)
233
		not_found_msg(needle);
Chris Allegretta's avatar
Chris Allegretta committed
234
235
236
	    return NULL;
	}

237
238
239
240
241
242
243
244
245
246
	fileptr = fileptr->next;

	if (!past_editbot && (fileptr == editbot))
	    past_editbot = 1;

	/* EOF reached, wrap around once */
	if (fileptr == NULL) {
	    fileptr = fileage;

	    past_editbot = 1;
Chris Allegretta's avatar
Chris Allegretta committed
247
248
249

	    if (!quiet)
		statusbar(_("Search Wrapped"));
250
251
252
253
254
255
256
257
258
259
	}

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

	searchstr = fileptr->data;
    }

    /* We found an instance */
260
    current_x_find = 0;
261
    for (tmp = fileptr->data; tmp != found; tmp++)
262
	current_x_find++;
263
264

    /* Ensure we haven't wrap around again! */
265
    if ((search_last_line) && (current_x_find >= beginx)) {
266
	if (!quiet)
267
	    not_found_msg(needle);
268
	return NULL;
Chris Allegretta's avatar
Chris Allegretta committed
269
270
    }

271
272
273
274
    /* Set globals now that we are sure we found something */
    current = fileptr;
    current_x = current_x_find;

275
276
277
278
279
280
281
282
    if (past_editbot)
	edit_update(fileptr, CENTER);
    else
	update_line(current, current_x);

    placewewant = xplustabs();
    reset_cursor();

Chris Allegretta's avatar
Chris Allegretta committed
283
284
285
286
287
288
289
290
    return fileptr;
}

void search_abort(void)
{
    UNSET(KEEP_CUTBUFFER);
    display_main_list();
    wrefresh(bottomwin);
291
    if (ISSET(MARK_ISSET))
292
	edit_refresh_clearok();
293

294
#ifdef HAVE_REGEX_H
295
    if (ISSET(REGEXP_COMPILED))
296
	regexp_cleanup();
297
#endif
Chris Allegretta's avatar
Chris Allegretta committed
298
299
300
301
302
303
304
305
306
}

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

    wrap_reset();
307
308
309
    i = search_init(0);
    switch (i) {
    case -1:
Chris Allegretta's avatar
Chris Allegretta committed
310
311
312
	current = fileptr;
	search_abort();
	return 0;
313
    case -3:
Chris Allegretta's avatar
Chris Allegretta committed
314
315
	search_abort();
	return 0;
316
    case -2:
Chris Allegretta's avatar
Chris Allegretta committed
317
318
	do_replace();
	return 0;
319
    case 1:
Chris Allegretta's avatar
Chris Allegretta committed
320
321
322
323
	do_search();
	search_abort();
	return 1;
    }
324
325

    /* The sneaky user deleted the previous search string */
326
    if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Jordi Mallach's avatar
   
Jordi Mallach committed
327
	statusbar(_("Search Cancelled"));
328
329
330
331
	search_abort();
	return 0;
    }

332
     /* If answer is now == "", then PICO_MODE is set.  So, copy
333
334
335
336
337
338
339
	last_search into answer... */

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

340
341
    search_last_line = 0;
    findnextstr(0, current, current_x, answer);
Chris Allegretta's avatar
Chris Allegretta committed
342
343
344
345
346
347
348
    search_abort();
    return 1;
}

void print_replaced(int num)
{
    if (num > 1)
Jordi Mallach's avatar
   
Jordi Mallach committed
349
	statusbar(_("Replaced %d occurrences"), num);
Chris Allegretta's avatar
Chris Allegretta committed
350
    else if (num == 1)
Jordi Mallach's avatar
   
Jordi Mallach committed
351
	statusbar(_("Replaced 1 occurrence"));
Chris Allegretta's avatar
Chris Allegretta committed
352
353
354
355
}

void replace_abort(void)
{
356
357
358
359
    /* Identicle to search_abort, so we'll call it here.  If it
       does something different later, we can change it back.  For now
       it's just a waste to duplicat code */
    search_abort();
360
    placewewant = xplustabs();
361
362
}

363
#ifdef HAVE_REGEX_H
364
365
366
367
368
369
370
371
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;
372
    int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
373
374
375
376
377
378
379
380

    new_size -= search_match_count;

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

    c = last_replace;
    while (*c) {
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
	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++;
	    }
	}
416
417
418
    }

    if (create_flag)
419
	*string = 0;
420
421
422

    return new_size;
}
423
#endif
424

Chris Allegretta's avatar
Chris Allegretta committed
425
char *replace_line(void)
426
427
428
429
430
431
{
    char *copy, *tmp;
    int new_line_size;
    int search_match_count;

    /* Calculate size of new line */
432
#ifdef HAVE_REGEX_H
433
    if (ISSET(USE_REGEXP)) {
434
435
436
437
438
439
	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
	 * text, return NULL indicating an error */
	if (new_line_size < 0)
	    return NULL;
440
    } else {
441
442
443
#else
    {
#endif
444
445
446
	search_match_count = strlen(last_search);
	new_line_size = strlen(current->data) - strlen(last_search) +
	    strlen(last_replace) + 1;
447
    }
448

449
450
451
452
453
454
455
456
457
    /* Create buffer */
    copy = nmalloc(new_line_size);

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

    /* Replacement Text */
    if (!ISSET(USE_REGEXP))
458
	strcat(copy, last_replace);
459
#ifdef HAVE_REGEX_H
460
    else
461
	(void) replace_regexp(copy + current_x, 1);
462
#endif
463
464
465
466
467
468
469
470
471
472
473
474

    /* The tail of the original line */
    /* This may expose other bugs, because it no longer
       goes through each character on the string
       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
475
476
}

477
/* step through each replace word and prompt user before replacing word */
Chris Allegretta's avatar
Chris Allegretta committed
478
479
int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx,
			int wholewords, int *i)
Chris Allegretta's avatar
Chris Allegretta committed
480
{
Chris Allegretta's avatar
Chris Allegretta committed
481
482
483
    int replaceall = 0, numreplaced = 0;
    filestruct *fileptr;
    char *copy;
Chris Allegretta's avatar
Chris Allegretta committed
484

Chris Allegretta's avatar
Chris Allegretta committed
485
    switch (*i) {
486
    case -1:				/* Aborted enter */
487
	if (strcmp(last_replace, ""))
488
	    answer = mallocstrcpy(answer, last_replace);
489
490
491
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
492
493
494
    case 0:		/* They actually entered something */
	break;
    default:
Chris Allegretta's avatar
Chris Allegretta committed
495
        if (*i != -2) {	/* First page, last page, for example 
496
				   could get here */
Chris Allegretta's avatar
Chris Allegretta committed
497
498
499
	    do_early_abort();
	    replace_abort();
	    return 0;
500
        }
Chris Allegretta's avatar
Chris Allegretta committed
501
502
    }

503
504
505
    if (ISSET(PICO_MODE) && !strcmp(answer, ""))
	answer = mallocstrcpy(answer, last_replace);

506
    last_replace = mallocstrcpy(last_replace, answer);
Chris Allegretta's avatar
Chris Allegretta committed
507
508
    while (1) {

509
	/* Sweet optimization by Rocco here */
Chris Allegretta's avatar
Chris Allegretta committed
510
	fileptr = findnextstr(replaceall, begin, *beginx, prevanswer);
Chris Allegretta's avatar
Chris Allegretta committed
511
512

	/* No more matches.  Done! */
513
	if (!fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
514
515
	    break;

Chris Allegretta's avatar
Chris Allegretta committed
516
517
518
519
	/* Make sure only wholewords are found */
	if (wholewords)
	{
	    /* start of line or previous character not a letter */
520
	    if ((current_x == 0) || (!isalpha((int) fileptr->data[current_x-1])))
Chris Allegretta's avatar
Chris Allegretta committed
521
522
523
	    {
		/* end of line or next character not a letter */
		if (((current_x + strlen(prevanswer)) == strlen(fileptr->data))
524
			|| (!isalpha((int) fileptr->data[current_x + strlen(prevanswer)])))
Chris Allegretta's avatar
Chris Allegretta committed
525
526
527
528
529
530
531
532
		    ;
		else
		    continue;
	    }
	    else
		continue;
	}

Chris Allegretta's avatar
Chris Allegretta committed
533
	/* If we're here, we've found the search string */
534
535
536
537
538
	if (!replaceall) {

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

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

541
542
543
544
	    do_replace_highlight(FALSE, prevanswer);
	    curs_set(1);
	}

Chris Allegretta's avatar
Chris Allegretta committed
545
546
	if (*i > 0 || replaceall) {	/* Yes, replace it!!!! */
	    if (*i == 2)
Chris Allegretta's avatar
Chris Allegretta committed
547
548
		replaceall = 1;

549
550
	    copy = replace_line();
	    if (!copy) {
Jordi Mallach's avatar
   
Jordi Mallach committed
551
		statusbar(_("Replace failed: unknown subexpression!"));
552
		replace_abort();
Chris Allegretta's avatar
Chris Allegretta committed
553
		return 0;
554
	    }
Chris Allegretta's avatar
Chris Allegretta committed
555
556
557
558
559
560

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

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

563
564
	    /* Adjust the original cursor position - COULD BE IMPROVED */
	    if (search_last_line) {
Chris Allegretta's avatar
Chris Allegretta committed
565
		*beginx += strlen(last_replace) - strlen(last_search);
566
567
568

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

Chris Allegretta's avatar
Chris Allegretta committed
573
574
575
	    edit_refresh();
	    set_modified();
	    numreplaced++;
Chris Allegretta's avatar
Chris Allegretta committed
576
	} else if (*i == -1)	/* Abort, else do nothing and continue loop */
Chris Allegretta's avatar
Chris Allegretta committed
577
578
579
	    break;
    }

Chris Allegretta's avatar
Chris Allegretta committed
580
581
582
583
584
585
586
587
588
589
    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
590
591
592
593
594
595
    if (ISSET(VIEW_MODE)) {
	print_view_warning();
	replace_abort();
	return 0;
    }

Chris Allegretta's avatar
Chris Allegretta committed
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
    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;
    }

    /* Again, there was a previous string but they deleted it and hit enter */
614
    if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Chris Allegretta's avatar
Chris Allegretta committed
615
616
617
618
619
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
    }

620
     /* If answer is now == "", then PICO_MODE is set.  So, copy
621
622
623
624
625
626
627
628
	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
629

630
    if (ISSET(PICO_MODE)) {
Chris Allegretta's avatar
Chris Allegretta committed
631
632
633
634
635
636
637
638
	buf = nmalloc(strlen(last_replace) + 5);
	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);

639
	    i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta's avatar
Chris Allegretta committed
640
641
642
			_("Replace with [%s]"), buf);
	}
	else
643
	    i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, "",
Chris Allegretta's avatar
Chris Allegretta committed
644
645
646
			_("Replace with"));
    }
    else
647
	i = statusq(0, replace_list_2, REPLACE_LIST_2_LEN, last_replace, 
Chris Allegretta's avatar
Chris Allegretta committed
648
649
650
651
652
653
654
655
656
657
658
			_("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
659
    current = begin;
660
    current_x = beginx - 1;
Chris Allegretta's avatar
Chris Allegretta committed
661
    renumber_all();
662
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
663
664
665
666
667
668
669
670
671
672
673
    print_replaced(numreplaced);
    replace_abort();
    return 1;
}

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

674
int do_gotoline(long line)
Chris Allegretta's avatar
Chris Allegretta committed
675
{
676
677
678
    long i = 1;

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

680
	long j = 0;
Chris Allegretta's avatar
Chris Allegretta committed
681

682
	j = statusq(0, goto_list, GOTO_LIST_LEN, "", _("Enter line number"));
683
	if (j != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
684
685
686
687
	    statusbar(_("Aborted"));
	    goto_abort();
	    return 0;
	}
688
689
690
691
692
693

	line = atoi(answer);

	/* Bounds check */
	if (line <= 0) {
	    statusbar(_("Come on, be reasonable"));
Chris Allegretta's avatar
Chris Allegretta committed
694
	    goto_abort();
695
	    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
696
697
698
	}
    }

699
700
    for (current = fileage; ((current->next != NULL) && (i < line)); i++)
	current = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
701

702
703
    current_x = 0;
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
704
705
706
707
708
709
710
711
712

    goto_abort();
    return 1;
}

int do_gotoline_void(void)
{
    return do_gotoline(0);
}