search.c 22.9 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   search.c                                                             *
 *                                                                        *
5
 *   Copyright (C) 2000-2002 Chris Allegretta                             *
Chris Allegretta's avatar
Chris Allegretta committed
6
7
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
8
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
11
12
13
14
15
16
17
18
19
20
21
 *   any later version.                                                   *
 *                                                                        *
 *   This program is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 *   GNU General Public License for more details.                         *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

22
23
#include "config.h"

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

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

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

   replacing = 1 if we call from do_replace, 0 if called from do_search func.
*/
int search_init(int replacing)
{
75
    int i = 0;
76
    char *buf;
77
    static char *backupstring = NULL;
78
#ifndef NANO_SMALL
79
    toggle *t;
80
#endif
81

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

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

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

    /* Clear the backupstring if we've changed from Pico mode to regular
	mode */
    if (ISSET(CLEAR_BACKUPSTRING)) {
	free(backupstring);
	backupstring = NULL;
93
	UNSET(CLEAR_BACKUPSTRING);
Chris Allegretta's avatar
Chris Allegretta committed
94
95
    }
	
96
97
     /* Okay, fun time.  backupstring is our holder for what is being 
	returned from the statusq call.  Using answer for this would be tricky.
98
	Here, if we're using PICO_MODE, we only want nano to put the
99
100
101
102
103
	old string back up as editable if it's not the same as last_search.

	Otherwise, if we don't already have a backupstring, set it to
	last_search.  */

104
    if (ISSET(PICO_MODE)) {
105
106
107
108
109
110
	if (backupstring == NULL || !strcmp(backupstring, last_search))
	    backupstring = mallocstrcpy(backupstring, "");
    }
    else if (backupstring == NULL)
	backupstring = mallocstrcpy(backupstring, last_search);

Chris Allegretta's avatar
Chris Allegretta committed
111
    /* If using Pico messages, we do things the old fashioned way... */
112
    if (ISSET(PICO_MODE)) {
113
114
115
116
117
118
119
120
121
122
123
	if (last_search[0]) {

	    /* We use COLS / 3 here because we need to see more on the line */
	    if (strlen(last_search) > COLS / 3) {
		snprintf(buf, COLS / 3 + 3, " [%s", last_search);
		sprintf(&buf[COLS / 3 + 2], "...]");
	    } else
		sprintf(buf, " [%s]", last_search);
	} else {
	    buf[0] = '\0';
	}
Chris Allegretta's avatar
Chris Allegretta committed
124
    }
125
    else
Chris Allegretta's avatar
Chris Allegretta committed
126
	buf[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
127

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

	/* This string is just a modifier for the search prompt,
	   no grammar is implied */
	ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") : "",

	/* This string is just a modifier for the search prompt,
	   no grammar is implied */
	ISSET(USE_REGEXP) ? _(" [Regexp]") : "",

	/* This string is just a modifier for the search prompt,
	   no grammar is implied */
	ISSET(REVERSE_SEARCH) ? _(" [Backwards]") : "",

Chris Allegretta's avatar
Chris Allegretta committed
145
146
	replacing ? _(" (to replace)") : "",
	buf);
Chris Allegretta's avatar
Chris Allegretta committed
147

Chris Allegretta's avatar
Chris Allegretta committed
148
149
150
    /* Release buf now that we don't need it anymore */
    free(buf);

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

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

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

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

    return 0;
}

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

	foo = mallocstrcpy(foo, str);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
224
	foo[COLS / 2] = '\0';
225
	statusbar(_("\"%s...\" not found"), foo);
226
227

	free(foo);
228
229
230
    }
}

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

245
int past_editbuff;	/* findnextstr() is now searching lines not displayed */
246
247

filestruct *findnextstr(int quiet, int bracket_mode, filestruct * begin, int beginx,
248
			char *needle)
Chris Allegretta's avatar
Chris Allegretta committed
249
{
Chris Allegretta's avatar
Chris Allegretta committed
250
    filestruct *fileptr = current;
Chris Allegretta's avatar
Chris Allegretta committed
251
    char *searchstr, *rev_start = NULL, *found = NULL;
252
    int current_x_find = 0;
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
	    /* finished processing file, get out */
	    if (search_last_line) {
		if (!quiet)
		    not_found_msg(needle);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
277
		update_line(fileptr, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
278
279
	        return NULL;
	    }
280

Chris Allegretta's avatar
Chris Allegretta committed
281
	    update_line(fileptr, 0);
Chris Allegretta's avatar
Chris Allegretta committed
282
	    fileptr = fileptr->next;
283

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

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

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

	    searchstr = fileptr->data;
	}
305

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

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

Chris Allegretta's avatar
Chris Allegretta committed
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;
	    }

Chris Allegretta's avatar
Chris Allegretta committed
344
	    update_line(fileptr, 0);
Chris Allegretta's avatar
Chris Allegretta committed
345
346
	    fileptr = fileptr->prev;

347
	    if (fileptr == edittop->prev)
348
		past_editbuff = 1;
349

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

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

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

387
388
389
390
391
    if (!bracket_mode) {
	if (past_editbuff)
	   edit_update(fileptr, CENTER);
	else
	   update_line(current, current_x);
392

393
394
395
	placewewant = xplustabs();
	reset_cursor();
    }
396

Chris Allegretta's avatar
Chris Allegretta committed
397
398
399
400
401
402
403
404
    return fileptr;
}

void search_abort(void)
{
    UNSET(KEEP_CUTBUFFER);
    display_main_list();
    wrefresh(bottomwin);
405
    if (ISSET(MARK_ISSET))
406
	edit_refresh_clearok();
407

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

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

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

    /* The sneaky user deleted the previous search string */
Chris Allegretta's avatar
Chris Allegretta committed
441
    if (!ISSET(PICO_MODE) && answer[0] == '\0') {
Jordi Mallach's avatar
   
Jordi Mallach committed
442
	statusbar(_("Search Cancelled"));
443
444
445
446
	search_abort();
	return 0;
    }

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

Chris Allegretta's avatar
Chris Allegretta committed
450
    if (answer[0] == '\0')
451
452
453
454
	answer = mallocstrcpy(answer, last_search);
    else
	last_search = mallocstrcpy(last_search, answer);

455
    search_last_line = 0;
456
457
458
459
460
461
    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
462
    search_abort();
463

Chris Allegretta's avatar
Chris Allegretta committed
464
465
466
467
468
469
    return 1;
}

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

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

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

    new_size -= search_match_count;

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

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

    if (create_flag)
540
	*string = 0;
541
542
543

    return new_size;
}
544
#endif
545

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

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

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

    /* Head of Original Line */
    strncpy(copy, current->data, current_x);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
575
    copy[current_x] = '\0';
576
577
578

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

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

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

    filestruct *fileptr = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
605
    char *copy;
Chris Allegretta's avatar
Chris Allegretta committed
606

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

Chris Allegretta's avatar
Chris Allegretta committed
625
    if (ISSET(PICO_MODE) && answer[0] == '\0')
626
627
	answer = mallocstrcpy(answer, last_replace);

628
    last_replace = mallocstrcpy(last_replace, answer);
Chris Allegretta's avatar
Chris Allegretta committed
629
630
    while (1) {

631
	/* Sweet optimization by Rocco here */
632
#if 0
633
	fileptr = findnextstr(replaceall, FALSE, begin, *beginx, prevanswer);
634
635
636
637
638
639
#else
	if (fileptr != 0)
	    fileptr = findnextstr(1, FALSE, begin, *beginx, prevanswer);
	else
	    fileptr = findnextstr(replaceall || (search_last_line ? 1 : 0), FALSE, begin, *beginx, prevanswer);
#endif
Chris Allegretta's avatar
Chris Allegretta committed
640
641

	/* No more matches.  Done! */
642
	if (!fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
643
644
	    break;

645
	/* Make sure only whole words are found */
646
647
	if ((wholewords) && (!is_whole_word(current_x, fileptr, prevanswer)))
	    continue;
Chris Allegretta's avatar
Chris Allegretta committed
648

Chris Allegretta's avatar
Chris Allegretta committed
649
	/* If we're here, we've found the search string */
650
651
652
653
654
	if (!replaceall) {

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

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

657
658
659
660
	    do_replace_highlight(FALSE, prevanswer);
	    curs_set(1);
	}

Chris Allegretta's avatar
Chris Allegretta committed
661
662
	if (*i > 0 || replaceall) {	/* Yes, replace it!!!! */
	    if (*i == 2)
Chris Allegretta's avatar
Chris Allegretta committed
663
664
		replaceall = 1;

665
666
	    copy = replace_line();
	    if (!copy) {
Jordi Mallach's avatar
   
Jordi Mallach committed
667
		statusbar(_("Replace failed: unknown subexpression!"));
668
		replace_abort();
Chris Allegretta's avatar
Chris Allegretta committed
669
		return 0;
670
	    }
Chris Allegretta's avatar
Chris Allegretta committed
671
672

	    /* Cleanup */
673
	    totsize -= strlen(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
674
675
	    free(current->data);
	    current->data = copy;
676
	    totsize += strlen(current->data);
Chris Allegretta's avatar
Chris Allegretta committed
677

678
679
680
	    if (!ISSET(REVERSE_SEARCH)) {
		/* Stop bug where we replace a substring of the replacement text */
		current_x += strlen(last_replace) - 1;
Chris Allegretta's avatar
Chris Allegretta committed
681

682
683
684
		/* Adjust the original cursor position - COULD BE IMPROVED */
		if (search_last_line) {
		    *beginx += strlen(last_replace) - strlen(last_search);
685

686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
		    /* For strings that cross the search start/end boundary */
		    /* Don't go outside of allocated memory */
		    if (*beginx < 1)
			*beginx = 1;
		}
	    } else {
		if (current_x > 1)
		    current_x--;

		if (search_last_line) {
		    *beginx += strlen(last_replace) - strlen(last_search);

		    if (*beginx > strlen(current->data))
			*beginx = strlen(current->data);
		}
701
702
	    }

Chris Allegretta's avatar
Chris Allegretta committed
703
704
705
	    edit_refresh();
	    set_modified();
	    numreplaced++;
Chris Allegretta's avatar
Chris Allegretta committed
706
	} else if (*i == -1)	/* Abort, else do nothing and continue loop */
Chris Allegretta's avatar
Chris Allegretta committed
707
708
709
	    break;
    }

Chris Allegretta's avatar
Chris Allegretta committed
710
711
712
713
714
715
716
717
718
719
    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
720
721
722
723
724
725
    if (ISSET(VIEW_MODE)) {
	print_view_warning();
	replace_abort();
	return 0;
    }

Chris Allegretta's avatar
Chris Allegretta committed
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
    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;
    }

743
    /* Again, there was a previous string, but they deleted it and hit enter */
Chris Allegretta's avatar
Chris Allegretta committed
744
    if (!ISSET(PICO_MODE) && answer[0] == '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
745
746
747
748
749
	statusbar(_("Replace Cancelled"));
	replace_abort();
	return 0;
    }

750
     /* If answer is now == "", then PICO_MODE is set.  So, copy
751
	last_search into answer (and prevanswer)... */
Chris Allegretta's avatar
Chris Allegretta committed
752
    if (answer[0] == '\0') {
753
754
755
756
757
758
	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
759

760
    if (ISSET(PICO_MODE)) {
761
	buf = charalloc(strlen(last_replace) + 5);
Chris Allegretta's avatar
Chris Allegretta committed
762
	if (last_replace[0] != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
763
764
765
766
767
768
	    if (strlen(last_replace) > (COLS / 3)) {
		strncpy(buf, last_replace, COLS / 3);
		sprintf(&buf[COLS / 3 - 1], "...");
	    } else
		sprintf(buf, "%s", last_replace);

769
	    i = statusq(0, replace_list_2, "",
Chris Allegretta's avatar
Chris Allegretta committed
770
771
772
			_("Replace with [%s]"), buf);
	}
	else
773
	    i = statusq(0, replace_list_2, "",
Chris Allegretta's avatar
Chris Allegretta committed
774
775
776
			_("Replace with"));
    }
    else
777
	i = statusq(0, replace_list_2, last_replace, 
Chris Allegretta's avatar
Chris Allegretta committed
778
779
780
781
			_("Replace with"));

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

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

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

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

812
int do_gotoline(int line, int save_pos)
Chris Allegretta's avatar
Chris Allegretta committed
813
{
814
    int i = 1;
815
816

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

818
	int j = 0;
Chris Allegretta's avatar
Chris Allegretta committed
819

820
	j = statusq(0, goto_list, "", _("Enter line number"));
821
	if (j != 0) {
Chris Allegretta's avatar
Chris Allegretta committed
822
823
824
825
	    statusbar(_("Aborted"));
	    goto_abort();
	    return 0;
	}
826
827
828
829
830
831

	line = atoi(answer);

	/* Bounds check */
	if (line <= 0) {
	    statusbar(_("Come on, be reasonable"));
Chris Allegretta's avatar
Chris Allegretta committed
832
	    goto_abort();
833
	    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
834
835
836
	}
    }

837
838
    for (current = fileage; ((current->next != NULL) && (i < line)); i++)
	current = current->next;
Chris Allegretta's avatar
Chris Allegretta committed
839

840
    current_x = 0;
841
842
843
844
845
846
847

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

849
    placewewant = xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
850
851
852
853
854
855
    goto_abort();
    return 1;
}

int do_gotoline_void(void)
{
856
    return do_gotoline(0, 0);
Chris Allegretta's avatar
Chris Allegretta committed
857
}
858

859
#if (defined ENABLE_MULTIBUFFER || !defined DISABLE_SPELLER)
860
void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant)
861
862
863
864
865
{
    /* 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);
866

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
867
868
869
    /* make sure that the x-coordinate is sane here */
    if (pos_x > strlen(current->data))
	pos_x = strlen(current->data);
870
871

    /* set the rest of the coordinates up */
872
873
874
875
876
    current_x = pos_x;
    placewewant = pos_placewewant;
    update_line(current, pos_x);
}
#endif
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947

#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