1 /*
2  *	Search & Replace system
3  *	Copyright
4  *		(C) 1992 Joseph H. Allen
5  *
6  *	This file is part of JOE (Joe's Own Editor)
7  */
8 #include "config.h"
9 #include "types.h"
10 
11 __RCSID("$MirOS: contrib/code/jupp/usearch.c,v 1.22 2018/10/20 16:34:40 tg Exp $");
12 
13 #include <stdlib.h>
14 
15 #include "b.h"
16 #include "bw.h"
17 #include "main.h"
18 #include "pw.h"
19 #include "queue.h"
20 #include "qw.h"
21 #include "regex.h"
22 #include "ublock.h"
23 #include "uedit.h"
24 #include "undo.h"
25 #include "usearch.h"
26 #include "utils.h"
27 #include "vs.h"
28 #include "charmap.h"
29 #include "w.h"
30 #include "va.h"
31 #include "tty.h"
32 #include "menu.h"
33 #include "hash.h"
34 
35 int wrap = 0;			/* Allow wrap */
36 int smode = 0;			/* Decremented to zero by execmd */
37 int csmode = 0;			/* Set for continued search mode */
38 int icase = 0;			/* Set to force case insensitive search */
39 
40 B *findhist = NULL;		/* Search string history */
41 B *replhist = NULL;		/* Replacement string history */
42 
43 SRCH *globalsrch = NULL;	/* Most recent completed search data */
44 
45 SRCHREC fsr = { {&fsr, &fsr}, 0, 0, 0 };
46 
47 /* Completion stuff: should go somewhere else */
48 
49 unsigned char **word_list;
50 
51 #define MAX_WORD_SIZE 64
52 static unsigned char **
get_word_list(B * b,int ignore)53 get_word_list(B *b, int ignore)
54 {
55 	unsigned char buf[MAX_WORD_SIZE];
56 	unsigned char *s;
57 	unsigned char **list = 0;
58 	HASH *h;
59 	HENTRY *t;
60 	P *p;
61 	int c;
62 	int idx;
63 	int start = 0;
64 
65 	h = htmk(1024);
66 
67 	p = pdup(b->bof);
68 	idx = 0;
69 	while ((c=pgetc(p))!=NO_MORE_DATA)
70 		if (idx) {
71 			if (joe_isalnux(b->o.charmap, c)) {
72 				if (idx!=MAX_WORD_SIZE)
73 					buf[idx++] = c;
74 			} else {
75 				if (idx!=MAX_WORD_SIZE && start!=ignore) {
76 					buf[idx] = 0;
77 					if (!htfind(h,buf)) {
78 						s = vsncpy(NULL,0,buf,idx);
79 						htadd(h, s, s);
80 					}
81 				}
82 				idx = 0;
83 			}
84 		} else {
85 			start=p->byte-1;
86 			if (joe_isalphx(b->o.charmap, c))
87 				buf[idx++] = c;
88 		}
89 	prm(p);
90 
91 	for (idx = 0;idx != h->len;++idx)
92 		for (t = h->tab[idx];t;t=t->next)
93 			list = vaadd(list, /* checked */ US t->name);
94 	if (list)
95 		vasort(list, sLEN(list));
96 
97 	htrm(h);
98 
99 	return list;
100 }
101 
102 static void
fcmplt_ins(BW * bw,unsigned char * line)103 fcmplt_ins(BW *bw, unsigned char *line)
104 {
105 	P *p;
106 	int c;
107 
108 	if (!piseol(bw->cursor)) {
109 		c = brch(bw->cursor);
110 		if (joe_isalnux(bw->b->o.charmap,c))
111 			return;
112 	}
113 
114 	/* Move p to beginning of word */
115 
116 	p = pdup(bw->cursor);
117 	do
118 		c = prgetc(p);
119 		while (joe_isalnux(bw->b->o.charmap,c));
120 	if (c!=NO_MORE_DATA)
121 		pgetc(p);
122 
123 	if (bw->cursor->byte!=p->byte && bw->cursor->byte-p->byte<64) {
124 		/* Insert single match */
125 		bdel(p,bw->cursor);
126 		binsm(bw->cursor,sv(line));
127 		pfwrd(bw->cursor,sLEN(line));
128 		bw->cursor->xcol = piscol(bw->cursor);
129 		prm(p);
130 	} else {
131 		prm(p);
132 	}
133 }
134 
135 static int
fcmplt_abrt(BW * bw,int x,unsigned char * line)136 fcmplt_abrt(BW *bw, int x, unsigned char *line)
137 {
138 	if (line) {
139 		fcmplt_ins(bw, line);
140 		vsrm(line);
141 	}
142 	return -1;
143 }
144 
145 static int
fcmplt_rtn(MENU * m,int x,unsigned char * line)146 fcmplt_rtn(MENU *m, int x, unsigned char *line)
147 {
148 	fcmplt_ins(m->parent->win->object.bw, m->list[x]);
149 	vsrm(line);
150 	m->object = NULL;
151 	wabort(m->parent);
152 	return 0;
153 }
154 
ufinish(BW * bw)155 int ufinish(BW *bw)
156 {
157 	unsigned char *line;
158 	unsigned char *line1;
159 	unsigned char **lst;
160 	P *p;
161 	int c;
162 	MENU *m;
163 
164 	/* Make sure we're not in a word */
165 
166 	if (!piseol(bw->cursor)) {
167 		c = brch(bw->cursor);
168 		if (joe_isalnux(bw->b->o.charmap,c))
169 			return -1;
170 	}
171 
172 	/* Move p to beginning of word */
173 
174 	p = pdup(bw->cursor);
175 	do
176 		c = prgetc(p);
177 		while (joe_isalnux(bw->b->o.charmap,c));
178 	if (c!=NO_MORE_DATA)
179 		pgetc(p);
180 
181 	if (bw->cursor->byte!=p->byte && bw->cursor->byte-p->byte<64) {
182 		line = brvs(p, bw->cursor->byte-p->byte);
183 
184 		/* We have a word */
185 
186 		/* Get word list */
187 		if (word_list)
188 			varm(word_list);
189 
190 		word_list = get_word_list(bw->b, p->byte);
191 
192 		if (!word_list) {
193 			vsrm(line);
194 			prm(p);
195 			return -1;
196 		}
197 
198 		line1 = vsncpy(NULL,0,sv(line));
199 		line1 = vsadd(line1,'*');
200 		lst = regsub(word_list, aLEN(word_list), line1);
201 		vsrm(line1);
202 
203 		if (!lst) {
204 			ttputc(7);
205 			vsrm(line);
206 			return -1;
207 		}
208 
209 		m = mkmenu(bw->parent, lst, fcmplt_rtn, fcmplt_abrt, NULL, 0, line, NULL);
210 		if (!m) {
211 			varm(lst);
212 			vsrm(line);
213 			return -1;
214 		}
215 
216 		/* Possible match list is now in lst */
217 
218 		if (aLEN(lst) == 1)
219 			return fcmplt_rtn(m, 0, line);
220 		else if (smode)
221 			return 0;
222 		else {
223 			unsigned char *com = mcomplete(m);
224 			vsrm(m->object);
225 			m->object = com;
226 			wabort(m->parent);
227 			smode = 2;
228 			ttputc(7);
229 			return 0;
230 		}
231 	} else {
232 		prm(p);
233 		return -1;
234 	}
235 }
236 
srch_cmplt(BW * bw)237 static int srch_cmplt(BW *bw)
238 {
239 	jobject jO;
240 
241 	jO.bw = bw;
242 	utypebw(jO, 9);
243 	return 0;
244 }
245 
246 /* Search forward.
247    bw, pattern and ignore must be set
248 
249    The first possible string we can find is the one beginning under p
250 
251    Returns p if we found a string:
252      The found string is placed in entire/pieces
253      p is placed right after the found string
254 
255    Return 0 if we did not find the string:
256      p is left in its orignal spot
257 */
258 
searchf(BW * bw,SRCH * srch,P * p)259 static P *searchf(BW *bw,SRCH *srch, P *p)
260 {
261 	unsigned char *pattern = srch->pattern;
262 	P *start;
263 	P *end;
264 	int x;
265 
266 	start = pdup(p);
267 	end = pdup(p);
268 
269 	for (x = 0; x != sLEN(pattern) && pattern[x] != '\\' && (pattern[x]<128 || !p->b->o.charmap->type); ++x)
270 		if (srch->ignore)
271 			pattern[x] = joe_tolower(p->b->o.charmap,pattern[x]);
272  wrapped:
273 	while (srch->ignore ? pifind(start, pattern, x) : pfind(start, pattern, x)) {
274 		pset(end, start);
275 		pfwrd(end, (long) x);
276 		if (srch->wrap_flag && start->byte>=srch->wrap_p->byte)
277 			break;
278 		if (pmatch(srch->pieces, pattern + x, sLEN(pattern) - x, end, 0, srch->ignore)) {
279 			srch->entire = vstrunc(srch->entire, (int) (end->byte - start->byte));
280 			brmem(start, srch->entire, (int) (end->byte - start->byte));
281 			pset(p, end);
282 			prm(start);
283 			prm(end);
284 			return p;
285 		}
286 		if (pgetc(start) == NO_MORE_DATA)
287 			break;
288 	}
289 	if (wrap && !srch->wrap_flag && srch->wrap_p) {
290 		msgnw(bw->parent, UC "Wrapped");
291 		srch->wrap_flag = 1;
292 		p_goto_bof(start);
293 		goto wrapped;
294 	}
295 
296 	prm(start);
297 	prm(end);
298 	return NULL;
299 }
300 
301 /* Search backwards.
302    bw, pattern and ignore must be set
303 
304    The first possible string we can find is the one beginning one position
305    to the left of p.
306 
307    Returns 1 if we found a string:
308      The found string is placed in entire
309      p is placed at the beginning of the string
310 
311    Return 0 if we did not find the string:
312      p is left in its orignal spot
313 */
314 
searchb(BW * bw,SRCH * srch,P * p)315 static P *searchb(BW *bw,SRCH *srch, P *p)
316 {
317 	unsigned char *pattern = srch->pattern;
318 	P *start;
319 	P *end;
320 	int x;
321 
322 	start = pdup(p);
323 	end = pdup(p);
324 
325 	for (x = 0; x != sLEN(pattern) && pattern[x] != '\\' && (pattern[x]<128 || !p->b->o.charmap->type); ++x)
326 		if (srch->ignore)
327 			pattern[x] = joe_tolower(p->b->o.charmap,pattern[x]);
328 
329  wrapped:
330 	while (pbkwd(start, 1L)
331 	       && (srch->ignore ? prifind(start, pattern, x) : prfind(start, pattern, x))) {
332 		pset(end, start);
333 		pfwrd(end, (long) x);
334 		if (srch->wrap_flag && start->byte<srch->wrap_p->byte)
335 			break;
336 		if (pmatch(srch->pieces, pattern + x, sLEN(pattern) - x, end, 0, srch->ignore)) {
337 			srch->entire = vstrunc(srch->entire, (int) (end->byte - start->byte));
338 			brmem(start, srch->entire, (int) (end->byte - start->byte));
339 			pset(p, start);
340 			prm(start);
341 			prm(end);
342 			return p;
343 		}
344 	}
345 
346 	if (wrap && !srch->wrap_flag && srch->wrap_p) {
347 		msgnw(bw->parent, UC "Wrapped");
348 		srch->wrap_flag = 1;
349 		p_goto_eof(start);
350 		goto wrapped;
351 	}
352 
353 	prm(start);
354 	prm(end);
355 	return NULL;
356 }
357 
358 /* Make a search stucture */
359 
setmark(SRCH * srch)360 static SRCH *setmark(SRCH *srch)
361 {
362 	if (markv(0))
363 		srch->valid = 1;
364 
365 	srch->markb = markb;
366 	if (srch->markb)
367 		srch->markb->owner = &srch->markb;
368 	markb = NULL;
369 
370 	srch->markk = markk;
371 	if (srch->markk)
372 		srch->markk->owner = &srch->markk;
373 	markk = NULL;
374 
375 	return srch;
376 }
377 
mksrch(unsigned char * pattern,unsigned char * replacement,int ignore,int backwards,int repeat,int replace,int rest)378 SRCH *mksrch(unsigned char *pattern, unsigned char *replacement, int ignore, int backwards, int repeat, int replace, int rest)
379 {
380 	SRCH *srch = malloc(sizeof(SRCH));
381 	int x;
382 
383 	srch->pattern = pattern;
384 	srch->replacement = replacement;
385 	srch->ignore = ignore;
386 	srch->backwards = backwards;
387 	srch->repeat = repeat;
388 	srch->replace = replace;
389 	srch->rest = rest;
390 	srch->entire = NULL;
391 	srch->flg = 0;
392 	srch->addr = -1;
393 	srch->markb = NULL;
394 	srch->markk = NULL;
395 	srch->wrap_p = NULL;
396 	srch->wrap_flag = 0;
397 	srch->valid = 0;
398 	srch->block_restrict = 0;
399 	izque(SRCHREC, link, &srch->recs);
400 	for (x = 0; x != 26; ++x)
401 		srch->pieces[x] = NULL;
402 	return srch;
403 }
404 
405 /* Eliminate a search structure */
406 
rmsrch(SRCH * srch)407 void rmsrch(SRCH *srch)
408 {
409 	int x;
410 
411 	prm(markb);
412 	prm(markk);
413 	prm(srch->wrap_p);
414 	if (srch->markb) {
415 		markb = srch->markb;
416 		markb->owner = &markb;
417 		markb->xcol = piscol(markb);
418 	}
419 	if (srch->markk) {
420 		markk = srch->markk;
421 		markk->owner = &markk;
422 		markk->xcol = piscol(markk);
423 	}
424 	for (x = 0; x != 26; ++x)
425 		vsrm(srch->pieces[x]);
426 	frchn(&fsr, &srch->recs);
427 	vsrm(srch->pattern);
428 	vsrm(srch->replacement);
429 	vsrm(srch->entire);
430 	free(srch);
431 	updall();
432 }
433 
434 /* Insert a replacement string
435  * p is advanced past the inserted text
436  */
437 
insert(SRCH * srch,P * p,unsigned char * s,int len)438 static P *insert(SRCH *srch, P *p, unsigned char *s, int len)
439 {
440 	int x;
441 
442 	while (len) {
443 		for (x = 0; x != len && s[x] != '\\'; ++x) ;
444 		if (x) {
445 			binsm(p, s, x);
446 			pfwrd(p, (long) x);
447 			len -= x;
448 			s += x;
449 		} else if (len >= 2) {
450 			if (((s[1] | 0x20) >= 'a' && (s[1] | 0x20) <= 'z') &&
451 			    srch->pieces[(s[1] & 0x1f) - 1]) {
452 				binsm(p, sv(srch->pieces[(s[1] & 0x1f) - 1]));
453 				pfwrd(p, (long) sLEN(srch->pieces[(s[1] & 0x1f) - 1]));
454 				s += 2;
455 				len -= 2;
456 			} else if (s[1] >= '0' && s[1] <= '9' && srch->pieces[s[1] - '0']) {
457 				binsm(p, sv(srch->pieces[s[1] - '0']));
458 				pfwrd(p, (long) sLEN(srch->pieces[s[1] - '0']));
459 				s += 2;
460 				len -= 2;
461 			} else if (s[1] == '&' && srch->entire) {
462 				binsm(p, sv(srch->entire));
463 				pfwrd(p, (long) sLEN(srch->entire));
464 				s += 2;
465 				len -= 2;
466 			} else {
467 				unsigned char *a=(unsigned char *)s+x;
468 				int l=len-x;
469 				binsc(p,escape(p->b->o.charmap->type,&a,&l));
470 				pgetc(p);
471 				len -= a - (unsigned char *)s;
472 				s = a;
473 			}
474 		} else
475 			len = 0;
476 	}
477 	return p;
478 }
479 
480 /* Search system user interface */
481 
482 /* Query for search string, search options, possible replacement string,
483  * and execute first search */
484 
485 /* Context sensitive help identifier */
486 const unsigned char srchstr[] = "Search";
487 
pfabort(BW * bw,SRCH * srch)488 static int pfabort(BW *bw, SRCH *srch)
489 {
490 	if (srch)
491 		rmsrch(srch);
492 	return -1;
493 }
494 
495 /* always returns -1 */
pfsave(BW * bw,SRCH * srch)496 static int pfsave(BW *bw, SRCH *srch)
497 {
498 	if (srch) {
499 		if (globalsrch)
500 			rmsrch(globalsrch);
501 		globalsrch = srch;
502 		srch->rest = 0;
503 		srch->repeat = -1;
504 		srch->flg = 0;
505 
506 		prm(markb);
507 		prm(markk);
508 		if (srch->markb) {
509 			markb = srch->markb;
510 			markb->owner = &markb;
511 			markb->xcol = piscol(markb);
512 		}
513 		if (srch->markk) {
514 			markk = srch->markk;
515 			markk->owner = &markk;
516 			markk->xcol = piscol(markk);
517 		}
518 		srch->markb = NULL;
519 		srch->markk = NULL;
520 
521 		updall();
522 	}
523 	return -1;
524 }
525 
set_replace(BW * bw,unsigned char * s,SRCH * srch,int * notify)526 static int set_replace(BW *bw, unsigned char *s, SRCH *srch, int *notify)
527 {
528 	srch->replacement = s;
529 	return dopfnext(bw, srch, notify);
530 }
531 
set_options(BW * bw,unsigned char * s,SRCH * srch,int * notify)532 static int set_options(BW *bw, unsigned char *s, SRCH *srch, int *notify)
533 {
534 	int x;
535 
536 	srch->ignore = icase;
537 
538 	for (x = 0; s[x]; ++x) {
539 		switch (s[x] | 0x20) {
540 		case 'r':
541 			srch->replace = 1;
542 			break;
543 		case 'b':
544 			srch->backwards = 1;
545 			break;
546 		case 'i':
547 			srch->ignore = 1;
548 			break;
549 		case 's':
550 			srch->ignore = 0;
551 			break;
552 		case 'k':
553 			srch->block_restrict = 1;
554 			break;
555 		case '0':
556 		case '1':
557 		case '2':
558 		case '3':
559 		case '4':
560 		case '5':
561 		case '6':
562 		case '7':
563 		case '8':
564 		case '9':
565 			if (srch->repeat == -1)
566 				srch->repeat = 0;
567 			srch->repeat = srch->repeat * 10 + s[x] - '0';
568 			break;
569 		}
570 	}
571 	vsrm(s);
572 	if (srch->replace) {
573 		if (wmkpw(bw->parent, UC "Replace with (^C to abort): ", &replhist, set_replace, srchstr, pfabort, srch_cmplt, srch, notify, bw->b->o.charmap))
574 			return 0;
575 		else
576 			return -1;
577 	} else
578 		return dopfnext(bw, srch, notify);
579 }
580 
set_pattern(BW * bw,unsigned char * s,SRCH * srch,int * notify)581 static int set_pattern(BW *bw, unsigned char *s, SRCH *srch, int *notify)
582 {
583 	BW *pbw;
584 	const unsigned char *p;
585 
586 	if (icase)
587 		p = UC "case (S)ensitive (R)eplace (B)ackwards Bloc(K) NNN (^C to abort): ";
588 	else
589 		p = UC "(I)gnore (R)eplace (B)ackwards Bloc(K) NNN (^C to abort): ";
590 
591 	vsrm(srch->pattern);
592 	srch->pattern = s;
593 	if ((pbw = wmkpw(bw->parent, p, NULL, set_options, srchstr, pfabort, utypebw, srch, notify, bw->b->o.charmap)) != NULL) {
594 		unsigned char buf[12];
595 
596 		if (srch->ignore)
597 			binsc(pbw->cursor, 'i');
598 		if (srch->replace)
599 			binsc(pbw->cursor, 'r');
600 		if (srch->backwards)
601 			binsc(pbw->cursor, 'b');
602 		if (srch->repeat >= 0) {
603 			joe_snprintf_1((char *)buf, sizeof(buf), "%d", srch->repeat);
604 			binss(pbw->cursor, buf);
605 		}
606 		pset(pbw->cursor, pbw->b->eof);
607 		pbw->cursor->xcol = piscol(pbw->cursor);
608 		srch->ignore = 0;
609 		srch->replace = 0;
610 		srch->backwards = 0;
611 		srch->repeat = -1;
612 		return 0;
613 	} else {
614 		rmsrch(srch);
615 		return -1;
616 	}
617 }
618 
dofirst(BW * bw,int back,int repl)619 static int dofirst(BW *bw, int back, int repl)
620 {
621 	SRCH *srch;
622 
623 	if (smode && globalsrch) {
624 		globalsrch->backwards = back;
625 		globalsrch->replace = repl;
626 		return pfnext(bw);
627 	}
628 	if (bw->parent->huh == srchstr) {
629 		long byte;
630 		jobject jO;
631 
632 		p_goto_eol(bw->cursor);
633 		byte = bw->cursor->byte;
634 		p_goto_bol(bw->cursor);
635 		if (byte == bw->cursor->byte)
636 			prgetc(bw->cursor);
637 		jO.bw = bw;
638 		return urtn(jO, -1);
639 	}
640 	srch = setmark(mksrch(NULL, NULL, 0, back, -1, repl, 0));
641 	srch->addr = bw->cursor->byte;
642 	srch->wrap_p = pdup(bw->cursor);
643 	srch->wrap_p->owner = &srch->wrap_p;
644 	if (wmkpw(bw->parent, UC "Find (^C to abort): ", &findhist, set_pattern, srchstr, pfabort, srch_cmplt, srch, NULL, bw->b->o.charmap))
645 		return 0;
646 	else {
647 		rmsrch(srch);
648 		return -1;
649 	}
650 }
651 
pffirst(BW * bw)652 int pffirst(BW *bw)
653 {
654 	return dofirst(bw, 0, 0);
655 }
656 
prfirst(BW * bw)657 int prfirst(BW *bw)
658 {
659 	return dofirst(bw, 1, 0);
660 }
661 
pqrepl(BW * bw)662 int pqrepl(BW *bw)
663 {
664 	return dofirst(bw, 0, 1);
665 }
666 
667 /* Execute next search */
668 
doreplace(BW * bw,SRCH * srch)669 static int doreplace(BW *bw, SRCH *srch)
670 {
671 	P *q;
672 
673 	if (bw->b->rdonly) {
674 		msgnw(bw->parent, UC "Read only");
675 		return -1;
676 	}
677 	if (markk)
678 		markk->end = 1;
679 	if (srch->markk)
680 		srch->markk->end = 1;
681 	q = pdup(bw->cursor);
682 	if (srch->backwards) {
683 		q = pfwrd(q, (long) sLEN(srch->entire));
684 		bdel(bw->cursor, q);
685 		prm(q);
686 	} else {
687 		q = pbkwd(q, (long) sLEN(srch->entire));
688 		bdel(q, bw->cursor);
689 		prm(q);
690 	}
691 	insert(srch, bw->cursor, sv(srch->replacement));
692 	srch->addr = bw->cursor->byte;
693 	if (markk)
694 		markk->end = 0;
695 	if (srch->markk)
696 		srch->markk->end = 0;
697 	return 0;
698 }
699 
visit(SRCH * srch,BW * bw,int yn)700 static void visit(SRCH *srch, BW *bw, int yn)
701 {
702 	SRCHREC *r = (SRCHREC *) alitem(&fsr, sizeof(SRCHREC));
703 
704 	r->addr = bw->cursor->byte;
705 	r->yn = yn;
706 	r->wrap_flag = srch->wrap_flag;
707 	enqueb(SRCHREC, link, &srch->recs, r);
708 }
709 
goback(SRCH * srch,BW * bw)710 static void goback(SRCH *srch, BW *bw)
711 {
712 	SRCHREC *r = srch->recs.link.prev;
713 
714 	if (r != &srch->recs) {
715 		if (r->yn)
716 			uundo(bw);
717 		if (bw->cursor->byte != r->addr)
718 			pgoto(bw->cursor, r->addr);
719 		srch->wrap_flag = r->wrap_flag;
720 		demote(SRCHREC, link, &fsr, r);
721 	}
722 }
723 
dopfrepl(BW * bw,int c,SRCH * srch,int * notify)724 static int dopfrepl(BW *bw, int c, SRCH *srch, int *notify)
725 {
726 	srch->addr = bw->cursor->byte;
727 	if ((c | 0x20) == 'n')
728 		return dopfnext(bw, srch, notify);
729 	else if ((c | 0x20) == 'y' || (c | 0x20) == 'l' || c == ' ') {
730 		srch->recs.link.prev->yn = 1;
731 		/* why do I return -1 on 'L' here? */
732 		return ((doreplace(bw, srch) || (c | 0x20) == 'l') ?
733 		    pfsave(bw, srch) : dopfnext(bw, srch, notify));
734 	} else if ((c | 0x20) == 'r') {
735 		if (doreplace(bw, srch))
736 			return -1;
737 		srch->rest = 1;
738 		return dopfnext(bw, srch, notify);
739 	} else if (c == 8 || c == 127 || (c | 0x20) == 'b') {
740 		goback(srch, bw);
741 		goback(srch, bw);
742 		return dopfnext(bw, srch, notify);
743 	} else if (c != -1) {
744 		if (notify)
745 			*notify = 1;
746 		pfsave(bw, srch);
747 		nungetc(c);
748 		return 0;
749 	}
750 	if (mkqwnsr(bw->parent, sc("Replace (Y)es (N)o (L)ast (R)est (B)ackup (^C to abort)?"), dopfrepl, pfsave, srch, notify))
751 		return 0;
752 	else
753 		return pfsave(bw, srch);
754 }
755 
756 /* Test if found text is within region
757  * return 0 if it is,
758  * -1 if we should keep searching
759  * 1 if we're done
760  */
761 
restrict_to_block(BW * bw,SRCH * srch)762 static int restrict_to_block(BW *bw, SRCH *srch)
763 {
764 	if (!srch->block_restrict)
765 		return 0;
766 	bw->cursor->xcol = piscol(bw->cursor);
767 	if (srch->backwards)
768 		if (!square) {
769 			if (bw->cursor->byte < srch->markb->byte)
770 				return 1;
771 			else if (bw->cursor->byte + sLEN(srch->entire) > srch->markk->byte)
772 				return -1;
773 		} else {
774 			if (bw->cursor->line < srch->markb->line)
775 				return 1;
776 			else if (bw->cursor->line > srch->markk->line)
777 				return -1;
778 			else if (piscol(bw->cursor) + sLEN(srch->entire) > srch->markk->xcol || piscol(bw->cursor) < srch->markb->xcol)
779 				return -1;
780 	} else if (!square) {
781 		if (bw->cursor->byte > srch->markk->byte)
782 			return 1;
783 		else if (bw->cursor->byte - sLEN(srch->entire) < srch->markb->byte)
784 			return -1;
785 	} else {
786 		if (bw->cursor->line > srch->markk->line)
787 			return 1;
788 		if (bw->cursor->line < srch->markb->line)
789 			return -1;
790 		if (piscol(bw->cursor) > srch->markk->xcol || piscol(bw->cursor) - sLEN(srch->entire) < srch->markb->xcol)
791 			return -1;
792 	}
793 	return 0;
794 }
795 
796 /* Possible results:
797  *   0) Search or search & replace is finished.
798  *   1) Search string was not found.
799  *   2) Search string was found.
800  */
801 
fnext(BW * bw,SRCH * srch)802 static int fnext(BW *bw, SRCH *srch)
803 {
804 	P *sta;
805 
806  next:
807 	if (srch->repeat != -1) {
808 		if (!srch->repeat)
809 			return 0;
810 		else
811 			--srch->repeat;
812 	}
813  again:
814 	if (srch->backwards)
815 		sta = searchb(bw, srch, bw->cursor);
816 	else
817 		sta = searchf(bw, srch, bw->cursor);
818 	if (!sta) {
819 		srch->repeat = -1;
820 		return 1;
821 	} else if (srch->rest || (srch->repeat != -1 && srch->replace)) {
822 		if (srch->valid)
823 			switch (restrict_to_block(bw, srch)) {
824 			case -1:
825 				goto again;
826 			case 1:
827 				if (srch->addr >= 0)
828 					pgoto(bw->cursor, srch->addr);
829 				return !srch->rest;
830 			}
831 		if (doreplace(bw, srch))
832 			return 0;
833 		goto next;
834 	} else if (srch->repeat != -1) {
835 		if (srch->valid)
836 			switch (restrict_to_block(bw, srch)) {
837 			case -1:
838 				goto again;
839 			case 1:
840 				if (srch->addr >= 0)
841 					pgoto(bw->cursor, srch->addr);
842 				return 1;
843 			}
844 		srch->addr = bw->cursor->byte;
845 		goto next;
846 	} else
847 		return 2;
848 }
849 
dopfnext(BW * bw,SRCH * srch,int * notify)850 int dopfnext(BW *bw, SRCH *srch, int *notify)
851 {
852 	int orgmid = mid;	/* Original mid status */
853 	int ret = 0;
854 
855 	mid = 1;		/* Screen recenters mode during search */
856 	if (csmode)
857 		smode = 2;	/* We have started a search mode */
858 	if (srch->replace)
859 		visit(srch, bw, 0);
860  again:
861 	switch (fnext(bw, srch)) {
862 	case 0:
863 		break;
864 	case 1:
865  bye:
866 		if (!srch->flg && !srch->rest) {
867 			if (srch->valid && srch->block_restrict)
868 				msgnw(bw->parent, UC "Not found (search restricted to marked block)");
869 			else
870 				msgnw(bw->parent, UC "Not found");
871 			ret = -1;
872 		}
873 		break;
874 	case 2:
875 		if (srch->valid)
876 			switch (restrict_to_block(bw, srch)) {
877 			case -1:
878 				goto again;
879 			case 1:
880 				if (srch->addr >= 0)
881 					pgoto(bw->cursor, srch->addr);
882 				goto bye;
883 			}
884 		srch->addr = bw->cursor->byte;
885 
886 		/* Make sure found text is fully on screen */
887 		if(srch->backwards) {
888 			bw->offset=0;
889 			pfwrd(bw->cursor,sLEN(srch->entire));
890 			bw->cursor->xcol = piscol(bw->cursor);
891 			dofollows();
892 			pbkwd(bw->cursor,sLEN(srch->entire));
893 		} else {
894 			bw->offset=0;
895 			pbkwd(bw->cursor,sLEN(srch->entire));
896 			bw->cursor->xcol = piscol(bw->cursor);
897 			dofollows();
898 			pfwrd(bw->cursor,sLEN(srch->entire));
899 		}
900 
901 		if (srch->replace) {
902 			if (square)
903 				bw->cursor->xcol = piscol(bw->cursor);
904 			if (srch->backwards) {
905 				pdupown(bw->cursor, &markb);
906 				markb->xcol = piscol(markb);
907 				pdupown(markb, &markk);
908 				pfwrd(markk, (long) sLEN(srch->entire));
909 				markk->xcol = piscol(markk);
910 			} else {
911 				pdupown(bw->cursor, &markk);
912 				markk->xcol = piscol(markk);
913 				pdupown(bw->cursor, &markb);
914 				pbkwd(markb, (long) sLEN(srch->entire));
915 				markb->xcol = piscol(markb);
916 			}
917 			srch->flg = 1;
918 			if (dopfrepl(bw, -1, srch, notify))
919 				ret = -1;
920 			notify = 0;
921 			srch = 0;
922 		}
923 		break;
924 	}
925 	bw->cursor->xcol = piscol(bw->cursor);
926 	dofollows();
927 	mid = orgmid;
928 	if (notify)
929 		*notify = 1;
930 	if (srch)
931 		pfsave(bw, srch);
932 	else
933 		updall();
934 	return ret;
935 }
936 
pfnext(BW * bw)937 int pfnext(BW *bw)
938 {
939 	SRCH *srch;
940 
941 	if (!globalsrch) {
942 		/* Query for search string if there isn't any */
943 		return pffirst(bw);
944 	}
945 
946 	srch = globalsrch;
947 	globalsrch = NULL;
948 	srch->addr = bw->cursor->byte;
949 	if (!srch->wrap_p || srch->wrap_p->b!=bw->b) {
950 		prm(srch->wrap_p);
951 		srch->wrap_p = pdup(bw->cursor);
952 		srch->wrap_p->owner = &srch->wrap_p;
953 		srch->wrap_flag = 0;
954 	}
955 	srch->valid = 0;
956 	return dopfnext(bw, setmark(srch), NULL);
957 }
958