summaryrefslogtreecommitdiff
path: root/src/lib/libast/man/sfio.3
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/lib/libast/man/sfio.3
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/lib/libast/man/sfio.3')
-rw-r--r--src/lib/libast/man/sfio.32373
1 files changed, 2373 insertions, 0 deletions
diff --git a/src/lib/libast/man/sfio.3 b/src/lib/libast/man/sfio.3
new file mode 100644
index 0000000..f22d0b7
--- /dev/null
+++ b/src/lib/libast/man/sfio.3
@@ -0,0 +1,2373 @@
+.fp 5 CW
+.TH SFIO 3 "01 June 2008"
+.SH NAME
+\fBsfio\fR \- safe/fast string/file input/output
+.SH SYNOPSIS
+.de Tp
+.fl
+.ne 3
+.TP
+..
+.de Ss
+.fl
+.ne 3
+.SS "\\$1"
+..
+.ta 1.0i 2.0i 3.0i 4.0i 5.0i
+.Ss "LIBRARIES"
+.nf
+.ft 5
+#include <sfio.h>
+
+libsfio.a -lsfio
+libstdio.a -lstdio
+libsfio-mt.a -lsfio-mt
+libstdio-mt.a -lstdio-mt
+.ft 1
+.fi
+.Ss "DATA TYPES"
+.nf
+.ft 5
+Void_t;
+Sfoff_t;
+Sflong_t;
+Sfulong_t;
+Sfdouble_t;
+
+Sfio_t;
+
+Sfdisc_t;
+ssize_t (*Sfread_f)(Sfio_t*, Void_t*, size_t, Sfdisc_t*);
+ssize_t (*Sfwrite_f)(Sfio_t*, const Void_t*, size_t, Sfdisc_t*);
+Sfoff_t (*Sfseek_f)(Sfio_t*, Sfoff_t, int, Sfdisc_t*);
+int (*Sfexcept_f)(Sfio_t*, int, Void_t*, Sfdisc_t*);
+
+Sffmt_t;
+int (*Sffmtext_f)(Sfio_t*, Void_t*, Sffmt_t*);
+int (*Sffmtevent_f)(Sfio_t*, int, Void_t*, Sffmt_t*);
+
+SFIO_VERSION
+.ft 1
+.fi
+.Ss "BIT FLAGS"
+.nf
+.ft 5
+SF_STRING
+SF_READ
+SF_WRITE
+SF_APPENDWR (SF_APPEND)
+SF_LINE
+SF_SHARE
+SF_PUBLIC
+SF_MALLOC
+SF_STATIC
+SF_IOCHECK
+SF_WHOLE
+SF_MTSAFE
+SF_IOINTR
+.ft 1
+.fi
+.Ss "OPENING/CLOSING STREAMS"
+.nf
+.ft 5
+Sfio_t* sfnew(Sfio_t* f, Void_t* buf, size_t 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(size_t size);
+int sfclose(Sfio_t* f);
+
+
+.ft 1
+.fi
+.Ss "THREAD SAFETY"
+.nf
+.ft 5
+int sfmutex(Sfio_t* f, int type);
+
+SFMTX_LOCK
+SFMTX_TRYLOCK
+SFMTX_UNLOCK
+SFMTX_CLRLOCK
+.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);
+
+Sfulong_t sfgetm(Sfio_t* f, Sfulong_t max);
+int sfputm(Sfio_t* f, Sfulong_t v, Sfulong_t max);
+Sfulong_t sfgetu(Sfio_t* f);
+int sfputu(Sfio_t* f, Sfulong_t v);
+Sflong_t sfgetl(Sfio_t* f);
+int sfputl(Sfio_t* f, Sflong_t v);
+Sfdouble_t sfgetd(Sfio_t* f);
+int sfputd(Sfio_t* f, Sfdouble_t v);
+
+char* sfgetr(Sfio_t* f, int rsc, int type);
+ssize_t sfputr(Sfio_t* f, const char* s, int rsc);
+Sfoff_t sfmove(Sfio_t* fr, Sfio_t* fw, Sfoff_t n, int rsc);
+
+ssize_t sfread(Sfio_t* f, Void_t* buf, size_t n);
+ssize_t sfwrite(Sfio_t* f, const Void_t* buf, size_t n);
+Sfoff_t sfseek(Sfio_t* f, Sfoff_t offset, int type);
+Void_t* sfreserve(Sfio_t* f, ssize_t n, int type);
+.ft 1
+.fi
+.Ss "DATA FORMATTING"
+.nf
+.ft 5
+int sfscanf(Sfio_t* f, const char* format, ...);
+int sfsscanf(const char* s, const char* format, ...);
+int sfvsscanf(const char* s, const char* format, va_list args);
+int sfvscanf(Sfio_t* f, const char* format, va_list args);
+
+int sfprintf(Sfio_t* f, const char* format, ...);
+char* sfprints(const char* format, ...);
+char* sfvprints(const char* format, va_list args);
+ssize_t sfaprints(char** sp, const char* format, ...);
+ssize_t sfvaprints(char** sp, const char* format, va_list args);
+int sfsprintf(char* s, int n, const char* format, ...);
+int sfvsprintf(char* s, int n, const char* format, va_list args);
+int sfvprintf(Sfio_t* f, const char* format, va_list args);
+
+Sffmt_t;
+
+SFFMT_LEFT
+SFFMT_SIGN
+SFFMT_BLANK
+SFFMT_ZERO
+SFFMT_THOUSAND
+SFFMT_LONG
+SFFMT_LLONG
+SFFMT_SHORT
+SFFMT_LDOUBLE
+SFFMT_IFLAG
+SFFMT_JFLAG
+SFFMT_CENTER
+SFFMT_CHOP
+SFFMT_ALTER
+SFFMT_SKIP
+SFFMT_ARGPOS
+SFFMT_VALUE
+
+int (*Sffmtext_f)(Sfio_t* f, Void_t* v, Sffmt_t* fe);
+int (*Sffmtevent_f)(Sfio_t* f, int type, Void_t* v, Sffmt_t* fe);
+void va_copy(va_list to, va_list fr);
+long sffmtversion(Sffmt_t* fe, type);
+.ft 1
+.fi
+.Ss "BUFFERING, SYNCHRONIZATION"
+.nf
+.ft 5
+Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size);
+int sfsync(Sfio_t* f);
+int sfpoll(Sfio_t** flist, int n, int timeout);
+Sfio_t* sfpool(Sfio_t* f, Sfio_t* poolf, int mode);
+int sfpurge(Sfio_t* f);
+.ft 1
+.fi
+.Ss "DISCIPLINE, EVENT HANDLING"
+.nf
+.ft 5
+Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc);
+int sfraise(Sfio_t* f, int type, Void_t* data);
+ssize_t sfrd(Sfio_t* f, Void_t* buf, size_t n, Sfdisc_t* disc);
+ssize_t sfwr(Sfio_t* f, const Void_t* buf, size_t n, Sfdisc_t* disc);
+Sfoff_t sfsk(Sfio_t* f, Sfoff_t offset, int type, Sfdisc_t* disc);
+
+SF_NEW
+SF_READ
+SF_WRITE
+SF_SEEK
+SF_CLOSING (SF_CLOSE)
+SF_DPUSH
+SF_DPOP
+SF_DPOLL
+SF_DBUFFER
+SF_SYNC
+SF_PURGE
+SF_FINAL
+SF_READY
+SF_LOCKED
+SF_ATEXIT
+SF_EVENT
+.ft 1
+.fi
+.Ss "STREAM CONTROL"
+.nf
+.ft 5
+int sfresize(Sfio_t* f, Sfoff_t size);
+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);
+ssize_t 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, Void_t* data));
+
+int sfwalk(Sfwalk_f walkf, Void_t* data, int type);
+.ft 1
+.fi
+.Ss "MISCELLANEOUS FUNCTIONS"
+.nf
+.ft 5
+ssize_t sfmaxr(ssize_t maxr, int s);
+ssize_t sfslen();
+int sfulen(Sfulong_t v);
+int sfllen(Sflong_t v);
+int sfdlen(Sfdouble_t v);
+ssize_t sfpkrd(int fd, Void_t* buf, size_t n,
+ int rsc, long tm, int action);
+.ft 1
+.fi
+.Ss "FULL STRUCTURE SFIO_T"
+.nf
+.ft 5
+#include <sfio_t.h>
+#define SFNEW(buf,size,file,flags,disc)
+.ft 1
+.fi
+.Ss "EXAMPLE DISCIPLINES"
+.nf
+.ft 5
+#include <sfdisc.h>
+
+int sfdcdio(Sfio_t* f, size_t bufsize);
+int sfdcdos(Sfio_t* f);
+int sfdcfilter(Sfio_t* f, const char* cmd);
+int sfdcseekable(Sfio_t* f);
+int sfdcslow(Sfio_t* f);
+int sfdcsubstream(Sfio_t* f, Sfio_t* parent,
+ Sfoff_t offset, Sfoff_t extent);
+int sfdctee(Sfio_t* f, Sfio_t* tee);
+int sfdcunion(Sfio_t* f, Sfio_t** array, int n);
+int sfdclzw(Sfio_t* f);
+int sfdcgzip(Sfio_t* f, int flags);
+.ft 1
+.fi
+.Ss "STDIO-COMPATIBILITY"
+.nf
+.ft 5
+#include <stdio.h>
+cc ... -lstdio -lsfio
+cc ... -lstdio-mt -lsfio-mt
+.ft 1
+.fi
+.SH DESCRIPTION
+.PP
+Sfio provides I/O functions to manage buffered streams.
+Each Sfio stream is a \fIfile stream\fP, representing a file (see \f5open(2)\fP),
+or a \fIstring stream\fP, representing a memory segment.
+Beyond the usual I/O operations on streams,
+Sfio provides I/O disciplines for extended data processing,
+stream stacks for recursive stream processing, and
+stream pools for automatic data synchronization.
+Applications can extend the \f5sfprintf()/sfscanf()\fP functions
+to define their own conversion patterns as well as redefine existing ones.
+.PP
+A discipline defines analogues of
+the system calls \f5read(2), write(2)\fP and \f5lseek(2)\fP.
+Such system calls or their discipline replacements are used to process stream data.
+Henceforth, ``\fIsystem call\fP'' will refer to either a system call
+or its discipline replacement.
+.PP
+A system call is said to cause an exception if its return value is non-positive.
+Unless overridden by exception handlers (see \f5sfdisc()\fP),
+an interrupted system call (\f5errno == EINTR\fP on UNIX systems)
+will be automatically reinvoked to continue the ongoing operation.
+.PP
+The buffer of a stream is typically a memory segment allocated via \f5malloc(3)\fP
+or supplied by the application.
+File streams may also use memory mapping (\f5mmap(2)\fP) if that is more efficient.
+When memory mapping is used,
+the underlying file should not be truncated while the stream is active.
+Memory mapping can be turned off using \f5sfsetbuf()\fP.
+.PP
+There are three \fIstandard streams\fP:
+\f5sfstdin\fP for input (file descriptor \f50\fP on UNIX systems),
+\f5sfstdout\fP for normal output (file descriptor \f51\fP), and
+\f5sfstderr\fP for error output (file descriptor \f52\fP).
+
+.PP
+.Ss "LIBRARIES"
+.PP
+This version of Sfio can be built and used for both uni-threaded and multi-threaded
+environments. In the former case, streams are not protected from
+simultaneous accesses by different threads. In the latter case, a stream
+is typically locked with a mutex during access so that another thread
+trying to access the same stream will block until the mutex is released.
+
+A program that does not use multiple threads can link with \fBlibsfio.a\fP
+while a program that uses multiple threads should link with \fBlibsfio-mt.a\fP.
+The libraries \fBlibstdio.a\fP and \fBlibstdio-mt.a\fP provide
+corresponding Stdio functions to link with code already compiled using the
+native header \fBstdio.h\fP instead of the one provided by Sfio.
+
+.PP
+.Ss "DATA TYPES"
+.PP
+.Ss " Void_t*"
+This defines a type suitable to exchange
+data of unknown types between application and Sfio.
+\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
+the largest possible file extent.
+.PP
+.Ss " Sfulong_t, Sflong_t, Sfdouble_t"
+These are respectively the largest
+unsigned integer, signed integer, and floating point value types on the local platform.
+.PP
+.Ss " Sfio_t"
+This defines the type of a stream handle.
+.PP
+.Ss " Sfdisc_t"
+.Ss " ssize_t (*Sfread_f)(Sfio_t*, Void_t*, size_t, Sfdisc_t*)"
+.Ss " ssize_t (*Sfwrite_f)(Sfio_t*, const Void_t*, size_t, 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.
+\f5Sfread_f\fP, \f5Sfwrite_f\fP and \f5Sfseek_f\fP are the types
+of discipline functions to replace the system calls:
+\f5read(2)\fP, \f5write(2)\fP and \f5lseek(2)\fP.
+\f5Sfexcept_f\fP is the type of an event-handling function.
+See \f5sfdisc()\fP for more details.
+.PP
+.Ss " Sffmt_t"
+.Ss " int (*Sffmtext_f)(Sfio_t*, Void_t*, Sffmt_t*)"
+.Ss " int (*Sffmtevent_f)(Sfio_t*, int, Void_t*, Sffmt_t*)"
+\f5Sffmt_t\fP defines a formatting environment that can be used
+to extend scanning and printing in the \f5sfprint()/sfscanf()\fP
+functions. \f5Sffmtext_f\fP and \f5Sffmtevent_f\fP define the types
+of extension functions definable in \f5Sffmt_t\fP.
+See \f5Sffmt_t\fP below for more details.
+.PP
+.Ss " SFIO_VERSION"
+This is a macro value of type \f5long int\fP that defines
+the current version number of Sfio. For example, the Sfio2000's
+version number is \f520000515L\fP
+(which also indicates its latest version date: 05/15/2000).
+
+.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 stream is memory-based.
+.Tp
+\f5SF_READ\fP, \f5SF_WRITE\fP, \f5SF_APPENDWR\fP (\f5SF_APPEND\fP):
+Flags \f5SF_READ\fP and \f5SF_WRITE\fP indicate readability and writability.
+Flag \f5SF_APPENDWR\fP asserts that the stream is a file opened in append mode
+(see \f5open(2)\fP and \f5fcntl(2)\fP)
+so that data is always output at the end of file.
+On systems without direct support for append mode,
+Sfio uses \f5lseek(2)\fP or its discipline replacement
+to approximate this behavior.
+.Tp
+\f5SF_LINE\fP:
+The stream is line-oriented.
+For a \f5SF_WRITE\fP stream,
+this means that buffered data is flushed
+whenever a new-line character, \f5\en\fP, is output.
+For a \f5SF_READ\fP stream, \f5SF_LINE\fP is only
+significant during calls to functions in the \f5sfscanf()\fP family.
+\f5SF_LINE\fP is set on initialization of
+any stream representing a terminal device.
+.Tp
+\f5SF_SHARE\fP, \f5SF_PUBLIC\fP:
+Flag \f5SF_SHARE\fP means that the underlying file descriptor
+is shared by independent entities (for example, multiple processes).
+
+For a seekable file stream, \f5SF_SHARE\fP means that
+the logical stream and the physical file positions will be made the same
+before a system call to perform physical I/O.
+There are different possibilities.
+If \f5SF_PUBLIC\fP is not set,
+the physical file position is made equal to the logical stream position.
+If \f5SF_PUBLIC\fP is set, there are two cases.
+If the physical file position has changed from its last known position,
+the logical stream position is made equal to the new physical file position.
+Finally, if the physical file location remains the same as its last known position,
+the physical file position is made the same as the logical stream position.
+
+For an unseekable stream (e.g., pipes or terminal devices), if possible,
+\f5SF_SHARE\fP means that
+the block and record I/O operations (\f5sfread()\fP, \f5sfwrite()\fP, \f5sfmove()\fP,
+\f5sfgetr()\fP, \f5sfputr()\fP, \f5sfreserve()\fP, \f5sfscanf()\fP
+and \f5sfvprintf()\fP) will ensure:
+(1) after each writing operation, the stream is synchronized and
+(2) each reading operation only reads the requested amount.
+Note, however, that (2) is not always possible
+without proper OS facilities such as \f5recv(2)\fP or \f5streamio(4)\fP.
+
+A standard stream that is seekable will be initialized with \f5SF_SHARE|SF_PUBLIC\fP.
+.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 should not be freed when closed (\f5sfclose()\fP).
+This flag is used by an applications that allocate their own
+stream structures. Such applications must use the header file \f5sfio_t.h\fP
+instead of \f5sfio.h\fP.
+.Tp
+\f5SF_IOCHECK\fP:
+If the stream has a discipline exception handler,
+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_WHOLE\fP:
+This flag guarantees that data written in any single \f5sfwrite()\fP or
+\f5sfputr()\fP call will always be output as a whole to the output device.
+This is useful in certain applications (e.g., networking) where a complex object
+must be output without being split in different system calls.
+Note that the respective stream still buffers data as much as the buffer can accomodate.
+.Tp
+\f5SF_MTSAFE\fP:
+This flag indicates that the respective stream may be accessed by more than one threads.
+A mutex lock will be used to ensure that only one thread at a time can access
+the stream. Note that this flag can only be set at stream opening time
+(see \f5sfopen()\fP, \f5sfpopen()\fP and \f5sfnew()\fP).
+Certain fast macro functions such as \f5sfgetc()\fP and \f5sfputc()\fP will
+no longer behave as macros. Thus, an application that requires such fast macro functions
+should leave \f5SF_MTSAFE\fP off and performs explicit locking with \f5sfmutex()\fP.
+.Tp
+\f5SF_IOINTR\fP:
+This flag indicates that I/O system calls should not be resumed
+after being interrupted by signals. It is useful for
+aborting I/O operations on such interruptions. Note, however,
+than certain operating systems (e.g., BSD Unix systems) may automatically
+resume interrupted system calls outside the scope of the library. On such systems,
+\f5SF_IOINTR\fP will be ineffective.
+
+.PP
+.Ss "OPENING/CLOSING STREAMS"
+.PP
+.Ss " Sfio_t* sfnew(Sfio_t* f, Void_t* buf, size_t 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, \f5f\fP is reused.
+In this case, if \f5flags\fP does not have \f5SF_EOF\fP,
+\f5f\fP shall be closed via \f5sfclose()\fP before being reused.
+During a stream renewal, buffer, pool and discipline stack are preserved.
+Note that, except for \f5SF_STATIC\fP streams,
+renewing a stream already closed will result in undefined behavior.
+.Tp
+\f5buf\fP, \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.
+Note that Sfio supports unseekable file descriptors
+opened for both read and write, e.g., sockets.
+.Tp
+\f5flags\fP:
+This is composed from \f5SF_EOF\fP and
+bit values defined in the \fBBIT FLAGS\fP section.
+Note, in particular, that a multi-threaded application should
+set the bit \f5SF_MTSAFE\fP to protect the new stream from
+being simultaneously accessed by multiple threads.
+
+.Ss " Sfio_t* sfopen(Sfio_t* f, const char* string, const char* mode)"
+
+If \f5string\fP is \f5NULL\fP,
+\f5f\fP is a file stream and
+\f5mode\fP does not imply a string stream,
+\f5sfopen()\fP changes the modes of \f5f\fP according to \f5mode\fP.
+In this case, \f5sfopen()\fP returns \f5f\fP on success and \f5NULL\fP on error.
+This somewhat unusual usage of \f5sfopen()\fP is good for
+resetting certain predefined modes in standard streams including
+\fItext/binary\fP and \fIappend\fP that are inherited from some parent process.
+Note also that \f5SF_READ\fP and \f5SF_WRITE\fP can only be reset if the stream
+is not yet initialized.
+
+\f5sfopen()\fP is normally used to create a new stream or renew a stream.
+In this case, it returns the new stream on success and \f5NULL\fP on error.
+Below are the meanings of the arguments:
+.Tp
+\f5f\fP:
+This is treated as in \f5sfnew()\fP.
+.Tp
+\f5string\fP:
+This is a file name or a string to perform I/O on.
+See above for when this is \f5NULL\fP.
+.Tp
+\f5mode\fP:
+This is composed from the set of letters \f5{s, r, w, +, a, b, t, x, m, u}\fP.
+When conflicting options are present in the same \f5mode\fP string,
+the last one will take effect.
+
+\f5s\fP specifies opening a string stream.
+\f5string\fP can be a null-terminated string or \f5NULL\fP.
+Specifying \f5s\fP alone is equivalent to specifying \f5sr\fP.
+If \f5s\fP is not specified, \f5string\fP defines a file name.
+
+\f5r\fP and \f5w\fP specify read and write modes.
+Write mode creates and/or truncates the given file to make an empty file.
+The \f5+\fP modifier indicates that the stream is opened for both read and write.
+
+\f5a\fP specifies append mode, i.e., data is always output at end of file.
+
+\f5b\fP and \f5t\fP specify binary and text modes.
+
+\f5x\fP specifies exclusive mode, i.e.,
+a file opened for writing should not already exist.
+
+\f5m\fP specifies that the stream needs to be protected from
+simultaneous accesses by multiple threads.
+This turns on the bit flag \f5SF_MTSAFE\fP.
+
+\f5u\fP specifies that the stream is guaranteed to be accessed
+by only one thread at a time. The bit flag \f5SF_MTSAFE\fP is left off.
+The absence of option \f5m\fP is the same as the presence of option \f5u\fP.
+
+.Ss " Sfio_t* sfpopen(Sfio_t* f, const char* cmd, const char* mode)"
+This function opens a stream that corresponds to the coprocess \f5cmd\fP.
+The argument \f5mode\fP should be composed from \f5r\fP, \f5w\fP, and \f5+\fP.
+The argument \f5f\fP, if not \f5NULL\fP, is a stream to be renewed (see \f5sfnew()\fP).
+\f5sfpopen()\fP returns the new stream or \f5NULL\fP on error.
+
+The standard input/output of \f5cmd\fP
+is connected to the application via a pipe if the stream is opened for writing/reading.
+If the stream is opened for both reading and writing,
+there will be two different associated file descriptors, one for each type of I/O
+(note the effect on \f5sffileno()\fP).
+
+On opening a coprocess for writing (i.e., \f5mode\fP contains \f5w\fP or \f5+\fP),
+the signal handler for \f5SIGPIPE\fP in the parent application
+will be set to \f5SIG_IGN\fP if it is \f5SIG_DFL\fP at that time.
+This protects the parent application from being accidentally killed
+on writing to a coprocess that closes its reading end.
+Applications that need to detect such write errors should use
+disciplines and exception handlers (see \f5sfdisc()\fP).
+
+The command \f5cmd\fP
+is executed by an \fIinterpreter\fP which is either \f5/bin/sh\fP
+or an executable command defined by the environment variable \f5SHELL\fP.
+In either case, the interpreter is invoked with 2 arguments, respectively \f5-c\fP
+and the given command \f5cmd\fP. When the interpreter is \f5/bin/sh\fP or
+\f5/bin/ksh\fP, \f5sfpopen()\fP may execute the command \f5cmd\fP itself
+if there are no shell meta-characters in \f5cmd\fP.
+
+.Ss " Sfio_t* sftmp(size_t size)"
+This function creates a stream for temporary data.
+It returns the new stream or \f5NULL\fP on error.
+
+A stream created by \f5sftmp()\fP can be completely or partially memory-resident.
+If \f5size\fP is \f5SF_UNBOUND\fP, the stream is a pure string stream.
+If \f5size\fP is zero, the stream is a pure file stream.
+Otherwise, the stream is first created as a string stream but when
+its buffer grows larger than \f5size\fP or on any attempt to change disciplines,
+a temporary file is created.
+Two environment variables, \f5TMPPATH\fP and \f5TMPDIR\fP,
+direct where temporary files are created.
+\f5TMPPATH\fP, if defined,
+specifies a colon-separated set of directories to be
+used in a round-robin fashion to create files.
+If \f5TMPPATH\fP is undefined,
+\f5TMPDIR\fP can be used to specify a single directory to create files.
+If neither of \f5TMPPATH\fP and \f5TMPDIR\fP are defined, \f5/tmp\fP is used.
+
+.Ss " int sfclose(Sfio_t* f)"
+This function closes the stream \f5f\fP and frees its resources.
+\f5SF_STATIC\fP should be used if the stream space is to be preserved.
+If \f5f\fP is the base of a stream stack (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.
+\f5sfclose()\fP returns \f5-1\fP for failure and \f50\fP for success.
+
+\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 argument as one of
+\f5SF_CLOSING\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 as \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.
+
+.PP
+.Ss "THREAD SAFETY"
+.PP
+The libraries \f5libsfio.a\fP and \f5libstdio.a\fP (providing binary
+compatibility to Stdio-based code) only support uni-threaded code.
+Multi-threaded applications should link with
+\f5libsfio-mt.a\fP and \f5libstdio-mt.a\fP.
+When this is done, certain platforms may require additional
+thread libraries for linkage. For example, Linux, Irix and Solaris
+require \f5-lpthread\fP while HPUX requires \f5-lcma\fP.
+Aside from linkage differences, the Sfio API remains identical in all cases.
+
+Note that unlike Stdio streams which are in thread-safe mode by default.
+Sfio streams can be opened in either uni-threaded or multi-threaded mode.
+A uni-threaded stream is more efficient than a multi-threaded one.
+For example, functions such as \f5sfgetc()\fP and \f5sfputc()\fP
+remain as macro or inline functions for a uni-threaded stream while
+they will act as full function calls in a multi-threaded case.
+The three standard streams \f5sfstdin/sfstdout/sfstderr\fP
+are in multi-threaded mode by default
+(however, see \f5sfopen()\fP for how this may be changed).
+Other Sfio streams are normally opened uni-threaded unless
+the flag \f5SF_MTSAFE\fP or the option \f5m\fP were specified.
+Stdio-based code can also make a Stdio stream uni-threaded by
+using the option \f5u\fP when opening a file.
+
+.PP
+.Ss "int sfmutex(Sfio_t* f, int type)"
+This function acquires or releases a mutex
+(mutually exclusive) lock on the stream \f5f\fP.
+It can be used by a thread to serialize a sequence of I/O operations
+executed together in some critical section.
+\f5sfmutex()\fP is implicitly used by
+all Sfio operations on a stream with the flag \f5SF_MTSAFE\fP to
+protect it from concurrent accesses via multiple threads.
+\f5sfmutex()\fP returns \f50\fP on success and some non-zero value on failure.
+
+Each stream has a lock count which starts at \f50\fP.
+When the count is positive, a single thread holds the stream.
+Only this thread can further lock or unlock the stream.
+A different thread attempting to acquire such a locked stream will suspend
+until the lock count returns to \f50\fP.
+Each successful locking operation increases the lock count
+while each successful unlocking operation decreases it,
+thus, allowing nesting of matching lock/unlock operations.
+
+The \f5type\fP argument of \f5sfmutex()\fP takes on the below values:
+.Tp
+\f5SFMTX_LOCK\fP:
+Locking a stream if it is unlocked or increasing the lock count of the stream
+if it is already locked by the same thread. This call will block until it is
+possible to lock the stream.
+.Tp
+\f5SFMTX_TRYLOCK\fP:
+This is the non-blocking version of \f5SFMTX_LOCK\fP.
+If the stream is already locked by a different thread, \f5sfmutex()\fP will
+immediately return with an error status.
+.Tp
+\f5SFMTX_UNLOCK\fP:
+Decreasing the lock count and releasing the stream when the lock count reaches 0.
+An attempt to unlock a stream without a previously successful lock may
+result in undefined behavior in certain implementations.
+The current Sfio implementation returns an error status.
+.Tp
+\f5SFMTX_CLRLOCK\fP:
+Resetting the lock count to \f50\fP and releasing the stream.
+As with \f5SFMTX_LOCK\fP,
+an attempt to clear the lock count without a previously successful lock
+may result in undefined behavior.
+.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 " ssize_t sfnputc(Sfio_t* f, int c, size_t n)"
+This function attempts to write the byte \f5c\fP to \f5f\fP \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.
+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.
+\f5sfungetc()\fP returns \f5c\fP on success and \f5-1\fP on failure.
+
+.Ss " Sfulong_t sfgetm(Sfio_t* f, Sfulong_t max)"
+.Ss " int sfputm(Sfio_t* f, Sfulong_t v, Sfulong_t max)"
+These functions read and write \f5Sfulong_t\fP values
+encoded in a portable format given that the values are at most \f5max\fP.
+Portability across a write architecture and a read architecture
+requires that the bit order in a byte is the same on both architectures and
+the written value is storable in an \f5Sfulong_t\fP on the read architecture.
+\f5sfgetm()\fP returns the value read or \f5-1\fP on error.
+\f5sfputm()\fP returns the number of bytes written or \f5-1\fP on error.
+
+.Ss " Sfulong_t sfgetu(Sfio_t* f)"
+.Ss " int sfputu(Sfio_t* f, Sfulong_t v)"
+These functions read and write \f5Sfulong_t\fP values
+in a compact variable-length portable format.
+Portability across a write architecture and a read architecture
+requires that the bit order in a byte is the same on both architectures and
+the written value is storable in an \f5Sfulong_t\fP on the read 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 " Sflong_t sfgetl(Sfio_t* f)"
+.Ss " int sfputl(Sfio_t* f, Sflong_t v)"
+These functions are similar to \f5sfgetu()\fP and \f5sfputu()\fP
+but for reading and writing (signed) \f5Sflong_t\fP values.
+See also \f5sfllen()\fP.
+
+.Ss " Sfdouble_t sfgetd(Sfio_t* f)"
+.Ss " int sfputd(Sfio_t* f, Sfdouble_t v)"
+These functions read and write \f5Sfdouble_t\fP values.
+In this case, portability 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
+so they are constrained to the sizes supported by these functions.
+See also \f5sfdlen()\fP.
+
+.Ss " char* sfgetr(Sfio_t* f, int rsc, int type)"
+This function reads a record of data ending in the record separator \f5rsc\fP.
+After \f5sfgetr()\fP returns, 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.
+See also \f5sfmaxr()\fP for limiting the amount of data read to construct a record.
+
+The \f5type\fP argument is composed of some subset of the below bit flags:
+.Tp
+\f5SF_STRING\fP:
+A null byte will replace the record separator to make the record into a C string.
+Otherwise, the record separator is left alone.
+.Tp
+\f5SF_LOCKR\fP:
+Upon successfully obtaining a record \f5r\fP,
+the stream will be locked from further access until it is released with
+a call \f5sfread(f,r,0)\fP.
+.Tp
+\f5SF_LASTR\fP:
+This should be used only after a failed \f5sfgetr()\fP to retrieve
+the last incomplete record. In this case, \f5rsc\fP is ignored.
+
+.Ss " ssize_t 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, \f5(unsigned char)rsc\fP is output after 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.
+\f5sfmove()\fP returns the number of objects moved or \f5-1\fP on failure.
+
+An object can be either a byte if the record separator argument
+\f5rsc\fP is negative or a record of \f5rsc\fP is non-negative.
+In the latter case, a record is incomplete if it does not end in \f5rsc\fP.
+Generally speaking, a stream can have at most one incomplete record.
+If \f5n\fP is negative, all complete objects of \f5fr\fP will be moved.
+Otherwise, \f5n\fP indicates the number of objects to move.
+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,(Sfoff_t)(-1),'\en')\fP
+counts the number of complete lines in stream \f5f\fP.
+
+.Ss " ssize_t sfread(Sfio_t* f, Void_t* buf, size_t 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 " ssize_t sfwrite(Sfio_t* f, const Void_t* buf, size_t n)"
+This function writes \f5n\fP bytes from \f5buf\fP to \f5f\fP.
+If \f5f\fP is \f5SF_STRING\fP, and the buffer is not large enough,
+an \f5SF_WRITE\fP exception shall be 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.
+It returns the new position or \f5-1\fP on failure.
+
+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).
+
+The new position is determined based on \f5offset\fP and
+\f5type\fP which is composed from the bit flags:
+.Tp
+\f50\fP or \f5SEEK_SET\fP:
+\f5offset\fP is the desired position.
+.Tp
+\f51\fP or \f5SEEK_CUR\fP:
+\f5offset\fP is relative to the current position (see \f5SF_PUBLIC\fP below).
+.Tp
+\f52\fP or \f5SEEK_END\fP:
+\f5offset\fP is relative to the physical end of file.
+.Tp
+\f5SF_SHARE\fP:
+The stream is treated as if it has the control bit \f5SF_SHARE\fP on.
+This implies that a system call seek will be done to ensure that the
+location seeking to is valid.
+.Tp
+\f5SF_PUBLIC\fP:
+The stream is treated as if it has the control bit \f5SF_PUBLIC\fP on.
+If the physical file position has changed from its last known location,
+the current position is taken as the new physical position.
+Otherwise, the current position is the logical stream position.
+
+.Ss " Void_t* sfreserve(Sfio_t* f, ssize_t n, int type)"
+This function reserves a data block from the stream \f5f\fP.
+It returns the reserved data block on success and \f5NULL\fP on failure.
+
+If \f5f\fP is a \f5SF_READ\fP stream, the data block is a segment of input data.
+If \f5f\fP is a \f5SF_WRITE\fP stream, the data block is a buffer
+suitable for writing output data.
+For consistency, if \f5f\fP is opened with \f5SF_READ|SF_WRITE\fP,
+it will normally be treated as if it is a \f5SF_READ\fP stream
+(see \f5sfset()\fP for forcing a particular mode) but the returned
+buffer can also be written into (more below).
+However, it is possible to bias to \f5SF_WRITE\fP when the \f5type\fP
+argument is non-negative by adding the \f5SF_WRITE\fP bit \f5type\fP.
+In any case, a reserved data block is guaranteed to be valid only until
+a future access to the stream \f5f\fP.
+
+When \f5f\fP is \f5SF_READ\fP, \f5SF_SHARE\fP and unseekable,
+\f5sfreserve()\fP will attempt to peek at input data without
+consuming it. This enables separate processes to share in reading
+input from unseekable file descriptors (e.g., pipes or devices).
+However, this use of \f5sfreserve()\fP may fail
+on certain platforms that do not properly support
+peeking on unseekable file descriptors.
+
+After a \f5sfreserve()\fP call, whether or not it succeeds,
+\f5sfvalue(f)\fP gives the size of the available data block.
+Any partially reserved data block after a failed \f5sfreserve()\fP
+call can be obtained in another \f5sfreserve()\fP call with the argument
+\f5type\fP being \f5SF_LASTR\fP. The second argument \f5n\fP
+to \f5sfreserve()\fP will be ignored in this case.
+
+A \f5sfreserve()\fP call is successful if it can obtain a data block
+of size at least the absolute value of \f5n\fP.
+For a \f5SF_READ\fP atream, the argument \f5n\fP is treated as follows:
+.Tp
+\f5n < 0\fP:
+\f5sfreserve()\fP attempts to get \fIat least\fP \f5|n|\fP bytes
+into the buffer.
+.Tp
+\f5n == 0\fP:
+If the argument \f5type\fP is \f50\fP,
+\f5sfreserve()\fP attempts to get \fIat least\fP \f51\fP byte into the buffer
+but does not consume it (as consistent with \f5n == 0\fP).
+If \f5type != 0\fP, no attempt will be made to read data into the buffer.
+For example, the call \f5sfreserve(f, 0, -1)\fP only returns the buffer status,
+i.e., size of existing buffered data and pointer to such data, if any.
+The call \f5sfreserve(f, 0, SF_LOCKR)\fP is similar but also locks the stream.
+.Tp
+\f5n > 0\fP:
+\f5sfreserve()\fP will use attempt to get \fIat most\fP \f5n\fP bytes into
+the buffer. Further, if \f5type == \f5SF_LOCKR\fP (see below), read attempts
+end on a positive amount.
+
+For a successful reservation, the argument \f5type\fP dictates treatment
+as follows:
+.Tp
+\f5type == SF_LASTR\fP:
+After a \f5sfreserve()\fP call with \f5type != SF_LOCKR\fP fails,
+there may be some left over data not accessible via conventional Sfio calls.
+Immediately after such a failed call,
+another call to \f5sfreserve\fP with \f5type == SF_LASTR\fP will return any left over
+data and also advance the stream I/O position by the amount of returned data.
+.Tp
+\f5type < 0\fP:
+If \f5n > 0\fP, the stream I/O position is advanced by \f5n\fP.
+If \f5n < 0\fP, the stream I/O position is advanced by the amount
+of available data.
+For example, a successful \f5sfreserve(f, -1, -1)\fP call will return a
+buffer of data and simultanously advance the stream I/O position by the amount
+indicated by \f5sfvalue(f)\fP.
+.Tp
+\f5type == SF_LOCKR\fP:
+The stream I/O position remains unchanged.
+In addition, \f5f\fP will be locked from further access.
+As appropriate to the stream type (\f5SF_READ\fP, \f5SF_WRITE\fP or both),
+\f5f\fP can be unlocked later
+with one of \f5sfread(f,rsrv,size)\fP or \f5sfwrite(f,rsrv,size)\fP
+where \f5rsrv\fP is the reserved data block and \f5size\fP is the amount of
+data to be consumed. For example, if \f5f\fP is a locked \f5SF_READ\fP stream,
+the call \f5sfread(f,rsrv,1)\fP will reopen the stream and simultaneously
+advance the stream I/O position by \f51\fP.
+Finally, a stream opened for both reading and writing
+can release the lock with either call (with associated operational semantics!)
+For example, the below code reads 10 bytes of data from a stream
+opened with both \f5SF_READ\fP and \f5SF_WRITE\fP, modifies the data in place,
+then rewrites the new data back to the stream:
+
+.nf
+.ft 5
+ rsrv = sfreserve(f, 10, 1);
+ for(i = 0; i < 10; ++i)
+ rsrv[i] = toupper(rsrv[i]);
+ sfwrite(f, rsrv, 10);
+.ft 1
+.fi
+
+.ne 6
+.PP
+.Ss "DATA FORMATTING"
+.PP
+Data printing and scanning are done via the
+\f5sfprintf()\fP and \f5sfscanf()\fP family of functions.
+These functions are similar to their
+ANSI-C \f5fprintf()\fP and \f5fscanf()\fP counterparts.
+However, the Sfio versions have been extended for both portability and generality.
+In particular, a notion of a formatting environment stack is introduced.
+Each formatting element on the stack
+defines a separate \fIformatting pair\fP of a format specification string,
+\f5char* format\fP (the usual second argument in the formatting
+functions), and an argument list, \f5va_list args\fP (the third argument
+in functions \f5sfvprintf()\fP and \f5sfvscanf()\fP).
+A formatting environment element may also specify extension functions
+to obtain or assign arguments and to provide new semantics for pattern processing.
+To simplify the description below, whenever we talk
+about an argument list, unless noted otherwise,
+it is understood that this means either the true
+argument list when there is no extension function or the action to be taken
+by such a function in processing arguments.
+The manipulation of the formatting environment stack is done
+via the pattern \f5!\fP discussed below.
+
+.Ss "%! and Sffmt_t"
+The pattern \f5%!\fP manipulates the formatting environment stack to
+(1) change the top environment to a new environment,
+(2) stack a new environment on top of the current top,
+or (3) pop the top environment.
+The bottom of the environment stack always contains a virtual environment with the
+original formatting pair and without any extension functions.
+
+The top environment of a stack, say \f5fe\fP, is automatically popped whenever
+its format string is completely processed.
+In this case, its event-handling function (if any) is called
+as \f5(*eventf)(f,SF_FINAL,NIL(Void_t*),fe)\fP.
+The top environment
+can also be popped by giving an argument \f5NULL\fP to \f5%!\fP
+or by returning a negative value in an extension function.
+In these cases, the event-handling function is called
+as \f5(*eventf)(f,SF_DPOP,form,fe)\fP where \f5form\fP is the remainder
+of the format string. A negative return value from the event handling function
+will prevent the environment from being popped.
+
+A formatting environment is a structure of type \f5Sffmt_t\fP
+which contains the following elements:
+
+.nf
+.ft 5
+ Sffmtext_f extf; /* extension processor */
+ Sffmtevent_f eventf; /* event handler */
+
+ char* form; /* format string to stack */
+ va_list args; /* corresponding arg list */
+
+ int fmt; /* pattern being processed */
+ ssize_t size; /* object size */
+ int flags; /* formatting control flags */
+ int width; /* width of field */
+ int precis; /* precision required */
+ int base; /* conversion base */
+
+ char* t_str; /* extfdata string */
+ int n_str; /* length of t_str */
+.ft 1
+.fi
+
+The first four elements of \f5Sffmt_t\fP must be defined by the application
+before the structure is passed to a formatting function.
+The two function fields should not be changed during processing.
+Other elements of \f5Sffmt_t\fP are set by the respective formatting function
+before it calls the extension function \f5Sffmt_t.extf\fP and, subsequently,
+can be modified by this function to redirect formatting or scanning.
+For example, consider a call from a \f5sfprintf()\fP function to process an
+unknown pattern \f5%t\fP (which we may take to mean ``time'') based on a
+formatting environment \f5fe\fP.
+\f5fe->extf\fP may reset \f5fe->fmt\fP to `\f5d\fP' upon returing
+to cause \f5sfprintf()\fP to process the value being formatted as an integer.
+
+Below are the fields of \f5Sffmt_t\fP:
+.Tp
+\f5extf\fP:
+\f5extf\fP is a function to extend scanning and formatting patterns.
+Its usage is discussed below.
+.Tp
+\f5eventf\fP:
+This is a function to process events as discussed earlier.
+.Tp
+\f5form\fP and \f5args\fP:
+This is the formatting pair of a specification string and corresponding argument list.
+When an environment \f5fe\fP is being inserted into the stack,
+if \f5fe->form\fP is \f5NULL\fP, the top environment is changed to \f5fe\fP
+and its associated extension functions
+but processing of the current formatting pair continues.
+On the other hand, if \f5fe->form\fP is not \f5NULL\fP,
+the new environment is pushed onto the stack
+so that pattern processing will start with the new formatting pair as well as
+any associated extension functions.
+During processing, whenever \f5extf\fP is called,
+\f5form\fP and \f5args\fP will be set to the current values of
+the formatting pair in use.
+.Tp
+\f5fmt\fP:
+This is set to the pattern being processed or one of '.', 'I', '('.
+.Tp
+\f5size\fP:
+This is the size of the object being processed.
+.Tp
+\f5flags\fP:
+This is a collection of bits defining the formatting flags specified for the pattern.
+The bits are:
+
+\f5SFFMT_LEFT\fP: Flag \f5-\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_SIGN\fP: Flag \f5+\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_BLANK\fP: Flag \fIspace\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_ZERO\fP: Flag \f50\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_THOUSAND\fP: Flag \f5'\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_LONG\fP: Flag \f5l\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_LLONG\fP: Flag \f5ll\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_SHORT\fP: Flag \f5h\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_LDOUBLE\fP: Flag \f5L\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_IFLAG\fP: flag \f5I\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_JFLAG\fP: flag \f5j\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_CENTER\fP: flag \f5=\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_CHOP\fP: flag \f5-\fP in \fIprecis\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_ALTER\fP: Flag \f5#\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_SKIP\fP: Flag \f5*\fP in \f5sfscanf()\fP.
+
+\f5SFFMT_ARGPOS\fP: This indicates argument processing for \f5pos$\fP.
+
+\f5SFFMT_VALUE\fP: This is set by \f5fe->extf\fP
+to indicate that it is returning a value to be formatted or
+the address of an object to be assigned.
+
+.Tp
+\f5width\fP:
+This is the field width.
+.Tp
+\f5precis\fP:
+This is the precision.
+.Tp
+\f5base\fP:
+This is the conversion base.
+.Tp
+\f5t_str\fP and \f5n_str\fP:
+This is the type string and its size.
+
+.Ss " int (*Sffmtext_f)(Sfio_t* f, Void_t* v, Sffmt_t* fe)"
+This is the type of the extension function \f5fe->extf\fP to process
+patterns and arguments.
+Arguments are always processed in order and
+\f5fe->extf\fP is called exactly once per argument.
+Note that, when \f5pos$\fP (below) is not used anywhere in a format string,
+each argument is used exactly once per a corresponding pattern.
+In that case, \f5fe->extf\fP is called
+as soon as the pattern is recognized and before any scanning or formatting.
+On the other hand, when \f5pos$\fP is used in a format string,
+an argument may be used multiple times.
+In this case, all arguments shall be processed in order
+by calling \f5fe->extf\fP exactly once per argument before any pattern processing.
+This case is signified by the flag \f5SFFMT_ARGPOS\fP in \f5fe->flags\fP.
+
+In addition to the predefined formatting patterns and other application-defined
+patterns, \f5fe->extf\fP may be called with \f5fe->fmt\fP being one
+of `\f5(\fP' (left parenthesis), `\f5.\fP' (dot), and `\f5I\fP'.
+
+The left parenthesis requests a string to be used as the \f5extfdata\fP string discussed below.
+In this case, upon returning, \f5fe->extf\fP should set the \f5fe->size\fP field
+to be the length of the string or a negative value to indicate a null-terminated string.
+
+The `\f5I\fP' requests an integer to define the object size.
+
+The dot requests an integer for width, precision, base, or a separator.
+In this case, the \f5fe->size\fP field will indicate how many dots have appeared
+in the pattern specification. Note that, if the actual conversion pattern is 'c' or 's',
+the value \f5*form\fP will be one of these characters.
+.Tp
+\f5f\fP:
+This is the input/output stream in the calling formatting function.
+During a call to \f5fe->extf\fP, the stream shall be unlocked
+so that \f5fe->extf\fP can read from or write to it as appropriate.
+.Tp
+\f5v\fP:
+For both \f5sfscanf()\fP and \f5sfprintf()\fP functions,
+\f5v\fP points to a location suitable for storing any scalars or pointers.
+On return, \f5fe->extf\fP treats \f5v\fP as discussed below.
+.Tp
+\f5fe\fP:
+This is the current formatting environment.
+.PP
+The return value \f5rv\fP of \f5fe->extf\fP directs further processing.
+There are two cases.
+When \f5pos$\fP is present, a negative return value means to ignore \f5fe\fP
+in further argument processing while a non-negative return value is treated
+as the case \f5rv == 0\fP below.
+When \f5pos$\fP is not present, \f5fe->extf\fP is called per argument
+immediately before pattern processing and its return values are treated
+as below:
+.Tp
+\f5rv < 0:\fP
+The environment stack is immediately popped.
+.Tp
+\f5rv == 0:\fP
+The extension function has not consumed (in a scanning case) or
+output (in a printing case) data out of or into the given stream \f5f\fP.
+The fields \f5fmt\fP, \f5flags\fP, \f5size\fP,
+\f5width\fP, \f5precis\fP and \f5base\fP of \f5fe\fP
+shall direct further processing.
+
+For \f5sfprintf()\fP functions, if \f5fe->flags\fP
+has the bit \f5SFFMT_VALUE\fP,
+\f5fe->extf\fP should have set \f5*v\fP to the value to be processed;
+otherwise, a value should be obtained from the argument list.
+Likewise, for \f5sfscanf()\fP functions,
+\f5SFFMT_VALUE\fP means that
+\f5*v\fP should have a suitable address; otherwise,
+an address to assign value should be obtained from the argument list.
+
+When \f5pos$\fP is present,
+if \f5fe->extf\fP changes \f5fe->fmt\fP, this pattern shall be used regardless of
+the pattern defined in the format string. On the other hand, if \f5fe->fmt\fP
+is unchanged by \f5fe->extf\fP, the pattern in the format string is used.
+In any case, the effective pattern should be one of the standardly defined pattern.
+Otherwise, it shall be treated as unmatched.
+.Tp
+\f5rv > 0:\fP
+The extension function has accessed the stream \f5f\fP
+to the extent of \f5rv\fP bytes.
+Processing of the current pattern ceases except that,
+for scanning functions, if \f5fe->flags\fP does not contain
+the bit \f5SFFMT_SKIP\fP, the assignment count shall increase by 1.
+
+.Ss "void va_copy(va_list to, va_list fr)"
+This macro function portably copies the argument list \f5fr\fP to
+the argument list \f5to\fP. It should be used to set the field \f5Sffmt_t.args\fP.
+
+.Ss "long sffmtversion(Sffmt_t* fe, int type)"
+This macro function initializes
+the formatting environment \f5fe\fP with a version number if \f5type\fP is
+non-zero. Otherwise, it returns the current value of the version number of \f5fe\fP.
+This is useful for applications to find out
+when the format of the structure \f5Sffmt_t\fP changes.
+Note that the version number corresponds to the Sfio version number
+which is defined in the macro value \f5SFIO_VERSION\fP.
+
+.Ss " int sfprintf(Sfio_t* f, const char* format, ...);"
+.Ss " char* sfprints(const char* format, ...);"
+.Ss " char* sfvprints(const char* format, va_list args);"
+.Ss " ssize_t sfaprints(char** sp, const char* format, ...);"
+.Ss " ssize_t sfvaprints(char** sp, const char* format, va_list args);"
+.Ss " int sfsprintf(char* s, int n, const char* format, ...)"
+.Ss " int sfvsprintf(char* s, int n, const char* format, va_list args);"
+.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 and \f5sfvsprintf()\fP write to buffer \f5s\fP
+which is of size \f5n\fP.
+\f5sfprints()\fP and \f5sfvprints()\fP construct data in some Sfio-defined buffer.
+\f5sfaprints()\fP and \f5sfvaprints()\fP are similar to \f5sfprints()\fP
+and \f5sfvprints()\fP
+but they return a string constructed via \f5malloc()\fP in \f5*sp\fP
+and expect this string to be freed by the caller when no longer needed.
+\f5sfvprintf()\fP is the underlying primitive for the other functions.
+Except for \f5sfprints()\fP and \f5sfvprints()\fP
+which return a null-terminated string or \f5NULL\fP,
+other functions return the number of output bytes or \f5-1\fP on failure.
+
+The length of string constructed by \f5sfprints()\fP, \f5sfsprintf()\fP, or
+\f5sfvsprintf()\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, f\fP and \f5!\fP.
+Except for \f5!\fP which shall be described below,
+see the ANSI-C specification of \f5fprintf(3)\fP for details on the other patterns.
+Let \f5z\fP be some pattern type. A formatting pattern is defined as below:
+
+.nf
+.ft 5
+ %[pos$][flag][width][.precision[.base]][(extfdata)]z
+.ft 1
+.fi
+
+.Tp
+\f5pos$\fP:
+A pattern can specify which argument in the argument list to use.
+This is done via \f5pos$\fP where \f5pos\fP is the argument position.
+Arguments are numbered so that the first argument after \f5format\fP is at position 1.
+If \f5pos\fP is not specified, the argument following the most recently used one
+will be used.
+The pattern \f5%!\fP (see below) cannot be used subsequent to a usage of \f5pos$\fP.
+Doing so may cause unexpected behaviors.
+.Tp
+\f5flag\fP:
+The flag characters are
+\f5h\fP, \f5hh\fP, \f5l\fP, \f5ll\fP, \f5L\fP, \f5I\fP, \f5j\fP, \f5t\fP, \f5z\fP,
+\f5\-\fP, \f5+\fP, \fIspace\fP, \f50\fP, \f5'\fP, \f5=\fP and \f5#\fP.
+
+Flag \f5I\fP defines the size or type of the object being formatted.
+There are two cases: (1) \f5I\fP by itself and (2) \f5I\fP
+followed by either a decimal number or `*'.
+
+In the first case, for integer and floating point patterns,
+the object type is taken to be the largest appropriate type
+(i.e., one of \f5Sflong_t\fP, \f5Sfulong_t\fP or \f5Sfdouble_t\fP).
+For conversion specifiers \f5s\fP and \f5c\fP, the flag is ignored.
+
+In the second case, a given decimal value would define a size while
+`*' would cause the size to be obtained from the argument list.
+Then, if the conversion specifier is \f5s\fP, this size defines the
+length of the string or strings being formatted (see the discussion of \f5base\fP below).
+For integer and floating point patterns,
+the size is used to select a type from one of the below lists as
+indicated by the conversion specifier:
+
+.nf
+.ft 5
+ Sflong_t, long, int, short
+ Sfulong_t, unsigned long, unsigned int, unsigned short
+ Sfdouble_t, double, float
+.ft 1
+.fi
+
+The selection algorithm always matches types from left to right in any given list.
+Although selection is generally based on sizes in bytes,
+for compatibility with Microsoft-C, the size 64
+is matched with an appropriate type with the same number of bits, if any.
+If the given size does not match any of the listed types,
+it shall match one of \f5int\fP, \f5unsigned int\fP, and \f5double\fP
+as defined by the formatting pattern.
+
+Below are a few examples of using the \f5I\fP flag.
+The first example prints an \f5Sflong_t\fP integer.
+This example is actually not portable and
+only works on platforms where \f5sizeof(Sflong_t)\fP is 8.
+The second example shows how to that portably.
+The third example specifies printing a string of length 16.
+This length shall be used regardless of whether or not the given string
+is shorter or longer than 16.
+The last example shows the use of the pattern \f5%n\fP to assign the amount
+of data already output into a \f5short\fP integer \f5n_output\fP.
+
+.nf
+.ft 5
+ sfprintf(sfstdout,"%I8d", Sflong_obj);
+ sfprintf(sfstdout,"%I*d", sizeof(Sflong_obj), Sflong_obj);
+ sfprintf(sfstdout,"%I*s", 16, s);
+ sfprintf(sfstdout,"%d%I*n", 1001, sizeof(short), &n_output);
+.ft 1
+.fi
+
+Flags \f5h\fP, \f5l\fP, \f5j\fP and \f5L\fP are the ANSI-C conventions to
+select the types of input objects.
+For example, \f5%hd\fP indicates a \f5short int\fP,
+while \f5%ld\fP indicates a \f5long int\fP.
+
+Flag \f5hh\fP addresses the byte value types, i.e., \f5char\fP and \f5unsigned char\fP.
+
+Flags \f5z\fP, \f5t\fP and \f5j\fP address respectively
+the types \f5size_t\fP, \f5ptrdiff_t\fP and \f5Sflong_t\fP.
+
+Flags \f5ll\fP and \f5L\fP address respectively
+the largest integer and floating value types, i.e.,
+\f5Sfulong_t\fP, \f5Sflong_t\fP, and \f5Sfdouble_t\fP.
+
+Flag \f5-\fP left-justifies data within the field (otherwise, right-justification).
+
+Flag \f5+\fP means that a signed conversion will always begin with a plus or minus sign.
+
+Flag \fIspace\fP 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 prepended.
+
+Flag \f50\fP means padding with zeros on the left.
+
+Flag \f5'\fP outputs thousands-separator used by the current locale.
+\f5setlocale(3)\fP should have been used to set the desired locale.
+
+Flag \f5=\fP centers data within the field.
+
+Flag \f5#\fP indicates an alternative format processing.
+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 contains 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.
+Finally, for \f5%c\fP, bytes will be printed in the C format.
+For example, when the ASCII character set is used,
+the byte value 10 will be printed as \f5\\n\fP while 255 is printed
+as \f5\\377\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:
+After a first dot appears, an integral value defines a precision.
+For floating point value patterns, precision is the number of precision digits.
+For \f5%c\fP, precision defines the number of times to repeat the
+character being formatted.
+For \f5%s\fP, precision defines the maximum number of characters to output;
+-\f5precis\fP also defines the maximum number of characters to output, but
+retains the rightmost \f5precis\fP characters.
+.Tp
+\f5base\fP:
+This is defined after exactly two dots have appeared.
+
+For \f5%i\fP, \f5%d\fP, and \f5%u\fP,
+\f5base\fP should be an integer value in the inclusive range \f5[2,64]\fP
+and defines a conversion base.
+If \f5base\fP is not in this range, it is defined to be \f510\fP.
+The digits to represent numbers are:
+
+.nf
+.ft 5
+ 01234567890
+ abcdefghijklmnopqrstuvwxyz
+ ABCDEFGHIJKLMNOPQRSTUVWXYZ @_
+.ft 1
+.fi
+
+For \f5%s\fP and \f5%c\fP, \f5base\fP defines a separator.
+Then, for \f5%s\fP, the input argument is taken to be a NULL-terminated array of strings
+while, for \f5%c\fP, this is a null-terminated array of characters.
+The strings or characters will be formatted one of a time based
+on the usual width and precision rules.
+After each formatted string or character, except for the last one,
+the separator \f5base\fP is output if it is a non-zero.
+
+There are further restrictions on the syntax of \f5%s\fP and \f5%c\fP when
+a separator is defined.
+Below are the legitimate sequences for \f5%s\fP and \f5%c\fP after the second dot:
+
+.nf
+\f5 s c\fP
+\f5 *s *c\fP
+\f5 \fP\fIz\fP\f5s \fP\fIz\fP\f5c\fP
+.fi
+
+In the first case, no separator is defined so \f5base\fP is set to zero.
+In the second case, \f5base\fP is obtained from the argument list.
+In the third case, the character \fIz\fP
+must be non-alphanumeric and \f5base\fP will be set to this character.
+
+The below example shows both the call and the result
+of printing a \f5NULL\fP-terminated array
+of three strings \f5apple\fP, \f5orange\fP, and \f5grape\fP:
+
+.nf
+.ft 5
+ sfprintf(sfstdout,"|%8..:s|",list);
+ | apple: orange: grape|
+.ft 1
+.fi
+
+.Tp
+\f5(extfdata)\fP:
+This defines a string \f5extfdata\fP
+to be passed to the extension function \f5Sffmt_t.extf\fP.
+Parentheses shall be balanced.
+If \f5extfdata\fP is \f5*\fP, the string is obtained from the argument list.
+
+.PP
+.Ss " int sfscanf(Sfio_t* f, const char* format, ...)"
+.Ss " int sfsscanf(const char* s, const char* format, ...)"
+.Ss " int sfvsscanf(const char* s, const char* format, va_list args)"
+.Ss " int sfvscanf(Sfio_t* f, const char* format, va_list args)"
+These functions scan data items.
+\f5sfscanf()\fP scans from the input stream \f5f\fP
+while \f5sfsscanf()\fP and \f5sfvsscanf()\fP
+scan from the null-terminated string \f5s\fP.
+\f5sfvscanf()\fP is the underlying primitive that performs the actual scanning.
+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),
+a new-line character only matches white spaces up to an input new-line character.
+This is useful to avoid blocking when scanning typed inputs.
+.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.
+Except for \f5!\fP which shall be described below,
+see the ANSI-C specification of \f5fscanf(3)\fP for details on other patterns.
+Let \f5z\fP be some pattern type. A formatting pattern is specified as below:
+
+.nf
+.ft 5
+ %[*][pos$][width][.width.base][(extfdata)][flag]z
+.ft 1
+.fi
+
+.Tp
+\f5pos$\fP:
+A pattern can specify which argument in the argument list to use.
+This is done via \f5pos$\fP where \f5pos\fP is the argument position.
+Arguments are numbered so that the first argument after \f5format\fP is at position 1.
+If \f5pos\fP is not specified, the argument following the most recently used one
+will be used.
+The pattern \f5%!\fP (see below) cannot be used subsequent to a usage of \f5pos$\fP.
+.Tp
+\f5*:\fP
+This discards the corresponding scanned item.
+.Tp
+\f5width\fP and \f5base\fP:
+\f5width\fP defines the maximum number of bytes to scan
+and \f5base\fP defines the base of an integral value being scanned.
+The `.' (dot) notation also allows specifying a `*' (star) to obtain
+the value from the argument list. The below example specifies scanning
+4 bytes to obtain the value of an integer in base 10. At the end of scanning,
+the variable \f5v\fP should have the value \f51234\fP.
+
+.nf
+.ft 5
+ sfsscanf("12345678","%.*.*d", 4, 10, &v);
+.ft 1
+.fi
+
+.Tp
+\f5(extfdata)\fP:
+This defines a string \f5extfdata\fP
+to be passed to the extension function \f5Sffmt_t.extf\fP.
+Parentheses shall be balanced.
+If \f5extfdata\fP is \f5*\fP, the string is obtained from the argument list.
+.Tp
+\f5flag:\fP
+This is \f5#\fP, \f5I\fP, or some sequence of \f5h\fP, \f5l\fP, and \f5L\fP.
+
+Flag \f5#\fP is significant for pattern \f5%i\fP and \f5%[\fP.
+For \f5%i\fP, it means that the \f5#\fP symbol does not have its usual
+meaning in an input sequence \f5base#value\fP.
+For example, the scanning result of \f5%#i\fP on input \f52#1001\fP is \f52\fP
+and the next \f5sfgetc()\fP call will return \f5#\fP.
+For \f5%[\fP, if the next character in the input stream does not match
+the given scan set of characters, \f5#\fP causes a match to a null string
+instead of a failure.
+
+Flag \f5I\fP defines the size or type of the object being formatted.
+There are two cases: (1) \f5I\fP by itself and (2) \f5I\fP
+followed by either a decimal number or `*'.
+
+In the first case, for integer and floating point patterns,
+the object type is taken to be the largest appropriate type
+(i.e., one of \f5Sflong_t\fP, \f5Sfulong_t\fP or \f5Sfdouble_t\fP).
+For string patterns such as \f5%s\fP, the flag is ignored.
+
+In the second case, a given decimal value would define a size while
+`*' would cause the size to be obtained from the argument list.
+For string patterns such as \f5%s\fP or \f5%[\fP, this size defines the
+length of the buffer to store scanned data.
+Specifying a buffer size only limits the amount of data copied into the buffer.
+Scanned data beyond the buffer limit will be discarded.
+For integer and floating point patterns,
+the size is used to select a type from one of the below lists as
+indicated by the conversion specifier:
+
+.nf
+.ft 5
+ Sflong_t, long, int, short
+ Sfulong_t, unsigned long, unsigned int, unsigned short
+ Sfdouble_t, double, float
+.ft 1
+.fi
+
+The selection algorithm always matches types from left to right in any given list.
+Although selection is generally based on sizes in bytes,
+for compatibility with Microsoft-C, the size 64
+is matched with an appropriate type with the same number of bits, if any.
+If the given size does not match any of the listed types,
+it shall match one of \f5int\fP, \f5unsigned int\fP, and \f5double\fP
+as indicated by the formatting pattern.
+
+Below are examples of using the \f5I\fP flag.
+The first example scans a 64-bit integer.
+The second scans some floating point value
+whose size is explicitly computed and given.
+The last example scans a string into a buffer with the given size 128.
+Note that if the scanned string is longer than 127, only the first 127
+bytes shall be copied into the buffer. The rest of the scanned data
+shall be discarded.
+
+.nf
+.ft 5
+ sfscanf(sfstdin,"%I64d", &int64_obj);
+ sfscanf(sfstdin,"%I*f", sizeof(float_obj), &float_obj);
+ sfscanf(sfstdin,"%I*s", 128, buffer);
+.ft 1
+.fi
+
+Flags \f5h\fP, \f5l\fP, and \f5L\fP are the ANSI-C conventions
+for indicating the type of a scanned element.
+For example, \f5%hd\fP means scanning a \f5short int\fP.
+The flags \f5ll\fP and \f5L\fP mean respectively scanning an
+integer or a floating point value with largest size
+(i.e, \f5Sflong_t\fP or \f5Sfdouble_t\fP).
+.PP
+The \f5%i\fP, \f5%d\fP and \f5%u\fP patterns scan numbers in bases
+from \f52\fP to \f564\fP.
+\f5%i\fP scans integral values in self-describing formats.
+Except for octal, decimal and hexadecimal numbers with the usual formats,
+numbers in general bases are assumed to be of the form: \fIbase#value\fP
+where \fIbase\fP is a number in base 10 and \fIvalue\fP
+is a number in the given base.
+For example, \f52#1001\fP is the binary representation of the decimal value \f59\fP.
+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
+.ft 5
+ 0123456789
+ abcdefghijklmnopqrstuvwxyz
+ ABCDEFGHIJKLMNOPQRSTUVWXYZ @_
+.ft 1
+.fi
+
+.PP
+.Ss "BUFFERING, SYNCHRONIZATION"
+.PP
+.Ss " Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size)"
+This function changes the buffering scheme for the stream \f5f\fP.
+The stream will be synchronized before any buffer modification.
+If a new buffer is successfully set and the old buffer has not been freed,
+\f5sfsetbuf()\fP returns the old buffer. Otherwise, it returns \f5NULL\fP.
+After a \f5sfsetbuf()\fP call,
+\f5sfvalue()\fP returns the size of the returned buffer.
+
+Sfio attempts to read data in blocks likely to be serviced fast by the file system.
+This means block sizes being multiples of a suitable alignment value
+(e.g., 512, 1024 or 8192). By default, the alignment value
+is computed via some internal mechanism depending on the local platform but
+it can also be explicitly set via the call \f5sfsetbuf(f, (Void_t*)f, size)\fP.
+
+In invocations of \f5sfsetbuf()\fP other than the above case,
+the \f5size\fP argument is treated as follows:
+.Tp
+\f5size == SF_UNBOUND\fP:
+Sfio will pick a suitable buffer size.
+If \f5buf\fP is \f5NULL\fP,
+Sfio 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,
+Sfio 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.
+It returns a negative value for failure and \f50\fP for success.
+
+For a \f5SF_WRITE\fP stream, synchronization 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.
+
+If \f5f\fP has flag \f5SF_IOCHECK\fP, the \f5SF_SYNC\fP event is raised
+before and after synchronization. See \f5sfdisc()\fP for details.
+
+.Ss " int sfpoll(Sfio_t** flist, 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.
+If a stream has a discipline, the exception function may be called
+before and after the stream is polled (see \f5sfdisc()\fP for details).
+After a successful \f5sfpoll()\fP call,
+for each ready stream \f5f\fP, \f5sfvalue(f)\fP returns
+a bit combination of \f5SF_READ\fP and \f5SF_WRITE\fP to tell which I/O
+mode is available. If \f5SF_READ\fP is available, an attempt to read
+a byte will not block. If \f5SF_WRITE\fP is available,
+an attempt to flush will not block.
+\f5sfpoll()\fP returns the number of ready streams or \f5-1\fP on failure.
+.Tp
+\f5flist\fP and \f5n\fP:
+\f5flist\fP is an array of \f5n\fP streams to be polled.
+Upon return, ready streams are moved to the front
+of \f5flist\fP in the same relative order.
+.Tp
+\f5timeout\fP:
+This defines an elapse 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 querying the operating system.
+
+.Ss " Sfio_t* sfpool(Sfio_t* f, Sfio_t* poolf, int mode)"
+This function manipulates pools of 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 simply returns the head of the pool containing \f5poolf\fP.
+If \f5f\fP is not \f5NULL\fP and \f5poolf\fP is \f5NULL\fP,
+\f5f\fP is deleted from its pool.
+In this case, if no other stream from the same pool can become head,
+\f5sfpool()\fP will return \f5NULL\fP; otherwise, it returns some stream
+from the remainder of the pool.
+If both \f5f\fP and \f5poolf\fP are not \f5NULL\fP,
+\f5f\fP is moved from its current pool (if any)
+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 contains streams with \f5SF_WRITE\fP mode.
+In addition, on change to a new head stream,
+buffered write data of the current head
+is transferred 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.)
+After purging, if \f5f\fP has flag \f5SF_IOCHECK\fP,
+the event \f5SF_PURGE\fP is raised.
+\f5sfpurge()\fP returns \f5-1\fP for failure and \f50\fP for success.
+
+.PP
+.Ss "DISCIPLINE, EVENT-HANDLING"
+.PP
+A file stream uses the system calls \f5read(2)\fP, \f5write(2)\fP
+and \f5lseek(2)\fP to read, write and position in the underlying file.
+Disciplines enable application-defined I/O methods including exception handling and
+data pre/post-processing.
+
+.Ss " Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)"
+Each stream has a discipline stack whose bottom is a virtual discipline
+representing the actual system calls.
+\f5sfdisc()\fP manipulates the discipline stack of stream \f5f\fP.
+\f5f\fP will be synchronized before any discipline stack manipulation.
+After a successful discipline stack manipulation,
+the stream I/O position (see \f5sfseek()\fP and \f5sftell()\fP)
+and extent (see \f5sfsize()\fP) are updated
+to reflect that defined by the top discipline.
+\f5sfdisc()\fP returns \f5NULL\fP on failure.
+
+If the value of \f5disc\fP is identical to the value of \f5f\fP,
+then the top discipline on the discipline
+stack is returned without any further action.
+An application can then use this feature of \f5sfdisc()\fP
+and the field \f5disc\fP (below) of the discipline structure
+to traverse the entire discipline stack of a stream \f5f\fP as follows:
+
+.nf
+.ft 5
+ for(disc = sfdisc(f, (Sfdisc_t*)f); disc; disc = disc->disc)
+.ft 1
+.fi
+
+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 pushed onto the discipline stack.
+In this case, if successful, \f5sfdisc()\fP returns
+the discipline that was pushed down.
+
+Note that a discipline can be used on only one stream at a time.
+An application should take care to allocate different discipline
+structures for use with different streams.
+A discipline structure is of the type \f5Sfdisc_t\fP which
+contains the following public fields:
+
+.nf
+.ft 5
+ Sfread_f readf;
+ Sfwrite_f writef;
+ Sfseek_f seekf;
+ Sfexcept_f exceptf;
+ Sfdisc_t* disc;
+.ft 1
+.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.
+For some events, e.g., \f5SF_DPOLL\fP, the return value may also have
+additional meanings.
+.Tp
+\f5=0\fP:
+The on-going operation performs default actions with respect to the raised event.
+For example, on a reading error or reaching 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_LOCKED\fP:
+The stream cannot be accessed.
+Note that this lock state is not related to the mutex lock
+that protects a stream from multiple accesses by different threads
+(see section THREAD SAFETY). Rather, the stream was frozen by
+certain operations such as \f5sfreserve()\fP or \f5sfstack()\fP.
+Thus, a stream can be in this state even if the application is uni-threaded.
+.Tp
+\f5SF_READ\fP, \f5SF_WRITE\fP:
+These events are raised around reading and writing operations.
+
+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*((ssize_t*)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.
+
+\f5SF_READ\fP and \f5SF_WRITE\fP are also raised on operation failures.
+In such a case, \f5*((ssize_t*)value)\fP
+is the return value from the failed operation.
+.Tp
+\f5SF_SEEK\fP:
+This event is raised when a seek operation fails.
+.Tp
+\f5SF_NEW\fP, \f5SF_CLOSING\fP (\f5SF_CLOSE\fP), \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 (see \f5sfnew()\fP).
+\f5SF_CLOSING\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, \f5SF_DPOP\fP, \f5SF_DBUFFER\fP:
+Events \f5SF_DPUSH\fP and \f5SF_DPOP\fP are raised when a
+discipline is about to be pushed or popped.
+\f5(Sfdisc_t*)value\fP is the to-be top discipline, if any.
+
+A stream buffer is always synchronized before pushing or popping a discipline.
+If this synchronization fails, \f5SF_DBUFFER\fP will be raised with
+\f5*((size_t*)value)\fP being the amount of data still in the buffer.
+If the return value of \f5exceptf\fP is non-negative,
+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 the stream is ready for I/O.
+\f5*((int*)value)\fP indicates a time-out interval to wait.
+A negative return value from the exception function means blocking.
+A zero return value means that \f5sfpoll()\fP should
+query the underlying file descriptor.
+A positive return value means non-blocking. In addition,
+this value will be a bit combination of \f5SF_READ\fP and \f5SF_WRITE\fP
+to indicate what I/O modes are ready.
+.Tp
+\f5SF_READY\fP:
+This event is raised by \f5sfpoll()\fP for each ready stream.
+The third argument to the event handler is an integer composed with
+the two bits \f5SF_READ\fP and \f5SF_WRITE\fP to indicate which
+I/O modes are ready.
+.Tp
+\f5SF_SYNC\fP, \f5SF_PURGE\fP:
+If \f5SF_IOCHECK\fP is set,
+these events are raised respectively for a \f5sfsync()\fP or \f5sfpurge()\fP call.
+In each case, the respective event is raised once before the appropriate
+operation (synchronization or purging) with \f5((int)value)\fP being \f51\fP
+and once after with \f5((int)value)\fP being \f50\fP.
+Note that \f5sfsync()\fP is called for each
+\f5SF_WRITE\fP or \f5SF_SHARE|SF_READ\fP stream on closing.
+
+.Tp
+\f5SF_ATEXIT\fP:
+This event is raised for each open stream before the process exits.
+
+.Ss " int sfraise(Sfio_t* f, int type, Void_t* data)"
+If \f5f\fP is non-\f5NULL\fP, \f5sfraise()\fP calls all exception handlers
+of \f5f\fP with the event \f5type\fP and associated \f5data\fP.
+If an exception handler returns a non-zero value,
+\f5sfraise()\fP immediate returns the same value.
+Application-defined events should start from the value \f5SF_EVENT\fP
+so as to avoid confusion with system-defined events,
+\f5sfraise()\fP returns \f50\fP on success and \f5-1\fP on failure.
+
+If \f5f\fP is \f5NULL\fP, \f5sfraise()\fP iterates over all streams
+and raise events as described above. In this case,
+\f5sfraise()\fP returns \f50\fP on success and a negative value
+on failure. The absolute value of the return value tells how many
+streams failed on raising the given event.
+
+.Ss " ssize_t sfrd(Sfio_t* f, Void_t* buf, size_t n, Sfdisc_t* disc)"
+.Ss " ssize_t sfwr(Sfio_t* f, const Void_t* buf, size_t n, Sfdisc_t* disc)"
+.Ss " Sfoff_t sfsk(Sfio_t* f, Sfoff_t offset, int type, Sfdisc_t* disc)"
+These functions provides safe methods 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 sfresize(Sfio_t* f, Sfoff_t size)"
+This function resizes the stream \f5f\P so that its extent is \f5size\fP.
+If the stream corresponds to a file, the file size is set to \f5size\fP
+via the system call \f5ftruncate()\fP.
+When a stream is made larger, the new data space is filled with zero's.
+\f5sfresize()\fP returns \f50\fP on success and a negative value on failure.
+
+.Ss " int sfset(Sfio_t* f, int flags, int set)"
+This function sets control flags for the stream \f5f\fP.
+It returns the previous set of flags or \f50\fP on error.
+
+Settable flags are:
+\f5SF_READ\fP, \f5SF_WRITE\fP, \f5SF_IOCHECK\fP,
+\f5SF_LINE\fP, \f5SF_SHARE\fP, \f5SF_PUBLIC\fP, \f5SF_MALLOC\fP and
+\f5SF_STATIC\fP.
+Note that \f5SF_READ\fP and \f5SF_WRITE\fP can be turned on or off 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.
+If legal, an attempt to turn on either \f5SF_READ\fP or \f5SF_WRITE\fP
+will cause the stream to be in the given I/O mode.
+.Tp
+\f5set == 0:\fP
+If \f5flags\fP is zero, the current set of flags is simply returned.
+Note that when a stream is first opened, not
+all of its flags are initialized yet (more below). If \f5flags\fP is
+non-zero, an attempt is made to turn off the specified flags.
+.Tp
+\f5set != 0:\fP
+If \f5flags\fP is zero, the stream is initialized if not yet done so.
+Then the current set of flags is returned.
+If \f5flags\fP is non-zero, an attempt is made to turn on the
+specified flags.
+
+.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 the 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.
+If it is \f5NULL\fP, \f5sfstack()\fP does nothing and returns \f5top\fP.
+.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 a writing operation was just performed,
+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 " ssize_t 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 stream has an end-of-file condition.
+\f5sferror()\fP tells whether or not the stream has an error condition.
+\f5sfclrerr()\fP clears both end-of-file and error conditions.
+The end-of-file and error conditions are also cleared on an I/O operation.
+
+.Ss " int sfclrlock(Sfio_t* f)"
+This function restores the stream back to a normal state.
+This means clearing locks and possibly throwing away unprocessed data.
+As such, this operation is unsafe and should be used with care.
+For example, it may be used before a long jump (\f5longjmp(3)\fP)
+out of some discipline I/O function to restore the internal stream states.
+\f5sfclrlock()\fP returns the current set of flags.
+
+.Ss " int sfnotify((void(*)notify)(Sfio_t*, int, void*) )"
+This sets a function \f5(*notify)()\fP to be called
+as \f5(*notify)(f, type, data)\fP on various stream events.
+Arguments \f5type\fP and \f5data\fP indicate the reason for the call and accompanying data:
+.Tp
+\f5SF_NEW\fP:
+\f5f\fP is being opened and \f5data\fP is the underlying file descriptor.
+.Tp
+\f5SF_CLOSING\fP (\f5SF_CLOSE\fP):
+\f5f\fP is the stream being closed and \f5data\fP is the underlying file descriptor.
+.Tp
+\f5SF_SETFD\fP:
+The file descriptor of \f5f\fP is being changed to the one
+defined by \f5data\fP (see \f5sfsetfd()\fP.)
+.Tp
+\f5SF_READ\fP:
+An attempt to change \f5f\fP to read mode failed.
+\f5data\fP is the file descriptor of the stream.
+.Tp
+\f5SF_WRITE\fP:
+An attempt to change \f5f\fP to write mode failed.
+\f5data\fP is the file descriptor of the stream.
+.Tp
+\f5SF_MTACCESS\fP:
+When a notifying function was registered (see \f5sfnotify()\fP),
+every Sfio call on a stream with flag \f5SF_MTSAFE\fP will
+invoke the notifying function
+once on entry after the stream is locked
+as \f5(*notify)(f, SF_MTACCESS, Sfio_t** fp), and
+once on return before unlocking as
+as \f5(*notify)(f, SF_MTACCESS, (Sfio_t**)0).
+In the call entry case,
+the notification function could use the argument \f5fp\fP
+to set a stream that would be used for performing the actual I/O operation.
+In this way, certain global streams such as the standard streams \f5sfstdin\fP,
+\f5sfstdout\fP and \f5sfstderr\fP could be made to act differently when used
+in different streams.
+
+.Ss " int sfwalk(Sfwalk_f walkf, Void_t* data, int type)"
+This function invokes \f5(*walkf)(f, data)\fP on every open stream \f5f\fP
+whose flags as defined by \f5sfset()\fP contains all bit flags given in \f5type\fP.
+On such a call, if the return value is negative, \f5sfwalk()\fP will terminate.
+\f5sfwalk()\fP returns 0 if no stream was processed.
+Otherwise, it returns the return value from the last invocation of \f5walkf()\fP.
+
+As an example, the call \f5sfwalk(walkf, data, SF_READ)\fP will iterate over all streams
+opened for reading. Similarly, \f5sfwalk(walkf, data, SF_READ|SF_WRITE)\fP
+iterates over all streams opened for both reading and writing.
+Lastly, \f5sfwalk(walkf, data, 0)\fP iterates over all streams.
+
+.PP
+.Ss "MISCELLANEOUS FUNCTIONS"
+.PP
+.Ss " ssize_t sfmaxr(ssize_t maxr, int set)"
+Certain records may require too much memory for storage, thus, causing
+undesirable side effects. Therefore, the library normally bounds the amount
+of memory used by \f5sfgetr()\fP. A different memory bound
+can be set via \f5sfmaxr()\fP. While a positive \f5maxr\fP hints to \f5sfgetr()\fP
+to use only about that much memory to construct a record, a non-positive bound
+allows \f5sfgetr()\fP to use as much memory as necessary.
+\f5sfmaxr()\fP sets the value only if \f5set\fP is non-zero.
+It returns the value before setting or the current value if not setting.
+
+.Ss " ssize_t sfslen()"
+This function returns the length of a string just constructed
+by \f5sfsprintf()\fP or \f5sfprints()\fP. See also \f5sfvalue()\fP.
+
+.Ss " int sfulen(Sfulong_t v)"
+.Ss " int sfllen(Sflong_t v)"
+.Ss " int sfdlen(Sfdouble_t v)"
+These functions return respectively the number of bytes required to code the
+\f5Sfulong_t\fP, \f5Sflong_t\fP or \f5Sfdouble_t\fP value \f5v\fP by \f5sfputu()\fP,
+\f5sfputl()\fP or \f5sfputd()\fP.
+
+.Ss " ssize_t sfpkrd(int fd, char* buf, size_t 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.
+If the last returned byte is not the record separator, then
+the read data did not contain a complete record. Otherwise,
+it contains one or more records.
+See also \f5action\fP below.
+.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 and there are two cases.
+If \f5rsc < 0\fP, an attempt is made to read \f5n\fP bytes.
+If \f5rsc >= 0\fP, an attempt is made to read one record.
+
+.PP
+.Ss "FULL STRUCTURE SFIO_T"
+.PP
+.Ss " #include <sfio_t.h>"
+Most applications based on Sfio only need to include
+the header file \f5sfio.h\fP which defines an abbreviated \f5Sfio_t\fP
+structure without certain fields private to Sfio.
+However, there are times (e.g., debugging)
+when an application may require more details about the full \f5Sfio_t\fP structure.
+In such cases, the header file \f5sfio_t.h\fP can be used in place of \f5sfio.h\fP.
+Note that an application doing this will become sensitive to changes
+in the internal architecture of Sfio.
+
+.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 "EXAMPLE DISCIPLINES"
+.PP
+The below functions create disciplines and insert them into
+the given streams \f5f\fP. These functions return \f50\fP
+on success and \f5-1\fP on failure.
+
+.Ss "int sfdcdio(Sfio_t* f, size_t bufsize)"
+This creates a discipline that uses the direct IO feature
+available on file systems such as SGI's XFS to speed up IO.
+The argument \f5bufsize\fP suggests a buffer size to use for data transfer.
+
+.Ss "int sfdcdos(Sfio_t* f)"
+This creates a discipline to read DOS text files.
+It basically transforms pairs of \er\en to \en.
+
+.Ss "int sfdcfilter(Sfio_t* f, const char* cmd)"
+This creates a discipline that sends data from \f5f\fP
+to the given command \f5cmd\fP to process, then reads back the processed data.
+
+.Ss "int sfdcseekable(Sfio_t* f)"
+This creates a discipline that makes an unseekable reading stream seekable.
+
+.Ss "int sfdcslow(Sfio_t* f)"
+This creates a discipline that makes all Sfio operations return immediately
+on interrupts. This is useful for dealing with slow devices.
+
+.Ss "int sfdcsubstream(Sfio_t* f, Sfio_t* parent, Sfoff_t offset, Sfoff_t extent)"
+This creates a discipline that makes \f5f\fP acts as if it
+corresponds exactly to the subsection of \f5parent\fP
+starting at \f5offset\fP with size \f5extent\fP.
+
+.Ss "int sfdctee(Sfio_t* f, Sfio_t* tee)"
+This creates a discipline that copies to the stream \f5tee\fP
+any data written to \f5f\fP.
+
+.Ss "int sfdcunion(Sfio_t* f, Sfio_t** array, int n)"
+This creates a discipline that makes \f5f\fP act as if it is
+the concatenation of the \f5n\fP streams given in \f5array\fP.
+
+.Ss "int sfdclzw(Sfio_t* f)"
+This creates a discipline that would decompress data in \f5f\fP.
+The stream \f5f\fP should have data from a source compressed by
+the Unix \fBcompress\fP program.
+
+.Ss "int sfdcgzip(Sfio_t* f, int opt)"
+This creates a discipline for reading/writing data compressed by zlib.
+The argument \f5opt\fP defines the optimization level.
+
+.PP
+.Ss "STDIO-COMPATIBILITY"
+.PP
+Sfio provides compatibility functions for all various popular
+Stdio implementations at source and binary level.
+The source Stdio-compatibility interface
+provides the header file \f5stdio.h\fP that defines
+a set of macros or inlined functions to map Stdio calls to Sfio ones.
+This mapping may benignly extend or change the meaning of certain
+original Stdio operations. For example, the Sfio'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.
+Similarly, the Sfio's \f5fopen()\fP call can be used to create
+string streams in addition to file streams.
+.PP
+The standard streams \f5stdin\fP, \f5stdout\fP and \f5stderr\fP
+are mapped via \f5#define\fP to \f5sfstdin\fP, \f5sfstdout\fP and \f5sfstderr\fP.
+The latter are typically declared of the type \f5Sfio_t*\fP.
+Certain older Stdio applications require these to be declared
+as addresses of structures so that static initializations of
+the sort ``\f5FILE*\ f\ =\ stdin;\fP'' would work.
+Such applications should use the compile time flag \f5SF_FILE_STRUCT\fP
+to achieve the desired effect.
+.PP
+The binary Stdio-compatibility libraries, \f5libstdio.a\fP and \f5libstdio-mt.a\fP,
+provide complete implementations of Stdio functions suitable
+for linking applications already compiled with native header \f5stdio.h\fP.
+These functions are also slightly altered or extended
+as discussed above.
+.PP
+Below are the supported Stdio 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 flockfile(FILE* stream)
+int ftrylockfile(FILE* stream)
+void funlockfile(FILE* stream)
+
+void setbuf(FILE* stream, char* buf);
+int setvbuf(FILE* stream, char* buf, int mode, size_t size);
+void setbuffer(FILE* stream, char* buf, size_t 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, fpos_t* pos);
+int fsetpos(FILE* stream, fpos_t* 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);
+size_t fread(Void_t* ptr, size_t size, size_t nelt, 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);
+size_t fwrite(const Void_t* ptr, size_t size, size_t nelt, 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
+
+.PP
+.Ss "RECENT CHANGES"
+.PP
+A few exception types have been added. In particular, exception handlers shall
+be raised with \f5SF_LOCKED\fP on accessing a stream frozen either by
+an ongoing operation or a previous operation (e.g., \f5sfgetr()\fP).
+Before a process exits, the event \f5SF_ATEXIT\fP is raised for each open stream.
+.PP
+A number of disciplines were added for various processing functions.
+Of interests are disciplines to use the direct I/O feature on IRIX6.2,
+read DOS text files, and decompress files compressed by Unix \fIcompress\fP.
+.PP
+Various new stream and function flags have been added. For example,
+the third argument of \f5sfgetr()\fP is now a set of bit flags and not
+just a three-value object. However, the old semantics of this argument
+of \f5sfgetr()\fP is still supported.
+.PP
+The \f5sfopen()\fP call has been extended so that sfopen(f,NULL,mode) can be
+used to changed the mode of a file stream before any I/O operations.
+This is most useful for changing the modes of the standard streams.
+.PP
+The buffering strategy has been significantly enhanced for streams
+that perform many seek operations. Also, the handling of stream and
+file positions have been better clarified so that applications that
+share file descriptors across streams and/or processes can be sure that
+the file states will be consistent.
+.PP
+The strategy for mapping between Sfio and Stdio streams in the binary
+compatibility package has been significantly enhanced for efficiency.
+For most platforms, the mapping is now constant time per look-up.
+.PP
+The \f5SF_BUFCONST\fP flag was deleted. This is largely unused anyway.
+.PP
+The library can be built for thread-safety. This is based largely on
+Posix pthread mutexes except for on UWIN where native Windows APIs
+are used.
+.PP
+The functions \f5sfgetm()\fP and \f5sfputm()\fP were added to encode
+unsigned integer values with known ranges.
+.PP
+The flag \f5SF_APPEND\fP is identical to \f5SF_APPENDWR\fP.
+However it conflicts with a different token of the same name
+defined in the system header \f5stat.h\fP of BSDI Unix systems.
+On such systems, we shall not define \f5SF_APPEND\fP and this
+symbol may be removed in a future release.
+.PP
+Similarly, the exception \f5SF_CLOSE\fP is identical to \f5SF_CLOSING\fP.
+However it conflicts with a different token of the same name
+defined in the system header \f5socket.h\fP of AIX Unix systems.
+On such systems, we shall not define \f5SF_CLOSE\fP and this
+symbol may be removed in a future release.
+.PP
+The printing and scanning functions were extended to handle multibyte characters
+and to conform to the C99 standard.
+.PP
+The function \f5sfpoll()\fP was rehauled to make it useful
+for writing servers that must commnunicate with multiple streams
+without blocking.
+.PP
+The formatting pattern \f5%c\fP for \f5sf*printf\fP was extended
+to allow the flag \f5#\fP to print unprintable character values
+using the C convention. For example, \f5%#c\fP prints the octal value 012
+as \f5\\n\fP.
+
+.SH AUTHORS
+Kiem-Phong Vo, kpv@research.att.com,
+.br
+David G. Korn, dgk@research.att.com, and
+.br
+Glenn S. Fowler, gsf@research.att.com.