1 /************************************************************************
2  *	Miscellaneous routines used by procmail				*
3  *									*
4  *	Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands	*
5  *	Copyright (c) 1999-2001, Philip Guenther, The United States	*
6  *							of America	*
7  *	#include "../README"						*
8  ************************************************************************/
9 #ifdef RCS
10 static /*const*/char rcsid[]=
11  "$Id: misc.c,v 1.117 2001/06/26 08:46:48 guenther Exp $";
12 #endif
13 #include "procmail.h"
14 #include "acommon.h"
15 #include "sublib.h"
16 #include "robust.h"
17 #include "misc.h"
18 #include "pipes.h"
19 #include "common.h"
20 #include "cstdio.h"
21 #include "exopen.h"
22 #include "regexp.h"
23 #include "mcommon.h"
24 #include "goodies.h"
25 #include "locking.h"
26 #include "comsat.h"
27 #include "mailfold.h"
28 #include "lastdirsep.h"
29 #include "memblk.h"
30 #include "authenticate.h"
31 #include "variables.h"
32 #include "shell.h"
33 
34 		       /* line buffered to keep concurrent entries untangled */
elog(newt)35 void elog(newt)const char*const newt;
36 { int lnew;size_t i;static size_t lold,lmax;static char*old;
37   if(lcking&lck_LOGGING)			 /* reentered via sighandler */
38      lold=lmax=0;		       /* so give up on any buffered message */
39   i=lold+(lnew=strlen(newt));	   /* calculate additional and total lengths */
40   if(lnew&&				/* if this is not a forced flush and */
41    (lmax>=i||		      /* either we have enough room in the buffer or */
42     (MAXlogbuf>=i&&			 /* the buffer won't get too big and */
43      !nextexit)))	    /* we're not in a signal handler, then it's safe */
44    { if(i>lmax)				      /* to use or expand the buffer */
45       { char*p;size_t newmax=lmax*2;	 /* exponential expansion by default */
46 	if(i>newmax)				   /* ...unless we need more */
47 	   newmax=i;
48 	if(MINlogbuf>newmax)		    /* ...or that would be too small */
49 	   newmax=MINlogbuf;
50 	lcking|=lck_LOGGING;		      /* about to change old or lmax */
51 	if(p=lmax?frealloc(old,newmax):fmalloc(newmax))/* fragile allocation */
52 	   lmax=newmax,old=p;				/* update the values */
53 	lcking&=~lck_LOGGING;		       /* okay, they're stable again */
54 	if(!p)				      /* couldn't expand the buffer? */
55 	   goto flush;					/* then flush it now */
56       }				    /* okay, we now have enough buffer space */
57      tmemmove(old+lold,newt,lnew);		      /* append the new text */
58      lnew=0;lold=i;		    /* the text in newt is now in the buffer */
59      if(old[i-1]!='\n')			    /* if we don't need to flush now */
60 	return;						  /* then we're done */
61    }
62 flush:
63 #ifndef O_CREAT
64   lseek(STDERR,(off_t)0,SEEK_END);	  /* locking should be done actually */
65 #endif
66   if(lold)					       /* anything buffered? */
67    { rwrite(STDERR,old,lold);
68      lold=0;					 /* we never free the buffer */
69    }
70   if(lnew)
71      rwrite(STDERR,newt,lnew);
72 }
73 
ignoreterm(void)74 void ignoreterm P((void))
75 { signal(SIGTERM,SIG_IGN);signal(SIGHUP,SIG_IGN);signal(SIGINT,SIG_IGN);
76   signal(SIGQUIT,SIG_IGN);
77 }
78 
shutdesc(void)79 void shutdesc P((void))
80 { rclose(savstdout);closelog();closerc();
81 }
82 
83 /* On systems with `capabilities', setuid/setgid can fail for root! */
checkroot(c,Xid)84 void checkroot(c,Xid)const int c;const unsigned long Xid;
85 { uid_t eff;
86   if((eff=geteuid())!=ROOT_uid&&getuid()!=ROOT_uid)
87      return;
88   syslog(LOG_CRIT,"set%cid(%lu) failed with ruid/euid = %lu/%lu",c,Xid,
89    (unsigned long)getuid(),(unsigned long)eff);
90   nlog(insufprivs);
91   exit(EX_NOPERM);
92 }
93 
setids(void)94 void setids P((void))
95 { if(privileged)
96    { if(setRgid(gid)&&	/* due to these !@#$%^&*() POSIX semantics, setgid() */
97       setgid(gid))	   /* sets the saved gid as well; we can't use that! */
98 	checkroot('g',(unsigned long)gid);	 /* did setgid fail as root? */
99      setruid(uid);
100      if(setuid(uid))				     /* "This cannot happen" */
101 	checkroot('u',(unsigned long)uid);			/* Whoops... */
102      setegid(gid);
103      privileged=0;
104 #if !DEFverbose
105      verbose=0;			/* to avoid peeking in rcfiles using SIGUSR1 */
106 #endif
107    }
108 }
109 
writeerr(line)110 void writeerr(line)const char*const line;
111 { nlog(errwwriting);logqnl(line);
112 }
113 
forkerr(pid,a)114 int forkerr(pid,a)const pid_t pid;const char*const a;
115 { if(pid==-1)
116    { nlog("Failed forking");logqnl(a);
117      return 1;
118    }
119   return 0;
120 }
121 
progerr(line,xitcode,okay)122 void progerr(line,xitcode,okay)const char*const line;int xitcode,okay;
123 { charNUM(num,thepid);
124   nlog(okay?"Non-zero exitcode (":"Program failure (");
125   ltstr(0,(long)xitcode,num);elog(num);elog(okay?") from":") of");
126   logqnl(line);
127 }
128 
chderr(dir)129 void chderr(dir)const char*const dir;
130 { nlog("Couldn't chdir to");logqnl(dir);
131 }
132 
readerr(file)133 void readerr(file)const char*const file;
134 { nlog("Couldn't read");logqnl(file);
135 }
136 
buildpath(name,path,file)137 int buildpath(name,path,file)const char*name,*const path,*const file;
138 { static const char toolong[]=" path too long",
139    notabsolute[]=" is not an absolute path";
140   sgetcp=path;
141   if(readparse(buf,sgetc,2,0))
142    { syslog(LOG_CRIT,"%s%s for LINEBUF for uid \"%lu\"\n",name,toolong,
143       (unsigned long)uid);
144 bad: nlog(name);elog(toolong);elog(newline);
145      return 1;
146    }
147   if(!strchr(dirsep,buf[0]))
148    { nlog(name);elog(notabsolute);elog(newline);
149      syslog(LOG_CRIT,"%s%s for uid \"%lu\"\n",name,notabsolute,
150       (unsigned long)uid);
151      return 1;
152    }
153   if(file)
154    { char*chp=strchr(buf,'\0');
155      if(chp-buf+strlen(file)+2>linebuf)			  /* +2 for / and \0 */
156       { name="full rcfile";		  /* this should be passed in... XXX */
157 	goto bad;
158       }
159      *chp++= *MCDIRSEP_;
160      strcpy(chp,file);				      /* append the filename */
161    }
162   return 0;
163 }
164 
verboff(void)165 void verboff P((void))
166 { verbose=0;
167 #ifdef SIGUSR1
168   qsignal(SIGUSR1,verboff);
169 #endif
170 }
171 
verbon(void)172 void verbon P((void))
173 { verbose=1;
174 #ifdef SIGUSR2
175   qsignal(SIGUSR2,verbon);
176 #endif
177 }
178 
yell(a,b)179 void yell(a,b)const char*const a,*const b;		/* log if VERBOSE=on */
180 { if(verbose)
181      nlog(a),logqnl(b);
182 }
183 
184 static time_t oldtime;
185 
newid(void)186 void newid P((void))
187 { thepid=getpid();oldtime=0;
188 }
189 
zombiecollect(void)190 void zombiecollect P((void))
191 { while(waitpid((pid_t)-1,(int*)0,WNOHANG)>0);	      /* collect any zombies */
192 }
193 
nlog(a)194 void nlog(a)const char*const a;
195 { time_t newtime;
196   static const char colnsp[]=": ";
197   elog(procmailn);elog(colnsp);
198   if(verbose&&!nextexit&&oldtime!=(newtime=time((time_t*)0)))  /* don't call */
199    { charNUM(num,thepid);			  /* ctime from a sighandler */
200      elog("[");oldtime=newtime;ultstr(0,(unsigned long)thepid,num);elog(num);
201      elog("] ");elog(ctime(&oldtime));elog(procmailn);elog(colnsp);
202    }
203   elog(a);
204 }
205 
logqnl(a)206 void logqnl(a)const char*const a;
207 { elog(oquote);elog(a);elog(cquote);
208 }
209 
skipped(x)210 void skipped(x)const char*const x;
211 { if(*x)
212      nlog("Skipped"),logqnl(x);
213 }
214 
nextrcfile(void)215 int nextrcfile P((void))	/* next rcfile specified on the command line */
216 { const char*p;int rval=2;
217   while(p= *gargv)
218    { gargv++;
219      if(!strchr(p,'='))
220       { if(strlen(p)>linebuf-1)
221 	 { nlog("Excessively long rcfile path skipped\n");
222 	   continue;
223 	 }
224 	rcfile=p;
225 	return rval;
226       }
227      rval=1;			       /* not the first argument encountered */
228    }
229   return 0;
230 }
231 
tstrdup(a)232 char*tstrdup(a)const char*const a;
233 { int i;
234   i=strlen(a)+1;
235   return tmemmove(malloc(i),a,i);
236 }
237 
cstr(a,b)238 char*cstr(a,b)char*const a;const char*const b;	/* dynamic buffer management */
239 { if(a)
240      free(a);
241   return tstrdup(b);
242 }
243 
onguard(void)244 void onguard P((void))
245 { lcking|=lck_DELAYSIG;
246 }
247 
offguard(void)248 void offguard P((void))
249 { lcking&=~lck_DELAYSIG;
250   if(nextexit==1)	  /* make sure we are not inside Terminate() already */
251      elog(newline),Terminate();
252 }
253 
sterminate(void)254 static void sterminate P((void))
255 { static const char*const msg[]={"memory","fork",	  /* crosscheck with */
256    "a file descriptor","a kernel-lock"};	  /* lck_ defs in procmail.h */
257   ignoreterm();
258   if(pidchild>0)	    /* don't kill what is not ours, we might be root */
259      kill(pidchild,SIGTERM);
260   if(!nextexit)
261    { nextexit=1;nlog("Terminating prematurely");
262      if(!(lcking&lck_DELAYSIG))
263       { register unsigned i,j;
264 	if(i=(lcking&~lck__NOMSG)>>1)
265 	 { elog(whilstwfor);
266 	   for(j=0;!((i>>=1)&1);j++);
267 	   elog(msg[j]);
268 	 }
269 	elog(newline);Terminate();
270       }
271    }
272 }
273 
274 int fakedelivery;
275 
Terminate(void)276 void Terminate P((void))
277 { ignoreterm();
278   if(getpid()==thepid)
279    { const char*lstfolder;
280      if(retval!=EXIT_SUCCESS)
281       { lasttell= -1;				   /* mark it for sendcomsat */
282 	lstfolder=fakedelivery?"**Lost**":
283 	 retval==EX_TEMPFAIL?"**Requeued**":"**Bounced**";
284 	sendcomsat(lstfolder);
285       }
286      else
287       { lstfolder=tgetenv(lastfolder);
288 	sendcomsat(0);
289       }
290      logabstract(lstfolder);
291      if(!nextexit)			/* these are unsafe from sighandlers */
292       { shutdesc();
293 	exectrap(traps);
294 	fdunlock();
295       }
296      nextexit=2;unlock(&loclock);unlock(&globlock);
297    }					/* flush the logfile & exit procmail */
298   elog(empty);
299   _exit(retvl2!=EXIT_SUCCESS?retvl2:		      /* unsuccessful child? */
300    fakedelivery==2?EXIT_SUCCESS:		   /* told to throw it away? */
301    retval);					/* okay, use the real status */
302 }
303 
suspend(void)304 void suspend P((void))
305 { ssleep((unsigned)suspendv);
306 }
307 
srequeue(void)308 static void srequeue P((void))
309 { retval=EX_TEMPFAIL;sterminate();
310 }
311 
slose(void)312 static void slose P((void))
313 { fakedelivery=2;sterminate();
314 }
315 
sbounce(void)316 static void sbounce P((void))
317 { retval=EX_CANTCREAT;sterminate();
318 }
319 
setupsigs(void)320 void setupsigs P((void))
321 { qsignal(SIGTERM,srequeue);qsignal(SIGINT,sbounce);
322   qsignal(SIGHUP,sbounce);qsignal(SIGQUIT,slose);
323   signal(SIGALRM,(void(*)())ftimeout);
324 }
325 
squeeze(target)326 static void squeeze(target)char*target;
327 { int state;char*src;
328   for(state=0,src=target;;target++,src++)
329    { switch(*target= *src)
330       { case '\n':
331 	   if(state==1)
332 	      target-=2;			     /* throw out \ \n pairs */
333 	   state=2;
334 	   continue;
335 	case '\\':state=1;
336 	   continue;
337 	case ' ':case '\t':
338 	   if(state==2)					     /* skip leading */
339 	    { target--;					       /* whitespace */
340 	      continue;
341 	    }
342 	default:state=0;
343 	   continue;
344 	case '\0':;
345       }
346      break;
347    }
348 }
349 
egrepin(expr,source,len,casesens)350 char*egrepin(expr,source,len,casesens)char*expr;const char*source;
351  const long len;int casesens;
352 { if(*expr)		 /* only do the search if the expression is nonempty */
353    { source=(const char*)bregexec((struct eps*)(expr=(char*)
354       bregcomp(expr,!casesens)),(const uchar*)source,(const uchar*)source,
355       len>0?(size_t)len:(size_t)0,!casesens);
356      free(expr);
357    }
358   return (char*)source;
359 }
360 
enoughprivs(passinvk,euid,egid,uid,gid)361 int enoughprivs(passinvk,euid,egid,uid,gid)const auth_identity*const passinvk;
362  const uid_t euid,uid;const gid_t egid,gid;
363 { return euid==ROOT_uid||
364    passinvk&&auth_whatuid(passinvk)==uid||
365    euid==uid&&egid==gid;
366 }
367 
newdynstring(adrp,chp)368 const char*newdynstring(adrp,chp)struct dynstring**const adrp;
369  const char*const chp;
370 { struct dynstring*curr;size_t len;
371   curr=malloc(ioffsetof(struct dynstring,ename[0])+(len=strlen(chp)+1));
372   tmemmove(curr->ename,chp,len);curr->enext= *adrp;*adrp=curr;
373   return curr->ename;
374 }
375 
app_val_(sp,size)376 void*app_val_(sp,size)struct dyna_array*const sp;int size;
377 { if(sp->filled==sp->tspace)			    /* growth limit reached? */
378    { size_t len=(sp->tspace+=4)*size;
379      sp->vals=sp->vals?realloc(sp->vals,len):malloc(len);	   /* expand */
380    }
381   return &sp->vals[size*sp->filled++];			     /* append to it */
382 }
383 
384 			     /* lifted out of main() to reduce main()'s size */
conditions(flags,prevcond,lastsucc,lastcond,skipping,nrcond)385 int conditions(flags,prevcond,lastsucc,lastcond,skipping,nrcond)char flags[];
386  const int prevcond,lastsucc,lastcond,skipping;int nrcond;
387 { char*chp,*chp2,*startchar;double score;int scored,i,skippedempty;
388   long tobesent;static const char suppressed[]=" suppressed\n";
389   score=scored=0;
390   if(nrcond<0)		      /* assume appropriate default nr of conditions */
391      nrcond=!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC]&&!flags[ELSE_DO]&&
392       !flags[ERROR_DO];
393   startchar=themail.p;tobesent=thebody-themail.p;
394   if(flags[BODY_GREP])			       /* what needs to be egrepped? */
395      if(flags[HEAD_GREP])
396 	tobesent=filled;
397      else
398       { startchar=thebody;tobesent=filled-tobesent;
399 	goto noconcat;
400       }
401    if(!skipping)
402       concon(' ');
403 noconcat:
404    i=!skipping;						  /* init test value */
405    if(flags[ERROR_DO])
406     { i&=prevcond&&!lastsucc;
407       if(flags[ELSE_DO])
408 	 nlog(conflicting),elog("else-if-flag"),elog(suppressed),
409 	  flags[ELSE_DO]=0;
410       if(flags[ALSO_N_IF_SUCC])
411 	 nlog(conflicting),elog("also-if-succeeded-flag"),elog(suppressed),
412 	  flags[ALSO_N_IF_SUCC]=0;
413     }
414    if(flags[ELSE_DO])
415       i&=!prevcond;
416    if(flags[ALSO_N_IF_SUCC])
417       i&=lastcond&&lastsucc;
418    if(flags[ALSO_NEXT_RECIPE])
419       i=i&&lastcond;
420    for(skippedempty=0;;)
421     { skipspace();--nrcond;
422       if(!testB('*'))			    /* marks a condition, new syntax */
423 	 if(nrcond<0)		/* keep counting, for backward compatibility */
424 	  { if(testB('#'))		      /* line starts with a comment? */
425 	     { skipline();				    /* skip the line */
426 	       continue;
427 	     }
428 	    if(testB('\n'))				 /* skip empty lines */
429 	     { skippedempty=1;			 /* make a note of this fact */
430 	       continue;
431 	     }
432 	    if(skippedempty&&testB(':'))
433 	     { nlog("Missing action\n");i=2;
434 	       goto ret;
435 	     }
436 	    break;		     /* no more conditions, time for action! */
437 	  }
438       skipspace();
439       if(getlline(buf2,buf2+linebuf))
440 	 i=0;				       /* assume failure on overflow */
441       if(i)					 /* check out all conditions */
442        { int negate,scoreany;double weight,xponent,lscore;
443 	 char*lstartchar=startchar;long ltobesent=tobesent,sizecheck=filled;
444 	 for(chp=strchr(buf2,'\0');--chp>=buf2;)
445 	  { switch(*chp)		  /* strip off whitespace at the end */
446 	     { case ' ':case '\t':*chp='\0';
447 		  continue;
448 	     }
449 	    break;
450 	  }
451 	 negate=scoreany=0;lscore=score;
452 	 for(chp=buf2+1;;strcpy(buf2,buf))
453 copydone: { switch(*(sgetcp=buf2))
454 	     { case '0':case '1':case '2':case '3':case '4':case '5':case '6':
455 	       case '7':case '8':case '9':case '-':case '+':case '.':case ',':
456 		{ char*chp3;double w;
457 		  w=strtod(buf2,&chp3);chp2=chp3;
458 		  if(chp2>buf2&&*(chp2=skpspace(chp2))=='^')
459 		   { double x;
460 		     x=strtod(chp2+1,&chp3);
461 		     if(chp3>chp2+1)
462 		      { if(score>=MAX32)
463 			   goto skiptrue;
464 			xponent=x;weight=w;scored=scoreany=1;
465 			chp2=skpspace(chp3);
466 			goto copyrest;
467 		      }
468 		   }
469 		  chp--;
470 		  goto normalregexp;
471 		}
472 	       default:chp--;		     /* no special character, backup */
473 		{ if(alphanum(*(chp2=chp))==1)
474 		   { char*chp3;
475 		     while(alphanum(*++chp2));
476 		     if(!strncmp(chp3=skpspace(chp2),"??",2))
477 		      { *chp2='\0';lstartchar=themail.p;
478 			if(!chp[1])
479 			 { ltobesent=thebody-themail.p;
480 			   switch(*chp)
481 			    { case 'B':lstartchar=thebody;
482 				 ltobesent=filled-ltobesent;
483 				 goto partition;
484 			      case 'H':
485 				 goto docon;
486 			    }
487 			 }
488 			else if(!strcmp("HB",chp)||
489 			 !strcmp("BH",chp))
490 			 { ltobesent=filled;
491 docon:			   concon(' ');
492 			   goto partition;
493 			 }
494 			ltobesent=strlen(lstartchar=(char*)tgetenv(chp));
495 partition:		chp2=skpspace(chp3+2);chp++;sizecheck=ltobesent;
496 			goto copyrest;
497 		      }
498 		   }
499 		}
500 	       case '\\':
501 normalregexp:	{ int or_nocase;		/* case-distinction override */
502 		  static const struct {const char*regkey,*regsubst;}
503 		   *regsp,regs[]=
504 		    { {FROMDkey,FROMDsubstitute},
505 		      {TO_key,TO_substitute},
506 		      {TOkey,TOsubstitute},
507 		      {FROMMkey,FROMMsubstitute},
508 		      {0,0}
509 		    };
510 		  squeeze(chp);or_nocase=0;
511 		  goto jinregs;
512 		  do			   /* find special keyword in regexp */
513 		     if((chp2=strstr(chp,regsp->regkey))&&
514 		      (chp2==buf2||chp2[-1]!='\\'))		 /* escaped? */
515 		      { size_t lregs,lregk;			   /* no, so */
516 			lregk=strlen(regsp->regkey);		/* insert it */
517 			tmemmove(chp2+(lregs=strlen(regsp->regsubst)),
518 			 chp2+lregk,strlen(chp2)-lregk+1);
519 			tmemmove(chp2,regsp->regsubst,lregs);
520 			if(regsp==regs)			   /* daemon regexp? */
521 			   or_nocase=1;		     /* no case sensitivity! */
522 jinregs:		regsp=regs;		/* start over and look again */
523 		      }
524 		     else
525 			regsp++;			     /* next keyword */
526 		  while(regsp->regkey);
527 		  ;{ int igncase;
528 		     igncase=or_nocase||!flags[DISTINGUISH_CASE];
529 		     if(scoreany)
530 		      { struct eps*re;
531 			re=bregcomp(chp,igncase);chp=lstartchar;
532 			if(negate)
533 			 { if(weight&&!bregexec(re,(const uchar*)chp,
534 			    (const uchar*)chp,(size_t)ltobesent,igncase))
535 			      score+=weight;
536 			 }
537 			else
538 			 { double oweight=weight*weight;
539 			   while(weight!=0&&
540 				 MIN32<score&&
541 				 score<MAX32&&
542 				 ltobesent>=0&&
543 				 (chp2=bregexec(re,(const uchar*)lstartchar,
544 				  (const uchar*)chp,(size_t)ltobesent,
545 				  igncase)))
546 			    { score+=weight;weight*=xponent;
547 			      if(chp>=chp2)		  /* break off empty */
548 			       { if(0<xponent&&xponent<1)
549 				    score+=weight/(1-xponent);
550 				 else if(xponent>=1&&weight!=0)
551 				    score+=weight<0?MIN32:MAX32;
552 				 break;			    /* matches early */
553 			       }
554 			      ;{ volatile double nweight=weight*weight;
555 				 if(nweight<oweight&&oweight<1)
556 				    break;
557 				 oweight=nweight;
558 			       }
559 			      ltobesent-=chp2-chp;chp=chp2;
560 			    }
561 			 }
562 			free(re);
563 		      }
564 		     else				     /* egrep for it */
565 			i=!!egrepin(chp,lstartchar,ltobesent,!igncase)^negate;
566 		   }
567 		  break;
568 		}
569 	       case '$':*buf2='"';squeeze(chp);
570 		  if(readparse(buf,sgetc,2,0)&&(i=0,1))
571 		     break;
572 		  strcpy(buf2,skpspace(buf));
573 		  goto copydone;
574 	       case '!':negate^=1;chp2=skpspace(chp);
575 copyrest:	  strcpy(buf,chp2);
576 		  continue;
577 	       case '?':pwait=2;metaparse(chp);inittmout(buf);ignwerr=1;
578 		   pipin(buf,lstartchar,ltobesent,0);
579 		   resettmout();
580 		   if(scoreany&&lexitcode>=0)
581 		    { int j=lexitcode;
582 		      if(negate)
583 			 while(--j>=0&&(score+=weight)<MAX32&&score>MIN32)
584 			    weight*=xponent;
585 			 else
586 			    score+=j?xponent:weight;
587 		    }
588 		   else if(!!lexitcode^negate)
589 		      i=0;
590 		   strcpy(buf2,buf);
591 		   break;
592 	       case '>':case '<':
593 		{ long pivot;
594 		  if(readparse(buf,sgetc,2,0)&&(i=0,1))
595 		     break;
596 		  ;{ char*chp3;
597 		     pivot=strtol(buf+1,&chp3,10);chp=chp3;
598 		   }
599 		  skipped(skpspace(chp));strcpy(buf2,buf);
600 		  if(scoreany)
601 		   { double f;
602 		     if((*buf=='<')^negate)
603 			if(sizecheck)
604 			   f=(double)pivot/sizecheck;
605 			else if(pivot>0)
606 			   goto plusinfty;
607 			else
608 			   goto mininfty;
609 		     else if(pivot)
610 			f=(double)sizecheck/pivot;
611 		     else
612 			goto plusinfty;
613 		     score+=weight*tpow(f,xponent);
614 		   }
615 		  else if(!((*buf=='<'?sizecheck<pivot:sizecheck>pivot)^
616 		   negate))
617 		     i=0;
618 		}
619 	     }
620 	    break;
621 	  }
622 	 if(score>MAX32)			/* chop off at plus infinity */
623 plusinfty:  score=MAX32;
624 	 if(score<=MIN32)		       /* chop off at minus infinity */
625 mininfty:   score=MIN32,i=0;
626 	 if(verbose)
627 	  { if(scoreany)	     /* not entirely correct, but it will do */
628 	     { charNUM(num,long);
629 	       nlog("Score: ");ltstr(7,(long)(score-lscore),num);
630 	       elog(num);elog(" ");
631 	       ;{ long iscore=score;
632 		  ltstr(7,iscore,num);
633 		  if(!iscore&&score>0)
634 		     num[7-2]='+';			/* show +0 for (0,1) */
635 		}
636 	       elog(num);
637 	     }
638 	    else
639 	       nlog(i?"M":"No m"),elog("atch on");
640 	    if(negate)
641 	       elog(" !");
642 	    logqnl(buf2);
643 	  }
644 skiptrue:;
645        }
646     }
647    if(!(lastscore=score)&&score>0)			   /* save it for $= */
648       lastscore=1;					 /* round up +0 to 1 */
649    if(scored&&i&&score<=0)
650       i=0;					     /* it was not a success */
651 ret:
652    return i;
653 }
654