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