#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	./FEATURE/atexit
#	./FEATURE/string
#	./FEATURE/mmap
#	./FEATURE/vfork
#	./FEATURE/filio
#	./FEATURE/mktemp
#	./FEATURE/waitpid
#	./FEATURE/getpagesize
#	./FEATURE/double
#	./FEATURE/blksize
#	./FEATURE/native
#	./FEATURE/prototype
#	./FEATURE/stdio
#	./FEATURE/pragma
#	./FEATURE/stdarg
#	./FEATURE/poll
#	./FEATURE/peek
#	./sfclose.c
#	./sfclrlock.c
#	./sfcvt.c
#	./sfdisc.c
#	./sfdlen.c
#	./sfexcept.c
#	./sfexit.c
#	./sfextern.c
#	./sffilbuf.c
#	./sfflsbuf.c
#	./sfgetd.c
#	./sfgetl.c
#	./sfgetr.c
#	./sfgetu.c
#	./sfhdr.h
#	./sfio.h
#	./sfllen.c
#	./sfmode.c
#	./sfmove.c
#	./sfnew.c
#	./sfnotify.c
#	./sfnputc.c
#	./sfopen.c
#	./sfpeek.c
#	./sfpool.c
#	./sfpopen.c
#	./sfprintf.c
#	./sfprints.c
#	./sfpurge.c
#	./sfputd.c
#	./sfputl.c
#	./sfputr.c
#	./sfputu.c
#	./sfrd.c
#	./sfread.c
#	./sfscanf.c
#	./sfseek.c
#	./sfset.c
#	./sfsetbuf.c
#	./sfsetfd.c
#	./sfsize.c
#	./sfsk.c
#	./sfstack.c
#	./sfstrtod.c
#	./sfsync.c
#	./sftable.c
#	./sftell.c
#	./sftmp.c
#	./sfungetc.c
#	./sfvprintf.c
#	./Stdio_s/stdgets.c
#	./Stdio_s/stdio.h
#	./Stdio_s/stdopen.c
#	./Stdio_s/stdprintf.c
#	./Stdio_s/stdscanf.c
#	./Stdio_s/stdsprnt.c
#	./Stdio_s/stdvbuf.c
#	./Stdio_s/stdvsprnt.c
#	./Stdio_s/stdvsscn.c
#	./Stdio_s/Makefile
#	./Stdio_b/clearerr.c
#	./Stdio_b/doprnt.c
#	./Stdio_b/doscan.c
#	./Stdio_b/fclose.c
#	./Stdio_b/fdopen.c
#	./Stdio_b/feof.c
#	./Stdio_b/ferror.c
#	./Stdio_b/fflush.c
#	./Stdio_b/fgetc.c
#	./Stdio_b/fgets.c
#	./Stdio_b/filbuf.c
#	./Stdio_b/fileno.c
#	./Stdio_b/flsbuf.c
#	./Stdio_b/fopen.c
#	./Stdio_b/fprintf.c
#	./Stdio_b/fputc.c
#	./Stdio_b/fputs.c
#	./Stdio_b/fread.c
#	./Stdio_b/freopen.c
#	./Stdio_b/fscanf.c
#	./Stdio_b/fseek.c
#	./Stdio_b/ftell.c
#	./Stdio_b/fwrite.c
#	./Stdio_b/getchar.c
#	./Stdio_b/gets.c
#	./Stdio_b/getw.c
#	./Stdio_b/pclose.c
#	./Stdio_b/popen.c
#	./Stdio_b/printf.c
#	./Stdio_b/putchar.c
#	./Stdio_b/puts.c
#	./Stdio_b/putw.c
#	./Stdio_b/rewind.c
#	./Stdio_b/scanf.c
#	./Stdio_b/setbuf.c
#	./Stdio_b/setbuffer.c
#	./Stdio_b/setlinebuf.c
#	./Stdio_b/setvbuf.c
#	./Stdio_b/sfstdio.c
#	./Stdio_b/sprintf.c
#	./Stdio_b/sscanf.c
#	./Stdio_b/stdextern.c
#	./Stdio_b/stdstream.c
#	./Stdio_b/tmpfile.c
#	./Stdio_b/ungetc.c
#	./Stdio_b/vfprintf.c
#	./Stdio_b/vfscanf.c
#	./Stdio_b/vprintf.c
#	./Stdio_b/vscanf.c
#	./Stdio_b/vsprintf.c
#	./Stdio_b/vsscanf.c
#	./Stdio_b/fpurge.c
#	./Stdio_b/Makefile
#	./Stdio_b/sfstdhdr.sh
#	./Stdio_b/putc.c
#	./Stdio_b/getc.c
#	./Sfio_f/_sfclrerr.c
#	./Sfio_f/_sfecvt.c
#	./Sfio_f/_sfeof.c
#	./Sfio_f/_sferror.c
#	./Sfio_f/_sffcvt.c
#	./Sfio_f/_sffileno.c
#	./Sfio_f/_sfgetc.c
#	./Sfio_f/_sfgetl.c
#	./Sfio_f/_sfgetu.c
#	./Sfio_f/_sfputc.c
#	./Sfio_f/_sfputd.c
#	./Sfio_f/_sfputl.c
#	./Sfio_f/_sfputu.c
#	./Sfio_f/_sfslen.c
#	./Sfio_f/_sfstacked.c
#	./Sfio_f/_sfulen.c
#	./Sfio_f/Makefile
#	./sfvscanf.c
#	./sfwr.c
#	./sfwrite.c
#	./Iffile
#	./Makefile
#	./makefile
#	./sfpoll.c
#	./Doc/sfio.3
#	./Doc/sfio.tm
#	./README
#	./FILES
#	./Disc/disc.h
#	./Disc/dctee.c
#	./Disc/dcfilter.c
#	./Disc/dchdr.h
#	./Disc/dcsubstream.c
#	./Disc/dcskable.c
#	./sfpkrd.c
# This archive created: Mon Jul  6 09:51:03 1992
export PATH; PATH=/bin:$PATH
mkdir ./Doc ./Disc ./FEATURE ./Sfio_f ./Stdio_s ./Stdio_b
if test -f './FEATURE/atexit'
then
	echo shar: will not over-write existing file "'./FEATURE/atexit'"
else
cat << \SHAR_EOF > './FEATURE/atexit'
#ifndef _def_atexit
#define _def_atexit
#define _lib_atexit	0
#define _lib_onexit	0
#define _lib_on_exit	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/string'
then
	echo shar: will not over-write existing file "'./FEATURE/string'"
else
cat << \SHAR_EOF > './FEATURE/string'
#ifndef _def_string
#define _def_string
#define _lib_bcopy	1
#define _lib_bzero	1
#define _lib_memccpy	1
#define _lib_memchr	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/mmap'
then
	echo shar: will not over-write existing file "'./FEATURE/mmap'"
else
cat << \SHAR_EOF > './FEATURE/mmap'
#ifndef _def_mmap
#define _def_mmap
#define _hdr_mman	0
#define _sys_mman	1
#define _lib_mmap	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/vfork'
then
	echo shar: will not over-write existing file "'./FEATURE/vfork'"
else
cat << \SHAR_EOF > './FEATURE/vfork'
#ifndef _def_vfork
#define _def_vfork
#define _hdr_vfork	1
#define _sys_vfork	0
#define _lib_vfork	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/filio'
then
	echo shar: will not over-write existing file "'./FEATURE/filio'"
else
cat << \SHAR_EOF > './FEATURE/filio'
#ifndef _def_filio
#define _def_filio
#define _hdr_filio	0
#define _sys_filio	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/mktemp'
then
	echo shar: will not over-write existing file "'./FEATURE/mktemp'"
else
cat << \SHAR_EOF > './FEATURE/mktemp'
#ifndef _def_mktemp
#define _def_mktemp
#define _lib_mktemp	1
#define _lib_remove	1
#define _lib_unlink	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/waitpid'
then
	echo shar: will not over-write existing file "'./FEATURE/waitpid'"
else
cat << \SHAR_EOF > './FEATURE/waitpid'
#ifndef _def_waitpid
#define _def_waitpid
#define _lib_waitpid	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/getpagesize'
then
	echo shar: will not over-write existing file "'./FEATURE/getpagesize'"
else
cat << \SHAR_EOF > './FEATURE/getpagesize'
#ifndef _def_getpagesize
#define _def_getpagesize
#define _lib_getpagesize	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/double'
then
	echo shar: will not over-write existing file "'./FEATURE/double'"
else
cat << \SHAR_EOF > './FEATURE/double'
#ifndef _def_double
#define _def_double
#define _long_double	0
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/blksize'
then
	echo shar: will not over-write existing file "'./FEATURE/blksize'"
else
cat << \SHAR_EOF > './FEATURE/blksize'
#ifndef _def_blksize
#define _def_blksize
#define _stat_blksize	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/native'
then
	echo shar: will not over-write existing file "'./FEATURE/native'"
else
cat << \SHAR_EOF > './FEATURE/native'
#ifndef _def_native
#define _def_native
#define _vax_asm	0
#define _i386_cvt	0
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/prototype'
then
	echo shar: will not over-write existing file "'./FEATURE/prototype'"
else
cat << \SHAR_EOF > './FEATURE/prototype'
#ifndef _def_prototype
#define _def_prototype
#define _proto_open	0
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/stdio'
then
	echo shar: will not over-write existing file "'./FEATURE/stdio'"
else
cat << \SHAR_EOF > './FEATURE/stdio'
#ifndef _def_stdio
#define _def_stdio
#define _FILE_cnt	1
#define _FILE_ptr	1
#define _FILE_file	1
#define _FILE_flag	1
#define _FILE_base	1
#define _FILE_bufsiz	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/pragma'
then
	echo shar: will not over-write existing file "'./FEATURE/pragma'"
else
cat << \SHAR_EOF > './FEATURE/pragma'
#ifndef _def_pragma
#define _def_pragma
#define _pragma_weak	0
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/stdarg'
then
	echo shar: will not over-write existing file "'./FEATURE/stdarg'"
else
cat << \SHAR_EOF > './FEATURE/stdarg'
#ifndef _def_stdarg
#define _def_stdarg
#define _CC_stdarg	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/poll'
then
	echo shar: will not over-write existing file "'./FEATURE/poll'"
else
cat << \SHAR_EOF > './FEATURE/poll'
#ifndef _def_poll
#define _def_poll
#define _lib_poll	1
#define _lib_select	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './FEATURE/peek'
then
	echo shar: will not over-write existing file "'./FEATURE/peek'"
else
cat << \SHAR_EOF > './FEATURE/peek'
#ifndef _def_peek
#define _def_peek
#define _stream_peek	1
#define _socket_peek	1
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './sfclose.c'
then
	echo shar: will not over-write existing file "'./sfclose.c'"
else
cat << \SHAR_EOF > './sfclose.c'
#include	"sfhdr.h"

/*	Close a given file stream. If the file stream is associated with
**	a process via sfpopen(), both streams will be closed. The closed
**	stream is synced.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
sfclose(reg Sfile_t *f)
#else
sfclose(f)
reg Sfile_t	*f;
#endif
{
	reg int		rv, local;
	reg Sfdisc_t	*disc;

	if(!f)
		return -1;

	GETLOCAL(f,local);
	if(SFFROZEN(f))
		return -1;

	/* closing a stack of streams */
	while(f->push)
	{	reg Sfile_t*	pop;

		if(!(pop = (*_Sfstack)(f,NIL(Sfile_t*))) )
			return -1;
		if(sfclose(pop) < 0)
		{	(*_Sfstack)(f,pop);
			return -1;
		}
	}

	/* this is from popen */
	if(f->flags&SF_PROCESS)
		return (*_Sfpclose)(f);

	if(f->disc == _Sfudisc)	/* closing the ungetc stream */
		f->disc = NIL(Sfdisc_t*);
	/* sync file pointer */
	else if((f->flags&(SF_WRITE|SF_SHARE)) && sfsync(f) < 0)
		return -1;

	/* terminate disciplines */
	if((disc = f->disc) != NIL(Sfdisc_t*))
	{	/* let disciplines know that f is closing */
		while(disc)
		{	reg Sfdisc_t* next = disc->disc;

			if(disc->exceptf &&
			   (*disc->exceptf)(f,local ? SF_NEW : SF_CLOSE,disc) < 0)
				return -1;

			/* here we have to be careful that the discipline stack
			   has not been altered by the application */
			if(disc == f->disc)
			{	if(!local)
					f->disc = next;
				disc = next;
			}
			else	disc = f->disc;
		}
	}

	if(!local)
	{	/* remove from pool */
		if(!_Sfpmove)
		{	/* can only be in the discrete pool */
			if(f->back)
				f->back->fore = f->fore;
			else	_Sfpool.head = f->fore;
			if(f->fore)
				f->fore->back = f->back;
		}
		else if((*_Sfpmove)(f,-1) < 0)
			return -1;

		f->disc = NIL(Sfdisc_t*);
	}

	/* tell the register function */
	if(_Sfnotify)
		(*_Sfnotify)(f,SF_CLOSE,f->file);

	/* zap any associated sfgetr buffer */
	if(_Sfgetr)
		(*_Sfgetr)(f,-1);

	if(f->data && (!local || (f->flags&(SF_STRING|SF_MMAP))))
	{	/* free buffer */
#ifdef MAP_TYPE
		if(f->flags&SF_MMAP)
			munmap((caddr_t)f->data,f->size);
		else
#endif
		if(f->flags&SF_MALLOC)
			free((char*)f->data);

		f->data = NIL(uchar*);
		f->size = 0;
	}

	/* zap the file descriptor */
	if(f->file >= 0 && !(f->flags&SF_STRING))
		CLOSE(f->file);
	f->file = -1;

	f->mode = SF_AVAIL|SF_LOCK;	/* prevent muttiple closings */

	if(!local && f != sfstdin && f != sfstdout && f != sfstderr)
		SFFREE(f);
	else
	{	f->flags = 0;
		f->here = 0L;
		f->extent = -1L;
		f->endb = f->endr = f->endw = f->next = f->data;
	}

	return 0;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfclrlock.c'
then
	echo shar: will not over-write existing file "'./sfclrlock.c'"
else
cat << \SHAR_EOF > './sfclrlock.c'
#include	"sfhdr.h"

/*	Function to clear a locked stream.
**	This is useful for programs that longjmp from the mid of an sfio function.
**	There is no guarantee on data integrity in such a case.
**
**	Written by Kiem-Phong Vo (07/20/90).
*/
#ifdef __STD_C
sfclrlock(reg Sfile_t* f)
#else
sfclrlock(f)
reg Sfile_t	*f;
#endif
{
	f->mode &= ~(SF_LOCK|SF_PEEK);
	return SFSET(f,f->flags,1);
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfcvt.c'
then
	echo shar: will not over-write existing file "'./sfcvt.c'"
else
cat << \SHAR_EOF > './sfcvt.c'
#include	"sfhdr.h"

/*	Convert a floating point value to ASCII
**	This function unifies fcvt() and ecvt() in libc.a.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

static char	*Inf = "Inf", *Zero = "0";
#define INTPART		(IDIGITS/2)
#define INFINITE	((_Sfi = 3), Inf)
#define ZERO		((_Sfi = 1), Zero)

#ifdef __STD_C
char *_sfcvt(reg double dval, int n_digit, int* decpt, int* sign, int e_format)
#else
char *_sfcvt(dval,n_digit,decpt,sign,e_format)
reg double	dval;		/* value to convert */
int		n_digit;	/* number of digits wanted */
int		*decpt;		/* to return decimal point */
int		*sign;		/* to return sign */
int		e_format;	/* doing e-format */
#endif
{
#if _i386_cvt	/* native *cvt() can do this faster than we can */
	reg char	*sp;
	extern char	*fcvt(), *ecvt();
	sp = e_format ? ecvt(dval,n_digit,decpt,sign) : fcvt(dval,n_digit,decpt,sign);
	_Sfi = strlen(sp);
	return sp;
#else
	reg long	n, v;
	reg char	*sp, *ep, *buf, *endsp;
	static char	*Buf;

	/* set up local buffer */
	if(!Buf && !(Buf = malloc(MAXDIGITS)))
		return INFINITE;

	*sign = *decpt = 0;
	if(dval == 0.)
		return ZERO;
	else if(*sign = (dval < 0.))	/* assignment = */
		dval = -dval;

	n = 0;
	if(dval >= (double)MAXLONG)
	{	/* scale to a small enough number to fit an int */
		v = MAXEXP10-1;
		do
		{	if(dval < _Sfpos10[v])
				v -= 1;
			else
			{
				dval *= _Sfneg10[v];
				if((n += (1<<v)) >= IDIGITS)
					return INFINITE;
			}
		} while(dval >= (double)MAXLONG);
	}
	else if(dval < 1e-1)
	{	/* scale to get rid of leading zeros */
		v = MAXEXP10-1;
		do
		{	if(dval > _Sfneg10[v])
				v -= 1;
			else
			{
				dval *= _Sfpos10[v];
				if((n -= (1<<v)) <= -IDIGITS)
					return ZERO;
			}
		} while(dval < 1e-1);
	}
	*decpt = (int)n;

	buf = sp = Buf+INTPART;
	if((v = (int)dval) != 0)
	{	/* translate the integer part */
		dval -= (double)v;

		sfucvt(v,sp,n,ep);

		n = buf-sp;
		if((*decpt += (int)n) >= IDIGITS)
			return INFINITE;
		buf = sp;
		sp = Buf+INTPART;
	}
	else	n = 0;

	/* remaining number of digits to compute; add 1 for later rounding */
	n = ((e_format || *decpt <= 0) ? 1 : *decpt+1) - n;
	if(n_digit > 0)
		n += n_digit;

	if((ep = (sp+n)) > (endsp = Buf+(MAXDIGITS-2)))
		ep = endsp; 
	if(sp > ep)
		sp = ep;
	else while(sp < ep)
	{	/* generate fractional digits */
		if(dval <= 0.)
		{	/* fill with 0's */
			do { *sp++ = '0'; } while(sp < ep);
			goto done;
		}
		*sp++ = (char)('0' + (n = (int)(dval *= 10.)));
		dval -= (double)n;
	}

	if(ep <= buf)
		ep = buf+1;
	else if(ep < endsp)
	{	/* round the last digit */
		*--sp += 5;
		while(*sp > '9')
		{
			*sp = '0';
			if(sp > buf)
				*--sp += 1;
			else
			{	/* next power of 10 */
				*sp = '1';
				*decpt += 1;
				if(!e_format)
				{	/* add one more 0 for %f precision */
					ep[-1] = '0';
					ep += 1;
				}
			}
		}
	}

done:
	*--ep = '\0';
	_Sfi = ep-buf;
	return buf;

#endif /* _i386_cvt */
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfdisc.c'
then
	echo shar: will not over-write existing file "'./sfdisc.c'"
else
cat << \SHAR_EOF > './sfdisc.c'
#include	"sfhdr.h"

/*	Set a new discipline for a stream.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
Sfdisc_t* sfdisc(reg Sfile_t* f, reg Sfdisc_t* disc)
#else
Sfdisc_t* sfdisc(f,disc)
reg Sfile_t	*f;
reg Sfdisc_t	*disc;
#endif
{
	reg Sfdisc_t	*d;
	reg long	(*oldseekf) _ARG_((Sfile_t*, long, int, Sfdisc_t*));
	reg long	(*newseekf) _ARG_((Sfile_t*, long, int, Sfdisc_t*));

	/* can't change discipline if not accessible */
	if(SFFROZEN(f))
		return NIL(Sfdisc_t*);

	/* synchronize before switching to a new discipline */
	if(f->disc == _Sfudisc ||
	   ((f->mode&SF_WRITE) && f->next > f->data) ||
	   ((f->mode&SF_READ) && f->next < f->endb) )
	{	if(sfsync(f) < 0)
			return NIL(Sfdisc_t*);

		/* throw away buffered read data before changing disc */
		if(!(f->flags&SF_STRING) && (f->mode&SF_READ) )
			f->endb = f->endr = f->next = f->data;
	}

	/* save current seek function */
	if(d = f->disc)
		oldseekf = d->seekf;
	else	oldseekf = NIL(long(*)_ARG_((Sfile_t*, long, int, Sfdisc_t*)));

	if(disc == SF_POPDISC)
	{	/* popping, warn the discipline */
		if((d = f->disc) != NIL(Sfdisc_t*))
		{	disc = d->disc;
			if(d->exceptf && (*(d->exceptf))(f,SF_DPOP,d) < 0)
				return NIL(Sfdisc_t*);
		}
		f->disc = disc;
#ifdef MAP_TYPE
		if(!SFNOMAP(f) && (f->flags&SF_MALLOC))
			sfsetbuf(f,NIL(char*),-1);
#endif
		disc = d;
		goto done;
	}

	/* pushing, warn being pushed discipline */
	do
	{	/* loop to handle the case where d may pop itself */
		d = f->disc;
		if(d && d->exceptf && (*(d->exceptf))(f,SF_DPUSH,d) < 0)
			return NIL(Sfdisc_t*);
	} while(d != f->disc);

	/* make sure we are not creating an infinite loop */
	for(; d; d = d->disc)
		if(d == disc)
			return NIL(Sfdisc_t*);

#ifdef MAP_TYPE /* make sure memory mapping is off */
	if(f->flags&SF_MMAP)
	{	f->flags |= SF_SETBUF;
		sfsetbuf(f,NIL(char*),-1);
		f->flags &= ~SF_SETBUF;
	}
#endif

	/* set new disc */
	disc->disc = f->disc;
	f->disc = disc;

done:	/* make sure that seek locations are right */
	if(d = f->disc)
		newseekf = d->seekf;
	else	newseekf = NIL(long(*)_ARG_((Sfile_t*, long, int, Sfdisc_t*)));

	if(newseekf || (newseekf != oldseekf))
	{	reg long	here;
		if((here = SFSK(f,0L,1,f->disc)) < 0)
			f->here = f->extent = -1;
		else
		{	f->here = here;
			f->extent = SFSK(f,0L,2,f->disc);
			SFSK(f,here,0,f->disc);
		}
	}

	if(f->file >= 0 && f->extent < 0)
	{	/* check to see if peeking (sfgetr/sfmove) is ok on this stream */
		for(d = f->disc; d; d = d->disc)
			if(d->readf)
				break;
		if(d)
			f->unmap = SF_NOPEEK;
	}

	return disc;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfdlen.c'
then
	echo shar: will not over-write existing file "'./sfdlen.c'"
else
cat << \SHAR_EOF > './sfdlen.c'
#include	"sfhdr.h"

/*	Return the length of a double value if coded in a portable format
**
**	Written by Kiem-Phong Vo (08/05/90)
*/

#ifdef __STD_C
sfdlen(reg double v)
#else
sfdlen(v)
reg double	v;
#endif
{
#define N_ARRAY		(16*sizeof(double))
	reg int		n, w;
	reg double	x;
	int		exp;

	if(v < 0)
		v = -v;

	/* make the magnitude of v < 1 */
	if(v != 0.)
		v = frexp(v,&exp);
	else	exp = 0;

	for(w = 1; w <= N_ARRAY; ++w)
	{	/* get 2^SF_PRECIS precision at a time */
		n = (int)(x = ldexp(v,SF_PRECIS));
		if((v = x-n) <= 0.)
			break;
	}

	return 1 + sfulen(exp) + w;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfexcept.c'
then
	echo shar: will not over-write existing file "'./sfexcept.c'"
else
cat << \SHAR_EOF > './sfexcept.c'
#include	"sfhdr.h"

/*	Function to handle io exceptions.
**	Written by Kiem-Phong Vo (8/18/90)
*/
#ifdef __STD_C
_sfexcept(reg Sfile_t* f, reg int type, reg int io, reg Sfdisc_t* disc)
#else
_sfexcept(f,type,io,disc)
reg Sfile_t	*f;	/* stream where the exception happened */
reg int		type;	/* io type that was performed */
reg int		io;	/* the io return value that indicated exception */
reg Sfdisc_t	*disc;	/* discipline in use */
#endif
{
	reg int		d, local;
	reg uchar	*data;

	GETLOCAL(f,local);

	if(local)
		f->flags |= io < 0 ? SF_ERROR : SF_EOF;

	if(disc && disc->exceptf)
	{	/* check with discipline function */
		if(local)
			SFOPEN(f);
		d = (*(disc->exceptf))(f,type,disc);
		if(d < 0)
			return SF_EDONE;
		else if(d > 0)
			return SF_EDISC;
	}

	if(f->flags&SF_STRING)
	{	if(type == SF_READ)
			goto chk_stack;
		else if(type != SF_WRITE && type != SF_SEEK)
			return SF_EDONE;
		if(local && io >= 0)
		{	if(f->size >= 0 && !(f->flags&SF_MALLOC))
				goto chk_stack;
			/* extend buffer */
			if((d = f->size) < 0)
				d = 0;
			if(type == SF_SEEK)
				io -= d;
			else if(io <= 0)
				io = SF_GRAIN;
			d = ((d+io+SF_GRAIN-1)/SF_GRAIN)*SF_GRAIN;
			if(f->size > 0)
				data = (uchar*)realloc((char*)f->data,d);
			else	data = (uchar*)malloc(d);
			if(!data)
				goto chk_stack;
			f->flags |= SF_MALLOC;
			f->endb = data + d;
			f->next = data + (f->next - f->data);
			f->endr = f->endw = f->data = data;
			f->size = d;
		}
		return SF_EDISC;
	}

	if(errno == EINTR)
	{	/* if just an interrupt, we can continue */
		errno = 0;
		f->flags &= ~(SF_EOF|SF_ERROR);
		return SF_ECONT;
	}

chk_stack:
	if(local && f->push &&
	   ((type == SF_READ  && f->next >= f->endb) ||
	    (type == SF_WRITE && f->next <= f->data)))
	{	/* pop the stack */
		reg Sfile_t	*pf;
		SFOPEN(f);
		pf = (*_Sfstack)(f,NIL(Sfile_t*));
		if(sfclose(pf) < 0)
		{	(*_Sfstack)(f,pf);
			return SF_EDONE;
		}
		else	return SF_ESTACK;
	}

	return SF_EDONE;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfexit.c'
then
	echo shar: will not over-write existing file "'./sfexit.c'"
else
cat << \SHAR_EOF > './sfexit.c'
#include	"sfhdr.h"

/*
**	ANSI C atexit() to arrange for funcs to be called LIFO on exit()
*/

typedef struct _l_
{	struct _l_	*next;
	void		(*func) _ARG_((void));
} Func_t;

static Func_t	*Flist;

#if !_lib_atexit

#ifdef __STD_C
atexit(void (*func)(void))
#else
atexit(func)
void	(*func)();
#endif
{
	reg Func_t	*f;

	if(!(f = (Func_t*)malloc(sizeof(Func_t))))
		return -1;
	f->func = func;
	f->next = Flist;
	Flist = f;
	return 0;
}

#ifdef __STD_C
void exit(int code)
#else
exit(code)
int	code;
#endif
{
	reg Func_t	*f;

	for(f = Flist; f; f = f->next)
		(*f->func)();

	sfsync(NIL(Sfile_t*));
	_exit(code);
}

#endif /* _lib_atexit */
SHAR_EOF
fi # end of overwriting check
if test -f './sfextern.c'
then
	echo shar: will not over-write existing file "'./sfextern.c'"
else
cat << \SHAR_EOF > './sfextern.c'
#include	"sfhdr.h"

/*	External variables used by sfio
**	Written by Kiem-Phong Vo (06/27/90)
*/

/* global variables used internally to the package */
Sfext_t _Sfextern =
{	{ 0, &_Sfstdin, NIL(Sfpool_t*), NIL(Sfpool_t*) },	/* _Sfpool	*/
	NIL(Sfile_t*),						/* _Sffree	*/
	NIL(Fa_t*),						/* _Fafree	*/
	0,							/* _Sfpage	*/
	0,							/* _Sfmapsize	*/
	NIL(int(*)_ARG_((Sfile_t*,int))),			/* _Sfpmove	*/
	NIL(int(*)_ARG_((Sfile_t*))),				/* _Sfpclose	*/
	NIL(int(*)_ARG_((Sfile_t*, int))),			/* _Sfpmode	*/
	NIL(Sfile_t*(*)_ARG_((Sfile_t*, Sfile_t*))),		/* _Sfstack	*/
	NIL(void(*)_ARG_((Sfile_t*, int, int))),		/* _Sfnotify	*/
	NIL(int(*)_ARG_((Sfile_t*))),				/* _Sfstdio	*/
	{ NIL(int(*)_ARG_((Sfile_t*, char*, int, Sfdisc_t*))),	/* _Sfudisc	*/
	  NIL(int(*)_ARG_((Sfile_t*, const char*, int, Sfdisc_t*))),
	  NIL(long(*)_ARG_((Sfile_t*, long, int, Sfdisc_t*))),
	  NIL(int(*)_ARG_((Sfile_t*, int, Sfdisc_t*))),
	  NIL(Sfdisc_t*)
	}
};

/* accessible to application code for a few fast macro functions */
int	_Sfi;

Sfile_t	_Sfstdin =
{	NIL(uchar*),		/* next */
	NIL(uchar*),		/* endw */
	NIL(uchar*),		/* endr */
	NIL(uchar*),		/* endb */
	NIL(Sfile_t*),		/* push */
	SF_READ,		/* flags */
	0,			/* file */

	0L,			/* extent */
	0L,			/* here */
	NIL(uchar*),		/* data */
	0,			/* size */
	0,			/* unmap */
	"",			/* tiny */
	(SF_READ|SF_INIT),	/* mode */
	NIL(Sfdisc_t*),		/* disc */
	&_Sfpool,		/* pool */
	NIL(Sfile_t*),		/* back */
	&_Sfstdout		/* fore */
};
Sfile_t _Sfstdout =
{	NIL(uchar*),		/* next */
	NIL(uchar*),		/* endw */
	NIL(uchar*),		/* endr */
	NIL(uchar*),		/* endb */
	NIL(Sfile_t*),		/* push */
	SF_WRITE,		/* flags */
	1,			/* file */

	0L,			/* extent */
	0L,			/* here */
	NIL(uchar*),		/* data */
	0,			/* size */
	0,			/* unmap */
	"",			/* tiny */
	(SF_WRITE|SF_INIT),	/* mode */
	NIL(Sfdisc_t*),		/* disc */
	&_Sfpool,		/* pool */
	&_Sfstdin,		/* back */
	&_Sfstderr		/* fore */
};
Sfile_t _Sfstderr =
{	NIL(uchar*),		/* next */
	NIL(uchar*),		/* endw */
	NIL(uchar*),		/* endr */
	NIL(uchar*),		/* endb */
	NIL(Sfile_t*),		/* push */
	SF_WRITE,		/* flags */
	2,			/* file */

	0L,			/* extent */
	0L,			/* here */
	NIL(uchar*),		/* data */
	0,			/* size */
	0,			/* unmap */
	"",			/* tiny */
	(SF_WRITE|SF_INIT),	/* mode */
	NIL(Sfdisc_t*),		/* disc */
	&_Sfpool,		/* pool */
	&_Sfstdout,		/* back */
	NIL(Sfile_t*)		/* fore */
};
SHAR_EOF
fi # end of overwriting check
if test -f './sffilbuf.c'
then
	echo shar: will not over-write existing file "'./sffilbuf.c'"
else
cat << \SHAR_EOF > './sffilbuf.c'
#include	"sfhdr.h"

/*	Fill the buffer of a stream with data.
**	If n < 0, sffilbuf() attempts to fill the buffer if it's empty.
**	If n == 0, if the buffer is not empty, just return the first byte;
**		otherwise fill the buffer and return the first byte.
**	If n > 0, even if the buffer is not empty, try a read to get as
**		close to n as possible. n is reset to -1 if stack pops.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
_sffilbuf(reg Sfile_t* f, reg int n)
#else
_sffilbuf(f,n)
reg Sfile_t	*f;	/* fill the read buffer of this stream */
reg int		n;	/* see above */
#endif
{
	reg int		r;

	for(;; n = -1, SFopen(f))
	{	/* check mode */
		if(f->mode != SF_READ && _sfmode(f,SF_READ) < 0)
			return -1;
		SFLOCK(f);

		/* current extent of available data */
		if((r = f->endb-f->next) > 0)
		{	if(n <= 0 || (f->flags&SF_STRING))
				break;
		}
		else if(!(f->flags&SF_STRING))
			f->next = f->endb = f->endr = f->data;

		/* SFRD takes care of discipline read and stack popping */
		if((r = f->size - (f->endb-f->data)) > n && n > 0)
			r = n;
		if((r = SFRD(f,(char*)f->endb,r,f->disc)) >= 0)
		{	if((r = f->endb - f->next) < 0)
				r = 0;
			break;
		}
	}

	SFOPEN(f);
	return (n == 0) ? (r > 0 ? (int)(*f->next++) : EOF) : r;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfflsbuf.c'
then
	echo shar: will not over-write existing file "'./sfflsbuf.c'"
else
cat << \SHAR_EOF > './sfflsbuf.c'
#include	"sfhdr.h"

/*	Write a buffer out to a file descriptor or
**	extending a buffer for a SF_STRING stream.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
_sfflsbuf(reg Sfile_t* f, reg int c)
#else
_sfflsbuf(f,c)
reg Sfile_t	*f;	/* write out the buffered content of this stream */
reg int		c;	/* if c>=0, c is also written out */ 
#endif
{
	reg int		n, w;
	reg uchar	*data;
	uchar		outc;
	int		inpc = c;

	for(;; SFopen(f))
	{	/* check stream mode */
		if(f->mode != SF_WRITE && _sfmode(f,SF_WRITE) < 0)
			return -1;
		SFLOCK(f);

		/* current data extent */
		n = f->next - (data = f->data);

		if(n == f->size && (f->flags&SF_STRING))
		{	/* extend string stream buffer */
			if(SFWR(f,(char*)data,1,f->disc) >= 0 ||
			   (n = f->next - f->data) == f->size)
			{	SFOPEN(f);
				return -1;
			}
		}

		if(c >= 0)
		{	/* write into buffer */
			if(n < f->size)
			{	*f->next++ = c;
				if(c != '\n' ||
				   !(f->flags&SF_LINE) || (f->flags&SF_STRING))
					break;
				c = -1;
				n += 1;
			}
			else if(n == 0)
			{	/* unbuffered io */
				outc = (uchar)c;
				data = &outc;
				c = -1;
				n = 1;
			}
		}

		/* writing data */
		if(n == 0 || (f->flags&SF_STRING))
			break;
		else if((w = SFWR(f,(char*)data,n,f->disc)) > 0)
		{	if((n -= w) > 0) /* save unwritten data, then resume */
				memcpy((char*)f->data,(char*)data+w,n);
			f->next = f->data+n;
			if(n == 0 && c < 0)
				break;
		}
		else if(w == 0)
			return -1;
		else if(c < 0)
			break;
	}

	SFOPEN(f);
	return inpc < 0 ? (f->endb-f->next) : inpc;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfgetd.c'
then
	echo shar: will not over-write existing file "'./sfgetd.c'"
else
cat << \SHAR_EOF > './sfgetd.c'
#include	"sfhdr.h"

/*	Read a portably coded double value
**
**	Written by Kiem-Phong Vo (08/05/90)
*/

#ifdef __STD_C
double sfgetd(Sfile_t* f)
#else
double sfgetd(f)
Sfile_t	*f;
#endif
{
	reg uchar	*s, *ends, c;
	reg double	v;
	reg int		p, sign, exp;

	if((sign = sfgetc(f)) < 0 || (exp = (int)sfgetu(f)) < 0)
		return -1.;

	SFLOCK(f);

	v = 0.;
	for(;;)
	{	/* fast read for data */
		if(SFRPEEK(f,s,p) <= 0)
		{	f->flags |= SF_ERROR;
			v = -1.;
			goto done;
		}

		for(ends = s+p; s < ends; )
		{
			c = *s++;
			v += SFUVALUE(c);
			v = ldexp(v,-SF_PRECIS);
			if(!(c&SF_MORE))
			{	f->next = s;
				goto done;
			}
		}
		f->next = s;
	}

done:
	v = ldexp(v,(sign&02) ? -exp : exp);
	if(sign&01)
		v = -v;
	SFOPEN(f);
	return v;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfgetl.c'
then
	echo shar: will not over-write existing file "'./sfgetl.c'"
else
cat << \SHAR_EOF > './sfgetl.c'
#include	"sfhdr.h"

/*	Read a long value coded in a portable format.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
long _sfgetl(reg Sfile_t* f)
#else
long _sfgetl(f)
reg Sfile_t	*f;
#endif
{
	reg uchar	*s, *ends, c;
	reg int		p;
	reg long	v;

	if(!(_Sfi&SF_MORE))	/* must be a small negative number */
		return -SFSVALUE(_Sfi)-1;

	SFLOCK(f);
	v = SFUVALUE(_Sfi);
	for(;;)
	{
		if(SFRPEEK(f,s,p) <= 0)
		{	f->flags |= SF_ERROR;
			v = -1L;
			goto done;
		}
		for(ends = s+p; s < ends;)
		{
			c = *s++;
			if(c&SF_MORE)
				v = ((ulong)v << SF_UBITS) | SFUVALUE(c);
			else
			{	/* special translation for this byte */
				v = ((ulong)v << SF_SBITS) | SFSVALUE(c);
				f->next = s;
				v = (c&SF_SIGN) ? -v-1 : v;
				goto done;
			}
		}
		f->next = s;
	}
done :
	SFOPEN(f);
	return v;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfgetr.c'
then
	echo shar: will not over-write existing file "'./sfgetr.c'"
else
cat << \SHAR_EOF > './sfgetr.c'
#include	"sfhdr.h"

/*	Read a record delineated by a character.
**	The record length can be accessed via sfslen().
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

/* to maintain separate stream buffers for sfgetr() */
typedef struct _getr_
{
	struct _getr_	*next;
	Sfile_t		*sfp;
	int		sfs;	/* string length */
	int		sfn;	/* buffer length */
	uchar		sfb[1];
} Sfgetr_t;

#define SF_GETR	128	/* increase buffer by multiple of this */
static Sfgetr_t	*Sfs;	/* live sfgetr() streams */

#ifdef __STD_C
static int _sfgetr(reg Sfile_t* f, reg int size)
#else
static int _sfgetr(f,size)
reg Sfile_t*	f;
reg int		size;	/* closing if size < 0 */
#endif
{
	reg Sfgetr_t	*last, *gp;

	/* self-advertising */
	_Sfgetr = _sfgetr;

	last = NIL(Sfgetr_t*);
	for(gp = Sfs; gp != NIL(Sfgetr_t*); last = gp, gp = gp->next)
		if(gp->sfp == f)
			break;
	if(gp)
	{	/* unhook it from current position */
		if(last)
			last->next = gp->next;
		else	Sfs = gp->next;
	}

	if(size < 0)	/* closing stream */
	{	if(gp)
			free((char*)gp);
		return 0;
	}
	else if(size > 0)
	{	/* inserting, make buffer if nothing yet */
		size = ((size + SF_GETR-1)/SF_GETR)*SF_GETR;
		if(!gp || size > gp->sfn)
		{	if(!(last = (Sfgetr_t*)malloc(size+sizeof(Sfgetr_t))))
				size = -1;
			else
			{	if(gp)
				{	memcpy((char*)last,(char*)gp,size+sizeof(Sfgetr_t));
					free((char*)gp);
				}
				gp = last;
				gp->sfn = size;
				gp->sfs = 0;
			}
		}
	}
	else if(!gp)
		size = -1;

	/* insert at the front */
	if(gp)
	{	gp->sfp = f;
		gp->next = Sfs;
		Sfs = gp;
	}

	return size;
}

#ifdef __STD_C
char *sfgetr(reg Sfile_t *f, reg int rc, int string)
#else
char *sfgetr(f,rc,string)
reg Sfile_t	*f;	/* stream to read from	*/
reg int		rc;	/* record separator	*/
int		string; /* <0: get previous, 0: no nul, 1: change rc to nul */
#endif
{
	reg int		n;
	reg uchar	*s, *ends, *us;
	reg int		un, found;

	/* buffer to be returned */
	us = NIL(uchar*);
	un = 0;
	found = 0;

	/* set the right mode */
	if(rc < 0 || (f->mode != SF_READ && _sfmode(f,SF_READ) < 0) )
		goto done;
	SFLOCK(f);

	if(string < 0) /* return the previously read string only */
	{	if(_sfgetr(f,0) >= 0 && (un = Sfs->sfs) > 0)
			us = Sfs->sfb;
		goto done;
	}

	while(!found)
	{	/* fill buffer if necessary */
		if((n = (ends = f->endb) - (s = f->next)) <= 0)
		{	/* if stream device, try to peek-read exactly 1 record */
			if(f->extent < 0 && (f->flags&SF_SHARE) && f->unmap != SF_NOPEEK)
			{	/* n>=0 means rc is last byte in the buffer */
				if((n = _sfpkrd(f,rc,1L)) >= 0)
				{	s = n ? f->endb-1 : f->endb;
					n = (ends = f->endb) - f->next;
					goto do_copy;
				}
				/* we don't know about rc yet */
				else	n = (ends = f->endb) - (s = f->next);
			}

			/* fill buffer the conventional way */
			if(n <= 0 && SFRPEEK(f,s,n) <= 0)
			{	us = NIL(uchar*);
				goto done;
			}
			else	ends = s+n;
		}

#if _vax_asm	/* rc is r10, n is r9, s is r8, ends is r7 */
		asm( "locc	r10,r9,(r8)" );	/* find rc */
		asm( "movl	r1,r8" );	/* set s to be where it is */
#else
#if _lib_memchr
		if(!(s = (uchar*)memchr((char*)s,rc,n)))
			s = ends;
#else
		while(s < ends)
		{	if(*s++ == rc)
			{	s -= 1;
				break;
			}
		}
#endif
#endif
	do_copy:
		if(s < ends)
		{	s += 1;		/* include the separator */
			found = 1;
			if(!us && (!string || !(f->flags&(SF_STRING|SF_BOTH))) )
			{	/* just returning the buffer */
				us = f->next;
				un = s - f->next;
				f->next = s;
				if(string && rc != 0)
					f->flags |= SF_JNKBUF;
				goto done;
			}
		}

		/* amount to be read */
		n = s - f->next;

		/* get internal buffer */
		if(_sfgetr(f,un+n) < un+n)
		{	us = NIL(uchar*);
			goto done;
		}
		else	us = Sfs->sfb;

		/* now copy data */
		s = us+un;
		un += n;
		ends = f->next;
		f->next += n;
#if vax_asm
		asm( "movc3	r9,(r7),(r8)" );
#else
		MEMCPY(s,ends,n);
#endif
	}

done:
	_Sfi = un;
	if(found)
	{	if(string)
			us[un-1] = '\0';
	}
	else if(Sfs && Sfs->sfp == f)
	{	/* prepare for a call with string < 0 */
		Sfs->sfs = un;
		if(un < Sfs->sfn)
			Sfs->sfb[un] = '\0';
	}

	SFOPEN(f);
	return (char*)us;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfgetu.c'
then
	echo shar: will not over-write existing file "'./sfgetu.c'"
else
cat << \SHAR_EOF > './sfgetu.c'
#include	"sfhdr.h"

/*	Read an unsigned long value coded in a portable format.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
ulong _sfgetu(reg Sfile_t* f)
#else
ulong _sfgetu(f)
reg Sfile_t	*f;
#endif
{
	reg uchar	*s, *ends, c;
	reg int		p;
	reg ulong	v;

	if(f->mode != SF_READ && _sfmode(f,SF_READ) < 0)
		return (ulong)(-1L);

	SFLOCK(f);
	v = SFUVALUE(_Sfi);
	for(;;)
	{
		if(SFRPEEK(f,s,p) <= 0)
		{	f->flags |= SF_ERROR;
			v = (ulong)(-1L);
			goto done;
		}
		for(ends = s+p; s < ends;)
		{
			c = *s++;
			v = (v << SF_UBITS) | SFUVALUE(c);
			if(!(c&SF_MORE))
			{
				f->next = s;
				goto done;
			}
		}
		f->next = s;
	}
done:
	SFOPEN(f);
	return v;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfhdr.h'
then
	echo shar: will not over-write existing file "'./sfhdr.h'"
else
cat << \SHAR_EOF > './sfhdr.h'
/*	Internal definitions for sfio.
**	Written by Kiem-Phong Vo (07/16/90)
**	AT&T Bell Laboratories
*/

/* the parts of Sfio_t private to sfio functions */
#define _SFIO_PRIVATE \
	long		extent;	/* current file	size		*/ \
	long		here;	/* current physical location	*/ \
	unsigned char	*data;	/* data buffer			*/ \
	int		size;	/* buffer size			*/ \
	unsigned char	unmap;	/* number of munmap by sfseek	*/ \
	unsigned char	tiny[1];/* for unbuffered read stream	*/ \
	unsigned short	mode;	/* current io mode		*/ \
	struct _sfdc_	*disc;	/* discipline			*/ \
	struct _sfpl_	*pool;	/* the pool containing this	*/ \
	struct _sfio_	*back;	/* backward link in pool	*/ \
	struct _sfio_	*fore;	/* forward link in pool		*/

#include	"sfio.h"

/* file system info */
#if _PACKAGE_ast
#include	<ast.fs.h>
#else
#include	<sys/types.h>
#include	<sys/stat.h>
#include	"FEATURE/blksize"
#endif
#if _mem_st_blksize_stat
#define _stat_blksize 1
#endif

#include	<errno.h>
#include	<ctype.h>
#include	<fcntl.h>
#ifdef __STD_C
#include	<stdarg.h>
#else
#include	<varargs.h>
#endif

/* short-hands */
#define uchar		unsigned char
#define ulong		unsigned long
#define uint		unsigned int
#define reg		register
#define NIL(p)		((p)0)

#ifndef S_ISDIR
#define S_ISDIR(m)	(((m)&S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISREG
#define S_ISREG(m)	(((m)&S_IFMT) == S_IFREG)
#endif
#ifndef S_ISCHR
#define S_ISCHR(m)	(((m)&S_IFMT) == S_IFCHR)
#endif

#ifndef S_ISFIFO
#ifdef S_IFIFO
#define S_ISFIFO(m)	(((m)&S_IFIFO) == S_IFIFO)
#else
#define S_ISFIFO(m)	(0)
#endif
#endif

/* see if we can use memory mapping for io */
#include	"FEATURE/mmap"
#if _lib_mmap
#	if _hdr_mman
#		include	<mman.h>
#	else
#		if _sys_mman
#			include	<sys/mman.h>
#		endif
#	endif
#endif

/* test for an atexit function */
#if !_PACKAGE_ast
#include	"FEATURE/atexit"
#	if !_lib_atexit
#		if _lib_onexit
#			define atexit		onexit
#		else
#			if _lib_on_exit
#			define atexit(f)	on_exit(f,NIL(char*))
#			endif
#		endif
#		ifdef atexit
#			undef _lib_atexit
#			define _lib_atexit	1
#		endif
#	endif
#endif

/* see if there is a getpagesize function */
#include	"FEATURE/getpagesize"

/* see if some form of native code should be used */
#include	"FEATURE/native"

/* see if various string functions exist */
#include	"FEATURE/string"

/* Research UNIX file control stuff */
#include	"FEATURE/filio"
#include	"FEATURE/mktemp"

/* see if there are prototypes of certain system calls */
#include	"FEATURE/prototype"

/* stream pool structure. */
typedef struct _sfpl_
{
	int		mode;		/* type of pool			*/
	Sfile_t		*head;		/* head of pool's stream list	*/
	struct _sfpl_	*back;		/* linked list of pools		*/
	struct _sfpl_	*fore;
} Sfpool_t;

/* extension structures for sfvprintf/sfvscanf */
typedef int(*Argf_s)_ARG_((char,char*,uint));
typedef int(*Extf_s)_ARG_((Sfile_t*,int,int,char**));
typedef int(*Argf_p)_ARG_((int,char*));
typedef int(*Extf_p)_ARG_((char*,int,int,char*));
typedef struct _fa_
{
	char		*form;		/* format string		*/
	va_list		args;		/* corresponding arglist	*/
	union
	{ Argf_s	s;		/* argf for sfvscanf		*/
	  Argf_p	p;		/* argf for sfvprintf		*/
	}		argf;
	union
	{ Extf_s	s;		/* extf for sfvscanf		*/
	  Extf_p	p;		/* extf for sfvprintf		*/
	}		extf;
	struct _fa_	*next;		/* next on the stack		*/
} Fa_t;

/* memory management for the Fa_t structures */
#define FAMALLOC(f)	((f = _Fafree) ? (_Fafree = f->next, f) : \
				(f = (Fa_t*)malloc(sizeof(Fa_t))))
#define FAFREE(f)	(f->next = _Fafree, _Fafree = f)

/* local variables used across sf-functions */
#define _Sfpool		(_Sfextern.sf_pool)
#define _Sffree		(_Sfextern.sf_free)
#define _Fafree		(_Sfextern.fa_free)
#define _Sfpage		(_Sfextern.sf_page)
#define _Sfmapsize	(_Sfextern.sf_mapsize)
#define _Sfpmove	(_Sfextern.sf_pmove)
#define _Sfpclose	(_Sfextern.sf_pclose)
#define _Sfpmode	(_Sfextern.sf_pmode)
#define _Sfstack	(_Sfextern.sf_stack)
#define _Sfnotify	(_Sfextern.sf_notify)
#define _Sfstdio	(_Sfextern.sf_stdio)
#define _Sfudisc	(&(_Sfextern.sf_udisc))
#define _Sfgetr		(_Sfextern.sf_getr)
typedef struct _sfext_
{
	Sfpool_t	sf_pool;
	Sfile_t		*sf_free;
	Fa_t		*fa_free;
	int		sf_page;
	int		sf_mapsize;
	int		(*sf_pmove)_ARG_((Sfile_t*, int));
	int		(*sf_pclose)_ARG_((Sfile_t*));	
	int		(*sf_pmode)_ARG_((Sfile_t*, int));	
	Sfile_t		*(*sf_stack)_ARG_((Sfile_t*, Sfile_t*));
	void		(*sf_notify)_ARG_((Sfile_t*, int, int));
	int		(*sf_stdio)_ARG_((Sfile_t*));
	Sfdisc_t	sf_udisc;
	int		(*sf_getr)_ARG_((Sfile_t*, int));
} Sfext_t;

/* function to clear an sfio structure */
#define SFCLEAR(f) \
	((f)->next = (f)->endw = (f)->endr = (f)->endb = (f)->data = NIL(uchar*), \
	 (f)->flags = 0, (f)->file = -1, (f)->extent = -1L, (f)->here = 0L, \
	 (f)->unmap = 0, (f)->mode = 0, (f)->size = 0, (f)->disc = NIL(Sfdisc_t*), \
	 (f)->pool = NIL(Sfpool_t*), (f)->push = (f)->back = (f)->fore = NIL(Sfile_t*))

/* get the real value of a byte in a coded long or ulong */
#define SFUVALUE(v)	(((ulong)(v))&(SF_MORE-1))
#define SFSVALUE(v)	((( long)(v))&(SF_SIGN-1))

/* amount of precision to get in each iteration during coding of doubles */
#define SF_PRECIS	(SF_UBITS-1)

/* grain size for buffer increment */
#define SF_GRAIN	1024

/* NOTE: these flags share the same space with the public flags */
#define SF_JNKBUF	0001000	/* buffer must be junked on sfseek	*/
#define SF_SETBUF	0002000	/* must use buffering			*/
#define SF_MMAP		0004000	/* in memory mapping mode		*/
#define SF_PROCESS	0010000	/* this stream is sfpopen		*/
#define SF_BOTH		0020000	/* both read/write			*/
#define SF_HOLE		0040000	/* a hole of zero's was created		*/

/* bits for the mode field */
#define SF_INIT		0000004	/* stream still to be initialized	*/
#define SF_SYNC		0000010	/* stream was synced			*/
#define SF_PUSH		0000020	/* stream has been pushed		*/
#define SF_LOCK		0000040	/* stream is locked for io op		*/
#define SF_POOL		0000100	/* stream is in a pool but not current	*/
#define SF_PEEK		0000200	/* there is a pending peek		*/
#define SF_LOCAL	0010000	/* sentinel for a local call		*/
#define SF_STDIO	0020000	/* given up the buffer to stdio		*/
#define SF_AVAIL	0040000	/* was closed, available for reuse	*/
#define SF_OPEN		0100000	/* file descriptor was from sfopen()	*/

/* bits for the f->unmap field: used for peeking unseekable devices */
#define SF_IPEEK	0000001	/* can be peeked as a stream device	*/
#define SF_SPEEK	0000002	/* can be peeked as a socket		*/
#define SF_NOPEEK	0000200	/* can't peek on this device		*/

/* short-hand for common stream types */
#define SF_RDWR		(SF_READ|SF_WRITE)
#define SF_RDSTR	(SF_READ|SF_STRING)
#define SF_WRSTR	(SF_WRITE|SF_STRING)

/* exception types */
#define SF_EDONE	0	/* stop this operation and return	*/
#define SF_EDISC	1	/* discipline says it's ok		*/
#define SF_ESTACK	2	/* stack was popped			*/
#define SF_ECONT	3	/* can continue normally		*/

#define SETLOCAL(f)	((f)->mode |= SF_LOCAL)
#define GETLOCAL(f,v)	((v) = ((f)->mode&SF_LOCAL), (f)->mode &= ~SF_LOCAL)
#define SFSK(f,a,o,d)	(SETLOCAL(f),sfsk(f,a,o,d))
#define SFRD(f,b,n,d)	(SETLOCAL(f),sfrd(f,b,n,d))
#define SFWR(f,b,n,d)	(SETLOCAL(f),sfwr(f,b,n,d))
#define SFSET(f,flg,i)	(SETLOCAL(f),sfset(f,flg,i))

/* set&test for the bottomless bit bucket */
#define SETDEVNULL(f)	((f)->extent = -2L)
#define ISDEVNULL(f)	((f)->extent == -2L)

/* fast peek of a stream */
#define SFAVAIL(f,s,n)	((n) = (f)->endb - ((s) = (f)->next) )
#define SFRPEEK(f,s,n)	(SFAVAIL(f,s,n) > 0 ? (n) : \
				(SFopen(f), (n) = _sffilbuf(f,-1), SFLOCK(f), \
				 (s) = (f)->next, (n)) )
#define SFWPEEK(f,s,n)	(SFAVAIL(f,s,n) > 0 ? (n) : \
				(SFopen(f), (n) = _sfflsbuf(f,-1), SFLOCK(f), \
				 (s) = (f)->next, (n)) )

/* malloc and free of streams */
#define SFFREE(f)	(f->fore = _Sffree, _Sffree = f)
#define SFALLOC(f)	((f = _Sffree) ? (_Sffree = f->fore, f) : \
				   (f = (Sfile_t*)malloc(sizeof(Sfile_t))))

/* lock/open a stream */
#define SFlock(f)	((f)->mode |= SF_LOCK)
#define SFopen(f)	((f)->mode &= ~SF_LOCK)
#define SFLOCK(f)	(SFlock(f), (f)->endr = (f)->endw = (f)->data)
#define SFOPEN(f)	(SFopen(f), \
			 (f)->endr = ((f)->mode == SF_READ) ? (f)->endb : (f)->data, \
			 (f)->endw = ((f)->mode == SF_WRITE) ? (f)->endb : (f)->data)

/* check to see if the stream can be accessed */
#define SFFROZEN(f)	((f)->mode&(SF_PUSH|SF_LOCK|SF_PEEK) ? 1 : \
			 ((f)->mode&SF_STDIO) ? (*_Sfstdio)(f) : 0)

#define SFSETMODE(f)	(((f)->mode&~SF_RDWR) == 0 || _sfmode(f,0) >= 0)

/* test for memory mappability */
#define SFNOMAP(f) (((f)->flags&(SF_SETBUF|SF_STRING|SF_BOTH)) || (f)->disc || \
		    (f)->extent < 0 || ((f)->mode&SF_WRITE))

/* more than this for a line buffer, we might as well flush */
#define HIFORLINE	128

/* safe closing function */
#define CLOSE(f)	{ while(close(f) < 0 && errno == EINTR) errno = 0; }

/* control flags for open() */
#ifndef O_CREAT	/* RESEARCH */
#define NO_OFLAGS
#define O_CREAT		004
#define O_TRUNC		010
#define O_APPEND	020

#ifndef O_RDONLY
#define	O_RDONLY	000
#endif
#ifndef O_WRONLY
#define O_WRONLY	001
#endif
#ifndef O_RDWR
#define O_RDWR		002
#endif
#endif

#define	RADIX	64	/* maximum %r conversion base */

/* floating point to ascii conversion */
#define MAXLONG		((long)(((ulong)~0L) >> 1))
#define MAXEXP10	6
#define MAXPOW10	(1 << MAXEXP10)
#define FDIGITS		60		/* max allowed fractional digits */
#define IDIGITS		1536		/* max number of digits in int part */
#define MAXDIGITS	(((FDIGITS+IDIGITS)/sizeof(int) + 1)*sizeof(int))

/* tables for numerical translation */
#define _Sfpos10	(_Sftable.sf_pos10)
#define _Sfneg10	(_Sftable.sf_neg10)
#define _Sfdec		(_Sftable.sf_dec)
#define _Sfv36		(_Sftable.sf_v36)
#define _Sfvmax		(_Sftable.sf_vmax)
#define _Sfdigits	(_Sftable.sf_digits)
typedef struct _sftab_
{
	double	sf_pos10[MAXEXP10];	/* positive powers of 10		*/
	double	sf_neg10[MAXEXP10];	/* negative powers of 10		*/
	char	sf_dec[200];		/* ascii representations of values < 100 */
	char	sf_v36[128];		/* digit translation for base <= 36	*/
	char	sf_vmax[128];		/* digit translation for base > 36	*/
	char	*sf_digits;		/* digits for non-standard bases	*/ 
} Sftab_t;

/* sfucvt() converts decimal integers to ASCII */
#define SFDIGIT(v,scale,digit) \
	{ if(v < 5*scale) \
		if(v < 2*scale) \
			if(v < 1*scale) \
				{ digit = '0'; } \
			else	{ digit = '1'; v -= 1*scale; } \
		else	if(v < 3*scale) \
				{ digit = '2'; v -= 2*scale; } \
			else if(v < 4*scale) \
				{ digit = '3'; v -= 3*scale; } \
			else	{ digit = '4'; v -= 4*scale; } \
	  else	if(v < 7*scale) \
			if(v < 6*scale) \
				{ digit = '5'; v -= 5*scale; } \
			else	{ digit = '6'; v -= 6*scale; } \
		else	if(v < 8*scale) \
				{ digit = '7'; v -= 7*scale; } \
			else if(v < 9*scale) \
				{ digit = '8'; v -= 8*scale; } \
			else	{ digit = '9'; v -= 9*scale; } \
	}
#define sfucvt(v,s,n,list) \
	{ list = _Sfdec; \
	  while((ulong)v >= 10000) \
	  {	n = v; v = ((ulong)v)/10000; n = ((ulong)n) - ((ulong)v)*10000; \
		SFDIGIT(n,1000,s[-4]); \
		SFDIGIT(n,100,s[-3]); \
		*--s = list[(n <<= 1)+1]; \
		*--s = list[n]; \
		s -= 2; \
	  } \
	  if(v >= 100) \
	  {	if(v >= 1000) \
		{	n = 2; \
			SFDIGIT(v,1000,s[-4]); \
		} \
		else	n = 1; \
		SFDIGIT(v,100,s[-3]); \
	  	*--s = list[(v <<= 1)+1]; \
		*--s = list[v]; \
		s -= n; \
	  } \
	  else \
	  {	*--s = list[(v <<= 1)+1]; \
	  	if(v >= 20) \
			*--s = list[v]; \
	  } \
	}

/* handy functions */
#define min(x,y)	((x) < (y) ? (x) : (y))
#define max(x,y)	((x) > (y) ? (x) : (y))

/* fast functions for memory copy and memory clear */
#if _lib_bcopy
#ifndef memcpy
#define memcpy(to,fr,n)	bcopy((fr),(to),(n))
#endif
#endif
#if _lib_bzero
#define memclear(s,n)	bzero((s),(n))
#else
#define memclear(s,n)	memset((s),'\0',(n))
#endif

/* note that MEMCPY advances the associated pointers */
#define MEMCPY(to,fr,n) \
	switch(n) \
	{ default : memcpy((char*)to,(char*)fr,n); to += n; fr += n; break; \
	  case  7 : *to++ = *fr++; \
	  case  6 : *to++ = *fr++; \
	  case  5 : *to++ = *fr++; \
	  case  4 : *to++ = *fr++; \
	  case  3 : *to++ = *fr++; \
	  case  2 : *to++ = *fr++; \
	  case  1 : *to++ = *fr++; \
	  case  0 : break; \
	}
#define MEMSET(s,c,n) \
	switch(n) \
	{ default : memset((char*)s,(char)c,n); s += n; break; \
	  case  7 : *s++ = c; \
	  case  6 : *s++ = c; \
	  case  5 : *s++ = c; \
	  case  4 : *s++ = c; \
	  case  3 : *s++ = c; \
	  case  2 : *s++ = c; \
	  case  1 : *s++ = c; \
	  case  0 : break; \
	}

#ifdef __cplusplus
extern "C" {
#endif

extern Sfext_t	_Sfextern;
extern Sftab_t	_Sftable;

extern int	_sfmode _ARG_((Sfile_t*, int));
extern int	_sftype _ARG_((const char*, int*));
extern int	_sfexcept _ARG_((Sfile_t*, int, int, Sfdisc_t*));
extern int	_sfpkrd _ARG_((Sfile_t*, int, long));

extern int	errno;
extern double	frexp _ARG_((double, int*));
extern double	ldexp _ARG_((double,int));
extern double	strtod _ARG_((const char*, char**));
extern char	*malloc _ARG_((int));
extern char	*realloc _ARG_((char*,int));
extern void	free _ARG_((char*));
extern int	strlen _ARG_((char*));

extern void	memset _ARG_((char*,char,int));
extern char	*memccpy _ARG_((char*, char*, int, int));
extern char	*memchr _ARG_((char*, int, int));
#ifndef memcpy
extern void	memcpy _ARG_((void*,const void*,int));
#endif
extern void	bcopy _ARG_((const char*,char*,int));
extern void	bzero _ARG_((void*,int));

extern void	_exit _ARG_((int));
extern int	on_exit _ARG_((void(*)(void), char*));
extern int	onexit _ARG_((void(*)(void)));
#ifndef atexit
extern int	atexit _ARG_((void(*)(void)));
#endif

extern int	getpagesize _ARG_((void));

#if !_proto_open
extern int	open _ARG_((const char*, int, ...));
#endif
extern int	close _ARG_((int));
extern int	read _ARG_((int, char*, int));
extern int	write _ARG_((int, const char*, int));
extern long	lseek _ARG_((int, long, int));
extern int	dup _ARG_((int));
extern int	unlink _ARG_((const char*));

#ifdef __cplusplus
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './sfio.h'
then
	echo shar: will not over-write existing file "'./sfio.h'"
else
cat << \SHAR_EOF > './sfio.h'
#ifndef _SFIO_H		/* protect against multiple #includes */
#define _SFIO_H

#ifdef __STD_C
#undef __STD_C
#endif
#ifdef __STDC__
#define	__STD_C		1
#else
#ifdef __cplusplus
#define __STD_C		1
#endif
#endif

#ifdef _ARG_
#undef _ARG_
#endif
#ifdef __STD_C
#define _ARG_(x)	x
#include		<stdarg.h>
#else
#define _ARG_(x)	()
#endif

#ifndef NULL
#define NULL	0
#endif
#ifndef EOF
#define EOF	(-1)
#endif
#ifndef SEEK_SET
#define SEEK_SET	0
#define SEEK_CUR	1
#define SEEK_END	2
#endif

typedef struct _sfdc_	Sfdisc_t;
typedef struct _sfio_	Sfile_t, SFIO;

/* discipline structure */
struct _sfdc_
{
	int		(*readf) _ARG_((Sfile_t*, char*, int, Sfdisc_t*));
	int		(*writef) _ARG_((Sfile_t*, const char*, int, Sfdisc_t*));
	long		(*seekf) _ARG_((Sfile_t*, long, int, Sfdisc_t*));
	int		(*exceptf) _ARG_((Sfile_t*, int, Sfdisc_t*));
	Sfdisc_t	*disc;	/* the continuing discipline		*/
};

/* a file structure */
struct _sfio_
{
	unsigned char	*next;	/* next position to read/write from	*/
	unsigned char	*endw;	/* end of write buffer			*/
	unsigned char	*endr;	/* end of read buffer			*/
	unsigned char	*endb;	/* end of buffer			*/
	struct _sfio_	*push;	/* the stream that was pushed on	*/
	unsigned short	flags;	/* type of stream			*/
	short		file;	/* file descriptor			*/
#ifdef _SFIO_PRIVATE
	_SFIO_PRIVATE
#endif
};

/* bits for various types of files */
#define	SF_READ		0000001	/* open for reading			*/
#define SF_WRITE	0000002	/* open for writing			*/
#define SF_STRING	0000004	/* a string stream			*/
#define SF_APPEND	0000010	/* associated file is in append mode	*/
#define SF_MALLOC	0000020	/* buffered space malloc-ed		*/
#define SF_LINE		0000040	/* line buffering			*/
#define SF_SHARE	0000100	/* file stream that is shared		*/
#define SF_FLAGS	0000177	/* PUBLIC FLAGS PASSABLE TO SFNEW()	*/
#define SF_SETS		0000163	/* flags passable to sfset()		*/

#define SF_EOF		0000200	/* eof was detected			*/
#define SF_ERROR	0000400	/* an error happened			*/

/* exception events: SF_NEW(0), SF_READ(1), SF_WRITE(2) and the below 	*/
#define SF_SEEK		3	/* seek error				*/
#define SF_CLOSE	4	/* when stream is being closed		*/
#define SF_DPUSH	5	/* when discipline is being pushed	*/
#define SF_DPOP		6	/* when discipline is being popped	*/
#define SF_DPOLL	7	/* see if stream is ready for I/O	*/

/* for stack and disciplines */
#define SF_POPSTACK	(Sfile_t*)(0)	/* pop the stream stack		*/
#define SF_POPDISC	(Sfdisc_t*)(0)	/* pop the discipline stack	*/

/* for the notify function and discipline exception */
#define SF_NEW		0	/* new stream				*/
#define SF_SETFD	-1	/* about to set the file descriptor 	*/

#define SF_BUFSIZE	8192	/* suggested default buffer size	*/
#define SF_UNBOUND	(-1)	/* unbounded buffer size		*/

#define	sfstdin		(&_Sfstdin)	/* standard input stream	*/
#define	sfstdout	(&_Sfstdout)	/* standard output stream	*/
#define	sfstderr	(&_Sfstderr)	/* standard error stream	*/

#ifdef __cplusplus
extern "C"
{
#endif

extern int		_Sfi;
extern Sfile_t		_Sfstdin, _Sfstdout, _Sfstderr;

extern Sfile_t*		sfnew _ARG_((Sfile_t*, char*, int, int, int));
extern Sfile_t*		sfopen _ARG_((Sfile_t*, const char*, const char*));
extern Sfile_t*		sfpopen _ARG_((Sfile_t*, const char*, const char*));
extern Sfile_t*		sfstack _ARG_((Sfile_t*, Sfile_t*));
extern Sfile_t*		sftmp _ARG_((int));
extern int		_sfflsbuf _ARG_((Sfile_t*, int));
extern int		_sffilbuf _ARG_((Sfile_t*, int));
extern int		sfpurge _ARG_((Sfile_t*));
extern int		sfpeek _ARG_((Sfile_t*, char**, int));
extern int		sfsync _ARG_((Sfile_t*));
extern int		sfclrlock _ARG_((Sfile_t*));
extern char*		sfsetbuf _ARG_((Sfile_t*, char*, int));
extern Sfdisc_t*	sfdisc _ARG_((Sfile_t*,Sfdisc_t*));
extern int		sfnotify _ARG_((void(*)(Sfile_t*, int, int)));
extern int		sfset _ARG_((Sfile_t*, int, int));
extern int		sfsetfd _ARG_((Sfile_t*, int));
extern Sfile_t*		sfpool _ARG_((Sfile_t*, Sfile_t*, int));
extern int		sfread _ARG_((Sfile_t*, char*, int));
extern int		sfwrite _ARG_((Sfile_t*, const char*, int));
extern int		sfmove _ARG_((Sfile_t*, Sfile_t*, long, int));
extern int		sfclose _ARG_((Sfile_t*));
extern long		sftell _ARG_((Sfile_t*));
extern long		sfseek _ARG_((Sfile_t*, long, int));
extern int		sfllen _ARG_((long));
extern int		sfdlen _ARG_((double));
extern int		sfputr _ARG_((Sfile_t*, const char*, int));
extern char*		sfgetr _ARG_((Sfile_t*, int, int));
extern int		sfnputc _ARG_((Sfile_t*, int, int));
extern int		_sfputu _ARG_((Sfile_t*, unsigned long));
extern int		_sfputl _ARG_((Sfile_t*, long));
extern long		_sfgetl _ARG_((Sfile_t*));
extern unsigned long	_sfgetu _ARG_((Sfile_t*));
extern long		_sfgetl _ARG_((Sfile_t*));
extern int		_sfputd _ARG_((Sfile_t*, double));
extern double		sfgetd _ARG_((Sfile_t*));
extern int		sfungetc _ARG_((Sfile_t*, int));
extern char*		_sfcvt _ARG_((double, int, int*, int*, int));
extern char*		sfprints _ARG_((const char*, ...));
extern int		sfprintf _ARG_((Sfile_t*, const char*, ...));
extern int		sfsprintf _ARG_((char*, int, const char*, ...));
extern int		sfscanf _ARG_((Sfile_t*, const char*, ...));
extern int		sfsscanf _ARG_((const char*, const char*, ...));
extern int		sfvprintf _ARG_((Sfile_t*, const char*, va_list));
extern int		sfvscanf _ARG_((Sfile_t*, const char*, va_list));

/* io functions with discipline continuation */
extern int		sfrd _ARG_((Sfile_t*, char*, int, Sfdisc_t*));
extern int		sfwr _ARG_((Sfile_t*, const char*, int, Sfdisc_t*));
extern long		sfsk _ARG_((Sfile_t*, long, int, Sfdisc_t*));

/* function analogues of fast in-line functions */
extern int		sfgetc _ARG_((Sfile_t*));
extern long		sfgetl _ARG_((Sfile_t*));
extern unsigned long	sfgetu _ARG_((Sfile_t*));
extern int		sfputc _ARG_((Sfile_t*,int));
extern int		sfputd _ARG_((Sfile_t*,double));
extern int		sfputl _ARG_((Sfile_t*,long));
extern int		sfputu _ARG_((Sfile_t*,unsigned long));
extern int		sfslen _ARG_((void));
extern int		sfulen _ARG_((unsigned long));
extern long		sfsize _ARG_((Sfile_t*));
extern int		sfclrerr _ARG_((Sfile_t*));
extern int		sfeof _ARG_((Sfile_t*));
extern int		sferror _ARG_((Sfile_t*));
extern int		sffileno _ARG_((Sfile_t*));
extern int		sfstacked _ARG_((Sfile_t*));
extern char*		sfecvt _ARG_((double,int,int*,int*));
extern char*		sffcvt _ARG_((double,int,int*,int*));

#ifdef __cplusplus
}
#endif

/* fast in-line functions */
#define sfputc(f,c)	((f)->next >= (f)->endw ? \
				_sfflsbuf(f,(int)((unsigned char)(c))) : \
				(int)(*(f)->next++ = (unsigned char)(c)))
#define sfgetc(f)	((f)->next >= (f)->endr ? _sffilbuf(f,0) : (int)(*(f)->next++))
#define sfslen()	(_Sfi)
#define sffileno(f)	((f)->file)
#define sfeof(f)	(((f)->flags&(SF_EOF|SF_STRING)) && (f)->next >= (f)->endb)
#define sferror(f)	((f)->flags&SF_ERROR)
#define sfclrerr(f)	((f)->flags &= ~(SF_ERROR|SF_EOF))
#define sfstacked(f)	((f)->push != (Sfile_t*)0)

/* coding long integers in a portable and compact fashion */
#define SF_SBITS	6
#define SF_UBITS	7
#define SF_SIGN		(1 << SF_SBITS)
#define SF_MORE		(1 << SF_UBITS)
#define SF_U1		SF_MORE
#define SF_U2		(SF_U1*SF_U1)
#define SF_U3		(SF_U2*SF_U1)
#define SF_U4		(SF_U3*SF_U1)
#define sfulen(v)	((v) < SF_U1 ? 1 : (v) < SF_U2 ? 2 : \
			 (v) < SF_U3 ? 3 : (v) < SF_U4 ? 4 : 5)
#define sfgetu(f)	((_Sfi = sfgetc(f)) < 0 ? -1 : \
				((_Sfi&SF_MORE) ? _sfgetu(f) : (unsigned long)_Sfi))
#define sfgetl(f)	((_Sfi = sfgetc(f)) < 0 ? -1 : \
				((_Sfi&(SF_MORE|SF_SIGN)) ? _sfgetl(f) : (long)_Sfi))
#define sfputu(f,v)	_sfputu((f),(unsigned long)(v))
#define sfputl(f,v)	_sfputl((f),(long)(v))
#define sfputd(f,v)	_sfputd((f),(double)(v))

#define sfecvt(v,n,d,s)	_sfcvt((v),(n),(d),(s),1)
#define sffcvt(v,n,d,s)	_sfcvt((v),(n),(d),(s),0)

#endif /* _SFIO_H */
SHAR_EOF
fi # end of overwriting check
if test -f './sfllen.c'
then
	echo shar: will not over-write existing file "'./sfllen.c'"
else
cat << \SHAR_EOF > './sfllen.c'
#include	"sfhdr.h"

/*	Get size of a long value coded in a portable format
**
**	Written by Kiem-Phong Vo (06/27/90)
*/
#ifdef __STD_C
sfllen(reg long v)
#else
sfllen(v)
reg long	v;
#endif
{
	if(v < 0)
		v = -(v+1);
	v = (ulong)v >> SF_SBITS;
	return 1 + (v > 0 ? sfulen(v) : 0);
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfmode.c'
then
	echo shar: will not over-write existing file "'./sfmode.c'"
else
cat << \SHAR_EOF > './sfmode.c'
#include	"sfhdr.h"
static char	*Version = "\n@(#)Version 4/10/92-sfio-kpv\n";

/*	Switch the given stream to a desired mode
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

/* to be done on exit */
static int	_Cleanup = 0;
#ifdef __STD_C
static void _cleanup(void)
#else
static void _cleanup()
#endif
{	/* sync all streams */
	if(_Cleanup >= 0)
	{	_Cleanup = -1;
		sfsync(NIL(Sfile_t*));
	}
}

#ifdef __STD_C
_sfmode(reg Sfile_t* f, reg int wanted)
#else
_sfmode(f,wanted)
reg Sfile_t	*f;	/* change r/w mode and sync file pointer for this stream */
reg int		wanted;	/* desired mode */
#endif
{
	reg int		n, rv;
	reg long	addr;

	if(!_Cleanup)
	{	_Cleanup = 1;
		(void)atexit(_cleanup);
	}

	if(SFFROZEN(f) || (!(f->flags&SF_STRING) && f->file < 0))
		return -1;

	if(f->mode&SF_STDIO) /* synchronizing with stdio pointers */
		(*_Sfstdio)(f);

	/* check for ungetc stream */
	if(f->disc == _Sfudisc && wanted == SF_WRITE)
	{	if(!(f->push->flags&SF_WRITE))
			return -1;
		(void)sfclose((*_Sfstack)(f,SF_POPSTACK));
	}

	if(f->mode&SF_POOL)
	{	/* move to head of pool */
		if(f != f->pool->head &&
		   (SFFROZEN(f->pool->head) || (*_Sfpmove)(f,0) < 0))
			return -1;
		f->mode &= ~SF_POOL;
	}

	/* buffer initialization */
	wanted &= SF_RDWR;
	if(f->mode&SF_INIT)
	{	if(wanted && wanted != (f->mode&SF_RDWR) && !(f->flags&SF_PROCESS))
		{	if(!(f->flags&SF_BOTH) || !(f->flags&wanted))
				return -1;
			f->mode = (wanted|SF_INIT);
		}
		(void)sfsetbuf(f,NIL(char*),-1);

		if(!(f->flags&SF_PROCESS) || wanted == f->mode)
			return 0;
	}

	SFLOCK(f);
	rv = 0;
	switch(f->mode&~SF_LOCK)
	{
	case SF_WRITE: /* switching to SF_READ */
		if(wanted == 0 || wanted == SF_WRITE)
			goto done;
		if(!(f->flags&SF_BOTH) || !(f->flags&SF_READ) )
			rv = -1;
		else if(f->flags&SF_STRING)
		{	if((n = f->next-f->data) > f->here && (long)n != f->extent)
				f->here = f->extent = n;
			f->endb = f->data+f->here;
			f->mode = SF_READ;
		}
		else
		{	/* reset buffer */
			SFopen(f);
			if((rv = _sfflsbuf(f,-1)) < 0)
				goto done;
			SFLOCK(f);

			if(f->size == 0)
			{	/* unbuffered */
				f->data = f->tiny;
				f->size = sizeof(f->tiny);
			}
			f->next = f->endr = f->endw = f->endb = f->data;
			f->mode = SF_READ;
			if((f->flags&SF_MALLOC) && !SFNOMAP(f))
			{	SFopen(f);
				sfsetbuf(f,NIL(char*),-1);
				SFlock(f);
			}

			/* restore saved data */
			if(f->flags&SF_PROCESS)
				(*_Sfpmode)(f,wanted);
		}
		goto done;

	case (SF_READ|SF_SYNC): /* a previously sync-ed read stream */
		if(wanted != SF_WRITE)
		{	/* just reset the pointers */
			f->mode = SF_READ|SF_LOCK;
			addr = f->here + (f->endb - f->next);
			if(SFSK(f,addr,0,f->disc) < 0)
				rv = -1;
			else	f->here = addr;
			goto done;
		}
		/* fall thru */

	case SF_READ: /* switching to SF_WRITE */
		if(wanted != SF_WRITE)
			goto done;
		if(!(f->flags&SF_BOTH) || !(f->flags&SF_WRITE))
		{	rv = -1;
			goto done;
		}
		if(f->flags&SF_STRING)
		{	f->endb = f->data+f->size;
			f->mode = SF_WRITE;
			goto done;
		}

		/* save unread data before switching mode */
		if(f->flags&SF_PROCESS)
			(*_Sfpmode)(f,wanted);

		/* reset buffer and seek pointer */
		if(f->flags&SF_MMAP)
		{	if(SFSK(f,f->here,0,f->disc) < 0)
			{	rv = -1;
				goto done;
			}
			f->flags |= SF_SETBUF;
			SFopen(f); (void)sfsetbuf(f,NIL(char*),-1); SFLOCK(f);
			f->flags &= ~SF_SETBUF;
		}
		else if((n = f->endb-f->next) > 0)
		{	/* reset file pointer */
			addr = f->here - n;
			if(SFSK(f,addr,0,f->disc) < 0)
			{	rv = -1;
				goto done;
			}
			else	f->here = addr;
		}

		if(f->data == f->tiny)
		{	f->endb = f->data = f->next = NIL(uchar*);
			f->size = 0;
		}
		else	f->endb = (f->next = f->data) + f->size;

		f->mode = SF_WRITE;
		goto done;

	default: /* unknown case */
		rv = -1;
		goto done;
	}

done :
	SFSET(f,f->flags,1);
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfmove.c'
then
	echo shar: will not over-write existing file "'./sfmove.c'"
else
cat << \SHAR_EOF > './sfmove.c'
#include	"sfhdr.h"

/*	Move data from one stream to another.
**	This code is written so that it'll work even in the presence
**	of stacking streams, pool, and discipline.
**	If you must change it, be gentle.
**
**	Written by Kiem-Phong Vo (12/07/90)
*/

#ifdef __STD_C
sfmove(Sfile_t* fr, Sfile_t* fw, long n, reg int rc)
#else
sfmove(fr,fw,n,rc)
Sfile_t		*fr;	/* moving data from this stream */
Sfile_t		*fw;	/* moving data to this stream */
long		n;	/* number of bytes/records to move. <0 for unbounded move */
reg int		rc;	/* record separator */
#endif
{
	reg uchar	*cp, *next;
	reg int		r, w;
	reg uchar	*endb;
	reg int		direct;
	reg long	n_move;
	uchar		*rbuf = NIL(uchar*);
	int		rsize = 0;
	reg int		mapsize = _Sfmapsize;

	if(!fr)
		return 0;

	for(n_move = 0; n != 0; )
	{	/* get the streams into the right mode */
		if(fr->mode != SF_READ && _sfmode(fr,SF_READ) < 0)
			break;

		/* about to move all, set map to a large amount */
		if(n < 0 && (fr->flags&SF_MMAP))
			_Sfmapsize = 32*_Sfpage;

		/* if the write buffer is full, empty it */
		if(fw && fw->next && fw->next >= fw->endb && _sfflsbuf(fw,-1) < 0)
			break;

		/* try reading a block of data */
		direct = 0;
		if((r = fr->endb - (next = fr->next)) <= 0)
		{
			if(fr->extent < 0 && (fr->flags&SF_SHARE) &&
			   fr->unmap != SF_NOPEEK && rc >= 0 && n > 0)
			{	/* try peek-read records */
				w = _sfpkrd(fr,rc,n);
				if((r = fr->endb - (next = fr->next)) > 0)
				{	if(w >= 0)
					{	n_move += w;
						n -= w;
						goto do_write;
					}
					else	goto do_count;
				}
			}

			/* determine what buffer to use */
			w = fr->extent < 0 ? _Sfpage :
			    fr->extent < _Sfmapsize ? (int)fr->extent : _Sfmapsize;
			if((n < 0 || n > fr->size) && !fr->disc && !fr->push &&
			   !(fr->flags&(SF_MMAP|SF_STRING)) && w > fr->size)
			{	if(fw && w <= fw->endb - (next = fw->next))
				{	w = fw->endb - next;
					direct = SF_WRITE;
				}
				else
				{	if(rsize <= 0)
					{	/* let's not get carried away */
						if(w > _Sfmapsize)
							w = _Sfmapsize;
						if(rbuf = (uchar*)malloc(w))
							rsize = w;
					}
					if(rbuf)
					{	next = rbuf;
						w = rsize;
						direct = SF_STRING;
					}
				}
			}
			if(!direct)
			{	/* let sffilbuf worry about this */	
				if(fr->extent < 0 && (fr->flags&SF_SHARE) && rc < 0)
				{	r = fr->size;
					if((long)r > n)
						r = (int)n;
				}
				else	r = -1;
				if((r = _sffilbuf(fr,r)) <= 0)
					break;
				next = fr->next;
			}
			else
			{	/* use direct read */
				if(rc < 0 && n > 0 && n < w)
					w = (int)n;
				if(fr->flags&SF_SHARE)
					(void)sfsk(fr,fr->here,0,NIL(Sfdisc_t*));
				if((r = SFRD(fr,(char*)next,w,NIL(Sfdisc_t*))) <= 0)
					break;

				/* for future sfseek's correctness */
				fr->next = fr->endb = fr->endr = fr->data;
			}
		}

		/* compute the extent of data to be moved */
	do_count:
		endb = next+r;
		if(rc < 0)
		{	if(n > 0)
			{	if(r > n)
					r = (int)n;
				n -= r;
			}
			n_move += r;
			cp = next+r;
		}
		else
		{	/* count records */
			reg int	rdwr = (fr->flags&(SF_BOTH|SF_MALLOC|SF_MMAP));

			if(rdwr)
			{	w = endb[-1];
				endb[-1] = rc;
			}
			else	w = 0;
			for(cp = next; cp < endb; )
			{	/* find the line extent */
				if(rdwr)
					while(*cp++ != rc)
						;
				else	while(r-- && *cp++ != rc)
						;
				if(cp < endb || w == rc)
				{	n_move += 1;
					if(n > 0 && (n -= 1) == 0)
						break;
				}
			}
			if(rdwr)
				endb[-1] = w;
			r = cp-next;
		}

		if(!direct)
			fr->next += r;
		else
		{	fr->here += r;
			if((w = endb-cp) > 0)
			{	if(!(fr->flags&SF_MMAP) && fr->size > 0)
				{	/* move remainder to read stream */
					if(w > fr->size)
						w = fr->size;
					memcpy((char*)fr->data,(char*)cp,w);
					fr->endb = fr->endr = fr->data+w;
					fr->here += w;
					w = endb - (cp+w);
				}
				if(w > 0)
					(void)sfsk(fr,(long)(-w),1,NIL(Sfdisc_t*));
			}
		}

	do_write: /* write out data */
		if(fw && (w = sfwrite(fw,(char*)next,r)) != r)
		{	/* a write error happened */
			if(w > 0)
			{	r -= w;
				if(rc < 0)
					n_move -= r;
			}
			(void) sfseek(fr,(long)(-r),1);
			break;
		}
	}

	if(n < 0)
		_Sfmapsize = mapsize;
	if(rbuf)
		free((char*)rbuf);

	return n_move;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfnew.c'
then
	echo shar: will not over-write existing file "'./sfnew.c'"
else
cat << \SHAR_EOF > './sfnew.c'
#include	"sfhdr.h"

/*	Fundamental function to create a new stream.
**	The argument flags defines the type of stream and the scheme
**	of buffering.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
Sfile_t *sfnew(Sfile_t* oldf, char* buf, int size, int file, int flags)
#else
Sfile_t *sfnew(oldf,buf,size,file,flags)
Sfile_t	*oldf;	/* old stream to be reused */
char	*buf;	/* a buffer to read/write, if NULL, will be allocated */
int	size;	/* buffer size if buf is given or desired buffer size */
int	file;	/* file descriptor to read/write from */
int	flags;	/* type of file stream */
#endif
{
	reg Sfile_t	*f;
	reg int		sflags;

	if(!(flags&SF_RDWR))
		return NIL(Sfile_t*);

	sflags = 0;
	if((f = oldf) != NIL(Sfile_t*))
	{	if(f->mode&SF_AVAIL)
		{	/* only allow sfstd-streams to be already closed */
			if(f != sfstdin && f != sfstdout && f != sfstderr)
				return NIL(Sfile_t*);
			oldf = NIL(Sfile_t*);
		}
		else
		{	/* reopening an open stream, close it first */
			sflags = f->flags;
			SETLOCAL(f);
			if(sfclose(f) < 0)
				return NIL(Sfile_t*);
		}
	}

	if(!f)
	{	/* reuse a standard stream structure if possible */
		if(!(flags&SF_STRING) && file >= 0 && file <= 2)
		{	f = file == 0 ? sfstdin : file == 1 ? sfstdout : sfstderr;
			if(!(f->mode&SF_AVAIL) )
				f = NIL(Sfile_t*);
		}

		if(!f && !SFALLOC(f))	/* make a new one */
			return NIL(Sfile_t*);
	}

	if(!oldf)
		SFCLEAR(f);

	/* stream type */
	f->mode = (flags&SF_READ) ? SF_READ : SF_WRITE;
	f->flags = (flags&SF_FLAGS) | ((flags&SF_RDWR) == SF_RDWR ? SF_BOTH : 0);
	f->flags |= (sflags&SF_MALLOC);
	f->file = file;
	f->here = f->extent = 0L;

	f->mode |= SF_INIT | (flags&SF_OPEN);
	if(size >= 0 || (f->flags&SF_STRING))
	{	if((f->flags&SF_STRING))
			f->mode &= ~SF_INIT;
		(void)sfsetbuf(f,buf,size);
	}
	else	f->endb = f->endr = f->endw = f->next = f->data;

	if(!oldf)
	{	/* link to the list of active streams */
		f->pool = &_Sfpool;
		f->fore = _Sfpool.head;
		if(_Sfpool.head)
			_Sfpool.head->back = f;
		_Sfpool.head = f;
	}

	if(_Sfnotify)
		(*_Sfnotify)(f,SF_NEW,f->file);

	return f;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfnotify.c'
then
	echo shar: will not over-write existing file "'./sfnotify.c'"
else
cat << \SHAR_EOF > './sfnotify.c'
#include	"sfhdr.h"


/*	Set the function to be called when a stream is opened or closed
**
**	Written by Kiem-Phong Vo (01/06/91)
*/
#ifdef __STD_C
sfnotify(void (*notify)(Sfile_t*, int, int))
#else
sfnotify(notify)
void	(*notify)();
#endif
{
	_Sfnotify = notify;
	return 0;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfnputc.c'
then
	echo shar: will not over-write existing file "'./sfnputc.c'"
else
cat << \SHAR_EOF > './sfnputc.c'
#include	"sfhdr.h"

/*	Write out a character n times
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
sfnputc(reg Sfile_t* f, reg int c, reg int n)
#else
sfnputc(f,c,n)
reg Sfile_t	*f;	/* file to write */
reg int		c;	/* char to be written */
reg int		n;	/* number of time to repeat */
#endif
{
	reg uchar	*ps;
	reg int		p, w;
	uchar		buf[128];

	if(f->mode != SF_WRITE && _sfmode(f,SF_WRITE) < 0)
		return -1;

	SFLOCK(f);

	/* write into a suitable buffer */
	if((p = (f->endb-(ps = f->next))) < n)
		{ ps = buf; p = sizeof(buf); }
	if(p > n)
		p = n;
	MEMSET(ps,c,p);
	ps -= p;

	w = n;
	if(ps == f->next)
	{	/* simple sfwrite */
		f->next += p;
		if(c == '\n')
			{ SFopen(f); (void)_sfflsbuf(f,-1); }
		goto done;
	}

	for(;;)
	{	/* hard write of data */
		SFopen(f);
		if((p = sfwrite(f,(char*)ps,p)) <= 0 || (n -= p) <= 0)
		{	w -= n;
			goto done;
		}
		SFLOCK(f);
		if(p > n)
			p = n;
	}
done :
	SFOPEN(f);
	return w;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfopen.c'
then
	echo shar: will not over-write existing file "'./sfopen.c'"
else
cat << \SHAR_EOF > './sfopen.c'
#include	"sfhdr.h"

/*	Open a file/string for IO.
**	If f is not nil, it is taken as an existing stream that should be
**	closed and its structure reused for the new stream.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
Sfile_t *sfopen(reg Sfile_t* f, const char* file, const char* mode)
#else
Sfile_t *sfopen(f,file,mode)
reg Sfile_t	*f;		/* old stream structure */
char		*file;		/* file/string to be opened */
reg char	*mode;		/* mode of the stream */
#endif
{
	int	fd, oldfd, oflags, sflags;

	if((sflags = _sftype(mode,&oflags)) == 0)
		return NIL(Sfile_t*);

	if(sflags&SF_STRING)
		fd = -1;
	else
	{	/* open the file */
		if(!file)
			return NIL(Sfile_t*);

#ifndef NO_OFLAGS
		while((fd = open((char*)file,oflags,0666)) < 0 && errno == EINTR)
			errno = 0;
#else
		while((fd = open(file,oflags&03)) < 0 && errno == EINTR)
			errno = 0;
		if(fd >= 0)
		{	if(oflags&O_TRUNC)
			{	reg int	tf;
				while((tf = creat(file,0666)) < 0 && errno == EINTR)
					errno = 0;
				CLOSE(tf);
			}
		}
		else if(oflags&O_CREAT)
		{	while((fd = creat(file,0666)) < 0 && errno == EINTR)
				errno = 0;
			if(!(oflags&O_WRONLY))
			{	/* the file now exists, reopen it for read/write */
				CLOSE(fd);
				while((fd = open(file,oflags&03)) < 0 && errno == EINTR)
					errno = 0;
			}
		}
#endif
		if(fd < 0)
			return NIL(Sfile_t*);
	}

	oldfd = (f && !(f->flags&SF_STRING)) ? f->file : -1;

	if(sflags&SF_STRING)
		f = sfnew(f,(char*)file,file ? strlen((char*)file) : -1,fd,sflags);
	else if((f = sfnew(f,NIL(char*),-1,fd,sflags|SF_OPEN)) && oldfd >= 0)
		(void)sfsetfd(f,oldfd);

	return f;
}

#ifdef __STD_C
_sftype(reg const char *mode, int *oflagsp)
#else
_sftype(mode, oflagsp)
reg char	*mode;
int		*oflagsp;
#endif
{
	reg int	sflags, oflags;

	if(!mode)
		return 0;

	/* construct the open flags */
	sflags = oflags = 0;
	while(1) switch(*mode++)
	{
	case 'w' :
		sflags |= SF_WRITE;
		oflags |= O_WRONLY | O_TRUNC | O_CREAT;
		continue;
	case 'a' :
		sflags |= SF_WRITE | SF_APPEND;
		oflags |= O_WRONLY | O_APPEND | O_CREAT;
		continue;
	case 'r' :
		sflags |= SF_READ;
		oflags |= O_RDONLY;
		continue;
	case 's' :
		sflags |= SF_STRING|SF_READ;
		continue;
	case 'b' :
		continue;
	case '+' :
		if(sflags)
			sflags |= SF_READ|SF_WRITE;
		continue;
	default :
		if((sflags&SF_RDWR) == SF_RDWR)
			oflags = (oflags & ~(O_RDONLY|O_WRONLY))|O_RDWR;
		if(oflagsp)
			*oflagsp = oflags;
		return sflags;
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfpeek.c'
then
	echo shar: will not over-write existing file "'./sfpeek.c'"
else
cat << \SHAR_EOF > './sfpeek.c'
#include	"sfhdr.h"

/*	Safe access to the internal stream buffer.
**
**	Written by Kiem-Phong Vo (06/27/90).
*/

#ifdef __STD_C
sfpeek(reg Sfile_t* f, char** bp, int size)
#else
sfpeek(f,bp,size)
reg Sfile_t	*f;	/* file to peek */
char		**bp;	/* start of data area */
int		size;	/* size of peek */
#endif
{
	reg int		n;

	/* query for the extent of the remainder of the buffer */
	if(!bp || size == 0)
	{	if(f->mode == (SF_AVAIL|SF_LOCK))	/* already closed */
			return -1;
		if((f->flags&(SF_STRING|SF_RDWR)) == (SF_STRING|SF_RDWR))
		{	if((n = f->next-f->data) > f->here && n != f->extent)
				f->here = f->extent = n;
			n = (f->data+f->here) - f->next;
		}
		else	n = f->endb - f->next;
		if(!bp)
			return n;
		else if(n > 0)
		{	*bp = (char*)f->next;
			return 0;
		}
	}

	/* for consistency the desired mode for a RDWR stream is always READ */
	n = (f->flags&SF_READ) ? SF_READ : SF_WRITE;
	if(f->mode != n && _sfmode(f,n) < 0)
		return -1;

	/* make sure there is something in the buffer */
	if((n = f->endb - f->next) <= 0)
	{	if(f->mode == SF_READ)
			n = _sffilbuf(f,size <= 0 ? -1 : size);
		else	n = _sfflsbuf(f,-1);
	}

	*bp = (char*)f->next;
	if(n > 0)
	{	if(size < 0)
		{	f->mode |= SF_PEEK;
			f->endr = f->endw = f->data;
		}
		else
		{	/* try our best to accomodate the request size */
			if(n < size)
			{	if(f->mode&SF_WRITE)
				{	/* extend buffer */
					if(f->flags&SF_STRING)
					{	reg int	here = f->next-f->data;
						SETLOCAL(f);
						(void)_sfexcept(f,SF_WRITE,size-n,
							  NIL(Sfdisc_t*));
						f->next = f->data+here;
					}
					else if(f->next > f->data)
						(void)_sfflsbuf(f,-1);
				}
				else if(!(f->flags&SF_STRING) && f->extent > f->here &&
					((f->flags&SF_MMAP) || n < f->size) )
				{	if(f->flags&SF_MMAP)
					{	f->here -= f->endb-f->next;
						f->endb = f->next = f->data;
						size = _Sfmapsize;
					}
					else
					{	/* shift data */
						memcpy((char*)f->data,(char*)f->next,n);
						f->endb = f->data + n;
						f->next = f->data;
						if(f->extent >= 0 || size > f->size-n)
							size = f->size-n;
					}
					(void)_sffilbuf(f,size);
				}

				n = f->endb - f->next;
			}

			/* advance pointer by appropriate amount */
			if(n > size)
				n = size;
			*bp = (char*)f->next;
			f->next += n;
		}
	}

	return n;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfpool.c'
then
	echo shar: will not over-write existing file "'./sfpool.c'"
else
cat << \SHAR_EOF > './sfpool.c'
#include	"sfhdr.h"

/*	Management of pools of streams.
**	If pf is not nil, f is pooled with pf and f becomes current;
**	otherwise, f is isolated from its pool. flag can be any of:
**	0, SF_READ, SF_WRITE, SF_READ|SFWRITE, or SF_SHARE to indicate the type
**	of stream that gets synced when a stream becomes non-current.
**
**	Written by Kiem-Phong Vo (6/27/90).
*/

static Sfpool_t		*Pfree;	/* list of free pools */

#ifdef __STD_C
static void delpool(reg Sfpool_t* p)
#else
static delpool(p)
reg Sfpool_t	*p;
#endif
{
	if(p == &_Sfpool)
		return;
	if(p->fore)
		p->fore->back = p->back;
	if(p->back)
		p->back->fore = p->fore;
	p->fore = Pfree;
	Pfree = p;
}

#ifdef __STD_C
static Sfpool_t *newpool(reg int mode)
#else
static Sfpool_t *newpool(mode)
reg int	mode;
#endif
{
	reg Sfpool_t	*p;

	if(Pfree)
	{	/* from the free list */
		p = Pfree;
		Pfree = p->fore;
	}
	else	p = (Sfpool_t*) malloc(sizeof(Sfpool_t));

	p->mode = (mode&SF_SHARE) ? SF_SHARE : (mode&SF_RDWR);
	p->head = NIL(Sfile_t*);

	/* insert into chain of pools */
	if((p->fore = _Sfpool.fore) != NIL(Sfpool_t*))
		p->fore->back = p;
	p->back = &_Sfpool;
	_Sfpool.fore = p;
	return p;
}

#ifdef __STD_C
static int _sfpmove(reg Sfile_t* f, reg int type)
#else
static int _sfpmove(f,type)
reg Sfile_t	*f;
reg int		type;	/* <0 : deleting, 0: move-to-front, >0: inserting */
#endif
{
	reg Sfpool_t	*p;
	reg Sfile_t	*head;

	p = f->pool;
	if(type < 0)
	{	/* deleting from pool, make someone else head first */
		if(p != &_Sfpool && f == p->head)
		{	for(head = f->fore; head; head = head->fore)
				if(_sfpmove(head,0) >= 0)
					break;
			/* bad news, nobody wants to be head */
			if(f == p->head && f->fore)
				return -1;
		}

		SFLOCK(f);
		if(f->fore)
			f->fore->back = f->back;
		if(f->back)
			f->back->fore = f->fore;
		else	p->head = f->fore;
		f->pool = NIL(Sfpool_t*);
		f->mode &= ~SF_POOL;
		if(p->head)
			p->head->mode &= ~SF_POOL;

		if(p != &_Sfpool)
		{	if(!(head = p->head))
				delpool(p);
			else if(!head->fore)
			{	/* remove the last one */
				_sfpmove(head,-1);
				head->pool = &_Sfpool;
				_sfpmove(head,1);
			}
		}
	}
	else if(type > 0)
	{	/* insertion */
		SFLOCK(f);
		if((head = p->head) != NIL(Sfile_t*))
		{	/* right after current head */
			if((f->fore = head->fore) != NIL(Sfile_t*))
				f->fore->back = f;
			f->back = head;
			head->fore = f;
			if(p != &_Sfpool)
				f->mode |= SF_POOL;
			else	f->mode &= ~SF_POOL;
			SFopen(f);
			SFSET(f,f->flags,1);
		}
		else	
		{	f->fore = f->back = NIL(Sfile_t*);
			p->head = f;
		}
	}
	else if(p != &_Sfpool && (head = p->head) != f && !SFFROZEN(head))
	{	f->mode &= ~SF_POOL;

		if(p->mode&SF_SHARE)
		{	reg int	n, m;

			/* shared streams must be in write mode */
			if(head->mode != SF_WRITE && _sfmode(head,SF_WRITE) < 0)
				goto no_move;

			if(f->here != head->here)
			{	/* synchronize the seek pointer */
				if(f->extent >= 0 && SFSK(f,head->here,0,f->disc) < 0)
					goto no_move;
				f->here = head->here;
			}

			/* move data to the new stream if necessary */
			if((n = head->next - head->data) > 0 &&
			   (m = sfwrite(f,(char*)head->data,n)) != n)
			{	/* write failure, must retract as necessary */
				if((m -= f->next - f->data) > 0)
				{	n -= m;
					memcpy( (char*)head->data,
						(char*)(head->data+m),n);
					head->next = head->data + n;
				}
				f->next = f->data;

				/* resync seek pointer */
				if(head->here != f->here)
				{	if(head->extent >= 0 &&
					   SFSK(head,f->here,0,head->disc) < 0)
						goto no_move;
					head->here = f->here;
				}

				/* sync this stream if possible */
				goto do_sync;
			}

			head->next = head->endr = head->endw = head->data;
		}
		else if(head->mode&p->mode)
		{	/* sync head before pooling it */
		do_sync:if(sfsync(head) < 0)
			{	/* failed to sync, can't move */
			no_move:f->mode |= SF_POOL;
				return -1;
			}
		}

		head->mode |= SF_POOL;
		SFSET(head,head->flags,1);

		SFLOCK(f);
		if(f->fore)
			f->fore->back = f->back;
		f->back->fore = f->fore;
		f->back = NIL(Sfile_t*);
		f->fore = head;
		head->back = f;
		p->head = f;
	}

	SFOPEN(f);
	return 0;
}

#ifdef __STD_C
Sfile_t* sfpool(reg Sfile_t* f, reg Sfile_t* pf, reg int mode)
#else
Sfile_t* sfpool(f,pf,mode)
reg Sfile_t*	f;
reg Sfile_t*	pf;
reg int		mode;
#endif
{
	reg Sfpool_t*	p;
	reg Sfile_t*	rf;

	_Sfpmove = _sfpmove;

	/* nothing to do */
	if(!f || f == pf)
		return NIL(Sfile_t*);

	/* already isolated */
	if(!pf && f->pool == &_Sfpool)
		return NIL(Sfile_t*);

	/* always use current pool mode */
	if(pf && pf->pool != &_Sfpool)
		mode = pf->pool->mode;

	/* new pool but type is unknown */
	if(pf && pf->pool == &_Sfpool && (mode&(SF_RDWR|SF_SHARE)) == 0)
		return NIL(Sfile_t*);

	/* a share pool can only have streams of the same type */
	if((mode&SF_SHARE) && pf && f)
	{	if(!(f->mode&SF_WRITE) && _sfmode(f,SF_WRITE) < 0)
			return NIL(Sfile_t*);
		if(!(pf->mode&SF_WRITE) && _sfmode(pf,SF_WRITE) < 0)
			return NIL(Sfile_t*);
	}

	/* can't manipulate these now */
	if(SFFROZEN(f) || (pf && SFFROZEN(pf)))
		return NIL(Sfile_t*);

	/* throw away ungetc data */
	if(f->disc == _Sfudisc)
		(void)sfclose((*_Sfstack)(f,NIL(Sfile_t*)));
	if(pf && pf->disc == _Sfudisc)
		(void)sfclose((*_Sfstack)(pf,NIL(Sfile_t*)));

	/* return either the new pool or the old pool for deletion */
	p = f->pool;
	if((rf = pf) == NIL(Sfile_t*))
		if((rf = p->head) == f)		/* assert: p != &_Sfpool */
			rf = rf->fore;

	/* isolate f from current pool */
	_sfpmove(f,-1);

	if(!pf)
	{	/* add to the discrete pool */
		f->pool = &_Sfpool;
		_sfpmove(f,1);
		return rf;
	}

	if((p = pf->pool) == &_Sfpool)
	{	/* making a new pool */
		_sfpmove(pf,-1);
		p = newpool(mode);
		pf->pool = p;
		_sfpmove(pf,1);
	}

	if(f->pool != p)
	{	/* insert f into the pool and make it the head */
		if(mode&SF_SHARE)
		{	/* empty the buffer to start clean */
			if(f->next > f->data)
				sfsync(f);
			f->next = f->endr = f->endw = f->data;
		}
		f->pool = p;
		_sfpmove(f,1);
		_sfpmove(f,0);
	}

	return rf;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfpopen.c'
then
	echo shar: will not over-write existing file "'./sfpopen.c'"
else
cat << \SHAR_EOF > './sfpopen.c'
#include	"sfhdr.h"

#if _PACKAGE_ast
#include	<sig.h>
#else
#include	<signal.h>
typedef void(*Handler_t)_ARG_((int));
#endif

/* fork function to use */
#include	"FEATURE/vfork"
#include	"FEATURE/waitpid"

#if _lib_vfork
#define fork	vfork
#if _hdr_vfork
#include	<vfork.h>
#endif
#if _sys_vfork
#include	<sys/vfork.h>
#endif
#endif

#ifdef __cplusplus
extern "C" {
#endif
extern int	fork _ARG_((void));
extern int	waitpid _ARG_((int, int*, int));
extern int	wait _ARG_((int*));
extern int	pipe _ARG_((int*));
extern int	execl _ARG_((char*,char*,...));
#ifdef __cplusplus
}
#endif

/* pipe ends */
#define READ	0
#define WRITE	1

/* to keep track of opened processes */
typedef struct _sfp_
{
	int		pid;	/* process id			*/
	Sfile_t		*sf;	/* stream associated with	*/
	uchar		*rdata;	/* read data being cached	*/
	int		ndata;	/* size of cached data		*/
	int		size;	/* buffer size			*/
	int		file;	/* saved file descriptor	*/
	struct _sfp_	*fore;	/* link list			*/
} Popen_t;
static Popen_t	*Process;

#ifdef __STD_C
static _sfpclose(reg Sfile_t* f)
#else
static _sfpclose(f)
reg Sfile_t	*f;	/* stream to close */
#endif
{
	reg Popen_t	*last, *p;
	int		pid, status;

	/* find the associated process structure */
	for(last = NIL(Popen_t*), p = Process; p != NIL(Popen_t*); last = p, p = p->fore)
		if(f == p->sf)
			break;
	if(!p)
		return -1;

	/* close the associated streams */
	if(p->file >= 0)
		CLOSE(p->file);
	f->flags &= ~SF_PROCESS;
	(void)sfclose(f);

	/* wait for process termination */
#if _lib_waitpid
	pid = waitpid(p->pid,&status,0);
#else
	while((pid = wait(&status)) != p->pid && pid != -1)
		;
#endif

	/* delete from process table */
	if(last)
		last->fore = p->fore;
	else	Process = p->fore;
	if(p->rdata)
		free((char*)p->rdata);
	free((char*)p);

	return (pid == -1 ? -1 : status);
}

#ifdef __STD_C
static int _sfpmode(Sfile_t* f, int type)
#else
static int _sfpmode(f,type)
Sfile_t	*f;
int	type;
#endif
{	reg Popen_t	*last, *p;

	/* find the associated process structure */
	for(last = NIL(Popen_t*), p = Process; p != NIL(Popen_t*); last = p, p = p->fore)
		if(f == p->sf)
			break;
	if(!p)
		return -1;

	/* move-to-front heuristic */
	if(last)
	{	last->fore = p->fore;
		p->fore = Process;
		Process = p;
	}

	if(type == SF_WRITE)
	{	/* save unread data */
		p->ndata = f->endb-f->next;
		if(p->ndata > p->size)
		{	if(p->rdata)
				free((char*)p->rdata);
			if((p->rdata = (uchar*)malloc(p->ndata)) != NIL(uchar*))
				p->size = p->ndata;
			else
			{	p->size = 0;
				return -1;
			}
		}
		if(p->ndata > 0)
			memcpy((char*)p->rdata,(char*)f->next,p->ndata);
		f->endb = f->data;
	}
	else
	{	/* restore read data */
		if(p->ndata > f->size)	/* may lose data!!! */
			p->ndata = f->size;
		if(p->ndata > 0)
		{	memcpy((char*)f->data,(char*)p->rdata,p->ndata);
			f->endb = f->data+p->ndata;
			p->ndata = 0;
		}
	}

	/* switch file descriptor */
	type = f->file;
	f->file = p->file;
	p->file = type;
	return 0;
}

#ifdef __STD_C
Sfile_t *sfpopen(Sfile_t* f, const char* command, const char* mode)
#else
Sfile_t *sfpopen(f,command,mode)
Sfile_t	*f;
char	*command;	/* command to execute */
char	*mode;		/* mode of the stream */
#endif
{
	reg int		pid, pkeep, ckeep, sflags;
	reg Popen_t	*p, *cp;
	int		parent[2], child[2];
	Sfile_t		sf;
	Handler_t	sig;

	/* set the close and mode switch functions */
	_Sfpclose = _sfpclose;
	_Sfpmode = _sfpmode;

	/* sanity check */
	if(!command || !command[0] || !(sflags = _sftype(mode,NIL(int*))))
		return NIL(Sfile_t*);

	/* make a process structure */
	if(!(p = (Popen_t*) malloc(sizeof(Popen_t))) )
		return NIL(Sfile_t*);

	/* make pipes */
	parent[0] = parent[1] = child[0] = child[1] = -1;
	if(pipe(parent) < 0)
		goto error;
	if((sflags&SF_RDWR) == SF_RDWR && pipe(child) < 0)
		goto error;

	switch(pid = fork())
	{
	default :	/* in parent process */
		if(sflags&SF_READ)
			{ pkeep = READ; ckeep = WRITE; }
		else	{ pkeep = WRITE; ckeep = READ; }

		/* make the streams */
		if(!(f = sfnew(f,NIL(char*),-1,parent[pkeep],sflags)))
			goto error;
		f->flags |= SF_PROCESS;
		CLOSE(parent[!pkeep]);

		if((sflags&SF_RDWR) == SF_RDWR)
			CLOSE(child[!ckeep]);

		/* save process info */
		p->pid = pid;
		p->sf = f;
		p->size = p->ndata = 0;
		p->rdata = NIL(uchar*);
		p->file = (sflags&SF_RDWR) == SF_RDWR ? child[ckeep] : -1;
		p->fore = Process;
		Process = p;

		/* protect from broken pipe signal */
		if((sflags&SF_WRITE) &&
		   (sig = signal(SIGPIPE,SIG_IGN)) != SIG_DFL && sig != SIG_IGN)
			signal(SIGPIPE,sig);

		return f;

	case 0 :	/* in child process */
		for(cp = Process; cp != NIL(Popen_t*); cp = cp->fore)
		{	/* close all other pipe ends */
			if(cp->file >= 0)
				CLOSE(cp->file);
			CLOSE(cp->sf->file);
		}

		/* determine what to keep */
		if(sflags&SF_READ)
			{ pkeep = WRITE; ckeep = READ; }
		else	{ pkeep = READ; ckeep = WRITE; }

		/* zap fd that we don't need */
		CLOSE(parent[!pkeep]);
		if((sflags&SF_RDWR) == SF_RDWR)
			CLOSE(child[!ckeep]);

		/* use sfsetfd to make these descriptors the std-ones */
		SFCLEAR(&sf);

		/* must be careful so not to close something useful */
		if((sflags&SF_RDWR) == SF_RDWR && pkeep == child[ckeep])
			if((child[ckeep] = dup(pkeep)) < 0)
				_exit(127);

		if(parent[pkeep] != pkeep)
		{	sf.file = parent[pkeep];
			CLOSE(pkeep);
			if(sfsetfd(&sf,pkeep) != pkeep)
				_exit(127);
		}

		if((sflags&SF_RDWR) == SF_RDWR && child[ckeep] != ckeep)
		{	sf.file = child[ckeep];
			CLOSE(ckeep);
			if(sfsetfd(&sf,ckeep) != ckeep)
				_exit(127);
		}

		/* now exec the command */
		execl("/bin/sh", "sh", "-c", command, NIL(char*));
		_exit(127);
		return NIL(Sfile_t*);

	case -1 :	/* error */
	error:
		if(parent[0] >= 0)
			{ CLOSE(parent[0]); CLOSE(parent[1]); }
		if(child[0] >= 0)
			{ CLOSE(child[0]); CLOSE(child[1]); }
		free((char*)p);
		return NIL(Sfile_t*);
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfprintf.c'
then
	echo shar: will not over-write existing file "'./sfprintf.c'"
else
cat << \SHAR_EOF > './sfprintf.c'
#include	"sfhdr.h"

/*	Print data with a given format
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
sfprintf(Sfile_t *f, const char *form, ...)
#else
sfprintf(va_alist)
va_dcl
#endif
{
	va_list	args;
	reg int	rv;

#ifdef __STD_C
	va_start(args,form);
#else
	reg Sfile_t	*f;
	reg char	*form;
	va_start(args);
	f = va_arg(args,Sfile_t*);
	form = va_arg(args,char*);
#endif
	rv = sfvprintf(f,form,args);

	va_end(args);
	return rv;
}

#ifdef __STD_C
sfsprintf(char *s, int n, const char *form, ...)
#else
sfsprintf(va_alist)
va_dcl
#endif
{
	va_list	args;
	Sfile_t	f;
	reg int	rv;

#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*s;
	reg int		n;
	reg char	*form;
	va_start(args);
	s = va_arg(args,char*);
	n = va_arg(args,int);
	form = va_arg(args,char*);
#endif

	if(!s || n <= 0)
		return -1;

	/* make a fake stream */
	SFCLEAR(&f);
	f.flags = SF_STRING|SF_WRITE;
	f.mode = SF_WRITE;
	f.size = n-1;
	f.data = f.next = f.endr = (uchar*)s;
	f.endb = f.endw = f.data+f.size;

	rv = sfvprintf(&f,form,args);
	*f.next = '\0';
	_Sfi = f.next - f.data;

	va_end(args);

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfprints.c'
then
	echo shar: will not over-write existing file "'./sfprints.c'"
else
cat << \SHAR_EOF > './sfprints.c'
#include	"sfhdr.h"

/*	Construct a string with the given format and data.
**	This function allocates space as necessary to store the string.
**	This avoids overflow problems typical with sprintf() in stdio.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
char *sfprints(const char *form, ...)
#else
char *sfprints(va_alist)
va_dcl
#endif
{
	va_list		args;
	reg int		rv;
	static Sfile_t	*f;

#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*form;
	va_start(args);
	form = va_arg(args,char*);
#endif

	/* make a fake stream */
	if(!f && !(f = sfnew(NIL(Sfile_t*),NIL(char*),-1,-1,SF_WRITE|SF_STRING)) )
		return NIL(char*);

	sfseek(f,0L,0);
	rv = sfvprintf(f,form,args);
	va_end(args);

	if(rv < 0 || sfputc(f,'\0') < 0)
		return NIL(char*);

	_Sfi = (f->next - f->data) - 1;
	return (char*)f->data;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfpurge.c'
then
	echo shar: will not over-write existing file "'./sfpurge.c'"
else
cat << \SHAR_EOF > './sfpurge.c'
#include	"sfhdr.h"

/*	Delete all pending data in the buffer
**
**	Written by Kiem-Phong Vo (07/08/91)
*/

#ifdef __STD_C
sfpurge(reg Sfile_t* f)
#else
sfpurge(f)
reg Sfile_t	*f;
#endif
{
	if(SFFROZEN(f))
		return -1;

	if(f->disc == _Sfudisc)
		(void)sfclose((*_Sfstack)(f,SF_POPSTACK));

	/* cannot purge read string streams */
	if((f->flags&SF_STRING) && (f->mode&SF_READ) )
		return 0;

	/* if memory map must be a read stream, pretend data is gone */
	if(f->flags&SF_MMAP)
	{	f->here -= f->endb - f->next;
		f->endb = f->endr = f->next = f->data;
		return 0;
	}

	switch(f->mode)
	{
	default :
		return -1;
	case SF_WRITE :
		f->next = f->data;
		if((f->flags&(SF_PROCESS|SF_RDWR)) != (SF_PROCESS|SF_RDWR))
			break;

		/* 2-way pipe, must clear read buffer */
		(void)_sfmode(f,SF_READ);
		/* fall through */
	case SF_READ:
		if(f->extent >= 0 && f->endb > f->next)
		{	f->here -= f->endb-f->next;
			SFSK(f,f->here,0,f->disc);
		}
		f->endb = f->next = f->data;
		break;
	}

	SFOPEN(f);
	return 0;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfputd.c'
then
	echo shar: will not over-write existing file "'./sfputd.c'"
else
cat << \SHAR_EOF > './sfputd.c'
#include	"sfhdr.h"

/*	Write out a double value in a portable format
**
**	Written by Kiem-Phong Vo (08/05/90)
*/

#ifdef __STD_C
_sfputd(Sfile_t* f, reg double v)
#else
_sfputd(f,v)
Sfile_t		*f;
reg double	v;
#endif
{
#define N_ARRAY		(16*sizeof(double))
	reg int		n, w;
	reg double	x;
	reg uchar	*s, *ends;
	int		exp;
	uchar		c[N_ARRAY];

	if(f->mode != SF_WRITE && _sfmode(f,SF_WRITE) < 0)
		return -1;

	/* get the sign of v */
	if(v < 0.)
	{	v = -v;
		n = 1;
	}
	else	n = 0;

	/* make the magnitude of v < 1 */
	if(v != 0.)
		v = frexp(v,&exp);
	else	exp = 0;

	/* code the sign of v and exp */
	if((w = exp) < 0)
	{	n |= 02;
		w = -w;
	}

	/* write out the signs and the exp */
	if(sfputc(f,n) < 0 || (w = sfputu(f,w)) < 0)
		return -1;
	w += 1;

	s = (ends = &c[0])+sizeof(c);
	while(s > ends)
	{	/* get 2^SF_PRECIS precision at a time */
		n = (int)(x = ldexp(v,SF_PRECIS));
		*--s = n|SF_MORE;
		if((v = x-n) <= 0.)
			break;
	}

	/* last byte is not SF_MORE */
	ends = &c[0] + sizeof(c) -1;
	*ends &= ~SF_MORE;

	/* write out coded bytes */
	n = ends - s + 1;
	return sfwrite(f,(char*)s,n) == n ? w+n : -1;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfputl.c'
then
	echo shar: will not over-write existing file "'./sfputl.c'"
else
cat << \SHAR_EOF > './sfputl.c'
#include	"sfhdr.h"

/*	Write out a long value in a portable format
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
_sfputl(reg Sfile_t* f, reg long v)
#else
_sfputl(f,v)
reg Sfile_t	*f;	/* write a portable long to this stream */
reg long	v;	/* the value to be written */
#endif
{
#define N_ARRAY		(2*sizeof(long))
	reg uchar	*s, *ps;
	reg int		n, p;
	uchar		c[N_ARRAY];

	if(f->mode != SF_WRITE && _sfmode(f,SF_WRITE) < 0)
		return -1;

	s = ps = &(c[N_ARRAY-1]);
	if(v < 0)
	{	/* add 1 to avoid 2-complement problems with -MAXINT */
		v = -(v+1);
		*s = (uchar)(SFSVALUE(v) | SF_SIGN);
	}
	else	*s = (uchar)(SFSVALUE(v));
	v = (ulong)v >> SF_SBITS;

	while(v > 0)
	{
		*--s = (uchar)(SFUVALUE(v) | SF_MORE);
		v = (ulong)v >> SF_UBITS;
	}
	n = (ps-s)+1;

	/* write the hard way */
	if(n > 7 || SFWPEEK(f,ps,p) < n)
		return sfwrite(f,(char*)s,n);

	SFLOCK(f);
	switch(n)
	{
	case 7 : *ps++ = *s++;
	case 6 : *ps++ = *s++;
	case 5 : *ps++ = *s++;
	case 4 : *ps++ = *s++;
	case 3 : *ps++ = *s++;
	case 2 : *ps++ = *s++;
	case 1 : *ps++ = *s++;
	}
	f->next = ps;
	SFOPEN(f);

	return n;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfputr.c'
then
	echo shar: will not over-write existing file "'./sfputr.c'"
else
cat << \SHAR_EOF > './sfputr.c'
#include	"sfhdr.h"

/*	Put out a nul-terminated string
**
**	Written by Kiem-Phong Vo
*/
#ifdef __STD_C
sfputr(Sfile_t* f, const char* s, reg int t)
#else
sfputr(f,s,t)
reg Sfile_t	*f;
char		*s;
reg int		t;
#endif
{
	reg int		p;
	reg uchar	*os, *ps;
	reg int		n;

	if(f->mode != SF_WRITE && _sfmode(f,SF_WRITE) < 0)
		return -1;

	SFLOCK(f);
	os = (uchar*)s;
	if(f->size <= 0)
	{	/* unbuffered stream */
		n = strlen((char*)os);
		SFopen(f);
		if((p = sfwrite(f,(char*)os,n)) > 0)
			os += p;
		goto done;
	}

	while(*os)
	{	/* peek buffer for space */
		if(SFWPEEK(f,ps,p) <= 0)
			break;

#if _vax_asm	/* p is r9, os is r8, and ps is r7 */
		0;					/* avoid if() branching bug */
		asm( "locc	$0,r9,(r8)" );		/* look for the \0 */
		asm( "subl2	r0,r9" );		/* length of data to copy */
		asm( "movc3	r9,(r8),(r7)" );	/* copy data */
		ps += p;
		os += p;
#else
#if _lib_memccpy
		if((ps = (uchar*)memccpy((char*)ps,(char*)os,'\0',p)) != NIL(uchar*))
			ps -= 1;
		else	ps  = f->next+p;
		os += ps - f->next;
#else
		/* fast copy loop */
		while((*ps++ = *os++) != '\0' && --p > 0)
			;
		if(*--ps != 0)
			ps += 1;
		else	os -= 1;
#endif
#endif
		f->next = ps;
	}

done:
	p = (char*)os - (char*)s;
	if(t >= 0)
	{	if(f->next >= f->endb)
		{	SFopen(f);
			(void)_sfflsbuf(f,(int)((uchar)t));
		}
		else	*f->next++ = (uchar)t;
		p += 1;
	}

	SFOPEN(f);

	/* sync unseekable shared streams */
	if(f->extent < 0 && (f->flags&SF_SHARE) )
		(void)_sfflsbuf(f,-1);

	/* check for line buffering */
	else if((f->flags&SF_LINE) && !(f->flags&SF_STRING) && (n = f->next-f->data) > 0)
	{	if(n > p)
			n = p;
		f->next -= n;
		(void)sfwrite(f,(char*)f->next,n);
	}

	return p;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfputu.c'
then
	echo shar: will not over-write existing file "'./sfputu.c'"
else
cat << \SHAR_EOF > './sfputu.c'
#include	"sfhdr.h"

/*	Write out an unsigned long value in a portable format.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
_sfputu(reg Sfile_t* f, reg ulong v)
#else
_sfputu(f,v)
reg Sfile_t	*f;	/* write a portable ulong to this stream */
reg ulong	v;	/* the unsigned value to be written */
#endif
{
#define N_ARRAY		(2*sizeof(long))
	reg uchar	*s, *ps;
	reg int		n, p;
	uchar		c[N_ARRAY];

	if(f->mode != SF_WRITE && _sfmode(f,SF_WRITE) < 0)
		return -1;

	/* code v as integers in base SF_UBASE */
	s = ps = &(c[N_ARRAY-1]);
	*s = (uchar)SFUVALUE(v);
	while((v >>= SF_UBITS) > 0)
		*--s = (uchar)(SFUVALUE(v) | SF_MORE);
	n = (ps-s)+1;

	if(n > 7 || SFWPEEK(f,ps,p) < n)
		return sfwrite(f,(char*)s,n);

	SFLOCK(f);
	switch(n)
	{
	case 7 : *ps++ = *s++;
	case 6 : *ps++ = *s++;
	case 5 : *ps++ = *s++;
	case 4 : *ps++ = *s++;
	case 3 : *ps++ = *s++;
	case 2 : *ps++ = *s++;
	case 1 : *ps++ = *s++;
	}
	f->next = ps;
	SFOPEN(f);

	return n;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfrd.c'
then
	echo shar: will not over-write existing file "'./sfrd.c'"
else
cat << \SHAR_EOF > './sfrd.c'
#include	"sfhdr.h"

/*	Internal function to do a hard read.
**	This knows about discipline and memory mapping, etc.
**
**	Written by Kiem-Phong Vo (02/11/91)
*/

/* synchronize unseekable write streams */
static void _sfwrsync()
{	reg Sfpool_t	*pp;
	reg Sfile_t	*fp;

	/* sync all pool heads */
	for(pp = _Sfpool.fore; pp != NIL(Sfpool_t*); pp = pp->fore)
		if((fp = pp->head) && (fp->mode&SF_WRITE) &&
		   fp->extent < 0 && fp->next > fp->data)
			(void)_sfflsbuf(fp,-1);

	/* and all the ones in the discrete pool */
	for(fp = _Sfpool.head; fp != NIL(Sfile_t*); fp = fp->fore)
		if((fp->mode&SF_WRITE) && fp->extent < 0 && fp->next > fp->data)
			(void)_sfflsbuf(fp,-1);
}

#ifdef __STD_C
sfrd(reg Sfile_t* f, reg char* buf, reg int n, reg Sfdisc_t* disc)
#else
sfrd(f,buf,n,disc)
reg Sfile_t	*f;
reg char	*buf;
reg int		n;
reg Sfdisc_t	*disc;
#endif
{
	reg long	r;
	reg int		local, string, dosync;

	GETLOCAL(f,local);

#ifdef MAP_TYPE
	if(f->flags&SF_MMAP)	/* there should be no discipline used */
	{	reg int	a;

		/* this stream is inaccessible for now */
		if(f->file < 0)
			return 0;

		if(f->data)
		{	/* release current map */
			(void)munmap((caddr_t)f->data,f->size);
			if((uchar*)buf == f->data)
				n = 0;
			f->endb = f->endr = f->endw = f->next = f->data = NIL(uchar*);
			f->size = 0;
		}

		/* a huge read, let read() handle this one */
		if(n >= _Sfmapsize)
		{	(void)SFSK(f,f->here,0,disc);
			if((r = read(f->file,buf,n)) > 0)
				f->here += r;
			else	f->flags |= (r == 0 ? SF_EOF : SF_ERROR);
			return (int)r;
		}

		if((r = f->extent - f->here) <= 0)
		{	struct stat st;
			if(fstat(f->file,&st) >= 0 && st.st_size != f->extent)
			{	f->extent = st.st_size;
				if(f->here > f->extent)
					f->here = f->extent;
				r = f->extent - f->here;
			}
			if(r <= 0)
			{	f->flags |= SF_EOF;
				(void)SFSK(f,f->here,0,disc);
				return 0;
			}
		}

		/* make sure current position is page aligned */
		if((a = (int)(f->here%_Sfpage)) != 0)
		{	f->here -= a;
			r += a;
		}

		if(r > _Sfmapsize)
			r = _Sfmapsize;
		f->data = (uchar*) mmap(NIL(caddr_t),(int)r,PROT_READ|PROT_WRITE,
				(f->flags&SF_BOTH) ? MAP_SHARED : MAP_PRIVATE,
				f->file,f->here);
		if(f->data != (uchar*)((caddr_t)(-1)))
		{	/* success */
			f->size = (int)r;
			f->next = f->data+a;
			f->endr = f->endb = f->data+r;
			f->endw = f->data;
			f->here += r;
			if(n > 0)
			{	if(n > (r-a))
					n = (int)(r-a);
				memcpy((char*)buf,(char*)f->next,n);
				f->next += n;
				return n;
			}
			else	return (int)r;
		}
		else
		{	r = -1;
			f->here += a;
			f->data = NIL(uchar*);

			/* reset seek pointer to its physical location */
			(void)SFSK(f,f->here,0,disc);

			/* make a buffer */
			SFopen(f);
			f->flags |= SF_SETBUF;
			(void)sfsetbuf(f,NIL(char*),-1);
			SFlock(f);
			if(n == 0)
			{	buf = (char*)f->data;
				n = f->size;
			}
		}
	}
#endif

	/* continuation */
	if(!(string = (f->flags&SF_STRING)))
	{	reg Sfdisc_t	*d;
		if((d = disc) != NIL(Sfdisc_t*) && !local)
			d = disc = disc->disc;
		while(d && !d->readf)
			d = d->disc;
		if(d)
			disc = d;
	}

	dosync = 0;
	for(;;)
	{
		if(string)
		{	if(f->flags&SF_BOTH)
				r = (f->data+f->here) - f->next;
			else	r = f->endb - f->next;
		}
		else
		{
			if(f->flags&SF_SHARE) /* make sure file pointer is right */
				(void)SFSK(f,f->here,0,disc);

			/* sync unseekable write streams to prevent deadlock */
			if(!dosync && f->extent < 0)
			{	dosync = 1;
				_sfwrsync();
			}

			if(disc && disc->readf)
				r = (*(disc->readf))(f,buf,n,disc);
			else	r = read(f->file,buf,n);
			if(local && r > 0)
			{	f->here += r;
				if((uchar*)buf >= f->data &&
				   (uchar*)buf < f->data+f->size)
				{	f->endb = f->endr = ((uchar*)buf) + r;
					if((uchar*)buf == f->data)
						f->flags &= ~SF_JNKBUF;
				}
			}
		}
		if(r > 0)
			return (int)r;

		if(local)
			SETLOCAL(f);
		switch(_sfexcept(f,SF_READ,(int)r,disc))
		{
		case SF_ECONT :
			continue;
		case SF_EDONE :
			return local ? 0 : r;
		case SF_EDISC :
			if(!local && !string)
				continue;
			/* else fall thru */
		case SF_ESTACK :
			return -1;
		}
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfread.c'
then
	echo shar: will not over-write existing file "'./sfread.c'"
else
cat << \SHAR_EOF > './sfread.c'
#include	"sfhdr.h"

/*	Read n bytes from a tream into a buffer
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
sfread(reg Sfile_t* f, reg char* s, reg int n)
#else
sfread(f,s,n)
reg Sfile_t	*f;	/* read from this stream */
reg char	*s;	/* buffer to read into */
reg int		n;	/* number of bytes to be read */
#endif
{
	reg uchar	*next;
	reg int		r;
	reg char	*begs = s;	/* true start of buffer */

	for(;; SFopen(f))
	{	/* see if peek lock can be released */
		if((f->mode&SF_PEEK) && (uchar*)s == f->next)
		{	if(f->mode&SF_READ)
				f->mode &= ~SF_PEEK;
			/* note that sfpeek() turns r/w streams to read mode so
			   if f->mode&SF_WRITE, we must have a pure write stream.
			*/
			else	return -1;
		}

		/* check stream mode */
		if(f->mode != SF_READ && _sfmode(f,SF_READ) < 0)
			return s > begs ? s-begs : -1;

		/* nothing to do */
		if(n <= 0)
			break;

		SFLOCK(f);

		/* determine current amount of buffered data */
		if((f->flags&(SF_STRING|SF_BOTH)) == (SF_STRING|SF_BOTH))
		{	if((r = f->next-f->data) > f->here && r != f->extent)
				f->here = f->extent = r;
			r = (f->data+f->here) - f->next;
		}
		else	r = f->endb - f->next;
		if(r > n)
			r = n;

		if((uchar*)s == f->next)
		{	/* fast read after sfpeek() */
			f->next += r;
			break;
		}

		if(r > 0)
		{	/* copy data already in buffer */
			next = f->next;

#if _vax_asm		/* s is r10, next is r8, r is r7 */
			asm( "movc3	r7,(r8),(r10)" );
			s += r;
			next += r;
#else
			MEMCPY(s,next,r);
#endif
			f->next = next;
			n -= r;
		}
		else if((f->flags&SF_STRING) ||
			(n < f->size && (!(f->flags&SF_SHARE) || f->extent >= 0)) )
		{	if((SFopen(f),_sffilbuf(f,-1)) <= 0)
				break;
		}
		else
		{	/* buffer is now empty */
			f->next = f->endb = f->data;
			if(f->extent < 0 && (f->flags&SF_SHARE) )
				r = n;
			else	r = n < _Sfpage ? n : (n/_Sfpage)*_Sfpage;
			if((r = SFRD(f,s,r,f->disc)) == 0)
				break;
			else if(r > 0)
			{	s += r;
				n -= r;
			}
		}
	}

	SFOPEN(f);
	return s-begs;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfscanf.c'
then
	echo shar: will not over-write existing file "'./sfscanf.c'"
else
cat << \SHAR_EOF > './sfscanf.c'
#include	"sfhdr.h"

/*	Read formated data from a stream
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
sfscanf(Sfile_t *f, const char *form, ...)
#else
sfscanf(va_alist)
va_dcl
#endif
{
	va_list	args;
	reg int	rv;

#ifdef __STD_C
	va_start(args,form);
#else
	reg Sfile_t	*f;
	reg char	*form;
	va_start(args);
	f = va_arg(args,Sfile_t*);
	form = va_arg(args,char*);
#endif

	rv = sfvscanf(f,form,args);
	va_end(args);
	return rv;
}

#ifdef __STD_C
sfsscanf(const char *s, const char *form,...)
#else
sfsscanf(va_alist)
va_dcl
#endif
{
	va_list		args;
	Sfile_t		f;
	reg int		rv;
#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*s;
	reg char	*form;
	va_start(args);
	s = va_arg(args,char*);
	form = va_arg(args,char*);
#endif

	if(!s)
		return -1;

	/* make a fake stream */
	SFCLEAR(&f);
	f.flags = SF_STRING|SF_READ;
	f.mode = SF_READ;
	f.size = strlen((char*)s);
	f.data = f.next = f.endw = (uchar*)s;
	f.endb = f.endr = f.data+f.size;

	rv = sfvscanf(&f,form,args);
	va_end(args);

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfseek.c'
then
	echo shar: will not over-write existing file "'./sfseek.c'"
else
cat << \SHAR_EOF > './sfseek.c'
#include	"sfhdr.h"

/*	Set the IO pointer to a specific location in the stream
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
long sfseek(reg Sfile_t* f, reg long p, reg int type)
#else
long sfseek(f,p,type)
reg Sfile_t	*f;	/* seek to a new location in this stream */
reg long	p;	/* place to seek to */
int		type;	/* 0: from org, 1: from here, 2: from end */
#endif
{
	reg long	r, s;

	/* set the stream to the right mode */
	if(!SFSETMODE(f))
		return -1;

	if(type < 0 || type > 2 || f->extent < 0L)
		return -1;

	/* throw away ungetc data */
	if(f->disc == _Sfudisc)
		(void)sfclose((*_Sfstack)(f,NIL(Sfile_t*)));

	/* lock the stream for internal manipulations */
	SFLOCK(f);

	/* clear error and eof bits */
	f->flags &= ~(SF_EOF|SF_ERROR);

	if(f->flags&SF_STRING)
	{
		for(;;)
		{	/* to handle exception */
			if((f->flags&(SF_WRITE|SF_BOTH)) &&
			   (s = f->next-f->data) > f->here && s != f->extent)
				f->here = s;

			/* new location */
			if(type == 1)
				r = p + (f->next - f->data);
			else if(type == 2)
				r = p + f->here;
			else	r = p;

			if(r >= 0 && r <= f->size)
			{	p = r;
				break;
			}

			/* check exception handler */
			if(SFSK(f,r,0,f->disc) != 0)
			{	p = -1;
				goto done;
			}
		}

		f->next = f->data+p;
		f->extent = p;	/* use this to determine if writes occured */

		/* clear the part skipped over */
		if(p > f->here)
			memclear((char*)(f->data+f->here),(int)(p-f->here));

		goto done;
	}

	/* currently known seek location */
	s = f->here + ((f->mode&SF_READ) ? f->next-f->endb : f->next-f->data);

	/* no need to do anything */
	if(!(f->flags&SF_SHARE) && type == 0 && s == p)
		goto done;

#ifdef MAP_TYPE
	if(f->flags&SF_MMAP)
	{	/* move buffer pointer if possible */
		r = f->unmap;
		p += type == 0 ? 0L : type == 1 ? s : f->extent;
		if(p >= (f->here - f->size) && p < f->here &&
		   (p >= s || !(f->flags&SF_JNKBUF)))
			f->next = f->endb - (f->here - p);
		else if(p >= 0 || (f->flags&SF_JNKBUF))
		{	/* zap the map */
			if(f->data)
			{	(void)munmap((caddr_t)f->data,f->size);
				f->endb = f->endr = f->endw =
				f->next = f->data = NIL(uchar*);
				f->size = 0;
				f->unmap += 1;
			}
			if(p >= f->extent && (p = SFSK(f,p,0,f->disc)) >= 0)
				f->extent = p;
			if(p >= 0)
				f->here = p;
		}

		/* if too many munmap because of seeks, don't use mmap */
		if(r != f->unmap &&
		   (f->unmap == 0 || (long)(f->unmap*_Sfmapsize) > f->extent))
		{	f->unmap = 0;
			(void)SFSK(f,f->here,0,f->disc);
			f->flags |= SF_SETBUF;
			SFopen(f);
			(void)sfsetbuf(f,NIL(char*),-1);
		}

		goto done;
	}
#endif

	if(type == 1 && p == 0 && !(f->flags&(SF_APPEND|SF_SHARE)) )
	{	/* certify current location only */
		if((p = SFSK(f,f->here,0,f->disc)) != f->here)
			p = -1;
		else	p = s;
		goto done;
	}

	if(f->mode&SF_WRITE)
	{	/* sync physical and virtual views */
		SFopen(f);
		if(_sfflsbuf(f,-1) < 0)
		{	p = -1;
			goto done;
		}
		SFLOCK(f);
		s = f->here;

		if(f->flags&SF_APPEND)
		{	/* the true location for a SF_APPEND stream */
			s = f->here = SFSK(f,0L,2,f->disc);
			r = p + (type == 0 ? 0L : s);
		}
		else if(!(f->flags&SF_SHARE))
			r = type == 0 ? p : type == 1 ? p+s : -1;
		else	r = -1;
		if(r == s)
		{	/* at where we are supposed to be */
			p = r;
			goto done;
		}
	}
	else if(type < 2 && !(f->flags&(SF_SHARE|SF_APPEND|SF_MMAP)) &&
		(r = p + (type == 0 ? 0L : s)) > f->here && r < f->extent)
	{	/* move forward by reading */
		if(f->next == f->endb)
			f->endb = f->endr = f->next = f->data;
		if(r < (s + (f->size - (f->endb-f->data))))
			{ SFopen(f); (void)_sffilbuf(f,-1); SFLOCK(f); }
	}

	if(type < 2)
	{	/* place to seek to */	
		p += type == 0 ? 0L : s;

		if((f->mode&SF_READ) && !(f->flags&(SF_SHARE|SF_APPEND|SF_JNKBUF)) &&
		   p >= (f->here - (f->endb-f->data)) && p <= f->here)
		{	/* still in bound, safe to just move the pointer */
			f->next = f->endb - (f->here-p);
			goto done;
		}
	}
	else
	{	if((p = SFSK(f,p,2,f->disc)) < 0)
			goto done;
		f->here = p;
	}

	if(f->mode&SF_READ)
	{	/* any buffered data is invalid */
		f->next = f->endr = f->endb = f->data;
		f->flags &= ~SF_JNKBUF;

		/* seek to a rounded boundary to improve performance */
		if(f->size > 1 && p < f->extent && (r = (p/f->size)*f->size) < p &&
		   SFSK(f,r,0,f->disc) == r)
		{	f->here = r;
			SFopen(f); (void)_sffilbuf(f,-1); SFLOCK(f);
			if(f->here < p)
				p = -1;
			else	f->next = f->endb - (f->here-p);
			goto done;
		}
	}

	/* if get here must do a seek */
	if(type < 2 && (p = SFSK(f,p,0,f->disc)) >= 0)
		f->here = p;

done :
	SFOPEN(f);
	return p;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfset.c'
then
	echo shar: will not over-write existing file "'./sfset.c'"
else
cat << \SHAR_EOF > './sfset.c'
#include	"sfhdr.h"

/*	Set some control flags or file descript for the stream
**
**	Written by Kiem-Phong Vo (07/16/90)
*/

#ifdef __STD_C
sfset(reg Sfile_t* f, reg int flags, reg int set)
#else
sfset(f,flags,set)
reg Sfile_t	*f;
reg int		flags;
reg int		set;
#endif
{
	reg int	oflags, local;

	GETLOCAL(f,local);

	/* avoid SFSETMODE() so sfpool() won't cause recursion */
	if(!local)
	{	if(SFFROZEN(f))
			return 0;
		else if(f->mode&SF_INIT)
			(void)sfsetbuf(f,NIL(char*),-1);
	}

	SFLOCK(f);

	/* at least preserve one rd/wr flag of the right type */
	oflags = f->flags;
	if(!(oflags&SF_BOTH))
		flags &= ~SF_RDWR;

	/* set the flag */
	if(set)
		f->flags |=  (flags&SF_SETS);
	else	f->flags &= ~(flags&SF_SETS);

	/* must have at least one of read/write */
	if(!(f->flags&SF_RDWR))
		f->flags |= (oflags&SF_RDWR);

	if(f->extent < 0)
		f->flags &= ~SF_APPEND;

	/* turn to appropriate mode as necessary */
	if(!(flags &= SF_RDWR) )
		flags = f->flags&SF_RDWR;
	if((flags == SF_WRITE && !(f->mode&SF_WRITE)) ||
	   (flags == SF_READ && !(f->mode&(SF_READ|SF_SYNC))) )
	{	SFopen(f);
		(void)_sfmode(f,flags);
		SFlock(f);
	}

	SFOPEN(f);
	return (oflags&SF_FLAGS);
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfsetbuf.c'
then
	echo shar: will not over-write existing file "'./sfsetbuf.c'"
else
cat << \SHAR_EOF > './sfsetbuf.c'
#include	"sfhdr.h"

/*	Set a (new) buffer for a stream.
**	If size < 0, it is assigned a suitable value depending on the
**	kind of stream. The actual buffer size allocated is dependent
**	on how much memory is available.
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
char *sfsetbuf(reg Sfile_t* f, reg char* buf, reg int size)
#else
char *sfsetbuf(f,buf,size)
reg Sfile_t	*f;	/* stream to be buffered */
reg char	*buf;	/* new buffer */
reg int		size;	/* buffer size, -1 for default size */
#endif
{
	reg int		sf_malloc;
	struct stat	st;
	reg uchar	*obuf;
	reg int		osize, oflags, init, justopen, blksize;

	if(SFFROZEN(f))
		return NIL(char*);

	obuf = f->data;
	osize = f->size;
	oflags = f->flags;
	blksize = 0;

	justopen = f->mode&SF_OPEN;
	f->mode &= ~SF_OPEN;
	if((init = (f->mode&SF_INIT)) != 0)
		f->mode &= SF_RDWR;
	else if(sfsync(f) < 0) /* sync stream before switching buffer */
		return NIL(char*);
	else
	{	/* turn to read mode to ensure there is no saved data of a coprocess */
		if((f->flags&(SF_PROCESS|SF_BOTH)) == (SF_PROCESS|SF_BOTH) &&
		   (f->mode&SF_WRITE) && _sfmode(f,SF_READ) < 0)
			return NIL(char*);
		if((f->mode&SF_READ) && f->extent < 0 && f->endb > f->next)
			return NIL(char*);
	}

	/* lock for internal work */
	SFLOCK(f);

	/* pure read/string streams must have a valid string */
	if((f->flags&(SF_RDWR|SF_STRING)) == SF_RDSTR && (size < 0 || !buf))
		size = 0;

#ifdef MAP_TYPE
	if(obuf && (f->flags&SF_MMAP))
	{	/* release current map */
		(void)munmap((caddr_t)obuf,f->size);
		obuf = NIL(uchar*);
		osize = 0;
	}
#endif
	f->flags &= ~(SF_MMAP|SF_MALLOC);

	if(init && !(f->flags&SF_STRING))
	{	/* get the file status */
		if(f->file >= 0)
		{	if(fstat(f->file,&st) < 0)
				goto unseekable;

#if _stat_blksize	/* preferred io block size */
			blksize = (int)st.st_blksize;
#endif
			if(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
				f->here = justopen ? 0L : SFSK(f,0L,1,f->disc);
			else	f->here = -1;
			if(f->here < 0)
			{	/* check for various char devices */
				if(!S_ISFIFO(st.st_mode) && S_ISCHR(st.st_mode) )
				{	if(isatty(f->file))
						f->flags |= SF_LINE;
					else if(f == sfstdout || f == sfstderr)
					{	reg int	dev, ino;
						dev = (int)st.st_dev;	
						ino = (int)st.st_ino;	
						if(stat("/dev/null",&st) >= 0 &&
						   dev == st.st_dev && ino == st.st_ino)
						{	SETDEVNULL(f);
							blksize = 1024;
						}
					}
				}
				goto unseekable;
			}

			/* normal file, set file extent */
			f->extent = (long)st.st_size;

			/* don't mmap directories */
			if(S_ISDIR(st.st_mode))
				f->flags |= SF_SETBUF;
		}
		else
		{
		unseekable:
			if(f->extent >= 0L)
				f->extent = -1L;
			f->here = 0L;
			if(f == sfstdin || f == sfstdout || f == sfstderr)
				f->flags |= SF_SHARE;
		}

		/* set page size, this is also the desired default buffer size */
#if _lib_getpagesize
		if(_Sfpage <= 0)
			_Sfpage = (int)getpagesize();
#endif
		if(_Sfpage <= 0)
			_Sfpage = SF_BUFSIZE;
		if(_Sfmapsize <= 0)
			_Sfmapsize = 4*_Sfpage;
	}

	/* get buffer space */
	if(size >= 0)
		f->flags |= SF_SETBUF;
	else
	{	/* define a default size suitable for block transfer */
		buf = NIL(char*);
		if(osize > 0)
			size = osize;
		else if(f == sfstderr && (f->mode&SF_WRITE))
			size = 0;
		else if((f->flags&SF_STRING) || f->extent < 0)
			size = SF_GRAIN;
#ifdef MAP_TYPE
		else if(!SFNOMAP(f))
		{	/* give memory mapping a try */
			f->flags |= SF_MMAP;
			size = 0;
		}
#endif
		else if(blksize > 0)	/* natural size for io */
			size = blksize;
		/* default size is a page */
		else if((f->flags&SF_SHARE) || !(f->flags&SF_READ) ||
			f->extent <= 0 || f->extent >= _Sfpage)
			size = _Sfpage;
		/* small read files */
		else	size = (((int)f->extent + SF_GRAIN - 1)/SF_GRAIN)*SF_GRAIN;
	}

	sf_malloc = 0;
	if(size > 0 && !buf)
	{	/* try to allocate a buffer */
		if(obuf && size == osize)
		{	buf = (char*)obuf;
			obuf = NIL(uchar*);
			sf_malloc = oflags&SF_MALLOC;
		}
	
		if(!buf)
		{	/* do allocation */
			while(!(buf = (char*) malloc(size)) && size > 0)
				size /= 2;
			if(size > 0)
				sf_malloc = SF_MALLOC;
		}
	}

	if(size == 0 && !(f->flags&(SF_MMAP|SF_STRING)) && (f->mode&SF_READ))
	{	/* use the internal buffer */
		size = sizeof(f->tiny);
		buf = (char*)f->tiny;
	}

	/* set up new buffer */
	f->size = size;
	f->next = f->data = f->endr = f->endw = (uchar*)buf;
	f->endb = (f->mode&SF_READ) ? f->data : f->data+size;

	if(f->flags&SF_STRING)
	{	/* these fields are used to test actual size - see sfseek() */
		f->extent = 0L;
		f->here = sf_malloc ? 0L : size;
		if((f->flags&SF_READ) && !sf_malloc)
			f->endb = f->data+size;
	}

	f->flags = (f->flags & ~SF_MALLOC)|sf_malloc;

	if(obuf && osize > 0 && (oflags&SF_MALLOC))
	{	free((char*)obuf);
		obuf = NIL(uchar*);
	}

	SFSET(f,f->flags,1);
	return (char*)obuf;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfsetfd.c'
then
	echo shar: will not over-write existing file "'./sfsetfd.c'"
else
cat << \SHAR_EOF > './sfsetfd.c'
#include	"sfhdr.h"

/*	Change the file descriptor
**
**	Written by Kiem-Phong Vo (01/08/91)
*/

#ifdef __STD_C
static _sfdup(reg int fd, reg int newfd)
#else
static _sfdup(fd,newfd)
reg int	fd;
reg int	newfd;
#endif
{
	reg int	dupfd;

#ifdef F_DUPFD	/* the simple case */
	while((dupfd = fcntl(fd,F_DUPFD,newfd)) < 0 && errno == EINTR)
		errno = 0;
	return dupfd;

#else	/* do it the hard way */
	if((dupfd = dup(fd)) < 0 || dupfd >= newfd)
		return dupfd;

	/* dup() succeeded but didn't get the right number, recurse */
	newfd = _sfdup(fd,newfd);

	/* close the one that didn't match */
	CLOSE(dupfd);

	return newfd;
#endif
}

#ifdef __STD_C
sfsetfd(reg Sfile_t* f, reg int newfd)
#else
sfsetfd(f,newfd)
reg Sfile_t	*f;
reg int		newfd;
#endif
{
	reg int		oldfd;

	if(SFFROZEN(f) || (f->flags&SF_STRING) )
		return -1;

	oldfd = f->file;
	
	if(oldfd >= 0)
	{	if(newfd >= 0)
		{	if((newfd = _sfdup(oldfd,newfd)) < 0)
				return -1;
			CLOSE(oldfd);
		}
		else if(f->mode == SF_READ || f->mode == SF_WRITE)
		{	/* put stream in suspended state */
			(void)sfsync(f);
#ifdef MAP_TYPE
			if((f->flags&SF_MMAP) && f->data)
			{	(void)munmap((caddr_t)f->data,f->size);
				f->data = NIL(uchar*);
				f->size = 0;
			}
#endif
			f->endb = f->endr = f->endw = f->data;
			f->extent = f->here = 0;
			f->mode = (f->mode&SF_RDWR)|SF_INIT;
		}
	}

	/* notify changes */
	if(_Sfnotify)
		(*_Sfnotify)(f,SF_SETFD,newfd);

	f->file = newfd;

	return newfd;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfsize.c'
then
	echo shar: will not over-write existing file "'./sfsize.c'"
else
cat << \SHAR_EOF > './sfsize.c'
#include	"sfhdr.h"

/*	Get the size of a stream.
**
**	Written by Kiem-Phong Vo (02/12/91)
*/
#ifdef __STD_C
long sfsize(reg Sfile_t *f)
#else
long sfsize(f)
reg Sfile_t	*f;
#endif
{
	if(!SFSETMODE(f))
		return -1L;

	if(f->flags&SF_STRING)
	{	reg long	s;
		if((s = f->next - f->data) > f->here && s != f->extent)
			f->here = f->extent = s;
		return f->here;
	}

	if(f->extent >= 0 && (f->flags&(SF_SHARE|SF_APPEND)) )
	{	struct stat	st;
		if(fstat(f->file,&st) < 0)
			f->extent = -1L;
		else	f->extent = st.st_size;
	}

	if(f->extent < 0)
		return -1L;
	else if(f->mode&SF_READ)
		return f->extent;
	else if(f->flags&SF_APPEND)
		return f->extent + (f->next - f->data);
	else
	{	reg long extent = f->here + (f->next - f->data);
		return extent >= f->extent ? extent : f->extent;
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfsk.c'
then
	echo shar: will not over-write existing file "'./sfsk.c'"
else
cat << \SHAR_EOF > './sfsk.c'
#include	"sfhdr.h"

/*	Seek function that knows discipline
**
**	Written by Kiem-Phong Vo (02/12/91)
*/
#ifdef __STD_C
long sfsk(reg Sfile_t* f, reg long addr, reg int type, reg Sfdisc_t* disc)
#else
long sfsk(f,addr,type,disc)
reg Sfile_t	*f;
reg long	addr;
reg int		type;
reg Sfdisc_t	*disc;
#endif
{
	reg long	p;
	reg int		s, local, string;

	GETLOCAL(f,local);

	if(!(string = (f->flags&SF_STRING)))
	{	reg Sfdisc_t	*d;
		if((d = disc) != NIL(Sfdisc_t*) && !local)
			d = disc = disc->disc;
		while(d && !d->seekf)
			d = d->disc;
		if(d)
			disc = d;
	}

	for(;;)
	{
		if(string)
			s = (int)(type == 0 ? addr : (f->next-f->data)+addr);
		else
		{
			if(disc && disc->seekf)
				p = (*(disc->seekf))(f,addr,type,disc);
			else	p = lseek(f->file,addr,type);
			if(p >= 0)
				return p;
			s = -1;
		}

		if(local)
			SETLOCAL(f);
		switch(_sfexcept(f,SF_SEEK,s,disc))
		{
		case SF_EDISC:
		case SF_ECONT:
			if(string)
				return 0L;
			continue;
		default:
			return -1L;
		}
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfstack.c'
then
	echo shar: will not over-write existing file "'./sfstack.c'"
else
cat << \SHAR_EOF > './sfstack.c'
#include	"sfhdr.h"


/*	Push/pop streams
**
**	Written by Kiem-Phong Vo (07/10/90)
*/

#ifdef __STD_C
Sfile_t *sfstack(reg Sfile_t* f1, reg Sfile_t* f2)
#else
Sfile_t *sfstack(f1,f2)
reg Sfile_t	*f1, *f2;
#endif
{
	Sfile_t		tmp;
	reg Sfile_t	*rf;

	if(SFFROZEN(f1) || (f2 != SF_POPSTACK && SFFROZEN(f2)) || f1 == f2)
		return NIL(Sfile_t*);

	/* give access to other internal functions */
	_Sfstack = sfstack;

	if(f2 == SF_POPSTACK)
	{	if(!(f2 = f1->push))
			return NIL(Sfile_t*);
		f2->mode &= ~SF_PUSH;
	}
	else if(f1->pool && f1->pool != &_Sfpool && f1 == f1->pool->head)
	{	/* get something else to pool front since f1 will be locked */
		if(f1->pool == f2->pool)
			rf = f2;
		else for(rf = f1->fore; rf != NIL(Sfile_t*); rf = rf->fore)
			if(!SFFROZEN(rf))
				break;
		if(rf)
			(*_Sfpmove)(rf,0);
	}

	/* make sure f2 is accessible */
	if(f2->pool && f2->pool != &_Sfpool && f2 != f2->pool->head)
		(*_Sfpmove)(f2,0);

	SFLOCK(f1);
	SFLOCK(f2);

	/* break from respective pools */
	if(f1->back)
		f1->back->fore = f1->fore;
	else	f1->pool->head = f1->fore;
	if(f1->fore)
		f1->fore->back = f1->back;
	if(f2->back)
		f2->back->fore = f2->fore;
	else	f2->pool->head = f2->fore;
	if(f2->fore)
		f2->fore->back = f2->back;

	/* swap image */
	tmp = *f2; *f2 = *f1; *f1 = tmp;

	/* reinsert streams back into their "current" pool */
	if(f2->pool)
	{	if(f2->pool->head)
		{	if((f2->fore = f2->pool->head->fore) != NIL(Sfile_t*))
				f2->fore->back = f2;
			f2->pool->head->fore = f2;
			f2->back = f2->pool->head;
		}
		else
		{	f2->fore = f2->back = NIL(Sfile_t*);
			f2->pool->head = f2;
		}
	}
	if(f1->pool)
	{	f1->back = NIL(Sfile_t*);
		if((f1->fore = f1->pool->head) != NIL(Sfile_t*))
			f1->fore->back = f1;
		f1->pool->head = f1;
	}

	if(f2->push != f2)
	{	/* freeze the pushed stream */
		f2->mode |= SF_PUSH;
		f1->push = f2;
		rf = f1;
	}
	else
	{	/* unfreeze the just exposed stream */
		SFSET(f1,f1->flags,1);
		f2->push = NIL(Sfile_t*);
		rf = f2;
	}

	SFOPEN(f1);
	SFOPEN(f2);
	return rf;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfstrtod.c'
then
	echo shar: will not over-write existing file "'./sfstrtod.c'"
else
cat << \SHAR_EOF > './sfstrtod.c'
#include	"sfhdr.h"

/*	Convert a double value represented in an ASCII format into
**	the internal double representation.
**
**	Written by David Korn and Kiem-Phong Vo (06/27/90)
*/

#if _i386_cvt
int	_Sfstrtod;
#else

#define BATCH	(2*sizeof(int))	/* accumulate this many digits at a time */
#define IPART		0	/* doing integer part */
#define FPART		1	/* doing fractional part */
#define EPART		2	/* doing exponent part */

#ifdef __STD_C
static double pow10(reg int n)
#else
static double pow10(n)
reg int	n;
#endif
{
	reg int		m, pow;
	reg double	dval, d, *pow10;

	/* set up look up table */
	if((pow = n) < 0)
	{
		pow10 = _Sfneg10;
		pow = -pow;
	}
	else	pow10 = _Sfpos10;

	/* reduce to a low exponent */
	dval = 1.;
	if(pow >= MAXPOW10)
	{	d = pow10[MAXEXP10-1]*pow10[MAXEXP10-1];	
		for(m = pow/MAXPOW10; m > 0; --m)
			dval *= d;
		pow = pow%MAXPOW10;
	}

	/* fast loop for the rest */
	for(m = 1; m <= pow;)
	{
		if(m&pow)
			dval *= *pow10;
		pow10 += 1;
		if(n < 0)
			pow >>= 1;
		else	m <<= 1;
	}

	return dval;
}

#ifdef __STD_C
double strtod(reg const char* s, char** retp)
#else
double strtod(s,retp)
reg char	*s;	/* string to convert */
char		**retp;	/* to return the remainder of string */
#endif
{
	reg int	n, c, m;
	reg int	mode, fexp, sign, expsign;
	double	dval;

	/* skip initial blanks */
	while(isspace(*s))
		++s;

	/* get the sign */
	if((sign = (*s == '-')) || *s == '+')
		s += 1;

	mode = IPART;
	fexp = expsign = 0;
	dval = 0.;
	while(*s)
	{	/* accumulate a handful of the digits */
		for(m = BATCH, n = 0; m > 0; --m, ++s)
		{	/* get and process a char */
			c = *s;
			if(isdigit(c))
				n = 10*n + (c - '0');
			else	break;
		}

		/* number of digits accumulated */
		m = BATCH-m;

		if(mode == IPART)
		{	/* doing the integer part */
			if(dval == 0.)
				dval = (double)n;
			else	dval = dval*pow10(m) + (double)n;
		}
		else if(mode == FPART)
		{	/* doing the fractional part */
			fexp -= m;
			if(n > 0)
				dval += n*pow10(fexp);
		}
		else if(n)
		{	/* doing the exponent part */
			if(expsign)
				n = -n;
			dval *= pow10(n);
		}

		if(!c)
			break;

		if(m < BATCH)
		{	/* detected a non-digit */
			if(c == '.')
			{	/* start the fractional part or no match */
				if(mode != IPART)
					break;
				mode = FPART;
				s += 1;
			}
			else if(c == 'e' || c == 'E')
			{
				if(mode == EPART)
					break;
				mode = EPART;
				c = *++s;
				if((expsign = (c == '-')) || c == '+')
					s += 1;
			}
			else	break;
		}
	}

	if(retp)
		*retp = (char*)s;
	return sign ? -dval : dval;
}

#endif /* _i386_cvt */
SHAR_EOF
fi # end of overwriting check
if test -f './sfsync.c'
then
	echo shar: will not over-write existing file "'./sfsync.c'"
else
cat << \SHAR_EOF > './sfsync.c'
#include	"sfhdr.h"

/*	Synchronize data in buffers with the file system.
**	If f is nil, all streams are sync-ed
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
sfsync(reg Sfile_t* f)
#else
sfsync(f)
reg Sfile_t	*f;	/* stream to be synchronized */
#endif
{
	reg int		rv, flag;

	if(!f)
	{	/* use recursion to handle the sync-all case */
		reg Sfpool_t	*p;
		reg int		loop, allsync;

#define MAXLOOP	256
		rv = 0;
		for(loop = 0; loop < MAXLOOP; ++loop)
		{	allsync = 1;

			for(p = &_Sfpool; p != NIL(Sfpool_t*); p = p->fore)
			for(f = p->head; f != NIL(Sfile_t*); f = f->fore)
			{	/* can't sync these */	
				if(SFFROZEN(f) ||
				   ((f->mode&SF_READ) && f->next == f->endb) ||
				   ((f->mode&SF_WRITE) && f->next == f->data
				    && !(f->flags&SF_HOLE) ) )
					continue;

				/* not all was synced */
				allsync = 0;

				/* make sure pool list is not rearranged */
				flag = f->mode&SF_POOL;
				f->mode &= ~SF_POOL;
				if(sfsync(f) < 0)
					rv = -1;
				f->mode |= flag;
				SFOPEN(f);
			}

			/* this is necessary because disciplines may
			   unsync some streams while syncing others */
			if(allsync)
				break;
		}
		if(loop >= MAXLOOP && rv >= 0)
			rv = -1;
		return rv;
	}

	if(SFFROZEN(f))
		return -1;

	/* throw away ungetc data first */
	if(f->disc == _Sfudisc)
		(void)sfclose((*_Sfstack)(f,NIL(Sfile_t*)));

	/* if a stream is pooled, sync the head of the pool */
	if((f->mode&SF_POOL) && f != f->pool->head &&
	   (f->pool->head->mode&f->pool->mode))
		(void)sfsync(f->pool->head);

	/* the stream is already in sync */
	if((f->mode&SF_SYNC) || (f->flags&SF_STRING))
		return 0;

	rv = 0;
	for(; f; f = f->push)
	{	/* pretend that this stream is not on a stack */
		SFLOCK(f);
		flag = f->mode&SF_PUSH;
		f->mode &= ~SF_PUSH;

		if((f->mode&SF_WRITE) && (f->next > f->data || (f->flags&SF_HOLE)) )
		{	/* sync the buffer, make sure pool don't move */
			reg int pool = f->mode&SF_POOL;
			f->mode &= ~SF_POOL;
			SFopen(f);
			if(f->next > f->data && _sfflsbuf(f,-1) < 0)
				rv = -1;
			if(f->flags&SF_HOLE)
			{	/* realize a previously created hole of 0's */
				(void)lseek(f->file,-1L,1);
				(void)write(f->file,"",1);
				f->flags &= ~SF_HOLE;
			}
			SFLOCK(f);
			f->mode |= pool;
		}

		if((f->mode&SF_READ) && f->extent >= 0 &&
		   ((f->flags&SF_MMAP) || f->next < f->endb) )
		{	/* make sure the file pointer is at the right place */
			f->here -= f->endb-f->next;
			f->endr = f->endw = f->data;
			f->mode = SF_READ|SF_SYNC|SF_LOCK;

			if(SFSK(f,f->here,0,f->disc) == f->here &&
			   (f->flags&SF_SHARE) && !(f->flags&SF_MMAP))
			{	f->endb = f->data;	/* force rereading data */
				f->mode &= ~SF_SYNC;
			}
		}

		f->mode |= flag;
		SFOPEN(f);
	}

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sftable.c'
then
	echo shar: will not over-write existing file "'./sftable.c'"
else
cat << \SHAR_EOF > './sftable.c'
#include	"sfhdr.h"

Sftab_t	_Sftable =
{
	{ 1e1, 1e2, 1e4, 1e8, 1e16, 1e32 },
	{ 1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32 },
	{ '0','0', '0','1', '0','2', '0','3', '0','4',
	  '0','5', '0','6', '0','7', '0','8', '0','9',
	  '1','0', '1','1', '1','2', '1','3', '1','4',
	  '1','5', '1','6', '1','7', '1','8', '1','9',
	  '2','0', '2','1', '2','2', '2','3', '2','4',
	  '2','5', '2','6', '2','7', '2','8', '2','9',
	  '3','0', '3','1', '3','2', '3','3', '3','4',
	  '3','5', '3','6', '3','7', '3','8', '3','9',
	  '4','0', '4','1', '4','2', '4','3', '4','4',
	  '4','5', '4','6', '4','7', '4','8', '4','9',
	  '5','0', '5','1', '5','2', '5','3', '5','4',
	  '5','5', '5','6', '5','7', '5','8', '5','9',
	  '6','0', '6','1', '6','2', '6','3', '6','4',
	  '6','5', '6','6', '6','7', '6','8', '6','9',
	  '7','0', '7','1', '7','2', '7','3', '7','4',
	  '7','5', '7','6', '7','7', '7','8', '7','9',
	  '8','0', '8','1', '8','2', '8','3', '8','4',
	  '8','5', '8','6', '8','7', '8','8', '8','9',
	  '9','0', '9','1', '9','2', '9','3', '9','4',
	  '9','5', '9','6', '9','7', '9','8', '9','9',
	},
	{	64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 63, 64, 64,
		 0,  1,  2,  3,  4,  5,  6,  7,
		 8,  9, 64, 64, 64, 64, 64, 64,
		62, 10, 11, 12, 13, 14, 15, 16,
		17, 18, 19, 20, 21, 22, 23, 24,
		25, 26, 27, 28, 29, 30, 31, 32,
		33, 34, 35, 64, 64, 64, 64, 64,
		64, 10, 11, 12, 13, 14, 15, 16,
		17, 18, 19, 20, 21, 22, 23, 24,
		25, 26, 27, 28, 29, 30, 31, 32,
		33, 34, 35, 64, 64, 64, 64, 64
	},
	{	64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 64, 64, 64,
		64, 64, 64, 64, 64, 63, 64, 64,
		 0,  1,  2,  3,  4,  5,  6,  7,
		 8,  9, 64, 64, 64, 64, 64, 64,
		62, 36, 37, 38, 39, 40, 41, 42,
		43, 44, 45, 46, 47, 48, 49, 50,
		51, 52, 53, 54, 55, 56, 57, 58,
		59, 60, 61, 64, 64, 64, 64, 64,
		64, 10, 11, 12, 13, 14, 15, 16,
		17, 18, 19, 20, 21, 22, 23, 24,
		25, 26, 27, 28, 29, 30, 31, 32,
		33, 34, 35, 64, 64, 64, 64, 64
	},
	"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ@_",
};
SHAR_EOF
fi # end of overwriting check
if test -f './sftell.c'
then
	echo shar: will not over-write existing file "'./sftell.c'"
else
cat << \SHAR_EOF > './sftell.c'
#include	"sfhdr.h"

/*	Tell the current location in a given stream
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
long sftell(reg Sfile_t* f)
#else
long sftell(f)
reg Sfile_t	*f;
#endif
{	
	/* set the stream to the right mode */
	if(!SFSETMODE(f))
		return -1;

	/* throw away ungetc data */
	if(f->disc == _Sfudisc)
		(void)sfclose((*_Sfstack)(f,NIL(Sfile_t*)));

	if(f->flags&SF_STRING)
		return (long)(f->next-f->data);

	/* let sfseek() handle the hard case */
	if(f->flags&(SF_SHARE|SF_APPEND))
		return sfseek(f,0L,1);

	return f->here + ((f->mode&SF_WRITE) ? f->next-f->data : f->next-f->endb);
}
SHAR_EOF
fi # end of overwriting check
if test -f './sftmp.c'
then
	echo shar: will not over-write existing file "'./sftmp.c'"
else
cat << \SHAR_EOF > './sftmp.c'
#include	"sfhdr.h"
#ifndef F_SETFD
#ifndef FIOCLEX
#if _hdr_filio
#include	<filio.h>
#else
#if _sys_filio
#include	<sys/filio.h>
#endif
#endif
#endif
#endif


/*	Create a temporary stream for read/write.
**	The stream is originally created as a memory-resident stream.
**	When this memory is exceeded, a real temp file will be created.
**	The temp file creation sequence is somewhat convoluted so that
**	pool/stack/discipline will work correctly.
**
**	Written by David Korn and Kiem-Phong Vo (12/10/90)
*/

#if !_lib_unlink
#if _lib_remove
#define unlink	remove
#define _lib_unlink
#endif
#endif

#ifdef __cplusplus
extern "C" {
#endif
#if _lib_mktemp
extern char	*mktemp _ARG_((char*));
#else
extern int	unlink _ARG_((const char*));
extern int	access _ARG_((char*, int));
extern int	strcpy _ARG_((char*,char*));
#endif
#ifdef __cplusplus
}
#endif

#if !_lib_mktemp

static char	**Tmp;
static int	Ntmp, Mtmp;

/* remove all created temp files */
#ifdef __STD_C
static void rmtemp(void)
#else
static void rmtemp()
#endif
{
	reg int	i;
	for(i = 0; i < Ntmp; ++i)
		unlink(Tmp[i]);
}

/* make a new temp file name */
#ifdef __STD_C
static char *mktemp(char *name)
#else
static char *mktemp(name)
char	*name;
#endif
{	reg char	*p;

	if(Ntmp == 0)
		atexit(rmtemp);

	if(Ntmp >= Mtmp)
	{	/* space for new entry */
		if(Ntmp <= 0)
			Tmp = (char**)malloc(8*sizeof(char*));
		else	Tmp = (char**)realloc((char*)Tmp,(Mtmp+8)*sizeof(char*));
		if(!Tmp)
		{	Mtmp = Ntmp = 0;
			return NIL(char*);
		}
		else	Mtmp += 8;
	}

	while(access(name,0) == 0)
	{	p = name+7;	/* 7 == strlen("/tmp/sf") */
		while(*p == '9')
			*p++ = '0';
		if(*p == '\0')
			return NIL(char*);
		*p += 1;
	}

	if(!(Tmp[Ntmp] = (char*)malloc(strlen(name)+1)))
		return NIL(char*);
	strcpy(Tmp[Ntmp++],name);

	return name;
}
#endif

#ifdef __STD_C
static _tmpexcept(Sfile_t* f, int type, Sfdisc_t* disc)
#else
static _tmpexcept(f,type,disc)
Sfile_t		*f;
int		type;
Sfdisc_t	*disc;
#endif
{
	reg int		here, extent;
	reg uchar	*data;
	reg char	*file;
	reg int		flags;
	Sfile_t		newf;
#if !_lib_mktemp
	static char	Name[] = "/tmp/sf0000000000000";
#else
	static char	Name[] = "/tmp/sfXXXXXXXX";
#endif

	/* do nothing cases */
	if(type == SF_CLOSE || type == SF_READ)
		return 0;

	/* get the extent of current data */
	flags = f->flags;
	data = f->data;
	here = extent = f->next - data;
	if(extent < f->here)
		extent = (int)(f->here);

	/* try to create the temp file */
	SFCLEAR(&newf);
	file = mktemp(Name);
	if(!sfopen(&newf,file,"w+"))
		return -1;

#if _lib_mktemp
	/* remove the temp file */
	while(unlink(file) < 0 && errno == EINTR)
		errno = 0;
#endif

	/* set close-on-exec */
#ifdef F_SETFD
#ifndef FD_CLOEXEC
#define FD_CLOEXEC	1
#endif
	(void)fcntl(newf.file,F_SETFD,FD_CLOEXEC);
#else
#ifdef FIOCLEX
	(void)ioctl(newf.file,FIOCLEX,0);
#endif
#endif

	/* now remake the old stream into the new image */
	f->disc = disc = NIL(Sfdisc_t*);
	f->endb = f->endr = f->endw = f->next = f->data = NIL(uchar*);
	f->size = 0;
	if(!sfnew(f,NIL(char*),-1,newf.file,SF_READ|SF_WRITE))
	{	CLOSE(newf.file);
		return -1;
	}
	if(extent > 0)
	{	/* put back data */
		(void)sfwrite(f,(char*)data,extent);
		(void)sfseek(f,(long)here,0);
		if(flags&SF_MALLOC)
			free((char*)data);
	}

	return 1;
}

#ifdef __STD_C
Sfile_t* sftmp(reg int s)
#else
Sfile_t* sftmp(s)
reg int	s;
#endif
{
	reg Sfile_t	*f;
	static Sfdisc_t	Tmpdisc;

	/* make this a memory resident stream at first */
	if(!(f = sfnew(NIL(Sfile_t*),NIL(char*),s,-1,SF_STRING|SF_READ|SF_WRITE)))
		return NIL(Sfile_t*);

	if(s > 0)
	{	/* set up a discipline for out-of-bound, etc. */
		Tmpdisc.exceptf = _tmpexcept;
		(void)sfdisc(f,&Tmpdisc);
	}
	else if(s == 0) /* turn it into a real file */
	{	if(_tmpexcept(f,SF_WRITE,NIL(Sfdisc_t*)) < 0)
		{	sfclose(f);
			return NIL(Sfile_t*);
		}
	}

	return f;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfungetc.c'
then
	echo shar: will not over-write existing file "'./sfungetc.c'"
else
cat << \SHAR_EOF > './sfungetc.c'
#include	"sfhdr.h"

/*	Push back one byte to a given SF_READ stream
**
**	Written by Kiem-Phong Vo (03/02/91)
*/
#ifdef __STD_C
static _uexcept(reg Sfile_t* f, reg int type, reg Sfdisc_t* disc)
#else
static _uexcept(f,type,disc)
reg Sfile_t	*f;
reg int		type;
reg Sfdisc_t	*disc;
#endif
{	
	/* hmm! This should never happen */
	if(disc != _Sfudisc)
		return -1;

	/* close the unget stream */
	if(type != SF_CLOSE)
		(void)sfclose((*_Sfstack)(f,NIL(Sfile_t*)));

	return 1;
}

#ifdef __STD_C
sfungetc(reg Sfile_t* f, reg int c)
#else
sfungetc(f,c)
reg Sfile_t	*f;	/* push back one byte to this stream */
reg int		c;	/* the value to be pushed back */
#endif
{
	reg Sfile_t	*uf;

	if(c < 0 || (f->mode != SF_READ && _sfmode(f,SF_READ) < 0))
		return -1;
	SFLOCK(f);

	/* fast handling of the typical unget */
	if(f->next > f->data && f->next[-1] == (uchar)c)
	{	f->next -= 1;
		goto done;
	}

	/* make a string stream for unget characters */
	if(f->disc != _Sfudisc)
	{	if(!(uf = sfnew(NIL(Sfile_t*),NIL(char*),-1,-1,SF_STRING|SF_READ)))
		{	c = -1;
			goto done;
		}
		_Sfudisc->exceptf = _uexcept;
		uf->disc = _Sfudisc;
		SFOPEN(f); (void)sfstack(f,uf); SFLOCK(f);
	}

	/* space for data */
	if(f->next == f->data)
	{	reg uchar*	data;
		if(!(data = (uchar*)malloc(f->size+16)))
		{	c = -1;
			goto done;
		}
		f->flags |= SF_MALLOC;
		if(f->data)
			memcpy((char*)(data+16),(char*)f->data,f->size);
		f->size += 16;
		f->data  = data;
		f->next  = data+16;
		f->endb  = data+f->size;
	}

	*--f->next = (uchar)c;
done:
	SFOPEN(f);
	return c;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfvprintf.c'
then
	echo shar: will not over-write existing file "'./sfvprintf.c'"
else
cat << \SHAR_EOF > './sfvprintf.c'
#include	"sfhdr.h"

/*	The engine for formatting data
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef ulong_hibit
#define HIGHBIT		ulong_hibit
#else
#define HIGHBIT		(~(((ulong)~0L) >> 1))
#endif

#define F_LEFT		000001	/* left justification (-) */
#define F_SIGN		000002	/* must set a sign - or + */
#define F_BLANK		000004	/* if not - and +, then prepend a blank */
#define F_ZERO		000010	/* zero padding on the left side */
#define F_ALTER		000020	/* various alternative formats (#) */
#define F_PAD		000040	/* there will be some sort of padding */

#define F_REPEAT	000100	/* repeat the pattern until precision is exhausted */
#define F_MINUS		000200	/* has a minus sign */
#define F_PREFIX	(F_MINUS|F_SIGN|F_BLANK)

#define F_LONG		001000	/* object is long */
#define F_FLOAT		002000	/* %fFeEgG format */
#define F_GFORMAT	004000	/* a %gG format */

#define FPRECIS		6	/* default precision for floats */

#define GETARG(elt,arge,argf,args,type,fmt) \
	{ if(!argf) \
		elt = va_arg(args,type); \
	  else if((*argf)(fmt,(char*)(&arge)) < 0) \
		goto pop_fa; \
	  else	elt = arge; \
	}

#ifdef __STD_C
sfvprintf(Sfile_t* f, const char* form, va_list args)
#else
sfvprintf(f,form,args)
Sfile_t	*f;		/* file to print to */
char	*form;		/* format to use */
va_list	args;		/* arg list if argf == 0 */
#endif
{
	reg long	n, lval, base;
	reg char	*sp, *ssp, *d;
	reg long	v;
	reg int		flags;
	reg char	*ep, *endep, *endsp, *endd;
	reg int		precis, width, n_output, r;
	int		fmt, sign, decpt, dot;
	double		dval;
	long		al;
	int		ai, *ap;
	char		*as;
	Argf_p		argf;
	Extf_p		extf;
	va_list		alist;
	reg Fa_t	*fa, *fast;
	char		buf[MAXDIGITS];
	char		data[SF_GRAIN];

	/* fast io system */
#define SFflush(f,c)	(SFopen(f), r = _sfflsbuf(f,(int)c), SFLOCK(f), r)
#define SFwrite(f,s,n)	(SFopen(f), r = sfwrite(f,s,(int)n), SFLOCK(f), r)
#define SFnputc(f,c,n)	(SFopen(f), r = sfnputc(f,(int)c,(int)n), SFLOCK(f), r)
#define SFBUF(f)	(d = (char*)f->next, endd = (char*)f->endb)
#define SFINIT(f)	(SFBUF(f), n_output = 0)
#define SFEND(f)	((n_output += (uchar*)d - f->next), (f->next = (uchar*)d))
#define SFPUTC(f,c) \
	{ if(d >= endd) \
		{ SFEND(f); if(SFflush(f,c) <  0) break; n_output += 1; SFBUF(f); } \
	  else	{ *d++ = (char)c; } \
	}
#define SFNPUTC(f,c,n) \
	{ if((endd-d) < n) \
		{ SFEND(f); if(SFnputc(f,c,n) != n) break; n_output += (int)n; SFBUF(f); } \
	  else	{ while(n--) *d++ = (char)c; } \
	}
#define SFWRITE(f,s,n) \
	{ if((endd-d) < n) \
		{ SFEND(f); if(SFwrite(f,s,n) != n) break; n_output += (int)n; SFBUF(f); } \
	  else	MEMCPY(d,s,(int)n); \
	}

	/* make sure stream is in write mode and buffer is not NULL */
	if(f->mode != SF_WRITE && _sfmode(f,SF_WRITE) < 0)
		return -1;

	SFLOCK(f);

	if(f->data == NIL(uchar*))
	{	f->data = f->next = (uchar*)data;
		f->endw = f->endb = f->data+sizeof(data);
	}
	SFINIT(f);

	argf = NIL(Argf_p);
	extf = NIL(Extf_p);
	fast = NIL(Fa_t*);

loop_fa :
	while(n = *form++)
	{
		flags = 0;
		if(n != '%')
		{	/* collect the non-pattern chars */
			sp = ssp = (char*)(form-1);
			while((n = *++ssp) && n != '%')
				;
			form = endsp = ssp;
			goto do_output;
		}

		endep = ep = NIL(char*);
		endsp = sp = buf+(sizeof(buf)-1);
		width = precis = -1;
		dot = 0;
		base = 10;

	loop_flags:	/* LOOP FOR FLAGS, WIDTH AND PRECISION */
		switch(fmt = *form++)
		{
		case '-' :
			flags |= F_LEFT;
			goto loop_flags;
		case ' ' :
			flags |= F_BLANK;
			goto loop_flags;
		case '+' :
			flags |= F_SIGN;
			goto loop_flags;
		case '#' :
			flags |= F_ALTER;
			goto loop_flags;
		case '.' :	/* argument count */
			if((dot += 1) > 2)
			{	form -= 1;
				continue;
			}
			goto loop_flags;
		case '*' :	/* variable width, precision, or base */
			if((dot == 0 && width >= 0) || (dot == 1 && precis >= 0) )
			{	form -= 1;	/* bad pattern specification */
				continue;
			}
			GETARG(lval,ai,argf,args,int,'d');
			goto set_args;
		case '0' :	/* defining width or precision */
			if(dot == 0)
			{	flags |= F_ZERO;
				goto loop_flags;
			}
			/* fall thru */
		case '1' : case '2' : case '3' :
		case '4' : case '5' : case '6' :
		case '7' : case '8' : case '9' :
			lval = fmt - '0';
			for(n = *form; isdigit(n); n = *++form)
				lval = (lval<<3) + (lval<<1) + (n - '0');
		set_args:
			if(dot == 0)
			{	if((width = (int)lval) < 0)
				{	width = -width;
					flags |= F_LEFT;
				}
				flags |= F_PAD;
			}
			else if(dot == 1)
				precis = (int)lval;
			else	base = (int)lval;
			goto loop_flags;
		case 'l' :
			flags |= F_LONG;
		case 'h' : /* no short */
			goto loop_flags;

			/* PRINTF DIRECTIVES */

		case '&' : /* change extension function */
			if(!argf)
				extf = va_arg(args,Extf_p);
			else if((*argf)('&',(char*)(&extf)) < 0)
				goto pop_fa;
			continue;
		case '@' : /* change argument getting function */
			if(!argf)
				argf = va_arg(args,Argf_p);
			else if((*argf)('@',(char*)(&argf)) < 0)
				goto pop_fa;
			continue;
		case ':' : /* stack a pair of format/arglist */
			if(!FAMALLOC(fa))
				goto done;
			fa->form = (char*)form;
			GETARG(form,form,argf,args,char*,'1');
			if(!form)
				form = "";
			GETARG(alist,alist,argf,args,va_list,'2');
			fa->args = args;
			fa->argf.p = argf;
			fa->extf.p = extf;
			args = alist;
			fa->next = fast;
			fast = fa;
			continue;

		default :	/* unknown directive */
			if(extf)
			{	/* call the extension function */
				GETARG(sp,as,argf,args,char*,fmt);
				n = (*extf)(sp,fmt,precis,(char*)(&as));
				if((sp = as) != NIL(char*))
					goto s_format;
			}

			/* treat as text */
			form -= 1;
			continue;

		case 's':	/* a string */
			GETARG(sp,as,argf,args,char*,'s');
			n = -1;
			if(!sp)
			{	/* standard error string for null pointer */
				endsp = (sp = "(null)") + 6;
				flags = 0;
			}
			else
			{	/* set other bound */
			s_format:
				if(n < 0)
				{	for(ssp = sp; *ssp++;)
						;
					n = (ssp - sp) - 1;
				}
				if(precis >= 0 && precis < n)
					n = precis;
				endsp = sp+n;
			}
			flags &= ~(F_SIGN|F_BLANK|F_ALTER);
			precis = 0;
			break;

		case 'n':	/* return current output length */
			GETARG(ap,ap,argf,args,int*,'n');
			SFEND(f);
			*ap = n_output;
			continue;
		case 'c':	/* a character */
			GETARG(fmt,fmt,argf,args,int,'d');
		case '%':
			flags = (flags&~(F_SIGN|F_BLANK|F_ZERO))|F_REPEAT;
			if(precis <= 0)
				precis = 1;
			break;
		case 'p':	/* pointer value */
			GETARG(ssp,as,argf,args,char*,'p');
			lval = (long)ssp;
			flags = (flags&~(F_SIGN|F_BLANK|F_ZERO))|F_ALTER;
			dot = 0;
			fmt = 'x';
			goto unsigned_cvt;
		case 'o':
		case 'x':
		case 'X':
			dot = 0;
		case 'u':
			flags &= ~(F_SIGN|F_BLANK);
		case 'i':
		case 'd':
			if(flags&F_LONG)
				{ GETARG(lval,al,argf,args,long,'D'); }
			else	{ GETARG(lval,ai,argf,args,int,'d');  }

			if(lval == 0 && precis == 0)
				goto done_cvt;

			if(lval < 0 && (fmt == 'd' || fmt == 'i'))
			{	flags |= F_MINUS;
				if(lval == HIGHBIT)
				{	/* avoid overflow */
					if(base < 2 || base > RADIX)
						base = 10;
					lval = ((ulong)HIGHBIT)/base;
					*--sp = _Sfdigits[
						(ulong)HIGHBIT - ((ulong)lval)*base];
				}
				else	lval = -lval;
			}

		unsigned_cvt:
			ssp = _Sfdigits;
			switch(fmt)
			{
			case 'o' :
				base = 8;
				n = 3;
				goto power_cvt;
			case 'X' :
				ssp = "0123456789ABCDEF";
			case 'x' :
				base = 16;
				n = 4;
				goto power_cvt;
			default :
				if(base < 2 || base > RADIX)
					base = 10;
				break;
			}

			if(base == 10)
			{	/* special fast conversion for base 10 */
				sfucvt(lval,sp,n,ssp);
			}
			else if((base & (base-1)) == 0)
			{	/* calculate shift amount for power-of-2 base */
				if(base < 8)
					n = base <  4 ? 1 : 2;
				else if(base < 32)
					n = base < 16 ? 3 : 4;
				else	n = base < 64 ? 5 : 6;
			power_cvt:
				do
				{	*--sp = ssp[lval&(base-1)];
				} while(lval = ((ulong)lval) >> n);
			}
			else
			{	do
				{	*--sp = ssp[((ulong)lval)%base];
				} while(lval = ((ulong)lval)/((ulong)base));
			}

			/* zero padding for precision */
			for(precis -= (endsp-sp); precis > 0; --precis)
				*--sp = '0';

			if(flags&F_ALTER)
			{	/* prefix */
				if(fmt == 'o')
				{	if(*sp != '0')
						*--sp = '0';
				}
				else
				{	if(width > 0 && (flags&F_ZERO))
					{	/* do 0 padding first */
						if(dot == 2)
							n = base < 10 ? 2 : 3;
						else if(fmt == 'x' || fmt == 'X')
							n = 2;
						else	n = width;
						n += (flags&(F_MINUS|F_SIGN)) ? 1 : 0;
						n = width - (n + (endsp-sp));
						while(n-- > 0)
							*--sp = '0';
					}
					if(dot == 2)
					{	/* base#value notation */
						*--sp = '#';
						if(base < 10)
							*--sp = (char)('0'+base);
						else
						{	*--sp = _Sfdec[(base <<= 1)+1];
							*--sp = _Sfdec[base];
						}
					}
					else if(fmt == 'x' || fmt == 'X')
					{	*--sp = '0';
						*--sp = (char)fmt;
					}
				}
			}

		done_cvt:
			break;
		case 'g':	/* ultimately becomes %e or %f */
		case 'G':
			GETARG(dval,dval,argf,args,double,'F');
			precis = precis < 0 ? FPRECIS : precis == 0 ? 1 : precis;
			ep = sfecvt(dval,min(precis,FDIGITS),&decpt,&sign);
			if(dval == 0.)
				decpt = 1;
			else if(*ep == 'I')
				goto infinite;

			if(!(flags&F_ALTER))
			{	/* zap trailing 0s */
				if((n = sfslen()) > precis)
					n = precis;
				while((n -= 1) >= 1 && ep[n] == '0')
					;
				n += 1;
			}
			else	n = precis;

			flags = (flags & ~F_ZERO) | F_GFORMAT;
			if(decpt < -3 || decpt > precis)
			{
				precis = (int)(n-1);
				goto e_format;
			}
			else
			{
				precis = (int)(n - decpt);
				goto f_format;
			}
		case 'e':	/* double value in scientific notation */
		case 'E':
			/* convert data into ascii */
			GETARG(dval,dval,argf,args,double,'F');
			n = (precis = precis < 0 ? FPRECIS : precis)+1;
			ep = sfecvt(dval,(int)min(n,FDIGITS),&decpt,&sign);

		e_format: /* now build the x.yyyy string */
			if(isalpha(*ep))
				goto infinite;
			sp = endsp = buf+1;	/* reserve space for sign */
			*endsp++ = *ep ? *ep++ : '0';

			if(precis > 0 || (flags&F_ALTER))
				*endsp++ = '.';
			ssp = endsp;
			endep = ep+precis;
			while((*endsp++ = *ep++) && ep <= endep)
				;
			precis -= (endsp -= 1) - ssp;

			/* build the exponent */
			ep = endep = buf+(sizeof(buf)-1);
			if(dval != 0.)
			{
				if((n = decpt - 1) < 0)
					n = -n;
				while(n > 9)
				{	lval = n; n /= 10;	
					*--ep = (char)('0' + (lval - n*10));
				}
			}
			else	n = 0;
			*--ep = (char)('0' + n);
			if(endep-ep <= 1)	/* at least 2 digits */
				*--ep = '0';

			/* the e/Exponent separator and sign */
			*--ep = (decpt > 0 || dval == 0.) ? '+' : '-';
			*--ep = isupper(fmt) ? 'E' : 'e';

			flags = (flags&~F_ZERO)|F_FLOAT;
			goto end_efg;
		case 'f':	/* float or double in xxx.yyy notation */
		case 'F':
			GETARG(dval,dval,argf,args,double,'F');
			precis = precis < 0 ? FPRECIS : precis;
			ep = sffcvt(dval,min(precis,FDIGITS),&decpt,&sign);

		f_format: /* data before the '.' */
			if(isalpha(*ep))
			{
			infinite:
				endsp = (sp = ep)+sfslen();
				ep = endep;
				precis = 0;
				goto end_efg;
			}

			endsp = sp = buf+1;	/* save a space for sign */
			endep = ep+decpt;
			while(ep < endep && (*endsp++ = *ep++))
				;
			if(endsp == sp)
				*endsp++ = '0';

			if(precis > 0 || (flags&F_ALTER))
				*endsp++ = '.';

			if((n = -decpt) > 0)
			{	/* output zeros for negative exponent */
				ssp = endsp + min(n,precis);
				precis -= (int)n;
				while(endsp < ssp)
					*endsp++ = '0';
			}

			ssp = endsp;
			endep = ep+precis;
			while((*endsp++ = *ep++) && ep <= endep)
				;
			precis -= (endsp -= 1) - ssp;
			ep = endep;
			flags |= F_FLOAT;
		end_efg:
			if(sign)
			{	/* if a %gG, output the sign now */
				if(flags&F_GFORMAT)
				{	*--sp = '-';
					flags &= ~(F_SIGN|F_BLANK);
				}
				else	flags |= F_MINUS;
			}
			break;
		}

		if(!flags)
			goto do_output;
		else if(flags&(F_MINUS|F_SIGN|F_BLANK))
			fmt = (flags&F_MINUS) ? '-' : (flags&F_SIGN) ? '+' : ' ';

		n = (endsp-sp) + (endep-ep) + (precis <= 0 ? 0 : precis) +
		    ((flags&F_PREFIX) ? 1 : 0);
		if((lval = width-n) > 0)
		{	/* check for padding */
			if(!(flags&F_ZERO))
			{	/* right padding */
				if(flags&F_LEFT)
					lval = -lval;
				else if(flags&F_PREFIX)
				{	/* blank padding, output prefix now */
					*--sp = fmt;
					flags &= ~F_PREFIX;
				}
			}
		}
		else	lval = 0;

		if(flags&F_PREFIX)
		{	/* output prefix */
			SFPUTC(f,fmt);
			if(fmt != ' ')
				flags |= F_ZERO;
		}

		if((n = lval) > 0)
		{	/* left padding */
			v = (flags&F_ZERO) ? '0' : ' ';
			SFNPUTC(f,v,n);
		}

		if((n = precis) > 0 && ((flags&F_REPEAT) || !(flags&F_FLOAT)))
		{	/* repeated chars or padding for integer precision */
			v = (flags&F_REPEAT) ? fmt : '0';
			SFNPUTC(f,v,n);
			precis = 0;
		}

	do_output:
		if((n = endsp-sp) > 0)
			SFWRITE(f,sp,n);

		if(flags&(F_FLOAT|F_LEFT))
		{	/* F_FLOAT: right padding for float precision */
			if((n = precis) > 0)
				SFNPUTC(f,'0',n);

			/* F_FLOAT: the exponent of %eE */
			if((n = endep-(sp=ep)) > 0)
				SFWRITE(f,sp,n);

			/* F_LEFT: right padding */
			if((n = -lval) > 0)
				SFNPUTC(f,' ',n);
		}
	}

pop_fa:	if(fa = fast)
	{	/* pop the format stack and continue */
		form = fa->form;
		args = fa->args;
		argf = fa->argf.p;
		extf = fa->extf.p;
		fast = fa->next;
		FAFREE(fa);
		goto loop_fa;
	}

done:
	SFEND(f);

	r = f->next - f->data;
	if((d = (char*)f->data) == data)
		f->data = NIL(uchar*);
	f->next = f->data;
	f->endw = f->endb = f->data+f->size;

	SFOPEN(f);
	if((f->extent < 0 && (f->flags&SF_SHARE)) ||
	   (r > 0 && (d == data || ((f->flags&SF_LINE) && !(f->flags&SF_STRING)))) )
		(void) sfwrite(f,d,r);
	else	f->next += r;

	return n_output;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/stdgets.c'
then
	echo shar: will not over-write existing file "'./Stdio_s/stdgets.c'"
else
cat << \SHAR_EOF > './Stdio_s/stdgets.c'
#include	"sfhdr.h"
#include	"stdio.h"

/*	Read a line into a buffer.
**
**	Written by Kiem-Phong Vo (10/15/91)
*/

#ifdef __STD_C
char *_stdgets(reg Sfile_t *f, char* us, reg int n, int isgets)
#else
char *_stdgets(f,us,n,isgets)
reg Sfile_t	*f;	/* stream to read from */
char		*us;	/* space to read into */
reg int		n;	/* max number of bytes to read */
int		isgets;	/* gets(), not fgets() */
#endif
{
	reg int		p;
	reg uchar	*is, *ps;

	if(n <= 0 || !us || (f->mode != SF_READ && _sfmode(f,SF_READ) < 0))
		return NIL(char*);
	SFLOCK(f);

	n -= 1;
	is = (uchar*)us;
	while(n)
	{	/* peek the read buffer for data */
		if((p = f->endb - (ps = f->next)) <= 0)
		{	if(f->extent < 0 && f->unmap != SF_NOPEEK)
			{	if((p = _sfpkrd(f,'\n',1)) >= 0)
				{	if((p = f->endb - (ps = f->next)) > n)
						p = n;
					MEMCPY(is,ps,p);
					goto next;
				}
				else	p = f->endb - (ps = f->next);
			}

			if(p <= 0 && SFRPEEK(f,ps,p) <= 0)
				break;
		}

		if(p > n)
			p = n;

#if _vax_asm	/* p is r9, is is r8, ps is r7 */
		{ reg int	q;
		q = p;					/* save data length */
		asm( "locc	$0xa,r9,(r7)" );	/* look for \n */
		asm( "subl2	r0,r9" );		/* copy length */
		if(++p > q)				/* if \n found, copy it too */
			--p;
		0;					/* avoid if branching bug */
		asm( "movc3	r9,(r7),(r8)" );	/* copy data */
		ps += p;
		is += p;
		}
#else
#if _lib_memccpy
		if((ps = (uchar*)memccpy((char*)is,(char*)ps,'\n',p)) != NIL(uchar*))
			p = ps-is;
		is += p;
		ps  = f->next+p;
#else
		if(!(f->flags&(SF_BOTH|SF_MALLOC)))
		{	while(p-- && (*is++ = *ps++) != '\n')
				;
			p = ps-f->next;
		}
		else
		{	reg int	c = ps[p-1];
			if(c != '\n')
				ps[p-1] = '\n';
			while((*is++ = *ps++) != '\n')
				;
			if(c != '\n')
			{	f->next[p-1] = c;
				if((ps-f->next) >= p)
					is[-1] = c;
			}
		}
#endif
#endif

	next:	/* gobble up read data and continue */
		f->next = ps;
		if(is[-1] == '\n')
			break;
		else if(n > 0)
			n -= p;
	}

	if((_Sfi = is - ((uchar*)us)) <= 0)
		us = NIL(char*);
	else if(isgets && is[-1] == '\n')
	{	is[-1] = '\0';
		_Sfi -= 1;
	}
	else	*is = '\0';

	SFOPEN(f);
	return us;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/stdio.h'
then
	echo shar: will not over-write existing file "'./Stdio_s/stdio.h'"
else
cat << \SHAR_EOF > './Stdio_s/stdio.h'
#ifndef _SFSTDIO_H	/* protect against multiple #includes */
#define _SFSTDIO_H

#if __cplusplus || c_plusplus

#if __STDPP__
#include	<...>
#else
#include	<../CC/stdio.h>
#endif

#else

/* stdio emulation */
#include	<sfio.h>

#define _IOFBF		0
#define _IONBF		1
#define _IOLBF		2
#define L_ctermid	9
#define L_cuserid	9
#define P_tmpdir	"/usr/tmp/"
#define L_tmpnam	(sizeof(P_tmpdir)+15)

#if __cplusplus
extern "C"
{
#endif

extern char	*ctermid _ARG_((char*));
extern char	*cuserid _ARG_((char*));
extern char	*tmpnam _ARG_((char*));
extern char	*tempnam _ARG_((char*));
extern Sfile_t	*_stdopen _ARG_((int, const char*));
extern char	*_stdgets _ARG_((Sfile_t*, char*, int n, int isgets));
extern int	_stdprintf _ARG_((const char*, ...));
extern int	_stdsprintf _ARG_((char*, const char*, ...));
extern int	_stdscanf _ARG_((const char*, ...));
extern int	_stdsetvbuf _ARG_((Sfile_t*, char*, int, int));

#if __cplusplus
}
#endif

#define stdin		sfstdin
#define stdout		sfstdout
#define stderr		sfstderr
#define FILE		Sfile_t
#define BUFSIZ		SF_BUFSIZE

#define fopen(f,m)	sfopen((Sfile_t*)0,f,m)
#define freopen(f,m,p)	sfopen(p,f,m)
#define popen(cmd,m)	sfpopen((Sfile_t*)0,cmd,m)
#define fdopen		_stdopen
#define tmpfile()	sftmp(SF_BUFSIZE)
#define fclose(f)	sfclose(f)
#define pclose(f)	sfclose(f)

#define fwrite(p,s,n,f)	((_Sfi = sfwrite(f,p,(s)*(n))) <= 0 ? _Sfi : _Sfi/(s))
#define fputc(c,f)	sfputc(f,c)
#define putc(c,f)	sfputc(f,c)
#define putw(w,f)	(_Sfi = (int)w, sfwrite(f,&_Sfi,sizeof(int)) <= 0 ? 1 : 0)
#define putchar(c)	sfputc(sfstdout,c)
#define fputs(s,f)	sfputr(f,s,-1)
#define puts(s)		sfputr(sfstdout,s,'\n')
#define fprintf		sfprintf
#define vfprintf	sfvprintf
#define vprintf(f,a)	sfvprintf(sfstdout,f,a)
#define _doprnt(fm,a,f)	sfvprintf(f,fm,a)
#define vsprintf	_stdvsprintf
#define	printf		_stdprintf
#define sprintf		_stdsprintf

#define fread(p,s,n,f)	((_Sfi = sfread(f,p,(s)*(n))) <= 0 ? _Sfi : _Sfi/(s))
#define fgetc(f)	sfgetc(f)
#define getc(f)		sfgetc(f)
#define getw(f)		(sfread(f,&_Sfi,sizeof(int)) == sizeof(int) ? _Sfi : -1)
#define getchar()	sfgetc(sfstdin)
#define ungetc(c,f)	sfungetc(f,c)
#define fgets(s,n,f)	_stdgets(f,s,n,0)
#define _SFSIZEOF(s)	(sizeof(s) != sizeof(char*) ? sizeof(s) : BUFSIZ)
#define gets(s)		_stdgets(sfstdin,s,_SFSIZEOF(s),1)
#define fscanf		sfscanf
#define vfscanf		sfvscanf
#define _doscan		sfvscanf
#define sscanf		sfsscanf
#define vscanf(f,a)	sfvscanf(sfstdin,f,a)
#define scanf		_stdscanf
#define vsscanf		_stdvssanf

#define fflush(f)	sfsync(f)
#define fseek(f,o,t)	(sfseek(f,o,t) < 0L ? -1 : 0)
#define rewind(f)	sfseek((f),0L,0)
#define ftell(f)	sftell(f)
#define setbuf(f,b)	sfsetbuf(f,b,(b) ? BUFSIZ : 0)
#define setbuffer(f,b,n) sfsetbuf(f,b,n)
#define setlinebuf(f)	sfset(f,SF_LINE,1)
#define setvbuf		_stdsetvbuf

#define fileno(f)	sffileno(f)
#define feof(f)		sfeof(f)
#define ferror(f)	sferror(f)
#define clearerr(f)	(sfclrerr(f),sfclrlock(f))

#endif /* __cplusplus */
#endif /* _SFSTDIO_H */
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/stdopen.c'
then
	echo shar: will not over-write existing file "'./Stdio_s/stdopen.c'"
else
cat << \SHAR_EOF > './Stdio_s/stdopen.c'
#include	"sfhdr.h"

/*	Open a stream given a file descriptor.
**
**	Written by Kiem-Phong Vo (06/27/90).
*/

#ifdef __STD_C
Sfile_t *_stdopen(reg int fd, reg const char* mode)
#else
Sfile_t *_stdopen(fd,mode)
reg int		fd;
reg char	*mode;
#endif
{
	int		sflags;

	if(fd < 0 || (sflags = _sftype(mode,NIL(int*))) == 0)
		return NIL(Sfile_t*);
	else	return sfnew(NIL(Sfile_t*),NIL(char*),-1,fd,sflags);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/stdprintf.c'
then
	echo shar: will not over-write existing file "'./Stdio_s/stdprintf.c'"
else
cat << \SHAR_EOF > './Stdio_s/stdprintf.c'
#include	"sfhdr.h"
#include	"stdio.h"


/*	printf function
**
**	Written by Kiem-Phong Vo (12/10/90)
*/

#ifdef __STD_C
_stdprintf(const char *form, ...)
#else
_stdprintf(va_alist)
va_dcl
#endif
{
	va_list		args;
	reg int		rv;

#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*form;
	va_start(args);
	form = va_arg(args,char*);
#endif

	rv = sfvprintf(sfstdout,form,args);

	va_end(args);
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/stdscanf.c'
then
	echo shar: will not over-write existing file "'./Stdio_s/stdscanf.c'"
else
cat << \SHAR_EOF > './Stdio_s/stdscanf.c'
#include	"sfhdr.h"
#include	"stdio.h"

/*	Read formatted data from a stream
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
_stdscanf(const char *form, ...)
#else
_stdscanf(va_alist)
va_dcl
#endif
{
	va_list		args;
	reg int		rv;

#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*form;	/* scanning format */
	va_start(args);
	form = va_arg(args,char*);
#endif

	rv = sfvscanf(sfstdin,form,args);
	va_end(args);
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/stdsprnt.c'
then
	echo shar: will not over-write existing file "'./Stdio_s/stdsprnt.c'"
else
cat << \SHAR_EOF > './Stdio_s/stdsprnt.c'
#include	"sfhdr.h"
#include	"stdio.h"


/*	sprintf function
**
**	Written by Kiem-Phong Vo (12/10/90)
*/


#ifdef __STD_C
_stdsprintf(char *s, const char *form, ...)
#else
_stdsprintf(va_alist)
va_dcl
#endif
{
	va_list	args;
	Sfile_t	f;
	reg int	rv;

#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*s;
	reg char	*form;
	va_start(args);
	s = va_arg(args,char*);
	form = va_arg(args,char*);
#endif

	if(!s)
		return -1;

	/* make a fake stream */
	SFCLEAR(&f);
	f.flags = SF_STRING|SF_WRITE;
	f.mode = SF_WRITE;
	f.size = 4*SF_BUFSIZE;
	f.data = f.next = f.endr = (uchar*)s;
	f.endb = f.endw = f.data+f.size;

	rv = sfvprintf(&f,form,args);
	*f.next = '\0';
	_Sfi = f.next - f.data;

	va_end(args);

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/stdvbuf.c'
then
	echo shar: will not over-write existing file "'./Stdio_s/stdvbuf.c'"
else
cat << \SHAR_EOF > './Stdio_s/stdvbuf.c'
#include	"sfhdr.h"
#include	"stdio.h"

/*	Stdio function setvbuf()
**
**	Written by Kiem-Phong Vo (12/10/90)
*/

#ifdef __STD_C
_stdsetvbuf(Sfile_t* f, char *buf, int type, int size)
#else
_stdsetvbuf(f,buf,type,size)
Sfile_t	*f;
char	*buf;
int	type;
int	size;
#endif
{
	if(type == _IOLBF)
		sfset(f,SF_LINE,1);
	else if((f->flags&SF_STRING))
		return -1;
	else if(type == _IONBF)
	{	sfsync(f);
		sfsetbuf(f,NIL(char*),0);
	}
	else if(type == _IOFBF)
	{	if(size == 0)
			size = SF_BUFSIZE;
		sfsync(f);
		sfsetbuf(f,buf,size);
	}

	return 0;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/stdvsprnt.c'
then
	echo shar: will not over-write existing file "'./Stdio_s/stdvsprnt.c'"
else
cat << \SHAR_EOF > './Stdio_s/stdvsprnt.c'
#include	"sfhdr.h"

#ifdef __STD_C
_stdvsprintf(char *s, const char *form, va_list args)
#else
_stdvsprintf(s,form,args)
register char	*s;
register char	*form;
va_list		args;
#endif
{
	Sfile_t	f;
	reg int	rv;

	if(!s)
		return -1;

	/* make a fake stream */
	SFCLEAR(&f);
	f.flags = SF_STRING|SF_WRITE;
	f.mode = SF_WRITE;
	f.size = 4*SF_BUFSIZE;
	f.data = f.next = f.endr = (uchar*)s;
	f.endb = f.endw = f.data+f.size;

	rv = sfvprintf(&f,form,args);
	*f.next = '\0';
	_Sfi = f.next - f.data;

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/stdvsscn.c'
then
	echo shar: will not over-write existing file "'./Stdio_s/stdvsscn.c'"
else
cat << \SHAR_EOF > './Stdio_s/stdvsscn.c'
#include	"sfhdr.h"

#ifdef __STD_C
_stdvsscanf(char *s, const char *form, va_list args)
#else
_stdvsscanf(s,form,args)
register char	*s;
register char	*form;
va_list		args;
#endif
{
	Sfile_t	f;
	reg int	rv;

	if(!s)
		return -1;

	/* make a fake stream */
	SFCLEAR(&f);
	f.flags = SF_STRING|SF_READ;
	f.mode = SF_READ;
	f.size = strlen((char*)s);
	f.data = f.next = f.endr = (uchar*)s;
	f.endb = f.endw = f.data+f.size;

	return sfvscanf(&f,form,args);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_s/Makefile'
then
	echo shar: will not over-write existing file "'./Stdio_s/Makefile'"
else
cat << \SHAR_EOF > './Stdio_s/Makefile'
# makefile for stdio-source compatibility code
#
# Written by Kiem-Phong Vo (4/24/91)

# Compiler and flags to use
AR=	ar
CC=	cc
CXFLAGS=
CCFLAGS= -c -O -I.. $(CXFLAGS)

SRCS=	stdgets.c stdprintf.c stdscanf.c stdvbuf.c stdsprnt.c \
	stdvsprnt.c stdvsscn.c stdopen.c
OBJS=	stdgets.o stdprintf.o stdscanf.o stdvbuf.o stdsprnt.o \
	stdvsprnt.o stdvsscn.o stdopen.o

.c.o:
	$(CC) $(CCFLAGS) $*.c

must:	$(OBJS)
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/clearerr.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/clearerr.c'"
else
cat << \SHAR_EOF > './Stdio_b/clearerr.c'
#include	"sfstdio.h"

#ifdef __STD_C
clearerr(FILE* fp)
#else
clearerr(fp)
FILE	*fp;
#endif
{
	reg Sfile_t	*sp;
	
	if(!(sp = _sfstream(fp)))
		return -1;
	return _stdclrerr(fp,sp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/doprnt.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/doprnt.c'"
else
cat << \SHAR_EOF > './Stdio_b/doprnt.c'
#include	"sfstdio.h"

#ifdef __STD_C
_doprnt(const char* form, va_list args, FILE* fp)
#else
_doprnt(form,args,fp)
char    *form;          /* format to use */
va_list args;           /* arg list if argf == 0 */
FILE	*fp;
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if((rv = sfvprintf(sp,form,args)) < 0)
		_stderr(fp);
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/doscan.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/doscan.c'"
else
cat << \SHAR_EOF > './Stdio_b/doscan.c'
#include	"sfstdio.h"

#ifdef __STD_C
_doscan(FILE *fp, const char* form, va_list args)
#else
_doscan(fp,form,args)
FILE	*fp;
char    *form;          /* format to use */
va_list args;           /* arg list if argf == 0 */
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if((rv = sfvscanf(sp,form,args)) <= 0)
	{	if(sfeof(sp))
			_stdeof(fp);
		if(sferror(sp))
			_stderr(fp);
	}
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fclose.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fclose.c'"
else
cat << \SHAR_EOF > './Stdio_b/fclose.c'
#include	"sfstdio.h"

#ifdef __STD_C
int fclose(reg FILE *fp)
#else
int fclose(fp)
reg FILE	*fp;
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if((rv = sfclose(sp)) >= 0)
		_stdclose(fp);
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fdopen.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fdopen.c'"
else
cat << \SHAR_EOF > './Stdio_b/fdopen.c'
#include	"sfstdio.h"

#ifdef __STD_C
FILE* fdopen(int fd,const char *mode)
#else
FILE* fdopen(fd,mode)
int	fd;
char	*mode;
#endif
{
	reg Sfile_t	*sp;
	reg FILE	*fp;
	reg int		flags = 0;

	while(1) switch(*mode++)
	{
	case 0:
		goto e_mode;
	case 'r':
		flags = SF_READ;
		break;
	case 'w':
		flags = SF_WRITE;
		break;
	case 'a':
		flags = SF_WRITE|SF_APPEND;
		break;
	case '+':
		flags = SF_READ|SF_WRITE;
		break;
	case 'b':
		break;
	default:
		return NIL(FILE*);
	}
e_mode :
	if(!flags)
		return NIL(FILE*);

	if(!(sp = sfnew(NIL(Sfile_t*), NIL(char*), SF_UNBOUND, fd, flags)))
		return NIL(FILE*);
	if(!(fp = _stdstream(sp)))
	{	sfclose(sp);
		return NIL(FILE*);
	}

	return(fp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/feof.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/feof.c'"
else
cat << \SHAR_EOF > './Stdio_b/feof.c'
#include	"sfstdio.h"

#ifdef __STD_C
feof(FILE* fp)
#else
feof(fp)
FILE	*fp;
#endif
{
	reg Sfile_t	*sp;
	
	if(!(sp = _sfstream(fp)))
		return -1;
	return sfeof(sp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/ferror.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/ferror.c'"
else
cat << \SHAR_EOF > './Stdio_b/ferror.c'
#include	"sfstdio.h"

#ifdef __STD_C
ferror(FILE* fp)
#else
ferror(fp)
FILE	*fp;
#endif
{
	reg Sfile_t	*sp;
	
	if(!(sp = _sfstream(fp)))
		return -1;
	return sferror(sp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fflush.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fflush.c'"
else
cat << \SHAR_EOF > './Stdio_b/fflush.c'
#include	"sfstdio.h"

#ifdef __STD_C
int fflush(reg FILE *fp)
#else
int fflush(fp)
reg FILE *fp;
#endif
{
	reg Sfile_t	*sp;

	if(!fp)
		return sfsync(NIL(Sfile_t*));
	if(!(sp = _sfstream(fp)))
		return -1;
	_stdclrerr(fp,sp);
	return sfsync(sp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fgetc.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fgetc.c'"
else
cat << \SHAR_EOF > './Stdio_b/fgetc.c'
#include	"sfstdio.h"

#ifdef __STD_C
int fgetc(reg FILE *fp)
#else
int fgetc(fp)
reg FILE *fp;
#endif
{
	reg Sfile_t	*sp;
	reg int		rv;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if((rv = sfgetc(sp)) < 0)
	{	if(sfeof(sp))
			_stdeof(fp);
		if(sferror(sp))
			_stderr(fp);
	}
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fgets.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fgets.c'"
else
cat << \SHAR_EOF > './Stdio_b/fgets.c'
#include	"sfstdio.h"

#ifdef __STD_C
char *fgets(char *buf,int n,FILE *fp)
#else
char *fgets(buf,n,fp)
char	*buf;
int	n;
reg FILE *fp;
#endif
{
	reg Sfile_t	*sp;
	reg char	*rv;

	if(!(sp = _sfstream(fp)))
		return NIL(char*);

	_stdclrerr(fp,sp);
	if(!(rv = _stdgets(sp,buf,n,0)))
	{	if(sfeof(sp))
			_stdeof(fp);
		if(sferror(sp))
			_stderr(fp);
	}

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/filbuf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/filbuf.c'"
else
cat << \SHAR_EOF > './Stdio_b/filbuf.c'
#include	"sfstdio.h"


#ifdef __STD_C
int _filbuf(FILE *fp)
#else
int _filbuf(fp)
reg FILE	*fp;
#endif
{
	reg Sfile_t	*sp;
	reg int		rv;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if((rv = sfgetc(sp)) < 0)
	{
		if(sfeof(sp))
			_stdeof(fp);
		if(sferror(sp))
			_stderr(fp);
	}
#if _FILE_cnt
	else
	{	/* let user have fast access to the data */
		fp->std_ptr = sp->next;
		fp->std_cnt = sp->endb-sp->next;

		/* protection against mixing sfio/stdio */
		_Sfstdio = _sfstdio;
		sp->mode |= SF_STDIO;
		sp->endr = sp->endw = sp->data;
	}
#endif

	return(rv);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fileno.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fileno.c'"
else
cat << \SHAR_EOF > './Stdio_b/fileno.c'
#include	"sfstdio.h"

#ifdef __STD_C
fileno(FILE* fp)
#else
fileno(fp)
FILE	*fp;
#endif
{
	reg Sfile_t	*sp;
	
	if(!(sp = _sfstream(fp)))
		return -1;
	return sffileno(sp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/flsbuf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/flsbuf.c'"
else
cat << \SHAR_EOF > './Stdio_b/flsbuf.c'
#include	"sfstdio.h"

#ifdef __STD_C
int _flsbuf(int c, FILE *fp)
#else
int _flsbuf(c, fp)
reg int		c;
reg FILE	*fp;
#endif
{
	reg Sfile_t *sp;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if(sfputc(sp,c) < 0)
	{	_stderr(fp);
		return -1;
	}

	/* fast access to buffer for putc benefit */
#if _FILE_cnt
	if(!(sp->flags&SF_LINE))
	{	fp->std_ptr = sp->next;
		fp->std_cnt = sp->endb - sp->next;

		/* internal protection against mixing of sfio/stdio */
		_Sfstdio = _sfstdio;
		sp->mode |= SF_STDIO;
		sp->endr = sp->endw = sp->data;
	}
#endif

	return c;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fopen.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fopen.c'"
else
cat << \SHAR_EOF > './Stdio_b/fopen.c'
#include	"sfstdio.h"

#ifdef __STD_C
FILE* fopen(char *name,const char *mode)
#else
FILE* fopen(name,mode)
char	*name;
char	*mode;
#endif
{
	reg FILE	*fp;
	reg Sfile_t	*sp;

	sp = sfopen((Sfile_t*)0, name, mode);
	if(!(fp = _stdstream(sp)))
	{	sfclose(sp);
		return NIL(FILE*);
	}

	return(fp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fprintf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fprintf.c'"
else
cat << \SHAR_EOF > './Stdio_b/fprintf.c'
#include	"sfstdio.h"

#ifdef __STD_C
fprintf(FILE *fp, const char *form, ...)
#else
fprintf(va_alist)
va_dcl
#endif
{
	va_list		args;
	reg int		rv;
	reg Sfile_t	*sp;

#ifdef __STD_C
	va_start(args,form);
#else
	reg FILE	*fp;	/* file stream to print to */
	reg char	*form;	/* print format */
	va_start(args);
	fp = va_arg(args,FILE*);
	form = va_arg(args,char*);
#endif

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	rv = sfvprintf(sp,form,args);

	va_end(args);

	if(rv < 0)
		_stderr(fp);

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fputc.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fputc.c'"
else
cat << \SHAR_EOF > './Stdio_b/fputc.c'
#include	"sfstdio.h"


#ifdef __STD_C
int fputc(int c, FILE *fp)
#else
int fputc(c, fp)
reg int		c;
reg FILE	*fp;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if(sfputc(sp,c) < 0)
	{	_stderr(fp);
		return -1;
	}
	else	return c;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fputs.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fputs.c'"
else
cat << \SHAR_EOF > './Stdio_b/fputs.c'
#include	"sfstdio.h"


#ifdef __STD_C
int fputs(const char *s, FILE *fp)
#else
int fputs(s, fp)
reg char	*s;
reg FILE	*fp;
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if((rv = sfputr(sp,s,-1)) < 0)
		_stderr(fp);
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fread.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fread.c'"
else
cat << \SHAR_EOF > './Stdio_b/fread.c'
#include	"sfstdio.h"


#ifdef __STD_C
int fread(void *buf, int size, int nmem, reg FILE *fp)
#else
int fread(buf,size,nmem,fp)
reg char	*buf;
reg int		size;
reg int		nmem;
reg FILE	*fp;
#endif
{
	reg Sfile_t	*sp;
	reg int		rv;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if((rv = sfread(sp,(char*)buf,size*nmem)) <= 0)
	{	if(sfeof(sp))
			_stdeof(fp);
		if(sferror(sp))
			_stderr(fp);
	}
	else	rv /= size;

	return(rv);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/freopen.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/freopen.c'"
else
cat << \SHAR_EOF > './Stdio_b/freopen.c'
#include	"sfstdio.h"


#ifdef __STD_C
FILE* freopen(char *name,const char *mode, reg FILE *fp)
#else
FILE* freopen(name,mode,fp)
reg char	*name;
reg char	*mode;
reg FILE	*fp;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return NIL(FILE*);

	_stdclrerr(fp,sp);
	if(!sfopen(sp, name, mode))
		return NIL(FILE*);

#if _FILE_cnt
	fp->std_cnt = 0;
#endif
#if _FILE_flag
	fp->std_flag = 0;
#endif
#if _FILE_file
	fp->std_file = sffileno(sp);
#endif

	return fp;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fscanf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fscanf.c'"
else
cat << \SHAR_EOF > './Stdio_b/fscanf.c'
#include	"sfstdio.h"

#ifdef __STD_C
fscanf(FILE *fp, const char *form, ...)
#else
fscanf(va_alist)
va_dcl
#endif
{
	va_list		args;
	reg int		rv;
	reg Sfile_t	*sp;

#ifdef __STD_C
	va_start(args,form);
#else
	reg FILE	*fp;	/* file to be scanned */
	reg char	*form;	/* scanning format */
	va_start(args);
	fp = va_arg(args,FILE*);
	form = va_arg(args,char*);
#endif

	if(!(sp = _sfstream(fp)))
		return -1;
	_stdclrerr(fp,sp);

	rv = sfvscanf(sp,form,args);

	va_end(args);

	if(rv <= 0)
	{
		if(sfeof(sp))
			_stdeof(fp);
		if(sferror(sp))
			_stderr(fp);
	}

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fseek.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fseek.c'"
else
cat << \SHAR_EOF > './Stdio_b/fseek.c'
#include	"sfstdio.h"


#ifdef __STD_C
long fseek(reg FILE *fp, long offset, int whence)
#else
long fseek(fp,offset,whence)
reg FILE	*fp;
reg long	offset;
reg int		whence;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1L;
	_stdclrerr(fp,sp);
	return sfseek(sp,offset,whence) < 0L ? -1 : 0;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/ftell.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/ftell.c'"
else
cat << \SHAR_EOF > './Stdio_b/ftell.c'
#include	"sfstdio.h"

#ifdef __STD_C
long ftell(reg FILE *fp)
#else
long ftell(fp)
reg FILE	*fp;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1L;
	_stdclrerr(fp,sp);
	return sftell(sp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fwrite.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fwrite.c'"
else
cat << \SHAR_EOF > './Stdio_b/fwrite.c'
#include	"sfstdio.h"


#ifdef __STD_C
int fwrite(const void *buf, int size, int nmem, reg FILE *fp)
#else
int fwrite(buf,size,nmem,fp)
reg char	*buf;
reg int		size;
reg int		nmem;
reg FILE	*fp;
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);

	if((rv = sfwrite(sp,(char*)buf,size*nmem)) < 0)
		_stderr(fp);
	else	rv /= size;

	return(rv);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/getchar.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/getchar.c'"
else
cat << \SHAR_EOF > './Stdio_b/getchar.c'
#include	"sfstdio.h"


#ifdef __STD_C
int getchar(void)
#else
int getchar()
#endif
{
	return fgetc(stdin);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/gets.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/gets.c'"
else
cat << \SHAR_EOF > './Stdio_b/gets.c'
#include	"sfstdio.h"

#ifdef __STD_C
char *gets(char *buf)
#else
char *gets(buf)
char	*buf;
#endif
{
	reg Sfile_t	*sp;
	reg char	*rv;

	if(!(sp = _sfstream(stdin)))
		return NIL(char*);

	_stdclrerr(stdin,sp);
	if(!(rv = _stdgets(sp,buf,BUFSIZ,1)))
	{	if(sfeof(sp))
			_stdeof(stdin);
		if(sferror(sp))
			_stderr(stdin);
	}
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/getw.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/getw.c'"
else
cat << \SHAR_EOF > './Stdio_b/getw.c'
#include	"sfstdio.h"


#ifdef __STD_C
int getw(FILE *fp)
#else
int getw(fp)
reg FILE	*fp;
#endif
{
	reg Sfile_t	*sp;
	int		w;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);
	if(sfread(sp,(char*)(&w),sizeof(int)) != sizeof(int))
	{
		if(sfeof(sp))
			_stdeof(fp);
		if(sferror(sp))
			_stderr(fp);
		return -1;
	}

	return w;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/pclose.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/pclose.c'"
else
cat << \SHAR_EOF > './Stdio_b/pclose.c'
#include	"sfstdio.h"


#ifdef __STD_C
int pclose(reg FILE *fp)
#else
int pclose(fp)
reg FILE	*fp;
#endif
{
	return fclose(fp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/popen.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/popen.c'"
else
cat << \SHAR_EOF > './Stdio_b/popen.c'
#include	"sfstdio.h"


#ifdef __STD_C
FILE* popen(const char *string,const char *mode)
#else
FILE* popen(string,mode)
char	*string;
char	*mode;
#endif
{
	reg Sfile_t	*sp;
	reg FILE	*fp;

	if(!(sp = sfpopen(NIL(Sfile_t*), string, mode)))
		return NIL(FILE*);
	if(!(fp = _stdstream(sp)))
	{	sfclose(sp);
		return NIL(FILE*);
	}

	return(fp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/printf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/printf.c'"
else
cat << \SHAR_EOF > './Stdio_b/printf.c'
#include	"sfstdio.h"

#ifdef __STD_C
printf( const char *form, ...)
#else
printf(va_alist)
va_dcl
#endif
{
	va_list		args;
	reg int		rv;
	reg Sfile_t	*sp;
#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*form;	/* print format */
	va_start(args);
	form = va_arg(args,char*);
#endif

	if(!(sp = _sfstream(stdout)))
		return -1;
	_stdclrerr(stdout,sp);

	rv = sfvprintf(sp,form,args);

	va_end(args);

	if(rv < 0)
		_stderr(stdout);
	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/putchar.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/putchar.c'"
else
cat << \SHAR_EOF > './Stdio_b/putchar.c'
#include	"sfstdio.h"

#ifdef __STD_C
int putchar(int c)
#else
int putchar(c)
reg int	c;
#endif
{
	return fputc(c,stdout);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/puts.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/puts.c'"
else
cat << \SHAR_EOF > './Stdio_b/puts.c'
#include	"sfstdio.h"


#ifdef __STD_C
int puts(const char *str)
#else
int puts(str)
reg char	*str;
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(stdout)))
		return -1;
	_stdclrerr(stdout,sp);
	if((rv = sfputr(sfstdout,str,'\n')) < 0)
		_stderr(stdout);
	return rv;	
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/putw.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/putw.c'"
else
cat << \SHAR_EOF > './Stdio_b/putw.c'
#include	"sfstdio.h"


#ifdef __STD_C
int putw( int c, FILE *fp)
#else
int putw(c, fp)
int	c;
reg FILE *fp;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;
	_stdclrerr(fp,sp);
	if(sfwrite(sp,(char*)(&c),sizeof(int)) <= 0)
	{	_stderr(fp);
		return(1);
	}
	return(0);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/rewind.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/rewind.c'"
else
cat << \SHAR_EOF > './Stdio_b/rewind.c'
#include	"sfstdio.h"


#ifdef __STD_C
void rewind(reg FILE *fp)
#else
void rewind(fp)
reg FILE *fp;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return;
	_stdclrerr(fp,sp);
	(void)sfseek(sp,0L,0);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/scanf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/scanf.c'"
else
cat << \SHAR_EOF > './Stdio_b/scanf.c'
#include	"sfstdio.h"

#ifdef __STD_C
scanf(const char *form, ...)
#else
scanf(va_alist)
va_dcl
#endif
{
	va_list		args;
	reg int		rv;
	reg Sfile_t	*sp;
#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*form;	/* scanning format */
	va_start(args);
	form = va_arg(args,char*);
#endif

	if(!(sp = _sfstream(stdin)))
		return -1;
	_stdclrerr(stdin,sp);

	rv = sfvscanf(sp,form,args);
	va_end(args);

	if(rv <= 0)
	{	if(sfeof(sp))
			_stdeof(stdin);
		if(sferror(sp))
			_stderr(stdin);
	}

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/setbuf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/setbuf.c'"
else
cat << \SHAR_EOF > './Stdio_b/setbuf.c'
#include	"sfstdio.h"


#ifdef __STD_C
void setbuf(reg FILE *fp, char* buf)
#else
void setbuf(fp,buf)
reg FILE *fp;
char	*buf;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return;
	_stdclrerr(fp,sp);
	(void)sfsetbuf(sp,buf,BUFSIZ);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/setbuffer.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/setbuffer.c'"
else
cat << \SHAR_EOF > './Stdio_b/setbuffer.c'
#include	"sfstdio.h"


#ifdef __STD_C
int setbuffer(reg FILE *fp, char* buf, int size)
#else
int setbuffer(fp,buf, size)
reg FILE *fp;
char	*buf;
int	size;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;
	_stdclrerr(fp,sp);
	sfsetbuf(sp,buf, buf ? 0 : size);
	return 0;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/setlinebuf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/setlinebuf.c'"
else
cat << \SHAR_EOF > './Stdio_b/setlinebuf.c'
#include	"sfstdio.h"


#ifdef __STD_C
int setlinebuf(reg FILE *fp)
#else
int setlinebuf(fp)
reg FILE *fp;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;
	_stdclrerr(fp,sp);
	sfset(sp,SF_LINE,1);
	return(0);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/setvbuf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/setvbuf.c'"
else
cat << \SHAR_EOF > './Stdio_b/setvbuf.c'
#include	"sfstdio.h"


#ifdef __STD_C
int setvbuf(reg FILE *fp, char* buf, int flags, int size)
#else
int setvbuf(fp,buf, flags, size)
reg FILE *fp;
char	*buf;
int	flags;
int	size;
#endif
{
	reg Sfile_t *sp;

	if(!(sp = _sfstream(fp)))
		return -1;
	_stdclrerr(fp,sp);

	if(flags == _IOLBF)
		sfset(sp,SF_LINE,1);
	else if(flags == _IONBF)
	{	sfsync(sp);
		sfsetbuf(sp,NIL(char*),0);
	}
	else if(flags == _IOFBF)
	{	if(size == 0)
			size = BUFSIZ;
		sfsync(sp);
		sfsetbuf(sp,buf,size);
	}
	return(0);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/sfstdio.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/sfstdio.c'"
else
cat << \SHAR_EOF > './Stdio_b/sfstdio.c'
/*	Program to generate the header file sfstdio.h used
**	by the stdio-binary-compatible functions in Stdio_b.
**
**	Written by Kiem-Phong Vo.
*/
#ifdef __STD_C
#undef __STD_C
#endif
#ifdef __STDC__
#define	__STD_C		1
#else
#ifdef __cplusplus
#define __STD_C		1
#endif
#endif

#ifdef __STD_C
#ifdef __cplusplus
extern "C" {
#endif
extern int	printf(char*,...);
extern char*	strcmp(char*,char*);
#ifdef __cplusplus
}
#endif
#endif

/* standard names for fields of interest */
#include	"sfstdhdr.h"
#include	"FEATURE/stdio"
#include	"FEATURE/pragma"
#include	"FEATURE/stdarg"

#if _FILE_cnt
#define	std_cnt		_cnt
#endif
#if _FILE_ptr
#define std_ptr		_ptr
#endif
#if _FILE_flag
#define std_flag	_flag
#endif
#if _FILE_file
#define std_file	_file
#endif
#if _FILE_base
#define std_sf		_base
#endif
#if _FILE_bufsiz
#define std_next	_bufsiz
#endif

/* required fields in the FILE structure */
#define STD_CNT		1
#define STD_PTR		2
#define STD_FLAG	3
#define STD_FILE	4
#define STD_SF		5
#define STD_NEXT	6

typedef struct _fld_
{	int		offset;
	int		size;
	int		type;
	struct _fld_	*next;
} Field_t;

static FILE	_F_;
#define uchar			unsigned char
#define Offsetof(member)	(((uchar*)(&(_F_.member))) - ((uchar*)(&_F_)))
#define Sizeof(member)		sizeof(_F_.member)
#define NIL(type)		((type)0)

#ifdef __STD_C
Field_t *fldinsert(Field_t* head, Field_t* f)
#else
Field_t *fldinsert(head,f)
Field_t	*head, *f;
#endif
{	Field_t *fp, *last;

	if(!head)
		return f;

	for(last = NIL(Field_t*), fp = head; fp; last = fp, fp = fp->next)
		if(f->offset < fp->offset)
			break;
	if(last)
	{	f->next = last->next;
		last->next = f;
	}
	else
	{	f->next = head;
		head = f;
	}

	return head;
}

#ifdef __STD_C
fldprint(int size, char* name, char* type)
#else
fldprint(size,name,type)
int	size;
char	*name;
char	*type;
#endif
{
	if(type)
		printf("\t%s\t%s;\n",type,name);
	else if(size == sizeof(char))
		printf("\tchar\t%s;\n",name);
	else if(size == sizeof(short))
		printf("\tshort\t%s;\n",name);
	else if(size == sizeof(int))
		printf("\tint\t%s;\n",name);
	else	printf("\tlong\t%s;\n",name);
	return 0;
}

main()
{
	Field_t	ffile, fcnt, fptr, fflag, fsf, fnext, *list, *f;
	int	pos, gap, s, n_init;
	char	*inits[16];

	/* construct the order list of the fields of interest */
	list = NIL(Field_t*);

#ifdef std_cnt
	fcnt.offset = Offsetof(std_cnt);
	fcnt.size = Sizeof(std_cnt);
	fcnt.type = STD_CNT;
	fcnt.next = NIL(Field_t*);
	list = fldinsert(list,&fcnt);
#endif

#ifdef std_ptr
	fptr.offset = Offsetof(std_ptr);
	fptr.size = Sizeof(std_ptr);
	fptr.type = STD_PTR;
	fptr.next = NIL(Field_t*);
	list = fldinsert(list,&fptr);
#endif

#ifdef std_flag
	fflag.offset = Offsetof(std_flag);
	fflag.size = Sizeof(std_flag);
	fflag.type = STD_FLAG;
	fflag.next = NIL(Field_t*);
	list = fldinsert(list,&fflag);
#endif

#ifdef std_file
	ffile.offset = Offsetof(std_file);
	ffile.size = Sizeof(std_file);
	ffile.type = STD_FILE;
	ffile.next = NIL(Field_t*);
	list = fldinsert(list,&ffile);
#endif

#ifdef std_sf
	fsf.offset = Offsetof(std_sf);
	fsf.size = Sizeof(std_sf);
	fsf.type = STD_SF;
	fsf.next = NIL(Field_t*);
	list = fldinsert(list,&fsf);
#endif

#ifdef std_next
	fnext.offset = Offsetof(std_next);
	fnext.size = Sizeof(std_next);
	fnext.type = STD_NEXT;
	fnext.next = NIL(Field_t*);
	list = fldinsert(list,&fnext);
#endif

	/* output standard protection */
	printf("#ifndef	_STDIO_INIT\n\n");

	printf("#include\t\"FEATURE/stdio\"\n\n");

	printf("extern int\t_Stdio_load;\n\n");

	if(strcmp(NAME_iob,"_iob") != 0)
		printf("#define _iob	%s\n\n",NAME_iob);
	if(strcmp(NAME_filbuf,"_filbuf") != 0)
		printf("#define _filbuf	%s\n\n",NAME_filbuf);
	if(strcmp(NAME_flsbuf,"_flsbuf") != 0)
		printf("#define _flsbuf	%s\n\n",NAME_flsbuf);

#if _pragma_weak
	printf("#ifdef __STD_C\n");
	printf("#pragma weak _doprnt =	__doprnt\n");
	printf("#pragma weak _doscan =	__doscan\n");
	printf("#pragma weak %s =	_%s\n",NAME_filbuf,NAME_filbuf);
	printf("#pragma weak %s =	_%s\n",NAME_flsbuf,NAME_flsbuf);
	printf("#pragma weak %s =	_%s\n",NAME_iob,NAME_iob);

	printf("#define _doprnt	__doprnt\n");
	printf("#define _doscan	__doscan\n");
	printf("#define %s	_%s\n",NAME_filbuf,NAME_filbuf);
	printf("#define %s	_%s\n",NAME_flsbuf,NAME_flsbuf);
	printf("#define %s	_%s\n",NAME_iob,NAME_iob);
	printf("#endif\n\n");
#endif

#ifdef _NFILE
	printf("#define _NFILE	%d\n\n", _NFILE);
#else
	printf("#define _NFILE	%d\n\n", 3);
#endif

	/* the current stdarg.h file of CC may cause stdio.h to be included.
	   This horrible unportable piece of code prevents it from happening.
	   The right solution is to shoot a few people from the C++ group,
	   then fix stdarg.h and whatever C++ code dependent on this
	   abominable feature.
	*/
#if _CC_stdarg
	printf("#define pyr\t1\n");
	printf("#define BSD\t1\n");
#endif

	/* now get what we need from sfio */
	printf("#include\t\"sfhdr.h\"\n\n");

	/* output FILE structure */
	printf("typedef struct _std_\tFILE;\n");
	printf("struct _std_\n{\n");
	n_init = gap = pos = 0;
	for(f = list; f; f = f->next)
	{	if((s = f->offset - pos) > 0)
		{	/* fill the gap */
			printf("\tuchar\tstd_gap%d[%d];\n",gap++,s);
			inits[n_init++] = "\"\"";
		}

		switch(f->type)
		{
		case STD_CNT :
			fldprint(f->size,"std_cnt",NIL(char*));
			inits[n_init++] = "0";
			break;
		case STD_PTR :
			fldprint(f->size,"std_ptr","uchar*");
			inits[n_init++] = "(uchar*)0";
			break;
		case STD_FLAG :
			fldprint(f->size,"std_flag",NIL(char*));
			inits[n_init++] = "0";
			break;
		case STD_FILE :
			fldprint(f->size,"std_file",NIL(char*));
			inits[n_init++] = "(n)";
			break;
		case STD_SF :
			fldprint(f->size,"std_sf","uchar*");
			inits[n_init++] = "(uchar*)0";
			break;
		case STD_NEXT :
			fldprint(f->size,"std_next","uchar*");
			inits[n_init++] = "(uchar*)0";
			break;
		}

		pos = f->size+f->offset;
	}

	if((s = sizeof(FILE) - pos) > 0)
	{	/* fill the gap */
		printf("\tuchar\tstd_gap%d[%d];\n",gap++,s);
		inits[n_init++] = "\"\"";
	}

#ifndef std_sf
#define std_endif
	printf("#ifndef _STDEXTERN\n");
	printf("\tuchar*\tstd_sf;\n");
#endif
#ifndef std_next
#ifndef std_endif
#define std_endif
	printf("#ifndef _STDEXTERN\n");
#endif
	printf("\tuchar*\tstd_next;\n");
#endif
#ifdef std_endif
	printf("#endif\n");
#endif

	printf("};\n\n");

	/* output the init macro */
	printf("#define _STDIO_INIT(n)\t{");
	for(s = 0; s < n_init-1; ++s)
		printf("%s, ",inits[s]);
	printf("%s}\n\n",inits[s]);

	printf("#define BUFSIZ\t\t%d\n",BUFSIZ);
	printf("#define _IOERR\t\t%d\n",_IOERR);
	printf("#define _IOEOF\t\t%d\n",_IOEOF);
	printf("#define _IONBF\t\t%d\n",_IONBF);
	printf("#define _IOLBF\t\t%d\n",_IOLBF);
	printf("#define _IOFBF\t\t%d\n\n",_IOFBF);

#if _FILE_flag
	printf("#define _stdeof(fp)\t((fp)->std_flag |= _IOEOF)\n");
	printf("#define _stderr(fp)\t((fp)->std_flag |= _IOERR)\n");
	printf("#define _stdclrerr(fp,sp)\t(((fp)->std_flag &= ~(_IOEOF|_IOERR)),");
	printf("sfclrerr(sp),sfclrlock(sp))\n\n");
#else
	printf("#define _stdeof(fp)\n");
	printf("#define _stderr(fp)\n");
	printf("#define _stdclrerr(fp,sp)\t(sfclrerr(sp),sfclrlock(sp))\n\n");
#endif

	printf("#define stdfile(n)\t((FILE*)(((char*)(&_iob[0]))+(n)*%d))\n",
		sizeof(FILE));
	printf("#define stdin\t\tstdfile(0)\n");
	printf("#define stdout\t\tstdfile(1)\n");
	printf("#define stderr\t\tstdfile(2)\n\n");

	printf("#if __cplusplus\n");
	printf("extern \"C\" {\n");
	printf("#endif\n");

	printf("extern FILE\t\t_iob[];\n");

	printf("extern Sfile_t*\t\t_sfstream _ARG_((FILE*));\n");
	printf("extern FILE*\t\t_stdstream _ARG_((Sfile_t*));\n");
	printf("extern int\t\t_sfstdio _ARG_((Sfile_t*));\n");
	printf("extern char*\t\t_stdgets _ARG_((Sfile_t*,char*,int,int));\n");
	printf("extern void\t\t_stdclose _ARG_((FILE*));\n");
	printf("extern int\t\tclearerr _ARG_((FILE*));\n");
	printf("extern int\t\t_doprnt _ARG_((const char*, va_list, FILE*));\n");
	printf("extern int\t\t_doscan _ARG_((FILE*, const char*, va_list));\n");
	printf("extern int\t\tfclose _ARG_((FILE*));\n");
	printf("extern FILE*\t\tfdopen _ARG_((int, const char*));\n");
	printf("extern int\t\tfeof _ARG_((FILE*));\n");
	printf("extern int\t\tferror _ARG_((FILE*));\n");
	printf("extern int\t\tfflush _ARG_((FILE*));\n");
	printf("extern int\t\tfgetc _ARG_((FILE*));\n");
	printf("extern char*\t\tfgets _ARG_((char*, int, FILE*));\n");
	printf("extern int\t\t_filbuf _ARG_((FILE*));\n");
	printf("extern int\t\tfileno _ARG_((FILE*));\n");
	printf("extern int\t\t_flsbuf _ARG_((int, FILE*));\n");
	printf("extern FILE*\t\tfopen _ARG_((char*, const char*));\n");
	printf("extern int\t\tfprintf _ARG_((FILE*, const char* , ...));\n");
	printf("extern int\t\tfputc _ARG_((int, FILE*));\n");
	printf("extern int\t\tfputs _ARG_((const char*, FILE*));\n");
	printf("extern int\t\tfread _ARG_((void*, int, int, FILE*));\n");
	printf("extern FILE*\t\tfreopen _ARG_((char*, const char*, FILE*));\n");
	printf("extern int\t\tfscanf _ARG_((FILE*, const char* , ...));\n");
	printf("extern long\t\tfseek _ARG_((FILE*, long , int));\n");
	printf("extern long\t\tftell _ARG_((FILE*));\n");
	printf("extern int\t\tfpurge _ARG_((FILE*));\n");
	printf("extern int\t\tfwrite _ARG_((const void*, int, int, FILE*));\n");
	printf("extern int\t\tgetc _ARG_((FILE*));\n");
	printf("extern int\t\tgetchar _ARG_((void));\n");
	printf("extern char*\t\tgets _ARG_((char*));\n");
	printf("extern int\t\tgetw _ARG_((FILE*));\n");
	printf("extern int\t\tpclose _ARG_((FILE*));\n");
	printf("extern FILE*\t\tpopen _ARG_((const char*, const char*));\n");
	printf("extern int\t\tprintf _ARG_((const char* , ...));\n");
	printf("extern int\t\tputc _ARG_((int, FILE*));\n");
	printf("extern int\t\tputchar _ARG_((int));\n");
	printf("extern int\t\tputs _ARG_((const char*));\n");
	printf("extern int\t\tputw _ARG_((int, FILE*));\n");
	printf("extern void\t\trewind _ARG_((FILE*));\n");
	printf("extern int\t\tscanf _ARG_((const char* , ...));\n");
	printf("extern void\t\tsetbuf _ARG_((FILE*, char*));\n");
	printf("extern int\t\tsetbuffer _ARG_((FILE*, char*, int));\n");
	printf("extern int\t\tsetlinebuf _ARG_((FILE*));\n");
	printf("extern int\t\tsetvbuf _ARG_((FILE*,char*,int,int));\n");
	printf("extern int\t\tsprintf _ARG_((char*, const char* , ...));\n");
	printf("extern int\t\tsscanf _ARG_((const char*, const char* , ...));\n");
	printf("extern FILE*\t\ttmpfile _ARG_((void));\n");
	printf("extern int\t\tungetc _ARG_((int,FILE*));\n");
	printf("extern int\t\tvfprintf _ARG_((FILE*, const char* , va_list));\n");
	printf("extern int\t\tvfscanf _ARG_((FILE*, const char* , va_list));\n");
	printf("extern int\t\tvprintf _ARG_((const char* , va_list));\n");
	printf("extern int\t\tvscanf _ARG_((const char* , va_list));\n");
	printf("extern int\t\tvsprintf _ARG_((char*, const char* , va_list));\n");
	printf("extern int\t\tvsscanf _ARG_((char*, const char* , va_list));\n");
	printf("#if __cplusplus\n");
	printf("}\n");
	printf("#endif\n\n");

	printf("#endif /* _STDIO_INIT */\n");

	return 0;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/sprintf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/sprintf.c'"
else
cat << \SHAR_EOF > './Stdio_b/sprintf.c'
#include	"sfstdio.h"

#ifdef __STD_C
sprintf(char *s,  const char *form, ...)
#else
sprintf(va_alist)
va_dcl
#endif
{
	va_list		args;
	Sfile_t		f;
	reg int		rv;
#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*s;
	reg char	*form;
	va_start(args);
	s = va_arg(args,char*);
	form = va_arg(args,char*);
#endif

	if(!s)
		return -1;

	/* make a fake stream */
	SFCLEAR(&f);
	f.flags = SF_STRING|SF_WRITE;
	f.mode = SF_WRITE;
	f.size = 4*SF_BUFSIZE;
	f.data = f.next = f.endr = (uchar*)s;
	f.endb = f.endw = f.data+f.size;

	rv = sfvprintf(&f,form,args);
	*f.next = '\0';
	_Sfi = f.next - f.data;

	va_end(args);

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/sscanf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/sscanf.c'"
else
cat << \SHAR_EOF > './Stdio_b/sscanf.c'
#include	"sfstdio.h"

#ifdef __STD_C
sscanf(const char *s, const char *form,...)
#else
sscanf(va_alist)
va_dcl
#endif
{
	va_list		args;
	Sfile_t		f;
	reg int		rv;
#ifdef __STD_C
	va_start(args,form);
#else
	reg char	*s;
	reg char	*form;
	va_start(args);
	s = va_arg(args,char*);
	form = va_arg(args,char*);
#endif

	if(!s)
		return -1;

	/* make a fake stream */
	SFCLEAR(&f);
	f.flags = SF_STRING|SF_READ;
	f.mode = SF_READ;
	f.size = strlen((char*)s);
	f.data = f.next = f.endw = (uchar*)s;
	f.endb = f.endr = f.data+f.size;
	sfset(&f,f.flags,1);

	rv = sfvscanf(&f,form,args);
	va_end(args);

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/stdextern.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/stdextern.c'"
else
cat << \SHAR_EOF > './Stdio_b/stdextern.c'
#define _STDEXTERN

#include	"sfstdio.h"

#ifdef _STDIO_INIT
	FILE	_iob[_NFILE] =
	{
		_STDIO_INIT(0),
		_STDIO_INIT(1),
		_STDIO_INIT(2)
	};
#endif /* _STDIO_INIT */
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/stdstream.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/stdstream.c'"
else
cat << \SHAR_EOF > './Stdio_b/stdstream.c'
#include	"sfstdio.h"

/* symbol to force loading of this file */
int	_Stdio_load = 1;

/* lists of active and free FILE streams */
static FILE	*Files;
static FILE	*Ffree;

/* fast FILE allocation functions */
#define FALLOC(f)	((f = Ffree) ? (Ffree = (FILE*)f->std_next, f) : \
					(f = (FILE*) malloc(sizeof(FILE))))
#define FFREE(f)	(f->std_next = (uchar*)Ffree, Ffree = f)


/* synchronizing sfio and stdio buffer pointers */
#ifdef __STD_C
_sfstdio(reg Sfile_t* sp)
#else
_sfstdio(sp)
reg Sfile_t	*sp;
#endif
{
	FILE	*fp;

	if(!(fp = _stdstream(sp)))
		return -1;
#if _FILE_cnt
	if(fp->std_ptr >= sp->next && fp->std_ptr <= sp->endb)
		sp->next = fp->std_ptr;
	fp->std_cnt = 0;
	fp->std_ptr = NIL(uchar*);

	if((sp->mode &= ~SF_STDIO) == SF_READ)
		sp->endr = sp->endb;
	else if(sp->mode == SF_WRITE)
		sp->endw = sp->endb;
#endif

	return 0;
}


/* map a FILE stream to a Sfio_t stream */
#ifdef __STD_C
Sfile_t* _sfstream(reg FILE* std)
#else
Sfile_t* _sfstream(std)
reg FILE*	std;
#endif
{	reg FILE	*fp, *last;
	reg Sfile_t	*sp;

	/* not one of ours */
	if(!std
#if _FILE_flag
	   || (std->std_flag & (~(_IOEOF|_IOERR)))
#endif
	  )
		return NIL(Sfile_t*);

	sp = NIL(Sfile_t*);
	if(std == stdin)
		sp = sfstdin;
	else if(std == stdout)
		sp = sfstdout;
	else if(std == stderr)
		sp = sfstderr;
	else for(last = NIL(FILE*), fp = Files; fp; last = fp, fp = (FILE*)fp->std_next)
	{	if(fp != std)
			continue;

		/* found */
		if(last)
		{	/* move-to-front heuristic */
			last->std_next = fp->std_next;
			fp->std_next = (uchar*)Files;
			Files = fp;
		}
		sp = (Sfile_t*)fp->std_sf;
		break;
	}

	/* synchronize the data pointers */
	if(sp)
		_sfstdio(sp);

	return sp;
}

/* map a Sfio_t stream to a FILE stream */
#ifdef __STD_C
FILE* _stdstream(reg Sfile_t* sf)
#else
FILE* _stdstream(sf)
reg Sfile_t*	sf;
#endif
{	reg FILE	*fp, *last;

	if(!sf)
		return NIL(FILE*);
	else if(sf == sfstdin)
		return stdin;
	else if(sf == sfstdout)
		return stdout;
	else if(sf == sfstderr)
		return  stderr;

	for(last = NIL(FILE*), fp = Files; fp; last = fp, fp = (FILE*)fp->std_next)
	{	if(fp->std_sf != (uchar*)sf)
			continue;

		/* found */
		if(last)
		{	/* move-to-front heuristic */
			last->std_next = fp->std_next;
			fp->std_next = (uchar*)Files;
			Files = fp;
		}
		return fp;
	}

	/* create a new one */
	if(!FALLOC(fp))
		return NIL(FILE*);
	memclear(fp,sizeof(FILE));
#if _FILE_cnt
	fp->std_cnt = 0;
#endif
#if _FILE_flag
	fp->std_flag = ((sfeof(sf) ? _IOEOF : 0) | (sferror(sf) ? _IOERR : 0));
#endif
#if _FILE_file
	fp->std_file = sffileno(sf);
#endif
	fp->std_sf = (uchar*)sf;
	fp->std_next = (uchar*)Files;
	Files = fp;

	return fp;
}

/* close the map structure of a FILE stream */
#ifdef __STD_C
void _stdclose(FILE* std)
#else
void _stdclose(std)
reg FILE	*std;
#endif
{	reg FILE	*fp, *last;

	for(last = NIL(FILE*), fp = Files; fp; last = fp, fp = (FILE*)fp->std_next)
	{	if(fp != std)
			continue;
		if(last)
			last->std_next = fp->std_next;
		else	Files = (FILE*)fp->std_next;
		FFREE(fp);
		return;
	}
}

/* The following function is never called.
   It is used to force loading of all compatibility functions and
   avoid name conflicts that arise from certain local implementation
*/
void __stdiold()
{	
	clearerr(stdin);
	feof(stdin);
	ferror(stdin);
	fileno(stdin);
	setbuf(stdin,"");
	setbuffer(stdin,"",0);
	setlinebuf(stdin);
	setvbuf(stdin,"",0,0);

	fclose(stdin);
	fdopen(0,"");
	fopen("","");
	freopen("","",stdin);
	popen("","");
	pclose(stdin);
	tmpfile();

	fgetc(stdin);
	getc(stdin);
	getchar();
	fgets("",0,stdin);
	gets("");
	getw(stdin);
	fread("",0,0,stdin);
	fscanf(stdin,"");
	scanf("");
	sscanf("","");
	_doscan(stdin,"",(va_list)0);
	vfscanf(stdin,"",(va_list)0);
	vscanf("",(va_list)0);
	vsscanf("","",(va_list)0);
	ungetc(0,stdin);

	fputc(0,stdout);
	putc(0,stdout);
	putchar(0);
	fputs("",stdout);
	puts("");
	putw(0,stdout);
	fwrite("",0,0,stdout);
	fprintf(stdout,"");
	printf("");
	sprintf("","");
	_doprnt("",(va_list)0,stdout);
	vfprintf(stdout,"",(va_list)0);
	vprintf("",(va_list)0);
	vsprintf("","",(va_list)0);

	fseek(stdin,0L,0);
	ftell(stdin);
	rewind(stdin);
	fflush(stdout);
	fpurge(stdout);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/tmpfile.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/tmpfile.c'"
else
cat << \SHAR_EOF > './Stdio_b/tmpfile.c'
#include	"sfstdio.h"

#ifdef __STD_C
FILE* tmpfile(void)
#else
FILE* tmpfile()
#endif
{
	reg Sfile_t	*sp;
	reg FILE	*fp;

	if(!(sp = sftmp(SF_BUFSIZE)))
		return NIL(FILE*);
	if(!(fp = _stdstream(sp)))
	{	sfclose(sp);
		return NIL(FILE*);
	}

	return fp;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/ungetc.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/ungetc.c'"
else
cat << \SHAR_EOF > './Stdio_b/ungetc.c'
#include	"sfstdio.h"


#ifdef __STD_C
int ungetc(int c,FILE *fp)
#else
int ungetc(c,fp)
reg int		c;
reg FILE	*fp;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;
	_stdclrerr(fp,sp);
	return sfungetc(sp,c);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/vfprintf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/vfprintf.c'"
else
cat << \SHAR_EOF > './Stdio_b/vfprintf.c'
#include	"sfstdio.h"

#ifdef __STD_C
vfprintf(FILE *fp, const char* form, va_list args)
#else
vfprintf(fp,form,args)
FILE	*fp;
char    *form;          /* format to use */
va_list args;           /* arg list if argf == 0 */
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;
	_stdclrerr(fp,sp);

	if((rv = sfvprintf(sp,form,args)) < 0)
		_stderr(fp);

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/vfscanf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/vfscanf.c'"
else
cat << \SHAR_EOF > './Stdio_b/vfscanf.c'
#include	"sfstdio.h"

#ifdef __STD_C
vfscanf(FILE *fp, const char* form, va_list args)
#else
vfscanf(fp,form,args)
FILE	*fp;
char    *form;          /* format to use */
va_list args;           /* arg list if argf == 0 */
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;
	_stdclrerr(fp,sp);

	if((rv = sfvscanf(sp,form,args)) <= 0)
	{	if(sfeof(sp))
			_stdeof(fp);
		if(sferror(sp))
			_stderr(fp);
	}

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/vprintf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/vprintf.c'"
else
cat << \SHAR_EOF > './Stdio_b/vprintf.c'
#include	"sfstdio.h"

#ifdef __STD_C
vprintf(const char* form, va_list args)
#else
vprintf(form,args)
char    *form;          /* format to use */
va_list args;           /* arg list if argf == 0 */
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(stdout)))
		return -1;

	_stdclrerr(stdout,sp);
	if((rv = sfvprintf(sp,form,args)) < 0)
		_stderr(stdout);

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/vscanf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/vscanf.c'"
else
cat << \SHAR_EOF > './Stdio_b/vscanf.c'
#include	"sfstdio.h"

#ifdef __STD_C
vscanf(const char* form, va_list args)
#else
vscanf(form,args)
char    *form;          /* format to use */
va_list args;           /* arg list if argf == 0 */
#endif
{
	reg int		rv;
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(stdin)))
		return -1;
	_stdclrerr(stdin,sp);
	if((rv = sfvscanf(sp,form,args)) <= 0)
	{	if(sfeof(sp))
			_stdeof(stdin);
		if(sferror(sp))
			_stderr(stdin);
	}

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/vsprintf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/vsprintf.c'"
else
cat << \SHAR_EOF > './Stdio_b/vsprintf.c'
#include	"sfstdio.h"

#ifdef __STD_C
vsprintf(char *s, const char *form, va_list args)
#else
vsprintf(s,form,args)
register char	*s;
register char	*form;
va_list		args;
#endif
{
	Sfile_t	f;
	reg int	rv;

	if(!s)
		return -1;

	/* make a fake stream */
	SFCLEAR(&f);
	f.flags = SF_STRING|SF_WRITE;
	f.mode = SF_WRITE;
	f.size = 4*SF_BUFSIZE;
	f.data = f.next = f.endr = (uchar*)s;
	f.endb = f.endw = f.data+f.size;

	rv = sfvprintf(&f,form,args);
	*f.next = '\0';
	_Sfi = f.next - f.data;

	return rv;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/vsscanf.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/vsscanf.c'"
else
cat << \SHAR_EOF > './Stdio_b/vsscanf.c'
#include	"sfstdio.h"

#ifdef __STD_C
vsscanf(char *s, const char *form, va_list args)
#else
vsscanf(s,form,args)
register char	*s;
register char	*form;
va_list		args;
#endif
{
	Sfile_t	f;

	if(!s)
		return -1;

	/* make a fake stream */
	SFCLEAR(&f);
	f.flags = SF_STRING|SF_READ;
	f.mode = SF_READ;
	f.size = strlen((char*)s);
	f.data = f.next = f.endr = (uchar*)s;
	f.endb = f.endw = f.data+f.size;

	return sfvscanf(&f,form,args);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/fpurge.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/fpurge.c'"
else
cat << \SHAR_EOF > './Stdio_b/fpurge.c'
#include	"sfstdio.h"


#ifdef __STD_C
int fpurge(reg FILE *fp)
#else
int fpurge(fp)
reg FILE	*fp;
#endif
{
	reg Sfile_t	*sp;

	if(!(sp = _sfstream(fp)))
		return -1;

	_stdclrerr(fp,sp);

	return sfpurge(sp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/Makefile'
then
	echo shar: will not over-write existing file "'./Stdio_b/Makefile'"
else
cat << \SHAR_EOF > './Stdio_b/Makefile'
# makefile for libstdio.a
#
# Written by Kiem-Phong Vo (4/24/91)

# Native compiler if building libstdio.a for a cross compilation environment
NC=	cc

# Compiler and flags to use
AR=	ar
CC=	cc
CXFLAGS=
CCFLAGS= -c -O -I.. $(CXFLAGS)

SRCS=	doprnt.c doscan.c fclose.c fdopen.c fflush.c fgetc.c fgets.c filbuf.c \
	flsbuf.c fopen.c fprintf.c fputc.c fputs.c fread.c freopen.c fscanf.c \
	fseek.c ftell.c fwrite.c getc.c getchar.c gets.c getw.c pclose.c popen.c \
	printf.c putc.c putchar.c puts.c putw.c rewind.c scanf.c setbuf.c setbuffer.c \
	setlinebuf.c setvbuf.c sprintf.c sscanf.c stdextern.c stdstream.c \
	tmpfile.c ungetc.c vfprintf.c vfscanf.c vprintf.c vscanf.c vsprintf.c \
	vsscanf.c fileno.c feof.c ferror.c clearerr.c fpurge.c

OBJS=	doprnt.o doscan.o fclose.o fdopen.o fflush.o fgetc.o fgets.o filbuf.o \
	flsbuf.o fopen.o fprintf.o fputc.o fputs.o fread.o freopen.o fscanf.o \
	fseek.o ftell.o fwrite.o getc.o getchar.o gets.o getw.o pclose.o popen.o \
	printf.o putc.o putchar.o puts.o putw.o rewind.o scanf.o setbuf.o setbuffer.o \
	setlinebuf.o setvbuf.o sprintf.o sscanf.o stdextern.o stdstream.o \
	tmpfile.o ungetc.o vfprintf.o vfscanf.o vprintf.o vscanf.o vsprintf.o \
	vsscanf.o fileno.o feof.o ferror.o clearerr.o fpurge.o

.c.o:
	$(CC) $(CCFLAGS) $*.c

libstdio.a: must $(OBJS)
	$(AR) cr libstdio.a $(OBJS)
	-(ranlib libstdio.a; rm sfstdio.h; exit 0) >/dev/null 2>&1

must:
	sh ./sfstdhdr.sh $(CC)
	$(NC) -I.. sfstdio.c -o sfstdgen >/dev/null 2>&1
	./sfstdgen > sfstdio.h; rm sfstdgen sfstdhdr.h
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/sfstdhdr.sh'
then
	echo shar: will not over-write existing file "'./Stdio_b/sfstdhdr.sh'"
else
cat << \SHAR_EOF > './Stdio_b/sfstdhdr.sh'
trap "eval 'rm kpv.xxx.* >/dev/null 2>&1'" 0 1 2

# Take cross-compiler name as an argument
if test "$1" = ""
then	CC=cc
else	CC=$1
fi

# Get full path name for stdio.h
rm sfstdhdr.h >/dev/null 2>&1

# make sure that the right stdio.h file will be included
echo "#include <stdio.h>" > kpv.xxx.c
$CC -E kpv.xxx.c > kpv.xxx.cpp 2>/dev/null

ed kpv.xxx.cpp >/dev/null 2>&1 <<!
$
/stdio.h"/p
.w sfstdhdr.h
E sfstdhdr.h
1
s/ 1/include/p
w
s/ 1//
w
!

# determine the right names for _iob, _filbuf, and _flsbuf
for name in _iob _filbuf _flsbuf
do
echo "#include <stdio.h>" >kpv.xxx.c
case $name in
_iob)	echo "stdin;" >>kpv.xxx.c
	pat='__*iob_*'
	;;
_filbuf) echo "getc(stdin);" >>kpv.xxx.c
	pat='__*filbuf_*'
	;;
_flsbuf) echo "putc(0,stdout);" >>kpv.xxx.c
	pat='__*flsbuf_*'
	;;
esac

rm kpv.xxx.name >/dev/null 2>&1
$CC -E kpv.xxx.c > kpv.xxx.cpp 2>/dev/null
echo "kpv" >> kpv.xxx.cpp
ed kpv.xxx.cpp >/dev/null 2>&1 <<!
$
?$pat?
s/$pat/====&/
s/.*====//
s/$pat/&====/
s/====.*//
.w kpv.xxx.name
!

NAME="`cat kpv.xxx.name`"
echo "#define NAME$name \"$NAME\""	>> sfstdhdr.h
if test "$NAME" != "$name"
then	echo "#ifdef $name"		>> sfstdhdr.h
	echo "#undef $name"		>> sfstdhdr.h
	echo "#endif"			>> sfstdhdr.h
	echo "#define $name	$NAME"	>> sfstdhdr.h
fi

done

exit 0
SHAR_EOF
chmod +x './Stdio_b/sfstdhdr.sh'
fi # end of overwriting check
if test -f './Stdio_b/putc.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/putc.c'"
else
cat << \SHAR_EOF > './Stdio_b/putc.c'
#include	"sfstdio.h"


#ifdef __STD_C
int putc(int c, FILE *fp)
#else
int putc(c, fp)
reg int		c;
reg FILE	*fp;
#endif
{
	return fputc(c,fp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Stdio_b/getc.c'
then
	echo shar: will not over-write existing file "'./Stdio_b/getc.c'"
else
cat << \SHAR_EOF > './Stdio_b/getc.c'
#include	"sfstdio.h"

#ifdef __STD_C
int getc(reg FILE *fp)
#else
int getc(fp)
reg FILE *fp;
#endif
{
	return fgetc(fp);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfclrerr.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfclrerr.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfclrerr.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfclrerr(reg Sfile_t* f)
#else
static __sfclrerr(f)
reg Sfile_t	*f;
#endif
{
	return sfclrerr(f);
}

#undef sfclrerr

#ifdef __STD_C
sfclrerr(reg Sfile_t* f)
#else
sfclrerr(f)
reg Sfile_t	*f;
#endif
{
	return __sfclrerr(f);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfecvt.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfecvt.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfecvt.c'
#include	"sfhdr.h"

#ifdef __STD_C
static char *__sfecvt(reg double dval, int n_digit, int* decpt, int* sign)
#else
static char *__sfecvt(dval,n_digit,decpt,sign)
reg double	dval;		/* value to convert */
int		n_digit;	/* number of digits wanted */
int		*decpt;		/* to return decimal point */
int		*sign;		/* to return sign */
#endif
{
	return sfecvt(dval,n_digit,decpt,sign);
}

#undef sfecvt

#ifdef __STD_C
char *sfecvt(reg double dval, int n_digit, int* decpt, int* sign)
#else
char *sfecvt(dval,n_digit,decpt,sign)
reg double	dval;		/* value to convert */
int		n_digit;	/* number of digits wanted */
int		*decpt;		/* to return decimal point */
int		*sign;		/* to return sign */
#endif
{
	return __sfecvt(dval,n_digit,decpt,sign);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfeof.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfeof.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfeof.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfeof(reg Sfile_t* f)
#else
static __sfeof(f)
reg Sfile_t	*f;
#endif
{
	return sfeof(f);
}

#undef sfeof

#ifdef __STD_C
sfeof(reg Sfile_t* f)
#else
sfeof(f)
reg Sfile_t	*f;
#endif
{
	return __sfeof(f);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sferror.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sferror.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sferror.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sferror(reg Sfile_t* f)
#else
static __sferror(f)
reg Sfile_t	*f;
#endif
{
	return sferror(f);
}

#undef sferror

#ifdef __STD_C
sferror(reg Sfile_t* f)
#else
sferror(f)
reg Sfile_t	*f;
#endif
{
	return __sferror(f);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sffcvt.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sffcvt.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sffcvt.c'
#include	"sfhdr.h"

#ifdef __STD_C
static char* __sffcvt(reg double dval, int n_digit, int* decpt, int* sign)
#else
static char* __sffcvt(dval,n_digit,decpt,sign)
reg double	dval;		/* value to convert */
int		n_digit;	/* number of digits wanted */
int		*decpt;		/* to return decimal point */
int		*sign;		/* to return sign */
#endif
{
	return sffcvt(dval,n_digit,decpt,sign);
}

#undef sffcvt

#ifdef __STD_C
char *sffcvt(reg double dval, int n_digit, int* decpt, int* sign)
#else
char *sffcvt(dval,n_digit,decpt,sign)
reg double	dval;		/* value to convert */
int		n_digit;	/* number of digits wanted */
int		*decpt;		/* to return decimal point */
int		*sign;		/* to return sign */
#endif
{
	return __sffcvt(dval,n_digit,decpt,sign);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sffileno.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sffileno.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sffileno.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sffileno(reg Sfile_t* f)
#else
static __sffileno(f)
reg Sfile_t	*f;
#endif
{
	return sffileno(f);
}

#undef sffileno

#ifdef __STD_C
sffileno(reg Sfile_t* f)
#else
sffileno(f)
reg Sfile_t	*f;
#endif
{
	return __sffileno(f);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfgetc.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfgetc.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfgetc.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfgetc(reg Sfile_t* f)
#else
static __sfgetc(f)
reg Sfile_t	*f;
#endif
{
	return sfgetc(f);
}

#undef sfgetc

#ifdef __STD_C
sfgetc(reg Sfile_t* f)
#else
sfgetc(f)
reg Sfile_t	*f;
#endif
{
	return __sfgetc(f);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfgetl.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfgetl.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfgetl.c'
#include	"sfhdr.h"

#ifdef __STD_C
static long __sfgetl(reg Sfile_t* f)
#else
static long __sfgetl(f)
reg Sfile_t	*f;
#endif
{
	return sfgetl(f);
}

#undef sfgetl

#ifdef __STD_C
long sfgetl(reg Sfile_t* f)
#else
long sfgetl(f)
reg Sfile_t	*f;
#endif
{
	return __sfgetl(f);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfgetu.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfgetu.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfgetu.c'
#include	"sfhdr.h"

#ifdef __STD_C
static ulong __sfgetu(reg Sfile_t* f)
#else
static ulong __sfgetu(f)
reg Sfile_t	*f;
#endif
{
	return sfgetu(f);
}

#undef sfgetu

#ifdef __STD_C
ulong sfgetu(reg Sfile_t* f)
#else
ulong sfgetu(f)
reg Sfile_t	*f;
#endif
{
	return __sfgetu(f);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfputc.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfputc.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfputc.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfputc(reg Sfile_t* f, reg int c)
#else
static __sfputc(f,c)
reg Sfile_t	*f;
reg int		c;
#endif
{
	return sfputc(f,c);
}

#undef sfputc

#ifdef __STD_C
sfputc(reg Sfile_t* f, reg int c)
#else
sfputc(f,c)
reg Sfile_t	*f;
reg int		c;
#endif
{
	return __sfputc(f,c);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfputd.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfputd.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfputd.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfputd(reg Sfile_t* f, reg double v)
#else
static __sfputd(f,v)
reg Sfile_t	*f;
reg double	v;
#endif
{
	return sfputd(f,v);
}

#undef sfputd

#ifdef __STD_C
sfputd(reg Sfile_t* f, reg double v)
#else
sfputd(f,v)
reg Sfile_t	*f;
reg double	v;
#endif
{
	return __sfputd(f,v);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfputl.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfputl.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfputl.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfputl(reg Sfile_t* f, reg long v)
#else
static __sfputl(f,v)
reg Sfile_t	*f;
reg long	v;
#endif
{
	return sfputl(f,v);
}

#undef sfputl

#ifdef __STD_C
sfputl(reg Sfile_t* f, reg long v)
#else
sfputl(f,v)
reg Sfile_t	*f;
reg long	v;
#endif
{
	return __sfputl(f,v);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfputu.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfputu.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfputu.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfputu(reg Sfile_t* f, reg ulong v)
#else
static __sfputu(f,v)
reg Sfile_t	*f;
reg ulong	v;
#endif
{
	return sfputu(f,v);
}

#undef sfputu

#ifdef __STD_C
sfputu(reg Sfile_t* f, reg ulong v)
#else
sfputu(f,v)
reg Sfile_t	*f;
reg ulong	v;
#endif
{
	return __sfputu(f,v);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfslen.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfslen.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfslen.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfslen(void)
#else
static __sfslen()
#endif
{
	return sfslen();
}

#undef sfslen

#ifdef __STD_C
sfslen(void)
#else
sfslen()
#endif
{
	return __sfslen();
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfstacked.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfstacked.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfstacked.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfstacked(reg Sfile_t* f)
#else
static __sfstacked(f)
reg Sfile_t	*f;
#endif
{
	return sfstacked(f);
}

#undef sfstacked

#ifdef __STD_C
sfstacked(reg Sfile_t* f)
#else
sfstacked(f)
reg Sfile_t	*f;
#endif
{
	return __sfstacked(f);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/_sfulen.c'
then
	echo shar: will not over-write existing file "'./Sfio_f/_sfulen.c'"
else
cat << \SHAR_EOF > './Sfio_f/_sfulen.c'
#include	"sfhdr.h"

#ifdef __STD_C
static __sfulen(reg ulong v)
#else
static __sfulen(v)
reg ulong	v;
#endif
{
	return sfulen(v);
}

#undef sfulen

#ifdef __STD_C
sfulen(reg ulong v)
#else
sfulen(v)
reg ulong	v;
#endif
{
	return __sfulen(v);
}
SHAR_EOF
fi # end of overwriting check
if test -f './Sfio_f/Makefile'
then
	echo shar: will not over-write existing file "'./Sfio_f/Makefile'"
else
cat << \SHAR_EOF > './Sfio_f/Makefile'
# makefile for stdio-source compatibility code
#
# Written by Kiem-Phong Vo (4/24/91)

# Compiler and flags to use
AR=	ar
CC=	cc
CXFLAGS=
CCFLAGS= -c -O -I.. $(CXFLAGS)

SRCS=	_sfclrerr.c _sfecvt.c _sfeof.c _sferror.c _sffcvt.c _sffileno.c \
	_sfgetc.c _sfgetl.c _sfgetu.c _sfstacked.c _sfputc.c _sfputd.c _sfputl.c \
	_sfputu.c _sfslen.c _sfulen.c
OBJS=	_sfclrerr.o _sfecvt.o _sfeof.o _sferror.o _sffcvt.o _sffileno.o \
	_sfgetc.o _sfgetl.o _sfgetu.o _sfstacked.o _sfputc.o _sfputd.o _sfputl.o \
	_sfputu.o _sfslen.o _sfulen.o

.c.o:
	$(CC) $(CCFLAGS) $*.c

must:	$(OBJS)
SHAR_EOF
fi # end of overwriting check
if test -f './sfvscanf.c'
then
	echo shar: will not over-write existing file "'./sfvscanf.c'"
else
cat << \SHAR_EOF > './sfvscanf.c'
#include	"sfhdr.h"

/*	The main engine for reading formatted data
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#define	S_NORMAL	0	/* argument is a pointer to a normal object */
#define S_LONG		1	/* argument is a pointer to a long object */
#define S_SHORT		2	/* argument is a pointer to a short object */

#include	"FEATURE/double"
#if _long_double
#define S_LONGDOUBLE	3	/* argument is a pointer to a long double object */
#endif

#define a2f(s)	strtod(s,NIL(char**))

#define A_SIZE		(1<<8)	/* alphabet */
#ifdef __STD_C
static char *setclass(reg char* form, reg char* accept)
#else
static char *setclass(form,accept)
reg char	*form;		/* format string */
reg char	*accept;	/* accepted characters are set to 1 */
#endif
{
	reg int		fmt, c, yes;

	if((fmt = *form++) == '^')
	{	/* we want the complement of this set */
		yes = 0;
		fmt = *form++;
	}
	else	yes = 1;

	for(c = 0; c < A_SIZE; ++c)
		accept[c] = !yes;

	if(fmt == ']' || fmt == '-')
	{	/* special first char */
		accept[fmt] = yes;
		fmt = *form++;
	}

	for(; fmt != ']'; fmt = *form++)
	{	/* done */
		if(!fmt)
			return (form-1);

		/* interval */
		if(fmt != '-' || form[0] == ']' || form[-2] > form[0])
			accept[fmt] = yes;
		else for(c = form[-2]+1; c < form[0]; ++c)
			accept[c] = yes;
	}

	return form;
}

#define SETARG(val,argf,args,type,fmt,form) \
	{ if(!argf) \
		*va_arg(args,type*) = (type)val; \
	  else if((*argf)(fmt,(char*)(&val),sizeof(val)) < 0) \
		form = ""; \
	}

#ifdef __STD_C
sfvscanf(Sfile_t* f, reg const char* form, va_list args)
#else
sfvscanf(f,form,args)
Sfile_t		*f;		/* file to be scanned */
reg char	*form;		/* scanning format */
va_list		args;
#endif
{
	reg char	*d, *endd;
	reg int		inp, shift, base, length;
	reg int		fmt, skip, size, n_assign;
	reg char	*sp;
	Sfile_t		*sf;
	int		n_input;
	char		accept[MAXDIGITS];
	Argf_s		argf;
	Extf_s		extf;
	Fa_t		*fa, *fast;
	va_list		alist;

	/* fast io system */
#define SFBUF(f)	(d = (char*)f->next, endd = (char*)f->endb)
#define SFEND(f)	((n_input += (uchar*)d - f->next), f->next = (uchar*)d)
#define SFFILL(f,c)	(SFEND(f), (SFopen(f),c = _sffilbuf(f,0),SFLOCK(f)), SFBUF(f), c)
#define SFGETC(f,c)	(d >= endd ? SFFILL(f,c) : (c = (int)(*d++)))
#define SFUNGETC(f,c)	(--d)

	if(f->mode != SF_READ && _sfmode(f,SF_READ) < 0)
		return -1;
	SFLOCK(f);
	SFBUF(f);
	n_assign = n_input = 0;
	inp = -1;
	sf = NIL(Sfile_t*);
	argf = NIL(Argf_s);
	extf = NIL(Extf_s);
	fast = NIL(Fa_t*);

loop_fa :
	while(fmt = *form++)
	{
		if(fmt != '%')
		{	/* matching white space directive */
			if(isspace(fmt) && (fmt != '\n' || !(f->flags&SF_LINE)))
			{	for(;;)
				{	if(SFGETC(f,inp) < 0)
						goto done;
					else if(!isspace(inp))
					{	/* put back unmatched byte */
						SFUNGETC(f,inp);
						break;
					}
				}
			}
			else
			{
			literal : /* literal match */
				if(SFGETC(f,inp) != fmt)
				{	if(inp >= 0)
						SFUNGETC(f,inp);
					goto done;
				}
			}
			continue;
		}

		/* matching some pattern */
		skip = length = 0;
		base = 10;
		switch((fmt = *form++))
		{
		case '%' : /* match % literally */
			goto literal;

		case '@' : /* set argument getting function */
			argf = va_arg(args,Argf_s);
			continue;

		case '&' : /* set extension function */
			extf = va_arg(args,Extf_s);
			continue;

		case 'n' : /* return number of bytes read */
			SFEND(f);
			SETARG(n_input,argf,args,int,'n',form);
			continue;

		case ':' : /* stack a pair of format/arglist */
			if(!FAMALLOC(fa))
				goto done;
			fa->form = (char*)form;
			if(!(form = va_arg(args,char*)))
				form = "";
			alist = va_arg(args,va_list);
			fa->args = args;
			args = alist;
			fa->extf.s = extf;
			fa->argf.s = argf;
			fa->next = fast;
			fast = fa;
			continue;

		case '*' :	/* skip one argument */
			skip = 1;
			fmt = *form++;

		default :
			/* scan length */
			while(isdigit(fmt))
			{	length = length*10 + (fmt - '0');
				fmt = *form++;
			}

			if(fmt == '.')
			{	/* defining a base */
				fmt = *form++;
				base = 0;
				while(isdigit(fmt))
				{	base = base*10 + (fmt - '0');
					fmt = *form++;
				}
				if(base < 2 || base > RADIX)
					base = 10;
			}

			/* size of object to be assigned */
#ifdef S_LONGDOUBLE
			if(fmt == 'L')
				{ size = S_LONGDOUBLE; fmt = *form++; }
			else
#endif
			if(fmt == 'l')
				{ size = S_LONG; fmt = *form++; }
			else if(fmt == 'h')
				{ size = S_SHORT; fmt = *form++; }
			else	size = S_NORMAL;

			/* canonicalize format */
			switch(fmt)
			{
			case 'E' :
			case 'F' :
			case 'G' :
			case 'e' :
			case 'g' :
				fmt = 'f';
				break;
			}
#ifdef S_LONGDOUBLE
			if(size == S_LONGDOUBLE && fmt != 'f')
				size = S_LONG;
#endif
		}

		/* scan length */
		if(length == 0)
			length = fmt == 'c' ? 1 : SF_BUFSIZE;

		/* define the first input character */
		if(fmt == 'c' || fmt == '[')
			SFGETC(f,inp);
		else
		{	/* skip starting blanks */
			do	{ SFGETC(f,inp); }
			while(isspace(inp))
				;
		}
		if(inp < 0)
			goto done;

		if(fmt == 'f')
		{	/* a float or double */
			reg char	*val;
			reg int		dot, exponent;
			double		dval;

			val = accept;
			if(length >= MAXDIGITS)
				length = MAXDIGITS-1;
			dot = exponent = 0;
			do
			{	if(isdigit(inp))
					*val++ = inp;
				else if(inp == '.')
				{	/* too many dots */
					if(dot++ > 0)
						break;
					*val++ = '.';
				}
				else if(inp == 'e' || inp == 'E')
				{	/* too many e,E */
					if(exponent++ > 0)
						break;
					*val++ = inp;
					if(--length <= 0 || SFGETC(f,inp) < 0 ||
					   (inp != '-' && inp != '+' && !isdigit(inp)) )
						break;
					*val++ = inp;
				}
				else if(inp == '-' || inp == '+')
				{	/* too many signs */
					if(val > accept)
						break;
					*val++ = inp;
				}
				else	break;

			} while(--length > 0 && SFGETC(f,inp) >= 0);

			if(!skip && val > accept)
			{	/* there is something to convert */
				*val = '\0';
				n_assign += 1;
				dval = a2f(accept);

				switch(size)
				{
#ifdef S_LONGDOUBLE
				case S_LONGDOUBLE:
					SETARG(dval,argf,args,long double,'F',form);
					break;
#endif
				case S_LONG:
					SETARG(dval,argf,args,double,'F',form);
					break;
				case S_SHORT :
				case S_NORMAL:
					SETARG(dval,argf,args,float,'f',form);
					break;
				}
			}
		}
		else if(fmt == 's' || fmt == 'c' || fmt == '[')
		{	/* get buffer to copy to */
			sp = NIL(char*);
			if(!skip)
			{	if(!argf)
				{	sp = va_arg(args,char*);
					if(size == S_LONG) /* buffer size */
						size = va_arg(args,int);
					else	size = -1;
					if(fmt != 'c')
						size -= 1;
				}
				else if(!sf)
					sf = sfnew(NIL(Sfile_t*),NIL(char*),-1,-1,
						   SF_STRING|SF_WRITE);
				else	sfseek(sf,0L,0);
			}

			if(fmt == 's')
			{	/* copy a string */
				do
				{	if(isspace(inp))
						break;
					if(sp)
					{	if(size < 0 || size-- > 0)
							*sp++ = inp;
					}
					else if(!skip && sf)
						sfputc(sf,inp);
				} while(--length > 0 && SFGETC(f,inp) >= 0);
			}
			else if(fmt == 'c')
			{	/* copy characters */
				do
				{	if(sp)
					{	if(size < 0 || size-- > 0)
							*sp++ = inp;
					}
					else if(!skip && sf)
						sfputc(sf,inp);
				} while(--length > 0 && SFGETC(f,inp) >= 0);
			}
			else
			{	/* copy characters from a class */
				form = setclass((char*)form,accept);
				if(!accept[inp])
				{	SFUNGETC(f,inp);
					continue;
				}

				do
				{	if(!accept[inp])
						break;
					if(sp)
					{	if(size < 0 || size-- > 0)
							*sp++ = inp;
					}
					else if(!skip && sf)
						sfputc(sf,inp);
				} while(--length > 0 && SFGETC(f,inp) >= 0);
			}

			if(!skip)
			{	n_assign += 1;
				if(sp)
				{	if(fmt != 'c')
						*sp = '\0';
				}
				else if(sf)
				{	sfputc(sf,'\0');
					if((*argf)('s',(char*)sf->data,
						(sf->next-sf->data)-1) < 0)
							form = "";
				}
			}
		}
		else if(fmt == 'p' || fmt == 'u')
		{	/* make sure this is unsigned */
			if(inp == '-')
			{	SFUNGETC(f,inp);
				goto done;
			}
			goto dec_convert;
		}
		else if(fmt == 'd' || fmt == 'o' || fmt == 'x' || fmt == 'i')
		{	/* some integer type */
			long	lval;
			reg int	sign;

		dec_convert:
			if(inp == '-' || inp == '+')
			{	/* get the sign */
				sign = inp == '-' ? -1 : 1;

				/* skip until a non-blank */
				while(--length > 0 && SFGETC(f,inp) >= 0)
					if(!isspace(inp))
						break;
			}
			else	sign = 1;
			if(inp < 0)
				goto done;

			if(fmt == 'i')
			{	/* data type is self-described */
				if(inp == '0')
				{	if(--length > 0)
						SFGETC(f,inp);
					if(inp == 'x' || inp == 'X')
					{	base = 16;
						if(--length > 0)
							SFGETC(f,inp);
					}
					else	base = 8;
				}
				else	base = 10;
			}
			else if(fmt == 'o')
				base = 8;
			else if(fmt == 'x' || fmt == 'p')
				base = 16;

			/* now convert */
			lval = 0;
			if(base == 16)
			{	sp = _Sfv36;
				shift = 4;
				if(sp[inp] >= 16)
				{	SFUNGETC(f,inp);
					goto done;
				}
				if(inp == '0' && --length > 0)
				{	/* skip leading 0x or 0X */
					SFGETC(f,inp);
					if((inp == 'x' || inp == 'X') && --length > 0)
						SFGETC(f,inp);
				}
				if(inp >= 0 && sp[inp] < 16)
					goto base_shift;
			}
			else if(base == 10)
			{	/* fast base 10 conversion */
				if(inp < '0' || inp > '9')
				{	SFUNGETC(f,inp);
					goto done;
				}

				do	{ lval = (lval<<3) + (lval<<1) + (inp - '0'); }
				while(--length > 0 &&
				      SFGETC(f,inp) >= '0' && inp <= '9');

				if(fmt == 'i' && inp == '#' &&
				   lval >= 2 && lval <= RADIX)
				{	base = (int)lval;
					lval = 0;
					sp = base <= 36 ? _Sfv36 : _Sfvmax;
					if(--length > 0 &&
					   SFGETC(f,inp) >= 0 && sp[inp] < base)
						goto base_conv;
				}
			}
			else
			{	/* other bases */
				sp = base <= 36 ? _Sfv36 : _Sfvmax;
				if(base < 2 || base > RADIX || sp[inp] >= base)
				{	SFUNGETC(f,inp);
					goto done;
				}

			base_conv: /* check for power of 2 conversions */
				if((base & ~(base-1)) == base)
				{	if(base < 8)
						shift = base <  4 ? 1 : 2;
					else if(base < 32)
						shift = base < 16 ? 3 : 4;
					else	shift = base < 64 ? 5 : 6;

			base_shift:	/* fast conversion with shifting */
					do	{ lval = (lval << shift) + sp[inp]; }
					while(--length > 0 &&
					      SFGETC(f,inp) >= 0 && sp[inp] < base);
				}
				else
				{	do	{ lval = (lval * base) + sp[inp]; }
					while(--length > 0 &&
					      SFGETC(f,inp) >= 0 && sp[inp] < base);
				}
			}

			if(!skip)
			{	/* assign */
				n_assign += 1;
				if(sign < 0)
					lval = -lval;
				if(fmt == 'p')
				{	/* pointer conversion */
					SETARG(lval,argf,args,char*,'p',form);
				}
				else switch(size)
				{
				case S_SHORT :
					SETARG(lval,argf,args,short,'h',form);
					break;
				case S_NORMAL :
					SETARG(lval,argf,args,int,'d',form);
					break;
				case S_LONG :
					SETARG(lval,argf,args,long,'D',form);
					break;
				}
			}
		}
		else /* undefined pattern */
		{	/* return the read byte to the stream */
			SFUNGETC(f,inp);
			if(extf)
			{	/* call extension function */
				char	*rv;
				int	n;
				SFEND(f);
				SFOPEN(f);
				n = (*extf)(f,fmt,length,&rv);
				SFLOCK(f);
				SFBUF(f);
				if(n >= 0 && !skip)
				{	n_assign += 1;
					if(!argf)
					{	sp = va_arg(args,char*);
						while(n--)
							*sp++ = *rv++;
					}
					else if((*argf)(fmt,rv,n) < 0)
						form = "";
				}
			}
			continue;
		}

		if(length > 0 && inp >= 0)
			SFUNGETC(f,inp);
	}

	if(fa = fast)
	{	/* check for stacked formats/arglists */
		form = fa->form;
		args = fa->args;
		argf = fa->argf.s;
		extf = fa->extf.s;
		fast = fa->next;
		FAFREE(fa);
		goto loop_fa;
	}

done:
	if(sf)
		sfclose(sf);
	SFEND(f);
	SFOPEN(f);
	return (n_assign == 0 && inp < 0) ? -1 : n_assign;
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfwr.c'
then
	echo shar: will not over-write existing file "'./sfwr.c'"
else
cat << \SHAR_EOF > './sfwr.c'
#include	"sfhdr.h"

/*	Write with discipline.
**
**	Written by Kiem-Phong Vo (02/11/91)
*/

/* hole preserving writes */
#ifdef __STD_C
static sfoutput(Sfile_t* f, reg char* buf, reg int n)
#else
static sfoutput(f,buf,n)
Sfile_t		*f;
reg char	*buf;
reg int		n;
#endif
{	reg char	*sp, *wbuf, *endbuf;
	reg int		s, w, wr;

	w = 0;
	wbuf = buf;
	endbuf = buf+n;
	while(n > 0)
	{
		if(n < _Sfpage)
		{	/* no hole possible */
			buf += n;
			s = n = 0;
		}
		else while(n >= _Sfpage)
		{	/* see if a hole of 0's starts here */
			sp = buf+1;
			if(buf[0] == 0 && buf[_Sfpage-1] == 0)
				while(sp < endbuf)
					if(*sp++ != 0)
						break;

			/* found a hole */
			if((s = sp-buf) >= _Sfpage)
				break;

			/* skip a dirty page */
			n -= _Sfpage;
			buf += _Sfpage;
		}

		/* write out current dirty pages */
		if(buf > wbuf)
		{	if((wr = write(f->file,wbuf,buf-wbuf)) > 0)
			{	w += wr;
				f->flags &= ~SF_HOLE;
			}
			if(wr != (buf-wbuf))
				break;
			wbuf = buf;
		}

		/* seek to a rounded boundary within the hole */
		if(s >= _Sfpage)
		{	s = (s/_Sfpage)*_Sfpage;
			if(sfsk(f,(long)s,1,NIL(Sfdisc_t*)) < 0)
				break;
			w += s;
			n -= s;
			wbuf = (buf += s);
			f->flags |= SF_HOLE;

			if(n > 0)
			{	/* next page must be dirty */
				s = n <= _Sfpage ? 1 : _Sfpage;
				buf += s;
				n -= s;
			}
		}
	}

	return w > 0 ? w : -1;
}

#ifdef __STD_C
sfwr(reg Sfile_t* f, reg const char* buf, reg int n, reg Sfdisc_t* disc)
#else
sfwr(f,buf,n,disc)
reg Sfile_t	*f;
reg char	*buf;
reg int		n;
reg Sfdisc_t	*disc;
#endif
{
	reg int		w, local, string;

	GETLOCAL(f,local);

	if(!(string = (f->flags&SF_STRING)))
	{	reg Sfdisc_t	*d;
		if((d = disc) != NIL(Sfdisc_t*) && !local)
			d = disc = disc->disc;
		while(d && !d->writef)
			d = d->disc;
		if(d)
			disc = d;
	}

	for(;;)
	{
		if(string)
			w = n - (f->endb-f->next);
		else
		{
#ifdef NO_OFLAGS
			if(f->flags&SF_APPEND)
				f->here = SFSK(f,0L,2,disc);
			else /* not really dangling */
#endif
			if(f->extent >= 0 && (f->flags&SF_SHARE))
				(void)SFSK(f,f->here,0,disc);

			if(disc && disc->writef)
				w = (*(disc->writef))(f,buf,n,disc);
			else if(!disc && n >= _Sfpage &&
				!(f->flags&(SF_SHARE|SF_APPEND)) &&
				f->here == f->extent && (f->here%_Sfpage) == 0)
				w = sfoutput(f,(char*)buf,n);
			else if(ISDEVNULL(f))
				w = n;
			else if((w = write(f->file,buf,n)) > 0)
				f->flags &= ~SF_HOLE;

			if(w > 0)
			{	if(local)
				{	f->here += w;
					if(!(f->flags&(SF_SHARE|SF_APPEND)) &&
					   f->extent >= 0 && f->here > f->extent)
						f->extent = f->here;
				}
				return w;
			}
		}

		if(local)
			SETLOCAL(f);
		switch(_sfexcept(f,SF_WRITE,w,disc))
		{
		case SF_ECONT :
			continue;
		case SF_EDONE :
			return local ? 0 : w;
		case SF_EDISC :
			if(!local && !string)
				continue;
			/* else fall thru */
		case SF_ESTACK :
			return -1;
		}
	}
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfwrite.c'
then
	echo shar: will not over-write existing file "'./sfwrite.c'"
else
cat << \SHAR_EOF > './sfwrite.c'
#include	"sfhdr.h"

/*	Write data out to the file system
**
**	Written by Kiem-Phong Vo (06/27/90)
*/

#ifdef __STD_C
sfwrite(reg Sfile_t* f, reg const char* s, reg int n)
#else
sfwrite(f,s,n)
reg Sfile_t	*f;	/* write to this stream */
reg char	*s;	/* buffer to be written */
reg int		n;	/* number of bytes */
#endif
{
	reg uchar	*next;
	reg int		w;
	reg char	*begs = (char*)s;

	for(;; SFopen(f))
	{	/* see if peeked lock can be released */
		if((f->mode&SF_PEEK) && (uchar*)s == f->next &&
		   ((f->mode&SF_WRITE) || (f->flags&SF_RDWR) == SF_RDWR) )
		{	/* this code is a little tricky but it'll work even for
			   the case when switching mode from read to write.
			   In this case, we rely on the following facts:
			   1. The stream does not use memory mapping so that
			      the data area to be written will be preserved in 2.
			   2. _sfmode will reset the f->next pointer to f->data
			      or NULL if the stream is unbuffered.
			   3. The data to be written is either still in the
			      buffer or in the f->tiny field.
			   4. Any memory copy operation below will be done from
			      left-to-right so even overlapping copy areas
			      will be copied correctly.
			   Extreme care should be taken to preserve these
			   invariants if the code need be changed.
			*/
			f->mode &= ~SF_PEEK;
		}

		/* check stream mode */
		if(f->mode != SF_WRITE && _sfmode(f,SF_WRITE) < 0 )
			return s > begs ? s-begs : -1;

		/* nothing to do */
		if(n <= 0)
			break;

		SFLOCK(f);

		/* current available buffer space */
		if((w = f->endb - f->next) > n)
			w = n;

		if((uchar*)s == f->next)
		{	/* fast write after sfpeek() */
			f->next += w;
			s += w;
			n = 0;
			break;
		}

		if(w > 0 && (w != f->size || (f->flags&SF_STRING)))
		{	/* copy into buffer */
			next = f->next;

#if _vax_asm		/* s is r10, next is r8, w is r7 */
			asm( "movc3	r7,(r10),(r8)" );
			s += w;
			next += w;
#else
			MEMCPY(next,s,w);
#endif
			f->next = next;
			n -= w;
		}
		else if(!(f->flags&SF_STRING) && f->next > f->data)
		{	if((SFopen(f),_sfflsbuf(f,-1)) < 0)
				break;
		}
		else if((w = SFWR(f,s,n,f->disc)) == 0)
			break;
		else if(w > 0)
		{	n -= w;
			s += w;
		}
	}

	SFOPEN(f);

	/* sync shared unseekable streams */
	if(f->extent < 0 && (f->flags&SF_SHARE))
		(void)_sfflsbuf(f,-1);

	/* check for line buffering */
	else if(n == 0 && (f->flags&SF_LINE) && !(f->flags&SF_STRING))
	{	if((n = f->next-f->data) > (w = s-begs))
			n = w;
		if(n > 0 && n < HIFORLINE)
		{	w = *(next = f->next-n); *next = '\n'; next += n;
			while(*--next != '\n')
				;
			f->next[-n] = w;
			if(*next == '\n')
				n = HIFORLINE;
		}
		if(n >= HIFORLINE)
			(void)_sfflsbuf(f,-1);
	}

	return s-begs;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Iffile'
then
	echo shar: will not over-write existing file "'./Iffile'"
else
cat << \SHAR_EOF > './Iffile'
:: This file defines probes for local features that sfio requires.
:: Such probes are interpreted by the "iffe" language interpreter.
:: Results are stored in the FEATURE directory.
:: Written by Kiem-Phong Vo (06/27/92).

:: See if there is a local atexit function
:group:atexit
:lib:atexit
:lib:onexit
:lib:on_exit
:endgroup:atexit

:: various string functions that would be handy if available
:group:string
:lib:bcopy
:lib:bzero
:lib:memccpy
:lib:memchr
:endgroup:string

:: memory mapping
:group:mmap
:hdr:mman
:sys:mman
:lib:mmap
:endgroup:mmap

:: vfork and any associated header files
:group:vfork
:hdr:vfork
:sys:vfork
:lib:vfork
:endgroup:vfork

:: file control stuff for Research UNIX
:group:filio
:hdr:filio
:sys:filio
:endgroup:filio

:: See if we need our own mktemp (Plan9 does not have one)
:group:mktemp
:lib:mktemp
:lib:remove
:lib:unlink
:endgroup:mktemp

:lib:waitpid

:lib:getpagesize

:long:double
main() { long double d = 0; return 0; }

:: See if the preferred block size for a file system can be found
:stat:blksize
#include	<sys/types.h>
#include	<sys/stat.h>
main() { struct stat sb; sb.st_blksize = 0; return 0; }

:: See if we should use some form of native code
:group:native

:: See if register layout is ok for vax string operations
:vax:asm.x:
main()
{
#ifndef vax
	return absurd = -1;
#else
	register int	r11, r10, r9, r8, r7, r6;
	r11 = r10 = r9 = r8 = r7 = r6 = -1;
	asm("clrw	r11");
	asm("clrw	r10");
	asm("clrw	r9");
	asm("clrw	r8");
	asm("clrw	r7");
	asm("clrw	r6");
	if(sizeof(int) != sizeof(char*) || r11 || r10 || r9 || r8 || r7 || r6 )
		return -1;
	return 0;
#endif
}

:: 386 native floating point conversions are better than what we can do in C
:i386:cvt
main()
{
#ifndef i386
	return absurd = -1;
#else
	ecvt();
	fcvt();
	strtod();
	return 0;
#endif
}

:endgroup:native

:: check to see if certain prototypes are required
:group:prototype
:proto:open
#include	<sys/types.h>
#include	<errno.h>
#include	<ctype.h>
#include	<fcntl.h>

#ifdef __cplusplus
extern "C" {
#endif
extern int open(const char*,int,...);
#ifdef __cplusplus
}
#endif
main()
{
	open("file",0);
}

:endgroup:prototype

:: for generating the stdio binary compatibility package
:group:stdio
:FILE:cnt
#include	<stdio.h>
main() { return (stdin->_cnt == 0) ? 0 : -1; }

:FILE:ptr
#include	<stdio.h>
main() { return (stdin->_ptr == 0) ? 0 : -1; }

:FILE:file
#include	<stdio.h>
main() { return (stdin->_file == 0) ? 0 : -1; }

:FILE:flag
#include	<stdio.h>
main() { return (stdin->_flag != 0) ? 0 : -1; }

:FILE:base
#include	<stdio.h>
main() { return sizeof(stdin->_base) == sizeof(char*) ? 0 : -1; }

:FILE:bufsiz.x
#include	<stdio.h>
main() { return sizeof(stdin->_bufsiz) == sizeof(char*) ? 0 : -1; }
:endgroup:stdio

:: Check for #pragma weak (SysVr4 name hiding scheme)
:group:pragma
:pragma:weak
main() { return _xyz(); }
#pragma weak	_xyz =	__xyz
#define		_xyz	__xyz
_xyz() { return 0; }
:endgroup:pragma

:: See if stdarg.h includes stdio.h (CC's stupid hack)
:CC:stdarg:CC -o $FILE.x $CCFLAGS $FILE $CCLIBS >/dev/null 2>&1
#include        <stdarg.h>
main()
{
#ifndef _IOERR
        _stdarg_bug = 0;
#endif
}

:: see if select() or poll() is available
:group:poll

:lib:poll
#include	<poll.h>
main()
{	struct pollfd	fd;
	fd.fd = 0;
	fd.events = POLLIN;
	fd.revents = 0;
	if(poll(&fd,1,0) < 0)
		return -1;
	else	return 0;
}

:lib:select
#include	<sys/types.h>
#include	<sys/time.h>
main()
{	struct timeval	tmb;
	fd_set		rd;
	FD_ZERO(&rd);
	FD_SET(0,&rd);
	tmb.tv_sec = 0;
	tmb.tv_usec = 0;
	if(select(1,&rd,(fd_set*)0,(fd_set*)0,&tmb) < 0)
		return -1;
	else	return 0;
}

:endgroup:poll

:: See if we can peek ahead in unseekable devices
:group:peek:
:stream:peek:
#include	<sys/types.h>
#include        <stropts.h>
main()
{	struct strpeek  pbuf;
	pbuf.flags = 0;
	pbuf.ctlbuf.maxlen = pbuf.databuf.maxlen =
	pbuf.ctlbuf.len = pbuf.databuf.len = 0;
	pbuf.ctlbuf.buf = pbuf.databuf.buf = (char*)0;
	ioctl(0,I_PEEK,&pbuf);
	return 0;
}
:socket:peek:
#include	<sys/types.h>
#include        <sys/socket.h>
main()
{	char	buf[128];
	recv(0,buf,sizeof(buf),MSG_PEEK);
	return 0;
}
:endgroup:peek:
SHAR_EOF
fi # end of overwriting check
if test -f './Makefile'
then
	echo shar: will not over-write existing file "'./Makefile'"
else
cat << \SHAR_EOF > './Makefile'
/*	libsfio.a nmake Makefile
**	Written by Kiem-Phong Vo (12/10/90)
*/

VERSION = 1.0
.SOURCE : Sfio_f Stdio_b Stdio_s
.INCLUDE : Stdio_b
ancestor = 2

CCFLAGS = -O

SFIO=	sfexit.c sfclose.c sfclrlock.c sfcvt.c sfdlen.c sfexcept.c sfextern.c \
	sffilbuf.c sfflsbuf.c sfprints.c sfgetd.c sfgetl.c sfgetr.c sfgetu.c \
	sfllen.c sfmode.c sfmove.c sfnew.c sfnputc.c sfopen.c sfpeek.c \
	sfpool.c sfpopen.c sfprintf.c sfputd.c sfputl.c sfputr.c sfputu.c \
	sfread.c sfscanf.c sfseek.c sfset.c sfsetbuf.c sfdisc.c \
	sfstack.c sfstrtod.c sfsync.c sftable.c sftell.c sftmp.c \
	sfungetc.c sfvprintf.c sfvscanf.c sfwrite.c sfnotify.c sfsetfd.c \
	sfrd.c sfwr.c sfsk.c sfsize.c sfpurge.c sfpoll.c sfpkrd.c

SFIO_F=	_sfclrerr.c _sfecvt.c _sfeof.c _sferror.c _sffcvt.c _sffileno.c \
	_sfgetc.c _sfgetl.c _sfgetu.c _sfputc.c _sfputd.c _sfputl.c _sfputu.c \
	_sfslen.c _sfulen.c _sfstacked.c

STDIO_S=stdgets.c stdprintf.c stdscanf.c stdvbuf.c stdsprnt.c \
	stdvsprnt.c stdvsscn.c stdopen.c

STDIO_B=fclose.c fdopen.c fflush.c fgetc.c fgets.c filbuf.c flsbuf.c \
	fopen.c fprintf.c fputc.c fputs.c fread.c freopen.c fscanf.c fseek.c \
	ftell.c fwrite.c getc.c getchar.c gets.c getw.c pclose.c popen.c printf.c \
	putc.c putchar.c puts.c putw.c rewind.c scanf.c setbuf.c setbuffer.c \
	setlinebuf.c setvbuf.c sprintf.c sscanf.c stdextern.c stdstream.c \
	tmpfile.c ungetc.c vfprintf.c vfscanf.c vprintf.c vscanf.c vsprintf.c \
	vsscanf.c doprnt.c doscan.c fileno.c feof.c ferror.c clearerr.c

sfio  $(VERSION) :LIBRARY:	$(SFIO) $(SFIO_F) $(STDIO_S)
stdio $(VERSION) :LIBRARY:	$(STDIO_B)

sfstdgen : sfstdio.c
	$(CC) -I. $(*) -o sfstdgen
sfstdhdr.h :
	Stdio_b/sfstdhdr.sh $(CC)
sfstdio.h :	sfstdgen
	$(*) > $(<)
	rm sfstdgen sfstdhdr.h 

$(INCLUDEDIR):INSTALLDIR:	sfio.h

$(INCLUDEDIR)/stdio.h : .DONTCARE
$(INCLUDEDIR):INSTALLDIR:	stdio.h
	test -f Stdio_b/sfstdhdr.h && cp $(*) $(<) || true
SHAR_EOF
fi # end of overwriting check
if test -f './makefile'
then
	echo shar: will not over-write existing file "'./makefile'"
else
cat << \SHAR_EOF > './makefile'
# makefile for sfio
#
# Written by Kiem-Phong Vo (4/24/91)

# Native compiler if building libstdio.a for a cross compilation environment
NC=	cc

# Compiler and flags to use
CXFLAGS=
AR=	ar
CC=	cc
CCFLAGS= -c -O -I. $(CXFLAGS)

# sfio
SRCS=	sfclose.c sfclrlock.c sfcvt.c sfdisc.c sfdlen.c sfexcept.c \
	sfextern.c sffilbuf.c sfflsbuf.c sfprints.c sfgetd.c sfgetl.c \
	sfgetr.c sfgetu.c sfllen.c sfmode.c sfmove.c sfnew.c \
	sfnotify.c sfnputc.c sfopen.c sfpeek.c sfpool.c sfpopen.c \
	sfprintf.c sfputd.c sfputl.c sfputr.c sfputu.c sfrd.c sfread.c \
	sfscanf.c sfseek.c sfset.c sfsetbuf.c sfsetfd.c sfsize.c sfsk.c \
	sfstack.c sfstrtod.c sfsync.c sftable.c sftell.c sftmp.c \
	sfungetc.c sfvprintf.c sfvscanf.c sfwr.c sfwrite.c sfexit.c \
	sfpurge.c sfpoll.c sfpkrd.c
OBJS=	sfclose.o sfclrlock.o sfcvt.o sfdisc.o sfdlen.o sfexcept.o \
	sfextern.o sffilbuf.o sfflsbuf.o sfprints.o sfgetd.o sfgetl.o \
	sfgetr.o sfgetu.o sfllen.o sfmode.o sfmove.o sfnew.o \
	sfnotify.o sfnputc.o sfopen.o sfpeek.o sfpool.o sfpopen.o \
	sfprintf.o sfputd.o sfputl.o sfputr.o sfputu.o sfrd.o sfread.o \
	sfscanf.o sfseek.o sfset.o sfsetbuf.o sfsetfd.o sfsize.o sfsk.o \
	sfstack.o sfstrtod.o sfsync.o sftable.o sftell.o sftmp.o \
	sfungetc.o sfvprintf.o sfvscanf.o sfwr.o sfwrite.o sfexit.o \
	sfpurge.o sfpoll.o sfpkrd.o

# function versions of macros
SFIO_F=	Sfio_f/_sfclrerr.o Sfio_f/_sfecvt.o Sfio_f/_sfeof.o \
	Sfio_f/_sferror.o Sfio_f/_sffcvt.o Sfio_f/_sffileno.o \
	Sfio_f/_sfgetc.o Sfio_f/_sfgetl.o Sfio_f/_sfgetu.o \
	Sfio_f/_sfputc.o Sfio_f/_sfputd.o Sfio_f/_sfputl.o \
	Sfio_f/_sfputu.o Sfio_f/_sfslen.o Sfio_f/_sfulen.o \
	Sfio_f/_sfstacked.o

# stdio-source compatibility code
STDIO_S=Stdio_s/stdgets.o Stdio_s/stdprintf.o Stdio_s/stdscanf.o \
	Stdio_s/stdvbuf.o Stdio_s/stdsprnt.o Stdio_s/stdvsprnt.o \
	Stdio_s/stdvsscn.o

.c.o:
	$(CC) $(CCFLAGS) $*.c

all:	libsfio.a libstdio.a

libsfio.a: FEATURES $(OBJS) sfio_f stdio_s
	$(AR) cr libsfio.a $(OBJS) $(SFIO_F) $(STDIO_S)
	-(ranlib libsfio.a; exit 0) >/dev/null 2>&1
FEATURES:
	#export CC; CC=$(CC); iffe
sfio_f:
	cd Sfio_f; make -f Makefile CC="$(CC)" CXFLAGS="$(CXFLAGS)"
stdio_s:
	cd Stdio_s; make -f Makefile CC="$(CC)" CXFLAGS="$(CXFLAGS)"

libstdio.a:
	cd Stdio_b; make -f Makefile NC="$(NC)" CC="$(CC)" CXFLAGS="$(CXFLAGS)"
	-(rm libstdio.a; ln Stdio_b/libstdio.a .; exit 0) >/dev/null 2>&1
SHAR_EOF
fi # end of overwriting check
if test -f './sfpoll.c'
then
	echo shar: will not over-write existing file "'./sfpoll.c'"
else
cat << \SHAR_EOF > './sfpoll.c'
#include	"sfhdr.h"

/*	Poll a set of streams to see if any is available for I/O.
**
**	Written by Kiem-Phong Vo (06/18/92).
*/

#include	"FEATURE/poll"
#define USE_POLL	1
#define	USE_SELECT	2

#if _lib_poll
#define use_method	USE_POLL
#include	<poll.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int poll _ARG_((struct pollfd*, int, int));
#ifdef __cplusplus
}
#endif
#endif

#ifndef use_method
#if _lib_select
#define use_method	USE_SELECT
#include	<sys/time.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int select _ARG_((int, fd_set*, fd_set*, fd_set*, struct timeval*));
#ifdef __cplusplus
}
#endif
#endif
#endif

#define SECOND	1000		/* a second in milliseconds */

#ifdef __STD_C
sfpoll(Sfile_t** fa, reg int n, int tm)
#else
sfpoll(fa, n, tm)
Sfile_t**	fa;	/* array of streams to select */
int		n;	/* number of streams in array */
int		tm;	/* the amount of time in ms to wait for selecting */
#endif
{
	reg int		r, c, m;
	reg Sfile_t*	f;

	if(n <= 0 || !fa)
		return -1;

	/* this loop partitions the streams into 3 sets: Ready, Check, Notready */
	r = c = 0;
	while(c < n)
	{	f = fa[c];

		/* this loop pops a stream stack as necessary */
		for(;;)
		{	/* check accessibility */
			m = f->mode&SF_RDWR;
			if(f->mode != m && _sfmode(f,m) < 0)
				goto not_ready;

			/* clearly ready */
			if(f->next < f->endb)
				goto ready;

			/* has discipline, ask its opinion */
			if(f->disc && f->disc->exceptf)
			{	if((m = (*f->disc->exceptf)(f,SF_DPOLL,f->disc)) < 0)
					goto not_ready;
				else if(m > 0)
					goto ready;
			}

			/* unseekable stream, must check for blockability */
			if(f->extent < 0)
				goto check;

			/* string/regular streams with no possibility of blocking */
			if(!f->push)
				goto ready;

			/* stacked regular file stream with I/O possibility */
			if(!(f->flags&SF_STRING) &&
			   (f->mode == SF_WRITE || f->here < f->extent) )
				goto ready;

			/* at an apparent eof, pop stack if ok, then recheck */
			SETLOCAL(f);
			(void)_sfexcept(f,f->mode&SF_RDWR,0,f->disc);
		}

		check:	/* local function to set a stream for further checking */
		{	c += 1;
			continue;
		}

		ready:	/* local function to set the ready streams */
		{	if(r < c)
			{	fa[c] = fa[r];
				fa[r] = f;
			}
			r += 1;
			c += 1;
			continue;
		}

		not_ready: /* local function to set the not-ready streams */
		{	if((n -= 1) > c)
			{	fa[c] = fa[n];
				fa[n] = f;
			}
			continue;
		}
	}

	if(r < c)
	{	/* everything in [r,c) must be checked the hard way */
#if use_method == USE_POLL
		static struct pollfd	*fds;
		static int		n_fds = 0;

		/* get space for the poll structures */
		if((n = c-r) > n_fds)
		{	if(n_fds > 0)
				free((char*)fds);
			if(!(fds = (struct pollfd*)malloc(n*sizeof(struct pollfd))) )
			{	n_fds = 0;
				return -1;
			}
			n_fds = n;
		}

		/* construct the poll array */
		for(m = 0; m < n; ++m)
		{	f = fa[r+m];
			fds[m].fd = f->file;
			fds[m].events = (f->mode&SF_READ) ? POLLIN : POLLOUT;
			fds[m].revents = 0;
		}

		if(poll(fds,n,tm) > 0)
		{	m = r;
			while(r < c)
			{	f = fa[r];
				if(((f->mode&SF_READ) && (fds[r-m].revents&POLLIN)) ||
				   ((f->mode&SF_WRITE) && (fds[r-m].revents&POLLOUT)) )
					r += 1;
				else if((c -= 1) > r)
				{	fa[r] = fa[c];
					fa[c] = f;
				}
			}
		}
#endif /* USE_POLL */

#if use_method == USE_SELECT
		fd_set		rd, wr;
		struct timeval	tmb, *tmp;

		FD_ZERO(&rd);
		FD_ZERO(&wr);
		m = 0;
		for(n = r; n < c; ++n)
		{	f = fa[n];
			if(f->file > m)
				m = f->file;
			if(f->mode&SF_READ)
				FD_SET(f->file,&rd);
			else	FD_SET(f->file,&wr);
		}
		if(tm < 0)
			tmp = NIL(struct timeval*);
		else
		{	tmp = &tmb;
			tmb.tv_sec = tm/SECOND;
			tmb.tv_usec = (tm%SECOND)*SECOND;
		}
		if(select(m+1,&rd,&wr,NIL(fd_set*),tmp) > 0)
		{	while(r < c)
			{	f = fa[r];
				if(((f->mode&SF_READ) && FD_ISSET(f->file,&rd)) ||
				   ((f->mode&SF_WRITE) && FD_ISSET(f->file,&wr)) )
					r += 1;
				else if((c -= 1) > r)
				{	fa[r] = fa[c];
					fa[c] = f;
				}
			}
		}
#endif /* USE_SELECT */
	}

	return r;
}
SHAR_EOF
fi # end of overwriting check
if test -f './Doc/sfio.3'
then
	echo shar: will not over-write existing file "'./Doc/sfio.3'"
else
cat << \SHAR_EOF > './Doc/sfio.3'
.TH SFIO 3 "13 February 1992"
.PP
\fBNAME\fP
.PP
\fBsfio\fR \- safe/fast string/file input/output
.PP
\fBSYNOPSIS\fP
.ta 1.0i 2.0i 3.0i 4.0i 5.0i 6.0i
.PP
.nf
.ft 5
#include	<sfio.h>

#define ulong	unsigned long

Sfile_t*	sfnew(Sfile_t* f, char* buf, int size, int fd, int flags);
Sfile_t*	sfopen(Sfile_t* f, const char* string, const char* mode);
Sfile_t*	sfpopen(Sfile_t* f, const char* cmd, const char* mode);
Sfile_t*	sftmp(int size);

Sfile_t*	sfstack(Sfile_t* base, Sfile_t* top);
Sfile_t*	sfpool(Sfile_t* f, Sfile_t* poolf, int mode);

Sfdisc_t*	sfdisc(Sfile_t* f, Sfdisc_t* disc);
int	sfrd(Sfile_t* f, char* buf, int n, Sfdisc_t* disc);
int	sfwr(Sfile_t* f, char* buf, int n, Sfdisc_t* disc);
long	sfsk(Sfile_t* f, long addr, int offset, Sfdisc_t* disc);

int	sfclose(Sfile_t* f);
int	sfsync(Sfile_t* f);
int	sfpurge(Sfile_t* f);

int	sfpeek(Sfile_t* f, char** bufp, int n);
int	sfpoll(Sfile_t** fa, int n, int timeout); 

int	sfgetc(Sfile_t* f);
int	sfungetc(Sfile_t* f, int c);
ulong	sfgetu(Sfile_t* f);
long	sfgetl(Sfile_t* f);
double	sfgetd(Sfile_t* f);
char*	sfgetr(Sfile_t* f, int rsc, int string);
int	sfread(Sfile_t* f, char* buf, int n);
int	sfscanf(Sfile_t* f, const char* format, ...);
int	sfsscanf(const char* s, const char* format, ...);
int	sfvscanf(Sfile_t* f, const char* format, va_list args);

int	sfputc(Sfile_t* f, int c);
int	sfnputc(Sfile_t* f, int c, int n);
int	sfputu(Sfile_t* f, ulong v);
int	sfputl(Sfile_t* f, long v);
int	sfputd(Sfile_t* f, double v);
int	sfputr(Sfile_t* f, const char* s, int c);
int	sfwrite(Sfile_t* f, const char* buf, int n);
int	sfmove(Sfile_t* fr, Sfile_t* fw, long n, int rsc);
int	sfprintf(Sfile_t* f, const char* format, ...);
int	sfsprintf(char* s, int size, const char* format, ...);
char*	sfprints(const char* format, ...);
int	sfvprintf(Sfile_t* f, const char* format, va_list args);

void	sfnotify(void (*notify)(Sfile_t* f, int type, int fd));
int	sfsetfd(Sfile_t* f, int fd);
int	sfset(Sfile_t* f, int flags, int i);
char*	sfsetbuf(Sfile_t* f, char* buf, int size);
int	sffileno(Sfile_t* f);
int	sfstacked(Sfile_t* f);
int	sfeof(Sfile_t* f);
int	sferror(Sfile_t* f);
int	sfclrerr(Sfile_t* f);
int	sfclrlock(Sfile_t* f);

int	sfslen();
int	sfulen(ulong v);
int	sfllen(long v);
int	sfdlen(double v);

long	sfsize(Sfile_t* f);
long	sfseek(Sfile_t* f, long addr, int offset);
long	sftell(Sfile_t* f);

char*	sfecvt(double v, int n, int* decpt, int* sign);
char*	sffcvt(double v, int n, int* decpt, int* sign);
.fR
.fi
.PP
\fBDESCRIPTION\fP
.PP
\fISfio\fP is a library of functions for buffered I/O.
I/O operations are done on objects called streams,
each of which is of the type \f5Sfile_t\fP 
and corresponds to either a file descriptor (see \fIopen(2)\fP)
or some segmenT of memory.
Stream complexes can be built by stacking streams on one another.
Stream pooling handles
automatic stream synchronization when I/O operations are switched among related streams.
I/O disciplines (alternative I/O system calls) can be inserted into streams
to process data.
.PP
During an I/O request,
a system call or its discipline replacement may be invoked to fill the stream buffer.
Such a function is said to cause an exception if its return value is non-positive.
Unless an exception handler has been defined
(see \f5sfdisc()\fP), \fISfio\fP will examine \f5errno\fP (see \f5errno.h\fP)
for further actions. If \f5errno\fP is \f5EINTR\fP, the respective
function will be resumed. Otherwise, an error condition is returned.
.PP
A stream is normally locked during any I/O operation to prevent concurrent accesses.
Any attempt to access a locked stream results in error.
\fISfio\fP functions return either integer or pointer values.
Generally, in the event of an error, a function that returns integer values will
return \f5-1\fP while a function that returns a pointer value will return \f5NULL\fP.
More details on return values will be given for each function as appropriate.
.PP
A number of bit flags define stream types and their operations.
Following are the flags:
.IP
\f5SF_READ\fP:
The stream is readable.
.IP
\f5SF_WRITE\fP:
The stream is writable.
.IP
\f5SF_STRING\fP:
The stream is a string (a byte array) that
is readable if \f5SF_READ\fP is specified or
writable if \f5SF_WRITE\fP is specified.
.IP
\f5SF_APPEND\fP:
The stream is a file opened for appending data.
This means that data written to the stream is always
appended at the end of the file.
On systems where there is no primitive to specify
at file opening time that a file is opened for append only,
\f5lseek()\fP (or its discipline replacement) will be used on
the file stream to approximate this behavior.
.IP
\f5SF_LINE\fP:
The stream is line-oriented. For write streams, this means that the
buffer is flushed whenever a new-line character is output.
For read streams, this means that \f5sfscanf()\fP will specially treat the
matching of \en (see below).
\f5SF_LINE\fP is automatically set for any unseekable stream
that is not a FIFO device (e.g., pipes).
.IP
\f5SF_MALLOC\fP:
The buffer was obtained via \f5malloc()\fP
and can be reallocated or freed by \fISfio\fP.
.IP
\f5SF_SHARE\fP:
The associated stream is a file stream that may
be manipulated by different independent entities, for example,
a stream shared by multiple processes.
In this case, if the stream is seekable, each read or write system call will be
preceded by a \f5lseek()\fP (or its discipline replacement) to ensure that
the logical stream location corresponds to the physical file location.
If the stream is unseekable (e.g., pipes or ttys),
the block and record I/O operations (\f5sfread()\fP, \f5sfwrite()\fP, \f5sfmove()\fP,
\f5sfgetr()\fP, \f5sfputr()\fP, \f5sfpeek()\fP, and \f5sfvprintf()\fP)
will ensure that (1) after each write operation, the stream is synchronized and
(2) for each read operation, only the data requested will be read.
\f5SF_SHARE\fP is turned on automatically for the standard streams \f5sfstdin\fP,
\f5sfstdout\fP, and \f5sfstderr\fP if they are unseekable.
.PP
\f5sfnew(f,buf,size,fd,flags)\fP
is the primitive for creating or renewing streams.
\f5f\fP, if not \f5NULL\fP, is a stream to be reopened.
Except for \f5sfstdin\fP, \f5sfstdout\fP, and \f5sfstderr\fP,
the stream \f5f\fP should not have been closed.
If \f5f\fP is still open, it will be closed
before renewing but its buffer, pool and discipline stack remain unchanged.
If \f5f\fP is \f5NULL\fP,  a new stream is created.
\f5buf\fP and \f5size\fP determines a buffering scheme.
See \f5sfsetbuf()\fP for more details.
\f5fd\fP is a file descriptor (e.g., from \fIopen()\fP)
to use for I/O operations if the stream is not an \f5SF_STRING\fP stream.
\f5flags\fP is a bit vector composing from the bit flags described above.
On error, \f5sfnew()\fP returns \f5NULL\fP. Otherwise, it returns the new stream.
.PP
\f5sfopen(f,string,mode)\fP
is a high-level function based on \f5sfnew()\fP to create new streams from files
or strings.
\f5f\fP is treated in the same way as \f5sfnew()\fP treats its counterpart.
\f5mode\fP can be any one of: \f5"r"\fP, \f5"r+"\fP,
\f5"w"\fP, \f5"w+"\fP, \f5"a"\fP, \f5"a+"\fP, \f5"s"\fP and \f5"s+"\fP.
The \f5`r'\fP, \f5`w'\fP, and \f5`a'\fP specify
read, write and append mode for file streams.
In these cases, \f5string\fP defines a path name to a file.
The \f5`s'\fP specifies that
\f5string\fP is a string to be opened for read.
The \f5`+'\fP means that the new stream will be opened for both reading and writing.
On error, \f5sfopen()\fP returns \f5NULL\fP. Otherwise, it returns the new stream.
.PP
\f5sfpopen(f,cmd,mode)\fP
executes the command \f5cmd\fP and sets up pipes so that data can be communicated
between the current process and the child process \f5cmd\fP.
If the stream is open for read/write, then the standard input/output
(file descriptor 0/1) of the child is connected to the current process.
It is possible to open for both read and write.
In this case, since there are two different file descriptors associated
with the stream, beware that \f5sffileno()\fP may return different values
depending on the type of the last I/O operation done (see also \f5sfset()\fP).
In addition, when the stream is switched from read to write, unread data
will be saved in some system-defined area. Care should be taken so that
the stream buffer is not reduced in size (e.g., via \f5sfsetbuf\fP)
before switching back to read mode or else the saved data may be truncated.
If \f5sfpopen()\fP opens a stream for writing and
the signal handler for \f5SIGPIPE\fP is \f5SIG_DFL\fP (default handling),
it will be set to \f5SIG_IGN\fP. This protects the application from getting killed
on attempts to write to broken pipes.
On error, \f5sfpopen()\fP returns \f5NULL\fP. Otherwise, it returns the new stream.
.PP
\f5sftmp(size)\fP creates a stream for writing and reading temporary data.
If \f5size\fP is negative, the stream is a pure \f5SF_STRING\fP stream.
If \f5size\fP is zero, a stream is a pure file stream.
Otherwise, the stream is first created as a \f5SF_STRING\fP stream
with a buffer of length \f5size\fP. A discipline is set so that
when this buffer is exhausted, a real temporary file is created.
The temporary file is also created on any attempt to change this discipline.
On error, \f5sftmp()\fP returns \f5NULL\fP. Otherwise, it returns the new stream.
.PP
\f5sfstack(base,top)\fP is used to push or pop stream stacks.
Each stream stack is identified by a \f5base\fP stream
via which all I/O operations are performed.
Other streams on the stack are locked so that operations that may change
their internal states are forbidden.
The type of operations that can be done on a stack is defined by
the top level stream. If an I/O operation is performed and the top level stream
reaches the end of file condition or an error condition other than interrupts,
it is automatically popped and closed (see also \f5sfdisc()\fP for alternative
handling of these conditions).
If \f5top\fP is \f5SF_POPSTACK\fP, the stack is popped  and the pointer to
the popped stream is returned.
Otherwise, \f5top\fP is the pointer to a stream to be pushed on top of the stack.
In this case, the \f5base\fP stream pointer is returned.
.PP
\f5sfpool(f,poolf,mode)\fP manages pools of streams.
In a pool of streams, only one stream is current.
A stream becomes current when it is used for some I/O operation.
\f5f\fP is the stream to insert or delete from a pool.
\f5poolf\fP determines the operation to be done on \f5f\fP.
If \f5poolf\fP is \f5NULL\fP, \f5f\fP is deleted from its current pool.
Otherwise, \f5f\fP is put into the same pool with \f5poolf\fP.
If \f5poolf\fP is already in a pool, \f5mode\fP is ignored.
Otherwise, \f5mode\fP determines the type of the new pool.
\f5mode\fP should be \f5SF_SHARE\fP or some bitwise combination
of \f5SF_READ\fP and  \f5SF_WRITE\fP.
If \f5SF_SHARE\fP, all streams in the pool must be write streams.
and they will behave as if they are duplicates.
When a new stream is to become current,
data will be moved from the current stream to the new stream as necessary.
Thus, at any given time, only the current stream has any buffered data,
the other streams are all empty. When a stream is deleted from its pool
(e.g., upon closing), any buffered data is moved to some other stream in the pool.
The \f5SF_SHARE\fP mode is useful to serialize buffered data among streams that
direct to the same io object, for example, \f5sfstdout\fP and \f5sfstderr\fP which
normally share the terminal device.
If not \f5SF_SHARE\fP and
if the mode of the current stream matches the pool mode (from \f5SF_READ|SF_WRITE\fP),
it will be synchronized (see \f5sfsync()\fP) before the new stream
becomes current.
On failure, \f5sfpool()\fP returns \f5NULL\fP.
On success, if \f5poolf\fP is \f5NULL\fP, \f5sfpool()\fP returns a stream from
the previous pool of \f5f\fP; otherwise, it returns \f5poolf\fP.
.PP
\f5sfdisc(f,disc)\fP manipulates the discipline stack
of the stream \f5f\fP.
The stream is synchronized before the discipline stack is changed.
After the discipline stack has been successfully manipulated,
the current seek location (see \f5sftell()\fP)
and stream extent (see \f5sfsize()\fP) may be updated
to reflect that as defined by the current discipline.
.PP
If \f5disc\fP is \f5SF_POPDISC\fP, the
top element of the stack, if any, is popped.
Otherwise, \f5disc\fP is taken as the address
of a new discipline to be pushed onto the discipline stack.
Note that the memory space of this discipline is used on a per stream basis.
An application should take care to allocate different space for different
use of a discipline.
If \f5sfdisc()\fP is successful, it returns the
address of the new discipline for the pushing case, or the address of
the popped discipline in the other case.
\f5sfdisc()\fP returns \f5NULL\fP on failure.
.PP
\f5disc\fP is a pointer to a discipline structure of type \f5Sfdisc_t\fP
which defines alternative functions for
read, write, seek, and to handle exceptions.
Each file stream starts with the unremovable discipline
that consists of the system calls \f5read()\fP, \f5write()\fP, and \f5lseek()\fP.
\f5Sfdisc_t\fP contains the following public fields:
.nf
      \f5int   (*readf)();\fP
      \f5int   (*writef)();\fP
      \f5long  (*seekf)();\fP
      \f5int   (*exceptf)();\fP
.fi
.PP
The first three fields of \f5Sfdisc_t\fP specify the alternative I/O functions.
If any of them is \f5NULL\fP, it is inherited
from a discipline earlier on the stack.
The arguments to the I/O discipline functions
have the same meaning as that of the
functions \f5sfrd()\fP, \f5sfwr()\fP and \f5sfsk()\fP to be described later.
.PP
The exception function, \f5(*exceptf)()\fP, if provided, is used
to process exceptions. It is called as: \f5(*exceptf)(f,type,disc)\fP.
Here, \f5disc\fP is the discipline that defines the exception and
\f5type\fP is the exception.
The return value of \f5(*exceptf)()\fP is examined for further actions.
A negative value causes the calling function to return with an appropriate error value.
A positive value indicates that the exception is to be ignored and the calling
function will repeat the respective  I/O operation.
A zero value causes the calling function to execute certain default actions
with respect to the exception. For example, if the stream is a stacked stream,
and the exception is a \f5SF_READ\fP or \f5SF_WRITE\fP, the stream stack is popped
and the popped stream is closed.
Note that a \f5SF_STRING\fP stream does not perform external I/O so the
I/O discipline functions are never used. However, an exception occurs whenever
an I/O operation exceeds the stream buffer boundary and
\f5(*exceptf)()\fP, if defined, will be called as appropriate.
.PP
Following are the available values for \f5type\fP:
.IP
\f5SF_READ\fP: a read operation returns a zero or negative value.
.IP
\f5SF_WRITE\fP: a write operation returns a zero or negative value.
.IP
\f5SF_SEEK\fP: a seek operation returns a negative value.
.IP
\f5SF_NEW\fP: the stream is being renewed (see \f5sfnew()\fP).
.IP
\f5SF_CLOSE\fP: the stream is being closed (see \f5sfclose()\fP).
.IP
\f5SF_DPUSH\fP: the discipline is about to be pushed down by a new discipline.
.IP
\f5SF_DPOP\fP: the discipline is about to be popped from the discipline stack.
.IP
\f5SF_DPOLL\fP: \f5sfpoll()\fP is checking if the next I/O operation may block.
In this case, a negative return value means the stream will block,
a positive return value means the stream will not block.
A zero return value means that the file descriptor will be treated with
default actions.
.PP
\f5sfrd(f,buf,n,disc)\fP, \f5sfwr(f,buf,n,disc)\fP, and \f5sfsk(f,addr,offset,disc)\fP
are functions to be used within a discipline function to perform I/O.
The discipline stack can be viewed as a set of filters to process incoming data.
These functions provides a safe method to use the processing power
of earlier discipline functions and to properly handle exceptions.
\f5sfrd()\fP and \f5sfwr()\fP return the number of bytes read or written.
\f5sfsk()\fP returns the new seek location. On errors, all three functions
return a negative value (\f5-1\fP or the value returned by the exception handler).
.PP
\f5sfclose(f)\fP closes the given stream \f5f\fP and frees up its resources.
If \f5f\fP is a stack of streams, all streams on the stack are closed.
If \f5f\fP is a \f5sfpopen\fP stream, its companion stream, if any, is also closed.
In this case, \f5sfclose()\fP will wait until the associated command terminates,
then return its exit status.
\f5SF_WRITE\fP streams and \f5SF_READ\fP streams with the \f5SF_SHARE\fP
flag turned on are synchronized (see \f5sfsync()\fP).
In addition, if the stream has a non-trivial discipline stack, the exception
functions of the disciplines will be called
with the \f5type\fP argument being \f5SF_CLOSE\fP (see \f5sfdisc()\fP).
Space allocated for disciplines can be released in this phase.
\f5sfclose()\fP returns \f5-1\fP for failure and \f50\fP for success.
.PP
\f5sfsync(f)\fP synchronizes the physical file pointer of
\f5f\fP and the logical position in the stream.
For a write stream, this means to write out any buffered data.
For a read stream, if the stream is seekable,
the physical file pointer is moved to align with the stream pointer.
In addition, if the flag \f5SF_SHARE\fP is on, the buffer will be refreshed
with data from the underlying file.
If \f5f\fP is the base of a stack of streams, all streams on the stack
are synchronized. Further, a stacked stream can only be synchronized
via its base stream.
\f5sfsync()\fP returns \f5-1\fP for failure and \f50\fP for success.
.PP
\f5sfpurge(f)\fP throws away all pending data in the buffer of \f5f\fP
if \f5f\fP is not a \f5SF_STRING\fP stream.
Note that if \f5f\fP is an input stream corresponding to an unseekable device
such as a pipe or a terminal, such purged data will not be recoverable.
Finally, if \f5f\fP is a \f5sfpopen\fP-stream opened for both read and write,
data buffers for both the read and write pipe ends will be purged.
\f5sfpurge()\fP returns \f5-1\fP for failure and \f50\fP for success.
.PP
\f5sfpeek(f,bufp,n)\fP provides a safe and efficient method for accessing
the internal buffer of a stream.
Normally, access to a locked stream is forbidden. An exception is
made for the two cases when \f5bufp\fP is \f5NULL\fP or when \f5size\fP is \f50\fP and
there is still available buffer area.
If \f5bufp\fP is \f5NULL\fP, only the size of the available
buffer area is returned.
If \f5size\fP is \f50\fP, the address of the available buffer
area is returned in \f5*bufp\fP.
In both of these cases, the stream remains untouched.
Otherwise, if there is no currently available space or data in the buffer,
the stream will be flushed or filled as appropriate.
Then, \f5*bufp\fP is set to point to the available buffer area.
For streams that are opened for both read and write,
the stream will be put into read mode so that the pointer returned
in \f5*bufp\fP will point to meaningful data.
If \f5n\fP is non-negative,
\f5sfpeek()\fP will try to get buffer area as close to \f5n\fP as possible
subject to buffer or memory map size limits.
In addition, \f5sfpeek()\fP will advance the buffer pointer by
an amount that is the smaller of \f5n\fP and the available buffer areas,
the stream will not be locked, and the return value of \f5sfpeek()\fP
indicates the amount of buffer advance.
On the other hand,
if \f5n\fP is negative, \f5sfpeek()\fP returns the size of the available
buffer area and locks the stream.
For read streams, the lock can be released by a subsequent call to
\f5sfread()\fP using \f5*bufp\fP.
Similarly, write streams can be unlocked with \f5sfwrite()\fP
and read/write streams can be unlocked with either call.
.PP
\f5sfpoll(fa,n,timeout)\fP polls a set of streams to see if I/O operations
can be performed on them without blocking. This is useful for multiplexing
I/O over a set of streams.
The array \f5fa\fP contains the streams to be polled.
\f5n\fP is the number of streams in \f5fa\fP.
\f5timeout\fP defines length of time in milliseconds for \f5sfpoll()\fP
to wait to see if any stream is ready for I/O.
If \f5timeout\fP is negative, \f5sfpoll()\fP will block as long as necessary.
Note that \f5SF_STRING\fP and regular file streams can never block
so they are always ready for I/O.
If an unseekable stream with discipline is polled and its readiness is undetermined,
the discipline exception function will be called with \f5SF_DPOLL\fP (see \f5sfdisc()\fP).
Upon completion, \f5sfpoll()\fP
returns the number \f5r\fP of streams ready for I/O.
The array \f5fa\fP will be modified in place so that its first \f5r\fP streams
are the ready ones.
\f5sfpoll()\fP returns \f5-1\fP upon error.
.PP
\f5sfgetc(f)\fP returns a byte from the stream \f5f\fP or \f5-1\fP on end-of-file
or error.
.PP
\f5sfungetc(f,c)\fP pushes \f5c\fP back into the stream \f5f\fP
and makes it available on the next read.
For efficiency, if \f5c\fP matches the byte
immediately before the current read pointer in the buffer,
the buffer pointer is simply backed up (note the effect on \f5sftell()\fP and
\f5sfseek()\fP). There is no theoretical limit on the number of bytes that
can be pushed back into a stream. Pushed back bytes that were not part of
the buffer as noted will be discarded on any operation that require
buffer synchronization, e.g., \f5sftell()\fP, \f5sfseek()\fP, and \f5sfsync()\fP.
\f5sfungetc()\fP returns \f5-1\fP on failure and \f5c\fP on success.
.PP
\f5sfgetu(f)\fP, \f5sfgetl(f)\fP, and \f5sfgetd(f)\fP return
an \fIunsigned long\fP, \fIlong\fP , or \fIdouble\fP value
that was coded by \f5sfputu()\fP, \f5sfputl()\fP, and \f5sfputd()\fP.
If there is not enough data to decode a value,
these functions will return \f5-1\fP and the stream is set in an error state
(\f5see \f5sferror()\fP).
.PP
\f5sfgetr(f,rsc,string)\fP reads data from \f5f\fP up to
the record separator character \f5rsc\fP and returns a pointer
to the buffer containing the read data.
On errors or end of file without finding the record separator,
\f5sfgetr()\fP returns \f5NULL\fP. Note that, in such a case,
data may already have been read.
Such data can be retrieved by
calling \f5sfgetr()\fP again with the \f5string\fP argument being negative.
The behavior of \f5sfgetr()\fP when \f5string\fP is negative is undefined otherwise.
If \f5string\fP is positive, a nul byte will replace the record separator
to make it a C string.
The record length including the record separator (or its nul replacement)
can be retrieved with \f5sfslen()\fP.
.PP
\f5sfread(f,buf,n)\fP reads up to \f5n\fP bytes from the stream \f5f\fP and
stores them in the given buffer \f5buf\fP.
It returns the number of bytes actually read or \f5-1\fP on error.
.PP
\f5sfscanf(f,format,...)\fP scans a number of items from the stream \f5f\fP.
The item types are determined from the string \f5format\fP.
See \fIfscanf()\fP (UNIX User's Manual, Section 3) for details on predefined formats.
\f5sfscanf()\fP returns the number of items successfully scanned or \f5-1\fP
on error.
.PP
If the input stream is in \f5SF_LINE\fP mode, a \f5\en\fP character in the
format string will not be treated as a white space matcher. Therefore, it will
only match itself. This allows an application to avoid unnecessary blocking
when \f5sfscanf()\fP is used to scan values from terminals or other unseekable devices
with patterns such as ``\f5%s\en\fP''.
Note that other white space characters in the
format strings, i.e. blanks or tabs, still match any input \f5\en\fP.
.PP
The standardly supported formats are:
\f5i, d, u, o, x, p, n, f, F, e, E, g, G, c, %, s,\fP and \f5[]\fP.
The \f5i\fP and \f5d\fP format are extended to scan numbers with general bases
(from \f52\fP to \f564\fP).
The \f5d\fP format can be specified as \f5%[*][width][.base]d\fP where
\f5base\fP is the base for number to be scanned.
For the \f5i\fP format, numbers with general bases are of the form: \f5base#integer\fP.
Here \f5base\fP must be a number in base 10 and \f5integer\fP
is a number in the given base. If \f5base\fP is \f536\fP or less,
the digits for \f5integer\fP can be any combination of \f5[0-9], [a-z], [A-Z]\fP
where upper and lower case digits are not distinguishable.
If \f5base\fP is larger than \f536\fP, the set of digits is:
.nf
      \f50123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@_\fP
.fi
.PP
For robustness,
the formats \f5lc\fP, \f5ls\fP, and \f5l[]\fP
are supported.
These are the same as \f5c\fP, \f5s\fP, and \f5[]\fP but in the
argument list to \f5sfscanf()\fP, a buffer and its size must be defined in a pair.
Note that specifying a buffer size does not affect the scan length.
If the buffer size is \f5n\fP, only the first \f5n-1\fP byte of the scanned data
will be copied, the rest is discarded.
.PP
\f5sfscanf()\fP supports a few extension formats as described below.
.IP
The pattern \f5%&\fP indicates that the next argument in the argument list of
\f5sfscanf()\fP is a function, say \f5(*extf)()\fP, to process patterns that are not
predefined by the \f5sfscanf()\fP interface.
The prototype of \f5(*extf)()\fP is:
.nf
      \f5int (*extf)(Sfile_t* f, int fmt, int length, char** rv);\fP
.fi
\f5f\fP is the same input stream passed to \f5sfvscanf\fP.
\f5fmt\fP is the pattern to be processed.
\f5length\fP, if non-negative, is the maximum number of input bytes
to be read in processing the pattern,
\f5rv\fP is used to return the address of the value to be assigned.
\f5(*extf)()\fP returns the size of the value to be assigned.
A negative return value from \f5(*extf)()\fP means that the specified pattern
cannot be handled. This pattern is treated as if it is not matched.
.IP
The pattern \f5%@\fP indicates that the next argument in the argument list \f5args\fP
is a function, say \f5(*argf)()\fP, to process the values of matched patterns.
The prototype of \f5(*argf)()\fP is:
.nf
      \f5int (*argf)(int fmt, char* value, int n)\fP;
.fi
If the return value of \f5(*argf)()\fP is negative, the processing
of the current format string will be stopped (see \f5%$\fP below).
\f5fmt\fP determines the type of \f5value\fP: \f5f\fP for \fIfloat\fP,
\f5F\fP for \fIdouble\fP, \f5h\fP for \fIshort\fP, \f5d\fP for \fIint\fP,
\f5D\fP for \fIlong\fP, \f5s\fP for \fIchar*\fP. Any other value for \f5fmt\fP
means that it is an extended pattern and \f5value\fP contains an address
to the scanned value. \f5n\fP contains the size of the object if it is a
primitive type. If the object is \f5char*\fP or the address of the scanned
value of an extended format, \f5n\fP is the length of this object.
.IP
The pattern \f5%:\fP indicates that the next two arguments in the argument list
\f5args\fP define a new pair of format string and a list of arguments of
the type \f5va_list\fP (see \f5varargs.h\fP or \f5stdarg.h\fP).
The new pair is pushed on top of the stack and the scanning process continues with them.
The top pair of format string and argument list is popped when the processing
of the format string is stopped. When a new pair is stacked,
\f5(*argf)()\fP and \f5(*extf)()\fP are inherited.
They are reset when the stack is popped.
.PP
\f5sfsscanf(s,format,...)\fP is similar to \f5sfscanf()\fP
but it scans data from the string \f5s\fP.
.PP
\f5sfvscanf(f,format,args)\fP is the primitive underlying \f5sfscanf()\fP
and \f5sfscanf()\fP. It provides a portable variable argument interface.
.PP
\f5sfputc(f,c)\fP writes the byte \f5c\fP to the stream \f5f\fP.
It returns \f5c\fP on success and \f5-1\fP on error.
.PP
\f5sfnputc(f,c,n)\fP writes \f5c\fP to \f5f\fP \f5n\fP times.
It returns the number of bytes actually written or \f5-1\fP on failure.
.PP
\f5sfputu(f,v)\fP, \f5sfputl(f,v)\fP write the \fIunsigned long\fP or \fIlong\fP
value \f5v\fP in a format that is byte-order transparent.
\f5sfputd(f,v)\fP writes the \fIdouble\fP value \f5v\fP in a portable format.
Portability across two different machines
requires that the bit order in a byte is the same on both machines and
the size of the primitive type on the output machine is less than or equal
to that on the input machine.
\f5sfputd()\fP relies on the functions \f5ldexp()\fP and \f5frexp()\fP
(See \fIfrexp.3\fP) for coding.
Upon success, \f5sfputu()\fP, \f5sfputl()\fP and \f5sfputd()\fP
return the number of bytes output.
Otherwise, they return \f5-1\fP.
.PP
\f5sfputr(f,s,rsc)\fP writes the nul-terminated string
\f5s\fP to the stream \f5f\fP.
If \f5rsc\fP is non-negative,
it is a character to be appended after the string has been output.
\f5sfputr()\fP returns the number of bytes written or \f5-1\fP on failure.
.PP
\f5sfwrite(f,buf,n)\fP writes out \f5n\fP bytes from the buffer \f5buf\fP to the
stream \f5f\fP. It returns the number of bytes written or \f5-1\fP on failure.
.PP
\f5sfmove(fr,fw,n,rsc)\fP moves \f5n\fP objects
from the stream \f5fr\fP to the stream \f5fw\fP.
If either \f5fr\fP or \f5fw\fP is \f5NULL\fP, it acts
as if it is a stream corresponding to \fI/dev/null\fP.
If \f5n\fP is \f5<0\fP, all of \f5fr\fP is moved.
If \f5rsc\fP is non-negative, it defines a record separator.
In this case, the objects to be moved are records separated by \f5rsc\fP.
If \f5rsc\fP is negative, the objects are bytes.
\f5sfmove()\fP returns the number of objects moved or \f5-1\fP on failure.
.PP
\f5sfprintf(f,format,...)\fP writes out data in
a format as defined by the string \f5format\fP.
See \f5fprintf()\fP for details on the predefined formats:
\f5n, s, c, %, h, i, d, p, u, o, x, X, g, G, e, E, f,\fP and \f5F\fP.
\f5sfprintf()\fP supports additional formats as described below.
.IP
The formats \f5i\fP, \f5d\fP, and \f5u\fP are extended
to print integers in non-standard bases.
The general form for the \f5d\fP format is \f5%[width].[precision].[base]d\fP.
The forms for \f5i\fP and \f5u\fP are defined similarly.
For example, \f5%..2d\fP prints a signed integer in binary format and
\f5%..64u\fP prints an unsigned integer in base \f564\fP.
Currently, \f5base\fP can range from \f52\fP to \f564\fP.
If \f5base\fP is not defined or if it is
not within the defined range, it is taken to be \f510\fP.
The modifier \f5#\fP will output the number in the form \f5base#number\fP.
The digits to represent numbers are:
.nf
      \f501234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@_\fP
.fi
.IP
The pattern \f5%&\fP indicates that the next argument
is a function, say \f5(*extf)()\fP, to interpret patterns not yet defined
by \f5sfprintf()\fP.
The prototype of \f5(*extf)()\fP is:
.nf
      \f5int (*extf)(void* value, int fmt, int precis, char** sp);\fP
.fi
\f5value\fP is the value to be formatted.
\f5fmt\fP is the pattern to format the value.
\f5precis\fP is the amount of precision required.
\f5sp\fP is used to return the address of a string containing the formatted value.
If upon returning from \f5(*extf)()\fP, \f5*sp\fP is \f5NULL\fP, the pattern \f5fmt\fP
is treated as if it is not matched.
Otherwise, the return value of \f5(*extf)()\fP, if nonnegative, is taken as the length
of the string returned in \f5sp\fP. If not, the string is considered \f50\fP terminated.
The string \f5*sp\fP is processed as if the pattern \f5`s'\fP was specified.
.IP
The pattern \f5%@\fP indicates that the next argument is a function, say \f5(*argf)()\fP,
to get arguments. As long as \f5(*argf)()\fP is defined, the argument list is ignored.
The prototype of \f5(*argf)()\fP is:
.nf
      \f5int (*argf)(int fmt, char* val)\fP;
.fi
\f5fmt\fP is the pattern to be processed.
Following are ASCII characters and corresponding types:
\f5@\fP for getting a new \f5(*argf)()\fP,
\f5&\fP for getting a new \f5(*extf)()\fP,
\f51\fP for getting a new format string for stacking,
\f52\fP for getting a new argument list for stacking,
\f5d\fP for \fIint\fP,
\f5D\fP for \fIlong\fP,
\f5f\fP for \fIfloat\fP,
\f5F\fP for \f5double\fP, and
\f5s\fP for \fIchar*\fP.
If \f5(*extf)()\fP is defined, and an undefined pattern is encountered,
\f5(*argf)()\fP will be called with this pattern.
\f5val\fP is an address to store the value to be formatted.
The return value of \f5(*argf)()\fP, if negative, stops the processing
of the current format (see below).
.IP
The pattern \f5%:\fP indicates that the next two arguments define
a pair of format string and argument list of the type \f5va_list\fP.
If the argument getting function \f5(*argf)()\fP is already defined,
it is called with the argument \f5fmt\fP being the characters
\f51\fP and \fP2\fP for the new format string and argument list respectively.
The new pair is stacked on top and processing continue from there.
The top pair of format string and argument is popped when the format string
is exhausted. When a new pair is pushed, \f5(*argf)()\fP and \f5(*extf)()\fP
are inherited. When a pair is popped, these functions will be reset.
.PP
\f5sfsprintf(s,size,format,...)\fP is similar to \f5sfprintf()\fP
but it is used to format
the character array \f5s\fP which is of size \f5size\fP.
The length of the resulting string can be obtained with \f5sfslen()\fP.
.PP
\f5sfprints(format,...)\fP constructs a string using the given format
and data. The string is built in a static data area that is allocated
as necessary. The length of the result string can be obtained with \f5sfslen()\fP.
.PP
\f5sfvprintf(f,format,args)\fP is the primitive underlying \f5sfprintf()\fP
and \f5sfsprintf()\fP. It provides a portable variable argument interface.
.PP
\f5sfnotify(notify)\fP sets a function \f5(*notify)()\fP which will
be called whenever a stream is created, closed or when its file descriptor
is changing (see \f5sfsetfd()\fP).
\f5(*notify)(f,type,file)\fP is called with three arguments.
The first argument is the stream pointer \f5f\fP.
The second argument \f5type\fP
is one of \f5SF_NEW\fP, \f5SF_CLOSE\fP or \f5SF_SETFD\fP
to indicate whether the stream is being opened, or closed or if its file descriptor is
changing.
The third argument \f5file\fP is the new file descriptor if \f5type\fP is
\f5SF_SETFD\fP, otherwise, it is the current file descriptor.
.PP
\f5sfsetfd(f,fd)\fP changes the file descriptor of the file stream \f5f\fP.
If the current file descriptor and \f5fd\fP are both non-negative,
\f5f\fP's descriptor will be changed to a value larger or equal to \f5fd\fP.
If the change is successful, the previous file descriptor will be closed.
If \f5fd\fP is negative, the stream is synchronized (see \f5sfsync()\fP) and its
file descriptor will be set to this value.
The stream will remain inaccessible until
a future \f5sfsetfd()\fP call to reset the file descriptor to a non-negative value.
If the current file number is negative and \f5fd\fP
is non-negative, the stream will be reinitialized.
\f5(*notify)(f,SF_SETFD,fd)\fP is called
immediately before the file descriptor is changed (see \f5sfnotify()\fP).
\f5sfsetfd()\fP returns the new file descriptor on success and \f5-1\fP on failure.
.PP
\f5sfset(f,flags,set)\fP sets flags for the stream \f5f\fP.
\f5flags\fP defines a collection of flags to be
turned on or off depending on whether \f5set\fP is non-zero or zero.
The flags that can be set are:
\f5SF_READ\fP, \f5SF_WRITE\fP,
\f5SF_LINE\fP, \f5SF_MALLOC\fP and \f5SF_SHARE\fP.
Note that \f5SF_READ\fP and \f5SF_WRITE\fP can be set only
if the stream was opened as \f5SF_READ|SF_WRITE\fP.
Turning off one of them means that the stream is to be treated as
if it was opened with the other flag exclusively.
It is not possible to turn off both.
An attempt to turn on either \f5SF_READ\fP or \f5SF_WRITE\fP also
cause the stream to set its current I/O mode to the given type.
This is useful when an application needs to make sure that a stream
opened for both read and write be in a specific I/O mode.
\f5sfset()\fP returns the previous set of flags or \f50\fP if
the stream is currently unaccessible.
.PP
\f5sfsetbuf(f,buf,size)\fP changes the current buffer of the stream \f5f\fP to
the new buffer \f5buf\fP.
The stream will be synchronized before the buffer is changed.
If \f5size\fP is positive, \f5buf\fP is taken as a buffer of the given size.
If \f5size\fP is zero, the stream will be unbuffered.
If \f5size\fP is negative, \f5buf\fP is not used and
some internal buffering scheme is used, e.g., memory mapping.
If a new buffer is successfully set and the old buffer has not been deallocated,
\f5sfsetbuf()\fP returns the address of the old buffer.
Otherwise, it returns \f5NULL\fP.
.PP
\f5sffileno(f)\fP returns the file descriptor of the stream \f5f\fP.
.PP
\f5sfstacked(f)\fP returns 1 or 0 depending on whether or not the stream \f5f\fP
is the base of a stack.
.PP
\f5sfeof(f)\fP tells whether there is any more data in the stream \f5f\fP.
.PP
\f5sfsize(f)\fP returns the size of the stream \f5f\fP (see \f5sfnew()\fP).
If the stream is not seekable or if the size is not determinable,
\f5sfsize()\fP returns \f5-1\fP.
.PP
\f5sferror(f)\fP and \f5sfclrerr(f)\fP returns and clears the error condition
on \f5f\fP. Note that the error condition of a stream is only
informative. It does not prevent I/O operations from being performed.
.PP
\f5sfclrlock(f)\fP clears any lock on a locked stream.
Though this is unsafe, it is useful for emergency access to a stream.
It returns the current set of flags.
.PP
\f5sfslen()\fP returns the length of the string most recently obtained
via a \f5sfgetr()\fP, \f5sfsprintf()\fP, \f5sfecvt()\fP or \f5sffcvt()\fP call.
.PP
\f5sfulen(v)\fP, \f5sfllen(v)\fP and \f5sfdlen(v)\fP
return the number of bytes required to code the
\fIunsigned long\fP, \fIlong\fP or \fIdouble\fP value \f5v\fP.
.PP
\f5sfseek(f,addr,offset)\fP sets the next read/write location for the stream \f5f\fP
at a new address defined by \f5addr\fP and \f5offset\fP.
If \f5offset\fP is \f50\fP, \f5addr\fP is the desired address.
If \f5offset\fP is \f51\fP, \f5addr\fP is offset from the current location.
Note that if \f5f\fP was opened for appending (\f5SF_APPEND\fP) and the last operation
done on it was a write operation, the \fIcurrent location\fP is at the physical
end of file.
If \f5offset\fP is \f52\fP,
\f5addr\fP is offset from the \fIphysical\fP end of the stream.
If successful, \f5sfseek()\fP returns the new address.
Otherwise, it returns \f5-1\fP.
See also \f5sfungetc()\fP.
.PP
\f5sftell(f)\fP returns the current location in the stream \f5f\fP.
As with \f5sfseek()\fP, if \f5f\fP was opened for appending (\f5SF_APPEND\fP)
and the last operation done on it was a write operation,
the current location is at the physical end of file.
If the stream \f5f\fP is unseekable, \f5sftell\fP returns the number of bytes
read from or written to \f5f\fP.
See also \f5sfungetc()\fP
.PP
\f5sfecvt(v,n,decpt,sign)\fP and
\f5sffcvt(v,n,decpt,sign)\fP are functions to convert floating values to ASCII.
They corresponds to the standard functions \f5ecvt()\fP and \f5fcvt()\fP.
The length of the conversion string most recently done by
\f5sfecvt()\fP or \f5sffcvt()\fP can be found by \f5sfslen()\fP.
.PP
\fBAUTHORS\fP
.PP
Phong Vo (att!ulysses!kpv) and  David G. Korn (att!ulysses!dgk).
SHAR_EOF
fi # end of overwriting check
if test -f './Doc/sfio.tm'
then
	echo shar: will not over-write existing file "'./Doc/sfio.tm'"
else
cat << \SHAR_EOF > './Doc/sfio.tm'
.\" Strings for references
.ds [ \u\s-2
.ds ] \s+2\d
.SA 1  \"  right justified
.TL
SFIO: Safe/Fast String/File IO
.AU "David G. Korn" DGK MH "" 7975 3C-526B att!ulysses!dgk
.AU "Kiem-Phong Vo" KPV MH "" 4869 3C-536A att!ulysses!kpv
.TM
.AS 0
.in +0.3i
.ll -0.3i
This paper describes \fISfio\fP, a new input/output library, that
can be used as a replacement for \fIStdio\fP, the C language standard I/O library.
\fISfio\fP is more complete, consistent, and efficient than \fIStdio\fP.
New facilities are provided for
convenient, safe and efficient manipulation of data streams.
An \fISfio\fP stream may be entirely memory resident or it may
correspond to some actual file.
Alternative I/O disciplines can be 
applied to a stream to customize its behavior with respect to data
transformation and exception handling.
Stream pools can be maintained to guarantee stream synchronization
when switching streams for I/O operations.
Separate streams can be stacked on one another to make new virtual streams.
Both source and binary compatibility packages are provided
allowing \fIStdio\fP-based programs to benefit from the new library without recoding.
\fISfio\fP has been used successfully in a number of applications including
many rewritten standard system utilities.
Benchmark timings show that \fISfio\fP performs very well against \fIStdio\fP and
standard utilities can gain substantial performance improvement when
based completely on \fISfio\fP.
.AE
.MT 4
.H 1 Introduction.
.P
The C language does not have any input/output facilities.
I/O operations are typically negotiated via system or function calls.
Most C programs routinely use the standard I/O library, \fIStdio\fP\*[KR\*].
The library was written by Dennis Ritchie in the mid 1970's to replace an
earlier library called the portable I/O library.
\fIStdio\fP was a part of UNIX\*F
.FS
UNIX is a trademark of AT&T Unix System Laboratory
.FE
Version 7 which is the ancestor of virtually all current UNIX systems.
Central to \fIStdio\fP is the idea of a buffered stream that models
an input or output file. Application code can read or write
arbitrary chunks of data with confidence that the underlying
buffering scheme will perform efficiently. Beyond efficiency concerns,
\fIStdio\fP also provides other functions to read and write
various types of data included formatted records.
.P
Today the standard I/O library is ubiquitous in C programming environments.
Features from diverse versions
have been amalgamated in the ANSI C standard \*[AN\*].
Despite its success, there are recognized deficiencies in \fIStdio\fP both
in its design and various implementations.
For example, the library does not provide an efficient way to transfer
large amount of data between streams.
This weakness causes many standard system utilities to revert to
direct use of the underlying system calls.
The poor performance of \fIStdio\fP for line-oriented processing 
was addressed in the design of yet another I/O library, \fIFio\fP, done
by Andrew Hume for the 9th Edition UNIX system \*[Hu\*].
The interface of \fIStdio\fP is incomplete, prompting
a number of attempts over the years to extend it.
The ability to perform both input and output on a stream opened
for both read and write was added in the early 1980's by Andrew Koenig.
A few years later, the ability to tune buffer size and set line buffering mode
was added in the BSD UNIX version.
In our own experiences, a frustrating problem with \fIStdio\fP
is the lack of any facility for graceful handling of exceptions.
A bad case for most current versions of \fIStdio\fP
is when the \f5write()\fP system call is interrupted.
Most of the time, this would cause a loss of data.
Equally perplexing is the lack of any facility for synchronizing buffers of
streams that share the same underlying file, e.g., \f5/dev/tty\fP
and the streams \f5stdin\fP, \f5stdout\fP and \f5stderr\fP.
Thus, there is no way to guarantee serialization of I/O operations
short of flushing the buffer after each operation.
.P
The \fISfio\fP library was written partly to overcome \fIStdio\fP's deficiencies.
One may ask why not just rewrite \fIStdio\fP and
maintain if not binary compatibility, at least source compatibility?
One of us, David Korn, tried this tack a number of years back.
Numerous link editing problems occured because of name clashing.
A major problem is the use of a fixed array of streams in \fIStdio\fP.
This makes it impossible to extend the stream data structure without violating
binary compatibility. This limits the usefulness of a new implementation
unless one can recompile the entire system and its host of applications.
More important, preserving the \fIStdio\fP
interface means also preserving its various problems.
We feel that a better interface could be
designed that would avoid inconsistency, reduce redundancy, and be easily extensible.
The good news is that by providing a completely new interface
that occupies a different name space from that of \fIStdio\fP, we are able
to build both source and binary compatibility packages to \fIStdio\fP.
On a system with shared libraries, the compatibility packages can be installed
to provide transparent access to \fISfio\fP.
.P
The remainder of the paper will go into some details on
the design and implementation deficiencies in \fIStdio\fP.
Then, the motivations behind \fISfio\fP and its main features are discussed.
Benchmark data comparing the performance of
\fISfio\fP, \fIStdio\fP and \fIFio\fP is presented.
This includes data comparing standard versions of
system utilities and our reimplementations which
exploit the new abstractions provided in \fISfio\fP.
Finally, the manual pages for \fISfio\fP are attached for
detailed descriptions of the available functions.
.P
.H 1 "The In-Problems with Stdio"
.P
There are many problems with the current design and implementations of \fIStdio\fP.
For reasons that should become clear soon, we call them the \fIin-problem\fPs.
In the following discussion, the reader should keep in mind that
there are many versions of \fIStdio\fP and certain problems
may apply to only particular versions.
.BL
.LI
\fIIncorrectness:\fP
A design defect in \fIStdio\fP is that it does not check the type of a stream
when I/O operations are performed.
For example, it is perfectly ok to open a stream for read only, then write data into it.
Here is an example:
.P
.nf
	\f5f = fopen("data_file","r");\fP
	\f5getc(f);\fP
	\f5putc('a',f);\fP
.fi
.P
Of course, only the data in the internal buffer of the stream \f5f\fP is
changed when \f5putc()\fP is issued, not the underlying file.
However, interesting consequences can arise in the application code.
.P
At the implementation level, the library also suffers from a number of defects.
The case that caught our attention is when a system call gets
interrupted by a signal. Here is an example:
.P
.nf
	\f5alarm(1)\fP;
	\f5fwrite(buf,1,1000000,stdout)\fP;
.fi
.P
In this code fragment, the buffer being written out greatly exceeds the
internal buffer of the standard output stream. Therefore, from time to time,
a \f5write()\fP system call will be issued to flush data from the internal buffer.
If the alarm signal arrives at an importune moment, the system call is
interrupted and only a part of the buffer is written out.
Most current \fIStdio\fP implementations will note that
an incomplete write had happened but nothing is done
about the part of data not yet written out.
The result is a loss of data.
.LI
\fIInefficiency\fP:
\fIStdio\fP enforces the notion that the application
has to provide a buffer (different from the stream buffer)
to transfer data in and out of streams. This causes unnecessary
data copying in many common cases. In addition, it makes it hard to get
data whose size may not be known a priori to a call, for example,
a line of text. There are recent proprosals to add a new function \f5fgetline()\fP
precisely for this purpose. Even if this is done,
it is a kludge to get around the fundamental
problem of how to safely access stream data without unnecessary data copying.
.P
In many versions of \fIStdio\fP,
an I/O operation entails reading  or copying
data into the internal stream buffer before
transferring it to the final destination.
For reading or writing an amount of data that exceeds the buffer size,
this is wasteful.
For example, a call like \f5fread(buf,10,BUFSIZ,stdin)\fP may imply multiple
invocations of the \f5read()\fP system call.
.LI
\fIInadequacy:\fP
Parts of \fIStdio\fP simply fall short of what the interface promises.
For example, the \f5fread()\fP and \f5fwrite()\fP functions promise I/O
of objects which may have large sizes.
Since the system calls \f5read()\fP and \f5write()\fP only promise
I/O of byte streams, this is a promise that is hard to keep.
For example, if a structure of size, say 100, is written out to a pipe
but the \f5write()\fP system call is interrupted after the first 10 bytes
is copied, \f5fwrite()\fP can only report that no structure has been
written out. This is misinformation at best.
.P
Some implementations of \fIStdio\fP are also inadequate resulting
in unnecessary and complex interface requirement.
For example,
\fIStdio\fP allows streams to be opened for both read and write.
However, applications are not allowed to arbitrarily
mix input and output operations. Instead, extra \f5fseek()\fP calls must
be inserted before switching mode. This requirement results because
current implementations of \fIStdio\fP do not have a way
to know what mode the stream is in.
From an application's point of view,
if a stream is passed as an argument to some function,
since there is no way to know what mode it is in,
the application may have to use a defensive
measure causing many unnecessary calls to \f5fseek()\fP.
.P
Buffer synchronization for streams sharing the same file is a good source
of problems for \fIStdio\fP programmers. The following code fragment
can cause unexpected results:
.P
.nf
	\f5printf("Please type something:");\fP
	\f5fgets(buf,sizeof(buf),stdin);\fP
	\f5printf("You just typed: %s",buf);\fP
.fi
.P
Because of buffering, the user will not see the prompt
until s/he has typed a line of input.
Then both the prompt and the typed text will be printed.
Strictly speaking, this is not an error on \fIStdio\fP part.
It just lacks a facility that would allow the above ``natural'' piece
of code to work as expected (see section 3.4).
A sharp reader may bring up \f5setbuf(stdout,NULL)\fP
or \f5fflush(stsdout)\fP here but s/he should be forewarned at least of
the efficiency cost incurred in the former case and the inconvenience of the latter.
This is not to mention other more complex buffer synchronization situations such
as two streams writing to the same file. Can the output be guaranteed to
serialize logically?
.LI
\fIInsecurity:\fP
Parts of the interface of \fIStdio\fP are notoriously insecure.
For example, the \fIgets()\fP and \fIsprintf()\fP functions allow overwriting
data space that follows a buffer. Below is an example of unsafe coding style
possible with \fIStdio\fP. The existence of such a coding style in standard
system utilities was rumored to have been put to good use in at least
a few system break-in techniques.
.P
.nf
	\f5char buf[1];\fP
	\f5sprintf(buf,"1234");\fP
.fi
.P
More benign but equally troublesome for applications
is the possibility of concurrent access of streams due to signal handling.
In such cases, the behavior of the stream is unspecified since internal
stream pointers may be in a bad state during a concurrent access.
.LI
\fIInconsistency:\fP
An annoying problem with the interface design of \fIStdio\fP
is the positioning of the stream argument in a function call.
For example, \f5fseek()\fP and \f5setbuf()\fP
require the stream argument to come first while \f5fread()\fP and \f5fwrite()\fP
require it to come last. Though this is not a fatal flaw, inconsistency
of this type steepens the learning curve for programmers.
Some of us, after many years of programming with \fIStdio\fP,
still often have to open the manual to find the correct function calling sequences.
.LI
\fIIncompleteness:\fP
This is an open area where everyone's wish list can be inserted.
However, \fIStdio\fP does lack a number of fundamental features.
Most important is a lack of some way for applications
to handle exceptions such as the end-of-file condition or
interrupted system calls. More along the efficiency line, there is
no safe method to directly access the internal buffer of a stream.
The inefficiency resulting from this have forced many applications
to invent their own buffering scheme.
.P
Except for \f5sprintf()\fP and \f5sscanf()\fP,
the library is closedly tied to files.
There is no capability to manipulate arbitrary data streams so
the convenience of the I/O operations are lost for memory manipulation.
This is a lack recognized by providers of the \fIC++ Iostream\fP package \*[St\*].
.LE
.P
.H 1 "The Sfio Library"
.H 2 "Design Considerations"
.P
The \fISfio\fP library was built to correct the problems of \fIStdio\fP
and to simplify our life as programmers.
Interface design considerations concluded for us that a
rewrite of \fIStdio\fP just wouldn't do so we started fresh.
Below are some of the considerations that went into the design of \fISfio\fP.
.BL
.LI
\fIEfficiency\fP:
A package as basic as \fISfio\fP must be efficient in its implementation.
The I/O primitives in \fISfio\fP are designed to avoid any unnecessary
data copying. New and efficient algorithms are used when appropriate.
For example, the \fISfio\fP \f5sfprintf()\fP and \f5sfscanf()\fP functions
use new data conversion algorithms that are much faster than their counterparts
in \fIStdio\fP.
.P
In modern UNIX systems, there are often multiple ways to do I/O with different
trade-offs. For example, on SUN OS and System V Release 4,
it is possible to use memory
mapping to access file data. \fISfio\fP takes advantage of these facilities
where appropriate to speed up thoughput.
.P
Beyond internal efficiency, an I/O package like \fISfio\fP must also provide primitives
that allow an application to be efficient in its I/O manipulations.
An example is \f5sfpeek()\fP
which allows an application safe access to the internal buffer of a stream.
We have rewritten many standard system utilities
based on this primitive with good performance improvement.
It is worth noting that because of efficiency problems with \fIStdio\fP,
popular tools such as \fIcat\fP or \fIcp\fP were often rewritten using raw system calls.
However, by going to lower level system calls,
these implementations cannot take advantage of new efficient features such
as memory mapping without further recoding.
Our implementations of these tools based on \fISfio\fP
achieve the same or better efficiency.
.LI
\fIConsistency\fP:
All \fISfio\fP constants begin with the prefix \f5SF_\fP
and all functions begin with the prefix \f5sf\fP.
Where applicable, \fISfio\fP functions
simply emulate their system call counterparts.
For example, the \f5sfread()\fP function is called as \f5sfread(stream,buf,size)\fP.
This example also shows that the stream argument always positions first in
any function call.
Then, if required,
the second argument is a buffer and the third would be the buffer size.
.P
All streams are handled in the same way by the application.
There is no difference between string (memory only) and file streams.
This is used by \f5sftmp()\fP, a function to create
streams for storing and retrieving temporary data. For efficiency,
such a temporary stream starts out being a string stream with a buffer of some
size decidable by the application. A stream discipline is set up so that
if this memory area is exhausted, a true temporary file will be created.
From the application's point of view such a temporary stream simply appears
as if it is always a file.
.LI
\fIOrthogonality:\fP
All stream-applying primitives in \fISfio\fP are orthogonal so
they can be used in arbitrary orders.
For example, the \f5sfsetbuf()\fP functions can be called any time
to change the buffer of a stream, not only when the stream is just opened.
This is important for \fISfio\fP since it supports string streams
that correspond to in-memory data buffers.
In the same vein, there is no restriction on the order of I/O operations
on streams that are opened for both read and write.
.LI
\fIIntegrity:\fP
Except for convoluted escape hatches (e.g., \f5longjmp()\fP),
the library guarantees that internal stream buffers maintain their integrity
when exceptions occur.
For example, if a \f5write()\fP system call is interrupted, no data will be lost
(unless, of course, if \f5write()\fP itself loses it).
An application can also set alternative disciplines to handle such exceptions.
Streams that are opened for read only or write only will disallow
the opposite operations.
Finally, during an operation that modifies a stream,
the stream is locked to prevent other concurrent accesses
to the same stream.
.LI
\fIRobustness:\fP
We have mentioned the use of locks to prevent concurrent stream accesses.
The library also prevents most unsafe operations that may cause buffer overflow.
Except for string scanning patterns such as \f5%s\fP or \f5%[]\fP in \f5sfscanf()\fP, 
there is no way to specify a buffer without an accompanying size.
The only reason for saving the current semantics of
these patterns despite their insecurity
is a concession for portability of the legion of existing applications.
We do provide alternative patterns, \f5%S\fP, \f5%C\fP and \f5%{}\fP,
which require the accompanying buffer sizes.
Unbounded lines or records delineated by some arbitrary character can
be read via \f5sfgetr()\fP which either uses the internal stream buffer
or allocates buffer space as necessary to store the input.
.LI
\fIMinimality:\fP
As a general rule, we avoid providing functions unless they do something
that cannot be done outside of \fISfio\fP without some loss.
For example, there are no functions corresponding to the \f5getchar()\fP,
\f5putchar()\fP
family since they only provide short-hand notations to functions applying to the
standard output stream.
There are border cases where this rule is relaxed somewhat.
For example, \f5sfsprintf()\fP and \f5sfsscanf()\fP are provided even though
the respective input/output string could be opened for I/O as a string stream.
Their popularity and the saving of creating a stream structure is worth
their existence.
.P
At the implementation level, we try to minimize the amount of code that
gets pulled in with normal compilation. For example, even though stacked streams
can only be closed as a unit, the code for stack manipulation is never pulled
in unless stacking is used in the application.
.LI
\fIExtensibility:\fP
No library provider can anticipate all possible needs
that an application may want from the package. Thus, we have made
a stream parametrizable via the use of disciplines.
A discipline redefines the system calls
\f5read()\fP, \f5write()\fP, and \f5lseek()\fP, and provides a function
to handle exceptions. Different disciplines can be stacked on one another
to make a sequence of filters.
For example, the \fIpack\fP and \fIunpack\fP
utilities have been rewritten based on a discipline that
provides a virtual plaintext view of writing and reading packed files.
The beauty in such a scheme is that the same discipline can be reused in
other applications for accessing the same type of data.
.LE
.P
.H 2 "Creating and Deleting Streams"
.P
\fISfio\fP defines a stream type called \f5Sfile_t\fP
and a collection of functions to create, manipulate, and destroy streams.
A stream can be created from a file descriptor or memory.
It is common to call a memory stream a string stream.
Streams can be created for reading, writing or both and
its I/O mode can be set for byte-oriented or line-oriented.
.P
The primary mechanism for creating a stream is \f5sfnew()\fP:
.P
.nf
	\f5sfnew(Sfile_t* f, char* buf, int size, int fd, int type)\fP
.fi
.P
\f5f\fP is a stream pointer.
If it is \f5NULL\fP or not closable, a new stream is created;
otherwise, \f5f\fP is a stream to be closed and reused in making a new stream.
\f5buf\fP and \f5size\fP refer to a buffer and its size when
\f5size\fP is positive. If \f5size\fP is 0, the stream is unbuffered
and if \f5size\fP is negative, the actual buffer will be allocated by the system.
\f5fd\fP, refers to a file descriptor when the
stream models a file stream and is ignored otherwise.
\f5type\fP is a bit pattern defining the type of stream.
It includes the bits:
.VL 5
.LI "\f5SF_STRING\fP:"
This bit indicates that the stream is an array of bytes in memory, not a file.
If the stream is opened for reading, \f5buf\fP and \f5size\fP define
the data and its extent.
.LI "\f5SF_READ\fP and \f5SF_WRITE\fP:"
These bits specify read and write mode.
.LI "\f5SF_LINE\fP:"
This bit turns on the line mode for a stream.
The line mode of the standard input stream, \f5sfstdin\fP,
is automatically turned on if the stream is unseekable.
For write streams, \f5SF_LINE\fP means flushing the buffer whenever the new-line
character is output. For read streams, \f5sfscanf()\fP matches literally
the appearance of a new-line character in the format string.
This provides a convenience for matching patterns such as \f5``%s\en''\fP
on unseekable streams without unnecessary blocking.
.LI "\f5SF_SHARE\fP:"
This bit indicates that the stream corresponds to a file descriptor
that may be shared elsewhere by a different stream
or a different process. The file position will be reset to its logical
location before a call to \f5read()\fP or \f5write()\fP.
.LI "\f5SF_APPEND\fP:"
This bit indicates that the stream is a file opened for append.
On systems where this mode is not available, a seek to the end of the file
will be performed before a call to \f5write()\fP.
.LE
.P
Stream objects can also be created from higher level functions:
\f5sfopen()\fP, and \f5sfpopen()\fP.
\f5sfopen()\fP is similar to the \f5freopen()\fP function in \fIStdio\fP.
However, when the first argument is \f5NULL\fP, it creates a new stream.
In addition, if the third argument is \f5"s"\fP or \f5"s+"\fP, the second
argument, instead of being a file name, is a string to be opened for read
or read and write.
\f5sfpopen()\fP is similar to \f5popen()\fP in \fIStdio\fP:
.P
	\f5sfpopen(Sfile_t* f, const char* cmd, const char* type)\fP
.P
Here, \f5cmd\fP is a command connected to the application
via pipes. \f5type\fP determines the type of connection.
Unlike \fIStdio\fP, an \f5sfpopen\fP-stream can be opened for both read and write.
.P
When a stream is no longer needed, it can be closed with \f5sfclose()\fP.
.P
.H 2 "Reading and Writing Streams"
.P
All legal I/O operations can be performed on a stream in arbitrary order.
There are five ways to do I/O on streams:
.BL
.LI
\fIByte or number oriented\fP:
The function \f5sfgetc(f)\fP returns the next byte
from the stream \f5f\fP. If there are no more data, it returns \f5SF_EOF\fP.
The function \f5sfungetc(f,c)\fP can be used to push back a byte into the stream.
Unlike \fIStdio\fP, there is no limit to the number of bytes that can be pushed
back into a stream.
The inverse of \f5sfgetc()\fP is \f5sfputc()\fP which writes a byte
into the stream. Other functions,
\f5sfgetl()\fP, \f5sfputl()\fP, \f5sfgetu()\fP, \f5sfputu()\fP,
\f5sfgetd()\fP, and \f5sfputd()\fP,
are available to read and write integral and floating point values
in a portable format.
The coding format is portable as long as the bit order within a byte is the same
across the relevant machines and if two corresponding types have the same size.
The coding for floating point values
rely on the system provided functions \f5frexp()\fP and \f5ldexp()\fP.
Most programs manipulate only small values;
our coding method takes advantage of this and uses a minimal
number of bytes. This saves disk space when storing
large amounts of data. The cost of encoding and decoding data is
usually well paid for by the time saved in accessing disk data.
.LI
\fIString-oriented\fP:
The function \f5sfgetr(f,rsc,string)\fP reads up to and including the
record separator character \f5rsc\fP.
If \f5string\fP is positive, the record separator character is turned into
the nul character to make the input into a C string.
There is no hard limit on the length of the input string.
The library either uses the stream buffer space or allocates space as necessary
to store the input.
After a call to \f5sfgetr()\fP, the function \f5sfslen()\fP can be called to
get the length of the input.
.LI
\fIBlock-oriented\fP:
The function \f5sfread(f,buf,n)\fP tries to read \f5n\fP bytes from \f5f\fP
into the buffer \f5buf\fP. It returns the number of bytes actually read.
Similarly, \f5sfwrite(f,buf,n)\fP writes \f5n\fP bytes to the stream \f5f\fP.
The function \f5sfmove(fr,fw,n,rsc)\fP is used to move data from
the stream \f5fr\fP to the stream \f5fw\fP. If \f5n\fP is non-negative,
it indicates the number of units to move; otherwise all data in the stream is moved.
The movement unit is either a byte or a record
delineated by the record separator \f5rsc\fP when \f5rsc\fP is non-negative.
The below example skips \f510\fP lines from the standard input, then
moves the next \f510\fP lines to the standard output.
.P
.nf
	\f5sfmove(sfstdin,NULL,10,'\en')\fP;
	\f5sfmove(sfstdin,sfstdout,10,'\en')\fP;
.fi
.LI
\fIFormatted I/O\fP:
The functions \f5sfscanf()\fP and \f5sfprintf()\fP provides ways
to read and write data that are formatted.
These functions are similar to the \f5fscanf()\fP and \f5fprintf()\fP
functions of \fIStdio\fP though we have provided a few extensions so
an application can interpret unknown patterns or provide a different way
to get or assign formatted arguments. See the appendix for details.
.LI
\fIDirect stream buffer access\fP:
This is the fastest method for performing I/O using \fISfio\fP.
It is done via the function \f5sfpeek(f,bufp,n)\fP.
If \f5f\fP is a read stream, \f5sfpeek()\fP fills the buffer if it is empty,
then return in \f5*bufp\fP the pointer to the beginning of available data.
If \f5f\fP is a write stream, \f5sfpeek()\fP flushes the buffer if it is full,
then returns in \f5*bufp\fP the pointer to the start of the buffer area available
to write.
If \f5n\fP is non-negative, the buffer is advanced by an amount which is the
minimum of available buffer and \f5n\fP.
If \f5n\fP is negative, \f5sfpeek()\fP does not change the current stream location.
That is done via a subsequent \f5sfread()\fP or \f5sfwrite()\fP call.
Further, for buffer protection, until a \f5sfread()\fP or \f5sfwrite()\fP
is issued on the returned buffer pointer, the stream is locked from further access.
Below is an example of using \f5sfpeek()\fP to get access to a write buffer,
put some data into it, then advance the write pointer past the data.
This example works because \f5sfwrite()\fP will notice
that \f5buf\fP points to the same position as the next write position in the stream
buffer and simply advances the write position without doing any data copying.
.P
.nf
	\f5p = sfpeek(f,&buf,-1);\fP
	\f5n = process(buf,p);\fP
	\f5sfwrite(f,buf,n);\fP
.fi
.LE
.H 2 "Stream Synchonization"
.P
When a stream corresponds to a file, \fISfio\fP either buffers data or
use memory mapping to reduce
the number of \f5read()\fP and \f5write()\fP system calls.
This means that the logical seek location of a stream may not correspond
to the seek location of the underlying file.
The \f5sfsync(f)\fP function can be used to cause the two seek locations
to synchronize. For a write stream, any buffered data will be output.
For a read stream, if the stream is seekable,
the file seek location is rolled back as necessary.
The call \f5sfsync(NULL)\fP synchronizes all streams.
.P
Streams can be grouped together into auto-synchronizable pools
using the \f5sfpool(f,poolf,type)\fP function.
If \f5poolf\fP is \f5NULL\fP, \f5f\fP is taken out of its current pool, if any.
Otherwise, \f5f\fP is pooled with \f5poolf\fP.
The \f5type\fP argument of \f5sfpool()\fP can be any non-empty
combination of \f5SF_SHARE\fP, \f5SF_READ\fP and \f5SF_WRITE\fP.
In each pool, the most recently accessed stream is at the head of the pool.
A \f5SF_SHARE\fP pool consists only of write streams.
It is useful to serialize buffered data for all streams in a pool as
buffered data will be move from stream to stream as streams are switched.
If the pool is not \f5SF_SHARE\fP and
if the type of the current stream matches \f5type\fP,
it will be synchronized before the new stream becomes current.
Below is an example of pooling the standard input and output
streams so that the standard output is always flushed before reading from
the standard input:
.P
	\f5sfpool(sfstdin,sfstdout,SF_WRITE);\fP
.P
A typical use of \f5sfpool()\fP is to preserve
the order of I/O operations on streams sharing the same file.
For example, the following example guarantees that the output to the standard output
and standard error streams which typically go to the terminal will be
in the right order. Further, if both streams are buffered, the number of \f5write()\fP
calls will be minimized as if data is output to a single stream.
.P
.nf
	\f5sfpool(sfstderr,sfstdout,SF_SHARE);\fP
	\f5sfprintf(sfstdout,"This is a");\fP
	\f5sfprintf(sfstderr," sentence.\en");\fP
.fi
.P
.H 2 "Stream Stacking"
.P
There are many applications in which it is desirable to insert
a stream of data at some point of input. For example,
the C preprocessor routinely needs to insert a new macro definition
or an include file. The \f5sfstack(base,f)\fP function inserts
a new stream \f5f\fP on top of the stack referred to by the \f5base\fP stream.
All I/O operations are requested via the \f5base\fP stream but
actually performed on the top stream. A top stream will be popped and closed
on end-of-file. The stack can also be popped by specifying \f5NULL\fP
for the second argument of \f5sfstack()\fP.
Below is an example of using stream stacking.
The function \f5process()\fP checks to see if a line of text
defines an include file and returns its name.
.P
.nf
	\f5while((s = sfgetr(sfstdin,'\n',1)) != NULL)\fP
	\f5{   char *include = process(s);\fP
	\f5    if(include)\fP
	\f5    {   Sfile_t *f = sfopen(NULL,include,"r");\fP
	\f5        if(f)\fP
	\f5            sfstack(sfstdin,f);\fP
	\f5    }\fP
	\f5}\fP
.fi
.P
In the next section, we give an example showing
how a discipline can be used in conjunction
with stacking to catch the end-of-file event of the inserted file
and reset certain state information such as the current file name and line number.
.P
.H 2 "Stream Discipline"
.P
The function \f5sfdisc(f,disc)\fP pushes a new I/O discipline onto the stream \f5f\fP.
A discipline specifies functions
to replace the system calls \f5read()\fP, \f5write()\fP, and \f5lseek()\fP, and
to handle exceptions.
The argument \f5disc\fP is either \f5NULL\fP to pop the discipline stack
or a pointer to a \f5Sfdisc_t\fP structure.
This structure contains four
publically visible fields: \f5(*readf)()\fP, \f5(*writef)()\fP,
\f5(*seekf)()\fP, and \f5(*exceptf)()\fP.
The first three fields specify alternative I/O functions.
If one of these functions is \f5NULL\fP, it is inherited from an earlier discipline on
the discipline stack. For completeness, the bottom of the
stack of a file stream always contains the discipline consisting of system calls.
The fourth field, \f5(*exceptf)()\fP, defines an exception handler.
The stream \f5f\fP is always synchronized before its discipline stack
is manipulated. After successful manipulations, the seek location
and the stream extent may change to reflect that as defined by the current discipline.
.P
A discipline I/O function, \f5(*readf)()\fP, \f5(*writef)()\fP, or \f5(*seekf)()\fP,
is called with 4 arguments.
The first argument is the stream pointer.
The second and third arguments correspond to the second and third arguments
of the respected system call.
The fourth argument is the discipline structure, \f5disc\fP, itself.
Note that since a discipline function is invoked during a stream I/O operation,
the stream remains locked during its execution.
A discipline function should avoid using
I/O system calls directly and use \f5sfrd()\fP,
\f5sfwr()\fP and \f5sfsk()\fP instead.
The latter functions invoke lower level discipline functions
and ensure proper exception handling.
.P
The exception function, \f5(*exceptf)()\fP is called
when a read or write exception happens, when a stream
is being closed, or when the discipline is being reset.
A read or write exception occurs when the discipline I/O function
returns a zero or negative value.
During a call to \f5(*exceptf)()\fP, if it is at the top of the discipline stack,
the stream will be opened for general operations.
\f5(*exceptf)()\fP is called as \f5(*exceptf)(f,type,disc)\fP.
Here, \f5type\fP is:
\f5SF_DPOP\fP when the discipline is about to be popped of the discipline stack,
\f5SF_DPUSH\fP when the discipline is about to be pushed down in the stack,
\f5SF_NEW\fP when the stream is being renewed,
\f5SF_CLOSE\fP when the stream is being closed,
\f5SF_READ\fP when an exception happens during a read operation, and
\f5SF_WRITE\fP when an exception happens during a write operation.
The current \fISfio\fP function will examine
the return value of \f5(*exceptf)()\fP for further actions:
negative for immediate return,
zero for executing default actions associated with the exception,
and positive for resuming execution as if no exception happened.
.P
Here is an example of using a discipline to copy data from
the standard input to the standard output where all
upper case characters are translated to lower case.
.P
.nf
	\f5lower(Sfile_t* f, char* buf, int n, Sfdisc_t* disc)\fP
	\f5{   int c;\fP
	\f5    n = sfrd(f,buf,n,disc);\fP
	\f5    for(c = 0; c < n; ++c)\fP
	\f5            buf[c] = tolower(buf[c]);\fP
	\f5    return n;\fP
	\f5}\fP
	\f5...\fP
	\f5Sfdisc_t Disc = { lower, NULL, NULL, NULL, NULL };\fP
	\f5sfdisc(sfstdin,&Disc);\fP
	\f5sfmove(sfstdin,sfstdout,SF_UNBOUND,-1);\fP
.fi
.P
As usual, there are different solutions with trade-offs
in ease of coding, ease of understanding, reusability, and efficiency.
The above solution is attractive because it isolates
the translation act into a single, easily recognizable and reusable function.
The cost of an extra function call per buffer filling is minimal since it
typically occurs only once per 8Kb of data.
.P
In practice, most stream disciplines require state information.
This can be done by defining application-specific discipline structures.
For example, stacked streams frequently require the save and restore of
line numbers and file names. A discipline structure for a stacked stream
can be defined as:
.P
.nf
	\f5typedef struct _stk_disc\fP
	\f5{   Sfdisc_t   disc;\fP
	\f5    int        line;\fP
	\f5    char       *file;\fP
	\f5} Stk_disc_t;\fP
.fi
.P
The discipline exception function will restore these values when the stream is
about to be closed:
.P
.nf
	\f5stk_except(Sfile_t* f, int type, Sfdisc_t* disc)\fP
	\f5{   if(type == SF_CLOSE)\fP
	\f5    {   Line = ((Stk_disc_t*)disc)->line;\fP
	\f5        File = ((Stk_disc_t*)disc)->file;\fP
	\f5    }\fP
	\f5    return 0;\fP
	\f5}\fP
.fi
.P
The application code needs only set this discipline before stacking the new stream:
.P
.nf
	\f5Stk_disc_t *stk_disc = (Stk_disc_t*)calloc(1,sizeof(Stk_disc_t))\fP;
	\f5stk_disc->disc.exceptf = stk_except;\fP
	\f5stk_disc->line = Line;\fP
	\f5stk_disc->file = File;\fP
	\f5sfdisc(f,(Sfdisc_t*)stk_disc);\fP
	\f5sfstack(sfstdin,f);
.fi
.P
.H 2 "Extensions to sfprintf() and sfscanf()"
.P
The formatted I/O routines \f5sfprintf()\fP
and \f5sfscanf()\fP have a few extensions
for added security and to allow an application to tailor their processing.
The string scanning patterns \f5%C\fP, \f5%lc\fP, \f5%S\fP, \f5%ls\fP,
\f5%{}\fP and and \f5%l[]\fP are analogues of \f5%c\fP, \f5%s\fP,
and \f5%[]\fP but they require a buffer size.
.P
The formats \f5i\fP, \f5d\fP, and \f5u\fP are extended
to print integers in non-standard bases.
Here is the general form for the \f5d\fP format: \f5%[width].[precision].[base]d\fP.
The forms for \f5i\fP and \f5u\fP are defined similarly.
For example, \f5%..36d\fP prints a signed integer in base \f536\fP and
\f5%..64u\fP prints an unsigned integer in base \f564\fP.
Currently, \f5base\fP can range from \f52\fP to \f564\fP.
If \f5base\fP is not defined or if it is
not within the defined range, it is taken to be \f510\fP.
The modifier \f5#\fP will output the number in the form \f5base#number\fP.
The digits to represent numbers are:
.nf
        \f501234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@_\fP
.fi
.P
Three additional customizing patterns are supported: \f5%&\fP, \f5%@\fP and \f5%:\fP.
The \f5%&\fP pattern indicates that the next argument in the variable
argument list is the address of a function that is used to interpret
patterns not already defined by the respective utility.
The \f5%@\fP pattern indicates that the next argument is the address of
a function to get arguments in the case of \f5sfprintf()\fP and to assign
values in the case of \f5sfscanf()\fP. This is useful, for example,
to write an interpreter version of \f5printf()\fP where the arguments
are unknown at compile time.
Note that both extension functions are local to
an invocation of \f5sfprintf()\fP or \f5sfscanf()\fP.
This allows recursive calls of these functions (via the extension functions)
to define different types of processing.
Finally, the pattern \f5%:\fP
indicates that the next two arguments are a pattern string and a corresponding
variable argument list to be inserted. The processing continues with the
new pattern string until it is exhausted, then resumes with the earlier string.
.P
.H 1 "Stdio Compatibility"
.P
To ease the transition  for programs currently based on \fIStdio\fP,
we wrote both source and binary compatibility packages.
At the source level, the \f5FILE\fP type is simply redefined to be \f5Sfile_t\fP.
Most \fIStdio\fP functions and constants
can be emulated by macro definitions to their \fISfio\fP counterparts.
This is done in an emulation \f5stdio.h\fP header file.
A few functions such as \f5printf()\fP and \f5scanf()\fP that accepts
variable argument lists are reimplemented based on the \fISfio\fP versions.
.P
Binary compatibility means that the library must work with code
already compiled with the standard \fIStdio\fP header file.
In general, this is unsolvable because the
the \f5FILE\fP structure
may be defined in some non-standard way which would affect
macro functions such as \f5getc()\fP or \f5putc()\fP that directly accesses
certain internal fields.
A working solution for most \fIStdio\fP implementations
is to write a program to generate
from the header file \f5stdio.h\fP an emulation structure that contains
the fields \f5_cnt\fP, \f5_ptr\fP, \f5_flag\fP and \f5_file\fP in the
exact locations that they appear in the \f5FILE\fP structure.
This keeps correct the embedded pointers in
macro functions such as \f5getc()\fP or \f5putc()\fP.
Then, functions are written to provide one-to-one mappings between
the set of \f5FILE\fP emulation structures to the set of \f5Sfile_t\fP structures.
Each \fIStdio\fP function is reimplemented to
map a given \f5FILE\fP structure to its \f5Sfile_t\fP counterpart before
calling the appropriate \fISfio\fP function.
The reimplemented functions maintain the \f5_flag\fP field
up to date with respect to I/O exceptions.
Finally, by initializing the \f5_cnt\fP field to be 0,
the macro functions \f5putc()\fP and \f5getc()\fP are trapped
to call \f5_filbuf\fP and \f5_flsbuf\fP, internal \fIStdio\fP
functions to fill and flush buffers. Reimplementing these functions
to call their \fISfio\fP counterparts and to set \f5_cnt\fP and \f5_ptr\fP
to the appropriate values completes the job.
.P
.H 1 "Performance"
.H 2 "I/O Benchmark Results"
.P
To measure relative performances of \fISfio\fP and comparable packages,
\fIStdio\fP and \fIFio\fP,
we wrote a benchmark program that exercises the basic  I/O functions.
The program is based on the \fIStdio\fP interface so that we can measure
the performance of \fIStdio\fP.
To measure the performance of \fISfio\fP, we simply recompiled the program
using the source \fIStdio\fP emulation package.
Benchmark data were obtained under a variety of environments.
To reduce variance,
each set of data was an average of five different runs at night time.
In the data tables, the first column shows the functions being tested.
The second column shows the amount of data processed in units of lines or kilobytes.
or number of seeks in the case of \fIseek+rw\fP.
The remaining columns partitions into set of three each.
In each set of columns,
the first shows the amount of \fIcpu\fP time, the second \fIsystem\fP time,
and the third throughput rate which is computed by
dividing the total amount of data in K-bytes by the total cpu and system time.
The first three tests show results of block I/O where each block is of size 8Kb.
The \fIrevrd\fP test reads blocks in reverse order, i.e., starting
from the bottom of the file.
The next three tests show results of block I/O with blocks of size 757,
a number that does not divide 8K.
The tests \fIcopy&rw\fP and \fIsfmove\fP copy a large file
by either \f5fread()\fP and \f5fwrite()\fP or the \f5sfmove()\fP
primitive of \fISfio\fP.
The test \fIseek+rw\fP performs a sequence of seeking to a random location,
reading a block of size 8K, then copying that block to location 0.
The tests \fIfputc\fP and \fIfgetc\fP perform byte I/O.
The \fIfputs\fP, \fIfgets\fP and \f5revgets\fP tests perform line I/O.
\fIrevgets\fP reads lines in reverse order.
Both the \f5printf()\fP and \f5scanf()\fP tests exercise all
common conversion modes:
\f5%c\fP, \f5%d\fP, \f5%o\fP, \f5%x\fP, \f5%f\fP, \f5%e\fP and \f5%s\fP.
.P
The data will be presented in four tables.
Table 1 shows benchmark results on a two-processor Solbourne running SUN OS4.0,
comparing
\fISfio\fP, \fIbinary\fP (the \fIStdio\fP binary emulation package), and \fIStdio\fP.
In this version, \fISfio\fP uses memory mapping for all read streams.
This is why \f5sfmove()\fP is twice as fast as copying
data by block reads and block writes.
The functions \f5sfprintf()\fP and \f5sfscanf()\fP are much faster than
their counterparts in \fIStdio\fP due to new data conversion algorithms
which, among other improvements,
avoid division and multiplication which are expensive on a SPARC machine.
.P
.DF L F
.sp
.TS
box, center, tab(:);
c c||c ci c||c ci c||c ci c
c||c||c|c|c||c|c|c||c|c|c
c||r||r|r|r||r|r|r||r|r|r.
:::Sfio:::binary:::Stdio:
_
test:size:user:sys:Kb/s:user:sys:Kb/s:user:sys:Kb/s
=
fwrite:10000K:0.03:2.04:4813:0.09:2.13:4504:0.05:2.03:4796
fread:10000K:1.00:0.62:6163:1.07:0.66:5755:0.74:1.43:4608
revrd:10000K:0.31:1.62:5188:0.35:1.54:5298:0.79:1.39:4597
fw757:10000K:1.12:1.97:3236:1.25:2.06:3016:0.96:1.91:3481
fr757:10000K:1.20:0.58:5610:1.36:0.66:4944:0.96:1.38:4278
rev757:10000K:1.25:2.66:2560:1.71:2.58:2325:1.21:15.28:606
copy&rw:10000K:1.13:2.88:2496:1.25:2.94:2389:0.88:3.87:2105
sfmove:10000K:0.01:1.90:5228::::::
seek+rw:2000S:0.83:6.35:2268:0.90:6.07:2315:1.52:5.87:2185
fputc:5000K:3.92:1.14:987:4.29:1.08:929:4.18:1.14:940
fgetc:5000K:3.78:0.44:1184:4.02:0.23:1175:3.93:0.79:1059
fputs:50000L:2.15:1.03:1538:2.63:1.18:1285:2.07:1.08:1549
fgets:50000L:2.21:0.25:1981:2.58:0.33:1681:2.05:0.70:1783
revgets:50000L:2.56:1.25:1309:3.49:1.28:1028:4.15:36.59:122
fprintf:50000L:6.06:1.00:506:6.81:0.97:458:16.71:1.31:196
fscanf:50000L:6.56:0.29:518:7.47:0.41:454:17.90:1.00:187
.TE
.TB "Solbourne and SUN OS4.0"
.sp
.DE
.P
Table 2 compares \fISfio\fP and \fIStdio\fP
on a VAX 8650 running 4.3BSD.
Many parts of the native \fIStdio\fP package are implemented
in assembly code, especially \f5fputs()\fP, \f5fgets()\fP and \f5_doprnt()\fP,
the guts of \f5fprintf()\fP.
In the VAX version of \fISfio\fP,
\f5sfprintf()\fP is written entirely in C but
\f5sfgetr()\fP and \f5sfputr()\fP contain a few \f5asm()\fPs to
access hardware instructions for block search and block copy.
.P
.DF L F
.sp
.TS
box, center, tab(:);
c c||c ci c||c ci c
c||c||c|c|c||c|c|c
c||r||r|r|r||r|r|r.
:::Sfio:::Stdio:
_
test:size:user:sys:Kb/s:user:sys:Kb/s
=
fwrite:10000K:0.11:3.90:2496:1.11:4.09:1924
fread:10000K:0.11:1.78:5284:0.79:1.71:3996
revrd:10000K:0.18:1.88:4854:1.00:2.00:3336
fw757:10000K:0.97:4.32:1893:0.83:4.42:1904
fr757:10000K:1.07:1.78:3514:0.80:1.72:3976
rev757:10000K:1.47:4.98:1550:2.15:19.02:472
copy&rw:10000K:0.26:4.97:1911:1.87:5.19:1416
sfmove:10000K:0.04:4.87:2035:::
seek+rw:2000S:1.23:7.41:1851:3.02:7.87:1469
fputc:5000K:8.84:1.93:464:10.18:1.92:413
fgetc:5000K:6.74:1.07:640:8.00:1.04:553
fputs:50000L:1.33:2.15:1405:1.15:2.21:1456
fgets:50000L:1.21:0.93:2290:0.93:0.90:2659
revgets:50000L:2.29:2.30:1063:5.12:68.17:66
fprintf:50000L:18.86:1.49:174:22.28:1.67:147
fscanf:50000L:25.34:0.93:134:42.11:1.18:81
.TE
.TB "VAX 8650 and 4.3BSD"
.sp
.DE
.P
Table 3 compares \fISfio\fP and \fIStdio\fP
on an Intel 386 machine running System V Release 3.2.
Since this machine has limited disk space, we usesd smaller data sets.
.P
.DF L F
.sp
.TS
box, center, tab(:);
c c||c ci c||c ci c
c||c||c|c|c||c|c|c
c||r||r|r|r||r|r|r.
:::Sfio:::Stdio:
_
test:size:user:sys:Kb/s:user:sys:Kb/s
=
fwrite:1000K:0.02:2.91:340:0.02:2.87:346
fread:1000K:0.02:3.26:305:0.24:3.87:242
revrd:1000K:0.03:2.91:340:0.24:3.26:285
fw757:1000K:0.31:2.84:317:0.41:3.16:279
fr757:1000K:0.39:3.40:263:0.43:3.67:243
rev757:1000K:0.47:5.58:165:0.42:5.44:170
copy&rw:1000K:0.03:6.37:156:0.25:6.65:144
sfmove:1000K:0.01:6.60:151:::
seek+rw:500S:0.46:7.66:491:1.32:11.58:309
fputc:500K:3.81:1.50:94:3.73:1.75:91
fgetc:500K:3.44:1.70:97:3.16:1.86:99
fputs:10000L:2.72:2.60:185:2.66:3.09:169
fgets:10000L:2.58:3.28:167:2.58:3.83:152
revgets:10000L:2.32:4.18:149:4.20:32.74:26
fprintf:10000L:32.96:2.01:19:32.66:2.38:19
fscanf:10000L:30.60:2.56:20:38.60:2.63:16
.TE
.TB "Intel 386 and System V Release 3.2"
.sp
.DE
.P
Table 4 compares \fISfio\fP, \fIStdio\fP and Hume's \fIFio\fP.
This is done on a MIPS machine running UMIPS.
Again because of limited disk space, we used smaller data sets.
As \fIFio\fP does not have the same interface as \fIStdio\fP,
a few emulation macros were written to map the needed functions.
The poor performance of \fIFio\fP analogues
of \f5fputc()\fP and \f5fgetc()\fP is due to their implementation as subroutines.
.P
.DF L F
.sp
.TS
box, center, tab(:);
c c||c ci c||c ci c||c ci c
c||c||c|c|c||c|c|c||c|c|c
c||r||r|r|r||r|r|r||r|r|r.
:::Sfio:::Stdio:::Fio:
_
test:size:user:sys:Kb/s:user:sys:Kb/s:user:sys:Kb/s
=
fwrite:1000K:0.01:1.91:521:14.26:1.75:62:0.19:2.01:456
fread:1000K:0.01:0.91:1086:14.12:0.91:66:0.01:1.01:978
revrd:1000K:0.02:0.84:1152:13.84:0.92:67:0.04:0.79:1187
fw757:1000K:0.45:1.75:453:14.26:1.70:62:0.45:1.83:437
fr757:1000K:0.43:0.84:787:14.05:1.18:65:0.19:2.54:365
rev757:1000K:0.57:2.17:364:14.04:2.39:60:0.28:2.84:320
copy&rw:1000K:0.05:2.62:374:28.02:2.61:32:0.18:2.71:346
sfmove:1000K:0.01:2.65:376::::::
seek+rw:500S:0.74:5.79:612:111.92:6.53:33:0.92:6.23:559
fputc:500K:5.07:1.02:82:4.88:0.93:86:20.18:0.98:23
fgetc:500K:4.30:0.48:104:4.44:0.46:101:18.52:0.45:26
fputs:10000L:5.15:1.74:141:9.74:1.79:84:5.49:1.82:133
fgets:10000L:5.04:0.95:162:11.17:0.94:80:4.96:1.11:160
revgets:10000L:6.16:2.06:118:12.09:5.26:55:6.69:38.17:21
fprintf:10000L:20.74:1.18:31:60.08:1.40:11:52.33:1.29:13
fscanf:10000L:27.06:0.75:24:43.85:0.74:15:::
.TE
.TB "MIPS and UMIPS"
.sp
.DE
.H 2 "Reimplemented System Utilities"
.P
A goal of \fISfio\fP is to be sufficiently efficient so that
applications can be based on it without fear of performance loss.
To test this, we rewrote several standard UNIX utilities including
\fIksh\fP, \fIcat\fP, \fIwc\fP, \fIcut\fP, \fIpack\fP, and \fIunpack\fP.
\fIKsh\fP is a complex program that exercises virtually all aspects of \fISfio\fP.
\fICat\fP is an I/O bound program and, to a lesser extent, so is \fIwc\fP.
\fICut\fP is a non-trivial example where I/O is still likely to matter.
\fIPack\fP and \fIunpack\fP show how the Huffman coding
can be implemented as a discipline. For example, \fIunpack\fP entails
opening a packed file, setting the unpacking discipline, then transferring data
as if it is in plain text.
.P
Table 5 compares different versions of \fIcat\fP, \fIwc\fP, \fIcut\fP, \fIpack\fP
and \fIunpack\fP. The data were obtained on a two-processor Solbourne
running SUN OS4.0.
Each program is run with two different datasets, one of size 50000 bytes
and the other 5 million bytes. The data for each utility
will be presented in two successive rows, the smaller dataset first, then the
larger one. Where it is applicable, we show data of three different versions
of the tool, SUN OS, System V, and our implementation.
Note that \fIcut\fP, \fIpack\fP and \fIunpack\fP are the same on SUN OS and System V.
.P
.DF L F
.sp
.TS
box, center, tab(:);
c||ci s||ci s||ci s
c||c|c||c|c||c|c
c||r|r||r|r||r|r.
:SUN-OS:System V:Sfio
_
command:user:sys:user:sys:user:sys
=
cat:0.01:0.08:0.02:0.09:0.01:0.11
:0.01:0.09:0.05:1.08:0.03:0.17
_
wc:0.10:0.09:0.04:0.08:0.04:0.10
:5.72:0.75:2.55:0.69:1.57:0.68
_
cut:0.11:0.11:0.11:0.11:0.05:0.10
:9.69:1.21:9.69:1.21:3.79:1.30
_
pack:0.11:0.16:0.11:0.16:0.09:0.11
:9.92:3.76:9.92:3.76:6.87:1.40
_
unpack:0.24:0.13:0.24:0.13:0.10:0.10
:19.65:2.96:19.65:2.96:6.90:1.59
.TE
.TB "Reimplemented System Commands"
.sp
.DE
.P
The data shows that
our versions of the standard utilities perform at the same level
or better than the other versions.
The \fIcat\fP test shows that, even with the direct use of
\f5read()\fP and \f5write()\fP for data transfer,
the System V \fIcat\fP still lost in system time
to its SUN-OS and \fISfio\fP counterparts because the latter
use memory mapping.
The data for \fIwc\fP shows another aspect of efficiency at the library level.
In this case, the SUN-OS version of \fIwc\fP consumes much more cpu time than
System V version because it uses \f5getc()\fP for input
while the System V version uses \f5fread()\fP.
The \fISfio\fP version improves on the System V version via the use of \f5sfpeek()\fP
and a new algorithm for detecting word and line boundaries.
The big performance improvement in \fIunpack\fP
is due in large part to a new unpacking algorithm which makes use of
\f5sfpeek()\fP and discipline. The benefit of coding \fIunpack\fP as
a discipline is that the discipline can be reused directly in other contexts.
.H 1 "Conclusions"
.P
We presented \fISfio\fP, a new library for stream input/output that
can be used as a replacement for \fIStdio\fP, the standard I/O library for C.
A number of deficiencies in \fIStdio\fP were described.
We discussed how \fISfio\fP avoids such deficiencies.
Performance results from our own benchmark program
and a few reimplemented utilities were shown
The data shows that \fISfio\fP performs as well as or better than \fIStdio\fP
on popular hardware platforms running different UNIX versions.
This is nice especially since many versions of \fIStdio\fP that we
compare \fISfio\fP against have been hand-tuned in assembly code.
Standard utilities gain substantial efficiency when
reimplemented based on \fISfio\fP.
Despite many new features, the total size of the library is under 50K
on a SPARC1+ workstation.
.P
Beyond a being a better replacement for \fIStdio\fP,
\fISfio\fP introduces a number of new abstractions.
Streams have been generalized to represent both files and in-core memory areas.
Fast access to internal stream
buffers is provided so that applications should never have to revert
to bare system calls for efficiency.
New methods are provided to handle synchronization of collections of streams,
build new virtual streams by stacking and change stream I/O disciplines.
Stream disciplines allow applications to tune
the handling of exceptions and to change
or augment the processing power of the underlying system calls.
Many routine data processing tasks can be modeled and implemented by appropriate 
disciplines resulting in better code organization and code reuse.
In our experiences,
the combination of performance and new features makes \fISfio\fP a powerful
tool that enhances our ability to write programs.
.sp
.SG
.P
.HU "Acknowledgement"
Glenn Fowler helped firm up the design and implementation of \fISfio\fP.
Griff Smith contributed the main idea and code behind the new
decimal conversion algorithm for \f5sfprintf()\fP.
Finally, thanks are due to Andrew Hume and Griff Smith
who helped to improve the presentation of the paper.
.sp
.HU "References"
.VL 5
.LI
[AN] ANSI x3.159-1989,
\fIAmerican National Standard for Information Systems - Programming Language - C\fP,
Amer. Nat. Sta. Ins., 1990.
.LI
[Hu] A. Hume, \fIA Tale of Two Greps\fP, Soft. Prac. & Exp., v.18, pp.1063-1072, 1988.
.LI
[KR] B. Kernighan & D. Ritchie, \fIThe C Programming Language\fP, Prentice Hall, 1978.
.LI
[St] B. Strousup, \fIThe C++ Programming Language\fP, Addison-Wesley, 1986.
.bp
.HU "Appendix: Source \fIstdio\fP Emulation Header File"
.P
Below is the full text of the source emulation header file
for \fIStdio\fP. Note that the macro \f5_SFSIZEOF()\fP provides
an added security check for a frequent use of \f5gets()\fP where
the buffer is a declared fixed size array.
.P
.ft 5
.ta 1.75 1.75
.nf
#ifndef _SFSTDIO_H       /* protect against multiple #includes */
#define _SFSTDIO_H

#include                 <sfio.h>

#define _IOFBF           0
#define _IONBF           1
#define _IOLBF           2
#define L_ctermid        9
#define L_cuserid        9
#define P_tmpdir         "/usr/tmp/"
#define L_tmpnam         (sizeof(P_tmpdir)+15)

extern char              *ctermid(char*);
extern char              *cuserid(char*);
extern char              *tmpnam(char*);
extern char              *tempnam(char*);
extern char              *_stdgets(char*, int n);
extern int               _stdprintf(const char*, ...);
extern int               _stdsprintf(char*, const char*, ...);
extern int               _stdscanf(const char*, ...);
extern int               _stdsetvbuf(Sfile_t*, char*, int, int);

#define stdin            sfstdin
#define stdout           sfstdout
#define stderr           sfstderr
#define FILE             Sfile_t
#define BUFSIZ           SF_BUFSIZE

#define fopen(f,m)       sfopen((Sfile_t*)0,f,m)
#define freopen(f,m,p)   sfopen(p,f,m)
#define popen(cmd,m)     sfpopen((Sfile_t*)0,cmd,m)
#define fdopen           _stdopen
#define tmpfile()        sftmp(SF_BUFSIZE)
#define fclose(f)        sfclose(f)
#define pclose(f)        sfclose(f)

#define fwrite(p,s,n,f)  ((_Sfi = sfwrite(f,p,(s)*(n))) <= 0 ? _Sfi : _Sfi/(s))
#define fputc(c,f)       sfputc(f,c)
#define putc(c,f)        sfputc(f,c)
#define putw(w,f)        (_Sfi = (int)w, sfwrite(f,&_Sfi,sizeof(int)) <= 0 ? 1 : 0)
#define putchar(c)       sfputc(sfstdout,c)
#define fputs(s,f)       sfputr(f,s,-1)
#define puts(s)          sfputr(sfstdout,s,'\n')
#define fprintf          sfprintf
#define vfprintf         sfvprintf
#define vprintf(f,a)     sfvprintf(sfstdout,f,a)
#define _doprnt(fm,a,f)  sfvprintf(f,fm,a)
#define vsprintf         _stdvsprintf
#define printf           _stdprintf
#define sprintf          _stdsprintf

#define fread(p,s,n,f)   ((_Sfi = sfread(f,p,(s)*(n))) <= 0 ? _Sfi : _Sfi/(s))
#define fgetc(f)         sfgetc(f)
#define getc(f)          sfgetc(f)
#define getw(f)          (sfread(f,&_Sfi,sizeof(int)) == sizeof(int) ? _Sfi : -1)
#define getchar()        sfgetc(sfstdin)
#define ungetc(c,f)      sfungetc(f,c)
#define fgets(s,n,f)     _stdgets(f,s,n,0)
#define _SFSIZEOF(s)     (sizeof(s) != sizeof(char*) ? sizeof(s) : BUFSIZ)
#define gets(s)          _stdgets(s,_SFSIZEOF(s),1)
#define fscanf           sfscanf
#define vfscanf          sfvscanf
#define _doscan          sfvscanf
#define sscanf           sfsscanf
#define vscanf(f,a)      sfvscanf(sfstdin,f,a)
#define scanf            _stdscanf
#define vsscanf          _stdvssanf

#define fflush(f)        sfsync(f)
#define fseek(f,o,t)     (sfseek(f,o,t) < 0L ? -1 : 0)
#define rewind(f)        sfseek((f),0L,0)
#define ftell(f)         sftell(f)
#define setbuf(f,b)      sfsetbuf(f,b,(b) ? BUFSIZ : 0)
#define setbuffer(f,b,n) sfsetbuf(f,b,n)
#define setlinebuf(f)    sfset(f,SF_LINE,1)
#define setvbuf          _stdsetvbuf

#define fileno(f)        sffileno(f)
#define feof(f)          sfeof(f)
#define ferror(f)        sferror(f)
#define clearerr(f)      (sfclrerr(f),sfclrlock(f))

#endif /* _SFSTDIO_H */
.fi
SHAR_EOF
fi # end of overwriting check
if test -f './README'
then
	echo shar: will not over-write existing file "'./README'"
else
cat << \SHAR_EOF > './README'
This directory hierarchy contains the source code for the <sfio> library.
The library is written so that it will compile with KR-C, ANSI-C or C++.
It is also highly portable, having been ported to most known UNIX systems.

We believe that <sfio> is inherently a better interface than <stdio>.
However, to ease the transition from <stdio> to <sfio>, we provide two
separate compatibility packages. The source level compatibility package
consists of a header file <stdio.h> that translates <stdio> to <sfio> and
a few functions that <sfio> does not directly support. If you have source
written based on <stdio>, you can recompile using this header file to use
the <sfio> functions. The binary compatibility package allows your applications
to link code that has been compiled with <stdio> with the <sfio> library.
By necessity, much code is pulled in so if you do this, don't be surprised
by the size of your program.

If you get the package and use it, we would like feedbacks on how it works out
for you. Let us know of any problems you run into and of any ideas that you may
have on how to improve the library.

Below is the address to contact:
	Phong Vo
	AT&T Bell Labs, MH-3C-536A
	600 Mountain Ave.
	Murray Hill, NJ07974
	e-mail: att!ulysses!kpv or kpv@ulysses.att.com
	Phone: (908) 582-4869

Good luck.
David Korn and Phong Vo.

----DIRECTORY STRUCTURE---------------------------------------------------------
The sfio directory structure is as follows:

./*.[ch]			sfio source files
       	./Doc/*			a paper and a manual page
	./Disc/*		examples of io disciplines
	./FEATURE/*		header files to describe the features
				required for the local system (more below).
	./Sfio_f/*.c		function versions of sfio macros
	./Stdio_s/*.[ch]	code for the source level <stdio>
				compatibility package
	./Stdio_b/*.[ch]	code for the binary level <stdio>
				compatibility package

----IO DISCIPLINES--------------------------------------------------------------
IO discipline is a way for application to define filters that process data
input/output from/to a stream. See the manual page for details on how to insert
disciplines into a stream. The subdirectory Disc contains examples of a few
common disciplines. As disciplines is a good source of reusable code, we
encourage you to contribute any interesting disciplines that you come up with.
Sharing reusable code means that the name space must be managed. Therefore, we
recommend that each discipline package provides the following public interface:
	Sfdisc_t* dcnewXXX(arguments):
		Creates a discipline of the type XXX. For example, below is the
		function to create a discipline that duplicates output to a
		stream to another stream:
			dcnewtee(Sfile_t* tee)
	dcdelXXX(Sfdisc_t* disc):
		Frees the given discipline and any related resources.

----MAKING THE LIBRARIES--------------------------------------------------------
To make the libraries, you should check the FEATURE requirements below
then use "make" or "nmake". After the libraries are made, you will have:

	sfio.h:	the header file to be included by application code that wants
		to use sfio.
	libsfio.a: the sfio library.
	stdio.h: the header file to be used by applications that were written
		based on <stdio> but want to be recompiled for <sfio>.
		This file will be in ./Stdio_s/stdio.h.
	libstdio.a: the <stdio> binary compatibility package. This allows code
		already compiled with <stdio> to link with <sfio>.

You should move these files to wherever is appropriate.

----FEATURE DEFINITIONS FOR LOCAL SYSTEMS---------------------------------------
For efficiency and portability, <sfio> is configured based on what your local
system supports. Configuration information is defined in the files in the
FEATURE subdirectory. The configuration data required ranges from defining
what system calls are available on a system to the best local methods for
accessing memory. Normally, the FEATURE files are automatically generated using
a feature probing language. This mechanism is available with nmake2.3.
However, nmake2.3 is proprietary so we cannot release the feature probing tool
outside of AT&T. This, of course, does not pertain to people inside AT&T who
can obtain this tool by contacting us directly. For people outside AT&T,
we include here a set of example FEATURE files that were generated on a
SPARCstation 1+ running SUN4.0 UNIX system. You should modify these files as
necessary. Details for how to do that are given next.

Each FEATURE parameter is defined to be either 1 or 0 to indicate whether or not
the respected feature exists in the local system. For example, if your local
system supports memory mapping in the form of the system call mmap(), then the
file FEATURE/mmap should contain the following line:
	#define _lib_mmap	1
On the other hand, if your system does not have this feature, you should define:
	#define _lib_mmap	0

Below are brief descriptions of the parameters required in each FEATURE file:

FEATURE/atexit:
	_lib_atexit:	
	_lib_onexit:
	_lib_on_exit:
		These corresponds to the functions atexit(), onexit(), and
		on_exit(). These functions are used to install actions that
		will be performed when the program exits. The given file
		indicates that on SUN4.0, there is an atexit() function but
		not the other two.

FEATURE/blksize:
	_stat_blksize:
		This is set to 1 if the "struct stat" structure contains
		the field "blksize" that defines the optimal block size to use
		when doing io to a file descriptor. This feature is common to
		BSD-derived UNIX systems.

FEATURE/double:
	_long_double:
		This is set to 1 if your compile system allows the use
		of "long double". This is supported by ANSI-C.

FEATURE/filio:
	_hdr_filio:
	_sys_filio:
		One of these should be defined to 1 if your local system
		required either of the header files <filio.h> or <sys/filio.h>
		to be included for doing file control, fnctl(). This is common
		to Bell Labs Research UNIX systems.

FEATURE/getpagesize:
	_lib_getpagesize:
		This is set to 1 if the function getpagesize() exists. This is
		common to systems with paging.

FEATURE/mktemp:
	_lib_mktemp:
	_lib_remove:
	_lib_unlink:
		The appropriate parameter is set to 1 if the corresponding
		function mktemp(), remove() or unlink() exists. These functions
		are used to remove files.

FEATURE/mmap:
	_hdr_mman:
	_sys_mman:
		The appropriate parameter is set to 1 if the corresponding
		header file <mman.h> or <sys/mman.h> must be included to use
		memory mapping.
	_lib_mmap:
		This is set to 1 if the system call mmap() exists. Sfio uses
		memory mapping to speed up IO. If you don't feel that mmap()
		is a big gain on your system, you could just set this to 0.

FEATURE/native:
	_vax_asm:
		This is set to 1 if your system is a VAX with appropriate
		assembly instructions to search/copy characters. As this is
		highly dependent on the compiler in use, if you are not sure,
		don't set it to 1. You can look at one of the files sfread.c
		or sfgetr.c to get an idea of whether or not it should be set.
	_i386_cvt:
		Sfio has its own algorithms for floating point conversions,
		specifically, fcvt(), ecvt() and strtod(). These algorithms
		are very good on many architectures. In fact, they can outrun
		their system-provided counterparts by two to four times.
		However, the 386 (and probably 486) SYSV UNIX systems seems to
		have implemented better conversion functions. Set this to 1 as
		you see fit.

FEATURE/peek:
	_stream_peek:
		This is set to 1 if your system supports streams (typically, a
		System V derivative). This and _socket_peek below is used to 
		ensure that block operations on shared unseekable streams
		will not consume more data than necessary.
	_socket_peek:
		This is set to 1 if your system supports sockets (typically, a
		BSD derivative). Note that certain OS such as SUN OS4.0 will
		support both stream and socket. It is ok to define both
		_stream_peek and _socket_peek to 1.

FEATURE/poll:
	_lib_poll:
		This is set to 1 if your system supports the poll() system call
		(System V).
	_lib_select:
		This is set to 1 if your system supports the select() system call
		(BSD).

FEATURE/pragma:
	_pragma_weak:
		SYSV Release 4.0 systems provides ANSI-compatible libraries.
		#pragma weak is a way to hide certain local names in the library
		from the application. This affects a few of the functions
		in the <stdio> binary compatibility package such as
		_flsbuf or _filbuf.

FEATURE/prototype:
	_proto_open:
		The open() system call in ANSI-systems is a vararg function.
		This is set to 1 if a prototype of open() is already defined in
		the header file <fcntl.h>.

FEATURE/stdarg:
	_CC_stdarg:
		A design bug in some C++ compilation system is that if you
		include <stdarg.h>, you'll also include <stdio.h>. If you are
		building <sfio> using a C++ compiler with this bug, define
		this to be 1. If your compilation still fails after doing this,
		complain to your C++ vendor. On second thought, just complain.

FEATURE/stdio:
	_FILE_cnt:
	_FILE_ptr:
	_FILE_flag:
	_FILE_file:
	_FILE_base:
	_FILE_bufsiz:
		In order to accurately simulate <stdio> for the binary
		compatibility package, <sfio> must know if the fields:
		_cnt, _ptr, _flag, _file, _base, and _bufsiz exist in the FILE
		structure. Set these to 1 as appropriate. You can usually find
		this information by looking at /usr/include/stdio.h.

FEATURE/string:
	_lib_bcopy:
		This is set to 1 if the function bcopy() should be used to copy
		memory from one area to another. Otherwise, memcpy() is used.
	_lib_bzero:
		This is set to 1 if bzero() should be used to clear memory.
		Otherwise, memset() is used.
	_lib_memccpy:
		This is set to 1 if the function memccpy() exists.
	_lib_memchr:
		This is set to 1 if the function memchar() exists.

FEATURE/vfork:
	_hdr_vfork:
	_sys_vfork:
		A misfeature (bug?) on SUN systems is that either <vfork.h> or
		<sys/vfork.h> must be included if the system call vfork() is
		used. Check to see if either of these header file exists and
		set the appropriate parameter to 1.
	_lib_vfork:
		This is set to 1 if the system call vfork() exists.

FEATURE/waitpid:
	_lib_waitpid:
		This is set to 1 if your system has a call waitpid() to wait for
		the death of a child process with a given process id.
SHAR_EOF
fi # end of overwriting check
if test -f './FILES'
then
	echo shar: will not over-write existing file "'./FILES'"
else
cat << \SHAR_EOF > './FILES'
./FEATURE/atexit
./FEATURE/string
./FEATURE/mmap
./FEATURE/vfork
./FEATURE/filio
./FEATURE/mktemp
./FEATURE/waitpid
./FEATURE/getpagesize
./FEATURE/double
./FEATURE/blksize
./FEATURE/native
./FEATURE/prototype
./FEATURE/stdio
./FEATURE/pragma
./FEATURE/stdarg
./FEATURE/poll
./FEATURE/peek
./sfclose.c
./sfclrlock.c
./sfcvt.c
./sfdisc.c
./sfdlen.c
./sfexcept.c
./sfexit.c
./sfextern.c
./sffilbuf.c
./sfflsbuf.c
./sfgetd.c
./sfgetl.c
./sfgetr.c
./sfgetu.c
./sfhdr.h
./sfio.h
./sfllen.c
./sfmode.c
./sfmove.c
./sfnew.c
./sfnotify.c
./sfnputc.c
./sfopen.c
./sfpeek.c
./sfpool.c
./sfpopen.c
./sfprintf.c
./sfprints.c
./sfpurge.c
./sfputd.c
./sfputl.c
./sfputr.c
./sfputu.c
./sfrd.c
./sfread.c
./sfscanf.c
./sfseek.c
./sfset.c
./sfsetbuf.c
./sfsetfd.c
./sfsize.c
./sfsk.c
./sfstack.c
./sfstrtod.c
./sfsync.c
./sftable.c
./sftell.c
./sftmp.c
./sfungetc.c
./sfvprintf.c
./Stdio_s/stdgets.c
./Stdio_s/stdio.h
./Stdio_s/stdopen.c
./Stdio_s/stdprintf.c
./Stdio_s/stdscanf.c
./Stdio_s/stdsprnt.c
./Stdio_s/stdvbuf.c
./Stdio_s/stdvsprnt.c
./Stdio_s/stdvsscn.c
./Stdio_s/Makefile
./Stdio_b/clearerr.c
./Stdio_b/doprnt.c
./Stdio_b/doscan.c
./Stdio_b/fclose.c
./Stdio_b/fdopen.c
./Stdio_b/feof.c
./Stdio_b/ferror.c
./Stdio_b/fflush.c
./Stdio_b/fgetc.c
./Stdio_b/fgets.c
./Stdio_b/filbuf.c
./Stdio_b/fileno.c
./Stdio_b/flsbuf.c
./Stdio_b/fopen.c
./Stdio_b/fprintf.c
./Stdio_b/fputc.c
./Stdio_b/fputs.c
./Stdio_b/fread.c
./Stdio_b/freopen.c
./Stdio_b/fscanf.c
./Stdio_b/fseek.c
./Stdio_b/ftell.c
./Stdio_b/fwrite.c
./Stdio_b/getchar.c
./Stdio_b/gets.c
./Stdio_b/getw.c
./Stdio_b/pclose.c
./Stdio_b/popen.c
./Stdio_b/printf.c
./Stdio_b/putchar.c
./Stdio_b/puts.c
./Stdio_b/putw.c
./Stdio_b/rewind.c
./Stdio_b/scanf.c
./Stdio_b/setbuf.c
./Stdio_b/setbuffer.c
./Stdio_b/setlinebuf.c
./Stdio_b/setvbuf.c
./Stdio_b/sfstdio.c
./Stdio_b/sprintf.c
./Stdio_b/sscanf.c
./Stdio_b/stdextern.c
./Stdio_b/stdstream.c
./Stdio_b/tmpfile.c
./Stdio_b/ungetc.c
./Stdio_b/vfprintf.c
./Stdio_b/vfscanf.c
./Stdio_b/vprintf.c
./Stdio_b/vscanf.c
./Stdio_b/vsprintf.c
./Stdio_b/vsscanf.c
./Stdio_b/fpurge.c
./Stdio_b/Makefile
./Stdio_b/sfstdhdr.sh
./Stdio_b/putc.c
./Stdio_b/getc.c
./Sfio_f/_sfclrerr.c
./Sfio_f/_sfecvt.c
./Sfio_f/_sfeof.c
./Sfio_f/_sferror.c
./Sfio_f/_sffcvt.c
./Sfio_f/_sffileno.c
./Sfio_f/_sfgetc.c
./Sfio_f/_sfgetl.c
./Sfio_f/_sfgetu.c
./Sfio_f/_sfputc.c
./Sfio_f/_sfputd.c
./Sfio_f/_sfputl.c
./Sfio_f/_sfputu.c
./Sfio_f/_sfslen.c
./Sfio_f/_sfstacked.c
./Sfio_f/_sfulen.c
./Sfio_f/Makefile
./sfvscanf.c
./sfwr.c
./sfwrite.c
./Iffile
./Makefile
./makefile
./sfpoll.c
./Doc/sfio.3
./Doc/sfio.tm
./README
./FILES
./Disc/disc.h
./Disc/dctee.c
./Disc/dcfilter.c
./Disc/dchdr.h
./Disc/dcsubstream.c
./Disc/dcskable.c
./sfpkrd.c
SHAR_EOF
fi # end of overwriting check
if test -f './Disc/disc.h'
then
	echo shar: will not over-write existing file "'./Disc/disc.h'"
else
cat << \SHAR_EOF > './Disc/disc.h'
#include	<sfio.h>

#ifdef __cplusplus
extern "C" {
#endif
extern Sfdisc_t*	dcnewtee _ARG_((Sfile_t*));
extern int		dcdeltee _ARG_((Sfdisc_t*));

extern Sfdisc_t*	dcnewfilter _ARG_((char*));
extern int		dcdelfilter _ARG_((Sfdisc_t*));

extern Sfdisc_t*	dcnewsubstream _ARG_((Sfile_t*, long, long));
extern int		dcdelsubstream _ARG_((Sfdisc_t*));
#ifdef __cplusplus
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './Disc/dctee.c'
then
	echo shar: will not over-write existing file "'./Disc/dctee.c'"
else
cat << \SHAR_EOF > './Disc/dctee.c'
#include	"dchdr.h"

/*	A discipline to tee the output to a stream to another stream.
**	This is similar to what the "tee" program does. As implemented
**	this discipline only works with file streams.
**
**	Sfdisc_t* dcnewtee(Sfile_t* tee):
**		Create a discipline that would tee data to a stream to
**		the stream "tee".
**	int dcdeltee(Sfdisc_t* teedisc):
**		Delete the discipline.
**
**	Written by Kiem-Phong Vo (03/05/92).
*/

/* the discipline structure for tee-ing */
typedef struct _tee_
{
	Sfdisc_t	disc;	/* the sfio discipline structure */
	Sfile_t*	tee;	/* the stream to tee to */
	int		status;	/* if tee stream is still ok */
} Tee_t;

/*	write to the teed stream.  */
#ifdef __STD_C
static teewrite(Sfile_t* f, char* buf, int size, Sfdisc_t* disc)
#else
static teewrite(f,buf,size,disc)
Sfile_t* 	f;	/* the stream being written to */
char*		buf;	/* the buffer of data being output */
int		size;	/* the data size */
Sfdisc_t*	disc;	/* the tee discipline */
#endif
{
	reg Tee_t	*tdisc = (Tee_t*)disc;

	/* tee data if still ok */
	if(tdisc->status == 0 && sfwrite(tdisc->tee,buf,size) != size)
		tdisc->status = -1;

	/* do the actual write */
	return sfwr(f,buf,size,disc);
}

/* on close, remove the discipline */
#ifdef __STD_C
static teeexcept(Sfile_t* f, int type, Sfdisc_t* disc)
#else
static teeexcept(f,type,disc)
Sfile_t*	f;
int		type;
Sfdisc_t*	disc;
#endif
{
	if(type == SF_CLOSE)
	{	reg Sfdisc_t*	pop;
		if((pop = sfdisc(f,SF_POPDISC)) != disc)	/* hmm! */
			sfdisc(f,pop);
		else	dcdeltee(disc);
	}

	return 0;
}

#ifdef __STD_C
Sfdisc_t* dcnewtee(Sfile_t* tee)
#else
Sfdisc_t* dcnewtee(tee)
Sfile_t*	tee;	/* stream to tee to */
#endif
{
	reg Tee_t*	disc;

	if(!(disc = (Tee_t*)malloc(sizeof(Tee_t))) )
		return NIL(Sfdisc_t*);

	disc->disc.readf = NIL(int(*)_ARG_((Sfile_t*,char*,int,Sfdisc_t*)) );
	disc->disc.seekf = NIL(long(*)_ARG_((Sfile_t*,long,int,Sfdisc_t*)) );
	disc->disc.writef = teewrite;
	disc->disc.exceptf = teeexcept;
	disc->tee = tee;
	disc->status = 0;

	return (Sfdisc_t*)disc;
}

#ifdef __STD_C
dcdeltee(Sfdisc_t* disc)
#else
dcdeltee(disc)
Sfdisc_t*	disc;	/* tee discipline being ended */
#endif
{
	free((char*)disc);
	return 0;
}

#ifdef PROGRAM
/*	Here is the UNIX tee program
*/

#ifdef __STD_C
main(int argc, char** argv)
#else
main(argc,argv)
int	argc;
char**	argv;
#endif
{
	Sfdisc_t*	disc;
	Sfile_t*	tee;

	if(argc <= 1)
	{	sfprintf(sfstderr,"Called as: %s file\n", argv[0]);
		return -1;
	}

	/* open the file to tee data to */
	if(!(tee = sfopen(NIL(Sfile_t*),argv[1],"w")) )
	{	sfprintf(sfstderr,"%s: Can't create %s\n", argv[0], argv[1]);
		return -1;
	}

	/* make a tee discipline and insert it */
	if(!(disc = dcnewtee(tee)) )
		return -1;
	sfdisc(sfstdout,disc);

	/* now just copy data from stdin to stdout */
	sfmove(sfstdin,sfstdout,-1L,-1);

	return 0;
}

#endif /*PROGRAM*/
SHAR_EOF
fi # end of overwriting check
if test -f './Disc/dcfilter.c'
then
	echo shar: will not over-write existing file "'./Disc/dcfilter.c'"
else
cat << \SHAR_EOF > './Disc/dcfilter.c'
#include	"dchdr.h"
#include	<errno.h>
#include	<fcntl.h>

/*	Discipline to invoke UNIX processes to filter data.
**	These processes must be able to fit in pipelines.
**
**	Sfdisc_t* dcnewfilter(char* program):
**		Create a discipline that runs "program" to process data
**		from a stream before letting the application see such data.
**	int dcdelfilter(Sfdisc_t* disc):
**		Delete the discipline.
**
**	Written by Kiem-Phong Vo (03/06/92)
*/

typedef struct _filter_
{
	Sfdisc_t	disc;		/* discipline structure */
	Sfile_t*	filter;		/* the filter stream */
	char		raw[1024];	/* raw data buffer */
	char		*next;		/* remainder of data unwritten to pipe */
	char		*endb;		/* end of data */
} Filter_t;

/* read data from the filter */
#ifdef __STD_C
static filterread(Sfile_t* f, char* buf, int n, Sfdisc_t* disc)
#else
static filterread(f, buf, n, disc)
Sfile_t*	f;	/* stream reading from */
char*		buf;	/* buffer to read into */
int		n;	/* number of bytes requested */
Sfdisc_t*	disc;	/* discipline */
#endif
{
	reg Filter_t*	fdisc;
	int		r, w;

	fdisc = (Filter_t*)disc;
	for(;;)
	{	if(!fdisc->next)
			fdisc->next = fdisc->endb = fdisc->raw;
		else
		{	/* try to get data from filter, if any */
			errno = 0;
			if((r = sfread(fdisc->filter,buf,n)) > 0)
				return r;
			if(errno != EWOULDBLOCK)
				return 0;
		}

		/* get some raw data to stuff down the pipe */
		if(fdisc->next >= fdisc->endb)
		{	if((r = sfrd(f,fdisc->raw,sizeof(fdisc->raw),disc)) > 0)
			{	fdisc->next = fdisc->raw;
				fdisc->endb = fdisc->raw+r;
			}
			else
			{	/* eof, close write end of pipes */
				sfset(fdisc->filter,SF_READ,0);
				close(sffileno(fdisc->filter));
				sfset(fdisc->filter,SF_READ,1);
			}
		}

		if((w = fdisc->endb - fdisc->next) > 0)
		{	errno = 0;
			if((w = sfwrite(fdisc->filter,fdisc->next,w)) > 0)
				fdisc->next += w;
			else if(errno != EWOULDBLOCK)
				return 0;
			/* pipe is full, sleep for a while, then continue */
			else	sleep(1);
		}
	}
}

/* for the duration of this discipline, the stream is unseekable */
#ifdef __STD_C
static long filterseek(Sfile_t* f, long addr, int offset, Sfdisc_t* disc)
#else
static long filterseek(f, addr, offset, disc)
Sfile_t*	f;
long		addr;
int		offset;
Sfdisc_t*	disc;
#endif
{	f = NIL(Sfile_t*);
	addr = 0;
	offset = 0;
	disc = NIL(Sfdisc_t*);
	return -1L;
}

/* on close, remove the discipline */
#ifdef __STD_C
static filterexcept(Sfile_t* f, int type, Sfdisc_t* disc)
#else
static filterexcept(f,type,disc)
Sfile_t*	f;
int		type;
Sfdisc_t*	disc;
#endif
{
	if(type == SF_CLOSE)
	{	reg Sfdisc_t*	pop;
		if((pop = sfdisc(f,SF_POPDISC)) != disc)	/* hmm! */
			sfdisc(f,pop);
		else	dcdelfilter(disc);
	}

	return 0;
}

#ifdef __STD_C
Sfdisc_t* dcnewfilter(char* program)
#else
Sfdisc_t* dcnewfilter(program)
char*	program;	/* program to run as a filter */
#endif
{
	reg Filter_t*	disc;
	reg Sfile_t*	filter;

	/* open filter for read&write */
	if(!(filter = sfpopen(NIL(Sfile_t*),program,"r+")) )
		return NIL(Sfdisc_t*);

	/* unbuffered so that write data will get to the pipe right away */
	sfsetbuf(filter,NIL(char*),0);

	/* make the write descriptor nonblocking */
	sfset(filter,SF_READ,0);
	fcntl(sffileno(filter),F_SETFL,FNDELAY);
	sfset(filter,SF_READ,1);

	/* same for the read descriptor */
	sfset(filter,SF_WRITE,0);
	fcntl(sffileno(filter),F_SETFL,FNDELAY);
	sfset(filter,SF_WRITE,1);

	if(!(disc = (Filter_t*)malloc(sizeof(Filter_t))) )
	{	sfclose(filter);
		return NIL(Sfdisc_t*);
	}

	disc->disc.readf = filterread;
	disc->disc.seekf = filterseek;
	disc->disc.writef = NIL(int(*)_ARG_((Sfile_t*,char*,int,Sfdisc_t*)) );
	disc->disc.exceptf = filterexcept;
	disc->filter = filter;
	disc->next = disc->endb = NIL(char*);

	return (Sfdisc_t*)disc;
}


#ifdef __STD_C
dcdelfilter(Sfdisc_t* disc)
#else
dcdelfilter(disc)
Sfdisc_t*	disc;
#endif
{
	sfclose(((Filter_t*)disc)->filter);
	free((char*)disc);
	return 0;
}


#ifdef PROGRAM
/*	The below program uncompresses a compressed file,
**	then print all the lines contain the word "define"
**	in upper case.
*/
main()
{
	Sfdisc_t*	uncompress;
	Sfdisc_t*	upper;
	Sfdisc_t*	grep;

	/* insert the uncompress discipline first */
	if(!(uncompress = dcnewfilter("uncompress")) )
		return -1;
	sfdisc(sfstdin,uncompress);

	/* now insert the grep discipline */
	if(!(grep = dcnewfilter("grep define")) )
		return -1;
	sfdisc(sfstdin,grep);

	/* now insert the lower->upper case discipline */
	if(!(upper = dcnewfilter("tr a-z A-Z")) )
		return -1;
	sfdisc(sfstdin,upper);

	/* now just copy data from stdin to stdout */
	sfmove(sfstdin,sfstdout,-1L,-1);

	return 0;
}

#endif /*PROGRAM*/
SHAR_EOF
fi # end of overwriting check
if test -f './Disc/dchdr.h'
then
	echo shar: will not over-write existing file "'./Disc/dchdr.h'"
else
cat << \SHAR_EOF > './Disc/dchdr.h'
#include	"disc.h"

#define NIL(type)	((type)0)
#define reg		register

#ifdef __cplusplus
extern "C" {
#endif
extern char*	malloc _ARG_((int));
extern void	free _ARG_((char*));
#ifdef __cplusplus
}
#endif
SHAR_EOF
fi # end of overwriting check
if test -f './Disc/dcsubstream.c'
then
	echo shar: will not over-write existing file "'./Disc/dcsubstream.c'"
else
cat << \SHAR_EOF > './Disc/dcsubstream.c'
#include	"dchdr.h"


/*	Discipline to treat a contiguous subset of a stream as a stream
**	in its own right. The hard part in all this is to allow multiple
**	segments of the stream to be used as subfiles at the same time.
**
**	Sfdisc_t* dcnewsubstream(Sfile_t* f, long offset, long extent):
**		Create a new discipline that would make some other stream
**		a substream of the stream "f" starting at location "offset"
**		and having size "extent". If "extent" is <0, the substream
**		covers everything starting from "offset".
**	dcdelsubstream(Sfdisc_t* disc):
**		Delete a substream.
**
**	Written by David G. Korn and Kiem-Phong Vo (03/06/92)
*/

typedef struct _subfile_
{
	Sfdisc_t	disc;	/* sfio discipline */
	Sfile_t*	parent;	/* parent stream */
	long		offset;	/* starting offset */
	long		extent;	/* size wanted */
	long		here;	/* current seek location */
} Subfile_t;

#ifdef __STD_C
static streamio(Sfile_t* f, char* buf, int n, Sfdisc_t* disc, int type)
#else
static streamio(f, buf, n, disc, type)
Sfile_t*	f;
char*		buf;
int		n;
Sfdisc_t*	disc;
int		type;
#endif
{
	reg Subfile_t	*sdisc;
	reg long	here, parent;
	reg int		io;

	sdisc = (Subfile_t*)disc;

	/* read just what we need */
	if(sdisc->extent >= 0 && n > (io = (int)(sdisc->extent - sdisc->here)) )
		n = io;
	if(n <= 0)
		return n;

	/* save current location in parent stream */
	parent = sfseek(sdisc->parent,0L,1);

	/* read data */
	here = sdisc->here + sdisc->offset;
	if(sfseek(sdisc->parent,here,0) != here)
		io = 0;
	else
	{	if(type == SF_WRITE) 
			io = sfwrite(sdisc->parent,buf,n);
		else	io = sfread(sdisc->parent,buf,n);
		if(io > 0)
			sdisc->here += io;
	}

	/* restore parent current position */
	sfseek(sdisc->parent,parent,0);

	return io;
}

#ifdef __STD_C
static streamwrite(Sfile_t* f, char* buf, int n, Sfdisc_t* disc)
#else
static streamwrite(f, buf, n, disc)
Sfile_t*	f;
char*		buf;
int		n;
Sfdisc_t*	disc;
#endif
{
	return streamio(f,buf,n,disc,SF_WRITE);
}

#ifdef __STD_C
static streamread(Sfile_t* f, char* buf, int n, Sfdisc_t* disc)
#else
static streamread(f, buf, n, disc)
Sfile_t*	f;
char*		buf;
int		n;
Sfdisc_t*	disc;
#endif
{
	return streamio(f,buf,n,disc,SF_READ);
}

#ifdef __STD_C
static long streamseek(Sfile_t* f, long pos, int type, Sfdisc_t* disc)
#else
static long streamseek(f, pos, type, disc)
Sfile_t*	f;
long		pos;
int		type;
Sfdisc_t*	disc;
#endif
{
	reg Subfile_t*	sdisc;
	reg long	here, parent;

	sdisc = (Subfile_t*)disc;

	switch(type)
	{
	case 0:
		here = 0L;
		break;
	case 1:
		here = sdisc->here;
		break;
	case 2:
		if(sdisc->extent >= 0)
			here = sdisc->extent;
		else
		{	parent = sfseek(sdisc->parent,0L,1);
			if((here = sfseek(sdisc->parent,0L,2)) < 0)
				return -1L;
			else	here -= sdisc->offset;
			sfseek(sdisc->parent,parent,0L);
		}
		break;
	default:
		return -1L;
	}

	pos += here;
	if(pos < 0 || (sdisc->extent >= 0 && pos >= sdisc->extent))
		return -1L;

	return (sdisc->here = pos);
}

#ifdef __STD_C
static streamexcept(Sfile_t* f, int type, Sfdisc_t* disc)
#else
static streamexcept(f, type, disc)
Sfile_t*	f;
int		type;
Sfdisc_t*	disc;
#endif
{
	if(type == SF_CLOSE)
	{	reg Sfdisc_t*	pop;
		if((pop = sfdisc(f,SF_POPDISC)) != disc)	/* hmm! */
			sfdisc(f,pop);
		else	dcdelsubstream(disc);
	}

	return 0;
}

#ifdef __STD_C
Sfdisc_t* dcnewsubstream(Sfile_t* f, long offset, long extent)
#else
Sfdisc_t* dcnewsubstream(f, offset, extent)
Sfile_t*	f;	/* stream */
long		offset;	/* offset in f */
long		extent;	/* desired size */
#endif
{
	reg Subfile_t*	sdisc;
	reg long	here;

	/* establish that we can seek to offset */
	if((here = sfseek(f,0L,1)) < 0 || sfseek(f,offset,0) < 0)
		return NIL(Sfdisc_t*);
	else	sfseek(f,here,0);

	if(!(sdisc = (Subfile_t*)malloc(sizeof(Subfile_t))) )
		return NIL(Sfdisc_t*);

	sdisc->disc.readf = streamread;
	sdisc->disc.writef = streamwrite;
	sdisc->disc.seekf = streamseek;
	sdisc->disc.exceptf = streamexcept;
	sdisc->parent = f;
	sdisc->offset = offset;
	sdisc->extent = extent;

	return (Sfdisc_t*)sdisc;
}


#ifdef __STD_C
dcdelsubstream(Sfdisc_t* disc)
#else
dcdelsubstream(disc)
Sfdisc_t*	disc;
#endif
{
	free((char*)disc);
	return 0;
}


#ifdef PROGRAM
/*	The following program mixes lines from the top half and
**	bottom half of a file. It only works if stdin is seekable.
*/
main()
{
	Sfile_t		*top, *bot;
	Sfdisc_t	*topdisc, *botdisc;
	char		*s1, *s2;
	long		size;

	/* find midpoint */
	if((size = sfsize(sfstdin)) < 0 || sfseek(sfstdin,(size /= 2)-1,0) < 0)
		return -1;

	/* skip to next line */
	while(sfgetc(sfstdin) != '\n')
		size += 1;

	/* create dummy streams for top and bottom halves of the file */
	if(!(top = sfnew(NIL(Sfile_t*),NIL(char*),-1,1001,SF_READ)) ||
	   !(bot = sfnew(NIL(Sfile_t*),NIL(char*),-1,1001,SF_READ)) )
		return -1;

	/* create and install disciplines */
	if(!(topdisc = dcnewsubstream(sfstdin,0L,size)) ||
	   !(botdisc = dcnewsubstream(sfstdin,size,-1L)) )
		return -1;
	sfdisc(top,topdisc);
	sfdisc(bot,botdisc);

	/* now mix lines */
	for(;;)
	{
		if(s1 = sfgetr(top,'\n',0))
			sfwrite(sfstdout,s1,sfslen());
		if(s2 = sfgetr(bot,'\n',0))
			sfwrite(sfstdout,s2,sfslen());
		if(!s1 && !s2)
			return 0;
	}
}

#endif /* PROGRAM */
SHAR_EOF
fi # end of overwriting check
if test -f './Disc/dcskable.c'
then
	echo shar: will not over-write existing file "'./Disc/dcskable.c'"
else
cat << \SHAR_EOF > './Disc/dcskable.c'
#include	"dchdr.h"

/*	Discipline to make an unseekable read stream seekable
**
**	Written by Kiem-Phong Vo (03/13/92)
*/

typedef _skable_
{
	Sfdisc_t	disc;	/* sfio discipline */
	Sfile_t*	shadow;	/* to shadow data */
	long		here;	/* current location */
} Seek_t;

#ifdef __STD_C
static skread(Sfile_t* f, char* buf, int n, Sfdisc_t* disc)
#else
static skread(f, buf, n, disc)
Sfile_t*	f;	/* stream involved */
char*		buf;	/* buffer to read into */
int		n;	/* number of bytes to read */
Sfdisc_t*	disc;	/* discipline */
{
	if(sftell
}
SHAR_EOF
fi # end of overwriting check
if test -f './sfpkrd.c'
then
	echo shar: will not over-write existing file "'./sfpkrd.c'"
else
cat << \SHAR_EOF > './sfpkrd.c'
#include	"sfhdr.h"

#include	"FEATURE/peek"

#if _stream_peek
#include	<stropts.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int ioctl _ARG_((int, int, struct strpeek*));
#ifdef __cplusplus
}
#endif
#endif /* _stream_peek */

#if _socket_peek
#include	<sys/socket.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int recv _ARG_((int, char*, int, int));
#ifdef __cplusplus
}
#endif
#endif /* _socket_peek */

/*	Read exactly a given number of records from an unseekable device.
**	Return the number of records read if known; otherwise return -1.
**
**	Written by Kiem-Phong Vo (06/17/92)
*/

#ifdef __STD_C
_sfpkrd(Sfile_t* f, reg int rc, long n)
#else
_sfpkrd(f,rc,n)
Sfile_t*	f;	/* current stream */
reg int		rc;	/* record delimiter */
long		n;	/* number of record delimiters to see */
#endif
{
	reg int		r;
	reg char	*sp, *endsp;
	reg long	c;

	if(f->extent >= 0 || f->next < f->endb)
		return -1;
	else	f->next = f->endb = f->endr = f->endw = f->data;

	r = 0;
	sp = endsp = NIL(char*);
	if(f->unmap == 0)
		f->unmap = SF_IPEEK|SF_SPEEK;

#if _stream_peek
	if(f->unmap&SF_IPEEK)
	{
		struct strpeek	pbuf;
		pbuf.flags = 0;
		pbuf.ctlbuf.maxlen = -1;
		pbuf.ctlbuf.len = 0;
		pbuf.ctlbuf.buf = NIL(char*);
		pbuf.databuf.maxlen = f->size;
		pbuf.databuf.buf = (char*)f->data;
		pbuf.databuf.len = 0;

		/* failure, don't ever do this again */
		r = ioctl(f->file,I_PEEK,&pbuf);
		if(r < 0)
			f->unmap &= ~SF_IPEEK;
		else
		{	f->unmap &= ~SF_SPEEK;
			if(r > 0 && pbuf.databuf.len > 0)
			{	sp = pbuf.databuf.buf;
				endsp = sp + pbuf.databuf.len;
			}
			else	r = 0;
		}
	}
#endif
#if _socket_peek
	if(f->unmap&SF_SPEEK)
	{	errno = 0;
		r = recv(f->file,(char*)f->data,f->size,MSG_PEEK);
		if(r < 0)
		{	if(errno == EBADF || errno == ENOTSOCK)
				f->unmap &= ~SF_SPEEK;
		}
		else
		{	f->unmap &= ~SF_IPEEK;
			if(r > 0)
			{	sp = (char*)f->data;
				endsp = sp + r;
			}
		}
	}
#endif

	if(!(f->unmap&(SF_IPEEK|SF_SPEEK)))
		f->unmap = SF_NOPEEK;

	if(!sp)
	{	/* read 1 byte */
		if(r == 0)
			(void)SFRD(f,(char*)f->data,1,f->disc);
		return -1;
	}

	/* successful peek, find out how many records were read */
	c = n;
	while(sp < endsp)
		if(*sp++ == rc)
			if(--c == 0)
				break;
	n -= c;
	r = sp - (char*)f->data;
	SFRD(f,(char*)f->data,r,f->disc);

	return (f->endb-f->data) == r ? n : -1;
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
