[comp.os.minix] Minix stdio - 2 of 7

cechew@bruce.OZ (Earl Chew) (09/04/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 7)."
# Contents:  stdiolib.h _allocbu.c _cleanup.c _filbuf.c _file.c
#   _flsbuf.c _fopen.c _slot.c
# Wrapped by cechew@bruce on Mon Sep  4 12:50:13 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'stdiolib.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'stdiolib.h'\"
else
echo shar: Extracting \"'stdiolib.h'\" \(5997 characters\)
sed "s/^X//" >'stdiolib.h' <<'END_OF_FILE'
X/*			s t d i o l i b
X *
X * This include file is used by the stdio code. It provides some
X * useful macro definitions that make the code a bit easier to
X * write.
X *
X * Edit History:
X *
X * 03-Sep-1989	Added BUFFERSIZE() and altered UNUSEDINBUFFER() to
X *		accommodate line buffered streams. Added PUTC()
X *		and NPUTC() for non line buffered streams.
X */
X
X#include <stdio.h>
X
X#ifdef	MINIX
Xextern void (*__cleanup) Prototype(());	/* pointer to cleanup function */
X#endif
X
Xvoid _cleanup	Prototype(());		/* cleanup function */
Xint _allocbuf	Prototype((FILE *));	/* internal buffering */
Xint _fopen	Prototype((char *, char *, int, short *)); /* fopen assist */
XFILE **_slot	Prototype((FILE *));	/* find a slot */
XFILE *_file	Prototype((FILE *, int, short)); /* initialise FILE */
X
X/* Flag manipulation macros */
X
X#define TESTFLAG(f,x)	(((f)->_flag & (x)) != 0)
X#define SETFLAG(f,x)	((f)->_flag |= (x))
X#define CLEARFLAG(f,x)	((f)->_flag &= ~(x))
X#define GETFLAG(f,x)	((f)->_flag & (x))
X#define TOGGLEFLAG(f,x)	((f)->_flag ^= (x))
X
X/* Add _cleanup to exit code
X *
X * Check is _cleanup needs to be added to the exit code. If so then
X * add it. This macro is run time system dependent.
X */
X#if	defined(MINIX)
X# define SETCLEANUP() { if (__cleanup == NULL) __cleanup = _cleanup; }
X#endif
X#if	!defined(SETCLEANUP)
X# define SETCLEANUP()
X#endif
X
X/* Putc for non buffered streams
X *
X * This version of putc is explicitly for unbuffered streams. A
X * call is made directly to the buffer flushing code.
X */
X#define NPUTC(x,p) ( _flsbuf((x),(p)) )
X
X/* Putc without line buffering
X *
X * This version of putc() should be exactly the same as that declared
X * in stdio.h except that it shouldn't worry about line buffering.
X */
X#define PUTC(x,p) ( \
X  (p)->_ptr < (p)->_base + (p)->_bufsiz \
X  ? *(p)->_ptr++ = (unsigned char)(x) \
X  : _flsbuf((x),(p)) \
X)
X 
X/* Initialise an output buffer
X *
X * This macro uses _base and _bufsiz to initialise _ptr and _end. _ptr
X * will be set to point at the base of the buffer. _end will be set
X * to point at one past the end of the buffer if the stream is buffered
X * otherwise it will point at the base of the buffer. Line buffered
X * streams are considered to be fully buffered.
X */
X#define INITWRITEBUFFER(f) ( \
X  (f)->_end = ((f)->_ptr = (f)->_base) + \
X              (TESTFLAG(f, _IONBF | _IOLBF) ? 0 : (f)->_bufsiz) \
X)
X
X/* Initialise a buffer to call either _flsbuf or _filbuf
X *
X * This macro initialises the buffer by setting _end and _ptr to
X * _base. This will force the next putc or getc to call the
X * appropriate routine. This is used by the r+, w+ and a+ modes
X * of access.
X */
X#define FLUSHNEXTACCESS(f) ( \
X  (f)->_end = (f)->_ptr = (f)->_base \
X)
X
X/* Initialise an output buffer to call _flsbuf
X *
X * This macro initialises the output buffer setting _ptr to _base.
X * _end is set to be _base so that _flsbuf will be called on the
X * next putc.
X */
X#define FLUSHNEXTWRITE(f) ( \
X  (f)->_end = (f)->_ptr = (f)->_base \
X)
X
X/* Initialise an input buffer
X *
X * This macro empties an input buffer. It uses _base to initialise
X * _ptr and sets _end to point to the high water mark of the buffer.
X *
X * If the argument v is zero, this macro must have the same effect
X * as a call to FLUSHNEXTACCESS(f).
X */
X#define INITREADBUFFER(f, v) ( \
X  (f)->_end = ((f)->_ptr = (f)->_base) + (v) \
X)
X
X/* Initialise a buffer
X *
X * This macro will empty a buffer. It will decide whether it is
X * an input or output buffer by examining _IOWRITE. In the case
X * of a read, _ptr is set so that _filbuf will be called when
X * the stream is next read. In the case of a write, the
X * buffer is initialised so that it can be used for writing.
X *
X * Note that streams opened for update will be set as if they
X * were opened for reading.
X */
X#define INITBUFFER(f) ( \
X  TESTFLAG((f), _IOWRITE) ? INITWRITEBUFFER((f)) \
X			  : INITREADBUFFER((f), 0) \
X)
X
X/* Buffer size
X *
X * Return the size of the output buffer. This will return rubbish
X * for unbuffered streams. Line and fully buffered streams will
X * have the true buffer size returned.
X */
X#define BUFFERSIZE(f) ( (f)->_bufsiz )
X
X/* Bytes left in input buffer
X *
X * This macro returns the number of bytes left in an input buffer.
X */
X#define BYTESINREADBUFFER(f) ( (f)->_end - (f)->_ptr )
X
X/* Bytes written in output buffer
X *
X * This macro returns the number of bytes left in an output buffer.
X */
X#define BYTESINWRITEBUFFER(f) ( (f)->_ptr - (f)->_base )
X
X/* Unused bytes in output buffer
X *
X * This macro returns the number of unused bytes in an output buffer.
X * Unbuffered streams will return rubbish. Line and fully buffered streams
X * will have the amount of space remaining returned.
X */
X#define UNUSEDINWRITEBUFFER(f) ( (f)->_bufsiz - ((f)->_ptr - (f)->_base) )
X
X/* Get pointer into write buffer
X *
X * This macro gets the pointer into the write buffer.
X */
X#define GETWRITEPTR(f) ( (f)->_ptr )
X
X/* Set pointer into write buffer
X *
X * This macro sets the pointer into the write buffer.
X */
X#define SETWRITEPTR(f,p) ( (f)->_ptr = (p) )
X
X/* Get pointer into read buffer
X *
X * This macro gets the pointer into the read buffer.
X */
X#define GETREADPTR(f) ( (f)->_ptr )
X
X/* Set pointer into read buffer
X *
X * This macro sets the pointer into the read buffer.
X */
X#define SETREADPTR(f,p) ( (f)->_ptr = (p) )
X
X/* Check if buffering has been set
X *
X * Return true if buffering has already been set. A stream
X * set for unbuffered output is considered to have had
X * its buffering set.
X */
X#define HASBUFFER(f) ( (f)->_base != 0 )
X
X/* Unroll a loop
X *
X * Assume that the loop must execute at least once. The first argument
X * is the name of the loop control variable. The second argument is
X * the expression to be placed in the loop body. The control variable
X * should be unsigned, otherwise the right shift might propagate the sign
X * bit. The caller must also provide the name of a unique label.
X */
X# define UNROLL_DO(l,v,x) { \
X  char t = (v); \
X  (v) = ((v)+1) >> 1; \
X  if (t & 1) goto l; \
X  do { x; l: x; } while (--(v)); \
X}
END_OF_FILE
if test 5997 -ne `wc -c <'stdiolib.h'`; then
    echo shar: \"'stdiolib.h'\" unpacked with wrong size!
fi
# end of 'stdiolib.h'
fi
if test -f '_allocbu.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'_allocbu.c'\"
else
echo shar: Extracting \"'_allocbu.c'\" \(989 characters\)
sed "s/^X//" >'_allocbu.c' <<'END_OF_FILE'
X/*			_ a l l o c b u f
X *
X * Allocate a buffer to a stream. This routine is designed to
X * be called from _flsbuf or _filbuf and hence has some hooks
X * in it for these routines.
X *
X * The routine initialises _bufsiz, _base, _ptr and _ctr. No
X * buffer is allocated if _IONBF is in effect. Line buffering
X * is engaged if the stream is attached to a terminal and the
X * stream is not opened for input.
X *
X * Patchlevel 1.0
X *
X * Edit History:
X */
X
X#include "stdiolib.h"
X
X/*LINTLIBRARY*/
X
Xint _allocbuf(fp)
X
XFILE *fp;				/* stream */
X
X{
X  int isatty();				/* channel is tty */
X  void *malloc();			/* memory allocator */
X
X  if (TESTFLAG(fp, _IONBF)) {
X    fp->_base   = &fp->_buf;
X    fp->_bufsiz = sizeof(fp->_buf);
X  }
X  else {
X    if ((fp->_base = (unsigned char *) malloc(BUFSIZ)) == NULL)
X      return -1;
X    SETFLAG(fp, _IOMYBUF);
X    fp->_bufsiz = BUFSIZ;
X    if (! TESTFLAG(fp, _IOREAD) && isatty(fp->_file))
X      SETFLAG(fp, _IOLBF);
X  }
X  INITBUFFER(fp);
X  return fp->_bufsiz;
X}
END_OF_FILE
if test 989 -ne `wc -c <'_allocbu.c'`; then
    echo shar: \"'_allocbu.c'\" unpacked with wrong size!
fi
# end of '_allocbu.c'
fi
if test -f '_cleanup.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'_cleanup.c'\"
else
echo shar: Extracting \"'_cleanup.c'\" \(355 characters\)
sed "s/^X//" >'_cleanup.c' <<'END_OF_FILE'
X/*			_ c l e a n u p
X *
X * Cleanly close and flush all output buffers. This ensures that
X * all streams are written out and closed.
X *
X * Patchlevel 1.0
X *
X * Edit History:
X */
X
X#include "stdiolib.h"
X
X/*LINTLIBRARY*/
X
Xvoid _cleanup()
X
X{
X  int i;				/* slot index */
X
X  for (i = 0; i < _NFILE; i++)
X    if (_iop[i] != NULL)
X      (void) fclose(_iop[i]);
X}
END_OF_FILE
if test 355 -ne `wc -c <'_cleanup.c'`; then
    echo shar: \"'_cleanup.c'\" unpacked with wrong size!
fi
# end of '_cleanup.c'
fi
if test -f '_filbuf.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'_filbuf.c'\"
else
echo shar: Extracting \"'_filbuf.c'\" \(1232 characters\)
sed "s/^X//" >'_filbuf.c' <<'END_OF_FILE'
X/*			_ f i l b u f
X *
X * Allocate and fill a stream IO buffer. This function is
X * intimately tied to the macro getc() in the stdio library.
X *
X * The function will get a buffer full of data then return
X * the first character. EOF is returned on error.
X *
X * Patchlevel 1.0
X *
X * Edit History:
X */
X
X#include "stdiolib.h"
X
X/*LINTLIBRARY*/
X
Xint _filbuf(fp)
X
XFILE *fp;				/* stream */
X
X{
X  int bytes;				/* bytes read */
X  int read();				/* read from channel */
X
X  if (GETFLAG(fp, (_IORW | _IOREAD | _IOWRITE)) == _IORW)
X    SETFLAG(fp, _IOREAD);
X
X  if (GETFLAG(fp, (_IOREAD | _IOERR | _IOEOF | _IOSTRING)) != _IOREAD)
X    return EOF;
X
X/* Locate buffer space for this stream */
X  if (fp->_base == NULL && _allocbuf(fp) < 0) {
X    SETFLAG(fp, _IOERR);
X    return EOF;
X  }
X
X/* Flush stdout if we're reading from stdin */
X  if (fp == stdin && TESTFLAG(stdout, _IOLBF))
X    (void) fflush(stdout);
X
X/* Read data into the buffer */
X  bytes = read(fp->_file, (char *) fp->_base,
X	       TESTFLAG(fp, _IONBF) ? 1 : fp->_bufsiz);
X
X  INITREADBUFFER(fp, bytes == -1 ? 0 : bytes);
X
X  switch (bytes) {
X  case -1: SETFLAG(fp, _IOERR); return EOF;
X  case 0:  SETFLAG(fp, _IOEOF); return EOF;
X  default:                      return *fp->_ptr++;
X  }
X}
END_OF_FILE
if test 1232 -ne `wc -c <'_filbuf.c'`; then
    echo shar: \"'_filbuf.c'\" unpacked with wrong size!
fi
# end of '_filbuf.c'
fi
if test -f '_file.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'_file.c'\"
else
echo shar: Extracting \"'_file.c'\" \(813 characters\)
sed "s/^X//" >'_file.c' <<'END_OF_FILE'
X/*				_ f i l e
X *
X * Allocate and initialise a FILE structure. If the pointer passed
X * to the function is NULL, a FILE will be allocated, otherwise
X * the one specified will be used. The function will return
X * a pointer to the FILE structure, or NULL if it fails.
X *
X * Patchlevel 1.0
X *
X * Edit History:
X */
X
X#include "stdiolib.h"
X
X/*LINTLIBRARY*/
X
XFILE *_file(fp, fd, flags)
X
XFILE *fp;				/* stream */
Xint fd;					/* channel */
Xshort flags;				/* flags */
X
X{
X  void *malloc();			/* memory allocator */
X
X/* Allocate a file structure */
X  if (fp == NULL)
X    fp = (FILE *) malloc(sizeof(*fp));
X
X/* Now initialise the structure */
X  if (fp != NULL) {
X    fp->_end    = NULL;
X    fp->_ptr    = NULL;
X    fp->_base   = NULL;
X    fp->_bufsiz = 0;
X    fp->_flag   = flags;
X    fp->_file   = fd;
X  }
X
X  return fp;
X}
END_OF_FILE
if test 813 -ne `wc -c <'_file.c'`; then
    echo shar: \"'_file.c'\" unpacked with wrong size!
fi
# end of '_file.c'
fi
if test -f '_flsbuf.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'_flsbuf.c'\"
else
echo shar: Extracting \"'_flsbuf.c'\" \(1850 characters\)
sed "s/^X//" >'_flsbuf.c' <<'END_OF_FILE'
X/*			_ f l s b u f
X *
X * Allocate and flush an output buffer. If no buffer has been
X * previously allocated, one will be allocated. This function
X * is intimately tied to the putc() macro in the stdio library.
X *
X * The function returns the character that was written to the
X * file, otherwise EOF on error.
X *
X * Patchlevel 1.0
X *
X * Edit History:
X */
X
X#include "stdiolib.h"
X
X/*LINTLIBRARY*/
X
Xint _flsbuf(c, fp)
X
Xchar c;					/* character to write */
XFILE *fp;				/* stream */
X
X{
X  char valid;				/* character still valid */
X  int length;				/* length of write */
X  int write();				/* write to channel */
X
X  if (TESTFLAG(fp, _IOSTRING))
X    return (unsigned char) (*fp->_ptr++ = c);
X
X  if (GETFLAG(fp, _IORW | _IOWRITE) == _IORW) {
X    if (! TESTFLAG(fp, _IOREAD))
X      SETFLAG(fp, _IOWRITE);
X    else {
X      if (TESTFLAG(fp, _IOEOF))
X	TOGGLEFLAG(fp, (_IOEOF | _IOREAD | _IOWRITE));
X    }
X  }
X
X  if (GETFLAG(fp, (_IOWRITE | _IOERR)) != _IOWRITE)
X    return EOF;
X
X  if (! HASBUFFER(fp)) {
X    if (_allocbuf(fp) < 0)
X      return EOF;
X    SETCLEANUP();
X  }
X
X/* Signal character valid if fully buffered output */
X  if (! TESTFLAG(fp, (_IONBF | _IOLBF)))
X    valid = 1;
X
X/* Do the write here if unbuffered or line buffered */
X  else {
X    if (TESTFLAG(fp, _IONBF)) {
X      *fp->_ptr++ = c;
X      valid = 0;
X    }
X    else if (fp->_ptr < fp->_base + fp->_bufsiz) {
X      if ((*fp->_ptr++ = c) == '\n')
X	valid = 0;
X      else
X	return (unsigned char) c;
X    }
X    else
X      valid = 1;
X  }
X
X/* Flush the current buffer load */
X  if ((length = fp->_ptr - fp->_base) != 0 &&
X      write(fp->_file, (char *) fp->_base, length) != length) {
X    SETFLAG(fp, _IOERR);
X    FLUSHNEXTWRITE(fp);
X    return EOF;
X  }
X
X/* Reset the buffer pointers --- unbuffered => not valid */
X  INITWRITEBUFFER(fp);
X  if (valid)
X    *fp->_ptr++ = c;
X
X  return (unsigned char) c;
X}
END_OF_FILE
if test 1850 -ne `wc -c <'_flsbuf.c'`; then
    echo shar: \"'_flsbuf.c'\" unpacked with wrong size!
fi
# end of '_flsbuf.c'
fi
if test -f '_fopen.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'_fopen.c'\"
else
echo shar: Extracting \"'_fopen.c'\" \(1892 characters\)
sed "s/^X//" >'_fopen.c' <<'END_OF_FILE'
X/*			_ f o p e n m o d e
X *
X * This function scans the mode with which a channel should be
X * opened. It then opens the channel using that mode and returns
X * the channel number to the caller. On error, the value -1 will
X * be returned.
X *
X * If the function succeeds, the flags argument will be set to the
X * mode with which the channel was opened. If the fd argument is
X * -1, the channel will be allocated, otherwise the specified channel
X * will be used.
X *
X * Patchlevel 1.0
X *
X * Edit History:
X */
X
X
X#include <fcntl.h>
X#include <errno.h>
X#include "stdiolib.h"
X
X/*LINTLIBRARY*/
X
Xextern int errno;			/* error code */
X
X#define CREATMODE 0666			/* mode to creat file */
X
Xint _fopen(name, mode, fd, flags)
X
Xchar *name;				/* name of file */
Xchar *mode;				/* mode to open */
Xint fd;					/* allocated channel */
Xshort *flags;				/* stdio flags */
X
X{
X  int rw;				/* basic mode */
X  int update;				/* read and write required */
X  int close();				/* close a file */
X  int creat();				/* create a file */
X  int open();				/* open a file */
X  long lseek();				/* seek to position */
X
X  rw     = *mode++;
X  update = *mode == '+';
X
X  switch (rw) {
X
X  case 'w':
X    *flags = update ? _IORW : _IOWRITE;
X    if (fd == -1) {
X      fd = creat(name, CREATMODE);
X      if (update && fd != -1 && (fd = close(fd)) != -1)
X        fd = open(name, O_RDWR);
X    }
X    break;
X
X  case 'r':
X    *flags = update ? _IORW : _IOREAD;
X    if (fd == -1)
X      fd = open(name, update ? O_RDWR : O_RDONLY);
X    break;
X
X  case 'a':
X    *flags = update ? _IORW : _IOWRITE;
X    if (fd == -1) {
X      fd = open(name, update ? O_RDWR : O_WRONLY);
X      if (fd == -1 && errno == ENOENT) {
X        fd = creat(name, CREATMODE);
X	if (update && fd != -1 && (fd = close(fd)) != -1)
X	  fd = open(name, O_RDWR);
X      }
X    }
X    if (fd != -1 && lseek(fd, 0L, SEEK_END) != -1)
X      break;
X
X  default:
X    fd = -1;
X  }
X
X  return fd;
X}
END_OF_FILE
if test 1892 -ne `wc -c <'_fopen.c'`; then
    echo shar: \"'_fopen.c'\" unpacked with wrong size!
fi
# end of '_fopen.c'
fi
if test -f '_slot.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'_slot.c'\"
else
echo shar: Extracting \"'_slot.c'\" \(533 characters\)
sed "s/^X//" >'_slot.c' <<'END_OF_FILE'
X/*				_ s l o t
X *
X * Locate the stream slot with the specified FILE pointer. If the
X * pointer is NULL, this routine will locate the first free slot.
X * The function will return a pointer to the slot, otherwise
X * NULL.
X *
X * Patchlevel 1.0
X *
X * Edit History:
X */
X
X#include "stdiolib.h"
X
X/*LINTLIBRARY*/
X
XFILE **_slot(fp)
X
XFILE *fp;				/* stream required */
X
X{
X  FILE **sp;				/* slot in table */
X  int n;				/* number of slots */
X
X  for (n = _NFILE, sp = _iop; *sp != fp; sp++)
X    if (--n < 0)
X      return NULL;
X
X  return sp;
X}
END_OF_FILE
if test 533 -ne `wc -c <'_slot.c'`; then
    echo shar: \"'_slot.c'\" unpacked with wrong size!
fi
# end of '_slot.c'
fi
echo shar: End of archive 2 \(of 7\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 7 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

trt@rti.UUCP (Thomas Truscott) (09/07/89)

From postnews Wed Sep  6 14:07:22 1989
> X * Cleanly close and flush all output buffers. This ensures that
> X * all streams are written out and closed.
	...
> X  for (i = 0; i < _NFILE; i++)
> X    if (_iop[i] != NULL)
> X      (void) fclose(_iop[i]);

The last line above should instead be:
> X      (void) fflush(_iop[i]);

The fclose() is an inefficiency dating back to the original stdio.
Unless MINIX is strangely different from standard UN*X
there is no need to *close* any files,
one simply needs to ensure that all streams are written out.

If you have a system that can trace system calls,
it becomes painfully evident that virtually every command ends with:
	close(0);
	close(1);
	close(2);
	exit(0);
There is no reason whatsoever for the close() calls!

Further evidence:
* I have changed the fclose to be fflush in various UN*X flavors,
the first time was in 1980.  Never a glitch.

* I have posted flames such as this one (including to this very newsgroup
a couple years ago), never a counterargument.

I admit this is a minor inefficiency, and I should not be preoccupied with it,
but the fix is so trivial that it is very frustrating
that no one has bothered to apply it.
HELP ME!
	Tom Truscott