SFIO(3)
π¦ SFIO(3) β safe/fast string/file input/output π¦
Okay so this is the SFIO(3) page from the Library Functions Manual, bestie β and it is literally the main character of buffered I/O. π
πΈ NAME πΈ
sfio β safe/fast string/file input/output
β¨ SYNOPSIS β¨
π LIBRARIES π
#include <sfio.h>
libsfio.a -lsfio
libstdio.a -lstdio
libsfio-mt.a -lsfio-mt
libstdio-mt.a -lstdio-mt
π§Έ DATA TYPES π§Έ
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
π© BIT FLAGS π©
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
πͺ OPENING/CLOSING STREAMS πͺ
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);
π§΅ THREAD SAFETY π§΅
int sfmutex(Sfio_t* f, int type);
SFMTX_LOCK
SFMTX_TRYLOCK
SFMTX_UNLOCK
SFMTX_CLRLOCK
π INPUT/OUTPUT OPERATIONS π
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);
π DATA FORMATTING π
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);
π«§ BUFFERING, SYNCHRONIZATION π«§
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);
πͺ DISCIPLINE, EVENT HANDLING πͺ
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
ποΈ STREAM CONTROL ποΈ
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);
π STREAM INFORMATION π
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);
π§ MISCELLANEOUS FUNCTIONS π§
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);
ποΈ FULL STRUCTURE SFIO_T ποΈ
#include <sfio_t.h>
#define SFNEW(buf,size,file,flags,disc)
π EXAMPLE DISCIPLINES π
#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);
π§© STDIO-COMPATIBILITY π§©
#include <stdio.h>
cc ... -lstdio -lsfio
cc ... -lstdio-mt -lsfio-mt
π DESCRIPTION π
Sfio gives you I/O functions that manage buffered streams, no cap. Each Sfio stream is either 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 serves up 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. Obsessed, honestly.
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. iykyk.
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. Slay, but make it transactional.
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. That is the rule, periodt. 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). The holy trinity, fr fr.
π LIBRARIES π
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.
π§Έ DATA TYPES π§Έ
Void_t*
This defines a type suitable to exchange data of unknown types between application and Sfio. Void_t is a macro defined as void for ANSI-C and C++ and char for other compilation environments.
Sfoff_t
This defines an integral type suitable to address the largest possible file extent.
Sfulong_t, Sflong_t, Sfdouble_t
These are respectively the largest unsigned integer, signed integer, and floating point value types on the local platform.
Sfio_t
This defines the type of a stream handle.
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*)
Sfdisc_t defines a stream discipline structure. Sfread_f, Sfwrite_f and Sfseek_f are the types of discipline functions to replace the system calls: read(2), write(2) and lseek(2). Sfexcept_f is the type of an event-handling function. See sfdisc() for more details.
Sffmt_t
int (*Sffmtext_f)(Sfio_t*, Void_t*, Sffmt_t*)
int (*Sffmtevent_f)(Sfio_t*, int, Void_t*, Sffmt_t*)
Sffmt_t defines a formatting environment that can be used to extend scanning and printing in the sfprint()/sfscanf() functions. Sffmtext_f and Sffmtevent_f define the types of extension functions definable in Sffmt_t. See Sffmt_t below for more details.
SFIO_VERSION
This is a macro value of type long int that defines the current version number of Sfio. For example, the Sfio2000's version number is 20000515L (which also indicates its latest version date: 05/15/2000).
π© BIT FLAGS π©
A number of bit flags control stream operations. They are set either at stream initialization or by calling sfset(). Following are the flags:
SF_STRINGβ the stream is memory-based.SF_READ,SF_WRITE,SF_APPENDWR(SF_APPEND) βSF_READmeans the stream is readable,SF_WRITEmeans it's writable.SF_APPENDWRis the βbestie, this is a file opened in append modeβ flag (thinkopen(2)andfcntl(2)), so data always goes out at the end of the file. On systems that don't directly support append mode, Sfio approximates this behavior withlseek(2)or its discipline replacement. no cap.SF_LINEβ line-oriented stream energy. For anSF_WRITEstream, buffered data gets flushed every time a new-line character,\n, is output. For anSF_READstream,SF_LINEonly matters during calls to functions in thesfscanf()family. It's set on initialization of any stream representing a terminal device. terminal = main character, obviously.SF_SHARE,SF_PUBLICβSF_SHAREmeans the underlying file descriptor is shared by independent entities, for example multiple processes.- For a seekable file stream,
SF_SHAREmeans the logical stream position and the physical file position will be made the same before a system call to perform physical I/O. There are different possibilities:- If
SF_PUBLICis not set, the physical file position is made equal to the logical stream position. - If
SF_PUBLICis 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.
- If
- For an unseekable stream (e.g., pipes or terminal devices), if possible,
SF_SHAREmeans the block and record I/O operations (sfread(),sfwrite(),sfmove(),sfgetr(),sfputr(),sfreserve(),sfscanf()andsfvprintf()) 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 asrecv(2)orstreamio(4). - A standard stream that is seekable will be initialized with
SF_SHARE|SF_PUBLIC.
- For a seekable file stream,
SF_MALLOCβ the stream buffer was obtained viamalloc(3)and can be reallocated or freed. slay.SF_STATICβ the stream structure should not be freed when closed (sfclose()). This flag is used by applications that allocate their own stream structures. Such applications must use the header filesfio_t.hinstead ofsfio.h.SF_IOCHECKβ if the stream has a discipline exception handler, exceptions will be raised insfsync(),sfpurge()or before a system callread(2)orwrite(2)(seesfdisc()).SF_WHOLEβ this flag guarantees that data written in any singlesfwrite()orsfputr()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 accommodate.SF_MTSAFEβ this flag indicates that the respective stream may be accessed by more than one thread. 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 (seesfopen(),sfpopen()andsfnew()). Certain fast macro functions such assfgetc()andsfputc()will no longer behave as macros. Thus, an application that requires such fast macro functions should leaveSF_MTSAFEoff and perform explicit locking withsfmutex().SF_IOINTRβ 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, that certain operating systems (e.g., BSD Unix systems) may automatically resume interrupted system calls outside the scope of the library. On such systems,SF_IOINTRwill be ineffective. π
π OPENING/CLOSING STREAMS π
πΈ sfnew() β new stream, who dis?
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 NULL on error. π
fβ iffisNULL, a new stream is created. Otherwise,fis reused. In this case, ifflagsdoes not haveSF_EOF,fshall be closed viasfclose()before being reused. During a stream renewal, buffer, pool and discipline stack are preserved. Note that, except forSF_STATICstreams, renewing a stream already closed will result in undefined behavior. do not do that bestie.buf,sizeβ these determine a buffering scheme. Seesfsetbuf()for more details.fdβ ifSF_STRINGis specified inflags, this is ignored. Otherwise,fdis a file descriptor (e.g., fromopen(2)) to use for raw data I/O. Note that Sfio supports unseekable file descriptors opened for both read and write, e.g., sockets.flagsβ this is composed fromSF_EOFand bit values defined in the BIT FLAGS section. Note, in particular, that a multi-threaded application should set the bitSF_MTSAFEto protect the new stream from being simultaneously accessed by multiple threads.
π¦ sfopen()
Sfio_t* sfopen(Sfio_t* f, const char* string, const char* mode)
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:
fβ this is treated as insfnew().stringβ this is a file name or a string to perform I/O on. See above for when this isNULL.modeβ this is composed from the set of letters{s, r, w, +, a, b, t, x, m, u}. When conflicting options are present in the same mode string, the last one will take effect.sspecifies opening a string stream.stringcan be a null-terminated string orNULL. Specifyingsalone is equivalent to specifyingsr. Ifsis not specified,stringdefines a file name.randwspecify 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.aspecifies append mode, i.e., data is always output at end of file.bandtspecify binary and text modes.xspecifies exclusive mode, i.e., a file opened for writing should not already exist.mspecifies that the stream needs to be protected from simultaneous accesses by multiple threads. This turns on the bit flagSF_MTSAFE.uspecifies that the stream is guaranteed to be accessed by only one thread at a time. The bit flagSF_MTSAFEis left off. The absence of optionmis the same as the presence of optionu.
π§ sfpopen()
Sfio_t* sfpopen(Sfio_t* f, const char* cmd, const char* mode)
This function opens a stream that corresponds to the coprocess cmd. The argument mode should be composed from r, w, and +. The argument f, if not NULL, is a stream to be renewed (see sfnew()). sfpopen() returns the new stream or NULL on error.
- The standard input/output of
cmdis 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 onsffileno()). - On opening a coprocess for writing (i.e.,
modecontainswor+), the signal handler forSIGPIPEin the parent application will be set toSIG_IGNif it isSIG_DFLat 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 (seesfdisc()). - The command
cmdis executed by an interpreter which is either/bin/shor an executable command defined by the environment variableSHELL. In either case, the interpreter is invoked with 2 arguments, respectively-cand the given commandcmd. When the interpreter is/bin/shor/bin/ksh,sfpopen()may execute the commandcmditself if there are no shell meta-characters incmd.
π sftmp()
Sfio_t* sftmp(size_t size)
This function creates a stream for temporary data. It returns the new stream or NULL on error.
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 TMPPATH nor TMPDIR is defined, /tmp is used.
π sfclose()
int sfclose(Sfio_t* f)
This function closes the stream f and frees its resources. SF_STATIC should be used if the stream space is to be preserved. If f is the base of a stream stack (see sfstack()), all streams on the stack are closed. If f is a sfpopen-stream, sfclose() waits until the associated command terminates and returns its exit status. sfclose() returns -1 for failure and 0 for success. periodt.
SF_READ|SF_SHARE and SF_WRITE streams get synchronized before closing (peep sfsync() for the deets). If f has disciplines, their exception handlers are gonna get called twice. The first call has the type argument set to one of SF_CLOSING or SF_NEW (check sfdisc()). The SF_NEW one is used when a stream is being closed via sfnew() so it can be renewed, slay. The second call uses type as SF_FINAL and happens after all closing operations have succeeded but before the stream itself is deallocated. Either way, if the exception handler returns a negative value, sfclose() will immediately return that value. If it returns a positive value, sfclose() will immediately return zero. Iconic.
π§΅ THREAD SAFETY π§΅
Okay so the libraries libsfio.a and libstdio.a (which provide 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 you do that, 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. No cap.
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. Periodt.
int sfmutex(Sfio_t* f, int type)
This function acquires or releases a mutex (mutually exclusive) lock on the stream f. It can be used by a thread to serialize a sequence of I/O operations executed together in some critical section. sfmutex() is implicitly used by all Sfio operations on a stream with the flag SF_MTSAFE to protect it from concurrent accesses via multiple threads. sfmutex() returns 0 on success and some non-zero value on failure.
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. It's giving recursive lock energy.
The type argument of sfmutex() takes on the below values:
SFMTX_LOCKβ 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.SFMTX_TRYLOCKβ this is the non-blocking version ofSFMTX_LOCK. If the stream is already locked by a different thread,sfmutex()will immediately return with an error status.SFMTX_UNLOCKβ decreasing the lock count and releasing the stream when the lock count reaches0. 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. So like, don't do that.SFMTX_CLRLOCKβ resetting the lock count to0and releasing the stream. As withSFMTX_LOCK, an attempt to clear the lock count without a previously successful lock may result in undefined behavior. Again, just don't.
β¨ INPUT/OUPUT OPERATIONS β¨
int sfgetc(Sfio_t* f)andint sfputc(Sfio_t* f, int c)β these functions read/write a byte from/to streamf.sfgetc()returns the byte read or-1on error.sfputc()returnscon success and-1on error.ssize_t sfnputc(Sfio_t* f, int c, size_t n)β this function attempts to write the bytectofntimes. It returns the number of bytes actually written or-1on failure.int sfungetc(Sfio_t* f, int c)β this function pushes the bytecback intof. Ifcmatches the byte immediately before the current position in buffered data, the current position is simply backed up (note the effect onsftell()andsfseek()). 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.sfungetc()returnscon success and-1on failure.Sfulong_t sfgetm(Sfio_t* f, Sfulong_t max)andint sfputm(Sfio_t* f, Sfulong_t v, Sfulong_t max)β these functions read and writeSfulong_tvalues encoded in a portable format given that the values are at mostmax. 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 anSfulong_ton the read architecture.sfgetm()returns the value read or-1on error.sfputm()returns the number of bytes written or-1on error.Sfulong_t sfgetu(Sfio_t* f)andint sfputu(Sfio_t* f, Sfulong_t v)β these functions read and writeSfulong_tvalues 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 anSfulong_ton the read architecture.sfgetu()returns the value read or-1on error.sfputu()returns the number of bytes written or-1on error. See alsosfulen().Sflong_t sfgetl(Sfio_t* f)andint sfputl(Sfio_t* f, Sflong_t v)β these functions are similar tosfgetu()andsfputu()but for reading and writing (signed)Sflong_tvalues. See alsosfllen().Sfdouble_t sfgetd(Sfio_t* f)andint sfputd(Sfio_t* f, Sfdouble_t v)β these functions read and writeSfdouble_tvalues. In this case, portability depends on the input and output architectures having the same floating point value representation. Values are coded and decoded usingldexp(3)andfrexp(3)so they are constrained to the sizes supported by these functions. See alsosfdlen().char* sfgetr(Sfio_t* f, int rsc, int type)β this function reads a record of data ending in the record separatorrsc. Aftersfgetr()returns, the length of the record even if it is incomplete can be retrieved withsfvalue().sfgetr()returns the record on success andNULLon error. See alsosfmaxr()for limiting the amount of data read to construct a record.
The type argument is composed of some subset of the below bit flags:
SF_STRINGβ a null byte will replace the record separator to make the record into a C string. Otherwise, the record separator is left alone.SF_LOCKRβ upon successfully obtaining a recordr, the stream will be locked from further access until it is released with a callsfread(f,r,0).SF_LASTRβ this should be used only after a failedsfgetr()to retrieve the last incomplete record. In this case,rscis ignored.
ssize_t sfputr(Sfio_t* f, const char* s, int rsc)β this function writes the null-terminated stringstof. Ifrscis non-negative,(unsigned char)rscis output after the string.sfputr()returns the number of bytes written or-1on failure.Sfoff_t sfmove(Sfio_t* fr, Sfio_t* fw, Sfoff_t n, int rsc)β this function moves objects from input streamfrto output streamfw.sfmove()returns the number of objects moved or-1on failure.
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. Cute.
ssize_t sfread(Sfio_t* f, Void_t* buf, size_t n)β this function reads up tonbytes fromfinto bufferbuf. It returns the number of bytes actually read or-1on error.ssize_t sfwrite(Sfio_t* f, const Void_t* buf, size_t n)β this function writesnbytes frombuftof. IffisSF_STRING, and the buffer is not large enough, anSF_WRITEexception shall be raised.sfwrite()returns the number of bytes written or-1on failure.Sfoff_t sfseek(Sfio_t* f, Sfoff_t offset, int type)β this function sets a new I/O position forf. It returns the new position or-1on failure.
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:
0orSEEK_SETβoffsetis the desired position.1orSEEK_CURβoffsetis relative to the current position (seeSF_PUBLICbelow).2orSEEK_ENDβoffsetis relative to the physical end of file.SF_SHAREβ the stream is treated as if it has the control bitSF_SHAREon. This implies that a system call seek will be done to ensure that the location seeking to is valid.SF_PUBLICβ the stream is treated as if it has the control bitSF_PUBLICon. 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.
Void_t* sfreserve(Sfio_t* f, ssize_t n, int type)β this function reserves a data block from the streamf. It returns the reserved data block on success andNULLon failure.
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. So like, use it before you lose it, bestie.
okay so literally the first thing you need to know about this mode: when f is SF_READ, SF_SHARE and unseekable, sfreserve() will attempt to peek at input data without consuming it. That's the whole slay β¨ β it's what lets separate processes share in reading input from unseekable file descriptors (think pipes or devices, the classics π). But β and this is the part where you gotta pay attention bestie β this use of sfreserve() may fail on certain platforms that do not properly support peeking on unseekable file descriptors. Not our fault, not your fault, it's giving platform issue π.
After a sfreserve() call, whether or not it succeeds, sfvalue(f) gives you the size of the available data block. Which is so useful honestly. And if a sfreserve() call failed and left some partially reserved data block behind? You can still obtain it β in another sfreserve() call with the argument type being SF_LASTR. In that case the second argument n to sfreserve() will be ignored. like, completely ignored, don't even worry about it π.
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 stream, the argument n is treated as follows:
n < 0βsfreserve()attempts to get at least|n|bytes into the buffer.n == 0β okay this one has layers:- If the argument type is
0,sfreserve()attempts to get at least1byte into the buffer but does not consume it (as consistent withn == 0, see, it's consistent, it's fine π ). - If type !=
0, no attempt will be made to read data into the buffer. For example, the callsfreserve(f, 0, -1)only returns the buffer status, i.e., size of existing buffered data and pointer to such data, if any. And the callsfreserve(f, 0, SF_LOCKR)is similar but also locks the stream. Cute AND locked.
- If the argument type is
n > 0βsfreserve()will use attempt to get at mostnbytes into the buffer. Further, if type ==SF_LOCKR(see below), read attempts end on a positive amount.
For a successful reservation, the argument type dictates treatment as follows:
type == SF_LASTRβ After asfreserve()call with type !=SF_LOCKRfails, there may be some left over data not accessible via conventional Sfio calls. Immediately after such a failed call, another call tosfreservewith type ==SF_LASTRwill return any left over data and also advance the stream I/O position by the amount of returned data. Rescue mission, we love to see it π«Ά.type < 0β Ifn > 0, the stream I/O position is advanced byn. Ifn < 0, the stream I/O position is advanced by the amount of available data. For example, a successfulsfreserve(f, -1, -1)call will return a buffer of data and simultanously advance the stream I/O position by the amount indicated bysfvalue(f).type == SF_LOCKRβ The stream I/O position remains unchanged. In addition,fwill be locked from further access. As appropriate to the stream type (SF_READ,SF_WRITEor both),fcan be unlocked later with one ofsfread(f,rsrv,size)orsfwrite(f,rsrv,size)wherersrvis the reserved data block andsizeis the amount of data to be consumed. For example, iffis a lockedSF_READstream, the callsfread(f,rsrv,1)will reopen the stream and simultaneously advance the stream I/O position by1. Finally, a stream opened for both reading and writing can release the lock with either call (with associated operational semantics!) β yes queen, semantics, they matter π. For example, the below code reads 10 bytes of data from a stream opened with bothSF_READandSF_WRITE, modifies the data in place, then rewrites the new data back to the stream:
rsrv = sfreserve(f, 10, 1);
for(i = 0; i < 10; ++i)
rsrv[i] = toupper(rsrv[i]);
sfwrite(f, rsrv, 10);
β¨ DATA FORMATTING β¨
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 β like, same energy, different font π¦. 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.
πΈ %! and Sffmt_t πΈ
The pattern %! 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. Three modes, one pattern, main character energy fr fr. 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 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. (Iconic behaviour, honestly β the pop gets vetoed π
.)
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 β no touchy, ms. girl π. 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. Sneaky little redirect, we stan β¨.
Below are the fields of Sffmt_t:
extfβextfis a function to extend scanning and formatting patterns. Its usage is discussed below.eventfβ This is a function to process events as discussed earlier.formandargsβ This is the formatting pair of a specification string and corresponding argument list. When an environmentfeis being inserted into the stack, iffe->formisNULL, the top environment is changed tofeand its associated extension functions but processing of the current formatting pair continues. On the other hand, iffe->formis notNULL, 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, wheneverextfis called,formandargswill be set to the current values of the formatting pair in use.fmtβ This is set to the pattern being processed or one of.,I,(.sizeβ This is the size of the object being processed.flagsβ This is a collection of bits defining the formatting flags specified for the pattern. The bits are:SFFMT_LEFT: Flag-insfprintf().SFFMT_SIGN: Flag+insfprintf().SFFMT_BLANK: Flag space insfprintf().SFFMT_ZERO: Flag0insfprintf().SFFMT_THOUSAND: Flag'insfprintf().SFFMT_LONG: Flaglinsfprintf()andsfscanf().SFFMT_LLONG: Flagllinsfprintf()andsfscanf().SFFMT_SHORT: Flaghinsfprintf()andsfscanf().SFFMT_LDOUBLE: FlagLinsfprintf()andsfscanf().SFFMT_IFLAG: flagIinsfprintf()andsfscanf().SFFMT_JFLAG: flagjinsfprintf()andsfscanf().SFFMT_CENTER: flag=insfprintf()andsfscanf().SFFMT_CHOP: flag-in precis insfprintf()andsfscanf().SFFMT_ALTER: Flag#insfprintf()andsfscanf().SFFMT_SKIP: Flag*insfscanf().SFFMT_ARGPOS: This indicates argument processing forpos$.SFFMT_VALUE: This is set byfe->extfto indicate that it is returning a value to be formatted or the address of an object to be assigned.
widthβ This is the field width.precisβ This is the precision.baseβ This is the conversion base.t_strandn_strβ This is the type string and its size.
π int (*Sffmtext_f)(Sfio_t* f, Void_t* v, Sffmt_t* fe)
This is the type of the extension function fe->extf to process patterns and arguments. Arguments are always processed in order and fe->extf is called exactly once per argument. Note that, when pos$ (below) is not used anywhere in a format string, each argument is used exactly once per a corresponding pattern. In that case, fe->extf is called as soon as the pattern is recognized and before any scanning or formatting. On the other hand, when pos$ 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 fe->extf exactly once per argument before any pattern processing. This case is signified by the flag SFFMT_ARGPOS in fe->flags.
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.
Okay so the dot? Itβs requesting an integer for width, precision, base, or a separator. When that happens, the fe->size field will tell you 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.
fβ This is the input/output stream in the calling formatting function. During a call tofe->extf, the stream shall be unlocked so thatfe->extfcan read from or write to it as appropriate.vβ For bothsfscanf()andsfprintf()functions,vpoints to a location suitable for storing any scalars or pointers. On return,fe->extftreatsvas discussed below.feβ This is the current formatting environment.
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:
rv < 0β The environment stack is immediately popped.rv == 0β The extension function has not consumed (in a scanning case) or output (in a printing case) data out of or into the given streamf.- The fields
fmt,flags,size,width,precisandbaseoffeshall direct further processing. - For
sfprintf()functions, iffe->flagshas the bitSFFMT_VALUE,fe->extfshould have set*vto the value to be processed; otherwise, a value should be obtained from the argument list. - Likewise, for
sfscanf()functions,SFFMT_VALUEmeans that*vshould have a suitable address; otherwise, an address to assign value should be obtained from the argument list. - When
pos$is present, iffe->extfchangesfe->fmt, this pattern shall be used regardless of the pattern defined in the format string. On the other hand, iffe->fmtis unchanged byfe->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 fields
rv > 0β The extension function has accessed the streamfto the extent ofrvbytes. Processing of the current pattern ceases except that, for scanning functions, iffe->flagsdoes not contain the bitSFFMT_SKIP, the assignment count shall increase by 1.
β¨ void va_copy(va_list to, va_list fr) β¨
This macro function portably copies the argument list fr to the argument list to. It should be used to set the field Sffmt_t.args π«Ά.
β¨ long sffmtversion(Sffmt_t* fe, int type) β¨
This macro function initializes the formatting environment fe with a version number if type is non-zero. Otherwise, it returns the current value of the version number of fe. This is useful for applications to find out when the format of the structure Sffmt_t changes. Note that the version number corresponds to the Sfio version number which is defined in the macro value SFIO_VERSION.
β¨ Output formatting: sfprintf() and the squad β¨
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);
These functions format output data. sfprintf() and sfvprintf() write to output stream f. sfsprintf() and sfvsprintf() write to buffer s which is of size n. sfprints() and sfvprints() construct data in some Sfio-defined buffer. sfaprints() and sfvaprints() are similar to sfprints() and sfvprints() but they return a string constructed via malloc() in *sp and expect this string to be freed by the caller when no longer needed. sfvprintf() is the underlying primitive for the other functions. Except for sfprints() and sfvprints() which return a null-terminated string or NULL, other functions return the number of output bytes or -1 on failure.
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
β¨ pos$ β¨
A pattern can specify which argument in the argument list to use. This is done via pos$ where pos is the argument position. Arguments are numbered so that the first argument after format is at position 1. If pos is not specified, the argument following the most recently used one will be used. The pattern %! (see below) cannot be used subsequent to a usage of pos$. Doing so may cause unexpected behaviors π¬.
β¨ flag β¨
The flag characters are h, hh, l, ll, L, I, j, t, z, -, +, space, 0, ', = and #.
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 do 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.
β¨ width β¨
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.
β¨ precis β¨
After a first dot appears, an integral value defines a precision. For floating point value patterns, precision is the number of precision digits. For %c, precision defines the number of times to repeat the character being formatted. For %s, precision defines the maximum number of characters to output; -precis also defines the maximum number of characters to output, but retains the rightmost precis characters.
β¨ base β¨
This is defined after exactly two dots have appeared.
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 @_
okay so 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 it's a null-terminated array of characters. The strings or characters get formatted one at a time based on the usual width and precision rules. After each formatted string or characterβexcept the last oneβthe separator base is output if it's 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|
(extfdata): this defines a stringextfdatato be passed to the extension functionSffmt_t.extf. Parentheses shall be balanced. Ifextfdatais*, the string is obtained from the argument list.
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)
These functions scan data items. sfscanf() scans from the input stream f while sfsscanf() and sfvsscanf() scan from the null-terminated string s. sfvscanf() is the underlying primitive that performs the actual scanning. Item types are determined from patterns in string format. These functions return the number of items successfully scanned or -1 on error.
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
pos$: a pattern can specify which argument in the argument list to use. This is done viapos$whereposis the argument position. Arguments are numbered so that the first argument afterformatis at position 1. Ifposis not specified, the argument following the most recently used one will be used. The pattern%!(see below) cannot be used subsequent to a usage ofpos$.*: this discards the corresponding scanned item.widthandbase:widthdefines the maximum number of bytes to scan andbasedefines 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 variablevshould have the value 1234.
sfsscanf("12345678","%.*.*d", 4, 10, &v);
(extfdata): this defines a stringextfdatato be passed to the extension functionSffmt_t.extf. Parentheses shall be balanced. Ifextfdatais*, the string is obtained from the argument list.flag: this is#,I, or some sequence ofh,l, andL.#is significant for pattern%iand%[. For%i, it means that the#symbol does not have its usual meaning in an input sequencebase#value. For example, the scanning result of%#ion input2#1001is2and the nextsfgetc()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.Idefines the size or type of the object being formatted. There are two cases: (1)Iby itself and (2)Ifollowed 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 @_
π BUFFERING, SYNCHRONIZATION π
Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size)
This function changes the buffering scheme for the stream f. The stream will be synchronized before any buffer modification. If a new buffer is successfully set and the old buffer has not been freed, sfsetbuf() returns the old buffer. Otherwise, it returns NULL. After a sfsetbuf() call, sfvalue() 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 sfsetbuf(f, (Void_t*)f, size).
In invocations of sfsetbuf() other than the above case, the size argument is treated as follows:
size == SF_UNBOUND: Sfio will pick a suitable buffer size. IfbufisNULL, Sfio will also pick a suitable buffering scheme (such as memory mapping.) Ifbufis notNULL, its actual value is ignored but the buffer will be allocated viamalloc(3). This can be used to avoid memory mapping.size > 0: this is the suggested size to use for buffering or memory mapping. IfbufisNULL, Sfio will pick a suitable buffering scheme as discussed above. Ifbufis notNULL, thenbufandsizedetermine a buffer of the given size.size == 0: ifbufisNULL, the stream will be unbuffered. Ifbufis notNULL,sfsetbuf()simply returns the stream buffer. In this case, no attempt will be made to synchronize the stream.
int sfsync(Sfio_t* f)
This function synchronizes the logical and physical views of stream f. It returns a negative value for failure and 0 for success.
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.
int sfpoll(Sfio_t** flist, int n, int timeout)
Okay so bestie, this one is lowkey the multiplexing queen π β it polls a set of streams to see if I/O operations can be performed on them without blocking. Super 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 sfdisc() for details). After a successful sfpoll() call, for each ready stream f, sfvalue(f) returns a bit combination of SF_READ and SF_WRITE to tell you which I/O mode is available. If SF_READ is available, an attempt to read a byte will not block. If SF_WRITE is available, an attempt to flush will not block. sfpoll() returns the number of ready streams or -1 on failure. Periodt.
flistandn:flistis an array ofnstreams to be polled. Upon return, ready streams are moved to the front offlistin the same relative order.timeout: This defines an elapse time in milliseconds to wait to see if any stream is ready for I/O. Iftimeoutis negative,sfpoll()will block until some stream becomes ready. Note thatSF_STRINGand normal file streams never block and are always ready for I/O. If a stream with a discipline is being polled and its readiness is as yet undetermined (e.g., empty buffer), the discipline exception function will be called withSF_DPOLLbefore querying the operating system.
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. sfpool() returns NULL on failure.
fandpoolf: IffisNULL,sfpool()simply returns the head of the pool containingpoolf. Iffis notNULLandpoolfisNULL,fis deleted from its pool. In this case, if no other stream from the same pool can become head,sfpool()will returnNULL; otherwise, it returns some stream from the remainder of the pool. If bothfandpoolfare notNULL,fis moved from its current pool (if any) into the same pool withpoolf. In this case,poolfis returned.mode: Ifpoolfis already in a pool,modeis ignored. Otherwise,modeshould be0orSF_SHARE. ASF_SHAREpool contains streams withSF_WRITEmode. In addition, on change to a new head stream, buffered write data of the current head is transferred to the new head.
int sfpurge(Sfio_t* f)
This function discards all buffered data unless f is a SF_STRING stream π§Ή. Note that if f is a SF_READ stream based on an unseekable device, 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 sfset() to selectively turn off read or write mode if one set of data is to be preserved.) After purging, if f has flag SF_IOCHECK, the event SF_PURGE is raised. sfpurge() returns -1 for failure and 0 for success.
β¨ DISCIPLINE, EVENT-HANDLING β¨
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. Itβs giving βcustomize your streamβs whole personalityβ π
.
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. sfdisc() manipulates the discipline stack of stream f. f will be synchronized before any discipline stack manipulation. After a successful discipline stack manipulation, the stream I/O position (see sfseek() and sftell()) and extent (see sfsize()) are updated to reflect that defined by the top discipline. sfdisc() returns NULL on failure.
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:
<0: The on-going operation shall terminate.>0: 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.,SF_DPOLL, the return value may also have additional meanings.=0: 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.
The argument type of (*exceptf)() identifies the particular exceptional event:
SF_LOCKED: 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 sectionTHREAD SAFETY). Rather, the stream was frozen by certain operations such assfreserve()orsfstack(). Thus, a stream can be in this state even if the application is uni-threaded.SF_READ,SF_WRITE: These events are raised around reading and writing operations.
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.
SF_SEEK: This event is raised when a seek operation fails.SF_NEW,SF_CLOSING (SF_CLOSE),SF_FINAL: These events are raised during a stream closing.SF_NEWis raised for a stream about to be closed to be renewed (seesfnew()).SF_CLOSINGis raised for a stream about to be closed.SF_FINALis raised after a stream has been closed and before its space is to be destroyed (seesfclose()). For these events, a non-zero return value from(*exceptf)()causessfclose()to return immediately with the same value.SF_DPUSH,SF_DPOP,SF_DBUFFER: EventsSF_DPUSHandSF_DPOPare raised when a discipline is about to be pushed or popped.(Sfdisc_t*)valueis the to-be top discipline, if any.
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.
SF_DPOLL: This event is raised bysfpoll()to see if the stream is ready for I/O.*((int*)value)indicates a time-out interval to wait. A negative return value from the exception function means blocking. A zero return value means thatsfpoll()should query the underlying file descriptor. A positive return value means non-blocking. In addition, this value will be a bit combination ofSF_READandSF_WRITEto indicate what I/O modes are ready.SF_READY: This event is raised bysfpoll()for each ready stream. The third argument to the event handler is an integer composed with the two bitsSF_READandSF_WRITEto indicate which I/O modes are ready.SF_SYNC,SF_PURGE: IfSF_IOCHECKis set, these events are raised respectively for asfsync()orsfpurge()call. In each case, the respective event is raised once before the appropriate operation (synchronization or purging) with((int)value)being1and once after with((int)value)being0. Note thatsfsync()is called for eachSF_WRITEorSF_SHARE|SF_READstream on closing.SF_ATEXIT: This event is raised for each open stream before the process exits.
int sfraise(Sfio_t* f, int type, Void_t* data)
If f is non-NULL, sfraise() calls all exception handlers of f with the event type and associated data. If an exception handler returns a non-zero value, sfraise() immediately returns the same value. Application-defined events should start from the value SF_EVENT so as to avoid confusion with system-defined events, sfraise() returns 0 on success and -1 on failure.
If f is NULL, sfraise() iterates over all streams and raises 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 π.
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)
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)
okay so these three? absolute safe queens π They give a discipline I/O function a safe way to call back into earlier discipline I/O functions and to handle exceptions properly. They should not be used in any other context. Like, they're not for you, bestie, they're for the discipline machinery β respect the assignation π
sfrd()andsfwr()return the number of bytes read or written.sfsk()returns the new seek position.- On error, all three return a negative value β which should be
-1or the value returned by the exception handler.
π STREAM CONTROL π
int sfresize(Sfio_t* f, Sfoff_t size)
This one resizes the stream fP so its extent is size β¨ If the stream corresponds to a file, the file size gets set to size via the system call ftruncate(). When a stream is made larger, the new data space is filled with zero's π«§ Returns 0 on success and a negative value on failure.
int sfset(Sfio_t* f, int flags, int set)
Sets control flags for the stream f, and returns the previous set of flags β or 0 on error π©
The settable flags are: SF_READ, SF_WRITE, SF_IOCHECK, SF_LINE, SF_SHARE, SF_PUBLIC, SF_MALLOC and SF_STATIC.
ngl this is the part where you gotta pay attention π Note thatSF_READandSF_WRITEcan be turned on or off only if the stream was opened asSF_READ|SF_WRITE. Turning off one of them means 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 eitherSF_READorSF_WRITEwill cause the stream to be in the given I/O mode.
set == 0β ifflagsis 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). Ifflagsis non-zero, an attempt is made to turn off the specified flags.set != 0β ifflagsis zero, the stream is initialized if not yet done so. Then the current set of flags is returned. Ifflagsis non-zero, an attempt is made to turn on the specified flags.
int sfsetfd(Sfio_t* f, int fd)
Changes the file descriptor of f π¦ Before a change is realized, (*notify)(f,SF_SETFD,newfd) (see sfnotify()) is called. Returns -1 on failure and the new file descriptor on success.
fd >= 0β if the current file descriptor is non-negative, it will be changed usingdup(3)to a value larger or equal tofd. Upon a successful change, the previous file descriptor will be closed. If the current file descriptor is negative, it will be set tofdand the stream will be reinitialized.fd < 0β the stream is synchronized (seesfsync()) and its file descriptor will be set to this value. Then, except forsfclose(), the stream will be inaccessible until a futuresfsetfd()call resets the file descriptor to a non-negative value. Thus,sfsetfd(f,-1)can be used to avoid closing the file descriptor offwhenfis closed π
Sfio_t* sfstack(Sfio_t* base, Sfio_t* top)
Stacks or unstacks streams π₯ 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 sfdisc() for alternative handling of these conditions).
baseβ this is the base stream of the stack. If it isNULL,sfstack()does nothing and returnstop.topβ if this isSF_POPSTACKor(Sfio_t*)0, the stack is popped andsfstack()returns the popped stream. Otherwise,topis pushed on top of the stack identified bybaseandsfstack()returns thebasestream.
Sfio_t* sfswap(Sfio_t* f1, Sfio_t* f2)
Swaps the contents of f1 and f2 π This fails if either stream is in a stream stack but not being a base stream. If f2 is NULL, a new stream is constructed as a duplicate of f1. Returns f2 or the f1 duplicate on success and NULL on failure.
π¦ STREAM INFORMATION π¦
Sfoff_t sfsize(Sfio_t* f)
Returns the size of stream f (see sfnew()) β If f is not seekable or if its size is not determinable, sfsize() returns -1.
Sfoff_t sftell(Sfio_t* f)
Returns the current I/O position in stream f π Note that if f is SF_APPEND and a writing operation was just performed, the current I/O position is at the physical end of file. If f is unseekable, sftell returns the number of bytes read from or written to f. See also sfungetc().
ssize_t sfvalue(Sfio_t* f)
Returns the string or buffer length for sfreserve(), sfsetbuf(), and sfgetr() π
int sffileno(Sfio_t* f)
Returns the file descriptor of stream f π«
int sfstacked(Sfio_t* f)
Returns a non-zero value if stream f has been stacked π₯β¨
int sfeof(Sfio_t* f) / int sferror(Sfio_t* f) / int sfclrerr(Sfio_t* f)
sfeof()tells whether or not the stream has an end-of-file condition πΈsferror()tells whether or not the stream has an error condition π¨sfclrerr()clears both end-of-file and error conditions.
The end-of-file and error conditions are also cleared on an I/O operation π
int sfclrlock(Sfio_t* f)
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 (longjmp(3)) out of some discipline I/O function to restore the internal stream states. sfclrlock() returns the current set of flags.
int sfnotify((void(*)notify)(Sfio_t*, int, void*) )
This sets a function (*notify)() to be called as (*notify)(f, type, data) on various stream events π£ Arguments type and data indicate the reason for the call and the accompanying data:
SF_NEWβfis being opened anddatais the underlying file descriptor.SF_CLOSING (SF_CLOSE)βfis the stream being closed anddatais the underlying file descriptor.SF_SETFDβ the file descriptor offis being changed to the one defined bydata(seesfsetfd()).SF_READβ an attempt to changefto read mode failed.datais the file descriptor of the stream.SF_WRITEβ an attempt to changefto write mode failed.datais the file descriptor of the stream.SF_MTACCESSβ when a notifying function was registered (seesfnotify()), every Sfio call on a stream with flagSF_MTSAFEwill invoke the notifying function once on entry after the stream is locked as(*notify)(f, SF_MTACCESS, Sfio_t** fp), and once on return before unlocking as(*notify)(f, SF_MTACCESS, (Sfio_t**)0). In the call entry case, the notification function could use the argumentfpto set a stream that would be used for performing the actual I/O operation. In this way, certain global streams such as the standard streamssfstdin,sfstdoutandsfstderrcould be made to act differently when used in different streams π
int sfwalk(Sfwalk_f walkf, Void_t* data, int type)
Invokes (*walkf)(f, data) on every open stream f whose flags, as defined by sfset(), contain all bit flags given in type πΆββοΈ On such a call, if the return value is negative, sfwalk() will terminate. sfwalk() returns 0 if no stream was processed. Otherwise, it returns the return value from the last invocation of walkf().
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 β
π MISCELLANEOUS FUNCTIONS π
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 sfgetr(). A different memory bound can be set via sfmaxr(). While a positive maxr hints to sfgetr() to use only about that much memory to construct a record, a non-positive bound allows sfgetr() to use as much memory as necessary. sfmaxr() sets the value only if set is non-zero. It returns the value before setting, or the current value if not setting.
ssize_t sfslen()
Returns the length of a string just constructed by sfsprintf() or sfprints() π See also sfvalue().
int sfulen(Sfulong_t v) / int sfllen(Sflong_t v) / int sfdlen(Sfdouble_t v)
These return, respectively, the number of bytes required to code the Sfulong_t, Sflong_t or Sfdouble_t value v by sfputu(), sfputl() or sfputd() π’
ssize_t sfpkrd(int fd, char* buf, size_t n, int rsc, long tm, int action)
This one 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 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. 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 alsoactionbelow.tmβ if>=0, this defines a time interval in milliseconds to wait for incoming data β³actionβ ifaction > 0,sfpkrd()will peek on incoming data but will not read past it. Therefore, a futuresfpkrd()orread(2)will see the same data again π Ifaction <= 0,sfpkrd()will not peek and there are two cases. Ifrsc < 0, an attempt is made to readnbytes. Ifrsc >= 0, an attempt is made to read one record.
π FULL STRUCTURE SFIO_T π
#include <sfio_t.h>
Most applications based on Sfio only need to include the header file sfio.h, which defines an abbreviated Sfio_t structure without certain fields private to Sfio π«Ά However, there are times (e.g., debugging π) when an application may require more details about the full Sfio_t structure. In such cases, the header file sfio_t.h can be used in place of sfio.h. Note that an application doing this will become sensitive to changes in the internal architecture of Sfio π
#define SFNEW(buf,size,file,flags,disc)
This macro function is defined in sfio_t.h for use in static initialization of an Sfio_t structure π± It requires five arguments:
buf,sizeβ these define a buffer and its size.fileβ this defines the underlying file descriptor, if any.flagsβ this is composed from bit flags described above.discβ this is where the discipline lives, if there even is one. π
π¦ EXAMPLE DISCIPLINES π¦
okay so these functions below are the whole vibe β they create disciplines and then insert them into whatever stream you hand them as f. They return 0 on success and -1 on failure. no cap, that's the entire contract. π«Ά
int sfdcdio(Sfio_t* f, size_t bufsize)
This one builds a discipline that uses the direct IO feature β the kind of thing available on file systems like SGI's XFS β to speed up IO. The argument bufsize is a suggestion for the buffer size to use for data transfer. β¨
int sfdcdos(Sfio_t* f)
Makes a discipline for reading DOS text files. Basically it transforms pairs of \r\n into \n. that's it, that's the job π
int sfdcfilter(Sfio_t* f, const char* cmd)
Creates a discipline that sends data from f to the given command cmd to process it, then reads the processed data back. little pipeline moment π
int sfdcseekable(Sfio_t* f)
Creates a discipline that makes an unseekable reading stream seekable. giving main character energy to a stream that literally couldn't before π
int sfdcslow(Sfio_t* f)
Creates a discipline that makes all Sfio operations return immediately on interrupts. Super useful for dealing with slow devices. ngl, patience is a virtue but this one skips the line π
int sfdcsubstream(Sfio_t* f, Sfio_t* parent, Sfoff_t offset, Sfoff_t extent)
Creates a discipline that makes f act as if it corresponds exactly to the subsection of parent starting at offset with size extent. it's giving "you are a window into your parent" πͺ
int sfdctee(Sfio_t* f, Sfio_t* tee)
Creates a discipline that copies to the stream tee any data written to f. yes bestie, it tees β like the command, but a discipline π΅
int sfdcunion(Sfio_t* f, Sfio_t** array, int n)
Creates a discipline that makes f act as if it's the concatenation of the n streams given in array. one stream, many streams, lowkey both π
int sfdclzw(Sfio_t* f)
Creates a discipline that would decompress data in f. The stream f should have data from a source compressed by the Unix compress program. old school, still iconic π§
int sfdcgzip(Sfio_t* f, int opt)
Creates a discipline for reading/writing data compressed by zlib. The argument opt defines the optimization level. zlib girlies rise π¦
π STDIO-COMPATIBILITY π
Sfio provides compatibility functions for all the various popular Stdio implementations β at source level and at binary level. iconic behavior, honestly.
The source Stdio-compatibility interface provides the header file stdio.h that defines a set of macros or inlined functions mapping Stdio calls onto Sfio ones. This mapping may benignly extend or change the meaning of certain original Stdio operations. For example β and this is the fun part β Sfio's version of popen() lets a coprocess be opened for both reading and writing, unlike the original call which only allows a coprocess to be opened for a single mode. Similarly, Sfio's fopen() call can be used to create string streams in addition to file streams. upgrades only β¨
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. vintage app support, we love to see it π
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);
π RECENT CHANGES π
A few exception types got added, bestie. 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()). And before a process exits, the event SF_ATEXIT is raised for each open stream. closure, but make it official π
A number of disciplines were added for various processing functions. Of interest 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 were added too. 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 β backwards compatible queen π
The sfopen() call got extended so that sfopen(f,NULL,mode) can be used to change the mode of a file stream before any I/O operations. This is most useful for changing the modes of the standard streams. so handy fr fr
The buffering strategy has been significantly enhanced for streams that perform many seek operations. Also, the handling of stream and file positions has 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. consistency is the vibe π
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. ate and left no crumbs β
The SF_BUFCONST flag was deleted. It was largely unused anyway. rip bozo, we hardly knew ye π
The library can be built for thread-safety. This is based largely on Posix pthread mutexes β except on UWIN, where native Windows APIs are used. platforms, plural, we contain multitudes π
The functions sfgetm() and sfputm() were added to encode unsigned integer values with known ranges. precise and slaying β¨
The flag SF_APPEND is identical to SF_APPENDWR. However β plot twist β 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. so heads up π«Ά
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. same energy, different header π
The printing and scanning functions were extended to handle multibyte characters and to conform to the C99 standard. international icon behavior πΈ
The function sfpoll() was rehauled to make it useful for writing servers that must communicate with multiple streams without blocking. server girlies, this one's for you π
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. cute and correct π
π AUTHORS π
- Kiem-Phong Vo, kpv@research.att.com,
- David G. Korn, dgk@research.att.com, and
- Glenn S. Fowler, gsf@research.att.com.
and that's the whole page, bestie!! π this edition is dated 01 June 2008 and it answers to the name SFIO(3) β go forth, stream responsibly, and remember: the vibe changes, the facts do not ππ¦β¨
π¦ where this page comes from
- opensuse-leap 16.0: ksh-devel 93vu-bp160.1.33 Β· CPL-1.0
- opensuse-leap 16.0: ksh-devel 93vu-bp160.1.33 Β· CPL-1.0
- opensuse-leap 16.0: ksh-devel 93vu-bp160.1.33 Β· CPL-1.0
- opensuse-leap 16.0: ksh-devel 93vu-bp160.1.33 Β· CPL-1.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms