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