/*			s t d i o l i b
 *
 * (C) Copyright C E Chew
 *
 * Feel free to copy, use and distribute this software provided:
 *
 *	1. you do not pretend that you wrote it
 *	2. you leave this copyright notice intact.
 *
 * This include file is used by the stdio code. It provides some
 * useful macro definitions that make the code a bit easier to
 * write.
 *
 * Patchlevel 3.0
 */

#ifndef	STDIOLIB_H
#define STDIOLIB_H

#include "config.h"

/* System types */

#ifdef		SYSTYPES
# include <sys/types.h>
#endif

/* Promotions for system call parameter passing */

#ifndef		MODE_T
# define MODE_T	int
#endif

#ifndef		UID_T
# define UID_T	int
#endif

/* System file control */

#include <fcntl.h>

/* Time types */

#ifdef		TIME
# include <time.h>
#endif

/* Posix standard things */

#ifdef		UNISTD
# include <unistd.h>
#endif

/* Limits */

#ifdef		LIMITS
# include <limits.h>
#endif

/* Floating point limits */

#ifndef		NOFLOAT
# ifdef		FLOAT
#   include <float.h>
# endif
#endif

/* Long doubles */

#ifndef		NOFLOAT
# ifdef		LONGDOUBLE
    typedef long double longdouble;
#   define LMODF  lmodf
#   define LFREXP lfrexp
#   define LLDEXP lldexp
# else
    typedef double longdouble;
#   define LMODF  modf
#   define LFREXP frexp
#   define LLDEXP ldexp
#   undef	LDBL_DIG
#   define	LDBL_DIG	DBL_DIG
#   undef	LDBL_EPSILON
#   define	LDBL_EPSILON	DBL_EPSILON
#   undef	LDBL_MANT_DIG
#   define	LDBL_MANT_DIG	DBL_MANT_DIG
#   undef	LDBL_MAX
#   define	LDBL_MAX	DBL_MAX
#   undef	LDBL_MAX_10_EXP
#   define	LDBL_MAX_10_EXP	DBL_MAX_10_EXP
#   undef	LDBL_MAX_EXP
#   define	LDBL_MAX_EXP	DBL_MAX_EXP
#   undef	LDBL_MIN
#   define	LDBL_MIN	DBL_MIN
#   undef	LDBL_MIN_10_EXP
#   define	LDBL_MIN_10_EXP	DBL_MIN_10_EXP
#   undef	LDBL_MIN_EXP
#   define	LDBL_MIN_EXP	DBL_MIN_EXP
# endif
#endif

/* Significant digits in mantissa
 *
 * Since 10^k = 5^k.2^k, the highest power of ten that will fit in N bits
 * is N.log_5 2, if the exponent base if binary.
 */

#ifndef	NOFLOAT
# if	FLT_RADIX == 2
#   define MANTDIGITS		(LDBL_MANT_DIG * 1 * 43 / 100)
#   define HALFMANTDIGITS	(LDBL_MANT_DIG / 2 * 1 * 43 / 100)
# else
# if	FLT_RADIX == 4
#   define MANTDIGITS		(LDBL_MANT_DIG * 2 * 43 / 100)
#   define HALFMANTDIGITS	(LDBL_MANT_DIG / 2 * 2 * 43 / 100)
# else
# if	FLT_RADIX == 8
#   define MANTDIGITS		(LDBL_MANT_DIG * 3 * 43 / 100)
#   define HALFMANTDIGITS	(LDBL_MANT_DIG / 2 * 3 * 43 / 100)
# else
# if	FLT_RADIX == 16
#   define MANTDIGITS		(LDBL_MANT_DIG * 4 * 43 / 100)
#   define HALFMANTDIGITS	(LDBL_MANT_DIG / 2 * 4 * 43 / 100)
# else
# if	FLT_RADIX == 10
#   define MANTDIGITS		(LDBL_MANT_DIG)
#   define HALFMANTDIGITS	(LDBL_MANT_DIG/2)
# else
  << FLT_RADIX not on mantissa significant digits list >>
# endif
# endif
# endif
# endif
# endif
#endif

/* Constants */

#ifdef		__STDC__
# define CONST	const
#else
# define CONST
#endif

/* Variable argument list hiding */

#ifdef		STDARG
# include <stdarg.h>
# define VA_START(n,l)	va_start(n,l)
# ifdef		__STDC__
#   define VA_ALIST	, ...
# else
#   define VA_ALIST
# endif
# define VA_LIST	va_list
# define VA_END(n)	va_end(n)
# define VA_ARG(n,t)	va_arg(n,t)
# define VA_DCL
#else
# include <varargs.h>
# define VA_START(n,l)	va_start(n)
# define VA_ALIST	va_alist
# define VA_LIST	va_list
# define VA_END(n)	va_end(n)
# define VA_ARG(n,t)	va_arg(n,t)
# define VA_DCL		va_dcl
#endif

/* Function prototype hiding */

#ifdef		__STDC__
# define	P(x)	x
# define	F8(t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6,t7,n7,t8,n8) \
		  (t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6, t7 n7, t8 n8)
# define	F7(t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6,t7,n7) \
		  (t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6, t7 n7)
# define	F6(t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6) \
		  (t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6)
# define	F5(t1,n1,t2,n2,t3,n3,t4,n4,t5,n5) \
		  (t1 n1, t2 n2, t3 n3, t4 n4, t5 n5)
# define	F4(t1,n1,t2,n2,t3,n3,t4,n4) \
		  (t1 n1, t2 n2, t3 n3, t4 n4)
# define	F3(t1,n1,t2,n2,t3,n3) \
		  (t1 n1, t2 n2, t3 n3)
# define	F2(t1,n1,t2,n2) \
		  (t1 n1, t2 n2)
# define	F1(t1,n1) \
		  (t1 n1)
# define	F0()	(void)
#else
# define	P(x)	()
# define	F8(t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6,t7,n7,t8,n8) \
			(n1, n2, n3, n4, n5, n6, n7, n8) \
			t1 n1;t2 n2;t3 n3;t4 n4;t5 n5;t6 n6;t7 n7;t8 n8;
# define	F7(t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6,t7,n7) \
			(n1, n2, n3, n4, n5, n6, n7) \
			t1 n1;t2 n2;t3 n3;t4 n4;t5 n5;t6 n6;t7 n7;
# define	F6(t1,n1,t2,n2,t3,n3,t4,n4,t5,n5,t6,n6) \
			(n1, n2, n3, n4, n5, n6) \
			t1 n1;t2 n2;t3 n3;t4 n4;t5 n5;t6 n6;
# define	F5(t1,n1,t2,n2,t3,n3,t4,n4,t5,n5) \
			(n1, n2, n3, n4, n5) \
			t1 n1;t2 n2;t3 n3;t4 n4;t5 n5;
# define	F4(t1,n1,t2,n2,t3,n3,t4,n4) \
			(n1, n2, n3, n4) \
			t1 n1;t2 n2;t3 n3;t4 n4;
# define	F3(t1,n1,t2,n2,t3,n3) \
			(n1, n2, n3) \
			t1 n1;t2 n2;t3 n3;
# define	F2(t1,n1,t2,n2) \
			(n1, n2) \
			t1 n1;t2 n2;
# define	F1(t1,n1) \
			(n1) \
			t1 n1;
# define	F0()	()
#endif

/* Variadic function prototype hiding */

#ifdef STDARG
# ifdef __STDC__
#   define	F2V(t1,n1,t2,n2,t,a,l) \
		   (t1 n1, t2 n2 \
		    VA_ALIST) t a; l
#   define	F1V(t1,n1,t,a,l) \
		   (t1 n1 \
		    VA_ALIST) t a; l
# else
#   define	F2V(t1,n1,t2,n2,t,a,l) \
		   (n1,n2 VA_ALIST) \
		   t1 n1; t2 n2; \
		   t a; l
#   define	F1V(t1,n1,t,a,l) \
		   (n1 VA_ALIST) \
		   t1 n1; \
		   t a; l
# endif
#else
# define	F2V(t1,n1,t2,n2,t,a,l) \
			(VA_ALIST) VA_DCL \
			t a; t1 n1;t2 n2; l \
			n1 = VA_ARG(a,t1); \
			n2 = VA_ARG(a,t2);
# define	F1V(t1,n1,t,a,l) \
			(VA_ALIST) VA_DCL \
			t a; t1 n1; l \
			n1 = VA_ARG(a,t1);
#endif

/* Strings */

#ifdef		STRING
# include <string.h>
#endif

/* Memory */

#ifdef		MEMORY
# define MEMCPY(d,s,n)		((void) memcpy((d),(s),(n)))
# define MEMCHR(s,c,n)		(memchr((s),(c),(n)))
# define MEMSET(s,v,n)		((void) memset((s),(v),(n)))
# define MEMCCPY(d,s,c,n)	(memccpy((d),(s),(c),(n)))
#endif

/* Unsigned char casting */

#if		UNSIGNEDCHAR-0 == 0
# define UCHAR(x)	((int) ((unsigned char) (x)))
#else
# define UCHAR(x)	((int) (x) & ((1 << CHAR_BIT) - 1))
#endif

/* Signal return type */

#include <signal.h>

#ifdef		VOIDSIGNAL
  typedef void (*signal_t) P((int));
#else
  typedef int (*signal_t) P((int));
#endif

/* System error list */

#ifdef		ERRLIST
/* Conditional referenced in _errlist.c */
#endif

/* Rest of includes */

#include <sys/stat.h>
#ifndef QDOS
#include <pwd.h>
#endif /* QDOS */
#include <errno.h>
#include <stdio.h>

#ifdef GENERIC
/* Blows the c68 compiler ! */
typedef char size_t_check[((__stdiosize_t) -1) > 0 ? 1 : -1];
#endif /* GENERIC */

/* Hide system library names */

#ifdef	HIDDENLIBC
# include "hidden.h"
#endif

/* Assertion */

#ifndef	NDEBUG
#define	ASSERT(x)	(void) ((x) ? 0 : __xassert(__FILE__))
#else
#define	ASSERT(x)
#endif

/* Useful macros */

#define MAX(a,b)	((a)>(b)?(a):(b))

/* Print buffer size calculation */

#define MININTBITS	3		/* least bits required --- octal */
#define MAXINTPREFIX	3		/* overhead for prefix (+0x) */
#define MAXINTINSERT	0		/* overhead for insertions */
#define MAXINTSUFFIX	0		/* overhead for suffix */
#define MAXINTDIGITS	((sizeof(long)*CHAR_BIT+MININTBITS-1) \
			 /MININTBITS*MININTBITS)
#define PINTBUFFER	(MAXINTDIGITS+MAXINTPREFIX+MAXINTINSERT+MAXINTSUFFIX)

#ifndef	NOFLOAT
# define MAXFLTPREFIX	1		/* overhead for prefix (+) */
# define MAXFLTINSERT	1		/* overhead for insertions (.) */
# define MAXFLTSUFFIX	5		/* overhead for suffix (e-308) */
# define MAXFLTDIGITS	(LDBL_DIG+2)
# define PFLTBUFFER	(MAXFLTDIGITS+MAXFLTPREFIX+MAXFLTINSERT+MAXFLTSUFFIX)
#else
# define PFLTBUFFER	0
#endif

#define PWRITEAHEAD	4		/* write ahead in _vfprintf */

#define PBUFFERSIZE	MAX(MAX(PINTBUFFER, PFLTBUFFER),PWRITEAHEAD)

/* Formatted output conversion status bits */

#define F_LEFTJUSTIFY	0x0001		/* left justify */
#define F_SHOWSIGN	0x0002		/* display a sign */
#define F_BLANKPREFIX	0x0004		/* prefix with blank */
#define F_ALTERNATE	0x0008		/* alternate format */
#define F_SHORT		0x0010		/* argument is short */
#define F_LONG		0x0020		/* argument is long */
#define F_LONGDOUBLE	0x0040		/* argument is long double */
#define F_0X		0x0080		/* 0X instead of 0x */
#define F_FPFORMATF	0x0100		/* %f format */
#define F_FPFORMATE	0x0200		/* %e format */
#define F_FPFORMATG	0x0400		/* %g format */
#define F_FPCAPITAL	0x0800		/* %E or %G */
#define F_FPFORMAT	0x0f00		/* floating point converted */
#define F_PRECISION	0x1000		/* precision valid */

/* Formatted output vector element */

typedef struct {
  int att;				/* attributes */
  __stdiosize_t len;			/* length of vector */
  char *arg;				/* vector */
} FV;

#define FV_F_VECTOR	0x0000		/* length and pointer */
#define FV_F_PADDING	0x0001		/* padding only */
#define FV_F_STRING	0x0002		/* null terminated string */

#define FV_FMTPREFIX	0		/* global format prefix */
#define FV_FMTARGS	1

#define FV_FLT1SIGN	1		/* prefix */
#define FV_FLT1INT	2		/* integer digits */
#define FV_FLT1INTFILL	3		/* integer fill */
#define FV_FLT1DPFRAC	4		/* decimal point and fraction */
#define FV_FLT1FRACFILL	5		/* fractional fill */
#define FV_FLT1ARGS	6

#define FV_FLT2SIGN	1		/* prefix */
#define FV_FLT2INTDP	2		/* integer digits and decimal point */
#define FV_FLT2DPFILL	3		/* decimal point fill */
#define FV_FLT2FRAC	4		/* fractional digits */
#define FV_FLT2FRACFILL	5		/* fractional fill */
#define FV_FLT2ARGS	6

#define FV_FLT3SIGN	1		/* prefix */
#define FV_FLT3MANTISSA	2		/* mantissa */
#define FV_FLT3FRACFILL	3		/* fractional fill */
#define FV_FLT3SUFFIX	4		/* suffix */
#define FV_FLT3ARGS	5

#define FV_INTSIGN	1		/* sign or prefix */
#define FV_INTINT	2		/* integer digits */
#define FV_INTARGS	3

#define FV_ARGS		MAX(FV_FLT1ARGS, MAX(FV_FLT2ARGS, \
			MAX(FV_FLT3ARGS, FV_INTARGS)))

/* Internal buffer pool */

#define POOLSIZE	2		/* stdin and stdout only */

/* Stdio internal types */

typedef void (*atexit_t) P((void));	/* exit handlers */
#ifndef	NOFLOAT
typedef struct {			/* longdouble with guard */
  longdouble number;
  longdouble guard;
  int exponent;
} longguard;
#endif

/* Function prototypes */

int __bffil	P((FILE *));		/* fail */
int __bffls	P((int, FILE *));	/* fail */
int __btfls	P((FILE *));		/* succeed */

int __bwrupdate	P((int, FILE *));	/* write update */
int __bwronly	P((int, FILE *));	/* write only */

int __brdupdate	P((FILE *));		/* read update */
int __brdonly	P((FILE *));		/* read only */

__stdiosize_t __iowrite P((int, char *, __stdiosize_t)); /* repeated write */
__stdiosize_t __ioread	P((int, char *, __stdiosize_t)); /* repeated read */

void __ioflush	P((void));		/* flush output */
__stdiosize_t __allocbuf P((FILE *));	/* allocate buffer */
void __freebuf	P((FILE *));		/* deallocate buffer */
int __fopen	P((const char *, const char *, int, short *));
					/* fopen assist */
FILE *__file	P((FILE *, int, short));/* initialise FILE */

char *__fgetlx	P((char *, __stdiosize_t, FILE *));
					/* read line extended */
__stdiosize_t __rlbf P((FILE *, __stdiosize_t));
					/* adjust line buffered read */

int __cvt	P((__stdiosize_t *, FV *, char *, VA_LIST *, int, int));
					/* output */
int __tvc	P((FILE *, int, VA_LIST *, int, char *));
					/* input */

char *__utoa	P((char *, unsigned, int)); /* unsigned to string conversion */

int __xassert	P((char *));		/* assertion failed */

#ifndef	NOFLOAT
void __gpow10      P((int, longguard *));/* compute 10^x with guard */
longdouble __pow10 P((int));		 /* compute 10^x without guard */

longdouble __gmul    P((longguard *, longguard *));
void __ggmul         P((longguard *, longguard *, longguard *));
void __gguard        P((longguard *));
longdouble __gnumber P((longguard *));
void __gnormal       P((longguard *));
#endif

/* Extras not in ANSI but probably could be */

int __vscanf	P((const char *, VA_LIST));
int __vfscanf	P((FILE *, const char *, VA_LIST));
int __vsscanf	P((const char *, const char *, VA_LIST));

int __vfprintf	P((FILE *, const char *, VA_LIST));

/* Library globals */

extern int errno;			/* system error number */
extern int sys_nerr;			/* size of error table */
extern char *sys_errlist[];		/* error descriptions */

/* Stdio internal variables */

extern void (*__Zwrapup) P((void));	/* flush stdio linkage */
extern void (*__Zatexit) P((void));	/* exit handler linkage */
extern FILE *__Zout;			/* stdout linkage */
extern FILE *__Zerr;			/* stderr linkage */
extern __stdiosize_t (*__Zrlbf) P((FILE *, __stdiosize_t));
					/* rlbf() linkage */
extern FILE *__iop;			/* stream list */
extern __stdiobuf_t *__iob[POOLSIZE];	/* buffer pool */
extern char __zfill[];			/* zero fill */
extern long __ipow10[];			/* powers of 10 */
extern int __Mipow10;			/* exponent of the largest power */

#ifndef	NOFLOAT
extern longdouble *__fpow10;		/* floating point version */
extern char __xfptvc;			/* tvc linkage for library */
extern char __xfpcvt;			/* cvt linkage for library */
#endif

/* Library calls */

void *malloc	P((size_t));		/* memory allocator */
void free	P((void *));		/* free memory */

#ifndef	NOFLOAT
longdouble LMODF  P((longdouble, double *)); /* separate integer and fraction */
longdouble LFREXP P((longdouble, int *));    /* separate mantissa and exponent */
longdouble LLDEXP P((longdouble, int));      /* scale by a power of two */
#endif

/* System calls */

#ifndef		DUP2
# undef dup2
# define dup2	__dup2			/* fake two argument dup */
#endif

#ifndef		OPEN3
#ifdef GENERIC
  extern int open	P((const char *, int));	/* two argument open */
#else
  extern int open	P((const char *, int, ...));	/* two argument open */
#endif
# undef open
# define open	__open3			/* fake three argument open */
# undef O_CREAT
# undef O_APPEND
# undef O_TRUNC
# define O_CREAT	(~(O_RDONLY|O_WRONLY|O_RDWR) ^ \
			(~(O_RDONLY|O_WRONLY|O_RDWR) & \
                        (~(O_RDONLY|O_WRONLY|O_RDWR) - 1)))
# define O_APPEND	(~(O_CREAT|O_RDONLY|O_WRONLY|O_RDWR) ^ \
			(~(O_CREAT|O_RDONLY|O_WRONLY|O_RDWR) & \
                        (~(O_CREAT|O_RDONLY|O_WRONLY|O_RDWR) - 1)))
# define O_TRUNC	(~(O_APPEND|O_CREAT|O_RDONLY|O_WRONLY|O_RDWR) ^ \
			(~(O_APPEND|O_CREAT|O_RDONLY|O_WRONLY|O_RDWR) & \
                        (~(O_APPEND|O_CREAT|O_RDONLY|O_WRONLY|O_RDWR) - 1)))
#endif

void		abort    P((void));		/* abort to system */
void		_exit    P((int));		/* exit to system */
int		isatty   P((int));		/* channel is tty */
int		close    P((int));		/* close channel */
int		creat    P((const char *, MODE_T)); /* create a file */
#ifdef GENERIC
int		open     P((const char *, int, ...)); /* open a file */
#else
extern int		open     P((const char *, int, ...)); /* open a file */
#endif
int		dup	 P((int));		/* duplicate file descriptor */
int		dup2     P((int, int));		/* duplicate file descriptor */
off_t		lseek    P((int, off_t, int));	/* seek within file */
#ifdef GENERIC
int		read     P((int, char *, unsigned int)); /* read channel */
int		write    P((int, char *, unsigned int)); /* write channel */
#else
int		read     P((int, void *, unsigned int)); /* read channel */
int		write    P((int, void *, unsigned int)); /* write channel */
#endif
int		unlink   P((const char *));	/* unlink a file */
int		link     P((const char *, const char *)); /* link a file */
int		stat     P((const char *, struct stat *)); /* file status */
mode_t		umask    P((MODE_T));		/* permission mask */
int		chmod    P((const char *, MODE_T)); /* change permission */
pid_t		getpid   P((void));		/* get process id */
time_t		time     P((time_t *));		/* get time */
uid_t		geteuid  P((void));		/* get effective user id */
uid_t		getuid   P((void));		/* get user id */
struct passwd * getpwuid P((UID_T));		/* get passwd from uid */
#ifndef QDOS
signal_t	signal   P((int, signal_t));	/* set signals */
#endif /* QDOS */

/* Size of tty buffers */

#define TTYBUFSIZ		81

/* Flag manipulation macros */

#define TESTFLAG(f,x)		(((f)->__flag & (x)) != 0)
#define SETFLAG(f,x)		((f)->__flag |= (x))
#define CLEARFLAG(f,x)		((f)->__flag &= ~(x))
#define GETFLAG(f,x)		((f)->__flag & (x))
#define ALLFLAGS(f)		((f)->__flag)
#define TOGGLEFLAG(f,x)		((f)->__flag ^= (x))
#define SAVEFLAG(f,p)		((p) = (int) ((f)->__flag))
#define RESTOREFLAG(f,p)	((f)->__flag = (short) (p))

/* Insert stream descriptor into __iop chain
 *
 * The __iop list element pointed to by p is inserted into the
 * __iop chain.
 */
#define FINSERT(p) ( (p)->__next=__iop, __iop=(p) )

/* Getc with full buffering
 *
 * This version of getc assumes that there is an input buffer
 * that is not empty and simply grabs the character from there.
 */
#define FGETC(p) ( UCHAR(*(p)->__rptr++) )

/* Putc for non buffered streams
 *
 * This version of putc is explicitly for unbuffered streams. A
 * call is made directly to the buffer flushing code.
 */
#define NPUTC(x,p) ( (*(p)->__flsbuf)((x),(p)) )

/* Putc with full buffering
 *
 * This version of putc() assumes that there is an output buffer
 * that is not full and simply dumps the character in there.
 */
#define FPUTC(x, p) ( UCHAR((*(p)->__wptr++ = (x))) )
 
/* Flush a stream
 *
 * Call the flush routine to clear the output buffer.
 */
#define FFLUSH(f) ( (*(f)->__flush)(f) )

/* Set __flsbuf
 *
 * Set the __flsbuf function pointer.
 */
#define SETFLSBUF(f,p) ( (f)->__flsbuf = (p) )

/* Set __filbuf
 *
 * Set the __filbuf function pointer.
 */
#define SETFILBUF(f,p) ( (f)->__filbuf = (p) )

/* Set __flush
 *
 * Set the __flush function pointer.
 */
#define SETFLUSH(f,p) ( (f)->__flush = (p) )

/* Initialise an output buffer
 *
 * This macro uses __base and __bufsiz to initialise __wptr and
 * __wend. __wptr will be set to point at the base of the
 * buffer. __wend will be set to point at one past the end of the
 * buffer if the stream is buffered otherwise it will point at
 * the base of the buffer. Line buffered streams are considered to
 * be fully buffered. This macro must not alter __base unless the
 * code in fflush() is changed.
 */
#define INITWRITEBUFFER(f) ( \
  (f)->__wend = ((f)->__wptr = (f)->__base) + \
                (TESTFLAG(f, _IONBF | _IOLBF) ? 0 : (f)->__bufsiz) \
)

/* Initialise an input buffer
 *
 * This macro empties an input buffer. It uses __base to initialise
 * __rptr and sets __rend to point to the high water mark of the buffer.
 */
#define INITREADBUFFER(f, v) ( \
  (f)->__rend = ((f)->__rptr = (f)->__base) + (v) \
)

/* Flush the next write
 *
 * This macro initialises the buffer by setting __wend and
 * __wptr to nil. This will force the next putc to call
 * __flsbuf.
 */
#define CHECKNEXTWRITE(f) ( (f)->__wend = (f)->__wptr = 0 )

/* Check a write
 *
 * If __wptr is zero, the write buffer is checked.
 */
#define CHECKWRITE(f) ( \
  (f)->__wptr == 0 ? ((f)->__wend = &(f)->__buf, (*(f)->__flsbuf)(0, f)) : 0 \
)

/* Check write status
 *
 * Return non-zero if a write check is scheduled, zero otherwise. This
 * assumes that the stream is writeable.
 */
#define ISCHECKWRITE(f) ( (f)->__wptr == 0 )

/* Flush the next read
 *
 * This macro initialises the buffer by setting __rend and
 * __rptr to nil. This will force the next next getc to call
 * __filbuf.
 */
#define CHECKNEXTREAD(f) ( (f)->__rend = (f)->__rptr = 0 )

/* Check a read
 *
 * If __rptr is zero, the read buffer is checked.
 */
#define CHECKREAD(f) ( \
  (f)->__rptr == 0 ? ((f)->__rend = &(f)->__buf, (*(f)->__filbuf)(f)) : 0 \
)

/* Check read status
 *
 * Return non-zero if a read check is scheduled, zero otherwise. This
 * assumes that the stream is readable.
 */
#define ISCHECKREAD(f) ( (f)->__rptr == 0 )

/* Buffer size
 *
 * Return the size of the buffer. This will return rubbish
 * for unbuffered streams. Line and fully buffered streams will
 * have the true buffer size returned.
 */
#define BUFFERSIZE(f) ( (f)->__bufsiz )

/* Bytes left in input buffer
 *
 * This macro returns the number of bytes left in an input buffer.
 * The result returned will be zero even in the case where the stream
 * has a check scheduled.
 */
#define BYTESINREADBUFFER(f) ( (f)->__rend - (f)->__rptr )

/* Bytes written in output buffer
 *
 * This macro returns the number of bytes left in an output buffer.
 * The result is not valid when a check has been scheduled.
 */
#define BYTESINWRITEBUFFER(f) ( (f)->__wptr - (f)->__base )

/* Unused bytes in output buffer
 *
 * This macro returns the number of unused bytes in an output buffer.
 * Unbuffered streams will return rubbish. Line and fully buffered streams
 * will have the amount of space remaining returned. In order to accommodate
 * line buffered streams, __wend cannot be used. The result is not valid
 * when a check has been scheduled.
 */
#define UNUSEDINWRITEBUFFER(f) ( (f)->__bufsiz - BYTESINWRITEBUFFER(f) )

/* Get pointer into write buffer
 *
 * This macro gets the pointer into the write buffer.
 */
#define GETWRITEPTR(f) ( (f)->__wptr )

/* Set pointer into write buffer
 *
 * This macro sets the pointer into the write buffer.
 */
#define SETWRITEPTR(f,p) ( (f)->__wptr = (p) )

/* Get pointer to end of write buffer
 *
 * This macro returns the end of write buffer pointer. This may not
 * point to the real end of the write buffer.
 */
#define GETWRITEEND(f) ( (f)->__wend )

/* Get pointer to limit of write buffer
 *
 * This macro returns the limit of write buffer pointer. This will
 * point to the real end of the write buffer.
 */
#define GETWRITELIMIT(f) ( (f)->__base + BUFFERSIZE(f) )

/* Get pointer into read buffer
 *
 * This macro gets the pointer into the read buffer.
 */
#define GETREADPTR(f) ( (f)->__rptr )

/* Set pointer into read buffer
 *
 * This macro sets the pointer into the read buffer.
 */
#define SETREADPTR(f,p) ( (f)->__rptr = (p) )

/* Get pointer to end of read buffer
 *
 * This macro returns the end of read buffer pointer. This may not
 * point to the real end of the read buffer.
 */
#define GETREADEND(f) ( (f)->__rend )

/* Get pointer to limit of read buffer
 *
 * This macro returns the limit of read buffer pointer. This will
 * point to the real end of the read buffer.
 */
#define GETREADLIMIT(f) ( (f)->__base + BUFFERSIZE(f) )

/* Check if buffering has been set
 *
 * Return true if buffering has already been set. A stream
 * set for unbuffered output is considered to have had
 * its buffering set.
 */
#define HASBUFFER(f) ( (f)->__base != 0 )

/* Set __wend for line buffering
 *
 * Set __wend for line buffering. This means that _wend is set to
 * point at __base.
 */
#define SETWRITELINEBUFFERING(f) ( (f)->__wend = (f)->__base )

/* Set __wend for full buffering
 *
 * Set __wend to __base + __bufsiz so that the entire buffer can be
 * used.
 */
#define SETWRITEFULLBUFFERING(f) ( (f)->__wend = (f)->__base + (f)->__bufsiz )

/* Unroll a loop
 *
 * Assume that the loop must execute at least once. The first argument
 * is the name of the loop control variable. The second argument is the
 * name of the loop control variable. The third argument is the expression
 * to be placed in the loop body. The control variable should be unsigned,
 * otherwise the right shift might propagate the sign bit. The caller must
 * also provide the name of a unique label.
 */
#define UNROLL_DO(l,v,x) { \
  char t = (v); \
  (v) = ((v)+1) >> 1; \
  if (t & 1) goto l; \
  do { x; l: x; } while (--(v)); \
}

#endif
