diff options
author | Manuel Novoa III <mjn3@codepoet.org> | 2002-03-12 01:41:56 +0000 |
---|---|---|
committer | Manuel Novoa III <mjn3@codepoet.org> | 2002-03-12 01:41:56 +0000 |
commit | 870f09e54eb0a7e1651dd2066589627d312f77e2 (patch) | |
tree | 3f07f02552c99bc8e2bd48475fff3894d3ed172c /libc/stdio/stdio.c | |
parent | 03e039820dc5092e27e81f3671652f25da7f25f1 (diff) |
Deal with cvs "feature"... or is that cvs _user_...
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? */ < |