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
22
23
/**************************************************************************
 *   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.            *
 *                                                                        *
 **************************************************************************/

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

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

38
39
/* Regular expression helper functions */

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

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

Chris Allegretta's avatar
Chris Allegretta committed
54
55
56
57
58
59
60
61
62
63
64
65
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
66
67
68
69
70
71
72
73
/* 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)
{
74
75
    int i = 0;
    char *buf;
76
    char *prompt, *reprompt = "";
77
    static char *backupstring = NULL;
78

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

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

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

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

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

    if (replacing)
	reprompt = _(" (to replace)");
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
	prompt, reprompt, buf);
Chris Allegretta's avatar
Chris Allegretta committed
132
133
134
135
136

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

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

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

    return 0;
}

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

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

	free(foo);
202
203
204
    }
}

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

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

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

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

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

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

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

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

236
237
238
239
240
241
242
243
244
245
	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
246
247
248

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

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

	searchstr = fileptr->data;
    }

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

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

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

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

    placewewant = xplustabs();
    reset_cursor();

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

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

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

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

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

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

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

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

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

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

void replace_abort(void)
{
355
356
357
358
    /* 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();
359
    placewewant = xplustabs();
360
361
}

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

    new_size -= search_match_count;

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

    c = last_replace;
    while (*c) {
380
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
	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++;
	    }
	}
415
416
417
    }

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

    return new_size;
}
422
#endif
423

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

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

448
449
450
451
452
453
454
455
456
    /* 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))
457
	strcat(copy, last_replace);
458
#ifdef HAVE_REGEX_H
459
    else
460
	(void) replace_regexp(copy + current_x, 1);
461
#endif
462
463
464
465
466
467
468
469
470
471
472
473

    /* 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
474
475
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Chris Allegretta's avatar
Chris Allegretta committed
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
    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 */
613
    if (!ISSET(PICO_MODE) && !strcmp(answer, "")) {
Chris Allegretta's avatar
Chris Allegretta committed
614
615
616
617
618
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
    }

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

629
    if (ISSET(PICO_MODE)) {
Chris Allegretta's avatar
Chris Allegretta committed
630
631
632
633
634
635
636
637
	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);

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

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

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

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

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

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

	line = atoi(answer);

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

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

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

    goto_abort();
    return 1;
}

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