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
8 static /*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
19 static uchar rcbuf[STDBUF],*rcbufp,*rcbufend; /* buffer for custom stdio */
20 static off_t blasttell;
21 static struct dyna_array inced; /* includerc stack */
22 struct dynstring*incnamed;
23
refill(offset)24 static 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
pushrc(name)36 void 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 */
48 rerr: readerr(name);
49 }
50 }
51 }
52 }
53
changerc(name)54 void changerc(name)const char*const name; /* change rcfile */
55 { if(!*name||!strcmp(name,devnull))
56 pr:{ 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))
65 rerr: { 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
duprcs(void)81 void 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 */
91 dupfailed: /* 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
closeonerc(void)97 static 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
poprc(void)103 int 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
closerc(void)116 void 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 */
bopen(name)122 int 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
getbl(p,end)140 int 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
getb(void)153 int 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
ungetb(x)159 void ungetb(x)const int x; /* only for pushing back original characters */
160 { if(x!=EOF)
161 rcbufp--; /* backup */
162 }
163
testB(x)164 int 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
sgetc(void)172 int sgetc P((void)) /* a fake fgetc for a string */
173 { return *sgetcp?(int)*(uchar*)sgetcp++:EOF;
174 }
175
skipspace(void)176 int skipspace P((void))
177 { int any=0;
178 while(testB(' ')||testB('\t'))
179 any=1;
180 return any;
181 }
182
skipline(void)183 void 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
getlline(target,end)193 int 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
215 static int origfd= -1;
216
217 /* flush the input buffer and switch to a new input fd */
pushfd(fd)218 void pushfd(fd)int fd;
219 { origfd=rc;rc=fd;
220 rcbufend=rcbufp;
221 }
222
223 /* restore the original input fd */
popfd(void)224 static 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 */
endoread(void)236 int 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 */
refillL(void)244 void 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 */
getL(void)262 int 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 */
readL(p,len)269 int 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 */
readLe(p,len)285 int 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