1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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); /* declare a local stream variable for multithreading */
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