1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
20 * *
21 ***********************************************************************/
22 #include "sfhdr.h"
23
24 /* Add a new discipline to the discipline stack. Each discipline
25 ** provides alternative I/O functions that are analogues of the
26 ** system calls.
27 **
28 ** When the application fills or flushes the stream buffer, data
29 ** will be processed through discipline functions. A case deserving
30 ** consideration is stacking a discipline onto a read stream. Each
31 ** discipline operation implies buffer synchronization so the stream
32 ** buffer should be empty. However, a read stream representing an
33 ** unseekable device (eg, a pipe) may not be synchronizable. In that
34 ** case, any buffered data must then be fed to the new discipline
35 ** to preserve data processing semantics. This is done by creating
36 ** a temporary discipline to cache such buffered data and feed
37 ** them to the new discipline when its readf() asks for new data.
38 ** Care must then be taken to remove this temporary discipline
39 ** when it runs out of cached data.
40 **
41 ** Written by Kiem-Phong Vo
42 */
43
44 typedef struct _dccache_s
45 { Sfdisc_t disc;
46 uchar* data;
47 uchar* endb;
48 } Dccache_t;
49
50 #if __STD_C
_dccaexcept(Sfio_t * f,int type,Void_t * val,Sfdisc_t * disc)51 static int _dccaexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
52 #else
53 static int _dccaexcept(f,type,val,disc)
54 Sfio_t* f;
55 int type;
56 Void_t* val;
57 Sfdisc_t* disc;
58 #endif
59 {
60 if(disc && type == SF_FINAL)
61 free(disc);
62 return 0;
63 }
64
65 #if __STD_C
_dccaread(Sfio_t * f,Void_t * buf,size_t size,Sfdisc_t * disc)66 static ssize_t _dccaread(Sfio_t* f, Void_t* buf, size_t size, Sfdisc_t* disc)
67 #else
68 static ssize_t _dccaread(f, buf, size, disc)
69 Sfio_t* f;
70 Void_t* buf;
71 size_t size;
72 Sfdisc_t* disc;
73 #endif
74 {
75 ssize_t sz;
76 Sfdisc_t *prev;
77 Dccache_t *dcca;
78
79 if(!f) /* bad stream */
80 return -1;
81
82 /* make sure that this is on the discipline stack */
83 for(prev = f->disc; prev; prev = prev->disc)
84 if(prev->disc == disc)
85 break;
86 if(!prev)
87 return -1;
88
89 if(size <= 0) /* nothing to do */
90 return size;
91
92 /* read from available data */
93 dcca = (Dccache_t*)disc;
94 if((sz = dcca->endb - dcca->data) > (ssize_t)size)
95 sz = (ssize_t)size;
96 memcpy(buf, dcca->data, sz);
97
98 if((dcca->data += sz) >= dcca->endb) /* free empty cache */
99 { prev->disc = disc->disc;
100 free(disc);
101 }
102
103 return sz;
104 }
105
106 #if __STD_C
sfdisc(Sfio_t * f,Sfdisc_t * disc)107 Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)
108 #else
109 Sfdisc_t* sfdisc(f,disc)
110 Sfio_t* f;
111 Sfdisc_t* disc;
112 #endif
113 {
114 Sfdisc_t *d, *rdisc;
115 Sfread_f oreadf;
116 Sfwrite_f owritef;
117 Sfseek_f oseekf;
118 ssize_t n;
119 Dccache_t *dcca = NIL(Dccache_t*);
120 SFMTXDECL(f);
121
122 SFMTXENTER(f, NIL(Sfdisc_t*));
123
124 if((Sfio_t*)disc == f) /* special case to get the top discipline */
125 SFMTXRETURN(f,f->disc);
126
127 if((f->flags&SF_READ) && f->proc && (f->mode&SF_WRITE) )
128 { /* make sure in read mode to check for read-ahead data */
129 if(_sfmode(f,SF_READ,0) < 0)
130 SFMTXRETURN(f, NIL(Sfdisc_t*));
131 }
132 else
133 { if((f->mode&SF_RDWR) != f->mode && _sfmode(f,0,0) < 0)
134 SFMTXRETURN(f, NIL(Sfdisc_t*));
135 }
136
137 SFLOCK(f,0);
138 rdisc = NIL(Sfdisc_t*);
139
140 /* disallow popping while there is cached data */
141 if(!disc && f->disc && f->disc->disc && f->disc->disc->readf == _dccaread )
142 goto done;
143
144 /* synchronize before switching to a new discipline */
145 if(!(f->flags&SF_STRING))
146 { (void)SFSYNC(f); /* do a silent buffer synch */
147 if((f->mode&SF_READ) && (f->mode&SF_SYNCED) )
148 { f->mode &= ~SF_SYNCED;
149 f->endb = f->next = f->endr = f->endw = f->data;
150 }
151
152 /* if there is buffered data, ask app before proceeding */
153 if(((f->mode&SF_WRITE) && (n = f->next-f->data) > 0) ||
154 ((f->mode&SF_READ) && (n = f->endb-f->next) > 0) )
155 { int rv = 0;
156 if(rv == 0 && f->disc && f->disc->exceptf) /* ask current discipline */
157 { SFOPEN(f,0);
158 rv = (*f->disc->exceptf)(f, SF_DBUFFER, &n, f->disc);
159 SFLOCK(f,0);
160 }
161 if(rv == 0 && disc && disc->exceptf) /* ask discipline being pushed */
162 { SFOPEN(f,0);
163 rv = (*disc->exceptf)(f, SF_DBUFFER, &n, disc);
164 SFLOCK(f,0);
165 }
166 if(rv < 0)
167 goto done;
168 }
169
170 /* trick the new discipline into processing already buffered data */
171 if((f->mode&SF_READ) && n > 0 && disc && disc->readf )
172 { if(!(dcca = (Dccache_t*)malloc(sizeof(Dccache_t)+n)) )
173 goto done;
174 memclear(dcca, sizeof(Dccache_t));
175
176 dcca->disc.readf = _dccaread;
177 dcca->disc.exceptf = _dccaexcept;
178
179 /* move buffered data into the temp discipline */
180 dcca->data = ((uchar*)dcca) + sizeof(Dccache_t);
181 dcca->endb = dcca->data + n;
182 memcpy(dcca->data, f->next, n);
183 f->endb = f->next = f->endr = f->endw = f->data;
184 }
185 }
186
187 /* save old readf, writef, and seekf to see if stream need reinit */
188 #define GETDISCF(func,iof,type) \
189 { for(d = f->disc; d && !d->iof; d = d->disc) ; \
190 func = d ? d->iof : NIL(type); \
191 }
192 GETDISCF(oreadf,readf,Sfread_f);
193 GETDISCF(owritef,writef,Sfwrite_f);
194 GETDISCF(oseekf,seekf,Sfseek_f);
195
196 if(disc == SF_POPDISC)
197 { /* popping, warn the being popped discipline */
198 if(!(d = f->disc) )
199 goto done;
200 disc = d->disc;
201 if(d->exceptf)
202 { SFOPEN(f,0);
203 if((*(d->exceptf))(f,SF_DPOP,(Void_t*)disc,d) < 0 )
204 goto done;
205 SFLOCK(f,0);
206 }
207 f->disc = disc;
208 rdisc = d;
209 }
210 else
211 { /* pushing, warn being pushed discipline */
212 do
213 { /* loop to handle the case where d may pop itself */
214 d = f->disc;
215 if(d && d->exceptf)
216 { SFOPEN(f,0);
217 if( (*(d->exceptf))(f,SF_DPUSH,(Void_t*)disc,d) < 0 )
218 goto done;
219 SFLOCK(f,0);
220 }
221 } while(d != f->disc);
222
223 /* make sure we are not creating an infinite loop */
224 for(; d; d = d->disc)
225 if(d == disc)
226 goto done;
227
228 /* set new disc */
229 if(dcca) /* insert the discipline with cached data */
230 { dcca->disc.disc = f->disc;
231 disc->disc = &dcca->disc;
232 }
233 else disc->disc = f->disc;
234 f->disc = disc;
235 rdisc = disc;
236 }
237
238 if(!(f->flags&SF_STRING) )
239 { /* this stream may have to be reinitialized */
240 reg int reinit = 0;
241 #define DISCF(dst,iof,type) (dst ? dst->iof : NIL(type))
242 #define REINIT(oiof,iof,type) \
243 if(!reinit) \
244 { for(d = f->disc; d && !d->iof; d = d->disc) ; \
245 if(DISCF(d,iof,type) != oiof) \
246 reinit = 1; \
247 }
248
249 REINIT(oreadf,readf,Sfread_f);
250 REINIT(owritef,writef,Sfwrite_f);
251 REINIT(oseekf,seekf,Sfseek_f);
252
253 if(reinit)
254 { SETLOCAL(f);
255 f->bits &= ~SF_NULL; /* turn off /dev/null handling */
256 if((f->bits&SF_MMAP) || (f->mode&SF_INIT))
257 sfsetbuf(f,NIL(Void_t*),(size_t)SF_UNBOUND);
258 else if(f->data == f->tiny)
259 sfsetbuf(f,NIL(Void_t*),0);
260 else
261 { int flags = f->flags;
262 sfsetbuf(f,(Void_t*)f->data,f->size);
263 f->flags |= (flags&SF_MALLOC);
264 }
265 }
266 }
267
268 done :
269 SFOPEN(f,0);
270 SFMTXRETURN(f, rdisc);
271 }
272