Path: utzoo!attcan!uunet!cs.utexas.edu!rutgers!njin!princeton!notecnirp!nfs From: nfs@notecnirp.Princeton.EDU (Norbert Schlenker) Newsgroups: comp.os.minix Subject: Another new stdio (part 3 of 4) Summary: Stdio routines Message-ID: <19737@princeton.Princeton.EDU> Date: 30 Sep 89 16:22:19 GMT Sender: news@princeton.Princeton.EDU Reply-To: nfs@notecnirp.UUCP (Norbert Schlenker) Organization: Dept. of Computer Science, Princeton University Lines: 2762 echo x - _bufproc.c sed '/^X/s///' > _bufproc.c << '/' X/* --- _bufproc.c --- */ X/* Manages buffers for stdio */ X X#includeX#include X#include "stdiolib.h" X X/* _bufalloc - allocate a new buffer */ X/* Input: A valid stream with _io_bufsiz appropriately set */ X/* Output: Maybe a buffer, maybe not */ X Xvoid _bufalloc(stream) Xregister FILE *stream; X{ X if (stream->_io_bufsiz > 0 && X (stream->_io_buf = (unsigned char *) malloc(stream->_io_bufsiz)) != NULL) { X _io_setflag(stream, _IO_MYBUF); X stream->_io._io_ptr = stream->_io_buf; X } else { X stream->_io_bufsiz = 0; X stream->_io._io_char = EOF; X } X} X X/* _buffree - free an existing buffer if allocated by _bufalloc */ X/* Input: A valid stream */ X/* Output: A valid unbuffered stream */ X Xvoid _buffree(stream) Xregister FILE *stream; X{ X if (stream->_io_buf != NULL && _io_testflag(stream, _IO_MYBUF)) { X free(stream->_io_buf); X stream->_io_buf = NULL; X stream->_io_count = stream->_io_bufsiz = 0; X stream->_io._io_char = EOF; X _io_clearflag(stream, (_IO_LBUF | _IO_MYBUF)); X } X} / echo x - _cleanup.c sed '/^X/s///' > _cleanup.c << '/' X/* --- _cleanup.c --- */ X/* Flushes all buffers at program exit */ X/* Notes: exit() knows about this through __cleanup */ X X#include X#include "stdiolib.h" X Xvoid _cleanup() X{ X fflush(NULL); X} / echo x - _doprnt.c sed '/^X/s///' > _doprnt.c << '/' X/* --- _doprnt.c --- */ X/* Formatted printing workhorse */ X/* Notes: By default, this source compiles into _doprnt() */ X/* Floating point support is nominal */ X/* Cheap and nasty imitations of character type macros */ X/* Defining the symbol _KERNEL_ results in a routine named */ X/* printk() with the following differences: */ X/* No floating point support */ X/* An abbreviated version of the macro PUTC */ X X#include X X#ifndef _KERNEL_ X#include X#include X#include X#include "stdiolib.h" X#else Xextern int putc(); X#endif X X/* --- Constants --- */ X#define MAX_CONVERSION 512 /* largest possible conversion */ X X#define LEFT_JUSTIFY 0x0001 /* conversion flags */ X#define ALWAYS_SIGN 0x0002 X#define PREFIX_SPACE 0x0004 X#define ALTERNATE_FORM 0x0008 X#define ZERO_FILL 0x0010 X#define SHORT 0x0100 /* length modifiers */ X#define LONG 0x0200 X#define LONGDOUBLE 0x0400 X#define SIGNED 0x1000 /* signed integer argument */ X#define UPPER_CASE 0x4000 /* %X, %E, %G */ X X#define UNSPECIFIED (-1) /* for precision */ X X#define POINTER_BASE 16 /* constant for %p conversion */ X X/* --- Global state --- */ Xstatic struct { X#ifndef _KERNEL_ X FILE *stream; /* destination file */ X#endif X const char *fp; /* format pointer */ X va_list ap; /* argument pointer */ X int chars_written; /* number of characters written to the stream */ X} gs; X X/* --- Macros --- */ X#define TESTFLAG(x) (flags & (x)) X#define CLEARFLAG(x) (flags &= ~(x)) X#define SETFLAG(x) (flags |= (x)) X#define isdigit(c) ((c) >= '0' && (c) <= '9') X#define _tolower(c) ((c) - 'A' + 'a') X X#ifdef _KERNEL_ X#define EOF (-1) X#define PUTC(c, f) putc(c) X#else X/* --- Local unsafe fast version of putc: ignores line buffering; --- */ X/* --- has no rvalue; returns EOF from including routine on failure --- */ X#define PUTC(c, f) { \ X if (--(f)->_io_count >= 0) \ X *(f)->_io._io_ptr++ = c; \ X else \ X if (_flsbuf(c, f) == EOF) \ X return EOF; \ X } X#endif X X/* --- Format processors --- */ Xstatic int _parse_format(); /* parse formats */ Xstatic int _iconvert(); /* convert integers */ X#ifndef _KERNEL_ Xstatic int _fconvert(); /* convert floating point */ X#endif Xstatic int _sconvert(); /* convert strings */ Xstatic int _outcnvrt(); /* writes converted output to the stream */ X X#ifdef _KERNEL_ Xint printk(format, arg) X#else Xint _doprnt(stream, format, arg) XFILE *stream; X#endif Xregister const char *format; Xva_list arg; X{ X#ifndef _KERNEL_ X gs.stream = stream; X#endif X gs.fp = format; X gs.ap = arg; X gs.chars_written = 0; X X while (*format) { X if (*format != '%') { X PUTC(*format, stream); X format++; X gs.chars_written++; X } else { X gs.fp = ++format; X if (_parse_format() == EOF) X return EOF; X format = gs.fp; X } X } X X#ifndef _KERNEL_ X if (_io_testflag(stream, _IO_LBUF)) X _flspbuf(stream); X#endif X return gs.chars_written; X} X Xstatic int _parse_format() X{ X register const char *fp = gs.fp; X register int c; X int width; X int precision; X int flags; X union { X unsigned long l; X double d; X char c; X char *s; X } arg; X X if (*fp == '%') { X PUTC(*fp, gs.stream); X gs.fp++; X gs.chars_written++; X return 0; X } X X precision = UNSPECIFIED; X flags = 0; X X/* --- Extract the flags --- */ X c = *fp; X while (1) { X if (c == '-') X SETFLAG(LEFT_JUSTIFY); X else X if (c == '+') X SETFLAG(ALWAYS_SIGN); X else X if (c == ' ') X SETFLAG(PREFIX_SPACE); X else X if (c == '#') X SETFLAG(ALTERNATE_FORM); X else X if (c == '0') X SETFLAG(ZERO_FILL); X else X break; X c = *++fp; X } X X/* --- Extract the width --- */ X if (c == '*') { X width = va_arg(gs.ap, int); X ++fp; X } X else { X width = 0; X while (isdigit(*fp)) X width = 10 * width + *fp++ - '0'; X } X if (width < 0) { X SETFLAG(LEFT_JUSTIFY); X width = 0 - width; X } X X/* --- Extract the precision --- */ X if (*fp == '.') { X if (*++fp == '*') { X precision = va_arg(gs.ap, int); X ++fp; X } X else { /* PENDING - this should handle +/- signs */ X precision = 0; X while (isdigit(*fp)) X precision = 10 * precision + *fp++ - '0'; X } X if (precision < 0) X precision = UNSPECIFIED; X } X X/* --- Extract the length modifiers --- */ X if ((c = *fp) == 'h') { X SETFLAG(SHORT); X ++fp; X } else X if (c == 'l') { X SETFLAG(LONG); X ++fp; X } else X if (c == 'L') { X SETFLAG(LONGDOUBLE); X ++fp; X } X X/* --- Fetch argument type and save global format pointer --- */ X c = *fp; X gs.fp = ++fp; X if (c == 'D' || c == 'O' || c == 'U') { /* not ANSI */ X SETFLAG(LONG); X c = _tolower(c); X } X X/* --- Fetch the argument --- */ X switch (c) { X case 'd': X case 'i': X if (TESTFLAG(SHORT)) X arg.l = (long) va_arg(gs.ap, short); X else X if (TESTFLAG(LONG)) X arg.l = va_arg(gs.ap, long); X else X arg.l = (long) va_arg(gs.ap, int); X break; X case 'o': X case 'u': X case 'X': X case 'x': X if (TESTFLAG(SHORT)) X arg.l = (unsigned long) va_arg(gs.ap, unsigned short); X else X if (TESTFLAG(LONG)) X arg.l = va_arg(gs.ap, unsigned long); X else X arg.l = (unsigned long) va_arg(gs.ap, unsigned int); X break; X#ifndef _KERNEL_ X /* PENDING - This is probably wrong (due to argument widening). */ X case 'f': X case 'E': X case 'e': X case 'G': X case 'g': X if (TESTFLAG(LONG)) X arg.d = va_arg(gs.ap, double); X#ifdef __STDC__ X else X if (TESTFLAG(LONGDOUBLE)) X arg.d = (double) va_arg(gs.ap, long double); X#endif X else X arg.d = va_arg(gs.ap, float); X break; X#endif X case 'c': X arg.c = va_arg(gs.ap, int); X break; X case 's': X arg.s = va_arg(gs.ap, char *); X break; X case 'p': X arg.l = (unsigned long) va_arg(gs.ap, void *); X break; X default: X break; X } X X/* --- Resolve mutually exclusive flags --- */ X if (TESTFLAG(ALWAYS_SIGN)) X CLEARFLAG(PREFIX_SPACE); X if (TESTFLAG(LEFT_JUSTIFY)) X CLEARFLAG(ZERO_FILL); X X/* --- Produce some output (finally!) --- */ X switch (c) { X case 'd': X case 'i': X SETFLAG(SIGNED); X CLEARFLAG(ALTERNATE_FORM); X return _iconvert(arg.l, 10, width, precision, flags); X case 'o': X return _iconvert(arg.l, 8, width, precision, flags); X case 'u': X CLEARFLAG(ALTERNATE_FORM); X return _iconvert(arg.l, 10, width, precision, flags); X case 'X': X SETFLAG(UPPER_CASE); X case 'x': X return _iconvert(arg.l, 16, width, precision, flags); X#ifndef _KERNEL_ X case 'f': X return _fconvert(arg.d, width, precision, flags); X case 'E': X SETFLAG(UPPER_CASE); X case 'e': X return _fconvert(arg.d, width, precision, flags); X case 'G': X SETFLAG(UPPER_CASE); X case 'g': X return _fconvert(arg.d, width, precision, flags); X#endif X case 'c': X return _outcnvrt(&(arg.c), 1, width, flags); X case 's': X return _sconvert(arg.s, width, precision, flags); X case 'p': X SETFLAG(ALTERNATE_FORM); X return _iconvert(arg.l, POINTER_BASE, width, precision, flags); X case 'n': X *va_arg(gs.ap, int *) = gs.chars_written; X return 0; X default: X return EOF; X } X} X Xstatic int _iconvert(number, base, width, precision, flags) Xunsigned long number; Xint base; Xint width; Xint precision; Xint flags; X{ X char buf[MAX_CONVERSION]; X register char *p = &buf[MAX_CONVERSION - 1]; X register int digit; X int negative = 0; X X/* --- Set default precision (and annul zero fill if precision set) --- */ X if (precision == UNSPECIFIED) X precision = 1; X else X CLEARFLAG(ZERO_FILL); X X/* --- Get the sign right --- */ X if (TESTFLAG(SIGNED)) X if ((long) number < 0L) { X negative = 1; X number = (unsigned long) (0L - (long) number); X } X X/* --- Build the number back to front (at the back of the buffer) --- */ X while (--precision >= 0 || number != 0) { X digit = number % base; X if (digit < 10) X digit += '0'; X else X digit += (TESTFLAG(UPPER_CASE) ? 'A' : 'a') - 10; X *p-- = digit; X number /= base; X } X X/* --- Ensure enough precision --- */ X while (--precision >= 0) X *p-- = '0'; X X/* --- Zero fill if needed (but leave room for prefix and sign) --- */ X if (TESTFLAG(ZERO_FILL)) { X precision = width - (&buf[MAX_CONVERSION - 1] - p); X if (TESTFLAG(ALTERNATE_FORM)) X if (base == 8 && *(p+1) != '0') X precision--; X else X if (base == 16) X precision -= 2; X if (TESTFLAG(SIGNED)) X if (negative || TESTFLAG(ALWAYS_SIGN | PREFIX_SPACE)) X precision--; X while (--precision >= 0) X *p-- = '0'; X } X X/* --- Generate alternate forms for %o and %x --- */ X if (TESTFLAG(ALTERNATE_FORM)) { X if (base == 8 && *(p+1) != '0') X *p-- = '0'; X else X if (base == 16) { X *p-- = TESTFLAG(UPPER_CASE) ? 'X' : 'x'; X *p-- = '0'; X } X } X X/* --- Generate signs --- */ X if (TESTFLAG(SIGNED)) { X if (negative) X *p-- = '-'; X else { X if (TESTFLAG(ALWAYS_SIGN)) X *p-- = '+'; X else X if (TESTFLAG(PREFIX_SPACE)) X *p-- = ' '; X } X } X X p++; X return _outcnvrt(p, &buf[MAX_CONVERSION] - p, width, flags); X} X X#ifndef _KERNEL_ X Xstatic int _fconvert(arg, width, precision, flags) /* PENDING */ Xdouble arg; Xint width; Xint precision; Xint flags; X{ X/* --- Set default precision --- */ X if (precision == UNSPECIFIED) X precision = 6; X X/* --- What do you expect? --- */ X PUTC(' ', gs.stream); X PUTC('*', gs.stream); X PUTC('F', gs.stream); X PUTC('P', gs.stream); X PUTC('*', gs.stream); X PUTC(' ', gs.stream); X gs.chars_written += 6; X return 0; X} X X#endif X Xstatic int _sconvert(s, width, precision, flags) Xchar *s; Xint width; Xregister int precision; Xint flags; X{ X register char *p = s; X X if (precision == UNSPECIFIED) X precision = MAX_CONVERSION; X while (--precision >= 0 && *p) X p++; X return _outcnvrt(s, p - s, width, flags); X} X Xstatic int _outcnvrt(p, n, width, flags) Xregister char *p; /* start of converted field */ Xint n; /* number of characters in field */ Xint width; Xint flags; X{ X#ifndef _KERNEL_ X register FILE *stream = gs.stream; X#endif X int i; X X if (!TESTFLAG(LEFT_JUSTIFY)) /* pad to right justify */ X for (i = width - n ; --i >= 0; ) X PUTC(' ', stream); X X for (i = n; --i >= 0; p++) /* copy conversion result */ X PUTC(*p, stream); X X if (TESTFLAG(LEFT_JUSTIFY)) /* pad to left justify */ X for (i = width - n ; --i >= 0; ) X PUTC(' ', stream); X X gs.chars_written += (n > width) ? n : width; X return 0; X} / echo x - _doscan.c sed '/^X/s///' > _doscan.c << '/' X/* --- _doscan.c --- */ X/* Formatted input workhorse */ X/* Notes: Floating point support is nonexistent */ X X#include X#include X#include X#include X#include X#include "stdiolib.h" X X/* --- Constants --- */ X#define OK 0 /* return values from input eaters */ X#define INPUT_FAILURE EOF X#define MATCHING_FAILURE 1 X#define UNIMPLEMENTED 2 X X#define POINTER_BASE 16 /* constants for %p conversion */ X#define POINTER_TYPE '\0' X X#define SIGNED 0 /* constants for _strtol() */ X#define UNSIGNED 1 X X#define NO_NULL 0 /* constants for _strtostr() */ X#define ADD_NULL 1 X#define NO_COMPARE 0 X#define WHITE_COMPARE 1 X#define SET_COMPARE 2 X#define CHAR_SET_SIZE 128 X X/* --- Global state --- */ Xstatic struct { X int (*next_char)(); /* pointer to input function */ X union { X unsigned char *s; /* pointer to input string */ X FILE *f; /* pointer to input file */ X } src; X int input_char; /* buffer for next input character */ X const char *fp; /* format pointer */ X va_list ap; /* argument pointer */ X int chars_read; /* number of characters read from the stream (%n) */ X int items_converted; /* successful conversions */ X int items_assigned; /* successful assignments */ X int no_assign; /* if set, conversion is done but not assignment */ X int width; /* maximum input field width */ X char modifier; /* short/long modifier */ X} gs; X Xstatic char valid_char[CHAR_SET_SIZE]; X X/* --- Macros --- */ X#define BACKUP_INPUT(c) ((gs.input_char = (c)), (gs.chars_read--)) X X/* --- Input functions --- */ Xstatic int _i_string(); Xstatic int _i_stream(); X X/* --- Input processors --- */ Xstatic int _eat_white(), /* ' ' */ X _eat_one(), /* any literal character */ X _parse_format(), /* parse formats */ X _strtol(), /* convert integer formats */ X _strtod(), /* convert floating formats */ X _strtostr(); /* convert string formats */ X X Xint _doscan(is_string, source, format, arg) Xint is_string; Xvoid *source; Xconst char *format; Xva_list arg; X{ X register int c; X register int error = OK; X X gs.next_char = (is_string) ? _i_string : _i_stream; X gs.src.s = (unsigned char *) source; X gs.input_char = EOF; X gs.fp = format; X gs.ap = arg; X gs.items_converted = 0; X gs.items_assigned = 0; X X while ((c = (int) *gs.fp++) && (error == OK)) { X if (isspace(c)) X error = _eat_white(); X else X if (c == '%') X error = _parse_format(); X else X error = _eat_one(c); X } X if (gs.input_char != EOF && !is_string) X ungetc(gs.input_char, gs.src.f); X return (error == INPUT_FAILURE && gs.items_converted == 0) X ? EOF : gs.items_assigned; X} X Xstatic int _i_stream() X{ X int rc; X X gs.chars_read++; X if ((rc = gs.input_char) != EOF) { X gs.input_char = EOF; X return rc; X } X return getc(gs.src.f); X} X Xstatic int _i_string() X{ X int rc; X gs.chars_read++; X if ((rc = gs.input_char) != EOF) { X gs.input_char = EOF; X return rc; X } X if (*gs.src.s == '\0') X return EOF; X return (int) *gs.src.s++; X} X Xstatic int _parse_format() X{ X register int c; X X if ((c = *gs.fp++) == '%') X return _eat_one(c); X X gs.no_assign = 0; /* default: assign converted value */ X gs.width = INT_MAX; /* default: enormous maximum width */ X gs.modifier = '\0'; /* default: no long/short modifier */ X X if (c == '*') { X gs.no_assign = 1; X c = *gs.fp++; X } X if (isdigit(c)) { X gs.width = 0; X while (isdigit(c)) { /* PENDING - this should handle +/- signs */ X gs.width = 10 * gs.width + c - '0'; X c = *gs.fp++; X } X } X if (c == 'h' || c == 'l' || c == 'L') { X gs.modifier = c; X c = *gs.fp++; X } X if (c == 'D' || c == 'O' || c == 'U') { /* not ANSI */ X gs.modifier = 'l'; X c = _tolower(c); X } X switch (c) { X case 'd': X return _strtol(10, SIGNED); X case 'i': X return _strtol(0, SIGNED); X case 'o': X return _strtol(8, UNSIGNED); X case 'u': X return _strtol(10, UNSIGNED); X case 'x': X case 'X': X return _strtol(16, UNSIGNED); X case 'f': X case 'e': X case 'E': X case 'g': X case 'G': X return _strtod(); X case 's': X return _strtostr(WHITE_COMPARE, ADD_NULL); X case '[': X return _strtostr(SET_COMPARE, ADD_NULL); X case 'c': X if (gs.width == INT_MAX) X gs.width = 1; X return _strtostr(NO_COMPARE, NO_NULL); X case 'p': X return _strtol(POINTER_BASE, UNSIGNED); X case 'n': X if (!gs.no_assign) X *va_arg(gs.ap, int *) = gs.chars_read; X return OK; X default: X return MATCHING_FAILURE; X } X} X X/* --- Input processors --- */ Xstatic int _eat_white() X{ X register int c; X X while ((c = (*gs.next_char)()) != EOF && isspace(c)) ; X BACKUP_INPUT(c); X return (c == EOF) ? INPUT_FAILURE : OK; X} X Xstatic int _eat_one(c) Xint c; X{ X register int i; X X if ((i = (*gs.next_char)()) == EOF) X return INPUT_FAILURE; X return (i == c) ? OK : MATCHING_FAILURE; X} X Xstatic int _strtol(base, is_unsigned) Xint base; Xint is_unsigned; X{ X register int c; X long result = 0L; X int negative = 0; X int original_width = gs.width; X int digit; X X if (_eat_white() == INPUT_FAILURE) X return INPUT_FAILURE; X X/* --- Handle signs --- */ X c = (*gs.next_char)(); X if (c == '+' || c == '-') { X negative = (c == '-'); X gs.width--; X c = (*gs.next_char)(); X } X X/* --- Determine base if unknown --- */ X if (base == 0) { X base = 10; X if (c == '0') { X base = 8; X gs.width--; X c = (*gs.next_char)(); X if (c == 'x' || c == 'X') { X base = 16; X gs.width--; X c = (*gs.next_char)(); X } X } X } X X/* --- Discard 0x or 0X prefix if hexadecimal --- */ X else X if (base == 16 && c == '0') { X gs.width--; X c = (*gs.next_char)(); X if (c == 'x' || c == 'X') { X gs.width--; X c = (*gs.next_char)(); X } X } X X/* --- Convert the number --- */ X while (gs.width-- > 0 && c != EOF) { X if (isdigit(c)) X digit = c - '0'; X else X digit = c - (isupper(c) ? 'A' : 'a') + 10; X if (digit < 0 || digit >= base) X break; X result = base * result + digit; X c = (*gs.next_char)(); X } X gs.width++; X BACKUP_INPUT(c); X gs.items_converted++; X X/* --- Sign the result --- */ X if (negative) X result = 0L - result; X X/* --- Stash the result --- */ X if (gs.width == original_width) X return (c == EOF) ? INPUT_FAILURE : MATCHING_FAILURE; X if (!gs.no_assign) { X if (is_unsigned) { X if (gs.modifier == 'l') X *va_arg(gs.ap, unsigned long *) = (unsigned long) result; X else X if (gs.modifier == 'h') X *va_arg(gs.ap, unsigned short *) = (unsigned short) result; X else X *va_arg(gs.ap, unsigned int *) = (unsigned int) result; X } else { X if (gs.modifier == 'l') X *va_arg(gs.ap, long *) = (long) result; X else X if (gs.modifier == 'h') X *va_arg(gs.ap, short *) = (short) result; X else X *va_arg(gs.ap, int *) = (int) result; X } X gs.items_assigned++; X } X return OK; X} X Xstatic int _strtod() /* PENDING */ X{ X return UNIMPLEMENTED; X} X Xstatic int _strtostr(method, string_end) Xint method; Xint string_end; X{ X int negated; X register int c; X register unsigned char *target; X int original_width = gs.width; X X/* --- Set up for comparisons --- */ X if (method == WHITE_COMPARE) { X if (_eat_white() == INPUT_FAILURE) X return INPUT_FAILURE; X } else X if (method == SET_COMPARE) { X negated = 0; X if (*gs.fp == '^') { X negated = 1; X gs.fp++; X } X memset(valid_char, negated, CHAR_SET_SIZE); X negated = !negated; X c = EOF; X do { X register int next = gs.fp[1]; X X if (c != EOF && *gs.fp == '-' && next != ']') { X for (c++ ; c <= next; c++) X valid_char[c] = negated; X gs.fp++; X c = EOF; X } X else X valid_char[c = *gs.fp] = negated; X } while (*++gs.fp != ']'); X gs.fp++; X } X X/* --- Copy the string --- */ X if (!gs.no_assign) X target = va_arg(gs.ap, unsigned char *); X c = (*gs.next_char)(); X while (gs.width-- > 0 && c != EOF) { X if (method == WHITE_COMPARE && isspace(c)) X break; X else X if (method == SET_COMPARE && !valid_char[c]) X break; X if (!gs.no_assign) X *target++ = c; X c = (*gs.next_char)(); X } X gs.width++; X BACKUP_INPUT(c); X gs.items_converted++; X X/* --- Finish up --- */ X if (!gs.no_assign) { X if (string_end == ADD_NULL) X *target = '\0'; X gs.items_assigned++; X } X if (gs.width == original_width) X return (c == EOF) ? INPUT_FAILURE : MATCHING_FAILURE; X return OK; X} / echo x - _filbuf.c sed '/^X/s///' > _filbuf.c << '/' X/* --- _filbuf.c --- */ X/* Read from the file associated with a stream */ X/* Notes: Will flush stdout if reading stdin and both are tty's */ X X#include X#include X#include X#include "stdiolib.h" X Xint _filbuf(stream) Xregister FILE *stream; X{ X int c; X X/* --- Virgin stream: set things up --- */ X if (stream->_io_count < _IO_NEVER) X _i_init(stream); X X/* --- Read a bufferful --- */ X if (stream->_io_bufsiz == 0) { /* unbuffered */ X if (stream->_io._io_char != EOF) { /* check for ungetc()ed char */ X c = stream->_io._io_char; X stream->_io._io_char = EOF; X return c; X } X stream->_io_count = read(stream->_io_fd, (char *) &c, 1); X } else { X stream->_io_count = X read(stream->_io_fd, (char *) stream->_io_buf, stream->_io_bufsiz); X stream->_io._io_ptr = stream->_io_buf; X } X if (stream->_io_count < 0) { X _io_setflag(stream, _IO_ERR); X return EOF; X } X if (stream->_io_count == 0) { X _io_setflag(stream, _IO_EOF); X if (_io_testflag(stream, _IO_UPDATE)) X _io_clearflag(stream, (_IO_READ | _IO_WRITE)); X return EOF; X } X stream->_io_count--; X return (stream->_io_bufsiz == 0) X ? c X : *stream->_io._io_ptr++; X} / echo x - _flsbuf.c sed '/^X/s///' > _flsbuf.c << '/' X/* --- _flsbuf.c --- */ X/* Flushes full stream buffers */ X/* Notes: #define _V7 gives support for append mode when kernel doesn't*/ X X#include X#include X#include X#include "stdiolib.h" X Xint _flsbuf(c, stream) /* stream must be valid, writable */ Xint c; Xregister FILE *stream; X{ X/* --- Virgin stream: set things up --- */ X if (++stream->_io_count == _IO_NEVER) X _o_init(stream); X X#ifdef _V7 X/* --- Append kludge --- */ X if (_io_testflag(stream, _IO_APPEND) && _io_testflag(stream, _IO_NEEDSEEK)) { X lseek(stream->_io_fd, 0L, SEEK_END); X _io_clearflag(stream, _IO_NEEDSEEK); X } X#endif X X/* --- Unbuffered stream: write one character --- */ X if (stream->_io_bufsiz == 0) { X stream->_io_count = 0; X if (write(stream->_io_fd, (char *) &c, 1) != 1) { X _io_setflag(stream, _IO_ERR); X return EOF; X } X return c; X } X X/* --- Buffered stream: write out a full buffer --- */ X if (stream->_io_count == 0) { X if (write(stream->_io_fd, (char *) stream->_io_buf, stream->_io_bufsiz) X != stream->_io_bufsiz) { X _io_setflag(stream, _IO_ERR); X return EOF; X } X stream->_io._io_ptr = stream->_io_buf; X stream->_io_count = stream->_io_bufsiz; X } X X/* --- Buffered stream: start filling new buffer --- */ X --stream->_io_count; X *stream->_io._io_ptr++ = c; X X/* --- Line buffered stream: flush a partial buffer --- */ X if (_io_testflag(stream, _IO_LBUF) && c == '\n' && _flspbuf(stream) != 0) X return EOF; X X return c; X} / echo x - _flspbuf.c sed '/^X/s///' > _flspbuf.c << '/' X/* --- _flspbuf.c --- */ X/* Flushes partially full stream buffers */ X/* Notes: #define _V7 gives support for append mode when kernel doesn't*/ X X#include X#include X#include X#include "stdiolib.h" X Xint _flspbuf(stream) Xregister FILE *stream; X{ X register int bytes = stream->_io_bufsiz - stream->_io_count; X X#ifdef _V7 X if (_io_testflag(stream, _IO_APPEND) && _io_testflag(stream, _IO_NEEDSEEK)) { X lseek(stream->_io_fd, 0L, SEEK_END); X _io_clearflag(stream, _IO_NEEDSEEK); X } X#endif X X if (write(stream->_io_fd, stream->_io_buf, bytes) != bytes) { X _io_setflag(stream, _IO_ERR); X return EOF; X } X stream->_io._io_ptr = stream->_io_buf; X stream->_io_count = stream->_io_bufsiz; X return 0; X} / echo x - _iobdata.c sed '/^X/s///' > _iobdata.c << '/' X/* --- _iobdata.c --- */ X/* Data structures for stdio */ X X#include X#include "stdiolib.h" X X#define STDIN {0, _IO_READ | _IO_LBUF | _IO_TTY, _IO_NEVER, BUFSIZ, NULL} X#define STDOUT {1, _IO_WRITE | _IO_LBUF | _IO_TTY, _IO_NEVER, BUFSIZ, NULL} X#define STDERR {2, _IO_WRITE, _IO_NEVER, 0, NULL} X XFILE _iob[FOPEN_MAX] = {STDIN, STDOUT, STDERR }; Xchar _iov[FOPEN_MAX] = {1, 1, 1 }; X XFILE *_io_f; /* for fgetc()/fputc() macros */ Xint _io_c; X Xint (*__tmprm)(); /* pointer for cleaning up tmpfile()'s trash */ / echo x - _iobproc.c sed '/^X/s///' > _iobproc.c << '/' X/* --- _iobproc.c --- */ X/* Manages allocation of FILEs */ X/* Notes: Includes _ioballoc(), _iobfree(), and _iobindex() */ X X#include X#include "stdiolib.h" X X#define INVALID (-1) X Xextern char _iov[]; X X/* _ioballoc - allocates an i/o block (FILE) */ X/* Input: none */ X/* Output: Pointer to a currently free FILE */ X XFILE *_ioballoc() X{ X register int i; X X for (i = 0; i < FOPEN_MAX; i++) X if (_iov[i] == 0) { X _iov[i] = 1; X return &_iob[i]; X } X return NULL; X} X X/* _iobfree - frees an i/o block (FILE) */ X/* Input: A stream */ X/* Output: None (but FILE is freed) */ X Xvoid _iobfree(stream) XFILE *stream; X{ X register int i; X X if ((i = _iobindex(stream)) != INVALID) X _iov[i] = 0; X} X X/* _iobindex - finds the index of a given stream */ X/* Input: A stream */ X/* Output: Index of the stream if valid, -1 otherwise */ X Xint _iobindex(stream) Xregister FILE *stream; X{ X register FILE *f; X register int i; X X for (i = 0, f = _iob; i < FOPEN_MAX; i++, f++) X if (stream == f) X return _iov[i] ? i : INVALID; X return INVALID; X} / echo x - _ioinit.c sed '/^X/s///' > _ioinit.c << '/' X/* --- _ioinit.c --- */ X/* Checks for a whole bunch of initial conditions on i/o */ X/* Notes: Includes _i_init() and _o_init() */ X X#include X#include X#include X#include "stdiolib.h" X Xvoid _i_init(stream) Xregister FILE *stream; X{ X/* --- Allocate a buffer if necessary --- */ X if (stream->_io_buf == NULL && stream->_io_bufsiz != 0) X _bufalloc(stream); X stream->_io_count = 0; X _io_setflag(stream, _IO_READ); X X/* --- Line buffering: check once and turn it off if stream is not a tty --- */ X if (_io_testflag(stream, _IO_TTY)) { X if (_io_testflag(stream, _IO_LBUF) && !isatty(stream->_io_fd)) X _io_clearflag(stream, _IO_LBUF); X _io_clearflag(stream, _IO_TTY); X } X X/* --- Flush stdout (if _IOLBF) when reading from _IONBF/_IOLBF stream --- */ X if (stream->_io_bufsiz == 0 || _io_testflag(stream, _IO_LBUF)) X if (_io_testflag(stdout, _IO_LBUF)) X fflush(stdout); X} X Xvoid _o_init(stream) Xregister FILE *stream; X{ X/* --- Register _cleanup() so that exit() can find it --- */ X if (__cleanup == NULL) X __cleanup = _cleanup; X X/* --- Allocate a buffer if necessary --- */ X if (stream->_io_buf == NULL && stream->_io_bufsiz != 0) X _bufalloc(stream); X stream->_io_count = stream->_io_bufsiz; X _io_setflag(stream, _IO_WRITE); X X/* --- Line buffering: check once and turn it off if stream is not a tty --- */ X if (_io_testflag(stream, _IO_TTY)) { X if (_io_testflag(stream, _IO_LBUF) && !isatty(stream->_io_fd)) X _io_clearflag(stream, _IO_LBUF); X _io_clearflag(stream, _IO_TTY); X } X} / echo x - _valmode.c sed '/^X/s///' > _valmode.c << '/' X/* --- _valmode.c --- */ X/* Validates mode for stream opening functions */ X X/* Input: mode - a mode string for fopen() */ X/* Output: EOF for a bad string */ X/* 0 for a good string without a '+' */ X/* _IO_UPDATE for a good string with a '+' */ X X#include X#include "stdiolib.h" X Xint _valmode(mode) Xregister const char *mode; X{ X register int rc = 0; X X/* --- Check basic mode --- */ X if (*mode != 'r' && *mode != 'w' && *mode != 'a') X return EOF; X if (*++mode == '\0') X return rc; X X/* --- Check second character of mode string --- */ X if (*mode == '+') X rc = _IO_UPDATE; X else X if (*mode == 'b') /* ANSI mandated - of no use here */ X ; X else X return EOF; X if (*++mode == '\0') X return rc; X X/* --- Check third character of mode string --- */ X if (*mode == '+' && rc != _IO_UPDATE) X rc = _IO_UPDATE; X else X if (*mode == 'b' && rc == _IO_UPDATE) X ; X else X return EOF; X if (*++mode == '\0') X return rc; X X/* --- Mode string too long --- */ X return EOF; X} / echo x - atexit.c sed '/^X/s///' > atexit.c << '/' X/* Copyright 1988 Frank Wortner X** You may reproduce and use this software as long as you X** do not delete this notice from the source code. X*/ X X#define MAXFN 33 /* Maximum number of functions registerable X (includes 1 for __cleanup) */ X Xtypedef void (*PFN)(); /* Pointer to FuNction */ X Xextern PFN __cleanup; /* imported from exit.c */ X Xstatic PFN fn[MAXFN]; /* addresses of functions stored here */ Xstatic int nfn = -1; /* number of functions registered less one */ X Xstatic void checkcleanup(), run_atexit(); X Xint atexit(fun) XPFN fun; X{ X if (nfn == -1) /* Do we need to check for stdio? */ X checkcleanup(); X if (nfn >= MAXFN) /* Run out of room yet? */ X return (-1); X fn[++nfn] = fun; /* Save the address of the subr. */ X return (0); X} X X/* Check to see if stdio is used. If yes register stdio's cleanup routine X** with the atexit() system and then force stdio to call run_atexit(). X*/ Xstatic void checkcleanup() X{ X if (__cleanup) /* stdio used ? */ X fn[++nfn] = __cleanup; X __cleanup = run_atexit; X} X X/* Run the requested subroutines */ Xstatic void run_atexit() X{ X while (nfn >= 0) { X (*fn[nfn--])(); X } X} / echo x - exit.c sed '/^X/s///' > exit.c << '/' X#include X#include "lib.h" X XPUBLIC void (*__cleanup)() = NULL; X XPUBLIC int exit(status) Xint status; X{ X if (__cleanup != NULL) X (*__cleanup)(); X return callm1(MM, EXIT, status, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); X} X XPUBLIC int _exit(status) Xint status; X{ X return callm1(MM, EXIT, status, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); X} / echo x - fclose.c sed '/^X/s///' > fclose.c << '/' X/* --- fclose.c --- */ X/* Closes a stream and its associated file */ X X#include X#include X#include X#include "stdiolib.h" X#undef fclose X X#define _IO_ERRVAL EOF X Xextern int (*__tmprm)(); X Xint fclose(stream) Xregister FILE *stream; X{ X _io_assert(_io_valid(stream)); X X if (fflush(stream)) X return _IO_ERRVAL; X _buffree(stream); X if (close(stream->_io_fd)) X return _IO_ERRVAL; X if (_io_testflag(stream, _IO_TEMP)) X if (__tmprm) (*__tmprm)(_iobindex(stream)); X _iobfree(stream); X return 0; X} / echo x - fdopen.c sed '/^X/s///' > fdopen.c << '/' X/* --- fdopen.c --- */ X/* Associates a stream with an already open file */ X/* Notes: Open mode of file must agree with open mode specified */ X/* #define _V7 gives support for append mode when kernel doesn't*/ X/* POSIX function */ X X#include X#include X#include X#include "stdiolib.h" X#undef fdopen X X#define _IO_ERRVAL NULL X X XFILE *fdopen(fd, mode) Xint fd; Xconst char *mode; X{ X int flags; X register FILE *stream; X X _io_assert(fd >= 0); X X if ((flags = _valmode(mode)) < 0) X return _IO_ERRVAL; X if ((stream = _ioballoc()) == NULL) X return _IO_ERRVAL; X X switch (*mode) { X case 'r': X flags |= _IO_READ; X break; X case 'w': X flags |= _IO_WRITE; X break; X case 'a': X#ifdef _V7 X flags |= _IO_WRITE | _IO_APPEND; X lseek(fd, 0L, SEEK_END); X#else X flags |= _IO_WRITE; X#endif X break; X default: X _iobfree(stream); X return _IO_ERRVAL; X } X X if (isatty(fd)) X flags |= _IO_LBUF; X stream->_io_fd = fd; X stream->_io_flags = flags; X stream->_io_count = _IO_NEVER; X stream->_io_bufsiz = BUFSIZ; X return stream; X} / echo x - fflush.c sed '/^X/s///' > fflush.c << '/' X/* --- fflush.c --- */ X/* Flushes a stream's buffer */ X/* Notes: Given a NULL pointer as a stream, fflush() flushes all streams. */ X/* Flushing a readable stream discards the buffer contents */ X/* and lseek()'s the underlying file to meet expectations. */ X X#include X#include X#include X#include "stdiolib.h" X#undef fflush X X#define _IO_ERRVAL EOF X Xextern char _iov[]; Xstatic int _fflush _PROTO((FILE *stream)); X Xint fflush(stream) Xregister FILE *stream; X{ X register int i; X register int rc; X X if (stream == NULL) { X rc = 0; X for (i = 0, stream = _iob; i < FOPEN_MAX; i++, stream++) { X if (_iov[i]) X rc |= _fflush(stream); X } X return rc; X } else { X _io_assert(_io_valid(stream)); X return _fflush(stream); X } X} X Xstatic int _fflush(stream) Xregister FILE *stream; X{ X register int rc = 0; X X if (stream->_io_count == _IO_NEVER) X return rc; X if (stream->_io_bufsiz != 0) { X if (_io_testflag(stream, _IO_WRITE)) X rc = _flspbuf(stream); X else X if (_io_testflag(stream, _IO_READ) && !feof(stream)) X if (lseek(stream->_io_fd, 0L - stream->_io_count, SEEK_CUR) < 0L) X rc = EOF; X } X stream->_io_count = _IO_NEVER; X if (stream->_io_bufsiz == 0) X stream->_io._io_char = EOF; X else X stream->_io._io_ptr = stream->_io_buf; X if (_io_testflag(stream, _IO_UPDATE)) X _io_clearflag(stream, (_IO_READ | _IO_WRITE)); X return rc; X} / echo x - fgetc.c sed '/^X/s///' > fgetc.c << '/' X/* --- fgetc.c --- */ X/* Retrieves the next character from a stream */ X/* Notes: Includes getc() and getchar(), backstops for macros in */ X X#include X#include "stdiolib.h" X#undef fgetc X#undef getc X#undef getchar X X#define _IO_ERRVAL EOF X Xint fgetc(stream) Xregister FILE *stream; X{ X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_WRITE)); X _io_assert(!_io_testflag(stream, (_IO_EOF | _IO_ERR))); X X return (--stream->_io_count >= 0) ? X *stream->_io._io_ptr++ : _filbuf(stream); X} X Xint getc(stream) XFILE *stream; X{ X return fgetc(stream); X} X Xint getchar() X{ X return fgetc(stdin); X} / echo x - fgets.c sed '/^X/s///' > fgets.c << '/' X/* --- fgets.c --- */ X/* Reads limited number of characters from a stream, up to a newline */ X/* Notes: Returns EOF only if no characters read before end of file */ X X#include X#include "stdiolib.h" X#undef fgets X X#define _IO_ERRVAL NULL X Xchar *fgets(s, n, stream) Xchar *s; Xregister int n; XFILE *stream; X{ X register int c; X register char *p = s; X X _io_assert(s); X _io_assert(n > 0); X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_WRITE)); X _io_assert(!_io_testflag(stream, (_IO_EOF | _IO_ERR))); X X while (--n > 0 && (c = getc(stream)) != EOF) { X *p++ = c; X if (c == '\n') X break; X } X *p = '\0'; X return (p == s && c == EOF) ? NULL : s; X} / echo x - fopen.c sed '/^X/s///' > fopen.c << '/' X/* --- fopen.c --- */ X/* Opens a file and associates a stream with it */ X/* Notes: #define _V7 gives support for append mode when kernel doesn't*/ X X#include X#include X#include X#include X#include "stdiolib.h" X#undef fopen X X#define _IO_ERRVAL NULL X#define PMODE 0666 X XFILE *fopen(filename, mode) Xconst char *filename; Xconst char *mode; X{ X int fd; X int flags; X register FILE *stream; X X _io_assert(filename); X _io_assert(mode); X X if ((flags = _valmode(mode)) < 0) X return _IO_ERRVAL; X if ((stream = _ioballoc()) == NULL) X return _IO_ERRVAL; X X switch (*mode) { X case 'r': X fd = open(filename, flags ? O_RDWR : O_RDONLY); X flags |= _IO_READ; X break; X case 'w': X#ifdef _V7 X if ((fd = creat(filename, PMODE)) >= 0 && flags) { X close(fd); X fd = open(filename, O_RDWR); X } X#else X fd = open(filename, O_CREAT | O_TRUNC | (flags ? O_RDWR : O_WRONLY), PMODE); X#endif X flags |= _IO_WRITE; X break; X case 'a': X#ifdef _V7 X if ((fd = open(filename, flags ? O_RDWR : O_WRONLY)) >= 0) { X lseek(fd, 0L, SEEK_END); X } X else X if ((fd = creat(filename, PMODE)) >= 0 && flags) { X close(fd); X fd = open(filename, O_RDWR); X } X flags |= (_IO_WRITE | _IO_APPEND); X#else X fd = open(filename, O_CREAT | O_APPEND | (flags ? O_RDWR : O_WRONLY), PMODE); X flags |= _IO_WRITE; X#endif X break; X default: X fd = -1; X } X if (fd < 0) { X _iobfree(stream); X return _IO_ERRVAL; X } X X if (isatty(fd)) X flags |= _IO_LBUF; X stream->_io_fd = fd; X stream->_io_flags = flags; X stream->_io_count = _IO_NEVER; X stream->_io_bufsiz = BUFSIZ; X return stream; X} / echo x - fputc.c sed '/^X/s///' > fputc.c << '/' X/* --- fputc.c --- */ X/* Writes a single character onto a stream */ X/* Notes: Includes putc() and putchar(), macro backstops for */ X X#include X#include "stdiolib.h" X#undef fputc X#undef putc X#undef putchar X X#define _IO_ERRVAL EOF X X Xint fputc(c, stream) Xregister int c; Xregister FILE *stream; X{ X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_READ)); X _io_assert(!_io_testflag(stream, (_IO_EOF | _IO_ERR))); X X#ifndef NDEBUG X if (_io_testflag(stream, _IO_STRING)) X return (int) (*stream->_io._io_ptr++ = c); X#endif X X return ((--stream->_io_count < 0) || X (_io_testflag(stream, _IO_LBUF) && c == '\n')) X ? _flsbuf(c, stream) X : (int) (*stream->_io._io_ptr++ = c); X} X Xint putc(c, stream) Xint c; Xregister FILE *stream; X{ X return fputc(c, stream); X} X Xint putchar(c) Xint c; X{ X return fputc(c, stdout); X} / echo x - fputs.c sed '/^X/s///' > fputs.c << '/' X/* --- fputs.c --- */ X/* Writes a string to a stream */ X/* Notes: Includes puts(), which backstops a macro in */ X X#include X#include "stdiolib.h" X#undef fputs X#undef puts X X#define _IO_ERRVAL EOF X X Xint fputs(s, stream) Xregister const char *s; Xregister FILE *stream; X{ X _io_assert(s); X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_READ)); X _io_assert(!_io_testflag(stream, _IO_ERR)); X X while (*s) { X if (putc(*s, stream) == EOF) X return EOF; X s++; X } X return 0; X} X Xint puts(s) Xconst char *s; X{ X return (fputs(s, stdout) >= 0) ? ((putchar('\n') == '\n') ? 0 : EOF) : EOF; X} X / echo x - fread.c sed '/^X/s///' > fread.c << '/' X/* --- fread.c --- */ X/* Reads directly from a stream into a buffer */ X/* Notes: Attempts to buffer requests but reads directly if impossible */ X X#include X#include X#include X#include X#include X#include "stdiolib.h" X#undef fread X X#define _IO_ERRVAL 0 X X/* --- The following value determines whether fread() requests go --- */ X/* --- through the buffer or are done directly. As set, the limit --- */ X/* --- means that small requests (less than half the stream's buffer --- */ X/* --- size) will use buffering; larger requests are done directly. --- */ X/* --- Empirical testing shows this is reasonable; more should be done. --- */ X#define BUFFER_LIMIT (stream->_io_bufsiz >> 1) X X#define FROM_IO(bytes) { \ X memcpy(p, stream->_io._io_ptr, bytes); \ X p += bytes; \ X stream->_io._io_ptr += bytes; \ X stream->_io_count -= bytes; \ X } X#define FROM_FS(bytes) read(stream->_io_fd, (char *) p, bytes) X Xsize_t fread(ptr, size, nmemb, stream) Xvoid *ptr; Xsize_t size; Xsize_t nmemb; XFILE *stream; X{ X long total_size; X long bytes_left; X register unsigned char *p = (unsigned char *) ptr; X register int bytes; X int c; X int n; X X _io_assert(p != NULL); X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_WRITE)); X _io_assert(!_io_testflag(stream, (_IO_EOF | _IO_ERR))); X X if ((total_size = (long) nmemb * (long) size) == 0) X return 0; X X/* --- Virgin stream: set things up --- */ X if (stream->_io_count == _IO_NEVER) X _i_init(stream); X X/* --- Most common case(?): from the buffer --- */ X if (total_size <= stream->_io_count) { X bytes = total_size; X FROM_IO(bytes); X return nmemb; X } X X/* --- Empty the buffer --- */ X bytes_left = total_size; X if (stream->_io_count > 0) { X bytes_left -= stream->_io_count; X FROM_IO(stream->_io_count); X } X X/* --- Small objects: try filling the stdio buffer ONCE --- */ X if (bytes_left <= BUFFER_LIMIT) { X if ((c = _filbuf(stream)) == EOF) X return (total_size - bytes_left) / size; X *p++ = c; X if ((bytes = --bytes_left) <= stream->_io_count) { X FROM_IO(bytes); X return nmemb; X } X } X X/* --- Buffering failed: read() directly --- */ X if (stream->_io_count > 0) { X bytes_left -= stream->_io_count; X FROM_IO(stream->_io_count); X } X for ( ; bytes_left > INT_MAX; bytes_left -= n) { X if ((n = FROM_FS(INT_MAX)) <= 0) { X if (n < 0) { X _io_setflag(stream, _IO_ERR); X } else { X _io_setflag(stream, _IO_EOF); X if (_io_testflag(stream, _IO_UPDATE)) X _io_clearflag(stream, (_IO_READ | _IO_WRITE)); X } X return (total_size - bytes_left) / size; X } X p += n; X } X for (bytes = bytes_left; bytes > 0; bytes -= n) { X if ((n = FROM_FS(bytes)) <= 0) { X if (n < 0) { X _io_setflag(stream, _IO_ERR); X } else { X _io_setflag(stream, _IO_EOF); X if (_io_testflag(stream, _IO_UPDATE)) X _io_clearflag(stream, (_IO_READ | _IO_WRITE)); X } X return (total_size - bytes) / size; X } X p += n; X } X return nmemb; X} / echo x - freopen.c sed '/^X/s///' > freopen.c << '/' X/* --- freopen.c --- */ X/* Closes the file associated with a stream and then opens a new file */ X/* Notes: #define _V7 gives support for append mode when kernel doesn't*/ X/* Maintains buffer and buffering mode on stream. */ X X#include X#include X#include X#include X#include "stdiolib.h" X#undef freopen X X#define _IO_ERRVAL ((FILE *) NULL) X#define PMODE 0666 X X XFILE *freopen(filename, mode, stream) Xconst char *filename; Xconst char *mode; Xregister FILE *stream; X{ X int fd; X int flags; X X _io_assert(_io_valid(stream)); X X if (fflush(stream) || close(stream->_io_fd)) X return _IO_ERRVAL; X if ((flags = _valmode(mode)) < 0) X return _IO_ERRVAL; X X switch (*mode) { X case 'r': X fd = open(filename, flags ? O_RDWR : O_RDONLY); X flags |= _IO_READ; X break; X case 'w': X#ifdef _V7 X if ((fd = creat(filename, PMODE)) >= 0 && flags) { X close(fd); X fd = open(filename, O_RDWR); X } X#else X fd = open(filename, O_CREAT | O_TRUNC | (flags ? O_RDWR : O_WRONLY), PMODE); X#endif X flags |= _IO_WRITE; X break; X case 'a': X#ifdef _V7 X if ((fd = open(filename, flags ? O_RDWR : O_WRONLY)) >= 0) { X lseek(fd, 0L, SEEK_END); X } X else X if ((fd = creat(filename, PMODE)) >= 0 && flags) { X close(fd); X fd = open(filename, O_RDWR); X } X flags |= (_IO_WRITE | _IO_APPEND); X#else X fd = open(filename, O_CREAT | O_APPEND | (flags ? O_RDWR : O_WRONLY), PMODE); X flags |= _IO_WRITE; X#endif X break; X default: X fd = -1; X } X X if (fd < 0) { X _buffree(stream); X _iobfree(stream); X return _IO_ERRVAL; X } X X if (isatty(fd)) X flags |= _IO_LBUF; X stream->_io_fd = fd; X _io_clearflag(stream, ~(_IO_LBUF | _IO_MYBUF)); X _io_setflag(stream, flags); X stream->_io_count = _IO_NEVER; X return stream; X} / echo x - fseek.c sed '/^X/s///' > fseek.c << '/' X/* --- fseek.c --- */ X/* Sets the position of a stream */ X/* Notes: Always flushes a stream's buffer */ X/* Includes fsetpos() and rewind() */ X/* fsetpos() is a backstop for a macro in */ X/* #define _V7 gives support for append mode when kernel doesn't*/ X X#include X#include X#include X#include "stdiolib.h" X#undef fseek X#undef fsetpos X#undef rewind X X#define _IO_ERRVAL EOF X Xint fseek(stream, offset, whence) Xregister FILE *stream; Xlong int offset; Xregister int whence; X{ X _io_assert(_io_valid(stream)); X _io_assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END); X X fflush(stream); X _io_clearflag(stream, _IO_EOF); X X#ifdef _V7 X if (_io_testflag(stream, _IO_APPEND)) { X if (!_io_testflag(stream, _IO_UPDATE)) X return 0; X if (whence != SEEK_END || offset != 0L) X _io_setflag(stream, _IO_NEEDSEEK); X } X#endif X X return (lseek(stream->_io_fd, offset, whence) < 0L) ? _IO_ERRVAL : 0; X} X Xint fsetpos(stream, pos) XFILE *stream; Xconst fpos_t *pos; X{ X return fseek(stream, *pos, SEEK_SET); X} X Xvoid rewind(stream) XFILE *stream; X{ X (void) fseek(stream, 0L, SEEK_SET); X clearerr(stream); X} / echo x - ftell.c sed '/^X/s///' > ftell.c << '/' X/* --- ftell.c --- */ X/* Returns the position of a stream */ X/* Notes: Includes fgetpos(), which backstops a macro in */ X X#include X#include X#include X#include X#include "stdiolib.h" X#undef ftell X#undef fgetpos X X#define _IO_ERRVAL -1L X X Xlong ftell(stream) Xregister FILE *stream; X{ X extern int errno; X int adjustment; X long position; X long lseek(); X X errno = EBADF; X if (!_io_valid(stream)) X return _IO_ERRVAL; X errno = 0; X X if (stream->_io_count == _IO_NEVER) X adjustment = 0; X else X if (_io_testflag(stream, _IO_READ)) X adjustment = -stream->_io_count; X else X if (_io_testflag(stream, _IO_WRITE)) X adjustment = stream->_io_bufsiz - stream->_io_count; X X return ((position = lseek(stream->_io_fd, 0L, SEEK_CUR)) >= 0L) X ? position + (long) adjustment X : _IO_ERRVAL; X} X Xint fgetpos(stream, position) XFILE *stream; Xfpos_t *position; X{ X return (*position = (fpos_t) ftell(stream)) >= 0L ? 0 : EOF; X} / echo x - fwrite.c sed '/^X/s///' > fwrite.c << '/' X/* --- fwrite.c --- */ X/* Writes directly from a buffer into a stream */ X/* Notes: Attempts to buffer requests but writes directly if impossible*/ X/* #define _V7 gives support for append mode when kernel doesn't*/ X X#include X#include X#include X#include X#include X#include "stdiolib.h" X#undef fwrite X X#define _IO_ERRVAL 0 X X/* --- The following value determines whether fwrite() requests go --- */ X/* --- through the buffer or are done directly. As set, the limit --- */ X/* --- means that small requests (less than half the stream's buffer --- */ X/* --- size) will use buffering; larger requests are done directly. --- */ X/* --- Empirical testing shows this is reasonable; more should be done. --- */ X#define BUFFER_LIMIT (stream->_io_bufsiz >> 1) X X#define TO_IO(bytes) { \ X memcpy(stream->_io._io_ptr, p, bytes); \ X stream->_io._io_ptr += bytes; \ X stream->_io_count -= bytes; \ X } X#define TO_FS(bytes) write(stream->_io_fd, (char *) p, bytes) X Xsize_t fwrite(ptr, size, nmemb, stream) Xconst void *ptr; Xsize_t size; Xsize_t nmemb; XFILE *stream; X{ X register unsigned char *p = (unsigned char *) ptr; X long total_size; X int buffer_limit; X size_t items; X int n; X X _io_assert(p != NULL); X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_READ)); X _io_assert(!_io_testflag(stream, _IO_ERR)); X X if ((total_size = (long) nmemb * (long) size) == 0) X return 0; X X/* --- Virgin stream: set things up --- */ X if (stream->_io_count == _IO_NEVER) X _o_init(stream); X X#ifdef _V7 X/* --- Append kludge --- */ X if (_io_testflag(stream, _IO_APPEND) && _io_testflag(stream, _IO_NEEDSEEK)) { X lseek(stream->_io_fd, 0L, SEEK_END); X _io_clearflag(stream, _IO_NEEDSEEK); X } X#endif X X/* --- Most common case(?): into the buffer --- */ X buffer_limit = (stream->_io_count < BUFFER_LIMIT) X ? stream->_io_count : BUFFER_LIMIT; X if (total_size <= buffer_limit) { X register int bytes = (int) total_size; X X TO_IO(bytes); X return nmemb; X } X X/* --- Doesn't fit: flush what we've accumulated --- */ X _flspbuf(stream); X X/* --- Huge objects: write() in manageable sections --- */ X items = 0; X if (size > INT_MAX) { X while (nmemb-- > 0) { X register size_t obj_size = size; X X while (obj_size > INT_MAX) { X if ((n = TO_FS(INT_MAX)) != INT_MAX) { X _io_setflag(stream, _IO_ERR); X return items; X } X obj_size -= INT_MAX; X p += INT_MAX; X } X if ((n = TO_FS(obj_size)) != obj_size) { X _io_setflag(stream, _IO_ERR); X return items; X } X p += obj_size; X items++; X } X return items; X } X X/* --- Lots of small objects: write() in chunks --- */ X { X size_t items_per_chunk; X register int chunk_size; X long cutoff = stream->_io_count; X X items_per_chunk = INT_MAX / size; X if ((chunk_size = size * items_per_chunk) > cutoff) X cutoff = chunk_size; X while (total_size > cutoff) { X if ((n = TO_FS(chunk_size)) != chunk_size) { X _io_setflag(stream, _IO_ERR); X return items + n / size; X } else { X items += items_per_chunk; X } X total_size -= chunk_size; X p += chunk_size; X } X if ((chunk_size = (int) total_size) > BUFFER_LIMIT) { X if ((n = TO_FS(chunk_size)) != chunk_size) X _io_setflag(stream, _IO_ERR); X return items + n / size; X } X TO_IO(chunk_size); X return nmemb; X } X} / echo x - gets.c sed '/^X/s///' > gets.c << '/' X/* --- gets.c --- */ X/* Reads characters from stdin, up to a newline */ X/* Notes: Returns EOF only if no characters read before end of file */ X X#include "stdio.h" X#include "stdiolib.h" X#undef gets X X#define _IO_ERRVAL NULL X Xchar *gets(s) Xchar *s; X{ X register char *p = s; X register int c; X X _io_assert(s); X X while ((c = getchar()) != EOF && c != '\n') X *p++ = c; X *p = '\0'; X return (p == s && c == EOF) ? NULL : s; X} / echo x - perror.c sed '/^X/s///' > perror.c << '/' X/* --- perror.c --- */ X/* Prints the current error message */ X/* Notes: Only minor changes from standard Minix issue */ X X#include X#include X#include X#include X#include X Xchar *sys_errlist[] = { X "Error 0", X "Not owner", X "No such file or directory", X "No such process", X "Interrupted system call", X "I/O error", X "No such device or address", X "Arg list too long", X "Exec format error", X "Bad file number", X "No children", X "No more processes", X "Not enough core", X "Permission denied", X "Bad address", X "Block device required", X "Mount device busy", X "File exists", X "Cross-device link", X "No such device", X "Not a directory", X "Is a directory", X "Invalid argument", X "File table overflow", X "Too many open files", X "Not a typewriter", X "Text file busy", X "File too large", X "No space left on device", X "Illegal seek", X "Read-only file system", X "Too many links", X "Broken pipe", X "Math argument", X "Result too large" X}; X Xint sys_nerr = sizeof(sys_errlist)/sizeof(char *); X Xvoid perror(s) Xconst char *s; X{ X if (errno < 0 || errno >= sizeof(sys_errlist)/sizeof(char *)) { X write(2, "Invalid errno\n", 14); X } else { X write(2, s, strlen(s)); X write(2, ": ", 2); X write(2, sys_errlist[errno], strlen(sys_errlist[errno])); X write(2, "\n", 1); X } X} / echo x - printf.c sed '/^X/s///' > printf.c << '/' X/* --- printf.c --- */ X/* Formatted printing functions */ X/* Notes: Includes fprintf(), printf(), vfprintf(), and vprintf(); */ X/* All are front ends for _doprnt() */ X/* vfprintf() and vprintf() backstop macros in */ X X#include X#include X#include "stdiolib.h" X#undef fprintf X#undef printf X#undef vfprintf X#undef vprintf X X X#define _IO_ERRVAL 0 X Xint fprintf(stream, format /*, ... */) XFILE *stream; Xconst char *format; X{ X va_list arg; X int n; X X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_READ)); X _io_assert(!_io_testflag(stream, _IO_ERR)); X _io_assert(format); X X va_start(arg, format); X n = _doprnt(stream, format, arg); X va_end(arg); X return n; X} X Xint printf(format /*, ... */) Xconst char *format; X{ X va_list arg; X int n; X X _io_assert(_io_valid(stdout)); X _io_assert(!_io_testflag(stdout, _IO_READ)); X _io_assert(!_io_testflag(stdout, _IO_ERR)); X _io_assert(format); X X va_start(arg, format); X n = _doprnt(stdout, format, arg); X va_end(arg); X return n; X} X Xint vfprintf(stream, format, arg) XFILE *stream; Xconst char *format; Xva_list arg; X{ X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_READ)); X _io_assert(!_io_testflag(stream, _IO_ERR)); X _io_assert(format); X X return _doprnt(stream, format, arg); X} X Xint vprintf(format, arg) Xconst char *format; Xva_list arg; X{ X return _doprnt(stdout, format, arg); X} / echo x - remove.c sed '/^X/s///' > remove.c << '/' X/* --- remove.c --- */ X/* Removes a file from the file system */ X/* Notes: Backstops a macro in */ X X#include X#include X#include X#undef remove X Xint remove(filename) Xconst char *filename; X{ X return unlink(filename); X} / echo x - rename.c sed '/^X/s///' > rename.c << '/' X/* --- rename.c --- */ X/* Renames a file in the file system */ X/* Author: Freeman Pascal */ X X#include X#include X#include X#include X#include X#include X#undef rename X X/*========================================================================*\ X** rename() ** X\*========================================================================*/ Xint rename(old, new) Xconst char *old; Xconst char *new; X{ X/* X * Attempts new link 'old' as 'new'. If 'new' exists it is unlinked. X * X * NOTE: 'rename()' will not rename across file systems or X * file types. Also, if an attempt is made to copy across X * file systems both files will be left intact and an X * error will be returned. X */ X struct stat s_new, s_old; X void (*s_int)(), (*s_hup)(), (*s_quit)(); X int ret = 0; X X /* X * Get status of 'old' and 'new'; if either attempt fails we know X * one of the files doesn't exist. If 'old' doesn't exist, then X * return an error condition. If both files exist, then test if X * they are both on the same file system. If 'new' doesn't exist, X * then don't worry about it - it soon will. X */ X if (stat(old, &s_old) == 0) { X if (stat(new, &s_new) == 0) { X if (s_new.st_dev == s_old.st_dev) { X errno = EXDEV; X return -1; X } X } X /* Ignore SIGINT, SIGHUP, and SIGQUIT until we're finished. */ X s_int = signal(SIGINT, SIG_IGN); X s_hup = signal(SIGHUP, SIG_IGN); X s_quit = signal(SIGQUIT, SIG_IGN); X /* Does 'new' exist? If so, remove it. */ X ret = unlink(new); X if ((ret = link(old, new)) == 0) X ret = unlink(old); X /* Restore signals. */ X signal(SIGINT, s_int ); X signal(SIGHUP, s_hup ); X signal(SIGQUIT, s_quit); X return ret; X } X return -1; X} / echo x - scanf.c sed '/^X/s///' > scanf.c << '/' X/* --- scanf.c --- */ X/* Formatted input functions */ X/* Notes: Includes fscanf() and scanf(), front ends for _doscan() */ X X#include X#include X#include "stdiolib.h" X#undef fscanf X#undef scanf X X#define _IO_ERRVAL EOF X Xint fscanf(stream, format /*, ... */) XFILE *stream; Xconst char *format; X{ X va_list arg; X int n; X X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_WRITE)); X _io_assert(!_io_testflag(stream, (_IO_EOF | _IO_ERR))); X _io_assert(format); X X va_start(arg, format); X n = _doscan(0, (void *) stream, format, arg); X va_end(arg); X return n; X} X X Xint scanf(format /*, ... */) Xconst char *format; X{ X va_list arg; X int n; X X _io_assert(_io_valid(stdin)); X _io_assert(!_io_testflag(stdin, _IO_WRITE)); X _io_assert(!_io_testflag(stdin, (_IO_EOF | _IO_ERR))); X _io_assert(format); X X va_start(arg, format); X n = _doscan(0, (void *) stdin, format, arg); X va_end(arg); X return n; X} / echo x - setvbuf.c sed '/^X/s///' > setvbuf.c << '/' X/* --- setvbuf.c --- */ X/* Associates a buffer with a stream and sets buffering modes */ X/* Notes: Does not actually allocate a buffer; just sets things up */ X/* Includes setbuf(), which backstops a macro in */ X X#include X#include "stdiolib.h" X#undef setvbuf X#undef setbuf X X#define _IO_ERRVAL EOF X X Xint setvbuf(stream, buffer, type, size) Xregister FILE *stream; Xchar *buffer; Xint type; Xsize_t size; X{ X _io_assert(_io_valid(stream)); X _io_assert(type == _IONBF || size != 0); X _io_assert(stream->_io_count == _IO_NEVER); X X fflush(stream); X _buffree(stream); X _io_clearflag(stream, (_IO_LBUF | _IO_MYBUF)); X switch (type) { X case _IONBF: X stream->_io_bufsiz = 0; X stream->_io_buf = NULL; X stream->_io._io_char = EOF; X stream->_io_count = _IO_NEVER; X return 0; X case _IOLBF: X case _IOFBF: X stream->_io_bufsiz = size; X stream->_io._io_ptr = stream->_io_buf = (unsigned char *) buffer; X stream->_io_count = _IO_NEVER; X _io_setflag(stream, type); X return 0; X default: X return _IO_ERRVAL; X } X} X X Xvoid setbuf(stream, buffer) /* ANSI requires function definition */ XFILE *stream; /* (even if the default is a macro) */ Xchar *buffer; X{ X (void) setvbuf(stream, buffer, (buffer == NULL) ? _IONBF : _IOFBF, BUFSIZ); X} / echo x - sprintf.c sed '/^X/s///' > sprintf.c << '/' X/* --- sprintf.c --- */ X/* Formatted printing functions for strings */ X/* Notes: Includes sprintf() and vsprintf() */ X/* Both are front ends for _doprnt() */ X X#include X#include X#include X#include "stdiolib.h" X#undef sprintf X#undef vsprintf X X#define _IO_ERRVAL 0 X X Xint sprintf(s, format /*, ... */) Xchar *s; Xconst char *format; X{ X FILE fake; X va_list arg; X int n; X X _io_assert(s); X _io_assert(format); X X fake._io_fd = -1; X fake._io_flags = _IO_WRITE | _IO_STRING; X fake._io_bufsiz = fake._io_count = INT_MAX; X fake._io._io_ptr = fake._io_buf = (unsigned char *) s; X va_start(arg, format); X n = _doprnt(&fake, format, arg); X putc('\0', &fake); X va_end(arg); X return n; X} X Xint vsprintf(s, format, arg) Xchar *s; Xconst char *format; Xva_list arg; X{ X FILE fake; X int n; X X _io_assert(s); X _io_assert(format); X X fake._io_fd = -1; X fake._io_flags = _IO_WRITE | _IO_STRING; X fake._io_bufsiz = fake._io_count = INT_MAX; X fake._io._io_ptr = fake._io_buf = (unsigned char *) s; X n = _doprnt(&fake, format, arg); X putc('\0', &fake); X return n; X} / echo x - sscanf.c sed '/^X/s///' > sscanf.c << '/' X/* --- sscanf.c --- */ X/* Formatted input function for strings */ X/* Notes: Front end for _doscan() */ X X#include X#include X#include "stdiolib.h" X#undef sscanf X X#define _IO_ERRVAL EOF X Xint sscanf(s, format /*, ... */) Xconst char *s; Xconst char *format; X{ X va_list arg; X int n; X X _io_assert(s); X _io_assert(format); X X va_start(arg, format); X n = _doscan(1, (void *) s, format, arg); X va_end(arg); X return n; X} / echo x - stdio_misc.c sed '/^X/s///' > stdio_misc.c << '/' X/* --- stdio_misc.c --- */ X/* Miscellaneous stream functions */ X/* Notes: Includes clearerr(), feof(), ferror(), and (POSIX) fileno() */ X/* All functions backstop macros in */ X X#include X#include "stdiolib.h" X#undef clearerr X#undef feof X#undef ferror X#undef fileno X X#define _IO_ERRVAL EOF X Xvoid clearerr(stream) XFILE *stream; X{ X _io_assert(_io_valid(stream)); X _io_clearflag(stream, (_IO_EOF | _IO_ERR)); X} X Xint feof(stream) XFILE *stream; X{ X _io_assert(_io_valid(stream)); X return _io_testflag(stream, _IO_EOF); X} X Xint ferror(stream) XFILE *stream; X{ X _io_assert(_io_valid(stream)); X return _io_testflag(stream, _IO_ERR); X} X Xint fileno(stream) /* Posix */ XFILE *stream; X{ X _io_assert(_io_valid(stream)); X return stream->_io_fd; X} / echo x - tmpfile.c sed '/^X/s///' > tmpfile.c << '/' X/* --- tmpfile.c --- */ X/* Opens a temporary file which will be deleted on close or exit */ X/* Notes: Also includes two cleanup routines: */ X/* _tmp_cleanup - registered with atexit() if tmpfile() is used */ X/* _tmp_remove - removes one temporary file */ X X#include X#include X#include X#include X#include "stdiolib.h" X#undef tmpfile X Xstatic void _tmp_cleanup(); Xstatic int _tmp_remove(); X Xextern int (*__tmprm)(); Xstatic char* tmp_name[FOPEN_MAX]; X XFILE *tmpfile() X{ X register char *name; X register FILE *stream; X X if ((name = (char *) malloc(L_tmpnam)) == NULL) X return NULL; X X (void) tmpnam(name); X if (stream = fopen(name, "w+b")) { X _io_setflag(stream, _IO_TEMP); X tmp_name[_iobindex(stream)] = name; X if (!__tmprm) { X __tmprm = _tmp_remove; X atexit(_tmp_cleanup); X } X } else { X free(name); X } X return stream; X} X Xstatic void _tmp_cleanup() X{ X register int i; X X for (i = 0; i < FOPEN_MAX; i++) X _tmp_remove(i); X} X Xstatic int _tmp_remove(i) Xregister int i; X{ X int rc = -1; X X if (tmp_name[i]) { X rc = unlink(tmp_name[i]); X free(tmp_name[i]); X tmp_name[i] = NULL; X } X return rc; X} / echo x - tmpnam.c sed '/^X/s///' > tmpnam.c << '/' X/* --- tmpnam.c --- */ X/* Generates a unique file name */ X/* Notes: File name is of the form Tmpxxxxx.yyyyy */ X/* Not reentrant */ X X#include X#include X#include X#include X#include "stdiolib.h" X#undef tmpnam X X#define DIGITS 5 /* maximum digits converted */ X Xchar *tmpnam(s) Xchar *s; X{ X static char buf[L_tmpnam]; X static int pid = 0; X static int suffix = 1; X int length; X register int width; X register char *p; X int k; X X if (s == NULL) X s = buf; X if (suffix >= TMP_MAX) { X *s = '\0'; X return NULL; X } X if (pid == 0) X pid = getpid(); X X strcpy(s, P_tmpdir); X strcat(s, "/Tmp00000.00000"); X length = strlen(s); X p = s + (length - 1 - DIGITS - 1); /* pid ends here */ X width = DIGITS; X k = pid; X do { X *p-- = k % 10 + '0'; X } while ((k /= 10) && (--width > 0)); X p = s + (length - 1); /* suffix ends here */ X width = DIGITS; X k = suffix++; X do { X *p-- = k % 10 + '0'; X } while ((k /= 10) && (--width > 0)); X return s; X} / echo x - ungetc.c sed '/^X/s///' > ungetc.c << '/' X/* --- ungetc.c --- */ X/* Pushes one character back into an input stream */ X/* Notes: Will not push back EOF */ X/* Can fail if tried too many times */ X X#include X#include "stdiolib.h" X#undef ungetc X X#define _IO_ERRVAL EOF X Xint ungetc(c, stream) Xregister int c; Xregister FILE *stream; X{ X _io_assert(_io_valid(stream)); X _io_assert(!_io_testflag(stream, _IO_WRITE)); X X if (c == EOF || stream->_io_count == _IO_NEVER) X return _IO_ERRVAL; X X if (stream->_io_bufsiz == 0) { X if (stream->_io._io_char == EOF) { X stream->_io._io_char = c; X _io_clearflag(stream, _IO_EOF); X } else { X c = _IO_ERRVAL; X } X } else { X if (stream->_io._io_ptr != stream->_io_buf) { X stream->_io_count++; X *--stream->_io._io_ptr = c; X _io_clearflag(stream, _IO_EOF); X } else { X c = _IO_ERRVAL; X } X } X return c; X} /