diff options
Diffstat (limited to 'libc/stdio/stdio.c')
-rw-r--r-- | libc/stdio/stdio.c | 926 |
1 files changed, 505 insertions, 421 deletions
diff --git a/libc/stdio/stdio.c b/libc/stdio/stdio.c index 7ed545742..0522c1b5d 100644 --- a/libc/stdio/stdio.c +++ b/libc/stdio/stdio.c @@ -16,6 +16,26 @@ * buffer for the unbuffered stderr! */ +/* + * Feb 27, 2001 Manuel Novoa III + * + * Most of the core functionality has been completely rewritten. + * A number of functions have been added as well, as mandated by C89. + * + * An extension function "fsfopen" has been added: + * Open a file using an automatically (stack) or statically allocated FILE. + * The FILE * returned behaves just as any other FILE * with respect to the + * stdio functions, but be aware of the following: + * NOTE: The buffer used for the file is FILE's builtin 2-byte buffer, so + * setting a new buffer is probably advisable. + * NOTE: This function is primarily intended to be used for stack-allocated + * FILEs when uClibc stdio has no dynamic memory support. + * For the statically allocated case, it is probably better to increase + * the value of FIXED_STREAMS in stdio.c. + * WARNING: If allocated on the stack, make sure you call fclose before the + * stack memory is reclaimed! + */ + #include <stdlib.h> #include <stdio.h> @@ -26,55 +46,105 @@ #include <errno.h> #include <string.h> #include <assert.h> +#include <limits.h> + +extern off_t _uClibc_fwrite(const unsigned char *buf, off_t bytes, FILE *fp); +extern off_t _uClibc_fread(unsigned char *buf, off_t bytes, FILE *fp); + +/* Note: This def of READING is ok since 1st ungetc puts in buf. */ +#if 0 +#define READING(fp) (fp->bufpos < fp->bufread) +#else +#define READING(fp) (fp->bufstart < fp->bufread) +#endif +#if 1 +#define WRITING(fp) (fp->bufwrite > fp->bufstart) +#else +#define WRITING(fp) ((fp->bufpos > fp->bufread) && (fp->bufpos > fp->bufstart)) +#endif + +#define READABLE(fp) (fp->bufread != 0) +#define WRITEABLE(fp) (fp->bufwrite != 0) +#define EOF_OR_ERROR(fp) (fp->mode & (__MODE_EOF | __MODE_ERR)) + +/***********************************************************************/ +/* BUILD TIME OPTIONS */ +/***********************************************************************/ +/* + * FIXED_STREAMS must be >= 3 and FIXED_BUFFERS must be >= 2. + * As a feature, these can be increased, although this is probably + * only useful if DISABLE_DYNAMIC is set to 1 below. + */ #define FIXED_STREAMS 3 #define FIXED_BUFFERS 2 -struct fixed_buffer { - unsigned char data[BUFSIZ]; - unsigned char used; -}; +/* + * As a feature, you can build uClibc with no dynamic allocation done + * by the stdio package. Just set DISABLE_DYNAMIC to nonzero. Note that + * use of asprintf, getdelim, or getline will pull malloc into the link. + * + * Note: You can't trust FOPEN_MAX if DISABLE_DYNAMIC != 0. + */ +#define DISABLE_DYNAMIC 0 + +/* + * As a feature, you can try to allow setvbuf calls after file operations. + * Setting FLEXIBLE_SETVBUF to nonzero will cause setvbuf to try to fflush + * any buffered writes or sync the file position for buffered reads. If it + * is successful, the buffer change can then take place. + */ +#define FLEXIBLE_SETVBUF 0 +/***********************************************************************/ + +#if DISABLE_DYNAMIC != 0 +#undef malloc +#undef free +#define malloc(x) 0 +#define free(x) +#endif -extern FILE *__IO_list; /* For fflush at exit */ +extern FILE *__IO_list; /* For fflush. */ +extern FILE *_free_file_list; +extern char _free_buffer_index; extern FILE _stdio_streams[FIXED_STREAMS]; -extern struct fixed_buffer _fixed_buffers[FIXED_BUFFERS]; +extern unsigned char _fixed_buffers[FIXED_BUFFERS * BUFSIZ]; -#if defined L__fopen || defined L_fclose || defined L_setvbuf extern unsigned char *_alloc_stdio_buffer(size_t size); -extern void _free_stdio_buffer(unsigned char *buf); -#endif - -#if defined L__fopen || defined L_fclose +extern void _free_stdio_buffer_of_file(FILE *fp); extern void _free_stdio_stream(FILE *fp); -#endif #ifdef L__alloc_stdio_buffer unsigned char *_alloc_stdio_buffer(size_t size) { - if (size == BUFSIZ) { - int i; + unsigned char *buf; - for (i = 0; i < FIXED_BUFFERS; i++) - if (!_fixed_buffers[i].used) { - _fixed_buffers[i].used = 1; - return _fixed_buffers[i].data; - } + if ((size == BUFSIZ) && (_free_buffer_index < FIXED_BUFFERS)) { + buf = _fixed_buffers + ((unsigned int)_free_buffer_index) * BUFSIZ; + _free_buffer_index = *buf; + return buf; } return malloc(size); } #endif -#ifdef L__free_stdio_buffer -void _free_stdio_buffer(unsigned char *buf) +#ifdef L__free_stdio_buffer_of_file +void _free_stdio_buffer_of_file(FILE *fp) { - int i; + unsigned char *buf; - for (i = 0; i < FIXED_BUFFERS; i++) { - if (buf == _fixed_buffers[i].data) { - _fixed_buffers[i].used = 0; - return; - } + if (!(fp->mode & __MODE_FREEBUF)) { + return; + } + fp->mode &= ~(__MODE_FREEBUF); + buf = fp->bufstart; + + if ((buf >= _fixed_buffers) + && (buf < _fixed_buffers + (FIXED_BUFFERS * BUFSIZ))) { + *buf = _free_buffer_index; + _free_buffer_index = (buf - _fixed_buffers)/BUFSIZ; + return; } free(buf); } @@ -86,26 +156,30 @@ void _free_stdio_buffer(unsigned char *buf) #error FIXED_BUFFERS must be >= 2 #endif -#define bufin (_fixed_buffers[0].data) -#define bufout (_fixed_buffers[1].data) -#define buferr (_stdio_streams[2].unbuf) /* Stderr is unbuffered */ +#if FIXED_BUFFERS >= UCHAR_MAX +#error FIXED_BUFFERS must be < UCHAR_MAX +#endif + +#define bufin (_fixed_buffers) +#define bufout (_fixed_buffers + BUFSIZ) +#define buferr (_stdio_streams[2].unbuf) /* Stderr is unbuffered */ -struct fixed_buffer _fixed_buffers[FIXED_BUFFERS]; +unsigned char _fixed_buffers[FIXED_BUFFERS * BUFSIZ]; #if FIXED_STREAMS < 3 #error FIXED_STREAMS must be >= 3 #endif FILE _stdio_streams[FIXED_STREAMS] = { - {bufin, bufin, bufin, bufin, bufin + BUFSIZ, - 0, _IOFBF | __MODE_READ | __MODE_FREEBUF, - _stdio_streams + 1}, - {bufout, bufout, bufout, bufout, bufout + BUFSIZ, - 1, _IOFBF | __MODE_WRITE | __MODE_FREEBUF, - _stdio_streams + 2}, - {buferr, buferr, buferr, buferr, buferr + sizeof(buferr), - 2, _IONBF | __MODE_WRITE, - 0}, + {bufin, bufin, 0, bufin, bufin + BUFSIZ, + _stdio_streams + 1, + 0, _IOFBF | __MODE_FREEFIL | __MODE_FREEBUF | __MODE_TIED }, + {bufout, 0, bufout, bufout, bufout + BUFSIZ, + _stdio_streams + 2, + 1, _IOFBF | __MODE_FREEFIL | __MODE_FREEBUF | __MODE_TIED }, + {buferr, 0, buferr, buferr, buferr + 1, + NULL, + 2, _IONBF | __MODE_FREEFIL } }; FILE *_stdin = _stdio_streams + 0; @@ -117,7 +191,10 @@ FILE *_stderr = _stdio_streams + 2; * any of the stdio functions are used since they all call fflush directly * or indirectly. */ -FILE *__IO_list = _stdio_streams; /* For fflush at exit */ +FILE *__IO_list = _stdio_streams; /* For fflush. */ + +FILE *_free_file_list = 0; +char _free_buffer_index = FIXED_BUFFERS; /* * __stdio_close_all is automatically when exiting if stdio is used. @@ -125,12 +202,7 @@ FILE *__IO_list = _stdio_streams; /* For fflush at exit */ */ void __stdio_close_all(void) { - FILE *fp; - - for (fp = __IO_list; fp; fp = fp->next) { - fflush(fp); - close(fp->fd); - } + fflush(NULL); /* Files will be closed on _exit call. */ } /* @@ -142,19 +214,19 @@ void __init_stdio(void) int i; #endif #if FIXED_BUFFERS > 2 + _free_buffer_index = 2; for ( i = 2 ; i < FIXED_BUFFERS ; i++ ) { - _fixed_buffers[i].used = 0; + _fixed_buffers[i*BUFSIZ] = i; } #endif #if FIXED_STREAMS > 3 - for ( i = 3 ; i < FIXED_STREAMS ; i++ ) { - _stdio_streams[i].fd = -1; + _free_file_list = _stdio_streams + 3; + for ( i = 3 ; i < FIXED_STREAMS-1 ; i++ ) { + _stdio_streams[i].next = _stdio_streams + i + 1; } + _stdio_streams[i].next = 0; #endif - _fixed_buffers[0].used = 1; - _fixed_buffers[1].used = 1; - #if _IOFBF != 0 || _IOLBF != 1 #error Assumption violated -- values of _IOFBF and/or _IOLBF /* This asssumption is also made in _fopen. */ @@ -166,229 +238,129 @@ void __init_stdio(void) #endif #ifdef L_fputc -int fputc(ch, fp) -int ch; -FILE *fp; +int fputc(int c, FILE *fp) { - register int v; + unsigned char buf[1]; - v = fp->mode; - /* If last op was a read ... */ - if ((v & __MODE_READING) && fflush(fp)) - return EOF; - - /* Can't write or there's been an EOF or error then return EOF */ - if ((v & (__MODE_WRITE | __MODE_EOF | __MODE_ERR)) != __MODE_WRITE) - return EOF; - - /* Buffer is full */ - if (fp->bufpos >= fp->bufend && fflush(fp)) - return EOF; - - /* Right! Do it! */ - *(fp->bufpos++) = ch; - fp->mode |= __MODE_WRITING; - - /* Unbuffered or Line buffered and end of line */ - if (((ch == '\n' && (v & _IOLBF)) || (v & _IONBF)) - && fflush(fp)) - return EOF; - - /* Can the macro handle this by itself ? */ - if (v & (_IOLBF | _IONBF)) - fp->bufwrite = fp->bufstart; /* Nope */ - else - fp->bufwrite = fp->bufend; /* Yup */ + *buf = (unsigned char) c; - /* Correct return val */ - return (unsigned char) ch; + if (_uClibc_fwrite(buf, 1, fp)) { + return (unsigned char) c; + } + return EOF; } #endif #ifdef L_fgetc -int fgetc(fp) -FILE *fp; +int fgetc(FILE *fp) { - int ch; - - if (fp->mode & __MODE_WRITING) - fflush(fp); - -#if 1 -#warning Need to check out tie between stdin and stdout. - /* - * This bit of code needs checking. The way I read the C89 standard, - * there is no guarantee that stdout is flushed before reading stdin. - * Plus, this is broken if either stdin or stdout has been closed and - * reopend. - */ - if ( (fp == stdin) && (stdout->fd != -1) - && (stdout->mode & __MODE_WRITING) ) - fflush(stdout); -#endif - - /* Can't read or there's been an EOF or error then return EOF */ - if ((fp->mode & (__MODE_READ | __MODE_EOF | __MODE_ERR)) != - __MODE_READ) return EOF; + unsigned char buf[1]; - /* Nothing in the buffer - fill it up */ - if (fp->bufpos >= fp->bufread) { - fp->bufpos = fp->bufread = fp->bufstart; - ch = fread(fp->bufpos, 1, fp->bufend - fp->bufstart, fp); - if (ch == 0) - return EOF; - fp->bufread += ch; - fp->mode |= __MODE_READING; - fp->mode &= ~__MODE_UNGOT; + if (_uClibc_fread(buf, 1, fp)) { + return *buf; } - ch = *(fp->bufpos++); - - return ch; + return EOF; } #endif #ifdef L_fflush -int fflush(fp) -FILE *fp; +int fflush(FILE *fp) { - int len, cc, rv; - char *bstart; + int rv; rv = 0; + if (fp == NULL) { /* On NULL flush the lot. */ for (fp = __IO_list; fp; fp = fp->next) { - if (fflush(fp)) { - rv = EOF; - } - } - return rv; - } - - /* If there's output data pending */ - if (fp->mode & __MODE_WRITING) { - len = fp->bufpos - fp->bufstart; - - if (len) { - bstart = fp->bufstart; - /* - * The loop is so we don't get upset by signals or partial writes. - */ - do { - cc = write(fp->fd, bstart, len); - if (cc > 0) { - bstart += cc; - len -= cc; - } - } - while (cc > 0 || (cc == -1 && errno == EINTR)); - /* - * If we get here with len!=0 there was an error, exactly what to - * do about it is another matter ... - * - * I'll just clear the buffer. - */ - if (len) { - fp->mode |= __MODE_ERR; + if (WRITEABLE(fp) && fflush(fp)) { rv = EOF; } } - } - /* If there's data in the buffer sychronise the file positions */ - else if (fp->mode & __MODE_READING) { - /* Humm, I think this means sync the file like fpurge() ... */ - /* Anyway the user isn't supposed to call this function when reading */ - - len = fp->bufread - fp->bufpos; /* Bytes buffered but unread */ - /* If it's a file, make it good */ - if (len > 0 && lseek(fp->fd, (long) -len, 1) < 0) { - /* Hummm - Not certain here, I don't think this is reported */ - /* - * fp->mode |= __MODE_ERR; return EOF; - */ + } else if (WRITING(fp)) { /* Output buffer contents. */ + _uClibc_fwrite(NULL, 0, fp); + if (fp->mode & __MODE_ERR) { + rv = -1; } + } else if (!WRITEABLE(fp)) { /* File opened read-only!!! */ + /* + * According to info, glibc returns an error when the file is opened + * in read-only mode. + * ANSI says behavior in this case is undefined but also says you + * shouldn't flush a stream you were reading from. + */ + errno = EBADF; /* Should we set stream error indicator? */ + rv = -1; } - /* All done, no problem */ - fp->mode &= - (~(__MODE_READING | __MODE_WRITING | __MODE_EOF | __MODE_UNGOT)); - fp->bufread = fp->bufwrite = fp->bufpos = fp->bufstart; return rv; } #endif #ifdef L_fgets /* Nothing special here ... */ -char *fgets(s, count, f) -char *s; -int count; -FILE *f; -{ - char *ret; - register size_t i; - register int ch; - - ret = s; - for (i = count-1; i > 0; i--) { - ch = getc(f); +char *fgets(char *s, int count, FILE *fp) +{ + int ch; + char *p; + + p = s; + while (count-- > 1) { /* Guard against count arg == INT_MIN. */ + ch = getc(fp); if (ch == EOF) { - if (s == ret) - return 0; break; } - *s++ = (char) ch; - if (ch == '\n') + *p++ = ch; + if (ch == '\n') { break; + } } - *s = 0; - - if (ferror(f)) + if (ferror(fp) || (s == p)) { return 0; - return ret; + } + *p = 0; + return s; } #endif #ifdef L_gets -char *gets(str) /* BAD function; DON'T use it! */ -char *str; +char *gets(char *str) /* This is an UNSAFE function! */ { - /* Auwlright it will work but of course _your_ program will crash */ - /* if it's given a too long line */ - register char *p = str; - register int c; - - while (((c = getc(stdin)) != EOF) && (c != '\n')) - *p++ = c; - *p = '\0'; - return (((c == EOF) && (p == str)) ? NULL : str); /* NULL == EOF */ + /* + * Strictly speaking, this implementation is incorrect as the number + * of chars gets can read should be unlimited. However, I can't + * imagine anyone wanting to gets() into a buffer bigger than INT_MAX. + * + * Besides, this function is inherently unsafe and shouldn't be used. + */ + return fgets(str, INT_MAX, _stdin); } #endif #ifdef L_fputs -int fputs(str, fp) -const char *str; -FILE *fp; +int fputs(const char *str, FILE *fp) { - register int n = 0; + int n; - while (*str) { - if (putc(*str++, fp) == EOF) - return (EOF); - ++n; + n = strlen(str); + + _uClibc_fwrite((const unsigned char *)str, n, fp); + if (fp->mode & __MODE_ERR) { + n = EOF; } - return (n); + return n; } #endif #ifdef L_puts -int puts(str) -const char *str; +int puts(const char *str) { - register int n; + int n; - if (((n = fputs(str, stdout)) == EOF) - || (putc('\n', stdout) == EOF)) - return (EOF); - return (++n); + n = fputs(str, _stdout); /* Let next fputc handle EOF or error. */ + if (fputc('\n', _stdout) == EOF) { /* Don't use putc since we want to */ + return EOF; /* fflush stdout if it is line buffered. */ + } + return n + 1; } #endif @@ -404,43 +376,74 @@ size_t size; size_t nelm; FILE *fp; { - int len, v; - unsigned bytes, got = 0; + off_t bytes; - v = fp->mode; +#warning TODO: handle possible overflow for bytes + bytes = size * nelm; /* How many bytes do we want? */ - /* Want to do this to bring the file pointer up to date */ - if (v & __MODE_WRITING) - fflush(fp); + bytes = _uClibc_fread((unsigned char *)buf, bytes, fp); + + return bytes/size; +} +#endif + +#ifdef L__uClibc_fread +off_t _uClibc_fread(unsigned char *buf, off_t bytes, FILE *fp) +{ + unsigned char *p; + off_t len; - /* Can't read or there's been an EOF or error then return zero */ - if ((v & (__MODE_READ | __MODE_EOF | __MODE_ERR)) != __MODE_READ) + if (!READABLE(fp)) { + fp->mode |= __MODE_ERR; + } else if (WRITING(fp)) { + fflush(fp); + } else if (fp->mode & _stdout->mode & __MODE_TIED) { + fflush(_stdout); + } + if (EOF_OR_ERROR(fp) || (bytes <= 0)) { return 0; + } + + p = (unsigned char *) buf; - /* This could be long, doesn't seem much point tho */ - bytes = size * nelm; + if (fp->mode & __MODE_UNGOT) { /* If we had an ungetc'd char, */ + fp->mode ^= __MODE_UNGOT; /* reset the flag and return it. */ + *p++ = fp->ungot; + --bytes; + } + FROM_BUF: len = fp->bufread - fp->bufpos; - if (len >= bytes) { /* Enough buffered */ - memcpy(buf, fp->bufpos, (unsigned) bytes); - fp->bufpos += bytes; - return bytes; - } else if (len > 0) { /* Some buffered */ - memcpy(buf, fp->bufpos, len); - got = len; + if (len > bytes) { /* Enough buffered */ + len = bytes; + } + + bytes -= len; + while (len--) { + *p++ = *fp->bufpos++; } - /* Need more; do it with a direct read */ - len = read(fp->fd, buf + got, (unsigned) (bytes - got)); + if (bytes && !EOF_OR_ERROR(fp)) { /* More requested but buffer empty. */ + if (bytes < fp->bufend - fp->bufstart) { + fp->bufpos = fp->bufread = fp->bufstart; /* Reset pointers. */ + fp->bufread += _uClibc_fread(fp->bufstart, + fp->bufend - fp->bufstart, fp); + goto FROM_BUF; + } - /* Possibly for now _or_ later */ - if (len < 0) { - fp->mode |= __MODE_ERR; - len = 0; - } else if (len == 0) - fp->mode |= __MODE_EOF; + len = read(fp->fd, p, (unsigned) bytes); + if (len < 0) { + fp->mode |= __MODE_ERR; + } else { + p += len; + if (len == 0) { + fp->mode |= __MODE_EOF; + } + } + } + + return (p - (unsigned char *)buf); - return (got + len) / size; } #endif @@ -457,60 +460,95 @@ size_t size; size_t nelm; FILE *fp; { - register int v; - int len; - unsigned bytes, put; + off_t bytes; - v = fp->mode; - /* If last op was a read ... */ - if ((v & __MODE_READING) && fflush(fp)) - return 0; +#warning TODO: handle possible overflow for bytes + bytes = size * nelm; /* How many bytes do we want? */ - /* Can't write or there's been an EOF or error then return 0 */ - if ((v & (__MODE_WRITE | __MODE_EOF | __MODE_ERR)) != __MODE_WRITE) - return 0; + bytes = _uClibc_fwrite((const unsigned char *)buf, bytes, fp); + + return bytes/size; +} +#endif - /* This could be long, doesn't seem much point tho */ - bytes = size * nelm; +#ifdef L__uClibc_fwrite +/* + * If buf == NULL, fflush. + * If buf != NULL, (fflush and) write + * Returns number of chars written from fp buffer _OR_ from buf. + */ - len = fp->bufend - fp->bufpos; +off_t _uClibc_fwrite(const unsigned char *buf, off_t bytes, FILE *fp) +{ + unsigned char *p; + int rv, had_newline; - /* Flush the buffer if not enough room */ - if (bytes > len) - if (fflush(fp)) - return 0; + /* + * Fail if stream isn't writable, if we were reading and get an error + * changing over to write mode (ie. can't update stream position), + * or if the stream was already in an error state. + */ + if (!WRITEABLE(fp)) { /* Fail if stream isn't writable. */ + fp->mode |= __MODE_ERR; + } else if (READING(fp)) { /* If read buffer isn't empty, */ + fseek(fp, 0, SEEK_CUR); /* stop reading and update position. */ + } else if (READABLE(fp)) { + fp->bufread = fp->bufstart; /* Reset start of read buffer. */ + } + if (EOF_OR_ERROR(fp)) { + return 0; + } - len = fp->bufend - fp->bufpos; - if (bytes <= len) { /* It'll fit in the buffer ? */ - fp->mode |= __MODE_WRITING; - memcpy(fp->bufpos, buf, bytes); - fp->bufpos += bytes; - - /* If we're not fully buffered */ - if (v & (_IOLBF | _IONBF)) - fflush(fp); - - return nelm; - } else - /* Too big for the buffer */ - { - put = bytes; - do { - len = write(fp->fd, buf, bytes); - if (len > 0) { - buf += len; - bytes -= len; + p = (unsigned char *)buf; + if (p && (fp->bufpos + bytes <= fp->bufend)) { /* Enough buffer space? */ + had_newline = 0; + while (bytes--) { + if (*p == '\n') { + had_newline = 1; } + *fp->bufpos++ = *p++; } - while (len > 0 || (len == -1 && errno == EINTR)); - - if (len < 0) - fp->mode |= __MODE_ERR; + if (fp->bufpos < fp->bufend) { /* Buffer is not full. */ + fp->bufwrite = fp->bufend; + if ((fp->mode & __MODE_BUF) == _IOLBF) { + fp->bufwrite = fp->bufpos; + if (had_newline) { + goto FFLUSH; + } + } + goto DONE; + } + FFLUSH: + /* If we get here, either buffer is full or we need to flush anyway. */ + p = NULL; + } + if (!p) { /* buf == NULL means fflush */ + p = fp->bufstart; + bytes = fp->bufpos - p; + buf = fp->bufpos = fp->bufwrite = p; + } else if (fp->bufpos > fp->bufstart) { /* If there are buffered chars, */ + _uClibc_fwrite(NULL, 0, fp); /* write them. */ + if (ferror(fp)) { + return 0; + } + } - put -= bytes; + while (bytes) { + if ((rv = write(fp->fd, p, bytes)) < 0) { + rv = 0; + if (errno != EINTR) { + break; + } + } + p += rv; + bytes -= rv; + } + if (bytes) { + fp->mode |= __MODE_ERR; } - return put / size; + DONE: + return (p - (unsigned char *)buf); } #endif @@ -518,47 +556,45 @@ FILE *fp; void rewind(fp) FILE *fp; { - fseek(fp, (long) 0, 0); - clearerr(fp); + clearerr(fp); /* Clear errors first, then seek in case */ + fseek(fp, 0, SEEK_SET); /* there is an error seeking. */ } #endif #ifdef L_fseek -int fseek(fp, offset, ref) -FILE *fp; -long offset; -int ref; +int fseek(FILE *fp, long int offset, int ref) { -#if 0 - /* FIXME: this is broken! BROKEN!!!! */ - /* if __MODE_READING and no ungetc ever done can just move pointer */ - /* This needs testing! */ - - if ((fp->mode & (__MODE_READING | __MODE_UNGOT)) == __MODE_READING && - (ref == SEEK_SET || ref == SEEK_CUR)) { - long fpos = lseek(fp->fd, 0L, SEEK_CUR); +#if SEEK_SET != 0 || SEEK_CUR != 1 || SEEK_END != 2 +#error Assumption violated -- values of SEEK_SET, SEEK_CUR, SEEK_END +#endif - if (fpos == -1) - return EOF; + if ((ref < 0) || (ref > 2)) { + errno = EINVAL; + return -1; + } + if (WRITING(fp)) { + fflush(fp); /* We'll deal with errors below. */ + /* After fflush, bufpos is at CUR. */ + } else if (READING(fp)) { if (ref == SEEK_CUR) { - ref = SEEK_SET; - offset = fpos + offset + fp->bufpos - fp->bufread; - } - if (ref == SEEK_SET) { - if (offset < fpos - && offset >= fpos + fp->bufstart - fp->bufread) { - fp->bufpos = offset - fpos + fp->bufread; - return 0; + /* Correct offset to take into account position in buffer. */ + offset -= (fp->bufread - fp->bufpos); + if (fp->mode & __MODE_UNGOT) { /* If we had an ungetc'd char, */ + --offset; /* adjust offset (clear flag below). */ } } + fp->bufpos = fp->bufread = fp->bufstart; } -#endif - /* Use fflush to sync the pointers */ - if (fflush(fp) || (lseek(fp->fd, offset, ref) < 0)) { - return EOF; + if ((fp->mode & __MODE_ERR) || + (((ref != SEEK_CUR) || offset) && (lseek(fp->fd, offset, ref) < 0))) { + fp->mode |= __MODE_ERR; /* Possibly redundant, but doesn't hurt. */ + return -1; } + + fp->mode &= ~(__MODE_EOF | __MODE_UNGOT); + return 0; } #endif @@ -567,9 +603,28 @@ int ref; long ftell(fp) FILE *fp; { - if (fflush(fp) == EOF) - return EOF; - return lseek(fp->fd, 0L, SEEK_CUR); + /* Note: can't do fflush here since it would discard any ungetc's. */ + off_t pos; + + pos = lseek(fp->fd, 0, SEEK_CUR); /* Get kernels idea of position. */ + if (pos < 0) { + return -1; + } + + if (WRITING(fp)) { + pos += (fp->bufpos - fp->bufstart); /* Adjust for buffer position. */ + } else if (READING(fp)) { + pos -= (fp->bufread - fp->bufpos); /* Adjust for buffer position. */ + if (fp->mode & __MODE_UNGOT) { + --pos; + } + if (pos < 0) { /* ungetcs at start of file? */ + errno = EIO; + pos = -1; + } + } + + return pos; } #endif @@ -578,6 +633,23 @@ FILE *fp; * This Fopen is all three of fopen, fdopen and freopen. The macros in * stdio.h show the other names. */ +static __inline FILE *_alloc_stdio_stream(void) +{ + FILE *fp; + + if (_free_file_list) { + fp = _free_file_list; + _free_file_list = fp->next; + } else if (!(fp = malloc(sizeof(FILE)))) { + return 0; + } + fp->mode = __MODE_FREEFIL | _IOFBF; + /* Initially set to use builtin buffer of FILE structure. */ + fp->bufstart = fp->unbuf; + fp->bufend = fp->unbuf + sizeof(fp->unbuf); + return fp; +} + FILE *__fopen(fname, fd, fp, mode) const char *fname; int fd; @@ -586,30 +658,19 @@ const char *mode; { FILE *nfp; int open_mode; - int fopen_mode; - int i; + int cur_mode; nfp = fp; - /* If we've got an fp, flush it and close the old fd (freopen) */ - if (nfp) { /* We don't want to deallocate fp. */ - fflush(nfp); - close(nfp->fd); - nfp->mode &= (__MODE_FREEFIL | __MODE_FREEBUF); - } - /* Parse the mode string arg. */ switch (*mode++) { case 'r': /* read */ - fopen_mode = __MODE_READ; open_mode = O_RDONLY; break; case 'w': /* write (create or truncate)*/ - fopen_mode = __MODE_WRITE; open_mode = (O_WRONLY | O_CREAT | O_TRUNC); break; case 'a': /* write (create or append) */ - fopen_mode = __MODE_WRITE; open_mode = (O_WRONLY | O_CREAT | O_APPEND); break; default: /* illegal mode */ @@ -621,9 +682,13 @@ const char *mode; ++mode; } + +#if O_RDONLY != 0 || O_WRONLY != 1 || O_RDWR != 2 +#error Assumption violated concerning open mode constants! +#endif + if (*mode == '+') { /* read-write */ ++mode; - fopen_mode |= __MODE_RDWR; open_mode &= ~(O_RDONLY | O_WRONLY); open_mode |= O_RDWR; } @@ -636,32 +701,23 @@ const char *mode; } if (fp == 0) { /* We need a FILE so allocate it before */ - for (i = 0; i < FIXED_STREAMS; i++) { /* we potentially call open. */ - if (_stdio_streams[i].fd == -1) { - nfp = _stdio_streams + i; - break; - } - } - if ((i == FIXED_STREAMS) && (!(nfp = malloc(sizeof(FILE))))) { + if (!(nfp = _alloc_stdio_stream())) { return 0; } - nfp->mode = __MODE_FREEFIL; - /* Initially set to use 8 byte buffer in FILE structure */ - nfp->bufstart = nfp->unbuf; - nfp->bufend = nfp->unbuf + sizeof(nfp->unbuf); } if (fname) { /* Open the file itself */ fd = open(fname, open_mode, 0666); + } else { /* fdopen -- check mode is compatible. */ + cur_mode = fcntl(fd, F_GETFL); + if ((cur_mode == -1) || ((cur_mode ^ open_mode) & O_ACCMODE)) { + fd = -1; + } } -#warning fdopen should check that modes are compatible with existing fd. if (fd < 0) { /* Error from open or bad arg passed. */ _fopen_ERROR: if (nfp) { - if (nfp->mode & __MODE_FREEBUF) { - _free_stdio_buffer(nfp->bufstart); - } _free_stdio_stream(nfp); } return 0; @@ -680,9 +736,15 @@ const char *mode; } /* Ok, file's ready clear the buffer and save important bits */ - nfp->bufpos = nfp->bufread = nfp->bufwrite = nfp->bufstart; - nfp->mode |= fopen_mode; + nfp->bufpos = nfp->bufstart; nfp->mode |= isatty(fd); + nfp->bufread = nfp->bufwrite = 0; + if (!(open_mode & O_WRONLY)) { + nfp->bufread = nfp->bufstart; + } + if (open_mode & (O_WRONLY | O_RDWR)) { + nfp->bufwrite = nfp->bufstart; + } return nfp; } @@ -696,18 +758,14 @@ FILE *fp; FILE *ptr; int rv; - assert(fp); /* Shouldn't be NULL */ - assert(fp->fd >= 0); /* Need file descriptor in valid range. */ - - rv = fflush(fp); + rv = 0; + if (WRITING(fp)) { /* If there are buffered chars to write... */ + rv = fflush(fp); /* write them. */ + } if (close(fp->fd)) { /* Need to close even if fflush fails. */ rv = EOF; } - if (fp->mode & __MODE_FREEBUF) { /* Free buffer if necessary. */ - _free_stdio_buffer(fp->bufstart); - } - prev = 0; /* Remove file from open list. */ for (ptr = __IO_list; ptr ; ptr = ptr->next) { if (ptr == fp) { @@ -731,28 +789,24 @@ FILE *fp; /* The following is only called by fclose and _fopen. */ void _free_stdio_stream(FILE *fp) { - int i; - + _free_stdio_buffer_of_file(fp); /* Free buffer if necessary. */ + if (!(fp->mode & __MODE_FREEFIL)) { return; } - for (i = 0; i < FIXED_STREAMS; i++) { - if (fp == _stdio_streams + i) { - fp->fd = -1; - return; - } + /* Note: we generally won't bother checking for bad pointers here. */ + if ((fp >= _stdio_streams) && (fp < _stdio_streams + FIXED_STREAMS)) { + assert( (fp - _stdio_streams) % sizeof(_stdio_streams[0]) == 0 ); + fp->next = _free_file_list; + _free_file_list = fp; + return; } free(fp); } #endif #ifdef L_setbuffer -/* - * Rewritten Feb 2001 Manuel Novoa III - * - * Just call setvbuf with appropriate args. - */ void setbuffer(FILE *fp, char *buf, size_t size) { int mode; @@ -766,31 +820,27 @@ void setbuffer(FILE *fp, char *buf, size_t size) #endif #ifdef L_setvbuf -/* - * Rewritten Feb 2001 Manuel Novoa III - * - * Bugs in previous version: - * No checking on mode arg. - * If alloc of new buffer failed, some FILE fields not set correctly. - * If requested buf is same size as current and buf is NULL, then - * don't free current buffer; just use it. - */ - int setvbuf(FILE *fp, char *buf, int mode, size_t size) { int allocated_buf_flag; - if (fflush(fp)) { /* Standard requires no ops before setvbuf */ - return EOF; /* called. We'll try to be more flexible. */ + if ((mode < 0) || (mode > 2)) { /* Illegal mode. */ + return EOF; } - if (mode & ~__MODE_BUF) { /* Illegal mode. */ +#if FLEXIBLE_SETVBUF + /* C89 standard requires no ops before setvbuf, but we can be flexible. */ + /* NOTE: This will trash any chars ungetc'd!!! */ + if (fseek(fp, 0, SEEK_CUR)) { return EOF; } +#endif - if ((mode == _IONBF) || (size <= sizeof(fp->unbuf))) { - size = sizeof(fp->unbuf); /* Either no buffering requested or */ - buf = fp->unbuf; /* requested buffer size very small. */ + /* Note: If size == 2 we could use FILE's builting buffer as well, but */ + /* I don't think the benefit is worth the code size increase. */ + if ((mode == _IONBF) || (size < 1)) { + size = 1; /* size == 1 _REQUIRED_ for _IONBF!!! */ + buf = fp->unbuf; } fp->mode &= ~(__MODE_BUF); /* Clear current mode */ @@ -801,19 +851,22 @@ int setvbuf(FILE *fp, char *buf, int mode, size_t size) /* No buffer supplied and requested size different from current. */ allocated_buf_flag = __MODE_FREEBUF; if (!(buf = _alloc_stdio_buffer(size))) { - return EOF; + return EOF; /* Keep current buffer. */ } } if (buf && (buf != (char *) fp->bufstart)) { /* Want different buffer. */ - if (fp->mode & __MODE_FREEBUF) { - _free_stdio_buffer(fp->bufstart); - fp->mode &= ~(__MODE_FREEBUF); - } - fp->mode |= allocated_buf_flag; + _free_stdio_buffer_of_file(fp); /* Free the old buffer. */ + fp->mode |= allocated_buf_flag; /* Allocated? or FILE's builtin. */ fp->bufstart = buf; fp->bufend = buf + size; - fp->bufpos = fp->bufread = fp->bufwrite = fp->bufstart; + fp->bufpos = fp->bufstart; + if (READABLE(fp)) { + fp->bufread = fp->bufstart; + } + if (WRITEABLE(fp)) { + fp->bufwrite = fp->bufstart; + } } return 0; @@ -841,26 +894,47 @@ void setlinebuf(FILE *fp) #endif #ifdef L_ungetc +/* + * NOTE: Only one character of pushback is guaranteed, although sometimes + * it is possible to do more. You have 1 plus as many characters of pushback + * as have been read since that last buffer-fill. + */ int ungetc(c, fp) int c; FILE *fp; { - if (fp->mode & __MODE_WRITING) + unsigned char *p; + + /* If can't read or there's been an error, or c == EOF, or ungot slot + * already filled, then return EOF */ + /* + * This can only happen if an fgetc triggered a read (that filled + * the buffer for case 2 above) and then we ungetc 3 chars. + */ + if (!READABLE(fp) || (fp->mode & (__MODE_UNGOT | __MODE_ERR)) + || (c == EOF) ) { + return EOF; + } + + if (WRITING(fp)) { /* Commit any write-buffered chars. */ fflush(fp); + } - /* Can't read or there's been an error then return EOF */ - if ((fp->mode & (__MODE_READ | __MODE_ERR)) != __MODE_READ) - return EOF; + if (fp->bufpos > fp->bufstart) { /* We have space before bufpos. */ + p = --fp->bufpos; + } else if (fp->bufread == fp->bufpos) { /* Buffer is empty. */ + p = fp->bufread++; + } else { + fp->mode |= __MODE_UNGOT; + p = &(fp->ungot); + } + fp->mode &= ~(__MODE_EOF); /* Clear EOF indicator. */ - /* Can't do fast fseeks */ - fp->mode |= __MODE_UNGOT; + if (*p != (unsigned char) c) { /* Don't store if same, because could */ + *p = (unsigned char) c; /* be sscanf from a const string!!! */ + } - if (fp->bufpos > fp->bufstart) - return *--fp->bufpos = (unsigned char) c; - else if (fp->bufread == fp->bufstart) - return *fp->bufread++ = (unsigned char) c; - else - return EOF; + return c; } #endif @@ -874,10 +948,29 @@ FILE *fopen(const char *__restrict filename, #endif #ifdef L_freopen -#undef freopen FILE *freopen(__const char *__restrict filename, __const char *__restrict mode, FILE *__restrict fp) { + /* fflush file, close the old fd, and reset modes. */ + if (WRITING(fp)) { /* If anything in the write buffer... */ + fflush(fp); /* write it. */ + } + close(fp->fd); /* Close the file. */ + fp->mode &= (__MODE_FREEFIL | __MODE_FREEBUF); /* Reset the FILE modes. */ + fp->mode |= _IOFBF; + + return __fopen(filename, -1, fp, mode); +} +#endif + +#ifdef L_fsfopen +FILE *fsfopen(__const char *__restrict filename, + __const char *__restrict mode, FILE *__restrict fp) +{ + fp->mode = _IOFBF; + fp->bufstart = fp->unbuf; + fp->bufend = fp->unbuf + sizeof(fp->unbuf); + return __fopen(filename, -1, fp, mode); } #endif @@ -894,7 +987,7 @@ FILE *fdopen(int fd, __const char *mode) #undef getchar int getchar(void) { - return getc(stdin); + return getc(_stdin); } #endif @@ -902,7 +995,7 @@ int getchar(void) #undef putchar int putchar(int c) { - return putc(c, stdout); + return putc(c, _stdout); } #endif @@ -910,9 +1003,7 @@ int putchar(int c) #undef clearerr void clearerr(FILE *fp) { - assert(fp); - - fp->mode &= ~(__MODE_EOF|__MODE_ERR); + fp->mode &= ~(__MODE_EOF | __MODE_ERR); } #endif @@ -920,9 +1011,7 @@ void clearerr(FILE *fp) #undef feof int feof(FILE *fp) { - assert(fp); - - return ((fp->mode & __MODE_EOF) != 0); + return fp->mode & __MODE_EOF; } #endif @@ -930,18 +1019,13 @@ int feof(FILE *fp) #undef ferror int ferror(FILE *fp) { - assert(fp); - - return ((fp->mode & __MODE_ERR) != 0); + return fp->mode & __MODE_ERR; } #endif #ifdef L_fileno int fileno(FILE *fp) { - if (!fp || (fp->fd < 0)) { - return -1; - } return fp->fd; } #endif |