xref: /onnv-gate/usr/src/lib/libast/common/sfio/sfdisc.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                   Phong Vo <kpv@research.att.com>                    *
204887Schin *                                                                      *
214887Schin ***********************************************************************/
224887Schin #include	"sfhdr.h"
234887Schin 
244887Schin /*	Add a new discipline to the discipline stack. Each discipline
254887Schin **	provides alternative I/O functions that are analogues of the
264887Schin **	system calls.
274887Schin **
284887Schin **	When the application fills or flushes the stream buffer, data
294887Schin **	will be processed through discipline functions. A case deserving
304887Schin **	consideration is stacking a discipline onto a read stream. Each
314887Schin **	discipline operation implies buffer synchronization so the stream
324887Schin **	buffer should be empty. However, a read stream representing an
334887Schin **	unseekable device (eg, a pipe) may not be synchronizable. In that
344887Schin **	case, any buffered data must then be fed to the new discipline
354887Schin **	to preserve data processing semantics. This is done by creating
364887Schin **	a temporary discipline to cache such buffered data and feed
374887Schin **	them to the new discipline when its readf() asks for new data.
384887Schin **	Care must then be taken to remove this temporary discipline
394887Schin **	when it runs out of cached data.
404887Schin **
414887Schin **	Written by Kiem-Phong Vo
424887Schin */
434887Schin 
444887Schin typedef struct _dccache_s
454887Schin {	Sfdisc_t	disc;
464887Schin 	uchar*		data;
474887Schin 	uchar*		endb;
484887Schin } Dccache_t;
494887Schin 
504887Schin #if __STD_C
_dccaexcept(Sfio_t * f,int type,Void_t * val,Sfdisc_t * disc)514887Schin static int _dccaexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
524887Schin #else
534887Schin static int _dccaexcept(f,type,val,disc)
544887Schin Sfio_t*		f;
554887Schin int		type;
564887Schin Void_t*		val;
574887Schin Sfdisc_t*	disc;
584887Schin #endif
594887Schin {
604887Schin 	if(disc && type == SF_FINAL)
614887Schin 		free(disc);
624887Schin 	return 0;
634887Schin }
644887Schin 
654887Schin #if __STD_C
_dccaread(Sfio_t * f,Void_t * buf,size_t size,Sfdisc_t * disc)664887Schin static ssize_t _dccaread(Sfio_t* f, Void_t* buf, size_t size, Sfdisc_t* disc)
674887Schin #else
684887Schin static ssize_t _dccaread(f, buf, size, disc)
694887Schin Sfio_t*		f;
704887Schin Void_t*		buf;
714887Schin size_t		size;
724887Schin Sfdisc_t*	disc;
734887Schin #endif
744887Schin {
754887Schin 	ssize_t		sz;
764887Schin 	Sfdisc_t	*prev;
774887Schin 	Dccache_t	*dcca;
784887Schin 
794887Schin 	if(!f) /* bad stream */
804887Schin 		return -1;
814887Schin 
824887Schin 	/* make sure that this is on the discipline stack */
834887Schin 	for(prev = f->disc; prev; prev = prev->disc)
844887Schin 		if(prev->disc == disc)
854887Schin 			break;
864887Schin 	if(!prev)
874887Schin 		return -1;
884887Schin 
894887Schin 	if(size <= 0) /* nothing to do */
904887Schin 		return size;
914887Schin 
924887Schin 	/* read from available data */
934887Schin 	dcca = (Dccache_t*)disc;
944887Schin 	if((sz = dcca->endb - dcca->data) > (ssize_t)size)
954887Schin 		sz = (ssize_t)size;
964887Schin 	memcpy(buf, dcca->data, sz);
974887Schin 
984887Schin 	if((dcca->data += sz) >= dcca->endb) /* free empty cache */
994887Schin 	{	prev->disc = disc->disc;
1004887Schin 		free(disc);
1014887Schin 	}
1024887Schin 
1034887Schin 	return sz;
1044887Schin }
1054887Schin 
1064887Schin #if __STD_C
sfdisc(Sfio_t * f,Sfdisc_t * disc)1078462SApril.Chin@Sun.COM Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)
1084887Schin #else
1094887Schin Sfdisc_t* sfdisc(f,disc)
1108462SApril.Chin@Sun.COM Sfio_t*		f;
1118462SApril.Chin@Sun.COM Sfdisc_t*	disc;
1124887Schin #endif
1134887Schin {
1148462SApril.Chin@Sun.COM 	Sfdisc_t	*d, *rdisc;
1158462SApril.Chin@Sun.COM 	Sfread_f	oreadf;
1168462SApril.Chin@Sun.COM 	Sfwrite_f	owritef;
1178462SApril.Chin@Sun.COM 	Sfseek_f	oseekf;
1184887Schin 	ssize_t		n;
1198462SApril.Chin@Sun.COM 	Dccache_t	*dcca = NIL(Dccache_t*);
1208462SApril.Chin@Sun.COM 	SFMTXDECL(f);
1214887Schin 
1228462SApril.Chin@Sun.COM 	SFMTXENTER(f, NIL(Sfdisc_t*));
1238462SApril.Chin@Sun.COM 
1248462SApril.Chin@Sun.COM 	if((Sfio_t*)disc == f) /* special case to get the top discipline */
1258462SApril.Chin@Sun.COM 		SFMTXRETURN(f,f->disc);
1264887Schin 
1274887Schin 	if((f->flags&SF_READ) && f->proc && (f->mode&SF_WRITE) )
1284887Schin 	{	/* make sure in read mode to check for read-ahead data */
1294887Schin 		if(_sfmode(f,SF_READ,0) < 0)
1304887Schin 			SFMTXRETURN(f, NIL(Sfdisc_t*));
1314887Schin 	}
1328462SApril.Chin@Sun.COM 	else
1338462SApril.Chin@Sun.COM 	{	if((f->mode&SF_RDWR) != f->mode && _sfmode(f,0,0) < 0)
1348462SApril.Chin@Sun.COM 			SFMTXRETURN(f, NIL(Sfdisc_t*));
1358462SApril.Chin@Sun.COM 	}
1364887Schin 
1374887Schin 	SFLOCK(f,0);
1384887Schin 	rdisc = NIL(Sfdisc_t*);
1394887Schin 
1404887Schin 	/* disallow popping while there is cached data */
1414887Schin 	if(!disc && f->disc && f->disc->disc && f->disc->disc->readf == _dccaread )
1424887Schin 		goto done;
1434887Schin 
1444887Schin 	/* synchronize before switching to a new discipline */
1454887Schin 	if(!(f->flags&SF_STRING))
1464887Schin 	{	(void)SFSYNC(f); /* do a silent buffer synch */
1474887Schin 		if((f->mode&SF_READ) && (f->mode&SF_SYNCED) )
1484887Schin 		{	f->mode &= ~SF_SYNCED;
1494887Schin 			f->endb = f->next = f->endr = f->endw = f->data;
1504887Schin 		}
1514887Schin 
1524887Schin 		/* if there is buffered data, ask app before proceeding */
1534887Schin 		if(((f->mode&SF_WRITE) && (n = f->next-f->data) > 0) ||
1544887Schin 		   ((f->mode&SF_READ) && (n = f->endb-f->next) > 0) )
1554887Schin 		{	int	rv = 0;
1564887Schin 			if(rv == 0 && f->disc && f->disc->exceptf) /* ask current discipline */
1574887Schin 			{	SFOPEN(f,0);
1584887Schin 				rv = (*f->disc->exceptf)(f, SF_DBUFFER, &n, f->disc);
1594887Schin 				SFLOCK(f,0);
1604887Schin 			}
1614887Schin 			if(rv == 0 && disc && disc->exceptf) /* ask discipline being pushed */
1624887Schin 			{	SFOPEN(f,0);
1634887Schin 				rv = (*disc->exceptf)(f, SF_DBUFFER, &n, disc);
1644887Schin 				SFLOCK(f,0);
1654887Schin 			}
1664887Schin 			if(rv < 0)
1674887Schin 				goto done;
1684887Schin 		}
1694887Schin 
1704887Schin 		/* trick the new discipline into processing already buffered data */
1714887Schin 		if((f->mode&SF_READ) && n > 0 && disc && disc->readf )
1724887Schin 		{	if(!(dcca = (Dccache_t*)malloc(sizeof(Dccache_t)+n)) )
1734887Schin 				goto done;
1744887Schin 			memclear(dcca, sizeof(Dccache_t));
1754887Schin 
1764887Schin 			dcca->disc.readf = _dccaread;
1774887Schin 			dcca->disc.exceptf = _dccaexcept;
1784887Schin 
1794887Schin 			/* move buffered data into the temp discipline */
1804887Schin 			dcca->data = ((uchar*)dcca) + sizeof(Dccache_t);
1814887Schin 			dcca->endb = dcca->data + n;
1824887Schin 			memcpy(dcca->data, f->next, n);
1834887Schin 			f->endb = f->next = f->endr = f->endw = f->data;
1844887Schin 		}
1854887Schin 	}
1864887Schin 
1874887Schin 	/* save old readf, writef, and seekf to see if stream need reinit */
1884887Schin #define GETDISCF(func,iof,type) \
1894887Schin 	{ for(d = f->disc; d && !d->iof; d = d->disc) ; \
1904887Schin 	  func = d ? d->iof : NIL(type); \
1914887Schin 	}
1924887Schin 	GETDISCF(oreadf,readf,Sfread_f);
1934887Schin 	GETDISCF(owritef,writef,Sfwrite_f);
1944887Schin 	GETDISCF(oseekf,seekf,Sfseek_f);
1954887Schin 
1964887Schin 	if(disc == SF_POPDISC)
1974887Schin 	{	/* popping, warn the being popped discipline */
1984887Schin 		if(!(d = f->disc) )
1994887Schin 			goto done;
2004887Schin 		disc = d->disc;
2014887Schin 		if(d->exceptf)
2024887Schin 		{	SFOPEN(f,0);
2034887Schin 			if((*(d->exceptf))(f,SF_DPOP,(Void_t*)disc,d) < 0 )
2044887Schin 				goto done;
2054887Schin 			SFLOCK(f,0);
2064887Schin 		}
2074887Schin 		f->disc = disc;
2084887Schin 		rdisc = d;
2094887Schin 	}
2104887Schin 	else
2114887Schin 	{	/* pushing, warn being pushed discipline */
2124887Schin 		do
2134887Schin 		{	/* loop to handle the case where d may pop itself */
2144887Schin 			d = f->disc;
2154887Schin 			if(d && d->exceptf)
2164887Schin 			{	SFOPEN(f,0);
2174887Schin 				if( (*(d->exceptf))(f,SF_DPUSH,(Void_t*)disc,d) < 0 )
2184887Schin 					goto done;
2194887Schin 				SFLOCK(f,0);
2204887Schin 			}
2214887Schin 		} while(d != f->disc);
2224887Schin 
2234887Schin 		/* make sure we are not creating an infinite loop */
2244887Schin 		for(; d; d = d->disc)
2254887Schin 			if(d == disc)
2264887Schin 				goto done;
2274887Schin 
2284887Schin 		/* set new disc */
2294887Schin 		if(dcca) /* insert the discipline with cached data */
2304887Schin 		{	dcca->disc.disc = f->disc;
2314887Schin 			disc->disc = &dcca->disc;
2324887Schin 		}
2334887Schin 		else	disc->disc = f->disc;
2344887Schin 		f->disc = disc;
2354887Schin 		rdisc = disc;
2364887Schin 	}
2374887Schin 
2384887Schin 	if(!(f->flags&SF_STRING) )
2394887Schin 	{	/* this stream may have to be reinitialized */
2404887Schin 		reg int	reinit = 0;
2414887Schin #define DISCF(dst,iof,type)	(dst ? dst->iof : NIL(type))
2424887Schin #define REINIT(oiof,iof,type) \
2434887Schin 		if(!reinit) \
2444887Schin 		{	for(d = f->disc; d && !d->iof; d = d->disc) ; \
2454887Schin 			if(DISCF(d,iof,type) != oiof) \
2464887Schin 				reinit = 1; \
2474887Schin 		}
2484887Schin 
2494887Schin 		REINIT(oreadf,readf,Sfread_f);
2504887Schin 		REINIT(owritef,writef,Sfwrite_f);
2514887Schin 		REINIT(oseekf,seekf,Sfseek_f);
2524887Schin 
2534887Schin 		if(reinit)
2544887Schin 		{	SETLOCAL(f);
2554887Schin 			f->bits &= ~SF_NULL;	/* turn off /dev/null handling */
2564887Schin 			if((f->bits&SF_MMAP) || (f->mode&SF_INIT))
2574887Schin 				sfsetbuf(f,NIL(Void_t*),(size_t)SF_UNBOUND);
2584887Schin 			else if(f->data == f->tiny)
2594887Schin 				sfsetbuf(f,NIL(Void_t*),0);
2604887Schin 			else
2614887Schin 			{	int	flags = f->flags;
2624887Schin 				sfsetbuf(f,(Void_t*)f->data,f->size);
2634887Schin 				f->flags |= (flags&SF_MALLOC);
2644887Schin 			}
2654887Schin 		}
2664887Schin 	}
2674887Schin 
2684887Schin done :
2694887Schin 	SFOPEN(f,0);
2704887Schin 	SFMTXRETURN(f, rdisc);
2714887Schin }
272