1/************************************************************************ 2 * Custom standard-io library * 3 * * 4 * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * 5 * #include "../README" * 6 ************************************************************************/ 7#ifdef RCS 8static /*const*/char rcsid[]= 9 "$Id: cstdio.c,v 1.52 2000/11/22 01:29:56 guenther Exp $"; 10#endif 11#include "procmail.h" 12#include "robust.h" 13#include "misc.h" 14#include "lmtp.h" 15#include "variables.h" 16#include "shell.h" 17#include "cstdio.h" 18 19static uchar rcbuf[STDBUF],*rcbufp,*rcbufend; /* buffer for custom stdio */ 20static off_t blasttell; 21static struct dyna_array inced; /* includerc stack */ 22struct dynstring*incnamed; 23 24static void refill(offset)const int offset; /* refill the buffer */ 25{ int ret=rread(rc,rcbuf,STDBUF); 26 if(ret>0) 27 { rcbufend=rcbuf+ret; 28 rcbufp=rcbuf+offset; /* restore position */ 29 } 30 else 31 { rcbufend=rcbuf; 32 rcbufp=rcbuf+1; /* looks like EOF */ 33 } 34} 35 36void pushrc(name)const char*const name; /* open include rcfile */ 37{ if(*name&&strcmp(name,devnull)) 38 { struct stat stbuf; 39 if(stat(name,&stbuf)||!S_ISREG(stbuf.st_mode)) 40 goto rerr; 41 if(stbuf.st_size) /* only if size>0 */ 42 { app_vali(inced,rcbufp?rcbufp-rcbuf:0); /* save old */ 43 app_valo(inced,blasttell);app_vali(inced,ifdepth);/* position, brace */ 44 app_vali(inced,rc); /* depth & fd */ 45 ifdepth=ifstack.filled; /* new stack depth */ 46 if(bopen(name)<0) /* try to open the new one */ 47 { poprc(); /* we couldn't, so restore rc */ 48rerr: readerr(name); 49 } 50 } 51 } 52} 53 54void changerc(name)const char*const name; /* change rcfile */ 55{ if(!*name||!strcmp(name,devnull)) 56pr:{ ifstack.filled=ifdepth; /* lose all the braces to avoid a warning */ 57 rclose(rc);rcbufp=rcbufend+1; /* make it look like EOF */ 58 return; 59 } 60 if(!strcmp(name,incnamed->ename)) /* just restart this one */ 61 lseek(rc,0,SEEK_SET),refill(0); 62 else 63 { struct stat stbuf;int orc;uchar*orbp,*orbe;struct dynstring*dp; 64 if(stat(name,&stbuf)||!S_ISREG(stbuf.st_mode)) 65rerr: { readerr(name); /* skip irregularities */ 66 return; 67 } 68 if(!stbuf.st_size) /* avoid opening trivial rcfiles */ 69 goto pr; 70 if(orbp=rcbufp,orbe=rcbufend,orc=rc,bopen(name)<0) 71 { rcbufp=orbp;rcbufend=orbe;rc=orc; /* restore state */ 72 goto rerr; 73 } 74 rclose(orc); /* success! drop the old and */ 75 if(dp=incnamed->enext) /* fixup the name list */ 76 incnamed->enext=dp->enext,free(dp); 77 } 78 ifstack.filled=ifdepth; /* close all the braces */ 79} 80 81void duprcs P((void)) /* `duplicate' all the fds of opened rcfiles */ 82{ size_t i;struct dynstring*dp; 83 dp=incnamed;rclose(rc); 84 if(0>(rc=ropen(dp->ename,O_RDONLY,0))) /* first reopen the current one */ 85 goto dupfailed; 86 lseek(rc,blasttell+STDBUF,SEEK_SET); /* you'll never know the difference */ 87 for(i=inced.filled;dp=dp->enext,i;i-=3) 88 { int fd; 89 rclose(acc_vali(inced,--i)); 90 if(0>(fd=ropen(dp->ename,O_RDONLY,0))) /* reopen all (nested) others */ 91dupfailed: /* oops, file disappeared */ 92 nlog("Lost"),logqnl(dp->ename),exit(EX_NOINPUT); /* panic */ 93 acc_vali(inced,i)=fd; /* new & improved fd, decoupled from */ 94 } /* fd in the parent */ 95} 96 97static void closeonerc P((void)) 98{ struct dynstring*last; 99 if(rc>=0) 100 rclose(rc),rc= -1,last=incnamed,incnamed=last->enext,free(last); 101} 102 103int poprc P((void)) 104{ closeonerc(); /* close it in any case */ 105 if(ifstack.filled>ifdepth) /* force the matching of braces */ 106 ifstack.filled=ifdepth,nlog("Missing closing brace\n"); 107 if(!inced.filled) /* include stack is empty? */ 108 return 0; /* restore rc, seekpos, prime rcbuf and restore rcbufp */ 109 rc=acc_vali(inced,--inced.filled); 110 ifdepth=acc_vali(inced,--inced.filled); 111 blasttell=lseek(rc,acc_valo(inced,--inced.filled),SEEK_SET); 112 refill(acc_vali(inced,--inced.filled)); 113 return 1; 114} 115 116void closerc P((void)) /* {while(poprc());} */ 117{ while(closeonerc(),inced.filled) 118 rc=acc_vali(inced,inced.filled-1),inced.filled-=4; 119 ifstack.filled=ifdepth=0; 120} 121 /* destroys buf2 */ 122int bopen(name)const char*const name; /* my fopen */ 123{ rcbufp=rcbufend=0;rc=ropen(name,O_RDONLY,0); 124 if(rc>=0) 125 { char*md;size_t len; /* if it's a relative name and an absolute $MAILDIR */ 126 if(!strchr(dirsep,*name)&& 127 *(md=(char*)tgetenv(maildir))&& 128 strchr(dirsep,*md)&& 129 (len=strlen(md))+strlen(name)+2<linebuf) 130 { strcpy(buf2,md);*(md=buf2+len)= *dirsep;strcpy(++md,name); 131 md=buf2; /* then prepend $MAILDIR */ 132 } 133 else 134 md=(char*)name; /* pick the original otherwise */ 135 newdynstring(&incnamed,md); 136 } 137 return rc; 138} 139 140int getbl(p,end)char*p,*end; /* my gets */ 141{ int i,overflow=0;char*q; 142 for(q=p,end--;;) 143 { switch(i=getb()) 144 { case '\n':case EOF:*q='\0'; 145 return overflow?-1:p!=q; /* did we read anything at all? */ 146 } 147 if(q==end) /* check here so that a trailing backslash won't be lost */ 148 q=p,overflow=1; 149 *q++=i; 150 } 151} 152 153int getb P((void)) /* my fgetc */ 154{ if(rcbufp==rcbufend) /* refill */ 155 blasttell=tell(rc),refill(0); 156 return rcbufp<rcbufend?(int)*rcbufp++:EOF; 157} 158 159void ungetb(x)const int x; /* only for pushing back original characters */ 160{ if(x!=EOF) 161 rcbufp--; /* backup */ 162} 163 164int testB(x)const int x; /* fgetc that only succeeds if it matches */ 165{ int i; 166 if((i=getb())==x) 167 return 1; 168 ungetb(i); 169 return 0; 170} 171 172int sgetc P((void)) /* a fake fgetc for a string */ 173{ return *sgetcp?(int)*(uchar*)sgetcp++:EOF; 174} 175 176int skipspace P((void)) 177{ int any=0; 178 while(testB(' ')||testB('\t')) 179 any=1; 180 return any; 181} 182 183void skipline P((void)) 184{ for(;;) /* skip the rest of the line */ 185 switch(getb()) 186 { default: 187 continue; 188 case '\n':case EOF: 189 return; 190 } 191} 192 193int getlline(target,end)char*target,*end; 194{ char*chp2;int overflow; 195 for(overflow=0;;*target++='\n') 196 switch(getbl(chp2=target,end)) /* read line-wise */ 197 { case -1:overflow=1; 198 case 1: 199 if(*(target=strchr(target,'\0')-1)=='\\') 200 { if(chp2!=target) /* non-empty line? */ 201 target++; /* then preserve the backslash */ 202 if(target>end-2) /* space enough for getbl? */ 203 target=end-linebuf,overflow=1; /* toss what we have */ 204 continue; 205 } 206 case 0: 207 if(overflow) 208 { nlog(exceededlb);setoverflow(); 209 } 210 return overflow; 211 } 212} 213 214#ifdef LMTP 215static int origfd= -1; 216 217/* flush the input buffer and switch to a new input fd */ 218void pushfd(fd)int fd; 219{ origfd=rc;rc=fd; 220 rcbufend=rcbufp; 221} 222 223/* restore the original input fd */ 224static int popfd P((void)) 225{ rclose(rc);rc=origfd; 226 if(0>origfd) 227 return 0; 228 origfd= -1; 229 return 1; 230} 231 232/* 233 * Are we at the end of an input read? If so, we'll need to flush our 234 * output buffer to prevent a possible deadlock from the pipelining 235 */ 236int endoread P((void)) 237{ return rcbufp>=rcbufend; 238} 239 240/* 241 * refill the LMTP input buffer, switching back to the original input 242 * stream if needed 243 */ 244void refillL P((void)) 245{ int retcode; 246 refill(0); 247 if(rcbufp>=rcbufend) /* we must have run out */ 248 { if(popfd()) /* try the original fd */ 249 { refill(0); /* fill the buffer */ 250 if(rcbufp<rcbufend) /* looks good, clean up the child */ 251 { if((retcode=waitfor(childserverpid))==EXIT_SUCCESS) 252 return; /* successfully switched and the child was fine */ 253 syslog(LOG_WARNING,"LMTP child failed: exit code %d",retcode); 254 exit(EX_SOFTWARE); /* give up, give up, wherever you are */ 255 } 256 } 257 exit(EX_NOINPUT); /* we ran out of input! */ 258 } 259} 260 261/* Like getb(), except for the LMTP input stream */ 262int getL P((void)) 263{ if(rcbufp==rcbufend) 264 refillL(); 265 return (int)*rcbufp++; 266} 267 268/* read a bunch of characters from the LMTP input stream */ 269int readL(p,len)char*p;const int len; 270{ size_t min; 271 if(rcbufp==rcbufend) 272 refillL(); 273 min=rcbufend-rcbufp; 274 if(min>len) 275 min=len; 276 tmemmove(p,rcbufp,min); 277 rcbufp+=min; 278 return min; 279} 280 281/* 282 * read exactly len bytes from the LMTP input stream 283 * return 1 on success, 0 on EOF, and -1 on read error 284 */ 285int readLe(p,len)char*p;int len; 286{ long got=rcbufend-rcbufp; 287 if(got>0) /* first, copy from the buffer */ 288 { if(got>len) /* is that more than we need? */ 289 got=len; 290 tmemmove(p,rcbufp,got); 291 rcbufp+=got; 292 p+=got;len-=got; 293 } 294 while(len) /* read the rest directly */ 295 { if(0>(got=rread(rc,p,len))) 296 return -1; 297 if(!got&&!popfd()) 298 return 0; 299 p+=got;len-=got; 300 } 301 return 1; 302} 303 304#endif 305