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