another update from CVS HEAD, for QA
[alioth/jupp.git] / usearch.c
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.20 2017/12/08 02:00:43 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 **
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
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
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
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
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
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
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
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
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
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
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
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] >= 'a' && s[1] <= 'z') || (s[1] >= 'A' && s[1] <= '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
488 static int pfabort(BW *bw, SRCH *srch)
489 {
490         if (srch)
491                 rmsrch(srch);
492         return -1;
493 }
494
495 /* always returns -1 */
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
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
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
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[10];
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), binss(pbw->cursor, buf);
604                 pset(pbw->cursor, pbw->b->eof);
605                 pbw->cursor->xcol = piscol(pbw->cursor);
606                 srch->ignore = 0;
607                 srch->replace = 0;
608                 srch->backwards = 0;
609                 srch->repeat = -1;
610                 return 0;
611         } else {
612                 rmsrch(srch);
613                 return -1;
614         }
615 }
616
617 static int dofirst(BW *bw, int back, int repl)
618 {
619         SRCH *srch;
620
621         if (smode && globalsrch) {
622                 globalsrch->backwards = back;
623                 globalsrch->replace = repl;
624                 return pfnext(bw);
625         }
626         if (bw->parent->huh == srchstr) {
627                 long byte;
628                 jobject jO;
629
630                 p_goto_eol(bw->cursor);
631                 byte = bw->cursor->byte;
632                 p_goto_bol(bw->cursor);
633                 if (byte == bw->cursor->byte)
634                         prgetc(bw->cursor);
635                 jO.bw = bw;
636                 return urtn(jO, -1);
637         }
638         srch = setmark(mksrch(NULL, NULL, 0, back, -1, repl, 0));
639         srch->addr = bw->cursor->byte;
640         srch->wrap_p = pdup(bw->cursor);
641         srch->wrap_p->owner = &srch->wrap_p;
642         if (wmkpw(bw->parent, UC "Find (^C to abort): ", &findhist, set_pattern, srchstr, pfabort, srch_cmplt, srch, NULL, bw->b->o.charmap))
643                 return 0;
644         else {
645                 rmsrch(srch);
646                 return -1;
647         }
648 }
649
650 int pffirst(BW *bw)
651 {
652         return dofirst(bw, 0, 0);
653 }
654
655 int prfirst(BW *bw)
656 {
657         return dofirst(bw, 1, 0);
658 }
659
660 int pqrepl(BW *bw)
661 {
662         return dofirst(bw, 0, 1);
663 }
664
665 /* Execute next search */
666
667 static int doreplace(BW *bw, SRCH *srch)
668 {
669         P *q;
670
671         if (bw->b->rdonly) {
672                 msgnw(bw->parent, UC "Read only");
673                 return -1;
674         }
675         if (markk)
676                 markk->end = 1;
677         if (srch->markk)
678                 srch->markk->end = 1;
679         q = pdup(bw->cursor);
680         if (srch->backwards) {
681                 q = pfwrd(q, (long) sLEN(srch->entire));
682                 bdel(bw->cursor, q);
683                 prm(q);
684         } else {
685                 q = pbkwd(q, (long) sLEN(srch->entire));
686                 bdel(q, bw->cursor);
687                 prm(q);
688         }
689         insert(srch, bw->cursor, sv(srch->replacement));
690         srch->addr = bw->cursor->byte;
691         if (markk)
692                 markk->end = 0;
693         if (srch->markk)
694                 srch->markk->end = 0;
695         return 0;
696 }
697
698 static void visit(SRCH *srch, BW *bw, int yn)
699 {
700         SRCHREC *r = (SRCHREC *) alitem(&fsr, sizeof(SRCHREC));
701
702         r->addr = bw->cursor->byte;
703         r->yn = yn;
704         r->wrap_flag = srch->wrap_flag;
705         enqueb(SRCHREC, link, &srch->recs, r);
706 }
707
708 static void goback(SRCH *srch, BW *bw)
709 {
710         SRCHREC *r = srch->recs.link.prev;
711
712         if (r != &srch->recs) {
713                 if (r->yn)
714                         uundo(bw);
715                 if (bw->cursor->byte != r->addr)
716                         pgoto(bw->cursor, r->addr);
717                 srch->wrap_flag = r->wrap_flag;
718                 demote(SRCHREC, link, &fsr, r);
719         }
720 }
721
722 static int dopfrepl(BW *bw, int c, SRCH *srch, int *notify)
723 {
724         srch->addr = bw->cursor->byte;
725         if ((c | 0x20) == 'n')
726                 return dopfnext(bw, srch, notify);
727         else if ((c | 0x20) == 'y' || (c | 0x20) == 'l' || c == ' ') {
728                 srch->recs.link.prev->yn = 1;
729                 /* why do I return -1 on 'L' here? */
730                 return ((doreplace(bw, srch) || (c | 0x20) == 'l') ?
731                     pfsave(bw, srch) : dopfnext(bw, srch, notify));
732         } else if ((c | 0x20) == 'r') {
733                 if (doreplace(bw, srch))
734                         return -1;
735                 srch->rest = 1;
736                 return dopfnext(bw, srch, notify);
737         } else if (c == 8 || c == 127 || (c | 0x20) == 'b') {
738                 goback(srch, bw);
739                 goback(srch, bw);
740                 return dopfnext(bw, srch, notify);
741         } else if (c != -1) {
742                 if (notify)
743                         *notify = 1;
744                 pfsave(bw, srch);
745                 nungetc(c);
746                 return 0;
747         }
748         if (mkqwnsr(bw->parent, sc("Replace (Y)es (N)o (L)ast (R)est (B)ackup (^C to abort)?"), dopfrepl, pfsave, srch, notify))
749                 return 0;
750         else
751                 return pfsave(bw, srch);
752 }
753
754 /* Test if found text is within region
755  * return 0 if it is,
756  * -1 if we should keep searching
757  * 1 if we're done
758  */
759
760 static int restrict_to_block(BW *bw, SRCH *srch)
761 {
762         if (!srch->block_restrict)
763                 return 0;
764         bw->cursor->xcol = piscol(bw->cursor);
765         if (srch->backwards)
766                 if (!square) {
767                         if (bw->cursor->byte < srch->markb->byte)
768                                 return 1;
769                         else if (bw->cursor->byte + sLEN(srch->entire) > srch->markk->byte)
770                                 return -1;
771                 } else {
772                         if (bw->cursor->line < srch->markb->line)
773                                 return 1;
774                         else if (bw->cursor->line > srch->markk->line)
775                                 return -1;
776                         else if (piscol(bw->cursor) + sLEN(srch->entire) > srch->markk->xcol || piscol(bw->cursor) < srch->markb->xcol)
777                                 return -1;
778         } else if (!square) {
779                 if (bw->cursor->byte > srch->markk->byte)
780                         return 1;
781                 else if (bw->cursor->byte - sLEN(srch->entire) < srch->markb->byte)
782                         return -1;
783         } else {
784                 if (bw->cursor->line > srch->markk->line)
785                         return 1;
786                 if (bw->cursor->line < srch->markb->line)
787                         return -1;
788                 if (piscol(bw->cursor) > srch->markk->xcol || piscol(bw->cursor) - sLEN(srch->entire) < srch->markb->xcol)
789                         return -1;
790         }
791         return 0;
792 }
793
794 /* Possible results:
795  *   0) Search or search & replace is finished.
796  *   1) Search string was not found.
797  *   2) Search string was found.
798  */
799
800 static int fnext(BW *bw, SRCH *srch)
801 {
802         P *sta;
803
804  next:
805         if (srch->repeat != -1) {
806                 if (!srch->repeat)
807                         return 0;
808                 else
809                         --srch->repeat;
810         }
811  again:
812         if (srch->backwards)
813                 sta = searchb(bw, srch, bw->cursor);
814         else
815                 sta = searchf(bw, srch, bw->cursor);
816         if (!sta) {
817                 srch->repeat = -1;
818                 return 1;
819         } else if (srch->rest || (srch->repeat != -1 && srch->replace)) {
820                 if (srch->valid)
821                         switch (restrict_to_block(bw, srch)) {
822                         case -1:
823                                 goto again;
824                         case 1:
825                                 if (srch->addr >= 0)
826                                         pgoto(bw->cursor, srch->addr);
827                                 return !srch->rest;
828                         }
829                 if (doreplace(bw, srch))
830                         return 0;
831                 goto next;
832         } else if (srch->repeat != -1) {
833                 if (srch->valid)
834                         switch (restrict_to_block(bw, srch)) {
835                         case -1:
836                                 goto again;
837                         case 1:
838                                 if (srch->addr >= 0)
839                                         pgoto(bw->cursor, srch->addr);
840                                 return 1;
841                         }
842                 srch->addr = bw->cursor->byte;
843                 goto next;
844         } else
845                 return 2;
846 }
847
848 int dopfnext(BW *bw, SRCH *srch, int *notify)
849 {
850         int orgmid = mid;       /* Original mid status */
851         int ret = 0;
852
853         mid = 1;                /* Screen recenters mode during search */
854         if (csmode)
855                 smode = 2;      /* We have started a search mode */
856         if (srch->replace)
857                 visit(srch, bw, 0);
858  again:
859         switch (fnext(bw, srch)) {
860         case 0:
861                 break;
862         case 1:
863  bye:
864                 if (!srch->flg && !srch->rest) {
865                         if (srch->valid && srch->block_restrict)
866                                 msgnw(bw->parent, UC "Not found (search restricted to marked block)");
867                         else
868                                 msgnw(bw->parent, UC "Not found");
869                         ret = -1;
870                 }
871                 break;
872         case 2:
873                 if (srch->valid)
874                         switch (restrict_to_block(bw, srch)) {
875                         case -1:
876                                 goto again;
877                         case 1:
878                                 if (srch->addr >= 0)
879                                         pgoto(bw->cursor, srch->addr);
880                                 goto bye;
881                         }
882                 srch->addr = bw->cursor->byte;
883
884                 /* Make sure found text is fully on screen */
885                 if(srch->backwards) {
886                         bw->offset=0;
887                         pfwrd(bw->cursor,sLEN(srch->entire));
888                         bw->cursor->xcol = piscol(bw->cursor);
889                         dofollows();
890                         pbkwd(bw->cursor,sLEN(srch->entire));
891                 } else {
892                         bw->offset=0;
893                         pbkwd(bw->cursor,sLEN(srch->entire));
894                         bw->cursor->xcol = piscol(bw->cursor);
895                         dofollows();
896                         pfwrd(bw->cursor,sLEN(srch->entire));
897                 }
898
899                 if (srch->replace) {
900                         if (square)
901                                 bw->cursor->xcol = piscol(bw->cursor);
902                         if (srch->backwards) {
903                                 pdupown(bw->cursor, &markb);
904                                 markb->xcol = piscol(markb);
905                                 pdupown(markb, &markk);
906                                 pfwrd(markk, (long) sLEN(srch->entire));
907                                 markk->xcol = piscol(markk);
908                         } else {
909                                 pdupown(bw->cursor, &markk);
910                                 markk->xcol = piscol(markk);
911                                 pdupown(bw->cursor, &markb);
912                                 pbkwd(markb, (long) sLEN(srch->entire));
913                                 markb->xcol = piscol(markb);
914                         }
915                         srch->flg = 1;
916                         if (dopfrepl(bw, -1, srch, notify))
917                                 ret = -1;
918                         notify = 0;
919                         srch = 0;
920                 }
921                 break;
922         }
923         bw->cursor->xcol = piscol(bw->cursor);
924         dofollows();
925         mid = orgmid;
926         if (notify)
927                 *notify = 1;
928         if (srch)
929                 pfsave(bw, srch);
930         else
931                 updall();
932         return ret;
933 }
934
935 int pfnext(BW *bw)
936 {
937         SRCH *srch;
938
939         if (!globalsrch) {
940                 /* Query for search string if there isn't any */
941                 return pffirst(bw);
942         }
943
944         srch = globalsrch;
945         globalsrch = NULL;
946         srch->addr = bw->cursor->byte;
947         if (!srch->wrap_p || srch->wrap_p->b!=bw->b) {
948                 prm(srch->wrap_p);
949                 srch->wrap_p = pdup(bw->cursor);
950                 srch->wrap_p->owner = &srch->wrap_p;
951                 srch->wrap_flag = 0;
952         }
953         srch->valid = 0;
954         return dopfnext(bw, setmark(srch), NULL);
955 }