| SFIO(3) | C LIBRARY FUNCTIONS | SFIO(3) |
|---|
#include <sfio.h>libsfio.a -lsfio libstdio.a -lstdio libsfio-mt.a -lsfio-mt libstdio-mt.a -lstdio-mt
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
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
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);
int sfmutex(Sfio_t* f, int type);SFMTX_LOCK SFMTX_TRYLOCK SFMTX_UNLOCK SFMTX_CLRLOCK
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);
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);
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);
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
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);
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);
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);
#include <sfio_t.h> #define SFNEW(buf,size,file,flags,disc)
#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);
#include <stdio.h> cc ... -lstdio -lsfio cc ... -lstdio-mt -lsfio-mt
Sfio provides I/O functions to manage buffered streams. Each Sfio stream is a file stream, representing a file (see open(2)), or a string stream, 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 sfprintf()/sfscanf() functions to define their own conversion patterns as well as redefine existing ones.
A discipline defines analogues of the system calls read(2), write(2) and lseek(2). Such system calls or their discipline replacements are used to process stream data. Henceforth, ``system call'' will refer to either a system call or its discipline replacement.
A system call is said to cause an exception if its return value is non-positive. Unless overridden by exception handlers (see sfdisc()), an interrupted system call (errno == EINTR on UNIX systems) will be automatically reinvoked to continue the ongoing operation.
The buffer of a stream is typically a memory segment allocated via malloc(3) or supplied by the application. File streams may also use memory mapping (mmap(2)) 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 sfsetbuf().
There are three standard streams: sfstdin for input (file descriptor 0 on UNIX systems), sfstdout for normal output (file descriptor 1), and sfstderr for error output (file descriptor 2).
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 libsfio.a while a program that uses multiple threads should link with libsfio-mt.a. The libraries libstdio.a and libstdio-mt.a provide corresponding Stdio functions to link with code already compiled using the native header stdio.h instead of the one provided by Sfio.
For a seekable file stream, SF_SHARE 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 SF_PUBLIC is not set, the physical file position is made equal to the logical stream position. If SF_PUBLIC 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, SF_SHARE means that the block and record I/O operations (sfread(), sfwrite(), sfmove(), sfgetr(), sfputr(), sfreserve(), sfscanf() and sfvprintf()) 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 recv(2) or streamio(4).
A standard stream that is seekable will be initialized with SF_SHARE|SF_PUBLIC.
If string is NULL, f is a file stream and mode does not imply a string stream, sfopen() changes the modes of f according to mode. In this case, sfopen() returns f on success and NULL on error. This somewhat unusual usage of sfopen() is good for resetting certain predefined modes in standard streams including text/binary and append that are inherited from some parent process. Note also that SF_READ and SF_WRITE can only be reset if the stream is not yet initialized.
sfopen() is normally used to create a new stream or renew a stream. In this case, it returns the new stream on success and NULL on error. Below are the meanings of the arguments:
s specifies opening a string stream. string can be a null-terminated string or NULL. Specifying s alone is equivalent to specifying sr. If s is not specified, string defines a file name.
r and w specify read and write modes. Write mode creates and/or truncates the given file to make an empty file. The + modifier indicates that the stream is opened for both read and write.
a specifies append mode, i.e., data is always output at end of file.
b and t specify binary and text modes.
x specifies exclusive mode, i.e., a file opened for writing should not already exist.
m specifies that the stream needs to be protected from simultaneous accesses by multiple threads. This turns on the bit flag SF_MTSAFE.
u specifies that the stream is guaranteed to be accessed by only one thread at a time. The bit flag SF_MTSAFE is left off. The absence of option m is the same as the presence of option u.
The standard input/output of cmd 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 sffileno()).
On opening a coprocess for writing (i.e., mode contains w or +), the signal handler for SIGPIPE in the parent application will be set to SIG_IGN if it is SIG_DFL 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 sfdisc()).
The command cmd is executed by an interpreter which is either /bin/sh or an executable command defined by the environment variable SHELL. In either case, the interpreter is invoked with 2 arguments, respectively -c and the given command cmd. When the interpreter is /bin/sh or /bin/ksh, sfpopen() may execute the command cmd itself if there are no shell meta-characters in cmd.
A stream created by sftmp() can be completely or partially memory-resident. If size is SF_UNBOUND, the stream is a pure string stream. If size 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 size or on any attempt to change disciplines, a temporary file is created. Two environment variables, TMPPATH and TMPDIR, direct where temporary files are created. TMPPATH, if defined, specifies a colon-separated set of directories to be used in a round-robin fashion to create files. If TMPPATH is undefined, TMPDIR can be used to specify a single directory to create files. If neither of TMPPATH and TMPDIR are defined, /tmp is used.
SF_READ|SF_SHARE and SF_WRITE streams are synchronized before closing (see sfsync()). If f has disciplines, their exception handlers will be called twice. The first exception handler call has the type argument as one of SF_CLOSING or SF_NEW (see sfdisc().) The latter, SF_NEW is used when a stream is being closed via sfnew() so that it can be renewed. The second call uses type as SF_FINAL 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, sfclose() will immediately return this value. If the exception handler returns a positive value, sfclose() will immediately return a zero value.
The libraries libsfio.a and libstdio.a (providing binary compatibility to Stdio-based code) only support uni-threaded code. Multi-threaded applications should link with libsfio-mt.a and libstdio-mt.a. When this is done, certain platforms may require additional thread libraries for linkage. For example, Linux, Irix and Solaris require -lpthread while HPUX requires -lcma. 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 sfgetc() and sfputc() 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 sfstdin/sfstdout/sfstderr are in multi-threaded mode by default (however, see sfopen() for how this may be changed). Other Sfio streams are normally opened uni-threaded unless the flag SF_MTSAFE or the option m were specified. Stdio-based code can also make a Stdio stream uni-threaded by using the option u when opening a file.
Each stream has a lock count which starts at 0. 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 0. Each successful locking operation increases the lock count while each successful unlocking operation decreases it, thus, allowing nesting of matching lock/unlock operations.
The type argument of sfmutex() takes on the below values:
The type argument is composed of some subset of the below bit flags:
An object can be either a byte if the record separator argument rsc is negative or a record of rsc is non-negative. In the latter case, a record is incomplete if it does not end in rsc. Generally speaking, a stream can have at most one incomplete record. If n is negative, all complete objects of fr will be moved. Otherwise, n indicates the number of objects to move. If either fr or fw is NULL, it acts as if it is a stream corresponding to /dev/null, the UNIX device that has no read data and throws away any write data. For example, the call sfmove(f,(Sfio_t*)0,(Sfoff_t)(-1),'\n') counts the number of complete lines in 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 new position is determined based on offset and type which is composed from the bit flags:
If f is a SF_READ stream, the data block is a segment of input data. If f is a SF_WRITE stream, the data block is a buffer suitable for writing output data. For consistency, if f is opened with SF_READ|SF_WRITE, it will normally be treated as if it is a SF_READ stream (see sfset() for forcing a particular mode) but the returned buffer can also be written into (more below). However, it is possible to bias to SF_WRITE when the type argument is non-negative by adding the SF_WRITE bit type. In any case, a reserved data block is guaranteed to be valid only until a future access to the stream f.
When f is SF_READ, SF_SHARE and unseekable, sfreserve() 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 sfreserve() may fail on certain platforms that do not properly support peeking on unseekable file descriptors.
After a sfreserve() call, whether or not it succeeds, sfvalue(f) gives the size of the available data block. Any partially reserved data block after a failed sfreserve() call can be obtained in another sfreserve() call with the argument type being SF_LASTR. The second argument n to sfreserve() will be ignored in this case.
A sfreserve() call is successful if it can obtain a data block of size at least the absolute value of n. For a SF_READ atream, the argument n is treated as follows:
For a successful reservation, the argument type dictates treatment as follows:
rsrv = sfreserve(f, 10, 1);
for(i = 0; i < 10; ++i)
rsrv[i] = toupper(rsrv[i]);
sfwrite(f, rsrv, 10);
Data printing and scanning are done via the sfprintf() and sfscanf() family of functions. These functions are similar to their ANSI-C fprintf() and fscanf() 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 formatting pair of a format specification string, char* format (the usual second argument in the formatting functions), and an argument list, va_list args (the third argument in functions sfvprintf() and sfvscanf()). 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 ! discussed below.
The top environment of a stack, say fe, is automatically popped whenever its format string is completely processed. In this case, its event-handling function (if any) is called as (*eventf)(f,SF_FINAL,NIL(Void_t*),fe). The top environment can also be popped by giving an argument NULL to %! or by returning a negative value in an extension function. In these cases, the event-handling function is called as (*eventf)(f,SF_DPOP,form,fe) where form 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 Sffmt_t which contains the following elements:
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 */
The first four elements of Sffmt_t 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 Sffmt_t are set by the respective formatting function before it calls the extension function Sffmt_t.extf and, subsequently, can be modified by this function to redirect formatting or scanning. For example, consider a call from a sfprintf() function to process an unknown pattern %t (which we may take to mean ``time'') based on a formatting environment fe. fe->extf may reset fe->fmt to `d' upon returing to cause sfprintf() to process the value being formatted as an integer.
Below are the fields of Sffmt_t:
SFFMT_LEFT: Flag - in sfprintf().
SFFMT_SIGN: Flag + in sfprintf().
SFFMT_BLANK: Flag space in sfprintf().
SFFMT_ZERO: Flag 0 in sfprintf().
SFFMT_THOUSAND: Flag ' in sfprintf().
SFFMT_LONG: Flag l in sfprintf() and sfscanf().
SFFMT_LLONG: Flag ll in sfprintf() and sfscanf().
SFFMT_SHORT: Flag h in sfprintf() and sfscanf().
SFFMT_LDOUBLE: Flag L in sfprintf() and sfscanf().
SFFMT_IFLAG: flag I in sfprintf() and sfscanf().
SFFMT_JFLAG: flag j in sfprintf() and sfscanf().
SFFMT_CENTER: flag = in sfprintf() and sfscanf().
SFFMT_CHOP: flag - in precis in sfprintf() and sfscanf().
SFFMT_ALTER: Flag # in sfprintf() and sfscanf().
SFFMT_SKIP: Flag * in sfscanf().
SFFMT_ARGPOS: This indicates argument processing for pos$.
SFFMT_VALUE: This is set by fe->extf to indicate that it is returning a value to be formatted or the address of an object to be assigned.
In addition to the predefined formatting patterns and other application-defined patterns, fe->extf may be called with fe->fmt being one of `(' (left parenthesis), `.' (dot), and `I'.
The left parenthesis requests a string to be used as the extfdata string discussed below. In this case, upon returning, fe->extf should set the fe->size field to be the length of the string or a negative value to indicate a null-terminated string.
The `I' requests an integer to define the object size.
The dot requests an integer for width, precision, base, or a separator. In this case, the fe->size 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 *form will be one of these characters.
The return value rv of fe->extf directs further processing. There are two cases. When pos$ is present, a negative return value means to ignore fe in further argument processing while a non-negative return value is treated as the case rv == 0 below. When pos$ is not present, fe->extf is called per argument immediately before pattern processing and its return values are treated as below:
For sfprintf() functions, if fe->flags has the bit SFFMT_VALUE, fe->extf should have set *v to the value to be processed; otherwise, a value should be obtained from the argument list. Likewise, for sfscanf() functions, SFFMT_VALUE means that *v should have a suitable address; otherwise, an address to assign value should be obtained from the argument list.
When pos$ is present, if fe->extf changes fe->fmt, this pattern shall be used regardless of the pattern defined in the format string. On the other hand, if fe->fmt is unchanged by fe->extf, 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.
The length of string constructed by sfprints(), sfsprintf(), or sfvsprintf() can be retrieved by sfslen().
The standard patterns are: n, s, c, %, h, i, d, p, u, o, x, X, g, G, e, E, f and !. Except for ! which shall be described below, see the ANSI-C specification of fprintf(3) for details on the other patterns. Let z be some pattern type. A formatting pattern is defined as below:
%[pos$][flag][width][.precision[.base]][(extfdata)]z
Flag I defines the size or type of the object being formatted. There are two cases: (1) I by itself and (2) I 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 Sflong_t, Sfulong_t or Sfdouble_t). For conversion specifiers s and c, 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 s, this size defines the length of the string or strings being formatted (see the discussion of base 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:
Sflong_t, long, int, short
Sfulong_t, unsigned long, unsigned int, unsigned short
Sfdouble_t, double, float
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 int, unsigned int, and double as defined by the formatting pattern.
Below are a few examples of using the I flag. The first example prints an Sflong_t integer. This example is actually not portable and only works on platforms where sizeof(Sflong_t) 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 %n to assign the amount of data already output into a short integer n_output.
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);
Flags h, l, j and L are the ANSI-C conventions to select the types of input objects. For example, %hd indicates a short int, while %ld indicates a long int.
Flag hh addresses the byte value types, i.e., char and unsigned char.
Flags z, t and j address respectively the types size_t, ptrdiff_t and Sflong_t.
Flags ll and L address respectively the largest integer and floating value types, i.e., Sfulong_t, Sflong_t, and Sfdouble_t.
Flag - left-justifies data within the field (otherwise, right-justification).
Flag + means that a signed conversion will always begin with a plus or minus sign.
Flag space is ignored if + 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 0 means padding with zeros on the left.
Flag ' outputs thousands-separator used by the current locale. setlocale(3) should have been used to set the desired locale.
Flag = centers data within the field.
Flag # indicates an alternative format processing. For %o, the first digit is always a zero. For %x and %X, a non-zero result will have a prefix 0x or 0X. For %e, %E, %f, %g, and %G, the result always contains a decimal point. For %g and %G, trailing zeros will not be removed. For %d, %i and %u, the form is base#number where base is the conversion base and number is represented by digits for this base. For example, a base 2 conversion with %#..2d for 10 is 2#1010 instead of 1010 as printed with %..2d. Finally, for %c, 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 \\n while 255 is printed as \\377.
For %i, %d, and %u, base should be an integer value in the inclusive range [2,64] and defines a conversion base. If base is not in this range, it is defined to be 10. The digits to represent numbers are:
01234567890
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ @_
For %s and %c, base defines a separator. Then, for %s, the input argument is taken to be a NULL-terminated array of strings while, for %c, 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 base is output if it is a non-zero.
There are further restrictions on the syntax of %s and %c when a separator is defined. Below are the legitimate sequences for %s and %c after the second dot:
s c
*s *c
zs zc
In the first case, no separator is defined so base is set to zero. In the second case, base is obtained from the argument list. In the third case, the character z must be non-alphanumeric and base will be set to this character.
The below example shows both the call and the result of printing a NULL-terminated array of three strings apple, orange, and grape:
sfprintf(sfstdout,"|%8..:s|",list);
| apple: orange: grape|
A white space character (blank, tab, or new-line) in format normally matches a maximal sequence of input white space characters. However, if the input stream is in SF_LINE mode (see sfset()), 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.
The standard scan patterns are: i, d, u, o, x, X, p, n, f, e, E, g, G, c, %, s, [] and !. Except for ! which shall be described below, see the ANSI-C specification of fscanf(3) for details on other patterns. Let z be some pattern type. A formatting pattern is specified as below:
%[*][pos$][width][.width.base][(extfdata)][flag]z
sfsscanf("12345678","%.*.*d", 4, 10, &v);
Flag # is significant for pattern %i and %[. For %i, it means that the # symbol does not have its usual meaning in an input sequence base#value. For example, the scanning result of %#i on input 2#1001 is 2 and the next sfgetc() call will return #. For %[, if the next character in the input stream does not match the given scan set of characters, # causes a match to a null string instead of a failure.
Flag I defines the size or type of the object being formatted. There are two cases: (1) I by itself and (2) I 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 Sflong_t, Sfulong_t or Sfdouble_t). For string patterns such as %s, 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 %s or %[, 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:
Sflong_t, long, int, short
Sfulong_t, unsigned long, unsigned int, unsigned short
Sfdouble_t, double, float
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 int, unsigned int, and double as indicated by the formatting pattern.
Below are examples of using the I 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.
sfscanf(sfstdin,"%I64d", &int64_obj);
sfscanf(sfstdin,"%I*f", sizeof(float_obj), &float_obj);
sfscanf(sfstdin,"%I*s", 128, buffer);
Flags h, l, and L are the ANSI-C conventions for indicating the type of a scanned element. For example, %hd means scanning a short int. The flags ll and L mean respectively scanning an integer or a floating point value with largest size (i.e, Sflong_t or Sfdouble_t).
The %i, %d and %u patterns scan numbers in bases from 2 to 64. %i 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: base#value where base is a number in base 10 and value is a number in the given base. For example, 2#1001 is the binary representation of the decimal value 9. If base is 36 or less, the digits for value 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:
0123456789
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ @_
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 sfsetbuf(f, (Void_t*)f, size).
In invocations of sfsetbuf() other than the above case, the size argument is treated as follows:
For a SF_WRITE stream, synchronization means to write out any buffered data. For a seekable SF_READ file stream, the physical file position is aligned with the logical stream position and, if SF_SHARE is on, buffered data is discarded. If f is NULL, all streams are synchronized. If f is the base of a stream stack (see sfstack()), all stacked streams are synchronized. Note that a stacked stream can only be synchronized this way. If f is in a pool (see sfpool()) but not being the head, the pool head is synchronized.
If f has flag SF_IOCHECK, the SF_SYNC event is raised before and after synchronization. See sfdisc() for details.
A file stream uses the system calls read(2), write(2) and lseek(2) to read, write and position in the underlying file. Disciplines enable application-defined I/O methods including exception handling and data pre/post-processing.
If the value of disc is identical to the value of f, then the top discipline on the discipline stack is returned without any further action. An application can then use this feature of sfdisc() and the field disc (below) of the discipline structure to traverse the entire discipline stack of a stream f as follows:
for(disc = sfdisc(f, (Sfdisc_t*)f); disc; disc = disc->disc)
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 pushed onto the discipline stack. In this case, if successful, sfdisc() 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 Sfdisc_t which contains the following public fields:
Sfread_f readf;
Sfwrite_f writef;
Sfseek_f seekf;
Sfexcept_f exceptf;
Sfdisc_t* disc;
The first three fields of Sfdisc_t specify alternative I/O functions. If any of them is NULL, it is inherited from a discipline pushed earlier on the stack. Note that a file stream always has read(2), write(2), lseek(2) and NIL(Sfexcept_f) as the logical bottom discipline. Arguments to I/O discipline functions have the same meaning as that of the functions sfrd(), sfwr() and sfsk() described below.
The exception function, (*exceptf)() announces exceptional events during I/O operations. It is called as (*exceptf)(Sfio_t* f, int type, Void_t* value, Sfdisc_t* disc). Unless noted otherwise, the return value of (*exceptf)() is used as follows:
The argument type of (*exceptf)() identifies the particular exceptional event:
If SF_IOCHECK is on, SF_READ and SF_WRITE are raised immediately before read(2) and write(2) calls. In this case, *((ssize_t*)value) is the amount of data to be processed. The return value of (*exceptf)(), 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.
SF_READ and SF_WRITE are also raised on operation failures. In such a case, *((ssize_t*)value) is the return value from the failed operation.
A stream buffer is always synchronized before pushing or popping a discipline. If this synchronization fails, SF_DBUFFER will be raised with *((size_t*)value) being the amount of data still in the buffer. If the return value of exceptf is non-negative, the push or pop operation will continue normally; otherwise, sfdisc() returns failure.
If f is NULL, sfraise() iterates over all streams and raise events as described above. In this case, sfraise() returns 0 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.
Settable flags are: SF_READ, SF_WRITE, SF_IOCHECK, SF_LINE, SF_SHARE, SF_PUBLIC, SF_MALLOC and SF_STATIC. Note that SF_READ and SF_WRITE can be turned on or off only if the stream was opened as SF_READ|SF_WRITE. 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 SF_READ or SF_WRITE will cause the stream to be in the given I/O mode.
As an example, the call sfwalk(walkf, data, SF_READ) will iterate over all streams opened for reading. Similarly, sfwalk(walkf, data, SF_READ|SF_WRITE) iterates over all streams opened for both reading and writing. Lastly, sfwalk(walkf, data, 0) iterates over all streams.
The below functions create disciplines and insert them into the given streams f. These functions return 0 on success and -1 on failure.
Sfio provides compatibility functions for all various popular Stdio implementations at source and binary level. The source Stdio-compatibility interface provides the header file stdio.h 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 popen() 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 fopen() call can be used to create string streams in addition to file streams.
The standard streams stdin, stdout and stderr are mapped via #define to sfstdin, sfstdout and sfstderr. The latter are typically declared of the type Sfio_t*. Certain older Stdio applications require these to be declared as addresses of structures so that static initializations of the sort ``FILE* f = stdin;'' would work. Such applications should use the compile time flag SF_FILE_STRUCT to achieve the desired effect.
The binary Stdio-compatibility libraries, libstdio.a and libstdio-mt.a, provide complete implementations of Stdio functions suitable for linking applications already compiled with native header stdio.h. These functions are also slightly altered or extended as discussed above.
Below are the supported Stdio functions:
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);
A few exception types have been added. In particular, exception handlers shall be raised with SF_LOCKED on accessing a stream frozen either by an ongoing operation or a previous operation (e.g., sfgetr()). Before a process exits, the event SF_ATEXIT is raised for each open stream.
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 compress.
Various new stream and function flags have been added. For example, the third argument of sfgetr() is now a set of bit flags and not just a three-value object. However, the old semantics of this argument of sfgetr() is still supported.
The sfopen() 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.
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.
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.
The SF_BUFCONST flag was deleted. This is largely unused anyway.
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.
The functions sfgetm() and sfputm() were added to encode unsigned integer values with known ranges.
The flag SF_APPEND is identical to SF_APPENDWR. However it conflicts with a different token of the same name defined in the system header stat.h of BSDI Unix systems. On such systems, we shall not define SF_APPEND and this symbol may be removed in a future release.
Similarly, the exception SF_CLOSE is identical to SF_CLOSING. However it conflicts with a different token of the same name defined in the system header socket.h of AIX Unix systems. On such systems, we shall not define SF_CLOSE and this symbol may be removed in a future release.
The printing and scanning functions were extended to handle multibyte characters and to conform to the C99 standard.
The function sfpoll() was rehauled to make it useful for writing servers that must commnunicate with multiple streams without blocking.
The formatting pattern %c for sf*printf was extended to allow the flag # to print unprintable character values using the C convention. For example, %#c prints the octal value 012 as \\n.
| 01 June 2008 | November 08, 2011 |