SFIO(3)

NAME
sfio - safe/fast string/file input/output

SYNOPSIS
#include	<sfio.h>
#define ulong	unsigned long
Void_t;
Sfio_t, Sfile_t;
Sfdisc_t;
int	(*Sfread_f)(Sfio_t*, Void_t*, int, Sfdisc_t*);
int	(*Sfwrite_f)(Sfio_t*, Void_t*, int, Sfdisc_t*);
long	(*Sfseek_f)(Sfio_t*, long, int, Sfdisc_t*);
int	(*Sfexcept_t)(Sfio_t*, int, Sfdisc_t*);
SF_READ
SF_WRITE
SF_STRING
SF_APPEND
SF_LINE
SF_SHARE
SF_PUBLIC
SF_MALLOC
SF_STATIC
SF_IOCHECK
Sfio_t*	sfnew(Sfio_t* f, char* buf, int size, int fd, int flags);
Sfio_t*	sfopen(Sfio_t* f, const char* string, const char* mode);
Sfio_t*	sfpopen(Sfio_t* f, const char* cmd, const char* mode);
Sfio_t*	sftmp(int size);
Sfdisc_t*	sfdisc(Sfio_t* f, Sfdisc_t* disc);
int	sfrd(Sfio_t* f, Void_t* buf, int n, Sfdisc_t* disc);
int	sfwr(Sfio_t* f, Void_t* buf, int n, Sfdisc_t* disc);
long	sfsk(Sfio_t* f, long addr, int offset, Sfdisc_t* disc);
int	sfclose(Sfio_t* f);
int	sfsync(Sfio_t* f);
int	sfpurge(Sfio_t* f);
Sfio_t*	sfpool(Sfio_t* f, Sfio_t* poolf, int mode);
char*	sfsetbuf(Sfio_t* f, char* buf, int size);
Sfio_t*	sfstack(Sfio_t* base, Sfio_t* top);
Sfio_t* sfswap(Sfio_t* f1, Sfio_t* f2);
char*	sfreserve(Sfio_t* f, int n, int lock);
int	sfpeek(Sfio_t* f, char** bufp, int n); (obsolete)
int	sfpoll(Sfio_t** fa, int n, int timeout);
long	sfseek(Sfio_t* f, long addr, int offset);
long	sftell(Sfio_t* f);
int	sfsetfd(Sfio_t* f, int fd);
int	sfset(Sfio_t* f, int flags, int i);
int	sfgetc(Sfio_t* f);
int	sfungetc(Sfio_t* f, int c);
ulong	sfgetu(Sfio_t* f);
long	sfgetl(Sfio_t* f);
double	sfgetd(Sfio_t* f);
char*	sfgetr(Sfio_t* f, int rsc, int string);
int	sfread(Sfio_t* f, Void_t* buf, int n);
int	sfscanf(Sfio_t* f, const char* format, ...);
int	sfsscanf(const char* s, const char* format, ...);
int	sfvscanf(Sfio_t* f, const char* format, va_list args);
int	sfputc(Sfio_t* f, int c);
int	sfnputc(Sfio_t* f, int c, int n);
int	sfputu(Sfio_t* f, ulong v);
int	sfputl(Sfio_t* f, long v);
int	sfputd(Sfio_t* f, double v);
int	sfputr(Sfio_t* f, const char* s, int c);
int	sfwrite(Sfio_t* f, const Void_t* buf, int n);
long	sfmove(Sfio_t* fr, Sfio_t* fw, long n, int rsc);
int	sfprintf(Sfio_t* f, const char* format, ...);
int	sfvprintf(Sfio_t* f, const char* format, va_list args);
int	sfsprintf(char* s, int n, const char* format, ...);
char*	sfprints(const char* format, ...);
int	sfnotify(void (*notify)(Sfio_t* f, int type, int fd));
long	sfsize(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);
#include	<sfio_t.h>
#define	SFNEW(buf,size,file,flags,disc)
int	sfslen();
int	sfulen(ulong v);
int	sfllen(long v);
int	sfdlen(double v);
int	sfpkrd(int fd, char* buf, int n, int rsc, long tm, int peek);
char*	sfecvt(double v, int n, int* decpt, int* sign);
char*	sffcvt(double v, int n, int* decpt, int* sign);

DESCRIPTION

Sfio is a library of functions for efficient I/O on buffered streams. Each sfio stream is of the type Sfio_t and corresponds to either a file descriptor (see open(2)) or some memory segment (a string stream). Among the innovations of sfio are I/O disciplines for data processing, stream stacks for recursive stream processing, and stream pools for auto-synchronization or data serialization.

During an I/O request, a system call (read(2), write() or lseek()) or its discipline replacement may be invoked. Such a function is said to cause an exception if its return value is non-positive. An application can handle the exception itself via an exception handler (see sfdisc()) or leave it to sfio. In the latter case, sfio examines errno (see errno.h) for further actions. Typically, if errno is EINTR, the respective function will be resumed. Otherwise, an error condition is returned.

There are various circumstances where sfio functions may fail. For example, during the processing of an IO request on a stream, the stream is usually locked to prevent concurrent accesses. Accessing such a locked stream results in error. In the event of an error, a function that returns integer values will return -1 or some negative value while a function that returns a pointer value will return NULL. More details on return values will be given for each function as appropriate.

For each file stream, to reduce the number of system calls, a buffer is used to store data. Normally, this buffer is a memory area allocated by malloc(3) or some memory area supplied by the application. On systems with memory mapping (mmap(2)), memory maps may be used to speed up IO. It is important that the extent of such a stream not be truncated while the map is in use; otherwise, memory access errors (SIGBUS - signal(3)) can occur. If an application cannot guarantee this, it should use sfsetbuf() to make sure that no memory mapping will be used.

This defines a suitable type to use with functions such as sfread() or sfwrite() that perform IO with objects of types unknown to the package. Void_t is #defined as void for ANSI-C and C++ and char for other compilation environments. Sfio_t or Sfile_t defines the type of a stream. Sfdisc_t is the type of a stream discipline and contains fields of the types Sfread_f, Sfwrite_f, Sfseek_f, and Sfexcept_f. These fields define alternative discipline functions which replace their counterpart system calls to read, write and seek. See sfdisc() for more details. A number of bit flags define stream types and control their operations. They are either set when a stream is initialized or by calling sfset(). Following are the flags: SF_READ: The stream is readable. SF_WRITE: The stream is writable. SF_STRING: The stream is a string (a byte array) that is readable if SF_READ is specified and writable if SF_WRITE is specified. SF_APPEND: The stream is a file opened for appending data (see open(2) and fcntl(2)). This means that data written to the stream is always appended at the end of the file. On systems where there is no primitive to specify at file opening time that a file is opened for append only, lseek() (or its discipline replacement) will be used on the file stream to approximate this behavior by always seeking to the end of the stream before writing out data. SF_LINE: The stream is line-oriented. For write streams, this means that the buffer is flushed whenever a new-line character, \n, is output. For read streams, this means that sfscanf() will specially treat the matching of \n (see below). SF_LINE is automatically set for terminal devices. SF_SHARE: The stream is a file stream accessible by independent entities, e.g., multiple processes. If the stream is seekable and not SF_PUBLIC (see below), each read or write system call will be preceded by a lseek() (or its discipline replacement) to ensure that the logical stream location corresponds to the physical file location. If the stream is unseekable (e.g., pipes or ttys), the block and record I/O operations (sfread(), sfwrite(), sfmove(), sfgetr(), sfputr(), sfreserve(), sfvscanf() and sfvprintf()) will ensure that (1) after each write operation, the stream is synchronized and (2) for each read operation, only the data requested will be read. Note that this guarantee may not be possible if the underlying operating system does not provide appropriate support facilities such as recv(2) or streamio(4) to peek on incoming data without reading past them. SF_SHARE is automatically set for the standard streams sfstdin, sfstdout, and sfstderr if they are pipes or seekable. SF_PUBLIC: This flag must be used in conjunction with SF_SHARE. It is automatically turned off for unseekable streams. For seekable streams, it means that after a sfsync() call, the next IO operation will cause the internal stream pointer to align with the current location of the physical seek pointer. This allows applications involving multiple processes to schedule and serialize their IO on a shared file descriptor. SF_MALLOC: The stream buffer was obtained via malloc(3) and can be reallocated or freed by sfio. SF_STATIC: The stream structure is not to be freed when closed (sfclose()). This is frequently used by applications that allocate their own stream structures (such an application must use the header file sfio_t.h). SF_IOCHECK: If the stream has a discipline exception function, this flag causes this function to be called in sfsync(), sfpurge() and before a call to read() or write() or their discipline equivalent.

This creates or renews a stream. It returns the new stream on success and NULL on error. f: If f is NULL, a new stream is created. Otherwise, it is taken to be an existing stream to be initialized or renewed. The initialization case is indicated by the bit SF_EOF in flags. In this case, the space provided by f simply gets used as if it was just allocated. If SF_EOF is not specified, f will be closed (sfclose()) before renewing. In this case, its buffer, pool and discipline stack will be preserved. Note that, except for sfstdin, sfstdout, sfstderr, and other SF_STATIC streams, it is an error to renew a stream already closed since its space may already been deallocated. buf and size: These determine a buffering scheme. See sfsetbuf() for more details. fd: If SF_STRING is specified, this is ignored. Otherwise, fd is a file descriptor (e.g., from open(2)) to use for I/O operations. flags: This is composed from SF_EOF and the bit values defined in the BIT FLAGS section. This is a high-level function based on sfnew() to create new streams from files or strings. It returns the new stream on success and NULL on error. f: This is treated in the same way as sfnew() treats its counterpart. mode: This can be composed from the set of letters {r, w, +, s, a}. r and w specify read and write mode. + means that the new stream will be opened for both reading and writing. s specifies that string is a null-terminated string to be opened for string manipulation. In this case, string can be NULL and is equivalent to the empty string. Specifying s alone is equivalent to specifying sr. If s is not specified, string defines a path name to a file. a specifies the file to be opened in append mode. This means that data is always written at the end of the file. This function executes a command and sets up pipes so that data can be communicated between the application and the resulting child process. If the stream is open for read/write, then the standard input/output (file descriptor 0/1) of the child is connected to the application. If the child process was opened for both read and write, there will be two different file descriptors associated with the stream. Therefore, beware that sffileno() may return different values depending on the type of the last I/O operation done (see also sfset()). If sfpopen() opens a stream for writing and the signal handler for SIGPIPE is SIG_DFL (default handling), it will be set to SIG_IGN. This protects the application from getting killed on attempts to write to a broken pipe. sfpopen() returns the new stream or NULL on error. f: The stream to be renewed (see sfnew()). cmd: The command to be executed. mode: This should be composed from r, w and +. This function creates a stream for writing and reading temporary data. If size is negative, the stream is a pure SF_STRING stream. If size is zero, the stream is a pure file stream. Otherwise, the stream is first created as a SF_STRING stream with a buffer of length size. A discipline is set so that when this buffer is exhausted, a real temporary file is created. The temporary file is also created on any attempt to modify this discipline, e.g., stacking a new discipline on top of it. sftmp() returns the new stream or NULL on error.

A normal file stream makes use of the system calls read(), write and lseek() to read, write and position in the underlying file. Certain applications may implement alternative IO methods or may further process the data before handling it to the application or before writing out to the file. This is done by defining an IO discipline. The functions in this section are used to manipulate disciplines. This function either pushes a new discipline or pops the top discipline. The stream f is synchronized before anything is done to the discipline stack. After the discipline stack has been successfully manipulated, the current seek location (see sftell()) and stream extent (see sfsize()) may be updated to reflect that defined by the top level discipline. If disc is SF_POPDISC or (Sfdisc_t*)0, the top element of the stack, if any, is popped and its address is returned. Otherwise, disc is a new discipline to be pushed onto the discipline stack. Note that a discipline can be used only on one stream at a time. An application should take care to allocate different discipline structures for use with multiple streams.

A discipline structure is of the type Sfdisc_t. It defines alternative functions to read, write, seek, and handle exceptions. Note that each file stream starts with the unremovable discipline consisting of the system calls read(), write(), and lseek(). Sfdisc_t contains the following public fields:
      Sfread_f   readf;
      Sfwrite_f  writef;
      Sfseek_f   seekf;
      Sfexcept_f exceptf;

The first three fields of Sfdisc_t specify the alternative I/O functions. If any of them is NULL, it is inherited from a discipline pushed earlier on the stack. The arguments to the I/O discipline functions have the same meaning as that of the functions sfrd(), sfwr() and sfsk() to be described later.

The exception function, (*exceptf)(), if supplied is used to announce to the application certain IO events. It is called as (*exceptf)(Sfio_t* f, int type, Sfdisc_t* disc). Unless noted otherwise, the return value of (*exceptf)() is used as follows: <0: The calling function terminates with an appropriate error value. >0: If the event was raised due to an IO error, the error has been repaired and the calling function shall continue normally. =0: The exception handler does not handle the raised event and the calling function should perform whatever default action as appropriate. For example, if the stream is on top of a stack (see sfstack()) and the IO event was a read or write error or end-of-file, then the stream will be popped and closed.

Following are the available values for the argument type of (*exceptf)(): SF_READ: SF_WRITE: These events are always raised after a read or write operation fails. In this case, sfslen() can be used to get the return value of the failed operation (which should be nonpositive). SF_READ and SF_WRITE are also raised immediately before a read or write operation if the flag SF_IOCHECK is on. In this case, sfslen() returns the amount of data to be processed by the respective IO operation. In addition, the sign of the return value of (*exceptf)() has the following meaning. If it is negative, the stream is not ready for IO and the calling sfio operation will abort with failure. If it is positive, the stream is ready for IO but the amount should be restricted to the amount specified. Finally, if (*exceptf)() returns zero, the IO operation can be carried out normally. SF_SEEK: This event is raised when a seek operation fails. SF_NEW: This event is raised when the stream is about to be closed to be reused (see sfnew()). SF_CLOSE: This event is raised when the stream is being closed (see sfclose()). SF_DPUSH: This event is raised when the discipline is about to be pushed down by a new discipline. SF_DPOP: This event is raised when the discipline is about to be popped from the discipline stack. SF_DPOLL: This event is raised by sfpoll() to check to see if the next I/O operation may block. In this case, a negative return value means the stream will block, a positive return value means the stream will not block. A zero return value means that the file descriptor should be treated with default actions by sfpoll(). SF_DBUFFER: Before sfdisc() pushes or pops a discipline, the buffer must be synchronized so that all write data is flushed and there is no buffered read data. If this is not possible, SF_DBUFFER will be raised. In the case of pushing a new discipline, the exception function is called as (*exceptf)(f,SF_DBUFFER,(Sfdisc_t*)0) and in the case of popping, it is called as (*exceptf)(f,SF_DBUFFER,disc) where disc is the discipline being popped. If the return value of exceptf is >0, the push or pop operation will be allowed to go through. Otherwise, sfdisc() returns failure. SF_SYNC: SF_PURGE: These events are enabled only if the flag SF_IOCHECK is set. They are raised when the application calls sfsync() or sfpurge() on a stream f whose top discipline has an exception function. In either case, the respective event is raised only after the IO operation for synchronizing or purging the stream has been successfully completed. Note that sfsync() is implicit in a closing operation for a write stream or a share read stream so SF_SYNC is also raised when such a stream is closed. Finally, although sfsync((Sfio_t*)0) will synchronize all streams, it does not cause the SF_SYNC event to be raised on any of the streams. The discipline stack can be viewed as a set of filters to process incoming data. These functions provides a safe method for a discipline I/O function to invoke earlier discipline I/O functions and to properly handle exceptions. It is not allowed to use them in any other context. sfrd() and sfwr() return the number of bytes read or written. sfsk() returns the new seek location. On error, all three functions return a negative value which should be -1 or the value returned by the exception handler.

This function closes the stream f and possibly frees up its resources. If f is a stack of streams, all streams on the stack are popped and closed. If f is a sfpopen-stream, sfclose() waits until the associated command terminates and returns its exit status. SF_READ streams with the SF_SHARE flag turned on and SF_WRITE streams are synchronized before closing (see sfsync()). If the stream f has disciplines, their exception handlers will be called with the type argument being one of SF_CLOSE or SF_NEW (see sfdisc()). The latter, SF_NEW is used when a stream is being closed via sfnew() so that it can be reused. If such an exception handler returns a negative value, sfclose() will immediately return this value without further processing of the stream. If the exception handler returns a positive value, sfclose() will immediately return a zero value. Normally, all space associated with a stream is deallocated upon closing. However, the stream space will be preserved if the SF_STATIC flag is set. sfclose() returns -1 for failure and 0 for success. This function synchronizes the physical file pointer of f and the logical position in the stream. For a write stream, this means to write out any buffered data. For a seekable read stream, the physical file pointer is moved to align with the stream pointer and, if the flag SF_SHARE is on, at the next input operation, the buffer will be refreshed with data from the underlying file. If f is the base of a stack of streams, all streams on the stack are synchronized. Note that a stacked stream can only be synchronized via its base stream. If f is in a pool (see sfpool()) but not at the head of the pool, the head of the pool is synchronized. sfsync() returns a negative value for failure and 0 for success. This function throws away all pending data in the buffer of f unless f is a SF_STRING stream. Note that if f is an input stream corresponding to an unseekable device such as a pipe or a terminal, the purged data will not be recoverable. If f is a sfpopen-stream opened for both read and write, data of both the read and write pipe ends will be purged. See also sfset() to selectively turn off read or write mode if one set of data is to be preserved. sfpurge() returns -1 for failure and 0 for success. Occasionally, it is desirable to coordinate I/O from different streams. This can be done by pooling streams. In a pool, only one stream is current and can have buffered data. All other streams in the pool will be synchronized. A stream becomes current when it is used for some I/O operation. On success, sfpool() returns a value as defined below. On failure, it returns NULL. f and poolf: If f is NULL, sfpool() returns the head stream in the pool identified by poolf. Otherwise, if poolf is NULL, f is deleted from its current pool. In this case, sfpool() returns some stream from the pool. Note also that some other stream from the same pool will become current. If this is not possible, sfpool() will return error. If both f and poolf are not NULL, f is deleted from its current pool if necessary and put into the same pool with poolf. In this case, poolf is returned. mode: If poolf is already in a pool, mode is ignored. Otherwise, mode should be 0 or SF_SHARE. A SF_SHARE pool must contain all streams capable of being in SF_WRITE mode. In this case, when a new stream is to become current, its currently buffered data will be moved to the buffer of the new stream if there is room. This function changes or inquires about the buffer of the stream f. Except for buffer inquiry (see the case size == 0), the stream will be synchronized before any modification of the buffer. Upon completion, if a new buffer is successfully set and the old buffer has not been deallocated, sfsetbuf() returns the address of the old buffer. Otherwise, it returns NULL. After a call to sfsetbuf(), sfslen() can be used to get the size of the buffer whose address was returned. size < 0: sfio will pick a buffer size suitable for the stream. If buf is NULL, sfio will also pick a suitable buffering scheme. For example, on systems with memory mapping, a memory map may be used to speed up I/O. If buf is not NULL, its actual value is ignored but it signifies that the buffer should be allocated (malloc(3)). This is often used to avoid memory mapping on file streams whose underlying files may be truncated during processing. Such file truncation events can cause memory fault errors if occurred after a memory map has been established (see mmap(2) for details.) size > 0: This is the suggested size to use for buffering or memory mapping. If buf is NULL, sfio will pick a suitable buffering scheme as discussed above. If buf is not NULL, then buf and size determine a buffer of the given size. size == 0: If buf is NULL, the stream will be unbuffered. If buf is not NULL, its actual value is ignored but it tells sfsetbuf() to return the address of the stream buffer. In this case, no attempt will be made to synchronize the buffer.

In certain applications, it is useful to be able to process related streams in a recursive manner. For example, when a C preprocessor processes a #include file, it is convenient to be able to treat the content of such a file as if it is part of the base stream. This is done by using sfstack() to stack streams. Every stream stack is identified by the base stream via which all I/O operations are performed. However, an I/O operation always takes effect on the top stream. If an I/O operation is performed and the top stream reaches the end of file condition or an error condition other than interrupts, it is automatically popped and closed (see also sfdisc() for alternative handling of these conditions). base: This is the base stream of the stack. top: If this is SF_POPSTACK or (Sfio_t*)0, the stack is popped and sfstack() returns the popped stream. Otherwise, top is the pointer to a stream to be pushed on top of the stack. In this case, the base stream is returned. This function swaps the content of the streams f1 and f2 so that each will function as the other. This will fail if either stream is in a stream stack but not being the base stream. If f2 is NULL, it will be allocated. sfswap() returns f2 or its allocated version on success and NULL on failure. This function reserves a data block. For a read stream, a data block is a segment of data and for a write stream, it is a buffer suitable for writing. For consistency, a stream opened for both read and write will be treated as if it is a read stream. On systems where it is possible to peek at incoming data from an unseekable device (recv(2) or streamio(4)), sfreserve() will use this feature if the SF_SHARE bit is on and lock is not zero. This allows applications that share a common unseekable file descriptor to peek at incoming data without actually consuming them. sfreserve() returns the address of data block on success and NULL on failure. After a sfreserve() call, whether or not it succeeds, sfslen() can be used to obtain the maximal size of the (would-be) available data block. n < 0: The stream will be filled or flushed as necessary to ensure that a data block of positive size is available. If this is successful and lock is zero, the stream I/O position will advance by the size of the available data block. For example, upon success, sfreserve(f,-1,0) returns the pointer to a positive size data block in the stream buffer and advances the I/O position by the size of that block. n == 0: If lock is zero, the stream will be filled or flushed as necessary to ensure that a positive-size data block is available. If lock is non-zero, no fill or flush will be performed. In either case, the I/O position will not be advanced. However, if lock is non-zero, the stream will be locked from further access. Therefore, an application can use sfreserve(f,0,1) to lock a stream. n > 0: An attempt is made to reserve a data block of size at least n. If this succeeds and lock is zero, the stream I/O position will advance by n. lock: If the reservation is successful and lock is non-zero, the stream I/O position will remain the same and the stream will be locked. A locked stream remains inaccessible until the application calls sfread/sfwrite(f,data_block,size) to unlock. sfread() should be used on read-only stream and sfwrite() should be used on write-only stream. A stream in both read and write modes can release the lock with either call. data_block is the address of the data block returned by sfreserve(). This function is obsolete and will be deleted in a future release. It uses sfreserve() to secure a data block confined to the internal buffer of a stream f. bufp: If this is NULL, only the size of the available data block is returned. This is allowed even if the stream is locked. If this is not NULL, the available data buffer is returned in *bufp. n: If n > 0, sfpeek() returns the size of the maximum available data block and locks the stream from further access. The lock can be released in a similar fashion to sfreserve(). If n < 0, sfpeek() advances the stream I/O location by whichever smaller of n or the available data block and returns the amount advanced. The stream remains unlocked. Finally, ifn == 0 and bufp is not NULL, *bufp is set to the address of the available data block and sfpeek() returns 0. This is done even if the stream is locked. 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. sfpoll() returns the number of ready streams or -1 on failure. fa and n: These define an array of n streams to be polled. This array will be modified in place by sfpoll() so that the ready streams will come first. timeout: This defines the length of time in milliseconds for sfpoll() to wait to see if any stream is ready for I/O. If timeout is negative, sfpoll() will block as long as necessary. Note that SF_STRING and regular file streams can never block so they are always ready for I/O. If a stream with discipline is polled and its readiness is as yet undetermined (no data in the buffer), the discipline exception function will be called with SF_DPOLL before normal actions are taken. See also sfdisc(). This function sets a new I/O position for the stream f. If the stream is a SF_STRING stream and the new address is beyond the current buffer extent, an SF_SEEK exception will be raised (see sfdisc()). The discipline exception handler, if any, can call sfslen() to find out the total buffer size needed. sfseek() returns the new address or -1 on failure. The new address is determined based on addr and the following values for offset: 0 or SEEK_SET: addr is the desired address. 1 or SEEK_CUR: addr is a relative offset from the current location. Note that if f was opened for appending (SF_APPEND) and the last operation done on it was a write operation, the current location is at the physical end of file. 2 or SEEK_END: addr is offset from the physical end of the stream. This function returns the current location in the stream f. As with sfseek(), if f was opened for appending (SF_APPEND) and the last operation done on it was a write operation, the current location is at the physical end of file. If the stream f is unseekable, sftell returns the number of bytes read from or written to f. See also sfungetc(). This function changes the file descriptor of the file stream f. If the change is deemed possible, sfsetfd() will call (*notify)(f,SF_SETFD,newfd) (see sfnotify()) before switching the file descriptor of f to the new file descript newfd. sfsetfd() returns -1 on failure and the new file descriptor on success. fd >= 0: If the current file descriptor is also non-negative, it will be changed using dup(3) to a value larger or equal to fd. Upon a successful change, the previous file descriptor will be closed. If the current file descriptor is negative, it will be set to fd and the stream will be reinitialized. fd < 0: The stream is synchronized (see sfsync()) and its file descriptor will be set to this value. Then, except for sfclose(), the stream will be inaccessible to all other IO operations until a future sfsetfd() call resets the file descriptor to a non-negative value. Thus, if the file descriptor of a stream is to be preserved upon closing, sfsetfd() should be used to set the stream's file descriptor to a negative value first. This function sets or inquires flags for the stream f. It returns the previous set of flags or 0 if the stream is currently unaccessible. flags defines a collection of flags to be turned on or off based on whether set is non-zero or zero. For example, the current set of flags can be obtained via sfset(f,0,0). The flags that can be set are: SF_READ, SF_WRITE, SF_LINE, SF_SHARE, SF_MALLOC and SF_STATIC. Note that SF_READ and SF_WRITE can be set only if the stream was opened as SF_READ|SF_WRITE. Turning off one of them means that the stream is to be treated as if it was opened with the other flag exclusively. It is not possible to turn off both. An attempt to turn on either SF_READ or SF_WRITE also cause the stream to set its current I/O mode to the given type. This is useful when an application needs to make sure that a stream opened for both read and write be in a specific I/O mode.

This function returns a byte from the stream f or -1 on end-of-file or error. This function pushes the byte c back into the stream f and makes it available on the next read. For efficiency, if c matches the byte immediately before the current byte in the buffer, the buffer pointer is simply backed up (note the effect on sftell() and sfseek()). There is no theoretical limit on the number of bytes that can be pushed back into a stream. Pushed back bytes that were not part of the buffer as noted will be discarded on any operation that require buffer synchronization, e.g., sftell(), sfseek(), and sfsync(). sfungetc() returns c on success and -1 on failure. These functions return respectively unsigned long, long, and double values coded by sfputu(), sfputl(), and sfputd(). On error, they return -1. This function reads a record of data and returns the data in some buffer defined by sfio. This buffer is constructed on a per stream basis so that calls to sfgetr() on different streams will result in different buffer areas. On error, sfgetr() returns NULL. In this case, there may be an incomplete record. Such a record can be obtained by calling sfgetr() again with a negative value for string. After a successful call to sfgetr(), the record length including the record separator (or its 0 replacement) can be retrieved with sfslen(). f: This is the stream to read a record from. rsc: This is the record separator. Note that reaching end-of-file without finding the record separator is an error and sfgetr() will return NULL. string: If string is positive, a 0 byte will replace the record separator to make it a C string. If string is 0, the record separator will be left intact so the returned buffer may or may not be a null-terminated string. After sfgetr() returns NULL due to an error, a negative value for string can be used to retrieve any incomplete record. This function reads up to n bytes from the stream f and stores them in the given buffer buf. It returns the number of bytes actually read or -1 on error. These functions scan data items from an input source. sfscanf() scans from the input stream f while sfsscanf() scans from the input string s. sfvscanf() is the underlying primitive for the other functions based on a variable argument format. The item types are determined from the string format. These functions return the number of items successfully scanned or -1 on error.

The predefined scanning formats are the same as that of fscanf(3) (UNIX User's Manual). They are: i, d, u, o, x, p, n, f, F, e, E, g, G, c, %, s, and []. A few modifications described below extend the usage of these formats.

Unlike fscanf(3) where the \n character in the format string is treated as any white space character, if the input stream is in SF_LINE mode (see sfset()) the \n character matches white spaces but stops after matching the first input \n. This avoids unnecessary blocking when scanning values from terminals or other unseekable devices with patterns such as ``%s\n''. Note that other white space characters in the format strings, i.e. blanks or tabs, still match any input white space characters including \n.

The i and d format are extended to scan numbers with general bases from 2 to 64. The d format can be specified as %[*][width][.base]d where base is the base for number to be scanned. For the i format, numbers with general bases are assumed to be of the form: base#integer. Here base must be a number in base 10 and integer is a number in the given base. If base is 36 or less, the digits for integer can be any combination of [0-9], [a-z], [A-Z] where upper and lower case digits are not distinguishable. If base is larger than 36, the set of digits is:
    0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@_

To avoid overwriting array boundary, the formats c, s, and [] can be modified with the qualifier l. Then, immediately after specifying a buffer, the buffer size must also be given. Note that the buffer size does not affect the scan length. Rather, only the appropriate amount of scanned data will be copied, the rest is discarded.

Scanning is extended with the below formats: %& This pattern indicates that the next argument in the argument list of sfscanf() is a function, (*extf)(), to process patterns that are not predefined. The prototype of (*extf)() is:
      int (*extf)(Sfio_t* f, int fmt, int length, char** rv);
f is the same input stream passed to sfvscanf. fmt is the pattern to be processed. length, if non-negative, is the maximum number of input bytes to be read in processing the pattern, rv is used to return the address of the value to be assigned. (*extf)() should return the size of the value to be assigned. A negative return value from (*extf)() means that the specified pattern fmt cannot be handled and it will be treated as unmatched. %@ This pattern indicates that the next argument in the argument list args is a function, (*argf)(), to process the values of matched patterns. The prototype of (*argf)() is:
      int (*argf)(int fmt, char* value, int n);
If the return value of (*argf)() is negative, the processing of the current format string will be stopped (see %: below). fmt determines the type of value: f for float, F for double, h for short, d for int, D for long, s for char*. Any other value for fmt means that it is an extended pattern and value contains an address to the scanned value. n contains the size of the object if it is a primitive type. If the object is char* or the address of the scanned value of an extended format, n is the length of this object. %: This pattern indicates that the next two arguments in the argument list args define a new pair of format string and a list of arguments of the type va_list* (see varargs.h or stdarg.h). The new pair is pushed on top of the stack and the scanning process continues with them. The top pair of format string and argument list is popped when the processing of the format string is stopped. When a new pair is stacked, (*argf)() and (*extf)() are inherited. They are reset when the stack is popped.

This function writes the byte c to the stream f. It returns c on success and -1 on error. This function tries to write n bytes of c to f. It returns the number of bytes written or -1 on failure. These functions write respectively the unsigned long, long, or double value v in a minimal, portable and variable length format. Portability across two different machines requires that the bit order in a byte is the same on both machines and the size of the respective primitive type on the output machine is less than or equal to that on the input machine. sfputd() relies on the functions ldexp() and frexp() (See frexp.3) for coding. sfputu(), sfputl() and sfputd() return the number of bytes output or -1 on error. See also sfulen(), sfllen() and sfdlen(). This function writes the null-terminated string s to the stream f. If rsc is non-negative, it is a character to be appended after the string has been output. sfputr() returns the number of bytes written or -1 on failure. This function writes out n bytes from the buffer buf to the stream f. If the stream is SF_STRING, and the buffer extent is not large enough, an SF_WRITE exception is raised. The discipline exception handler, if any, can call sfslen() to find out the total buffer size needed for existing as well as data remained to be written. sfwrite() returns the number of bytes written or -1 on failure. This function moves objects from the input stream fr to the output stream fw. An object is either a byte or a record. The latter is indicated by the record separator rsc if it is non-negative. If n is non-negative, it indicates the number of objects to move; otherwise, all of fr will be moved. If either fr or fw is NULL, it acts as if it is a stream corresponding to /dev/null. sfmove() returns the number of objects moved or -1 on failure. These function write data in the given format to some output source. sfprintf() and sfvprintf() write to the output stream f. sfsprintf() writes to the buffer s of size n. sfprints() constructs the output in some statically allocated area and returns the address of this area upon completion. Except for sfprints() which returns a null-terminated string, the other functions return the number of bytes output or -1 on error. The length of the string constructed by either sfprints() or sfsprintf() can be retrieved using sfslen().

The predefined formats are that of fprintf(3) (UNIX User's Manual): n, s, c, %, h, i, d, p, u, o, x, X, g, G, e, E, f, and F. A pattern specification is of the form: %[flag][width].[precision].[base][(type)]z where z stands for the format type. flag: The flag characters are -, +, space, #, and 0. width: This defines the width of the printing field. If the value to be printed has less characters than the field width it may be padded. precis: This defines the precision for the conversion. base: This defines the base of conversion for the formats i, d, and u. The range for base is from 2 to 64. If base is not defined or not within the defined range, it is taken to be 10. The digits to represent numbers are:
01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@_
Note that the flag # will cause the output to be int the format base#number. For example, the number 10 printed with %..2d is 1010 while it will be 2#1010 if printed with %#..2d. (type): The characters between the parentheses define some type information that will be passed on the extension functions defined by the format %@ and %& below. The right parenthesis can be escaped as %).

In addition to the standard formats, there are three extended formats: %&, %@, and %:. They are described below.

The %& pattern indicates that the next argument is a function, (*extf)(), to interpret patterns not yet defined by sfprintf(). The prototype of (*extf)() is:
int (*extf)(void* value,int fmt,int precis,char** sp,int base,char* type,int n);
value: This is the value to be formatted. fmt: This is the pattern to format the value. precis: This is the amount of precision required. sp: This is used to return the address of a string containing the formatted value. If upon returning from (*extf)(), *sp is NULL, the pattern fmt is treated as if it is not matched. Otherwise, the return value of (*extf)(), if nonnegative, is taken as the length of the string returned in sp. If not, the string is considered null-terminated. The string *sp will be processed as if the pattern `s' was specified. base: This is the base to format the value. It should be a value between 2 and 64. type, n: type is the string between parentheses that was specified before the format fmt. n is the length of this string.

The %@ pattern indicates that the next argument is a function, (*argf)(), to get arguments. As long as (*argf)() is defined, the argument list is ignored. The return value of (*argf)(), if negative, stops the processing of the current format (see also %:). The prototype of (*argf)() is:
int (*argf)(int fmt, char* val, char* type, int n);
fmt: This defines the pattern to be processed. In addition to the below patterns, if (*extf)() is defined, and an undefined pattern is encountered, (*argf)() will be called with this pattern.
   @ to get a new (*argf)(),
   & to get a new (*extf)(),
   1 to get a new format string for stacking,
   2 to get a new argument list for stacking,
   c to get a char,
   h to get a short,
   d to get an int,
   D to get a long,
   n to get a int*,
   N to get a long*,
   f to get a float,
   F to get a double, and
   s to get a char*.
val: This is used to return the requested value. type, n: type is the string between parentheses that was specified before the format fmt. n is the length of this string.

The %: pattern indicates that the next two arguments define a pair of format string and argument list of the type va_list*. If the argument getting function (*argf)() is already defined, it is called with the argument fmt being the characters 1 and 2 for the new format string and argument list respectively. The new pair is stacked on top and processing continue from there. The top pair of format string and argument is popped when the format string is exhausted. When a new pair is pushed, (*argf)() and (*extf)() are inherited. When a pair is popped, these functions will be reset.

This sets a function (*notify)() which is used to notify the applications of various stream events. (*notify)(f,type,file) is called with three arguments. The first and third arguments are the affected stream f and a file descriptor. The second argument type is one of the following values: SF_NEW: The stream is being opened. SF_CLOSE: The stream is being closed. SF_SETFD: The file descriptor of the stream is being changed to the value in the third argument (see sfsetfd()). SF_READ: The stream fails trying to change to read mode. SF_WRITE: The stream fails trying to change to write mode. This function returns the size of the stream f (see sfnew()). If the stream is not seekable or if the size is not determinable, sfsize() returns -1. This function returns the file descriptor of the stream f. This function returns 1 or 0 depending on whether or not the stream f has been stacked. sfeof() tells whether or not the previous I/O operation generated and end-of-file condition. sferror() tells whether or not the previous I/O operation generated an error. sfclrerr() clears the end-of-file and error conditions. Note that even without using sfclrerr(), the end-of-file and error conditions are also cleared on any successful I/O operation. This function clears any lock on a locked stream. Though this is unsafe, it is useful for emergency access to a stream. It returns the current set of flags.

Most applications based on the sfio library only need to include the header file sfio.h. However, there are circumstances (e.g., debugging) where an application may require more details about the full Sfio_t structure. In such cases, the header file sfio_t.h can be used instead of sfio.h. Note that applications based on features defined in sfio_t.h may become invalid whenever the internal architecture of sfio changes. This macro function is defined only in sfio_t.h. It is used to statically initialize a full Sfio_t structure. It requires five arguments: buf, size: These define a buffer for I/O and the size of that buffer. file: This defines the file descriptor associated with the stream. flags: This defines the type of the stream. It is composed from the bit flags as described above. disc: This defines a discipline associated with the stream (sfdisc()).

This function returns the length of the string most recently obtained via sfreserve(), sfsetbuf(), sfgetr(), sfsprintf(), sfprints(), sfecvt() or sffcvt(). It can also be used during an exception handler to find certain state information about the I/O operation (see sfdisc()). These functions return respectively the number of bytes required to code the unsigned long, long or double value v by sfputu(), sfputl() or sfputd(). This function acts directly on the file descriptor fd. 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 0 means that the end-of-file condition has been detected. A negative value represents an error. buf, n: These define a buffer and its size to read data into. rsc: If >=0, this defines a record separator. Depending on action, sfpkrd() may read data only up to this record separator. tm: If >=0, this defines a time interval in milliseconds to wait for incoming data. action: If action > 0, sfpkrd() will peek on incoming data but will not read past it. Therefore, a future sfpkrd() or read(2) will see the same data again. If action == 0, sfpkrd() will not peek. In addition if rsc >= 0, sfpkrd() guarantees that only a record of data up to the first appearance of rsc is read. Finally, if action < 0, on systems with peeking ability, sfpkrd() guarantees that only a record of data up to the first appearance of rsc is read. Otherwise, it will do a single read(fd,buf,n) to fill the buffer. These functions convert floating point values to ASCII. They correspond to the standard C library functions ecvt(3) and fcvt(3). The length of the conversion string most recently done by sfecvt() or sffcvt() can be found by sfslen().

AUTHORS
(Kiem-)Phong Vo (kpv@ulysses.att.com) and David G. Korn (dgk@ulysses.att.com).