summaryrefslogtreecommitdiff
path: root/libc/stdio/stdio.c
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2004-02-11 23:48:50 +0000
committerManuel Novoa III <mjn3@codepoet.org>2004-02-11 23:48:50 +0000
commit082e680bd54e999f2bb4eb77141958938b1e9ee9 (patch)
tree203c45b85ca608e1550d8ffc459456fc9cf0b30b /libc/stdio/stdio.c
parent17c21765b4a97c6f0b74ba8466073e5a3f97cdee (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.c3677
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