summaryrefslogtreecommitdiff
path: root/libc/stdio/stdio.c
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2002-03-12 01:41:56 +0000
committerManuel Novoa III <mjn3@codepoet.org>2002-03-12 01:41:56 +0000
commit870f09e54eb0a7e1651dd2066589627d312f77e2 (patch)
tree3f07f02552c99bc8e2bd48475fff3894d3ed172c /libc/stdio/stdio.c
parent03e039820dc5092e27e81f3671652f25da7f25f1 (diff)
Deal with cvs "feature"... or is that cvs _user_...
Diffstat (limited to 'libc/stdio/stdio.c')
-rw-r--r--libc/stdio/stdio.c3171
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? */
<