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