/* * 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 */