diff options
Diffstat (limited to 'libc/stdio')
100 files changed, 8657 insertions, 7225 deletions
diff --git a/libc/stdio/Makefile b/libc/stdio/Makefile index b0ba70ba8..59e80a359 100644 --- a/libc/stdio/Makefile +++ b/libc/stdio/Makefile @@ -2,6 +2,7 @@ # # Copyright (C) 2000 by Lineo, inc. # Copyright (C) 2000,2001 Erik Andersen <andersen@uclibc.org> +# Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> # # This program 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 @@ -25,61 +26,96 @@ TOPDIR=../../ include $(TOPDIR)Rules.mak # Note: The *64.o objects are empty when compiled without large file support. -# -# Note: Use the libpthreads of: flockfile.o ftrylockfile.o funlockfile.o -# Also, maybe move __fsetlocking.o as well? - -MSRC = stdio.c -MOBJ = fclose.o fflush.o fopen.o freopen.o perror.o remove.o \ - setbuf.o setvbuf.o fgetc.o fgets.o fputc.o fputs.o \ - getc.o getchar.o gets.o putc.o putchar.o puts.o \ - ungetc.o fread.o fwrite.o fgetpos.o fseek.o fsetpos.o ftell.o \ - rewind.o clearerr.o feof.o ferror.o \ - fileno.o fdopen.o getw.o putw.o setbuffer.o setlinebuf.o fcloseall.o \ - fopen64.o freopen64.o ftello64.o fseeko64.o fsetpos64.o fgetpos64.o \ - __fbufsize.o __freading.o __fwriting.o __freadable.o __fwritable.o \ - __flbf.o __fpurge.o __fpending.o _flushlbf.o \ - fopencookie.o fmemopen.o open_memstream.o \ - __fsetlocking.o flockfile.o ftrylockfile.o funlockfile.o \ - _stdio_fopen.o _stdio_fread.o _stdio_fwrite.o _stdio_adjpos.o \ - _stdio_lseek.o _stdio_init.o \ - _stdio_fsfopen.o _stdio_fdout.o _uintmaxtostr.o _stdio_strerror_r.o \ - getdelim.o getline.o ctermid.o - -MSRC2= printf.c -MOBJ2= vsnprintf.o vdprintf.o vasprintf.o vprintf.o vsprintf.o \ - fprintf.o snprintf.o dprintf.o asprintf.o printf.o sprintf.o \ - _store_inttype.o _load_inttype.o - -MSRC3=scanf.c -MOBJ3=scanf.o sscanf.o fscanf.o vscanf.o vsscanf.o vfscanf.o \ - __scan_cookie.o __psfs_parse_spec.o __psfs_do_numeric.o +# SUSv3 functions +CSRC = fclose.c fcloseall.c fdopen.c fgetpos.c fopen.c freopen.c \ + fseeko.c fsetpos.c ftello.c getdelim.c getline.c gets.c getw.c \ + perror.c puts.c putw.c remove.c rewind.c setbuf.c setbuffer.c \ + setlinebuf.c setvbuf.c ungetc.c \ + printf.c vprintf.c vsprintf.c fprintf.c snprintf.c dprintf.c \ + asprintf.c sprintf.c vasprintf.c vdprintf.c vsnprintf.c \ + tmpfile.c tmpnam.c tmpnam_r.c popen.c tempnam.c ctermid.c + +# getc -> alias for fgetc +# putc -> alias for fputc +# rename is a syscall + +# Implementation support functions +CSRC += _READ.c _WRITE.c _adjust_pos.c _fopen.c _fwrite.c \ + _rfill.c _stdio.c _trans2r.c _trans2w.c _wcommit.c \ + _load_inttype.c _store_inttype.c _uintmaxtostr.c +ifeq ($(strip $(UCLIBC_HAS_FLOATS)),y) +CSRC += _fpmaxtostr.c +endif -ifeq ($(UCLIBC_HAS_WCHAR),y) - MOBJ += _wstdio_fwrite.o - MOBJ2 += fwprintf.o wprintf.o swprintf.o vwprintf.o vswprintf.o \ - vfwprintf.o - MOBJ3 += wscanf.o swscanf.o fwscanf.o vwscanf.o vswscanf.o vfwscanf.o +# stdio_ext.h functions +CSRC += __fbufsize.c __flbf.c __fpending.c __fpurge.c __freadable.c \ + __freading.c __fsetlocking.c __fwritable.c __fwriting.c _flushlbf.c + +# Other glibc extensions +CSRC += fopencookie.c fmemopen.c open_memstream.c _cs_funcs.c + +# pthread functions +ifeq ($(strip $(UCLIBC_HAS_THREADS)),y) +CSRC += flockfile.c ftrylockfile.c funlockfile.c endif +# Functions with unlocked versions +CUSRC = clearerr.c feof.c ferror.c fflush.c fgetc.c fgets.c fileno.c \ + fputc.c fputs.c fread.c fwrite.c getchar.c putchar.c +# getc_unlocked -> alias for fgetc_unlocked +# putc_unlocked -> alias for fputc_unlocked + +# Largefile functions +CLOBJS = fgetpos64.o fopen64.o freopen64.o fseeko64.o fsetpos64.o ftello64.o +# tmpfile64.o + +# vfprintf and support functions +MSRC2= vfprintf.c ifneq ($(USE_OLD_VFPRINTF),y) - MOBJ2 += _ppfs_init.o _ppfs_prepargs.o _ppfs_setargs.o \ - _ppfs_parsespec.o vfprintf.o \ - register_printf_function.o parse_printf_format.o +MOBJ2= vfprintf.o \ + _ppfs_init.o _ppfs_prepargs.o _ppfs_setargs.o _ppfs_parsespec.o \ + register_printf_function.o parse_printf_format.o +else +MOBJ2= +CSRC += old_vfprintf.c +endif + +# vfscanf and support functions plus other *scanf funcs +MSRC3= scanf.c +MOBJ3= vfscanf.o __scan_cookie.o __psfs_parse_spec.o __psfs_do_numeric.o \ + scanf.o sscanf.o fscanf.o vscanf.o vsscanf.o + +MWSRC= wstdio.c +MWOBJ= + +CWSRC = +ifeq ($(UCLIBC_HAS_WCHAR),y) +CWSRC += _wfwrite.c fwprintf.c swprintf.c vswprintf.c vwprintf.c wprintf.c \ + fwide.c ungetwc.c +CUSRC += fgetwc.c getwchar.c fgetws.c fputwc.c putwchar.c fputws.c +# getwc (fgetwc alias) getwc_unlocked (fgetwc_unlocked alias) +# putwc (fputwc alias) putwc_unlocked (fputwc_unlocked alias) +MOBJ2 += vfwprintf.o +MOBJ3 += wscanf.o swscanf.o fwscanf.o vwscanf.o vswscanf.o vfwscanf.o endif -ifeq ($(UCLIBC_HAS_FLOATS),y) - MOBJ2 += _fpmaxtostr.o +CSRC += $(CUSRC) + +COBJS = $(patsubst %.c,%.o, $(CSRC)) +CUOBJS = $(patsubst %.c,%_unlocked.o, $(CUSRC)) +CWOBJS = $(patsubst %.c,%.o, $(CWSRC)) + +ifeq ($(strip $(UCLIBC_HAS_WCHAR)),y) +COBJS += $(CWOBJS) endif -CSRC=popen.c tmpfile.c tmpnam.c tmpnam_r.c tempnam.c -ifeq ($(USE_OLD_VFPRINTF),y) - CSRC += old_vfprintf.c +OBJS = $(COBJS) $(CUOBJS) $(MOBJ2) $(MOBJ3) $(MWOBJ) + +ifeq ($(strip $(UCLIBC_HAS_LFS)),y) +OBJS += $(CLOBJS) endif -COBJS=$(patsubst %.c,%.o, $(CSRC)) -OBJS=$(MOBJ) $(MOBJ2) $(MOBJ3) $(COBJS) all: $(OBJS) $(LIBC) @@ -88,9 +124,17 @@ $(LIBC): ar-target ar-target: $(OBJS) $(AR) $(ARFLAGS) $(LIBC) $(OBJS) -$(MOBJ): $(MSRC) - $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o - $(STRIPTOOL) -x -R .note -R .comment $*.o +$(COBJS): %.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $@ + +%_unlocked.o : %.c + $(CC) $(CFLAGS) -D__DO_UNLOCKED -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $@ + +%64.o : %.c + $(CC) $(CFLAGS) -D__DO_LARGEFILE -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $@ $(MOBJ2): $(MSRC2) $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o @@ -100,12 +144,12 @@ $(MOBJ3): $(MSRC3) $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o -$(COBJS): %.o : %.c - $(CC) $(CFLAGS) -c $< -o $@ +$(MWOBJ): $(MWSRC) + $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o $(OBJ): Makefile clean: - $(RM) *.[oa] *~ core + rm -f *.[oa] *~ core diff --git a/libc/stdio/_READ.c b/libc/stdio/_READ.c new file mode 100644 index 000000000..7d3c38ce6 --- /dev/null +++ b/libc/stdio/_READ.c @@ -0,0 +1,66 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* Given a reading stream without its end-of-file indicator set and + * with no buffered input or ungots, read at most 'bufsize' bytes + * into 'buf' (which may be the stream's __bufstart). + * If a read error occurs, set the stream's error indicator. + * If EOF is encountered, set the stream's end-of-file indicator. + * + * Returns the number of bytes read, even in EOF and error cases. + * + * Notes: + * Calling with bufsize == 0 is NOT permitted (unlike __stdio_WRITE). + * NOT THREADSAFE! Assumes stream already locked if necessary. + */ + +size_t __stdio_READ(register FILE *stream, + unsigned char *buf, size_t bufsize) +{ + ssize_t rv = 0; + + __STDIO_STREAM_VALIDATE(stream); + assert(stream->__filedes >= -1); + assert(__STDIO_STREAM_IS_READING(stream)); + assert(!__STDIO_STREAM_BUFFER_RAVAIL(stream)); /* Buffer must be empty. */ + assert(!(stream->__modeflags & __FLAG_UNGOT)); + assert(bufsize); + + if (!__FEOF_UNLOCKED(stream)) { + if (bufsize > SSIZE_MAX) { + bufsize = SSIZE_MAX; + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning EINTR? +#endif +/* RETRY: */ + if ((rv = __READ(stream, buf, bufsize)) <= 0) { + if (rv == 0) { + __STDIO_STREAM_SET_EOF(stream); + } else { +/* if (errno == EINTR) goto RETRY; */ + __STDIO_STREAM_SET_ERROR(stream); + rv = 0; + } +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Make custom stream read return check optional. +#endif +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + } else { + assert(rv <= bufsize); + if (rv > bufsize) { /* Read more than bufsize! */ + abort(); + } +#endif + } + } + + return rv; +} diff --git a/libc/stdio/_WRITE.c b/libc/stdio/_WRITE.c new file mode 100644 index 000000000..d300d3919 --- /dev/null +++ b/libc/stdio/_WRITE.c @@ -0,0 +1,100 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* Given a writing stream with no buffered output, write the + * data in 'buf' (which may be the stream's bufstart) of size + * 'bufsize' to the stream. If a write error occurs, set the + * stream's error indicator and (if buffering) buffer as much + * data as possible (FBF) or only up to '\n' (LBF) to implement + * "as if fputc()" clause in the standard. + * + * Returns the number of bytes written and/or buffered. + * + * Notes: + * Calling with bufsize == 0 is permitted, and buf is ignored in + * that case. + * We implement fflush() by setting bufpos to bufstart and passing + * bufstart as the buf arg. If there is a write error, the + * unwritten buffered data will simply be moved to the beginning + * of the buffer. Since the data obviously fits in the buffer + * and since there will be no '\n' chars in the buffer in the LBF + * case, no data will be lost. + * NOT THREADSAFE! Assumes stream already locked if necessary. + */ + +size_t __stdio_WRITE(register FILE *stream, + register const unsigned char *buf, size_t bufsize) +{ + size_t todo; + ssize_t rv, stodo; + + __STDIO_STREAM_VALIDATE(stream); + assert(stream->__filedes >= -1); + assert(__STDIO_STREAM_IS_WRITING(stream)); + assert(!__STDIO_STREAM_BUFFER_WUSED(stream)); /* Buffer must be empty. */ + + todo = bufsize; + + do { + if (todo == 0) { /* Done? */ + __STDIO_STREAM_VALIDATE(stream); + return bufsize; + } + stodo = (todo <= SSIZE_MAX) ? todo : SSIZE_MAX; + if ((rv = __WRITE(stream, buf, stodo)) >= 0) { +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Make custom stream write return check optional. +#endif +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + assert(rv <= stodo); + if (rv > stodo) { /* Wrote more than stodo! */ +/* abort(); */ + } +#endif + todo -= rv; + buf += rv; + } else +#ifdef __UCLIBC_MJN3_ONLY__ +#warning EINTR? +#endif +/* if (errno != EINTR) */ + { + __STDIO_STREAM_SET_ERROR(stream); + +#ifdef __STDIO_BUFFERS + if ((stodo = __STDIO_STREAM_BUFFER_SIZE(stream)) != 0) { + unsigned char *s; + + if (stodo > todo) { + stodo = todo; + } + + s = stream->__bufstart; + + do { + if (((*s = *buf) == '\n') + && __STDIO_STREAM_IS_LBF(stream) + ) { + break; + } + ++s; + ++buf; + } while (--stodo); + + stream->__bufpos = s; + + todo -= (s - stream->__bufstart); + } +#endif /* __STDIO_BUFFERS */ + + __STDIO_STREAM_VALIDATE(stream); + return bufsize - todo; + } + } while (1); +} diff --git a/libc/stdio/__fbufsize.c b/libc/stdio/__fbufsize.c new file mode 100644 index 000000000..09ade15ae --- /dev/null +++ b/libc/stdio/__fbufsize.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Solaris function -- + * Returns the size of the buffer in bytes.. + */ + +size_t __fbufsize(register FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return __STDIO_STREAM_BUFFER_SIZE(stream); +} diff --git a/libc/stdio/__flbf.c b/libc/stdio/__flbf.c new file mode 100644 index 000000000..13d8cea96 --- /dev/null +++ b/libc/stdio/__flbf.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Solaris function -- + * Returns nonzero if the stream is line buffered, and 0 otherwise. + */ + +int __flbf(FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return __STDIO_STREAM_IS_LBF(stream); +} diff --git a/libc/stdio/__fpending.c b/libc/stdio/__fpending.c new file mode 100644 index 000000000..a7fe05463 --- /dev/null +++ b/libc/stdio/__fpending.c @@ -0,0 +1,35 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Solaris function -- + * Returns the number of bytes in the buffer for a writing stream. + * + * NOTE: GLIBC DIFFERENCE!!! + * + * glibc will return the number of wide chars pending for wide oriented + * streams. We always return the number of bytes in the buffer, as we + * convert wide chars to their multibyte encodings and buffer _those_. + */ + +#ifdef __UCLIBC_HAS_WCHAR__ +#warning Note: 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 + +size_t __fpending(register FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return (__STDIO_STREAM_IS_WRITING(stream)) + ? __STDIO_STREAM_BUFFER_WUSED(stream) + : 0; +} diff --git a/libc/stdio/__fpurge.c b/libc/stdio/__fpurge.c new file mode 100644 index 000000000..c17ecf4c0 --- /dev/null +++ b/libc/stdio/__fpurge.c @@ -0,0 +1,34 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Solaris function -- + * Discard all buffered data whether reading or writing. + */ + +void __fpurge(register FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + __STDIO_STREAM_DISABLE_GETC(stream); + __STDIO_STREAM_DISABLE_PUTC(stream); + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream); + stream->__ungot[1] = 0; + +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(stream->__state)); +#endif +#ifdef __UCLIBC_HAS_WCHAR__ + stream->__ungot_width[0] = 0; +#endif + + stream->__modeflags &= ~(__MASK_READING|__FLAG_WRITING); + + __STDIO_STREAM_VALIDATE(stream); +} diff --git a/libc/stdio/__freadable.c b/libc/stdio/__freadable.c new file mode 100644 index 000000000..006a66fc8 --- /dev/null +++ b/libc/stdio/__freadable.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Solaris function -- + * Return nonzero if readable, and 0 if write-only. + */ + +int __freadable(FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return !__STDIO_STREAM_IS_WRITEONLY(stream); +} diff --git a/libc/stdio/__freading.c b/libc/stdio/__freading.c new file mode 100644 index 000000000..aab91b238 --- /dev/null +++ b/libc/stdio/__freading.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Solaris function -- + * Return nonzero if read-only or was last read from, and 0 otherwise. + */ + +int __freading(FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return __STDIO_STREAM_IS_READING_OR_READONLY(stream); +} diff --git a/libc/stdio/__fsetlocking.c b/libc/stdio/__fsetlocking.c new file mode 100644 index 000000000..f49503207 --- /dev/null +++ b/libc/stdio/__fsetlocking.c @@ -0,0 +1,45 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Not threadsafe. */ + +/* Notes: + * When setting the locking mode, glibc returns the previous setting. + * glibc treats invalid locking_mode args as FSETLOCKING_INTERNAL. + */ + +int __fsetlocking(FILE *stream, int locking_mode) +{ +#ifdef __UCLIBC_HAS_THREADS__ + int current = 1 + (stream->__user_locking & 1); + + /* Check constant assumptions. We can't test at build time + * since these are enums. */ + assert((FSETLOCKING_QUERY == 0) && (FSETLOCKING_INTERNAL == 1) + && (FSETLOCKING_BYCALLER == 2)); + + __STDIO_STREAM_VALIDATE(stream); + assert(((unsigned int) locking_mode) <= 2); + + if (locking_mode != FSETLOCKING_QUERY) { + stream->__user_locking = ((locking_mode == FSETLOCKING_BYCALLER) + ? 1 + : _stdio_user_locking); /* 0 or 2 */ + __STDIO_STREAM_VALIDATE(stream); + } + + return current; +#else + __STDIO_STREAM_VALIDATE(stream); + assert(((unsigned int) locking_mode) <= 2); + + return FSETLOCKING_INTERNAL; +#endif +} diff --git a/libc/stdio/__fwritable.c b/libc/stdio/__fwritable.c new file mode 100644 index 000000000..59c70a648 --- /dev/null +++ b/libc/stdio/__fwritable.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Solaris function -- + * Return nonzero if writable, and 0 if read-only. + */ + +int __fwritable(FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return !__STDIO_STREAM_IS_READONLY(stream); +} diff --git a/libc/stdio/__fwriting.c b/libc/stdio/__fwriting.c new file mode 100644 index 000000000..926eaa95f --- /dev/null +++ b/libc/stdio/__fwriting.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Solaris function -- + * Return nonzero if write-only or was last written to, and 0 otherwise. + */ + +int __fwriting(FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return __STDIO_STREAM_IS_WRITING_OR_WRITEONLY(stream); +} diff --git a/libc/stdio/_adjust_pos.c b/libc/stdio/_adjust_pos.c new file mode 100644 index 000000000..bc48d32b7 --- /dev/null +++ b/libc/stdio/_adjust_pos.c @@ -0,0 +1,68 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* Both ftell() and fseek() (for SEEK_CUR) need to correct the stream's + * position to take into account buffered data and ungotten chars. + * + * If successful, store corrected position in *pos and return >= 0. + * Otherwise return < 0. + * + * If position is unrepresentable, set errno to EOVERFLOW. + */ + +int __stdio_adjust_position(register FILE * __restrict stream, + register __offmax_t *pos) +{ + __offmax_t oldpos; + int corr; + + if ((corr = stream->__modeflags & __MASK_READING) != 0) { + --corr; /* Correct for ungots. Assume narrow, and fix below. */ + } + +#ifdef __UCLIBC_HAS_WCHAR__ + if (corr && __STDIO_STREAM_IS_WIDE(stream)) { + /* A wide stream and we have at least one ungotten wchar. + * If it is a user ungot, we need to fail since position + * is unspecified as per C99. */ + if ((corr > 1) || stream->__ungot[1]) { /* User ungetwc, */ + return -1; /* so position is undefined. */ + } + corr -= (1 + stream->__ungot_width[1]); + if (stream->__state.__mask > 0) { /* Incomplete (bad?) mb char. */ + corr -= stream->__ungot_width[0]; + } + } +#endif + +#ifdef __STDIO_BUFFERS + corr += (((__STDIO_STREAM_IS_WRITING(stream)) + ? stream->__bufstart : stream->__bufread) + - stream->__bufpos); +#endif + + oldpos = *pos; + + /* Range checking cases: + * (pos - corr > pos) && (corr > 0) : underflow? return -corr < 0 + * (pos - corr > pos) && (corr < 0) : ok .. return -corr > 0 + * (pos - corr <= pos) && (corr >= 0) : ok .. return corr > 0 + * (pos - corr <= pos) && (corr < 0) : overflow .. return corr < 0 + */ + + if ((*pos -= corr) > oldpos) { + corr = -corr; + } + + if (corr < 0) { + __set_errno(EOVERFLOW); + } + + return corr; +} diff --git a/libc/stdio/_cs_funcs.c b/libc/stdio/_cs_funcs.c new file mode 100644 index 000000000..fd81a6f95 --- /dev/null +++ b/libc/stdio/_cs_funcs.c @@ -0,0 +1,67 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/**********************************************************************/ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ +/**********************************************************************/ + +ssize_t _cs_read(void *cookie, char *buf, size_t bufsize) +{ + return read(*((int *) cookie), buf, bufsize); +} + +/**********************************************************************/ + +ssize_t _cs_write(void *cookie, const char *buf, size_t bufsize) +{ + return write(*((int *) cookie), (char *) buf, bufsize); +} + +/**********************************************************************/ + +int _cs_seek(void *cookie, register __offmax_t *pos, int whence) +{ + __offmax_t res; + +#ifdef __UCLIBC_HAS_LFS__ + res = lseek64(*((int *) cookie), *pos, whence); +#else + res = lseek(*((int *) cookie), *pos, whence); +#endif + + return (res >= 0) ? ((*pos = res), 0) : ((int) res); +} + +/**********************************************************************/ + +int _cs_close(void *cookie) +{ + return close(*((int *) cookie)); +} + +/**********************************************************************/ +#else +/**********************************************************************/ + +int __stdio_seek(FILE *stream, register __offmax_t *pos, int whence) +{ + __offmax_t res; + +#ifdef __UCLIBC_HAS_LFS__ + res = lseek64(stream->__filedes, *pos, whence); +#else + res = lseek(stream->__filedes, *pos, whence); +#endif + + return (res >= 0) ? ((*pos = res), 0) : ((int) res); +} + +/**********************************************************************/ +#endif +/**********************************************************************/ diff --git a/libc/stdio/_flushlbf.c b/libc/stdio/_flushlbf.c new file mode 100644 index 000000000..31ed2fc55 --- /dev/null +++ b/libc/stdio/_flushlbf.c @@ -0,0 +1,18 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdio_ext.h> + +/* Solaris function -- + * Flush all line buffered (writing) streams. + */ + +void _flushlbf(void) +{ + __STDIO_FLUSH_LBF_STREAMS; +} diff --git a/libc/stdio/_fopen.c b/libc/stdio/_fopen.c new file mode 100644 index 000000000..6e3d53bd8 --- /dev/null +++ b/libc/stdio/_fopen.c @@ -0,0 +1,207 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* + * Cases: + * fopen64 : filename != NULL, stream == NULL, filedes == -2 + * fopen : filename != NULL, stream == NULL, filedes == -1 + * freopen : filename != NULL, stream != NULL, filedes == -1 + * fdopen : filename == NULL, stream == NULL, filedes valid + * + * fsfopen : filename != NULL, stream != NULL, filedes == -1 + */ + +#if (O_ACCMODE != 3) || (O_RDONLY != 0) || (O_WRONLY != 1) || (O_RDWR != 2) +#error Assumption violated - mode constants +#endif + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +/* Internal function -- reentrant (locks open file list) */ + +FILE *_stdio_fopen(intptr_t fname_or_mode, + register const char * __restrict mode, + register FILE * __restrict stream, int filedes) +{ + __mode_t open_mode; + int i; + + /* Parse the specified mode. */ + open_mode = O_RDONLY; + if (*mode != 'r') { /* Not read... */ + open_mode = (O_WRONLY | O_CREAT | O_TRUNC); + if (*mode != 'w') { /* Not write (create or truncate)... */ + open_mode = (O_WRONLY | O_CREAT | O_APPEND); + if (*mode != 'a') { /* Not write (create or append)... */ + DO_EINVAL: + __set_errno(EINVAL); /* So illegal mode. */ + if (stream) { + FREE_STREAM: + assert(!(stream->__modeflags & __FLAG_FREEBUF)); + __STDIO_STREAM_FREE_FILE(stream); + } + return NULL; + } + } + } + + if ((mode[1] == 'b')) { /* Binary mode (NO-OP currently). */ + ++mode; + } + + if (mode[1] == '+') { /* Read and Write. */ + ++mode; + open_mode |= (O_RDONLY | O_WRONLY); + open_mode += (O_RDWR - (O_RDONLY | O_WRONLY)); + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Implement glibc ccs option to bind a codeset? +#warning CONSIDER: Implement glibc mmap option for readonly files? +#warning CONSIDER: Implement a text mode using custom read/write funcs? +#endif +#if defined(__UCLIBC_HAS_FOPEN_EXCLUSIVE_MODE__) || defined(__UCLIBC_HAS_FOPEN_LARGEFILE_MODE__) + + while (*++mode) { +# ifdef __UCLIBC_HAS_FOPEN_EXCLUSIVE_MODE__ + if (*mode == 'x') { /* Open exclusive (a glibc extension). */ + open_mode |= O_EXCL; + continue; + } +# endif +# ifdef __UCLIBC_HAS_FOPEN_LARGEFILE_MODE__ + if (*mode == 'F') { /* Open as large file (uClibc extension). */ + open_mode |= O_LARGEFILE; + continue; + } +# endif + } + +#endif + + if (!stream) { /* Need to allocate a FILE (not freopen). */ + if ((stream = malloc(sizeof(FILE))) == NULL) { + return stream; + } + stream->__modeflags = __FLAG_FREEFILE; +#ifdef __STDIO_BUFFERS + stream->__bufstart = NULL; /* We allocate a buffer below. */ +#endif +#ifdef __UCLIBC_HAS_THREADS__ + /* We only initialize the mutex in the non-freopen case. */ + /* stream->__user_locking = _stdio_user_locking; */ + __stdio_init_mutex(&stream->__lock); +#endif + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Verify fdopen append behavior of glibc. +#endif + + if (filedes >= 0) { /* Handle fdopen trickery. */ + stream->__filedes = filedes; + /* NOTE: it is insufficient to just check R/W/RW agreement. + * We must also check largefile compatibility if applicable. + * Also, if append mode is desired for fdopen but O_APPEND isn't + * currently set, then set it as recommended by SUSv3. However, + * if append mode is not specified for fdopen but O_APPEND is set, + * leave it set (glibc compat). */ + i = (open_mode & (O_ACCMODE|O_LARGEFILE)) + 1; + + /* NOTE: fopencookie needs changing if the basic check changes! */ + if (((i & (((int) fname_or_mode) + 1)) != i) /* Basic agreement? */ + || (((open_mode & ~((__mode_t) fname_or_mode)) & O_APPEND) + && fcntl(filedes, F_SETFL, O_APPEND)) /* Need O_APPEND. */ + ) { + goto DO_EINVAL; + } + /* For later... to reflect largefile setting in stream flags. */ + __STDIO_WHEN_LFS( open_mode |= (((__mode_t) fname_or_mode) + & O_LARGEFILE) ); + } else { + __STDIO_WHEN_LFS( if (filedes < -1) open_mode |= O_LARGEFILE ); + if ((stream->__filedes = open(((const char *) fname_or_mode), + open_mode, 0666)) < 0) { + goto FREE_STREAM; + } + } + + stream->__modeflags &= __FLAG_FREEFILE; +/* stream->__modeflags &= ~(__FLAG_READONLY|__FLAG_WRITEONLY); */ + + stream->__modeflags |= /* Note: Makes assumptions about flag vals! */ +#if (O_APPEND != __FLAG_APPEND) || ((O_LARGEFILE != __FLAG_LARGEFILE) && (O_LARGEFILE != 0)) +# if (O_APPEND != __FLAG_APPEND) + ((open_mode & O_APPEND) ? __FLAG_APPEND : 0) | +# else + (open_mode & O_APPEND) | +# endif +# if (O_LARGEFILE != __FLAG_LARGEFILE) && (O_LARGEFILE != 0) + ((open_mode & O_LARGEFILE) ? __FLAG_LARGEFILE : 0) | +# else + (open_mode & O_LARGEFILE) | +# endif +#else + (open_mode & (O_APPEND|O_LARGEFILE)) | /* i386 linux and elks */ +#endif + ((((open_mode & O_ACCMODE) + 1) ^ 0x03) * __FLAG_WRITEONLY); + +#ifdef __STDIO_BUFFERS + i = errno; /* Preserve errno against isatty call. */ + stream->__modeflags |= (isatty(stream->__filedes) * __FLAG_LBF); + __set_errno(i); + + if (!stream->__bufstart) { + if ((stream->__bufstart = malloc(BUFSIZ)) != NULL) { + stream->__bufend = stream->__bufstart + BUFSIZ; + stream->__modeflags |= __FLAG_FREEBUF; + } else { +# if __STDIO_BUILTIN_BUF_SIZE > 0 +#warning if builtin buffer, then need probably want to test vs that too + stream->__bufstart = stream->unbuf; + stream->__bufend = stream->unbuf + sizeof(stream->unbuf); +# else /* __STDIO_BUILTIN_BUF_SIZE > 0 */ + stream->__bufend = stream->__bufstart; +# endif /* __STDIO_BUILTIN_BUF_SIZE > 0 */ + } + } + + __STDIO_STREAM_DISABLE_GETC(stream); + __STDIO_STREAM_DISABLE_PUTC(stream); + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream); +#endif + + __STDIO_STREAM_RESET_GCS(stream); + +#ifdef __UCLIBC_HAS_WCHAR__ + stream->__ungot_width[0] = 0; +#endif +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(stream->__state)); +#endif + +#ifdef __UCLIBC_HAS_THREADS__ + /* Even in the freopen case, we reset the user locking flag. */ + stream->__user_locking = _stdio_user_locking; + /* __stdio_init_mutex(&stream->__lock); */ +#endif + +#ifdef __STDIO_HAS_OPENLIST + __STDIO_THREADLOCK_OPENLIST; + stream->__nextopen = _stdio_openlist; /* New files are inserted at */ + _stdio_openlist = stream; /* the head of the list. */ + __STDIO_THREADUNLOCK_OPENLIST; +#endif + + __STDIO_STREAM_VALIDATE(stream); + + return stream; +} diff --git a/libc/stdio/_fpmaxtostr.c b/libc/stdio/_fpmaxtostr.c new file mode 100644 index 000000000..7fd67ffb4 --- /dev/null +++ b/libc/stdio/_fpmaxtostr.c @@ -0,0 +1,738 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <printf.h> +#include <float.h> +#include <locale.h> +#include <bits/uClibc_fpmax.h> + +typedef void (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len, + intptr_t buf); + + +/* Copyright (C) 2000, 2001, 2003 Manuel Novoa III + * + * Function: + * + * size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + * __fp_outfunc_t fp_outfunc); + * + * This is derived from the old _dtostr, whic I wrote for uClibc to provide + * floating point support for the printf functions. It handles +/- infinity, + * nan, and signed 0 assuming you have ieee arithmetic. It also now handles + * digit grouping (for the uClibc supported locales) and hexadecimal float + * notation. Finally, via the fp_outfunc parameter, it now supports wide + * output. + * + * Notes: + * + * At most DECIMAL_DIG significant digits are kept. Any trailing digits + * are treated as 0 as they are really just the results of rounding noise + * anyway. If you want to do better, use an arbitary precision arithmetic + * package. ;-) + * + * It should also be fairly portable, as no assumptions are made about the + * bit-layout of doubles. Of course, that does make it less efficient than + * it could be. + * + */ + +/*****************************************************************************/ +/* Don't change anything that follows unless you know what you're doing. */ +/*****************************************************************************/ +/* Fairly portable nan check. Bitwise for i386 generated larger code. + * If you have a better version, comment this out. + */ +#define isnan(x) ((x) != (x)) + +/* Without seminumerical functions to examine the sign bit, this is + * about the best we can do to test for '-0'. + */ +#define zeroisnegative(x) ((1./(x)) < 0) + +/*****************************************************************************/ +/* Don't change anything that follows peroid!!! ;-) */ +/*****************************************************************************/ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ +#if FLT_RADIX != 2 +#error FLT_RADIX != 2 is not currently supported +#endif +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + +#define NUM_HEX_DIGITS ((FPMAX_MANT_DIG + 3)/ 4) + +/* WARNING: Adjust _fp_out_wide() below if this changes! */ +/* With 32 bit ints, we can get 9 decimal digits per block. */ +#define DIGITS_PER_BLOCK 9 +#define HEX_DIGITS_PER_BLOCK 8 + +/* Maximum number of subcases to output double is... + * 0 - sign + * 1 - padding and initial digit + * 2 - digits left of the radix + * 3 - 0s left of the radix or radix + * 4 - radix or digits right of the radix + * 5 - 0s right of the radix + * 6 - exponent + * 7 - trailing space padding + * although not all cases may occur. + */ +#define MAX_CALLS 8 + +/*****************************************************************************/ + +#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) +#define NUM_HEX_DIGIT_BLOCKS \ + ((NUM_HEX_DIGITS+HEX_DIGITS_PER_BLOCK-1)/HEX_DIGITS_PER_BLOCK) + +/* WARNING: Adjust _fp_out_wide() below if this changes! */ + +/* extra space for '-', '.', 'e+###', and nul */ +#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) + +/*****************************************************************************/ + +static const char fmt[] = "inf\0INF\0nan\0NAN\0.\0,"; + +#define INF_OFFSET 0 /* must be 1st */ +#define NAN_OFFSET 8 /* must be 2nd.. see hex sign handling */ +#define DECPT_OFFSET 16 +#define THOUSEP_OFFSET 18 + +#define EMPTY_STRING_OFFSET 3 + +/*****************************************************************************/ +#if FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP +#error scaling code can not handle FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP +#endif + +static const __fpmax_t exp10_table[] = +{ + 1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, /* floats */ +#if FPMAX_MAX_10_EXP < 32 +#error unsupported FPMAX_MAX_10_EXP (< 32). ANSI/ISO C requires >= 37. +#endif +#if FPMAX_MAX_10_EXP >= 64 + 1e64L, +#endif +#if FPMAX_MAX_10_EXP >= 128 + 1e128L, +#endif +#if FPMAX_MAX_10_EXP >= 256 + 1e256L, +#endif +#if FPMAX_MAX_10_EXP >= 512 + 1e512L, +#endif +#if FPMAX_MAX_10_EXP >= 1024 + 1e1024L, +#endif +#if FPMAX_MAX_10_EXP >= 2048 + 1e2048L, +#endif +#if FPMAX_MAX_10_EXP >= 4096 + 1e4096L +#endif +#if FPMAX_MAX_10_EXP >= 8192 +#error unsupported FPMAX_MAX_10_EXP. please increase table +#endif +}; + +#define EXP10_TABLE_SIZE (sizeof(exp10_table)/sizeof(exp10_table[0])) +#define EXP10_TABLE_MAX (1U<<(EXP10_TABLE_SIZE-1)) + +/*****************************************************************************/ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + +#if FLT_RADIX != 2 +#error FLT_RADIX != 2 is not currently supported +#endif + +#if FPMAX_MAX_EXP < -FPMAX_MIN_EXP +#error scaling code can not handle FPMAX_MAX_EXP < -FPMAX_MIN_EXP +#endif + +static const __fpmax_t exp16_table[] = { + 0x1.0p4L, 0x1.0p8L, 0x1.0p16L, 0x1.0p32L, 0x1.0p64L, +#if FPMAX_MAX_EXP >= 128 + 0x1.0p128L, +#endif +#if FPMAX_MAX_EXP >= 256 + 0x1.0p256L, +#endif +#if FPMAX_MAX_EXP >= 512 + 0x1.0p512L, +#endif +#if FPMAX_MAX_EXP >= 1024 + 0x1.0p1024L, +#endif +#if FPMAX_MAX_EXP >= 2048 + 0x1.0p2048L, +#endif +#if FPMAX_MAX_EXP >= 4096 + 0x1.0p4096L, +#endif +#if FPMAX_MAX_EXP >= 8192 + 0x1.0p8192L, +#endif +#if FPMAX_MAX_EXP >= 16384 + 0x1.0p16384L +#endif +#if FPMAX_MAX_EXP >= 32768 +#error unsupported FPMAX_MAX_EXP. please increase table +#endif +}; + +#define EXP16_TABLE_SIZE (sizeof(exp16_table)/sizeof(exp16_table[0])) +#define EXP16_TABLE_MAX (1U<<(EXP16_TABLE_SIZE-1)) + +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ +/*****************************************************************************/ + +#define FPO_ZERO_PAD (0x80 | '0') +#define FPO_STR_WIDTH (0x80 | ' '); +#define FPO_STR_PREC 'p' + +size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + __fp_outfunc_t fp_outfunc) +{ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + __fpmax_t lower_bnd; + __fpmax_t upper_bnd = 1e9; +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + uint_fast32_t digit_block; +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + uint_fast32_t base = 10; + const __fpmax_t *power_table; + int dpb = DIGITS_PER_BLOCK; + int ndb = NUM_DIGIT_BLOCKS; + int nd = DECIMAL_DIG; + int sufficient_precision = 0; +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + int num_groups = 0; + int initial_group; /* This does not need to be initialized. */ + int tslen; /* This does not need to be initialized. */ + int nblk2; /* This does not need to be initialized. */ + const char *ts; /* This does not need to be initialized. */ +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + int i, j; + int round, o_exp; + int exp, exp_neg; + int width, preci; + int cnt; + char *s; + char *e; + intptr_t pc_fwi[3*MAX_CALLS]; + intptr_t *ppc; + intptr_t *ppc_last; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: The size of exp_buf[] should really be determined by the float constants. +#endif /* __UCLIBC_MJN3_ONLY__ */ + char exp_buf[16]; + char buf[BUF_SIZE]; + char sign_str[6]; /* Last 2 are for 1st digit + nul. */ + char o_mode; + char mode; + + + width = info->width; + preci = info->prec; + mode = info->spec; + + *exp_buf = 'e'; + if ((mode|0x20) == 'a') { +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + *exp_buf = 'p'; + if (preci < 0) { + preci = NUM_HEX_DIGITS; + sufficient_precision = 1; + } +#else + mode += ('g' - 'a'); +#endif + } + + if (preci < 0) { + preci = 6; + } + + *sign_str = '\0'; + if (PRINT_INFO_FLAG_VAL(info,showsign)) { + *sign_str = '+'; + } else if (PRINT_INFO_FLAG_VAL(info,space)) { + *sign_str = ' '; + } + + *(sign_str+1) = 0; + pc_fwi[5] = INF_OFFSET; + if (isnan(x)) { /* First, check for nan. */ + pc_fwi[5] = NAN_OFFSET; + goto INF_NAN; + } + + if (x == 0) { /* Handle 0 now to avoid false positive. */ +#if 1 + if (zeroisnegative(x)) { /* Handle 'signed' zero. */ + *sign_str = '-'; + } +#endif + exp = -1; + goto GENERATE_DIGITS; + } + + if (x < 0) { /* Convert negatives to positives. */ + *sign_str = '-'; + x = -x; + } + + if (__FPMAX_ZERO_OR_INF_CHECK(x)) { /* Inf since zero handled above. */ + INF_NAN: + info->pad = ' '; + ppc = pc_fwi + 6; + pc_fwi[3] = FPO_STR_PREC; + pc_fwi[4] = 3; + if (mode < 'a') { + pc_fwi[5] += 4; + } + pc_fwi[5] = (intptr_t)(fmt + pc_fwi[5]); + goto EXIT_SPECIAL; + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Clean up defines when hexadecimal float notation is unsupported. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + + if ((mode|0x20) == 'a') { + lower_bnd = 0x1.0p31L; + upper_bnd = 0x1.0p32L; + power_table = exp16_table; + exp = HEX_DIGITS_PER_BLOCK - 1; + i = EXP16_TABLE_SIZE; + j = EXP16_TABLE_MAX; + dpb = HEX_DIGITS_PER_BLOCK; + ndb = NUM_HEX_DIGIT_BLOCKS; + nd = NUM_HEX_DIGITS; + base = 16; + } else { + lower_bnd = 1e8; +/* upper_bnd = 1e9; */ + power_table = exp10_table; + exp = DIGITS_PER_BLOCK - 1; + i = EXP10_TABLE_SIZE; + j = EXP10_TABLE_MAX; +/* dpb = DIGITS_PER_BLOCK; */ +/* ndb = NUM_DIGIT_BLOCKS; */ +/* base = 10; */ + } + + + +#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + +#define lower_bnd 1e8 +#define upper_bnd 1e9 +#define power_table exp10_table +#define dpb DIGITS_PER_BLOCK +#define base 10 +#define ndb NUM_DIGIT_BLOCKS +#define nd DECIMAL_DIG + + exp = DIGITS_PER_BLOCK - 1; + i = EXP10_TABLE_SIZE; + j = EXP10_TABLE_MAX; + +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + + exp_neg = 0; + if (x < lower_bnd) { /* Do we need to scale up or down? */ + exp_neg = 1; + } + + do { + --i; + if (exp_neg) { + if (x * power_table[i] < upper_bnd) { + x *= power_table[i]; + exp -= j; + } + } else { + if (x / power_table[i] >= lower_bnd) { + x /= power_table[i]; + exp += j; + } + } + j >>= 1; + } while (i); + if (x >= upper_bnd) { /* Handle bad rounding case. */ + x /= power_table[0]; + ++exp; + } + assert(x < upper_bnd); + + GENERATE_DIGITS: + s = buf + 2; /* Leave space for '\0' and '0'. */ + i = 0; + do { + digit_block = (uint_fast32_t) x; + assert(digit_block < upper_bnd); +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Can rounding be a problem? +#endif /* __UCLIBC_MJN3_ONLY__ */ + x = (x - digit_block) * upper_bnd; + s += dpb; + j = 0; + do { + s[- ++j] = '0' + (digit_block % base); + digit_block /= base; + } while (j < dpb); + } while (++i < ndb); + + /*************************************************************************/ + + if (mode < 'a') { + *exp_buf -= ('a' - 'A'); /* e->E and p->P */ + mode += ('a' - 'A'); + } + + o_mode = mode; + if ((mode == 'g') && (preci > 0)){ + --preci; + } + round = preci; + + if (mode == 'f') { + round += exp; + if (round < -1) { + memset(buf, '0', DECIMAL_DIG); /* OK, since 'f' -> decimal case. */ + exp = -1; + round = -1; + } + } + + s = buf; + *s++ = 0; /* Terminator for rounding and 0-triming. */ + *s = '0'; /* Space to round. */ + + i = 0; + e = s + nd + 1; + if (round < nd) { + e = s + round + 2; + if (*e >= '0' + (base/2)) { /* NOTE: We always round away from 0! */ + i = 1; + } + } + + do { /* Handle rounding and trim trailing 0s. */ + *--e += i; /* Add the carry. */ + } while ((*e == '0') || (*e > '0' - 1 + base)); + +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + if ((mode|0x20) == 'a') { + char *q; + + for (q = e ; *q ; --q) { + if (*q > '9') { + *q += (*exp_buf - ('p' - 'a') - '9' - 1); + } + } + + if (e > s) { + exp *= 4; /* Change from base 16 to base 2. */ + } + } +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + + o_exp = exp; + if (e <= s) { /* We carried into an extra digit. */ + ++o_exp; + e = s; /* Needed if all 0s. */ + } else { + ++s; + } + *++e = 0; /* Terminating nul char. */ + + if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) { + mode = 'f'; + preci = round - o_exp; + } + + exp = o_exp; + if (mode != 'f') { + o_exp = 0; + } + + if (o_exp < 0) { /* Exponent is < 0, so */ + *--s = '0'; /* fake the first 0 digit. */ + } + + pc_fwi[3] = FPO_ZERO_PAD; + pc_fwi[4] = 1; + pc_fwi[5] = (intptr_t)(sign_str + 4); + sign_str[4] = *s++; + sign_str[5] = 0; + ppc = pc_fwi + 6; + + i = e - s; /* Total digits is 'i'. */ + if (o_exp >= 0) { +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + + const char *p; + + if (PRINT_INFO_FLAG_VAL(info,group) + && *(p = __UCLIBC_CURLOCALE_DATA.grouping) + ) { + int nblk1; + + nblk2 = nblk1 = *p; + if (*++p) { + nblk2 = *p; + assert(!*++p); + } + + if (o_exp >= nblk1) { + num_groups = (o_exp - nblk1) / nblk2 + 1; + initial_group = (o_exp - nblk1) % nblk2; + +#ifdef __UCLIBC_HAS_WCHAR__ + if (PRINT_INFO_FLAG_VAL(info,wide)) { + /* _fp_out_wide() will fix this up. */ + ts = fmt + THOUSEP_OFFSET; + tslen = 1; + } else { +#endif /* __UCLIBC_HAS_WCHAR__ */ + ts = __UCLIBC_CURLOCALE_DATA.thousands_sep; + tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len; +#ifdef __UCLIBC_HAS_WCHAR__ + } +#endif /* __UCLIBC_HAS_WCHAR__ */ + + width -= num_groups * tslen; + } + } + + +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + ppc[0] = FPO_STR_PREC; + ppc[2] = (intptr_t)(s); + if (o_exp >= i) { /* all digit(s) left of decimal */ + ppc[1] = i; + ppc += 3; + o_exp -= i; + i = 0; + if (o_exp>0) { /* have 0s left of decimal */ + ppc[0] = FPO_ZERO_PAD; + ppc[1] = o_exp; + ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); + ppc += 3; + } + } else if (o_exp > 0) { /* decimal between digits */ + ppc[1] = o_exp; + ppc += 3; + s += o_exp; + i -= o_exp; + } + o_exp = -1; + } + + if (PRINT_INFO_FLAG_VAL(info,alt) + || (i) + || ((o_mode != 'g') +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + && (o_mode != 'a') +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + && (preci > 0)) + ) { + ppc[0] = FPO_STR_PREC; +#ifdef __LOCALE_C_ONLY + ppc[1] = 1; + ppc[2] = (intptr_t)(fmt + DECPT_OFFSET); +#else /* __LOCALE_C_ONLY */ +#ifdef __UCLIBC_HAS_WCHAR__ + if (PRINT_INFO_FLAG_VAL(info,wide)) { + /* _fp_out_wide() will fix this up. */ + ppc[1] = 1; + ppc[2] = (intptr_t)(fmt + DECPT_OFFSET); + } else { +#endif /* __UCLIBC_HAS_WCHAR__ */ + ppc[1] = __UCLIBC_CURLOCALE_DATA.decimal_point_len; + ppc[2] = (intptr_t)(__UCLIBC_CURLOCALE_DATA.decimal_point); +#ifdef __UCLIBC_HAS_WCHAR__ + } +#endif /* __UCLIBC_HAS_WCHAR__ */ +#endif /* __LOCALE_C_ONLY */ + ppc += 3; + } + + if (++o_exp < 0) { /* Have 0s right of decimal. */ + ppc[0] = FPO_ZERO_PAD; + ppc[1] = -o_exp; + ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); + ppc += 3; + } + if (i) { /* Have digit(s) right of decimal. */ + ppc[0] = FPO_STR_PREC; + ppc[1] = i; + ppc[2] = (intptr_t)(s); + ppc += 3; + } + + if (((o_mode != 'g') || PRINT_INFO_FLAG_VAL(info,alt)) +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + && !sufficient_precision +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + ) { + i -= o_exp; + if (i < preci) { /* Have 0s right of digits. */ + i = preci - i; + ppc[0] = FPO_ZERO_PAD; + ppc[1] = i; + ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); + ppc += 3; + } + } + + /* Build exponent string. */ + if (mode != 'f') { + char *p = exp_buf + sizeof(exp_buf); + char exp_char = *exp_buf; + char exp_sign = '+'; +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + int min_exp_dig_plus_2 = ((o_mode != 'a') ? (2+2) : (2+1)); +#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ +#define min_exp_dig_plus_2 (2+2) +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + + if (exp < 0) { + exp_sign = '-'; + exp = -exp; + } + + *--p = 0; /* nul-terminate */ + j = 2; /* Count exp_char and exp_sign. */ + do { + *--p = '0' + (exp % 10); + exp /= 10; + } while ((++j < min_exp_dig_plus_2) || exp); /* char+sign+mindigits */ + *--p = exp_sign; + *--p = exp_char; + + ppc[0] = FPO_STR_PREC; + ppc[1] = j; + ppc[2] = (intptr_t)(p); + ppc += 3; + } + + EXIT_SPECIAL: + ppc_last = ppc; + ppc = pc_fwi + 4; /* Need width fields starting with second. */ + do { + width -= *ppc; + ppc += 3; + } while (ppc < ppc_last); + + ppc = pc_fwi; + ppc[0] = FPO_STR_WIDTH; + ppc[1] = i = ((*sign_str) != 0); + ppc[2] = (intptr_t) sign_str; + +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + if (((mode|0x20) == 'a') && (pc_fwi[3] >= 16)) { /* Hex sign handling. */ + /* Hex and not inf or nan, so prefix with 0x. */ + char *h = sign_str + i; + *h = '0'; + *++h = 'x' - 'p' + *exp_buf; + *++h = 0; + ppc[1] = (i += 2); + } +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + + if ((width -= i) > 0) { + if (PRINT_INFO_FLAG_VAL(info,left)) { /* Left-justified. */ + ppc_last[0] = FPO_STR_WIDTH; + ppc_last[1] = width; + ppc_last[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); + ppc_last += 3; + } else if (info->pad == '0') { /* 0 padding */ + ppc[4] += width; /* Pad second field. */ + } else { + ppc[1] += width; /* Pad first (sign) field. */ + } + } + + cnt = 0; + + do { +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + + if ((ppc == pc_fwi + 6) && num_groups) { + const char *gp = (const char *) ppc[2]; + int len = ppc[1]; + int blk = initial_group; + + cnt += num_groups * tslen; /* Adjust count now for sep chars. */ + +/* printf("\n"); */ + do { + if (!blk) { /* Initial group could be 0 digits long! */ + blk = nblk2; + } else if (len >= blk) { /* Enough digits for a group. */ +/* printf("norm: len=%d blk=%d \"%.*s\"\n", len, blk, blk, gp); */ + fp_outfunc(fp, *ppc, blk, (intptr_t) gp); + assert(gp); + if (*gp) { + gp += blk; + } + len -= blk; + } else { /* Transition to 0s. */ +/* printf("trans: len=%d blk=%d \"%.*s\"\n", len, blk, len, gp); */ + if (len) { +/* printf("len\n"); */ + fp_outfunc(fp, *ppc, len, (intptr_t) gp); + gp += len; + } + + if (ppc[3] == FPO_ZERO_PAD) { /* Need to group 0s */ +/* printf("zeropad\n"); */ + cnt += ppc[1]; + ppc += 3; + gp = (const char *) ppc[2]; + blk -= len; /* blk > len, so blk still > 0. */ + len = ppc[1]; + continue; /* Don't decrement num_groups here. */ + } else { + assert(num_groups == 0); + break; + } + } + + if (num_groups <= 0) { + break; + } + --num_groups; + + fp_outfunc(fp, FPO_STR_PREC, tslen, (intptr_t) ts); + blk = nblk2; + +/* printf("num_groups=%d blk=%d\n", num_groups, blk); */ + + } while (1); + } else + +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + + fp_outfunc(fp, *ppc, ppc[1], ppc[2]); /* NOTE: Remember 'else' above! */ + + cnt += ppc[1]; + ppc += 3; + } while (ppc < ppc_last); + + return cnt; +} diff --git a/libc/stdio/_fwrite.c b/libc/stdio/_fwrite.c new file mode 100644 index 000000000..a706ba7e6 --- /dev/null +++ b/libc/stdio/_fwrite.c @@ -0,0 +1,78 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __STDIO_BUFFERS + +/* Either buffer data or (commit buffer if necessary and) write. */ + +size_t __stdio_fwrite(const unsigned char * __restrict buffer, + size_t bytes, + register FILE * __restrict stream) +{ + size_t pending; + const unsigned char *p; + + __STDIO_STREAM_VALIDATE(stream); + assert(__STDIO_STREAM_IS_WRITING(stream)); + assert(buffer); + assert(bytes); + + if (!__STDIO_STREAM_IS_NBF(stream)) { /* FBF or LBF. */ +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Try to consolidate some of the code? +#endif + if (__STDIO_STREAM_IS_FAKE_VSNPRINTF(stream)) { + pending = __STDIO_STREAM_BUFFER_WAVAIL(stream); + if (pending > bytes) { + pending = bytes; + } + memcpy(stream->__bufpos, buffer, pending); + stream->__bufpos += pending; + __STDIO_STREAM_VALIDATE(stream); + return bytes; + } + +/* RETRY: */ + if (bytes <= __STDIO_STREAM_BUFFER_WAVAIL(stream)) { + memcpy(stream->__bufpos, buffer, bytes); + stream->__bufpos += bytes; + if (__STDIO_STREAM_IS_LBF(stream) + && memrchr(buffer, '\n', bytes) /* Search backwards. */ + ) { + if ((pending = __STDIO_COMMIT_WRITE_BUFFER(stream)) > 0) { + if (pending > bytes) { + pending = bytes; + } + buffer += (bytes - pending); + if ((p = memchr(buffer, '\n', pending)) != NULL) { + pending = (buffer + pending) - p; + bytes -= pending; + stream->__bufpos -= pending; + } + } + } + __STDIO_STREAM_VALIDATE(stream); + return bytes; + } + /* FBF or LBF and not enough space in buffer. */ + if (__STDIO_STREAM_BUFFER_WUSED(stream)) { /* Buffered data. */ + if (__STDIO_COMMIT_WRITE_BUFFER(stream)) { /* Commit failed! */ + return 0; + } +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Do we want to try again if data now fits in buffer? +#endif +/* goto RETRY; */ + } + } + + return __stdio_WRITE(stream, buffer, bytes); +} + +#endif diff --git a/libc/stdio/_load_inttype.c b/libc/stdio/_load_inttype.c new file mode 100644 index 000000000..2dd559a53 --- /dev/null +++ b/libc/stdio/_load_inttype.c @@ -0,0 +1,66 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <printf.h> + +uintmax_t _load_inttype(int desttype, register const void *src, int uflag) +{ + if (uflag >= 0) { /* unsigned */ +#if LONG_MAX != INT_MAX + if (desttype & (PA_FLAG_LONG|PA_FLAG_LONG_LONG)) { +#ifdef LLONG_MAX + if (desttype == PA_FLAG_LONG_LONG) { + return *((unsigned long long int *) src); + } +#endif + return *((unsigned long int *) src); + } +#else /* LONG_MAX != INT_MAX */ +#ifdef LLONG_MAX + if (desttype & PA_FLAG_LONG_LONG) { + return *((unsigned long long int *) src); + } +#endif +#endif /* LONG_MAX != INT_MAX */ + { + unsigned int x; + x = *((unsigned int *) src); + if (desttype == __PA_FLAG_CHAR) x = (unsigned char) x; +#if SHRT_MAX != INT_MAX + if (desttype == PA_FLAG_SHORT) x = (unsigned short int) x; +#endif + return x; + } + } else { /* signed */ +#if LONG_MAX != INT_MAX + if (desttype & (PA_FLAG_LONG|PA_FLAG_LONG_LONG)) { +#ifdef LLONG_MAX + if (desttype == PA_FLAG_LONG_LONG) { + return *((long long int *) src); + } +#endif + return *((long int *) src); + } +#else /* LONG_MAX != INT_MAX */ +#ifdef LLONG_MAX + if (desttype & PA_FLAG_LONG_LONG) { + return *((long long int *) src); + } +#endif +#endif /* LONG_MAX != INT_MAX */ + { + int x; + x = *((int *) src); + if (desttype == __PA_FLAG_CHAR) x = (char) x; +#if SHRT_MAX != INT_MAX + if (desttype == PA_FLAG_SHORT) x = (short int) x; +#endif + return x; + } + } +} diff --git a/libc/stdio/_rfill.c b/libc/stdio/_rfill.c new file mode 100644 index 000000000..145c1d78e --- /dev/null +++ b/libc/stdio/_rfill.c @@ -0,0 +1,45 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Do we really need a seperate rfill function? +#endif + +#ifdef __STDIO_BUFFERS + +/* Read some data into the buffer. + * Returns number of bytes read into the buffer. + * If 0 is returned, then either EOF or ERROR. + * Side effects are those of _stdio_READ. + */ + +size_t __stdio_rfill(register FILE *__restrict stream) +{ + size_t rv; + + __STDIO_STREAM_VALIDATE(stream); + assert(stream->__filedes >= -1); + assert(__STDIO_STREAM_IS_READING(stream)); + assert(!__STDIO_STREAM_BUFFER_RAVAIL(stream)); /* Buffer must be empty. */ + assert(__STDIO_STREAM_BUFFER_SIZE(stream)); /* Must have a buffer. */ + assert(!(stream->__modeflags & __FLAG_UNGOT)); +#ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ + assert(stream->__bufgetc_u == stream->__bufstart); +#endif + + rv = __stdio_READ(stream, stream->__bufstart, + stream->__bufend - stream->__bufstart); + stream->__bufpos = stream->__bufstart; + stream->__bufread = stream->__bufstart + rv; + + __STDIO_STREAM_VALIDATE(stream); + return rv; +} + +#endif diff --git a/libc/stdio/_stdio.c b/libc/stdio/_stdio.c new file mode 100644 index 000000000..4aae3c418 --- /dev/null +++ b/libc/stdio/_stdio.c @@ -0,0 +1,432 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* This is pretty much straight from uClibc, but with one important + * difference. + * + * We now initialize the locking flag to user locking instead of + * auto locking (i.e. FSETLOCKING_BYCALLER vs FSETLOCKING_INTERNAL). + * This greatly benefits non-threading applications linked to a + * shared thread-enabled library. In threading applications, we + * walk the stdio open file list and reset the locking mode + * appropriately when the thread subsystem is initialized. + */ + +/**********************************************************************/ + +#ifdef __UCLIBC_HAS_WCHAR__ +#define __STDIO_FILE_INIT_WUNGOT { 0, 0 }, +#else +#define __STDIO_FILE_INIT_WUNGOT +#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 + +#ifdef __STDIO_HAS_OPENLIST +#define __STDIO_FILE_INIT_NEXT(next) (next), +#else +#define __STDIO_FILE_INIT_NEXT(next) +#endif + +#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 __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ +#define __STDIO_FILE_INIT_CUSTOM_STREAM(stream) \ + &((stream).__filedes), { _cs_read, _cs_write, _cs_seek, _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 __UCLIBC_HAS_XLOCALE__ +#define __STDIO_FILE_INIT_UNUSED \ + NULL, +#else +#define __STDIO_FILE_INIT_UNUSED +#endif + +#ifdef __UCLIBC_HAS_THREADS__ +#define __STDIO_FILE_INIT_THREADSAFE \ + 2, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, +#else +#define __STDIO_FILE_INIT_THREADSAFE +#endif + +#define __STDIO_INIT_FILE_STRUCT(stream, flags, filedes, next, buf, bufsize) \ + { (flags), \ + { 0, 0 }, /* ungot[2] (no wchar) or ungot_width[2] (wchar)*/ \ + (filedes), \ + __STDIO_FILE_INIT_BUFFERS(buf,bufsize) \ + __STDIO_FILE_INIT_BUFGETC((buf)) \ + __STDIO_FILE_INIT_BUFPUTC((buf)) \ + __STDIO_FILE_INIT_NEXT(next) \ + __STDIO_FILE_INIT_CUSTOM_STREAM(stream) \ + __STDIO_FILE_INIT_WUNGOT \ + __STDIO_FILE_INIT_MBSTATE \ + __STDIO_FILE_INIT_UNUSED \ + __STDIO_FILE_INIT_THREADSAFE \ +} /* TODO: builtin buf */ + +/**********************************************************************/ +/* First we need the standard files. */ + +#ifdef __STDIO_BUFFERS +static unsigned char _fixed_buffers[2 * BUFSIZ]; +#endif + +static FILE _stdio_streams[] = { + __STDIO_INIT_FILE_STRUCT(_stdio_streams[0], \ + __FLAG_LBF|__FLAG_READONLY, \ + 0, \ + _stdio_streams + 1, \ + _fixed_buffers, \ + BUFSIZ ), + __STDIO_INIT_FILE_STRUCT(_stdio_streams[1], \ + __FLAG_LBF|__FLAG_WRITEONLY, \ + 1, \ + _stdio_streams + 2, \ + _fixed_buffers + BUFSIZ, \ + BUFSIZ ), + __STDIO_INIT_FILE_STRUCT(_stdio_streams[2], \ + __FLAG_NBF|__FLAG_WRITEONLY, \ + 2, \ + NULL, \ + NULL, \ + 0 ) +}; + +FILE *stdin = _stdio_streams; +FILE *stdout = _stdio_streams + 1; +FILE *stderr = _stdio_streams + 2; + +#ifdef __STDIO_GETC_MACRO +FILE *__stdin = _stdio_streams; /* For getchar() macro. */ +#endif +#ifdef __STDIO_PUTC_MACRO +FILE *__stdout = _stdio_streams + 1; /* For putchar() macro. */ +/* FILE *__stderr = _stdio_streams + 2; */ +#endif + +/**********************************************************************/ +#ifdef __STDIO_HAS_OPENLIST + +/* In certain configurations, we need to keep a list of open files. + * 1) buffering enabled - We need to initialize the buffering mode + * (full or line buffering) of stdin and stdout. We also + * need to flush all write buffers prior to normal termination. + * 2) custom streams - Even if we aren't buffering in the library + * itself, we need to fclose() all custom streams when terminating + * so that any special cleanup is done. + * 3) threads enabled - We need to be able to reset the locking mode + * of all open streams when the threading system is initialized. + */ + +FILE *_stdio_openlist = _stdio_streams; + +# ifdef __UCLIBC_HAS_THREADS__ +pthread_mutex_t _stdio_openlist_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +int _stdio_openlist_delflag = 0; +# endif + +#endif +/**********************************************************************/ +#ifdef __UCLIBC_HAS_THREADS__ + +/* 2 if threading not initialized and 0 otherwise; */ +int _stdio_user_locking = 2; + +void __stdio_init_mutex(pthread_mutex_t *m) +{ + static const pthread_mutex_t __stdio_mutex_initializer + = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + + memcpy(m, &__stdio_mutex_initializer, sizeof(__stdio_mutex_initializer)); +} + +#endif +/**********************************************************************/ + +/* We assume here that we are the only remaining thread. */ +void _stdio_term(void) +{ +#if defined(__STDIO_BUFFERS) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) + register FILE *ptr; + +#ifdef __UCLIBC_HAS_THREADS__ + /* First, make sure the open file list is unlocked. If it was + * locked, then I suppose there is a chance that a pointer in the + * chain might be corrupt due to a partial store. + */ + __stdio_init_mutex(&_stdio_openlist_lock); + + /* Next we need to worry about the streams themselves. If a stream + * is currently locked, then it may be in an invalid state. So we + * 'disable' it in case a custom stream is stacked on top of it. + * Then we reinitialize the locks. + */ + for (ptr = _stdio_openlist ; ptr ; ptr = ptr->__nextopen ) { + if (__STDIO_ALWAYS_THREADTRYLOCK(ptr)) { + /* The stream is already locked, so we don't want to touch it. + * However, if we have custom streams, we can't just close it + * or leave it locked since a custom stream may be stacked + * on top of it. So we do unlock it, while also disabling it. + */ + ptr->__modeflags = (__FLAG_READONLY|__FLAG_WRITEONLY); + __STDIO_STREAM_DISABLE_GETC(ptr); + __STDIO_STREAM_DISABLE_PUTC(ptr); + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(ptr); + } + + ptr->__user_locking = 1; /* Set locking mode to "by caller". */ + __stdio_init_mutex(&ptr->__lock); /* Shouldn't be necessary, but... */ + } +#endif + + /* Finally, flush all writing streams and shut down all custom streams. + * NOTE: We assume that any stacking by custom streams is done on top + * of streams previously allocated, and hence further down the + * list. Otherwise we have no way of knowing the order in which + * to shut them down. + * Remember that freopen() counts as a new allocation here, even + * though the stream is reused. That's because it moves the + * stream to the head of the list. + */ + for (ptr = _stdio_openlist ; ptr ; ptr = ptr->__nextopen ) { +#ifdef __STDIO_BUFFERS + /* Write any pending buffered chars. */ + if (__STDIO_STREAM_IS_WRITING(ptr)) { + __STDIO_COMMIT_WRITE_BUFFER(ptr); + } +#endif +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + /* Actually close all custom streams to perform any special cleanup. */ + if (ptr->__cookie != &ptr->__filedes) { + __CLOSE(ptr); + } +#endif + } + +#endif +} + +void _stdio_init(void) +{ +#ifdef __STDIO_BUFFERS + int old_errno = errno; + /* stdin and stdout uses line buffering when connected to a tty. */ + _stdio_streams[0].__modeflags ^= (1-isatty(0)) * __FLAG_LBF; + _stdio_streams[1].__modeflags ^= (1-isatty(1)) * __FLAG_LBF; + __set_errno(old_errno); +#endif +#ifndef __UCLIBC__ + /* _stdio_term is done automatically when exiting if stdio is used. + * See misc/internals/__uClibc_main.c and and stdlib/atexit.c. */ + atexit(_stdio_term); +#endif /* __UCLIBC__ */ +} + +/**********************************************************************/ + +#if !(__MASK_READING & __FLAG_UNGOT) +#error Assumption violated about __MASK_READING and __FLAG_UNGOT +#endif + +#ifdef __UCLIBC_HAS_THREADS__ +#include <pthread.h> +#endif + +#ifndef NDEBUG + +void _stdio_validate_FILE(const FILE *stream) +{ +#ifdef __UCLIBC_HAS_THREADS__ + assert(((unsigned int)(stream->__user_locking)) <= 2); +#endif + +#warning Define a constant for minimum possible valid __filedes? + assert(stream->__filedes >= -3); + + if (stream->__filedes < 0) { +/* assert((stream->__filedes != -1) */ +/* #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ */ +/* || (stream->__cookie == &stream->__filedes) /\* custom *\/ */ +/* #endif */ +/* ); */ +/* assert((stream->__filedes == -1) || __STDIO_STREAM_IS_FBF(stream)); */ + + assert(!__STDIO_STREAM_IS_FAKE_VSNPRINTF(stream) + || __STDIO_STREAM_IS_NARROW(stream)); + assert(!__STDIO_STREAM_IS_FAKE_VSSCANF(stream) + || __STDIO_STREAM_IS_NARROW(stream)); +#ifdef __STDIO_STREAM_IS_FAKE_VSWPRINTF + assert(!__STDIO_STREAM_IS_FAKE_VSWPRINTF(stream) + || __STDIO_STREAM_IS_WIDE(stream)); +#endif +#ifdef __STDIO_STREAM_IS_FAKE_VSWSCANF + assert(!__STDIO_STREAM_IS_FAKE_VSWSCANF(stream) + || __STDIO_STREAM_IS_WIDE(stream)); +#endif + } + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + if (stream->__cookie != &stream->__filedes) { /* custom */ + assert(stream->__filedes == -1); + } +#endif + + /* Can not be both narrow and wide oriented at the same time. */ + assert(!(__STDIO_STREAM_IS_NARROW(stream) + && __STDIO_STREAM_IS_WIDE(stream))); + + + /* The following impossible case is used to disable a stream. */ + if ((stream->__modeflags & (__FLAG_READONLY|__FLAG_WRITEONLY)) + == (__FLAG_READONLY|__FLAG_WRITEONLY) + ) { + assert(stream->__modeflags == (__FLAG_READONLY|__FLAG_WRITEONLY)); + assert(stream->__filedes == -1); +#ifdef __STDIO_BUFFERS + assert(stream->__bufpos == stream->__bufstart); + assert(stream->__bufread == stream->__bufstart); +# ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ + assert(stream->__bufputc_u == stream->__bufstart); +# endif +# ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ + assert(stream->__bufgetc_u == stream->__bufstart); +# endif +#endif + } + + if (__STDIO_STREAM_IS_READONLY(stream)) { +/* assert(!__STDIO_STREAM_IS_WRITEONLY(stream)); */ + assert(!__STDIO_STREAM_IS_WRITING(stream)); + if (stream->__modeflags & __FLAG_UNGOT) { + assert(((unsigned)(stream->__ungot[1])) <= 1); + assert(!__FEOF_UNLOCKED(stream)); + } + } + + if (__STDIO_STREAM_IS_WRITEONLY(stream)) { +/* assert(!__STDIO_STREAM_IS_READONLY(stream)); */ + assert(!__STDIO_STREAM_IS_READING(stream)); + assert(!(stream->__modeflags & __FLAG_UNGOT)); + } + + if (__STDIO_STREAM_IS_NBF(stream)) { + /* We require that all non buffered streams have no buffer. */ + assert(!__STDIO_STREAM_BUFFER_SIZE(stream)); + } + + assert((stream->__modeflags & __MASK_BUFMODE) <= __FLAG_NBF); + +#ifdef __STDIO_BUFFERS + /* Ensure __bufstart <= __bufpos <= __bufend. */ + assert(stream->__bufpos >= stream->__bufstart); + assert(stream->__bufpos <= stream->__bufend); + /* Ensure __bufstart <= __bufread <= __bufend. */ + assert(stream->__bufread >= stream->__bufstart); + assert(stream->__bufread <= stream->__bufend); +#endif + + /* If EOF, then we must have no buffered readable or ungots. */ + if (__FEOF_UNLOCKED(stream)) { +#ifdef __STDIO_BUFFERS + assert(stream->__bufpos == stream->__bufread); +#endif + assert(!(stream->__modeflags & __FLAG_UNGOT)); + } + + + if (!__STDIO_STREAM_IS_WRITING(stream)) { +#ifdef __STDIO_BUFFERS + /* If not writing, then putc macro must be disabled. */ +# ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ + assert(stream->__bufputc_u == stream->__bufstart); +# endif +#endif + } + + if (!__STDIO_STREAM_IS_READING(stream)) { + /* If not reading, then can not have ungots. */ + assert(!(stream->__modeflags & __FLAG_UNGOT)); +#ifdef __STDIO_BUFFERS + /* Ensure __bufread == __bufstart. */ + assert(stream->__bufread == stream->__bufstart); + /* If not reading, then getc macro must be disabled. */ +# ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ + assert(stream->__bufgetc_u == stream->__bufstart); +# endif +#endif + } + + if (__STDIO_STREAM_IS_READING(stream)) { + assert(!__STDIO_STREAM_IS_WRITING(stream)); +#ifdef __STDIO_BUFFERS + /* Ensure __bufpos <= __bufread. */ + assert(stream->__bufpos <= stream->__bufread); + + /* Ensure __bufgetc_u is valid. */ +# ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ + assert(stream->__bufgetc_u >= stream->__bufstart); + assert(stream->__bufgetc_u <= stream->__bufread); +# endif + +#endif + } + + if (__STDIO_STREAM_IS_WRITING(stream)) { + assert(!__STDIO_STREAM_IS_READING(stream)); +#ifdef __STDIO_BUFFERS +# ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ + assert(stream->__bufputc_u >= stream->__bufstart); + assert(stream->__bufputc_u <= stream->__bufend); +# endif +#endif + } + + /* If have an ungotten char, then getc (and putc) must be disabled. */ + /* Also, wide streams must have the getc/putc macros disabled. */ + if ((stream->__modeflags & __FLAG_UNGOT) + || __STDIO_STREAM_IS_WIDE(stream) + ) { +#ifdef __STDIO_BUFFERS +# ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ + assert(stream->__bufputc_u == stream->__bufstart); +# endif +# ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ + assert(stream->__bufgetc_u == stream->__bufstart); +# endif +#endif + } + + /* TODO -- filepos? ungot_width? filedes? nextopen? */ +} + +#endif diff --git a/libc/stdio/_stdio.h b/libc/stdio/_stdio.h new file mode 100644 index 000000000..aedcc526a --- /dev/null +++ b/libc/stdio/_stdio.h @@ -0,0 +1,436 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#define _GNU_SOURCE + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef __UCLIBC_HAS_WCHAR__ +#include <wchar.h> +#endif + +#ifdef __UCLIBC_HAS_THREADS__ +#include <pthread.h> + +#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) + +#else + +#define __STDIO_THREADLOCK_OPENLIST ((void)0) +#define __STDIO_THREADUNLOCK_OPENLIST ((void)0) + +#endif + +#define __UNDEFINED_OR_NONPORTABLE ((void)0) + +/**********************************************************************/ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + +extern __ssize_t _cs_read(void *cookie, char *buf, size_t bufsize); +extern __ssize_t _cs_write(void *cookie, const char *buf, size_t bufsize); +extern int _cs_seek(void *cookie, __offmax_t *pos, int whence); +extern int _cs_close(void *cookie); + +#define __STDIO_STREAM_RESET_GCS(S) \ + (S)->__cookie = &((S)->__filedes); \ + (S)->__gcs.read = _cs_read; \ + (S)->__gcs.write = _cs_write; \ + (S)->__gcs.seek = _cs_seek; \ + (S)->__gcs.close = _cs_close + + +#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 __SEEK(STREAMPTR,PPOS,WHENCE) \ + ((((STREAMPTR)->__gcs.seek) == NULL) ? -1 : \ + (((STREAMPTR)->__gcs.seek)((STREAMPTR)->__cookie,(PPOS),(WHENCE)))) +#define __CLOSE(STREAMPTR) \ + ((((STREAMPTR)->__gcs.close) == NULL) ? 0 : \ + (((STREAMPTR)->__gcs.close)((STREAMPTR)->__cookie))) + +#else /* __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ */ + +#define __STDIO_STREAM_RESET_GCS(S) ((void)0) + +#define __READ(STREAMPTR,BUF,SIZE) \ + (read((STREAMPTR)->__filedes,(BUF),(SIZE))) +#define __WRITE(STREAMPTR,BUF,SIZE) \ + (write((STREAMPTR)->__filedes,(BUF),(SIZE))) +#define __SEEK(STREAMPTR,PPOS,WHENCE) \ + (__stdio_seek((STREAMPTR),(PPOS),(WHENCE))) +#define __CLOSE(STREAMPTR) \ + (close((STREAMPTR)->__filedes)) + +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ */ + +/**********************************************************************/ +#ifdef __UCLIBC_HAS_WCHAR__ + +#define __STDIO_STREAM_TRANS_TO_WRITE(S,O) __stdio_trans2w_o((S), (O)) +#define __STDIO_STREAM_TRANS_TO_READ(S,O) __stdio_trans2r_o((S), (O)) + +#else + +#define __STDIO_STREAM_TRANS_TO_WRITE(S,O) __stdio_trans2w((S)) +#define __STDIO_STREAM_TRANS_TO_READ(S,O) __stdio_trans2r((S)) + +#endif +/**********************************************************************/ + +#define __STDIO_STREAM_IS_READING(S) ((S)->__modeflags & __MASK_READING) +#define __STDIO_STREAM_IS_WRITING(S) ((S)->__modeflags & __FLAG_WRITING) + +#define __STDIO_STREAM_SET_READING(S) ((S)->__modeflags |= __FLAG_READING) +#define __STDIO_STREAM_SET_WRITING(S) ((S)->__modeflags |= __FLAG_WRITING) + +#define __STDIO_STREAM_IS_READING_OR_READONLY(S) \ + ((S)->__modeflags & (__MASK_READING|__FLAG_READONLY)) + +#define __STDIO_STREAM_IS_WRITING_OR_WRITEONLY(S) \ + ((S)->__modeflags & (__FLAG_WRITING|__FLAG_WRITEONLY)) + +#define __STDIO_STREAM_IS_READONLY(S) ((S)->__modeflags & __FLAG_READONLY) +#define __STDIO_STREAM_IS_WRITEONLY(S) ((S)->__modeflags & __FLAG_WRITEONLY) + + +/**********************************************************************/ +#ifdef __UCLIBC_HAS_WCHAR__ + +#define __STDIO_STREAM_IS_NARROW_WRITING(S) \ + (((S)->__modeflags & (__FLAG_WRITING|__FLAG_NARROW)) \ + == (__FLAG_WRITING|__FLAG_NARROW)) + +#define __STDIO_STREAM_IS_WIDE_WRITING(S) \ + (((S)->__modeflags & (__FLAG_WRITING|__FLAG_WIDE)) \ + == (__FLAG_WRITING|__FLAG_WIDE)) + +#if (__FLAG_NARROW <= __MASK_READING) +#error assumption violated regarding __FLAG_NARROW +#endif + +#define __STDIO_STREAM_IS_NARROW_READING(S) \ + (((S)->__modeflags & (__MASK_READING|__FLAG_NARROW)) > __FLAG_NARROW) + +#define __STDIO_STREAM_IS_WIDE_READING(S) \ + (((S)->__modeflags & (__MASK_READING|__FLAG_WIDE)) > __FLAG_WIDE) + +#define __STDIO_STREAM_IS_NARROW(S) ((S)->__modeflags & __FLAG_NARROW) +#define __STDIO_STREAM_IS_WIDE(S) ((S)->__modeflags & __FLAG_WIDE) + +#define __STDIO_STREAM_SET_NARROW(S) \ + ((void)((S)->__modeflags |= __FLAG_NARROW)) +#define __STDIO_STREAM_SET_WIDE(S) \ + ((void)((S)->__modeflags |= __FLAG_WIDE)) + +#else + +#define __STDIO_STREAM_IS_NARROW_WRITING(S) __STDIO_STREAM_IS_WRITING(S) + +#define __STDIO_STREAM_IS_NARROW_READING(S) __STDIO_STREAM_IS_READING(S) + +#define __STDIO_STREAM_IS_NARROW(S) (1) +#define __STDIO_STREAM_IS_WIDE(S) (0) + +#define __STDIO_STREAM_SET_NARROW(S) ((void)0) +#define __STDIO_STREAM_SET_WIDE(S) ((void)0) + +#endif +/**********************************************************************/ + +#define __STDIO_STREAM_SET_EOF(S) \ + ((void)((S)->__modeflags |= __FLAG_EOF)) +#define __STDIO_STREAM_SET_ERROR(S) \ + ((void)((S)->__modeflags |= __FLAG_ERROR)) + +#define __STDIO_STREAM_CLEAR_EOF(S) \ + ((void)((S)->__modeflags &= ~__FLAG_EOF)) +#define __STDIO_STREAM_CLEAR_ERROR(S) \ + ((void)((S)->__modeflags &= ~__FLAG_ERROR)) + +#define __STDIO_STREAM_CLEAR_READING_AND_UNGOTS(S) \ + ((void)((S)->__modeflags &= ~__MASK_READING)) +#define __STDIO_STREAM_CLEAR_WRITING(S) \ + ((void)((S)->__modeflags &= ~__FLAG_WRITING)) + +#ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ +# define __STDIO_STREAM_DISABLE_GETC(S) \ + ((void)((S)->__bufgetc_u = (S)->__bufstart)) +# define __STDIO_STREAM_ENABLE_GETC(S) \ + ((void)((S)->__bufgetc_u = (S)->__bufread)) +# define __STDIO_STREAM_CAN_USE_BUFFER_GET(S) \ + ((S)->__bufpos < (S)->__bufgetc_u) +#else +# define __STDIO_STREAM_DISABLE_GETC(S) ((void)0) +# define __STDIO_STREAM_ENABLE_GETC(S) ((void)0) +# define __STDIO_STREAM_CAN_USE_BUFFER_GET(S) (0) +#endif + +#ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ +# define __STDIO_STREAM_DISABLE_PUTC(S) \ + ((void)((S)->__bufputc_u = (S)->__bufstart)) +# define __STDIO_STREAM_ENABLE_PUTC(S) \ + ((void)((S)->__bufputc_u = (S)->__bufend)) +# define __STDIO_STREAM_CAN_USE_BUFFER_ADD(S) \ + ((S)->__bufpos < (S)->__bufputc_u) +#else +# define __STDIO_STREAM_DISABLE_PUTC(S) ((void)0) +# define __STDIO_STREAM_ENABLE_PUTC(S) ((void)0) +# define __STDIO_STREAM_CAN_USE_BUFFER_ADD(S) (0) +#endif + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ +#define __STDIO_STREAM_IS_CUSTOM(S) ((S)->__cookie != &((S)->__filedes)) +#else +#define __STDIO_STREAM_IS_CUSTOM(S) (0) +#endif + +/**********************************************************************/ + +#ifdef __STDIO_BUFFERS +#define __STDIO_STREAM_FREE_BUFFER(S) \ + do { if ((S)->__modeflags & __FLAG_FREEBUF) free((S)->__bufstart); } while (0) +#else +#define __STDIO_STREAM_FREE_BUFFER(S) ((void)0) +#endif + +#define __STDIO_STREAM_FREE_FILE(S) \ + do { if ((S)->__modeflags & __FLAG_FREEFILE) free((S)); } while (0) + + +#ifdef __UCLIBC_HAS_LFS__ +#define __STDIO_WHEN_LFS(E) E +#else +#define __STDIO_WHEN_LFS(E) ((void)0) +#endif + +/**********************************************************************/ +/* The following return 0 on success. */ + +#ifdef __STDIO_BUFFERS +/* Assume stream in valid writing state. Do not reset writing flag + * or disble putc macro unless error. */ +/* Should we assume that buffer is not empty to avoid a check? */ +extern size_t __stdio_wcommit(FILE *__restrict stream); + +/* Remember to fail if at EOF! */ +extern size_t __stdio_rfill(FILE *__restrict stream); + +extern size_t __stdio_fwrite(const unsigned char *__restrict buffer, + size_t bytes, FILE *__restrict stream); +#else + +#define __stdio_fwrite(B,N,S) __stdio_WRITE((S),(B),(N)) + +#endif + +extern size_t __stdio_WRITE(FILE *stream, const unsigned char *buf, + size_t bufsize); +extern size_t __stdio_READ(FILE *stream, unsigned char *buf, size_t bufsize); + +extern int __stdio_trans2r(FILE *__restrict stream); +extern int __stdio_trans2w(FILE *__restrict stream); + +extern int __stdio_trans2r_o(FILE *__restrict stream, int oflag); +extern int __stdio_trans2w_o(FILE *__restrict stream, int oflag); + +/**********************************************************************/ +#ifdef __STDIO_BUFFERS + +#define __STDIO_STREAM_IS_FBF(S) (!((S)->__modeflags & __MASK_BUFMODE)) +#define __STDIO_STREAM_IS_LBF(S) ((S)->__modeflags & __FLAG_LBF) +#define __STDIO_STREAM_IS_NBF(S) ((S)->__modeflags & __FLAG_NBF) + +#define __STDIO_STREAM_BUFFER_SIZE(S) ((S)->__bufend - (S)->__bufstart) + +/* Valid when writing... */ +#define __STDIO_STREAM_BUFFER_ADD(S,C) (*(S)->__bufpos++ = (C)) +#define __STDIO_STREAM_BUFFER_UNADD(S) (--(S)->__bufpos) +#define __STDIO_STREAM_BUFFER_WAVAIL(S) ((S)->__bufend - (S)->__bufpos) +#define __STDIO_STREAM_BUFFER_WUSED(S) ((S)->__bufpos - (S)->__bufstart) +#define __STDIO_COMMIT_WRITE_BUFFER(S) __stdio_wcommit((S)) +#ifdef __UCLIBC_HAS_WCHAR__ +#define __STDIO_STREAM_IS_NARROW_FBF(S) \ + (!((S)->__modeflags & (__MASK_BUFMODE|__FLAG_WIDE))) +#else +#define __STDIO_STREAM_IS_NARROW_FBF(S) __STDIO_STREAM_IS_FBF((S)) +#endif + +/* Valid when reading... */ +#define __STDIO_STREAM_BUFFER_RAVAIL(S) ((S)->__bufread - (S)->__bufpos) +#define __STDIO_STREAM_BUFFER_GET(S) (*(S)->__bufpos++) +#define __STDIO_FILL_READ_BUFFER(S) __stdio_rfill((S)) + +#define __STDIO_STREAM_INIT_BUFREAD_BUFPOS(S) \ + (S)->__bufread = (S)->__bufpos = (S)->__bufstart + + +#define __STDIO_STREAM_FAKE_VSNPRINTF_FILEDES (-2) +#define __STDIO_STREAM_FAKE_VSSCANF_FILEDES (-2) +#define __STDIO_STREAM_FAKE_VSWPRINTF_FILEDES (-3) +#define __STDIO_STREAM_FAKE_VSWSCANF_FILEDES (-3) + +#define __STDIO_STREAM_IS_FAKE_VSNPRINTF(S) \ + ((S)->__filedes == __STDIO_STREAM_FAKE_VSNPRINTF_FILEDES) +#define __STDIO_STREAM_IS_FAKE_VSSCANF(S) \ + ((S)->__filedes == __STDIO_STREAM_FAKE_VSSCANF_FILEDES) +#define __STDIO_STREAM_IS_FAKE_VSWPRINTF(S) \ + ((S)->__filedes == __STDIO_STREAM_FAKE_VSWPRINTF_FILEDES) +#define __STDIO_STREAM_IS_FAKE_VSWSCANF(S) \ + ((S)->__filedes == __STDIO_STREAM_FAKE_VSWSCANF_FILEDES) + +#else /* __STDIO_BUFFERS */ + +#define __STDIO_STREAM_IS_FBF(S) (0) +#define __STDIO_STREAM_IS_LBF(S) (0) +#define __STDIO_STREAM_IS_NBF(S) (1) + +#define __STDIO_STREAM_BUFFER_SIZE(S) (0) +#define __STDIO_STREAM_BUFFER_ADD(S,C) ((void)0) +#define __STDIO_STREAM_BUFFER_UNADD(S) ((void)0) +#define __STDIO_STREAM_BUFFER_WAVAIL(S) (0) +#define __STDIO_STREAM_BUFFER_WUSED(S) (0) +#define __STDIO_COMMIT_WRITE_BUFFER(S) (0) +#define __STDIO_STREAM_IS_NARROW_FBF(S) (0) + +#define __STDIO_STREAM_BUFFER_RAVAIL(S) (0) +#define __STDIO_STREAM_BUFFER_GET(S) (EOF) +#define __STDIO_FILL_READ_BUFFER(S) (0) +#define __STDIO_STREAM_INIT_BUFREAD_BUFPOS(S) ((void)0) + +#undef __STDIO_STREAM_FAKE_VSNPRINTF_FILEDES +#undef __STDIO_STREAM_FAKE_VSSCANF_FILEDES +#undef __STDIO_STREAM_FAKE_VSWPRINTF_FILEDES + +#define __STDIO_STREAM_IS_FAKE_VSNPRINTF(S) (0) +#define __STDIO_STREAM_IS_FAKE_VSSCANF(S) (0) +#undef __STDIO_STREAM_IS_FAKE_VSWPRINTF + +# ifdef __USE_OLD_VFPRINTF__ +# define __STDIO_STREAM_FAKE_VSNPRINTF_FILEDES_NB (-2) +# define __STDIO_STREAM_IS_FAKE_VSNPRINTF_NB(S) \ + ((S)->__filedes == __STDIO_STREAM_FAKE_VSNPRINTF_FILEDES_NB) +# endif + +# ifndef __UCLIBC_HAS_WCHAR__ +# define __STDIO_STREAM_FAKE_VSSCANF_FILEDES_NB (-2) +# define __STDIO_STREAM_IS_FAKE_VSSCANF_NB(S) \ + ((S)->__filedes == __STDIO_STREAM_FAKE_VSSCANF_FILEDES_NB) +# endif + +#endif /* __STDIO_BUFFERS */ +/**********************************************************************/ + +extern int __fputs_unlocked(const char *__restrict s, FILE *__restrict stream); + +extern int __putchar_unlocked(int c); + + +extern size_t __fwrite_unlocked(const void *__restrict ptr, size_t size, + size_t nmemb, FILE *__restrict stream); + +extern size_t __fread_unlocked(void *__restrict ptr, size_t size, + size_t nmemb, FILE *__restrict stream); + +extern int __fputc_unlocked(int c, FILE *stream); + +extern int __fflush_unlocked(FILE *stream); + +extern int __stdio_adjust_position(FILE *__restrict stream, __offmax_t *pos); + +extern void __clearerr_unlocked(FILE *stream); +extern int __feof_unlocked(FILE *stream); +extern int __ferror_unlocked(FILE *stream); + +extern int __fgetc_unlocked(FILE *stream); +extern char *__fgets_unlocked(char *__restrict s, int n, + FILE * __restrict stream); + +extern int __fileno_unlocked(FILE *stream); + +extern int __getchar_unlocked(void); + +#ifdef __UCLIBC_HAS_LFS__ +extern int __fseeko64(FILE *stream, __off64_t offset, int whence); +extern __off64_t __ftello64(FILE *stream); +#endif + +#ifdef __STDIO_HAS_OPENLIST + /* Uses an implementation hack!!! */ +#define __STDIO_FLUSH_LBF_STREAMS __fflush_unlocked((FILE *) &_stdio_openlist) +#else +#define __STDIO_FLUSH_LBF_STREAMS ((void)0) +#endif + +#ifdef NDEBUG +#define __STDIO_STREAM_VALIDATE(S) ((void)0) +#else +extern void _stdio_validate_FILE(const FILE *stream); +#define __STDIO_STREAM_VALIDATE(S) _stdio_validate_FILE((S)) +#endif + +#ifdef __STDIO_MBSTATE +#define __COPY_MBSTATE(dest,src) \ + ((void)((dest)->__mask = (src)->__mask, (dest)->__wc = (src)->__wc)) +#define __INIT_MBSTATE(dest) ((void)((dest)->__mask = 0)) +#else +#define __COPY_MBSTATE(dest,src) ((void)0) +#define __INIT_MBSTATE(dest) ((void)0) +#endif + +/**********************************************************************/ + +extern int _stdio_adjpos(FILE *__restrict stream, __offmax_t * pos); +extern int _stdio_lseek(FILE *stream, __offmax_t *pos, int whence); + +extern size_t _stdio_fwrite(const unsigned char *buffer, size_t bytes, + FILE *stream); +extern size_t _stdio_fread(unsigned char *buffer, size_t bytes, + FILE *stream); + +extern FILE *_stdio_fopen(intptr_t fname_or_mode, + const char *__restrict mode, + FILE *__restrict stream, int filedes); + +#ifdef __UCLIBC_HAS_WCHAR__ +extern size_t _wstdio_fwrite(const wchar_t *__restrict ws, size_t n, + FILE *__restrict stream); + +extern wint_t __fgetwc_unlocked(register FILE *stream); +extern wint_t __fputwc_unlocked(wchar_t wc, FILE *stream); +#endif + +/**********************************************************************/ +/* Only use the macro below if you know fp is a valid FILE for a valid fd. + * This is _not_ true for custom streams! */ +#define __FILENO_UNLOCKED(fp) ((fp)->__filedes) + +#define __FEOF_OR_FERROR_UNLOCKED(stream) \ + ((stream)->__modeflags & (__FLAG_EOF|__FLAG_ERROR)) + +#if defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) +#define __STDIO_HAS_VSNPRINTF 1 +#endif diff --git a/libc/stdio/_store_inttype.c b/libc/stdio/_store_inttype.c new file mode 100644 index 000000000..228e90619 --- /dev/null +++ b/libc/stdio/_store_inttype.c @@ -0,0 +1,57 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <printf.h> + +/* Right now, we assume intmax_t is either long or long long */ + +#ifdef INTMAX_MAX + +#ifdef LLONG_MAX + +#if INTMAX_MAX > LLONG_MAX +#error INTMAX_MAX > LLONG_MAX! The printf code needs to be updated! +#endif + +#elif INTMAX_MAX > LONG_MAX + +#error No LLONG_MAX and INTMAX_MAX > LONG_MAX! The printf code needs to be updated! + +#endif /* LLONG_MAX */ + +#endif /* INTMAX_MAX */ + +/* We assume int may be short or long, but short and long are different. */ + +void _store_inttype(register void *dest, int desttype, uintmax_t val) +{ + if (desttype == __PA_FLAG_CHAR) { /* assume char not int */ + *((unsigned char *) dest) = val; + return; + } +#if defined(LLONG_MAX) && (LONG_MAX != LLONG_MAX) + if (desttype == PA_FLAG_LONG_LONG) { + *((unsigned long long int *) dest) = val; + return; + } +#endif /* LLONG_MAX */ +#if SHRT_MAX != INT_MAX + if (desttype == PA_FLAG_SHORT) { + *((unsigned short int *) dest) = val; + return; + } +#endif /* SHRT_MAX */ +#if LONG_MAX != INT_MAX + if (desttype == PA_FLAG_LONG) { + *((unsigned long int *) dest) = val; + return; + } +#endif /* LONG_MAX */ + + *((unsigned int *) dest) = val; +} diff --git a/libc/stdio/_trans2r.c b/libc/stdio/_trans2r.c new file mode 100644 index 000000000..d33d9eba8 --- /dev/null +++ b/libc/stdio/_trans2r.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* Function to handle transition to reading. + * Initialize or verify the stream's orientation (even if writeonly). + * Check that the stream is readable. + * If currently reading, check that we can transition to writing. + * C99 requires that we not be reading, but attempting to + * auto-transition by commiting the write buffer is a configurable + * option. + * Returns 0 on success and EOF otherwise. + * + * Notes: + * There are two function signatures, depending on wchar support, + * since with no wchar support the orientation is narrow by default. + */ + +#ifdef __UCLIBC_HAS_WCHAR__ +int __stdio_trans2r_o(FILE * __restrict stream, int oflag) +#else +int __stdio_trans2r(FILE * __restrict stream) +#endif +{ + __STDIO_STREAM_VALIDATE(stream); + assert(!__STDIO_STREAM_IS_READING(stream)); + +#ifdef __UCLIBC_HAS_WCHAR__ + if (!(stream->__modeflags & oflag)) { + if (stream->__modeflags & (__FLAG_NARROW|__FLAG_WIDE)) { + __UNDEFINED_OR_NONPORTABLE; + goto DO_EBADF; + } + stream->__modeflags |= oflag; + } +#endif + + if (stream->__modeflags & __FLAG_WRITEONLY) { + DO_EBADF: + __set_errno(EBADF); + ERROR: + __STDIO_STREAM_SET_ERROR(stream); + __STDIO_STREAM_VALIDATE(stream); + return EOF; + } + + if (__STDIO_STREAM_IS_WRITING(stream)) { +#ifdef __UCLIBC_HAS_STDIO_AUTO_RW_TRANSITION__ + if (__STDIO_COMMIT_WRITE_BUFFER(stream)) { /* commit failed! */ + goto ERROR; + } + assert(!__STDIO_STREAM_BUFFER_WUSED(stream)); + + __STDIO_STREAM_DISABLE_PUTC(stream); + __STDIO_STREAM_CLEAR_WRITING(stream); +#else + /* C99: Output shall not be directly followed by input without an + intervening call to the fflush function or to a file positioning + function (fseek, fsetpos, or rewind). */ + __UNDEFINED_OR_NONPORTABLE; + goto DO_EBADF; +#endif + } + + __STDIO_STREAM_SET_READING(stream); + /* getc macro is enabled when data is read into buffer. */ + + __STDIO_STREAM_VALIDATE(stream); + return 0; +} diff --git a/libc/stdio/_trans2w.c b/libc/stdio/_trans2w.c new file mode 100644 index 000000000..fdde2bebf --- /dev/null +++ b/libc/stdio/_trans2w.c @@ -0,0 +1,89 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* Function to handle transition to writing. + * Initialize or verify the stream's orientation (even if readonly). + * Check that the stream is writable. + * If currently reading, check that we can transition to writing. + * C99 requires that the stream is at EOF, but attempting to + * auto-transition via fseek() is a configurable option. + * Returns 0 on success and EOF otherwise. + * + * Notes: + * There are two function signatures, depending on wchar support, + * since with no wchar support the orientation is narrow by default. + */ + +#ifdef __UCLIBC_HAS_WCHAR__ +int __stdio_trans2w_o(FILE * __restrict stream, int oflag) +#else +int __stdio_trans2w(FILE * __restrict stream) +#endif +{ + __STDIO_STREAM_VALIDATE(stream); + assert(!__STDIO_STREAM_IS_WRITING(stream)); + +#ifdef __UCLIBC_HAS_WCHAR__ + if (!(stream->__modeflags & oflag)) { + if (stream->__modeflags & (__FLAG_NARROW|__FLAG_WIDE)) { + __UNDEFINED_OR_NONPORTABLE; + goto DO_EBADF; + } + stream->__modeflags |= oflag; + } +#endif + + if (stream->__modeflags & __FLAG_READONLY) { + DO_EBADF: + __set_errno(EBADF); + ERROR: + __STDIO_STREAM_SET_ERROR(stream); + __STDIO_STREAM_VALIDATE(stream); + return EOF; + } + + if (__STDIO_STREAM_IS_READING(stream)) { + if (!__FEOF_UNLOCKED(stream)) { +#ifdef __UCLIBC_HAS_STDIO_AUTO_RW_TRANSITION__ + /* Need to seek to correct position if we have buffered + * read data or ungots. If appending, we might as well + * seek to the end. + * + * NOTE: If the OS does not handle append files correctly, + * this is insufficient since we would need to seek to + * the end even if not reading.*/ + if (((__STDIO_STREAM_BUFFER_RAVAIL(stream)) + || (stream->__modeflags & __FLAG_UNGOT)) + && fseek(stream, 0L, + ((stream->__modeflags & __FLAG_APPEND) + ? SEEK_END : SEEK_CUR)) + ) { + /* fseek() only sets error indicator on read/write error. */ + goto ERROR; + } +#else + /* C99 requires either at EOF or currently not reading. */ + __UNDEFINED_OR_NONPORTABLE; + goto DO_EBADF; +#endif + } + __STDIO_STREAM_CLEAR_READING_AND_UNGOTS(stream); + __STDIO_STREAM_DISABLE_GETC(stream); + /* Reaching EOF does not reset buffer pointers... */ + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream); + } + + __STDIO_STREAM_SET_WRITING(stream); + if (__STDIO_STREAM_IS_NARROW_FBF(stream)) { + __STDIO_STREAM_ENABLE_PUTC(stream); + } + + __STDIO_STREAM_VALIDATE(stream); + return 0; +} diff --git a/libc/stdio/_uintmaxtostr.c b/libc/stdio/_uintmaxtostr.c new file mode 100644 index 000000000..858a39118 --- /dev/null +++ b/libc/stdio/_uintmaxtostr.c @@ -0,0 +1,151 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#define _ISOC99_SOURCE /* for ULLONG primarily... */ +#include "_stdio.h" +#include <limits.h> +#include <locale.h> +#include <bits/uClibc_uintmaxtostr.h> + +/* Avoid using long long / and % operations to cut down dependencies on + * libgcc.a. Definitely helps on i386 at least. */ +#if (INTMAX_MAX > INT_MAX) && (((INTMAX_MAX/INT_MAX)/2) - 2 <= INT_MAX) +#define INTERNAL_DIV_MOD +#endif + +char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval, + int base, __UIM_CASE alphacase) +{ + int negative; + unsigned int digit; +#ifdef INTERNAL_DIV_MOD + unsigned int H, L, high, low, rh; +#endif +#ifndef __LOCALE_C_ONLY + int grouping, outdigit; + const char *g; /* This does not need to be initialized. */ +#endif /* __LOCALE_C_ONLY */ + + negative = 0; + if (base < 0) { /* signed value */ + base = -base; + if (uval > INTMAX_MAX) { + uval = -uval; + negative = 1; + } + } + + /* this is an internal routine -- we shouldn't need to check this */ + assert(!((base < 2) || (base > 36))); + +#ifndef __LOCALE_C_ONLY + grouping = -1; + outdigit = 0x80 & alphacase; + alphacase ^= outdigit; + if (alphacase == __UIM_GROUP) { + assert(base == 10); + if (*(g = __UCLIBC_CURLOCALE_DATA.grouping)) { + grouping = *g; + } + } +#endif /* __LOCALE_C_ONLY */ + + *bufend = '\0'; + +#ifndef INTERNAL_DIV_MOD + do { +#ifndef __LOCALE_C_ONLY + if (!grouping) { /* Finished a group. */ + bufend -= __UCLIBC_CURLOCALE_DATA.thousands_sep_len; + memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep, + __UCLIBC_CURLOCALE_DATA.thousands_sep_len); + if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */ + /* Note: g[1] == -1 means no further grouping. But since + * we'll never wrap around, we can set grouping to -1 without + * fear of */ + ++g; + } + grouping = *g; + } + --grouping; +#endif /* __LOCALE_C_ONLY */ + digit = uval % base; + uval /= base; + +#ifndef __LOCALE_C_ONLY + if (unlikely(outdigit)) { + bufend -= __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]; + memcpy(bufend, + (&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit], + __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]); + } else +#endif + { + *--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase ); + } + } while (uval); + +#else /* ************************************************** */ + + H = (UINT_MAX / base); + L = UINT_MAX % base + 1; + if (L == base) { + ++H; + L = 0; + } + low = (unsigned int) uval; + high = (unsigned int) (uval >> (sizeof(unsigned int) * CHAR_BIT)); + + do { +#ifndef __LOCALE_C_ONLY + if (!grouping) { /* Finished a group. */ + bufend -= __UCLIBC_CURLOCALE_DATA.thousands_sep_len; + memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep, + __UCLIBC_CURLOCALE_DATA.thousands_sep_len); + if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */ + /* Note: g[1] == -1 means no further grouping. But since + * we'll never wrap around, we can set grouping to -1 without + * fear of */ + ++g; + } + grouping = *g; + } + --grouping; +#endif /* __LOCALE_C_ONLY */ + + if (unlikely(high)) { + rh = high % base; + high /= base; + digit = (low % base) + (L * rh); + low = (low / base) + (H * rh) + (digit / base); + digit %= base; + } else { + digit = low % base; + low /= base; + } + +#ifndef __LOCALE_C_ONLY + if (unlikely(outdigit)) { + bufend -= __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]; + memcpy(bufend, + (&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit], + __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]); + } else +#endif + { + *--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase ); + } + } while (low | high); + +#endif /******************************************************/ + + if (negative) { + *--bufend = '-'; + } + + return bufend; +} diff --git a/libc/stdio/_wcommit.c b/libc/stdio/_wcommit.c new file mode 100644 index 000000000..71720e83f --- /dev/null +++ b/libc/stdio/_wcommit.c @@ -0,0 +1,31 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __STDIO_BUFFERS + +/* Commit the data in the write buffer. + * Returns 0 on success, >0 (pending) on failure. + * Side effects are those of _stdio_WRITE + */ + +size_t __stdio_wcommit(register FILE * __restrict stream) +{ + size_t bufsize; + + __STDIO_STREAM_VALIDATE(stream); + + if ((bufsize = __STDIO_STREAM_BUFFER_WUSED(stream)) != 0) { + stream->__bufpos = stream->__bufstart; + __stdio_WRITE(stream, stream->__bufstart, bufsize); + } + + return __STDIO_STREAM_BUFFER_WUSED(stream); +} + +#endif diff --git a/libc/stdio/_wfwrite.c b/libc/stdio/_wfwrite.c new file mode 100644 index 000000000..8f9469162 --- /dev/null +++ b/libc/stdio/_wfwrite.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <wchar.h> + +#ifndef __UCLIBC_HAS_WCHAR__ +#error wide function when no wide support! +#endif + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Fix prototype. +#endif +extern size_t __wcsnrtombs(char *__restrict dst, + const wchar_t **__restrict src, + size_t NWC, size_t len, mbstate_t *__restrict ps); + +size_t _wstdio_fwrite(const wchar_t *__restrict ws, size_t n, + register FILE *__restrict stream) +{ + size_t r, count; + char buf[64]; + const wchar_t *pw; + + __STDIO_STREAM_VALIDATE(stream); + +#ifdef __STDIO_BUFFERS + if (__STDIO_STREAM_IS_FAKE_VSWPRINTF(stream)) { + /* We know buffer is wchar aligned for fake streams. */ + count = (((wchar_t *)(stream->__bufend)) + - ((wchar_t *)(stream->__bufpos))); + if (count > n) { + count = n; + } + if (count) { + wmemcpy((wchar_t *)(stream->__bufpos), ws, count); + stream->__bufpos = (char *)(((wchar_t *)(stream->__bufpos)) + count); + } + __STDIO_STREAM_VALIDATE(stream); + return n; + } +#endif + + count = 0; + + if (__STDIO_STREAM_IS_WIDE_WRITING(stream) + || !__STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_WIDE) + ) { + + pw = ws; + while (n > count) { + r = __wcsnrtombs(buf, &pw, n-count, sizeof(buf), &stream->__state); + if (r != ((size_t) -1)) { /* No encoding errors */ + if (!r) { + ++r; /* 0 is returned when nul is reached. */ + pw = ws + count + r; /* pw was set to NULL, so correct. */ + } + if (__stdio_fwrite(buf, r, stream) == r) { + count = pw - ws; + continue; + } + } + break; + } + + /* Note: The count is incorrect if 0 < __stdio_fwrite return < r!!! */ + } + + __STDIO_STREAM_VALIDATE(stream); + return count; +} diff --git a/libc/stdio/asprintf.c b/libc/stdio/asprintf.c new file mode 100644 index 000000000..54998fa1d --- /dev/null +++ b/libc/stdio/asprintf.c @@ -0,0 +1,29 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +#ifndef __STDIO_HAS_VSNPRINTF +#warning Skipping asprintf and __asprintf since no vsnprintf! +#else + +weak_alias(__asprintf,asprintf) + +int __asprintf(char **__restrict buf, const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vasprintf(buf, format, arg); + va_end(arg); + + return rv; +} + +#endif diff --git a/libc/stdio/clearerr.c b/libc/stdio/clearerr.c new file mode 100644 index 000000000..177088a77 --- /dev/null +++ b/libc/stdio/clearerr.c @@ -0,0 +1,39 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_UNLOCKED + +weak_alias(__clearerr_unlocked,clearerr_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__clearerr_unlocked,clearerr); +#endif + +void __clearerr_unlocked(register FILE *stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + __CLEARERR_UNLOCKED(stream); +} + +#elif defined __UCLIBC_HAS_THREADS__ + +void clearerr(register FILE *stream) +{ + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + __STDIO_STREAM_VALIDATE(stream); + + __CLEARERR_UNLOCKED(stream); + + __STDIO_AUTO_THREADUNLOCK(stream); +} + +#endif diff --git a/libc/stdio/ctermid.c b/libc/stdio/ctermid.c index 004b8fecf..118608780 100644 --- a/libc/stdio/ctermid.c +++ b/libc/stdio/ctermid.c @@ -1,36 +1,26 @@ -/* Copyright (C) 1991, 1997 Free Software Foundation, Inc. - This file is part of the GNU C Library. +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. +#include "_stdio.h" - The GNU C 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -#include <stdio.h> -#include <string.h> - - -/* Return the name of the controlling terminal. - If S is not NULL, the name is copied into it (it should be at - least L_ctermid bytes long), otherwise a static buffer is used. */ -char * -ctermid (s) - char *s; +char *ctermid(register char *s) { - static char name[L_ctermid]; - - if (s == NULL) - s = name; - - return strcpy (s, "/dev/tty"); + 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 } diff --git a/libc/stdio/dprintf.c b/libc/stdio/dprintf.c new file mode 100644 index 000000000..dfdd4977d --- /dev/null +++ b/libc/stdio/dprintf.c @@ -0,0 +1,21 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +int dprintf(int filedes, const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vdprintf(filedes, format, arg); + va_end(arg); + + return rv; +} diff --git a/libc/stdio/fclose.c b/libc/stdio/fclose.c new file mode 100644 index 000000000..4df2e4229 --- /dev/null +++ b/libc/stdio/fclose.c @@ -0,0 +1,86 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +int fclose(register FILE *stream) +{ + int rv = 0; + __STDIO_AUTO_THREADLOCK_VAR; + + /* First, remove the file from the open file list. */ +#ifdef __STDIO_HAS_OPENLIST + { + register FILE *ptr; + + __STDIO_THREADLOCK_OPENLIST; + if ((ptr = _stdio_openlist) == stream) { + _stdio_openlist = stream->__nextopen; + } else { + while (ptr) { + if (ptr->__nextopen == stream) { + ptr->__nextopen = stream->__nextopen; + break; + } + ptr = ptr->__nextopen; + } + } + __STDIO_THREADUNLOCK_OPENLIST; + + if (!ptr) { /* Did not find stream in the open file list! */ + return EOF; + } + } +#endif + + __STDIO_AUTO_THREADLOCK(stream); + + __STDIO_STREAM_VALIDATE(stream); + +#ifdef __STDIO_BUFFERS + /* Write any pending buffered chars. */ + if (__STDIO_STREAM_IS_WRITING(stream)) { + rv = __fflush_unlocked(stream); + } +#endif + + if (__CLOSE(stream) < 0) { /* Must close even if fflush failed. */ + rv = EOF; + } + + stream->__filedes = -1; + + /* We need a way for freopen to know that a file has been closed. + * Since a file can't be both readonly and writeonly, that makes + * an effective signal. It also has the benefit of disabling + * transitions to either reading or writing. */ + stream->__modeflags &= (__FLAG_FREEBUF|__FLAG_FREEFILE); + stream->__modeflags |= (__FLAG_READONLY|__FLAG_WRITEONLY); + +#ifndef NDEBUG + __STDIO_STREAM_RESET_GCS(stream); + + /* Reinitialize everything (including putc since fflush could fail). */ + __STDIO_STREAM_DISABLE_GETC(stream); + __STDIO_STREAM_DISABLE_PUTC(stream); + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream); + +# ifdef __UCLIBC_HAS_WCHAR__ + stream->__ungot_width[0] = 0; +# endif +# ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(stream->__state)); +# endif +#endif + + __STDIO_AUTO_THREADUNLOCK(stream); + + __STDIO_STREAM_FREE_BUFFER(stream); + __STDIO_STREAM_FREE_FILE(stream); + + return rv; +} diff --git a/libc/stdio/fcloseall.c b/libc/stdio/fcloseall.c new file mode 100644 index 000000000..dbb600067 --- /dev/null +++ b/libc/stdio/fcloseall.c @@ -0,0 +1,40 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* 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. + */ + +int fcloseall (void) +{ +#ifdef __STDIO_HAS_OPENLIST + + int retval = 0; + + __STDIO_THREADLOCK_OPENLIST; + while (_stdio_openlist) { + if (fclose(_stdio_openlist)) { + retval = EOF; + } + } + __STDIO_THREADUNLOCK_OPENLIST; + + return retval; + +#else + +#warning Always fails in this configuration because no open file list. + + return EOF; + +#endif +} diff --git a/libc/stdio/fdopen.c b/libc/stdio/fdopen.c new file mode 100644 index 000000000..fa08c976d --- /dev/null +++ b/libc/stdio/fdopen.c @@ -0,0 +1,17 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +FILE *fdopen(int filedes, const char *mode) +{ + intptr_t cur_mode; + + return (((cur_mode = fcntl(filedes, F_GETFL))) != -1) + ? _stdio_fopen(cur_mode, mode, NULL, filedes) + : NULL; +} diff --git a/libc/stdio/feof.c b/libc/stdio/feof.c new file mode 100644 index 000000000..e98e7e1c2 --- /dev/null +++ b/libc/stdio/feof.c @@ -0,0 +1,42 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_UNLOCKED + +weak_alias(__feof_unlocked,feof_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__feof_unlocked,feof); +#endif + +int __feof_unlocked(register FILE *stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return __FEOF_UNLOCKED(stream); +} + +#elif defined __UCLIBC_HAS_THREADS__ + +int feof(register FILE *stream) +{ + int retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + __STDIO_STREAM_VALIDATE(stream); + + retval = __FEOF_UNLOCKED(stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/ferror.c b/libc/stdio/ferror.c new file mode 100644 index 000000000..3bfaf68ee --- /dev/null +++ b/libc/stdio/ferror.c @@ -0,0 +1,42 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_UNLOCKED + +weak_alias(__ferror_unlocked,ferror_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__ferror_unlocked,ferror); +#endif + +int __ferror_unlocked(register FILE *stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return __FERROR_UNLOCKED(stream); +} + +#elif defined __UCLIBC_HAS_THREADS__ + +int ferror(register FILE *stream) +{ + int retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + __STDIO_STREAM_VALIDATE(stream); + + retval = __FERROR_UNLOCKED(stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/fflush.c b/libc/stdio/fflush.c new file mode 100644 index 000000000..6baa0ec82 --- /dev/null +++ b/libc/stdio/fflush.c @@ -0,0 +1,161 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +extern int __fflush_unlocked(register FILE *stream); + +#ifdef __DO_UNLOCKED + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning WISHLIST: Add option to test for undefined behavior of fflush. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +weak_alias(__fflush_unlocked,fflush_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fflush_unlocked,fflush); +#endif + +#ifdef __UCLIBC_HAS_THREADS__ +/* Even if the stream is set to user-locking, we still need to lock + * when all (lbf) writing streams are flushed. */ +#define MY_STDIO_THREADLOCK(STREAM) \ + if (_stdio_user_locking != 2) { \ + __STDIO_ALWAYS_THREADLOCK(STREAM); \ + } + +#define MY_STDIO_THREADUNLOCK(STREAM) \ + if (_stdio_user_locking != 2) { \ + __STDIO_ALWAYS_THREADUNLOCK(STREAM); \ + } +#else +#define MY_STDIO_THREADLOCK(STREAM) ((void)0) +#define MY_STDIO_THREADUNLOCK(STREAM) ((void)0) +#endif + + +int __fflush_unlocked(register FILE *stream) +{ +#ifdef __STDIO_BUFFERS + + int retval = 0; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning REMINDER: should probably define a modeflags type +#endif + unsigned short bufmask = __FLAG_LBF; + +#ifndef NDEBUG + if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { + __STDIO_STREAM_VALIDATE(stream); /* debugging only */ + } +#endif + + if (stream == (FILE *) &_stdio_openlist) { /* Flush all lbf streams. */ + stream = NULL; + bufmask = 0; + } + + if (!stream) { /* Flush all (lbf) writing streams. */ + __STDIO_THREADLOCK_OPENLIST; + for (stream = _stdio_openlist; stream ; stream = stream->__nextopen) { + MY_STDIO_THREADLOCK(stream); + if (!(((stream->__modeflags | bufmask) + ^ (__FLAG_WRITING|__FLAG_LBF) + ) & (__FLAG_WRITING|__MASK_BUFMODE)) + ) { + if (!__STDIO_COMMIT_WRITE_BUFFER(stream)) { + __STDIO_STREAM_DISABLE_PUTC(stream); + __STDIO_STREAM_CLEAR_WRITING(stream); + } else { + retval = EOF; + } + } + MY_STDIO_THREADUNLOCK(stream); + } + __STDIO_THREADUNLOCK_OPENLIST; + } else if (__STDIO_STREAM_IS_WRITING(stream)) { + if (!__STDIO_COMMIT_WRITE_BUFFER(stream)) { + __STDIO_STREAM_DISABLE_PUTC(stream); + __STDIO_STREAM_CLEAR_WRITING(stream); + } else { + retval = EOF; + } + } +#if 0 + else if (stream->__modeflags & (__MASK_READING|__FLAG_READONLY)) { + /* ANSI/ISO says behavior in this case is undefined but also says you + * shouldn't flush a stream you were reading from. As usual, glibc + * caters to broken programs and simply ignores this. */ + __UNDEFINED_OR_NONPORTABLE; + __STDIO_STREAM_SET_ERROR(stream); + __set_errno(EBADF); + retval = EOF; + } +#endif + +#ifndef NDEBUG + if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { + __STDIO_STREAM_VALIDATE(stream); /* debugging only */ + } +#endif + + return retval; + +#else /* __STDIO_BUFFERS --------------------------------------- */ + +#ifndef NDEBUG + if ((stream != NULL) +#ifdef __STDIO_HAS_OPENLIST + && (stream != (FILE *) &_stdio_openlist) +#endif + ) { + __STDIO_STREAM_VALIDATE(stream); /* debugging only */ + } +#endif + +#if 0 + if (stream && (stream->__modeflags & (__MASK_READING|__FLAG_READONLY))) { + /* ANSI/ISO says behavior in this case is undefined but also says you + * shouldn't flush a stream you were reading from. As usual, glibc + * caters to broken programs and simply ignores this. */ + __UNDEFINED_OR_NONPORTABLE; + __STDIO_STREAM_SET_ERROR(stream); + __set_errno(EBADF); + return EOF; + } +#endif + + return 0; +#endif /* __STDIO_BUFFERS */ +} + +#elif defined __UCLIBC_HAS_THREADS__ + +int fflush(register FILE *stream) +{ + int retval; + __STDIO_AUTO_THREADLOCK_VAR; + + if (stream +#ifdef __STDIO_HAS_OPENLIST + && (stream != (FILE *) &_stdio_openlist) +#endif + ) { + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fflush_unlocked(stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + } else { + retval = __fflush_unlocked(stream); + } + + return retval; +} + +#endif diff --git a/libc/stdio/fgetc.c b/libc/stdio/fgetc.c new file mode 100644 index 000000000..c672d9fe9 --- /dev/null +++ b/libc/stdio/fgetc.c @@ -0,0 +1,98 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#undef fgetc +#undef fgetc_unlocked +#undef getc +#undef getc_unlocked + +extern int __fgetc_unlocked(FILE *stream); + +#ifdef __DO_UNLOCKED + +weak_alias(__fgetc_unlocked,fgetc_unlocked); +weak_alias(__fgetc_unlocked,getc_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fgetc_unlocked,fgetc); +weak_alias(__fgetc_unlocked,getc); +#endif + +int __fgetc_unlocked(FILE *stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + /* First the fast path. We're good to go if getc macro enabled. */ + if (__STDIO_STREAM_CAN_USE_BUFFER_GET(stream)) { + return __STDIO_STREAM_BUFFER_GET(stream); + } + + /* Next quickest... reading and narrow oriented, but macro + * disabled and/or buffer is exhausted. */ + if (__STDIO_STREAM_IS_NARROW_READING(stream) + || !__STDIO_STREAM_TRANS_TO_READ(stream, __FLAG_NARROW) + ) { + if (stream->__modeflags & __FLAG_UNGOT) { /* Use ungots first. */ + unsigned char uc = stream->__ungot[(stream->__modeflags--) & 1]; + stream->__ungot[1] = 0; + __STDIO_STREAM_VALIDATE(stream); + return uc; + } + + if (__STDIO_STREAM_BUFFER_RAVAIL(stream)) { /* Have buffered? */ + return __STDIO_STREAM_BUFFER_GET(stream); + } + + /* Is this a fake stream for *sscanf? */ + if (__STDIO_STREAM_IS_FAKE_VSSCANF(stream)) { + __STDIO_STREAM_SET_EOF(stream); + return EOF; + } + + /* We need to read from the host environment, so we must + * flush all line buffered streams if the stream is not + * fully buffered. */ + if (!__STDIO_STREAM_IS_FBF(stream)) { + __STDIO_FLUSH_LBF_STREAMS; + } + + if (__STDIO_STREAM_BUFFER_SIZE(stream)) { /* Do we have a buffer? */ + __STDIO_STREAM_DISABLE_GETC(stream); + if(__STDIO_FILL_READ_BUFFER(stream)) { /* Refill succeeded? */ + __STDIO_STREAM_ENABLE_GETC(stream); /* FBF or LBF */ + return __STDIO_STREAM_BUFFER_GET(stream); + } + } else { + unsigned char uc; + if (__stdio_READ(stream, &uc, 1)) { + return uc; + } + } + } + + return EOF; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +weak_alias(fgetc,getc); + +int fgetc(register FILE *stream) +{ + if (stream->__user_locking != 0) { + return __GETC_UNLOCKED_MACRO(stream); + } else { + int retval; + __STDIO_ALWAYS_THREADLOCK(stream); + retval = __GETC_UNLOCKED_MACRO(stream); + __STDIO_ALWAYS_THREADUNLOCK(stream); + return retval; + } +} + +#endif diff --git a/libc/stdio/fgetpos.c b/libc/stdio/fgetpos.c new file mode 100644 index 000000000..9c6c26297 --- /dev/null +++ b/libc/stdio/fgetpos.c @@ -0,0 +1,44 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_LARGEFILE +# ifndef __UCLIBC_HAS_LFS__ +# error large file support is not enabled! +# endif + +# define fgetpos fgetpos64 +# define fpos_t fpos64_t +# define ftell ftello64 +#endif + +int fgetpos(FILE * __restrict stream, register fpos_t * __restrict pos) +{ +#ifdef __STDIO_MBSTATE + + int retval = -1; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + if ((pos->__pos = ftell(stream)) >= 0) { + __COPY_MBSTATE(&(pos->__mbstate), &(stream->__state)); + pos->__mblen_pending = stream->__ungot_width[0]; + retval = 0; + } + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; + +#else + + return ((pos->__pos = ftell(stream)) >= 0) ? 0 : -1; + +#endif +} diff --git a/libc/stdio/fgets.c b/libc/stdio/fgets.c new file mode 100644 index 000000000..6bcfc2957 --- /dev/null +++ b/libc/stdio/fgets.c @@ -0,0 +1,84 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_UNLOCKED + +weak_alias(__fgets_unlocked,fgets_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fgets_unlocked,fgets); +#endif + + +char *__fgets_unlocked(char *__restrict s, int n, + register FILE * __restrict stream) +{ + register char *p; + int c; + + __STDIO_STREAM_VALIDATE(stream); + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: What should fgets do if n <= 0? +#endif /* __UCLIBC_MJN3_ONLY__ */ + /* Should we assert here? Or set errno? Or just fail... */ + if (n <= 0) { +/* __set_errno(EINVAL); */ + goto ERROR; + } + + p = s; + + while (--n) { + if (__STDIO_STREAM_CAN_USE_BUFFER_GET(stream)) { + if ((*p++ = __STDIO_STREAM_BUFFER_GET(stream)) == '\n') { + break; + } + } else { + if ((c = __fgetc_unlocked(stream)) == EOF) { + if (__FERROR_UNLOCKED(stream)) { + goto ERROR; + } + break; + } + if ((*p++ = c) == '\n') { + break; + } + } + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: If n==1 and not at EOF, should fgets return an empty string? +#endif /* __UCLIBC_MJN3_ONLY__ */ + if (p > s) { + *p = 0; + return s; + } + + ERROR: + return NULL; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +char *fgets(char *__restrict s, int n, + register FILE * __restrict stream) +{ + char *retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fgets_unlocked(s, n, stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/fgetwc.c b/libc/stdio/fgetwc.c new file mode 100644 index 000000000..9f1f9c481 --- /dev/null +++ b/libc/stdio/fgetwc.c @@ -0,0 +1,134 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_UNLOCKED + +weak_alias(__fgetwc_unlocked,fgetwc_unlocked); +weak_alias(__fgetwc_unlocked,getwc_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fgetwc_unlocked,fgetwc); +weak_alias(__fgetwc_unlocked,getwc); +#endif + +static void munge_stream(register FILE *stream, unsigned char *buf) +{ + stream->__bufend = stream->__bufstart = buf; + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream); + __STDIO_STREAM_DISABLE_GETC(stream); + __STDIO_STREAM_DISABLE_PUTC(stream); +} + +wint_t __fgetwc_unlocked(register FILE *stream) +{ + wint_t wi; + wchar_t wc[1]; + int n; + size_t r; + unsigned char sbuf[1]; + + __STDIO_STREAM_VALIDATE(stream); + + wi = WEOF; /* Prepare for failure. */ + + if (__STDIO_STREAM_IS_WIDE_READING(stream) + || !__STDIO_STREAM_TRANS_TO_READ(stream, __FLAG_WIDE) + ) { + if (stream->__modeflags & __FLAG_UNGOT) { /* Any ungetwc()s? */ + if (((stream->__modeflags & 1) || stream->__ungot[1])) { + stream->__ungot_width[0] = 0; /* Application ungot... */ + } else { /* scanf ungot */ + stream->__ungot_width[0] = stream->__ungot_width[1]; + } + + wi = stream->__ungot[(stream->__modeflags--) & 1]; + stream->__ungot[1] = 0; + goto DONE; + } + + if (!stream->__bufstart) { /* Ugh... stream isn't buffered! */ + /* Munge the stream temporarily to use a 1-byte buffer. */ + munge_stream(stream, sbuf); + ++stream->__bufend; + } + + if (stream->__state.__mask == 0) { /* If last was a complete char */ + stream->__ungot_width[0] = 0; /* then reset the width. */ + } + + LOOP: + if ((n = __STDIO_STREAM_BUFFER_RAVAIL(stream)) == 0) { + goto FILL_BUFFER; + } + + r = mbrtowc(wc, stream->__bufpos, n, &stream->__state); + if (((ssize_t) r) >= 0) { /* Success... */ + if (r == 0) { /* Nul wide char... means 0 byte for us so */ + ++r; /* increment r and handle below as single. */ + } + stream->__bufpos += r; + stream->__ungot_width[0] += r; + wi = *wc; + goto DONE; + } + + if (r == ((size_t) -2)) { + /* Potentially valid but incomplete and no more buffered. */ + stream->__bufpos += n; /* Update bufpos for stream. */ + stream->__ungot_width[0] += n; + FILL_BUFFER: + if(__STDIO_FILL_READ_BUFFER(stream)) { /* Refill succeeded? */ + goto LOOP; + } + if (!__FERROR_UNLOCKED(stream)) { /* EOF with no error. */ + if (!stream->__state.__mask) { /* No partial wchar. */ + goto DONE; + } + /* EOF but partially complete wchar. */ + /* TODO: should EILSEQ be set? */ + __set_errno(EILSEQ); + } + } + + /* If we reach here, either r == ((size_t)-1) and mbrtowc set errno + * to EILSEQ, or r == ((size_t)-2) and stream is in an error state + * or at EOF with a partially complete wchar. Make sure stream's + * error indicator is set. */ + stream->__modeflags |= __FLAG_ERROR; + + DONE: + if (stream->__bufstart == sbuf) { /* Need to un-munge the stream. */ + munge_stream(stream, NULL); + } + + } + + __STDIO_STREAM_VALIDATE(stream); + + return wi; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +weak_alias(fgetwc,getwc); + +wint_t fgetwc(register FILE *stream) +{ + wint_t retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fgetwc_unlocked(stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/fgetws.c b/libc/stdio/fgetws.c new file mode 100644 index 000000000..ec8547e25 --- /dev/null +++ b/libc/stdio/fgetws.c @@ -0,0 +1,61 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +extern wchar_t *__fgetws_unlocked(wchar_t *__restrict ws, int n, + FILE *__restrict stream); + +#ifdef __DO_UNLOCKED + +weak_alias(__fgetws_unlocked,fgetws_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fgetws_unlocked,fgetws); +#endif + +wchar_t *__fgetws_unlocked(wchar_t *__restrict ws, int n, + FILE *__restrict stream) +{ + register wchar_t *p = ws; + wint_t wi; + + __STDIO_STREAM_VALIDATE(stream); + + while ((n > 1) + && ((wi = __fgetwc_unlocked(stream)) != WEOF) + && ((*p++ = wi) != '\n') + ) { + --n; + } + if (p == ws) { + /* TODO -- should we set errno? */ +/* if (n <= 0) { */ +/* errno = EINVAL; */ +/* } */ + return NULL; + } + *p = 0; + return ws; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +wchar_t *fgetws(wchar_t *__restrict ws, int n, FILE *__restrict stream) +{ + wchar_t *retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fgetws_unlocked(ws, n, stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/fileno.c b/libc/stdio/fileno.c new file mode 100644 index 000000000..fbfa66551 --- /dev/null +++ b/libc/stdio/fileno.c @@ -0,0 +1,45 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_UNLOCKED + +weak_alias(__fileno_unlocked,fileno_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fileno_unlocked,fileno); +#endif + +int __fileno_unlocked(register FILE *stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + if ((!__STDIO_STREAM_IS_CUSTOM(stream)) && (stream->__filedes >= 0)) { + return stream->__filedes; + } + + __set_errno(EBADF); + return -1; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +int fileno(register FILE *stream) +{ + int retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fileno_unlocked(stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/flockfile.c b/libc/stdio/flockfile.c new file mode 100644 index 000000000..0dcc7c266 --- /dev/null +++ b/libc/stdio/flockfile.c @@ -0,0 +1,16 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +void flockfile(FILE *stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + __STDIO_ALWAYS_THREADLOCK(stream); +} + diff --git a/libc/stdio/fmemopen.c b/libc/stdio/fmemopen.c new file mode 100644 index 000000000..ff03cb426 --- /dev/null +++ b/libc/stdio/fmemopen.c @@ -0,0 +1,176 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifndef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ +#error no custom streams! +#endif + +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; + } + + memcpy(buf, COOKIE->buf + COOKIE->pos, bufsize); + COOKIE->pos += bufsize; + + 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; + } + } + + 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; + } + } + + 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. */ + } + + __STDIO_STREAM_VALIDATE(fp); + + return fp; + } + } + + if (!s) { + free(cookie->buf); + } + EXIT_cookie: + free(cookie); + + return NULL; +} diff --git a/libc/stdio/fopen.c b/libc/stdio/fopen.c new file mode 100644 index 000000000..8303fa88c --- /dev/null +++ b/libc/stdio/fopen.c @@ -0,0 +1,24 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_LARGEFILE +# ifndef __UCLIBC_HAS_LFS__ +# error large file support is not enabled! +# endif + +# define fopen fopen64 +# define FILEDES_ARG (-2) +#else +# define FILEDES_ARG (-1) +#endif + +FILE *fopen(const char * __restrict filename, const char * __restrict mode) +{ + return _stdio_fopen(((intptr_t) filename), mode, NULL, FILEDES_ARG); +} diff --git a/libc/stdio/fopencookie.c b/libc/stdio/fopencookie.c new file mode 100644 index 000000000..73a5a028a --- /dev/null +++ b/libc/stdio/fopencookie.c @@ -0,0 +1,59 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifndef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ +#error no custom streams! +#endif + +/* 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. */ + +/* 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. + */ + +/* 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) +#else +FILE *_fopencookie(void * __restrict cookie, const char * __restrict mode, + register cookie_io_functions_t *io_functions) +#endif +{ + FILE *stream; + + /* Fake an fdopen guaranteed to pass the _stdio_fopen basic agreement + * check without an fcntl call. */ + stream = _stdio_fopen(((intptr_t)(INT_MAX-1)), mode, NULL, INT_MAX); + if (stream) { + stream->__filedes = -1; +#ifndef __BCC__ + stream->__gcs = io_functions; +#else + stream->__gcs.read = io_functions->read; + stream->__gcs.write = io_functions->write; + stream->__gcs.seek = io_functions->seek; + stream->__gcs.close = io_functions->close; +#endif + stream->__cookie = cookie; + + __STDIO_STREAM_VALIDATE(stream); + } + + return stream; +} diff --git a/libc/stdio/fprintf.c b/libc/stdio/fprintf.c new file mode 100644 index 000000000..388eb0c3b --- /dev/null +++ b/libc/stdio/fprintf.c @@ -0,0 +1,21 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +int fprintf(FILE * __restrict stream, const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vfprintf(stream, format, arg); + va_end(arg); + + return rv; +} diff --git a/libc/stdio/fputc.c b/libc/stdio/fputc.c new file mode 100644 index 000000000..0cfb4f943 --- /dev/null +++ b/libc/stdio/fputc.c @@ -0,0 +1,96 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#undef fputc +#undef fputc_unlocked +#undef putc +#undef putc_unlocked + +#ifdef __DO_UNLOCKED + +weak_alias(__fputc_unlocked,fputc_unlocked); +weak_alias(__fputc_unlocked,putc_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fputc_unlocked,fputc); +weak_alias(__fputc_unlocked,putc); +#endif + +int __fputc_unlocked(int c, register FILE *stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + /* First the fast path. We're good to go if putc macro enabled. */ + if (__STDIO_STREAM_CAN_USE_BUFFER_ADD(stream)) { + __STDIO_STREAM_BUFFER_ADD(stream, ((unsigned char) c)); + return (unsigned char) c; + } + + /* Next quickest... writing and narrow oriented, but macro + * disabled and/or buffer is full. */ + if (__STDIO_STREAM_IS_NARROW_WRITING(stream) + || !__STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW) + ) { + if (__STDIO_STREAM_IS_FAKE_VSNPRINTF(stream)) { + return (unsigned char) c; + } + + if (__STDIO_STREAM_BUFFER_SIZE(stream)) { /* Do we have a buffer? */ + /* The buffer is full and/or the stream is line buffered. */ + if (!__STDIO_STREAM_BUFFER_WAVAIL(stream) /* Buffer full? */ + && __STDIO_COMMIT_WRITE_BUFFER(stream) /* Commit failed! */ + ) { + goto BAD; + } +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Should we fail if the commit fails but we now have room? +#endif + + __STDIO_STREAM_BUFFER_ADD(stream, ((unsigned char) c)); + + if (__STDIO_STREAM_IS_LBF(stream)) { + if ((((unsigned char) c) == '\n') + && __STDIO_COMMIT_WRITE_BUFFER(stream)) { + /* Commit failed! */ + __STDIO_STREAM_BUFFER_UNADD(stream); /* Undo the write! */ + goto BAD; + } + } + } else { + /* NOTE: Do not try to save space by moving uc to the top of + * the file, as that dramaticly increases runtime. */ + unsigned char uc = (unsigned char) c; + if (! __stdio_WRITE(stream, &uc, 1)) { + goto BAD; + } + } + return (unsigned char) c; + } + + BAD: + return EOF; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +weak_alias(fputc,putc); + +int fputc(int c, register FILE *stream) +{ + if (stream->__user_locking != 0) { + return __PUTC_UNLOCKED_MACRO(c, stream); + } else { + int retval; + __STDIO_ALWAYS_THREADLOCK(stream); + retval = __PUTC_UNLOCKED_MACRO(c, stream); + __STDIO_ALWAYS_THREADUNLOCK(stream); + return retval; + } +} + +#endif diff --git a/libc/stdio/fputs.c b/libc/stdio/fputs.c new file mode 100644 index 000000000..b3d77fa46 --- /dev/null +++ b/libc/stdio/fputs.c @@ -0,0 +1,46 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* Note: The standard says fputs returns a nonnegative number on + * success. In this implementation, we return the length of the + * string written on success. + */ + +#ifdef __DO_UNLOCKED + +weak_alias(__fputs_unlocked,fputs_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fputs_unlocked,fputs); +#endif + +int __fputs_unlocked(register const char * __restrict s, + FILE * __restrict stream) +{ + size_t n = strlen(s); + + return ((__fwrite_unlocked(s, 1, n, stream) == n) ? n : EOF); +} + +#elif defined __UCLIBC_HAS_THREADS__ + +int fputs(const char * __restrict s, register FILE * __restrict stream) +{ + int retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fputs_unlocked(s, stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/fputwc.c b/libc/stdio/fputwc.c new file mode 100644 index 000000000..fc980dcef --- /dev/null +++ b/libc/stdio/fputwc.c @@ -0,0 +1,42 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_UNLOCKED + +weak_alias(__fputwc_unlocked,fputwc_unlocked); +weak_alias(__fputwc_unlocked,putwc_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fputwc_unlocked,fputwc); +weak_alias(__fputwc_unlocked,putwc); +#endif + +wint_t __fputwc_unlocked(wchar_t wc, FILE *stream) +{ + return _wstdio_fwrite(&wc, 1, stream) ? wc : WEOF; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +weak_alias(fputwc,putwc); + +wint_t fputwc(wchar_t wc, register FILE *stream) +{ + wint_t retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fputwc_unlocked(wc, stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/fputws.c b/libc/stdio/fputws.c new file mode 100644 index 000000000..7b6456a09 --- /dev/null +++ b/libc/stdio/fputws.c @@ -0,0 +1,44 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +extern int __fputws_unlocked(const wchar_t *__restrict ws, + FILE *__restrict stream); + +#ifdef __DO_UNLOCKED + +weak_alias(__fputws_unlocked,fputws_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fputws_unlocked,fputws); +#endif + +int __fputws_unlocked(const wchar_t *__restrict ws, + register FILE *__restrict stream) +{ + size_t n = wcslen(ws); + + return (_wstdio_fwrite(ws, n, stream) == n) ? 0 : -1; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +int fputws(const wchar_t *__restrict ws, register FILE *__restrict stream) +{ + int retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fputws_unlocked(ws, stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c new file mode 100644 index 000000000..875c82616 --- /dev/null +++ b/libc/stdio/fread.c @@ -0,0 +1,108 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_UNLOCKED + +weak_alias(__fread_unlocked,fread_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fread_unlocked,fread); +#endif + +size_t __fread_unlocked(void * __restrict ptr, size_t size, size_t nmemb, + FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + assert(stream->__filedes >= -1); + + /* Note: If nmbem * size > SIZE_MAX then there is an application + * bug since no array can be larger than SIZE_MAX in size. */ + + if ((__STDIO_STREAM_IS_NARROW_READING(stream) + || !__STDIO_STREAM_TRANS_TO_READ(stream, __FLAG_NARROW)) + && size && nmemb + ) { + + if (nmemb <= (SIZE_MAX / size)) { + unsigned char *buffer = (unsigned char *) ptr; + size_t todo, bytes, avail; + + todo = bytes = size * nmemb; + + /* Check for ungots... */ + while (stream->__modeflags & __FLAG_UNGOT) { + *buffer++ = stream->__ungot[(stream->__modeflags--) & 1]; + stream->__ungot[1] = 0; + if (!--todo) { + goto DONE; + } + } + +#ifdef __STDIO_BUFFERS + /* Next check for available buffered... */ + if ((avail = stream->__bufread - stream->__bufpos) > 0) { + if (avail > todo) { + avail = todo; + } + memcpy(buffer, stream->__bufpos, avail); + buffer += avail; + stream->__bufpos += avail; + if (!(todo -= avail)) { + goto DONE; + } + } + + /* We need to read from the host environment, so we must + * flush all line buffered streams if the stream is not + * fully buffered. */ + if (!__STDIO_STREAM_IS_FBF(stream)) { + __STDIO_FLUSH_LBF_STREAMS; + } +#endif + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: should we refill and read from the buffer sometimes? +#endif + while ((avail = __stdio_READ(stream, buffer, todo)) > 0) { + buffer += avail; + if (!(todo -= avail)) { + break; + } + } + + DONE: + __STDIO_STREAM_VALIDATE(stream); + return (bytes - todo) / size; + } + + __STDIO_STREAM_SET_ERROR(stream); + __set_errno(EINVAL); + } + + __STDIO_STREAM_VALIDATE(stream); + return 0; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +size_t fread(void * __restrict ptr, size_t size, size_t nmemb, + register FILE * __restrict stream) +{ + size_t retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fread_unlocked(ptr, size, nmemb, stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/freopen.c b/libc/stdio/freopen.c new file mode 100644 index 000000000..0eccaac1f --- /dev/null +++ b/libc/stdio/freopen.c @@ -0,0 +1,66 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_LARGEFILE +# ifndef __UCLIBC_HAS_LFS__ +# error large file support is not enabled! +# endif + +# define freopen freopen64 +# define FILEDES_ARG (-2) +#else +# define FILEDES_ARG (-1) +#endif + +FILE *freopen(const char * __restrict filename, const char * __restrict mode, + register FILE * __restrict stream) +{ + /* + * ANSI/ISO allow (implementation-defined) change of mode for an + * existing file if filename is NULL. It doesn't look like Linux + * supports this, so we don't here. + * + * NOTE: Whether or not the stream is free'd on failure is unclear + * w.r.t. ANSI/ISO. This implementation chooses to NOT free + * the stream and associated buffer if they were dynamically + * allocated. + * NOTE: Previous versions of uClibc did free dynamic storage. + * + * TODO: Apparently linux allows setting append mode. Implement? + */ + unsigned short dynmode; + register FILE *fp; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + __STDIO_STREAM_VALIDATE(stream); + + /* First, flush and close, but don't deallocate, the stream. */ + /* This also removes the stream for the open file list. */ + dynmode = (stream->__modeflags & (__FLAG_FREEBUF|__FLAG_FREEFILE)); + + stream->__modeflags &= ~(__FLAG_FREEBUF|__FLAG_FREEFILE); + + /* Only call fclose on the stream if it is not already closed. */ + if ((stream->__modeflags & (__FLAG_READONLY|__FLAG_WRITEONLY)) + != (__FLAG_READONLY|__FLAG_WRITEONLY) + ) { + fclose(stream); /* Failures are ignored. */ + } + + fp = _stdio_fopen(((intptr_t) filename), mode, stream, FILEDES_ARG); + + /* Reset the allocation flags. */ + stream->__modeflags |= dynmode; + + __STDIO_AUTO_THREADUNLOCK(stream); + + return fp; +} diff --git a/libc/stdio/fseeko.c b/libc/stdio/fseeko.c new file mode 100644 index 000000000..fed425730 --- /dev/null +++ b/libc/stdio/fseeko.c @@ -0,0 +1,89 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#if SEEK_SET != 0 || SEEK_CUR != 1 || SEEK_END != 2 +# error Assumption violated -- values of SEEK_SET, SEEK_CUR, SEEK_END +#endif + +#ifdef __DO_LARGEFILE +# ifndef __UCLIBC_HAS_LFS__ +# error large file support is not enabled! +# endif + +# define FSEEK __fseeko64 +# define OFFSET_TYPE __off64_t + +weak_alias(__fseeko64,fseeko64); + +#else + +# define FSEEK fseek +# define OFFSET_TYPE long int + +weak_alias(fseek,fseeko); + +#endif + + +int FSEEK(register FILE *stream, OFFSET_TYPE offset, int whence) +{ +#if defined(__UCLIBC_HAS_LFS__) && !defined(__DO_LARGEFILE) + + return __fseeko64(stream, offset, whence); + +#else + + __offmax_t pos = offset; + int retval = -1; + __STDIO_AUTO_THREADLOCK_VAR; + + if (((unsigned int) whence) > 2) { + __set_errno(EINVAL); + } else { + __STDIO_AUTO_THREADLOCK(stream); + + __STDIO_STREAM_VALIDATE(stream); + + if ((!__STDIO_STREAM_IS_WRITING(stream) + || !__STDIO_COMMIT_WRITE_BUFFER(stream)) + && ((whence != SEEK_CUR) + || (__stdio_adjust_position(stream, &pos) >= 0)) + && (__SEEK(stream, &pos, whence) >= 0) + ) { + + /* Clear reading/writing modes, EOF, and ungots. */ + stream->__modeflags &= + ~(__MASK_READING|__FLAG_WRITING|__FLAG_EOF); + + /* Make sure all pointers are reset. */ + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream); + __STDIO_STREAM_DISABLE_GETC(stream); + __STDIO_STREAM_DISABLE_PUTC(stream); + + /* We reinitialize the mbstate object. Doing so is + * implementation defined behavior. */ +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(stream->__state)); +#endif +#ifdef __UCLIBC_HAS_WCHAR__ + stream->__ungot_width[0] = 0; +#endif + + retval = 0; + } + + __STDIO_STREAM_VALIDATE(stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + } + + return retval; + +#endif +} diff --git a/libc/stdio/fsetpos.c b/libc/stdio/fsetpos.c new file mode 100644 index 000000000..44104b4df --- /dev/null +++ b/libc/stdio/fsetpos.c @@ -0,0 +1,44 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_LARGEFILE +# ifndef __UCLIBC_HAS_LFS__ +# error large file support is not enabled! +# endif + +# define fsetpos fsetpos64 +# define fpos_t fpos64_t +# define fseek fseeko64 +#endif + +int fsetpos(FILE *stream, register const fpos_t *pos) +{ +#ifdef __STDIO_MBSTATE + + int retval = -1; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + if ((retval = fseek(stream, pos->__pos, SEEK_SET)) == 0) { + __COPY_MBSTATE(&(stream->__state), &(pos->__mbstate)); + stream->__ungot_width[0]= pos->__mblen_pending; + } + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; + +#else + + return fseek(stream, pos->__pos, SEEK_SET); + +#endif +} + diff --git a/libc/stdio/ftello.c b/libc/stdio/ftello.c new file mode 100644 index 000000000..7092f34cf --- /dev/null +++ b/libc/stdio/ftello.c @@ -0,0 +1,61 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_LARGEFILE +# ifndef __UCLIBC_HAS_LFS__ +# error large file support is not enabled! +# endif + +# define FTELL __ftello64 +# define OFFSET_TYPE __off64_t + +weak_alias(__ftello64,ftello64); + +#else + +# define FTELL ftell +# define OFFSET_TYPE long int + +weak_alias(ftell,ftello); + +#endif + +OFFSET_TYPE FTELL(register FILE *stream) +{ +#if defined(__UCLIBC_HAS_LFS__) && !defined(__DO_LARGEFILE) + + __offmax_t pos = __ftello64(stream); + + if ((sizeof(long) >= sizeof(__offmax_t)) || (((long) pos) == pos)) { + return ((long) pos); + } else { + __set_errno(EOVERFLOW); + return -1; + } + +#else + + __offmax_t pos = 0; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + __STDIO_STREAM_VALIDATE(stream); + + if ((__SEEK(stream, &pos, SEEK_CUR) < 0) + || (__stdio_adjust_position(stream, &pos) < 0)) { + pos = -1; + } + + __STDIO_AUTO_THREADUNLOCK(stream); + + return pos; + +#endif +} diff --git a/libc/stdio/ftrylockfile.c b/libc/stdio/ftrylockfile.c new file mode 100644 index 000000000..d85b8ff59 --- /dev/null +++ b/libc/stdio/ftrylockfile.c @@ -0,0 +1,19 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: What should this return when not threading? +#endif + +int ftrylockfile(FILE *stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + return __STDIO_ALWAYS_THREADTRYLOCK(stream); +} diff --git a/libc/stdio/funlockfile.c b/libc/stdio/funlockfile.c new file mode 100644 index 000000000..048c093d5 --- /dev/null +++ b/libc/stdio/funlockfile.c @@ -0,0 +1,15 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +void funlockfile(FILE *stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + __STDIO_ALWAYS_THREADUNLOCK(stream); +} diff --git a/libc/stdio/fwide.c b/libc/stdio/fwide.c new file mode 100644 index 000000000..422d789f3 --- /dev/null +++ b/libc/stdio/fwide.c @@ -0,0 +1,32 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* TODO: According to SUSv3 should return EBADF if invalid stream. */ + +int fwide(register FILE *stream, int mode) +{ + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + __STDIO_STREAM_VALIDATE(stream); + + if (mode && !(stream->__modeflags & (__FLAG_WIDE|__FLAG_NARROW))) { + stream->__modeflags |= ((mode > 0) ? __FLAG_WIDE : __FLAG_NARROW); + } + + mode = (stream->__modeflags & __FLAG_WIDE) + - (stream->__modeflags & __FLAG_NARROW); + + assert((stream->__modeflags & (__FLAG_WIDE|__FLAG_NARROW)) + != (__FLAG_WIDE|__FLAG_NARROW)); + __STDIO_AUTO_THREADUNLOCK(stream); + + return mode; +} diff --git a/libc/stdio/fwprintf.c b/libc/stdio/fwprintf.c new file mode 100644 index 000000000..fa5a6bdf6 --- /dev/null +++ b/libc/stdio/fwprintf.c @@ -0,0 +1,22 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> +#include <wchar.h> + +int fwprintf(FILE * __restrict stream, const wchar_t * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vfwprintf(stream, format, arg); + va_end(arg); + + return rv; +} diff --git a/libc/stdio/fwrite.c b/libc/stdio/fwrite.c new file mode 100644 index 000000000..a5e8fd6bf --- /dev/null +++ b/libc/stdio/fwrite.c @@ -0,0 +1,59 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __DO_UNLOCKED + +weak_alias(__fwrite_unlocked,fwrite_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__fwrite_unlocked,fwrite); +#endif + +size_t __fwrite_unlocked(const void * __restrict ptr, size_t size, + size_t nmemb, register FILE * __restrict stream) +{ + __STDIO_STREAM_VALIDATE(stream); + + /* Note: If nmbem * size > SIZE_MAX then there is an application + * bug since no array can be larger than SIZE_MAX in size. */ + + if ((__STDIO_STREAM_IS_NARROW_WRITING(stream) + || !__STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW)) + && size && nmemb + ) { + + if (nmemb <= (SIZE_MAX / size)) { + return __stdio_fwrite((const unsigned char *) ptr, + size*nmemb, stream) / size; + } + + __STDIO_STREAM_SET_ERROR(stream); + __set_errno(EINVAL); + } + + return 0; +} + +#elif defined __UCLIBC_HAS_THREADS__ + +size_t fwrite(const void * __restrict ptr, size_t size, + size_t nmemb, register FILE * __restrict stream) +{ + size_t retval; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + retval = __fwrite_unlocked(ptr, size, nmemb, stream); + + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; +} + +#endif diff --git a/libc/stdio/getchar.c b/libc/stdio/getchar.c new file mode 100644 index 000000000..49414eb70 --- /dev/null +++ b/libc/stdio/getchar.c @@ -0,0 +1,44 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#undef getchar_unlocked +#undef getchar + +#ifdef __DO_UNLOCKED + +weak_alias(__getchar_unlocked,getchar_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__getchar_unlocked,getchar); +#endif + +int __getchar_unlocked(void) +{ + register FILE *stream = stdin; + + return __GETC_UNLOCKED_MACRO(stream); +} + +#elif defined __UCLIBC_HAS_THREADS__ + +int getchar(void) +{ + register FILE *stream = stdin; + + if (stream->__user_locking != 0) { + return __GETC_UNLOCKED_MACRO(stream); + } else { + int retval; + __STDIO_ALWAYS_THREADLOCK(stream); + retval = __GETC_UNLOCKED_MACRO(stream); + __STDIO_ALWAYS_THREADUNLOCK(stream); + return retval; + } +} + +#endif diff --git a/libc/stdio/getdelim.c b/libc/stdio/getdelim.c index fd073a4d2..fe388ee11 100644 --- a/libc/stdio/getdelim.c +++ b/libc/stdio/getdelim.c @@ -1,81 +1,77 @@ -/* vi: set sw=4 ts=4: */ -/* getdelim for uClibc +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> * - * Copyright (C) 2000 by Lineo, inc. and Erik Andersen - * Copyright (C) 2000,2001 by Erik Andersen <andersen@uclibc.org> - * Written by Erik Andersen <andersen@uclibc.org> + * GNU Library General Public License (LGPL) version 2 or later. * - * This program 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 program 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 program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* Note: There is a defect in this function. (size_t vs ssize_t). */ + +/* glibc function -- + * Return -1 if error or EOF prior to any chars read. + * Return number of chars read (including possible delimiter but not + * the terminating nul) otherwise. * + * NOTE: If we need to allocate a buffer, we do so prior to attempting + * a reading. So space may be allocated even if initially at EOF. */ -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> -#include <errno.h> +weak_alias(__getdelim,getdelim); +#define GETDELIM_GROWBY 64 -/* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR - (and null-terminate it). *LINEPTR is a pointer returned from malloc (or - NULL), pointing to *N characters of space. It is realloc'd as - necessary. Returns the number of characters read (not including the - null delimiter), or -1 on error or EOF. */ -ssize_t getdelim(char **linebuf, size_t *linebufsz, int delimiter, FILE *file) +ssize_t __getdelim(char **__restrict lineptr, size_t *__restrict n, + int delimiter, register FILE *__restrict stream) { - static const int GROWBY = 80; /* how large we will grow strings by */ - - int ch; - int idx = 0; + register char *buf; + ssize_t pos = -1; + int c; + __STDIO_AUTO_THREADLOCK_VAR; - if (file == NULL || linebuf==NULL || linebufsz == NULL) { - __set_errno(EINVAL); - return -1; - } + if (!lineptr || !n || !stream) { /* Be compatable with glibc... even */ + __set_errno(EINVAL); /* though I think we should assert here */ + } else { + __STDIO_AUTO_THREADLOCK(stream); - if (*linebuf == NULL || *linebufsz < 2) { - *linebuf = malloc(GROWBY); - if (!*linebuf) { - __set_errno(ENOMEM); - return -1; + if (!(buf = *lineptr)) { /* If passed NULL for buffer, */ + *n = 0; /* ignore value passed and treat size as 0. */ } - *linebufsz += GROWBY; - } - while (1) { - ch = fgetc(file); - if (ch == EOF) - break; - /* grow the line buffer as necessary */ - while (idx > *linebufsz-2) { - *linebuf = realloc(*linebuf, *linebufsz += GROWBY); - if (!*linebuf) { - __set_errno(ENOMEM); - return -1; + /* Within the loop, pos is actually the current buffer index + 2, + * because we want to make sure we have enough space to store + * an additional char plus a nul terminator. + */ + pos = 1; + + do { + if (pos >= *n) { + if (!(buf = realloc(buf, *n + GETDELIM_GROWBY))) { + pos = -1; + break; + } + *n += GETDELIM_GROWBY; + *lineptr = buf; + } + + if ((c = __GETC_UNLOCKED(stream)) != EOF) { + buf[++pos - 2] = c; + if (c != delimiter) { + continue; + } + } + + /* We're done, so correct pos back to being the current index. */ + if ((pos -= 2) >= 0) { + buf[++pos] = 0; } - } - (*linebuf)[idx++] = (char)ch; - if ((char)ch == delimiter) break; + + } while (1); + + __STDIO_AUTO_THREADUNLOCK(stream); } - if (idx != 0) - (*linebuf)[idx] = 0; - else if ( ch == EOF ) - return -1; - return idx; + return pos; } - diff --git a/libc/stdio/getline.c b/libc/stdio/getline.c index 8f63bc6e0..0708aedaa 100644 --- a/libc/stdio/getline.c +++ b/libc/stdio/getline.c @@ -1,34 +1,14 @@ -/* vi: set sw=4 ts=4: */ -/* getline for uClibc +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> * - * Copyright (C) 2000 by Lineo, inc. and Erik Andersen - * Copyright (C) 2000,2001 by Erik Andersen <andersen@uclibc.org> - * Written by Erik Andersen <andersen@uclibc.org> - * - * This program 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 program 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 program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * GNU Library General Public License (LGPL) version 2 or later. * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. */ -#include <stddef.h> -#include <sys/types.h> -#define __USE_GNU -#include <stdio.h> +#include "_stdio.h" -/* Basically getdelim() with the delimiter hard wired to '\n' */ -ssize_t getline(char **linebuf, size_t *n, FILE *file) +ssize_t getline(char **__restrict lineptr, size_t *__restrict n, + FILE *__restrict stream) { - return (getdelim (linebuf, n, '\n', file)); + return __getdelim(lineptr, n, '\n', stream); } - diff --git a/libc/stdio/gets.c b/libc/stdio/gets.c new file mode 100644 index 000000000..1badd8152 --- /dev/null +++ b/libc/stdio/gets.c @@ -0,0 +1,36 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +link_warning(gets, "the 'gets' function is dangerous and should not be used.") + +/* UNSAFE FUNCTION -- do not bother optimizing */ + +char *gets(char *s) +{ + register char *p = s; + int c; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stdin); + + /* Note: don't worry about performance here... this shouldn't be used! + * Therefore, force actual function call. */ + while (((c = __getchar_unlocked()) != EOF) && ((*p = c) != '\n')) { + ++p; + } + if ((c == EOF) || (s == p)) { + s = NULL; + } else { + *p = 0; + } + + __STDIO_AUTO_THREADUNLOCK(stdin); + + return s; +} diff --git a/libc/stdio/getw.c b/libc/stdio/getw.c new file mode 100644 index 000000000..6b3b9e5e7 --- /dev/null +++ b/libc/stdio/getw.c @@ -0,0 +1,18 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* SUSv2 Legacy function -- need not be reentrant. */ + +int getw(FILE *stream) +{ + int aw; + + return (__fread_unlocked((void *) &aw, sizeof(int), 1, stream) != 0) + ? aw : EOF; +} diff --git a/libc/stdio/getwchar.c b/libc/stdio/getwchar.c new file mode 100644 index 000000000..98d9fcba0 --- /dev/null +++ b/libc/stdio/getwchar.c @@ -0,0 +1,31 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +wint_t __getwchar_unlocked(void); + +#ifdef __DO_UNLOCKED + +weak_alias(__getwchar_unlocked,getwchar_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__getwchar_unlocked,getwchar); +#endif + +wint_t __getwchar_unlocked(void) +{ + return __fgetwc_unlocked(stdin); +} + +#elif defined __UCLIBC_HAS_THREADS__ + +wint_t getwchar(void) +{ + return fgetwc(stdin); +} + +#endif diff --git a/libc/stdio/old_vfprintf.c b/libc/stdio/old_vfprintf.c index 5afbefb07..1b7af96f9 100644 --- a/libc/stdio/old_vfprintf.c +++ b/libc/stdio/old_vfprintf.c @@ -129,21 +129,22 @@ #define _ISOC99_SOURCE /* for ULLONG primarily... */ #define _GNU_SOURCE /* for strnlen */ -#define _STDIO_UTILITY -#include <stdio.h> +#include "_stdio.h" +/* #include <stdio.h> */ #include <stdarg.h> #include <limits.h> #include <stdint.h> #include <string.h> #include <errno.h> #include <ctype.h> +#include <bits/uClibc_uintmaxtostr.h> #define __PRINTF_INFO_NO_BITFIELD #include <printf.h> -#ifdef __STDIO_THREADSAFE +#ifdef __UCLIBC_HAS_THREADS__ #include <pthread.h> -#endif /* __STDIO_THREADSAFE */ +#endif /* __UCLIBC_HAS_THREADS__ */ /* #undef __UCLIBC_HAS_FLOATS__ */ /* #undef WANT_FLOAT_ERROR */ @@ -151,7 +152,7 @@ /* #define __isdigit(c) (((unsigned int)(c - '0')) < 10) */ -#ifdef __STDIO_PRINTF_M_SUPPORT +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ #define WANT_GNU_ERRNO 1 #else #define WANT_GNU_ERRNO 0 @@ -165,7 +166,7 @@ #define PUTC(C,F) putc_unlocked((C),(F)) #define OUTNSTR _outnstr -#define _outnstr(stream, string, len) _stdio_fwrite(string, len, stream) +#define _outnstr(stream, string, len) __stdio_fwrite(string, len, stream) #else /* __STDIO_BUFFERS */ @@ -180,24 +181,22 @@ static void _outnstr(FILE *stream, const unsigned char *s, size_t n) { __FILE_vsnprintf *f = (__FILE_vsnprintf *) stream; - if (f->f.filedes != -2) { - _stdio_fwrite(s, n, &f->f); - } else { - if (f->bufend > f->bufpos) { - size_t r = f->bufend - f->bufpos; - if (r > n) { - r = n; - } - memcpy(f->bufpos, s, r); - f->bufpos += r; + if (!__STDIO_STREAM_IS_FAKE_VSNPRINTF_NB(&f->f)) { + __stdio_fwrite(s, n, &f->f); + } else if (f->bufend > f->bufpos) { + size_t r = f->bufend - f->bufpos; + if (r > n) { + r = n; } + memcpy(f->bufpos, s, r); + f->bufpos += r; } } #endif static void putc_unlocked_sprintf(int c, __FILE_vsnprintf *f) { - if (f->f.filedes != -2) { + if (!__STDIO_STREAM_IS_FAKE_VSNPRINTF_NB(&f->f)) { putc_unlocked(c, &f->f); } else if (f->bufpos < f->bufend) { *f->bufpos++ = c; @@ -241,7 +240,9 @@ static void _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) } len = buflen; } - OUTNSTR(fp, (const char *) buf, len); + if (len) { + OUTNSTR(fp, (const char *) buf, len); + } } #endif @@ -359,11 +360,18 @@ int vfprintf(FILE * __restrict op, register const char * __restrict fmt, int radix, dpoint /*, upcase*/; char tmp[65]; /* TODO - determing needed size from headers */ char flag[sizeof(spec)]; + __STDIO_AUTO_THREADLOCK_VAR; - __STDIO_THREADLOCK(op); + __STDIO_AUTO_THREADLOCK(op); + + __STDIO_STREAM_VALIDATE(op); cnt = 0; + if (__STDIO_STREAM_IS_NARROW_WRITING(op) + || !__STDIO_STREAM_TRANS_TO_WRITE(op, __FLAG_NARROW) + ) { + while (*fmt) { if (*fmt == '%') { fmt0 = fmt; /* save our position in case of bad format */ @@ -691,9 +699,13 @@ int vfprintf(FILE * __restrict op, register const char * __restrict fmt, ++fmt; } - i = (__FERROR(op)) ? -1 : cnt; + } + + i = (__FERROR_UNLOCKED(op)) ? -1 : cnt; + + __STDIO_STREAM_VALIDATE(op); - __STDIO_THREADUNLOCK(op); + __STDIO_AUTO_THREADUNLOCK(op); return i; } diff --git a/libc/stdio/open_memstream.c b/libc/stdio/open_memstream.c new file mode 100644 index 000000000..0e3df43d1 --- /dev/null +++ b/libc/stdio/open_memstream.c @@ -0,0 +1,162 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifndef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ +#error no custom streams! +#endif + +#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) { + __STDIO_STREAM_VALIDATE(fp); + return fp; + } + } + + if (cookie->buf != NULL) { + free(cookie->buf); + } + EXIT_cookie: + free(cookie); + + return NULL; +} diff --git a/libc/stdio/perror.c b/libc/stdio/perror.c new file mode 100644 index 000000000..83db7689f --- /dev/null +++ b/libc/stdio/perror.c @@ -0,0 +1,36 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Increase buffer size for error message (non-%m case)? +#endif + +void perror(register const char *s) +{ + /* If the program is calling perror, it's a safe bet that printf and + * friends are used as well. It is also possible that the calling + * program could buffer stderr, or reassign it. */ + + register const char *sep; + + sep = ": "; + if (!(s && *s)) { /* Caller did not supply a prefix message */ + s = (sep += 2); /* or passed an empty string. */ + } + +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + fprintf(stderr, "%s%s%m\n", s, sep); /* Use the gnu %m feature. */ +#else + { + char buf[64]; + fprintf(stderr, "%s%s%s\n", s, sep, + _glibc_strerror_r(errno, buf, sizeof(buf))); + } +#endif +} diff --git a/libc/stdio/popen.c b/libc/stdio/popen.c index 8ebaedfd6..c7887ad41 100644 --- a/libc/stdio/popen.c +++ b/libc/stdio/popen.c @@ -1,18 +1,8 @@ -/* Copyright (C) 2004 Manuel Novoa III +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> * - * 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. + * GNU Library General Public License (LGPL) version 2 or later. * - * 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. + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. */ /* Jan 1, 2004 @@ -54,11 +44,6 @@ static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; # define VFORK_UNLOCK UNLOCK #endif -/* Temporarily support old stdio code. */ -#ifndef __MASK_READING -#define __filedes filedes -#endif - struct popen_list_item { struct popen_list_item *next; FILE *f; diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c index bc184ee5f..67db4b384 100644 --- a/libc/stdio/printf.c +++ b/libc/stdio/printf.c @@ -1,1582 +1,13 @@ -/* Copyright (C) 2002, 2003 Manuel Novoa III - * My stdio library for linux and (soon) elks. +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> * - * 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. + * GNU Library General Public License (LGPL) version 2 or later. * - * 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. - */ - -/* This code needs a lot of clean up. Some of that is on hold until uClibc - * gets a better configuration system (on Erik's todo list). - * The other cleanup will take place during the implementation/integration of - * the wide char (un)formatted i/o functions which I'm currently working on. - */ - -/* 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! */ - - -/* April 1, 2002 - * Initialize thread locks for fake files in vsnprintf and vdprintf. - * reported by Erik Andersen (andersen@codepoet.com) - * Fix an arg promotion handling bug in _do_one_spec for %c. - * reported by Ilguiz Latypov <ilatypov@superbt.com> - * - * May 10, 2002 - * Remove __isdigit and use new ctype.h version. - * Add conditional setting of QUAL_CHARS for size_t and ptrdiff_t. - * - * Aug 16, 2002 - * Fix two problems that showed up with the python 2.2.1 tests; one - * involving %o and one involving %f. - * - * Oct 28, 2002 - * Fix a problem in vasprintf (reported by vodz a while back) when built - * without custom stream support. In that case, it is necessary to do - * a va_copy. - * Make sure each va_copy has a matching va_end, as required by C99. - * - * Nov 4, 2002 - * Add locale-specific grouping support for integer decimal conversion. - * Add locale-specific decimal point support for floating point conversion. - * Note: grouping will have to wait for _dtostr() rewrite. - * Add printf wchar support for %lc (%C) and %ls (%S). - * Require printf format strings to be valid multibyte strings beginning and - * ending in their initial shift state, as per the stds. - * - * Nov 21, 2002 - * Add *wprintf functions. Currently they don't support floating point - * conversions. That will wait until the rewrite of _dtostr. - * - * Aug 1, 2003 - * Optional hexadecimal float notation support for %a/%A. - * Floating point output now works for *wprintf. - * Support for glibc locale-specific digit grouping for floats. - * Misc bug fixes. - * - * Aug 31, 2003 - * Fix precision bug for %g conversion specifier when using %f style. - * - * Sep 5, 2003 - * Implement *s*scanf for the non-buffered stdio case with old_vfprintf. - * - * Sep 23, 2003 - * vfprintf was not always checking for narrow stream orientation. + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. */ -/* TODO: - * - * Should we validate that *printf format strings are valid multibyte - * strings in the current locale? ANSI/ISO C99 seems to imply this - * and Plauger's printf implementation in his Standard C Library book - * treats this as an error. - */ - - -#define _ISOC99_SOURCE /* for ULLONG primarily... */ -#define _GNU_SOURCE -#define _STDIO_UTILITY /* We're using _uintmaxtostr. */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stddef.h> -#include <ctype.h> -#include <limits.h> +#include "_stdio.h" #include <stdarg.h> -#include <assert.h> -#include <stdint.h> -#include <errno.h> -#include <locale.h> - -#define __PRINTF_INFO_NO_BITFIELD -#include <printf.h> - -#ifdef __STDIO_THREADSAFE -#include <stdio_ext.h> -#include <pthread.h> -#endif /* __STDIO_THREADSAFE */ - -#ifdef __UCLIBC_HAS_WCHAR__ -#include <wchar.h> -#endif /* __UCLIBC_HAS_WCHAR__ */ - -/* Some older or broken gcc toolchains define LONG_LONG_MAX but not - * LLONG_MAX. Since LLONG_MAX is part of the standard, that's what - * we use. So complain if we do not have it but should. - */ -#if !defined(LLONG_MAX) && defined(LONG_LONG_MAX) -#error Apparently, LONG_LONG_MAX is defined but LLONG_MAX is not. You need to fix your toolchain headers to support the standard macros for (unsigned) long long. -#endif - -/**********************************************************************/ -/* These provide some control over printf's feature set */ - -/* This is undefined below depeding on uClibc's configuration. */ -#define __STDIO_PRINTF_FLOAT 1 - -/* Now controlled by uClibc_stdio.h. */ -/* #define __STDIO_PRINTF_M_SUPPORT */ - - -/**********************************************************************/ - -#if defined(__UCLIBC__) && !defined(__UCLIBC_HAS_FLOATS__) -#undef __STDIO_PRINTF_FLOAT -#endif - -#ifdef __BCC__ -#undef __STDIO_PRINTF_FLOAT -#endif - -#ifdef __STDIO_PRINTF_FLOAT -#include <float.h> -#include <bits/uClibc_fpmax.h> -#else /* __STDIO_PRINTF_FLOAT */ -#undef L__fpmaxtostr -#endif /* __STDIO_PRINTF_FLOAT */ - - -#undef __STDIO_HAS_VSNPRINTF -#if defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#define __STDIO_HAS_VSNPRINTF 1 -#endif - -/**********************************************************************/ - -/* Now controlled by uClibc_stdio.h. */ -/* #define __STDIO_GLIBC_CUSTOM_PRINTF */ - -/* TODO -- move these to a configuration section? */ -#define MAX_FIELD_WIDTH 4095 - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_register_printf_function -/* emit only once */ -#warning WISHLIST: Make MAX_USER_SPEC configurable? -#warning WISHLIST: Make MAX_ARGS_PER_SPEC configurable? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF - -#define MAX_USER_SPEC 10 -#define MAX_ARGS_PER_SPEC 5 - -#else /* __STDIO_GLIBC_CUSTOM_PRINTF */ - -#undef MAX_USER_SPEC -#define MAX_ARGS_PER_SPEC 1 - -#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ - -#if MAX_ARGS_PER_SPEC < 1 -#error MAX_ARGS_PER_SPEC < 1! -#undef MAX_ARGS_PER_SPEC -#define MAX_ARGS_PER_SPEC 1 -#endif - -#if defined(NL_ARGMAX) && (NL_ARGMAX < 9) -#error NL_ARGMAX < 9! -#endif - -#if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2)) -#define MAX_ARGS NL_ARGMAX -#else -/* N for spec itself, plus 1 each for width and precision */ -#define MAX_ARGS (MAX_ARGS_PER_SPEC + 2) -#endif - - -/**********************************************************************/ -/* Deal with pre-C99 compilers. */ - -#ifndef va_copy - -#ifdef __va_copy -#define va_copy(A,B) __va_copy(A,B) -#else - /* TODO -- maybe create a bits/vacopy.h for arch specific versions - * to ensure we get the right behavior? Either that or fall back - * on the portable (but costly in size) method of using a va_list *. - * That means a pointer derefs in the va_arg() invocations... */ -#warning Neither va_copy (C99/SUSv3) or __va_copy is defined. Using a simple copy instead. But you should really check that this is appropriate... - /* the glibc manual suggests that this will usually suffice when - __va_copy doesn't exist. */ -#define va_copy(A,B) A = B -#endif - -#endif /* va_copy */ - -/**********************************************************************/ - -#define __PA_FLAG_INTMASK \ - (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG) - -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF -extern printf_function _custom_printf_handler[MAX_USER_SPEC]; -extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; -extern char *_custom_printf_spec; -#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ - -/**********************************************************************/ - -#define SPEC_FLAGS " +0-#'I" -enum { - FLAG_SPACE = 0x01, - FLAG_PLUS = 0x02, /* must be 2 * FLAG_SPACE */ - FLAG_ZERO = 0x04, - FLAG_MINUS = 0x08, /* must be 2 * FLAG_ZERO */ - FLAG_HASH = 0x10, - FLAG_THOUSANDS = 0x20, - FLAG_I18N = 0x40, /* only works for d, i, u */ - FLAG_WIDESTREAM = 0x80 -}; - -/**********************************************************************/ - -/* float layout 01234567890123456789 TODO: B?*/ -#define SPEC_CHARS "npxXoudifFeEgGaACScs" -enum { - CONV_n = 0, - CONV_p, - CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i, - CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A, - CONV_C, CONV_S, CONV_c, CONV_s, -#ifdef __STDIO_PRINTF_M_SUPPORT - CONV_m, -#endif - CONV_custom0 /* must be last */ -}; - -/* p x X o u d i */ -#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 10 } - -#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \ - CONV_C, CONV_S, CONV_c, CONV_s, CONV_custom0 } - -#define SPEC_OR_MASK { \ - /* n */ (PA_FLAG_PTR|PA_INT), \ - /* p */ PA_POINTER, \ - /* oxXudi */ PA_INT, \ - /* fFeEgGaA */ PA_DOUBLE, \ - /* C */ PA_WCHAR, \ - /* S */ PA_WSTRING, \ - /* c */ PA_CHAR, \ - /* s */ PA_STRING, \ -} - -#define SPEC_AND_MASK { \ - /* n */ (PA_FLAG_PTR|__PA_INTMASK), \ - /* p */ PA_POINTER, \ - /* oxXudi */ (__PA_INTMASK), \ - /* fFeEgGaA */ (PA_FLAG_LONG_DOUBLE|PA_DOUBLE), \ - /* C */ (PA_WCHAR), \ - /* S */ (PA_WSTRING), \ - /* c */ (PA_CHAR), \ - /* s */ (PA_STRING), \ -} - -/**********************************************************************/ -/* - * In order to ease translation to what arginfo and _print_info._flags expect, - * we map: 0:int 1:char 2:longlong 4:long 8:short - * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701) - */ - -/* TODO -- Fix the table below to take into account stdint.h. */ -/* #ifndef LLONG_MAX */ -/* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */ -/* #else */ -/* #if LLONG_MAX != INTMAX_MAX */ -/* #error fix QUAL_CHARS intmax_t entry 'j'! */ -/* #endif */ -/* #endif */ - -#ifdef PDS -#error PDS already defined! -#endif -#ifdef SS -#error SS already defined! -#endif -#ifdef IMS -#error IMS already defined! -#endif - -#if PTRDIFF_MAX == INT_MAX -#define PDS 0 -#elif PTRDIFF_MAX == LONG_MAX -#define PDS 4 -#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX) -#define PDS 8 -#else -#error fix QUAL_CHARS ptrdiff_t entry 't'! -#endif - -#if SIZE_MAX == UINT_MAX -#define SS 0 -#elif SIZE_MAX == ULONG_MAX -#define SS 4 -#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX) -#define SS 8 -#else -#error fix QUAL_CHARS size_t entries 'z', 'Z'! -#endif - -#if INTMAX_MAX == INT_MAX -#define IMS 0 -#elif INTMAX_MAX == LONG_MAX -#define IMS 4 -#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX) -#define IMS 8 -#else -#error fix QUAL_CHARS intmax_t entry 'j'! -#endif - -#define QUAL_CHARS { \ - /* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int */ \ - /* q:long_long Z:(s)size_t */ \ - 'h', 'l', 'L', 'j', 'z', 't', 'q', 'Z', 0, \ - 2, 4, 8, IMS, SS, PDS, 8, SS, 0, /* TODO -- fix!!! */\ - 1, 8 \ -} - -/**********************************************************************/ - -#ifdef __STDIO_VA_ARG_PTR -#ifdef __BCC__ -#define __va_arg_ptr(ap,type) (((type *)(ap += sizeof(type))) - 1) -#endif - -#if 1 -#ifdef __GNUC__ -/* TODO -- need other than for 386 as well! */ - -#ifndef __va_rounded_size -#define __va_rounded_size(TYPE) \ - (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) -#endif -#define __va_arg_ptr(AP, TYPE) \ - (AP = (va_list) ((char *) (AP) + __va_rounded_size (TYPE)), \ - ((void *) ((char *) (AP) - __va_rounded_size (TYPE)))) -#endif -#endif -#endif /* __STDIO_VA_ARG_PTR */ - -#ifdef __va_arg_ptr -#define GET_VA_ARG(AP,F,TYPE,ARGS) (*(AP) = __va_arg_ptr(ARGS,TYPE)) -#define GET_ARG_VALUE(AP,F,TYPE) (*((TYPE *)(*(AP)))) -#else -typedef union { - wchar_t wc; - unsigned int u; - unsigned long ul; -#ifdef ULLONG_MAX - unsigned long long ull; -#endif -#ifdef __STDIO_PRINTF_FLOAT - double d; - long double ld; -#endif /* __STDIO_PRINTF_FLOAT */ - void *p; -} argvalue_t; - -#define GET_VA_ARG(AU,F,TYPE,ARGS) (AU->F = va_arg(ARGS,TYPE)) -#define GET_ARG_VALUE(AU,F,TYPE) ((TYPE)((AU)->F)) -#endif - -typedef struct { - const char *fmtpos; /* TODO: move below struct?? */ - struct printf_info info; -#ifdef NL_ARGMAX - int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */ -#endif /* NL_ARGMAX */ - int num_data_args; /* TODO: use sentinal??? */ - unsigned int conv_num; - unsigned char argnumber[4]; /* width | prec | 1st data | unused */ - int argtype[MAX_ARGS]; - va_list arg; -#ifdef __va_arg_ptr - void *argptr[MAX_ARGS]; -#else -/* if defined(NL_ARGMAX) || defined(__STDIO_GLIBC_CUSTOM_PRINTF) */ - /* While this is wasteful of space in the case where pos args aren't - * enabled, it is also needed to support custom printf handlers. */ - argvalue_t argvalue[MAX_ARGS]; -#endif -} ppfs_t; /* parse printf format state */ - -/**********************************************************************/ - -/* TODO: fix printf to return 0 and set errno if format error. Standard says - only returns -1 if sets error indicator for the stream. */ - -#ifdef __STDIO_PRINTF_FLOAT -typedef void (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len, - intptr_t buf); - -extern size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, - __fp_outfunc_t fp_outfunc); -#endif - -extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0); /* validates */ -extern void _ppfs_prepargs(ppfs_t *ppfs, va_list arg); /* sets posargptrs */ -extern void _ppfs_setargs(ppfs_t *ppfs); /* sets argptrs for current spec */ -extern int _ppfs_parsespec(ppfs_t *ppfs); /* parses specifier */ - -extern void _store_inttype(void *dest, int desttype, uintmax_t val); -extern uintmax_t _load_inttype(int desttype, const void *src, int uflag); - -/**********************************************************************/ -#ifdef L_parse_printf_format - -/* NOTE: This function differs from the glibc version in that parsing stops - * upon encountering an invalid conversion specifier. Since this is the way - * my printf functions work, I think it makes sense to do it that way here. - * Unfortunately, since glibc sets the return type as size_t, we have no way - * of returning that the template is illegal, other than returning 0. - */ - -size_t parse_printf_format(register const char *template, - size_t n, register int *argtypes) -{ - ppfs_t ppfs; - size_t i; - size_t count = 0; - - if (_ppfs_init(&ppfs, template) >= 0) { -#ifdef NL_ARGMAX - if (ppfs.maxposarg > 0) { /* Using positional args. */ - count = ppfs.maxposarg; - if (n > count) { - n = count; - } - for (i = 0 ; i < n ; i++) { - *argtypes++ = ppfs.argtype[i]; - } - } else { /* Not using positional args. */ -#endif /* NL_ARGMAX */ - while (*template) { - if ((*template == '%') && (*++template != '%')) { - ppfs.fmtpos = template; - _ppfs_parsespec(&ppfs); /* Can't fail. */ - template = ppfs.fmtpos; /* Update to one past spec end. */ - if (ppfs.info.width == INT_MIN) { - ++count; - if (n > 0) { - *argtypes++ = PA_INT; - --n; - } - } - if (ppfs.info.prec == INT_MIN) { - ++count; - if (n > 0) { - *argtypes++ = PA_INT; - --n; - } - } - for (i = 0 ; i < ppfs.num_data_args ; i++) { - if ((ppfs.argtype[i]) != __PA_NOARG) { - ++count; - if (n > 0) { - *argtypes++ = ppfs.argtype[i]; - --n; - } - } - } - } else { - ++template; - } - } -#ifdef NL_ARGMAX - } -#endif /* NL_ARGMAX */ - } - - return count; -} - -#endif -/**********************************************************************/ -#ifdef L__ppfs_init - -int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) -{ - int r; - - /* First, zero out everything... argnumber[], argtype[], argptr[] */ - memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */ -#ifdef NL_ARGMAX - --ppfs->maxposarg; /* set to -1 */ -#endif /* NL_ARGMAX */ - ppfs->fmtpos = fmt0; -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Make checking of the format string in C locale an option. -#endif -#ifdef __UCLIBC_HAS_LOCALE__ - /* To support old programs, don't check mb validity if in C locale. */ - if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) { - /* ANSI/ISO C99 requires format string to be a valid multibyte string - * beginning and ending in its initial shift state. */ - static const char invalid_mbs[] = "Invalid multibyte format string."; - mbstate_t mbstate; - const char *p; - mbstate.mask = 0; /* Initialize the mbstate. */ - p = fmt0; - if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { - ppfs->fmtpos = invalid_mbs; - return -1; - } - } -#endif /* __UCLIBC_HAS_LOCALE__ */ - /* now set all argtypes to no-arg */ - { -#if 1 - /* TODO - use memset here since already "paid for"? */ - register int *p = ppfs->argtype; - - r = MAX_ARGS; - do { - *p++ = __PA_NOARG; - } while (--r); -#else - /* TODO -- get rid of this?? */ - register char *p = (char *) ((MAX_ARGS-1) * sizeof(int)); - - do { - *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG; - p -= sizeof(int); - } while (p); -#endif - } - - /* - * Run through the entire format string to validate it and initialize - * the positional arg numbers (if any). - */ - { - register const char *fmt = fmt0; - - while (*fmt) { - if ((*fmt == '%') && (*++fmt != '%')) { - ppfs->fmtpos = fmt; /* back up to the '%' */ - if ((r = _ppfs_parsespec(ppfs)) < 0) { - return -1; - } - fmt = ppfs->fmtpos; /* update to one past end of spec */ - } else { - ++fmt; - } - } - ppfs->fmtpos = fmt0; /* rewind */ - } - -#ifdef NL_MAX_ARG - /* If we have positional args, make sure we know all the types. */ - { - register int *p = ppfs->argtype; - r = ppfs->maxposarg; - while (--r >= 0) { - if ( *p == __PA_NOARG ) { /* missing arg type!!! */ - return -1; - } - ++p; - } - } -#endif /* NL_MAX_ARG */ - - return 0; -} -#endif -/**********************************************************************/ -#ifdef L__ppfs_prepargs -void _ppfs_prepargs(register ppfs_t *ppfs, va_list arg) -{ - int i; - - va_copy(ppfs->arg, arg); - -#ifdef NL_ARGMAX - if ((i = ppfs->maxposarg) > 0) { /* init for positional args */ - ppfs->num_data_args = i; - ppfs->info.width = ppfs->info.prec = ppfs->maxposarg = 0; - _ppfs_setargs(ppfs); - ppfs->maxposarg = i; - } -#endif /* NL_ARGMAX */ -} -#endif -/**********************************************************************/ -#ifdef L__ppfs_setargs - -void _ppfs_setargs(register ppfs_t *ppfs) -{ -#ifdef __va_arg_ptr - register void **p = ppfs->argptr; -#else - register argvalue_t *p = ppfs->argvalue; -#endif - int i; - -#ifdef NL_ARGMAX - if (ppfs->maxposarg == 0) { /* initing for or no pos args */ -#endif /* NL_ARGMAX */ - if (ppfs->info.width == INT_MIN) { - ppfs->info.width = -#ifdef __va_arg_ptr - *(int *) -#endif - GET_VA_ARG(p,u,unsigned int,ppfs->arg); - } - if (ppfs->info.prec == INT_MIN) { - ppfs->info.prec = -#ifdef __va_arg_ptr - *(int *) -#endif - GET_VA_ARG(p,u,unsigned int,ppfs->arg); - } - i = 0; - while (i < ppfs->num_data_args) { - switch(ppfs->argtype[i++]) { - case (PA_INT|PA_FLAG_LONG_LONG): -#ifdef ULLONG_MAX - GET_VA_ARG(p,ull,unsigned long long,ppfs->arg); - break; -#endif - case (PA_INT|PA_FLAG_LONG): -#if ULONG_MAX != UINT_MAX - GET_VA_ARG(p,ul,unsigned long,ppfs->arg); - break; -#endif - case PA_CHAR: /* TODO - be careful */ - /* ... users could use above and really want below!! */ - case (PA_INT|__PA_FLAG_CHAR):/* TODO -- translate this!!! */ - case (PA_INT|PA_FLAG_SHORT): - case PA_INT: - GET_VA_ARG(p,u,unsigned int,ppfs->arg); - break; - case PA_WCHAR: /* TODO -- assume int? */ - /* we're assuming wchar_t is at least an int */ - GET_VA_ARG(p,wc,wchar_t,ppfs->arg); - break; -#ifdef __STDIO_PRINTF_FLOAT - /* PA_FLOAT */ - case PA_DOUBLE: - GET_VA_ARG(p,d,double,ppfs->arg); - break; - case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): - GET_VA_ARG(p,ld,long double,ppfs->arg); - break; -#else /* __STDIO_PRINTF_FLOAT */ - case PA_DOUBLE: - case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): - assert(0); - continue; -#endif /* __STDIO_PRINTF_FLOAT */ - default: - /* TODO -- really need to ensure this can't happen */ - assert(ppfs->argtype[i-1] & PA_FLAG_PTR); - case PA_POINTER: - case PA_STRING: - case PA_WSTRING: - GET_VA_ARG(p,p,void *,ppfs->arg); - break; - case __PA_NOARG: - continue; - } - ++p; - } -#ifdef NL_ARGMAX - } else { - if (ppfs->info.width == INT_MIN) { - ppfs->info.width - = (int) GET_ARG_VALUE(p + ppfs->argnumber[0] - 1,u,unsigned int); - } - if (ppfs->info.prec == INT_MIN) { - ppfs->info.prec - = (int) GET_ARG_VALUE(p + ppfs->argnumber[1] - 1,u,unsigned int); - } - } -#endif /* NL_ARGMAX */ - - /* Now we know the width and precision. */ - if (ppfs->info.width < 0) { - ppfs->info.width = -ppfs->info.width; - PRINT_INFO_SET_FLAG(&(ppfs->info),left); - PRINT_INFO_CLR_FLAG(&(ppfs->info),space); - ppfs->info.pad = ' '; - } -#if 0 - /* NOTE -- keep neg for now so float knows! */ - if (ppfs->info.prec < 0) { /* spec says treat as omitted. */ - /* so use default prec... 1 for everything but floats and strings. */ - ppfs->info.prec = 1; - } -#endif -} -#endif -/**********************************************************************/ -#ifdef L__ppfs_parsespec - -/* Notes: argtype differs from glibc for the following: - * mine glibc - * lc PA_WCHAR PA_CHAR the standard says %lc means %C - * ls PA_WSTRING PA_STRING the standard says %ls means %S - * {*}n {*}|PA_FLAG_PTR PA_FLAG_PTR size of n can be qualified - */ - -/* TODO: store positions of positional args */ - -/* TODO -- WARNING -- assumes aligned on integer boundaries!!! */ - -/* TODO -- disable if not using positional args!!! */ -#define _OVERLAPPING_DIFFERENT_ARGS - -/* TODO -- rethink this -- perhaps we should set to largest type??? */ - -#ifdef _OVERLAPPING_DIFFERENT_ARGS - -#define PROMOTED_SIZE_OF(X) ((sizeof(X) + sizeof(int) - 1) / sizeof(X)) - -static const short int type_codes[] = { - __PA_NOARG, /* must be first entry */ - PA_POINTER, - PA_STRING, - PA_WSTRING, - PA_CHAR, - PA_INT|PA_FLAG_SHORT, - PA_INT, - PA_INT|PA_FLAG_LONG, - PA_INT|PA_FLAG_LONG_LONG, - PA_WCHAR, -#ifdef __STDIO_PRINTF_FLOAT - /* PA_FLOAT, */ - PA_DOUBLE, - PA_DOUBLE|PA_FLAG_LONG_DOUBLE, -#endif /* __STDIO_PRINTF_FLOAT */ -}; - -static const unsigned char type_sizes[] = { - /* unknown type consumes no arg */ - 0, /* must be first entry */ - PROMOTED_SIZE_OF(void *), - PROMOTED_SIZE_OF(char *), - PROMOTED_SIZE_OF(wchar_t *), - PROMOTED_SIZE_OF(char), - PROMOTED_SIZE_OF(short), - PROMOTED_SIZE_OF(int), - PROMOTED_SIZE_OF(long), -#ifdef ULLONG_MAX - PROMOTED_SIZE_OF(long long), -#else - PROMOTED_SIZE_OF(long), /* TODO -- is this correct? (above too) */ -#endif - PROMOTED_SIZE_OF(wchar_t), -#ifdef __STDIO_PRINTF_FLOAT - /* PROMOTED_SIZE_OF(float), */ - PROMOTED_SIZE_OF(double), - PROMOTED_SIZE_OF(long double), -#endif /* __STDIO_PRINTF_FLOAT */ -}; - -static int _promoted_size(int argtype) -{ - register const short int *p; - - /* note -- since any unrecognized type is treated as a pointer */ - p = type_codes + sizeof(type_codes)/sizeof(type_codes[0]); - do { - if (*--p == argtype) { - break; - } - } while (p > type_codes); - - return type_sizes[(int)(p - type_codes)]; -} - -static int _is_equal_or_bigger_arg(int curtype, int newtype) -{ - /* Quick test */ - if (newtype == __PA_NOARG) { - return 0; - } - if ((curtype == __PA_NOARG) || (curtype == newtype)) { - return 1; - } - /* Ok... slot is already filled and types are different in name. */ - /* So, compare promoted sizes of curtype and newtype args. */ - return _promoted_size(curtype) <= _promoted_size(newtype); -} - -#else - -#define _is_equal_or_bigger_arg(C,N) (((C) == __PA_NOARG) || ((C) == (N))) - -#endif - -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF -/* TODO - do this differently? */ -static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us. */ - -char *_custom_printf_spec = _bss_custom_printf_spec; -printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; -printf_function _custom_printf_handler[MAX_USER_SPEC]; -#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ - -extern int _ppfs_parsespec(ppfs_t *ppfs) -{ - register const char *fmt; - register const char *p; - int preci; - int width; - int flags; - int dataargtype; - int i; - int dpoint; -#ifdef NL_ARGMAX - int maxposarg; -#endif /* NL_ARGMAX */ - int p_m_spec_chars; - int n; - int argtype[MAX_ARGS_PER_SPEC+2]; - int argnumber[3]; /* width, precision, 1st data arg */ - static const char spec_flags[] = SPEC_FLAGS; - static const char spec_chars[] = SPEC_CHARS;/* TODO: b? */ - static const char spec_ranges[] = SPEC_RANGES; - static const short spec_or_mask[] = SPEC_OR_MASK; - static const short spec_and_mask[] = SPEC_AND_MASK; - static const char qual_chars[] = QUAL_CHARS; -#ifdef __UCLIBC_HAS_WCHAR__ - char buf[32]; -#endif /* __UCLIBC_HAS_WCHAR__ */ - - /* WIDE note: we can test against '%' here since we don't allow */ - /* WIDE note: other mappings of '%' in the wide char set. */ - preci = -1; - argnumber[0] = 0; - argnumber[1] = 0; - argtype[0] = __PA_NOARG; - argtype[1] = __PA_NOARG; -#ifdef NL_ARGMAX - maxposarg = ppfs->maxposarg; -#endif /* NL_ARGMAX */ -#ifdef __UCLIBC_HAS_WCHAR__ - /* This is somewhat lame, but saves a lot of code. If we're dealing with - * a wide stream, that means the format is a wchar string. So, copy it - * char-by-char into a normal char buffer for processing. Make the buffer - * (buf) big enough so that any reasonable format specifier will fit. - * While there a legal specifiers that won't, the all involve duplicate - * flags or outrageous field widths/precisions. */ - width = dpoint = 0; - if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { - fmt = ppfs->fmtpos; - } else { - fmt = buf + 1; - i = 0; - do { - if ((buf[i] = (char) (((wchar_t *) ppfs->fmtpos)[i-1])) - != (((wchar_t *) ppfs->fmtpos)[i-1]) - ) { - return -1; - } - } while (buf[i++]); - buf[sizeof(buf)-1] = 0; - } -#else /* __UCLIBC_HAS_WCHAR__ */ - width = flags = dpoint = 0; - fmt = ppfs->fmtpos; -#endif /* __UCLIBC_HAS_WCHAR__ */ - - assert(fmt[-1] == '%'); - assert(fmt[0] != '%'); - - /* Process arg pos and/or flags and/or width and/or precision. */ - width_precision: - p = fmt; - if (*fmt == '*') { - argtype[-dpoint] = PA_INT; - ++fmt; - } - i = 0; - while (isdigit(*fmt)) { - if (i < MAX_FIELD_WIDTH) { /* Avoid overflow. */ - i = (i * 10) + (*fmt - '0'); - } - ++fmt; - } - if (p[-1] == '%') { /* Check for a position. */ - - /* TODO: if val not in range, then error */ - -#ifdef NL_ARGMAX - if ((*fmt == '$') && (i > 0)) {/* Positional spec. */ - ++fmt; - if (maxposarg == 0) { - return -1; - } - if ((argnumber[2] = i) > maxposarg) { - maxposarg = i; - } - /* Now fall through to check flags. */ - } else { - if (maxposarg > 0) { -#ifdef __STDIO_PRINTF_M_SUPPORT -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Support prec and width for %m when positional args used - /* Actually, positional arg processing will fail in general - * for specifiers that don't require an arg. */ -#endif /* __UCLIBC_MJN3_ONLY__ */ - if (*fmt == 'm') { - goto PREC_WIDTH; - } -#endif /* __STDIO_PRINTF_M_SUPPORT */ - return -1; - } - maxposarg = 0; /* Possible redundant store, but cuts size. */ - - if ((fmt > p) && (*p != '0')) { - goto PREC_WIDTH; - } - - fmt = p; /* Back up for possible '0's flag. */ - /* Now fall through to check flags. */ - } -#else /* NL_ARGMAX */ - if (*fmt == '$') { /* Positional spec. */ - return -1; - } - - if ((fmt > p) && (*p != '0')) { - goto PREC_WIDTH; - } - - fmt = p; /* Back up for possible '0's flag. */ - /* Now fall through to check flags. */ -#endif /* NL_ARGMAX */ - - restart_flags: /* Process flags. */ - i = 1; - p = spec_flags; - - do { - if (*fmt == *p++) { - ++fmt; - flags |= i; - goto restart_flags; - } - i += i; /* Better than i <<= 1 for bcc */ - } while (*p); - i = 0; - - /* If '+' then ignore ' ', and if '-' then ignore '0'. */ - /* Note: Need to ignore '0' when prec is an arg with val < 0, */ - /* but that test needs to wait until the arg is retrieved. */ - flags &= ~((flags & (FLAG_PLUS|FLAG_MINUS)) >> 1); - /* Note: Ignore '0' when prec is specified < 0 too (in printf). */ - - if (fmt[-1] != '%') { /* If we've done anything, loop for width. */ - goto width_precision; - } - } - PREC_WIDTH: - if (*p == '*') { /* Prec or width takes an arg. */ -#ifdef NL_ARGMAX - if (maxposarg) { - if ((*fmt++ != '$') || (i <= 0)) { - /* Using pos args and no $ or invalid arg number. */ - return -1; - } - argnumber[-dpoint] = i; - } else -#endif /* NL_ARGMAX */ - if (++p != fmt) { - /* Not using pos args but digits followed *. */ - return -1; - } - i = INT_MIN; - } - - if (!dpoint) { - width = i; - if (*fmt == '.') { - ++fmt; - dpoint = -1; /* To use as default precison. */ - goto width_precision; - } - } else { - preci = i; - } - - /* Process qualifier. */ - p = qual_chars; - do { - if (*fmt == *p) { - ++fmt; - break; - } - } while (*++p); - if ((p - qual_chars < 2) && (*fmt == *p)) { - p += ((sizeof(qual_chars)-2) / 2); - ++fmt; - } - dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8; - - /* Process conversion specifier. */ - if (!*fmt) { - return -1; - } - - p = spec_chars; - - do { - if (*fmt == *p) { - p_m_spec_chars = p - spec_chars; - - if ((p_m_spec_chars >= CONV_c) - && (dataargtype & PA_FLAG_LONG)) { - p_m_spec_chars -= 2; /* lc -> C and ls -> S */ - } - - ppfs->conv_num = p_m_spec_chars; - p = spec_ranges-1; - while (p_m_spec_chars > *++p) {} - - i = p - spec_ranges; - argtype[2] = (dataargtype | spec_or_mask[i]) & spec_and_mask[i]; - p = spec_chars; - break; - } - } while(*++p); - - ppfs->info.spec = *fmt; - ppfs->info.prec = preci; - ppfs->info.width = width; - ppfs->info.pad = ((flags & FLAG_ZERO) ? '0' : ' '); - ppfs->info._flags = (flags & ~FLAG_ZERO) | (dataargtype & __PA_INTMASK); - ppfs->num_data_args = 1; - - if (!*p) { -#ifdef __STDIO_PRINTF_M_SUPPORT - if (*fmt == 'm') { - ppfs->conv_num = CONV_m; - ppfs->num_data_args = 0; - goto DONE; - } -#endif -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF - - /* Handle custom arg -- WARNING -- overwrites p!!! */ - ppfs->conv_num = CONV_custom0; - p = _custom_printf_spec; - do { - if (*p == *fmt) { - if ((ppfs->num_data_args - = ((*_custom_printf_arginfo[(int)(p-_custom_printf_spec)]) - (&(ppfs->info), MAX_ARGS_PER_SPEC, argtype+2))) - > MAX_ARGS_PER_SPEC) { - break; /* Error -- too many args! */ - } - goto DONE; - } - } while (++p < (_custom_printf_spec + MAX_USER_SPEC)); -#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ - /* Otherwise error. */ - return -1; - } - -#if defined(__STDIO_GLIBC_CUSTOM_PRINTF) || defined(__STDIO_PRINTF_M_SUPPORT) - DONE: -#endif - -#ifdef NL_ARGMAX - if (maxposarg > 0) { - i = 0; - do { - /* Update maxposarg and check that NL_ARGMAX is not exceeded. */ - n = ((i <= 2) - ? (ppfs->argnumber[i] = argnumber[i]) - : argnumber[2] + (i-2)); - if (n > maxposarg) { - if ((maxposarg = n) > NL_ARGMAX) { - return -1; - } - } - --n; - /* Record argtype with largest size (current, new). */ - if (_is_equal_or_bigger_arg(ppfs->argtype[n], argtype[i])) { - ppfs->argtype[n] = argtype[i]; - } - } while (++i < ppfs->num_data_args + 2); - } else { -#endif /* NL_ARGMAX */ - ppfs->argnumber[2] = 1; - memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int)); -#ifdef NL_ARGMAX - } - - ppfs->maxposarg = maxposarg; -#endif /* NL_ARGMAX */ - -#ifdef __UCLIBC_HAS_WCHAR__ - if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { - ppfs->fmtpos = ++fmt; - } else { - ppfs->fmtpos = (const char *) (((const wchar_t *)(ppfs->fmtpos)) - + (fmt - buf) ); - } -#else /* __UCLIBC_HAS_WCHAR__ */ - ppfs->fmtpos = ++fmt; -#endif /* __UCLIBC_HAS_WCHAR__ */ - - return ppfs->num_data_args + 2; -} - -#endif -/**********************************************************************/ -#ifdef L_register_printf_function - -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF - -int register_printf_function(int spec, printf_function handler, - printf_arginfo_function arginfo) -{ - register char *r; - register char *p; - - if (spec && (arginfo != NULL)) { /* TODO -- check if spec is valid char */ - r = NULL; - p = _custom_printf_spec + MAX_USER_SPEC; - do { - --p; - if (!*p) { - r = p; - } -#ifdef __BCC__ - else /* bcc generates less code with fall-through */ -#endif - if (*p == spec) { - r = p; - p = _custom_printf_spec; - } - } while (p > _custom_printf_spec); - - if (r) { - if (handler) { - *r = spec; - _custom_printf_handler[(int)(r - p)] = handler; - _custom_printf_arginfo[(int)(r - p)] = arginfo; - } else { - *r = 0; - } - return 0; - } - /* TODO -- if asked to unregister a non-existent spec, return what? */ - } - return -1; -} - -#endif - -#endif -/**********************************************************************/ -#ifdef L_vsnprintf - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning WISHLIST: Implement vsnprintf for non-buffered and no custom stream case. -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#ifdef __STDIO_BUFFERS - -int vsnprintf(char *__restrict buf, size_t size, - const char * __restrict format, va_list arg) -{ - FILE f; - int rv; - -#ifdef __STDIO_GETC_MACRO - f.bufgetc = -#endif - f.bufpos = f.bufread = f.bufstart = buf; - - if (size > SIZE_MAX - (size_t) buf) { - size = SIZE_MAX - (size_t) buf; - } -#ifdef __STDIO_PUTC_MACRO - f.bufputc = -#endif - f.bufend = buf + size; - -#if 0 /* shouldn't be necessary */ -/* #ifdef __STDIO_GLIBC_CUSTOM_STREAMS */ - f.cookie = &(f.filedes); - f.gcs.read = 0; - f.gcs.write = 0; - f.gcs.seek = 0; - f.gcs.close = 0; -#endif - f.filedes = -2; /* for debugging */ - f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING); - -#ifdef __STDIO_MBSTATE - __INIT_MBSTATE(&(f.state)); -#endif /* __STDIO_MBSTATE */ - -#ifdef __STDIO_THREADSAFE - f.user_locking = 0; - __stdio_init_mutex(&f.lock); -#endif - - rv = vfprintf(&f, format, arg); - if (size) { - if (f.bufpos == f.bufend) { - --f.bufpos; - } - *f.bufpos = 0; - } - return rv; -} - -#elif defined(__USE_OLD_VFPRINTF__) - -typedef struct { - FILE f; - unsigned char *bufend; /* pointer to 1 past end of buffer */ - unsigned char *bufpos; -} __FILE_vsnprintf; - -int vsnprintf(char *__restrict buf, size_t size, - const char * __restrict format, va_list arg) -{ - __FILE_vsnprintf f; - int rv; - - f.bufpos = buf; - - if (size > SIZE_MAX - (size_t) buf) { - size = SIZE_MAX - (size_t) buf; - } - f.bufend = buf + size; - -#if 0 /* shouldn't be necessary */ -/* #ifdef __STDIO_GLIBC_CUSTOM_STREAMS */ - f.f.cookie = &(f.f.filedes); - f.f.gcs.read = 0; - f.f.gcs.write = 0; - f.f.gcs.seek = 0; - f.f.gcs.close = 0; -#endif - f.f.filedes = -2; /* for debugging */ - f.f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING); - -#ifdef __STDIO_MBSTATE - __INIT_MBSTATE(&(f.f.state)); -#endif /* __STDIO_MBSTATE */ - -#ifdef __STDIO_THREADSAFE - f.f.user_locking = 0; - __stdio_init_mutex(&f.f.lock); -#endif - - rv = vfprintf((FILE *) &f, format, arg); - if (size) { - if (f.bufpos == f.bufend) { - --f.bufpos; - } - *f.bufpos = 0; - } - return rv; -} - -#elif defined(__STDIO_GLIBC_CUSTOM_STREAMS) - -typedef struct { - size_t pos; - size_t len; - unsigned char *buf; - FILE *fp; -} __snpf_cookie; - -#define COOKIE ((__snpf_cookie *) cookie) - -static ssize_t snpf_write(register void *cookie, const char *buf, - size_t bufsize) -{ - size_t count; - register char *p; - - /* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */ - - if (COOKIE->len > COOKIE->pos) { - count = COOKIE->len - COOKIE->pos - 1; /* Leave space for nul. */ - if (count > bufsize) { - count = bufsize; - } - - p = COOKIE->buf + COOKIE->pos; - while (count) { - *p++ = *buf++; - --count; - } - *p = 0; - } - - COOKIE->pos += bufsize; - - return bufsize; -} - -#undef COOKIE - -int vsnprintf(char *__restrict buf, size_t size, - const char * __restrict format, va_list arg) -{ - FILE f; - __snpf_cookie cookie; - int rv; - - cookie.buf = buf; - cookie.len = size; - cookie.pos = 0; - cookie.fp = &f; - - f.cookie = &cookie; - f.gcs.write = snpf_write; - f.gcs.read = NULL; - f.gcs.seek = NULL; - f.gcs.close = NULL; - - f.filedes = -1; /* For debugging. */ - f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING); - -#ifdef __STDIO_MBSTATE - __INIT_MBSTATE(&(f.state)); -#endif /* __STDIO_MBSTATE */ - -#ifdef __STDIO_THREADSAFE - f.user_locking = 0; - __stdio_init_mutex(&f.lock); -#endif - - rv = vfprintf(&f, format, arg); - - return rv; -} - -#else -#warning Skipping vsnprintf since no buffering, no custom streams, and not old vfprintf! -#ifdef __STDIO_HAS_VSNPRINTF -#error WHOA! __STDIO_HAS_VSNPRINTF is defined! -#endif -#endif - -#endif -/**********************************************************************/ -#ifdef L_vdprintf - -int vdprintf(int filedes, const char * __restrict format, va_list arg) -{ - FILE f; - int rv; -#ifdef __STDIO_BUFFERS - char buf[64]; /* TODO: provide _optional_ buffering? */ - -#ifdef __STDIO_GETC_MACRO - f.bufgetc = -#endif - f.bufpos = f.bufread = f.bufstart = buf; -#ifdef __STDIO_PUTC_MACRO - f.bufputc = -#endif - f.bufend = buf + sizeof(buf); -#endif /* __STDIO_BUFFERS */ -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS - f.cookie = &(f.filedes); - f.gcs.read = _cs_read; - f.gcs.write = _cs_write; - f.gcs.seek = 0; /* The internal seek func handles normals. */ - f.gcs.close = _cs_close; -#endif - f.filedes = filedes; - f.modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING); - -#ifdef __STDIO_MBSTATE - __INIT_MBSTATE(&(f.state)); -#endif /* __STDIO_MBSTATE */ - -#ifdef __STDIO_THREADSAFE - f.user_locking = 0; - __stdio_init_mutex(&f.lock); -#endif - - rv = vfprintf(&f, format, arg); - - return fflush(&f) ? -1 : rv; -} - -#endif -/**********************************************************************/ -#ifdef L_vasprintf - -#ifndef __STDIO_HAS_VSNPRINTF -#warning Skipping vasprintf since no vsnprintf! -#else - -int vasprintf(char **__restrict buf, const char * __restrict format, - va_list arg) -{ - /* TODO -- change the default implementation? */ -#ifndef __STDIO_GLIBC_CUSTOM_STREAMS - /* This implementation actually calls the printf machinery twice, but only - * only does one malloc. This can be a problem though when custom printf - * specs or the %m specifier are involved because the results of the - * second call might be different from the first. */ - va_list arg2; - int rv; - - va_copy(arg2, arg); - rv = vsnprintf(NULL, 0, format, arg2); - va_end(arg2); - - return (((rv >= 0) && ((*buf = malloc(++rv)) != NULL)) - ? vsnprintf(*buf, rv, format, arg) - : -1); -#else - FILE *f; - size_t size; - int rv; - - /* TODO - do the memstream stuff inline here to avoid fclose and the openlist? */ - if ((f = open_memstream(buf, &size)) == NULL) { - return -1; - } - rv = vfprintf(f, format, arg); - fclose(f); - if (rv < 0) { - free(*buf); - *buf = NULL; - return -1; - } - return rv; -#endif -} -#endif -#endif -/**********************************************************************/ -#ifdef L_vprintf -int vprintf(const char * __restrict format, va_list arg) -{ - return vfprintf(stdout, format, arg); -} -#endif -/**********************************************************************/ -#ifdef L_vsprintf - -#ifndef __STDIO_HAS_VSNPRINTF -#warning Skipping vsprintf since no vsnprintf! -#else - -int vsprintf(char *__restrict buf, const char * __restrict format, - va_list arg) -{ - return vsnprintf(buf, SIZE_MAX, format, arg); -} - -#endif -#endif -/**********************************************************************/ -#ifdef L_fprintf - -int fprintf(FILE * __restrict stream, const char * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vfprintf(stream, format, arg); - va_end(arg); - - return rv; -} - -#endif -/**********************************************************************/ -#ifdef L_snprintf - -#ifndef __STDIO_HAS_VSNPRINTF -#warning Skipping snprintf since no vsnprintf! -#else - -int snprintf(char *__restrict buf, size_t size, - const char * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vsnprintf(buf, size, format, arg); - va_end(arg); - return rv; -} - -#endif -#endif -/**********************************************************************/ -#ifdef L_dprintf - -int dprintf(int filedes, const char * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vdprintf(filedes, format, arg); - va_end(arg); - - return rv; -} - -#endif -/**********************************************************************/ -#ifdef L_asprintf - -#ifndef __STDIO_HAS_VSNPRINTF -#warning Skipping asprintf and __asprintf since no vsnprintf! -#else - -weak_alias(__asprintf,asprintf) - -int __asprintf(char **__restrict buf, const char * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vasprintf(buf, format, arg); - va_end(arg); - - return rv; -} - -#endif -#endif -/**********************************************************************/ -#ifdef L_printf int printf(const char * __restrict format, ...) { va_list arg; @@ -1588,1685 +19,3 @@ int printf(const char * __restrict format, ...) return rv; } -#endif -/**********************************************************************/ -#ifdef L_sprintf - -#ifndef __STDIO_HAS_VSNPRINTF -#warning Skipping sprintf since no vsnprintf! -#else - -int sprintf(char *__restrict buf, const char * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vsnprintf(buf, SIZE_MAX, format, arg); - va_end(arg); - - return rv; -} - -#endif -#endif -/**********************************************************************/ -#ifdef L_vswprintf - -#ifdef __STDIO_BUFFERS -int vswprintf(wchar_t *__restrict buf, size_t size, - const wchar_t * __restrict format, va_list arg) -{ - FILE f; - int rv; - -#ifdef __STDIO_GETC_MACRO - f.bufgetc = -#endif - f.bufpos = f.bufread = f.bufstart = (char *) buf; - -/* if (size > SIZE_MAX - (size_t) buf) { */ -/* size = SIZE_MAX - (size_t) buf; */ -/* } */ -#ifdef __STDIO_PUTC_MACRO - f.bufputc = -#endif - f.bufend = (char *)(buf + size); - -#if 0 /* shouldn't be necessary */ -/* #ifdef __STDIO_GLIBC_CUSTOM_STREAMS */ - f.cookie = &(f.filedes); - f.gcs.read = 0; - f.gcs.write = 0; - f.gcs.seek = 0; - f.gcs.close = 0; -#endif - f.filedes = -3; /* for debugging */ - f.modeflags = (__FLAG_WIDE|__FLAG_WRITEONLY|__FLAG_WRITING); - -#ifdef __STDIO_MBSTATE - __INIT_MBSTATE(&(f.state)); -#endif /* __STDIO_MBSTATE */ - -#ifdef __STDIO_THREADSAFE - f.user_locking = 0; - __stdio_init_mutex(&f.lock); -#endif - - rv = vfwprintf(&f, format, arg); - - /* NOTE: Return behaviour differs from snprintf... */ - if (f.bufpos == f.bufend) { - rv = -1; - if (size) { - f.bufpos = (char *)(((wchar_t *) f.bufpos) - 1); - } - } - if (size) { - *((wchar_t *) f.bufpos) = 0; - } - return rv; -} -#else /* __STDIO_BUFFERS */ -#warning Skipping vswprintf since no buffering! -#endif /* __STDIO_BUFFERS */ -#endif -/**********************************************************************/ -#ifdef L_swprintf -#ifdef __STDIO_BUFFERS - -int swprintf(wchar_t *__restrict buf, size_t size, - const wchar_t * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vswprintf(buf, size, format, arg); - va_end(arg); - return rv; -} - -#else /* __STDIO_BUFFERS */ -#warning Skipping vsWprintf since no buffering! -#endif /* __STDIO_BUFFERS */ -#endif -/**********************************************************************/ -#ifdef L_fwprintf - -int fwprintf(FILE * __restrict stream, const wchar_t * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vfwprintf(stream, format, arg); - va_end(arg); - - return rv; -} - -#endif -/**********************************************************************/ -#ifdef L_vwprintf -int vwprintf(const wchar_t * __restrict format, va_list arg) -{ - return vfwprintf(stdout, format, arg); -} -#endif -/**********************************************************************/ -#ifdef L_wprintf -int wprintf(const wchar_t * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vfwprintf(stdout, format, arg); - va_end(arg); - - return rv; -} -#endif -/**********************************************************************/ -#ifdef L__fpmaxtostr - -/* Copyright (C) 2000, 2001, 2003 Manuel Novoa III - * - * Function: - * - * size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, - * __fp_outfunc_t fp_outfunc); - * - * This is derived from the old _dtostr, whic I wrote for uClibc to provide - * floating point support for the printf functions. It handles +/- infinity, - * nan, and signed 0 assuming you have ieee arithmetic. It also now handles - * digit grouping (for the uClibc supported locales) and hexadecimal float - * notation. Finally, via the fp_outfunc parameter, it now supports wide - * output. - * - * Notes: - * - * At most DECIMAL_DIG significant digits are kept. Any trailing digits - * are treated as 0 as they are really just the results of rounding noise - * anyway. If you want to do better, use an arbitary precision arithmetic - * package. ;-) - * - * It should also be fairly portable, as no assumptions are made about the - * bit-layout of doubles. Of course, that does make it less efficient than - * it could be. - * - */ - -/*****************************************************************************/ -/* Don't change anything that follows unless you know what you're doing. */ -/*****************************************************************************/ -/* Fairly portable nan check. Bitwise for i386 generated larger code. - * If you have a better version, comment this out. - */ -#define isnan(x) ((x) != (x)) - -/* Without seminumerical functions to examine the sign bit, this is - * about the best we can do to test for '-0'. - */ -#define zeroisnegative(x) ((1./(x)) < 0) - -/*****************************************************************************/ -/* Don't change anything that follows peroid!!! ;-) */ -/*****************************************************************************/ -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ -#if FLT_RADIX != 2 -#error FLT_RADIX != 2 is not currently supported -#endif -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - -#define NUM_HEX_DIGITS ((FPMAX_MANT_DIG + 3)/ 4) - -/* WARNING: Adjust _fp_out_wide() below if this changes! */ -/* With 32 bit ints, we can get 9 decimal digits per block. */ -#define DIGITS_PER_BLOCK 9 -#define HEX_DIGITS_PER_BLOCK 8 - -/* Maximum number of subcases to output double is... - * 0 - sign - * 1 - padding and initial digit - * 2 - digits left of the radix - * 3 - 0s left of the radix or radix - * 4 - radix or digits right of the radix - * 5 - 0s right of the radix - * 6 - exponent - * 7 - trailing space padding - * although not all cases may occur. - */ -#define MAX_CALLS 8 - -/*****************************************************************************/ - -#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) -#define NUM_HEX_DIGIT_BLOCKS \ - ((NUM_HEX_DIGITS+HEX_DIGITS_PER_BLOCK-1)/HEX_DIGITS_PER_BLOCK) - -/* WARNING: Adjust _fp_out_wide() below if this changes! */ - -/* extra space for '-', '.', 'e+###', and nul */ -#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) - -/*****************************************************************************/ - -static const char fmt[] = "inf\0INF\0nan\0NAN\0.\0,"; - -#define INF_OFFSET 0 /* must be 1st */ -#define NAN_OFFSET 8 /* must be 2nd.. see hex sign handling */ -#define DECPT_OFFSET 16 -#define THOUSEP_OFFSET 18 - -#define EMPTY_STRING_OFFSET 3 - -/*****************************************************************************/ -#if FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP -#error scaling code can not handle FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP -#endif - -static const __fpmax_t exp10_table[] = -{ - 1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, /* floats */ -#if FPMAX_MAX_10_EXP < 32 -#error unsupported FPMAX_MAX_10_EXP (< 32). ANSI/ISO C requires >= 37. -#endif -#if FPMAX_MAX_10_EXP >= 64 - 1e64L, -#endif -#if FPMAX_MAX_10_EXP >= 128 - 1e128L, -#endif -#if FPMAX_MAX_10_EXP >= 256 - 1e256L, -#endif -#if FPMAX_MAX_10_EXP >= 512 - 1e512L, -#endif -#if FPMAX_MAX_10_EXP >= 1024 - 1e1024L, -#endif -#if FPMAX_MAX_10_EXP >= 2048 - 1e2048L, -#endif -#if FPMAX_MAX_10_EXP >= 4096 - 1e4096L -#endif -#if FPMAX_MAX_10_EXP >= 8192 -#error unsupported FPMAX_MAX_10_EXP. please increase table -#endif -}; - -#define EXP10_TABLE_SIZE (sizeof(exp10_table)/sizeof(exp10_table[0])) -#define EXP10_TABLE_MAX (1U<<(EXP10_TABLE_SIZE-1)) - -/*****************************************************************************/ -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - -#if FLT_RADIX != 2 -#error FLT_RADIX != 2 is not currently supported -#endif - -#if FPMAX_MAX_EXP < -FPMAX_MIN_EXP -#error scaling code can not handle FPMAX_MAX_EXP < -FPMAX_MIN_EXP -#endif - -static const __fpmax_t exp16_table[] = { - 0x1.0p4L, 0x1.0p8L, 0x1.0p16L, 0x1.0p32L, 0x1.0p64L, -#if FPMAX_MAX_EXP >= 128 - 0x1.0p128L, -#endif -#if FPMAX_MAX_EXP >= 256 - 0x1.0p256L, -#endif -#if FPMAX_MAX_EXP >= 512 - 0x1.0p512L, -#endif -#if FPMAX_MAX_EXP >= 1024 - 0x1.0p1024L, -#endif -#if FPMAX_MAX_EXP >= 2048 - 0x1.0p2048L, -#endif -#if FPMAX_MAX_EXP >= 4096 - 0x1.0p4096L, -#endif -#if FPMAX_MAX_EXP >= 8192 - 0x1.0p8192L, -#endif -#if FPMAX_MAX_EXP >= 16384 - 0x1.0p16384L -#endif -#if FPMAX_MAX_EXP >= 32768 -#error unsupported FPMAX_MAX_EXP. please increase table -#endif -}; - -#define EXP16_TABLE_SIZE (sizeof(exp16_table)/sizeof(exp16_table[0])) -#define EXP16_TABLE_MAX (1U<<(EXP16_TABLE_SIZE-1)) - -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ -/*****************************************************************************/ - -#define FPO_ZERO_PAD (0x80 | '0') -#define FPO_STR_WIDTH (0x80 | ' '); -#define FPO_STR_PREC 'p' - -size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, - __fp_outfunc_t fp_outfunc) -{ -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - __fpmax_t lower_bnd; - __fpmax_t upper_bnd = 1e9; -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - uint_fast32_t digit_block; -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - uint_fast32_t base = 10; - const __fpmax_t *power_table; - int dpb = DIGITS_PER_BLOCK; - int ndb = NUM_DIGIT_BLOCKS; - int nd = DECIMAL_DIG; - int sufficient_precision = 0; -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - int num_groups = 0; - int initial_group; /* This does not need to be initialized. */ - int tslen; /* This does not need to be initialized. */ - int nblk2; /* This does not need to be initialized. */ - const char *ts; /* This does not need to be initialized. */ -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ - int i, j; - int round, o_exp; - int exp, exp_neg; - int width, preci; - int cnt; - char *s; - char *e; - intptr_t pc_fwi[3*MAX_CALLS]; - intptr_t *ppc; - intptr_t *ppc_last; -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: The size of exp_buf[] should really be determined by the float constants. -#endif /* __UCLIBC_MJN3_ONLY__ */ - char exp_buf[16]; - char buf[BUF_SIZE]; - char sign_str[6]; /* Last 2 are for 1st digit + nul. */ - char o_mode; - char mode; - - - width = info->width; - preci = info->prec; - mode = info->spec; - - *exp_buf = 'e'; - if ((mode|0x20) == 'a') { -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - *exp_buf = 'p'; - if (preci < 0) { - preci = NUM_HEX_DIGITS; - sufficient_precision = 1; - } -#else - mode += ('g' - 'a'); -#endif - } - - if (preci < 0) { - preci = 6; - } - - *sign_str = '\0'; - if (PRINT_INFO_FLAG_VAL(info,showsign)) { - *sign_str = '+'; - } else if (PRINT_INFO_FLAG_VAL(info,space)) { - *sign_str = ' '; - } - - *(sign_str+1) = 0; - pc_fwi[5] = INF_OFFSET; - if (isnan(x)) { /* First, check for nan. */ - pc_fwi[5] = NAN_OFFSET; - goto INF_NAN; - } - - if (x == 0) { /* Handle 0 now to avoid false positive. */ -#if 1 - if (zeroisnegative(x)) { /* Handle 'signed' zero. */ - *sign_str = '-'; - } -#endif - exp = -1; - goto GENERATE_DIGITS; - } - - if (x < 0) { /* Convert negatives to positives. */ - *sign_str = '-'; - x = -x; - } - - if (__FPMAX_ZERO_OR_INF_CHECK(x)) { /* Inf since zero handled above. */ - INF_NAN: - info->pad = ' '; - ppc = pc_fwi + 6; - pc_fwi[3] = FPO_STR_PREC; - pc_fwi[4] = 3; - if (mode < 'a') { - pc_fwi[5] += 4; - } - pc_fwi[5] = (intptr_t)(fmt + pc_fwi[5]); - goto EXIT_SPECIAL; - } - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Clean up defines when hexadecimal float notation is unsupported. -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - - if ((mode|0x20) == 'a') { - lower_bnd = 0x1.0p31L; - upper_bnd = 0x1.0p32L; - power_table = exp16_table; - exp = HEX_DIGITS_PER_BLOCK - 1; - i = EXP16_TABLE_SIZE; - j = EXP16_TABLE_MAX; - dpb = HEX_DIGITS_PER_BLOCK; - ndb = NUM_HEX_DIGIT_BLOCKS; - nd = NUM_HEX_DIGITS; - base = 16; - } else { - lower_bnd = 1e8; -/* upper_bnd = 1e9; */ - power_table = exp10_table; - exp = DIGITS_PER_BLOCK - 1; - i = EXP10_TABLE_SIZE; - j = EXP10_TABLE_MAX; -/* dpb = DIGITS_PER_BLOCK; */ -/* ndb = NUM_DIGIT_BLOCKS; */ -/* base = 10; */ - } - - - -#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - -#define lower_bnd 1e8 -#define upper_bnd 1e9 -#define power_table exp10_table -#define dpb DIGITS_PER_BLOCK -#define base 10 -#define ndb NUM_DIGIT_BLOCKS -#define nd DECIMAL_DIG - - exp = DIGITS_PER_BLOCK - 1; - i = EXP10_TABLE_SIZE; - j = EXP10_TABLE_MAX; - -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - - exp_neg = 0; - if (x < lower_bnd) { /* Do we need to scale up or down? */ - exp_neg = 1; - } - - do { - --i; - if (exp_neg) { - if (x * power_table[i] < upper_bnd) { - x *= power_table[i]; - exp -= j; - } - } else { - if (x / power_table[i] >= lower_bnd) { - x /= power_table[i]; - exp += j; - } - } - j >>= 1; - } while (i); - if (x >= upper_bnd) { /* Handle bad rounding case. */ - x /= power_table[0]; - ++exp; - } - assert(x < upper_bnd); - - GENERATE_DIGITS: - s = buf + 2; /* Leave space for '\0' and '0'. */ - i = 0; - do { - digit_block = (uint_fast32_t) x; - assert(digit_block < upper_bnd); -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: Can rounding be a problem? -#endif /* __UCLIBC_MJN3_ONLY__ */ - x = (x - digit_block) * upper_bnd; - s += dpb; - j = 0; - do { - s[- ++j] = '0' + (digit_block % base); - digit_block /= base; - } while (j < dpb); - } while (++i < ndb); - - /*************************************************************************/ - - if (mode < 'a') { - *exp_buf -= ('a' - 'A'); /* e->E and p->P */ - mode += ('a' - 'A'); - } - - o_mode = mode; - if ((mode == 'g') && (preci > 0)){ - --preci; - } - round = preci; - - if (mode == 'f') { - round += exp; - if (round < -1) { - memset(buf, '0', DECIMAL_DIG); /* OK, since 'f' -> decimal case. */ - exp = -1; - round = -1; - } - } - - s = buf; - *s++ = 0; /* Terminator for rounding and 0-triming. */ - *s = '0'; /* Space to round. */ - - i = 0; - e = s + nd + 1; - if (round < nd) { - e = s + round + 2; - if (*e >= '0' + (base/2)) { /* NOTE: We always round away from 0! */ - i = 1; - } - } - - do { /* Handle rounding and trim trailing 0s. */ - *--e += i; /* Add the carry. */ - } while ((*e == '0') || (*e > '0' - 1 + base)); - -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - if ((mode|0x20) == 'a') { - char *q; - - for (q = e ; *q ; --q) { - if (*q > '9') { - *q += (*exp_buf - ('p' - 'a') - '9' - 1); - } - } - - if (e > s) { - exp *= 4; /* Change from base 16 to base 2. */ - } - } -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - - o_exp = exp; - if (e <= s) { /* We carried into an extra digit. */ - ++o_exp; - e = s; /* Needed if all 0s. */ - } else { - ++s; - } - *++e = 0; /* Terminating nul char. */ - - if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) { - mode = 'f'; - preci = round - o_exp; - } - - exp = o_exp; - if (mode != 'f') { - o_exp = 0; - } - - if (o_exp < 0) { /* Exponent is < 0, so */ - *--s = '0'; /* fake the first 0 digit. */ - } - - pc_fwi[3] = FPO_ZERO_PAD; - pc_fwi[4] = 1; - pc_fwi[5] = (intptr_t)(sign_str + 4); - sign_str[4] = *s++; - sign_str[5] = 0; - ppc = pc_fwi + 6; - - i = e - s; /* Total digits is 'i'. */ - if (o_exp >= 0) { -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - - const char *p; - - if (PRINT_INFO_FLAG_VAL(info,group) - && *(p = __UCLIBC_CURLOCALE_DATA.grouping) - ) { - int nblk1; - - nblk2 = nblk1 = *p; - if (*++p) { - nblk2 = *p; - assert(!*++p); - } - - if (o_exp >= nblk1) { - num_groups = (o_exp - nblk1) / nblk2 + 1; - initial_group = (o_exp - nblk1) % nblk2; - -#ifdef __UCLIBC_HAS_WCHAR__ - if (PRINT_INFO_FLAG_VAL(info,wide)) { - /* _fp_out_wide() will fix this up. */ - ts = fmt + THOUSEP_OFFSET; - tslen = 1; - } else { -#endif /* __UCLIBC_HAS_WCHAR__ */ - ts = __UCLIBC_CURLOCALE_DATA.thousands_sep; - tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len; -#ifdef __UCLIBC_HAS_WCHAR__ - } -#endif /* __UCLIBC_HAS_WCHAR__ */ - - width -= num_groups * tslen; - } - } - - -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ - ppc[0] = FPO_STR_PREC; - ppc[2] = (intptr_t)(s); - if (o_exp >= i) { /* all digit(s) left of decimal */ - ppc[1] = i; - ppc += 3; - o_exp -= i; - i = 0; - if (o_exp>0) { /* have 0s left of decimal */ - ppc[0] = FPO_ZERO_PAD; - ppc[1] = o_exp; - ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); - ppc += 3; - } - } else if (o_exp > 0) { /* decimal between digits */ - ppc[1] = o_exp; - ppc += 3; - s += o_exp; - i -= o_exp; - } - o_exp = -1; - } - - if (PRINT_INFO_FLAG_VAL(info,alt) - || (i) - || ((o_mode != 'g') -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - && (o_mode != 'a') -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - && (preci > 0)) - ) { - ppc[0] = FPO_STR_PREC; -#ifdef __LOCALE_C_ONLY - ppc[1] = 1; - ppc[2] = (intptr_t)(fmt + DECPT_OFFSET); -#else /* __LOCALE_C_ONLY */ -#ifdef __UCLIBC_HAS_WCHAR__ - if (PRINT_INFO_FLAG_VAL(info,wide)) { - /* _fp_out_wide() will fix this up. */ - ppc[1] = 1; - ppc[2] = (intptr_t)(fmt + DECPT_OFFSET); - } else { -#endif /* __UCLIBC_HAS_WCHAR__ */ - ppc[1] = __UCLIBC_CURLOCALE_DATA.decimal_point_len; - ppc[2] = (intptr_t)(__UCLIBC_CURLOCALE_DATA.decimal_point); -#ifdef __UCLIBC_HAS_WCHAR__ - } -#endif /* __UCLIBC_HAS_WCHAR__ */ -#endif /* __LOCALE_C_ONLY */ - ppc += 3; - } - - if (++o_exp < 0) { /* Have 0s right of decimal. */ - ppc[0] = FPO_ZERO_PAD; - ppc[1] = -o_exp; - ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); - ppc += 3; - } - if (i) { /* Have digit(s) right of decimal. */ - ppc[0] = FPO_STR_PREC; - ppc[1] = i; - ppc[2] = (intptr_t)(s); - ppc += 3; - } - - if (((o_mode != 'g') || PRINT_INFO_FLAG_VAL(info,alt)) -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - && !sufficient_precision -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - ) { - i -= o_exp; - if (i < preci) { /* Have 0s right of digits. */ - i = preci - i; - ppc[0] = FPO_ZERO_PAD; - ppc[1] = i; - ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); - ppc += 3; - } - } - - /* Build exponent string. */ - if (mode != 'f') { - char *p = exp_buf + sizeof(exp_buf); - char exp_char = *exp_buf; - char exp_sign = '+'; -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - int min_exp_dig_plus_2 = ((o_mode != 'a') ? (2+2) : (2+1)); -#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ -#define min_exp_dig_plus_2 (2+2) -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - - if (exp < 0) { - exp_sign = '-'; - exp = -exp; - } - - *--p = 0; /* nul-terminate */ - j = 2; /* Count exp_char and exp_sign. */ - do { - *--p = '0' + (exp % 10); - exp /= 10; - } while ((++j < min_exp_dig_plus_2) || exp); /* char+sign+mindigits */ - *--p = exp_sign; - *--p = exp_char; - - ppc[0] = FPO_STR_PREC; - ppc[1] = j; - ppc[2] = (intptr_t)(p); - ppc += 3; - } - - EXIT_SPECIAL: - ppc_last = ppc; - ppc = pc_fwi + 4; /* Need width fields starting with second. */ - do { - width -= *ppc; - ppc += 3; - } while (ppc < ppc_last); - - ppc = pc_fwi; - ppc[0] = FPO_STR_WIDTH; - ppc[1] = i = ((*sign_str) != 0); - ppc[2] = (intptr_t) sign_str; - -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - if (((mode|0x20) == 'a') && (pc_fwi[3] >= 16)) { /* Hex sign handling. */ - /* Hex and not inf or nan, so prefix with 0x. */ - char *h = sign_str + i; - *h = '0'; - *++h = 'x' - 'p' + *exp_buf; - *++h = 0; - ppc[1] = (i += 2); - } -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - - if ((width -= i) > 0) { - if (PRINT_INFO_FLAG_VAL(info,left)) { /* Left-justified. */ - ppc_last[0] = FPO_STR_WIDTH; - ppc_last[1] = width; - ppc_last[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); - ppc_last += 3; - } else if (info->pad == '0') { /* 0 padding */ - ppc[4] += width; /* Pad second field. */ - } else { - ppc[1] += width; /* Pad first (sign) field. */ - } - } - - cnt = 0; - - do { -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - - if ((ppc == pc_fwi + 6) && num_groups) { - const char *gp = (const char *) ppc[2]; - int len = ppc[1]; - int blk = initial_group; - - cnt += num_groups * tslen; /* Adjust count now for sep chars. */ - -/* printf("\n"); */ - do { - if (!blk) { /* Initial group could be 0 digits long! */ - blk = nblk2; - } else if (len >= blk) { /* Enough digits for a group. */ -/* printf("norm: len=%d blk=%d \"%.*s\"\n", len, blk, blk, gp); */ - fp_outfunc(fp, *ppc, blk, (intptr_t) gp); - assert(gp); - if (*gp) { - gp += blk; - } - len -= blk; - } else { /* Transition to 0s. */ -/* printf("trans: len=%d blk=%d \"%.*s\"\n", len, blk, len, gp); */ - if (len) { -/* printf("len\n"); */ - fp_outfunc(fp, *ppc, len, (intptr_t) gp); - gp += len; - } - - if (ppc[3] == FPO_ZERO_PAD) { /* Need to group 0s */ -/* printf("zeropad\n"); */ - cnt += ppc[1]; - ppc += 3; - gp = (const char *) ppc[2]; - blk -= len; /* blk > len, so blk still > 0. */ - len = ppc[1]; - continue; /* Don't decrement num_groups here. */ - } else { - assert(num_groups == 0); - break; - } - } - - if (num_groups <= 0) { - break; - } - --num_groups; - - fp_outfunc(fp, FPO_STR_PREC, tslen, (intptr_t) ts); - blk = nblk2; - -/* printf("num_groups=%d blk=%d\n", num_groups, blk); */ - - } while (1); - } else - -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ - - fp_outfunc(fp, *ppc, ppc[1], ppc[2]); /* NOTE: Remember 'else' above! */ - - cnt += ppc[1]; - ppc += 3; - } while (ppc < ppc_last); - - return cnt; -} - -#endif -/**********************************************************************/ -#ifdef L__store_inttype - -/* Right now, we assume intmax_t is either long or long long */ - -#ifdef INTMAX_MAX - -#ifdef LLONG_MAX - -#if INTMAX_MAX > LLONG_MAX -#error INTMAX_MAX > LLONG_MAX! The printf code needs to be updated! -#endif - -#elif INTMAX_MAX > LONG_MAX - -#error No LLONG_MAX and INTMAX_MAX > LONG_MAX! The printf code needs to be updated! - -#endif /* LLONG_MAX */ - -#endif /* INTMAX_MAX */ - -/* We assume int may be short or long, but short and long are different. */ - -void _store_inttype(register void *dest, int desttype, uintmax_t val) -{ - if (desttype == __PA_FLAG_CHAR) { /* assume char not int */ - *((unsigned char *) dest) = val; - return; - } -#if defined(LLONG_MAX) && (LONG_MAX != LLONG_MAX) - if (desttype == PA_FLAG_LONG_LONG) { - *((unsigned long long int *) dest) = val; - return; - } -#endif /* LLONG_MAX */ -#if SHRT_MAX != INT_MAX - if (desttype == PA_FLAG_SHORT) { - *((unsigned short int *) dest) = val; - return; - } -#endif /* SHRT_MAX */ -#if LONG_MAX != INT_MAX - if (desttype == PA_FLAG_LONG) { - *((unsigned long int *) dest) = val; - return; - } -#endif /* LONG_MAX */ - - *((unsigned int *) dest) = val; -} - -#endif -/**********************************************************************/ -#ifdef L__load_inttype - -extern uintmax_t _load_inttype(int desttype, register const void *src, - int uflag) -{ - if (uflag >= 0) { /* unsigned */ -#if LONG_MAX != INT_MAX - if (desttype & (PA_FLAG_LONG|PA_FLAG_LONG_LONG)) { -#ifdef LLONG_MAX - if (desttype == PA_FLAG_LONG_LONG) { - return *((unsigned long long int *) src); - } -#endif - return *((unsigned long int *) src); - } -#else /* LONG_MAX != INT_MAX */ -#ifdef LLONG_MAX - if (desttype & PA_FLAG_LONG_LONG) { - return *((unsigned long long int *) src); - } -#endif -#endif /* LONG_MAX != INT_MAX */ - { - unsigned int x; - x = *((unsigned int *) src); - if (desttype == __PA_FLAG_CHAR) x = (unsigned char) x; -#if SHRT_MAX != INT_MAX - if (desttype == PA_FLAG_SHORT) x = (unsigned short int) x; -#endif - return x; - } - } else { /* signed */ -#if LONG_MAX != INT_MAX - if (desttype & (PA_FLAG_LONG|PA_FLAG_LONG_LONG)) { -#ifdef LLONG_MAX - if (desttype == PA_FLAG_LONG_LONG) { - return *((long long int *) src); - } -#endif - return *((long int *) src); - } -#else /* LONG_MAX != INT_MAX */ -#ifdef LLONG_MAX - if (desttype & PA_FLAG_LONG_LONG) { - return *((long long int *) src); - } -#endif -#endif /* LONG_MAX != INT_MAX */ - { - int x; - x = *((int *) src); - if (desttype == __PA_FLAG_CHAR) x = (char) x; -#if SHRT_MAX != INT_MAX - if (desttype == PA_FLAG_SHORT) x = (short int) x; -#endif - return x; - } - } -} - -#endif -/**********************************************************************/ -#if defined(L_vfprintf) || defined(L_vfwprintf) - -/* We only support ascii digits (or their USC equivalent codes) in - * precision and width settings in *printf (wide) format strings. - * In other words, we don't currently support glibc's 'I' flag. - * We do accept it, but it is currently ignored. */ - -static void _charpad(FILE * __restrict stream, int padchar, size_t numpad); - -#ifdef L_vfprintf - -#define VFPRINTF vfprintf -#define FMT_TYPE char -#define OUTNSTR _outnstr -#define STRLEN strlen -#define _PPFS_init _ppfs_init -#define OUTPUT(F,S) fputs(S,F) -#define _outnstr(stream, string, len) _stdio_fwrite(string, len, stream) -#define FP_OUT _fp_out_narrow - -#ifdef __STDIO_PRINTF_FLOAT - -static void _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) -{ - if (type & 0x80) { /* Some type of padding needed. */ - int buflen = strlen((const char *) buf); - if ((len -= buflen) > 0) { - _charpad(fp, (type & 0x7f), len); - } - len = buflen; - } - OUTNSTR(fp, (const char *) buf, len); -} - -#endif /* __STDIO_PRINTF_FLOAT */ - -#else /* L_vfprintf */ - -#define VFPRINTF vfwprintf -#define FMT_TYPE wchar_t -#define OUTNSTR _outnwcs -#define STRLEN wcslen -#define _PPFS_init _ppwfs_init -#define OUTPUT(F,S) fputws(S,F) -#define _outnwcs(stream, wstring, len) _wstdio_fwrite(wstring, len, stream) -#define FP_OUT _fp_out_wide - -static void _outnstr(FILE *stream, const char *s, size_t wclen) -{ - /* NOTE!!! len here is the number of wchars we want to generate!!! */ - wchar_t wbuf[64]; - mbstate_t mbstate; - size_t todo, r; - - mbstate.mask = 0; - todo = wclen; - - while (todo) { - r = mbsrtowcs(wbuf, &s, - ((todo <= sizeof(wbuf)/sizeof(wbuf[0])) - ? todo - : sizeof(wbuf)/sizeof(wbuf[0])), - &mbstate); - assert(((ssize_t)r) > 0); - _outnwcs(stream, wbuf, r); - todo -= r; - } -} - -#ifdef __STDIO_PRINTF_FLOAT - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Move defines from _fpmaxtostr. Put them in a common header. -#endif - -/* The following defines are from _fpmaxtostr.*/ -#define DIGITS_PER_BLOCK 9 -#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) -#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) - -static void _fp_out_wide(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) -{ - wchar_t wbuf[BUF_SIZE]; - const char *s = (const char *) buf; - int i; - - if (type & 0x80) { /* Some type of padding needed */ - int buflen = strlen(s); - if ((len -= buflen) > 0) { - _charpad(fp, (type & 0x7f), len); - } - len = buflen; - } - - if (len > 0) { - i = 0; - do { -#ifdef __LOCALE_C_ONLY - wbuf[i] = s[i]; -#else /* __LOCALE_C_ONLY */ - -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - if (s[i] == ',') { - wbuf[i] = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc; - } else -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ - if (s[i] == '.') { - wbuf[i] = __UCLIBC_CURLOCALE_DATA.decimal_point_wc; - } else { - wbuf[i] = s[i]; - } -#endif /* __LOCALE_C_ONLY */ - - } while (++i < len); - - OUTNSTR(fp, wbuf, len); - } -} - -#endif /* __STDIO_PRINTF_FLOAT */ - -static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0) -{ - static const wchar_t invalid_wcs[] = L"Invalid wide format string."; - int r; - - /* First, zero out everything... argnumber[], argtype[], argptr[] */ - memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */ -#ifdef NL_ARGMAX - --ppfs->maxposarg; /* set to -1 */ -#endif /* NL_ARGMAX */ - ppfs->fmtpos = (const char *) fmt0; - ppfs->info._flags = FLAG_WIDESTREAM; - - { - mbstate_t mbstate; - const wchar_t *p; - mbstate.mask = 0; /* Initialize the mbstate. */ - p = fmt0; - if (wcsrtombs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { - ppfs->fmtpos = (const char *) invalid_wcs; - return -1; - } - } - - /* now set all argtypes to no-arg */ - { -#if 1 - /* TODO - use memset here since already "paid for"? */ - register int *p = ppfs->argtype; - - r = MAX_ARGS; - do { - *p++ = __PA_NOARG; - } while (--r); -#else - /* TODO -- get rid of this?? */ - register char *p = (char *) ((MAX_ARGS-1) * sizeof(int)); - - do { - *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG; - p -= sizeof(int); - } while (p); -#endif - } - - /* - * Run through the entire format string to validate it and initialize - * the positional arg numbers (if any). - */ - { - register const wchar_t *fmt = fmt0; - - while (*fmt) { - if ((*fmt == '%') && (*++fmt != '%')) { - ppfs->fmtpos = (const char *) fmt; /* back up to the '%' */ - if ((r = _ppfs_parsespec(ppfs)) < 0) { - return -1; - } - fmt = (const wchar_t *) ppfs->fmtpos; /* update to one past end of spec */ - } else { - ++fmt; - } - } - ppfs->fmtpos = (const char *) fmt0; /* rewind */ - } - -#ifdef NL_ARGMAX - /* If we have positional args, make sure we know all the types. */ - { - register int *p = ppfs->argtype; - r = ppfs->maxposarg; - while (--r >= 0) { - if ( *p == __PA_NOARG ) { /* missing arg type!!! */ - return -1; - } - ++p; - } - } -#endif /* NL_ARGMAX */ - - return 0; -} - -#endif /* L_vfprintf */ - -static void _charpad(FILE * __restrict stream, int padchar, size_t numpad) -{ - /* TODO -- Use a buffer to cut down on function calls... */ - FMT_TYPE pad[1]; - - *pad = padchar; - while (numpad) { - OUTNSTR(stream, pad, 1); - --numpad; - } -} - -/* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */ -static int _do_one_spec(FILE * __restrict stream, - register ppfs_t *ppfs, int *count) -{ - static const char spec_base[] = SPEC_BASE; -#ifdef L_vfprintf - static const char prefix[] = "+\0-\0 \0000x\0000X"; - /* 0 2 4 6 9 11*/ -#else /* L_vfprintf */ - static const wchar_t prefix[] = L"+\0-\0 \0000x\0000X"; -#endif /* L_vfprintf */ - enum { - PREFIX_PLUS = 0, - PREFIX_MINUS = 2, - PREFIX_SPACE = 4, - PREFIX_LWR_X = 6, - PREFIX_UPR_X = 9, - PREFIX_NONE = 11 - }; - -#ifdef __va_arg_ptr - const void * const *argptr; -#else - const void * argptr[MAX_ARGS_PER_SPEC]; -#endif - int *argtype; -#ifdef __UCLIBC_HAS_WCHAR__ - const wchar_t *ws = NULL; - mbstate_t mbstate; -#endif /* __UCLIBC_HAS_WCHAR__ */ - size_t slen; -#ifdef L_vfprintf -#define SLEN slen -#else - size_t SLEN; - wchar_t wbuf[2]; -#endif - int base; - int numpad; - int alphacase; - int numfill = 0; /* TODO: fix */ - int prefix_num = PREFIX_NONE; - char padchar = ' '; -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Determine appropriate buf size. -#endif /* __UCLIBC_MJN3_ONLY__ */ - /* TODO: buf needs to be big enough for any possible error return strings - * and also for any locale-grouped long long integer strings generated. - * This should be large enough for any of the current archs/locales, but - * eventually this should be handled robustly. */ - char buf[128]; - -#ifdef NDEBUG - _ppfs_parsespec(ppfs); -#else - if (_ppfs_parsespec(ppfs) < 0) { /* TODO: just for debugging */ - abort(); - } -#endif - _ppfs_setargs(ppfs); - - argtype = ppfs->argtype + ppfs->argnumber[2] - 1; - /* Deal with the argptr vs argvalue issue. */ -#ifdef __va_arg_ptr - argptr = (const void * const *) ppfs->argptr; -#ifdef NL_ARGMAX - if (ppfs->maxposarg > 0) { /* Using positional args... */ - argptr += ppfs->argnumber[2] - 1; - } -#endif /* NL_ARGMAX */ -#else - /* Need to build a local copy... */ - { - register argvalue_t *p = ppfs->argvalue; - int i; -#ifdef NL_ARGMAX - if (ppfs->maxposarg > 0) { /* Using positional args... */ - p += ppfs->argnumber[2] - 1; - } -#endif /* NL_ARGMAX */ - for (i = 0 ; i < ppfs->num_data_args ; i++ ) { - argptr[i] = (void *) p++; - } - } -#endif - { - register char *s = NULL; /* TODO: Should s be unsigned char * ? */ - - if (ppfs->conv_num == CONV_n) { - _store_inttype(*(void **)*argptr, - ppfs->info._flags & __PA_INTMASK, - (intmax_t) (*count)); - return 0; - } - if (ppfs->conv_num <= CONV_i) { /* pointer or (un)signed int */ - alphacase = __UIM_LOWER; - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_vfprintf -#warning CONSIDER: Should we ignore these flags if stub locale? What about custom specs? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - if ((base = spec_base[(int)(ppfs->conv_num - CONV_p)]) == 10) { - if (PRINT_INFO_FLAG_VAL(&(ppfs->info),group)) { - alphacase = __UIM_GROUP; - } - if (PRINT_INFO_FLAG_VAL(&(ppfs->info),i18n)) { - alphacase |= 0x80; - } - } - - if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */ - if (ppfs->conv_num == CONV_X) { - alphacase = __UIM_UPPER; - } - if (ppfs->conv_num == CONV_p) { /* pointer */ - prefix_num = PREFIX_LWR_X; - } else { /* unsigned int */ - } - } else { /* signed int */ - base = -base; - } - if (ppfs->info.prec < 0) { /* Ignore '0' flag if prec specified. */ - padchar = ppfs->info.pad; - } -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_vfprintf -#warning CONSIDER: If using outdigits and/or grouping, how should we interpret precision? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - s = _uintmaxtostr(buf + sizeof(buf) - 1, - (uintmax_t) - _load_inttype(*argtype & __PA_INTMASK, - *argptr, base), base, alphacase); - if (ppfs->conv_num > CONV_u) { /* signed int */ - if (*s == '-') { - PRINT_INFO_SET_FLAG(&(ppfs->info),showsign); - ++s; /* handle '-' in the prefix string */ - prefix_num = PREFIX_MINUS; - } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),showsign)) { - prefix_num = PREFIX_PLUS; - } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),space)) { - prefix_num = PREFIX_SPACE; - } - } - slen = (char *)(buf + sizeof(buf) - 1) - s; -#ifdef L_vfwprintf - { - const char *q = s; - mbstate.mask = 0; /* Initialize the mbstate. */ - SLEN = mbsrtowcs(NULL, &q, 0, &mbstate); - } -#endif - numfill = ((ppfs->info.prec < 0) ? 1 : ppfs->info.prec); - if (PRINT_INFO_FLAG_VAL(&(ppfs->info),alt)) { - if (ppfs->conv_num <= CONV_x) { /* x or p */ - prefix_num = PREFIX_LWR_X; - } - if (ppfs->conv_num == CONV_X) { - prefix_num = PREFIX_UPR_X; - } - if ((ppfs->conv_num == CONV_o) && (numfill <= SLEN)) { - numfill = ((*s == '0') ? 1 : SLEN + 1); - } - } - if (*s == '0') { - if (prefix_num >= PREFIX_LWR_X) { - prefix_num = PREFIX_NONE; - } - if (ppfs->conv_num == CONV_p) {/* null pointer */ - s = "(nil)"; -#ifdef L_vfwprintf - SLEN = -#endif - slen = 5; - numfill = 0; - } else if (numfill == 0) { /* if precision 0, no output */ -#ifdef L_vfwprintf - SLEN = -#endif - slen = 0; - } - } - numfill = ((numfill > SLEN) ? numfill - SLEN : 0); - } else if (ppfs->conv_num <= CONV_A) { /* floating point */ -#ifdef __STDIO_PRINTF_FLOAT - *count += - _fpmaxtostr(stream, - (__fpmax_t) - (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double) - ? *(long double *) *argptr - : (long double) (* (double *) *argptr)), - &ppfs->info, FP_OUT ); - return 0; -#else /* __STDIO_PRINTF_FLOAT */ - return -1; /* TODO -- try to continue? */ -#endif /* __STDIO_PRINTF_FLOAT */ - } else if (ppfs->conv_num <= CONV_S) { /* wide char or string */ -#ifdef L_vfprintf - -#ifdef __UCLIBC_HAS_WCHAR__ - mbstate.mask = 0; /* Initialize the mbstate. */ - if (ppfs->conv_num == CONV_S) { /* wide string */ - if (!(ws = *((const wchar_t **) *argptr))) { - goto NULL_STRING; - } - /* We use an awful uClibc-specific hack here, passing - * (char*) &ws as the conversion destination. This signals - * uClibc's wcsrtombs that we want a "restricted" length - * such that the mbs fits in a buffer of the specified - * size with no partial conversions. */ - if ((slen = wcsrtombs((char *) &ws, &ws, /* Use awful hack! */ - ((ppfs->info.prec >= 0) - ? ppfs->info.prec - : SIZE_MAX), &mbstate)) - == ((size_t)-1) - ) { - return -1; /* EILSEQ */ - } - } else { /* wide char */ - s = buf; - slen = wcrtomb(s, (*((const wchar_t *) *argptr)), &mbstate); - if (slen == ((size_t)-1)) { - return -1; /* EILSEQ */ - } - s[slen] = 0; /* TODO - Is this necessary? */ - } -#else /* __UCLIBC_HAS_WCHAR__ */ - return -1; -#endif /* __UCLIBC_HAS_WCHAR__ */ - } else if (ppfs->conv_num <= CONV_s) { /* char or string */ - if (ppfs->conv_num == CONV_s) { /* string */ - s = *((char **) (*argptr)); - if (s) { -#ifdef __STDIO_PRINTF_M_SUPPORT - SET_STRING_LEN: -#endif - slen = strnlen(s, ((ppfs->info.prec >= 0) - ? ppfs->info.prec : SIZE_MAX)); - } else { -#ifdef __UCLIBC_HAS_WCHAR__ - NULL_STRING: -#endif - s = "(null)"; - slen = 6; - } - } else { /* char */ - s = buf; - *s = (unsigned char)(*((const int *) *argptr)); - s[1] = 0; - slen = 1; - } - -#else /* L_vfprintf */ - - if (ppfs->conv_num == CONV_S) { /* wide string */ - ws = *((wchar_t **) (*argptr)); - if (!ws) { - goto NULL_STRING; - } - SLEN = wcsnlen(ws, ((ppfs->info.prec >= 0) - ? ppfs->info.prec : SIZE_MAX)); - } else { /* wide char */ - *wbuf = (wchar_t)(*((const wint_t *) *argptr)); - CHAR_CASE: - ws = wbuf; - wbuf[1] = 0; - SLEN = 1; - } - - } else if (ppfs->conv_num <= CONV_s) { /* char or string */ - - if (ppfs->conv_num == CONV_s) { /* string */ -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Fix %s for vfwprintf... output upto illegal sequence? -#endif /* __UCLIBC_MJN3_ONLY__ */ - s = *((char **) (*argptr)); - if (s) { -#ifdef __STDIO_PRINTF_M_SUPPORT - SET_STRING_LEN: -#endif - /* We use an awful uClibc-specific hack here, passing - * (wchar_t*) &mbstate as the conversion destination. - * This signals uClibc's mbsrtowcs that we want a - * "restricted" length such that the mbs fits in a buffer - * of the specified size with no partial conversions. */ - { - const char *q = s; - mbstate.mask = 0; /* Initialize the mbstate. */ - SLEN = mbsrtowcs((wchar_t *) &mbstate, &q, - ((ppfs->info.prec >= 0) - ? ppfs->info.prec : SIZE_MAX), - &mbstate); - } - if (SLEN == ((size_t)(-1))) { - return -1; /* EILSEQ */ - } - } else { - NULL_STRING: - s = "(null)"; - SLEN = slen = 6; - } - } else { /* char */ - *wbuf = btowc( (unsigned char)(*((const int *) *argptr)) ); - goto CHAR_CASE; - } - -#endif /* L_vfprintf */ - -#ifdef __STDIO_PRINTF_M_SUPPORT - } else if (ppfs->conv_num == CONV_m) { - s = _glibc_strerror_r(errno, buf, sizeof(buf)); - goto SET_STRING_LEN; -#endif - } else { -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF - assert(ppfs->conv_num == CONV_custom0); - - s = _custom_printf_spec; - do { - if (*s == ppfs->info.spec) { - int rv; - /* TODO -- check return value for sanity? */ - rv = (*_custom_printf_handler - [(int)(s-_custom_printf_spec)]) - (stream, &ppfs->info, argptr); - if (rv < 0) { - return -1; - } - *count += rv; - return 0; - } - } while (++s < (_custom_printf_spec + MAX_USER_SPEC)); -#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ - assert(0); - return -1; - } - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_vfprintf -#warning CONSIDER: If using outdigits and/or grouping, how should we pad? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - { - size_t t; - - t = SLEN + numfill; - if (prefix_num != PREFIX_NONE) { - t += ((prefix_num < PREFIX_LWR_X) ? 1 : 2); - } - numpad = ((ppfs->info.width > t) ? (ppfs->info.width - t) : 0); - *count += t + numpad; - } - if (padchar == '0') { /* TODO: check this */ - numfill += numpad; - numpad = 0; - } - - /* Now handle the output itself. */ - if (!PRINT_INFO_FLAG_VAL(&(ppfs->info),left)) { - _charpad(stream, ' ', numpad); - numpad = 0; - } - OUTPUT(stream, prefix + prefix_num); - _charpad(stream, '0', numfill); - -#ifdef L_vfprintf - -#ifdef __UCLIBC_HAS_WCHAR__ - if (!ws) { - assert(s); - _outnstr(stream, s, slen); - } else { /* wide string */ - size_t t; - mbstate.mask = 0; /* Initialize the mbstate. */ - while (slen) { - t = (slen <= sizeof(buf)) ? slen : sizeof(buf); - t = wcsrtombs(buf, &ws, t, &mbstate); - assert (t != ((size_t)(-1))); - _outnstr(stream, buf, t); - slen -= t; - } - } -#else /* __UCLIBC_HAS_WCHAR__ */ - _outnstr(stream, s, slen); -#endif /* __UCLIBC_HAS_WCHAR__ */ - -#else /* L_vfprintf */ - - if (!ws) { - assert(s); - _outnstr(stream, s, SLEN); - } else { - _outnwcs(stream, ws, SLEN); - } - -#endif /* L_vfprintf */ - _charpad(stream, ' ', numpad); - } - - return 0; -} - -int VFPRINTF (FILE * __restrict stream, - register const FMT_TYPE * __restrict format, - va_list arg) -{ - ppfs_t ppfs; - int count, r; - register const FMT_TYPE *s; - - __STDIO_THREADLOCK(stream); - - count = 0; - s = format; - -#if defined(L_vfprintf) && defined(__UCLIBC_HAS_WCHAR__) - /* Sigh... I forgot that by calling _stdio_fwrite, vfprintf doesn't - * always check the stream's orientation. This is just a temporary - * fix until I rewrite the stdio core work routines. */ - if (stream->modeflags & __FLAG_WIDE) { - stream->modeflags |= __FLAG_ERROR; - count = -1; - goto DONE; - } - stream->modeflags |= __FLAG_NARROW; -#endif - - if (_PPFS_init(&ppfs, format) < 0) { /* Bad format string. */ - OUTNSTR(stream, (const FMT_TYPE *) ppfs.fmtpos, - STRLEN((const FMT_TYPE *)(ppfs.fmtpos))); -#if defined(L_vfprintf) && !defined(NDEBUG) - fprintf(stderr,"\nIMbS: \"%s\"\n\n", format); -#endif - count = -1; - } else { - _ppfs_prepargs(&ppfs, arg); /* This did a va_copy!!! */ - - do { - while (*format && (*format != '%')) { - ++format; - } - - if (format-s) { /* output any literal text in format string */ - if ( (r = OUTNSTR(stream, s, format-s)) < 0) { - count = -1; - break; - } - count += r; - } - - if (!*format) { /* we're done */ - break; - } - - if (format[1] != '%') { /* if we get here, *format == '%' */ - /* TODO: _do_one_spec needs to know what the output funcs are!!! */ - ppfs.fmtpos = (const char *)(++format); - /* TODO: check -- should only fail on stream error */ - if ( (r = _do_one_spec(stream, &ppfs, &count)) < 0) { - count = -1; - break; - } - s = format = (const FMT_TYPE *) ppfs.fmtpos; - } else { /* %% means literal %, so start new string */ - s = ++format; - ++format; - } - } while (1); - - va_end(ppfs.arg); /* Need to clean up after va_copy! */ - } - -#if defined(L_vfprintf) && defined(__UCLIBC_HAS_WCHAR__) - DONE: -#endif - - __STDIO_THREADUNLOCK(stream); - - return count; -} -#endif -/**********************************************************************/ diff --git a/libc/stdio/putchar.c b/libc/stdio/putchar.c new file mode 100644 index 000000000..20f5aadcc --- /dev/null +++ b/libc/stdio/putchar.c @@ -0,0 +1,44 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#undef putchar_unlocked +#undef putchar + +#ifdef __DO_UNLOCKED + +weak_alias(__putchar_unlocked,putchar_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__putchar_unlocked,putchar); +#endif + +int __putchar_unlocked(int c) +{ + register FILE *stream = stdout; + + return __PUTC_UNLOCKED_MACRO(c, stream); +} + +#elif defined __UCLIBC_HAS_THREADS__ + +int putchar(int c) +{ + register FILE *stream = stdout; + + if (stream->__user_locking != 0) { + return __PUTC_UNLOCKED_MACRO(c, stream); + } else { + int retval; + __STDIO_ALWAYS_THREADLOCK(stream); + retval = __PUTC_UNLOCKED_MACRO(c, stream); + __STDIO_ALWAYS_THREADUNLOCK(stream); + return retval; + } +} + +#endif diff --git a/libc/stdio/puts.c b/libc/stdio/puts.c new file mode 100644 index 000000000..a7d0eaf19 --- /dev/null +++ b/libc/stdio/puts.c @@ -0,0 +1,33 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +int puts(register const char * __restrict s) +{ + register FILE *stream = stdout; /* This helps bcc optimize. */ + int n; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + /* Note: Don't try to optimize by switching to FBF until the newline. + * If the string itself contained a newline a write error occurred, + * then we could have a newline in the buffer of an LBF stream. */ + + /* Note: Nonportable as fputs need only return nonnegative on success. */ + if ((n = __fputs_unlocked(s, stream)) != EOF) { + ++n; + if (__fputc_unlocked('\n', stream) == EOF) { + n = EOF; + } + } + + __STDIO_AUTO_THREADUNLOCK(stream); + + return n; +} diff --git a/libc/stdio/putw.c b/libc/stdio/putw.c new file mode 100644 index 000000000..5dfa06890 --- /dev/null +++ b/libc/stdio/putw.c @@ -0,0 +1,28 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* SUSv2 Legacy function -- need not be reentrant. */ + +int putw(int w, FILE *stream) +{ +#define PW &w + /* If w is passed in a register, enable the following. */ +#if 0 +#undef PW + int PW[1]; + PW[0] = w; +#endif + +#if EOF == -1 + return __fwrite_unlocked((void *) PW, sizeof(int), 1, stream) - 1; +#else + return (__fwrite_unlocked((void *) PW, sizeof(int), 1, stream) != 0) + ? 0 : EOF; +#endif +} diff --git a/libc/stdio/putwchar.c b/libc/stdio/putwchar.c new file mode 100644 index 000000000..9a563dcf0 --- /dev/null +++ b/libc/stdio/putwchar.c @@ -0,0 +1,31 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +extern wint_t __putwchar_unlocked(wchar_t wc); + +#ifdef __DO_UNLOCKED + +weak_alias(__putwchar_unlocked,putwchar_unlocked); +#ifndef __UCLIBC_HAS_THREADS__ +weak_alias(__putwchar_unlocked,putwchar); +#endif + +wint_t __putwchar_unlocked(wchar_t wc) +{ + return __fputwc_unlocked(wc, stdout); +} + +#elif defined __UCLIBC_HAS_THREADS__ + +wint_t putwchar(wchar_t wc) +{ + return fputc(wc, stdout); +} + +#endif diff --git a/libc/stdio/remove.c b/libc/stdio/remove.c new file mode 100644 index 000000000..aaef3342b --- /dev/null +++ b/libc/stdio/remove.c @@ -0,0 +1,29 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#include <unistd.h> +#include <errno.h> + +/* SUSv3 states: + * If path does not name a directory, remove(path) shall be equivalent + * to unlink(path). If path names a directory, remove(path) shall be + * equivalent to rmdir(path). + */ + +int remove(register const char *filename) +{ + int saved_errno = errno; + int rv; + + if (((rv = rmdir(filename)) < 0) && (errno == ENOTDIR)) { + __set_errno(saved_errno); /* Need to restore errno. */ + rv = unlink(filename); + } + return rv; +} diff --git a/libc/stdio/rewind.c b/libc/stdio/rewind.c new file mode 100644 index 000000000..7f36a77ff --- /dev/null +++ b/libc/stdio/rewind.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +void rewind(register FILE *stream) +{ + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + __STDIO_STREAM_CLEAR_ERROR(stream); /* Clear the error indicator */ + fseek(stream, 0L, SEEK_SET); /* first since fseek could set it. */ + + __STDIO_AUTO_THREADUNLOCK(stream); +} diff --git a/libc/stdio/scanf.c b/libc/stdio/scanf.c index b1299196f..0bb6c0f4d 100644 --- a/libc/stdio/scanf.c +++ b/libc/stdio/scanf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002, 2003 Manuel Novoa III +/* Copyright (C) 2002-2004 Manuel Novoa III * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -48,7 +48,7 @@ #define _GNU_SOURCE #define _STDIO_UTILITY #include <features.h> -#include <stdio.h> +#include "_stdio.h" #include <stdlib.h> #include <unistd.h> #include <ctype.h> @@ -70,10 +70,10 @@ #include <assert.h> #include <limits.h> -#ifdef __STDIO_THREADSAFE +#ifdef __UCLIBC_HAS_THREADS__ #include <stdio_ext.h> #include <pthread.h> -#endif /* __STDIO_THREADSAFE */ +#endif /* __UCLIBC_HAS_THREADS__ */ #ifdef __UCLIBC_HAS_FLOATS__ #include <float.h> @@ -89,7 +89,7 @@ #endif #undef __STDIO_HAS_VSSCANF -#if defined(__STDIO_BUFFERS) || !defined(__UCLIBC_HAS_WCHAR__) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) +#if defined(__STDIO_BUFFERS) || !defined(__UCLIBC_HAS_WCHAR__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) #define __STDIO_HAS_VSSCANF 1 #if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__) @@ -205,70 +205,101 @@ int vscanf(const char * __restrict format, va_list arg) #ifdef L_vsscanf #ifdef __UCLIBC_MJN3_ONLY__ -#warning WISHLIST: Implement vsscanf for non-buffered and no custom stream case. +#warning WISHLIST: Implement vsscanf for non-buf and no custom stream case. #endif /* __UCLIBC_MJN3_ONLY__ */ #ifdef __STDIO_BUFFERS int vsscanf(__const char *sp, __const char *fmt, va_list ap) { - FILE string[1]; + FILE f; - string->filedes = -2; - string->modeflags = (__FLAG_NARROW|__FLAG_READONLY); - string->bufstart = string->bufpos = (unsigned char *) ((void *) sp); -#ifdef __STDIO_GETC_MACRO - string->bufgetc = -#endif /* __STDIO_GETC_MACRO */ - string->bufread = string->bufstart + strlen(sp); +/* __STDIO_STREAM_RESET_GCS(&f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.__cookie = &(f.__filedes); + f.__gcs.read = NULL; + f.__gcs.write = NULL; + f.__gcs.seek = NULL; + f.__gcs.close = NULL; +#endif -#ifdef __STDIO_MBSTATE - __INIT_MBSTATE(&(string->state)); -#endif /* __STDIO_MBSTATE */ + f.__filedes = __STDIO_STREAM_FAKE_VSSCANF_FILEDES; + f.__modeflags = (__FLAG_NARROW|__FLAG_READONLY|__FLAG_READING); -#ifdef __STDIO_THREADSAFE - string->user_locking = 0; - __stdio_init_mutex(&string->lock); +#ifdef __UCLIBC_HAS_WCHAR__ + f.__ungot_width[0] = 0; +#endif +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(f.__state)); #endif - return vfscanf(string, fmt, ap); +#ifdef __UCLIBC_HAS_THREADS__ + f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.__lock); +#endif + f.__nextopen = NULL; + + /* Set these last since __bufgetc initialization depends on + * __user_locking and only gets set if user locking is on. */ + f.__bufstart = + f.__bufpos = (unsigned char *) ((void *) sp); + f.__bufread = + f.__bufend = f.__bufstart + strlen(sp); + __STDIO_STREAM_ENABLE_GETC(&f); + __STDIO_STREAM_DISABLE_PUTC(&f); + + return vfscanf(&f, fmt, ap); } #elif !defined(__UCLIBC_HAS_WCHAR__) int vsscanf(__const char *sp, __const char *fmt, va_list ap) { - __FILE_vsscanf string[1]; + __FILE_vsscanf f; + + f.bufpos = (unsigned char *) ((void *) sp); + f.bufread = f.bufpos + strlen(sp); + +/* __STDIO_STREAM_RESET_GCS(&f.f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.f.__cookie = &(f.f.__filedes); + f.f.__gcs.read = NULL; + f.f.__gcs.write = NULL; + f.f.__gcs.seek = NULL; + f.f.__gcs.close = NULL; +#endif - string->f.filedes = -2; - string->f.modeflags = (__FLAG_NARROW|__FLAG_READONLY); - string->bufpos = (unsigned char *) ((void *) sp); - string->bufread = string->bufpos + strlen(sp); + f.f.__filedes = __STDIO_STREAM_FAKE_VSSCANF_FILEDES_NB; + f.f.__modeflags = (__FLAG_NARROW|__FLAG_READONLY|__FLAG_READING); +/* #ifdef __UCLIBC_HAS_WCHAR__ */ +/* f.f.__ungot_width[0] = 0; */ +/* #endif */ #ifdef __STDIO_MBSTATE #error __STDIO_MBSTATE is defined! -#endif /* __STDIO_MBSTATE */ +/* __INIT_MBSTATE(&(f.f.__state)); */ +#endif -#ifdef __STDIO_THREADSAFE - string->user_locking = 0; - __stdio_init_mutex(&string->f.lock); +#ifdef __UCLIBC_HAS_THREADS__ + f.f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.f.__lock); #endif + f.f.__nextopen = NULL; - return vfscanf(&string->f, fmt, ap); + return vfscanf(&f.f, fmt, ap); } -#elif defined(__STDIO_GLIBC_CUSTOM_STREAMS) +#elif defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) int vsscanf(__const char *sp, __const char *fmt, va_list ap) { FILE *f; - int rv; + int rv = EOF; - if ((f = fmemopen((char *)sp, strlen(sp), "r")) == NULL) { - return -1; + if ((f = fmemopen((char *)sp, strlen(sp), "r")) != NULL) { + rv = vfscanf(f, fmt, ap); + fclose(f); } - rv = vfscanf(f, fmt, ap); - fclose(f); return rv; } @@ -354,16 +385,37 @@ int vswscanf(const wchar_t * __restrict str, const wchar_t * __restrict format, { FILE f; - f.filedes = -3; /* FAKE STREAM TO SUPPORT *wscanf! */ - f.modeflags = (__FLAG_WIDE|__FLAG_READONLY|__FLAG_READING); - f.bufpos = (char *) str; - f.bufend = (char *)(str + wcslen(str)); - f.ungot_width[0] = 0; -#ifdef __STDIO_THREADSAFE - f.user_locking = 0; - __stdio_init_mutex(&f.lock); + f.__bufstart = + f.__bufpos = (char *) str; + f.__bufread = + f.__bufend = (char *)(str + wcslen(str)); + __STDIO_STREAM_DISABLE_GETC(&f); + __STDIO_STREAM_DISABLE_PUTC(&f); + +/* __STDIO_STREAM_RESET_GCS(&f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.__cookie = &(f.__filedes); + f.__gcs.read = NULL; + f.__gcs.write = NULL; + f.__gcs.seek = NULL; + f.__gcs.close = NULL; #endif + f.__filedes = __STDIO_STREAM_FAKE_VSWSCANF_FILEDES; + f.__modeflags = (__FLAG_WIDE|__FLAG_READONLY|__FLAG_READING); + +#ifdef __UCLIBC_HAS_WCHAR__ + f.__ungot_width[0] = 0; +#endif /* __UCLIBC_HAS_WCHAR__ */ +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(f.__state)); +#endif /* __STDIO_MBSTATE */ + +#ifdef __UCLIBC_HAS_THREADS__ + f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.__lock); +#endif + f.__nextopen = NULL; return vfwscanf(&f, format, arg); } @@ -624,7 +676,7 @@ void __init_scan_cookie(register struct scan_cookie *sc, sc->fp = fp; sc->nread = 0; sc->ungot_flag = 0; - sc->app_ungot = ((fp->modeflags & __MASK_UNGOT) ? fp->ungot[1] : 0); + sc->app_ungot = ((fp->__modeflags & __FLAG_UNGOT) ? fp->__ungot[1] : 0); #ifdef __UCLIBC_HAS_WCHAR__ sc->ungot_wflag = 0; /* vfwscanf */ sc->mb_fail = 0; @@ -676,7 +728,7 @@ int __scan_getc(register struct scan_cookie *sc) if (sc->ungot_flag == 0) { #if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__) - if (sc->fp->filedes != -2) { + if (!__STDIO_STREAM_IS_FAKE_VSSCANF_NB(sc->fp)) { c = GETC(sc); } else { __FILE_vsscanf *fv = (__FILE_vsscanf *)(sc->fp); @@ -684,7 +736,7 @@ int __scan_getc(register struct scan_cookie *sc) c = *fv->bufpos++; } else { c = EOF; - sc->fp->modeflags |= __FLAG_EOF; + sc->fp->__modeflags |= __FLAG_EOF; } } if (c == EOF) { @@ -956,12 +1008,12 @@ static int sc_getc(register struct scan_cookie *sc) { wint_t wc; - if (sc->fp->filedes == -3) { - if (sc->fp->bufpos < sc->fp->bufend) { - wc = *((wchar_t *)(sc->fp->bufpos)); - sc->fp->bufpos += sizeof(wchar_t); + if (__STDIO_STREAM_IS_FAKE_VSWSCANF(sc->fp)) { + if (sc->fp->__bufpos < sc->fp->__bufend) { + wc = *((wchar_t *)(sc->fp->__bufpos)); + sc->fp->__bufpos += sizeof(wchar_t); } else { - sc->fp->modeflags |= __FLAG_EOF; + sc->fp->__modeflags |= __FLAG_EOF; return EOF; } } else if ((wc = fgetwc_unlocked(sc->fp)) == WEOF) { @@ -970,7 +1022,7 @@ static int sc_getc(register struct scan_cookie *sc) sc->ungot_wflag = 1; sc->ungot_wchar = wc; - sc->ungot_wchar_width = sc->fp->ungot_width[0]; + sc->ungot_wchar_width = sc->fp->__ungot_width[0]; #ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ if (wc == sc->thousands_sep_wc) { @@ -1002,11 +1054,10 @@ static int scan_getwc(register struct scan_cookie *sc) } if (sc->ungot_flag == 0) { - - if (sc->fp->filedes == -3) { - if (sc->fp->bufpos < sc->fp->bufend) { - wc = *((wchar_t *)(sc->fp->bufpos)); - sc->fp->bufpos += sizeof(wchar_t); + if (__STDIO_STREAM_IS_FAKE_VSWSCANF(sc->fp)) { + if (sc->fp->__bufpos < sc->fp->__bufend) { + wc = *((wchar_t *)(sc->fp->__bufpos)); + sc->fp->__bufpos += sizeof(wchar_t); } else { sc->ungot_flag |= 2; return -1; @@ -1017,7 +1068,7 @@ static int scan_getwc(register struct scan_cookie *sc) } sc->ungot_wflag = 1; sc->ungot_char = wc; - sc->ungot_wchar_width = sc->fp->ungot_width[0]; + sc->ungot_wchar_width = sc->fp->__ungot_width[0]; } else { assert(sc->ungot_flag == 1); sc->ungot_flag = 0; @@ -1039,7 +1090,7 @@ static __inline void kill_scan_cookie(register struct scan_cookie *sc) if (sc->ungot_flag & 1) { #if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__) - if (sc->fp->filedes != -2) { + if (!__STDIO_STREAM_IS_FAKE_VSSCANF_NB(sc->fp)) { ungetc(sc->ungot_char, sc->fp); } #else @@ -1047,25 +1098,26 @@ static __inline void kill_scan_cookie(register struct scan_cookie *sc) #endif /* Deal with distiction between user and scanf ungots. */ if (sc->nread == 0) { /* Only one char was read... app ungot? */ - sc->fp->ungot[1] = sc->app_ungot; /* restore ungot state. */ + sc->fp->__ungot[1] = sc->app_ungot; /* restore ungot state. */ } else { - sc->fp->ungot[1] = 0; + sc->fp->__ungot[1] = 0; } } #else if ((sc->ungot_flag & 1) && (sc->ungot_wflag & 1) - && (sc->fp->filedes != -3) && (sc->fp->state.mask == 0) + && !__STDIO_STREAM_IS_FAKE_VSWSCANF(sc->fp) + && (sc->fp->__state.__mask == 0) ) { ungetwc(sc->ungot_char, sc->fp); /* Deal with distiction between user and scanf ungots. */ if (sc->nread == 0) { /* Only one char was read... app ungot? */ - sc->fp->ungot[1] = sc->app_ungot; /* restore ungot state. */ + sc->fp->__ungot[1] = sc->app_ungot; /* restore ungot state. */ } else { - sc->fp->ungot[1] = 0; + sc->fp->__ungot[1] = 0; } - sc->fp->ungot_width[1] = sc->ungot_wchar_width; + sc->fp->__ungot_width[1] = sc->ungot_wchar_width; } #endif @@ -1101,7 +1153,9 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) int i; -#warning fix MAX_DIGITS. we do not do binary, so...! +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Fix MAX_DIGITS. We do not do binary, so...! +#endif #define MAX_DIGITS 65 /* Allow one leading 0. */ unsigned char buf[MAX_DIGITS+2]; #ifdef L_vfscanf @@ -1110,6 +1164,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) #endif /* L_vfscanf */ unsigned char fail; unsigned char zero_conversions = 1; + __STDIO_AUTO_THREADLOCK_VAR; #ifdef __UCLIBC_MJN3_ONLY__ #warning TODO: Make checking of the format string in C locale an option. @@ -1120,7 +1175,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) * beginning and ending in its initial shift state. */ if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) { const char *p = format; - mbstate.mask = 0; /* Initialize the mbstate. */ + mbstate.__mask = 0; /* Initialize the mbstate. */ if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { __set_errno(EINVAL); /* Format string is invalid. */ return 0; @@ -1134,12 +1189,14 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) memset(psfs.pos_args, 0, sizeof(psfs.pos_args)); #endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ - __STDIO_THREADLOCK(fp); + __STDIO_AUTO_THREADLOCK(fp); + + __STDIO_STREAM_VALIDATE(fp); __init_scan_cookie(&sc,fp); #ifdef __UCLIBC_HAS_WCHAR__ sc.sc_getc = sc_getc; - sc.ungot_wchar_width = sc.fp->ungot_width[1]; + sc.ungot_wchar_width = sc.fp->__ungot_width[1]; #ifdef L_vfwscanf @@ -1279,7 +1336,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) if (psfs.conv_num == CONV_n) { #ifdef __UCLIBC_MJN3_ONLY__ -#warning Should %n count as a conversion as far as EOF return value? +#warning CONSIDER: Should %n count as a conversion as far as EOF return value? #endif /* zero_conversions = 0; */ if (psfs.store) { @@ -1424,7 +1481,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) wchar_t wbuf[1]; wchar_t *wb; - sc.mbstate.mask = 0; + sc.mbstate.__mask = 0; wb = (psfs.store ? ((wchar_t *) psfs.cur_ptr) : wbuf); fail = 1; @@ -1497,7 +1554,7 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) b = buf; wb = wbuf; if (psfs.conv_num >= CONV_c) { - mbstate.mask = 0; /* Initialize the mbstate. */ + mbstate.__mask = 0; /* Initialize the mbstate. */ if (psfs.store) { b = (unsigned char *) psfs.cur_ptr; } @@ -1662,19 +1719,21 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) NEXT_FMT: ++fmt; - if (__FERROR(fp)) { + if (__FERROR_UNLOCKED(fp)) { break; } } DONE: - if (__FERROR(fp) || (*fmt && zero_conversions && __FEOF(fp))) { + if (__FERROR_UNLOCKED(fp) || (*fmt && zero_conversions && __FEOF_UNLOCKED(fp))) { psfs.cnt = EOF; /* Yes, vfwscanf also returns EOF. */ } kill_scan_cookie(&sc); - __STDIO_THREADUNLOCK(fp); + __STDIO_STREAM_VALIDATE(fp); + + __STDIO_AUTO_THREADUNLOCK(fp); return psfs.cnt; } @@ -1693,16 +1752,20 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc) #ifdef __UCLIBC_HAS_FLOATS__ int exp_adjust = 0; #endif -#warning fix MAX_DIGITS. we do not do binary, so...! +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Fix MAX_DIGITS. We do not do binary, so...! +#warning TODO: Fix buf! +#endif #define MAX_DIGITS 65 /* Allow one leading 0. */ -#warning fix buf! unsigned char buf[MAX_DIGITS+2+ 100]; unsigned char usflag, base; unsigned char nonzero = 0; unsigned char seendigit = 0; -#warning what should be returned for an invalid conversion specifier? +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: What should be returned for an invalid conversion specifier? +#endif #ifndef __UCLIBC_HAS_FLOATS__ if (psfs->conv_num > CONV_i) { /* floating point */ goto DONE; @@ -2105,7 +2168,9 @@ int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc) __scan_getc(sc); } -#warning fix MAX_EXP_DIGITS! +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Fix MAX_EXP_DIGITS! +#endif #define MAX_EXP_DIGITS 20 assert(seendigit); seendigit = 0; diff --git a/libc/stdio/setbuf.c b/libc/stdio/setbuf.c new file mode 100644 index 000000000..21158638b --- /dev/null +++ b/libc/stdio/setbuf.c @@ -0,0 +1,15 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +void setbuf(FILE * __restrict stream, register char * __restrict buf) +{ +#ifdef __STDIO_BUFFERS + setvbuf(stream, buf, ((buf != NULL) ? _IOFBF : _IONBF), BUFSIZ); +#endif +} diff --git a/libc/stdio/setbuffer.c b/libc/stdio/setbuffer.c new file mode 100644 index 000000000..ccd4d275e --- /dev/null +++ b/libc/stdio/setbuffer.c @@ -0,0 +1,21 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* A BSD function. The implementation matches the linux man page, + * except that we do not bother calling setvbuf if not configured + * for stream buffering. + */ + +void setbuffer(FILE * __restrict stream, register char * __restrict buf, + size_t size) +{ +#ifdef __STDIO_BUFFERS + setvbuf(stream, buf, (buf ? _IOFBF : _IONBF), size); +#endif +} diff --git a/libc/stdio/setlinebuf.c b/libc/stdio/setlinebuf.c new file mode 100644 index 000000000..6147fa8f4 --- /dev/null +++ b/libc/stdio/setlinebuf.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* A BSD function. The implementation matches the linux man page, + * except that we do not bother calling setvbuf if not configured + * for stream buffering. + */ + +void setlinebuf(FILE * __restrict stream) +{ +#ifdef __STDIO_BUFFERS + setvbuf(stream, NULL, _IOLBF, (size_t) 0); +#endif +} diff --git a/libc/stdio/setvbuf.c b/libc/stdio/setvbuf.c new file mode 100644 index 000000000..3fe62c6a8 --- /dev/null +++ b/libc/stdio/setvbuf.c @@ -0,0 +1,106 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +#if (_IOFBF != 0) || (_IOLBF != 1) || (_IONBF != 2) +#error Assumption violated -- values of _IOFBF, _IOLBF, _IONBF +#endif +#if (__FLAG_FBF != 0) || (__FLAG_NBF != (2*__FLAG_LBF)) +#error Assumption violated for buffering mode flags +#endif + +int setvbuf(register FILE * __restrict stream, register char * __restrict buf, + int mode, size_t size) +{ +#ifdef __STDIO_BUFFERS + + int retval = EOF; + int alloc_flag = 0; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + __STDIO_STREAM_VALIDATE(stream); + + if (((unsigned int) mode) > 2) { + __set_errno(EINVAL); + goto ERROR; + } + + /* C99 states that setvbuf may only be used between a successful + * open of the stream and before any other operation other than + * an unsuccessful call to setvbuf. */ + +#ifdef __STDIO_FLEXIBLE_SETVBUF + /* If we aren't currently reading (including ungots) or writing, + * then allow the request to proceed. */ + + if (stream->__modeflags & (__MASK_READING|__FLAG_WRITING)) { + goto ERROR; + } +#else + /* The following test isn't quite as strict as C99, as it will + * not detect file positioning operations. */ + + if (stream->__modeflags & (__MASK_READING|__FLAG_WRITING + |__FLAG_NARROW|__FLAG_WIDE + |__FLAG_ERROR|__FLAG_EOF) + ) { + goto ERROR; + } +#endif + + stream->__modeflags &= ~(__MASK_BUFMODE); /* Clear current mode */ + stream->__modeflags |= mode * __FLAG_LBF; /* and set new one. */ + + if ((mode == _IONBF) || !size) { + size = 0; + buf = NULL; + } else if (!buf) { + if ((__STDIO_STREAM_BUFFER_SIZE(stream) == size) /* Same size or */ + || !(buf = malloc(size)) /* malloc failed, so don't change. */ + ) { + goto DONE; + } + alloc_flag = __FLAG_FREEBUF; + } + + if (stream->__modeflags & __FLAG_FREEBUF) { + stream->__modeflags &= ~(__FLAG_FREEBUF); + free(stream->__bufstart); + } + + stream->__modeflags |= alloc_flag; + stream->__bufstart = buf; + stream->__bufend = buf + size; + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(stream); + __STDIO_STREAM_DISABLE_GETC(stream); + __STDIO_STREAM_DISABLE_PUTC(stream); + + DONE: + retval = 0; + + ERROR: + __STDIO_STREAM_VALIDATE(stream); + __STDIO_AUTO_THREADUNLOCK(stream); + + return retval; + +#else /* __STDIO_BUFFERS */ + + if (mode == _IONBF) { + return 0; + } + + if (((unsigned int) mode) > 2) { + __set_errno(EINVAL); + } + + return EOF; + +#endif +} diff --git a/libc/stdio/snprintf.c b/libc/stdio/snprintf.c new file mode 100644 index 000000000..a827971ba --- /dev/null +++ b/libc/stdio/snprintf.c @@ -0,0 +1,27 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +#ifndef __STDIO_HAS_VSNPRINTF +#warning Skipping snprintf since no vsnprintf! +#else + +int snprintf(char *__restrict buf, size_t size, + const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vsnprintf(buf, size, format, arg); + va_end(arg); + return rv; +} + +#endif diff --git a/libc/stdio/sprintf.c b/libc/stdio/sprintf.c new file mode 100644 index 000000000..eb83f424a --- /dev/null +++ b/libc/stdio/sprintf.c @@ -0,0 +1,27 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +#ifndef __STDIO_HAS_VSNPRINTF +#warning Skipping sprintf since no vsnprintf! +#else + +int sprintf(char *__restrict buf, const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vsnprintf(buf, SIZE_MAX, format, arg); + va_end(arg); + + return rv; +} + +#endif 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 thread support... */ -#endif -} - -#endif -/**********************************************************************/ -#ifdef L_flockfile - -void flockfile(FILE *stream) -{ -#ifdef __STDIO_THREADSAFE - __pthread_mutex_lock(&stream->lock); -#endif -} - -#endif -/**********************************************************************/ -#ifdef L_ftrylockfile - -int ftrylockfile(FILE *stream) -{ -#ifdef __STDIO_THREADSAFE - return __pthread_mutex_trylock(&stream->lock); -#else - return 1; -#endif -} - -#endif -/**********************************************************************/ -#ifdef L_funlockfile - -void funlockfile(FILE *stream) -{ -#ifdef __STDIO_THREADSAFE - __pthread_mutex_unlock(&stream->lock); -#endif -} - -#endif -/**********************************************************************/ -#ifdef L_getline - -ssize_t getline(char **__restrict lineptr, size_t *__restrict n, - FILE *__restrict stream) -{ - return __getdelim(lineptr, n, '\n', stream); -} - -#endif -/**********************************************************************/ -#ifdef L_getdelim - -weak_alias(__getdelim,getdelim); - -#define GETDELIM_GROWBY 64 - -ssize_t __getdelim(char **__restrict lineptr, size_t *__restrict n, - int delimiter, register FILE *__restrict stream) -{ - register char *buf; - size_t pos; - int c; - - if (!lineptr || !n || !stream) { /* Be compatable with glibc... even */ - __set_errno(EINVAL); /* though I think we should assert here */ - return -1; /* if anything. */ - } - - if (!(buf = *lineptr)) { /* If passed NULL for buffer, */ - *n = 0; /* ignore value passed and treat size as 0. */ - } - pos = 1; /* Make sure we have space for terminating nul. */ - - __STDIO_THREADLOCK(stream); - - do { - if (pos >= *n) { - if (!(buf = realloc(buf, *n + GETDELIM_GROWBY))) { - __set_errno(ENOMEM); /* Emulate old uClibc implementation. */ - break; - } - *n += GETDELIM_GROWBY; - *lineptr = buf; - } - } while (((c = (getc_unlocked)(stream)) != EOF) /* Disable the macro */ - && ((buf[pos++ - 1] = c) != delimiter)); - - __STDIO_THREADUNLOCK(stream); - - if (--pos) { - buf[pos] = 0; - return pos; - } - - return -1; /* Either initial realloc failed or first read was EOF. */ -} - -#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 - -/* According to the ANSI/ISO C99 definition of ungetwc() - * For a text or binary stream, the value of its file position indicator - * after a successful call to the ungetwc function is unspecified until - * all pushedback wide characters are read or discarded. - * 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 ungot_width. 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 */ - - assert(cor <= 2); - -#ifdef __STDIO_WIDE - /* Assumed narrow stream so correct if wide. */ - if (cor && (stream->modeflags & __FLAG_WIDE)) { - if ((((stream->modeflags & __MASK_UNGOT) > 1) || stream->ungot[1])) { - return -1; /* App did ungetwc, so position is indeterminate. */ - } - if (stream->modeflags & __MASK_UNGOT) { - cor = cor - 1 + stream->ungot_width[1]; - } - if (stream->state.mask > 0) { /* Incomplete character (possible bad) */ - cor -= stream->ungot_width[0]; - } - } -#endif /* __STDIO_WIDE */ - -#ifdef __STDIO_BUFFERS - if (stream->modeflags & __FLAG_WRITING) { - cor -= (stream->bufpos - stream->bufstart); /* pending writes */ - } - if (stream->modeflags & __FLAG_READING) { - cor += (stream->bufread - stream->bufpos); /* 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(register FILE *stream, register __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(register FILE *stream, unsigned char *buf, size_t bufsize) -{ - ssize_t rv; - - /* NOTE: C99 change: Input fails once the stream's EOF indicator is set. */ - if ((bufsize == 0) || (stream->modeflags & __FLAG_EOF)) { - return 0; - } - - if (bufsize > SSIZE_MAX) { - bufsize = SSIZE_MAX; - } - -#ifdef __BCC__ - TRY_READ: -#endif - rv = __READ(stream, buf, bufsize); - if (rv > 0) { -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS - assert(rv <= bufsize); /* buggy user handler... TODO: check? */ - if (rv > bufsize) { /* Num reported written > number requested */ - rv = bufsize; /* Treat as a full read??? */ - } -#endif - } else if (rv == 0) { - stream->modeflags |= __FLAG_EOF; - } else { -#ifdef __BCC__ - if (errno == EINTR) { - goto TRY_READ; - } -#endif - stream->modeflags |= __FLAG_ERROR; - rv = 0; - } - - return rv; -} - -/* Internal function -- not reentrant. */ - -size_t _stdio_fread(unsigned char *buffer, size_t bytes, register FILE *stream) -{ - __stdio_validate_FILE(stream); /* debugging only */ - -#ifdef __STDIO_BUFFERS - - if (stream->modeflags -#ifdef __STDIO_AUTO_RW_TRANSITION - & (__FLAG_WRITEONLY) -#else /* __STDIO_AUTO_RW_TRANSITION */ - /* ANSI/ISO and SUSv3 require not currently writing. */ - & (__FLAG_WRITEONLY|__FLAG_WRITING) -#endif /* __STDIO_AUTO_RW_TRANSITION */ - ) { -#ifdef __STDIO_PUTC_MACRO - stream->bufputc = stream->bufstart; /* Must disable putc. */ -#endif /* __STDIO_PUTC_MACRO */ - stream->modeflags |= __FLAG_ERROR; - /* TODO: This is for posix behavior if writeonly. To save space, we - * use this errno for read attempt while writing, as no errno is - * specified by posix for this case, even though the restriction is - * mentioned in fopen(). */ - __set_errno(EBADF); - return 0; - } - - /* We need to disable putc and getc macros in case of error */ -#if defined(__STDIO_PUTC_MACRO) || defined(__STDIO_GETC_MACRO) -#ifdef __STDIO_PUTC_MACRO - stream->bufputc = -#endif /* __STDIO_GETC_MACRO */ -#ifdef __STDIO_GETC_MACRO - stream->bufgetc = -#endif /* __STDIO_GETC_MACRO */ - stream->bufstart; -#endif /* defined(__STDIO_PUTC_MACRO) || defined(__STDIO_GETC_MACRO) */ - - if (stream->modeflags & __MASK_BUFMODE) { - /* If the stream is readable and not fully buffered, we must first - * flush all line buffered output streams. Do this before the - * error check as this may be a read/write line-buffered stream. - * Note: Uses an implementation-specific hack!!! */ - fflush_unlocked((FILE *) &_stdio_openlist); - } - -#ifdef __STDIO_AUTO_RW_TRANSITION - if ((stream->modeflags & __FLAG_WRITING) - && (fflush_unlocked(stream) == EOF) - ) { - return 0; /* Fail if we need to fflush but can't. */ - } -#endif /* __STDIO_AUTO_RW_TRANSITION */ - - stream->modeflags |= __FLAG_READING; /* Make sure Reading flag is set. */ - - { - register unsigned char *p = (unsigned char *) buffer; - - /* First, grab appropriate ungetc() chars. NOT FOR WIDE ORIENTATED! */ - while (bytes && (stream->modeflags & __MASK_UNGOT)) { -#ifdef __STDIO_WIDE - assert(stream->modeflags & __FLAG_NARROW); -#endif /* __STDIO_WIDE */ - *p++ = stream->ungot[(--stream->modeflags) & __MASK_UNGOT]; - stream->ungot[1] = 0; - --bytes; - } - - /* Now get any other needed chars from the buffer or the file. */ - FROM_BUF: - while (bytes && (stream->bufpos < stream->bufread)) { - --bytes; - *p++ = *stream->bufpos++; - } - - if (bytes > 0) { - ssize_t len; - - if (stream->filedes == -2) { - stream->modeflags |= __FLAG_EOF; - goto DONE; - } - - /* The buffer is exhausted, but we still need chars. */ - stream->bufpos = stream->bufread = stream->bufstart; - - if (bytes <= stream->bufend - stream->bufread) { - /* We have sufficient space in the buffer. */ - len = _stdio_READ(stream, stream->bufread, - stream->bufend - stream->bufread); - if (len > 0) { - stream->bufread += len; - goto FROM_BUF; - } - } else { - /* More bytes needed than fit in the buffer, so read */ - /* directly into caller's buffer. */ - len = _stdio_READ(stream, p, bytes); - if (len > 0) { - p += len; - bytes -= len; - goto FROM_BUF; /* Redundant work, but stops extra read. */ - } - } - } - -#ifdef __STDIO_GETC_MACRO - if (!(stream->modeflags - & (__FLAG_WIDE|__MASK_UNGOT|__MASK_BUFMODE|__FLAG_ERROR)) - ) { - stream->bufgetc = stream->bufread; /* Enable getc macro. */ - } -#endif - - DONE: - __stdio_validate_FILE(stream); /* debugging only */ - return (p - (unsigned char *)buffer); - } - -#else /* __STDIO_BUFFERS --------------------------------------- */ - - if (stream->modeflags -#ifdef __STDIO_AUTO_RW_TRANSITION - & (__FLAG_WRITEONLY) -#else /* __STDIO_AUTO_RW_TRANSITION */ - /* ANSI/ISO and SUSv3 require not currently writing. */ - & (__FLAG_WRITEONLY|__FLAG_WRITING) -#endif /* __STDIO_AUTO_RW_TRANSITION */ - ) { - stream->modeflags |= __FLAG_ERROR; - /* TODO: This is for posix behavior if writeonly. To save space, we - * use this errno for read attempt while writing, as no errno is - * specified by posix for this case, even though the restriction is - * mentioned in fopen(). */ - __set_errno(EBADF); - return 0; - } - -#ifdef __STDIO_AUTO_RW_TRANSITION - stream->modeflags &= ~(__FLAG_WRITING); /* Make sure Writing flag clear. */ -#endif /* __STDIO_AUTO_RW_TRANSITION */ - - stream->modeflags |= __FLAG_READING; /* Make sure Reading flag is set. */ - - { - register unsigned char *p = (unsigned char *) buffer; - - /* First, grab appropriate ungetc() chars. NOT FOR WIDE ORIENTATED! */ - while (bytes && (stream->modeflags & __MASK_UNGOT)) { -#ifdef __STDIO_WIDE - assert(stream->modeflags & __FLAG_NARROW); -#endif /* __STDIO_WIDE */ - *p++ = stream->ungot[(--stream->modeflags) & __MASK_UNGOT]; - stream->ungot[1] = 0; - --bytes; - } - - while (bytes > 0) { - ssize_t len = _stdio_READ(stream, p, (unsigned) bytes); - if (len == 0) { - break; - } - p += len; - bytes -= len; - } - - __stdio_validate_FILE(stream); /* debugging only */ - return (p - (unsigned char *)buffer); - } - -#endif /* __STDIO_BUFFERS */ -} - -#endif -/**********************************************************************/ -#ifdef L__stdio_fwrite -/* - * If buffer == NULL, attempt to fflush and return number of chars - * remaining in buffer (0 if successful fflush). - */ - -/* WARNING!!!! Current standards say that termination due to an asyncronous - * signal may not result in stdio streams being flushed. This libary makes - * an effort to do so but there is no way, short of blocking signals for - * each _stdio_fwrite call, that we can maintain the correct state if a - * signal is recieved mid-call. So any stream in mid-_stdio_fwrite could - * not some flush data or even duplicate-flush some data. It is possible - * to avoid the duplicate-flush case by setting/clearing the stream - * error flag before/after the write process, but it doesn't seem worth - * the trouble. */ - -/* Like standard write, but always does a full write unless error, plus - * deals correctly with bufsize > SSIZE_MAX... not much on an issue on linux - * but definitly could be on Elks. Also on Elks, always loops for EINTR.. - * Returns number of bytes written, so a short write indicates an error */ -static size_t _stdio_WRITE(register FILE *stream, - register const unsigned char *buf, size_t bufsize) -{ - size_t todo; - ssize_t rv, stodo; - - todo = bufsize; - - while (todo) { - stodo = (todo <= SSIZE_MAX) ? todo : SSIZE_MAX; - rv = __WRITE(stream, buf, stodo); - if (rv >= 0) { -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS - assert(rv <= stodo); /* buggy user handler... TODO: check? */ - if (rv > stodo) { /* Num reported written > number requested */ - rv = stodo; /* Treat as a full write??? */ - } -#endif - todo -= rv; - buf += rv; - } else -#ifdef __BCC__ - if (errno != EINTR) -#endif - { - stream->modeflags |= __FLAG_ERROR; - break; - } - } - - return bufsize - todo; -} - -/* Internal function -- not reentrant. */ - -size_t _stdio_fwrite(const unsigned char *buffer, size_t bytes, - register FILE *stream) -{ -#ifdef __STDIO_BUFFERS - register const unsigned char *p; - - __stdio_validate_FILE(stream); /* debugging only */ - - if ((stream->modeflags & __FLAG_READONLY) -#ifndef __STDIO_AUTO_RW_TRANSITION - /* ANSI/ISO requires either at EOF or currently not reading. */ - || ((stream->modeflags & (__FLAG_READING|__FLAG_EOF)) - == __FLAG_READING) -#endif /* __STDIO_AUTO_RW_TRANSITION */ - ) { - stream->modeflags |= __FLAG_ERROR; - /* TODO: This is for posix behavior if readonly. To save space, we - * use this errno for write attempt while reading, as no errno is - * specified by posix for this case, even though the restriction is - * mentioned in fopen(). */ - __set_errno(EBADF); - return 0; - } - -#ifdef __STDIO_AUTO_RW_TRANSITION - /* If reading, deal with ungots and read-buffered chars. */ - if (stream->modeflags & __FLAG_READING) { - if (((stream->bufpos < stream->bufread) - || (stream->modeflags & __MASK_UNGOT)) - /* If appending, we might as well seek to end to save a seek. */ - /* TODO: set EOF in fseek when appropriate? */ - && fseek(stream, 0L, - ((stream->modeflags & __FLAG_APPEND) - ? SEEK_END : SEEK_CUR)) - ) { - /* Note: This differs from glibc's apparent behavior of - not setting the error flag and discarding the buffered - read data. */ - stream->modeflags |= __FLAG_ERROR; /* fseek may not set this. */ - return 0; /* Fail if we need to fseek but can't. */ - } - /* Always reset even if fseek called (saves a test). */ -#ifdef __STDIO_GETC_MACRO - stream->bufgetc = -#endif /* __STDIO_GETC_MACRO */ - stream->bufpos = stream->bufread = stream->bufstart; - } else -#endif - if ((stream->modeflags & (__FLAG_WRITING|__FLAG_APPEND)) == __FLAG_APPEND) { - /* Append mode, but not currently writing. Need to seek to end for proper - * ftell() return values. Don't worry if the stream isn't seekable. */ - __offmax_t pos[1]; - *pos = 0; - if (_stdio_lseek(stream, pos, SEEK_END) && (errno != EPIPE)) { /* Too big? */ - stream->modeflags |= __FLAG_ERROR; - return 0; - } - } - -#ifdef __STDIO_PUTC_MACRO - /* We need to disable putc macro in case of error */ - stream->bufputc = stream->bufstart; -#endif /* __STDIO_GETC_MACRO */ - - /* Clear both reading and writing flags. We need to clear the writing - * flag in case we're fflush()ing or in case of an error. */ - stream->modeflags &= ~(__FLAG_READING|__FLAG_WRITING); - - { - const unsigned char *buf0 = buffer; - size_t write_count = 1; /* 0 means a write failed */ - - if (!buffer) { /* fflush the stream */ - FFLUSH: - { - size_t count = stream->bufpos - stream->bufstart; - p = stream->bufstart; - - if (stream->filedes == -2) { /* TODO -- document this hack! */ - stream->modeflags |= __FLAG_WRITING; - return (!buffer) ? 0 : ((buffer - buf0) + bytes); - } - - { - write_count = _stdio_WRITE(stream, p, count); - p += write_count; - count -= write_count; - } - - stream->bufpos = stream->bufstart; - while (count) { - *stream->bufpos++ = *p++; - --count; - } - - if (!buffer) { /* fflush case... */ - __stdio_validate_FILE(stream); /* debugging only */ - return stream->bufpos - stream->bufstart; - } - } - } - -#if 1 - /* TODO: If the stream is buffered, we may be able to omit. */ - if ((stream->bufpos == stream->bufstart) /* buf empty */ - && (stream->bufend - stream->bufstart <= bytes) /* fills */ - && (stream->filedes != -2)) { /* not strinf fake file */ - /* so want to do a direct write of supplied buffer */ - { - size_t rv = _stdio_WRITE(stream, buffer, bytes); - buffer += rv; - bytes -= rv; - } - } else -#endif - /* otherwise buffer not empty and/or data fits */ - { - size_t count = stream->bufend - stream->bufpos; - p = buffer; - - if (count > bytes) { - count = bytes; - } - bytes -= count; - - while (count) { - *stream->bufpos++ = *buffer++; - --count; - } - - if (write_count) { /* no write errors */ - if (bytes) { - goto FFLUSH; - } - - if (stream->modeflags & __FLAG_LBF) { - while (p < buffer) { /* check for newline. */ - if (*p++ == '\n') { - goto FFLUSH; - } - } - } - } - } - -#ifdef __STDIO_PUTC_MACRO - if (!(stream->modeflags & (__FLAG_WIDE|__MASK_BUFMODE|__FLAG_ERROR))) { - /* Not wide, no errors and fully buffered, so enable putc macro. */ - stream->bufputc = stream->bufend; - } -#endif /* __STDIO_GETC_MACRO */ - stream->modeflags |= __FLAG_WRITING; /* Ensure Writing flag is set. */ - - __stdio_validate_FILE(stream); /* debugging only */ - return buffer - buf0; - - } - -#else /* __STDIO_BUFFERS --------------------------------------- */ - - __stdio_validate_FILE(stream); /* debugging only */ - - if ((stream->modeflags & __FLAG_READONLY) -#ifndef __STDIO_AUTO_RW_TRANSITION - /* ANSI/ISO requires either at EOF or currently not reading. */ - || ((stream->modeflags & (__FLAG_READING|__FLAG_EOF)) - == __FLAG_READING) -#endif /* __STDIO_AUTO_RW_TRANSITION */ - ) { - stream->modeflags |= __FLAG_ERROR; - /* TODO: This is for posix behavior if readonly. To save space, we - * use this errno for write attempt while reading, as no errno is - * specified by posix for this case, even though the restriction is - * mentioned in fopen(). */ - __set_errno(EBADF); - return 0; - } - - /* We always clear the reading flag in case at EOF. */ - stream->modeflags &= ~(__FLAG_READING); - - /* Unlike the buffered case, we set the writing flag now since we don't - * need to do anything here for fflush(). */ - stream->modeflags |= __FLAG_WRITING; - - { - register unsigned char *p = (unsigned char *) buffer; - - ssize_t rv = _stdio_WRITE(stream, p, bytes); - - p += rv; - bytes -= rv; - - __stdio_validate_FILE(stream); /* debugging only */ - return (p - (unsigned char *)buffer); - } - -#endif /* __STDIO_BUFFERS *****************************************/ -} - -#endif -/**********************************************************************/ -#ifdef L__stdio_init - -/* Internal functions -- _stdio_init() and __stdio_validate_FILE - * are not reentrant, but _stdio_term() is through fflush(). - * Also, the _cs_{read|write|close} functions are not reentrant. */ - -#ifndef NDEBUG -void __stdio_validate_FILE(FILE *stream) -{ - if (stream->filedes == -2) { /* fake FILE for sprintf, scanf, etc. */ - return; - } - - __STDIO_THREADLOCK(stream); - -#ifdef __STDIO_BUFFERS - assert(stream->bufstart <= stream->bufread); - if (stream->modeflags & (__FLAG_READING)) { - assert(stream->bufpos <= stream->bufread); - } - assert(stream->bufread <= stream->bufend); - assert(stream->bufpos <= stream->bufend); - if ((stream->modeflags & __MASK_BUFMODE) == __FLAG_NBF) { - assert(stream->bufstart == stream->bufend); - } - assert((stream->modeflags & __MASK_BUFMODE) <= __FLAG_NBF); -#endif -#ifdef __STDIO_PUTC_MACRO - assert(stream->bufstart <= stream->bufputc); - assert(stream->bufputc <= stream->bufend); - if (stream->bufstart < stream->bufputc) { - assert(stream->bufputc == stream->bufend); - assert(stream->modeflags & (__FLAG_WRITING)); - assert(!(stream->modeflags - & (__FLAG_WIDE|__MASK_BUFMODE|__MASK_UNGOT|__FLAG_READONLY)) - ); - } -#endif -#ifdef __STDIO_GETC_MACRO - assert(stream->bufstart <= stream->bufgetc); - assert(stream->bufgetc <= stream->bufread); - if (stream->bufstart < stream->bufgetc) { - assert(stream->modeflags & (__FLAG_READING)); - assert(!(stream->modeflags - & (__FLAG_WIDE|__MASK_BUFMODE|__MASK_UNGOT|__FLAG_WRITEONLY)) - ); - } -#endif - assert((stream->modeflags & __MASK_UNGOT) != __MASK_UNGOT); - if (stream->modeflags & __MASK_UNGOT1) { - assert(stream->ungot[1] <= 1); - } - if (stream->modeflags & __MASK_UNGOT) { - assert(!(stream->modeflags & __FLAG_EOF)); - } - assert((stream->modeflags & (__FLAG_READONLY|__FLAG_WRITEONLY)) - != (__FLAG_READONLY|__FLAG_WRITEONLY)); - - /* TODO -- filepos? ungot_width? filedes? nextopen? */ - - __STDIO_THREADUNLOCK(stream); -} -#endif - -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS -ssize_t _cs_read(void *cookie, char *buf, size_t bufsize) -{ - return read(*((int *) cookie), buf, bufsize); -} - -ssize_t _cs_write(void *cookie, const char *buf, size_t bufsize) -{ - return write(*((int *) cookie), (char *) buf, bufsize); -} - -int _cs_close(void *cookie) -{ - return close(*((int *) cookie)); -} -#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ - -#ifdef __STDIO_BUFFERS -static unsigned char _fixed_buffers[2 * BUFSIZ]; -#define bufin (_fixed_buffers) -#define bufout (_fixed_buffers + BUFSIZ) -#endif /* __STDIO_BUFFERS */ - -static FILE _stdio_streams[] = { - __STDIO_INIT_FILE_STRUCT(_stdio_streams[0], __FLAG_LBF|__FLAG_READONLY, \ - 0, _stdio_streams + 1, bufin, BUFSIZ ), - __STDIO_INIT_FILE_STRUCT(_stdio_streams[1], __FLAG_LBF|__FLAG_WRITEONLY, \ - 1, _stdio_streams + 2, bufout, BUFSIZ ), - __STDIO_INIT_FILE_STRUCT(_stdio_streams[2], __FLAG_NBF|__FLAG_WRITEONLY, \ - 2, 0, 0, 0 ) -}; - -FILE *stdin = _stdio_streams + 0; -FILE *stdout = _stdio_streams + 1; -FILE *stderr = _stdio_streams + 2; - -#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) - -#ifdef __STDIO_BUFFERS -FILE *_stdio_openlist = _stdio_streams; -#else /* __STDIO_BUFFERS */ -FILE *_stdio_openlist = NULL; -#endif /* __STDIO_BUFFERS */ - -#ifdef __STDIO_THREADSAFE -pthread_mutex_t _stdio_openlist_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; - - -void __stdio_init_mutex(pthread_mutex_t *m) -{ - static const pthread_mutex_t __stdio_mutex_initializer - = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; - - memcpy(m, &__stdio_mutex_initializer, sizeof(__stdio_mutex_initializer)); -} -#endif /* __STDIO_THREADSAFE */ - -/* TODO - do we need to lock things, or do we just assume we're the only - * remaining thread? */ - -/* Note: We assume here that we are the only remaining thread. */ -void _stdio_term(void) -{ -#if defined(__STDIO_GLIBC_CUSTOM_STREAMS) || defined(__STDIO_THREADSAFE) - register FILE *ptr; -#endif - - /* TODO: if called via a signal handler for a signal mid _stdio_fwrite, - * the stream may be in an unstable state... what do we do? - * perhaps set error flag before and clear when done if successful? */ - -#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#ifdef __STDIO_THREADSAFE - /* First, forceably unlock the open file list and all files. - * Note: Set locking mode to "by caller" to save some overhead later. */ - __stdio_init_mutex(&_stdio_openlist_lock); - for (ptr = _stdio_openlist ; ptr ; ptr = ptr->nextopen ) { - ptr->user_locking = 1; - __stdio_init_mutex(&ptr->lock); - } -#endif /* __STDIO_THREADSAFE */ -#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ - -#ifdef __STDIO_BUFFERS - /* TODO -- set an alarm and flush each file "by hand"? to avoid blocking? */ - - /* Now flush all streams. */ - fflush_unlocked(NULL); -#endif /* __STDIO_BUFFERS */ - - /* Next close all custom streams in case of any special cleanup, but - * don't use fclose() because that pulls in free and malloc. Also, - * don't worry about removing them from the list. Just set the cookie - * pointer to NULL so that an error will be generated if someone tries - * to use the stream. */ -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS - for (ptr = _stdio_openlist ; ptr ; ptr = ptr->nextopen ) { - if (ptr->cookie != &ptr->filedes) { /* custom stream */ - __CLOSE(ptr); - ptr->cookie = NULL; /* Generate an error if used later. */ -#if 0 -/* #ifdef __STDIO_BUFFERS */ - } else { - /* TODO: "unbuffer" files like glibc does? Inconsistent with - * custom stream handling above, but that's necessary to deal - * with special user-defined close behavior. */ - stream->bufpos = stream->bufread = stream->bufend -#ifdef __STDIO_GETC_MACRO - = stream->bufgetc -#endif -#ifdef __STDIO_PUTC_MACRO - = stream->bufputc -#endif - = stream->bufstart; -#endif /* __STDIO_BUFFERS */ - } - } -#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ -} - - -#if defined(__STDIO_BUFFERS) || !defined(__UCLIBC__) -void _stdio_init(void) -{ -#ifdef __STDIO_BUFFERS - int old_errno = errno; - /* stdin and stdout uses line buffering when connected to a tty. */ - _stdio_streams[0].modeflags ^= (1-isatty(0)) * __FLAG_LBF; - _stdio_streams[1].modeflags ^= (1-isatty(1)) * __FLAG_LBF; - __set_errno(old_errno); -#endif /* __STDIO_BUFFERS */ -#ifndef __UCLIBC__ -/* __stdio_term is automatically when exiting if stdio is used. - * See misc/internals/__uClibc_main.c and and stdlib/atexit.c. */ - atexit(_stdio_term); -#endif /* __UCLIBC__ */ -} -#endif /* defined(__STDIO_BUFFERS) || !defined(__UCLIBC__) */ -#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ - -#endif -/**********************************************************************/ -/* ANSI/ISO functions. */ -/**********************************************************************/ -#ifdef L_remove -#include <unistd.h> -#include <errno.h> - -/* No reentrancy issues. */ - -int remove(register const char *filename) -{ - int old_errno = errno; - - /* SUSv3 says equivalent to rmdir() if a directory, and unlink() - * otherwise. Hence, we need to try rmdir() first. */ - - return (((rmdir(filename) == 0) - || ((errno == ENOTDIR) - && ((__set_errno(old_errno), unlink(filename)) == 0))) - ? 0 : -1); -} -#endif -/**********************************************************************/ -/* rename is a syscall -#ifdef L_rename -int rename(const char *old, const char *new); -#endif -*/ -/**********************************************************************/ -/* TODO: tmpfile */ -/* #ifdef L_tmpfile */ -/* FILE *tmpfile(void); */ -/* #endif */ -/**********************************************************************/ -/* TODO: tmpname */ -/* #ifdef L_tmpname */ -/* char *tmpname(char *s); */ -/* #endif */ -/**********************************************************************/ -#ifdef L_fclose - -/* We need to be careful here to avoid deadlock when threading, as we - * need to lock both the file and the open file list. This can clash - * with fflush. Since fflush is much more common, we do the extra - * work here. */ - -int fclose(register FILE *stream) -{ -#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) - - register FILE *ptr; - int rv = 0; - -#ifdef __STDIO_THREADSAFE - /* Need two non-heirchal mutexs... be careful to avoid deadlock*/ - do { - __STDIO_THREADLOCK(stream); - if (__STDIO_THREADTRYLOCK_OPENLIST == 0) { - break; - } - __STDIO_THREADUNLOCK(stream); - usleep(10000); - } while (1); -#endif /* __STDIO_THREADSAFE */ - - __stdio_validate_FILE(stream); /* debugging only */ - -#ifdef __STDIO_BUFFERS - if (stream->modeflags & __FLAG_WRITING) { - rv = fflush_unlocked(stream); /* Write any pending buffered chars. */ - } /* Also disables putc macro if used. */ - -#ifdef __STDIO_GETC_MACRO - /* Not necessary after fflush, but always do this to reduce size. */ - stream->bufgetc = stream->bufstart; /* Disable getc macro for safety. */ -#endif /* __STDIO_GETC_MACRO */ -#endif /* __STDIO_BUFFERS */ - - /* Remove file from open list before closing file descriptor. */ - ptr = _stdio_openlist; - if (ptr == stream) { - _stdio_openlist = stream->nextopen; - } else { - while (ptr) { - if (ptr->nextopen == stream) { - ptr->nextopen = stream->nextopen; - break; - } - ptr = ptr->nextopen; - } - } - __STDIO_THREADUNLOCK_OPENLIST; /* We're done with the open file list. */ - - if (__CLOSE(stream) < 0) { /* Must close even if fflush failed. */ - rv = EOF; - } -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS - stream->cookie = &(stream->filedes); - stream->gcs.read = _cs_read; - stream->gcs.write = _cs_write; - stream->gcs.seek = 0; /* The internal seek func handles normals. */ - stream->gcs.close = _cs_close; -#endif - stream->filedes = -1; /* To aid debugging... */ - -#ifdef __STDIO_BUFFERS - if (stream->modeflags & __FLAG_FREEBUF) { - free(stream->bufstart); - } -#endif /* __STDIO_BUFFERS */ - - /* TODO -- leave the stream locked to catch any dangling refs? */ - __STDIO_THREADUNLOCK(stream); - - /* At this point, any dangling refs to the stream are the result of - * a programming bug... so free the unlocked stream. */ - if (stream->modeflags & __FLAG_FREEFILE) { -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS - stream->cookie = NULL; /* To aid debugging... */ -#endif - free(stream); - } - - return rv; - -#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ - - int rv = 0; - - __STDIO_THREADLOCK(stream); - - __stdio_validate_FILE(stream); /* debugging only */ - - if (__CLOSE(stream) < 0) { /* Must close even if fflush failed. */ - rv = EOF; - } - -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS - stream->cookie = &(stream->filedes); - stream->gcs.read = _cs_read; - stream->gcs.write = _cs_write; - stream->gcs.seek = 0; /* The internal seek func handles normals. */ - stream->gcs.close = _cs_close; -#endif - stream->filedes = -1; /* To aid debugging... */ - - __STDIO_THREADUNLOCK(stream); - - /* At this point, any dangling refs to the stream are the result of - * a programming bug... so free the unlocked stream. */ - if (stream->modeflags & __FLAG_FREEFILE) { -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS - stream->cookie = NULL; /* To aid debugging... */ -#endif - free(stream); - } - - return rv; - -#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS )*/ -} - -#endif -/**********************************************************************/ -#ifdef L_fflush - -/* - * Special cases: - * stream == NULL means fflush all writing streams (ANSI/ISO). - * stream == (FILE *) &_stdio_openlist -- implementation-specific hack - * meaning fflush all line buffered writing streams - */ - -/* - * NOTE: ANSI/ISO difference!!! According to the standard, fflush is only - * defined for write-only streams, or read/write streams whose last op - * was a write. However, reading is allowed for a read/write stream if - * a file positioning operation was done (fseek, fsetpos) even though there - * is no guarantee of flushing the write data in that case. Hence, for - * this case we keep a flag to indicate whether or not the buffer needs to - * be flushed even if the last operation was a read. This falls under the - * implementation-defined behavior. Otherwise, we would need to flush - * every time we did fseek, etc. even if we were still in the buffer's range. - */ - -/* Since the stream pointer arg is allowed to be NULL, or the address of the - * stdio open file list if stdio is buffered in this implementation, we can't - * use the UNLOCKED() macro here. */ - -#ifndef __STDIO_THREADSAFE -strong_alias(fflush_unlocked,fflush) -#else /* __STDIO_THREADSAFE */ -int fflush(register FILE *stream) -{ - int retval; - - if ((stream != NULL) -#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) - && (stream != (FILE *) &_stdio_openlist) -#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ - ) { - __STDIO_THREADLOCK(stream); - retval = fflush_unlocked(stream); - __STDIO_THREADUNLOCK(stream); - } else { - retval = fflush_unlocked(stream); - } - - return retval; -} -#endif /* __STDIO_THREADSAFE */ - -int fflush_unlocked(register FILE *stream) -{ -#ifdef __STDIO_BUFFERS - - int rv = 0; - unsigned short mask = (__FLAG_NBF|__FLAG_LBF); - -#ifndef NDEBUG - if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { - __stdio_validate_FILE(stream); /* debugging only */ - } -#endif - - if (stream == (FILE *) &_stdio_openlist) { /* fflush all line-buffered */ - stream = NULL; - mask = __FLAG_LBF; - } - - if (stream == NULL) { /* flush all (line) buffered writing streams */ - /* Note -- We have to lock the list even in the unlocked function. */ - __STDIO_THREADLOCK_OPENLIST; - /* TODO -- Can we work around locking the list to avoid keeping it - * locked if the write blocks? */ - for (stream = _stdio_openlist; stream; stream = stream->nextopen) { - if (((stream->modeflags ^ __FLAG_NBF) & mask) - && (stream->modeflags & __FLAG_WRITING) - && fflush(stream)) { - rv = EOF; - } - } - __STDIO_THREADUNLOCK_OPENLIST; - } else if (stream->modeflags & __FLAG_WRITING) { - if (_stdio_fwrite(NULL, 0, stream) > 0) { /* flush buffer contents. */ - rv = -1; /* Not all chars written. */ - } -#ifdef __UCLIBC_MJN3_ONLY__ -#warning WISHLIST: Add option to test for undefined behavior of fflush. -#endif /* __UCLIBC_MJN3_ONLY__ */ -#if 0 - } else if (stream->modeflags & (__FLAG_READING|__FLAG_READONLY)) { - /* ANSI/ISO says behavior in this case is undefined but also says you - * shouldn't flush a stream you were reading from. As usual, glibc - * caters to broken programs and simply ignores this. */ - stream->modeflags |= __FLAG_ERROR; - __set_errno(EBADF); - rv = -1; -#endif - } - -#ifndef NDEBUG - if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { - __stdio_validate_FILE(stream); /* debugging only */ - } -#endif - return rv; - -#else /* __STDIO_BUFFERS --------------------------------------- */ - -#ifndef NDEBUG - if ((stream != NULL) -#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) - && (stream != (FILE *) &_stdio_openlist) -#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ - ) { - __stdio_validate_FILE(stream); /* debugging only */ - } -#endif - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning WISHLIST: Add option to test for undefined behavior of fflush. -#endif /* __UCLIBC_MJN3_ONLY__ */ -#if 0 - return ((stream != NULL) - && (stream->modeflags & (__FLAG_READING|__FLAG_READONLY)) - ? ((stream->modeflags |= __FLAG_ERROR), __set_errno(EBADF), EOF) - : 0 ); -#else - return 0; -#endif - -#endif /* __STDIO_BUFFERS */ -} -#endif -/**********************************************************************/ -#ifdef L_fopen - -/* No reentrancy issues. */ - -FILE *fopen(const char * __restrict filename, const char * __restrict mode) -{ - return _stdio_fopen(filename, mode, NULL, -1); -} - -#endif -/**********************************************************************/ -#ifdef L__stdio_fopen - -/* - * Cases: - * fopen : filename != NULL, stream == NULL, filedes == -1 - * freopen : filename != NULL, stream != NULL, filedes == -1 - * fdopen : filename == NULL, stream == NULL, filedes valid - * fsfopen : filename != NULL, stream != NULL, filedes == -1 - * fopen64 : filename != NULL, stream == NULL, filedes == -2 - */ - -#if O_ACCMODE != 3 || O_RDONLY != 0 || O_WRONLY != 1 || O_RDWR != 2 -#error Assumption violated - mode constants -#endif - -/* Internal function -- reentrant (locks open file list) */ - -FILE *_stdio_fopen(const char * __restrict filename, - register const char * __restrict mode, - register FILE * __restrict stream, int filedes) -{ - __mode_t open_mode; - - /* parse mode */ - open_mode = O_RDONLY; - if (*mode != 'r') { /* not read */ - open_mode = (O_WRONLY | O_CREAT | O_TRUNC); - if (*mode != 'w') { /* not write (create or truncate)*/ - open_mode = (O_WRONLY | O_CREAT | O_APPEND); - if (*mode != 'a') { /* not write (create or append) */ - __set_errno(EINVAL); /* then illegal mode */ - return NULL; - } - } - } - - if ((*++mode == 'b')) { /* binary mode (NOP currently) */ - ++mode; - } - - if (*mode == '+') { /* read-write */ - ++mode; - open_mode &= ~(O_RDONLY | O_WRONLY); - open_mode |= O_RDWR; - } - -#if defined(__STDIO_FOPEN_EXCLUSIVE_MODE) || defined(__STDIO_FOPEN_LARGEFILE_MODE) - for ( ; *mode ; ++mode) { /* ignore everything else except ... */ -#ifdef __STDIO_FOPEN_EXCLUSIVE_MODE - if (*mode == 'x') { /* open exclusive -- glibc extension */ - open_mode |= O_EXCL; - continue; - } -#endif /* __STDIO_FOPEN_EXCLUSIVE_MODE */ -#ifdef __STDIO_FOPEN_LARGEFILE_MODE - if (*mode == 'F') { /* open large file */ - open_mode |= O_LARGEFILE; - continue; - } -#endif /* __STDIO_FOPEN_LARGEFILE_MODE */ - } -#endif /* __STDIO_FOPEN_EXCLUSIVE_MODE or __STDIO_FOPEN_LARGEFILE_MODE def'd */ - -#ifdef __BCC__ - mode = filename; /* TODO: help BCC with register allocation. */ -#define filename mode -#endif /* __BCC__ */ - - if (!stream) { /* Need to allocate a FILE. */ -#ifdef __STDIO_BUFFERS - if ((stream = malloc(sizeof(FILE))) == NULL) { - return stream; - } - stream->modeflags = __FLAG_FREEFILE; - if ((stream->bufstart = malloc(BUFSIZ)) != 0) { - stream->bufend = stream->bufstart + BUFSIZ; - stream->modeflags |= __FLAG_FREEBUF; - } else { -#if __STDIO_BUILTIN_BUF_SIZE > 0 - stream->bufstart = stream->unbuf; - stream->bufend = stream->unbuf + sizeof(stream->unbuf); -#else /* __STDIO_BUILTIN_BUF_SIZE > 0 */ - stream->bufstart = stream->bufend = NULL; -#endif /* __STDIO_BUILTIN_BUF_SIZE > 0 */ - } -#else /* __STDIO_BUFFERS */ - if ((stream = malloc(sizeof(FILE))) == NULL) { - return stream; - } - stream->modeflags = __FLAG_FREEFILE; -#endif /* __STDIO_BUFFERS */ - } - - if (filedes >= 0) { /* Handle fdopen trickery. */ - /* NOTE: it is insufficient to just check R/W/RW agreement. - * We must also check largefile compatibility if applicable. - * Also, if append mode is desired for fdopen but O_APPEND isn't - * currently set, then set it as recommended by SUSv3. However, - * if append mode is not specified for fdopen but O_APPEND is set, - * leave it set (glibc compat). */ - int i = (open_mode & (O_ACCMODE|O_LARGEFILE)) + 1; - - /* NOTE: fopencookie needs changing if the basic check changes! */ - if (((i & (((int) filename) + 1)) != i) /* Check basic agreement. */ - || (((open_mode & O_APPEND) - && !(((int) filename) & O_APPEND) - && fcntl(filedes, F_SETFL, O_APPEND))) /* Need O_APPEND. */ - ) { - __set_errno(EINVAL); - filedes = -1; - } -#ifdef __STDIO_LARGE_FILES - /* For later... to reflect largefile setting in stream flags. */ - open_mode |= (((int) filename) & O_LARGEFILE); -#endif /* __STDIO_LARGE_FILES */ - stream->filedes = filedes; - } else { -#ifdef __STDIO_LARGE_FILES - if (filedes < -1) { - open_mode |= O_LARGEFILE; - } -#endif /* __STDIO_LARGE_FILES */ - stream->filedes = open(filename, open_mode, 0666); - } - - if (stream->filedes < 0) { -#ifdef __STDIO_BUFFERS - if (stream->modeflags & __FLAG_FREEBUF) { - free(stream->bufstart); - } -#endif /* __STDIO_BUFFERS */ - if (stream->modeflags & __FLAG_FREEFILE) { - free(stream); - } - return NULL; - } - -#ifdef __STDIO_BUFFERS - { - /* Do not let isatty mess up errno */ - int old_errno = errno; - stream->modeflags |= (isatty(stream->filedes) * __FLAG_LBF); - __set_errno(old_errno); - } -#endif - - stream->modeflags |= -#if (O_APPEND == __FLAG_APPEND) \ -&& ((O_LARGEFILE == __FLAG_LARGEFILE) || (O_LARGEFILE == 0)) - (open_mode & (O_APPEND|O_LARGEFILE)) | /* i386 linux and elks */ -#else /* (O_APPEND == __FLAG_APPEND) && (O_LARGEFILE == __FLAG_LARGEFILE) */ - ((open_mode & O_APPEND) ? __FLAG_APPEND : 0) | -#ifdef __STDIO_LARGE_FILES - ((open_mode & O_LARGEFILE) ? __FLAG_LARGEFILE : 0) | -#endif /* __STDIO_LARGE_FILES */ -#endif /* (O_APPEND == __FLAG_APPEND) && (O_LARGEFILE == __FLAG_LARGEFILE) */ - ((((open_mode & O_ACCMODE) + 1) ^ 0x03) * __FLAG_WRITEONLY); - -#ifdef __STDIO_BUFFERS -#ifdef __STDIO_GETC_MACRO - stream->bufgetc = -#endif -#ifdef __STDIO_PUTC_MACRO - stream->bufputc = -#endif - stream->bufpos = stream->bufread = stream->bufstart; -#endif /* __STDIO_BUFFERS */ - -#ifdef __STDIO_GLIBC_CUSTOM_STREAMS - stream->cookie = &(stream->filedes); - stream->gcs.read = _cs_read; - stream->gcs.write = _cs_write; - stream->gcs.seek = 0; /* The internal seek func handles normals. */ - stream->gcs.close = _cs_close; -#endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ - -#ifdef __STDIO_WIDE - stream->ungot_width[0] = 0; -#endif /* __STDIO_WIDE */ -#ifdef __STDIO_MBSTATE - __INIT_MBSTATE(&(stream->state)); -#endif /* __STDIO_MBSTATE */ - -#ifdef __STDIO_THREADSAFE - stream->user_locking = 0; - __stdio_init_mutex(&stream->lock); -#endif /* __STDIO_THREADSAFE */ - -#if defined(__STDIO_BUFFERS) \ -|| (defined(__STDIO_THREADSAFE) && defined(__STDIO_GLIBC_CUSTOM_STREAMS)) - __STDIO_THREADLOCK_OPENLIST; - stream->nextopen = _stdio_openlist; /* New files are inserted at */ - _stdio_openlist = stream; /* the head of the list. */ - __STDIO_THREADUNLOCK_OPENLIST; -#endif - - __stdio_validate_FILE(stream); /* debugging only */ - return stream; -#ifdef __BCC__ -#undef filename -#endif /* __BCC__ */ -} - -#endif -/**********************************************************************/ -#ifdef L_freopen - -/* Reentrant. */ - -FILE *freopen(const char * __restrict filename, const char * __restrict mode, - register FILE * __restrict stream) -{ - /* - * ANSI/ISO allow (implementation-defined) change of mode for an - * existing file if filename is NULL. It doesn't look like Linux - * supports this, so we don't here. - * - * NOTE: Whether or not the stream is free'd on failure is unclear - * w.r.t. ANSI/ISO. This implementation chooses to NOT free - * the stream and associated buffer if they were dynamically - * allocated. - * TODO: Check the above. - * TODO: Apparently linux allows setting append mode. Implement? - */ - unsigned short dynmode; - register FILE *fp; - - __STDIO_THREADLOCK(stream); - - /* First, flush and close, but don't deallocate, the stream. */ - /* This also removes the stream for the open file list. */ - dynmode = -#ifdef __STDIO_BUFFERS - /* __MASK_BUFMODE | */ /* TODO: check */ -#endif /* __STDIO_BUFFERS */ - (stream->modeflags & (__FLAG_FREEBUF|__FLAG_FREEFILE)); - - stream->modeflags &= ~(__FLAG_FREEBUF|__FLAG_FREEFILE); - fclose(stream); /* Failures are ignored. */ - - fp = _stdio_fopen(filename, mode, stream, -1); - - stream->modeflags |= dynmode; - - __STDIO_THREADUNLOCK(stream); - - return fp; -} -#endif -/**********************************************************************/ -#ifdef L_freopen64 - -/* Reentrant. */ - -/* TODO -- is it worth collecting the common work (40 bytes) in a function? */ -FILE *freopen64(const char * __restrict filename, const char * __restrict mode, - register FILE * __restrict stream) -{ - unsigned short dynmode; - register FILE *fp; - - __STDIO_THREADLOCK(stream); - - /* First, flush and close, but don't deallocate, the stream. */ - /* This also removes the stream for the open file list. */ - dynmode = -#ifdef __STDIO_BUFFERS - /* __MASK_BUFMODE | */ /* TODO: check */ -#endif /* __STDIO_BUFFERS */ - (stream->modeflags & (__FLAG_FREEBUF|__FLAG_FREEFILE)); - - stream->modeflags &= ~(__FLAG_FREEBUF|__FLAG_FREEFILE); - fclose(stream); /* Failures are ignored. */ - stream->modeflags = dynmode; - - fp = _stdio_fopen(filename, mode, stream, -2); /* TODO -- magic const */ - - __STDIO_THREADUNLOCK(stream); - - return fp; -} - -#endif -/**********************************************************************/ -#ifdef L_setbuf - -/* Reentrant through setvbuf(). */ - -void setbuf(FILE * __restrict stream, register char * __restrict buf) -{ -#ifdef __STDIO_BUFFERS - int mode; - - mode = (buf != NULL) ? _IOFBF : _IONBF; - setvbuf(stream, buf, mode, BUFSIZ); -#else /* __STDIO_BUFFERS */ - /* TODO -- assert on stream? */ - /* Nothing to do. */ -#endif /* __STDIO_BUFFERS */ -} - -#endif -/**********************************************************************/ -#ifdef L_setvbuf - -/* Reentrant. */ - -int setvbuf(register FILE * __restrict stream, register char * __restrict buf, - int mode, size_t size) -{ -#ifdef __STDIO_BUFFERS - - int allocated_buf_flag; - int rv = EOF; - - __STDIO_THREADLOCK(stream); - - __stdio_validate_FILE(stream); /* debugging only */ - - if (((unsigned int) mode) > 2) { /* Illegal mode. */ - /* TODO -- set an errno? */ - goto DONE; - } - -#ifdef __STDIO_FLEXIBLE_SETVBUF - /* C89 standard requires no ops before setvbuf, but we can be flexible. */ - /* NOTE: This will trash any chars ungetc'd!!! */ - /* TODO: hmm could preserve unget count since ungot slots aren't changed (true?) - * but this will fail when buffered chars read from a pipe unless the user buf - * is big enough to copy everything over. */ - if (fseek(stream, 0L, SEEK_CUR)) { - goto DONE; - } -#else /* __STDIO_FLEXIBLE_SETVBUF */ - /* - * Note: ANSI/ISO requires setvbuf to be called after opening the file - * but before any other operation other than a failed setvbuf call. - * We'll cheat here and only test if the wide or narrow mode flag has - * been set; i.e. no read or write (or unget or fwide) operations have - * taken place. - */ -#ifdef __STDIO_WIDE - if (stream->modeflags & (__FLAG_WIDE|__FLAG_NARROW)) { - goto DONE; - } -#else /* __STDIO_WIDE */ - /* Note: This only checks if not currently reading or writing. */ - if (stream->modeflags & (__FLAG_READING|__FLAG_WRITING)) { - goto DONE; - } -#endif /* __STDIO_WIDE */ -#endif /* __STDIO_FLEXIBLE_SETVBUF */ - - if (mode == _IONBF) { - size = 0; - buf = NULL; - } else if (!buf && !size) { - /* If buf==NULL && size==0 && either _IOFBF or _IOLBF, keep - * current buffer and only set buffering mode. */ - size = stream->bufend - stream->bufstart; - } - - stream->modeflags &= ~(__MASK_BUFMODE); /* Clear current mode */ - stream->modeflags |= mode * __FLAG_LBF; /* and set new one. */ - - allocated_buf_flag = 0; - if ((!buf) && (size != (stream->bufend - stream->bufstart))) { - /* No buffer supplied and requested size different from current. */ - /* If size == 0, create a (hopefully) bogus non-null pointer... */ - if (!(buf = ((size > 0) - ? ((allocated_buf_flag = __FLAG_FREEBUF), malloc(size)) - : ((char *)NULL) + 1)) - ) { - /* TODO -- should we really keep current buffer if it was passed - * to us earlier by the app? */ - goto DONE; /* Keep current buffer. */ - } - } - - /* TODO: setvbuf "signal" safety */ - if (buf && (buf != (char *) stream->bufstart)) { /* Want new buffer. */ - if (stream->modeflags & __FLAG_FREEBUF) { - stream->modeflags &= ~(__FLAG_FREEBUF); - free(stream->bufstart); - } - stream->modeflags |= allocated_buf_flag; /* Free-able buffer? */ -#ifdef __STDIO_GETC_MACRO - stream->bufgetc = -#endif -#ifdef __STDIO_PUTC_MACRO - stream->bufputc = -#endif - stream->bufpos = stream->bufread = stream->bufstart = buf; - stream->bufend = buf + size; - } - - __stdio_validate_FILE(stream); /* debugging only */ - - rv = 0; - - DONE: - __STDIO_THREADUNLOCK(stream); - - return rv; - -#else /* __STDIO_BUFFERS */ - __stdio_validate_FILE(stream); /* debugging only */ - /* TODO -- set errno for illegal mode? */ - - return EOF; -#endif /* __STDIO_BUFFERS */ -} - -#endif -/********************************************************************** -int fprintf(FILE * __restrict stream, const char * __restrict format, ...); -int fscanf(FILE * __restrict stream, const char * __restrict format, ...); -int printf(const char * __restrict format, ...); -int scanf(const char * __restrict format, ...); -int snprintf(char * __restrict s, size_t n, - const char * __restrict format, ...); -int sprintf(char * __restrict s, const char * __restrict format, ...); -int sscanf(char * __restrict s, const char * __restrict format, ...); -int vfprintf(FILE * __restrict stream, const char * __restrict format, - va_list arg); -int vfscanf(FILE * __restrict stream, const char * __restrict format, - va_list arg); -int vprintf(const char * __restrict format, va_list arg); -int vscanf(const char * __restrict format, va_list arg); -int vsnprintf(char * __restrict s, size_t n, - const char * __restrict format, va_list arg); -int vsprintf(char * __restrict s, const char * __restrict format, - va_list arg); -int vsscanf(char * __restrict s, const char * __restrict format, - va_list arg); -**********************************************************************/ -#ifdef L_fgetc - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(int,fgetc,(FILE *stream),(stream)) -{ - unsigned char buf[1]; - -#ifdef __STDIO_WIDE - - return (fread_unlocked(buf, (size_t) 1, (size_t) 1, stream) > 0) - ? *buf : EOF; - -#else /* __STDIO_WIDE */ - - return (_stdio_fread(buf, (size_t) 1, stream) > 0) ? *buf : EOF; - -#endif /* __STDIO_WIDE */ -} - -#endif -/**********************************************************************/ -#ifdef L_fgets - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(char *,fgets, - (char *__restrict s, int n, register FILE * __restrict stream), - (s, n, stream)) -{ - register char *p; - int c; - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: What should fgets do if n <= 0? -#endif /* __UCLIBC_MJN3_ONLY__ */ - /* Should we assert here? Or set errno? Or just fail... */ - if (n <= 0) { -/* __set_errno(EINVAL); */ - goto ERROR; - } - - p = s; - - while (--n) { - if ((c = (getc_unlocked)(stream)) == EOF) { /* Disable the macro. */ - if (__FERROR(stream)) { - goto ERROR; - } - break; - } - if ((*p++ = c) == '\n') { - break; - } - } - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: If n==1 and not at EOF, should fgets return an empty string? -#endif /* __UCLIBC_MJN3_ONLY__ */ - if (p > s) { - *p = 0; - return s; - } - - ERROR: - return NULL; -} - -#endif -/**********************************************************************/ -#ifdef L_fputc - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(int,fputc,(int c, FILE *stream),(c,stream)) -{ - unsigned char buf[1]; - - *buf = (unsigned char) c; - -#ifdef __STDIO_WIDE - - return (fwrite_unlocked(buf, (size_t) 1, (size_t) 1, stream) > 0) - ? (*buf) : EOF; - -#else /* __STDIO_WIDE */ - - return (_stdio_fwrite(buf, (size_t) 1, stream) > 0) ? (*buf) : EOF; - -#endif /* __STDIO_WIDE */ -} -#endif -/**********************************************************************/ -#ifdef L_fputs - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(int,fputs, - (register const char * __restrict s, FILE * __restrict stream), - (s, stream)) -{ - size_t n = strlen(s); - -#ifdef __STDIO_WIDE - - return (fwrite_unlocked(s, (size_t) 1, n, stream) == n) ? n : EOF; - -#else /* __STDIO_WIDE */ - - return (_stdio_fwrite(s, n, stream) == n) ? n : EOF; - -#endif /* __STDIO_WIDE */ -} - -#endif -/**********************************************************************/ -#ifdef L_getc -#undef getc -#undef getc_unlocked - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(int,getc,(register FILE *stream),(stream)) -{ - return __GETC(stream); /* Invoke the macro. */ -} - -#endif -/**********************************************************************/ -#ifdef L_getchar -#undef getchar /* Just in case. */ - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED_STREAM(int,getchar,(void),(),stdin) -{ - register FILE *stream = stdin; /* This helps bcc optimize. */ - - return __GETC(stream); -} - -#endif -/**********************************************************************/ -#ifdef L_gets - -link_warning(gets, "the 'gets' function is dangerous and should not be used.") - -/* Reentrant. */ - -char *gets(char *s) /* WARNING!!! UNSAFE FUNCTION!!! */ -{ - register FILE *stream = stdin; /* This helps bcc optimize. */ - register char *p = s; - int c; - - __STDIO_THREADLOCK(stream); - - /* Note: don't worry about performance here... this shouldn't be used! - * Therefore, force actual function call. */ - while (((c = (getc_unlocked)(stream)) != EOF) && ((*p = c) != '\n')) { - ++p; - } - if ((c == EOF) || (s == p)) { - s = NULL; - } else { - *p = 0; - } - - __STDIO_THREADUNLOCK(stream); - - return s; -} - -#endif -/**********************************************************************/ -#ifdef L_putc -#undef putc -#undef putc_unlocked - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(int,putc,(int c, register FILE *stream),(c,stream)) -{ - return __PUTC(c, stream); /* Invoke the macro. */ -} - -#endif -/**********************************************************************/ -#ifdef L_putchar -#undef putchar /* Just in case. */ - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED_STREAM(int,putchar,(int c),(c),stdout) -{ - register FILE *stream = stdout; /* This helps bcc optimize. */ - - return __PUTC(c, stream); -} - -#endif -/**********************************************************************/ -#ifdef L_puts - -/* Reentrant. */ - -int puts(register const char *s) -{ - register FILE *stream = stdout; /* This helps bcc optimize. */ - int n; - - __STDIO_THREADLOCK(stream); - - n = fputs_unlocked(s,stream) + 1; - if ( -#if 1 - fputc_unlocked('\n',stream) -#else - fputs_unlocked("\n",stream) -#endif - == EOF) { - n = EOF; - } - - __STDIO_THREADUNLOCK(stream); - - return n; -} - -#endif -/**********************************************************************/ -#ifdef L_ungetc -/* - * Note: This is the application-callable ungetc. If scanf calls this, it - * should also set stream->ungot[1] to 0 if this is the only ungot. - */ - -/* Reentrant. */ - -int ungetc(int c, register FILE *stream) -{ - __STDIO_THREADLOCK(stream); - - __stdio_validate_FILE(stream); /* debugging only */ - -#ifdef __STDIO_WIDE - if (stream->modeflags & __FLAG_WIDE) { - stream->modeflags |= __FLAG_ERROR; - c = EOF; - goto DONE; - } - stream->modeflags |= __FLAG_NARROW; -#endif /* __STDIO_WIDE */ - - /* If can't read or c == EOF or ungot slots already filled, then fail. */ - if ((stream->modeflags - & (__MASK_UNGOT2|__FLAG_WRITEONLY -#ifndef __STDIO_AUTO_RW_TRANSITION - |__FLAG_WRITING /* Note: technically no, but yes in spirit */ -#endif /* __STDIO_AUTO_RW_TRANSITION */ - )) - || ((stream->modeflags & __MASK_UNGOT1) && (stream->ungot[1])) - || (c == EOF) ) { - c = EOF; - goto DONE;; - } - -#ifdef __STDIO_BUFFERS -#ifdef __STDIO_AUTO_RW_TRANSITION - if (stream->modeflags & __FLAG_WRITING) { - fflush_unlocked(stream); /* Commit any write-buffered chars. */ - } -#endif /* __STDIO_AUTO_RW_TRANSITION */ -#endif /* __STDIO_BUFFERS */ - - /* Clear EOF and WRITING flags, and set READING FLAG */ - stream->modeflags &= ~(__FLAG_EOF|__FLAG_WRITING); -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: Is setting the reading flag after an ungetc necessary? -#endif /* __UCLIBC_MJN3_ONLY__ */ - stream->modeflags |= __FLAG_READING; - stream->ungot[1] = 1; /* Flag as app ungetc call; scanf fixes up. */ - stream->ungot[(stream->modeflags++) & __MASK_UNGOT] = c; - -#ifdef __STDIO_GETC_MACRO - stream->bufgetc = stream->bufstart; /* Must disable getc macro. */ -#endif - - __stdio_validate_FILE(stream); /* debugging only */ - - DONE: - __STDIO_THREADUNLOCK(stream); - - return c; -} - -#endif -/**********************************************************************/ -#ifdef L_fread -/* NOTE: Surely ptr cannot point to a buffer of size > SIZE_MAX. - * Therefore, we treat the case size * nmemb > SIZE_MAX as an error, - * and include an assert() for it. */ - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(size_t,fread, - (void * __restrict ptr, size_t size, size_t nmemb, - FILE * __restrict stream), - (ptr,size,nmemb,stream)) -{ -#ifdef __STDIO_WIDE - if (stream->modeflags & __FLAG_WIDE) { - stream->modeflags |= __FLAG_ERROR; - /* TODO -- errno? it this correct? */ - return 0; - } - stream->modeflags |= __FLAG_NARROW; -#endif /* __STDIO_WIDE */ - - return (size == 0) - ? 0 - : ( assert( ((size_t)(-1)) / size >= nmemb ), - _stdio_fread(ptr, nmemb * size, stream) / size ); -} - -#endif -/**********************************************************************/ -#ifdef L_fwrite -/* NOTE: Surely ptr cannot point to a buffer of size > SIZE_MAX. - * Therefore, we treat the case size * nmemb > SIZE_MAX as an error, - * and include an assert() for it. */ - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(size_t,fwrite, - (const void * __restrict ptr, size_t size, size_t nmemb, - FILE * __restrict stream), - (ptr,size,nmemb,stream)) -{ -#ifdef __STDIO_WIDE - if (stream->modeflags & __FLAG_WIDE) { - stream->modeflags |= __FLAG_ERROR; - /* TODO -- errno? it this correct? */ - return 0; - } - stream->modeflags |= __FLAG_NARROW; -#endif /* __STDIO_WIDE */ - - return (size == 0) - ? 0 - : ( assert( ((size_t)(-1)) / size >= nmemb ), - _stdio_fwrite(ptr, nmemb * size, stream) / size ); -} - -#endif -/**********************************************************************/ -#if defined(L_fgetpos) || defined(L_fgetpos64) - -/* Reentrant -- fgetpos() and fgetpos64(). */ - -#if defined(L_fgetpos) && defined(L_fgetpos64) -#error L_fgetpos and L_fgetpos64 are defined simultaneously! -#endif - -#ifndef L_fgetpos64 -#define fgetpos64 fgetpos -#define fpos64_t fpos_t -#define ftello64 ftell -#endif - - -int fgetpos64(FILE * __restrict stream, register fpos64_t * __restrict pos) -{ - int retval = -1; - -#ifdef __STDIO_WIDE - - if (pos == NULL) { - __set_errno(EINVAL); - } else { - __STDIO_THREADLOCK(stream); - - if ((pos->__pos = ftello64(stream)) >= 0) { - __COPY_MBSTATE(&(pos->__mbstate), &(stream->state)); - pos->mblen_pending = stream->ungot_width[0]; - retval = 0; - } - - __STDIO_THREADUNLOCK(stream); - } - -#else /* __STDIO_WIDE */ - - if (pos == NULL) { - __set_errno(EINVAL); - } else if ((pos->__pos = ftello64(stream)) >= 0) { - retval = 0; - } - -#endif /* __STDIO_WIDE */ - - return retval; -} - -#ifndef L_fgetpos64 -#undef fgetpos64 -#undef fpos64_t -#undef ftello64 -#endif - -#endif -/**********************************************************************/ -#ifdef L_fseek -strong_alias(fseek,fseeko); -#endif - -#if defined(L_fseek) && defined(__STDIO_LARGE_FILES) - -int fseek(register FILE *stream, long int offset, int whence) -{ - return fseeko64(stream, offset, whence); -} - -#endif - -#if defined(L_fseeko64) || (defined(L_fseek) && !defined(__STDIO_LARGE_FILES)) - -#ifndef L_fseeko64 -#define fseeko64 fseek -#define __off64_t long int -#endif - -/* Reentrant -- fseek(), fseeko(), fseeko64() */ - -int fseeko64(register FILE *stream, __off64_t offset, int whence) -{ -#if SEEK_SET != 0 || SEEK_CUR != 1 || SEEK_END != 2 -#error Assumption violated -- values of SEEK_SET, SEEK_CUR, SEEK_END -#endif - __offmax_t pos[1]; - int retval; - - if ( ((unsigned int) whence) > 2 ) { - __set_errno(EINVAL); - return -1; - } - - __STDIO_THREADLOCK(stream); - - __stdio_validate_FILE(stream); /* debugging only */ - - retval = -1; - *pos = offset; - if ( -#ifdef __STDIO_BUFFERS - /* First commit any pending buffered writes. */ - ((stream->modeflags & __FLAG_WRITING) && fflush_unlocked(stream)) || -#endif /* __STDIO_BUFFERS */ - ((whence == SEEK_CUR) && (_stdio_adjpos(stream, pos) < 0)) - || (_stdio_lseek(stream, pos, whence) < 0) - ) { - __stdio_validate_FILE(stream); /* debugging only */ - goto DONE; - } - -#ifdef __STDIO_BUFFERS - /* only needed if reading but do it anyway to avoid test */ -#ifdef __STDIO_GETC_MACRO - stream->bufgetc = /* Must disable getc. */ -#endif - stream->bufpos = stream->bufread = stream->bufstart; -#endif /* __STDIO_BUFFERS */ - - stream->modeflags &= - ~(__FLAG_READING|__FLAG_WRITING|__FLAG_EOF|__MASK_UNGOT); - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: How do we deal with fseek to an ftell position for incomplete or error wide? Right now, we clear all multibyte state info. If we do not clear, then fix rewind() to do so if fseek() succeeds. -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#ifdef __STDIO_WIDE - /* TODO: don't clear state if don't move? */ - stream->ungot_width[0] = 0; -#endif /* __STDIO_WIDE */ -#ifdef __STDIO_MBSTATE - /* TODO: don't clear state if don't move? */ - __INIT_MBSTATE(&(stream->state)); -#endif /* __STDIO_MBSTATE */ - - __stdio_validate_FILE(stream); /* debugging only */ - - retval = 0; - - DONE: - __STDIO_THREADUNLOCK(stream); - - return retval; -} - -#ifndef L_fseeko64 -#undef fseeko64 -#undef __off64_t -#endif - -#endif -/**********************************************************************/ -#if defined(L_fsetpos) || defined(L_fsetpos64) - -#if defined(L_fsetpos) && defined(L_fsetpos64) -#error L_fsetpos and L_fsetpos64 are defined simultaneously! -#endif - -#ifndef L_fsetpos64 -#define fsetpos64 fsetpos -#define fpos64_t fpos_t -#define fseeko64 fseek -#endif - -/* Reentrant -- fgetpos{64}() through fseek{64}(). */ - -int fsetpos64(FILE *stream, register const fpos64_t *pos) -{ - if (!pos) { - __set_errno(EINVAL); - return EOF; - } -#ifdef __STDIO_WIDE - { - int retval; - - __STDIO_THREADLOCK(stream); - - if ((retval = fseeko64(stream, pos->__pos, SEEK_SET)) == 0) { - __COPY_MBSTATE(&(stream->state), &(pos->__mbstate)); -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: Moving mblen_pending into some mbstate field might be useful. But we would need to modify all the mb<->wc funcs. -#endif /* __UCLIBC_MJN3_ONLY__ */ - stream->ungot_width[0] = pos->mblen_pending; - } - - __STDIO_THREADUNLOCK(stream); - - return retval; - } -#else /* __STDIO_WIDE */ - return fseeko64(stream, pos->__pos, SEEK_SET); -#endif /* __STDIO_WIDE */ -} - -#ifndef L_fsetpos64 -#undef fsetpos64 -#undef fpos64_t -#undef fseeko64 -#endif - -#endif -/**********************************************************************/ -#ifdef L_ftell -strong_alias(ftell,ftello); -#endif - -#if defined(L_ftell) && defined(__STDIO_LARGE_FILES) -long int ftell(register FILE *stream) -{ - __offmax_t pos = ftello64(stream); - - return (pos == ((long int) pos)) ? pos : (__set_errno(EOVERFLOW), -1); -} -#endif - -#if defined(L_ftello64) || (defined(L_ftell) && !defined(__STDIO_LARGE_FILES)) - -#ifndef L_ftello64 -#define ftello64 ftell -#define __off64_t long int -#endif - -/* Reentrant -- ftell, ftello, ftello64. */ - -__off64_t ftello64(register FILE *stream) -{ - __offmax_t pos[1]; - __off64_t retval; - - __STDIO_THREADLOCK(stream); - - retval = (((*pos = 0), (_stdio_lseek(stream, pos, SEEK_CUR) < 0)) - || (_stdio_adjpos(stream, pos) < 0)) ? -1 : *pos; - - __STDIO_THREADUNLOCK(stream); - - return retval; -} - -#ifndef L_ftello64 -#undef ftello64 -#undef __off64_t -#endif - -#endif -/**********************************************************************/ -#ifdef L_rewind - -void rewind(register FILE *stream) -{ - __STDIO_THREADLOCK(stream); - - __CLEARERR(stream); /* Clear errors first and then seek */ - fseek(stream, 0L, SEEK_SET); /* in case there is an error seeking. */ - - __STDIO_THREADUNLOCK(stream); -} - -#endif -/**********************************************************************/ -#ifdef L_clearerr -#undef clearerr - -/* Reentrancy handled by UNLOCKED_VOID_RETURN() macro. */ - -UNLOCKED_VOID_RETURN(clearerr,(FILE *stream),(stream)) -{ - __CLEARERR(stream); -} - -#endif -/**********************************************************************/ -#ifdef L_feof -#undef feof - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(int,feof,(FILE *stream),(stream)) -{ - return __FEOF(stream); -} - -#endif -/**********************************************************************/ -#ifdef L_ferror -#undef ferror - -/* Reentrancy handled by UNLOCKED() macro. */ - -UNLOCKED(int,ferror,(FILE *stream),(stream)) -{ - return __FERROR(stream); -} - -#endif -/**********************************************************************/ -#ifdef L_perror - -void perror(register const char *s) -{ - /* If the program is calling perror, it's a safe bet that printf and - * friends are used as well. It is also possible that the calling - * program could buffer stderr, or reassign it. */ - - register const char *sep; - - sep = ": "; - if (!(s && *s)) { /* Caller did not supply a prefix message */ - s = (sep += 2); /* or passed an empty string. */ - } - -#if 1 -#ifdef __STDIO_PRINTF_M_SPEC - fprintf(stderr, "%s%s%m\n", s, sep); /* Use the gnu %m feature. */ -#else - { - char buf[64]; - fprintf(stderr, "%s%s%s\n", s, sep, - _glibc_strerror_r(errno, buf, sizeof(buf))); - } -#endif -#else - /* Note: Assumes stderr not closed or buffered. */ - { - char buf[64]; - - __STDIO_THREADLOCK(stderr); - _stdio_fdout(STDERR_FILENO, s, sep, - _glibc_strerror_r(errno, buf, sizeof(buf))); - __STDIO_THREADUNLOCK(stderr); - } -#endif -} - -#endif -/**********************************************************************/ -/* UTILITY funcs */ -/**********************************************************************/ -#ifdef L__stdio_fdout - -/* Not reentrant -- TODO: lock associated stream if a know file descriptor? */ - -void _stdio_fdout(int fd, ...) -{ - va_list arg; - register const char *p; - - va_start(arg, fd); - while ((p = va_arg(arg, const char *)) != NULL) { - write(fd, p, strlen(p)); - } - va_end(arg); -} - -#endif -/**********************************************************************/ -#ifdef L__uintmaxtostr - -/* Avoid using long long / and % operations to cut down dependencies on - * libgcc.a. Definitely helps on i386 at least. */ -#if (INTMAX_MAX > INT_MAX) && (((INTMAX_MAX/INT_MAX)/2) - 2 <= INT_MAX) -#define INTERNAL_DIV_MOD -#endif - -#include <locale.h> - -char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval, - int base, __UIM_CASE alphacase) -{ - int negative; - unsigned int digit; -#ifdef INTERNAL_DIV_MOD - unsigned int H, L, high, low, rh; -#endif -#ifndef __LOCALE_C_ONLY - int grouping, outdigit; - const char *g; /* This does not need to be initialized. */ -#endif /* __LOCALE_C_ONLY */ - - negative = 0; - if (base < 0) { /* signed value */ - base = -base; - if (uval > INTMAX_MAX) { - uval = -uval; - negative = 1; - } - } - - /* this is an internal routine -- we shouldn't need to check this */ - assert(!((base < 2) || (base > 36))); - -#ifndef __LOCALE_C_ONLY - grouping = -1; - outdigit = 0x80 & alphacase; - alphacase ^= outdigit; - if (alphacase == __UIM_GROUP) { - assert(base == 10); - if (*(g = __UCLIBC_CURLOCALE_DATA.grouping)) { - grouping = *g; - } - } -#endif /* __LOCALE_C_ONLY */ - - *bufend = '\0'; - -#ifndef INTERNAL_DIV_MOD - do { -#ifndef __LOCALE_C_ONLY - if (!grouping) { /* Finished a group. */ - bufend -= __UCLIBC_CURLOCALE_DATA.thousands_sep_len; - memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep, - __UCLIBC_CURLOCALE_DATA.thousands_sep_len); - if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */ - /* Note: g[1] == -1 means no further grouping. But since - * we'll never wrap around, we can set grouping to -1 without - * fear of */ - ++g; - } - grouping = *g; - } - --grouping; -#endif /* __LOCALE_C_ONLY */ - digit = uval % base; - uval /= base; - -#ifndef __LOCALE_C_ONLY - if (unlikely(outdigit)) { - bufend -= __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]; - memcpy(bufend, - (&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit], - __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]); - } else -#endif - { - *--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase ); - } - } while (uval); - -#else /* ************************************************** */ - - H = (UINT_MAX / base); - L = UINT_MAX % base + 1; - if (L == base) { - ++H; - L = 0; - } - low = (unsigned int) uval; - high = (unsigned int) (uval >> (sizeof(unsigned int) * CHAR_BIT)); - - do { -#ifndef __LOCALE_C_ONLY - if (!grouping) { /* Finished a group. */ - bufend -= __UCLIBC_CURLOCALE_DATA.thousands_sep_len; - memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep, - __UCLIBC_CURLOCALE_DATA.thousands_sep_len); - if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */ - /* Note: g[1] == -1 means no further grouping. But since - * we'll never wrap around, we can set grouping to -1 without - * fear of */ - ++g; - } - grouping = *g; - } - --grouping; -#endif /* __LOCALE_C_ONLY */ - - if (unlikely(high)) { - rh = high % base; - high /= base; - digit = (low % base) + (L * rh); - low = (low / base) + (H * rh) + (digit / base); - digit %= base; - } else { - digit = low % base; - low /= base; - } - -#ifndef __LOCALE_C_ONLY - if (unlikely(outdigit)) { - bufend -= __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]; - memcpy(bufend, - (&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit], - __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]); - } else -#endif - { - *--bufend = ( (digit < 10) ? digit + '0' : digit + alphacase ); - } - } while (low | high); - -#endif /******************************************************/ - - if (negative) { - *--bufend = '-'; - } - - return bufend; -} -#undef INTERNAL_DIV_MOD - -#endif -/**********************************************************************/ -#ifdef L__wstdio_fwrite - -#include <wchar.h> - -size_t _wstdio_fwrite(const wchar_t *__restrict ws, size_t n, - register FILE *__restrict stream) -{ - size_t r, count; - char buf[64]; - const wchar_t *pw; - -#if defined(__STDIO_WIDE) && defined(__STDIO_BUFFERS) - if (stream->filedes == -3) { /* Special case to support {v}swprintf. */ - count = ((wchar_t *)(stream->bufend)) - ((wchar_t *)(stream->bufpos)); - if (count > n) { - count = n; - } - if (count) { - wmemcpy((wchar_t *)(stream->bufpos), ws, count); - stream->bufpos = (char *)(((wchar_t *)(stream->bufpos)) + count); - } - return n; - } -#endif /* defined(__STDIO_WIDE) && defined(__STDIO_BUFFERS) */ - - if (stream->modeflags & __FLAG_NARROW) { - stream->modeflags |= __FLAG_ERROR; - __set_errno(EBADF); - return 0; - } - stream->modeflags |= __FLAG_WIDE; - - pw = ws; - count = 0; - while (n > count) { - r = wcsnrtombs(buf, &pw, n-count, sizeof(buf), &stream->state); - if (r != ((size_t) -1)) { /* No encoding errors */ - if (!r) { - ++r; /* 0 is returned when nul is reached. */ - pw = ws + count + r; /* pw was set to NULL, so correct. */ - } - if (_stdio_fwrite(buf, r, stream) == r) { - count = pw - ws; - continue; - } - } - break; - } - - /* Note: The count is incorrect if 0 < _stdio_fwrite return < r!!! */ - return count; -} - -#endif -/**********************************************************************/ diff --git a/libc/stdio/swprintf.c b/libc/stdio/swprintf.c new file mode 100644 index 000000000..bdb092403 --- /dev/null +++ b/libc/stdio/swprintf.c @@ -0,0 +1,29 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> +#include <wchar.h> + +#ifndef __STDIO_BUFFERS +#warning Skipping swprintf since no buffering! +#else /* __STDIO_BUFFERS */ + +int swprintf(wchar_t *__restrict buf, size_t size, + const wchar_t * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vswprintf(buf, size, format, arg); + va_end(arg); + + return rv; +} + +#endif /* __STDIO_BUFFERS */ diff --git a/libc/stdio/ungetc.c b/libc/stdio/ungetc.c new file mode 100644 index 000000000..1b9197a18 --- /dev/null +++ b/libc/stdio/ungetc.c @@ -0,0 +1,77 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* Having ungotten characters implies the stream is reading. + * The scheme used here treats the least significant 2 bits of + * the stream's modeflags member as follows: + * 0 0 Not currently reading. + * 0 1 Reading, but no ungetc() or scanf() push back chars. + * 1 0 Reading with one ungetc() char (ungot[1] is 1) + * or one scanf() pushed back char (ungot[1] is 0). + * 1 1 Reading with both an ungetc() char and a scanf() + * pushed back char. Note that this must be the result + * of a scanf() push back (in ungot[0]) _followed_ by + * an ungetc() call (in ungot[1]). + * + * Notes: + * scanf() can NOT use ungetc() to push back characters. + * (See section 7.19.6.2 of the C9X rationale -- WG14/N897.) + */ + +int ungetc(int c, register FILE *stream) +{ + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + __STDIO_STREAM_VALIDATE(stream); + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Make fast ungetc an option? +#endif +#ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ + /* If buffered narrow reading with no ungot slots filled, and if not + * ungetting a different char than the one last read from the buffer, + * we can simply decrement the position and not worry about disabling + * the getc macros. This will cut down on overhead in applications + * that use getc/ungetc extensively (like gcc). */ + /* NOTE: If we can use getc, then we are buffered narrow reading with + * no ungot slots filled. */ + if (__STDIO_STREAM_CAN_USE_BUFFER_GET(stream) + && (c != EOF) + && (stream->__bufpos > stream->__bufstart) + && (stream->__bufpos[-1] == ((unsigned char)c)) + ) { + --stream->__bufpos; + __STDIO_STREAM_CLEAR_EOF(stream); /* Must clear end-of-file flag. */ + } else +#endif + /* Note: Even if c == EOF, we need to initialize/verify the + * stream's orientation and ensure the stream is in reading + * mode (if readable and properly oriented). */ + if ((!__STDIO_STREAM_IS_NARROW_READING(stream) + && __STDIO_STREAM_TRANS_TO_READ(stream, __FLAG_NARROW)) + || ((stream->__modeflags & __FLAG_UNGOT) + && ((stream->__modeflags & 1) || stream->__ungot[1])) + ) { + c = EOF; + } else if (c != EOF) { + __STDIO_STREAM_DISABLE_GETC(stream); + + /* Flag this as a user ungot, as scanf does the necessary fixup. */ + stream->__ungot[1] = 1; + stream->__ungot[(++stream->__modeflags) & 1] = c; + + __STDIO_STREAM_CLEAR_EOF(stream); /* Must clear end-of-file flag. */ + } + + __STDIO_STREAM_VALIDATE(stream); + __STDIO_AUTO_THREADUNLOCK(stream); + + return c; +} diff --git a/libc/stdio/ungetwc.c b/libc/stdio/ungetwc.c new file mode 100644 index 000000000..cf4b9c488 --- /dev/null +++ b/libc/stdio/ungetwc.c @@ -0,0 +1,48 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" + +/* Note: This is the application-callable ungetwc. If wscanf calls this, it + * should also set stream->__ungot[1] to 0 if this is the only ungot, as well + * as reset stream->__ungot_width[1] for use by _stdio_adjpos(). + */ + +wint_t ungetwc(wint_t c, register FILE *stream) +{ + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + __STDIO_STREAM_VALIDATE(stream); /* debugging only */ + + /* Note: Even if c == WEOF, we need to initialize/verify the + * stream's orientation and ensure the stream is in reading + * mode (if readable and properly oriented). */ + if ((!__STDIO_STREAM_IS_WIDE_READING(stream) + && __STDIO_STREAM_TRANS_TO_READ(stream, __FLAG_WIDE)) + || ((stream->__modeflags & __FLAG_UNGOT) + && ((stream->__modeflags & 1) || stream->__ungot[1])) + || (c == WEOF) + ) { + c = WEOF; + } else { + /* In the wide case, getc macros should already be disabled. */ + /* __STDIO_STREAM_DISABLE_GETC(stream); */ + + /* Flag this as a user ungot, as scanf does the necessary fixup. */ + stream->__ungot[1] = 1; + stream->__ungot[(++stream->__modeflags) & 1] = c; + /* Note: ungot_width is handled by fgetwc. */ + + __STDIO_STREAM_CLEAR_EOF(stream); /* Must clear end-of-file flag. */ + } + + __STDIO_STREAM_VALIDATE(stream); + __STDIO_AUTO_THREADUNLOCK(stream); + + return c; +} diff --git a/libc/stdio/vasprintf.c b/libc/stdio/vasprintf.c new file mode 100644 index 000000000..21ef8e145 --- /dev/null +++ b/libc/stdio/vasprintf.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +#ifdef __UCLIBC_MJN3_ONLY__ +/* Do the memstream stuff inline to avoid fclose and the openlist? */ +#warning CONSIDER: avoid open_memstream call? +#endif + +#ifndef __STDIO_HAS_VSNPRINTF +#warning Skipping vasprintf since no vsnprintf! +#else + +int vasprintf(char **__restrict buf, const char * __restrict format, + va_list arg) +{ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + + FILE *f; + size_t size; + int rv = -1; + + *buf = NULL; + + if ((f = open_memstream(buf, &size)) != NULL) { + rv = vfprintf(f, format, arg); + fclose(f); + if (rv < 0) { + free(*buf); + *buf = NULL; + } + } + + assert(rv >= -1); + + return rv; + +#else /* __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ */ + + /* This implementation actually calls the printf machinery twice, but only + * only does one malloc. This can be a problem though when custom printf + * specs or the %m specifier are involved because the results of the + * second call might be different from the first. */ + va_list arg2; + int rv; + + va_copy(arg2, arg); + rv = vsnprintf(NULL, 0, format, arg2); + va_end(arg2); + + *buf = NULL; + + if (rv >= 0) { + if ((*buf = malloc(++rv)) != NULL) { + if ((rv = vsnprintf(*buf, rv, format, arg)) < 0) { + free(*buf); + *buf = NULL; + } + } + } + + assert(rv >= -1); + + return rv; + +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ */ +} + +#endif diff --git a/libc/stdio/vdprintf.c b/libc/stdio/vdprintf.c new file mode 100644 index 000000000..de8362c20 --- /dev/null +++ b/libc/stdio/vdprintf.c @@ -0,0 +1,62 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +int vdprintf(int filedes, const char * __restrict format, va_list arg) +{ + FILE f; + int rv; +#ifdef __STDIO_BUFFERS + char buf[64]; /* TODO: provide _optional_ buffering? */ + + f.__bufend = buf + sizeof(buf); + f.__bufstart = buf; + __STDIO_STREAM_DISABLE_GETC(&f); + __STDIO_STREAM_DISABLE_PUTC(&f); + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(&f); +#endif + +/* __STDIO_STREAM_RESET_GCS(&f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.__cookie = &(f.__filedes); + f.__gcs.read = NULL; + f.__gcs.write = _cs_write; + f.__gcs.seek = NULL; + f.__gcs.close = NULL; +#endif + + f.__filedes = filedes; + f.__modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING); + +#ifdef __UCLIBC_HAS_WCHAR__ + f.__ungot_width[0] = 0; +#endif /* __UCLIBC_HAS_WCHAR__ */ +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(f.__state)); +#endif /* __STDIO_MBSTATE */ + +#ifdef __UCLIBC_HAS_THREADS__ + f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.__lock); +#endif + f.__nextopen = NULL; + + rv = vfprintf(&f, format, arg); + +#ifdef __STDIO_BUFFERS + /* If not buffering, then fflush is unnecessary. */ + if ((rv > 0) && __fflush_unlocked(&f)) { + rv = -1; + } +#endif + + assert(rv >= -1); + + return rv; +} diff --git a/libc/stdio/vfprintf.c b/libc/stdio/vfprintf.c new file mode 100644 index 000000000..49b4d11a3 --- /dev/null +++ b/libc/stdio/vfprintf.c @@ -0,0 +1,1901 @@ +/* Copyright (C) 2002-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. + */ + +/* This code needs a lot of clean up. Some of that is on hold until uClibc + * gets a better configuration system (on Erik's todo list). + * The other cleanup will take place during the implementation/integration of + * the wide char (un)formatted i/o functions which I'm currently working on. + */ + +/* 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! */ + + +/* April 1, 2002 + * Initialize thread locks for fake files in vsnprintf and vdprintf. + * reported by Erik Andersen (andersen@codepoet.com) + * Fix an arg promotion handling bug in _do_one_spec for %c. + * reported by Ilguiz Latypov <ilatypov@superbt.com> + * + * May 10, 2002 + * Remove __isdigit and use new ctype.h version. + * Add conditional setting of QUAL_CHARS for size_t and ptrdiff_t. + * + * Aug 16, 2002 + * Fix two problems that showed up with the python 2.2.1 tests; one + * involving %o and one involving %f. + * + * Oct 28, 2002 + * Fix a problem in vasprintf (reported by vodz a while back) when built + * without custom stream support. In that case, it is necessary to do + * a va_copy. + * Make sure each va_copy has a matching va_end, as required by C99. + * + * Nov 4, 2002 + * Add locale-specific grouping support for integer decimal conversion. + * Add locale-specific decimal point support for floating point conversion. + * Note: grouping will have to wait for _dtostr() rewrite. + * Add printf wchar support for %lc (%C) and %ls (%S). + * Require printf format strings to be valid multibyte strings beginning and + * ending in their initial shift state, as per the stds. + * + * Nov 21, 2002 + * Add *wprintf functions. Currently they don't support floating point + * conversions. That will wait until the rewrite of _dtostr. + * + * Aug 1, 2003 + * Optional hexadecimal float notation support for %a/%A. + * Floating point output now works for *wprintf. + * Support for glibc locale-specific digit grouping for floats. + * Misc bug fixes. + * + * Aug 31, 2003 + * Fix precision bug for %g conversion specifier when using %f style. + * + * Sep 5, 2003 + * Implement *s*scanf for the non-buffered stdio case with old_vfprintf. + * + * Sep 23, 2003 + * vfprintf was not always checking for narrow stream orientation. + */ + +/* TODO: + * + * Should we validate that *printf format strings are valid multibyte + * strings in the current locale? ANSI/ISO C99 seems to imply this + * and Plauger's printf implementation in his Standard C Library book + * treats this as an error. + */ + + +#define _ISOC99_SOURCE /* for ULLONG primarily... */ +#define _GNU_SOURCE +#define _STDIO_UTILITY /* We're using _uintmaxtostr. */ +#include <features.h> +#include "_stdio.h" +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <ctype.h> +#include <limits.h> +#include <stdarg.h> +#include <assert.h> +#include <stdint.h> +#include <errno.h> +#include <locale.h> + +#define __PRINTF_INFO_NO_BITFIELD +#include <printf.h> + +#ifdef __UCLIBC_HAS_THREADS__ +#include <stdio_ext.h> +#include <pthread.h> +#endif /* __UCLIBC_HAS_THREADS__ */ + +#ifdef __UCLIBC_HAS_WCHAR__ +#include <wchar.h> +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#include <bits/uClibc_uintmaxtostr.h> + +/* Some older or broken gcc toolchains define LONG_LONG_MAX but not + * LLONG_MAX. Since LLONG_MAX is part of the standard, that's what + * we use. So complain if we do not have it but should. + */ +#if !defined(LLONG_MAX) && defined(LONG_LONG_MAX) +#error Apparently, LONG_LONG_MAX is defined but LLONG_MAX is not. You need to fix your toolchain headers to support the standard macros for (unsigned) long long. +#endif + +/**********************************************************************/ +/* These provide some control over printf's feature set */ + +/* This is undefined below depeding on uClibc's configuration. */ +#define __STDIO_PRINTF_FLOAT 1 + +/* Now controlled by uClibc_stdio.h. */ +/* #define __UCLIBC_HAS_PRINTF_M_SPEC__ */ + + +/**********************************************************************/ + +#if defined(__UCLIBC__) && !defined(__UCLIBC_HAS_FLOATS__) +#undef __STDIO_PRINTF_FLOAT +#endif + +#ifdef __BCC__ +#undef __STDIO_PRINTF_FLOAT +#endif + +#ifdef __STDIO_PRINTF_FLOAT +#include <float.h> +#include <bits/uClibc_fpmax.h> +#else /* __STDIO_PRINTF_FLOAT */ +#undef L__fpmaxtostr +#endif /* __STDIO_PRINTF_FLOAT */ + + +#undef __STDIO_HAS_VSNPRINTF +#if defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) +#define __STDIO_HAS_VSNPRINTF 1 +#endif + +/**********************************************************************/ + +/* Now controlled by uClibc_stdio.h. */ +/* #define __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +/* TODO -- move these to a configuration section? */ +#define MAX_FIELD_WIDTH 4095 + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_register_printf_function +/* emit only once */ +#warning WISHLIST: Make MAX_USER_SPEC configurable? +#warning WISHLIST: Make MAX_ARGS_PER_SPEC configurable? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + +#define MAX_USER_SPEC 10 +#define MAX_ARGS_PER_SPEC 5 + +#else /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +#undef MAX_USER_SPEC +#define MAX_ARGS_PER_SPEC 1 + +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +#if MAX_ARGS_PER_SPEC < 1 +#error MAX_ARGS_PER_SPEC < 1! +#undef MAX_ARGS_PER_SPEC +#define MAX_ARGS_PER_SPEC 1 +#endif + +#if defined(NL_ARGMAX) && (NL_ARGMAX < 9) +#error NL_ARGMAX < 9! +#endif + +#if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2)) +#define MAX_ARGS NL_ARGMAX +#else +/* N for spec itself, plus 1 each for width and precision */ +#define MAX_ARGS (MAX_ARGS_PER_SPEC + 2) +#endif + + +/**********************************************************************/ +/* Deal with pre-C99 compilers. */ + +#ifndef va_copy + +#ifdef __va_copy +#define va_copy(A,B) __va_copy(A,B) +#else + /* TODO -- maybe create a bits/vacopy.h for arch specific versions + * to ensure we get the right behavior? Either that or fall back + * on the portable (but costly in size) method of using a va_list *. + * That means a pointer derefs in the va_arg() invocations... */ +#warning Neither va_copy (C99/SUSv3) or __va_copy is defined. Using a simple copy instead. But you should really check that this is appropriate... + /* the glibc manual suggests that this will usually suffice when + __va_copy doesn't exist. */ +#define va_copy(A,B) A = B +#endif + +#endif /* va_copy */ + +/**********************************************************************/ + +#define __PA_FLAG_INTMASK \ + (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG) + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ +extern printf_function _custom_printf_handler[MAX_USER_SPEC]; +extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; +extern char *_custom_printf_spec; +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +/**********************************************************************/ + +#define SPEC_FLAGS " +0-#'I" +enum { + FLAG_SPACE = 0x01, + FLAG_PLUS = 0x02, /* must be 2 * FLAG_SPACE */ + FLAG_ZERO = 0x04, + FLAG_MINUS = 0x08, /* must be 2 * FLAG_ZERO */ + FLAG_HASH = 0x10, + FLAG_THOUSANDS = 0x20, + FLAG_I18N = 0x40, /* only works for d, i, u */ + FLAG_WIDESTREAM = 0x80 +}; + +/**********************************************************************/ + +/* float layout 01234567890123456789 TODO: B?*/ +#define SPEC_CHARS "npxXoudifFeEgGaACScs" +enum { + CONV_n = 0, + CONV_p, + CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i, + CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A, + CONV_C, CONV_S, CONV_c, CONV_s, +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + CONV_m, +#endif + CONV_custom0 /* must be last */ +}; + +/* p x X o u d i */ +#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 10 } + +#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \ + CONV_C, CONV_S, CONV_c, CONV_s, CONV_custom0 } + +#define SPEC_OR_MASK { \ + /* n */ (PA_FLAG_PTR|PA_INT), \ + /* p */ PA_POINTER, \ + /* oxXudi */ PA_INT, \ + /* fFeEgGaA */ PA_DOUBLE, \ + /* C */ PA_WCHAR, \ + /* S */ PA_WSTRING, \ + /* c */ PA_CHAR, \ + /* s */ PA_STRING, \ +} + +#define SPEC_AND_MASK { \ + /* n */ (PA_FLAG_PTR|__PA_INTMASK), \ + /* p */ PA_POINTER, \ + /* oxXudi */ (__PA_INTMASK), \ + /* fFeEgGaA */ (PA_FLAG_LONG_DOUBLE|PA_DOUBLE), \ + /* C */ (PA_WCHAR), \ + /* S */ (PA_WSTRING), \ + /* c */ (PA_CHAR), \ + /* s */ (PA_STRING), \ +} + +/**********************************************************************/ +/* + * In order to ease translation to what arginfo and _print_info._flags expect, + * we map: 0:int 1:char 2:longlong 4:long 8:short + * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701) + */ + +/* TODO -- Fix the table below to take into account stdint.h. */ +/* #ifndef LLONG_MAX */ +/* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */ +/* #else */ +/* #if LLONG_MAX != INTMAX_MAX */ +/* #error fix QUAL_CHARS intmax_t entry 'j'! */ +/* #endif */ +/* #endif */ + +#ifdef PDS +#error PDS already defined! +#endif +#ifdef SS +#error SS already defined! +#endif +#ifdef IMS +#error IMS already defined! +#endif + +#if PTRDIFF_MAX == INT_MAX +#define PDS 0 +#elif PTRDIFF_MAX == LONG_MAX +#define PDS 4 +#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX) +#define PDS 8 +#else +#error fix QUAL_CHARS ptrdiff_t entry 't'! +#endif + +#if SIZE_MAX == UINT_MAX +#define SS 0 +#elif SIZE_MAX == ULONG_MAX +#define SS 4 +#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX) +#define SS 8 +#else +#error fix QUAL_CHARS size_t entries 'z', 'Z'! +#endif + +#if INTMAX_MAX == INT_MAX +#define IMS 0 +#elif INTMAX_MAX == LONG_MAX +#define IMS 4 +#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX) +#define IMS 8 +#else +#error fix QUAL_CHARS intmax_t entry 'j'! +#endif + +#define QUAL_CHARS { \ + /* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int */ \ + /* q:long_long Z:(s)size_t */ \ + 'h', 'l', 'L', 'j', 'z', 't', 'q', 'Z', 0, \ + 2, 4, 8, IMS, SS, PDS, 8, SS, 0, /* TODO -- fix!!! */\ + 1, 8 \ +} + +/**********************************************************************/ + +#ifdef __STDIO_VA_ARG_PTR +#ifdef __BCC__ +#define __va_arg_ptr(ap,type) (((type *)(ap += sizeof(type))) - 1) +#endif + +#if 1 +#ifdef __GNUC__ +/* TODO -- need other than for 386 as well! */ + +#ifndef __va_rounded_size +#define __va_rounded_size(TYPE) \ + (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) +#endif +#define __va_arg_ptr(AP, TYPE) \ + (AP = (va_list) ((char *) (AP) + __va_rounded_size (TYPE)), \ + ((void *) ((char *) (AP) - __va_rounded_size (TYPE)))) +#endif +#endif +#endif /* __STDIO_VA_ARG_PTR */ + +#ifdef __va_arg_ptr +#define GET_VA_ARG(AP,F,TYPE,ARGS) (*(AP) = __va_arg_ptr(ARGS,TYPE)) +#define GET_ARG_VALUE(AP,F,TYPE) (*((TYPE *)(*(AP)))) +#else +typedef union { + wchar_t wc; + unsigned int u; + unsigned long ul; +#ifdef ULLONG_MAX + unsigned long long ull; +#endif +#ifdef __STDIO_PRINTF_FLOAT + double d; + long double ld; +#endif /* __STDIO_PRINTF_FLOAT */ + void *p; +} argvalue_t; + +#define GET_VA_ARG(AU,F,TYPE,ARGS) (AU->F = va_arg(ARGS,TYPE)) +#define GET_ARG_VALUE(AU,F,TYPE) ((TYPE)((AU)->F)) +#endif + +typedef struct { + const char *fmtpos; /* TODO: move below struct?? */ + struct printf_info info; +#ifdef NL_ARGMAX + int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */ +#endif /* NL_ARGMAX */ + int num_data_args; /* TODO: use sentinal??? */ + unsigned int conv_num; + unsigned char argnumber[4]; /* width | prec | 1st data | unused */ + int argtype[MAX_ARGS]; + va_list arg; +#ifdef __va_arg_ptr + void *argptr[MAX_ARGS]; +#else +/* if defined(NL_ARGMAX) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) */ + /* While this is wasteful of space in the case where pos args aren't + * enabled, it is also needed to support custom printf handlers. */ + argvalue_t argvalue[MAX_ARGS]; +#endif +} ppfs_t; /* parse printf format state */ + +/**********************************************************************/ + +/* TODO: fix printf to return 0 and set errno if format error. Standard says + only returns -1 if sets error indicator for the stream. */ + +#ifdef __STDIO_PRINTF_FLOAT +typedef void (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len, + intptr_t buf); + +extern size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + __fp_outfunc_t fp_outfunc); +#endif + +extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0); /* validates */ +extern void _ppfs_prepargs(ppfs_t *ppfs, va_list arg); /* sets posargptrs */ +extern void _ppfs_setargs(ppfs_t *ppfs); /* sets argptrs for current spec */ +extern int _ppfs_parsespec(ppfs_t *ppfs); /* parses specifier */ + +extern void _store_inttype(void *dest, int desttype, uintmax_t val); +extern uintmax_t _load_inttype(int desttype, const void *src, int uflag); + +/**********************************************************************/ +#ifdef L_parse_printf_format + +/* NOTE: This function differs from the glibc version in that parsing stops + * upon encountering an invalid conversion specifier. Since this is the way + * my printf functions work, I think it makes sense to do it that way here. + * Unfortunately, since glibc sets the return type as size_t, we have no way + * of returning that the template is illegal, other than returning 0. + */ + +size_t parse_printf_format(register const char *template, + size_t n, register int *argtypes) +{ + ppfs_t ppfs; + size_t i; + size_t count = 0; + + if (_ppfs_init(&ppfs, template) >= 0) { +#ifdef NL_ARGMAX + if (ppfs.maxposarg > 0) { /* Using positional args. */ + count = ppfs.maxposarg; + if (n > count) { + n = count; + } + for (i = 0 ; i < n ; i++) { + *argtypes++ = ppfs.argtype[i]; + } + } else { /* Not using positional args. */ +#endif /* NL_ARGMAX */ + while (*template) { + if ((*template == '%') && (*++template != '%')) { + ppfs.fmtpos = template; + _ppfs_parsespec(&ppfs); /* Can't fail. */ + template = ppfs.fmtpos; /* Update to one past spec end. */ + if (ppfs.info.width == INT_MIN) { + ++count; + if (n > 0) { + *argtypes++ = PA_INT; + --n; + } + } + if (ppfs.info.prec == INT_MIN) { + ++count; + if (n > 0) { + *argtypes++ = PA_INT; + --n; + } + } + for (i = 0 ; i < ppfs.num_data_args ; i++) { + if ((ppfs.argtype[i]) != __PA_NOARG) { + ++count; + if (n > 0) { + *argtypes++ = ppfs.argtype[i]; + --n; + } + } + } + } else { + ++template; + } + } +#ifdef NL_ARGMAX + } +#endif /* NL_ARGMAX */ + } + + return count; +} + +#endif +/**********************************************************************/ +#ifdef L__ppfs_init + +int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) +{ + int r; + + /* First, zero out everything... argnumber[], argtype[], argptr[] */ + memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */ +#ifdef NL_ARGMAX + --ppfs->maxposarg; /* set to -1 */ +#endif /* NL_ARGMAX */ + ppfs->fmtpos = fmt0; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Make checking of the format string in C locale an option. +#endif +#ifdef __UCLIBC_HAS_LOCALE__ + /* To support old programs, don't check mb validity if in C locale. */ + if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) { + /* ANSI/ISO C99 requires format string to be a valid multibyte string + * beginning and ending in its initial shift state. */ + static const char invalid_mbs[] = "Invalid multibyte format string."; + mbstate_t mbstate; + const char *p; + mbstate.__mask = 0; /* Initialize the mbstate. */ + p = fmt0; + if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { + ppfs->fmtpos = invalid_mbs; + return -1; + } + } +#endif /* __UCLIBC_HAS_LOCALE__ */ + /* now set all argtypes to no-arg */ + { +#if 1 + /* TODO - use memset here since already "paid for"? */ + register int *p = ppfs->argtype; + + r = MAX_ARGS; + do { + *p++ = __PA_NOARG; + } while (--r); +#else + /* TODO -- get rid of this?? */ + register char *p = (char *) ((MAX_ARGS-1) * sizeof(int)); + + do { + *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG; + p -= sizeof(int); + } while (p); +#endif + } + + /* + * Run through the entire format string to validate it and initialize + * the positional arg numbers (if any). + */ + { + register const char *fmt = fmt0; + + while (*fmt) { + if ((*fmt == '%') && (*++fmt != '%')) { + ppfs->fmtpos = fmt; /* back up to the '%' */ + if ((r = _ppfs_parsespec(ppfs)) < 0) { + return -1; + } + fmt = ppfs->fmtpos; /* update to one past end of spec */ + } else { + ++fmt; + } + } + ppfs->fmtpos = fmt0; /* rewind */ + } + +#ifdef NL_MAX_ARG + /* If we have positional args, make sure we know all the types. */ + { + register int *p = ppfs->argtype; + r = ppfs->maxposarg; + while (--r >= 0) { + if ( *p == __PA_NOARG ) { /* missing arg type!!! */ + return -1; + } + ++p; + } + } +#endif /* NL_MAX_ARG */ + + return 0; +} +#endif +/**********************************************************************/ +#ifdef L__ppfs_prepargs +void _ppfs_prepargs(register ppfs_t *ppfs, va_list arg) +{ + int i; + + va_copy(ppfs->arg, arg); + +#ifdef NL_ARGMAX + if ((i = ppfs->maxposarg) > 0) { /* init for positional args */ + ppfs->num_data_args = i; + ppfs->info.width = ppfs->info.prec = ppfs->maxposarg = 0; + _ppfs_setargs(ppfs); + ppfs->maxposarg = i; + } +#endif /* NL_ARGMAX */ +} +#endif +/**********************************************************************/ +#ifdef L__ppfs_setargs + +void _ppfs_setargs(register ppfs_t *ppfs) +{ +#ifdef __va_arg_ptr + register void **p = ppfs->argptr; +#else + register argvalue_t *p = ppfs->argvalue; +#endif + int i; + +#ifdef NL_ARGMAX + if (ppfs->maxposarg == 0) { /* initing for or no pos args */ +#endif /* NL_ARGMAX */ + if (ppfs->info.width == INT_MIN) { + ppfs->info.width = +#ifdef __va_arg_ptr + *(int *) +#endif + GET_VA_ARG(p,u,unsigned int,ppfs->arg); + } + if (ppfs->info.prec == INT_MIN) { + ppfs->info.prec = +#ifdef __va_arg_ptr + *(int *) +#endif + GET_VA_ARG(p,u,unsigned int,ppfs->arg); + } + i = 0; + while (i < ppfs->num_data_args) { + switch(ppfs->argtype[i++]) { + case (PA_INT|PA_FLAG_LONG_LONG): +#ifdef ULLONG_MAX + GET_VA_ARG(p,ull,unsigned long long,ppfs->arg); + break; +#endif + case (PA_INT|PA_FLAG_LONG): +#if ULONG_MAX != UINT_MAX + GET_VA_ARG(p,ul,unsigned long,ppfs->arg); + break; +#endif + case PA_CHAR: /* TODO - be careful */ + /* ... users could use above and really want below!! */ + case (PA_INT|__PA_FLAG_CHAR):/* TODO -- translate this!!! */ + case (PA_INT|PA_FLAG_SHORT): + case PA_INT: + GET_VA_ARG(p,u,unsigned int,ppfs->arg); + break; + case PA_WCHAR: /* TODO -- assume int? */ + /* we're assuming wchar_t is at least an int */ + GET_VA_ARG(p,wc,wchar_t,ppfs->arg); + break; +#ifdef __STDIO_PRINTF_FLOAT + /* PA_FLOAT */ + case PA_DOUBLE: + GET_VA_ARG(p,d,double,ppfs->arg); + break; + case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): + GET_VA_ARG(p,ld,long double,ppfs->arg); + break; +#else /* __STDIO_PRINTF_FLOAT */ + case PA_DOUBLE: + case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): + assert(0); + continue; +#endif /* __STDIO_PRINTF_FLOAT */ + default: + /* TODO -- really need to ensure this can't happen */ + assert(ppfs->argtype[i-1] & PA_FLAG_PTR); + case PA_POINTER: + case PA_STRING: + case PA_WSTRING: + GET_VA_ARG(p,p,void *,ppfs->arg); + break; + case __PA_NOARG: + continue; + } + ++p; + } +#ifdef NL_ARGMAX + } else { + if (ppfs->info.width == INT_MIN) { + ppfs->info.width + = (int) GET_ARG_VALUE(p + ppfs->argnumber[0] - 1,u,unsigned int); + } + if (ppfs->info.prec == INT_MIN) { + ppfs->info.prec + = (int) GET_ARG_VALUE(p + ppfs->argnumber[1] - 1,u,unsigned int); + } + } +#endif /* NL_ARGMAX */ + + /* Now we know the width and precision. */ + if (ppfs->info.width < 0) { + ppfs->info.width = -ppfs->info.width; + PRINT_INFO_SET_FLAG(&(ppfs->info),left); + PRINT_INFO_CLR_FLAG(&(ppfs->info),space); + ppfs->info.pad = ' '; + } +#if 0 + /* NOTE -- keep neg for now so float knows! */ + if (ppfs->info.prec < 0) { /* spec says treat as omitted. */ + /* so use default prec... 1 for everything but floats and strings. */ + ppfs->info.prec = 1; + } +#endif +} +#endif +/**********************************************************************/ +#ifdef L__ppfs_parsespec + +/* Notes: argtype differs from glibc for the following: + * mine glibc + * lc PA_WCHAR PA_CHAR the standard says %lc means %C + * ls PA_WSTRING PA_STRING the standard says %ls means %S + * {*}n {*}|PA_FLAG_PTR PA_FLAG_PTR size of n can be qualified + */ + +/* TODO: store positions of positional args */ + +/* TODO -- WARNING -- assumes aligned on integer boundaries!!! */ + +/* TODO -- disable if not using positional args!!! */ +#define _OVERLAPPING_DIFFERENT_ARGS + +/* TODO -- rethink this -- perhaps we should set to largest type??? */ + +#ifdef _OVERLAPPING_DIFFERENT_ARGS + +#define PROMOTED_SIZE_OF(X) ((sizeof(X) + sizeof(int) - 1) / sizeof(X)) + +static const short int type_codes[] = { + __PA_NOARG, /* must be first entry */ + PA_POINTER, + PA_STRING, + PA_WSTRING, + PA_CHAR, + PA_INT|PA_FLAG_SHORT, + PA_INT, + PA_INT|PA_FLAG_LONG, + PA_INT|PA_FLAG_LONG_LONG, + PA_WCHAR, +#ifdef __STDIO_PRINTF_FLOAT + /* PA_FLOAT, */ + PA_DOUBLE, + PA_DOUBLE|PA_FLAG_LONG_DOUBLE, +#endif /* __STDIO_PRINTF_FLOAT */ +}; + +static const unsigned char type_sizes[] = { + /* unknown type consumes no arg */ + 0, /* must be first entry */ + PROMOTED_SIZE_OF(void *), + PROMOTED_SIZE_OF(char *), + PROMOTED_SIZE_OF(wchar_t *), + PROMOTED_SIZE_OF(char), + PROMOTED_SIZE_OF(short), + PROMOTED_SIZE_OF(int), + PROMOTED_SIZE_OF(long), +#ifdef ULLONG_MAX + PROMOTED_SIZE_OF(long long), +#else + PROMOTED_SIZE_OF(long), /* TODO -- is this correct? (above too) */ +#endif + PROMOTED_SIZE_OF(wchar_t), +#ifdef __STDIO_PRINTF_FLOAT + /* PROMOTED_SIZE_OF(float), */ + PROMOTED_SIZE_OF(double), + PROMOTED_SIZE_OF(long double), +#endif /* __STDIO_PRINTF_FLOAT */ +}; + +static int _promoted_size(int argtype) +{ + register const short int *p; + + /* note -- since any unrecognized type is treated as a pointer */ + p = type_codes + sizeof(type_codes)/sizeof(type_codes[0]); + do { + if (*--p == argtype) { + break; + } + } while (p > type_codes); + + return type_sizes[(int)(p - type_codes)]; +} + +static int _is_equal_or_bigger_arg(int curtype, int newtype) +{ + /* Quick test */ + if (newtype == __PA_NOARG) { + return 0; + } + if ((curtype == __PA_NOARG) || (curtype == newtype)) { + return 1; + } + /* Ok... slot is already filled and types are different in name. */ + /* So, compare promoted sizes of curtype and newtype args. */ + return _promoted_size(curtype) <= _promoted_size(newtype); +} + +#else + +#define _is_equal_or_bigger_arg(C,N) (((C) == __PA_NOARG) || ((C) == (N))) + +#endif + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ +/* TODO - do this differently? */ +static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us. */ + +char *_custom_printf_spec = _bss_custom_printf_spec; +printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; +printf_function _custom_printf_handler[MAX_USER_SPEC]; +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +extern int _ppfs_parsespec(ppfs_t *ppfs) +{ + register const char *fmt; + register const char *p; + int preci; + int width; + int flags; + int dataargtype; + int i; + int dpoint; +#ifdef NL_ARGMAX + int maxposarg; +#endif /* NL_ARGMAX */ + int p_m_spec_chars; + int n; + int argtype[MAX_ARGS_PER_SPEC+2]; + int argnumber[3]; /* width, precision, 1st data arg */ + static const char spec_flags[] = SPEC_FLAGS; + static const char spec_chars[] = SPEC_CHARS;/* TODO: b? */ + static const char spec_ranges[] = SPEC_RANGES; + static const short spec_or_mask[] = SPEC_OR_MASK; + static const short spec_and_mask[] = SPEC_AND_MASK; + static const char qual_chars[] = QUAL_CHARS; +#ifdef __UCLIBC_HAS_WCHAR__ + char buf[32]; +#endif /* __UCLIBC_HAS_WCHAR__ */ + + /* WIDE note: we can test against '%' here since we don't allow */ + /* WIDE note: other mappings of '%' in the wide char set. */ + preci = -1; + argnumber[0] = 0; + argnumber[1] = 0; + argtype[0] = __PA_NOARG; + argtype[1] = __PA_NOARG; +#ifdef NL_ARGMAX + maxposarg = ppfs->maxposarg; +#endif /* NL_ARGMAX */ + +#ifdef __UCLIBC_HAS_WCHAR__ + /* This is somewhat lame, but saves a lot of code. If we're dealing with + * a wide stream, that means the format is a wchar string. So, copy it + * char-by-char into a normal char buffer for processing. Make the buffer + * (buf) big enough so that any reasonable format specifier will fit. + * While there a legal specifiers that won't, the all involve duplicate + * flags or outrageous field widths/precisions. */ + width = dpoint = 0; + if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { + fmt = ppfs->fmtpos; + } else { + fmt = buf + 1; + i = 0; + do { + if ((buf[i] = (char) (((wchar_t *) ppfs->fmtpos)[i-1])) + != (((wchar_t *) ppfs->fmtpos)[i-1]) + ) { + return -1; + } + } while (buf[i++]); + buf[sizeof(buf)-1] = 0; + } +#else /* __UCLIBC_HAS_WCHAR__ */ + width = flags = dpoint = 0; + fmt = ppfs->fmtpos; +#endif /* __UCLIBC_HAS_WCHAR__ */ + + assert(fmt[-1] == '%'); + assert(fmt[0] != '%'); + + /* Process arg pos and/or flags and/or width and/or precision. */ + width_precision: + p = fmt; + if (*fmt == '*') { + argtype[-dpoint] = PA_INT; + ++fmt; + } + i = 0; + while (isdigit(*fmt)) { + if (i < MAX_FIELD_WIDTH) { /* Avoid overflow. */ + i = (i * 10) + (*fmt - '0'); + } + ++fmt; + } + if (p[-1] == '%') { /* Check for a position. */ + + /* TODO: if val not in range, then error */ + +#ifdef NL_ARGMAX + if ((*fmt == '$') && (i > 0)) {/* Positional spec. */ + ++fmt; + if (maxposarg == 0) { + return -1; + } + if ((argnumber[2] = i) > maxposarg) { + maxposarg = i; + } + /* Now fall through to check flags. */ + } else { + if (maxposarg > 0) { +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Support prec and width for %m when positional args used + /* Actually, positional arg processing will fail in general + * for specifiers that don't require an arg. */ +#endif /* __UCLIBC_MJN3_ONLY__ */ + if (*fmt == 'm') { + goto PREC_WIDTH; + } +#endif /* __UCLIBC_HAS_PRINTF_M_SPEC__ */ + return -1; + } + maxposarg = 0; /* Possible redundant store, but cuts size. */ + + if ((fmt > p) && (*p != '0')) { + goto PREC_WIDTH; + } + + fmt = p; /* Back up for possible '0's flag. */ + /* Now fall through to check flags. */ + } +#else /* NL_ARGMAX */ + if (*fmt == '$') { /* Positional spec. */ + return -1; + } + + if ((fmt > p) && (*p != '0')) { + goto PREC_WIDTH; + } + + fmt = p; /* Back up for possible '0's flag. */ + /* Now fall through to check flags. */ +#endif /* NL_ARGMAX */ + + restart_flags: /* Process flags. */ + i = 1; + p = spec_flags; + + do { + if (*fmt == *p++) { + ++fmt; + flags |= i; + goto restart_flags; + } + i += i; /* Better than i <<= 1 for bcc */ + } while (*p); + i = 0; + + /* If '+' then ignore ' ', and if '-' then ignore '0'. */ + /* Note: Need to ignore '0' when prec is an arg with val < 0, */ + /* but that test needs to wait until the arg is retrieved. */ + flags &= ~((flags & (FLAG_PLUS|FLAG_MINUS)) >> 1); + /* Note: Ignore '0' when prec is specified < 0 too (in printf). */ + + if (fmt[-1] != '%') { /* If we've done anything, loop for width. */ + goto width_precision; + } + } + PREC_WIDTH: + if (*p == '*') { /* Prec or width takes an arg. */ +#ifdef NL_ARGMAX + if (maxposarg) { + if ((*fmt++ != '$') || (i <= 0)) { + /* Using pos args and no $ or invalid arg number. */ + return -1; + } + argnumber[-dpoint] = i; + } else +#endif /* NL_ARGMAX */ + if (++p != fmt) { + /* Not using pos args but digits followed *. */ + return -1; + } + i = INT_MIN; + } + + if (!dpoint) { + width = i; + if (*fmt == '.') { + ++fmt; + dpoint = -1; /* To use as default precison. */ + goto width_precision; + } + } else { + preci = i; + } + + /* Process qualifier. */ + p = qual_chars; + do { + if (*fmt == *p) { + ++fmt; + break; + } + } while (*++p); + if ((p - qual_chars < 2) && (*fmt == *p)) { + p += ((sizeof(qual_chars)-2) / 2); + ++fmt; + } + dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8; + + /* Process conversion specifier. */ + if (!*fmt) { + return -1; + } + + p = spec_chars; + + do { + if (*fmt == *p) { + p_m_spec_chars = p - spec_chars; + + if ((p_m_spec_chars >= CONV_c) + && (dataargtype & PA_FLAG_LONG)) { + p_m_spec_chars -= 2; /* lc -> C and ls -> S */ + } + + ppfs->conv_num = p_m_spec_chars; + p = spec_ranges-1; + while (p_m_spec_chars > *++p) {} + + i = p - spec_ranges; + argtype[2] = (dataargtype | spec_or_mask[i]) & spec_and_mask[i]; + p = spec_chars; + break; + } + } while(*++p); + + ppfs->info.spec = *fmt; + ppfs->info.prec = preci; + ppfs->info.width = width; + ppfs->info.pad = ((flags & FLAG_ZERO) ? '0' : ' '); + ppfs->info._flags = (flags & ~FLAG_ZERO) | (dataargtype & __PA_INTMASK); + ppfs->num_data_args = 1; + + if (!*p) { +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + if (*fmt == 'm') { + ppfs->conv_num = CONV_m; + ppfs->num_data_args = 0; + goto DONE; + } +#endif +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + + /* Handle custom arg -- WARNING -- overwrites p!!! */ + ppfs->conv_num = CONV_custom0; + p = _custom_printf_spec; + do { + if (*p == *fmt) { + if ((ppfs->num_data_args + = ((*_custom_printf_arginfo[(int)(p-_custom_printf_spec)]) + (&(ppfs->info), MAX_ARGS_PER_SPEC, argtype+2))) + > MAX_ARGS_PER_SPEC) { + break; /* Error -- too many args! */ + } + goto DONE; + } + } while (++p < (_custom_printf_spec + MAX_USER_SPEC)); +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + /* Otherwise error. */ + return -1; + } + +#if defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) || defined(__UCLIBC_HAS_PRINTF_M_SPEC__) + DONE: +#endif + +#ifdef NL_ARGMAX + if (maxposarg > 0) { + i = 0; + do { + /* Update maxposarg and check that NL_ARGMAX is not exceeded. */ + n = ((i <= 2) + ? (ppfs->argnumber[i] = argnumber[i]) + : argnumber[2] + (i-2)); + if (n > maxposarg) { + if ((maxposarg = n) > NL_ARGMAX) { + return -1; + } + } + --n; + /* Record argtype with largest size (current, new). */ + if (_is_equal_or_bigger_arg(ppfs->argtype[n], argtype[i])) { + ppfs->argtype[n] = argtype[i]; + } + } while (++i < ppfs->num_data_args + 2); + } else { +#endif /* NL_ARGMAX */ + ppfs->argnumber[2] = 1; + memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int)); +#ifdef NL_ARGMAX + } + + ppfs->maxposarg = maxposarg; +#endif /* NL_ARGMAX */ + +#ifdef __UCLIBC_HAS_WCHAR__ + if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { + ppfs->fmtpos = ++fmt; + } else { + ppfs->fmtpos = (const char *) (((const wchar_t *)(ppfs->fmtpos)) + + (fmt - buf) ); + } +#else /* __UCLIBC_HAS_WCHAR__ */ + ppfs->fmtpos = ++fmt; +#endif /* __UCLIBC_HAS_WCHAR__ */ + + return ppfs->num_data_args + 2; +} + +#endif +/**********************************************************************/ +#ifdef L_register_printf_function + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + +int register_printf_function(int spec, printf_function handler, + printf_arginfo_function arginfo) +{ + register char *r; + register char *p; + + if (spec && (arginfo != NULL)) { /* TODO -- check if spec is valid char */ + r = NULL; + p = _custom_printf_spec + MAX_USER_SPEC; + do { + --p; + if (!*p) { + r = p; + } +#ifdef __BCC__ + else /* bcc generates less code with fall-through */ +#endif + if (*p == spec) { + r = p; + p = _custom_printf_spec; + } + } while (p > _custom_printf_spec); + + if (r) { + if (handler) { + *r = spec; + _custom_printf_handler[(int)(r - p)] = handler; + _custom_printf_arginfo[(int)(r - p)] = arginfo; + } else { + *r = 0; + } + return 0; + } + /* TODO -- if asked to unregister a non-existent spec, return what? */ + } + return -1; +} + +#endif + +#endif +/**********************************************************************/ +#if defined(L_vfprintf) || defined(L_vfwprintf) + +/* We only support ascii digits (or their USC equivalent codes) in + * precision and width settings in *printf (wide) format strings. + * In other words, we don't currently support glibc's 'I' flag. + * We do accept it, but it is currently ignored. */ + +static void _charpad(FILE * __restrict stream, int padchar, size_t numpad); + +#ifdef L_vfprintf + +#define VFPRINTF vfprintf +#define FMT_TYPE char +#define OUTNSTR _outnstr +#define STRLEN strlen +#define _PPFS_init _ppfs_init +#define OUTPUT(F,S) __fputs_unlocked(S,F) +/* #define _outnstr(stream, string, len) __stdio_fwrite(string, len, stream) */ +#define _outnstr(stream, string, len) ((len > 0) ? __stdio_fwrite(string, len, stream) : 0) +#define FP_OUT _fp_out_narrow + +#ifdef __STDIO_PRINTF_FLOAT + +static void _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) +{ + if (type & 0x80) { /* Some type of padding needed. */ + int buflen = strlen((const char *) buf); + if ((len -= buflen) > 0) { + _charpad(fp, (type & 0x7f), len); + } + len = buflen; + } + OUTNSTR(fp, (const char *) buf, len); +} + +#endif /* __STDIO_PRINTF_FLOAT */ + +#else /* L_vfprintf */ + +#define VFPRINTF vfwprintf +#define FMT_TYPE wchar_t +#define OUTNSTR _outnwcs +#define STRLEN wcslen +#define _PPFS_init _ppwfs_init +#define OUTPUT(F,S) fputws(S,F) +#define _outnwcs(stream, wstring, len) _wstdio_fwrite(wstring, len, stream) +#define FP_OUT _fp_out_wide + +static void _outnstr(FILE *stream, const char *s, size_t wclen) +{ + /* NOTE!!! len here is the number of wchars we want to generate!!! */ + wchar_t wbuf[64]; + mbstate_t mbstate; + size_t todo, r; + + mbstate.__mask = 0; + todo = wclen; + + while (todo) { + r = mbsrtowcs(wbuf, &s, + ((todo <= sizeof(wbuf)/sizeof(wbuf[0])) + ? todo + : sizeof(wbuf)/sizeof(wbuf[0])), + &mbstate); + assert(((ssize_t)r) > 0); + _outnwcs(stream, wbuf, r); + todo -= r; + } +} + +#ifdef __STDIO_PRINTF_FLOAT + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Move defines from _fpmaxtostr. Put them in a common header. +#endif + +/* The following defines are from _fpmaxtostr.*/ +#define DIGITS_PER_BLOCK 9 +#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) +#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) + +static void _fp_out_wide(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) +{ + wchar_t wbuf[BUF_SIZE]; + const char *s = (const char *) buf; + int i; + + if (type & 0x80) { /* Some type of padding needed */ + int buflen = strlen(s); + if ((len -= buflen) > 0) { + _charpad(fp, (type & 0x7f), len); + } + len = buflen; + } + + if (len > 0) { + i = 0; + do { +#ifdef __LOCALE_C_ONLY + wbuf[i] = s[i]; +#else /* __LOCALE_C_ONLY */ + +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + if (s[i] == ',') { + wbuf[i] = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc; + } else +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + if (s[i] == '.') { + wbuf[i] = __UCLIBC_CURLOCALE_DATA.decimal_point_wc; + } else { + wbuf[i] = s[i]; + } +#endif /* __LOCALE_C_ONLY */ + + } while (++i < len); + + OUTNSTR(fp, wbuf, len); + } +} + +#endif /* __STDIO_PRINTF_FLOAT */ + +static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0) +{ + static const wchar_t invalid_wcs[] = L"Invalid wide format string."; + int r; + + /* First, zero out everything... argnumber[], argtype[], argptr[] */ + memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */ +#ifdef NL_ARGMAX + --ppfs->maxposarg; /* set to -1 */ +#endif /* NL_ARGMAX */ + ppfs->fmtpos = (const char *) fmt0; + ppfs->info._flags = FLAG_WIDESTREAM; + + { + mbstate_t mbstate; + const wchar_t *p; + mbstate.__mask = 0; /* Initialize the mbstate. */ + p = fmt0; + if (wcsrtombs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { + ppfs->fmtpos = (const char *) invalid_wcs; + return -1; + } + } + + /* now set all argtypes to no-arg */ + { +#if 1 + /* TODO - use memset here since already "paid for"? */ + register int *p = ppfs->argtype; + + r = MAX_ARGS; + do { + *p++ = __PA_NOARG; + } while (--r); +#else + /* TODO -- get rid of this?? */ + register char *p = (char *) ((MAX_ARGS-1) * sizeof(int)); + + do { + *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG; + p -= sizeof(int); + } while (p); +#endif + } + + /* + * Run through the entire format string to validate it and initialize + * the positional arg numbers (if any). + */ + { + register const wchar_t *fmt = fmt0; + + while (*fmt) { + if ((*fmt == '%') && (*++fmt != '%')) { + ppfs->fmtpos = (const char *) fmt; /* back up to the '%' */ + if ((r = _ppfs_parsespec(ppfs)) < 0) { + return -1; + } + fmt = (const wchar_t *) ppfs->fmtpos; /* update to one past end of spec */ + } else { + ++fmt; + } + } + ppfs->fmtpos = (const char *) fmt0; /* rewind */ + } + +#ifdef NL_ARGMAX + /* If we have positional args, make sure we know all the types. */ + { + register int *p = ppfs->argtype; + r = ppfs->maxposarg; + while (--r >= 0) { + if ( *p == __PA_NOARG ) { /* missing arg type!!! */ + return -1; + } + ++p; + } + } +#endif /* NL_ARGMAX */ + + return 0; +} + +#endif /* L_vfprintf */ + +static void _charpad(FILE * __restrict stream, int padchar, size_t numpad) +{ + /* TODO -- Use a buffer to cut down on function calls... */ + FMT_TYPE pad[1]; + + *pad = padchar; + while (numpad) { + OUTNSTR(stream, pad, 1); + --numpad; + } +} + +/* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */ +static int _do_one_spec(FILE * __restrict stream, + register ppfs_t *ppfs, int *count) +{ + static const char spec_base[] = SPEC_BASE; +#ifdef L_vfprintf + static const char prefix[] = "+\0-\0 \0000x\0000X"; + /* 0 2 4 6 9 11*/ +#else /* L_vfprintf */ + static const wchar_t prefix[] = L"+\0-\0 \0000x\0000X"; +#endif /* L_vfprintf */ + enum { + PREFIX_PLUS = 0, + PREFIX_MINUS = 2, + PREFIX_SPACE = 4, + PREFIX_LWR_X = 6, + PREFIX_UPR_X = 9, + PREFIX_NONE = 11 + }; + +#ifdef __va_arg_ptr + const void * const *argptr; +#else + const void * argptr[MAX_ARGS_PER_SPEC]; +#endif + int *argtype; +#ifdef __UCLIBC_HAS_WCHAR__ + const wchar_t *ws = NULL; + mbstate_t mbstate; +#endif /* __UCLIBC_HAS_WCHAR__ */ + size_t slen; +#ifdef L_vfprintf +#define SLEN slen +#else + size_t SLEN; + wchar_t wbuf[2]; +#endif + int base; + int numpad; + int alphacase; + int numfill = 0; /* TODO: fix */ + int prefix_num = PREFIX_NONE; + char padchar = ' '; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Determine appropriate buf size. +#endif /* __UCLIBC_MJN3_ONLY__ */ + /* TODO: buf needs to be big enough for any possible error return strings + * and also for any locale-grouped long long integer strings generated. + * This should be large enough for any of the current archs/locales, but + * eventually this should be handled robustly. */ + char buf[128]; + +#ifdef NDEBUG + _ppfs_parsespec(ppfs); +#else + if (_ppfs_parsespec(ppfs) < 0) { /* TODO: just for debugging */ + abort(); + } +#endif + _ppfs_setargs(ppfs); + + argtype = ppfs->argtype + ppfs->argnumber[2] - 1; + /* Deal with the argptr vs argvalue issue. */ +#ifdef __va_arg_ptr + argptr = (const void * const *) ppfs->argptr; +#ifdef NL_ARGMAX + if (ppfs->maxposarg > 0) { /* Using positional args... */ + argptr += ppfs->argnumber[2] - 1; + } +#endif /* NL_ARGMAX */ +#else + /* Need to build a local copy... */ + { + register argvalue_t *p = ppfs->argvalue; + int i; +#ifdef NL_ARGMAX + if (ppfs->maxposarg > 0) { /* Using positional args... */ + p += ppfs->argnumber[2] - 1; + } +#endif /* NL_ARGMAX */ + for (i = 0 ; i < ppfs->num_data_args ; i++ ) { + argptr[i] = (void *) p++; + } + } +#endif + { + register char *s = NULL; /* TODO: Should s be unsigned char * ? */ + + if (ppfs->conv_num == CONV_n) { + _store_inttype(*(void **)*argptr, + ppfs->info._flags & __PA_INTMASK, + (intmax_t) (*count)); + return 0; + } + if (ppfs->conv_num <= CONV_i) { /* pointer or (un)signed int */ + alphacase = __UIM_LOWER; + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_vfprintf +#warning CONSIDER: Should we ignore these flags if stub locale? What about custom specs? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + if ((base = spec_base[(int)(ppfs->conv_num - CONV_p)]) == 10) { + if (PRINT_INFO_FLAG_VAL(&(ppfs->info),group)) { + alphacase = __UIM_GROUP; + } + if (PRINT_INFO_FLAG_VAL(&(ppfs->info),i18n)) { + alphacase |= 0x80; + } + } + + if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */ + if (ppfs->conv_num == CONV_X) { + alphacase = __UIM_UPPER; + } + if (ppfs->conv_num == CONV_p) { /* pointer */ + prefix_num = PREFIX_LWR_X; + } else { /* unsigned int */ + } + } else { /* signed int */ + base = -base; + } + if (ppfs->info.prec < 0) { /* Ignore '0' flag if prec specified. */ + padchar = ppfs->info.pad; + } +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_vfprintf +#warning CONSIDER: If using outdigits and/or grouping, how should we interpret precision? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + s = _uintmaxtostr(buf + sizeof(buf) - 1, + (uintmax_t) + _load_inttype(*argtype & __PA_INTMASK, + *argptr, base), base, alphacase); + if (ppfs->conv_num > CONV_u) { /* signed int */ + if (*s == '-') { + PRINT_INFO_SET_FLAG(&(ppfs->info),showsign); + ++s; /* handle '-' in the prefix string */ + prefix_num = PREFIX_MINUS; + } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),showsign)) { + prefix_num = PREFIX_PLUS; + } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),space)) { + prefix_num = PREFIX_SPACE; + } + } + slen = (char *)(buf + sizeof(buf) - 1) - s; +#ifdef L_vfwprintf + { + const char *q = s; + mbstate.__mask = 0; /* Initialize the mbstate. */ + SLEN = mbsrtowcs(NULL, &q, 0, &mbstate); + } +#endif + numfill = ((ppfs->info.prec < 0) ? 1 : ppfs->info.prec); + if (PRINT_INFO_FLAG_VAL(&(ppfs->info),alt)) { + if (ppfs->conv_num <= CONV_x) { /* x or p */ + prefix_num = PREFIX_LWR_X; + } + if (ppfs->conv_num == CONV_X) { + prefix_num = PREFIX_UPR_X; + } + if ((ppfs->conv_num == CONV_o) && (numfill <= SLEN)) { + numfill = ((*s == '0') ? 1 : SLEN + 1); + } + } + if (*s == '0') { + if (prefix_num >= PREFIX_LWR_X) { + prefix_num = PREFIX_NONE; + } + if (ppfs->conv_num == CONV_p) {/* null pointer */ + s = "(nil)"; +#ifdef L_vfwprintf + SLEN = +#endif + slen = 5; + numfill = 0; + } else if (numfill == 0) { /* if precision 0, no output */ +#ifdef L_vfwprintf + SLEN = +#endif + slen = 0; + } + } + numfill = ((numfill > SLEN) ? numfill - SLEN : 0); + } else if (ppfs->conv_num <= CONV_A) { /* floating point */ +#ifdef __STDIO_PRINTF_FLOAT + *count += + _fpmaxtostr(stream, + (__fpmax_t) + (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double) + ? *(long double *) *argptr + : (long double) (* (double *) *argptr)), + &ppfs->info, FP_OUT ); + return 0; +#else /* __STDIO_PRINTF_FLOAT */ + return -1; /* TODO -- try to continue? */ +#endif /* __STDIO_PRINTF_FLOAT */ + } else if (ppfs->conv_num <= CONV_S) { /* wide char or string */ +#ifdef L_vfprintf + +#ifdef __UCLIBC_HAS_WCHAR__ + mbstate.__mask = 0; /* Initialize the mbstate. */ + if (ppfs->conv_num == CONV_S) { /* wide string */ + if (!(ws = *((const wchar_t **) *argptr))) { + goto NULL_STRING; + } + /* We use an awful uClibc-specific hack here, passing + * (char*) &ws as the conversion destination. This signals + * uClibc's wcsrtombs that we want a "restricted" length + * such that the mbs fits in a buffer of the specified + * size with no partial conversions. */ + if ((slen = wcsrtombs((char *) &ws, &ws, /* Use awful hack! */ + ((ppfs->info.prec >= 0) + ? ppfs->info.prec + : SIZE_MAX), &mbstate)) + == ((size_t)-1) + ) { + return -1; /* EILSEQ */ + } + } else { /* wide char */ + s = buf; + slen = wcrtomb(s, (*((const wchar_t *) *argptr)), &mbstate); + if (slen == ((size_t)-1)) { + return -1; /* EILSEQ */ + } + s[slen] = 0; /* TODO - Is this necessary? */ + } +#else /* __UCLIBC_HAS_WCHAR__ */ + return -1; +#endif /* __UCLIBC_HAS_WCHAR__ */ + } else if (ppfs->conv_num <= CONV_s) { /* char or string */ + if (ppfs->conv_num == CONV_s) { /* string */ + s = *((char **) (*argptr)); + if (s) { +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + SET_STRING_LEN: +#endif + slen = strnlen(s, ((ppfs->info.prec >= 0) + ? ppfs->info.prec : SIZE_MAX)); + } else { +#ifdef __UCLIBC_HAS_WCHAR__ + NULL_STRING: +#endif + s = "(null)"; + slen = 6; + } + } else { /* char */ + s = buf; + *s = (unsigned char)(*((const int *) *argptr)); + s[1] = 0; + slen = 1; + } + +#else /* L_vfprintf */ + + if (ppfs->conv_num == CONV_S) { /* wide string */ + ws = *((wchar_t **) (*argptr)); + if (!ws) { + goto NULL_STRING; + } + SLEN = wcsnlen(ws, ((ppfs->info.prec >= 0) + ? ppfs->info.prec : SIZE_MAX)); + } else { /* wide char */ + *wbuf = (wchar_t)(*((const wint_t *) *argptr)); + CHAR_CASE: + ws = wbuf; + wbuf[1] = 0; + SLEN = 1; + } + + } else if (ppfs->conv_num <= CONV_s) { /* char or string */ + + if (ppfs->conv_num == CONV_s) { /* string */ +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Fix %s for vfwprintf... output upto illegal sequence? +#endif /* __UCLIBC_MJN3_ONLY__ */ + s = *((char **) (*argptr)); + if (s) { +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + SET_STRING_LEN: +#endif + /* We use an awful uClibc-specific hack here, passing + * (wchar_t*) &mbstate as the conversion destination. + * This signals uClibc's mbsrtowcs that we want a + * "restricted" length such that the mbs fits in a buffer + * of the specified size with no partial conversions. */ + { + const char *q = s; + mbstate.__mask = 0; /* Initialize the mbstate. */ + SLEN = mbsrtowcs((wchar_t *) &mbstate, &q, + ((ppfs->info.prec >= 0) + ? ppfs->info.prec : SIZE_MAX), + &mbstate); + } + if (SLEN == ((size_t)(-1))) { + return -1; /* EILSEQ */ + } + } else { + NULL_STRING: + s = "(null)"; + SLEN = slen = 6; + } + } else { /* char */ + *wbuf = btowc( (unsigned char)(*((const int *) *argptr)) ); + goto CHAR_CASE; + } + +#endif /* L_vfprintf */ + +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + } else if (ppfs->conv_num == CONV_m) { + s = _glibc_strerror_r(errno, buf, sizeof(buf)); + goto SET_STRING_LEN; +#endif + } else { +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + assert(ppfs->conv_num == CONV_custom0); + + s = _custom_printf_spec; + do { + if (*s == ppfs->info.spec) { + int rv; + /* TODO -- check return value for sanity? */ + rv = (*_custom_printf_handler + [(int)(s-_custom_printf_spec)]) + (stream, &ppfs->info, argptr); + if (rv < 0) { + return -1; + } + *count += rv; + return 0; + } + } while (++s < (_custom_printf_spec + MAX_USER_SPEC)); +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + assert(0); + return -1; + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_vfprintf +#warning CONSIDER: If using outdigits and/or grouping, how should we pad? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + { + size_t t; + + t = SLEN + numfill; + if (prefix_num != PREFIX_NONE) { + t += ((prefix_num < PREFIX_LWR_X) ? 1 : 2); + } + numpad = ((ppfs->info.width > t) ? (ppfs->info.width - t) : 0); + *count += t + numpad; + } + if (padchar == '0') { /* TODO: check this */ + numfill += numpad; + numpad = 0; + } + + /* Now handle the output itself. */ + if (!PRINT_INFO_FLAG_VAL(&(ppfs->info),left)) { + _charpad(stream, ' ', numpad); + numpad = 0; + } + OUTPUT(stream, prefix + prefix_num); + _charpad(stream, '0', numfill); + +#ifdef L_vfprintf + +#ifdef __UCLIBC_HAS_WCHAR__ + if (!ws) { + assert(s); + _outnstr(stream, s, slen); + } else { /* wide string */ + size_t t; + mbstate.__mask = 0; /* Initialize the mbstate. */ + while (slen) { + t = (slen <= sizeof(buf)) ? slen : sizeof(buf); + t = wcsrtombs(buf, &ws, t, &mbstate); + assert (t != ((size_t)(-1))); + _outnstr(stream, buf, t); + slen -= t; + } + } +#else /* __UCLIBC_HAS_WCHAR__ */ + _outnstr(stream, s, slen); +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#else /* L_vfprintf */ + + if (!ws) { + assert(s); + _outnstr(stream, s, SLEN); + } else { + _outnwcs(stream, ws, SLEN); + } + +#endif /* L_vfprintf */ + _charpad(stream, ' ', numpad); + } + + return 0; +} + +int VFPRINTF (FILE * __restrict stream, + register const FMT_TYPE * __restrict format, + va_list arg) +{ + ppfs_t ppfs; + int count, r; + register const FMT_TYPE *s; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + count = 0; + s = format; + + if +#ifdef L_vfprintf + (!__STDIO_STREAM_IS_NARROW_WRITING(stream) + && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW)) +#else + (!__STDIO_STREAM_IS_WIDE_WRITING(stream) + && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_WIDE)) +#endif + { + count = -1; + } else if (_PPFS_init(&ppfs, format) < 0) { /* Bad format string. */ + OUTNSTR(stream, (const FMT_TYPE *) ppfs.fmtpos, + STRLEN((const FMT_TYPE *)(ppfs.fmtpos))); +#if defined(L_vfprintf) && !defined(NDEBUG) + fprintf(stderr,"\nIMbS: \"%s\"\n\n", format); +#endif + count = -1; + } else { + _ppfs_prepargs(&ppfs, arg); /* This did a va_copy!!! */ + + do { + while (*format && (*format != '%')) { + ++format; + } + + if (format-s) { /* output any literal text in format string */ + if ( (r = OUTNSTR(stream, s, format-s)) < 0) { + count = -1; + break; + } + count += r; + } + + if (!*format) { /* we're done */ + break; + } + + if (format[1] != '%') { /* if we get here, *format == '%' */ + /* TODO: _do_one_spec needs to know what the output funcs are!!! */ + ppfs.fmtpos = (const char *)(++format); + /* TODO: check -- should only fail on stream error */ + if ( (r = _do_one_spec(stream, &ppfs, &count)) < 0) { + count = -1; + break; + } + s = format = (const FMT_TYPE *) ppfs.fmtpos; + } else { /* %% means literal %, so start new string */ + s = ++format; + ++format; + } + } while (1); + + va_end(ppfs.arg); /* Need to clean up after va_copy! */ + } + +/* #if defined(L_vfprintf) && defined(__UCLIBC_HAS_WCHAR__) */ +/* DONE: */ +/* #endif */ + + __STDIO_AUTO_THREADUNLOCK(stream); + + return count; +} +#endif +/**********************************************************************/ diff --git a/libc/stdio/vprintf.c b/libc/stdio/vprintf.c new file mode 100644 index 000000000..45d580faa --- /dev/null +++ b/libc/stdio/vprintf.c @@ -0,0 +1,14 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +int vprintf(const char * __restrict format, va_list arg) +{ + return vfprintf(stdout, format, arg); +} diff --git a/libc/stdio/vsnprintf.c b/libc/stdio/vsnprintf.c new file mode 100644 index 000000000..ffe33ada3 --- /dev/null +++ b/libc/stdio/vsnprintf.c @@ -0,0 +1,210 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning WISHLIST: Implement vsnprintf for non-buffered and no custom stream case. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef __STDIO_BUFFERS + +int vsnprintf(char *__restrict buf, size_t size, + const char * __restrict format, va_list arg) +{ + FILE f; + int rv; + +/* __STDIO_STREAM_RESET_GCS(&f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.__cookie = &(f.__filedes); + f.__gcs.read = NULL; + f.__gcs.write = NULL; + f.__gcs.seek = NULL; + f.__gcs.close = NULL; +#endif + + f.__filedes = __STDIO_STREAM_FAKE_VSNPRINTF_FILEDES; + f.__modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING); + +#ifdef __UCLIBC_HAS_WCHAR__ + f.__ungot_width[0] = 0; +#endif /* __UCLIBC_HAS_WCHAR__ */ +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(f.__state)); +#endif /* __STDIO_MBSTATE */ + +#ifdef __UCLIBC_HAS_THREADS__ + f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.__lock); +#endif + f.__nextopen = NULL; + + if (size > SIZE_MAX - (size_t) buf) { + size = SIZE_MAX - (size_t) buf; + } + + /* Set these last since __bufputc initialization depends on + * __user_locking and only gets set if user locking is on. */ + f.__bufstart = buf; + f.__bufend = buf + size; + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(&f); + __STDIO_STREAM_DISABLE_GETC(&f); + __STDIO_STREAM_ENABLE_PUTC(&f); + + rv = vfprintf(&f, format, arg); + if (size) { + if (f.__bufpos == f.__bufend) { + --f.__bufpos; + } + *f.__bufpos = 0; + } + return rv; +} + +#elif defined(__USE_OLD_VFPRINTF__) + +typedef struct { + FILE f; + unsigned char *bufend; /* pointer to 1 past end of buffer */ + unsigned char *bufpos; +} __FILE_vsnprintf; + +int vsnprintf(char *__restrict buf, size_t size, + const char * __restrict format, va_list arg) +{ + __FILE_vsnprintf f; + int rv; + + f.bufpos = buf; + + if (size > SIZE_MAX - (size_t) buf) { + size = SIZE_MAX - (size_t) buf; + } + f.bufend = buf + size; + +/* __STDIO_STREAM_RESET_GCS(&f.f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.f.__cookie = &(f.f.__filedes); + f.f.__gcs.read = NULL; + f.f.__gcs.write = NULL; + f.f.__gcs.seek = NULL; + f.f.__gcs.close = NULL; +#endif + + f.f.__filedes = __STDIO_STREAM_FAKE_VSNPRINTF_FILEDES_NB; + f.f.__modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING); + +#ifdef __UCLIBC_HAS_WCHAR__ + f.f.__ungot_width[0] = 0; +#endif /* __UCLIBC_HAS_WCHAR__ */ +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(f.f.__state)); +#endif /* __STDIO_MBSTATE */ + +#ifdef __UCLIBC_HAS_THREADS__ + f.f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.f.__lock); +#endif + f.f.__nextopen = NULL; + + rv = vfprintf((FILE *) &f, format, arg); + if (size) { + if (f.bufpos == f.bufend) { + --f.bufpos; + } + *f.bufpos = 0; + } + return rv; +} + +#elif defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) + +typedef struct { + size_t pos; + size_t len; + unsigned char *buf; + FILE *fp; +} __snpf_cookie; + +#define COOKIE ((__snpf_cookie *) cookie) + +static ssize_t snpf_write(register void *cookie, const char *buf, + size_t bufsize) +{ + size_t count; + register char *p; + + /* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */ + + if (COOKIE->len > COOKIE->pos) { + count = COOKIE->len - COOKIE->pos - 1; /* Leave space for nul. */ + if (count > bufsize) { + count = bufsize; + } + + p = COOKIE->buf + COOKIE->pos; + while (count) { + *p++ = *buf++; + --count; + } + *p = 0; + } + + COOKIE->pos += bufsize; + + return bufsize; +} + +#undef COOKIE + +int vsnprintf(char *__restrict buf, size_t size, + const char * __restrict format, va_list arg) +{ + FILE f; + __snpf_cookie cookie; + int rv; + + cookie.buf = buf; + cookie.len = size; + cookie.pos = 0; + cookie.fp = &f; + + f.__cookie = &cookie; + f.__gcs.write = snpf_write; + f.__gcs.read = NULL; + f.__gcs.seek = NULL; + f.__gcs.close = NULL; + + f.__filedes = -1; /* For debugging. */ + f.__modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING); + +#ifdef __UCLIBC_HAS_WCHAR__ + f.__ungot_width[0] = 0; +#endif /* __UCLIBC_HAS_WCHAR__ */ +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(f.__state)); +#endif /* __STDIO_MBSTATE */ + +#ifdef __UCLIBC_HAS_THREADS__ + f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.__lock); +#endif + f.__nextopen = NULL; + + rv = vfprintf(&f, format, arg); + + return rv; +} + +#else +#warning Skipping vsnprintf since no buffering, no custom streams, and not old vfprintf! +#ifdef __STDIO_HAS_VSNPRINTF +#error WHOA! __STDIO_HAS_VSNPRINTF is defined! +#endif +#endif diff --git a/libc/stdio/vsprintf.c b/libc/stdio/vsprintf.c new file mode 100644 index 000000000..81d3961ad --- /dev/null +++ b/libc/stdio/vsprintf.c @@ -0,0 +1,21 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> + +#ifndef __STDIO_HAS_VSNPRINTF +#warning Skipping vsprintf since no vsnprintf! +#else + +int vsprintf(char *__restrict buf, const char * __restrict format, + va_list arg) +{ + return vsnprintf(buf, SIZE_MAX, format, arg); +} + +#endif diff --git a/libc/stdio/vswprintf.c b/libc/stdio/vswprintf.c new file mode 100644 index 000000000..a5839b7bd --- /dev/null +++ b/libc/stdio/vswprintf.c @@ -0,0 +1,70 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> +#include <wchar.h> + +#ifndef __STDIO_BUFFERS +#warning Skipping vswprintf since no buffering! +#else /* __STDIO_BUFFERS */ + +int vswprintf(wchar_t *__restrict buf, size_t size, + const wchar_t * __restrict format, va_list arg) +{ + FILE f; + int rv; + +/* __STDIO_STREAM_RESET_GCS(&f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.__cookie = &(f.__filedes); + f.__gcs.read = NULL; + f.__gcs.write = NULL; + f.__gcs.seek = NULL; + f.__gcs.close = NULL; +#endif + + f.__filedes = __STDIO_STREAM_FAKE_VSWPRINTF_FILEDES; + f.__modeflags = (__FLAG_WIDE|__FLAG_WRITEONLY|__FLAG_WRITING); + + f.__ungot_width[0] = 0; +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(f.__state)); +#endif /* __STDIO_MBSTATE */ + +#ifdef __UCLIBC_HAS_THREADS__ + f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.__lock); +#endif + f.__nextopen = NULL; + + if (size > ((SIZE_MAX - (size_t) buf)/sizeof(wchar_t))) { + size = ((SIZE_MAX - (size_t) buf)/sizeof(wchar_t)); + } + + f.__bufstart = (char *) buf; + f.__bufend = (char *)(buf + size); + __STDIO_STREAM_INIT_BUFREAD_BUFPOS(&f); + __STDIO_STREAM_DISABLE_GETC(&f); + __STDIO_STREAM_DISABLE_PUTC(&f); + + rv = vfwprintf(&f, format, arg); + + /* NOTE: Return behaviour differs from snprintf... */ + if (f.__bufpos == f.__bufend) { + rv = -1; + if (size) { + f.__bufpos = (char *)(((wchar_t *) f.__bufpos) - 1); + } + } + if (size) { + *((wchar_t *) f.__bufpos) = 0; + } + return rv; +} + +#endif /* __STDIO_BUFFERS */ diff --git a/libc/stdio/vwprintf.c b/libc/stdio/vwprintf.c new file mode 100644 index 000000000..2ad6bbdfd --- /dev/null +++ b/libc/stdio/vwprintf.c @@ -0,0 +1,15 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> +#include <wchar.h> + +int vwprintf(const wchar_t * __restrict format, va_list arg) +{ + return vfwprintf(stdout, format, arg); +} diff --git a/libc/stdio/wprintf.c b/libc/stdio/wprintf.c new file mode 100644 index 000000000..c64fc086a --- /dev/null +++ b/libc/stdio/wprintf.c @@ -0,0 +1,23 @@ +/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> + * + * GNU Library General Public License (LGPL) version 2 or later. + * + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. + */ + +#include "_stdio.h" +#include <stdarg.h> +#include <wchar.h> + +int wprintf(const wchar_t * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vfwprintf(stdout, format, arg); + va_end(arg); + + return rv; +} + |