DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH
 

sfio(3)





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_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, int fd));

   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  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 auto-
       matically reinvoked to continue the ongoing operation.

       The buffer of a stream is typically a memory segment allocated via mal-
       loc(3)  or supplied by the application.  File streams may also use mem-
       ory 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).

   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 pro-
       tected 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  sys-
       tem  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):
              Flags SF_READ and SF_WRITE indicate readability and writability.
              Flag SF_APPENDWR asserts that the stream is  a  file  opened  in
              append  mode  (see  open(2) and fcntl(2)) so that data is always
              output at the end of file.  On systems  without  direct  support
              for  append  mode, Sfio uses lseek(2) or its discipline replace-
              ment to approximate this behavior.

       SF_LINE:
              The stream is line-oriented.  For a SF_WRITE stream, this  means
              that buffered data is flushed whenever a new-line character, \n,
              is output.  For a SF_READ stream, SF_LINE  is  only  significant
              during  calls  to functions in the sfscanf() family.  SF_LINE is
              set on initialization of  any  stream  representing  a  terminal
              device.

       SF_SHARE, SF_PUBLIC:
              Flag  SF_SHARE  means  that  the  underlying  file descriptor is
              shared by  independent  entities  (for  example,  multiple  pro-
              cesses).

              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 differ-
              ent 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  opera-
              tions  (sfread(), sfwrite(), sfmove(), sfgetr(), sfputr(), sfre-
              serve(), sfscanf() and sfvprintf()) will ensure: (1) after  each
              writing operation, the stream is synchronized and (2) each read-
              ing 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.

       SF_MALLOC:
              The  stream buffer was obtained via malloc(3) and can be reallo-
              cated or freed.

       SF_STATIC:
              The  stream  structure  should  not   be   freed   when   closed
              (sfclose()).  This flag is used by an applications that allocate
              their own stream structures.  Such  applications  must  use  the
              header file sfio_t.h instead of sfio.h.

       SF_IOCHECK:
              If  the  stream  has  a discipline exception handler, exceptions
              will be raised in sfsync(), sfpurge() or before  a  system  call
              read(2) or write(2) (see sfdisc()).

       SF_WHOLE:
              This  flag  guarantees that data written in any single sfwrite()
              or sfputr() call will always be output as a whole to the  output
              device.   This is useful in certain applications (e.g., network-
              ing) where a complex object must be output without  being  split
              in  different  system  calls.   Note  that the respective stream
              still buffers data as much as the buffer can accomodate.

       SF_MTSAFE:
              This flag indicates that the respective stream may  be  accessed
              by  more  than one threads.  A mutex lock will be used to ensure
              that only one thread at a time can access the stream. Note  that
              this  flag can only be set at stream opening time (see sfopen(),
              sfpopen() and sfnew()).  Certain fast macro  functions  such  as
              sfgetc()  and sfputc() will no longer behave as macros. Thus, an
              application that requires such fast macro functions should leave
              SF_MTSAFE off and performs explicit locking with sfmutex().

       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, than cer-
              tain operating systems (e.g., BSD Unix  systems)  may  automati-
              cally  resume  interrupted system calls outside the scope of the
              library. On such systems, SF_IOINTR will be ineffective.

   OPENING/CLOSING STREAMS
     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:     If  f is NULL, a new stream is created.  Otherwise, f is reused.
              In this case, if flags does not have SF_EOF, f shall  be  closed
              via  sfclose()  before  being  reused.  During a stream renewal,
              buffer, pool and discipline stack  are  preserved.   Note  that,
              except  for  SF_STATIC streams, renewing a stream already closed
              will result in undefined behavior.

       buf, size:
              These determine a buffering scheme.   See  sfsetbuf()  for  more
              details.

       fd:    If SF_STRING is specified in flags, this is ignored.  Otherwise,
              fd is a file descriptor (e.g., from open(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 from SF_EOF and bit values defined in  the  BIT
              FLAGS  section.   Note,  in  particular,  that  a multi-threaded
              application should set the bit  SF_MTSAFE  to  protect  the  new
              stream from being simultaneously accessed by multiple threads.

     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 in sfnew().

       string:
              This  is  a  file name or a string to perform I/O on.  See above
              for when this is NULL.

       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.

              s specifies opening a string stream.  string can be a  null-ter-
              minated  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 simulta-
              neous 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.

     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 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  asso-
       ciated  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 appli-
       cation  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.

     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-resi-
       dent.  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.

     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.

       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 deal-
       located.   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.

   THREAD SAFETY
       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.

   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 delineate  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.   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 pos-
       itive,  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:

       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.

       SFMTX_TRYLOCK:
              This  is  the non-blocking version of SFMTX_LOCK.  If the stream
              is already locked by a different thread, sfmutex() will  immedi-
              ately return with an error status.

       SFMTX_UNLOCK:
              Decreasing the lock count and releasing the stream when the lock
              count reaches 0.  An attempt to unlock a stream without a previ-
              ously  successful  lock may result in undefined behavior in cer-
              tain implementations.  The current Sfio  implementation  returns
              an error status.

       SFMTX_CLRLOCK:
              Resetting the lock count to 0 and releasing the stream.  As with
              SFMTX_LOCK, an attempt to clear the lock count without a  previ-
              ously successful lock may result in undefined behavior.

   INPUT/OUPUT OPERATIONS
     int sfgetc(Sfio_t* f)
     int sfputc(Sfio_t* f, int c)
       These  functions read/write a byte  from/to stream f.  sfgetc() returns
       the byte read or -1 on error.  sfputc() returns c on success and -1  on
       error.

     ssize_t sfnputc(Sfio_t* f, int c, size_t n)
       This  function  attempts  to write the byte c to f n times.  It returns
       the number of bytes actually written or -1 on failure.

     int sfungetc(Sfio_t* f, int c)
       This function pushes the byte c back into f.  If  c  matches  the  byte
       immediately  before  the current position in buffered data, the current
       position  is  simply  backed  up  (note  the  effect  on  sftell()  and
       sfseek()).  There  is  no theoretical limit on the number of bytes that
       can be pushed back into  a  stream.  Pushed  back  bytes  not  part  of
       buffered  data  will  be discarded on any operation that implies buffer
       synchronization.  sfungetc() returns c on success and -1 on failure.

     Sfulong_t sfgetm(Sfio_t* f, Sfulong_t max)
     int sfputm(Sfio_t* f, Sfulong_t v, Sfulong_t max)
       These functions read and write Sfulong_t values encoded in  a  portable
       format  given  that  the  values are at most max.  Portability across a
       write architecture and a read architecture requires that the bit  order
       in  a  byte  is the same on both architectures and the written value is
       storable in an Sfulong_t on the read  architecture.   sfgetm()  returns
       the  value  read  or -1 on error.  sfputm() returns the number of bytes
       written or -1 on error.

     Sfulong_t sfgetu(Sfio_t* f)
     int sfputu(Sfio_t* f, Sfulong_t v)
       These functions read and write Sfulong_t values in a compact  variable-
       length  portable format.  Portability across a write architecture and a
       read architecture requires that the bit order in a byte is the same  on
       both architectures and the written value is storable in an Sfulong_t on
       the read architecture.  sfgetu() returns the value read or -1 on error.
       sfputu()  returns the number of bytes written or -1 on error.  See also
       sfulen().

     Sflong_t sfgetl(Sfio_t* f)
     int sfputl(Sfio_t* f, Sflong_t v)
       These functions are similar to sfgetu() and sfputu()  but  for  reading
       and writing (signed) Sflong_t values.  See also sfllen().

     Sfdouble_t sfgetd(Sfio_t* f)
     int sfputd(Sfio_t* f, Sfdouble_t v)
       These functions read and write Sfdouble_t values.  In this case, porta-
       bility depends on the input and output architectures  having  the  same
       floating  point  value  representation.   Values  are coded and decoded
       using ldexp(3) and frexp(3) so they are constrained to the  sizes  sup-
       ported by these functions.  See also sfdlen().

     char* sfgetr(Sfio_t* f, int rsc, int type)
       This  function  reads  a  record of data ending in the record separator
       rsc.  After sfgetr() returns, the length of the record even  if  it  is
       incomplete  can  be  retrieved  with  sfvalue().   sfgetr() returns the
       record on success and NULL on error.  See also  sfmaxr()  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  record  r,  the stream will be
              locked from further access until it  is  released  with  a  call
              sfread(f,r,0).

       SF_LASTR:
              This should be used only after a failed sfgetr() to retrieve the
              last incomplete record. In this case, rsc is ignored.

     ssize_t sfputr(Sfio_t* f, const char* s, int rsc)
       This function writes the null-terminated string s to f.  If rsc is non-
       negative,  (unsigned  char)rsc  is  output  after the string.  sfputr()
       returns the number of bytes written or -1 on failure.

     Sfoff_t sfmove(Sfio_t* fr, Sfio_t* fw, Sfoff_t n, int rsc)
       This function moves objects from input stream fr to output  stream  fw.
       sfmove() returns the number of objects moved or -1 on 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 num-
       ber 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.

     ssize_t sfread(Sfio_t* f, Void_t* buf, size_t n)
       This function reads up to n bytes from f into buffer buf.   It  returns
       the number of bytes actually read or -1 on error.

     ssize_t sfwrite(Sfio_t* f, const Void_t* buf, size_t n)
       This function writes n bytes from buf to f.  If f is SF_STRING, and the
       buffer is not large enough, an  SF_WRITE  exception  shall  be  raised.
       sfwrite() returns the number of bytes written or -1 on failure.

     Sfoff_t sfseek(Sfio_t* f, Sfoff_t offset, int type)
       This  function sets a new I/O position for f.  It returns the new posi-
       tion or -1 on 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  com-
       posed from the bit flags:

       0 or SEEK_SET:
              offset is the desired position.

       1 or SEEK_CUR:
              offset  is  relative  to  the  current  position  (see SF_PUBLIC
              below).

       2 or SEEK_END:
              offset is relative to the physical end of file.

       SF_SHARE:
              The stream is treated as if it has the control bit SF_SHARE  on.
              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 bit SF_PUBLIC on.
              If  the  physical  file position has changed from its last known
              location, the current position is  taken  as  the  new  physical
              position.  Otherwise, the current position is the logical stream
              position.

     Void_t* sfreserve(Sfio_t* f, ssize_t n, int type)
       This function reserves a data block from the stream f.  It returns  the
       reserved data block on success and NULL on 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  guaran-
       teed 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  pro-
       cesses  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  sfre-
       serve() 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:

       n < 0: sfreserve() attempts to get at least |n| bytes into the  buffer.

       n == 0:
              If  the argument type is 0, sfreserve() attempts to get at least
              1 byte into the buffer; otherwise, no attempt will  be  made  to
              read  data  into the buffer.  For example, the call sfreserve(f,
              0, -1) only returns the buffer status, i.e.,  size  of  existing
              buffered  data and pointer to such data, if any.  The call sfre-
              serve(f, 0, SF_LOCKR) is similar but also locks the stream.

       n > 0: sfreserve() will use attempt to get at most  n  bytes  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  treat-
              ment as follows:

       type == SF_LASTR:
              After  a sfreserve() call with type != SF_LOCKR fails, there may
              be some left over data  not  accessible  via  conventional  Sfio
              calls.   Immediately  after  such a failed call, another call to
              sfreserve with type == SF_LASTR will return any left  over  data
              and  also  advance  the  stream  I/O  position  by the amount of
              returned data.

       type < 0:
              If n > 0, the stream I/O position is advanced by n.  If n  <  0,
              the  stream  I/O position is advanced by the amount of available
              data.  For example, a successful sfreserve(f, -1, -1) call  will
              return a buffer of data and simultanously advance the stream I/O
              position by the amount indicated by sfvalue(f).

       type == SF_LOCKR:
              The stream I/O position remains unchanged.  In addition, f  will
              be  locked  from  further  access.  As appropriate to the stream
              type (SF_READ, SF_WRITE or both), f can be unlocked  later  with
              one of sfread(f,rsrv,size) or sfwrite(f,rsrv,size) where rsrv is
              the reserved data block and size is the amount  of  data  to  be
              consumed. For example, if f is a locked SF_READ stream, the call
              sfread(f,rsrv,1)  will  reopen  the  stream  and  simultaneously
              advance  the stream I/O position by 1.  Finally, a stream opened
              for both reading and writing can release the  lock  with  either
              call  (with associated operational semantics!)  For example, the
              below code reads 10 bytes of data from a stream opened with both
              SF_READ  and SF_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.  However, the Sfio  versions  have
       been  extended  for  both portability and generality.  In particular, a
       notion of a formatting environment stack is introduced.   Each  format-
       ting  element on the stack defines a separate formatting pair of a for-
       mat 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 pat-
       tern ! 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 envi-
       ronment on top of the current top, or (3) pop the top environment.  The
       bottom  of  the environment stack always contains a virtual environment
       with the original formatting pair and without any extension  functions.

       The  top  environment of a stack, say fe, is automatically popped when-
       ever 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 func-
       tion 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, con-
       sider a call from a sfprintf() function to process an  unknown  pattern
       %t  (which we may take to mean ``time'') based on a formatting environ-
       ment 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:

       extf:  extf  is  a function to extend scanning and formatting patterns.
              Its usage is discussed below.

       eventf:
              This is a function to process events as discussed earlier.

       form and args:
              This is the formatting pair of a specification string and corre-
              sponding  argument  list.   When  an  environment  fe  is  being
              inserted into the stack, if fe->form is NULL, the  top  environ-
              ment is changed to fe and its associated extension functions but
              processing of the current formatting  pair  continues.   On  the
              other  hand,  if  fe->form  is  not NULL, the new environment is
              pushed onto the stack so that pattern processing will start with
              the  new  formatting  pair  as  well as any associated extension
              functions.  During processing, whenever extf is called, form and
              args will 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 spec-
              ified for the pattern.  The bits are:

              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_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.

       width: This is the field width.

       precis:
              This is the precision.

       base:  This is the conversion base.

       t_str and n_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  pat-
       tern  processing.   This  case is signified by the flag SFFMT_ARGPOS in
       fe->flags.

       In addition to the predefined formatting patterns  and  other  applica-
       tion-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 conver-
       sion pattern is 'c' or 's', the value *form will be one of these  char-
       acters.

       f:     This  is the input/output stream in the calling formatting func-
              tion.  During a call to fe->extf, the stream shall  be  unlocked
              so that fe->extf can read from or write to it as appropriate.

       v:     For both sfscanf() and sfprintf() functions, v points to a loca-
              tion suitable for storing any scalars or pointers.   On  return,
              fe->extf treats v as 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 stream
              f.  The fields fmt, flags, size, width, precis and  base  of  fe
              shall direct further processing.

              For  sfprintf() functions, if fe->flags has the bit SFFMT_VALUE,
              fe->extf should have set *v to the value to be processed; other-
              wise,  a value should be obtained from the argument list.  Like-
              wise, 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  pat-
              tern.  Otherwise, it shall be treated as unmatched.

       rv > 0:
              The  extension  function has accessed the stream f to the extent
              of rv bytes.  Processing of the current  pattern  ceases  except
              that,  for scanning functions, if fe->flags does not contain the
              bit SFFMT_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  argu-
       ment 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.

     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 follow-
              ing  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 spec-
              ifiers 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 pat-
              terns, 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  dou-
              ble 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 porta-
              ble  and  only  works  on platforms where sizeof(Sflong_t) is 8.
              The second example shows how to that portably.  The third  exam-
              ple 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 pat-
              tern %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, 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 # 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 rep-
              resented by digits for this base.  For example, a base 2 conver-
              sion  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 preci-
              sion.  For floating point value patterns, precision is the  num-
              ber  of  precision digits.  For %c, precision defines the number
              of times to repeat the character being formatted.  For %s,  pre-
              cision defines the maximum number of characters to output.

       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 @_

              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-alphanu-
              meric 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  string  extfdata to be passed to the extension
              function Sffmt_t.extf.  Parentheses shall be balanced.  If extf-
              data is *, 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  char-
       acter  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 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 follow-
              ing the most recently used one will be  used.   The  pattern  %!
              (see below) cannot be used subsequent to a usage of pos$.

       *:     This discards the corresponding scanned item.

       width and base:
              width  defines  the  maximum  number  of  bytes to scan and base
              defines the base of an integral value being  scanned.   The  `.'
              (dot) notation also allows specifying a `*' (star) to obtain the
              value from the argument list. The below example specifies  scan-
              ning  4  bytes  to obtain the value of an integer in base 10. At
              the end of scanning, the variable v should have the value  1234.

                  sfsscanf("12345678","%.*.*d", 4, 10, &v);

       (extfdata):
              This  defines  a  string  extfdata to be passed to the extension
              function Sffmt_t.extf.  Parentheses shall be balanced.  If extf-
              data is *, the string is obtained from the argument list.

       flag:  This is #, I, or some sequence of h, l, and L.

              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 dou-
              ble 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 gen-
       eral  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  exam-
       ple,  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 distin-
       guishable.  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 changed 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 argu-
       ment is treated as follows:

       size == SF_UNBOUND:
              Sfio will pick a suitable buffer size.  If  buf  is  NULL,  Sfio
              will  also pick a suitable buffering scheme (such as memory map-
              ping.)  If buf is not NULL, its actual value is ignored but  the
              buffer  will  be  allocated  via malloc(3).  This can be used to
              avoid memory mapping.

       size > 0:
              This is the suggested size to use for buffering or  memory  map-
              ping.   If  buf  is  NULL,  Sfio  will pick a suitable buffering
              scheme as discussed above.  If buf is not  NULL,  then  buf  and
              size determine a buffer of the given size.

       size == 0:
              If  buf  is  NULL, the stream will be unbuffered.  If buf is not
              NULL, 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 synchro-
       nized.  If f is the base of a stream stack (see sfstack()), all stacked
       streams  are synchronized.  Note that a stacked stream can only be syn-
       chronized 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)
       This function polls a set of streams to see if I/O  operations  can  be
       performed  on  them  without blocking.  This is useful for multiplexing
       I/O over a set of streams.  If a stream has a discipline, the exception
       function  may  be  called  before  and  after the stream is polled (see
       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 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.

       flist and n:
              flist is an array of n streams to be polled.  Upon return, ready
              streams are moved to the front of flist  in  the  same  relative
              order.

       timeout:
              This  defines  an  elapse time in milliseconds to wait to see if
              any stream is ready for I/O.  If timeout is  negative,  sfpoll()
              will  block until some stream become ready.  Note that SF_STRING
              and normal file streams never block and  are  always  ready  for
              I/O.  If a stream with discipline is being polled and its readi-
              ness is as yet undetermined (e.g., empty buffer,) the discipline
              exception  function will be called with SF_DPOLL before 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.

       f and poolf:
              If  f is NULL, sfpool() simply returns the head of the pool con-
              taining poolf.  If f is not NULL and poolf is NULL, f is deleted
              from  its  pool.  In this case, if no other stream from the same
              pool can become head, sfpool() will return NULL;  otherwise,  it
              returns  some  stream from the remainder of the pool.  If both f
              and poolf are not NULL, f is moved from  its  current  pool  (if
              any)  into  the  same  pool  with poolf.  In this case, poolf is
              returned.

       mode:  If poolf is already in a pool, mode is ignored.  Otherwise, mode
              should  be 0 or SF_SHARE.  A SF_SHARE pool contains streams with
              SF_WRITE mode.  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.

     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 disci-
       pline 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.
       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.  sfdisc() returns NULL on
       failure.

       Note that a discipline can be used only on one stream at  a  time.   An
       application  should  take  care to allocate different discipline struc-
       tures 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;

       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  ear-
       lier  on  the  stack.   Note  that  a  file  stream always has read(2),
       write(2), lseek(2) and NIL(Sfexcept_f) as  the  logical  bottom  disci-
       pline.   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  dur-
       ing  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  section  THREAD  SAFETY).
              Rather,  the  stream  was  frozen  by certain operations such as
              sfreserve() or sfstack().  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_NEW is
              raised for a stream about  to  be  closed  to  be  renewed  (see
              sfnew()).  SF_CLOSING is raised for a stream about to be closed.
              SF_FINAL is raised after a stream has been closed and before its
              space  is  to be destroyed (see sfclose()).  For these events, a
              non-zero return value  from  (*exceptf)()  causes  sfclose()  to
              return immediately with the same value.

       SF_DPUSH, SF_DPOP, SF_DBUFFER:
              Events  SF_DPUSH  and  SF_DPOP  are  raised when a discipline is
              about to be pushed or popped.  (Sfdisc_t*)value is 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 buffered data.
              If the return value of exceptf is  positive,  the  push  or  pop
              operation  will  continue  normally; otherwise, sfdisc() returns
              failure.

       SF_DPOLL:
              This event is raised by sfpoll() 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 block-
              ing.   A  zero return value means that sfpoll() should query the
              underlying file descriptor.  A positive return value means  non-
              blocking.  In  addition, this value will be a bit combination of
              SF_READ and SF_WRITE to indicate what I/O modes are ready.

       SF_READY:
              This event is raised by sfpoll() for  each  ready  stream.   The
              third  argument to the event handler is an integer composed with
              the two bits SF_READ and SF_WRITE to indicate  which  I/O  modes
              are ready.

       SF_SYNC, SF_PURGE:
              If SF_IOCHECK is set, these events are raised respectively for a
              sfsync() or sfpurge() call.  In each case, the respective  event
              is raised once before the appropriate operation (synchronization
              or purging) with  ((int)value)  being  1  and  once  after  with
              ((int)value)  being  0.   Note  that sfsync() is called for each
              SF_WRITE or SF_SHARE|SF_READ stream 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() immediate returns the same value.  Application-
       defined events should start from the value SF_EVENT so as to avoid con-
       fusion  with  system-defined events, sfraise() returns 0 on success and
       -1 on 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.

     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)
       These  functions provides safe methods for a discipline I/O function to
       invoke earlier discipline I/O functions and to properly  handle  excep-
       tions.   They  should  not  be  used  in any other context.  sfrd() and
       sfwr() return the number of bytes read or written.  sfsk() returns  the
       new  seek  position.   On  error, all three functions return a negative
       value which should be -1 or the value returned by  the  exception  han-
       dler.

   STREAM CONTROL
     int sfresize(Sfio_t* f, Sfoff_t size)
       This function resizes the stream fP so that its extent is size.  If the
       stream corresponds to a file, the file size is set to size via the sys-
       tem call ftruncate().  When a stream is made larger, the new data space
       is filled with zero's.  sfresize() returns 0 on success and a  negative
       value on failure.

     int sfset(Sfio_t* f, int flags, int set)
       This function sets control flags for the stream f.  It returns the pre-
       vious set of flags or 0 on error.

       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 exclu-
       sively 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.

       set == 0:
              If flags is zero, the current set of flags is  simply  returned.
              Note  that  when  a stream is first opened, not all of its flags
              are initialized yet (more  below).  If  flags  is  non-zero,  an
              attempt is made to turn off the specified flags.

       set != 0:
              If  flags is zero, the stream is initialized if not yet done so.
              Then the current set of flags is returned.   If  flags  is  non-
              zero, an attempt is made to turn on the specified flags.

     int sfsetfd(Sfio_t* f, int fd)
       This  function  changes  the  file descriptor of f.  Before a change is
       realized,  (*notify)(f,SF_SETFD,newfd)  (see  sfnotify())  is   called.
       sfsetfd() 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  using  dup(3) to a value larger or equal to fd.  Upon a
              successful change, the previous file descriptor will be  closed.
              If the current file descriptor is negative, it will be set to fd
              and the stream will be reinitialized.

       fd < 0:
              The stream is synchronized (see sfsync()) and its file  descrip-
              tor  will be set to this value.  Then, except for sfclose(), the
              stream will be inaccessible until a future sfsetfd() call resets
              the   file   descriptor   to   a   non-negative   value.   Thus,
              sfsetfd(f,-1) can be used to avoid closing the  file  descriptor
              of f when f is closed.

     Sfio_t* sfstack(Sfio_t* base, Sfio_t* top)
       This function stacks or unstacks stream.  Every stream stack is identi-
       fied by a base stream via which all I/O operations are performed.  How-
       ever,  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 condi-
       tion,  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 is NULL,  sfstack()
              does nothing and returns top.

       top:   If  this  is  SF_POPSTACK or (Sfio_t*)0, the stack is popped and
              sfstack() returns the popped stream.  Otherwise, top  is  pushed
              on top of the stack identified by base and sfstack() returns the
              base stream.

     Sfio_t* sfswap(Sfio_t* f1, Sfio_t* f2)
       This function swaps 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.  sfswap() returns f2 or  f1
       duplicate on success and NULL on failure.

   STREAM INFORMATION
     Sfoff_t sfsize(Sfio_t* f)
       This  function 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)
       This function returns the current I/O position in stream f.  Note  that
       if  f is SF_APPEND and a writing operation was just performed, the cur-
       rent 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)
       This function returns the string  or  buffer  length  for  sfreserve(),
       sfsetbuf(), and sfgetr().

     int sffileno(Sfio_t* f)
       This function returns the file descriptor of stream f.

     int sfstacked(Sfio_t* f)
       This function 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)
       This function restores the stream back to a normal state.   This  means
       clearing  locks  and possibly throwing away unprocessed data.  As such,
       this operation is unsafe and should be used with care.  For example, it
       may  be used before a long jump (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, int) )
       This sets a function (*notify)() to be called as (*notify)(f,type,file)
       on various stream events.  Arguments f and file are stream and  related
       file descriptor.  Argument type indicates the reason for the call:

       SF_NEW:
              f is being opened and file is the underlying file descriptor.

       SF_CLOSING (SF_CLOSE):
              f and file are being closed.

       SF_SETFD:
              The  file  descriptor  of  f  is  being  changed  to  file  (see
              sfsetfd().)

       SF_READ:
              An attempt to change f to read mode failed.

       SF_WRITE:
              An attempt to change f to write mode failed.

   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()
       This  function  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 functions 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  function acts directly on the file descriptor fd.  It does a com-
       bination of peeking on incoming data and a time-out  read.   Upon  suc-
       cess,  it  returns  the  number of bytes received.  A return value of 0
       means that the end-of-file condition has  been  detected.   A  negative
       value represents an error.

       buf, n:
              These define a buffer and its size to read data into.

       rsc:   If >=0, this defines a record separator.  See action for detail.

       tm:    If >=0, this defines a time interval in milliseconds to wait for
              incoming data.

       action:
              When  rsc  >= 0, the absolute value of action, r, determines the
              number of records to be read.  If action > 0, sfpkrd() will peek
              on  incoming data but will not read past it. Therefore, a future
              sfpkrd() or read(2) will see the same data again.  If action  ==
              0,  sfpkrd() will not peek.  If action < 0, there are two cases:
              if rsc < 0, sfpkrd() reads n bytes; otherwise, exactly r records
              will be read.  Note that, in the last case, reading records from
              an unseekable device may be slow if the underlying platform does
              not allow peeking on such a device.

   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  sen-
       sitive 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 initial-
       ization 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 defines a discipline if any.

   EXAMPLE DISCIPLINES
       The below functions create disciplines and insert them into  the  given
       streams f. These functions return 0 on success and -1 on failure.

   int sfdcdio(Sfio_t* f, size_t bufsize)
       This  creates a discipline that uses the direct IO feature available on
       file systems such as SGI's XFS to speed up IO.   The  argument  bufsize
       suggests a buffer size to use for data transfer.

   int sfdcdos(Sfio_t* f)
       This  creates a discipline to read DOS text files.  It basically trans-
       forms pairs of \r\n to \n.

   int sfdcfilter(Sfio_t* f, const char* cmd)
       This creates a discipline that sends data from f to the  given  command
       cmd to process, then reads back the processed data.

   int sfdcseekable(Sfio_t* f)
       This creates a discipline that makes an unseekable reading stream seek-
       able.

   int sfdcslow(Sfio_t* f)
       This creates a discipline that makes all Sfio operations return immedi-
       ately on interrupts. This is useful for dealing with slow devices.

   int  sfdcsubstream(Sfio_t*  f,  Sfio_t*  parent,  Sfoff_t  offset,  Sfoff_t
       extent)
       This creates a discipline that  makes  f  acts  as  if  it  corresponds
       exactly  to  the  subsection  of  parent  starting  at offset with size
       extent.

   int sfdctee(Sfio_t* f, Sfio_t* tee)
       This creates a discipline that copies to the stream tee any data  writ-
       ten to f.

   int sfdcunion(Sfio_t* f, Sfio_t** array, int n)
       This  creates  a discipline that makes f act as if it is the concatena-
       tion of the n streams given in array.

   int sfdclzw(Sfio_t* f)
       This creates a discipline that would decompress data in f.  The  stream
       f  should  have data from a source compressed by the Unix compress pro-
       gram.

   int sfdcgzip(Sfio_t* f, int opt)
       This creates a discipline for reading/writing data compressed by  zlib.
       The argument opt defines the optimization level.

   STDIO-COMPATIBILITY
       Sfio  provides  compatibility  functions  for all various popular Stdio
       implementations at source and binary level.  The source  Stdio-compati-
       bility interface provides the header file stdio.h that defines a set of
       macros or inlined functions to map Stdio calls to Sfio ones.  This map-
       ping  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  link-
       ing  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 have been added. In  particular,  exception  han-
       dlers  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 inte-
       ger 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.


AUTHORS

       Kiem-Phong Vo, kpv@research.att.com,
       David G. Korn, dgk@research.att.com, and
       Glenn S. Fowler, gsf@research.att.com.

                               01 February 2005                        SFIO(3)

Man(1) output converted with man2html