diff options
Diffstat (limited to 'libc/stdio/stdio.c')
-rw-r--r-- | libc/stdio/stdio.c | 3171 |
1 files changed, 3171 insertions, 0 deletions
diff --git a/libc/stdio/stdio.c b/libc/stdio/stdio.c new file mode 100644 index 000000000..01e0e35e9 --- /dev/null +++ b/libc/stdio/stdio.c @@ -0,0 +1,3171 @@ +/* Copyright (C) 2002 Manuel Novoa III + * My stdio library for linux and (soon) elks. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! + * + * This code is currently under development. Also, I plan to port + * it to elks which is a 16-bit environment with a fairly limited + * compiler. Therefore, please refrain from modifying this code + * and, instead, pass any bug-fixes, etc. to me. Thanks. Manuel + * + * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */ + +#define _ISOC99_SOURCE /* for ULLONG primarily... */ +#define _GNU_SOURCE +#define _STDIO_UTILITY /* for _stdio_fdout and _uintmaxtostr. */ +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <stdint.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <stdio_ext.h> +#include <unistd.h> +#include <fcntl.h> + +/**********************************************************************/ +/* First deal with some build issues... */ + +#ifndef __STDIO_THREADSAFE +/* Just build empty object files if any of these were defined. */ +/* Note though that we do keep the various *_unlocked names as aliases. */ +#undef L___fsetlocking +#undef L___flockfile +#undef L___ftrylockfile +#undef L___funlockfile +#endif + +#ifndef __STDIO_LARGE_FILES +/* Just build empty object files if any of these were defined. */ +#undef L_fopen64 +#undef L_freopen64 +#undef L_ftello64 +#undef L_fseeko64 +#undef L_fsetpos64 +#undef L_fgetpos64 +#endif + +/**********************************************************************/ + +/* TODO -- make this the default except for bcc with it's broken preproc? */ +#ifdef __UCLIBC__ +#define _stdin stdin +#define _stdout stdout +#define _stderr stderr +#endif /* __UCLIBC__ */ + +/**********************************************************************/ + +#ifndef __STDIO_THREADSAFE + +#define UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,STREAM) \ +strong_alias(NAME,NAME##_unlocked) \ +RETURNTYPE NAME PARAMS + +#define UNLOCKED(RETURNTYPE,NAME,PARAMS,ARGS) \ + UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,stream) + +#define UNLOCKED_VOID_RETURN(NAME,PARAMS,ARGS) \ +strong_alias(NAME,NAME##_unlocked) \ +void NAME PARAMS + +#define __STDIO_THREADLOCK_OPENLIST +#define __STDIO_THREADUNLOCK_OPENLIST + +#else /* __STDIO_THREADSAFE */ + +#include <pthread.h> + +#define UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,STREAM) \ +RETURNTYPE NAME PARAMS \ +{ \ + RETURNTYPE retval; \ + __STDIO_THREADLOCK(STREAM); \ + retval = NAME##_unlocked ARGS ; \ + __STDIO_THREADUNLOCK(STREAM); \ + return retval; \ +} \ +RETURNTYPE NAME##_unlocked PARAMS + +#define UNLOCKED(RETURNTYPE,NAME,PARAMS,ARGS) \ + UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,stream) + +#define UNLOCKED_VOID_RETURN(NAME,PARAMS,ARGS) \ +void NAME PARAMS \ +{ \ + __STDIO_THREADLOCK(stream); \ + NAME##_unlocked ARGS ; \ + __STDIO_THREADUNLOCK(stream); \ +} \ +void NAME##_unlocked PARAMS + +#define __STDIO_THREADLOCK_OPENLIST \ + pthread_mutex_lock(&_stdio_openlist_lock) + +#define __STDIO_THREADUNLOCK_OPENLIST \ + pthread_mutex_unlock(&_stdio_openlist_lock) + +#define __STDIO_THREADTRYLOCK_OPENLIST \ + pthread_mutex_trylock(&_stdio_openlist_lock) + +#endif /* __STDIO_THREADSAFE */ + +/**********************************************************************/ + +#ifdef __STDIO_WIDE +#define __STDIO_FILE_INIT_UNGOT { 0, 0 }, { 0, 0 }, +#else +#define __STDIO_FILE_INIT_UNGOT { 0, 0 }, +#endif + +#ifdef __STDIO_GETC_MACRO +#define __STDIO_FILE_INIT_BUFGETC(x) x, +#else +#define __STDIO_FILE_INIT_BUFGETC(x) +#endif + +#ifdef __STDIO_PUTC_MACRO +#define __STDIO_FILE_INIT_BUFPUTC(x) x, +#else +#define __STDIO_FILE_INIT_BUFPUTC(x) +#endif + +#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) +#define __STDIO_FILE_INIT_NEXT(next) (next), +#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ +#define __STDIO_FILE_INIT_NEXT(next) +#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ + +#ifdef __STDIO_BUFFERS +#define __STDIO_FILE_INIT_BUFFERS(buf,bufsize) \ + (buf), (buf)+(bufsize), (buf), (buf), +#else +#define __STDIO_FILE_INIT_BUFFERS(buf,bufsize) +#endif + +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS +#define __STDIO_FILE_INIT_CUSTOM_STREAM(stream) \ + &((stream).filedes), { _cs_read, _cs_write, NULL, _cs_close }, +#else +#define __STDIO_FILE_INIT_CUSTOM_STREAM(stream) +#endif + +#ifdef __STDIO_THREADSAFE +#define __STDIO_FILE_INIT_THREADSAFE \ + 0, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, +#else +#define __STDIO_FILE_INIT_THREADSAFE +#endif + +#define __STDIO_INIT_FILE_STRUCT(stream, flags, filedes, next, buf, bufsize) \ + { (flags), \ + __STDIO_FILE_INIT_UNGOT \ + (filedes), \ + __STDIO_FILE_INIT_NEXT(next) \ + __STDIO_FILE_INIT_BUFFERS(buf,bufsize) \ + __STDIO_FILE_INIT_BUFGETC((buf)) \ + __STDIO_FILE_INIT_BUFPUTC((buf)) \ + __STDIO_FILE_INIT_CUSTOM_STREAM(stream) \ + __STDIO_FILE_INIT_THREADSAFE \ +} /* TODO: mbstate and builtin buf */ + +#ifdef __STDIO_MBSTATE_DATA +extern void _init_mbstate(mbstate_t *dest); + +#define __COMMA_CLEAN_MBSTATE , 0 +#define __COPY_MBSTATE(dest,src) memcpy(dest, src, sizeof(mbstate_t)) +#define __INIT_MBSTATE(dest) _init_mbstate(dest) +#else +#define __COMMA_CLEAN_MBSTATE +#define __COPY_MBSTATE(dest,src) +#define __INIT_MBSTATE(dest) +#endif + +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + +/* TODO -- what does glibc do for undefined funcs? errno set? */ +#define __READ(STREAMPTR,BUF,SIZE) \ + ((((STREAMPTR)->gcs.read) == NULL) ? -1 : \ + (((STREAMPTR)->gcs.read)((STREAMPTR)->cookie,(BUF),(SIZE)))) +#define __WRITE(STREAMPTR,BUF,SIZE) \ + ((((STREAMPTR)->gcs.write) == NULL) ? -1 : \ + (((STREAMPTR)->gcs.write)((STREAMPTR)->cookie,(BUF),(SIZE)))) +#define __CLOSE(STREAMPTR) \ + ((((STREAMPTR)->gcs.close) == NULL) ? 0 : \ + (((STREAMPTR)->gcs.close)((STREAMPTR)->cookie))) + +#else /* __STDIO_GLIBC_CUSTOM_STREAMS */ + +#define __READ(STREAMPTR,BUF,SIZE) \ + (read((STREAMPTR)->filedes,(BUF),(SIZE))) +#define __WRITE(STREAMPTR,BUF,SIZE) \ + (write((STREAMPTR)->filedes,(BUF),(SIZE))) +#define __CLOSE(STREAMPTR) \ + (close((STREAMPTR)->filedes)) + +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ + +/**********************************************************************/ +/* POSIX functions */ +/**********************************************************************/ +#ifdef L_getw + +/* SUSv2 Legacy function -- need not be reentrant. */ + +int getw (register FILE *stream) +{ + int aw[1]; + +#ifdef __STDIO_WIDE + + return (fread((void *)aw, sizeof(int), 1, stream) > 0) ? (*aw) : EOF; + +#else /* __STDIO_WIDE */ + + return (_stdio_fread((unsigned char *)(aw), sizeof(int), stream) + == sizeof(int)) ? (*aw) : EOF; + +#endif /* __STDIO_WIDE */ +} + +#endif +/**********************************************************************/ +#ifdef L_putw + +/* SUSv2 Legacy function -- need not be reentrant. */ + +int putw (int w, register FILE *stream) +{ + int aw[1]; + + *aw = w; /* In case 'w' is in a register... */ + +#ifdef __STDIO_WIDE + + return (fwrite((void *)aw, sizeof(int), 1, stream) == 1) ? 0 : EOF; + +#else /* __STDIO_WIDE */ + + return (_stdio_fwrite((unsigned char *)aw, sizeof(int), stream) + == sizeof(int)) ? 0 : EOF; + +#endif /* __STDIO_WIDE */ +} + +#endif +/**********************************************************************/ +#ifdef L_fileno + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(int,fileno,(register FILE *stream),(stream)) +{ +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + return ( ((stream->cookie == &(stream->filedes)) && (stream->filedes >= 0)) + ? stream->filedes + : (__set_errno(EBADF), -1) ); +#else /* __STDIO_GLIBC_CUSTOM_STREAMS */ + return (stream->filedes >= 0) ? stream->filedes : (__set_errno(EBADF), -1); +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ +} + +#endif +/**********************************************************************/ +#ifdef L_fdopen + +/* No reentrancy issues. */ + +FILE *fdopen(int filedes, const char *mode) +{ + register char *cur_mode; /* TODO -- replace by intptr_t?? */ + + return (((int)(cur_mode = (char *) fcntl(filedes, F_GETFL))) != -1) + ? _stdio_fopen(cur_mode, mode, NULL, filedes) + : NULL; +} + +#endif +/**********************************************************************/ +#ifdef L_fopen64 + +/* No reentrancy issues. */ + +FILE *fopen64(const char * __restrict filename, const char * __restrict mode) +{ + return _stdio_fopen(filename, mode, NULL, -2); +} + +#endif +/**********************************************************************/ +/* BSD functions */ +/**********************************************************************/ +#ifdef L_setbuffer + +/* No reentrancy issues. */ + +void setbuffer(FILE * __restrict stream, register char * __restrict buf, + size_t size) +{ +#ifdef __STDIO_BUFFERS + setvbuf(stream, buf, (buf ? _IOFBF : _IONBF), size); +#else /* __STDIO_BUFFERS */ + /* Nothing to do. */ +#endif /* __STDIO_BUFFERS */ +} + +#endif +/**********************************************************************/ +#ifdef L_setlinebuf + +/* No reentrancy issues. */ + +void setlinebuf(FILE * __restrict stream) +{ +#ifdef __STDIO_BUFFERS + setvbuf(stream, NULL, _IOLBF, (size_t) BUFSIZ); +#else /* __STDIO_BUFFERS */ + /* Nothing to do. */ +#endif /* __STDIO_BUFFERS */ +} + +#endif +/**********************************************************************/ +/* GLIBC functions */ +/**********************************************************************/ +#ifdef L_fcloseall + +/* NOTE: GLIBC difference!!! -- fcloseall + * According to the info pages, glibc actually fclose()s all open files. + * Apparently, glibc's new version only fflush()s and unbuffers all + * writing streams to cope with unordered destruction of c++ static + * objects. Here we implement the old behavior as default. + */ + +/* Not reentrant. */ + +int fcloseall (void) +{ +#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) + register FILE *stream; + int rv; + + _stdio_term(); /* Let _stdio_term() do all the work. */ + + rv = 0; + for (stream = _stdio_openlist ; stream ; stream = stream->nextopen) { + if (stream->modeflags & (__FLAG_WRITING|__FLAG_ERROR)) { + /* TODO -- is this correct? Maybe ferror set before flush... + * could check if pending writable but what if term unbuffers? + * in that case, could clear error flag... */ + rv = EOF; /* Only care about failed writes. */ + } + } + + /* Make sure _stdio_term() does nothing on exit. */ + _stdio_openlist = NULL; + + return rv; +#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ + + return 0; + +#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ +} + +#endif +/**********************************************************************/ +#ifdef L_fmemopen +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + +typedef struct { + size_t pos; + size_t len; + size_t eof; + int dynbuf; + unsigned char *buf; + FILE *fp; +} __fmo_cookie; + +#define COOKIE ((__fmo_cookie *) cookie) + +static ssize_t fmo_read(void *cookie, register char *buf, size_t bufsize) +{ + size_t count = COOKIE->len - COOKIE->pos; + + /* Note: bufsize < SSIZE_MAX because of _stdio_READ. */ + + if (bufsize > count) { + bufsize = count; + } + +#if 1 /* TODO - choose code option */ + memcpy(buf, COOKIE->buf + COOKIE->pos, bufsize); + COOKIE->pos += bufsize; +#else + { + register char *p = COOKIE->buf + COOKIE->pos; + + count = bufsize; + while (count) { + *buf++ = *p++; + --count; + } + COOKIE->pos += bufsize; + } +#endif + + return bufsize; +} + +static ssize_t fmo_write(void *cookie, register const char *buf, size_t bufsize) +{ + size_t count; + + /* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */ + + /* If appending, need to seek to end of file!!!! */ + if (COOKIE->fp->modeflags & __FLAG_APPEND) { + COOKIE->pos = COOKIE->eof; + } + + count = COOKIE->len - COOKIE->pos; + + if (bufsize > count) { + bufsize = count; + if (count == 0) { /* We're at the end of the buffer... */ + errno = EFBIG; + return -1; + } + } + +#if 1 /* TODO - choose code option */ + memcpy(COOKIE->buf + COOKIE->pos, buf, bufsize); + COOKIE->pos += bufsize; + + if (COOKIE->pos > COOKIE->eof) { + COOKIE->eof = COOKIE->pos; + if (bufsize < count) { /* New eof and still room in buffer? */ + *(COOKIE->buf + COOKIE->pos) = 0; + } + } + +#else + { + register char *p = COOKIE->buf + COOKIE->pos; + size_t i = bufsize; + + while (i > 0) { + *p++ = *buf++; + --i; + } + COOKIE->pos += bufsize; + + if (COOKIE->pos > COOKIE->eof) { + COOKIE->eof = COOKIE->pos; + if (bufsize < count) { /* New eof and still room in buffer? */ + *p = 0; + } + } + } + +#endif + + return bufsize; +} + +/* glibc doesn't allow seeking, but it has in-buffer seeks... we don't. */ +static int fmo_seek(void *cookie, __offmax_t *pos, int whence) +{ + __offmax_t p = *pos; + + /* Note: fseek already checks that whence is legal, so don't check here + * unless debugging. */ + assert(((unsigned int) whence) <= 2); + + if (whence != SEEK_SET) { + p += (whence == SEEK_CUR) ? COOKIE->pos : /* SEEK_END */ COOKIE->eof; + } + + /* Note: glibc only allows seeking in the buffer. We'll actually restrict + * to the data. */ + /* Check for offset < 0, offset > eof, or offset overflow... */ + if (((uintmax_t) p) > COOKIE->eof) { + return -1; + } + + COOKIE->pos = *pos = p; + return 0; +} + +static int fmo_close(void *cookie) +{ + if (COOKIE->dynbuf) { + free(COOKIE->buf); + } + free(cookie); + return 0; +} + +#undef COOKIE + +static const cookie_io_functions_t _fmo_io_funcs = { + fmo_read, fmo_write, fmo_seek, fmo_close +}; + +/* TODO: If we have buffers enabled, it might be worthwile to add a pointer + * to the FILE in the cookie and have read, write, and seek operate directly + * on the buffer itself (ie replace the FILE buffer with the cookie buffer + * and update FILE bufstart, etc. whenever we seek). */ + +FILE *fmemopen(void *s, size_t len, const char *modes) +{ + FILE *fp; + __fmo_cookie *cookie; + size_t i; + + if ((cookie = malloc(sizeof(__fmo_cookie))) != NULL) { + cookie->len = len; + cookie->eof = cookie->pos = 0; /* pos and eof adjusted below. */ + cookie->dynbuf = 0; + if (((cookie->buf = s) == NULL) && (len > 0)) { + if ((cookie->buf = malloc(len)) == NULL) { + goto EXIT_cookie; + } + cookie->dynbuf = 1; + *cookie->buf = 0; /* If we're appending, treat as empty file. */ + } + +#ifndef __BCC__ + fp = fopencookie(cookie, modes, _fmo_io_funcs); +#else + fp = fopencookie(cookie, modes, &_fmo_io_funcs); +#endif + /* Note: We don't need to worry about locking fp in the thread case + * as the only possible access would be a close or flush with + * nothing currently in the FILE's write buffer. */ + + if (fp != NULL) { + cookie->fp = fp; + if ((fp->modeflags & __FLAG_APPEND) && (len > 0)) { + for (i = 0 ; i < len ; i++) { + if (cookie->buf[i] == 0) { + break; + } + } + cookie->eof = cookie->pos = i; /* Adjust eof and pos. */ + } + return fp; + } + } + + if (!s) { + free(cookie->buf); + } + EXIT_cookie: + free(cookie); + + return NULL; +} + +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ +#endif +/**********************************************************************/ +#ifdef L_open_memstream +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + +#define COOKIE ((__oms_cookie *) cookie) + +typedef struct { + char *buf; + size_t len; + size_t pos; + size_t eof; + char **bufloc; + size_t *sizeloc; +} __oms_cookie; + +/* Nothing to do here, as memstreams are write-only. */ +/* static ssize_t oms_read(void *cookie, char *buf, size_t bufsize) */ +/* { */ +/* } */ + +static ssize_t oms_write(void *cookie, const char *buf, size_t bufsize) +{ + char *newbuf; + size_t count; + + /* Note: we already know bufsize < SSIZE_MAX... */ + + count = COOKIE->len - COOKIE->pos - 1; + assert(COOKIE->pos < COOKIE->len); /* Always nul-terminate! */ + + if (bufsize > count) { + newbuf = realloc(COOKIE->buf, COOKIE->len + bufsize - count); + if (newbuf) { + *COOKIE->bufloc = COOKIE->buf = newbuf; + COOKIE->len += (bufsize - count); + } else { + bufsize = count; + if (count == 0) { + errno = EFBIG; /* TODO: check glibc errno setting... */ + return -1; + } + } + } + + memcpy(COOKIE->buf + COOKIE->pos, buf, bufsize); + COOKIE->pos += bufsize; + + if (COOKIE->pos > COOKIE->eof) { + *COOKIE->sizeloc = COOKIE->eof = COOKIE->pos; + } + + return bufsize; +} + +static int oms_seek(void *cookie, __offmax_t *pos, int whence) +{ + __offmax_t p = *pos; + char *buf; + size_t leastlen; + + /* Note: fseek already checks that whence is legal, so don't check here + * unless debugging. */ + assert(((unsigned int) whence) <= 2); + + if (whence != SEEK_SET) { + p += (whence == SEEK_CUR) ? COOKIE->pos : /* SEEK_END */ COOKIE->eof; + } + + /* Note: glibc only allows seeking in the buffer. We'll actually restrict + * to the data. */ + /* Check for offset < 0, offset >= too big (need nul), or overflow... */ + if (((uintmax_t) p) >= SIZE_MAX - 1) { + return -1; + } + + leastlen = ((size_t) p) + 1; /* New pos + 1 for nul if necessary. */ + + if (leastlen >= COOKIE->len) { /* Need to grow buffer... */ + buf = realloc(COOKIE->buf, leastlen); + if (buf) { + *COOKIE->bufloc = COOKIE->buf = buf; + COOKIE->len = leastlen; + memset(buf + COOKIE->eof, leastlen - COOKIE->eof, 0); /* 0-fill */ + } else { + /* TODO: check glibc errno setting... */ + return -1; + } + } + + *pos = COOKIE->pos = --leastlen; + + if (leastlen > COOKIE->eof) { + memset(COOKIE->buf + COOKIE->eof, leastlen - COOKIE->eof, 0); + *COOKIE->sizeloc = COOKIE->eof; + } + + return 0; +} + +static int oms_close(void *cookie) +{ + free(cookie); + return 0; +} + +#undef COOKIE + +static const cookie_io_functions_t _oms_io_funcs = { + NULL, oms_write, oms_seek, oms_close +}; + +/* TODO: If we have buffers enabled, it might be worthwile to add a pointer + * to the FILE in the cookie and operate directly on the buffer itself + * (ie replace the FILE buffer with the cookie buffer and update FILE bufstart, + * etc. whenever we seek). */ + +FILE *open_memstream(char **__restrict bufloc, size_t *__restrict sizeloc) +{ + __oms_cookie *cookie; + FILE *fp; + + if ((cookie = malloc(sizeof(__oms_cookie))) != NULL) { + if ((cookie->buf = malloc(cookie->len = BUFSIZ)) == NULL) { + goto EXIT_cookie; + } + *cookie->buf = 0; /* Set nul terminator for buffer. */ + *(cookie->bufloc = bufloc) = cookie->buf; + *(cookie->sizeloc = sizeloc) = cookie->eof = cookie->pos = 0; + +#ifndef __BCC__ + fp = fopencookie(cookie, "w", _oms_io_funcs); +#else + fp = fopencookie(cookie, "w", &_oms_io_funcs); +#endif + /* Note: We don't need to worry about locking fp in the thread case + * as the only possible access would be a close or flush with + * nothing currently in the FILE's write buffer. */ + + if (fp != NULL) { + return fp; + } + } + + if (cookie->buf != NULL) { + free(cookie->buf); + } + EXIT_cookie: + free(cookie); + + return NULL; +} + +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ +#endif +/**********************************************************************/ +#ifdef L_fopencookie +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + +/* NOTE: GLIBC difference!!! -- fopencookie + * According to the info pages, glibc allows seeking within buffers even if + * no seek function is supplied. We don't. */ + +/* NOTE: GLIBC difference!!! -- fopencookie + * When compiled without large file support, the offset pointer for the + * cookie_seek function is off_t * and not off64_t * as for glibc. */ + +/* TODO: rewrite _stdio_fopen() to avoid the fopencookie() kludge. */ + +/* Currently no real reentrancy issues other than a possible double close(). */ + +#ifndef __BCC__ + +FILE *fopencookie (void * __restrict cookie, const char * __restrict mode, + cookie_io_functions_t io_functions) +{ + FILE *stream; + int fd; + + if ((stream = _stdio_fopen("/dev/null", mode, NULL, -1)) != NULL) { + fd = stream->filedes; + stream->filedes = -1; + close(fd); + stream->gcs = io_functions; + stream->cookie = cookie; + } + +#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_THREADSAFE) + /* I we don't have buffers or threads, we only need to worry about + * custom streams on the open list, as no flushing is necessary and + * no locking of possible underlying normal streams need be done. + * We do need to explicitly close custom streams on termination of stdio, + * and we need to lock the list as it can be modified by fclose(). */ + __STDIO_THREADLOCK_OPENLIST; + stream->nextopen = _stdio_openlist; /* New files are inserted at */ + _stdio_openlist = stream; /* the head of the list. */ + __STDIO_THREADUNLOCK_OPENLIST; +#endif /* !defined(__STDIO_BUFFERS) && !defined(__STDIO_THREADSAFE) */ + + return stream; +} + +#else /* __BCC__ */ + +/* NOTE: GLIBC difference!!! -- fopencookie (bcc only) + * Since bcc doesn't support passing of structs, we define fopencookie as a + * macro in terms of _fopencookie which takes a struct * for the io functions + * instead. + */ + +FILE *_fopencookie (void * __restrict cookie, const char * __restrict mode, + cookie_io_functions_t *io_functions) +{ + FILE *stream; + + if ((stream = _stdio_fopen("/dev/null", mode, NULL, -1)) != NULL) { + int fd = stream->filedes; + stream->filedes = -1; + close(fd); + stream->gcs.read = io_functions->read; + stream->gcs.write = io_functions->write; + stream->gcs.seek = io_functions->seek; + stream->gcs.close = io_functions->close; + stream->cookie = cookie; + } + +#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_THREADSAFE) + /* I we don't have buffers or threads, we only need to worry about + * custom streams on the open list, as no flushing is necessary and + * no locking of possible underlying normal streams need be done. + * We do need to explicitly close custom streams on termination of stdio, + * and we need to lock the list as it can be modified by fclose(). */ + __STDIO_THREADLOCK_OPENLIST; + stream->nextopen = _stdio_openlist; /* New files are inserted at */ + _stdio_openlist = stream; /* the head of the list. */ + __STDIO_THREADUNLOCK_OPENLIST; +#endif /* !defined(__STDIO_BUFFERS) && !defined(__STDIO_THREADSAFE) */ + + return stream; +} + +#endif /* __BCC__ */ + +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ +#endif +/**********************************************************************/ +#ifdef L___fbufsize + +/* Not reentrant. */ + +size_t __fbufsize(register FILE * __restrict stream) +{ +#ifdef __STDIO_BUFFERS + return (stream->modeflags & __FLAG_NBF) + ? 0 : (stream->bufend - stream->bufstart); +#else /* __STDIO_BUFFERS */ + return 0; +#endif /* __STDIO_BUFFERS */ +} + +#endif +/**********************************************************************/ +#ifdef L___freading + +/* No reentrancy issues. */ + +int __freading(FILE * __restrict stream) +{ + return stream->modeflags & (__FLAG_READING|__FLAG_READONLY); +} + +#endif +/**********************************************************************/ +#ifdef L___fwriting + +/* No reentrancy issues. */ + +int __fwriting(FILE * __restrict stream) +{ + return stream->modeflags & (__FLAG_WRITING|__FLAG_WRITEONLY); +} + +#endif +/**********************************************************************/ +#ifdef L___freadable + +/* No reentrancy issues. */ + +int __freadable(FILE * __restrict stream) +{ + return ~(stream->modeflags & __FLAG_WRITEONLY); +} + +#endif +/**********************************************************************/ +#ifdef L___fwritable + +/* No reentrancy issues. */ + +int __fwritable(FILE * __restrict stream) +{ + return ~(stream->modeflags & __FLAG_READONLY); +} + +#endif +/**********************************************************************/ +#ifdef L___flbf + +/* No reentrancy issues. */ + +int __flbf(FILE * __restrict stream) +{ +#ifdef __STDIO_BUFFERS + return (stream->modeflags & __FLAG_LBF); +#else /* __STDIO_BUFFERS */ + /* TODO -- Even though there is no buffer, return flag setting? */ + return __FLAG_NBF; +#endif /* __STDIO_BUFFERS */ +} + +#endif +/**********************************************************************/ +#ifdef L___fpurge + +/* Not reentrant. */ + +void __fpurge(register FILE * __restrict stream) +{ +#ifdef __STDIO_BUFFERS +#ifdef __STDIO_PUTC_MACRO + stream->bufputc = /* Must disable putc. */ +#endif /* __STDIO_PUTC_MACRO */ +#ifdef __STDIO_GETC_MACRO + stream->bufgetc = /* Must disable getc. */ +#endif + stream->bufwpos = stream->bufrpos = stream->bufstart; /* Reset pointers. */ +#endif /* __STDIO_BUFFERS */ + /* Reset r/w flags and clear ungots. */ + stream->modeflags &= ~(__FLAG_READING|__FLAG_WRITING|__MASK_UNGOT); +} + +#endif +/**********************************************************************/ +#ifdef L___fpending + +/* Not reentrant. */ + +#ifdef __STDIO_WIDE +#warning TODO -- implement __fpending for wide streams! */ +#else /* __STDIO_WIDE */ +size_t __fpending(register FILE * __restrict stream) +{ +#ifdef __STDIO_BUFFERS + /* TODO -- should we check this? should we set errno? just assert? */ + return (stream->modeflags & (__FLAG_READING|__FLAG_READONLY)) + ? 0 : (stream->bufwpos - stream->bufstart); +#else /* __STDIO_BUFFERS */ + return 0; +#endif /* __STDIO_BUFFERS */ +} +#endif /* __STDIO_WIDE */ + +#endif +/**********************************************************************/ +#ifdef L__flushlbf + +/* No reentrancy issues. */ + +void _flushlbf(void) +{ +#ifdef __STDIO_BUFFERS + fflush((FILE *) &_stdio_openlist); /* Uses an implementation hack!!! */ +#else /* __STDIO_BUFFERS */ + /* Nothing to do. */ +#endif /* __STDIO_BUFFERS */ +} + +#endif +/**********************************************************************/ +#ifdef L___fsetlocking + +/* No (serious) reentrancy issues -- return value could be incorrect. */ +/* TODO -- fix race */ + +int __fsetlocking(FILE *stream, int locking_mode) +{ + int old_mode; + + assert((FSETLOCKING_QUERY == 0) && (FSETLOCKING_INTERNAL == 1) + && (FSETLOCKING_BYCALLER == 2)); + + assert(((unsigned int) locking_mode) <= 2); + + /* Note: don't even bother locking here... */ + + old_mode = stream->user_locking; + + if (locking_mode != FSETLOCKING_QUERY) { + /* In case we're not debugging, treat any unknown as a request to + * set internal locking, in order to match glibc behavior. */ + stream->user_locking = ((locking_mode == FSETLOCKING_BYCALLER) + ? FSETLOCKING_BYCALLER + : FSETLOCKING_INTERNAL); + } + + return old_mode; +} + +#endif +/**********************************************************************/ +#ifdef L_flockfile + +void flockfile(FILE *stream) +{ + pthread_mutex_lock(&stream->lock); +} + +#endif +/**********************************************************************/ +#ifdef L_ftrylockfile + +int ftrylockfile(FILE *stream) +{ + return pthread_mutex_trylock(&stream->lock); +} + +#endif +/**********************************************************************/ +#ifdef L_funlockfile + +void funlockfile(FILE *stream) +{ + pthread_mutex_unlock(&stream->lock); +} + +#endif +/**********************************************************************/ +/* my extension functions */ +/**********************************************************************/ +#ifdef L__stdio_fsfopen +/* + * Stack|Static File open -- open a file where the FILE is either + * stack or staticly allocated. + */ + +/* No reentrancy issues. */ + +FILE *_stdio_fsfopen(const char * __restrict filename, + const char * __restrict mode, + register FILE * __restrict stream) +{ +#ifdef __STDIO_BUFFERS + stream->modeflags = __FLAG_FBF; +#if __STDIO_BUILTIN_BUF_SIZE > 0 + stream->bufstart = stream->builtinbuf; + stream->bufend = stream->builtinbuf + sizeof(stream->builtinbuf); +#else /* __STDIO_BUILTIN_BUF_SIZE > 0 */ + stream->bufend = stream->bufstart = NULL; +#endif /* __STDIO_BUILTIN_BUF_SIZE > 0 */ +#endif /* __STDIO_BUFFERS */ + + return _stdio_fopen(filename, mode, stream, -1); +} +#endif +/**********************************************************************/ +/* stdio internal functions */ +/**********************************************************************/ +#ifdef L__stdio_adjpos +/* + * ANSI/ISO p. 370: The file positioning indicator is unspecified after + * a successful call to ungetwc. + * Note however, that this applies only to _user_ calls to ungetwc. We + * need to allow for internal calls by scanf. So we store the byte count + * of the first ungot wide char in ungot0_bytes. If it is 0 (user case) + * then the file position is treated as unknown. + */ + + +/* Internal function -- not reentrant. */ + +int _stdio_adjpos(register FILE * __restrict stream, + register __offmax_t *pos) +{ + __offmax_t r; + int cor = stream->modeflags & __MASK_UNGOT; /* handle ungots */ + +#ifdef __STDIO_WIDE + /* Assumed narrow stream so correct if wide. */ + if (cor && (stream->modeflags & __FLAG_WIDE)) { + cor = cor - 1 + stream->ungot_width[0]; + if ((stream->ungot_width[0] == 0) /* don't know byte count or */ + || ((stream->modeflags & __MASK_UNGOT) > 1)) { /* app case */ + return -1; + } + } +#endif /* __STDIO_WIDE */ +#ifdef __STDIO_BUFFERS + if (stream->modeflags & __FLAG_WRITING) { + cor -= (stream->bufwpos - stream->bufstart); /* pending writes */ + } + if (stream->modeflags & __FLAG_READING) { + cor += (stream->bufwpos - stream->bufrpos); /* extra's read */ + } +#endif /* __STDIO_BUFFERS */ + + r = *pos; + return ((*pos -= cor) > r) ? -cor : cor; +} + +#endif +/**********************************************************************/ +#ifdef L__stdio_lseek +/* + * This function is only called by fseek and ftell. + * fseek -- doesn't care about pos val, just success or failure. + * ftell -- needs pos val but offset == 0 and whence == SET_CUR. + */ + +/* Internal function -- not reentrant. */ + +int _stdio_lseek(FILE *stream, __offmax_t *pos, int whence) +{ + __offmax_t res; + +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + if (stream->cookie != &stream->filedes) { + return (((stream->gcs.seek == NULL) + || ((stream->gcs.seek)(stream->cookie, pos, whence) < 0)) + ? -1 : 0); + } +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ +#ifdef __STDIO_LARGE_FILES + res = lseek64(stream->filedes, *pos, whence); +#else + res = lseek(stream->filedes, *pos, whence); +#endif /* __STDIO_LARGE_FILES */ + return (res >= 0) ? ((*pos = res), 0) : -1; +} + +#endif +/**********************************************************************/ +#ifdef L__stdio_fread +/* + * NOTE!!! This routine is meant to be callable by both narrow and wide + * functions. However, if called by a wide function, there must be + * NO pending ungetwc()s!!! + */ + +/* Unlike write, it's ok for read to return fewer than bufsize, since + * we may not need all of them. */ +static ssize_t _stdio_READ(FILE *stream, void *buf, size_t bufsize) +{ + ssize_t rv; + + if (bufsize == 0) { + return 0; + } + + if (bufsize > SSIZE_MAX) { + bufsize = SSIZE_MAX; + } + +#ifdef __BCC__ + TRY_READ: +#endif + rv = __READ(stream, buf, bufsize); + if (rv > 0) { +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + assert(rv <= bufsize); /* buggy user handler... TODO: check? */ + if (rv > bufsize) { /* Num reported written > number requested */ + rv = bufsize; /* Treat as a full read??? */ + } +#endif + } else if (rv == 0) { + stream->modeflags |= __FLAG_EOF; + } else { +#ifdef __BCC__ + if (errno == EINTR) { + goto TRY_READ; + } +#endif + stream->modeflags |= __FLAG_ERROR; + rv = 0; + } + + return rv; +} + +/* Internal function -- not reentrant. */ + +size_t _stdio_fread(unsigned char *buffer, size_t bytes, + register FILE *stream) +{ + __stdio_validate_FILE(stream); /* debugging only */ + +#ifdef __STDIO_BUFFERS + + if (stream->modeflags +#ifdef __STDIO_AUTO_RW_TRANSITION + & (__FLAG_WRITEONLY) +#else /* __STDIO_AUTO_RW_TRANSITION */ + /* ANSI/ISO and SUSv3 require not currently writing. */ + & (__FLAG_WRITEONLY|__FLAG_WRITING) +#endif /* __STDIO_AUTO_RW_TRANSITION */ + ) { +#ifdef __STDIO_PUTC_MACRO + stream->bufputc = stream->bufstart; /* Must disable putc. */ +#endif /* __STDIO_PUTC_MACRO */ + stream->modeflags |= __FLAG_ERROR; + /* TODO: This is for posix behavior if writeonly. To save space, we + * use this errno for read attempt while writing, as no errno is + * specified by posix for this case, even though the restriction is + * mentioned in fopen(). */ + errno = EBADF; + return 0; + } + + /* We need to disable putc and getc macros in case of error */ +#if defined(__STDIO_PUTC_MACRO) || defined(__STDIO_GETC_MACRO) +#ifdef __STDIO_PUTC_MACRO + stream->bufputc = +#endif /* __STDIO_GETC_MACRO */ +#ifdef __STDIO_GETC_MACRO + stream->bufgetc = +#endif /* __STDIO_GETC_MACRO */ + stream->bufstart; +#endif /* defined(__STDIO_PUTC_MACRO) || defined(__STDIO_GETC_MACRO) */ + + if (stream->modeflags & __MASK_BUFMODE) { + /* If the stream is readable and not fully buffered, we must first + * flush all line buffered output streams. Do this before the + * error check as this may be a read/write line-buffered stream. */ + fflush((FILE *) &_stdio_openlist); /* Uses an implementation hack!!! */ + } + +#ifdef __STDIO_AUTO_RW_TRANSITION + if (stream->modeflags & __FLAG_WRITING) { + /* TODO -- return if error? Test glibc behavior with a custom r/w. */ + fflush(stream); + } +#endif /* __STDIO_AUTO_RW_TRANSITION */ + + stream->modeflags |= __FLAG_READING; /* Make sure Reading flag is set. */ + + { + register unsigned char *p = (unsigned char *) buffer; + + /* First, grab appropriate ungetc() chars. NOT FOR WIDE ORIENTATED! */ + while (bytes && (stream->modeflags & __MASK_UNGOT)) { +#ifdef __STDIO_WIDE + assert(stream->modeflags & __FLAG_NARROW); +#endif /* __STDIO_WIDE */ + *p++ = stream->ungot[(--stream->modeflags) & __MASK_UNGOT]; + stream->ungot[1] = 0; + --bytes; + } + + /* Now get any other needed chars from the buffer or the file. */ + FROM_BUF: + while (bytes && (stream->bufrpos < stream->bufwpos)) { + --bytes; + *p++ = *stream->bufrpos++; + } + + if (bytes > 0) { + ssize_t len; + + /* The buffer is exhausted, but we still need chars. */ + stream->bufrpos = stream->bufwpos = stream->bufstart; + + if (bytes <= stream->bufend - stream->bufwpos) { + /* We have sufficient space in the buffer. */ + len = _stdio_READ(stream, stream->bufwpos, + stream->bufend - stream->bufwpos); + if (len > 0) { + stream->bufwpos += len; + goto FROM_BUF; + } + } else { + /* More bytes needed than fit in the buffer, so read */ + /* directly into caller's buffer. */ + len = _stdio_READ(stream, p, bytes); + if (len > 0) { + p += len; + bytes -= len; + goto FROM_BUF; /* Redundant work, but stops extra read. */ + } + } + } + +#ifdef __STDIO_GETC_MACRO + if (!(stream->modeflags & (__FLAG_WIDE|__MASK_UNGOT|__MASK_BUFMODE))) { + stream->bufgetc = stream->bufwpos; /* Enable getc macro. */ + } +#endif + + __stdio_validate_FILE(stream); /* debugging only */ + return (p - (unsigned char *)buffer); + } + +#else /* __STDIO_BUFFERS --------------------------------------- */ + + if (stream->modeflags +#ifdef __STDIO_AUTO_RW_TRANSITION + & (__FLAG_WRITEONLY) +#else /* __STDIO_AUTO_RW_TRANSITION */ + /* ANSI/ISO and SUSv3 require not currently writing. */ + & (__FLAG_WRITEONLY|__FLAG_WRITING) +#endif /* __STDIO_AUTO_RW_TRANSITION */ + ) { + stream->modeflags |= __FLAG_ERROR; + /* TODO: This is for posix behavior if writeonly. To save space, we + * use this errno for read attempt while writing, as no errno is + * specified by posix for this case, even though the restriction is + * mentioned in fopen(). */ + errno = EBADF; + return 0; + } + +#ifdef __STDIO_AUTO_RW_TRANSITION + stream->modeflags &= ~(__FLAG_WRITING); /* Make sure Writing flag clear. */ +#endif /* __STDIO_AUTO_RW_TRANSITION */ + + stream->modeflags |= __FLAG_READING; /* Make sure Reading flag is set. */ + + { + register unsigned char *p = (unsigned char *) buffer; + + /* First, grab appropriate ungetc() chars. NOT FOR WIDE ORIENTATED! */ + while (bytes && (stream->modeflags & __MASK_UNGOT)) { +#ifdef __STDIO_WIDE + assert(stream->modeflags & __FLAG_NARROW); +#endif /* __STDIO_WIDE */ + *p++ = stream->ungot[(--stream->modeflags) & __MASK_UNGOT]; + stream->ungot[1] = 0; + --bytes; + } + + while (bytes > 0) { + ssize_t len = _stdio_READ(stream, p, (unsigned) bytes); + if (len == 0) { + break; + } + p += len; + bytes -= len; + } + + __stdio_validate_FILE(stream); /* debugging only */ + return (p - (unsigned char *)buffer); + } + +#endif /* __STDIO_BUFFERS */ +} + +#endif +/**********************************************************************/ +#ifdef L__stdio_fwrite +/* + * If buffer == NULL, attempt to fflush and return number of chars + * remaining in buffer (0 if successful fflush). + */ + +/* WARNING!!!! Current standards say that termination due to an asyncronous + * signal may not result in stdio streams being flushed. This libary makes + * an effort to do so but there is no way, short of blocking signals for + * each _stdio_fwrite call, that we can maintain the correct state if a + * signal is recieved mid-call. So any stream in mid-_stdio_fwrite could + * not some flush data or even duplicate-flush some data. It is possible + * to avoid the duplicate-flush case by setting/clearing the stream + * error flag before/after the write process, but it doesn't seem worth + * the trouble. */ + +/* Like standard write, but always does a full write unless error plus + *deals correctly with bufsize > SSIZE_MAX... not much on an issue on linux + * but definitly could be on Elks. Also on Elks, always loops for EINTR.. + * Returns number of bytes written, so a short write indicates an error */ +static size_t _stdio_WRITE(FILE *stream, const void *buf, size_t bufsize) +{ + size_t todo; + ssize_t rv, stodo; + + todo = bufsize; + + while (todo) { + stodo = (todo <= SSIZE_MAX) ? todo : SSIZE_MAX; + rv = __WRITE(stream, buf, stodo); + if (rv >= 0) { +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + assert(rv <= stodo); /* buggy user handler... TODO: check? */ + if (rv > stodo) { /* Num reported written > number requested */ + rv = stodo; /* Treat as a full write??? */ + } +#endif + todo -= rv; + buf += rv; + } else +#ifdef __BCC__ + if (errno != EINTR) +#endif + { + stream->modeflags |= __FLAG_ERROR; + break; + } + } + + return bufsize - todo; +} + +/* Internal function -- not reentrant. */ + +size_t _stdio_fwrite(const unsigned char *buffer, size_t bytes, + register FILE *stream) +{ +#ifdef __STDIO_BUFFERS + register const unsigned char *p; + + __stdio_validate_FILE(stream); /* debugging only */ + + if ((stream->modeflags & __FLAG_READONLY) +#ifndef __STDIO_AUTO_RW_TRANSITION + /* ANSI/ISO requires either at EOF or currently not reading. */ + || ((stream->modeflags & (__FLAG_READING|__FLAG_EOF)) + == __FLAG_READING) +#endif /* __STDIO_AUTO_RW_TRANSITION */ + ) { + stream->modeflags |= __FLAG_ERROR; + /* TODO: This is for posix behavior if readonly. To save space, we + * use this errno for write attempt while reading, as no errno is + * specified by posix for this case, even though the restriction is + * mentioned in fopen(). */ + errno = EBADF; + return 0; + } + +#ifdef __STDIO_AUTO_RW_TRANSITION + /* If we were reading, deal with ungots and buffered chars. */ + if ((stream->modeflags & (__FLAG_EOF|__FLAG_WRITING|__FLAG_WRITEONLY)) + == 0) { + /* If appending, we might as well seek to end to save a seek. */ + fseek(stream, 0L, + ((stream->modeflags & __FLAG_APPEND) ? SEEK_END : SEEK_CUR)); + /* TODO: set EOF in fseek when appropriate? */ + } +#endif + + /* We need to disable putc and getc macros in case of error */ +#if defined(__STDIO_AUTO_RW_TRANSITION) \ + || defined(__STDIO_PUTC_MACRO) || defined(__STDIO_GETC_MACRO) +#ifdef __STDIO_AUTO_RW_TRANSITION + stream->bufrpos = /* TODO -- necessary? */ +#endif /* __STDIO_AUTO_RW_TRANSITION */ +#ifdef __STDIO_PUTC_MACRO + stream->bufputc = +#endif /* __STDIO_GETC_MACRO */ +#ifdef __STDIO_GETC_MACRO + stream->bufgetc = +#endif /* __STDIO_GETC_MACRO */ + stream->bufstart; +#endif /* not using ansi restrictions ; or using putc and/or getc macro */ + + /* Clear both reading and writing flags. We need to clear the writing + * flag in case we're fflush()ing or in case of an error. */ + stream->modeflags &= ~(__FLAG_READING|__FLAG_WRITING); + + { + const unsigned char *buf0 = buffer; + + if (!buffer) { /* fflush the stream */ + FFLUSH: + { + size_t count = stream->bufwpos - stream->bufstart; + p = stream->bufstart; + + if (stream->filedes == -2) { /* TODO -- document this hack! */ + stream->modeflags |= __FLAG_WRITING; + return (!buffer) ? 0 : ((buffer - buf0) + bytes); + } + + { + size_t rv = _stdio_WRITE(stream, p, count); + p += rv; + count -= rv; + } + + stream->bufwpos = stream->bufstart; + while (count) { + *stream->bufwpos++ = *p++; + --count; + } + + if (!buffer) { /* fflush case... */ + __stdio_validate_FILE(stream); /* debugging only */ + return stream->bufwpos - stream->bufstart; + } + } + } + +#if 1 + /* TODO: If the stream is buffered, we may be able to omit. */ + if ((stream->bufwpos == stream->bufstart) /* buf empty */ + && (stream->bufend - stream->bufstart <= bytes) /* fills */ + && (stream->filedes != -2)) { /* not strinf fake file */ + /* so want to do a direct write of supplied buffer */ + { + size_t rv = _stdio_WRITE(stream, buffer, bytes); + buffer += rv; + bytes -= rv; + } + } else +#endif + /* otherwise buffer not empty and/or data fits */ + { + size_t count = stream->bufend - stream->bufwpos; + p = buffer; + + if (count > bytes) { + count = bytes; + } + bytes -= count; + + while (count) { + *stream->bufwpos++ = *buffer++; + --count; + } + + if (bytes) { + goto FFLUSH; + } + + if (stream->modeflags & __FLAG_LBF) { + while (p < buffer) { /* check for newline. */ + if (*p++ == '\n') { + goto FFLUSH; + } + } + } + } + +#ifdef __STDIO_PUTC_MACRO + if (!(stream->modeflags & (__FLAG_WIDE|__MASK_BUFMODE))) { + /* Not wide, no errors and fully buffered, so enable putc macro. */ + stream->bufputc = stream->bufend; + } +#endif /* __STDIO_GETC_MACRO */ + stream->modeflags |= __FLAG_WRITING; /* Ensure Writing flag is set. */ + + __stdio_validate_FILE(stream); /* debugging only */ + return buffer - buf0; + + } + +#else /* __STDIO_BUFFERS --------------------------------------- */ + + __stdio_validate_FILE(stream); /* debugging only */ + + if ((stream->modeflags & __FLAG_READONLY) +#ifndef __STDIO_AUTO_RW_TRANSITION + /* ANSI/ISO requires either at EOF or currently not reading. */ + || ((stream->modeflags & (__FLAG_READING|__FLAG_EOF)) + == __FLAG_READING) +#endif /* __STDIO_AUTO_RW_TRANSITION */ + ) { + stream->modeflags |= __FLAG_ERROR; + /* TODO: This is for posix behavior if readonly. To save space, we + * use this errno for write attempt while reading, as no errno is + * specified by posix for this case, even though the restriction is + * mentioned in fopen(). */ + errno = EBADF; + return 0; + } + + /* We always clear the reading flag in case at EOF. */ + stream->modeflags &= ~(__FLAG_READING); + /* Unlike the buffered case, we set the writing flag now since we don't + * need to do anything here for fflush(). */ + stream->modeflags |= __FLAG_WRITING; + + { + register unsigned char *p = (unsigned char *) buffer; + + ssize_t rv = _stdio_WRITE(stream, p, bytes); + + p += rv; + bytes -= rv; + + __stdio_validate_FILE(stream); /* debugging only */ + return (p - (unsigned char *)buffer); + } + +#endif /* __STDIO_BUFFERS *****************************************/ +} + +#endif +/**********************************************************************/ +#ifdef L__stdio_init + +/* Internal functions -- _stdio_init() and __stdio_validate_FILE + * are not reentrant, but _stdio_term() is through fflush(). + * Also, the _cs_{read|write|close} functions are not reentrant. */ + +#ifndef NDEBUG +void __stdio_validate_FILE(FILE *stream) +{ + if (stream->filedes == -2) { /* fake FILE for sprintf, scanf, etc. */ + return; + } + + __STDIO_THREADLOCK(stream); + +#ifdef __STDIO_BUFFERS + assert(stream->bufstart <= stream->bufrpos); + assert(stream->bufrpos <= stream->bufwpos); + assert(stream->bufwpos <= stream->bufend); + assert(stream->bufwpos <= stream->bufend); + if ((stream->modeflags & __MASK_BUFMODE) == __FLAG_NBF) { + assert(stream->bufstart == stream->bufend); + } + assert((stream->modeflags & __MASK_BUFMODE) <= __FLAG_NBF); +#endif +#ifdef __STDIO_PUTC_MACRO + assert(stream->bufstart <= stream->bufputc); + assert(stream->bufputc <= stream->bufend); + if (stream->bufstart < stream->bufputc) { + assert(stream->bufputc == stream->bufend); + assert(stream->modeflags & (__FLAG_WRITING)); + assert(!(stream->modeflags + & (__FLAG_WIDE|__MASK_BUFMODE|__MASK_UNGOT|__FLAG_READONLY)) + ); + } +#endif +#ifdef __STDIO_GETC_MACRO + assert(stream->bufstart <= stream->bufgetc); + assert(stream->bufgetc <= stream->bufwpos); + if (stream->bufstart < stream->bufgetc) { + assert(stream->modeflags & (__FLAG_READING)); + assert(!(stream->modeflags + & (__FLAG_WIDE|__MASK_BUFMODE|__MASK_UNGOT|__FLAG_WRITEONLY)) + ); + } +#endif + assert((stream->modeflags & __MASK_UNGOT) != __MASK_UNGOT); + if (stream->modeflags & __MASK_UNGOT1) { + assert(stream->ungot[1] <= 1); + } + if (stream->modeflags & __MASK_UNGOT) { + assert(!(stream->modeflags & __FLAG_EOF)); + } + assert((stream->modeflags & (__FLAG_READONLY|__FLAG_WRITEONLY)) + != (__FLAG_READONLY|__FLAG_WRITEONLY)); + + /* TODO -- filepos? ungot_width? filedes? nextopen? */ + + __STDIO_THREADUNLOCK(stream); +} +#endif + +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS +ssize_t _cs_read(void *cookie, char *buf, size_t bufsize) +{ + return read(*((int *) cookie), buf, bufsize); +} + +ssize_t _cs_write(void *cookie, const char *buf, size_t bufsize) +{ + return write(*((int *) cookie), (char *) buf, bufsize); +} + +int _cs_close(void *cookie) +{ + return close(*((int *) cookie)); +} +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ + +#ifdef __STDIO_BUFFERS +static unsigned char _fixed_buffers[2 * BUFSIZ]; +#define bufin (_fixed_buffers) +#define bufout (_fixed_buffers + BUFSIZ) +#endif /* __STDIO_BUFFERS */ + +static FILE _stdio_streams[] = { + __STDIO_INIT_FILE_STRUCT(_stdio_streams[0], __FLAG_LBF|__FLAG_READONLY, \ + 0, _stdio_streams + 1, bufin, BUFSIZ ), + __STDIO_INIT_FILE_STRUCT(_stdio_streams[1], __FLAG_LBF|__FLAG_WRITEONLY, \ + 1, _stdio_streams + 2, bufout, BUFSIZ ), + __STDIO_INIT_FILE_STRUCT(_stdio_streams[2], __FLAG_NBF|__FLAG_WRITEONLY, \ + 2, 0, 0, 0 ) +}; + +FILE *_stdin = _stdio_streams + 0; +FILE *_stdout = _stdio_streams + 1; +FILE *_stderr = _stdio_streams + 2; + +#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) + +#ifdef __STDIO_BUFFERS +FILE *_stdio_openlist = _stdio_streams; +#else /* __STDIO_BUFFERS */ +FILE *_stdio_openlist = NULL; +#endif /* __STDIO_BUFFERS */ + +#ifdef __STDIO_THREADSAFE +pthread_mutex_t _stdio_openlist_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + + +void __stdio_init_mutex(pthread_mutex_t *m) +{ + static const pthread_mutex_t __stdio_mutex_initializer + = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + + memcpy(m, &__stdio_mutex_initializer, sizeof(__stdio_mutex_initializer)); +} +#endif /* __STDIO_THREADSAFE */ + +/* TODO - do we need to lock things, or do we just assume we're the only + * remaining thread? */ + +/* Note: We assume here that we are the only remaining thread. */ +void _stdio_term(void) +{ +#if defined(__STDIO_GLIBC_CUSTOM_STREAMS) || defined(__STDIO_THREADSAFE) + FILE *ptr; +#endif + + /* TODO: if called via a signal handler for a signal mid _stdio_fwrite, + * the stream may be in an unstable state... what do we do? + * perhaps set error flag before and clear when done if successful? */ + +#ifdef __STDIO_THREADSAFE + /* First, forceably unlock the open file list and all files. + * Note: Set locking mode to "by caller" to save some overhead later. */ + __stdio_init_mutex(&_stdio_openlist_lock); + for (ptr = _stdio_openlist ; ptr ; ptr = ptr->nextopen ) { + ptr->user_locking = FSETLOCKING_BYCALLER; + __stdio_init_mutex(&ptr->lock); + } +#endif /* __STDIO_THREADSAFE */ + + /* TODO -- set an alarm and flush each file "by hand"? to avoid blocking? */ + + /* Now flush all streams. */ + fflush(NULL); + + /* Next close all custom streams in case of any special cleanup, but + * don't use fclose() because that pulls in free and malloc. Also, + * don't worry about removing them from the list. Just set the cookie + * pointer to NULL so that an error will be generated if someone tries + * to use the stream. */ +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + for (ptr = _stdio_openlist ; ptr ; ptr = ptr->nextopen ) { + if (ptr->cookie != &ptr->filedes) { /* custom stream */ + __CLOSE(ptr); + ptr->cookie = NULL; /* Generate an error if used later. */ +#if 0 +/* #ifdef __STDIO_BUFFERS */ + } else { + /* TODO: "unbuffer" files like glibc does? Inconsistent with + * custom stream handling above, but that's necessary to deal + * with special user-defined close behavior. */ + stream->bufwpos = stream->bufrpos = stream->bufend +#ifdef __STDIO_GETC_MACRO + = stream->bufgetc +#endif +#ifdef __STDIO_PUTC_MACRO + = stream->bufputc +#endif + = stream->bufstart; +#endif /* __STDIO_BUFFERS */ + } + } +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ +} + + +#if defined(__STDIO_BUFFERS) || !defined(__UCLIBC__) +void _stdio_init(void) +{ +#ifdef __STDIO_BUFFERS + /* stdin and stdout uses line buffering when connected to a tty. */ + _stdio_streams[0].modeflags ^= isatty(0) * __FLAG_LBF; + _stdio_streams[1].modeflags ^= isatty(1) * __FLAG_LBF; +#endif /* __STDIO_BUFFERS */ +#ifndef __UCLIBC__ +/* __stdio_term is automatically when exiting if stdio is used. + * See misc/internals/__uClibc_main.c and and stdlib/atexit.c. */ + atexit(_stdio_term); +#endif /* __UCLIBC__ */ +} +#endif /* defined(__STDIO_BUFFERS) || !defined(__UCLIBC__) */ +#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ + +#endif +/**********************************************************************/ +/* ANSI/ISO functions. */ +/**********************************************************************/ +#ifdef L_remove +#include <unistd.h> +#include <errno.h> + +/* No reentrancy issues. */ + +int remove(register const char *filename) +{ + int old_errno = errno; + + /* SUSv3 says equivalent to rmdir() if a directory, and unlink() + * otherwise. Hence, we need to try rmdir() first. */ + + return (((rmdir(filename) == 0) + || ((errno == ENOTDIR) + && ((__set_errno(old_errno), unlink(filename)) == 0))) + ? 0 : -1); +} +#endif +/**********************************************************************/ +/* rename is a syscall +#ifdef L_rename +int rename(const char *old, const char *new); +#endif +*/ +/**********************************************************************/ +/* TODO: tmpfile */ +/* #ifdef L_tmpfile */ +/* FILE *tmpfile(void); */ +/* #endif */ +/**********************************************************************/ +/* TODO: tmpname */ +/* #ifdef L_tmpname */ +/* char *tmpname(char *s); */ +/* #endif */ +/**********************************************************************/ +#ifdef L_fclose + +/* We need to be careful here to avoid deadlock when threading, as we + * need to lock both the file and the open file list. This can clash + * with fflush. Since fflush is much more common, we do the extra + * work here. */ + +int fclose(register FILE *stream) +{ +#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) + + register FILE *ptr; + int rv = 0; + +#ifdef __STDIO_THREADSAFE + /* Need two non-heirchal mutexs... be careful to avoid deadlock*/ + do { + __STDIO_THREADLOCK(stream); + if (__STDIO_THREADTRYLOCK_OPENLIST == 0) { + break; + } + __STDIO_THREADUNLOCK(stream); + usleep(10000); + } while (1); +#endif /* __STDIO_THREADSAFE */ + + __stdio_validate_FILE(stream); /* debugging only */ + +#ifdef __STDIO_BUFFERS + if (stream->modeflags & __FLAG_WRITING) { + rv = fflush(stream); /* Write any pending buffered chars. */ + } /* Also disables putc macro if used. */ + +#ifdef __STDIO_GETC_MACRO + /* Not necessary after fflush, but always do this to reduce size. */ + stream->bufgetc = stream->bufstart; /* Disable getc macro for safety. */ +#endif /* __STDIO_GETC_MACRO */ +#endif /* __STDIO_BUFFERS */ + + /* Remove file from open list before closing file descriptor. */ + ptr = _stdio_openlist; + if (ptr == stream) { + _stdio_openlist = stream->nextopen; + } else { + while (ptr) { + if (ptr->nextopen == stream) { + ptr->nextopen = stream->nextopen; + break; + } + ptr = ptr->nextopen; + } + } + __STDIO_THREADUNLOCK_OPENLIST; /* We're done with the open file list. */ + + if (__CLOSE(stream) < 0) { /* Must close even if fflush failed. */ + rv = EOF; + } +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + stream->cookie = NULL; /* To aid debugging... */ +#endif + stream->filedes = -1; /* To aid debugging... */ + +#ifdef __STDIO_BUFFERS + if (stream->modeflags & __FLAG_FREEBUF) { + free(stream->bufstart); + } +#endif /* __STDIO_BUFFERS */ + + /* TODO -- leave the stream locked to catch any dangling refs? */ + __STDIO_THREADUNLOCK(stream); + + /* At this point, any dangling refs to the stream are the result of + * a programming bug... so free the unlocked stream. */ + if (stream->modeflags & __FLAG_FREEFILE) { + free(stream); + } + + return rv; + +#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ + + int rv = 0; + + __STDIO_THREADLOCK(stream); + + __stdio_validate_FILE(stream); /* debugging only */ + + if (__CLOSE(stream) < 0) { /* Must close even if fflush failed. */ + rv = EOF; + } + +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + stream->cookie = NULL; /* To aid debugging... */ +#endif + stream->filedes = -1; /* To aid debugging... */ + + __STDIO_THREADUNLOCK(stream); + + /* At this point, any dangling refs to the stream are the result of + * a programming bug... so free the unlocked stream. */ + if (stream->modeflags & __FLAG_FREEFILE) { + free(stream); + } + + return rv; + +#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS )*/ +} + +#endif +/**********************************************************************/ +#ifdef L_fflush + +/* + * Special cases: + * stream == NULL means fflush all writing streams (ANSI/ISO). + * stream == (FILE *) &_stdio_openlist -- implementation-specific hack + * meaning fflush all line buffered writing streams + */ + +/* + * NOTE: ANSI/ISO difference!!! According to the standard, fflush is only + * defined for write-only streams, or read/write streams whose last op + * was a write. However, reading is allowed for a read/write stream if + * a file positioning operation was done (fseek, fsetpos) even though there + * is no guarantee of flushing the write data in that case. Hence, for + * this case we keep a flag to indicate whether or not the buffer needs to + * be flushed even if the last operation was a read. This falls under the + * implementation-defined behavior. Otherwise, we would need to flush + * every time we did fseek, etc. even if we were still in the buffer's range. + */ + +/* Since the stream pointer arg is allowed to be NULL, or the address of the + * stdio open file list if stdio is buffered in this implementation, we can't + * use the UNLOCKED() macro here. */ + +#ifndef __STDIO_THREADSAFE +strong_alias(fflush_unlocked,fflush) +#else /* __STDIO_THREADSAFE */ +int fflush(register FILE *stream) +{ + int retval; + + if ((stream != NULL) +#ifdef __STDIO_BUFFERS + && (stream != (FILE *) &_stdio_openlist) +#endif /* __STDIO_BUFFERS */ + ) { + __STDIO_THREADLOCK(stream); + retval = fflush_unlocked(stream); + __STDIO_THREADUNLOCK(stream); + } else { + retval = fflush_unlocked(stream); + } + + return retval; +} +#endif /* __STDIO_THREADSAFE */ + +int fflush_unlocked(register FILE *stream) +{ +#ifdef __STDIO_BUFFERS + + int rv = 0; + unsigned short mask = (__FLAG_NBF|__FLAG_LBF); + +#ifndef NDEBUG + if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { + __stdio_validate_FILE(stream); /* debugging only */ + } +#endif + + if (stream == (FILE *) &_stdio_openlist) { /* fflush all line-buffered */ + stream = NULL; + mask = __FLAG_LBF; + } + + if (stream == NULL) { /* flush all (line) buffered writing streams */ + /* Note -- We have to lock the list even in the unlocked function. */ + __STDIO_THREADLOCK_OPENLIST; + /* TODO -- Can we work around locking the list to avoid keeping it + * locked if the write blocks? */ + for (stream = _stdio_openlist; stream; stream = stream->nextopen) { + if (((stream->modeflags ^ __FLAG_NBF) & mask) + && (stream->modeflags & __FLAG_WRITING) + && fflush(stream)) { + rv = EOF; + } + } + __STDIO_THREADUNLOCK_OPENLIST; + } else if (stream->modeflags & __FLAG_WRITING) { + if (_stdio_fwrite(NULL, 0, stream) > 0) { /* flush buffer contents. */ + rv = -1; /* Not all chars written. */ + } + } else if (stream->modeflags & (__FLAG_READONLY|__FLAG_READING)) { + /* TODO - __FLAG_READING too? check glibc behavior */ + /* According to info, glibc returns an error when the file is opened + * in read-only mode. + * ANSI/ISO says behavior in this case is undefined but also says you + * shouldn't flush a stream you were reading from. + */ + stream->modeflags |= __FLAG_ERROR; /* TODO - check glibc behavior */ + __set_errno(EBADF); + rv = -1; + } + +#ifndef NDEBUG + if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { + __stdio_validate_FILE(stream); /* debugging only */ + } +#endif + return rv; + +#else /* __STDIO_BUFFERS --------------------------------------- */ + + __stdio_validate_FILE(stream); /* debugging only */ + /* TODO -- check glibc behavior regarding error indicator */ + return (stream->modeflags & (__FLAG_READONLY|__FLAG_READING) + ? ((stream->modeflags |= __FLAG_ERROR), __set_errno(EBADF), EOF) + : 0 ); + +#endif /* __STDIO_BUFFERS */ +} +#endif +/**********************************************************************/ +#ifdef L_fopen + +/* No reentrancy issues. */ + +FILE *fopen(const char * __restrict filename, const char * __restrict mode) +{ + return _stdio_fopen(filename, mode, NULL, -1); +} + +#endif +/**********************************************************************/ +#ifdef L__stdio_fopen + +/* + * Cases: + * fopen : filename != NULL, stream == NULL, filedes == -1 + * freopen : filename != NULL, stream != NULL, filedes == -1 + * fdopen : filename == NULL, stream == NULL, filedes valid + * fsfopen : filename != NULL, stream != NULL, filedes == -1 + * fopen64 : filename != NULL, stream == NULL, filedes == -2 + */ + +#if O_ACCMODE != 3 || O_RDONLY != 0 || O_WRONLY != 1 || O_RDWR != 2 || O_APPEND != __FLAG_APPEND || O_LARGEFILE != __FLAG_LARGEFILE +#error Assumption violated - mode constants +#endif + +/* Internal function -- reentrant (locks open file list) */ + +FILE *_stdio_fopen(const char * __restrict filename, + register const char * __restrict mode, + register FILE * __restrict stream, int filedes) +{ + __mode_t open_mode; + + /* parse mode */ + open_mode = O_RDONLY; + if (*mode != 'r') { /* not read */ + open_mode = (O_WRONLY | O_CREAT | O_TRUNC); + if (*mode != 'w') { /* not write (create or truncate)*/ + open_mode = (O_WRONLY | O_CREAT | O_APPEND); + if (*mode != 'a') { /* not write (create or append) */ + __set_errno(EINVAL); /* then illegal mode */ + if (stream) { /* If this is freopen, free the stream. */ + goto FREE_STREAM; + } + return NULL; + } + } + } + + if ((*++mode == 'b')) { /* binary mode (NOP currently) */ + ++mode; + } + + if (*mode == '+') { /* read-write */ + ++mode; + open_mode &= ~(O_RDONLY | O_WRONLY); + open_mode |= O_RDWR; + } + +#if defined(__STDIO_GNU_FEATURE) || defined(__STDIO_FOPEN_LARGEFILE_MODE) + while (*mode) { /* ignore everything else except ... */ +#ifdef __STDIO_FOPEN_EXCLUSIVE_MODE + if (*mode++ == 'x') { /* open exclusive -- glibc extension */ + open_mode |= O_EXCL; + } +#endif /* __STDIO_FOPEN_EXCLUSIVE_MODE */ +#ifdef __STDIO_FOPEN_LARGEFILE_MODE + if (*mode++ == 'F') { /* open large file */ + open_mode |= O_LARGEFILE; + } +#endif /* __STDIO_FOPEN_LARGEFILE_MODE */ + } +#endif /* __STDIO_FOPEN_EXCLUSIVE_MODE or __STDIO_FOPEN_LARGEFILE_MODE def'd */ + +#ifdef __BCC__ + mode = filename; /* TODO: help BCC with register allocation. */ +#define filename mode +#endif /* __BCC__ */ + + if (!stream) { /* Need to allocate a FILE. */ +#ifdef __STDIO_BUFFERS + if ((stream = malloc(sizeof(FILE))) == NULL) { + return stream; + } + stream->modeflags = __FLAG_FREEFILE; + if ((stream->bufstart = malloc(BUFSIZ)) != 0) { + stream->bufend = stream->bufstart + BUFSIZ; + stream->modeflags |= __FLAG_FREEBUF; + } else { +#if __STDIO_BUILTIN_BUF_SIZE > 0 + stream->bufstart = stream->unbuf; + stream->bufend = stream->unbuf + sizeof(stream->unbuf); +#else /* __STDIO_BUILTIN_BUF_SIZE > 0 */ + stream->bufstart = stream->bufend = NULL; +#endif /* __STDIO_BUILTIN_BUF_SIZE > 0 */ + } +#else /* __STDIO_BUFFERS */ + if ((stream = malloc(sizeof(FILE))) == NULL) { + return stream; + } + stream->modeflags = __FLAG_FREEFILE; +#endif /* __STDIO_BUFFERS */ + } + + if (filedes >= 0) { /* Handle fdopen trickery. */ + /* + * NOTE: it is insufficient to just check R/W/RW agreement. + * We must also check for append mode agreement, as well as + * largefile agreement if applicable. + */ + int i = (open_mode & (O_ACCMODE|O_APPEND|O_LARGEFILE)) + 1; + + if ((i & (((int) filename) + 1)) != i) { + __set_errno(EINVAL); + filedes = -1; + } + stream->filedes = filedes; + } else { +#ifdef __STDIO_LARGE_FILES + if (filedes < -1) { + open_mode |= __FLAG_LARGEFILE; + } +#endif /* __STDIO_LARGE_FILES */ + stream->filedes = open(filename, open_mode, 0666); + } + + if (stream->filedes < 0) { + FREE_STREAM: +#ifdef __STDIO_BUFFERS + if (stream->modeflags & __FLAG_FREEBUF) { + free(stream->bufstart); + } +#endif /* __STDIO_BUFFERS */ + if (stream->modeflags & __FLAG_FREEFILE) { + free(stream); + } + return NULL; + } + +#ifdef __STDIO_BUFFERS + stream->modeflags |= (isatty(stream->filedes) * __FLAG_LBF) + | ((((open_mode & O_ACCMODE) + 1) ^ 0x03) * __FLAG_WRITEONLY) + | (open_mode & (O_APPEND|O_LARGEFILE)); + + +#ifdef __STDIO_GETC_MACRO + stream->bufgetc = +#endif +#ifdef __STDIO_PUTC_MACRO + stream->bufputc = +#endif + stream->bufwpos = stream->bufrpos = stream->bufstart; + +#else /* __STDIO_BUFFERS */ + stream->modeflags |= + ((((open_mode & O_ACCMODE) + 1) ^ 0x03) * __FLAG_WRITEONLY) + | (open_mode & (O_APPEND|O_LARGEFILE)); +#endif /* __STDIO_BUFFERS */ + +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS + stream->cookie = &(stream->filedes); + stream->gcs.read = _cs_read; + stream->gcs.write = _cs_write; + stream->gcs.seek = 0; /* The internal seek func handles normals. */ + stream->gcs.close = _cs_close; +#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ + +#ifdef __STDIO_THREADSAFE + __stdio_init_mutex(&stream->lock); +#endif /* __STDIO_THREADSAFE */ + +#if defined(__STDIO_BUFFERS) \ +|| (defined(__STDIO_THREADSAFE) && defined(__STDIO_GLIBC_CUSTOM_STREAMS)) + __STDIO_THREADLOCK_OPENLIST; + stream->nextopen = _stdio_openlist; /* New files are inserted at */ + _stdio_openlist = stream; /* the head of the list. */ + __STDIO_THREADUNLOCK_OPENLIST; +#endif + + __stdio_validate_FILE(stream); /* debugging only */ + return stream; +#ifdef __BCC__ +#undef filename +#endif /* __BCC__ */ +} + +#endif +/**********************************************************************/ +#ifdef L_freopen + +/* Reentrant. */ + +FILE *freopen(const char * __restrict filename, const char * __restrict mode, + register FILE * __restrict stream) +{ + /* + * ANSI/ISO allow (implementation-defined) change of mode for an + * existing file if filename is NULL. It doesn't look like Linux + * supports this, so we don't here. + * + * NOTE: Whether or not the stream is free'd on failure is unclear + * w.r.t. ANSI/ISO. This implementation chooses to free the + * stream and associated buffer if they were dynamically + * allocated. + * TODO: Check the above. + * TODO: Apparently linux allows setting append mode. Implement? + */ + unsigned short dynmode; + FILE *fp; + + __STDIO_THREADLOCK(stream); + + /* First, flush and close, but don't deallocate, the stream. */ + /* This also removes the stream for the open file list. */ + dynmode = +#ifdef __STDIO_BUFFERS + // __MASK_BUFMODE | /* TODO: check */ +#endif /* __STDIO_BUFFERS */ + (stream->modeflags & (__FLAG_FREEBUF|__FLAG_FREEFILE)); + + stream->modeflags &= ~(__FLAG_FREEBUF|__FLAG_FREEFILE); + fclose(stream); /* Failures are ignored. */ + stream->modeflags = dynmode; + + fp = _stdio_fopen(filename, mode, stream, -1); + + __STDIO_THREADUNLOCK(stream); + + return fp; +} +#endif +/**********************************************************************/ +#ifdef L_freopen64 + +/* Reentrant. */ + +/* TODO -- is it collecting the common work (40 bytes) into a function? */ +FILE *freopen64(const char * __restrict filename, const char * __restrict mode, + register FILE * __restrict stream) +{ + unsigned short dynmode; + FILE *fp; + + __STDIO_THREADLOCK(stream); + + /* First, flush and close, but don't deallocate, the stream. */ + /* This also removes the stream for the open file list. */ + dynmode = +#ifdef __STDIO_BUFFERS + // __MASK_BUFMODE | /* TODO: check */ +#endif /* __STDIO_BUFFERS */ + (stream->modeflags & (__FLAG_FREEBUF|__FLAG_FREEFILE)); + + stream->modeflags &= ~(__FLAG_FREEBUF|__FLAG_FREEFILE); + fclose(stream); /* Failures are ignored. */ + stream->modeflags = dynmode; + + fp = _stdio_fopen(filename, mode, stream, -2); /* TODO -- magic const */ + + __STDIO_THREADUNLOCK(stream); + + return fp; +} + +#endif +/**********************************************************************/ +#ifdef L_setbuf + +/* Reentrant through setvbuf(). */ + +void setbuf(FILE * __restrict stream, register char * __restrict buf) +{ +#ifdef __STDIO_BUFFERS + int mode; + + mode = (buf != NULL) ? _IOFBF : _IONBF; + setvbuf(stream, buf, mode, BUFSIZ); +#else /* __STDIO_BUFFERS */ + /* TODO -- assert on stream? */ + /* Nothing to do. */ +#endif /* __STDIO_BUFFERS */ +} + +#endif +/**********************************************************************/ +#ifdef L_setvbuf + +/* Reentrant. */ + +int setvbuf(register FILE * __restrict stream, register char * __restrict buf, + int mode, size_t size) +{ +#ifdef __STDIO_BUFFERS + + int allocated_buf_flag; + int rv = EOF; + + __STDIO_THREADLOCK(stream); + + __stdio_validate_FILE(stream); /* debugging only */ + + if (((unsigned int) mode) > 2) { /* Illegal mode. */ + /* TODO -- set an errno? */ + goto DONE; + } + +#ifdef __STDIO_FLEXIBLE_SETVBUF + /* C89 standard requires no ops before setvbuf, but we can be flexible. */ + /* NOTE: This will trash any chars ungetc'd!!! */ + /* TODO: hmm could preserve unget count since ungot slots aren't changed (true?) + * but this will fail when buffered chars read from a pipe unless the user buf + * is big enough to copy everything over. */ + if (fseek(stream, 0L, SEEK_CUR)) { + goto DONE; + } +#else /* __STDIO_FLEXIBLE_SETVBUF */ + /* + * Note: ANSI/ISO requires setvbuf to be called after opening the file + * but before any other operation other than a failed setvbuf call. + * We'll cheat here and only test if the wide or narrow mode flag has + * been set; i.e. no read or write (or unget or fwide) operations have + * taken place. + */ +#ifdef __STDIO_WIDE + if (stream->modeflags & (__FLAG_WIDE|__FLAG_NARROW)) { + goto DONE; + } +#else /* __STDIO_WIDE */ + /* Note: This only checks if not currently reading or writing. */ + if (stream->modeflags & (__FLAG_READING|__FLAG_WRITING)) { + goto DONE; + } +#endif /* __STDIO_WIDE */ +#endif /* __STDIO_FLEXIBLE_SETVBUF */ + + if (mode == _IONBF) { + size = 0; + buf = NULL; + } + + stream->modeflags &= ~(__MASK_BUFMODE); /* Clear current mode */ + stream->modeflags |= mode * __FLAG_LBF; /* and set new one. */ + + allocated_buf_flag = 0; + if ((!buf) && (size != (stream->bufend - stream->bufstart))) { + /* No buffer supplied and requested size different from current. */ + allocated_buf_flag = __FLAG_FREEBUF; + /* If size == 0, create a (hopefully) bogus non-null pointer... */ + if (!(buf = ((size > 0) ? malloc(size) : ((char *)NULL) + 1))) { + goto DONE; /* Keep current buffer. */ + } + } + + /* TODO: setvbuf "signal" safety */ + if (buf && (buf != (char *) stream->bufstart)) { /* Want new buffer. */ + if (stream->modeflags & __FLAG_FREEBUF) { + stream->modeflags &= ~(__FLAG_FREEBUF); + free(stream->bufstart); + } + stream->modeflags |= allocated_buf_flag; /* Free-able buffer? */ +#ifdef __STDIO_GETC_MACRO + stream->bufgetc = +#endif +#ifdef __STDIO_PUTC_MACRO + stream->bufputc = +#endif + stream->bufwpos = stream->bufrpos = stream->bufstart = buf; + stream->bufend = buf + size; + } + + __stdio_validate_FILE(stream); /* debugging only */ + + rv = 0; + + DONE: + __STDIO_THREADUNLOCK(stream); + + return rv; + +#else /* __STDIO_BUFFERS */ + __stdio_validate_FILE(stream); /* debugging only */ + /* TODO -- set errno for illegal mode? */ + + return EOF; +#endif /* __STDIO_BUFFERS */ +} + +#endif +/********************************************************************** +int fprintf(FILE * __restrict stream, const char * __restrict format, ...); +int fscanf(FILE * __restrict stream, const char * __restrict format, ...); +int printf(const char * __restrict format, ...); +int scanf(const char * __restrict format, ...); +int snprintf(char * __restrict s, size_t n, + const char * __restrict format, ...); +int sprintf(char * __restrict s, const char * __restrict format, ...); +int sscanf(char * __restrict s, const char * __restrict format, ...); +int vfprintf(FILE * __restrict stream, const char * __restrict format, + va_list arg); +int vfscanf(FILE * __restrict stream, const char * __restrict format, + va_list arg); +int vprintf(const char * __restrict format, va_list arg); +int vscanf(const char * __restrict format, va_list arg); +int vsnprintf(char * __restrict s, size_t n, + const char * __restrict format, va_list arg); +int vsprintf(char * __restrict s, const char * __restrict format, + va_list arg); +int vsscanf(char * __restrict s, const char * __restrict format, + va_list arg); +**********************************************************************/ +#ifdef L_fgetc + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(int,fgetc,(FILE *stream),(stream)) +{ + unsigned char buf[1]; + +#ifdef __STDIO_WIDE + + return (fread(buf, (size_t) 1, (size_t) 1, stream) > 0) ? *buf : EOF; + +#else /* __STDIO_WIDE */ + + return (_stdio_fread(buf, (size_t) 1, stream) > 0) ? *buf : EOF; + +#endif /* __STDIO_WIDE */ +} + +#endif +/**********************************************************************/ +#ifdef L_fgets + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(char *,fgets, + (char *__restrict s, int n, register FILE * __restrict stream), + (s, n, stream)) +{ + register char *p; + int c; + + p = s; + while ((n > 1) && ((c = getc(stream)) != EOF) && ((*p++ = c) != '\n')) { + --n; + } + if (p == s) { + /* TODO -- should we set errno? */ +/* if (n <= 0) { */ +/* errno = EINVAL; */ +/* } */ + return NULL; + } + *p = 0; + return s; +} + +#endif +/**********************************************************************/ +#ifdef L_fputc + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(int,fputc,(int c, FILE *stream),(c,stream)) +{ + unsigned char buf[1]; + + *buf = (unsigned char) c; + +#ifdef __STDIO_WIDE + + return (fwrite(buf, (size_t) 1, (size_t) 1, stream) > 0) ? (*buf) : EOF; + +#else /* __STDIO_WIDE */ + + return (_stdio_fwrite(buf, (size_t) 1, stream) > 0) ? (*buf) : EOF; + +#endif /* __STDIO_WIDE */ +} +#endif +/**********************************************************************/ +#ifdef L_fputs + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(int,fputs, + (register const char * __restrict s, FILE * __restrict stream), + (s, stream)) +{ + size_t n = strlen(s); + +#ifdef __STDIO_WIDE + + return (fwrite(s, n, (size_t) 1, stream) > 0) ? n : EOF; + +#else /* __STDIO_WIDE */ + + return (_stdio_fwrite(s, n, stream) == n) ? n : EOF; + +#endif /* __STDIO_WIDE */ +} + +#endif +/**********************************************************************/ +#ifdef L_getc +#undef getc + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(int,getc,(register FILE *stream),(stream)) +{ + return __GETC(stream); /* Invoke the macro. */ +} + +#endif +/**********************************************************************/ +#ifdef L_getchar +#undef getchar /* Just in case. */ + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED_STREAM(int,getchar,(void),(),_stdin) +{ + register FILE *stream = _stdin; /* This helps bcc optimize. */ + + return __GETC(stream); +} + +#endif +/**********************************************************************/ +#ifdef L_gets + +link_warning(gets, "the 'gets' function is dangerous and should not be used.") + +/* Reentrant. */ + +char *gets(char *s) /* WARNING!!! UNSAFE FUNCTION!!! */ +{ + register FILE *stream = _stdin; /* This helps bcc optimize. */ + register char *p = s; + int c; + + __STDIO_THREADLOCK(stream); + + /* Note: don't worry about performance here... this shouldn't be used! + * Therefore, force actual function call. */ + while (((c = (*getc)(stream)) != EOF) && ((*p = c) != '\n')) { + ++p; + } + if ((c == EOF) || (s == p)) { + s = NULL; + } else { + *p = 0; + } + + __STDIO_THREADUNLOCK(stream); + + return s; +} + +#endif +/**********************************************************************/ +#ifdef L_putc +#undef putc + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(int,putc,(int c, register FILE *stream),(c,stream)) +{ + return __PUTC(c, stream); /* Invoke the macro. */ +} + +#endif +/**********************************************************************/ +#ifdef L_putchar +#undef putchar /* Just in case. */ + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED_STREAM(int,putchar,(int c),(c),_stdout) +{ + register FILE *stream = _stdout; /* This helps bcc optimize. */ + + return __PUTC(c, stream); +} + +#endif +/**********************************************************************/ +#ifdef L_puts + +/* Reentrant. */ + +int puts(register const char *s) +{ + register FILE *stream = _stdout; /* This helps bcc optimize. */ + int n; + + __STDIO_THREADLOCK(stream); + + n = fputs(s,stream) + 1; + if ( +#if 1 + fputc('\n',stream) +#else + fputs("\n",stream) +#endif + == EOF) { + n = EOF; + } + + __STDIO_THREADUNLOCK(stream); + + return n; +} + +#endif +/**********************************************************************/ +#ifdef L_ungetc +/* + * Note: This is the application-callable ungetc. If scanf calls this, it + * should also set stream->ungot[1] to 0 if this is the only ungot. + */ + +/* Reentrant. */ + +int ungetc(int c, register FILE *stream) +{ + __STDIO_THREADLOCK(stream); + + __stdio_validate_FILE(stream); /* debugging only */ + +#ifdef __STDIO_WIDE + if (stream->modeflags & __FLAG_WIDE) { + stream->modeflags |= __FLAG_ERROR; + c = EOF; + goto DONE; + } + stream->modeflags |= __FLAG_NARROW; +#endif /* __STDIO_WIDE */ + + /* If can't read or there's been an error, or c == EOF, or ungot slots + * already filled, then return EOF */ + if ((stream->modeflags + & (__MASK_UNGOT2|__FLAG_WRITEONLY +#ifndef __STDIO_AUTO_RW_TRANSITION + |__FLAG_WRITING /* Note: technically no, but yes in spirit */ +#endif /* __STDIO_AUTO_RW_TRANSITION */ + )) + || ((stream->modeflags & __MASK_UNGOT1) && (stream->ungot[1])) + || (c == EOF) ) { + c = EOF; + goto DONE;; + } + +#ifdef __STDIO_BUFFERS + /* TODO: shouldn't allow writing??? */ + if (stream->modeflags & __FLAG_WRITING) { + fflush(stream); /* Commit any write-buffered chars. */ + } +#endif /* __STDIO_BUFFERS */ + + /* Clear EOF and WRITING flags, and set READING FLAG */ + stream->modeflags &= ~(__FLAG_EOF|__FLAG_WRITING); + stream->modeflags |= __FLAG_READING; + stream->ungot[1] = 1; /* Flag as app ungetc call; scanf fixes up. */ + stream->ungot[(stream->modeflags++) & __MASK_UNGOT] = c; + +#ifdef __STDIO_GETC_MACRO + stream->bufgetc = stream->bufstart; /* Must disable getc macro. */ +#endif + + __stdio_validate_FILE(stream); /* debugging only */ + + DONE: + __STDIO_THREADUNLOCK(stream); + + return c; +} + +#endif +/**********************************************************************/ +#ifdef L_fread +/* NOTE: Surely ptr cannot point to a buffer of size > SIZE_MAX. + * Therefore, we treat the case size * nmemb > SIZE_MAX as an error, + * and include an assert() for it. */ + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(size_t,fread, + (void * __restrict ptr, size_t size, size_t nmemb, + FILE * __restrict stream), + (ptr,size,nmemb,stream)) +{ +#ifdef __STDIO_WIDE + if (stream->modeflags & __FLAG_WIDE) { + stream->modeflags |= __FLAG_ERROR; + /* TODO -- errno? it this correct? */ + return 0; + } + stream->modeflags |= __FLAG_NARROW; +#endif /* __STDIO_WIDE */ + + return (size == 0) + ? 0 + : ( assert( ((size_t)(-1)) / size >= nmemb ), + _stdio_fread(ptr, nmemb * size, stream) / size ); +} + +#endif +/**********************************************************************/ +#ifdef L_fwrite +/* NOTE: Surely ptr cannot point to a buffer of size > SIZE_MAX. + * Therefore, we treat the case size * nmemb > SIZE_MAX as an error, + * and include an assert() for it. */ + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(size_t,fwrite, + (const void * __restrict ptr, size_t size, size_t nmemb, + FILE * __restrict stream), + (ptr,size,nmemb,stream)) +{ +#ifdef __STDIO_WIDE + if (stream->modeflags & __FLAG_WIDE) { + stream->modeflags |= __FLAG_ERROR; + /* TODO -- errno? it this correct? */ + return 0; + } + stream->modeflags |= __FLAG_NARROW; +#endif /* __STDIO_WIDE */ + + return (size == 0) + ? 0 + : ( assert( ((size_t)(-1)) / size >= nmemb ), + _stdio_fwrite(ptr, nmemb * size, stream) / size ); +} + +#endif +/**********************************************************************/ +#if defined(L_fgetpos) || defined(L_fgetpos64) + +/* Reentrant -- fgetpos() and fgetpos64(). */ + +#if defined(L_fgetpos) && defined(L_fgetpos64) +#error L_fgetpos and L_fgetpos64 are defined simultaneously! +#endif + +#ifndef L_fgetpos64 +#define fgetpos64 fgetpos +#define fpos64_t fpos_t +#define ftello64 ftell +#endif + + +int fgetpos64(FILE * __restrict stream, register fpos64_t * __restrict pos) +{ + int retval; + + __STDIO_THREADLOCK(stream); + + retval = ((pos != NULL) && ((pos->__pos = ftello64(stream)) >= 0)) + ? ( +#ifdef __STDIO_MBSTATE_DATA + __COPY_MBSTATE(&(pos->__mbstate), &(stream->mbstate)), +#endif /* __STDIO_MBSTATE_DATA */ + 0) + : (__set_errno(EINVAL), -1); + + __STDIO_THREADUNLOCK(stream); + + return retval; +} + +#ifndef L_fgetpos64 +#undef fgetpos64 +#undef fpos64_t +#undef ftello64 +#endif + +#endif +/**********************************************************************/ +#ifdef L_fseek +strong_alias(fseek, fseeko); +#endif + +#if defined(L_fseek) && defined(__STDIO_LARGE_FILES) + +int fseek(register FILE *stream, long int offset, int whence) +{ + return fseeko64(stream, offset, whence); +} + +#endif + +#if defined(L_fseeko64) || (defined(L_fseek) && !defined(__STDIO_LARGE_FILES)) + +#ifndef L_fseeko64 +#define fseeko64 fseek +#define __off64_t long int +#endif + +/* Reentrant -- fseek(), fseeko(), fseeko64() */ + +int fseeko64(register FILE *stream, __off64_t offset, int whence) +{ +#if SEEK_SET != 0 || SEEK_CUR != 1 || SEEK_END != 2 +#error Assumption violated -- values of SEEK_SET, SEEK_CUR, SEEK_END +#endif + __offmax_t pos[1]; + int retval; + + if ( ((unsigned int) whence) > 2 ) { + __set_errno(EINVAL); + return -1; + } + + __STDIO_THREADLOCK(stream); + + __stdio_validate_FILE(stream); /* debugging only */ + + retval = -1; + *pos = offset; + if ( +#ifdef __STDIO_BUFFERS + /* First commit any pending buffered writes. */ + ((stream->modeflags & __FLAG_WRITING) && fflush(stream)) || +#endif /* __STDIO_BUFFERS */ + ((whence == SEEK_CUR) && (_stdio_adjpos(stream, pos) < 0)) + || (_stdio_lseek(stream, pos, whence) < 0) + ) { + __stdio_validate_FILE(stream); /* debugging only */ + goto DONE; + } + +#ifdef __STDIO_BUFFERS + /* only needed if reading but do it anyway to avoid test */ +#ifdef __STDIO_GETC_MACRO + stream->bufgetc = /* Must disable getc. */ +#endif + stream->bufwpos = stream->bufrpos = stream->bufstart; +#endif /* __STDIO_BUFFERS */ + + stream->modeflags &= + ~(__FLAG_READING|__FLAG_WRITING|__FLAG_EOF|__MASK_UNGOT); + +#ifdef __STDIO_MBSTATE_DATA + /* TODO: don't clear state if don't move? */ + __INIT_MBSTATE(&(stream->mbstate)); +#endif /* __STDIO_MBSTATE_DATA */ + __stdio_validate_FILE(stream); /* debugging only */ + + retval = 0; + + DONE: + __STDIO_THREADUNLOCK(stream); + + return retval; +} + +#ifndef L_fseeko64 +#undef fseeko64 +#undef __off64_t +#endif + +#endif +/**********************************************************************/ +#if defined(L_fsetpos) || defined(L_fsetpos64) + +#if defined(L_fsetpos) && defined(L_fsetpos64) +#error L_fsetpos and L_fsetpos64 are defined simultaneously! +#endif + +#ifndef L_fsetpos64 +#define fsetpos64 fsetpos +#define fpos64_t fpos_t +#define fseeko64 fseek +#endif + +/* Reentrant -- fgetpos{64}() through fseek{64}(). */ + +int fsetpos64(FILE *stream, register const fpos64_t *pos) +{ + if (!pos) { + __set_errno(EINVAL); + return EOF; + } +#ifdef __STDIO_MBSTATE_DATA +#error unimplemented and non-reentrant besides! + { + int retval; + if ((retval = fseeko64(stream, pos->__pos, SEEK_SET)) == 0) { + __COPY_MBSTATE(&(stream->mbstate), &(pos->__mbstate)); + } + return retval; + } +#else /* __STDIO_MBSTATE_DATA */ + return fseeko64(stream, pos->__pos, SEEK_SET); +#endif /* __STDIO_MBSTATE_DATA */ +} + +#ifndef L_fsetpos64 +#undef fsetpos64 +#undef fpos64_t +#undef fseeko64 +#endif + +#endif +/**********************************************************************/ +#ifdef L_ftell +strong_alias(ftell, ftello); +#endif + +#if defined(L_ftell) && defined(__STDIO_LARGE_FILES) +long int ftell(register FILE *stream) +{ + __offmax_t pos = ftello64(stream); + + return (pos == ((long int) pos)) ? pos : (__set_errno(EOVERFLOW), -1); +} +#endif + +#if defined(L_ftello64) || (defined(L_ftell) && !defined(__STDIO_LARGE_FILES)) + +#ifndef L_ftello64 +#define ftello64 ftell +#define __off64_t long int +#endif + +/* Reentrant -- ftell, ftello, ftello64. */ + +__off64_t ftello64(register FILE *stream) +{ + __offmax_t pos[1]; + __off64_t retval; + + __STDIO_THREADLOCK(stream); + + retval = (((*pos = 0), (_stdio_lseek(stream, pos, SEEK_CUR) < 0)) + || (_stdio_adjpos(stream, pos) < 0)) ? -1 : *pos; + + __STDIO_THREADUNLOCK(stream); + + return retval; +} + +#ifndef L_ftello64 +#undef ftello64 +#undef __off64_t +#endif + +#endif +/**********************************************************************/ +#ifdef L_rewind + +void rewind(register FILE *stream) +{ + __STDIO_THREADLOCK(stream); + + __CLEARERR(stream); /* Clear errors first and then seek */ + fseek(stream, 0L, SEEK_SET); /* in case there is an error seeking. */ + + __STDIO_THREADUNLOCK(stream); +} + +#endif +/**********************************************************************/ +#ifdef L_clearerr +#undef clearerr + +/* Reentrancy handled by UNLOCKED_VOID_RETURN() macro. */ + +UNLOCKED_VOID_RETURN(clearerr,(FILE *stream),(stream)) +{ + __CLEARERR(stream); +} + +#endif +/**********************************************************************/ +#ifdef L_feof +#undef feof + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(int,feof,(FILE *stream),(stream)) +{ + return __FEOF(stream); +} + +#endif +/**********************************************************************/ +#ifdef L_ferror +#undef ferror + +/* Reentrancy handled by UNLOCKED() macro. */ + +UNLOCKED(int,ferror,(FILE *stream),(stream)) +{ + return __FERROR(stream); +} + +#endif +/**********************************************************************/ +#ifdef L_perror + +/* TODO -- not allowed to interfere with static storage of strerror(). */ + +void perror(register const char *s) +{ + /* If the program is calling perror, it's a safe bet that printf and + * friends are used as well. It is also possible that the calling + * program could buffer stderr, or reassign it. */ + + register const char *sep; + + sep = ": "; + if (!(s && *s)) { /* Caller did not supply a prefix message */ + s = (sep += 2); /* or passed an empty string. */ + } + +#if 1 +#ifdef __STDIO_PRINTF_M_SPEC + fprintf(_stderr, "%s%s%m\n", s, sep); /* Use the gnu %m feature. */ +#else + /* TODO: use strerror_r instead? */ + fprintf(_stderr, "%s%s%s\n", s, sep, strerror(errno)); +#endif +#else + /* Note: Assumes stderr not closed or buffered. */ + __STDIO_THREADLOCK(stderr); + _stdio_fdout(STDERR_FILENO, s, sep, strerror(errno)); + __STDIO_THREADUNLOCK(stderr); +#endif +} + +#endif +/**********************************************************************/ +/* UTILITY funcs */ +/**********************************************************************/ +#ifdef L__stdio_fdout + +/* Not reentrant -- TODO: lock associated stream if a know file descriptor? */ + +void _stdio_fdout(int fd, ...) +{ + va_list arg; + const char *p; + + va_start(arg, fd); + while ((p = va_arg(arg, const char *)) != NULL) { + write(fd, p, strlen(p)); + } + va_end(arg); +} + +#endif +/**********************************************************************/ +#ifdef L__uintmaxtostr + +/* Avoid using long long / and % operations to cut down dependencies on + * libgcc.a. Definitely helps on i386 at least. */ +#if (UINTMAX_MAX > UINT_MAX) && ((UINTMAX_MAX/UINT_MAX) - 2 <= UINT_MAX) +#define INTERNAL_DIV_MOD +#endif + +char *_uintmaxtostr(char * __restrict bufend, uintmax_t uval, + int base, __UIM_CASE alphacase) +{ + int negative; + unsigned int digit; +#ifdef INTERNAL_DIV_MOD + unsigned int H, L, high, low, rh; +#endif + + negative = 0; + if (base < 0) { /* signed value */ + base = -base; + if (uval > INTMAX_MAX) { + uval = -uval; + negative = 1; + } + } + + /* this is an internal routine -- we shouldn't need to check this */ + assert(!((base < 2) || (base > 36))); + + *bufend = '\0'; + +#ifndef INTERNAL_DIV_MOD + do { + digit = uval % base; + uval /= base; + + *--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase ); + } while (uval); + +#else /* ************************************************** */ + + H = (UINT_MAX / base); + L = UINT_MAX % base + 1; + if (L == base) { + ++H; + L = 0; + } + low = (unsigned int) uval; + high = (unsigned int) (uval >> (sizeof(unsigned int) * CHAR_BIT)); + + do { + rh = high % base; + high /= base; + digit = (low % base) + (L * rh); + low = (low / base) + (H * rh) + (digit / base); + digit %= base; + + *--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase ); + } while (low | high); + +#endif /******************************************************/ + + if (negative) { + *--bufend = '-'; + } + + return bufend; +} +#undef INTERNAL_DIV_MOD + +#endif |