/*
 * Copyright (c) 2000-2005 Sendmail, Inc. and its suppliers.
 *      All rights reserved.
 * Copyright (c) 1990
 * 	 The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Chris Torek.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 *	$Id: io.h,v 1.95 2007/10/23 04:03:12 ca Exp $
 */

#ifndef SM_IO_H
#define SM_IO_H 1

#include "sm/generic.h"
#include "sm/types.h"
#include "sm/magic.h"
#include "sm/varargs.h"
#include "sm/error.h"
#include "sm/iostub.h"
#include "sm/rpool.h"
#include "sm/str.h"
#if !SM_NO_CSTR
# include "sm/cstr.h"
#endif

/* mode for sm io (exposed) */
/*
**  XXX make this "compatible" with the internal flags to avoid
**  tranlation function sm_flags
  case SM_IO_RDONLY:	open for reading
	ret = SMRD;
  case SM_IO_WRONLY:	open for writing
	ret = SMWR;
  case SM_IO_APPEND:	open for appending
	ret = SMWR;
  case SM_IO_RDWR:	open for read and write
	ret = SMRW;
  default:
	ret = 0;
use the last few bits to "hide" the SMxy code in the SM_IO_ values.
*/
#define SM_IO_RDWR	1	/* read-write */
#define SM_IO_RDONLY	2	/* read-only */
#define SM_IO_WRONLY	3	/* write-only */
#define SM_IO_APPEND	4	/* write-only from eof */
#define SM_IO_APPENDRW	5	/* read-write from eof */
#define SM_IO_RDWRTR	6	/* read-write with truncation indicated */
#define SM_IO_WREXCL	7	/* write-only, open with O_EXCL */
#define SM_IO_RDWRCR	8	/* read-write with creation */
#define SM_IO_RDWRCRX	9	/* read-write with creation, O_EXCL  */

/* for sm_io_fseek, et al api's (exposed) */
#define SM_IO_SEEK_SET	0
#define SM_IO_SEEK_CUR	1
#define SM_IO_SEEK_END	2

/* default number of open files */
#define SM_IO_OPEN_MAX		20

/*
**  Arguments for open/set/get XXX not yet...
**
**	[O] means valid on open
**	[S] means valid for set
**	[G] means valid for get
**
**  Question: should the value encode the size of the argument so
**	functions can skip properly over unknown arguments?
*/


/* marker */
#define SM_IO_WHAT_END		0	/* end of list [O] */

/* flags for info what's with different types (exposed) */
#define SM_IO_WHAT_MODE		 1
#define SM_IO_WHAT_VECTORS	 2
#define SM_IO_WHAT_FD		 3
#define SM_IO_WHAT_TYPE		 4
#define SM_IO_WHAT_ISTYPE	 5
#define SM_IO_IS_READABLE	 6
#define SM_IO_WHAT_TIMEOUT	 7
#define SM_IO_DOUBLE		 8
#define SM_IO_WHAT_RD_FD	 9
#define SM_IO_WHAT_WR_FD	10
#define SM_IO_WHAT_SIZE		11	/* file size, not buffer size */
#define SM_IO_WHAT_RPOOL	12	/* specify resource pool [OG] */
#define SM_IO_WHAT_FMODE	13	/* file mode (chmod(2))*/
#define SM_IO_WHAT_COMMIT	14	/* commit file to stable storage */
#define SM_IO_WHAT_ABORT	15	/* abort, rm file */

#define SM_IO_WHAT_IBSIZE	16	/* input buffer size [OSG] */
#define SM_IO_WHAT_OBSIZE	17	/* output buffer size [OSG] */
#define SM_IO_WHAT_IRBSIZE	18	/* input read buffer size [OSG] */
#define SM_IO_WHAT_ORBSIZE	19	/* output read buffer size [OSG] */
#define SM_IO_WHAT_IWBSIZE	18	/* input write buffer size [OSG] */
#define SM_IO_WHAT_OWBSIZE	19	/* output write buffer size [OSG] */

#define SM_IO_WHAT_BSIZE	20	/* buffer size [OSG] */

/* Look for SM_IO_WHAT_* in other include files! They can be "spread out"! */

#if 0

typedef int	sm_file_arg_T;		/* do we need this? */

#define SM_IO_WHAT_IRSIZE	 2	/* specify record size [OSG] */
#define SM_IO_WHAT_ORSIZE	 3	/* specify record size [OSG] */
#define SM_IO_WHAT_IBSIZE	 4	/* specify buffer size [OSG] */
#define SM_IO_WHAT_OBSIZE	 5	/* specify buffer size [OSG] */
#define SM_IO_WHAT_IRBASE	 6	/* record base pointer [OSG] */
#define SM_IO_WHAT_ORBASE	 7	/* record base pointer [OSG] */
#define SM_IO_WHAT_IBBASE	 8	/* buffer base pointer [OSG] */
#define SM_IO_WHAT_OBBASE	 9	/* buffer base pointer [OSG] */
#define SM_IO_WHAT_IRCNT	10	/* record remaining count [G] */
#define SM_IO_WHAT_ORCNT	11	/* record remaining count [G] */
#define SM_IO_WHAT_IBCNT	12	/* buffer remaining count [G] */
#define SM_IO_WHAT_OBCNT	13	/* buffer remaining count [G] */
#define SM_IO_WHAT_TIMEOUT	14	/* default I/O timeout [OSG] */
#define SM_IO_WHAT_LINEBUF	15	/* set/clr line buffering [OSG] */

/* constructor for other types */
#define SM_IO_WHAT(stype, i)	(((stype) << 8) | ((i) & 0xff))
#endif /* 0 */


typedef uint sm_f_flags_T;

/*
**  For internal buffers
**  Notice: smb_r/smb_w could be one counter if we use <0, >0
**
**  This implements a simple buffer.
**  The buffer begins at smb_base and its size is smb_size.
**  The current usage of the buffer is encoded in smb_flags.
**  The interesting flags for the use as buffer are:
**	SMRD: currently reading
**	SMWR: currently writing
**	SMRD and SMWR are never simultaneously asserted
**	SMRW open for reading & writing, i.e., we can switch from one mode
**		to the other
**
**	The following always hold:
**
**		if flags&SMRD, smb_w is 0
**		if flags&SMWR, smb_r is 0
**
**	This ensures that the getc and putc macros (or inline functions) never
**	try to write or read from a file that is in `read' or `write' mode.
**	(Moreover, they can, and do, automatically switch from read mode to
**	write mode, and back, on "r+" and "w+" files.)
**
**  smb_r/smb_w denote the number of bytes left to read/write.
**  smb_p is the read/write pointer into the buffer, i.e., it points
**  to the location in the buffer where to read/write the next byte.
**
**	|<-      smb_size       ->|
**	|----------------|--------|
**      ^                ^        ^
**      smb_base         smb_p    smb_base + smb_size
**
**	if SMRD: smb_p + smb_r <= smb_base + smb_size
**	if SMWR: smb_p + smb_w <= smb_base + smb_size
**
**  The buffer acts as very simple queue between the producer and the consumer.
**  "Simple" approach means:
**	SMRD: the buffer is always completely read and then filled from
**		the base (but maybe not completely filled since there
**		might not be enough data).
**	SMWR: the buffer is written and then completely flushed such
**		that is empty again.
**  "Real" queues would have different pointers to write/read for
**  the producer and consumer such that the queue is entirely utilized.
**  However, that causes problems at "wrap-around", esp. if more than just
**  one item (byte) should be read/written: it must be done piecewise
**  (or at least special care must be taken for the wrap-around cases).
**  We can do something like that because smb_p + smb_r is our
**  pointer to write more data into the buffer.
*/

struct smbuf
{
	int		 smb_size;	/* total size */
	int		 smb_r;		/* left to read */
	int		 smb_w;		/* left to write */
	int		 smb_fd;	/* fileno, if Unix fd, else -1 */
	sm_f_flags_T	 smb_flags;	/* flags, below */
#if SM_IO_ERR_VAL
	sm_ret_T	 smb_error;	/* error code; for logging etc */
#endif
	uchar		*smb_base;	/* start of buffer */
	uchar		*smb_p;		/* pointer into buffer */
};

#define SM_NULL_BUF {0, 0, 0, -1, (sm_f_flags_T) 0, (uchar *) 0, (uchar *) 0}
#define SM_INIT_BUF(fl, fd) {0, 0, 0, (fd), (fl), (uchar *) 0, (uchar *) 0}

typedef struct smbuf smbuf_T;

#if 0
/* in iostub.h */
typedef struct sm_file sm_file_T;
typedef struct sm_stream sm_stream_T;
#endif

/*
**  note: we don't really need open() since it is immediately called
**  by sm_io_open() anyways. instead a stacking function like in
**  sfio would be sufficient. funopen() (from BSD) doesn't provide
**  an open() function pointer either
*/

/* XXX specify these properly! */
typedef sm_ret_T (open_F)(sm_file_T *, const void *, int, va_list);
typedef sm_ret_T (close_F)(sm_file_T *, int);

/* use size_t also for output value instead of ssize_t? */
typedef sm_ret_T (read_F)(sm_file_T *, uchar *, size_t, ssize_t *);
typedef sm_ret_T (write_F)(sm_file_T *, const uchar *, size_t, ssize_t *);
typedef sm_ret_T (getbuf_F)(sm_file_T *, ssize_t *);
typedef sm_ret_T (putbuf_F)(sm_file_T *, size_t, ssize_t *);
typedef sm_ret_T (seek_F)(sm_file_T *, off_t, int);
typedef sm_ret_T (setinfo_F)(sm_file_T *, int , void *);
typedef sm_ret_T (getinfo_F)(sm_file_T *, int , void *);
/* add more functions? see README.IO */

#define f_open(fp)	((fp).f_stream.fs_open)
#define f_close(fp)	((fp).f_stream.fs_close)
#define f_read(fp)	((fp).f_stream.fs_read)
#define f_write(fp)	((fp).f_stream.fs_write)
#define f_seek(fp)	((fp).f_stream.fs_seek)
#define f_getbuf(fp)	((fp).f_stream.fs_getbuf)
#define f_putbuf(fp)	((fp).f_stream.fs_putbuf)
#define f_getinfo(fp)	((fp).f_stream.fs_getinfo)
#define f_setinfo(fp)	((fp).f_stream.fs_setinfo)

/* flags for close() */
#define SM_IO_CF_NONE	0x0000	/* default: "persistance" */
#define SM_IO_CF_SYNC	0x0001	/* "persistance" + sync */
#define SM_IO_CF_RM	0x0002	/* unlink (remove) at close */

/* if you change something here, change SM_STREAM_STRUCT too! */
struct sm_stream
{
	sm_magic_T	 sm_magic;

	/* operations */
	open_F		*fs_open;
	close_F		*fs_close;
	read_F		*fs_read;
	write_F		*fs_write;
#if SM_IO_GETPUTBUF
	getbuf_F	*fs_getbuf;
	putbuf_F	*fs_putbuf;
#endif
	getinfo_F	*fs_getinfo;
	setinfo_F	*fs_setinfo;
	seek_F		*fs_seek;

};

/*
**  sm I/O state variables (internal only).
**
**	Notes about double buffering:
**	1. the current buffer is always f_bf, it is
**	a copy of f_rdbuf or f_wrbuf. Because it is a copy, the modifications
**	only apply to f_bf, i.e., the data in f_rdbuf or f_wrbuf is NOT
**	valid (unless the buffer is "inactive").
**	2. getc(), getb() etc do NOT switch the buffer to reading, i.e.,
**	if the buffer is in WR mode, then getb() will return EOB..
**	It might be useful to make f_bf a pointer to the correct buffer,
**	but that complicates the single buffered case (unless some tricks
**	are used, e.g., make f_bf a pointer and have at least one buffer
**	in sm_file).
**
**	XXX maybe need more cookies for statethread file descriptions,
**	or maybe need something else than "int" for fd (void * ?).
*/

/* XXX if you change something here, change SM_FILE_STRUCT too! */
struct sm_file
{
	sm_magic_T	 sm_magic;	/* free <=> SM_MAGIC_NULL */
	smbuf_T		 f_bf;		/* the buffer (>= 1 byte, if !NULL) */

	/* These can be used for any purpose by a file type implementation: */
	void		*f_cookie;	/* should this be in f_bf? */
	int		 f_ival;

	sm_stream_T	 f_stream;	/* XXX not a pointer right now! */

	sm_intvl_T	 f_timeout;	/* in seconds */

	/* tricks to meet minimum requirements even when malloc() fails */
	uchar		 f_nbuf[1];	/* guarantee a getc() buffer */

	/* Unix stdio files get aligned to block boundaries on fseek() */
	int		 f_blksize;	/* st_blksize (may be != bf.size) */

	off_t		 f_lseekoff;	/* current lseek offset */
	int		 f_dup_cnt;	/* count file dup'd */

	smbuf_T		 f_rdbuf;
	smbuf_T		 f_wrbuf;
};

/* macros to abstract buffer management a bit */
#define f_r(fp)	((fp).f_bf.smb_r)
#define f_w(fp)	((fp).f_bf.smb_w)
#define f_p(fp)	((fp).f_bf.smb_p)
#define f_cookie(fp)	((fp).f_cookie)
#define f_fd(fp)	((fp).f_bf.smb_fd)
#define f_flags(fp)	((fp).f_bf.smb_flags)
#if SM_IO_ERR_VAL
# define f_error(fp)	((fp).f_bf.smb_error)
#endif
#define f_bf(fp)	((fp).f_bf)
#define f_bfbase(fp)	((fp).f_bf.smb_base)
#define f_bfsize(fp)	((fp).f_bf.smb_size)
#define f_blksize(fp)	((fp).f_blksize)

#define f_rd_r(fp)	((fp).f_rdbuf.smb_r)
#define f_rd_p(fp)	((fp).f_rdbuf.smb_p)
#define f_rd_fd(fp)	((fp).f_rdbuf.smb_fd)
#define f_wr_w(fp)	((fp).f_wrbuf.smb_w)
#define f_wr_p(fp)	((fp).f_wrbuf.smb_p)
#define f_wr_fd(fp)	((fp).f_wrbuf.smb_fd)

#define f_rd_bfbase(fp)	((fp).f_rdbuf.smb_base)
#define f_wr_bfbase(fp)	((fp).f_wrbuf.smb_base)
#define f_rd_bfsize(fp)	((fp).f_rdbuf.smb_size)
#define f_wr_bfsize(fp)	((fp).f_wrbuf.smb_size)
#define f_rd_flags(fp)	((fp).f_rdbuf.smb_flags)
#define f_wr_flags(fp)	((fp).f_wrbuf.smb_flags)

__BEGIN_DECLS
extern sm_file_T	SmIoF[];
extern sm_file_T	SmFtStdio_def;
extern sm_file_T	SmFtStdiofd_def;
extern sm_file_T	SmFtString_def;
extern sm_file_T	SmFtSyslog_def;
extern sm_file_T	SmFtRealStdio_def;

#define SMIOIN_FILENO		0
#define SMIOOUT_FILENO		1
#define SMIOERR_FILENO		2
#define SMIO_FILES		3

/* Common predefined and already (usually) open files (exposed) */
#define smioin		(&SmIoF[SMIOIN_FILENO])
#define smioout		(&SmIoF[SMIOOUT_FILENO])
#define smioerr		(&SmIoF[SMIOERR_FILENO])

#define SmFtStdio	(&SmFtStdio_def)
#define SmFtStdiofd	(&SmFtStdiofd_def)
#define SmFtString	(&SmFtString_def)
#define SmFtSyslog	(&SmFtSyslog_def)
#define SmFtRealStdio	(&SmFtRealStdio_def)

#define SmStStdio	(&(SmFtStdio_def.f_stream))
#define SmStStdiofd	(&(SmFtStdiofd_def.f_stream))
#define SmStString	(&(SmFtString_def.f_stream))
#define SmStSyslog	(&(SmFtSyslog_def.f_stream))
#define SmStRealStdio	(&(SmFtRealStdio_def.f_stream))

#define SM_FILE_FUNCT_ASSIGN(dst, src)		dst = src

#if SM_IO_GETPUTBUF
# define SM_IO_GPBUF_ASSIGN(f)	\
	f_getbuf(f) = (getbuf);	\
	f_putbuf(f) = (putbuf)
#else /* SM_IO_GETPUTBUF */
# define SM_IO_GPBUF_ASSIGN(f)
#endif /* SM_IO_GETPUTBUF */


#if SM_IO_GETPUTBUF
# define SM_STREAM_STRUCT(open, close, read, write, getbuf, putbuf, seek, get, set) \
    {SM_STREAM_MAGIC, (open), (close), \
	(read), (write), (getbuf), (putbuf), (get), (set), (seek) }
#else
# define SM_STREAM_STRUCT(open, close, read, write, getbuf, putbuf, seek, get, set) \
    {SM_STREAM_MAGIC, (open), (close), \
	(read), (write), (get), (set), (seek) }
#endif /* SM_IO_GETPUTBUF */

#define SM_FILE_STRUCT(name, fileno, open, close, read, write, getbuf, putbuf, seek, get, set, flags, timeout) \
    {SM_FILE_MAGIC, SM_INIT_BUF(flags, fileno), \
	(void *) 0, 0, \
	SM_STREAM_STRUCT(open, close, read, write, getbuf, putbuf, seek, get, set), \
	(timeout)}

#ifdef __STDC__
# define SM_IO_SET_TYPE(f, name, open, close, read, write, getbuf, putbuf, seek, get, set, timeout) \
    (f) = SM_FILE_STRUCT(name, -1, open, close, read, write, getbuf, putbuf, seek, get, set, 0, timeout)
#  define SM_IO_INIT_TYPE(f, name, open, close, read, write, getbuf, putbuf, seek, get, set, timeout)

#else /* __STDC__ */
# define SM_IO_SET_TYPE(f, name, open, close, read, write, getbuf, putbuf, seek, get, set, timeout) (f)

/* XXX fix these? abstract out buffer assignments! */
#  define SM_IO_INIT_TYPE(f, name, open, close, read, write, getbuf, putbuf, seek, get, set, timeout) \
	(f).sm_magic = SM_FILE_MAGIC;	\
	f_p(f) = (uchar *) 0;	\
	f_r(f) = 0;	\
	f_w(f) = 0;	\
	f_flags(f) = 0L;	\
	f_fd(f) = 0;	\
	f_bfbase(f) = (uchar *) 0;	\
	f_bfsize(f) = 0;	\
	(f).f_cookie = (void *) 0;	\
	(f).f_ival = 0;	\
	f_open(f) = (open);	\
	f_close(f) = (close);	\
	f_read(f) = (read);	\
	f_write(f) = (write);	\
	SM_IO_GPBUF_ASSIGN(f);	\
	f_seek(f) = (seek);	\
	f_setinfo(f) = (set);	\
	f_getinfo(f) = (get);	\
	(f).f_timeout = (timeout);

#endif /* __STDC__ */

__END_DECLS

#define SM_IS_FP(fp)	SM_REQUIRE_ISA((fp), SM_FILE_MAGIC)

/* Internal flags */
/* XXX Order this differently so the masks down below are simpler */
/* XXX get rid of some of these, e.g., buffering */


/* read/write directions: */
#define SMRD		0x000001	/* OK to read */
#define SMWR		0x000002	/* OK to write */
	/* RD and WR are never simultaneously asserted */
#define SMRW		0x000004	/* open for reading & writing */
#define SMMODEMASK	0x000007	/* read/write mode bits */

/* buffering: */
#define SMFBF		0x000010	/* fully buffered */
#define SMNBF		0x000020	/* unbuffered */

/* conditions: EOF/error */
#define SMFEOF		0x000040	/* found EOF */
#define SMERR		0x000080	/* found error */
/* add a flag for timeout? what about other I/O errors? */

/* misc */
#define SMMBF		0x000100	/* buf is from malloc */
#define SMAPP		0x000200	/* fdopen()ed in append mode */
#define SMSTR		0x000400	/* this is an snprintf string */
#define SMSTRSTR	0x000800	/* this is a str */
#define SMOPT		0x001000	/* do fseek() optimisation */
#define SMNPT		0x002000	/* do not do fseek() optimisation */
#define SMOFF		0x004000	/* set iff offset is in fact correct */
#define SMALC		0x010000	/* allocate string space dynamically */
#define SMDOUBLE	0x020000	/* double buffered */
#define SMRDBUF		0x040000	/* using rd buf */
#define SMWRBUF		0x080000	/* using wr buf */
#define SMBLOCK		0x100000	/* blocking mode */


#define sm_io_blocking(fp)	(f_flags(*fp) & SMBLOCK)
#define sm_io_setblocking(fp)	f_flags(*fp) |= SMBLOCK
#define sm_io_clrblocking(fp)	f_flags(*fp) &= ~SMBLOCK

#define sm_io_double(fp)	(f_flags(*fp) & SMDOUBLE)
#define sm_io_setdouble(fp)	f_flags(*fp) |= SMDOUBLE
#define sm_io_clrdouble(fp)	f_flags(*fp) &= ~SMDOUBLE

/* defines for timeout constants */
#define SM_TIME_IMMEDIATE	((sm_intvl_T) (0))
#define SM_TIME_FOREVER		((sm_intvl_T) (-1))
#define SM_TIME_DEFAULT		((sm_intvl_T) (-2))

/* Exposed buffering type flags */
#define SM_IO_FBF	0	/* setvbuf should set fully buffered */
#define SM_IO_NBF	1	/* setvbuf should set unbuffered */

/*
**  size of buffer used by setbuf.
**  If underlying filesystem blocksize is discoverable that is used instead
*/

#define SM_IO_BUFSIZ	4096

/*
**  XXX how to return this properly from the functions?
**  how does this fit into our error model
*/

#define SM_IO_EOF	(-1)
#define SM_IO_EOB	(-2)

__BEGIN_DECLS
void	 sm_io_clearerr(sm_file_T *_fp);
sm_ret_T sm_io_close(sm_file_T *_fp, int _flags);
sm_ret_T sm_io_dup(sm_file_T *_fp);
int	 sm_io_eof(sm_file_T *_fp);
int	 sm_io_error(sm_file_T *_fp);
char	*sm_io_fgets(sm_file_T	*_fp, int, char *_buf, int _n);
sm_ret_T sm_io_flush(sm_file_T *_fp);
sm_ret_T sm_fgetline(sm_file_T *_fp, sm_str_P _str);
sm_ret_T sm_fgetline0(sm_file_T *_fp, sm_str_P _str);
sm_ret_T sm_io_ffill(sm_file_T *_fp, size_t _n);
sm_ret_T sm_io_fgetuint32(sm_file_T *_fp, uint32_t *_n);
sm_ret_T sm_io_fgetn(sm_file_T *_fp, uchar *_s, size_t _l);
sm_ret_T sm_io_fskip(sm_file_T *_fp, size_t _l);
#if !SM_NO_CSTR
sm_ret_T sm_io_fgetncstr(sm_file_T *_fp, sm_cstr_P *_pcstr, uint _l);
#endif

int
#if SM_CHK_PRINTF
PRINTFLIKE(2, 3)
#endif
sm_io_fprintf(sm_file_T *_fp,const char *_fmt, ...);

sm_ret_T sm_io_fputs(sm_file_T *_fp, const uchar *_s);
sm_ret_T sm_io_fputuint32(sm_file_T *_fp, uint32_t _n);
sm_ret_T sm_io_fput2uint32(sm_file_T *_fp, uint32_t _n1, uint32_t _n2);
sm_ret_T sm_io_fput3uint32(sm_file_T *_fp, uint32_t _n1, uint32_t _n2, uint32_t _n3);
sm_ret_T sm_io_fputv(sm_file_T *_fp, ...);
sm_ret_T sm_io_fputstr(sm_file_T *_fp, const sm_str_P _s);
#if !SM_NO_CSTR
sm_ret_T sm_io_fputcstr(sm_file_T *_fp, const sm_cstr_P _cstr);
#endif
sm_ret_T sm_io_fputn(sm_file_T *_fp, const uchar *_s, size_t _l);
sm_ret_T sm_io_fpad(sm_file_T *_fp, size_t _len);
sm_ret_T sm_io_falign(sm_file_T *_fp, size_t _n);

int SCANFLIKE(2, 3)
sm_io_fscanf(sm_file_T *_fp, const char *_fmt, ...);

int	 sm_io_getc(sm_file_T *_fp);
int	 sm_io_getinfo(sm_file_T *_fp, int _what, void *_valp);
sm_ret_T sm_io_open(const sm_stream_T *_type, const void *_info,
			int _flags, sm_file_T **_newfp, ...);
#if 0
sm_ret_T sm_io_reopen(const sm_stream_T *_type, const void *_info,
			int _flags, sm_file_T *_fp, sm_file_T **_newfp,
			sm_rpool_P _rpool);
#endif /* 0 */
int	 sm_io_purge(sm_file_T *_fp);
int	 sm_io_putc(sm_file_T *_fp, int _ch);
read_F	 sm_io_read;
int	 sm_io_seek(sm_file_T *_fp, long _offset, int _whence);
int	 sm_io_setinfo(sm_file_T *_fp, int _what, void *_valp);
sm_f_flags_T	 sm_whatbuf(sm_file_T *_fp, size_t *_bufsize);
sm_ret_T sm_io_setvbuf(sm_file_T *_fp, uchar *_buf, int _mode, size_t _size);

int SCANFLIKE(2, 3)
sm_io_sscanf(const char *_str, char const *_fmt, ...);
int	sm_vsscanf(const char *_str, const char *_fmt, va_list _ap);

long	 sm_io_tell(sm_file_T *_fp);
int	 sm_io_ungetc(sm_file_T *_fp, int _c);
int	 sm_io_vfprintf(sm_file_T *_fp, const char *_fmt0, va_list _ap);
write_F	 sm_io_write;

sm_ret_T sm_io_fopen(char *_pathname, int _flags, sm_file_T **_newfp, ...);

#if 0
sm_file_T	*sm_io_stdioopen(FILE *_stream, char *_mode);
#endif

int	 sm_vsnprintf(char *_str, size_t _n, const char *_fmt, va_list _ap);

int	 sm_asprintf(char **_str, const char *_fmt, ...);
int	 sm_vasprintf(char **_str, const char *_fmt, va_list _ap);

sm_ret_T sm_fd_nonblock(int _fd, bool _on);
sm_ret_T sm_fp_nonblock(sm_file_T *_fp, bool _on);
sm_ret_T sm_iotord(sm_file_T *_fp);
sm_ret_T sm_write_wait(int _fd, int _timeout);

#ifdef SMSTRSTR
int
#if SM_CHK_PRINTF
PRINTFLIKE(2, 3)
#endif
sm_strprintf(sm_str_P _str, const char *_fmt, ...);
int
sm_strvprintf(sm_str_P _str, const char *_fmt, va_list _ap);
sm_ret_T sm_str2file(sm_str_P _str, sm_file_T *_fp);
#endif /* SMSTRSTR */

__END_DECLS

/*
** Functions internal to the implementation.
*/

__BEGIN_DECLS
int	sm_rget(sm_file_T *_fp);
int	sm_vfscanf(sm_file_T *_fp, char const *_fmt0, va_list _ap);
int	sm_wbuf(sm_file_T *_fp, int _c);
__END_DECLS

/*
**  The macros are here so that we can
**  define function versions in the library.
*/

#define sm_getc(f) \
	(--f_r(*f) < 0 ? \
		sm_rget(f) : \
		(int)(*f_p(*(f))++))

/*
**  return element out of buffer or SM_IO_EOB if buffer is empty
**  Note: to do this properly (accessing the buffer almost directly)
**  we should provide some form of locking. Currently it's up to
**  the application to "ensure" that the buffer isn't modified, i.e.,
**  the file must not be accessed in any other way inbetween.
*/

#define sm_getb(f) \
	(f_r(*f) == 0 ? SM_IO_EOB : (--f_r(*f), (int)(*f_p(*(f))++)))

/*
**  This has been tuned to generate reasonable code on the vax using pcc.
**  (It also generates reasonable x86 code using gcc.)
*/

#define sm_putc(f, c) \
	(--f_w(*f) < 0 ? \
		sm_wbuf(f, (int)(c)) : \
		(*f_p(*(f)) = (c), (int)*f_p(*(f))++))

#define sm_eof(p)	((f_flags(*p) & SMFEOF) != 0)
#define sm_error(p)	((f_flags(*p) & SMERR) != 0)
#define sm_clearerr(p)	((void)(f_flags(*p) &= ~(SMERR|SMFEOF)))

#define sm_io_eof(p)	sm_eof(p)
#define sm_io_error(p)	sm_error(p)

#define sm_io_clearerr(p)	sm_clearerr(p)

#ifndef lint
/* # ifndef _POSIX_SOURCE  * XXX WHY XXX */
#  define sm_io_getc(fp)	sm_getc(fp)
# if 0
#  define sm_io_putc(fp, x)	sm_putc(fp, x)
# endif
/* # endif * _POSIX_SOURCE */
#endif /* ! lint */

/* valid file description (very simple test) */
/* #define ISVALIDFD(fd)	((fd) >= 0)	*/

#define sm_io_fileno(fp)	sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL)

/* I/O descriptors for sm_fvwrite() */
struct sm_iov
{
	void	*iov_base;
	size_t	 iov_len;
};

typedef struct sm_iov sm_iov_T;

struct sm_uio
{
	sm_iov_T	*uio_iov;
	int		 uio_iovcnt;	/* number of iov's */
	int		 uio_resid;
};

typedef struct sm_uio sm_uio_T;

#ifndef FDSET_CAST
# define FDSET_CAST		/* empty cast for fd_set arg to select */
#endif

/* minimum buffer size required? */
#if !HAVE_STBLKSZ_INET
# ifndef SM_IO_MIN_BUF
#  define SM_IO_MIN_BUF SM_IO_BUFSIZ
# endif
#endif

#if !HAVE_ASPRINTF
int
#if SM_CHK_PRINTF
PRINTFLIKE(2, 3)
#endif
asprintf(char **_str, const char *_fmt, ...);
#endif /* !HAVE_ASPRINTF */

#endif /* SM_IO_H */


syntax highlighted by Code2HTML, v. 0.9.1