Blame src/lib/libast/sfio/sfdisc.c

Packit Service a8c26c
/***********************************************************************
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*               This software is part of the ast package               *
Packit Service a8c26c
*          Copyright (c) 1985-2011 AT&T Intellectual Property          *
Packit Service a8c26c
*                      and is licensed under the                       *
Packit Service a8c26c
*                 Eclipse Public License, Version 1.0                  *
Packit Service a8c26c
*                    by AT&T Intellectual Property                     *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*                A copy of the License is available at                 *
Packit Service a8c26c
*          http://www.eclipse.org/org/documents/epl-v10.html           *
Packit Service a8c26c
*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*              Information and Software Systems Research               *
Packit Service a8c26c
*                            AT&T Research                             *
Packit Service a8c26c
*                           Florham Park NJ                            *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*                 Glenn Fowler <gsf@research.att.com>                  *
Packit Service a8c26c
*                  David Korn <dgk@research.att.com>                   *
Packit Service a8c26c
*                   Phong Vo <kpv@research.att.com>                    *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
***********************************************************************/
Packit Service a8c26c
#include	"sfhdr.h"
Packit Service a8c26c
Packit Service a8c26c
/*	Add a new discipline to the discipline stack. Each discipline
Packit Service a8c26c
**	provides alternative I/O functions that are analogues of the
Packit Service a8c26c
**	system calls.
Packit Service a8c26c
**
Packit Service a8c26c
**	When the application fills or flushes the stream buffer, data
Packit Service a8c26c
**	will be processed through discipline functions. A case deserving
Packit Service a8c26c
**	consideration is stacking a discipline onto a read stream. Each
Packit Service a8c26c
**	discipline operation implies buffer synchronization so the stream
Packit Service a8c26c
**	buffer should be empty. However, a read stream representing an
Packit Service a8c26c
**	unseekable device (eg, a pipe) may not be synchronizable. In that
Packit Service a8c26c
**	case, any buffered data must then be fed to the new discipline
Packit Service a8c26c
**	to preserve data processing semantics. This is done by creating
Packit Service a8c26c
**	a temporary discipline to cache such buffered data and feed
Packit Service a8c26c
**	them to the new discipline when its readf() asks for new data.
Packit Service a8c26c
**	Care must then be taken to remove this temporary discipline
Packit Service a8c26c
**	when it runs out of cached data.
Packit Service a8c26c
**
Packit Service a8c26c
**	Written by Kiem-Phong Vo
Packit Service a8c26c
*/
Packit Service a8c26c
Packit Service a8c26c
typedef struct _dccache_s
Packit Service a8c26c
{	Sfdisc_t	disc;
Packit Service a8c26c
	uchar*		data;
Packit Service a8c26c
	uchar*		endb;
Packit Service a8c26c
} Dccache_t;
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
static int _dccaexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
Packit Service a8c26c
#else
Packit Service a8c26c
static int _dccaexcept(f,type,val,disc)
Packit Service a8c26c
Sfio_t*		f;
Packit Service a8c26c
int		type;
Packit Service a8c26c
Void_t*		val;
Packit Service a8c26c
Sfdisc_t*	disc;
Packit Service a8c26c
#endif
Packit Service a8c26c
{
Packit Service a8c26c
	if(disc && type == SF_FINAL)
Packit Service a8c26c
		free(disc);
Packit Service a8c26c
	return 0;
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
static ssize_t _dccaread(Sfio_t* f, Void_t* buf, size_t size, Sfdisc_t* disc)
Packit Service a8c26c
#else
Packit Service a8c26c
static ssize_t _dccaread(f, buf, size, disc)
Packit Service a8c26c
Sfio_t*		f;
Packit Service a8c26c
Void_t*		buf;
Packit Service a8c26c
size_t		size;
Packit Service a8c26c
Sfdisc_t*	disc;
Packit Service a8c26c
#endif
Packit Service a8c26c
{
Packit Service a8c26c
	ssize_t		sz;
Packit Service a8c26c
	Sfdisc_t	*prev;
Packit Service a8c26c
	Dccache_t	*dcca;
Packit Service a8c26c
Packit Service a8c26c
	if(!f) /* bad stream */
Packit Service a8c26c
		return -1;
Packit Service a8c26c
Packit Service a8c26c
	/* make sure that this is on the discipline stack */
Packit Service a8c26c
	for(prev = f->disc; prev; prev = prev->disc)
Packit Service a8c26c
		if(prev->disc == disc)
Packit Service a8c26c
			break;
Packit Service a8c26c
	if(!prev)
Packit Service a8c26c
		return -1;
Packit Service a8c26c
Packit Service a8c26c
	if(size <= 0) /* nothing to do */
Packit Service a8c26c
		return size;
Packit Service a8c26c
Packit Service a8c26c
	/* read from available data */
Packit Service a8c26c
	dcca = (Dccache_t*)disc;
Packit Service a8c26c
	if((sz = dcca->endb - dcca->data) > (ssize_t)size)
Packit Service a8c26c
		sz = (ssize_t)size;
Packit Service a8c26c
	memcpy(buf, dcca->data, sz);
Packit Service a8c26c
Packit Service a8c26c
	if((dcca->data += sz) >= dcca->endb) /* free empty cache */
Packit Service a8c26c
	{	prev->disc = disc->disc;
Packit Service a8c26c
		free(disc);
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
	return sz;
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)
Packit Service a8c26c
#else
Packit Service a8c26c
Sfdisc_t* sfdisc(f,disc)
Packit Service a8c26c
Sfio_t*		f;
Packit Service a8c26c
Sfdisc_t*	disc;
Packit Service a8c26c
#endif
Packit Service a8c26c
{
Packit Service a8c26c
	Sfdisc_t	*d, *rdisc;
Packit Service a8c26c
	Sfread_f	oreadf;
Packit Service a8c26c
	Sfwrite_f	owritef;
Packit Service a8c26c
	Sfseek_f	oseekf;
Packit Service a8c26c
	ssize_t		n;
Packit Service a8c26c
	Dccache_t	*dcca = NIL(Dccache_t*);
Packit Service a8c26c
	SFMTXDECL(f); /* declare a local stream variable for multithreading */
Packit Service a8c26c
Packit Service a8c26c
	SFMTXENTER(f, NIL(Sfdisc_t*));
Packit Service a8c26c
Packit Service a8c26c
	if((Sfio_t*)disc == f) /* special case to get the top discipline */
Packit Service a8c26c
		SFMTXRETURN(f,f->disc);
Packit Service a8c26c
Packit Service a8c26c
	if((f->flags&SF_READ) && f->proc && (f->mode&SF_WRITE) )
Packit Service a8c26c
	{	/* make sure in read mode to check for read-ahead data */
Packit Service a8c26c
		if(_sfmode(f,SF_READ,0) < 0)
Packit Service a8c26c
			SFMTXRETURN(f, NIL(Sfdisc_t*));
Packit Service a8c26c
	}
Packit Service a8c26c
	else
Packit Service a8c26c
	{	if((f->mode&SF_RDWR) != f->mode && _sfmode(f,0,0) < 0)
Packit Service a8c26c
			SFMTXRETURN(f, NIL(Sfdisc_t*));
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
	SFLOCK(f,0);
Packit Service a8c26c
	rdisc = NIL(Sfdisc_t*);
Packit Service a8c26c
Packit Service a8c26c
	/* disallow popping while there is cached data */
Packit Service a8c26c
	if(!disc && f->disc && f->disc->disc && f->disc->disc->readf == _dccaread )
Packit Service a8c26c
		goto done;
Packit Service a8c26c
Packit Service a8c26c
	/* synchronize before switching to a new discipline */
Packit Service a8c26c
	if(!(f->flags&SF_STRING))
Packit Service a8c26c
	{	(void)SFSYNC(f); /* do a silent buffer synch */
Packit Service a8c26c
		if((f->mode&SF_READ) && (f->mode&SF_SYNCED) )
Packit Service a8c26c
		{	f->mode &= ~SF_SYNCED;
Packit Service a8c26c
			f->endb = f->next = f->endr = f->endw = f->data;
Packit Service a8c26c
		}
Packit Service a8c26c
Packit Service a8c26c
		/* if there is buffered data, ask app before proceeding */
Packit Service a8c26c
		if(((f->mode&SF_WRITE) && (n = f->next-f->data) > 0) ||
Packit Service a8c26c
		   ((f->mode&SF_READ) && (n = f->endb-f->next) > 0) )
Packit Service a8c26c
		{	int	rv = 0;
Packit Service a8c26c
			if(rv == 0 && f->disc && f->disc->exceptf) /* ask current discipline */
Packit Service a8c26c
			{	SFOPEN(f,0);
Packit Service a8c26c
				rv = (*f->disc->exceptf)(f, SF_DBUFFER, &n, f->disc);
Packit Service a8c26c
				SFLOCK(f,0);
Packit Service a8c26c
			}
Packit Service a8c26c
			if(rv == 0 && disc && disc->exceptf) /* ask discipline being pushed */
Packit Service a8c26c
			{	SFOPEN(f,0);
Packit Service a8c26c
				rv = (*disc->exceptf)(f, SF_DBUFFER, &n, disc);
Packit Service a8c26c
				SFLOCK(f,0);
Packit Service a8c26c
			}
Packit Service a8c26c
			if(rv < 0)
Packit Service a8c26c
				goto done;
Packit Service a8c26c
		}
Packit Service a8c26c
Packit Service a8c26c
		/* trick the new discipline into processing already buffered data */
Packit Service a8c26c
		if((f->mode&SF_READ) && n > 0 && disc && disc->readf )
Packit Service a8c26c
		{	if(!(dcca = (Dccache_t*)malloc(sizeof(Dccache_t)+n)) )
Packit Service a8c26c
				goto done;
Packit Service a8c26c
			memclear(dcca, sizeof(Dccache_t));
Packit Service a8c26c
Packit Service a8c26c
			dcca->disc.readf = _dccaread;
Packit Service a8c26c
			dcca->disc.exceptf = _dccaexcept;
Packit Service a8c26c
Packit Service a8c26c
			/* move buffered data into the temp discipline */
Packit Service a8c26c
			dcca->data = ((uchar*)dcca) + sizeof(Dccache_t);
Packit Service a8c26c
			dcca->endb = dcca->data + n;
Packit Service a8c26c
			memcpy(dcca->data, f->next, n);
Packit Service a8c26c
			f->endb = f->next = f->endr = f->endw = f->data;
Packit Service a8c26c
		}
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
	/* save old readf, writef, and seekf to see if stream need reinit */
Packit Service a8c26c
#define GETDISCF(func,iof,type) \
Packit Service a8c26c
	{ for(d = f->disc; d && !d->iof; d = d->disc) ; \
Packit Service a8c26c
	  func = d ? d->iof : NIL(type); \
Packit Service a8c26c
	}
Packit Service a8c26c
	GETDISCF(oreadf,readf,Sfread_f);
Packit Service a8c26c
	GETDISCF(owritef,writef,Sfwrite_f);
Packit Service a8c26c
	GETDISCF(oseekf,seekf,Sfseek_f);
Packit Service a8c26c
Packit Service a8c26c
	if(disc == SF_POPDISC)
Packit Service a8c26c
	{	/* popping, warn the being popped discipline */
Packit Service a8c26c
		if(!(d = f->disc) )
Packit Service a8c26c
			goto done;
Packit Service a8c26c
		disc = d->disc;
Packit Service a8c26c
		if(d->exceptf)
Packit Service a8c26c
		{	SFOPEN(f,0);
Packit Service a8c26c
			if((*(d->exceptf))(f,SF_DPOP,(Void_t*)disc,d) < 0 )
Packit Service a8c26c
				goto done;
Packit Service a8c26c
			SFLOCK(f,0);
Packit Service a8c26c
		}
Packit Service a8c26c
		f->disc = disc;
Packit Service a8c26c
		rdisc = d;
Packit Service a8c26c
	}
Packit Service a8c26c
	else
Packit Service a8c26c
	{	/* pushing, warn being pushed discipline */
Packit Service a8c26c
		do
Packit Service a8c26c
		{	/* loop to handle the case where d may pop itself */
Packit Service a8c26c
			d = f->disc;
Packit Service a8c26c
			if(d && d->exceptf)
Packit Service a8c26c
			{	SFOPEN(f,0);
Packit Service a8c26c
				if( (*(d->exceptf))(f,SF_DPUSH,(Void_t*)disc,d) < 0 )
Packit Service a8c26c
					goto done;
Packit Service a8c26c
				SFLOCK(f,0);
Packit Service a8c26c
			}
Packit Service a8c26c
		} while(d != f->disc);
Packit Service a8c26c
Packit Service a8c26c
		/* make sure we are not creating an infinite loop */
Packit Service a8c26c
		for(; d; d = d->disc)
Packit Service a8c26c
			if(d == disc)
Packit Service a8c26c
				goto done;
Packit Service a8c26c
Packit Service a8c26c
		/* set new disc */
Packit Service a8c26c
		if(dcca) /* insert the discipline with cached data */
Packit Service a8c26c
		{	dcca->disc.disc = f->disc;
Packit Service a8c26c
			disc->disc = &dcca->disc;
Packit Service a8c26c
		}
Packit Service a8c26c
		else	disc->disc = f->disc;
Packit Service a8c26c
		f->disc = disc;
Packit Service a8c26c
		rdisc = disc;
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
	if(!(f->flags&SF_STRING) )
Packit Service a8c26c
	{	/* this stream may have to be reinitialized */
Packit Service a8c26c
		reg int	reinit = 0;
Packit Service a8c26c
#define DISCF(dst,iof,type)	(dst ? dst->iof : NIL(type)) 
Packit Service a8c26c
#define REINIT(oiof,iof,type) \
Packit Service a8c26c
		if(!reinit) \
Packit Service a8c26c
		{	for(d = f->disc; d && !d->iof; d = d->disc) ; \
Packit Service a8c26c
			if(DISCF(d,iof,type) != oiof) \
Packit Service a8c26c
				reinit = 1; \
Packit Service a8c26c
		}
Packit Service a8c26c
Packit Service a8c26c
		REINIT(oreadf,readf,Sfread_f);
Packit Service a8c26c
		REINIT(owritef,writef,Sfwrite_f);
Packit Service a8c26c
		REINIT(oseekf,seekf,Sfseek_f);
Packit Service a8c26c
Packit Service a8c26c
		if(reinit)
Packit Service a8c26c
		{	SETLOCAL(f);
Packit Service a8c26c
			f->bits &= ~SF_NULL;	/* turn off /dev/null handling */
Packit Service a8c26c
			if((f->bits&SF_MMAP) || (f->mode&SF_INIT))
Packit Service a8c26c
				sfsetbuf(f,NIL(Void_t*),(size_t)SF_UNBOUND);
Packit Service a8c26c
			else if(f->data == f->tiny)
Packit Service a8c26c
				sfsetbuf(f,NIL(Void_t*),0);
Packit Service a8c26c
			else
Packit Service a8c26c
			{	int	flags = f->flags;
Packit Service a8c26c
				sfsetbuf(f,(Void_t*)f->data,f->size);
Packit Service a8c26c
				f->flags |= (flags&SF_MALLOC);
Packit Service a8c26c
			}
Packit Service a8c26c
		}
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
done :
Packit Service a8c26c
	SFOPEN(f,0);
Packit Service a8c26c
	SFMTXRETURN(f, rdisc);
Packit Service a8c26c
}