1 /***********************************************************************
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 *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
24 /* Add a new discipline to the discipline stack. Each discipline
25 ** provides alternative I/O functions that are analogues of the
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.
41 ** Written by Kiem-Phong Vo
44 typedef struct _dccache_s
51 static int _dccaexcept(Sfio_t
* f
, int type
, Void_t
* val
, Sfdisc_t
* disc
)
53 static int _dccaexcept(f
,type
,val
,disc
)
60 if(disc
&& type
== SF_FINAL
)
66 static ssize_t
_dccaread(Sfio_t
* f
, Void_t
* buf
, size_t size
, Sfdisc_t
* disc
)
68 static ssize_t
_dccaread(f
, buf
, size
, disc
)
79 if(!f
) /* bad stream */
82 /* make sure that this is on the discipline stack */
83 for(prev
= f
->disc
; prev
; prev
= prev
->disc
)
84 if(prev
->disc
== disc
)
89 if(size
<= 0) /* nothing to do */
92 /* read from available data */
93 dcca
= (Dccache_t
*)disc
;
94 if((sz
= dcca
->endb
- dcca
->data
) > (ssize_t
)size
)
96 memcpy(buf
, dcca
->data
, sz
);
98 if((dcca
->data
+= sz
) >= dcca
->endb
) /* free empty cache */
99 { prev
->disc
= disc
->disc
;
107 Sfdisc_t
* sfdisc(Sfio_t
* f
, Sfdisc_t
* disc
)
109 Sfdisc_t
* sfdisc(f
,disc
)
119 Dccache_t
*dcca
= NIL(Dccache_t
*);
122 SFMTXENTER(f
, NIL(Sfdisc_t
*));
124 if((Sfio_t
*)disc
== f
) /* special case to get the top discipline */
125 SFMTXRETURN(f
,f
->disc
);
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
*));
133 { if((f
->mode
&SF_RDWR
) != f
->mode
&& _sfmode(f
,0,0) < 0)
134 SFMTXRETURN(f
, NIL(Sfdisc_t
*));
138 rdisc
= NIL(Sfdisc_t
*);
140 /* disallow popping while there is cached data */
141 if(!disc
&& f
->disc
&& f
->disc
->disc
&& f
->disc
->disc
->readf
== _dccaread
)
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
;
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) )
156 if(rv
== 0 && f
->disc
&& f
->disc
->exceptf
) /* ask current discipline */
158 rv
= (*f
->disc
->exceptf
)(f
, SF_DBUFFER
, &n
, f
->disc
);
161 if(rv
== 0 && disc
&& disc
->exceptf
) /* ask discipline being pushed */
163 rv
= (*disc
->exceptf
)(f
, SF_DBUFFER
, &n
, disc
);
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
)) )
174 memclear(dcca
, sizeof(Dccache_t
));
176 dcca
->disc
.readf
= _dccaread
;
177 dcca
->disc
.exceptf
= _dccaexcept
;
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
;
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); \
192 GETDISCF(oreadf
,readf
,Sfread_f
);
193 GETDISCF(owritef
,writef
,Sfwrite_f
);
194 GETDISCF(oseekf
,seekf
,Sfseek_f
);
196 if(disc
== SF_POPDISC
)
197 { /* popping, warn the being popped discipline */
203 if((*(d
->exceptf
))(f
,SF_DPOP
,(Void_t
*)disc
,d
) < 0 )
211 { /* pushing, warn being pushed discipline */
213 { /* loop to handle the case where d may pop itself */
217 if( (*(d
->exceptf
))(f
,SF_DPUSH
,(Void_t
*)disc
,d
) < 0 )
221 } while(d
!= f
->disc
);
223 /* make sure we are not creating an infinite loop */
224 for(; d
; d
= d
->disc
)
229 if(dcca
) /* insert the discipline with cached data */
230 { dcca
->disc
.disc
= f
->disc
;
231 disc
->disc
= &dcca
->disc
;
233 else disc
->disc
= f
->disc
;
238 if(!(f
->flags
&SF_STRING
) )
239 { /* this stream may have to be reinitialized */
241 #define DISCF(dst,iof,type) (dst ? dst->iof : NIL(type))
242 #define REINIT(oiof,iof,type) \
244 { for(d = f->disc; d && !d->iof; d = d->disc) ; \
245 if(DISCF(d,iof,type) != oiof) \
249 REINIT(oreadf
,readf
,Sfread_f
);
250 REINIT(owritef
,writef
,Sfwrite_f
);
251 REINIT(oseekf
,seekf
,Sfseek_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);
261 { int flags
= f
->flags
;
262 sfsetbuf(f
,(Void_t
*)f
->data
,f
->size
);
263 f
->flags
|= (flags
&SF_MALLOC
);
270 SFMTXRETURN(f
, rdisc
);