1/************************************************************************
2 *	Custom regular expression library, *fully* egrep compatible	*
3 *									*
4 *	Seems to be perfect.						*
5 *									*
6 *	Copyright (c) 1991-1999, S.R. van den Berg, The Netherlands	*
7 *	#include "../README"						*
8 ************************************************************************/
9#ifdef RCS
10static /*const*/char rcsid[]=
11 "$Id: regexp.c,v 1.66 2000/10/23 09:04:25 guenther Exp $";
12#endif
13#include "procmail.h"
14#include "sublib.h"
15#include "robust.h"
16#include "shell.h"
17#include "misc.h"
18#include "variables.h"
19#include "regexp.h"
20
21#define R_BEG_GROUP	'('
22#define R_OR		'|'
23#define R_END_GROUP	')'
24#define R_0_OR_MORE	'*'
25#define R_0_OR_1	'?'
26#define R_1_OR_MORE	'+'
27#define R_DOT		'.'
28#define R_SOL		'^'
29#define R_EOL		'$'
30#define R_BEG_CLASS	'['
31#define R_NOT_CLASS	'^'
32#define R_RANGE		'-'
33#define R_END_CLASS	']'
34#define R_ESCAPE	'\\'
35
36#define R_BEG_WORD	'<'
37#define R_END_WORD	'>'
38#define NO_WORD_CLASS	"[^a-zA-Z0-9_]"
39#define R_SPLIT_EXPR	'/'
40
41#define BITS_P_CHAR		8
42#define OPB			(1<<BITS_P_CHAR)
43#define DONE_NODE		(OPB<<1)
44#define DONE_MASK		(DONE_NODE-1)
45#define LOOPL_NODE		(OPB<<2)
46#define LOOPR_NODE		(OPB<<3)
47#define LOOP_MASK		(LOOPL_NODE-1)
48#define OPC_SEMPTY		OPB		      /* stack empty special */
49#define OPC_TSWITCH		(OPB+1)		      /* task switch special */
50#define OPC_DOT			(OPB+2)
51#define OPC_BOTEXT		(OPB+3)
52#define OPC_EOTEXT		(OPB+4)
53#define OPC_EPS			(OPB+5)
54#define OPC_JUMP		(OPB+6)
55#define OPC_CLASS		(OPB+7)
56#define OPC_FIN			(OPB+8)
57#define OPC_BOM			(OPB+9)
58#define OPC_FILL		(OPB+10)      /* filler opcode, not executed */
59		  /* Don't change any opcode above without checking skplen[] */
60#define bit_type		unsigned
61#define bit_bits		(sizeof(bit_type)*8)
62#define bit_index(which)	((unsigned)(which)/bit_bits)
63#define bit_mask(which)		((unsigned)1<<(unsigned)(which)%bit_bits)
64#define bit_toggle(name,which)	(name[bit_index(which)]^=bit_mask(which))
65#define bit_test(name,which)	(!!(name[bit_index(which)]&bit_mask(which)))
66#define bit_set(name,which,value)	\
67 (value?(name[bit_index(which)]|=bit_mask(which)):\
68 (name[bit_index(which)]&=~bit_mask(which)))
69#define bit_field(name,size)	bit_type name[((size)+bit_bits-1)/bit_bits]
70
71#define SZ(x)		(sizeof(struct x))
72#define Ceps		(struct eps*)
73#define geno(to,add)	((char*)(to)+(add))
74#define epso(to,add)	(Ceps((char*)(to)+(add)))
75#define ii		(aleps.topc)
76#define jj		(aleps.au.sopc)
77#define spawn		sp.awn
78
79static struct eps*r,*opcfin;
80static struct{unsigned topc;union seps au;}aleps;
81static uchar*p,*cachea,*cachep;
82static size_t cacher;
83static unsigned case_ignore,errorno;
84
85struct jump {unsigned opcj_;union {struct eps*nextj;void*Irrelevoid;} nextj_;};
86struct mchar {unsigned opcc_;struct eps*next1_;
87 struct evoi {struct eps*st_;const void*wh_;} p1_,p2_;};
88struct chclass {unsigned opc_;struct eps*next_;struct evoi pos1,pos2;
89 bit_field(c,OPB);};
90					  /* length array, used by skiplen() */
91static /*const*/char skplen[]=		   /* it SHOULD have been const, but */
92 {SZ(eps),SZ(jump),SZ(chclass),0,sizeof(union seps),0};
93				       /* some !@#$%^&*() compilers disagree */
94static void puteps(spot,to)struct eps*const spot;const struct eps*const to;
95{ spot->opc=OPC_EPS;spot->next=Ceps to;spot->spawn=0;  /* epsilon transition */
96}
97
98#define Cc(p,memb)	(((struct chclass*)(p))->memb)
99#define rAc		Cc(r,c)
100
101static void bseti(i,j)unsigned i;const int j;
102{ bit_set(rAc,i,j);			   /* mark 'i' as being in the class */
103  if(case_ignore)				  /* mark the other case too */
104   { if(i-'A'<='Z'-'A')						/* uppercase */
105	i+='a'-'A';
106     else if(i-'a'<='z'-'a')					/* lowercase */
107	i-='a'-'A';
108     else
109	return;							  /* no case */
110     bit_set(rAc,i,j);
111   }
112}
113					   /* general purpose length routine */
114static struct eps*skiplen(ep)const struct eps*const ep;
115{ return epso(ep,(ep->opc&DONE_MASK)<OPC_EPS?
116   SZ(mchar):skplen[(ep->opc&DONE_MASK)-OPC_EPS]);
117}
118
119static int por P((const struct eps*const e));
120
121static void psimp(e)const struct eps*const e;
122{ switch(*p)
123   { case R_BEG_GROUP:p++;			  /* not so simple after all */
124	if(por(e))
125	   errorno=1;
126	return;
127     case R_BEG_CLASS:					   /* a simple class */
128      { unsigned i,j=R_NOT_CLASS==*++p;
129	if(e)
130	 { r->opc=OPC_CLASS;r->next=Ceps e;Cc(r,pos1.st_)=Cc(r,pos2.st_)=0;
131	   i=maxindex(rAc);
132	   do rAc[i]=j?~0:0;			     /* preset the bit field */
133	   while(i--);
134	 }
135	if(j)					  /* skip the 'not' modifier */
136	 { p++;
137	   if(e)
138	      bit_toggle(rAc,'\n');
139	 }
140	if(*p==R_END_CLASS)	  /* right at the start, cannot mean the end */
141	 { p++;
142	   if(e)
143	      i=R_END_CLASS,bit_toggle(rAc,R_END_CLASS);
144	 }
145	else if(*p==R_RANGE)				/* take it literally */
146	 { p++;
147	   if(e)
148	      i=R_RANGE,bit_toggle(rAc,R_RANGE);
149	 }
150	for(;;p++)
151	 { switch(*p)
152	    { case R_END_CLASS:p++;
153	      case '\0':r=epso(r,SZ(chclass));
154		 return;
155	      case R_RANGE:
156		 switch(*++p)
157		  { default:
158		       if(e)
159			  while(++i<*p)		    /* mark all in the range */
160			     bseti(i,!j);
161		       break;
162		    case '\0':case R_END_CLASS:p--;		/* literally */
163		  }
164	    }
165	   if(e)
166	      bseti(i= *p,!j);		      /* a normal character, mark it */
167	 }
168      }
169     case '\0':
170	return;
171     case R_DOT:			 /* matches everything but a newline */
172	if(e)
173	 { r->opc=OPC_DOT;
174	   goto fine;
175	 }
176	goto fine2;
177     case R_SOL:			      /* match a newline (in effect) */
178	if(p[1]==R_SOL)
179	 { p++;
180	   if(e)
181	    {  r->opc=e==opcfin?OPC_EOTEXT:OPC_BOTEXT;
182	       goto fine;
183	    }
184	 }
185	else
186     case R_EOL:
187	   if(e)
188	    { r->opc='\n';
189	      goto fine;
190	    }
191	goto fine2;
192     case R_ESCAPE:					  /* quote something */
193	switch(*++p)
194	 { case R_SPLIT_EXPR:
195	      if(e)
196		 r->opc=OPC_BOM;
197	      r=epso(r,sizeof(union seps));
198	      goto fine3;
199	   case R_BEG_WORD:case R_END_WORD:
200	    { uchar*pold=p;
201	      p=(uchar*)NO_WORD_CLASS;psimp(e);p=pold+1;
202	      if(e)
203		 bit_toggle(Cc(epso(r,-(int)SZ(chclass)),c),'\n');
204	      return;
205	    }
206	   case '\0':p--;				 /* nothing to quote */
207	 }
208   }
209  if(e)						      /* a regular character */
210   { r->opc=case_ignore&&(unsigned)*p-'A'<='Z'-'A'?*p+'a'-'A':*p;
211fine:
212     r->next=Ceps e;Cc(r,pos1.st_)=Cc(r,pos2.st_)=0;
213   }
214fine2:
215  r=epso(r,SZ(mchar));
216fine3:
217  p++;
218}
219
220#define EOS(x)	(jj?Ceps e:(x))
221
222static int endgroup(p)register const uchar*const p;
223{ switch(*p)
224   { case R_OR:case R_END_GROUP:case '\0':
225	return 1;
226   }
227  return 0;
228}
229
230static void pnorm(e)const struct eps*const e;
231{ void*pold;struct eps*rold;
232  for(;;)
233   { pold=p;rold=r;psimp(Ceps 0);ii= *p;		    /* skip it first */
234     if(endgroup(p))
235      { if(e)
236	   p=pold,r=rold,psimp(e);
237	return;
238      }
239     jj=endgroup(p+1);
240     if(e)
241	p=pold,pold=r;
242     switch(ii)			   /* check for any of the postfix operators */
243      { case R_0_OR_MORE:r++;
244	   if(e)			  /* first an epsilon, then the rest */
245	      puteps(rold,EOS(r)),r=rold+1,psimp(rold);
246	   goto incagoon;
247	case R_1_OR_MORE:				   /* first the rest */
248	   if(e)				      /* and then an epsilon */
249	    { puteps(r,rold);
250	      if(jj)
251		 (r+1)->opc=OPC_JUMP,(r+1)->next=Ceps e;
252	      r=rold;psimp(Ceps pold);
253	    }
254	   r++;
255	   if(endgroup(p+1))
256	      r=epso(r,SZ(jump));
257	   goto incagoon;
258	case R_0_OR_1:r++;
259	   if(e)			  /* first an epsilon, then the rest */
260	      puteps(rold,r=EOS(r)),pold=r,r=rold+1,psimp(Ceps pold);
261incagoon:  if(endgroup(++p))		/* at the end of this group already? */
262	      return;
263	   continue;				 /* regular end of the group */
264      }
265     if(e)			/* no fancy postfix operators, plain vanilla */
266	r=rold,psimp(Ceps pold);
267   }
268}
269
270static int por(e)const struct eps*const e;
271{ uchar*pvold;struct eps*rvold;
272  if(!e)
273   { rvold=r;
274     if(cachea==(pvold=p))
275      { p=cachep;r=epso(rvold,cacher);
276	goto ret0;
277      }
278   }
279  for(;;)
280   { uchar*pold;struct eps*rold;
281     for(pold=p,rold=r;;)
282      { switch(*p)
283	 { default:
284	      pnorm(Ceps 0);r=rold;		      /* still in this group */
285	      continue;
286	   case '\0':case R_END_GROUP:	       /* found the end of the group */
287	      if(p==pold)				 /* empty 'or' group */
288	       { if(e)
289		    r->opc=OPC_JUMP,r->next=Ceps e;
290		 r=epso(r,SZ(jump));
291	       }
292	      else
293		 p=pold,pnorm(e);			/* normal last group */
294	      if(!e)
295	       { if(*p)
296		    p++;
297		 cachea=pvold;cachep=p;cacher=(char*)r-(char*)rvold;
298		 goto ret0;
299	       }
300	      if(*p)
301	       { p++;
302ret0:		 return 0;
303	       }
304	      return 1;
305	   case R_OR:r++;
306	      if(p==pold)				 /* empty 'or' group */
307	       { if(e)
308		    puteps(rold,e);			  /* special epsilon */
309	       }
310	      else
311	       { p=pold;pnorm(e);	      /* normal 'or' group, first an */
312		 if(e)				   /* epsilon, then the rest */
313		    puteps(rold,r);
314	       }
315	      p++;
316	 }
317	break;
318      }
319   }
320}
321		  /* go down recursively, mark loopbacks on the way up again */
322static struct eps*maxback(down)struct eps*down;
323{ ii=0;				   /* didn't find a loop at this level (yet) */
324  for(;;)
325   { switch(down->opc&LOOP_MASK)			/* chase JUMP chains */
326      { default:
327	   goto ret0;				 /* oops, not an EPS, return */
328	case OPC_JUMP:down->opc=OPC_JUMP|DONE_NODE;	/* mark them as used */
329	case OPC_JUMP|DONE_NODE:down=down->next;
330	   continue;
331	case OPC_EPS|DONE_NODE:ii=1;   /* used EPS found, return loop number */
332	   return down->spawn==Ceps&aleps?down:down->spawn;
333	case OPC_EPS:;			/* unused EPS found, the work starts */
334      }
335     break;
336   }
337  if(!down->spawn)	 /* has it been visited (belongs to previous group?) */
338   { struct eps*left;					/* no, so process it */
339     down->opc=OPC_EPS|DONE_NODE;down->spawn=Ceps&aleps;     /* mark as used */
340     left=maxback(down->next);		   /* init loop no. and recurse left */
341     if(ii)				    /* loop found directly below us? */
342	down->opc|=LOOPL_NODE;				 /* mark a left-loop */
343     ;{ struct eps*right;		 /* recurse right, take the smallest */
344	if((right=maxback(down+1))&&(char*)left>(char*)right)	 /* loop no. */
345	   left=right;
346      }
347     if(ii)				       /* loop found directly below? */
348      { down->opc|=LOOPR_NODE;				/* mark a right-loop */
349	if(!(down->opc&LOOPL_NODE))    /* if we didn't also have a left-loop */
350	   ii=0;		/* we tell our predecessor we are not a loop */
351      }
352     if(!left)					    /* found no loop at all? */
353      { down->spawn=down;	     /* then give ourselves our own loop no. */
354	goto ret0;
355      }
356     if((down->spawn=left)!=down)     /* save the loop no., check if it's us */
357	return left;			       /* if not, pass the number up */
358   }				     /* otherwise we are the end of the loop */
359ret0:
360  return 0;					       /* no loop whatsoever */
361}
362
363struct eps*bregcomp(a,ign_case)const char*const a;const unsigned ign_case;
364{ struct eps*st;size_t i;
365  skplen[OPC_FILL-OPC_EPS]=SZ(eps)-ioffsetof(struct eps,sp);  /* a constant! */
366  errorno=0;p=(uchar*)a;case_ignore=ign_case;r=Ceps&aleps;cachea=0;
367  por(Ceps 0);st=r=malloc((i=(char*)r-(char*)&aleps)+sizeof r->opc);
368  p=(uchar*)a;		       /* first a trial run, determine memory needed */
369  if(!por(opcfin=epso(st,i)))				   /* really compile */
370     errorno=1;
371  r->opc=OPC_FIN;			     /* by now r should be == opcfin */
372  if(errorno)
373     nlog("Invalid regexp"),logqnl(a);
374  for(r=st;;st=skiplen(st))		 /* simplify the compiled code (i.e. */
375     switch(st->opc)		      /* take out cyclic epsilon references) */
376      { case OPC_FIN:
377	   return r;						 /* finished */
378	case OPC_EPS:		     /* check for any closed epsilon circles */
379	   if(!st->spawn)			   /* they can't be executed */
380	    { maxback(st);     /* if not visited yet, recurse and mark loops */
381	      ;{ register struct eps*i;
382		 for(i=r;;i=skiplen(i))		 /* search the whole program */
383		  { switch(i->opc&LOOP_MASK)
384		     { default:				/* renumber regulars */
385			{ register struct eps*f;		/* if needed */
386			  if(((f=i->next)->opc&DONE_MASK)==OPC_EPS&&f->spawn)
387			   { for(;f->spawn!=f;f=f->spawn);   /* search start */
388			     i->next=f;				  /* of loop */
389			   }
390			}	       /* spare the used nodes in this group */
391		       case OPC_EPS|DONE_NODE:case OPC_JUMP|DONE_NODE:
392		       case OPC_FILL:case OPC_BOM:
393			  continue;
394		       case OPC_FIN:;
395		     }
396		    break;
397		  }
398	       }
399	      ;{ register struct eps*i;
400		 for(i=r;;i=skiplen(i))		 /* search the whole program */
401		  { switch(i->opc)	  /* unmark/transform the used nodes */
402		     { case OPC_EPS|DONE_NODE|LOOPL_NODE:i->next=i+1;
403		       case OPC_EPS|DONE_NODE|LOOPR_NODE:i->sp.sopc=OPC_FILL;
404		       case OPC_JUMP|DONE_NODE:i->opc=OPC_JUMP;
405			  continue;
406		       case OPC_EPS|DONE_NODE|LOOPL_NODE|LOOPR_NODE:
407		       case OPC_EPS|DONE_NODE:i->opc=OPC_EPS;
408		       default:
409			  continue;
410		       case OPC_FIN:;
411		     }
412		    break;
413		  }
414	       }
415	    }
416      }
417}
418
419#define XOR1		\
420 (ioffsetof(struct chclass,pos1)^ioffsetof(struct chclass,pos2))
421#define PC(thiss,t)	(((struct evoi*)geno(thiss,t))->st_)
422#define PCp(thiss,t)	(((struct evoi*)geno(thiss,t))->wh_)
423#define PcP(reg)	(*(const void**)\
424 geno(reg,ioffsetof(struct evoi,wh_)-ioffsetof(struct evoi,st_)))
425
426static struct mchar tswitch={OPC_TSWITCH,Ceps&tswitch};
427
428static struct eps*cleantail(start,thiss,th1)const char*const start;
429 register struct eps*thiss;const unsigned th1;
430{ register struct eps**reg,*save=Ceps&tswitch,*oldthis;
431  while(thiss= *(reg= &PC(oldthis=thiss,th1)))	   /* wipe out list till you */
432     if(start<(char*)PcP(reg))
433	*reg=0;						    /* reach tswitch */
434     else
435	*reg=save,save=oldthis;
436  return save;
437}
438
439char*bregexec(code,text,str,len,ign_case)struct eps*code;
440 const uchar*const text;const uchar*str;size_t len;unsigned ign_case;
441{ register struct eps*reg,*stack,*other,*thiss;unsigned i,th1,ot1;
442  struct eps*initcode;const char*eom,*pend;
443  static struct eps sempty={OPC_SEMPTY,&sempty};
444  static const struct jump nop={OPC_FILL};
445  sempty.spawn= &sempty;			      /* static initialisers */
446  ign_case=ign_case?~(unsigned)0:0;eom=0;stack= &sempty;initcode=code;
447  th1=ioffsetof(struct chclass,pos1);ot1=ioffsetof(struct chclass,pos2);
448  other=Ceps&tswitch;pend=(const char*)str+len+1;	     /* two past end */
449  if(str--==text||*str=='\n')
450     goto begofline;	      /* make sure any beginning-of-line-hooks catch */
451  if(!len)
452   { str++;
453begofline:
454     i='\n';len++;
455     if(initcode->opc!=OPC_BOTEXT)
456	goto setups;
457     reg=initcode;initcode=Ceps&nop;thiss=Ceps&tswitch;
458     goto dobotext;
459   }
460  do
461   { i= *++str;				 /* get the next real-text character */
462     if(i-'A'<='Z'-'A')
463	i+=ign_case&'a'-'A';		     /* transmogrify it to lowercase */
464setups:					     /* switch this & other pc-stack */
465     th1^=XOR1;ot1^=XOR1;thiss=other;other=Ceps&tswitch;reg=initcode; /* pop */
466     for(;;thiss=PC(reg=thiss,th1),PC(reg,th1)=0,reg=reg->next)	 /* pc-stack */
467      { for(;;reg=stack->next,stack=stack->spawn)     /* pop from work-stack */
468	   for(;;)
469	    { switch(reg->opc-OPB)
470	       { default:
471		    if(i==reg->opc)		  /* regular character match */
472		       goto yep;
473		    break;	    /* push spawned branch on the work-stack */
474		 case OPC_EPS-OPB:reg->spawn=stack;reg=(stack=reg)+1;
475		    continue;
476		 case OPC_JUMP-OPB:reg=reg->next;
477		    continue;
478		 case OPC_BOM-OPB:
479		    goto foundbom;
480		 case OPC_FILL-OPB:		/* nop, nothing points at it */
481		    if(thiss==Ceps&tswitch)
482		       goto nomatch;	     /* so the stack is always empty */
483		 case OPC_SEMPTY-OPB:
484		    goto empty_stack;
485		 case OPC_TSWITCH-OPB:
486		    goto pcstack_switch;
487		 case OPC_EOTEXT-OPB:
488		    if(ign_case==2)		     /* only at the very end */
489		 case OPC_FIN-OPB:
490		       goto nobom;
491		 case OPC_BOTEXT-OPB:
492dobotext:	    if(str<text)	       /* only at the very beginning */
493		       goto yep;
494		    break;
495		 case OPC_CLASS-OPB:
496		    if(bit_test(((struct chclass*)reg)->c,i))
497		       goto yep;		       /* character in class */
498		    break;
499		 case OPC_DOT-OPB:			     /* dot-wildcard */
500		    if(i!='\n')
501yep:		       if(!PC(reg,ot1))		     /* state not yet pushed */
502			  PC(reg,ot1)=other,PCp(other=reg,ot1)=pend;
503	       }
504	      break;
505	    }
506empty_stack:;					  /* the work-stack is empty */
507      }
508pcstack_switch:;				   /* this pc-stack is empty */
509   }
510  while(--len);					     /* still text to search */
511  goto wrapup;
512  ;{ const char*start,*bom;
513     do
514      { i= *++str;			 /* get the next real-text character */
515	if(i-'A'<='Z'-'A')
516	   i+=ign_case&'a'-'A';		     /* transmogrify it to lowercase */
517	th1^=XOR1;ot1^=XOR1;start=pend;thiss=other;other=Ceps&tswitch;
518	reg=initcode;
519	for(;;							 /* pc-stack */
520	 thiss=PC(reg=thiss,th1),PC(reg,th1)=0,start=PCp(reg,th1),
521	 reg=reg->next)
522	 { for(;;reg=stack->next,stack=stack->spawn)  /* pop from work-stack */
523	      for(;;)
524	       { switch(reg->opc-OPB)
525		  { default:
526		       if(i==reg->opc)		  /* regular character match */
527			  goto Yep;
528		       break;	    /* push spawned branch on the work-stack */
529		    case OPC_EPS-OPB:reg->spawn=stack;reg=(stack=reg)+1;
530		       continue;
531		    case OPC_JUMP-OPB:reg=reg->next;
532		       continue;
533		    case OPC_BOM-OPB:
534		       if(!eom)
535foundbom:		  start=(const char*)str;
536		       reg=epso(reg,sizeof(union seps));
537		       continue;
538		    case OPC_FILL-OPB:		/* nop, nothing points at it */
539		       if(thiss==Ceps&tswitch)
540			  goto checkmatch;   /* so the stack is always empty */
541		    case OPC_SEMPTY-OPB:
542		       goto Empty_stack;
543		    case OPC_TSWITCH-OPB:
544		       goto Pcstack_switch;
545		    case OPC_EOTEXT-OPB:
546		       if(ign_case==2)		     /* only at the very end */
547		    case OPC_FIN-OPB:
548			{ if(start<pend)		       /* any match? */
549			   { thiss=cleantail(bom=start,thiss,th1);
550			     other=cleantail(start,other,ot1);
551			     eom=(const char*)str;initcode=Ceps&nop;
552			     break;
553			   }			      /* reset the automaton */
554nobom:			  cleantail(--pend,thiss,th1);
555			  cleantail(pend,other,ot1);
556			  return (char*)str;	       /* one past the match */
557			}
558		    case OPC_BOTEXT-OPB:
559		       if(str<text)	       /* only at the very beginning */
560			  goto Yep;
561		       break;
562		    case OPC_CLASS-OPB:
563		       if(bit_test(((struct chclass*)reg)->c,i))
564			  goto Yep;		       /* character in class */
565		       break;
566		    case OPC_DOT-OPB:			     /* dot-wildcard */
567		       if(i!='\n')
568Yep:			  if(!PC(reg,ot1))	     /* state not yet pushed */
569			   { PC(reg,ot1)=other;other=reg;   /* push location */
570earlier:		     PCp(reg,ot1)=start;      /* onto other pc-stack */
571			   }
572			  else if(start<(char*)PCp(reg,ot1))
573			     goto earlier;
574		  }
575		 break;
576	       }
577Empty_stack:;					  /* the work-stack is empty */
578	 }
579Pcstack_switch:;				   /* this pc-stack is empty */
580      }
581     while(--len);				     /* still text to search */
582wrapup:
583     switch(ign_case)
584      { case 0:case ~(unsigned)0:ign_case=1;i='\n';	   /* just finished? */
585	case 2:ign_case++;str++;len=1;th1^=XOR1;ot1^=XOR1;start=pend;
586	   thiss=other;other=Ceps&tswitch;
587	   goto Empty_stack;			 /* check if we just matched */
588      }
589checkmatch:
590     if(eom)
591      { static const char match[]=MATCHVAR,amatch[]=AMATCHVAR;char*q;
592	if(bom<(char*)text)
593	   bom=(const char*)text;
594	if(eom>--pend)
595	   eom=pend;
596	len=eom>bom?eom-bom:0;
597	if(getenv(match)==(const char*)text)	     /* anal retentive match */
598	   tmemmove(q=(char*)text,bom,len),q[len]='\0',bom=q;
599	else
600	 { char*p;
601	   primeStdout(amatch);p=realloc(Stdout,(Stdfilled+=len)+1);
602	   tmemmove(q=p+Stdfilled-(int)len,bom,len);retbStdout(p);
603	 }
604	yell("Matched",q);
605      }
606   }
607nomatch:
608  return (char*)eom;						   /* match? */
609}
610