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