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