.TH SFIO 3 "07 July 1996" .SH NAME \fBsfio\fR \- safe/fast string/file input/output .SH SYNOPSIS .de Tp .fl .ne 4 .TP .. .de Ss .fl .ne 4 .SS "\\$1" .. .ta 1.0i 2.0i 3.0i 4.0i 5.0i .nf .ft 5 #include #define ulong unsigned long .ft 1 .fi .Ss "DATA TYPES" .nf .ft 5 Void_t; Sfoff_t; Sfio_t; Sfdisc_t; int (*Sfread_f)(Sfio_t*, Void_t*, int, Sfdisc_t*); int (*Sfwrite_f)(Sfio_t*, Void_t*, int, Sfdisc_t*); Sfoff_t (*Sfseek_f)(Sfio_t*, Sfoff_t, int, Sfdisc_t*); int (*Sfexcept_f)(Sfio_t*, int, Void_t*, Sfdisc_t*); .ft 1 .fi .Ss "BIT FLAGS" .nf .ft 5 SF_STRING SF_READ SF_WRITE SF_APPEND SF_LINE SF_SHARE SF_PUBLIC SF_MALLOC SF_STATIC SF_IOCHECK SF_BUFCONST .ft 1 .fi .Ss "OPENING/CLOSING STREAMS" .nf .ft 5 Sfio_t* sfnew(Sfio_t* f, Void_t* buf, int size, int fd, int flags); Sfio_t* sfopen(Sfio_t* f, const char* string, const char* mode); Sfio_t* sfpopen(Sfio_t* f, const char* cmd, const char* mode); Sfio_t* sftmp(int size); int sfclose(Sfio_t* f); .ft 1 .fi .Ss "INPUT/OUTPUT OPERATIONS" .nf .ft 5 int sfgetc(Sfio_t* f); int sfputc(Sfio_t* f, int c); int sfnputc(Sfio_t* f, int c, int n); int sfungetc(Sfio_t* f, int c); ulong sfgetu(Sfio_t* f); int sfputu(Sfio_t* f, ulong v); long sfgetl(Sfio_t* f); int sfputl(Sfio_t* f, long v); double sfgetd(Sfio_t* f); int sfputd(Sfio_t* f, double v); char* sfgetr(Sfio_t* f, int rsc, int string); int sfputr(Sfio_t* f, const char* s, int rsc); Sfoff_t sfmove(Sfio_t* fr, Sfio_t* fw, Sfoff_t n, int rsc); int sfread(Sfio_t* f, Void_t* buf, int n); int sfwrite(Sfio_t* f, const Void_t* buf, int n); Sfoff_t sfseek(Sfio_t* f, Sfoff_t offset, int type); int sfpoll(Sfio_t** fa, int n, int timeout); Void_t* sfreserve(Sfio_t* f, int n, int lock); int sfscanf(Sfio_t* f, const char* format, ...); int sfsscanf(const char* s, const char* format, ...); int sfvscanf(Sfio_t* f, const char* format, va_list args); int sfprintf(Sfio_t* f, const char* format, ...); int sfsprintf(char* s, int n, const char* format, ...); char* sfprints(const char* format, ...); int sfvprintf(Sfio_t* f, const char* format, va_list args); .ft 1 .fi .Ss "BUFFERING, SYNCHRONIZATION" .nf .ft 5 Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, int size); int sfsync(Sfio_t* f); Sfio_t* sfpool(Sfio_t* f, Sfio_t* poolf, int mode); int sfpurge(Sfio_t* f); .ft 1 .fi .Ss "DISCIPLINE" .nf .ft 5 Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc); int sfraise(Sfio_t* f, int type, Void_t* data); int sfrd(Sfio_t* f, Void_t* buf, int n, Sfdisc_t* disc); int sfwr(Sfio_t* f, Void_t* buf, int n, Sfdisc_t* disc); Sfoff_t sfsk(Sfio_t* f, Sfoff_t offset, int type, Sfdisc_t* disc); .ft 1 .fi .Ss "STREAM CONTROL" .nf .ft 5 int sfset(Sfio_t* f, int flags, int i); int sfsetfd(Sfio_t* f, int fd); Sfio_t* sfstack(Sfio_t* base, Sfio_t* top); Sfio_t* sfswap(Sfio_t* f1, Sfio_t* f2); .ft 1 .fi .Ss "STREAM INFORMATION" .nf .ft 5 Sfoff_t sfsize(Sfio_t* f); Sfoff_t sftell(Sfio_t* f); int sfvalue(Sfio_t* f); int sffileno(Sfio_t* f); int sfstacked(Sfio_t* f); int sfeof(Sfio_t* f); int sferror(Sfio_t* f); int sfclrerr(Sfio_t* f); int sfclrlock(Sfio_t* f); int sfnotify(void (*notify)(Sfio_t* f, int type, int fd)); .ft 1 .fi .Ss "MISCELLANEOUS FUNCTIONS" .nf .ft 5 int sfslen(); int sfulen(ulong v); int sfllen(long v); int sfdlen(double v); int sfpkrd(int fd, Void_t* buf, int n, int rsc, long tm, int peek); .ft 1 .fi .Ss "FULL STRUCTURE SFIO_T" .nf .ft 5 #include #define SFNEW(buf,size,file,flags,disc) .ft 1 .fi .Ss "STDIO-COMPATIBILITY" .nf .ft 5 #include cc ... -lstdio -lsfio .ft 1 .fi .SH DESCRIPTION .PP \fISfio\fP is a library of functions for efficient I/O on buffered streams. Each \fISfio\fP stream corresponds to either a file (see \f5open(2)\fP) or some memory segment (a string stream.) In addition to usual I/O operations, \fISfio\fP supports I/O disciplines for application-specific data processing, stream stacks for recursive stream processing, and stream pools for automatic data synchronization and serialization. .PP The data buffer of a stream is usually a memory area allocated by \f5malloc(3)\fP or some memory area supplied by the application. Where applicable, certain file streams may use memory mapping (\f5mmap(2)\fP) for efficient I/O. The underlying file for such a stream should not be truncated while the stream is active; else, memory access errors may result. Memory mapping can be turned off using \f5sfsetbuf()\fP. .PP System calls (\f5read(2), write(2)\fP or \f5lseek(2)\fP) or their discipline replacements are used to process stream data. Henceforth, wherever applicable, a reference to a system call means a system call or its discipline replacement. .PP A system call causes an exception if its return value is non-positive. Exceptions are handled by \f5Sfio\fP in a default mode unless overridden by application-installed exception handlers (see \f5sfdisc()\fP.) In default handling mode, if an exception is due to a system call interrupt (\f5errno == EINTR\fP on UNIX systems,) the respective call is reinvoked; otherwise, a failure state is entered. .PP Three standard streams are provided: \f5sfstdin\fP for standard input (file descriptor \f50\fP on UNIX systems,) \f5sfstdout\fP for standard output (file descriptor \f51\fP,) and \f5sfstderr\fP for standard error output (file descriptor \f52\fP.) .PP .Ss "DATA TYPES" .PP .Ss " Void_t*" This defines a type suitable to use with functions such as \f5sfread()\fP or \f5sfwrite()\fP that perform I/O with objects of types unknown to \fISfio\fP. \f5Void_t\fP is a macro defined as \f5void\fP for ANSI-C and C++ and \f5char\fP for other compilation environments. .PP .Ss " Sfoff_t" This defines an integral type suitable to address file extent. .PP .Ss " Sfio_t" This defines the stream type. .PP .Ss " Sfdisc_t" .Ss " int (*Sfread_f)(Sfio_t*, Void_t*, int, Sfdisc_t*)" .Ss " int (*Sfwrite_f)(Sfio_t*, Void_t*, int, Sfdisc_t*)" .Ss " Sfoff_t (*Sfseek_f)(Sfio_t*, Sfoff_t, int, Sfdisc_t*)" .Ss " int (*Sfexcept_f)(Sfio_t*, int, Void_t*, Sfdisc_t*)" \f5Sfdisc_t\fP defines a stream discipline structure and contains fields of types \f5Sfread_f\fP, \f5Sfwrite_f\fP, \f5Sfseek_f\fP, and \f5Sfexcept_f\fP. The first three field types define functions to replace their counterpart system calls: \f5read(2)\fP, \f5write(2)\fP and \f5lseek(2)\fP. See \f5sfdisc()\fP for more details. .Ss "BIT FLAGS" A number of bit flags control stream operations. They are set either at stream initialization or by calling \f5sfset()\fP. Following are the flags: .Tp \f5SF_STRING\fP: The respective stream is memory-based and not file-based. .Tp \f5SF_READ\fP: .Tp \f5SF_WRITE\fP: These flags indicate readability and writability. .Tp \f5SF_APPEND\fP: The respective stream is a file opened for appending data (see \f5open(2)\fP and \f5fcntl(2)\fP.) Data written is always appended at end-of-file. On operating systems without direct support for append mode, \f5lseek(2)\fP (or its discipline replacement) will be used to approximate this behavior. .Tp \f5SF_LINE\fP: The respective stream is line-oriented. For a \f5SF_WRITE\fP stream, buffered data is flushed when a new-line character, \f5\en\fP, is output. For a \f5SF_READ\fP stream, \f5sfscanf()\fP treats matching the white-space character \f5\en\fP specially to avoid unnecessary blocking on unseekable devices. \f5SF_LINE\fP is automatically set for terminal devices at initialization time. .Tp \f5SF_SHARE\fP: The underlying file descriptor is shared by independent entities (for example, multiple processes.) If the stream is seekable and not \f5SF_PUBLIC\fP (see below,) each \f5read(2)\fP or \f5write(2)\fP system call will be preceded by a \f5lseek(2)\fP to ensure that the logical stream position corresponds to the physical file position. If the stream is unseekable (e.g., pipes or terminals,) on systems with proper support facilities (\f5recv(2)\fP or \f5streamio(4)\fP,) the block and record I/O operations (\f5sfread()\fP, \f5sfwrite()\fP, \f5sfmove()\fP, \f5sfgetr()\fP, \f5sfputr()\fP, \f5sfreserve()\fP, \f5sfvscanf()\fP and \f5sfvprintf()\fP) ensure that (1) after each write operation, the stream is synchronized and (2) each read operation only reads the requested requested (i.e., no buffering.) \f5SF_SHARE\fP is automatically set on initialization of \f5sfstdin\fP, \f5sfstdout\fP, and \f5sfstderr\fP if they are pipes or seekable. .Tp \f5SF_PUBLIC\fP: This flag must be used in conjunction with \f5SF_SHARE\fP. It is automatically turned off for unseekable streams. For a seekable stream, after a \f5sfsync()\fP call, the next I/O operation causes the internal stream position to align with the current physical seek position. This allows applications involving multiple processes to schedule and serialize their I/O on a shared file descriptor. \f5SF_SHARE\fP is automatically set on initialization of \f5sfstdin\fP, \f5sfstdout\fP, and \f5sfstderr\fP if they are seekable. .Tp \f5SF_MALLOC\fP: The stream buffer was obtained via \f5malloc(3)\fP and can be reallocated or freed. .Tp \f5SF_STATIC\fP: The stream structure is not to be freed when closed (\f5sfclose()\fP.) This is frequently used by applications that allocate their own stream structures (such an application must use the header file \f5sfio_t.h\fP.) .Tp \f5SF_IOCHECK\fP: If the stream has a discipline exception handler, appropriate exceptions will be raised in \f5sfsync()\fP, \f5sfpurge()\fP or before a system call \f5read(2)\fP or \f5write(2)\fP (see \f5sfdisc()\fP.) .Tp \f5SF_BUFCONST\fP: Application promises not to modify stream buffer after a call to \f5sfreserve()\fP or \f5sfgetr()\fP. For a memory-mappable stream, \f5SF_BUFCONST\fP means that \f5PROT_WRITE\fP is turned off and \f5MAP_SHARED\fP turned on for any mapped area. Thus, the file itself is the backing store for such a mapped area on any page swaps. .PP .Ss "OPENING/CLOSING STREAMS" .PP .Ss " Sfio_t* sfnew(Sfio_t* f, Void_t* buf, int size, int fd, int flags)" This function creates or renews a stream. It returns the new stream on success and \f5NULL\fP on error. .Tp \f5f\fP: If \f5f\fP is \f5NULL\fP, a new stream is created. Otherwise, it is a stream to be initialized or renewed. Initialization is indicated by \f5SF_EOF\fP in \f5flags\fP and \f5f\fP is treated as space just allocated for a new stream. When \f5SF_EOF\fP is not specified, \f5f\fP is renewed, i.e., closed before reinitialized. For renewing, buffer, pool and discipline stack are preserved. Note that, except for \f5SF_STATIC\fP or standard streams, renewing a stream already closed will result in undefined behavior. .Tp \f5buf\fP and \f5size\fP: These determine a buffering scheme. See \f5sfsetbuf()\fP for more details. .Tp \f5fd\fP: If \f5SF_STRING\fP is specified in \f5flags\fP, this is ignored. Otherwise, \f5fd\fP is a file descriptor (e.g., from \f5open(2)\fP) to use for raw data I/O. .Tp \f5flags\fP: This is composed from \f5SF_EOF\fP and bit values defined in the \fBBIT FLAGS\fP section. .Ss " Sfio_t* sfopen(Sfio_t* f, const char* string, const char* mode)" This function creates a new stream from a file name or a string. It returns the new stream on success and \f5NULL\fP on error. .Tp \f5f\fP: This is treated as in \f5sfnew()\fP. .Tp \f5mode\fP: This is composed from the set of letters \f5{s, r, w, +, a, x, b, t}\fP. \f5s\fP specifies that \f5string\fP is a null-terminated string to be opened for string manipulation. In this case, \f5string\fP can be \f5NULL\fP and is equivalent to the empty string. Specifying \f5s\fP alone is equivalent to specifying \f5sr\fP. If \f5s\fP is not specified, \f5string\fP defines a path name to a file. \f5r\fP specifies read mode. \f5w\fP specifies write mode and the underlying file, if any, will be truncated. \f5+\fP means that the new stream will be opened for both reading and writing. \f5a\fP specifies the file to be opened in append mode, meaning that data is always written at the end of the file. \f5x\fP specifies that a file opened for writing should not already exist. For example, \f5"wx"\fP succeeds only if the file does not yet exist. \f5b\fP and \f5t\fP specifies binary and text modes which are only significant on certain non-UNIX systems. .Ss " Sfio_t* sfpopen(Sfio_t* f, const char* cmd, const char* mode)" This function executes a command (via \f5/bin/sh\fP) as a coprocess and sets up pipes to communicate data between it and the application. If the stream was opened for both read and write, there will be two different associated file descriptors. Therefore, beware that \f5sffileno()\fP may return different values depending on the type of the last I/O operation done (see also \f5sfset()\fP.) If \f5sfpopen()\fP opens a stream for writing and the signal handler for \f5SIGPIPE\fP is \f5SIG_DFL\fP, it will be set to \f5SIG_IGN\fP. This protects the application from getting killed on attempts to write to a broken pipe. \f5sfpopen()\fP returns the new stream or \f5NULL\fP on error. .Tp \f5f\fP: The stream to be renewed (see \f5sfnew()\fP). .Tp \f5cmd\fP: The command to be executed. .Tp \f5mode\fP: This should be composed from \f5r\fP, \f5w\fP and \f5+\fP. .Ss " Sfio_t* sftmp(int size)" This function creates a stream for writing and reading temporary data. The stream can be made completely or partially memory-resident. If \f5size\fP is negative, the stream is a pure memory stream. If \f5size\fP is zero, the stream is a pure file stream. If \f5size\fP is positive, the stream is first created as a \f5SF_STRING\fP stream with a buffer of length \f5size\fP. When this buffer is exhausted or on any attempt to change disciplines, a real file is created. \f5sftmp()\fP returns the new stream or \f5NULL\fP on error. .Ss " int sfclose(Sfio_t* f)" This function closes the stream \f5f\fP and frees up its resources. If \f5f\fP is the base of a stack of streams (see \f5sfstack()\fP,) all streams on the stack are closed. If \f5f\fP is a \f5sfpopen\fP-stream, \f5sfclose()\fP waits until the associated command terminates and returns its exit status. \f5SF_READ|SF_SHARE\fP and \f5SF_WRITE\fP streams are synchronized before closing (see \f5sfsync()\fP.) If \f5f\fP has disciplines, their exception handlers will be called twice. The first exception handler call has the \f5type\fP rgument as one of \f5SF_CLOSE\fP or \f5SF_NEW\fP (see \f5sfdisc()\fP.) The latter, \f5SF_NEW\fP is used when a stream is being closed via \f5sfnew()\fP so that it can be renewed. The second call uses \f5type\fP \f5SF_FINAL\fP and is done after all closing operations have succeeded but before the stream itself is deallocated. In either case, if the exception handler returns a negative value, \f5sfclose()\fP will immediately return this value. If the exception handler returns a positive value, \f5sfclose()\fP will immediately return a zero value. Space associated with a stream is normally deallocated upon closing. The stream space can be preserved by setting \f5SF_STATIC\fP. Except for noted cases, \f5sfclose()\fP returns \f5-1\fP for failure and \f50\fP for success. .PP .Ss "INPUT/OUPUT OPERATIONS" .PP .Ss " int sfgetc(Sfio_t* f)" .Ss " int sfputc(Sfio_t* f, int c)" These functions read/write a byte from/to stream \f5f\fP. \f5sfgetc()\fP returns the byte read or \f5-1\fP on error. \f5sfputc()\fP returns \f5c\fP on success and \f5-1\fP on error. .Ss " int sfnputc(Sfio_t* f, int c, int n)" This function writes the byte \f5c\fP to \f5f\fP a maximum of \f5n\fP times. It returns the number of bytes actually written or \f5-1\fP on failure. .Ss " int sfungetc(Sfio_t* f, int c)" This function pushes the byte \f5c\fP back into \f5f\fP. For efficiency, if \f5c\fP matches the byte immediately before the current position in buffered data, the current position 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 not part of buffered data will be discarded on any operation that implies buffer synchronization (e.g., \f5sfdisc()\fP or \f5sfsync()\fP.) \f5sfungetc()\fP returns \f5c\fP on success and \f5-1\fP on failure. .Ss " long sfgetu(Sfio_t* f)" .Ss " int sfputu(Sfio_t* f, ulong v)" These functions read and write \f5unsigned long\fP values in a compact variable-length portable format. Portability across two different architectures requires that the bit order in a byte is the same on both architectures and the output value is storable in an \f5unsigned long\fP on the input architecture. \f5sfgetu()\fP returns the value read or \f5-1\fP on error. \f5sfputu()\fP returns the number of bytes written or \f5-1\fP on error. See also \f5sfulen()\fP. .Ss " long sfgetl(Sfio_t* f)" .Ss " int sfputl(Sfio_t* f, long v)" These functions are similar to \f5sfgetu()\fP and \f5sfputu()\fP but for reading and writing (signed) \f5long\fP values. See also \f5sfllen()\fP. .Ss " double sfgetd(Sfio_t* f)" .Ss " int sfputd(Sfio_t* f, double v)" These functions are similar to \f5sfgetu()\fP and \f5sfputu()\fP but for reading and writing \f5double\fP values. In this case, portability also depends on the input and output architectures having the same floating point value representation. Values are coded and decoded using \f5ldexp(3)\fP and \f5frexp(3)\fP. See also \f5sfdlen()\fP. .Ss " char* sfgetr(Sfio_t* f, int rsc, int string)" This function reads a record of data ending in the record separator \f5rsc\fP. The length of the record even if it is incomplete can be retrieved with \f5sfvalue()\fP. \f5sfgetr()\fP returns the record on success and \f5NULL\fP on error. .Tp \f5f\fP: This is the stream to read from. .Tp \f5rsc\fP: This is the record separator. Note that reaching end-of-file without finding the record separator is an error. .Tp \f5string\fP: If \f5string\fP is positive, a null byte will replace the record separator to make the record into a C string. If \f5string\fP is \f50\fP, the record separator is left intact. A negative value for \f5string\fP should be used only after a failed \f5sfgetr()\fP to retrieve any incomplete record. .Ss " int sfputr(Sfio_t* f, const char* s, int rsc)" This function writes the null-terminated string \f5s\fP to \f5f\fP. If \f5rsc\fP is non-negative, it is a character to be output after outputting the string. \f5sfputr()\fP returns the number of bytes written or \f5-1\fP on failure. .Ss " Sfoff_t sfmove(Sfio_t* fr, Sfio_t* fw, Sfoff_t n, int rsc)" This function moves objects from input stream \f5fr\fP to output stream \f5fw\fP. An object is either a byte or a record. The latter is indicated by a non-negative value for the record separator character \f5rsc\fP. If \f5n\fP is non-negative, it indicates the number of objects to move; otherwise, all of \f5fr\fP will be moved. If either \f5fr\fP or \f5fw\fP is \f5NULL\fP, it acts as if it is a stream corresponding to \f5/dev/null\fP, the UNIX device that has no read data and throws away any write data. For example, the call \f5sfmove(f,(Sfio_t*)0,-1L,'\en')\fP simply counts the number of lines in stream \f5f\fP. \f5sfmove()\fP returns the number of objects moved or \f5-1\fP on failure. .Ss " int sfread(Sfio_t* f, Void_t* buf, int n)" This function reads up to \f5n\fP bytes from \f5f\fP into buffer \f5buf\fP. It returns the number of bytes actually read or \f5-1\fP on error. .Ss " int sfwrite(Sfio_t* f, const Void_t* buf, int n)" This function writes \f5n\fP bytes from buffer \f5buf\fP to \f5f\fP. If \f5f\fP is \f5SF_STRING\fP, and its buffer extent is not large enough, an \f5SF_WRITE\fP exception is raised. \f5sfwrite()\fP returns the number of bytes written or \f5-1\fP on failure. .Ss " Sfoff_t sfseek(Sfio_t* f, Sfoff_t offset, int type)" This function sets a new I/O position for \f5f\fP. If the stream is a \f5SF_STRING\fP stream and the new address is beyond the current buffer extent, an \f5SF_SEEK\fP exception will be raised (see \f5sfdisc()\fP.) \f5sfseek()\fP returns the new position or \f5-1\fP on failure. The new position is determined based on \f5offset\fP and the following values for \f5type\fP: .Tp \f50\fP or \f5SEEK_SET\fP: \f5offset\fP is the desired position. .Tp \f51\fP or \f5SEEK_CUR\fP: \f5offset\fP is a relative offset from the current position. Note that if \f5f\fP is an \f5SF_APPEND\fP stream and the last operation done on it was a write operation, the \fIcurrent position\fP is at the physical end of the underlying file. .Tp \f52\fP or \f5SEEK_END\fP: \f5offset\fP is a relative offset from the physical end of the underlying file. .Ss " int sfpoll(Sfio_t** fa, int n, int timeout)" This function 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. \f5sfpoll()\fP returns the number of ready streams or \f5-1\fP on failure. .Tp \f5fa\fP and \f5n\fP: \f5fa\fP is an array of \f5n\fP streams to be polled. Upon return, ready streams, if any, will be moved to the front of \f5fa\fP in the same relative order. .Tp \f5timeout\fP: This defines an ellapse time in milliseconds to wait to see if any stream is ready for I/O. If \f5timeout\fP is negative, \f5sfpoll()\fP will block until some stream become ready. Note that \f5SF_STRING\fP and normal file streams never block and are always ready for I/O. If a stream with discipline is being polled and its readiness is as yet undetermined (e.g., empty buffer,) the discipline exception function will be called with \f5SF_DPOLL\fP before normal actions are taken. See also \f5sfdisc()\fP. .Ss " Void_t* sfreserve(Sfio_t* f, int n, int lock)" This function reserves a data block from \f5f\fP. For a \f5SF_READ\fP stream, a data block is a segment of data and for a \f5SF_WRITE\fP stream, it is a buffer suitable for writing. For consistency, a stream opened with \f5SF_READ|SF_WRITE\fP will be treated as if it is a \f5SF_READ\fP stream (see \f5sfset()\fP for setting a particular desired mode.) \f5sfreserve()\fP returns the data block on success and \f5NULL\fP on failure. After a \f5sfreserve()\fP call, whether or not it succeeds, \f5sfvalue()\fP can be used to obtain the size of the (may-have-been) available data block. .Tp \f5n < 0\fP: \f5f\fP will be filled or flushed as necessary to ensure that a data block of positive size is available. If this is successful and \f5lock\fP is zero, the I/O position will advance by the size of the available data block. For example, upon success, \f5sfreserve(f,-1,0)\fP returns a positive size data block and advances the I/O position by its size. .Tp \f5n == 0\fP: If \f5lock\fP is zero, \f5f\fP will be filled or flushed as necessary to ensure that a positive-size data block is available. If \f5lock\fP is non-zero, no fill or flush will be performed. In either case, the I/O position will not be advanced. However, if \f5lock\fP is non-zero, \f5f\fP will be locked from further access. Therefore, an application can use \f5sfreserve(f,0,1)\fP to lock \f5f\fP from further access. .Tp \f5n > 0\fP: An attempt is made to reserve a data block of size at least \f5n\fP. If this succeeds and \f5lock\fP is zero, the stream I/O position will advance by \f5n\fP. .Tp \f5lock\fP: When \f5lock\fP is non-zero, there are a number of restrictions. First, if \f5f\fP is \f5SF_SHARE\fP and unseekable, \f5sfreserve()\fP will peek at incoming data using either \f5recv(2)\fP or \f5streamio(4)\fP without reading ahead. In this case, if peeking is not possible, \f5sfreserve()\fP will fail. Second, if \f5f\fP is \f5SF_READ\fP and not using memory-mapping (see \f5sfsetbuf()\fP), reservation must be limited to stream buffer size. If a reservation successfully results in a data block \f5data\fP, and \f5lock\fP is non-zero, the stream I/O position does not advance and \f5f\fP will be locked until unlocked with \f5sfread/sfwrite(f,data,size)\fP. \f5sfread()\fP should be used on read-only stream and \f5sfwrite()\fP should be used on write-only stream. A stream in both read and write modes can release the lock with either call (with associated operational semantics!) .Ss " int sfscanf(Sfio_t* f, const char* format,...)" .Ss " int sfsscanf(const char* s, const char* format,...)" .Ss " int sfvscanf(Sfio_t* f, const char* format, va_list args)" These functions scan data items. \f5sfscanf()\fP scans from input stream \f5f\fP while \f5sfsscanf()\fP scans from null-terminated string \f5s\fP. \f5sfvscanf()\fP is the underlying primitive for the other functions. Item types are determined from patterns in string \f5format\fP. These functions return the number of items successfully scanned or \f5-1\fP on error. .PP A white space character (blank, tab, or new-line) in \f5format\fP normally matches a maximal sequence of input white space characters. However, if the input stream is in \f5SF_LINE\fP mode (see \f5sfset()\fP), the new-line character only matches up to the first input new-line character. This helps to avoid blocking when scanning values from an unseekable device such as a terminal. .PP The standard scan patterns are: \f5i, d, u, o, x, X, p, n, f, e, E, g, G, c, %, s,\fP and \f5[\fP. See the ANSI-C specification for \f5fscanf(3)\fP for details on these patterns. .PP A pattern specification is of the form: \f5%[*][width].[base][(type)][flag]z\fP where \f5z\fP is the pattern type. .Tp \f5*:\fP This discards the corresponding scanned item. .Tp \f5width:\fP This defines the field width, i.e., the maximum number of bytes to scan. .Tp \f5base:\fP This defines the base of an integral value. .Tp \f5(type)\fP: This specifies user-defined type information. Parentheses are balanced. Within a \f5type\fP specification, if necessary, a right parenthesis can be escaped as \f5%)\fP. Type information is passed to functions defined by extended patterns \f5%@\fP and \f5%&\fP as discussed below. .Tp \f5flag:\fP This is a sequence of \f5h\fP, \f5l\fP, or \f5L\fP to indicate the size of the element to be scanned. For example, \f5%hd\fP means scanning a \f5short int\fP, i.e., the argument to assign the scanned item should be a \f5short int*\fP. Flag \f5l\fP, when used with patterns \f5c\fP, \f5s\fP, and \f5[]\fP, indicates that a buffer size follows the buffer argument. This helps to avoid any accidental buffer overwrite. Note that specifying buffer size does not affect the amount of data to be scanned. Rather, only the appropriate amount of scanned data will be copied into the given buffer, the rest is discarded. .PP The \f5i\fP and \f5d\fP patterns scan numbers in bases from \f52\fP to \f564\fP. For the \f5i\fP format which scans integral values in self-describing formats, numbers with general bases are assumed to be of the form: \fIbase#value\fP. Here, \fIbase\fP is a number in base 10 and \fIvalue\fP is a number in the given base. If \fIbase\fP is \f536\fP or less, the digits for \fIvalue\fP can be any combination of \f5[0-9], [a-z], [A-Z]\fP where upper and lower case digits are not distinguishable. If \fIbase\fP is larger than \f536\fP, the set of digits is: .nf \f50123456789\fP \f5abcdefghijklmnopqrstuvwxyz\fP \f5ABCDEFGHIJKLMNOPQRSTUVWXYZ\fP \f5@_\fP .fi .PP In addition to the standard patterns, there are three extended patterns: \f5%:\fP, \f5%@\fP, and \f5%&\fP. .PP The \f5%:\fP pattern defines a new pair of format string and argument list (of the type \f5va_list\fP in \f5varargs.h\fP or \f5stdarg.h\fP.) The scanning process continues with the new pair while the old pair is pushed down in a stack. 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 (below) are inherited. They are reset when the stack is popped. .PP The \f5%@\fP pattern defines a function \f5(*argf)()\fP to set scanned values. The return value of \f5(*argf)()\fP is significant as negative, zero, or positive. A negative return value stops processing of the current format (see also \f5%:\fP.) A zero value means that the scanned value should be set normally using the argument list. A positive value means that \f5(*argf)()\fP successfully sets the value. The prototype of \f5(*argf)()\fP is: .nf \f5int (*argf)(int fmt, Void_t* v, int size, char* type, int n)\fP; .fi .Tp \f5fmt\fP: This defines the pattern to be processed. It is an \f5int\fP value encoded as follows: .nf \f5pattern | (n << 10) | (s << 8)\fP .fi \f5pattern\fP is an 8-bit quantity indicating the pattern type. \f5n\fP is a 6-bit quantity to indicate the number of appearances of one of the flags \f5h\fP, \f5l\fP, and \f5L\fP. If \f5n\fP is positive, a particular flag is identified respectively by the value \f51\fP, \f52\fP or \f53\fP of the 2-bit quantity \f5s\fP. .Tp \f5v\fP: This is a pointer to the value to be assigned. For example, if the value to be assigned is a \f5short int\fP, \f5v\fP is a \f5short int*\fP. Note, however, that if the value to be assigned is a string, \f5v\fP is \f5char*\fP, not \f5char**\fP. .Tp \f5size\fP: This is the length of the string to be assigned or the size of the value to be assigned if it is a primitive type. For example, if the assigned value is the string \f5foobar\fP, \f5size\fP is \f56\fP while if the value is an \f5int\fP, \f5size\fP would likely be \f54\fP on a 32-bit machine. .Tp \f5type, n:\fP \f5type\fP is the string between parentheses in the pattern specification. \f5n\fP is the length of \f5type\fP. .PP The \f5%&\fP pattern defines a function, \f5(*extf)()\fP, to process user-defined patterns, i.e., patterns not defined by \f5sfvscanf()\fP. The return value of \f5extf\fP, if negative, means that the scanned value (if any) is treated as if \f5*\fP was specified, i.e., discarded. Otherwise, it means that the scanned value was successfully processed. In addition, if it is one of the standard patterns, the scanned value (see \f5rv\fP below) will be assigned based on the meaning of that pattern. For example, if the return value is the character \f5d\fP, the scanned value will be assigned as an integer value, \f5short\fP for flag \f5h\fP, \f5long\fP for flag \f5l\fP, and \f5int\fP otherwise. The prototype of \f5(*extf)()\fP is: .nf \f5int (*extf)(Sfio_t* f, int fmt, Void_t* rv,\fP \f5 int width, int base, char* type, int n);\fP .fi .Tp \f5f\fP: This is the same input stream passed to \f5sfvscanf()\fP. Note that \f5sfsscanf()\fP uses a string stream to represent its string argument during the call to \f5sfvscanf()\fP. .Tp \f5fmt\fP: This is the pattern to be processed. It is encoded in the same way as the corresponding argument to \f5(*argf)()\fP. .Tp \f5rv\fP: This is used to return the scanned value. .Tp \f5width\fP: If non-negative, this is the maximum number of input bytes to be scanned. .Tp \f5base\fP: This is the conversion base when scanning integral values. .Tp \f5type, n\fP: This is the user-defined type between parentheses in the pattern specification. .Ss " int sfprintf(Sfio_t* f, const char* format,...)" .Ss " int sfsprintf(char* s, int n, const char* format,...)" .Ss " char* sfprints(const char* format,...)" .Ss " int sfvprintf(Sfio_t* f, const char* format, va_list args)" These functions format output data. \f5sfprintf()\fP and \f5sfvprintf()\fP write to output stream \f5f\fP. \f5sfsprintf()\fP writes to buffer \f5s\fP which is of size \f5n\fP. \f5sfprints()\fP constructs output in some \fISfio\fP-defined area. \f5sfvprintf()\fP is the underlying primitive for the other functions. Except for \f5sfprints()\fP which returns a null-terminated string or \f5NULL\fP on error, other functions return the number of output bytes or \f5-1\fP. The length of string constructed by \f5sfprints()\fP or \f5sfsprintf()\fP can be retrieved by \f5sfslen()\fP. .PP The standard patterns are: \f5n, s, c, %, h, i, d, p, u, o, x, X, g, G, e, E\fP and \f5f\fP. See the ANSI-C specification for the \f5fprintf(3)\fP function for details on these patterns. .PP A pattern specification is of the form: \f5%[flag][width].[precision].[base][(type)]z\fP where \f5z\fP stands for the pattern type. .Tp \f5flag\fP: The flag characters are \f5h\fP, \f5l\fP, \f5\-\fP, \f5+\fP, \fIspace\fP, \f5#\fP, and \f50\fP. \f5h\fP and \f5l\fP flags indicate sizes of formatted elements. For example, \f5%hd\fP formats a \f5short int\fP while \f5%ld\fP formats a \f5long int\fP. \f5-\fP flag left-justifies within the field (otherwise, right-justifying.) \f5+\fP flag means a signed conversion will always begin with a plus or minus sign. \fIspace\fP flag is ignored if \f5+\fP is specified; otherwise, it means that if the first character of a signed conversion is not a sign or if the result is empty, a space will be prefixed. \f5#\fP flag formats in an alternate form. For \f5%o\fP, the first digit is always a zero. For \f5%x\fP and \f5%X\fP, a non-zero result will have a prefix \f50x\fP or \f50X\fP. For \f5%e\fP, \f5%E\fP, \f5%f\fP, \f5%g\fP, and \f5%G\fP, the result always contain a decimal-point. For \f5%g\fP and \f5%G\fP, trailing zeros will not be removed. For \f5%d\fP, \f5%i\fP and \f5%u\fP, the form is \fIbase#number\fP where \fIbase\fP is the conversion base and \fInumber\fP is represented by digits for this \fIbase\fP. For example, a base \f52\fP conversion with \f5%#..2d\fP for \f510\fP is \f52#1010\fP instead of \f51010\fP as printed with \f5%..2d\fP. .Tp \f5width\fP: This defines the width of the printing field. A value to be printed will be justified and padded if necessary to fill out the field width. .Tp \f5precis\fP: For floating point value conversion, this defines the precision. For \f5%c\fP, this defines the number of times to repeat the character being formatted. For \f5%s\fP, this defines the maximum number of characters from the null-terminated input string to output. .Tp \f5base\fP: For \f5%s\fP, if \f5base\fP is defined (i.e., two dots were specified), the input argument is a \f5NULL\fP-terminated list of strings instead of a single string. For \f5%c\fP, if \f5base\fP is defined (i.e., two dots were specified), the input argument is a null-terminated string instead of a single character. Each object, i.e., a string or a character, will be formatted based on the width and precision rules for the particular pattern. In addition, if \f5base\fP is non-zero, it defines an ASCII character used to separate the formatted objects if there are more than one. For example, \f5sfprintf(sfstdout,"%..*s",',',list)\fP prints \f5apple,orange,grape\fP if \f5list\fP is a \f5NULL\fP-terminated list of three strings \f5apple\fP, \f5orange\fP, and \f5grape\fP. For \f5%i\fP, \f5%d\fP, and \f5%u\fP, \f5base\fP defines the conversion base which should be in the range \f5[2,64]\fP. If \f5base\fP is not in the defined range, it is defined to be \f510\fP. The digits to represent numbers are: .nf \f501234567890\fP \f5abcdefghijklmnopqrstuvwxyz\fP \f5ABCDEFGHIJKLMNOPQRSTUVWXYZ\fP \f5@_\fP .fi .Tp \f5(type)\fP: This specifies user-defined type information. Parentheses are balanced. Thus, within a \f5type\fP specification, if necessary, a right parenthesis can be escaped as \f5%)\fP. Type information is passed to functions defined by extended patterns \f5%@\fP and \f5%&\fP as discussed below. .PP There are three extended patterns: \f5%:\fP, \f5%@\fP, and \f5%&\fP. .PP The \f5%:\fP pattern defines a new pair of format string and argument list (an argument list is of type \f5va_list*\fP.) 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 (below) are inherited. When a pair is popped, these functions will be reset. .PP The \f5%@\fP pattern defines a function \f5(*argf)()\fP to get arguments. The return value of \f5(*argf)()\fP is significant as negative, zero, or positive. A negative return value stops processing of current format (see also \f5%:\fP.) A zero value means that to get the required argument from the argument list. A positive value means that \f5(*argf)()\fP successfully returns an argument. The prototype of \f5(*argf)()\fP is: .nf \f5int (*argf)(int fmt, Void_t* rv, char* type, int n)\fP; .fi .Tp \f5fmt\fP: This defines the pattern to be processed. It is an \f5int\fP value encoded as follows: .nf \f5pattern | (n << 10) | (s << 8)\fP .fi \f5pattern\fP is an 8-bit quantity indicating the pattern type. In addition to the standard, extended, and user-defined patterns (if any), pattern \f5*\fP indicates a request for an integer defining \fIwidth\fP, \fIprecision\fP, or \fI conversion base\fP which will be indicated respectively by \f5n==1\fP, \f5n==2\fP and \f5n==3\fP. For pattern \f5c\fP, \f5n!=0\fP means requesting a null-terminated string instead of a single character (because \f5base\fP is non-negative as explained above.) Similarly, for pattern \f5s\fP, \f5n!=0\fP means requesting a NULL-terminated list of strings instead of a single string. For pattern \f5:\fP which requires a format string and an argument list, \f5n==1\fP means requesting the former while \f5n==2\fP means requesting the latter. For patterns \f5@\fP and \f5&\fP, \f5n\fP and \f5s\fP are currently undefined. For other patterns, \f5n\fP is a 6-bit quantity indicating the number of appearances of one of the flags \f5h\fP, \f5l\fP, and \f5L\fP. If \f5n\fP is positive, a particular flag is identified respectively by the value \f51\fP, \f52\fP or \f53\fP of the 2-bit quantity \f5s\fP. .Tp \f5rv\fP: This is used to return the requested value. .Tp \f5type, n\fP: \f5type\fP is the string between parentheses in the pattern specification. \f5n\fP is the length of \f5type\fP. .PP The \f5%&\fP pattern defines a function, \f5(*extf)()\fP, to interpret user-defined patterns, i.e., patterns not yet defined by \f5sfvprintf()\fP. The prototype of \f5(*extf)()\fP is: .nf \f5int (*extf)(Void_t* v, int fmt, char** rv,\fP \f5 int precis, int base, char* type, int n);\fP .fi .Tp \f5v\fP: This is the value to be formatted. .Tp \f5fmt\fP: This is the pattern to format the value. It is encoded in the same way as the corresponding argument for the \f5(*argf)()\fP function above. .Tp \f5rv\fP: This is used to return a string representing the formatted result. If \f5*rv\fP is not \f5NULL\fP, a non-negative return value from \f5(*extf)()\fP defines the length of string \f5*rv\fP, and a negative return value means \f5*rv\fP is a null-terminated string. If \f5*rv\fP is \f5NULL\fP, a negative return value from \f5(*extf)()\fP terminates processing of the current format (see \f5%:\fP pattern) and a non-negative return value treats the pattern as unmatched. .Tp \f5precis\fP: This is the amount of precision required. .Tp \f5base\fP: This is the base for formatting values. .Tp \f5type, n\fP: \f5type\fP is the string between parentheses in the pattern specification. \f5n\fP is the length of this string. .PP .Ss "BUFFERING, SYNCHRONIZATION" .PP .Ss " Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, int size)" This function sets buffering scheme for stream \f5f\fP. Except for buffer inquiry (see the case \f5size == 0\fP,) \f5f\fP will be synchronized before any buffer modification. 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. After a \f5sfsetbuf()\fP call, \f5sfvalue()\fP returns the size of the returned buffer. .Tp \f5size < 0\fP: \fISfio\fP will pick a buffer size suitable for the stream. If \f5buf\fP is \f5NULL\fP, \fISfio\fP will also pick a suitable buffering scheme (such as memory mapping.) If \f5buf\fP is not \f5NULL\fP, its actual value is ignored but the buffer will be allocated via \f5malloc(3)\fP. This can be used to avoid memory mapping. .Tp \f5size > 0\fP: This is the suggested size to use for buffering or memory mapping. If \f5buf\fP is \f5NULL\fP, \fISfio\fP will pick a suitable buffering scheme as discussed above. If \f5buf\fP is not \f5NULL\fP, then \f5buf\fP and \f5size\fP determine a buffer of the given size. .Tp \f5size == 0\fP: If \f5buf\fP is \f5NULL\fP, the stream will be unbuffered. If \f5buf\fP is not \f5NULL\fP, \f5sfsetbuf()\fP simply returns the stream buffer. In this case, no attempt will be made to synchronize the stream. .Ss " int sfsync(Sfio_t* f)" This function synchronizes the logical and physical views of stream \f5f\fP. For a \f5SF_WRITE\fP stream, this means to write out any buffered data. For a seekable \f5SF_READ\fP file stream, the physical file position is aligned with the logical stream position and, if \f5SF_SHARE\fP is on, buffered data is discarded. If \f5f\fP is \f5NULL\fP, all streams are synchronized. If \f5f\fP is the base of a stream stack (see \f5sfstack()\fP,) all stacked streams are synchronized. Note that a stacked stream can only be synchronized this way. If \f5f\fP is in a pool (see \f5sfpool()\fP) but not being the head, the pool head is synchronized. \f5sfsync()\fP returns a negative value for failure and \f50\fP for success. .Ss " Sfio_t* sfpool(Sfio_t* f, Sfio_t* poolf, int mode)" Occasionally, it is desirable to coordinate I/O from different streams. This can be done by pooling streams. In a pool, only one stream is at the head and can have buffered data. All other streams in the pool will be synchronized. A stream becomes head when it is used for some I/O operation. \f5sfpool()\fP returns \f5NULL\fP on failure. .Tp \f5f\fP and \f5poolf\fP: If \f5f\fP is \f5NULL\fP, \f5sfpool()\fP returns the current head of the pool identified by \f5poolf\fP. Otherwise, if \f5poolf\fP is \f5NULL\fP, \f5f\fP is deleted from its pool and \f5sfpool()\fP returns some stream from the remainder. Note also that some other stream from the same pool will become head. If this is not possible, \f5sfpool()\fP will return error. If both \f5f\fP and \f5poolf\fP are not \f5NULL\fP, \f5f\fP is deleted from its current pool if necessary and put into the same pool with \f5poolf\fP. In this case, \f5poolf\fP is returned. .Tp \f5mode\fP: If \f5poolf\fP is already in a pool, \f5mode\fP is ignored. Otherwise, \f5mode\fP should be \f50\fP or \f5SF_SHARE\fP. A \f5SF_SHARE\fP pool must contain all streams capable of being in \f5SF_WRITE\fP mode. In this case, at each head stream change, buffered write data of the current head is moved to the new head. .Ss " int sfpurge(Sfio_t* f)" This function discards all buffered data unless \f5f\fP is a \f5SF_STRING\fP stream. Note that if \f5f\fP is a \f5SF_READ\fP stream based on an unseekable device, purged data will not be recoverable. If \f5f\fP is a \f5sfpopen\fP-stream opened for both read and write, data of both the read and write pipe ends will be purged (see \f5sfset()\fP to selectively turn off read or write mode if one set of data is to be preserved.) \f5sfpurge()\fP returns \f5-1\fP for failure and \f50\fP for success. .PP .Ss "DISCIPLINE" .PP A file stream makes use of the system calls \f5read(2)\fP, \f5write(2)\fP and \f5lseek(2)\fP to read, write and position in the underlying file. Alternative I/O methods including exception handling and data pre/post-processing for a stream is done by defining new I/O disciplines and inserting them into the discipline stack of the stream. .Ss " Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)" This function manipulates the discipline stack of stream \f5f\fP. \f5f\fP will be synchronized before any discipline stack manipulation. After a successful discipline stack manipulation, stream I/O position (\f5sfseek()\fP and \f5sftell()\fP) and extent (\f5sfsize()\fP) are updated to reflect that defined by the top discipline. If \f5disc\fP is \f5SF_POPDISC\fP or \f5(Sfdisc_t*)0\fP, the top element of the stack, if any, is popped and its address is returned. Otherwise, \f5disc\fP is a new discipline to be pushed onto the discipline stack. Note that a discipline can be used only on one stream at a time. An application should take care to allocate different discipline structures for use with different streams. .PP A discipline structure is of the type \f5Sfdisc_t\fP which contains the following public fields: .nf \f5Sfread_f readf;\fP \f5Sfwrite_f writef;\fP \f5Sfseek_f seekf;\fP \f5Sfexcept_f exceptf;\fP .fi .PP The first three fields of \f5Sfdisc_t\fP specify alternative I/O functions. If any of them is \f5NULL\fP, it is inherited from a discipline pushed earlier on the stack. Note that a file stream always has \f5read(2)\fP, \f5write(2)\fP, \f5lseek(2)\fP and \f5NIL(Sfexcept_f)\fP as the \fIlogical bottom discipline\fP. Arguments to I/O discipline functions have the same meaning as that of the functions \f5sfrd()\fP, \f5sfwr()\fP and \f5sfsk()\fP described below. .PP The exception function, \f5(*exceptf)()\fP announces exceptional events during I/O operations. It is called as \f5(*exceptf)(Sfio_t* f, int type, Void_t* value, Sfdisc_t* disc)\fP. Unless noted otherwise, the return value of \f5(*exceptf)()\fP is used as follows: .Tp \f5<0\fP: The on-going operation shall terminate. .Tp \f5>0\fP: If the event was raised due to an I/O error, the error has been repaired and the on-going operation shall continue normally. .Tp \f5=0\fP: The on-going operation performs default actions with respect to the raised event. For example, on a read error or end-of-file, the top stream of a stack will be popped and closed and the on-going operation continue with the new top stream. .PP The argument \f5type\fP of \f5(*exceptf)()\fP identifies the particular exceptional event: .Tp \f5SF_READ\fP: .Tp \f5SF_WRITE\fP: These events are normally raised only after a read or write operation fails. In this case, \f5*((int*)value)\fP is the return value from the failed operation (which should be nonpositive). If \f5SF_IOCHECK\fP is on, \f5SF_READ\fP and \f5SF_WRITE\fP are raised immediately before \f5read(2) and write(2)\fP calls. In this case, \f5*((int*)value)\fP is the amount of data to be processed. The return value of \f5(*exceptf)()\fP, if negative, indicates that the stream is not ready for I/O and the calling operation will abort with failure. If it is positive, the stream is ready for I/O but the amount should be restricted to the amount specified by this value. If the return value is zero, the I/O operation is carried out normally. .Tp \f5SF_SEEK\fP: This event is raised when a seek operation fails. .Tp \f5SF_NEW\fP: .Tp \f5SF_CLOSE\fP: .Tp \f5SF_FINAL\fP: These events are raised during a stream closing. \f5SF_NEW\fP is raised for a stream about to be closed to be renewed (\f5sfnew()\fP). \f5SF_CLOSE\fP is raised for a stream about to be closed. \f5SF_FINAL\fP is raised after a stream has been closed and before its space is to be destroyed (see \f5sfclose()\fP.). For these events, a non-zero return value from \f5(*exceptf)()\fP causes \f5sfclose()\fP to return immediately with the same value. .Tp \f5SF_DPUSH\fP: This event is raised when a discipline is about to be pushed down by a new discipline given in \f5(Sfdisc_t*)value\fP. .Tp \f5SF_DPOP\fP: This event is raised when a discipline is about to be popped. \f5(Sfdisc_t*)value\fP is the new top discipline, if any. .Tp \f5SF_DBUFFER\fP: Before \f5sfdisc()\fP pushes or pops a discipline, the stream must be synchronized. If this fails, \f5SF_DBUFFER\fP will be raised. The amount of buffered data is given in \f5*((int*)value)\fP. If the return value of \f5exceptf\fP is positive, the push or pop operation will continue normally; otherwise, \f5sfdisc()\fP returns failure. .Tp \f5SF_DPOLL\fP: This event is raised by \f5sfpoll()\fP to see if stream is ready for I/O. \f5*((int*)value)\fP indicates a time-out interval to wait. In this case, a negative return value means blocking, a positive return value means non-blocking. A zero return value means that \f5sfpoll()\fP should query the stream file descriptor using default methods. .Tp \f5SF_SYNC\fP: .Tp \f5SF_PURGE\fP: If \f5SF_IOCHECK\fP is set, these events are raised immediately after \f5sfsync()\fP or \f5sfpurge()\fP successfully complete their operations and before they return. Note that \f5sfsync()\fP is implied when a \f5SF_WRITE\fP or \f5SF_SHARE|SF_READ\fP stream is closed. Note also that \f5SF_SYNC\fP is not raised for a stream synchronized during a call \f5sfsync((Sfio_t*)0)\fP. .Ss " int sfraise(Sfio_t* f, int type, Void_t* data)" This function calls all exception handlers of stream \f5f\fP with (possibly application-defined) event \f5type\fP and associated \f5data\fP. If an exception handler returns a non-zero value, \f5sfraise()\fP immediate returns the same value. \f5sfraise()\fP normally returns \f50\fP on success and \f5-1\fP on failure. .Ss " int sfrd(Sfio_t* f, Void_t* buf, int n, Sfdisc_t* disc)" .Ss " int sfwr(Sfio_t* f, Void_t* buf, int n, Sfdisc_t* disc)" .Ss " Sfoff_t sfsk(Sfio_t* f, Sfoff_t offset, int type, Sfdisc_t* disc)" The discipline stack defines filters to process incoming/outgoing data. These functions provides a safe method for a discipline I/O function to invoke earlier discipline I/O functions and to properly handle exceptions. They should not be used in any other context. \f5sfrd()\fP and \f5sfwr()\fP return the number of bytes read or written. \f5sfsk()\fP returns the new seek position. On error, all three functions return a negative value which should be \f5-1\fP or the value returned by the exception handler. .PP .Ss "STREAM CONTROL" .PP .Ss " int sfset(Sfio_t* f, int flags, int set)" This function sets flags for the stream \f5f\fP. It returns the previous set of flags or \f50\fP if the stream is currently inaccessible. If \f5set\fP is non-zero, \f5flags\fP are turned on; else, they are turned off. Settable flags are: \f5SF_READ\fP, \f5SF_WRITE\fP, \f5SF_IOCHECK\fP, \f5SF_LINE\fP, \f5SF_SHARE\fP, \f5SF_PUBLIC\fP, \f5SF_MALLOC\fP, \f5SF_STATIC\fP and \f5SF_BUFCONST\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 exclusively in the other mode. 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. .Ss " int sfsetfd(Sfio_t* f, int fd)" This function changes the file descriptor of \f5f\fP. Before a change is realized, \f5(*notify)(f,SF_SETFD,newfd)\fP (see \f5sfnotify()\fP) is called. \f5sfsetfd()\fP returns \f5-1\fP on failure and the new file descriptor on success. .Tp \f5fd >= 0\fP: If the current file descriptor is non-negative, it will be changed using \f5dup(3)\fP to a value larger or equal to \f5fd\fP. Upon a successful change, the previous file descriptor will be closed. If the current file descriptor is negative, it will be set to \f5fd\fP and the stream will be reinitialized. .Tp \f5fd < 0\fP: The stream is synchronized (see \f5sfsync()\fP) and its file descriptor will be set to this value. Then, except for \f5sfclose()\fP, the stream will be inaccessible until a future \f5sfsetfd()\fP call resets the file descriptor to a non-negative value. Thus, \f5sfsetfd(f,-1)\fP can be used to avoid closing the file descriptor of \f5f\fP when \f5f\fP is closed. .Ss " Sfio_t* sfstack(Sfio_t* base, Sfio_t* top)" This function stacks or unstacks stream. Every stream stack is identified by a base stream via which all I/O operations are performed. However, an I/O operation always takes effect on the top stream. If the top stream reaches end-of-file or has an unrecoverable error condition, it is automatically popped and closed (see also \f5sfdisc()\fP for alternative handling of these conditions.) .Tp \f5base\fP: This is the base stream of the stack. .Tp \f5top\fP: If this is \f5SF_POPSTACK\fP or \f5(Sfio_t*)0\fP, the stack is popped and \f5sfstack()\fP returns the popped stream. Otherwise, \f5top\fP is pushed on top of the stack identified by \f5base\fP and \f5sfstack()\fP returns the \f5base\fP stream. .Ss " Sfio_t* sfswap(Sfio_t* f1, Sfio_t* f2)" This function swaps contents of \f5f1\fP and \f5f2\fP. This fails if either stream is in a stream stack but not being a base stream. If \f5f2\fP is \f5NULL\fP, a new stream is constructed as a duplicate of \f5f1\fP. \f5sfswap()\fP returns \f5f2\fP or \f5f1\fP duplicate on success and \f5NULL\fP on failure. .PP .Ss "STREAM INFORMATION" .PP .Ss " Sfoff_t sfsize(Sfio_t* f)" This function returns the size of stream \f5f\fP (see \f5sfnew()\fP). If \f5f\fP is not seekable or if its size is not determinable, \f5sfsize()\fP returns \f5-1\fP. .Ss " Sfoff_t sftell(Sfio_t* f)" This function returns the current I/O position in stream \f5f\fP. Note that if \f5f\fP is \f5SF_APPEND\fP and the last operation done on it was a write operation, the current I/O position is at the physical end-of-file. If \f5f\fP is unseekable, \f5sftell\fP returns the number of bytes read from or written to \f5f\fP. See also \f5sfungetc()\fP. .Ss " int sfvalue(Sfio_t* f)" This function returns the string or buffer length for \f5sfreserve()\fP, \f5sfsetbuf()\fP, and \f5sfgetr()\fP. .Ss " int sffileno(Sfio_t* f)" This function returns the file descriptor of stream \f5f\fP. .Ss " int sfstacked(Sfio_t* f)" This function returns a non-zero value if stream \f5f\fP has been stacked. .Ss " int sfeof(Sfio_t* f)" .Ss " int sferror(Sfio_t* f)" .Ss " int sfclrerr(Sfio_t* f)" \f5sfeof()\fP tells whether or not the previous I/O operation generated and end-of-file condition. \f5sferror()\fP tells whether or not the previous I/O operation generated an error. \f5sfclrerr()\fP clears the end-of-file and error conditions. Note that the end-of-file and error conditions are also cleared on any successful I/O operation. .Ss " int sfclrlock(Sfio_t* f)" This function 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. .Ss " int sfnotify((void(*)notify)(Sfio_t*, int, int) )" This sets a call-back function \f5(*notify)()\fP to announce various stream events. The first and third arguments in a call \f5(*notify)(f,type,file)\fP refer to a stream \f5f\fP and a related file descriptor \f5file\fP. The second argument \f5type\fP indicates the reason for the call: .Tp \f5SF_NEW\fP: \f5f\fP is being opened and \f5file\fP is the underlying file descriptor. .Tp \f5SF_CLOSE\fP: \f5f\fP and \f5file\fP are being closed. .Tp \f5SF_SETFD\fP: The file descriptor of \f5f\fP is being changed to \f5file\fP (see \f5sfsetfd()\fP.) .Tp \f5SF_READ\fP: An attempt to change \f5f\fP to read mode failed. .Tp \f5SF_WRITE\fP: An attempt to change \f5f\fP to write mode failed. .PP .Ss "MISCELLANEOUS FUNCTIONS" .PP .Ss " int sfslen()" This function returns the length of a string just constructed by \f5sfsprintf()\fP or \f5sfprints()\fP. As an obsolete feature, it returns certain exception handler's states and lengths of strings and buffers (see also \f5sfvalue()\fP.) .Ss " int sfulen(ulong v)" .Ss " int sfllen(long v)" .Ss " int sfdlen(double v)" These functions return respectively the number of bytes required to code the \f5unsigned long\fP, \f5long\fP or \f5double\fP value \f5v\fP by \f5sfputu()\fP, \f5sfputl()\fP or \f5sfputd()\fP. .Ss " int sfpkrd(int fd, char* buf, int n, int rsc, long tm, int action)" This function acts directly on the file descriptor \f5fd\fP. It does a combination of peeking on incoming data and a time-out read. Upon success, it returns the number of bytes received. A return value of \f50\fP means that the end-of-file condition has been detected. A negative value represents an error. .Tp \f5buf\fP, \f5n\fP: These define a buffer and its size to read data into. .Tp \f5rsc\fP: If \f5>=0\fP, this defines a record separator. Depending on \f5action\fP, \f5sfpkrd()\fP may read data only up to this record separator. .Tp \f5tm\fP: If \f5>=0\fP, this defines a time interval in milliseconds to wait for incoming data. .Tp \f5action\fP: If \f5action > 0\fP, \f5sfpkrd()\fP will peek on incoming data but will not read past it. Therefore, a future \f5sfpkrd()\fP or \f5read(2)\fP will see the same data again. If \f5action == 0\fP, \f5sfpkrd()\fP will not peek. In addition if \f5rsc >= 0\fP, \f5sfpkrd()\fP guarantees that only a record of data up to the first appearance of \f5rsc\fP is read. Finally, if \f5action < 0\fP, on systems with peeking ability, \f5sfpkrd()\fP guarantees that only a record of data up to the first appearance of \f5rsc\fP is read. Otherwise, it will do a single \f5read(fd,buf,n)\fP to fill the buffer. .PP .Ss "FULL STRUCTURE SFIO_T" .PP .Ss " #include " Most applications based on \fISfio\fP only need to include the header file \f5sfio.h\fP which defines an abbreviated \f5Sfio_t\fP structure without certain fields private to \fISfio\fP. However, there are circumstances (e.g., debugging) where an application may require more details about the full \f5Sfio_t\fP structure. In such a case, the header file \f5sfio_t.h\fP can be used in place of \f5sfio.h\fP. This means that the application will become sensitive to changes in the internal architecture of \fISfio\fP. .Ss " #define SFNEW(buf,size,file,flags,disc)" This macro function is defined in \f5sfio_t.h\fP for use in static initialization of an \f5Sfio_t\fP structure. It requires five arguments: .Tp \f5buf, size\fP: These define a buffer and its size. .Tp \f5file\fP: This defines the underlying file descriptor if any. .Tp \f5flags\fP: This is composed from bit flags described above. .Tp \f5disc\fP: This defines a discipline if any. .PP .Ss "STDIO-COMPATIBILITY" .PP \fISfio\fP provides two compatibility packages to \fIstdio\fP-based applications, a source level interface and a binary level library. These packages provide a union of functions in popular \fIstdio\fP implementations. .PP The source level \fIstdio\fP-compatibility interface is defined in a header \f5stdio.h\fP as a set of macros that map \fIstdio\fP calls to appropriate \fISfio\fP calls. Note that this mapping may extend or slightly change the meaning of certain original \fIstdio\fP operations. For example, \fISfio\fP's version of \f5popen()\fP allows a coprocess to be opened for both reading and writing unlike the original call which only allows a coprocess to be opened for a single mode at a time. Similarly, the \fISfio\fP's \f5fopen()\fP call can be used to create string or memory-based streams in addition to file streams. .PP The binary level \fIstdio\fP-compatibility library, \f5libstdio.a\fP, provides a complete implementation of \fIstdio\fP functions suitable for linking applications already compiled with native header \f5stdio.h\fP. Functions in this implementation are also slightly altered or extended based on underlying \fISfio\fP's functions. .PP Below are supported \fIstdio\fP functions: .PP .nf .ft 5 FILE* fopen(const char* file, const char* mode); FILE* freopen(const char* file, const char* mode, FILE* stream); FILE* fdopen(int filedesc, const char* mode); FILE* popen(const char* command, const char* mode); FILE* tmpfile(); int fclose(FILE* stream); int pclose(FILE* stream); void setbuf(FILE* stream, char* buf); int setvbuf(FILE* stream, char* buf, int mode, int size); void setbuffer(FILE* stream, char* buf, int size); int setlinebuf(FILE* stream); int fflush(FILE* stream); int fpurge(FILE* stream); int fseek(FILE* stream, long offset, int whence); void rewind(FILE* stream); int fgetpos(FILE* stream, long* pos); int fsetpos(FILE* stream, long* pos); long ftell(FILE* stream); int getc(FILE* stream); int fgetc(FILE* stream); int getchar(void); int ungetc(int c, FILE* stream); int getw(FILE* stream); char* gets(char* s); char* fgets(char* s, int n, FILE* stream); int fread(Void_t* ptr, int size, int nmemb, FILE* stream); int putc(int c, FILE* stream); int fputc(int c, FILE* stream); int putchar(int c); int putw(int w, FILE* stream); int puts(const char* s, FILE* stream); int fputs(const char* s, FILE* stream); int fwrite(const Void_t* ptr, int size, int nmemb, FILE* stream); int fscanf(FILE* stream, const char* format, ...); int vfscanf(FILE* stream, const char* format, va_list args); int _doscan(FILE* stream, const char* format, va_list args); int scanf(const char* format, ...); int vscanf(const char* format, va_list args); int sscanf(const char* s, const char* format, ...); int vsscanf(const char* s, const char* format, va_list args); int fprintf(FILE* stream, const char* format, ...); int vfprintf(FILE* stream, const char* format, va_list args); int _doprnt(FILE* stream, const char* format, va_list args); int printf(const char* format, ...); int vprintf(const char* format, va_list args); int sprintf(const char* s, const char* format, ...); int snprintf(const char* s, int n, const char* format, ...); int vsprintf(const char* s, const char* format, va_list args); int vsnprintf(const char* s, int n, const char* format, va_list args); int feof(FILE* stream); int ferror(FILE* stream); int clearerr(FILE* stream); .ft 1 .fi .SH BUGS The binary \fIstdio\fP-compatibility library has not been ported to Linux. .SH AUTHORS Kiem-Phong Vo, kpv@research.att.com, and David G. Korn, dgk@research.att.com.