1 /* $MirOS: contrib/code/jupp/usearch.c,v 1.10 2017/01/11 22:56:50 tg Exp $ */
3 * Search & Replace system
5 * (C) 1992 Joseph H. Allen
7 * This file is part of JOE (Joe's Own Editor)
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 */
40 B *findhist = NULL; /* Search string history */
41 B *replhist = NULL; /* Replacement string history */
43 SRCH *globalsrch = NULL; /* Most recent completed search data */
45 SRCHREC fsr = { {&fsr, &fsr} };
47 /* Completion stuff: should go somewhere else */
49 unsigned char **word_list;
51 #define MAX_WORD_SIZE 64
52 unsigned char **get_word_list(B *b,int ignore)
54 unsigned char buf[MAX_WORD_SIZE];
56 unsigned char **list = 0;
68 while ((c=pgetc(p))!=NO_MORE_DATA)
70 if (joe_isalnum_(b->o.charmap, c)) {
71 if (idx!=MAX_WORD_SIZE)
74 if (idx!=MAX_WORD_SIZE && start!=ignore) {
77 s = vsncpy(NULL,0,buf,idx);
85 if (joe_isalpha_(b->o.charmap, c))
90 for (idx = 0;idx != h->len;++idx)
91 for (t = h->tab[idx];t;t=t->next)
92 list = vaadd(list, t->name);
94 vasort(list,sLEN(list));
101 void fcmplt_ins(BW *bw, unsigned char *line)
106 if (!piseol(bw->cursor)) {
107 c = brch(bw->cursor);
108 if (joe_isalnum_(bw->b->o.charmap,c))
112 /* Move p to beginning of word */
114 p = pdup(bw->cursor);
117 while (joe_isalnum_(bw->b->o.charmap,c));
121 if (bw->cursor->byte!=p->byte && bw->cursor->byte-p->byte<64) {
122 /* Insert single match */
124 binsm(bw->cursor,sv(line));
125 pfwrd(bw->cursor,sLEN(line));
126 bw->cursor->xcol = piscol(bw->cursor);
133 int fcmplt_abrt(BW *bw, int x, unsigned char *line)
136 fcmplt_ins(bw, line);
142 int fcmplt_rtn(MENU *m, int x, unsigned char *line)
144 fcmplt_ins(m->parent->win->object, m->list[x]);
154 unsigned char *line1;
160 /* Make sure we're not in a word */
162 if (!piseol(bw->cursor)) {
163 c = brch(bw->cursor);
164 if (joe_isalnum_(bw->b->o.charmap,c))
168 /* Move p to beginning of word */
170 p = pdup(bw->cursor);
173 while (joe_isalnum_(bw->b->o.charmap,c));
177 if (bw->cursor->byte!=p->byte && bw->cursor->byte-p->byte<64) {
178 line = brvs(p, bw->cursor->byte-p->byte);
186 word_list = get_word_list(bw->b, p->byte);
194 line1 = vsncpy(NULL,0,sv(line));
195 line1 = vsadd(line1,'*');
196 lst = regsub(word_list, aLEN(word_list), line1);
205 m = mkmenu(bw->parent, lst, fcmplt_rtn, fcmplt_abrt, NULL, 0, line, NULL);
212 /* Possible match list is now in lst */
215 return fcmplt_rtn(m, 0, line);
219 unsigned char *com = mcomplete(m);
233 static int srch_cmplt(BW *bw)
240 bw, pattern and ignore must be set
242 The first possible string we can find is the one beginning under p
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
248 Return 0 if we did not find the string:
249 p is left in its orignal spot
252 static P *searchf(BW *bw,SRCH *srch, P *p)
254 unsigned char *pattern = srch->pattern;
262 for (x = 0; x != sLEN(pattern) && pattern[x] != '\\' && (pattern[x]<128 || !p->b->o.charmap->type); ++x)
264 pattern[x] = joe_tolower(p->b->o.charmap,pattern[x]);
266 while (srch->ignore ? pifind(start, pattern, x) : pfind(start, pattern, x)) {
268 pfwrd(end, (long) x);
269 if (srch->wrap_flag && start->byte>=srch->wrap_p->byte)
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));
279 if (pgetc(start) == NO_MORE_DATA)
282 if (wrap && !srch->wrap_flag && srch->wrap_p) {
283 msgnw(bw->parent, US "Wrapped");
295 bw, pattern and ignore must be set
297 The first possible string we can find is the one beginning one position
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
304 Return 0 if we did not find the string:
305 p is left in its orignal spot
308 static P *searchb(BW *bw,SRCH *srch, P *p)
310 unsigned char *pattern = srch->pattern;
318 for (x = 0; x != sLEN(pattern) && pattern[x] != '\\' && (pattern[x]<128 || !p->b->o.charmap->type); ++x)
320 pattern[x] = joe_tolower(p->b->o.charmap,pattern[x]);
323 while (pbkwd(start, 1L)
324 && (srch->ignore ? prifind(start, pattern, x) : prfind(start, pattern, x))) {
326 pfwrd(end, (long) x);
327 if (srch->wrap_flag && start->byte<srch->wrap_p->byte)
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));
339 if (wrap && !srch->wrap_flag && srch->wrap_p) {
340 msgnw(bw->parent, US "Wrapped");
351 /* Make a search stucture */
353 static SRCH *setmark(SRCH *srch)
360 srch->markb->owner = &srch->markb;
365 srch->markk->owner = &srch->markk;
371 SRCH *mksrch(unsigned char *pattern, unsigned char *replacement, int ignore, int backwards, int repeat, int replace, int rest)
373 SRCH *srch = (SRCH *) joe_malloc(sizeof(SRCH));
376 srch->pattern = pattern;
377 srch->replacement = replacement;
378 srch->ignore = ignore;
379 srch->backwards = backwards;
380 srch->repeat = repeat;
381 srch->replace = replace;
391 srch->block_restrict = 0;
392 izque(SRCHREC, link, &srch->recs);
393 for (x = 0; x != 26; ++x)
394 srch->pieces[x] = NULL;
398 /* Eliminate a search structure */
400 void rmsrch(SRCH *srch)
409 markb->owner = &markb;
410 markb->xcol = piscol(markb);
414 markk->owner = &markk;
415 markk->xcol = piscol(markk);
417 for (x = 0; x != 26; ++x)
418 vsrm(srch->pieces[x]);
419 frchn(&fsr, &srch->recs);
421 vsrm(srch->replacement);
427 /* Insert a replacement string
428 * p is advanced past the inserted text
431 static P *insert(SRCH *srch, P *p, unsigned char *s, int len)
436 for (x = 0; x != len && s[x] != '\\'; ++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]));
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']));
454 } else if (s[1] == '&' && srch->entire) {
455 binsm(p, sv(srch->entire));
456 pfwrd(p, (long) sLEN(srch->entire));
460 unsigned char *a=(unsigned char *)s+x;
462 binsc(p,escape(p->b->o.charmap->type,&a,&l));
464 len -= a - (unsigned char *)s;
473 /* Search system user interface */
475 /* Query for search string, search options, possible replacement string,
476 * and execute first search */
478 /* Context sensitive help identifier */
479 const unsigned char srchstr[] = "Search";
481 static int pfabort(BW *bw, SRCH *srch)
488 /* always returns -1 */
489 static int pfsave(BW *bw, SRCH *srch)
503 markb->owner = &markb;
504 markb->xcol = piscol(markb);
508 markk->owner = &markk;
509 markk->xcol = piscol(markk);
519 static int set_replace(BW *bw, unsigned char *s, SRCH *srch, int *notify)
521 srch->replacement = s;
522 return dopfnext(bw, srch, notify);
525 static int set_options(BW *bw, unsigned char *s, SRCH *srch, int *notify)
529 srch->ignore = icase;
531 for (x = 0; s[x]; ++x) {
551 srch->block_restrict = 1;
563 if (srch->repeat == -1)
565 srch->repeat = srch->repeat * 10 + s[x] - '0';
571 if (wmkpw(bw->parent, US "Replace with (^C to abort): ", &replhist, set_replace, srchstr, pfabort, srch_cmplt, srch, notify, bw->b->o.charmap))
576 return dopfnext(bw, srch, notify);
579 static int set_pattern(BW *bw, unsigned char *s, SRCH *srch, int *notify)
585 p = US "case (S)ensitive (R)eplace (B)ackwards Bloc(K) NNN (^C to abort): ";
587 p = US "(I)gnore (R)eplace (B)ackwards Bloc(K) NNN (^C to abort): ";
591 if ((pbw = wmkpw(bw->parent, p, NULL, set_options, srchstr, pfabort, utypebw, srch, notify, bw->b->o.charmap)) != NULL) {
592 unsigned char buf[10];
595 binsc(pbw->cursor, 'i');
597 binsc(pbw->cursor, 'r');
599 binsc(pbw->cursor, 'b');
600 if (srch->repeat >= 0)
601 joe_snprintf_1((char *)buf, sizeof(buf), "%d", srch->repeat), binss(pbw->cursor, buf);
602 pset(pbw->cursor, pbw->b->eof);
603 pbw->cursor->xcol = piscol(pbw->cursor);
615 static int dofirst(BW *bw, int back, int repl)
619 if (smode && globalsrch) {
620 globalsrch->backwards = back;
621 globalsrch->replace = repl;
624 if (bw->parent->huh == srchstr) {
627 p_goto_eol(bw->cursor);
628 byte = bw->cursor->byte;
629 p_goto_bol(bw->cursor);
630 if (byte == bw->cursor->byte)
632 return urtn((BASE *)bw, -1);
634 srch = setmark(mksrch(NULL, NULL, 0, back, -1, repl, 0));
635 srch->addr = bw->cursor->byte;
636 srch->wrap_p = pdup(bw->cursor);
637 srch->wrap_p->owner = &srch->wrap_p;
638 if (wmkpw(bw->parent, US "Find (^C to abort): ", &findhist, set_pattern, srchstr, pfabort, srch_cmplt, srch, NULL, bw->b->o.charmap))
648 return dofirst(bw, 0, 0);
653 return dofirst(bw, 1, 0);
658 return dofirst(bw, 0, 1);
661 /* Execute next search */
663 static int doreplace(BW *bw, SRCH *srch)
668 msgnw(bw->parent, US "Read only");
674 srch->markk->end = 1;
675 q = pdup(bw->cursor);
676 if (srch->backwards) {
677 q = pfwrd(q, (long) sLEN(srch->entire));
681 q = pbkwd(q, (long) sLEN(srch->entire));
685 insert(srch, bw->cursor, sv(srch->replacement));
686 srch->addr = bw->cursor->byte;
690 srch->markk->end = 0;
694 static void visit(SRCH *srch, BW *bw, int yn)
696 SRCHREC *r = (SRCHREC *) alitem(&fsr, sizeof(SRCHREC));
698 r->addr = bw->cursor->byte;
700 r->wrap_flag = srch->wrap_flag;
701 enqueb(SRCHREC, link, &srch->recs, r);
704 static void goback(SRCH *srch, BW *bw)
706 SRCHREC *r = srch->recs.link.prev;
708 if (r != &srch->recs) {
711 if (bw->cursor->byte != r->addr)
712 pgoto(bw->cursor, r->addr);
713 srch->wrap_flag = r->wrap_flag;
714 demote(SRCHREC, link, &fsr, r);
718 static int dopfrepl(BW *bw, int c, SRCH *srch, int *notify)
720 srch->addr = bw->cursor->byte;
721 if (c == 'N' || c == 'n')
722 return dopfnext(bw, srch, notify);
723 else if (c == 'Y' || c == 'y' || c == ' ' || c == 'L' || c == 'l') {
724 srch->recs.link.prev->yn = 1;
725 /* why do I return -1 on 'L' here? */
726 return ((doreplace(bw, srch) || c == 'L' || c == 'l') ?
727 pfsave(bw, srch) : dopfnext(bw, srch, notify));
728 } else if (c == 'R' || c == 'r') {
729 if (doreplace(bw, srch))
732 return dopfnext(bw, srch, notify);
733 } else if (c == 8 || c == 127 || c == 'b' || c == 'B') {
736 return dopfnext(bw, srch, notify);
737 } else if (c != -1) {
744 if (mkqwnsr(bw->parent, sc("Replace (Y)es (N)o (L)ast (R)est (B)ackup (^C to abort)?"), dopfrepl, pfsave, srch, notify))
747 return pfsave(bw, srch);
750 /* Test if found text is within region
752 * -1 if we should keep searching
756 static int restrict_to_block(BW *bw, SRCH *srch)
758 if (!srch->block_restrict)
760 bw->cursor->xcol = piscol(bw->cursor);
763 if (bw->cursor->byte < srch->markb->byte)
765 else if (bw->cursor->byte + sLEN(srch->entire) > srch->markk->byte)
768 if (bw->cursor->line < srch->markb->line)
770 else if (bw->cursor->line > srch->markk->line)
772 else if (piscol(bw->cursor) + sLEN(srch->entire) > srch->markk->xcol || piscol(bw->cursor) < srch->markb->xcol)
774 } else if (!square) {
775 if (bw->cursor->byte > srch->markk->byte)
777 else if (bw->cursor->byte - sLEN(srch->entire) < srch->markb->byte)
780 if (bw->cursor->line > srch->markk->line)
782 if (bw->cursor->line < srch->markb->line)
784 if (piscol(bw->cursor) > srch->markk->xcol || piscol(bw->cursor) - sLEN(srch->entire) < srch->markb->xcol)
791 * 0) Search or search & replace is finished.
792 * 1) Search string was not found.
793 * 2) Search string was found.
796 static int fnext(BW *bw, SRCH *srch)
801 if (srch->repeat != -1) {
807 again:if (srch->backwards)
808 sta = searchb(bw, srch, bw->cursor);
810 sta = searchf(bw, srch, bw->cursor);
814 } else if (srch->rest || (srch->repeat != -1 && srch->replace)) {
816 switch (restrict_to_block(bw, srch)) {
821 pgoto(bw->cursor, srch->addr);
824 if (doreplace(bw, srch))
827 } else if (srch->repeat != -1) {
829 switch (restrict_to_block(bw, srch)) {
834 pgoto(bw->cursor, srch->addr);
837 srch->addr = bw->cursor->byte;
843 int dopfnext(BW *bw, SRCH *srch, int *notify)
845 int orgmid = mid; /* Original mid status */
848 mid = 1; /* Screen recenters mode during search */
850 smode = 2; /* We have started a search mode */
853 again: switch (fnext(bw, srch)) {
857 bye: if (!srch->flg && !srch->rest) {
858 if (srch->valid && srch->block_restrict)
859 msgnw(bw->parent, US "Not found (search restricted to marked block)");
861 msgnw(bw->parent, US "Not found");
867 switch (restrict_to_block(bw, srch)) {
872 pgoto(bw->cursor, srch->addr);
875 srch->addr = bw->cursor->byte;
877 /* Make sure found text is fully on screen */
878 if(srch->backwards) {
880 pfwrd(bw->cursor,sLEN(srch->entire));
881 bw->cursor->xcol = piscol(bw->cursor);
883 pbkwd(bw->cursor,sLEN(srch->entire));
886 pbkwd(bw->cursor,sLEN(srch->entire));
887 bw->cursor->xcol = piscol(bw->cursor);
889 pfwrd(bw->cursor,sLEN(srch->entire));
894 bw->cursor->xcol = piscol(bw->cursor);
895 if (srch->backwards) {
896 pdupown(bw->cursor, &markb);
897 markb->xcol = piscol(markb);
898 pdupown(markb, &markk);
899 pfwrd(markk, (long) sLEN(srch->entire));
900 markk->xcol = piscol(markk);
902 pdupown(bw->cursor, &markk);
903 markk->xcol = piscol(markk);
904 pdupown(bw->cursor, &markb);
905 pbkwd(markb, (long) sLEN(srch->entire));
906 markb->xcol = piscol(markb);
909 if (dopfrepl(bw, -1, srch, notify))
916 bw->cursor->xcol = piscol(bw->cursor);
930 if (!globalsrch) /* Query for search string if there isn't any */
933 SRCH *srch = globalsrch;
936 srch->addr = bw->cursor->byte;
937 if (!srch->wrap_p || srch->wrap_p->b!=bw->b) {
939 srch->wrap_p = pdup(bw->cursor);
940 srch->wrap_p->owner = &srch->wrap_p;
944 return dopfnext(bw, setmark(srch), NULL);