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*                  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
44typedef struct _dccache_s
45{	Sfdisc_t	disc;
46	uchar*		data;
47	uchar*		endb;
48} Dccache_t;
49
50#if __STD_C
51static int _dccaexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
52#else
53static int _dccaexcept(f,type,val,disc)
54Sfio_t*		f;
55int		type;
56Void_t*		val;
57Sfdisc_t*	disc;
58#endif
59{
60	if(disc && type == SF_FINAL)
61		free(disc);
62	return 0;
63}
64
65#if __STD_C
66static ssize_t _dccaread(Sfio_t* f, Void_t* buf, size_t size, Sfdisc_t* disc)
67#else
68static ssize_t _dccaread(f, buf, size, disc)
69Sfio_t*		f;
70Void_t*		buf;
71size_t		size;
72Sfdisc_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
107Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)
108#else
109Sfdisc_t* sfdisc(f,disc)
110Sfio_t*		f;
111Sfdisc_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
268done :
269	SFOPEN(f,0);
270	SFMTXRETURN(f, rdisc);
271}
272