diff options
author | Manuel Novoa III <mjn3@codepoet.org> | 2004-02-11 23:48:50 +0000 |
---|---|---|
committer | Manuel Novoa III <mjn3@codepoet.org> | 2004-02-11 23:48:50 +0000 |
commit | 082e680bd54e999f2bb4eb77141958938b1e9ee9 (patch) | |
tree | 203c45b85ca608e1550d8ffc459456fc9cf0b30b /libc/stdio/stdio.c | |
parent | 17c21765b4a97c6f0b74ba8466073e5a3f97cdee (diff) |
New stdio core. Should be more maintainable. Fixes a couple of bugs.
Codepaths streamlined. Improved performance for nonthreaded apps
when linked with a thread-enabled libc.
Minor iconv bug and some locale/thread related startup issues fixed.
These showed up in getting a gcj-compiled java helloworld app running.
Removed some old extension functions... _stdio_fdout and _stdio_fsfopen.
Diffstat (limited to 'libc/stdio/stdio.c')
-rw-r--r-- | libc/stdio/stdio.c | 3677 |
1 files changed, 0 insertions, 3677 deletions
diff --git a/libc/stdio/stdio.c b/libc/stdio/stdio.c deleted file mode 100644 index 1bdccbfe9..000000000 --- a/libc/stdio/stdio.c +++ /dev/null @@ -1,3677 +0,0 @@ -/* Copyright (C) 2002,2003,2004 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! */ - -/* 8-05-2002 - * Changed fflush() behavior to no-op for r/w streams in read-mode. - * This falls under undefined behavior wrt ANSI/ISO C99, but - * SUSv3 seems to treat it as a no-op and it occurs in some apps. - * Fixed a problem with _stdio_fwrite() not checking for underlying - * write() failures. - * Fixed both _stdio_fwrite() and _stdio_fread() to make sure that - * the putc and getc macros were disabled if the stream was in - * and error state. - * The above changes should take care of a problem initially reported - * by "Steven J. Hill" <sjhill@realitydiluted.com>. - * - * 8-25-2002 - * Changed fclose behavior when custom streams were enabled. Previously, - * the cookie pointer was set to NULL as a debugging aid. However, - * some of the perl 5.8 test rely on being able to close stderr and - * still try writing to it. So now, the cookie pointer and handler - * function pointers are set to that it is a "normal" file with a - * file descriptor of -1. Note: The cookie pointer is reset to NULL - * if the FILE struct is free'd by fclose. - * - * Nov 21, 2002 - * Added internal function _wstdio_fwrite. - * Jan 3, 2003 - * Fixed a bug in _wstdio_fwrite. - * - * Jan 22, 2003 - * Fixed a bug related file position in append mode. _stdio_fwrite now - * seeks to the end of the stream when append mode is set and we are - * transitioning to write mode, so that subsequent ftell() return - * values are correct. - * Also fix _stdio_fopen to support fdopen() with append specified when - * the underlying file didn't have O_APPEND set. It now sets the - * O_APPEND flag as recommended by SUSv3 and is done by glibc. - * - * May 15, 2003 - * Modify __stdio_fread to deal with fake streams used by *sscanf. - * Set EOF to end of buffer when fmemopen used on a readonly stream. - * Note: I really need to run some tests on this to see what the - * glibc code does in each case. - * - * Sept 21, 2003 - * Modify _stdio_READ to conform with C99, as stdio input behavior upon - * encountering EOF changed with Defect Report #141. In the current - * standard, the stream's EOF indicator is "sticky". Once it is set, - * all further input from the stream should fail until the application - * explicitly clears the EOF indicator (clearerr(), file positioning), - * even if more data becomes available. - * Fixed a bug in fgets. Wasn't checking for read errors. - * Minor thread locking optimizations to avoid some unnecessary locking. - * Remove the explicit calls to __builtin_* funcs, as we really need to - * implement a more general solution. - * - * Nov 17, 2003 - * Fix the return value for fputs when passed an empty string. - * - * Jan 1, 2004 - * Fix __freadable and __fwritable... were using '~' instead of '!'. (ugh) - * Fix (hopefully) a potential problem with failed freopen() calls. The - * fix isn't tested since I've been working on the replacement stdio - * core code which will go in after the next release. - */ - -/* Before we include anything, convert L_ctermid to L_ctermid_function - * and undef L_ctermid if defined. This is necessary as L_ctermid is - * a SUSv3 standard macro defined in stdio.h. */ -#ifdef L_ctermid -#define L_ctermid_function -#undef L_ctermid -#endif - -#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> - -#ifndef O_LARGEFILE /* uClibc undefines this if no large file support. */ -#ifdef __STDIO_LARGE_FILES -#error missing define for O_LARGEFILE! -#endif -#define O_LARGEFILE 0 -#endif - -/**********************************************************************/ -/* 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 - -/**********************************************************************/ - -#ifndef __STDIO_THREADSAFE - -#if defined(__BCC__) && 0 -#define UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,STREAM) \ -asm(".text\nexport _" "NAME" "_unlocked\n_" "NAME" "_unlocked = _" "NAME"); \ -RETURNTYPE NAME PARAMS -#else -#define UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,STREAM) \ -strong_alias(NAME,NAME##_unlocked) \ -RETURNTYPE NAME PARAMS -#endif - -#define UNLOCKED(RETURNTYPE,NAME,PARAMS,ARGS) \ - UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,stream) - -#if defined(__BCC__) && 0 -#define UNLOCKED_VOID_RETURN(NAME,PARAMS,ARGS) \ -asm(".text\nexport _" "NAME" "_unlocked\n_" "NAME" "_unlocked = _" "NAME"); \ -void NAME PARAMS -#else -#define UNLOCKED_VOID_RETURN(NAME,PARAMS,ARGS) \ -strong_alias(NAME,NAME##_unlocked) \ -void NAME PARAMS -#endif - -#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_MBSTATE -#define __STDIO_FILE_INIT_MBSTATE \ - { 0, 0 }, -#else -#define __STDIO_FILE_INIT_MBSTATE -#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_MBSTATE \ - __STDIO_FILE_INIT_THREADSAFE \ -} /* TODO: mbstate and builtin buf */ - -#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(FILE *stream) -{ - int aw[1]; - -#ifdef __STDIO_WIDE - - return (fread_unlocked((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, FILE *stream) -{ - int aw[1]; - - *aw = w; /* In case 'w' is in a register... */ - -#ifdef __STDIO_WIDE - - return (fwrite_unlocked((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 && (stream->cookie == &(stream->filedes)) && (stream->filedes >= 0)) - ? stream->filedes - : (__set_errno(EBADF), -1) ); -#else /* __STDIO_GLIBC_CUSTOM_STREAMS */ - return ((stream && 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 -- use intptr_t?? (also fopencookie) */ - - 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 -/**********************************************************************/ -#ifdef L_ctermid_function - -/* Not required to be reentrant. */ - -char *ctermid(register char *s) -{ - static char sbuf[L_ctermid]; - -#ifdef __BCC__ - /* Currently elks doesn't support /dev/tty. */ - if (!s) { - s = sbuf; - } - *s = 0; - - return s; -#else - /* glibc always returns /dev/tty for linux. */ - return strcpy((s ? s : sbuf), "/dev/tty"); -#endif -} - -#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) 0); -#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(register void *cookie, char *buf, size_t bufsize) -{ - size_t count = COOKIE->len - COOKIE->pos; - - /* Note: 0 < bufsize < SSIZE_MAX because of _stdio_READ. */ - if (!count) { /* EOF! */ - return 0; - } - - 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(register void *cookie, 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... */ - __set_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(register 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(register 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; - register __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_READONLY) { - cookie->eof = len; - } - 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(register void *cookie, const char *buf, size_t bufsize) -{ - register 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) { - __set_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; - COOKIE->buf[COOKIE->eof] = 0; /* Need to nul-terminate. */ - } - - return bufsize; -} - -static int oms_seek(register void *cookie, __offmax_t *pos, int whence) -{ - __offmax_t p = *pos; - register 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) -{ - register __oms_cookie *cookie; - register 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. */ - -/* 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; - - /* Fake an fdopen guaranteed to pass the _stdio_fopen basic agreement - * check without an fcntl call. */ - if ((stream = _stdio_fopen(((char *)(INT_MAX-1)), - mode, NULL, INT_MAX)) /* TODO: use intptr_t? */ - != NULL - ) { - stream->filedes = -1; - 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, - register cookie_io_functions_t *io_functions) -{ - register FILE *stream; - - /* Fake an fdopen guaranteed to pass the _stdio_fopen basic agreement - * check without an fcntl call. */ - if ((stream = _stdio_fopen(((char *)(INT_MAX-1)), - mode, NULL, INT_MAX)) /* TODO: use intptr_t? */ - != NULL - ) { - stream->filedes = -1; - 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->bufpos = stream->bufread = 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 Unlike the glibc version, this __fpending returns bytes in buffer for wide streams too! - -link_warning(__fpending, "This version of __fpending returns bytes remaining in buffer for both narrow and wide streams. glibc's version returns wide chars in buffer for the wide stream case.") - -#endif /* __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->bufpos - stream->bufstart); -#else /* __STDIO_BUFFERS */ - return 0; -#endif /* __STDIO_BUFFERS */ -} - -#endif -/**********************************************************************/ -#ifdef L__flushlbf - -/* No reentrancy issues. */ - -void _flushlbf(void) -{ -#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) - fflush((FILE *) &_stdio_openlist); /* Uses an implementation hack!!! */ -#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ - /* Nothing to do. */ -#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ -} - -#endif -/**********************************************************************/ -#ifdef L___fsetlocking - -/* NOT threadsafe!!! (I don't think glibc's is either) - * - * This interacts badly with internal locking/unlocking. If you use this routine, - * make sure the file isn't being accessed by any other threads. Typical use would - * be to change to user locking immediately after opening the stream. - */ - -#ifdef __UCLIBC_MJN3_ONLY__ -link_warning(__fsetlocking, "Oddly enough, __fsetlocking() is NOT threadsafe.") -#endif - -int __fsetlocking(FILE *stream, int locking_mode) -{ -#ifdef __STDIO_THREADSAFE - int old_mode; -#endif - - assert((FSETLOCKING_QUERY == 0) && (FSETLOCKING_INTERNAL == 1) - && (FSETLOCKING_BYCALLER == 2)); - - assert(((unsigned int) locking_mode) <= 2); - -#ifdef __STDIO_THREADSAFE - old_mode = stream->user_locking; - - assert(((unsigned int) old_mode) <= 1); /* Must be 0 (internal) or 1 (user). */ - - 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); - } - - return 2 - old_mode; -#else - return FSETLOCKING_BYCALLER; /* Well, without threa |