1 /*
2 * The functions in this file implement commands that search in the forward
3 * and backward directions.
4 *
5 * (History comments formerly here have been moved to history.c)
6 */
7
8 #include <stdio.h>
9 #include "estruct.h"
10 #include "eproto.h"
11 #include "edef.h"
12 #include "elang.h"
13
14 #define SEARCH_HIGHLIGHT 10 /* mark # used to highlight */
15 #define MAGIC_JUMP_TABLES 1 /* Jump tables in MAGIC mode */
16
17 #if MAGIC
18 static int group_count;
19 static int group_len[MAXGROUPS];
20 static REGION group_reg[MAXGROUPS];
21
22 #endif
23
24 #if WINDOW_MSWIN
25 static int o = 0; /* For longop() calls.*/
26 #endif
27
28 /*
29 * forwsearch -- Search forward. Get a search string from the user, and
30 * search for the string. If found, reset the "." to be just after
31 * the match string, and (perhaps) repaint the display.
32 */
33 #if PROTO
forwsearch(int f,int n)34 int PASCAL NEAR forwsearch(int f, int n)
35 #else
36 int PASCAL NEAR forwsearch( f, n)
37 int f;
38 int n;
39 #endif
40 {
41 register int status;
42
43 /* If n is negative, search backwards.
44 * Otherwise proceed by asking for the search string.
45 */
46 if (n < 0)
47 return (backsearch(f, -n));
48
49 /* Ask the user for the text of a pattern. If the response is
50 * TRUE (responses other than FALSE are possible), search for
51 * pattern for up to n times, as long as the pattern is there
52 * to be found.
53 */
54 if ((status = readpattern(TEXT78, (char *)&pat[0], TRUE)) == TRUE)
55 status = forwhunt(f, n);
56 /* "Search" */
57
58 return (status);
59 }
60
61 /*
62 * forwhunt -- Search forward for a previously acquired search string.
63 * If found, reset the "." to be just after the match string,
64 * and (perhaps) repaint the display.
65 */
66 #if PROTO
forwhunt(int f,int n)67 int PASCAL NEAR forwhunt(int f, int n)
68 #else
69 int PASCAL NEAR forwhunt( f, n)
70 int f;
71 int n;
72 #endif
73 {
74 register int spoint = PTEND;
75 register int status;
76
77 if (n < 0) /* search backwards */
78 return (backhunt(f, -n));
79
80 /* Make sure a pattern exists, or that we didn't switch
81 * into MAGIC mode after we entered the pattern.
82 */
83 if (pat[0] == '\0') {
84 mlwrite(TEXT80);
85 /* "No pattern set" */
86 return FALSE;
87 }
88
89 #if MAGIC
90 if ((curwp->w_bufp->b_mode & MDMAGIC) && mcpat[0].mc_type == MCNIL) {
91 if (!mcstr())
92 return FALSE;
93 }
94 #endif
95
96 /*
97 * Do one extra search to get us past our current
98 * match, if the search type has us at the start
99 * of a match, instead of after a match.
100 */
101 if (searchtype == SRBEGIN) {
102 spoint = PTBEG;
103 if (lastflag & CFSRCH)
104 n = (n > 2)? (n + 1): 2;
105 }
106
107 #if MAGIC
108 #if 0
109 if (magical && (curwp->w_bufp->b_mode & MDMAGIC))
110 status = mcscanner(&mcpat[0], FORWARD, spoint, n);
111 else
112 status = mcscanner(&mcdeltapat[0], FORWARD, spoint, n);
113 #else
114 status = mcscanner((magical && (curwp->w_bufp->b_mode & MDMAGIC))?
115 &mcpat[0]: &mcdeltapat[0], FORWARD, spoint, n);
116 #endif
117 #else
118 status = scanner(FORWARD, spoint, n);
119 #endif
120
121 /* Complain if not there.
122 */
123 if (status == FALSE)
124 mlwrite(TEXT79);
125 /* "Not found" */
126
127 thisflag |= CFSRCH;
128 return (status);
129 }
130
131 /*
132 * backsearch -- Reverse search. Get a search string from the user, and
133 * search, starting at "." and proceeding toward the front of the buffer.
134 * If found "." is left pointing at the first character of the pattern
135 * (the last character that was matched).
136 */
137 #if PROTO
backsearch(int f,int n)138 int PASCAL NEAR backsearch(int f, int n)
139 #else
140 int PASCAL NEAR backsearch( f, n)
141 int f;
142 int n;
143 #endif
144 {
145 register int status;
146
147 /* If n is negative, search forwards.
148 * Otherwise proceed by asking for the search string.
149 */
150 if (n < 0)
151 return (forwsearch(f, -n));
152
153 /* Ask the user for the text of a pattern. If the response is
154 * TRUE (responses other than FALSE are possible), search for
155 * pattern for up to n times, as long as the pattern is there
156 * to be found.
157 */
158 if ((status = readpattern(TEXT81, (char *)&pat[0], TRUE)) == TRUE)
159 status = backhunt(f, n);
160 /* "Reverse search" */
161
162 return (status);
163 }
164
165 /*
166 * backhunt -- Reverse search for a previously acquired search string,
167 * starting at "." and proceeding toward the front of the buffer.
168 * If found "." is left pointing at the first character of the pattern
169 * (the last character that was matched).
170 */
171 #if PROTO
backhunt(int f,int n)172 int PASCAL NEAR backhunt(int f, int n)
173 #else
174 int PASCAL NEAR backhunt( f, n)
175 int f;
176 int n;
177 #endif
178 {
179 register int spoint = PTBEG;
180 register int status;
181
182 if (n < 0)
183 return (forwhunt(f, -n));
184
185 /* Make sure a pattern exists, or that we didn't switch
186 * into MAGIC mode after we entered the pattern.
187 */
188 if (tap[0] == '\0') {
189 mlwrite(TEXT80);
190 /* "No pattern set" */
191 return FALSE;
192 }
193
194 #if MAGIC
195 if ((curwp->w_bufp->b_mode & MDMAGIC) && tapcm[0].mc_type == MCNIL) {
196 if (!mcstr())
197 return FALSE;
198 }
199 #endif
200
201 /*
202 * Do one extra search to get us past our current
203 * match, if the search type has us at the start
204 * of a match, instead of after a match.
205 */
206 if (searchtype == SREND) {
207 spoint = PTEND;
208 if (lastflag & CFSRCH)
209 n = (n > 2)? (n + 1): 2;
210 }
211
212 #if MAGIC
213 #if 0
214 if (magical && (curwp->w_bufp->b_mode & MDMAGIC))
215 status = mcscanner(&tapcm[0], REVERSE, spoint, n);
216 else
217 status = mcscanner(&tapatledcm[0], REVERSE, spoint, n);
218 #else
219 status = mcscanner((magical && (curwp->w_bufp->b_mode & MDMAGIC))?
220 &tapcm[0]: &tapatledcm[0], REVERSE, spoint, n);
221 #endif
222 #else
223 status = scanner(REVERSE, spoint, n);
224 #endif
225
226 /* Complain if not there.
227 */
228 if (status == FALSE)
229 mlwrite(TEXT79);
230 /* "Not found" */
231
232 thisflag |= CFSRCH;
233 return (status);
234 }
235
236 #if MAGIC
237 /*
238 * mcscanner -- Search for a meta-pattern in either direction. If found,
239 * reset the "." to be at the start or just after the match string,
240 * and (perhaps) repaint the display.
241 */
242 #if PROTO
mcscanner(MC * mcpatrn,int direct,int beg_or_end,int repeats)243 int PASCAL NEAR mcscanner(MC *mcpatrn, int direct, int beg_or_end, int repeats)
244 #else
245 int PASCAL NEAR mcscanner( mcpatrn, direct, beg_or_end, repeats)
246 MC *mcpatrn;
247 int direct;
248 int beg_or_end;
249 int repeats;
250 #endif
251 {
252 LINE *curline; /* current line during scan */
253 int curoff; /* position within current line */
254 DELTA *tbl; /* structure with jump info */
255 int patlenadd;
256 int jump;
257
258 /* If we are going in reverse, then the 'end' is actually
259 * the beginning of the pattern. Toggle it.
260 */
261 beg_or_end ^= direct;
262
263 #if MAGIC_JUMP_TABLES
264 if (mcpatrn->mc_type == JMPTABLE) {
265 tbl = mcpatrn->u.jmptable;
266 jump = tbl->jump;
267 patlenadd = tbl->patlen;
268 mcpatrn++;
269 }
270 else
271 tbl = NULL;
272 #endif
273
274 /* Setup local scan pointers to global ".".
275 */
276 curline = curwp->w_dotp;
277 curoff = curwp->w_doto;
278
279 /* Scan each character until we hit the head link record.
280 */
281 while (!boundry(curline, curoff, direct)) {
282 /* Save the current position in case we need to
283 * restore it on a match, and initialize matchlen to
284 * zero in case we are doing a search for replacement.
285 */
286 matchline = curline;
287 matchoff = curoff;
288 matchlen = 0;
289
290 #if MAGIC_JUMP_TABLES
291 /*
292 * If the first thing to look for is a constant string
293 * that has been made into a jump table, use the fast
294 * scan routines. If that fails, then we have nothing
295 * to match and return immediately.
296 */
297 if (tbl != NULL) {
298 if (!fbound(tbl, patlenadd, &curline, &curoff, direct)) {
299 do
300 {
301 if (direct == FORWARD)
302 movelocalpoint(-patlenadd, &curoff, &curline);
303 else
304 movelocalpoint(patlenadd + 1, &curoff, &curline);
305
306 matchline = curline;
307 matchoff = curoff;
308
309 if ((matchlen = liteq(&curline, &curoff, direct, tbl->patrn)) > 0)
310 break;
311 } while (!fbound(tbl, jump, &curline, &curoff, direct));
312 }
313
314 if (matchlen == 0)
315 return FALSE;
316 }
317 #endif
318
319 #if WINDOW_MSWIN
320 /* to lower overhead, only 1/100 calls to longop */
321 if (--o < 0) {
322 longop(TRUE);
323 o = 100;
324 }
325 #endif
326 if (amatch(mcpatrn, direct, &curline, &curoff)) {
327 /* A SUCCESSFULL MATCH!!!
328 * Flag that we have moved,
329 * reset the global "." pointers.
330 */
331 curwp->w_markp[SEARCH_HIGHLIGHT] = matchline;
332 curwp->w_marko[SEARCH_HIGHLIGHT] = matchoff;
333 curwp->w_markp[SEARCH_HIGHLIGHT+1] = curline;
334 curwp->w_marko[SEARCH_HIGHLIGHT+1] = curoff;
335
336 curwp->w_flag |= WFMOVE;
337 if (beg_or_end == PTEND) /* at end of string */
338 {
339 curwp->w_dotp = curline;
340 curwp->w_doto = curoff;
341 }
342 else /* at beginning of string */
343 {
344 curwp->w_dotp = matchline;
345 curwp->w_doto = matchoff;
346 }
347
348 /* If we're heading in reverse, set the "match"
349 * pointers to the start of the string, for savematch().
350 */
351 if (direct == REVERSE) {
352 matchline = curline;
353 matchoff = curoff;
354 }
355
356 if (savematch() == ABORT)
357 return ABORT;
358
359 /* Continue scanning if we haven't found
360 * the nth match.
361 */
362 if (--repeats <= 0)
363 return TRUE;
364 }
365
366 /* Advance the cursor.
367 */
368 nextch(&curline, &curoff, direct);
369 }
370
371 return FALSE; /* We could not find a match.*/
372 }
373
374 /*
375 * amatch -- Search for a meta-pattern in either direction. Update
376 * the passed-in LINE and offset values if successful.
377 * Based on the recursive routine amatch() (for "anchored match")
378 * in Kernighan & Plauger's "Software Tools".
379 */
380 #if PROTO
amatch(MC * mcptr,int direct,LINE ** pcwline,int * pcwoff)381 int PASCAL NEAR amatch(MC *mcptr, int direct, LINE **pcwline, int *pcwoff)
382 #else
383 int PASCAL NEAR amatch( mcptr, direct, pcwline, pcwoff)
384 MC *mcptr;
385 int direct;
386 LINE **pcwline;
387 int *pcwoff;
388 #endif
389 {
390 LINE *curline; /* current line during scan */
391 int curoff; /* position within current line */
392 int pre_matchlen; /* matchlen before a recursive amatch() call.*/
393 int cl_matchlen; /* number of chars matched in a local closure.*/
394 int cl_min; /* minimum number of chars matched in closure.*/
395 int cl_type; /* Which closure type?.*/
396
397 /* Set up local scan pointers to ".",
398 * and set up our local character counting
399 * variable, which can correct matchlen on
400 * a failed partial match.
401 */
402 curline = *pcwline;
403 curoff = *pcwoff;
404 cl_matchlen = 0;
405
406 /*
407 * Loop through the meta-pattern, laughing all the way.
408 */
409 while (mcptr->mc_type != MCNIL) {
410 /* Is the current meta-character modified
411 * by a closure?
412 */
413 if (cl_type = (mcptr->mc_type & ALLCLOS)) {
414
415 /* Minimum number of characters that may
416 * match is 0 or 1.
417 */
418 cl_min = (cl_type == CLOSURE_1);
419
420 if (cl_type == ZEROONE)
421 {
422 if (mceq(nextch(&curline, &curoff, direct), mcptr))
423 {
424 nextch(&curline, &curoff, direct);
425 cl_matchlen++;
426 }
427 }
428 else
429 {
430 /* Match as many characters as possible
431 * against the current meta-character.
432 */
433 while (mceq(nextch(&curline, &curoff, direct), mcptr))
434 cl_matchlen++;
435 }
436
437 /* We are now at the character that made us
438 * fail. Try to match the rest of the pattern.
439 * Shrink the closure by one for each failure.
440 */
441 mcptr++;
442 matchlen += cl_matchlen;
443
444 for (;;)
445 {
446 if (cl_matchlen < cl_min) {
447 matchlen -= cl_matchlen;
448 return FALSE;
449 }
450
451 nextch(&curline, &curoff, direct ^ REVERSE);
452 pre_matchlen = matchlen;
453 if (amatch(mcptr, direct, &curline, &curoff))
454 goto success;
455 matchlen = pre_matchlen - 1;
456 cl_matchlen--;
457 }
458 }
459 else if (mcptr->mc_type == GRPBEG) {
460 group_reg[mcptr->u.group_no].r_offset = curoff;
461 group_reg[mcptr->u.group_no].r_linep = curline;
462 group_reg[mcptr->u.group_no].r_size = (direct == FORWARD)? -matchlen: matchlen;
463 }
464 else if (mcptr->mc_type == GRPEND) {
465 group_len[mcptr->u.group_no] = (direct == FORWARD)? matchlen: -matchlen;
466 }
467 else if (mcptr->mc_type == BOL) {
468 if (curoff != 0)
469 return FALSE;
470 }
471 else if (mcptr->mc_type == EOL) {
472 if (curoff != lused(curline))
473 return FALSE;
474 }
475 else if (mcptr->mc_type == BOWRD) {
476 if (!isinword(lgetc(curline, curoff)))
477 return FALSE;
478
479 if (curoff != 0 &&
480 isinword(lgetc(curline, curoff - 1)))
481 return FALSE;
482 }
483 else if (mcptr->mc_type == EOWRD) {
484 if (isinword(lgetc(curline, curoff)))
485 return FALSE;
486
487 if (curoff == 0 ||
488 !isinword(lgetc(curline, curoff - 1)))
489 return FALSE;
490 }
491 else if (mcptr->mc_type == LITSTRING) {
492 if ((pre_matchlen = liteq(&curline, &curoff, direct, mcptr->u.lstring)) == 0)
493 return FALSE;
494 matchlen += pre_matchlen;
495 }
496 else
497 {
498 /* A character to compare against
499 * the meta-character equal function.
500 * If we still match, increment the length.
501 */
502 if (!mceq(nextch(&curline, &curoff, direct), mcptr))
503 return FALSE;
504
505 matchlen++;
506 }
507
508 mcptr++;
509 } /* End of mcptr loop.*/
510
511 /* A SUCCESSFULL MATCH!!!
512 * Reset the "." pointers.
513 */
514 success:
515 *pcwline = curline;
516 *pcwoff = curoff;
517 return (TRUE);
518 }
519 #else
520
521 /*
522 * scanner -- Search for a pattern in either direction. If found,
523 * reset the "." to be at the start or just after the match string,
524 * and (perhaps) repaint the display.
525 * Fast version using simplified version of Boyer and Moore
526 * Software-Practice and Experience, vol 10, 501-506 (1980)
527 */
528 #if PROTO
scanner(int direct,int beg_or_end,int repeats)529 int PASCAL NEAR scanner(int direct, int beg_or_end, int repeats)
530 #else
531 int PASCAL NEAR scanner( direct, beg_or_end, repeats)
532 int direct;
533 int beg_or_end;
534 int repeats;
535 #endif
536 {
537 DELTA *tbl; /* structure holding the jump info */
538 char *patrn; /* string to scan for */
539 LINE *curline; /* current line during scan */
540 int curoff; /* position within current line */
541 int jump; /* next offset */
542 int patlenadd;
543
544 /* If we are going in reverse, then the 'end' is actually
545 * the beginning of the pattern. Toggle it.
546 */
547 beg_or_end ^= direct;
548
549 /* Another directional problem: if we are searching
550 * forwards, use the pattern and the jump size in
551 * deltapat. Otherwise, use the reversed pattern
552 * pattern and the jump size in tapatled.
553 */
554 if (direct == FORWARD) {
555 tbl = &deltapat;
556 patrn = deltapat.patrn;
557 patlenadd = deltapat.patlen;
558 jump = deltapat.jump;
559 }
560 else
561 {
562 tbl = &tapatled;
563 patrn = tapatled.patrn;
564 patlenadd = tapatled.patlen;
565 jump = tapatled.jump;
566 }
567
568 /* Set up local pointers to global ".".
569 */
570 curline = curwp->w_dotp;
571 curoff = curwp->w_doto;
572
573 /* Scan each character until we hit the head link record.
574 * Get the character resolving newlines, offset
575 * by the pattern length, i.e. the last character of the
576 * potential match.
577 */
578 if (!fbound(tbl, patlenadd, &curline, &curoff, direct)) {
579 do
580 {
581 /* Set up the scanning pointers, and save
582 * the current position in case we match
583 * the search string at this point.
584 */
585 if (direct == FORWARD)
586 movelocalpoint(-patlenadd, &curoff, &curline);
587 else
588 movelocalpoint(patlenadd + 1, &curoff, &curline);
589
590 matchline = curline;
591 matchoff = curoff;
592
593 if (liteq(&curline, &curoff, direct, patrn) == 0)
594 goto fail;
595
596 /* A SUCCESSFULL MATCH!!!
597 * Flag that we have moved and reset the
598 * global "." pointers.
599 */
600 curwp->w_markp[SEARCH_HIGHLIGHT] = matchline;
601 curwp->w_marko[SEARCH_HIGHLIGHT] = matchoff;
602 curwp->w_markp[SEARCH_HIGHLIGHT+1] = curline;
603 curwp->w_marko[SEARCH_HIGHLIGHT+1] = curoff;
604
605 curwp->w_flag |= WFMOVE;
606 if (beg_or_end == PTEND) /* at end of string */
607 {
608 curwp->w_dotp = curline;
609 curwp->w_doto = curoff;
610 }
611 else /* at beginning of string */
612 {
613 curwp->w_dotp = matchline;
614 curwp->w_doto = matchoff;
615 }
616
617 /* If we're heading in reverse, set the "match"
618 * pointers to the start of the string, for savematch().
619 */
620 if (direct == REVERSE) {
621 matchline = curline;
622 matchoff = curoff;
623 }
624
625 matchlen = patlenadd + 1;
626 if (savematch() == ABORT)
627 return ABORT;
628
629 /* Continue scanning if we haven't found
630 * the nth match.
631 */
632 if (--repeats <= 0)
633 return (TRUE);
634
635 fail:; /* continue to search */
636 } while (!fbound(tbl, jump, &curline, &curoff, direct));
637 }
638
639 return FALSE; /* We could not find a match */
640 }
641 #endif
642
643 /*
644 * fbound -- Return information depending on whether we have hit a boundry
645 * (and may therefore search no further) or if a trailing character
646 * of the search string has been found. See boundry() for search
647 * restrictions.
648 */
649 #if PROTO
fbound(DELTA * tbl,int jump,LINE ** pcurline,int * pcuroff,int dir)650 int PASCAL NEAR fbound(DELTA *tbl, int jump, LINE **pcurline, int *pcuroff, int dir)
651 #else
652 int PASCAL NEAR fbound( tbl, jump, pcurline, pcuroff, dir)
653 DELTA *tbl;
654 int jump;
655 LINE **pcurline;
656 int *pcuroff;
657 int dir;
658 #endif
659 {
660 int curoff;
661 LINE *curline;
662
663 curline = *pcurline;
664 curoff = *pcuroff;
665
666 if (dir == FORWARD) {
667 while (jump != 0) {
668 #if WINDOW_MSWIN
669 /* to lower overhead, only 1/100 calls to longop */
670 if (--o < 0) {
671 longop(TRUE);
672 o = 100;
673 }
674 #endif
675 if (movelocalpoint(jump, &curoff, &curline))
676 return (TRUE); /* hit end of buffer */
677
678 if (curoff == lused(curline))
679 jump = tbl->delta[(int) '\r'];
680 else
681 jump = tbl->delta[(int) lgetc(curline, curoff)];
682 }
683 }
684 else /* Reverse.*/
685 {
686 jump++; /* allow for offset in reverse */
687 while (jump != 0) {
688 #if WINDOW_MSWIN
689 /* to lower overhead, only 1/100 calls to longop */
690 if (--o < 0) {
691 longop(TRUE);
692 o = 100;
693 }
694 #endif
695 if (movelocalpoint(-jump, &curoff, &curline))
696 return (TRUE); /* hit end of buffer */
697
698 if (curoff == lused(curline))
699 jump = tbl->delta[(int) '\r'];
700 else
701 jump = tbl->delta[(int) lgetc(curline, curoff)];
702 }
703 }
704
705 *pcurline = curline;
706 *pcuroff = curoff;
707 return FALSE;
708 }
709
710 /*
711 * movelocalpoint -- Take the passed-in offset and LINE pointers, and
712 * adjust them by n characters. If boundry is hit, leave the pointers
713 * alone and return TRUE. Otherwise all went well, and return FALSE.
714 */
715 #if PROTO
movelocalpoint(int n,int * pcuroff,LINE ** pcurline)716 int PASCAL NEAR movelocalpoint(int n, int *pcuroff, LINE **pcurline)
717 #else
718 int PASCAL NEAR movelocalpoint( n, pcuroff, pcurline)
719 int n;
720 int *pcuroff;
721 LINE **pcurline;
722 #endif
723 {
724 register int spare;
725 register int curoff;
726 register LINE *curline;
727
728 curline = *pcurline;
729 curoff = *pcuroff + n;
730
731 if (n > 0) {
732 if (curline == curbp->b_linep)
733 return TRUE; /* hit end of buffer */
734
735 while ((spare = curoff - lused(curline)) > 0) {
736 curline = lforw(curline);/* skip to next line */
737 curoff = spare - 1;
738 if (curline == curbp->b_linep)
739 return (TRUE); /* hit end of buffer */
740 }
741 }
742 else {
743 while (curoff < 0) {
744 curline = lback(curline); /* skip back a line */
745 curoff += lused(curline) + 1;
746 if (curline == curbp->b_linep)
747 return (TRUE); /* hit end of buffer */
748 }
749 }
750 *pcurline = curline;
751 *pcuroff = curoff;
752 return FALSE;
753 }
754
755 /*
756 * make_delta -- Create the delta tables.
757 */
758 #if PROTO
make_delta(char * pstring,DELTA * tbl)759 VOID make_delta(char *pstring, DELTA *tbl)
760 #else
761 VOID make_delta( pstring, tbl)
762 char *pstring;
763 DELTA *tbl;
764 #endif
765 {
766 int j, jump_by, ch;
767
768 strcpy(tbl->patrn, pstring);
769
770 jump_by = strlen(pstring);
771
772 for (j = 0; j < HICHAR; j++)
773 tbl->delta[j] = jump_by;
774
775 jump_by -= 1;
776
777 /* Now put in the characters contained
778 * in the pattern, duplicating the CASE.
779 */
780 for (j = 0; j < jump_by; j++) {
781 ch = *pstring++;
782 if (is_letter(ch))
783 tbl->delta[(unsigned char) chcase(ch)] = jump_by - j;
784 tbl->delta[ch] = jump_by - j;
785 }
786
787 /* The last character (left over from the loop above) will
788 * have the pattern length, unless there are duplicates of
789 * it. Get the number to jump from the delta array, and
790 * overwrite with zeroes in delta duplicating the CASE.
791 */
792 ch = *pstring;
793 tbl->patlen = jump_by;
794 tbl->jump = jump_by + tbl->delta[ch];
795
796 if (is_letter(ch))
797 tbl->delta[(unsigned char) chcase(ch)] = 0;
798 tbl->delta[ch] = 0;
799 }
800
801 /*
802 * setjtable -- Settting up search delta forward and delta backward
803 * tables. The reverse search string and string lengths are
804 * set here, for table initialization and for substitution
805 * purposes. The default for any character to jump is the
806 * pattern length.
807 */
setjtable()808 VOID PASCAL NEAR setjtable()
809 {
810 make_delta(pat, &deltapat);
811 make_delta(strrev(strcpy((char *)tap, (char *)pat)), &tapatled);
812 }
813
814 /*
815 * eq -- Compare two characters. The "bc" comes from the buffer, "pc"
816 * from the pattern. If we are not in EXACT mode, fold out the case.
817 */
eq(bc,pc)818 int PASCAL NEAR eq(bc, pc)
819 register unsigned char bc;
820 register unsigned char pc;
821 {
822 if ((curwp->w_bufp->b_mode & MDEXACT) == 0) {
823 if (is_lower(bc))
824 bc = chcase(bc);
825
826 if (is_lower(pc))
827 pc = chcase(pc);
828 }
829
830 return (bc == pc);
831 }
832
833 /*
834 * readpattern -- Read a pattern. Stash it in apat. If it is the
835 * search string (which means that the global variable pat[]
836 * has been passed in), create the reverse pattern and the magic
837 * pattern, assuming we are in MAGIC mode (and #defined that way).
838 *
839 * Apat is not updated if the user types in an empty line. If
840 * the user typed an empty line, and there is no old pattern, it is
841 * an error. Display the old pattern, in the style of Jeff Lomicka.
842 * There is some do-it-yourself control expansion. Change to using
843 * <META> to delimit the end-of-pattern to allow <NL>s in the search
844 * string.
845 */
846 #if PROTO
readpattern(char * prompt,char apat[],int srch)847 int PASCAL NEAR readpattern(char *prompt, char apat[], int srch)
848 #else
849 int PASCAL NEAR readpattern( prompt, apat, srch)
850 char *prompt;
851 char apat[];
852 int srch;
853 #endif
854 {
855 register int status;
856 char tpat[NPAT+20];
857
858 mlprompt(prompt, apat, sterm);
859
860 /* Read a pattern. Either we get one,
861 * or we just get the META charater, and use the previous pattern.
862 * Then, if it's the search string, create the delta tables.
863 * *Then*, make the meta-pattern, if we are defined that way.
864 */
865 if ((status = nextarg(NULL, tpat, NPAT, sterm)) == TRUE) {
866 lastflag &= ~CFSRCH;
867 strcpy(apat, tpat);
868
869 if (srch)
870 setjtable();
871 }
872 else if (status == FALSE && apat[0] != 0) /* Old one */
873 status = TRUE;
874
875 #if MAGIC
876 /* Only make the meta-pattern if in magic mode, since the
877 * pattern in question might have an invalid meta combination.
878 */
879 if (status == TRUE)
880 if ((curwp->w_bufp->b_mode & MDMAGIC) == 0) {
881 mcclear();
882 rmcclear();
883 }
884 else
885 status = srch? mcstr(): rmcstr();
886 #endif
887 return (status);
888 }
889
890 /*
891 * savematch -- We found the pattern? Let's save it away.
892 */
savematch()893 int PASCAL NEAR savematch()
894 {
895 register int j;
896 REGION tmpreg;
897
898 if ((patmatch = reroom(patmatch, matchlen + 1)) == NULL) {
899 mlabort(TEXT94);
900 /* "%%Out of memory" */
901 return ABORT;
902 }
903
904 tmpreg.r_offset = matchoff;
905 tmpreg.r_linep = matchline;
906 tmpreg.r_size = matchlen;
907
908 #if MAGIC == 0
909 regtostr(patmatch, &tmpreg);
910 #else
911 /*
912 * Save the groups.
913 */
914 grpmatch[0] = regtostr(patmatch, &tmpreg);
915
916 for (j = 1; j <= group_count; j++)
917 {
918 group_reg[j].r_size += group_len[j];
919
920 if ((grpmatch[j] = reroom(grpmatch[j], group_reg[j].r_size + 1)) == NULL) {
921 mlabort(TEXT94);
922 /* "%%Out of memory" */
923 return ABORT;
924 }
925 regtostr(grpmatch[j], &group_reg[j]);
926 }
927 #endif
928 return TRUE;
929 }
930
931 /*
932 * boundry -- Return information depending on whether we may search no
933 * further. Beginning of file and end of file are the obvious
934 * cases, but we may want to add further optional boundry restrictions
935 * in future, a' la VMS EDT. At the moment, just return (TRUE) or
936 * FALSE depending on if a boundry is hit (ouch).
937 */
938 #if PROTO
boundry(LINE * curline,int curoff,int dir)939 int PASCAL NEAR boundry(LINE *curline, int curoff, int dir)
940 #else
941 int PASCAL NEAR boundry( curline, curoff, dir)
942 LINE *curline;
943 int curoff;
944 int dir;
945 #endif
946 {
947 register int border;
948
949 if (dir == FORWARD) {
950 border = (curoff == lused(curline)) &&
951 (lforw(curline) == curbp->b_linep);
952 }
953 else
954 {
955 border = (curoff == 0) &&
956 (lback(curline) == curbp->b_linep);
957 }
958 return (border);
959 }
960
961 /*
962 * nextch -- retrieve the next/previous character in the buffer,
963 * and advance/retreat the point.
964 * The order in which this is done is significant, and depends
965 * upon the direction of the search. Forward searches look at
966 * the current character and move, reverse searches move and
967 * look at the character.
968 */
969 #if PROTO
nextch(LINE ** pcurline,int * pcuroff,int dir)970 int PASCAL NEAR nextch(LINE **pcurline, int *pcuroff, int dir)
971 #else
972 int PASCAL NEAR nextch( pcurline, pcuroff, dir)
973 LINE **pcurline;
974 int *pcuroff;
975 int dir;
976 #endif
977 {
978 register LINE *curline;
979 register int curoff;
980 register int c;
981
982 curline = *pcurline;
983 curoff = *pcuroff;
984
985 if (dir == FORWARD) {
986 if (curoff == lused(curline)) /* if at EOL */
987 {
988 curline = lforw(curline); /* skip to next line */
989 curoff = 0;
990 c = '\r'; /* and return a <NL> */
991 }
992 else
993 c = lgetc(curline, curoff++); /* get the char */
994 }
995 else /* Reverse.*/
996 {
997 if (curoff == 0) {
998 curline = lback(curline);
999 curoff = lused(curline);
1000 c = '\r';
1001 }
1002 else
1003 c = lgetc(curline, --curoff);
1004 }
1005 *pcurline = curline;
1006 *pcuroff = curoff;
1007
1008 return (c);
1009 }
1010
1011 /*
1012 * liteq -- compare the string versus the current characters in the line.
1013 * Returns 0 (no match) or the number of characters matched.
1014 */
1015 #if PROTO
liteq(LINE ** curline,int * curpos,int direct,char * lstring)1016 int PASCAL NEAR liteq(LINE **curline, int *curpos, int direct, char *lstring)
1017 #else
1018 int PASCAL NEAR liteq( curline, curpos, direct, lstring)
1019 LINE **curline;
1020 int *curpos;
1021 int direct;
1022 char *lstring;
1023 #endif
1024 {
1025 LINE *scanline = *curline;
1026 int scanpos = *curpos;
1027 register int c;
1028 register int count = 0;
1029
1030 while ((c = (unsigned char)(*lstring++)) != '\0') {
1031 if (!eq(c, nextch(&scanline, &scanpos, direct)))
1032 return 0;
1033 count++;
1034 }
1035
1036 *curline = scanline;
1037 *curpos = scanpos;
1038 return count;
1039 }
1040
1041 #if MAGIC
1042 /*
1043 * mcstr -- Set up the 'magic' array. The closure symbol is taken as
1044 * a literal character when (1) it is the first character in the
1045 * pattern, and (2) when preceded by a symbol that does not allow
1046 * closure, such as beginning or end of line symbol, or another
1047 * closure symbol.
1048 *
1049 * Coding comment (jmg): yes, i know i have gotos that are, strictly
1050 * speaking, unnecessary. But right now we are so cramped for
1051 * code space that i will grab what i can in order to remain
1052 * within the 64K limit. C compilers actually do very little
1053 * in the way of optimizing - they expect you to do that.
1054 */
mcstr()1055 int PASCAL NEAR mcstr()
1056 {
1057 MC *mcptr, *rtpcm;
1058 DELTA *tbl;
1059 char *patptr;
1060 int pchr;
1061 int status = TRUE;
1062 int does_closure = FALSE;
1063 int mj = 0;
1064 int group_stack[MAXGROUPS];
1065 int stacklevel = 0;
1066
1067 /* If we had metacharacters in the MC array previously,
1068 * free up any bitmaps that may have been allocated, and
1069 * reset magical.
1070 */
1071 if (magical)
1072 mcclear();
1073
1074 mcptr = &mcpat[0];
1075 patptr = (char *)&pat[0];
1076
1077 while ((pchr = *patptr) && status) {
1078 switch (pchr) {
1079 case MC_CCL:
1080 status = cclmake(&patptr, mcptr);
1081 magical = TRUE;
1082 does_closure = TRUE;
1083 break;
1084
1085 case MC_BOL:
1086 /* If the BOL character isn't the
1087 * first in the pattern, we assume
1088 * it's a literal instead.
1089 */
1090 if (mj != 0)
1091 goto litcase;
1092
1093 mcptr->mc_type = BOL;
1094 magical = TRUE;
1095 break;
1096
1097 case MC_EOL:
1098 /* If the EOL character isn't the
1099 * last in the pattern, we assume
1100 * it's a literal instead.
1101 */
1102 if (*(patptr + 1) != '\0')
1103 goto litcase;
1104
1105 mcptr->mc_type = EOL;
1106 magical = TRUE;
1107 break;
1108
1109 case MC_ANY:
1110 mcptr->mc_type = ANY;
1111 magical = TRUE;
1112 does_closure = TRUE;
1113 break;
1114
1115 case MC_CLOSURE:
1116 case MC_CLOSURE_1:
1117 case MC_ZEROONE:
1118
1119 /* Does the closure symbol mean closure here?
1120 * If so, back up to the previous element
1121 * and indicate it is enclosed.
1122 */
1123 if (does_closure == FALSE)
1124 goto litcase;
1125 mj--;
1126 mcptr--;
1127 if (pchr == MC_CLOSURE)
1128 mcptr->mc_type |= CLOSURE;
1129 else if (pchr == MC_CLOSURE_1)
1130 mcptr->mc_type |= CLOSURE_1;
1131 else
1132 mcptr->mc_type |= ZEROONE;
1133
1134 magical = TRUE;
1135 does_closure = FALSE;
1136 break;
1137
1138 case MC_ESC:
1139
1140 /* No break between here and LITCHAR if
1141 * the next character is to be taken literally.
1142 */
1143 magical = TRUE;
1144 pchr = *++patptr;
1145 if (pchr == MC_GRPBEG) {
1146 /* Start of a group. Indicate it, and
1147 * set magical.
1148 */
1149 if (++group_count < MAXGROUPS) {
1150 mcptr->mc_type = GRPBEG;
1151 mcptr->u.group_no = group_count;
1152 group_stack[stacklevel++] = group_count;
1153 does_closure = FALSE;
1154 }
1155 else
1156 {
1157 mlwrite(TEXT221);
1158 /* "Too many groups" */
1159 status = FALSE;
1160 }
1161 break;
1162 }
1163 else if (pchr == MC_GRPEND) {
1164 /* If we've no groups to close, assume
1165 * a literal character. Otherwise,
1166 * indicate the end of a group.
1167 */
1168 if (stacklevel > 0) {
1169 mcptr->u.group_no = group_stack[--stacklevel];
1170 mcptr->mc_type = GRPEND;
1171 does_closure = FALSE;
1172 break;
1173 }
1174 }
1175 else if (pchr == MC_BOWRD) {
1176 mcptr->mc_type = BOWRD;
1177 does_closure = FALSE;
1178 break;
1179 }
1180 else if (pchr == MC_EOWRD) {
1181 mcptr->mc_type = EOWRD;
1182 does_closure = FALSE;
1183 break;
1184 }
1185 else if (pchr == '\0') {
1186 pchr = '\\';
1187 --patptr;
1188 }
1189 default:
1190 litcase:
1191 status = litmake(&patptr, mcptr);
1192 does_closure = TRUE;
1193 break;
1194 } /* End of switch.*/
1195 mcptr++;
1196 patptr++;
1197 mj++;
1198 } /* End of while.*/
1199
1200
1201 /* Close off the meta-string, then set up the reverse array,
1202 * if the status is good.
1203 *
1204 * If the status is not good, free up any bitmaps or strings,
1205 * and make mcpat[0].mc_type MCNIL.
1206 *
1207 * Please note the structure assignment - your compiler may
1208 * not like that.
1209 */
1210 mcptr->mc_type = MCNIL;
1211
1212 if (stacklevel) {
1213 status = FALSE;
1214 mlwrite(TEXT222);
1215 /* "Group not ended" */
1216 }
1217
1218 if (status) {
1219 rtpcm = &tapcm[0];
1220 while (--mj >= 0) {
1221 *rtpcm = *--mcptr;
1222
1223 if (rtpcm->mc_type == LITSTRING) {
1224 if ((rtpcm->u.lstring = copystr(mcptr->u.lstring)) == NULL) {
1225 status = FALSE;
1226 break;
1227 }
1228 strrev(rtpcm->u.lstring);
1229 }
1230
1231 rtpcm++;
1232 }
1233 rtpcm->mc_type = MCNIL;
1234 }
1235
1236 if (status) {
1237 #if MAGIC_JUMP_TABLES
1238 /*
1239 * Now see if we can use the fast jump tables instead
1240 * of a brute-force string search, if the first or
1241 * last meta-character types are strings.
1242 */
1243 if (mcpat[0].mc_type == LITSTRING)
1244 {
1245 if ((tbl = (DELTA *) room(sizeof(DELTA))) != NULL)
1246 {
1247 make_delta(mcpat[0].u.lstring, tbl);
1248 free(mcpat[0].u.lstring);
1249 mcpat[0].u.jmptable = tbl;
1250 mcpat[0].mc_type = JMPTABLE;
1251 }
1252 }
1253 if (tapcm[0].mc_type == LITSTRING)
1254 {
1255 if ((tbl = (DELTA *) room(sizeof(DELTA))) != NULL)
1256 {
1257 make_delta(tapcm[0].u.lstring, tbl);
1258 free(tapcm[0].u.lstring);
1259 tapcm[0].u.jmptable = tbl;
1260 tapcm[0].mc_type = JMPTABLE;
1261 }
1262 }
1263 #endif
1264 }
1265 else
1266 mcclear();
1267
1268 #if DEBUG_SEARCH
1269 mc_list(0,0);
1270 #endif
1271 return (status);
1272 }
1273
1274 /*
1275 * mcclear -- Free up any CCL bitmaps, and MCNIL the MC search arrays.
1276 */
mcclear()1277 VOID PASCAL NEAR mcclear()
1278 {
1279 register MC *mcptr;
1280 register int j;
1281
1282 /*
1283 * Free up any memory allocated for the meta-characters:
1284 * bitmaps, strings, or the DELTA jmptables.
1285 */
1286 mcptr = &mcpat[0];
1287 while (mcptr->mc_type != MCNIL) {
1288 if ((mcptr->mc_type == CCL) || (mcptr->mc_type == NCCL))
1289 free(mcptr->u.cclmap);
1290 else if (mcptr->mc_type == LITSTRING)
1291 free(mcptr->u.lstring);
1292 else if (mcptr->mc_type == JMPTABLE)
1293 free(mcptr->u.jmptable);
1294 mcptr++;
1295 }
1296
1297 /*
1298 * Do the same for the reverse pattern, with the exception of
1299 * of the bitmaps. The reverse pattern simply 'borrowed'
1300 * the forward pattern's bitmaps, which by now have been freed.
1301 */
1302 mcptr = &tapcm[0];
1303 while (mcptr->mc_type != MCNIL) {
1304 if (mcptr->mc_type == LITSTRING)
1305 free(mcptr->u.lstring);
1306 else if (mcptr->mc_type == JMPTABLE)
1307 free(mcptr->u.jmptable);
1308 mcptr++;
1309 }
1310 mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
1311
1312 /*
1313 * Remember that grpmatch[0] == patmatch.
1314 */
1315 for (j = 0; j < MAXGROUPS; j++) {
1316 if (grpmatch[j] != NULL) {
1317 free(grpmatch[j]);
1318 grpmatch[j] = NULL;
1319 }
1320 }
1321 patmatch = NULL;
1322 group_count = 0;
1323 magical = FALSE;
1324 }
1325
1326 /*
1327 * mceq -- meta-character equality with a character. In Kernighan & Plauger's
1328 * Software Tools, this is the function omatch(), but i felt there were
1329 * too many functions with the 'match' name already.
1330 */
1331 #if PROTO
mceq(unsigned char bc,MC * mt)1332 int PASCAL NEAR mceq(unsigned char bc, MC *mt)
1333 #else
1334 int PASCAL NEAR mceq( bc, mt)
1335 unsigned char bc;
1336 MC *mt;
1337 #endif
1338 {
1339 register int result;
1340
1341 switch (mt->mc_type & MASKCLO) {
1342 case LITCHAR:
1343 result = (unsigned char) eq(bc, (unsigned char) mt->u.lchar);
1344 break;
1345
1346 case ANY:
1347 result = (bc != '\r');
1348 break;
1349
1350 case CCL:
1351 if (!(result = biteq(bc, mt->u.cclmap))) {
1352 if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
1353 (is_letter(bc)))
1354 result = biteq(chcase(bc), mt->u.cclmap);
1355 }
1356 break;
1357
1358 case NCCL:
1359 result = !biteq(bc, mt->u.cclmap);
1360
1361 if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
1362 (is_letter(bc)))
1363 result &= !biteq(chcase(bc), mt->u.cclmap);
1364
1365 break;
1366
1367 default:
1368 mlwrite(TEXT95, mt->mc_type);
1369 /* "%%mceq: what is %d?" */
1370 result = FALSE;
1371 break;
1372 } /* End of switch.*/
1373
1374 return (result);
1375 }
1376
1377 /*
1378 * cclmake -- create the bitmap for the character class.
1379 * ppatptr is left pointing to the end-of-character-class character,
1380 * so that a loop may automatically increment with safety.
1381 */
1382 #if PROTO
cclmake(char ** ppatptr,MC * mcptr)1383 int PASCAL NEAR cclmake(char **ppatptr, MC *mcptr)
1384 #else
1385 int PASCAL NEAR cclmake( ppatptr, mcptr)
1386 char **ppatptr;
1387 MC *mcptr;
1388 #endif
1389 {
1390 EBITMAP bmap;
1391 register char *patptr;
1392 register int pchr, ochr;
1393
1394 if ((bmap = (EBITMAP) room(BMAPSIZE)) == NULL) {
1395 mlabort(TEXT94);
1396 /* "%%Out of memory" */
1397 mcptr->mc_type = MCNIL;
1398 return FALSE;
1399 }
1400
1401 memset(bmap, 0, BMAPSIZE);
1402
1403 mcptr->u.cclmap = bmap;
1404 patptr = *ppatptr;
1405 ochr = MC_CCL;
1406
1407 /*
1408 * Test the initial character(s) in ccl for
1409 * special cases - negate ccl, or an end ccl
1410 * character as a first character. Anything
1411 * else gets set in the bitmap.
1412 */
1413 if (*++patptr == MC_NCCL) {
1414 patptr++;
1415 mcptr->mc_type = NCCL;
1416 }
1417 else
1418 mcptr->mc_type = CCL;
1419
1420 if ((pchr = *patptr) == MC_ECCL) {
1421 mlwrite(TEXT96);
1422 /* "%%No characters in character class" */
1423 free(bmap);
1424 return FALSE;
1425 }
1426
1427 while (pchr != MC_ECCL && pchr != '\0') {
1428 switch (pchr) {
1429 /* Range character loses its meaning if it is
1430 * the first or last character in the class. We
1431 * also treat it as un-ordinary if the order is
1432 * wrong, e.g. "z-a".
1433 */
1434 case MC_RCCL:
1435 pchr = *(patptr + 1);
1436 if (ochr == MC_CCL || pchr == MC_ECCL ||
1437 ochr > pchr)
1438 setbit(MC_RCCL, bmap);
1439 else
1440 {
1441 do {
1442 setbit(++ochr, bmap);
1443 } while (ochr < pchr);
1444 patptr++;
1445 }
1446 break;
1447
1448 /* Note: no break between case MC_ESC and the default.
1449 */
1450 case MC_ESC:
1451 pchr = *++patptr;
1452 default:
1453 setbit(pchr, bmap);
1454 break;
1455 }
1456 ochr = pchr;
1457 pchr = *++patptr;
1458 }
1459
1460 *ppatptr = patptr;
1461
1462 if (pchr == '\0') {
1463 mlwrite(TEXT97);
1464 /* "%%Character class not ended" */
1465 free(bmap);
1466 return FALSE;
1467 }
1468 return TRUE;
1469 }
1470
1471 /*
1472 * litmake -- create the literal string from the collection of characters.
1473 * If there is only one character in the collection, then no memory
1474 * needs to be allocated.
1475 *
1476 * Please Note: If new meta-characters are added (see estruct.h) then
1477 * you will also need to update this function!
1478 */
1479 #if PROTO
litmake(char ** ppatptr,MC * mcptr)1480 int PASCAL NEAR litmake(char **ppatptr, MC *mcptr)
1481 #else
1482 int PASCAL NEAR litmake( ppatptr, mcptr)
1483 char **ppatptr;
1484 MC *mcptr;
1485 #endif
1486 {
1487 char collect[NPAT + 1];
1488 int collect_len;
1489 register int pchr;
1490 register char *patptr;
1491
1492 /*
1493 * The reason this function was called was because a literal
1494 * character was encountered, so collect it immediately.
1495 */
1496 collect[0] = *(patptr = *ppatptr);
1497 collect_len = 1;
1498
1499 /*
1500 * Now loop through the pattern, collecting characters until
1501 * we run into a meta-character.
1502 */
1503 while (pchr = *++patptr)
1504 {
1505 /*
1506 * If the current character is a closure character,
1507 * then the previous character cannot be part of the
1508 * collected string (it will be modified by closure).
1509 * Back up one, if it is not solo.
1510 */
1511 if (pchr == MC_CLOSURE || pchr == MC_CLOSURE_1 ||
1512 pchr == MC_ZEROONE)
1513 {
1514 if (collect_len > 1) {
1515 collect_len--;
1516 patptr--;
1517 }
1518 break;
1519 }
1520
1521 /*
1522 * The single-character meta-characters...
1523 */
1524 if (pchr == MC_ANY || pchr == MC_CCL ||
1525 pchr == MC_BOL || pchr == MC_EOL)
1526 break;
1527
1528 /*
1529 * See if an escaped character is part of a meta-character
1530 * or not. If not, then advance the pointer to collect the
1531 * next character, if there is a next character.
1532 */
1533 if (pchr == MC_ESC) {
1534 pchr = *(patptr + 1);
1535
1536 if (pchr == MC_GRPBEG || pchr == MC_GRPEND ||
1537 pchr == MC_BOWRD || pchr == MC_EOWRD)
1538 break;
1539
1540 if (pchr != '\0')
1541 patptr++;
1542 magical = TRUE;
1543 }
1544
1545 collect[collect_len++] = *patptr;
1546 }
1547
1548 /*
1549 * Finished collecting characters, so either make a string out
1550 * of them, or a simple character.
1551 */
1552 if (collect_len == 1) {
1553 mcptr->u.lchar = collect[0];
1554 mcptr->mc_type = LITCHAR;
1555 }
1556 else
1557 {
1558 collect[collect_len] = '\0';
1559 if ((mcptr->u.lstring = copystr(collect)) == NULL)
1560 mcptr->mc_type = MCNIL;
1561 else
1562 mcptr->mc_type = LITSTRING;
1563 }
1564 /*
1565 * Back up one so that the calling function will
1566 * increment onto the character we halted on.
1567 */
1568 *ppatptr = patptr -1;
1569 return (mcptr->mc_type != MCNIL);
1570 }
1571
1572 /*
1573 * biteq -- is the character in the bitmap?
1574 */
1575 #if PROTO
biteq(int bc,EBITMAP cclmap)1576 int PASCAL NEAR biteq(int bc, EBITMAP cclmap)
1577 #else
1578 int PASCAL NEAR biteq( bc, cclmap)
1579 int bc;
1580 EBITMAP cclmap;
1581 #endif
1582 {
1583 if ((unsigned)bc >= HICHAR)
1584 return FALSE;
1585
1586 return ( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
1587 }
1588
1589 /*
1590 * setbit -- Set a bit (ON only) in the bitmap.
1591 */
1592 #if PROTO
setbit(int bc,EBITMAP cclmap)1593 VOID PASCAL NEAR setbit(int bc, EBITMAP cclmap)
1594 #else
1595 VOID PASCAL NEAR setbit( bc, cclmap)
1596 int bc;
1597 EBITMAP cclmap;
1598 #endif
1599 {
1600 if ((unsigned)bc < HICHAR)
1601 *(cclmap + (bc >> 3)) |= BIT(bc & 7);
1602 }
1603 #endif
1604
1605 #if DEBUG_SEARCH
1606
1607 #if PROTO
mc_list(int f,int n)1608 int PASCAL NEAR mc_list(int f, int n)
1609 #else
1610 int PASCAL NEAR mc_list( f, n)
1611 int f;
1612 int n;
1613 #endif
1614 {
1615 MC *mcptr;
1616 BUFFER *patbuf; /* buffer containing pattern list */
1617 char pline[NPAT*2]; /* text buffer to hold current line */
1618 char cstr[2]; /* to turn single characters into strings.*/
1619 int status; /* return status from subcommands */
1620 int j;
1621
1622 /* prepare and clear the buffer holding the meta-character list */
1623 patbuf = bfind("[Debug Search Metacharacters]", TRUE, BFINVS);
1624 if (patbuf == NULL) {
1625 mlwrite(TEXT137);
1626 /* "Cannot create buffer" */
1627 return FALSE;
1628 }
1629
1630 patbuf->b_mode |= MDVIEW;
1631 if ((status = bclear(patbuf)) != TRUE) /* Blow old text away */
1632 return(status);
1633
1634 /* add in the header text */
1635 strcpy(pline, " Pattern = \"");
1636 strcat(pline, pat);
1637 strcat(pline, "\" (");
1638 strcat(pline, int_asc(deltapat.jump));
1639 strcat(pline, ", ");
1640 strcat(pline, int_asc(deltapat.patlen));
1641 strcat(pline, ")");
1642
1643 /* scan through the regular expression pattern */
1644 cstr[0] = cstr[1] = '\0';
1645 mcptr = &mcpat[0];
1646 for (j = 0; j < 2; j++)
1647 {
1648 if (addline(patbuf, " ") == FALSE
1649 || addline(patbuf, pline) == FALSE
1650 || addline(patbuf, "-----------------------") == FALSE
1651 || addline(patbuf, "Closure MC Type") == FALSE
1652 || addline(patbuf, "-----------------------") == FALSE)
1653 return(FALSE);
1654
1655 while (mcptr->mc_type != MCNIL) {
1656
1657 if ((mcptr->mc_type) & CLOSURE)
1658 strcpy(pline, "Zero to many ");
1659 else if ((mcptr->mc_type) & CLOSURE_1)
1660 strcpy(pline, "One to many ");
1661 else if ((mcptr->mc_type) & ZEROONE)
1662 strcpy(pline, "Optional ");
1663 else
1664 strcpy(pline, " ");
1665
1666 /* next, the meta-character type */
1667 mctype_cat(pline, (mcptr->mc_type) & MASKCLO);
1668
1669 /* and some additional information */
1670 switch ((mcptr->mc_type) & MASKCLO) {
1671 case JMPTABLE:
1672 strcat(pline, "\"");
1673 strcat(pline, mcptr->u.jmptable->patrn);
1674 strcat(pline, "\" (");
1675 strcat(pline, int_asc(mcptr->u.jmptable->jump));
1676 strcat(pline, ", ");
1677 strcat(pline, int_asc(mcptr->u.jmptable->patlen));
1678 strcat(pline, ")");
1679 break;
1680
1681 case LITSTRING:
1682 strcat(pline, "\"");
1683 strcat(pline, mcptr->u.lstring);
1684 strcat(pline, "\"");
1685 break;
1686
1687 case LITCHAR:
1688 cstr[0] = mcptr->u.lchar;
1689 strcat(pline, "\"");
1690 strcat(pline, cstr);
1691 strcat(pline, "\"");
1692 break;
1693
1694 case GRPBEG:
1695 cstr[0] = mcptr->u.group_no + '0';
1696 strcat(pline, cstr);
1697 break;
1698
1699 case GRPEND:
1700 cstr[0] = mcptr->u.group_no + '0';
1701 strcat(pline, cstr);
1702 break;
1703 }
1704
1705 /* terminate and add the built line into the buffer */
1706 if (addline(patbuf, pline) == FALSE)
1707 return(FALSE);
1708
1709 mcptr++;
1710 }
1711 /* add in the header text */
1712 strcpy(pline, " Reverse Pattern = \"");
1713 strcat(pline, tap);
1714 strcat(pline, "\" (");
1715 strcat(pline, int_asc(tapatled.jump));
1716 strcat(pline, ", ");
1717 strcat(pline, int_asc(tapatled.patlen));
1718 strcat(pline, ")");
1719
1720 mcptr = &tapcm[0];
1721 }
1722 return(wpopup(patbuf));
1723 }
1724
1725 #if PROTO
rmc_list(int f,int n)1726 int PASCAL NEAR rmc_list(int f, int n)
1727 #else
1728 int PASCAL NEAR rmc_list( f, n)
1729 int f;
1730 int n;
1731 #endif
1732 {
1733 RMC *rmcptr;
1734 BUFFER *patbuf; /* buffer containing pattern list */
1735 char pline[NPAT+32]; /* text buffer to hold current line */
1736 char cstr[2]; /* to turn single characters into strings.*/
1737 int status; /* return status from subcommands */
1738
1739 /* prepare and clear the buffer holding the meta-character list */
1740 patbuf = bfind("[Debug Replace Metacharacters]", TRUE, BFINVS);
1741 if (patbuf == NULL) {
1742 mlwrite(TEXT137);
1743 /* "Cannot create buffer" */
1744 return FALSE;
1745 }
1746
1747 patbuf->b_mode |= MDVIEW;
1748 if ((status = bclear(patbuf)) != TRUE) /* Blow old text away */
1749 return(status);
1750
1751 /* add in the header text */
1752 strcpy(pline, "Replacement Pattern = \"");
1753 strcat(pline, rpat);
1754 strcat(pline, "\"");
1755
1756 /* scan through the regular expression pattern */
1757 cstr[0] = cstr[1] = '\0';
1758 rmcptr = &rmcpat[0];
1759
1760 if (addline(patbuf, " ") == FALSE
1761 || addline(patbuf, pline) == FALSE
1762 || addline(patbuf, "-----------------------") == FALSE
1763 || addline(patbuf, "RMC Type") == FALSE
1764 || addline(patbuf, "-----------------------") == FALSE)
1765 return(FALSE);
1766
1767 while (rmcptr->mc_type != MCNIL) {
1768 mctype_cat(pline, rmcptr->mc_type);
1769
1770 /* and some additional information */
1771 switch (rmcptr->mc_type) {
1772 case LITSTRING:
1773 strcat(pline, "\"");
1774 strcat(pline, rmcptr->u.rstr);
1775 strcat(pline, "\"");
1776 break;
1777
1778 case GROUP:
1779 cstr[0] = rmcptr->u.group_no + '0';
1780 strcat(pline, cstr);
1781 break;
1782 }
1783
1784 /* terminate and add the built line into the buffer */
1785 if (addline(patbuf, pline) == FALSE)
1786 return(FALSE);
1787
1788 rmcptr++;
1789 }
1790 return(wpopup(patbuf));
1791 }
1792
1793 #if PROTO
mctype_cat(char pline[],int mc_type)1794 VOID PASCAL NEAR mctype_cat(char pline[], int mc_type)
1795 #else
1796 VOID PASCAL NEAR mctype_cat( pline, mc_type)
1797 char pline[];
1798 int mc_type;
1799 #endif
1800 {
1801 switch (mc_type) {
1802 case JMPTABLE:
1803 strcat(pline, "JMPTABLE ");
1804 break;
1805
1806 case LITSTRING:
1807 strcat(pline, "LITSTRING ");
1808 break;
1809
1810 case LITCHAR:
1811 strcat(pline, "LITCHAR ");
1812 break;
1813
1814 case ANY:
1815 strcat(pline, "ANY ");
1816 break;
1817
1818 case CCL:
1819 strcat(pline, "CCL ");
1820 break;
1821
1822 case NCCL:
1823 strcat(pline, "NCCL ");
1824 break;
1825
1826 case BOL:
1827 strcat(pline, "BOL ");
1828 break;
1829
1830 case EOL:
1831 strcat(pline, "EOL ");
1832 break;
1833
1834 case BOWRD:
1835 strcat(pline, "BOWORD ");
1836 break;
1837
1838 case EOWRD:
1839 strcat(pline, "EOWORD ");
1840 break;
1841
1842 case GRPBEG:
1843 strcat(pline, "GRPBEG ");
1844 break;
1845
1846 case GRPEND:
1847 strcat(pline, "GRPEND ");
1848 break;
1849
1850 case GROUP:
1851 strcat(pline, "GROUP ");
1852 break;
1853
1854 case DITTO:
1855 strcat(pline, "DITTO ");
1856 break;
1857
1858 default:
1859 strcat(pline, "Unknown type");
1860 break;
1861 }
1862 }
1863 #endif
1864