From 082e680bd54e999f2bb4eb77141958938b1e9ee9 Mon Sep 17 00:00:00 2001 From: Manuel Novoa III <mjn3@codepoet.org> Date: Wed, 11 Feb 2004 23:48:50 +0000 Subject: New stdio core. Should be more maintainable. Fixes a couple of bugs. Codepaths streamlined. Improved performance for nonthreaded apps when linked with a thread-enabled libc. Minor iconv bug and some locale/thread related startup issues fixed. These showed up in getting a gcj-compiled java helloworld app running. Removed some old extension functions... _stdio_fdout and _stdio_fsfopen. --- libc/stdio/Makefile | 144 +- libc/stdio/_READ.c | 66 + libc/stdio/_WRITE.c | 100 ++ libc/stdio/__fbufsize.c | 20 + libc/stdio/__flbf.c | 20 + libc/stdio/__fpending.c | 35 + libc/stdio/__fpurge.c | 34 + libc/stdio/__freadable.c | 20 + libc/stdio/__freading.c | 20 + libc/stdio/__fsetlocking.c | 45 + libc/stdio/__fwritable.c | 20 + libc/stdio/__fwriting.c | 20 + libc/stdio/_adjust_pos.c | 68 + libc/stdio/_cs_funcs.c | 67 + libc/stdio/_flushlbf.c | 18 + libc/stdio/_fopen.c | 207 +++ libc/stdio/_fpmaxtostr.c | 738 +++++++++ libc/stdio/_fwrite.c | 78 + libc/stdio/_load_inttype.c | 66 + libc/stdio/_rfill.c | 45 + libc/stdio/_stdio.c | 432 +++++ libc/stdio/_stdio.h | 436 +++++ libc/stdio/_store_inttype.c | 57 + libc/stdio/_trans2r.c | 75 + libc/stdio/_trans2w.c | 89 ++ libc/stdio/_uintmaxtostr.c | 151 ++ libc/stdio/_wcommit.c | 31 + libc/stdio/_wfwrite.c | 75 + libc/stdio/asprintf.c | 29 + libc/stdio/clearerr.c | 39 + libc/stdio/ctermid.c | 54 +- libc/stdio/dprintf.c | 21 + libc/stdio/fclose.c | 86 + libc/stdio/fcloseall.c | 40 + libc/stdio/fdopen.c | 17 + libc/stdio/feof.c | 42 + libc/stdio/ferror.c | 42 + libc/stdio/fflush.c | 161 ++ libc/stdio/fgetc.c | 98 ++ libc/stdio/fgetpos.c | 44 + libc/stdio/fgets.c | 84 + libc/stdio/fgetwc.c | 134 ++ libc/stdio/fgetws.c | 61 + libc/stdio/fileno.c | 45 + libc/stdio/flockfile.c | 16 + libc/stdio/fmemopen.c | 176 +++ libc/stdio/fopen.c | 24 + libc/stdio/fopencookie.c | 59 + libc/stdio/fprintf.c | 21 + libc/stdio/fputc.c | 96 ++ libc/stdio/fputs.c | 46 + libc/stdio/fputwc.c | 42 + libc/stdio/fputws.c | 44 + libc/stdio/fread.c | 108 ++ libc/stdio/freopen.c | 66 + libc/stdio/fseeko.c | 89 ++ libc/stdio/fsetpos.c | 44 + libc/stdio/ftello.c | 61 + libc/stdio/ftrylockfile.c | 19 + libc/stdio/funlockfile.c | 15 + libc/stdio/fwide.c | 32 + libc/stdio/fwprintf.c | 22 + libc/stdio/fwrite.c | 59 + libc/stdio/getchar.c | 44 + libc/stdio/getdelim.c | 124 +- libc/stdio/getline.c | 34 +- libc/stdio/gets.c | 36 + libc/stdio/getw.c | 18 + libc/stdio/getwchar.c | 31 + libc/stdio/old_vfprintf.c | 54 +- libc/stdio/open_memstream.c | 162 ++ libc/stdio/perror.c | 36 + libc/stdio/popen.c | 21 +- libc/stdio/printf.c | 3267 +------------------------------------- libc/stdio/putchar.c | 44 + libc/stdio/puts.c | 33 + libc/stdio/putw.c | 28 + libc/stdio/putwchar.c | 31 + libc/stdio/remove.c | 29 + libc/stdio/rewind.c | 20 + libc/stdio/scanf.c | 227 ++- libc/stdio/setbuf.c | 15 + libc/stdio/setbuffer.c | 21 + libc/stdio/setlinebuf.c | 20 + libc/stdio/setvbuf.c | 106 ++ libc/stdio/snprintf.c | 27 + libc/stdio/sprintf.c | 27 + libc/stdio/stdio.c | 3677 ------------------------------------------- libc/stdio/swprintf.c | 29 + libc/stdio/ungetc.c | 77 + libc/stdio/ungetwc.c | 48 + libc/stdio/vasprintf.c | 75 + libc/stdio/vdprintf.c | 62 + libc/stdio/vfprintf.c | 1901 ++++++++++++++++++++++ libc/stdio/vprintf.c | 14 + libc/stdio/vsnprintf.c | 210 +++ libc/stdio/vsprintf.c | 21 + libc/stdio/vswprintf.c | 70 + libc/stdio/vwprintf.c | 15 + libc/stdio/wprintf.c | 23 + 100 files changed, 8661 insertions(+), 7229 deletions(-) create mode 100644 libc/stdio/_READ.c create mode 100644 libc/stdio/_WRITE.c create mode 100644 libc/stdio/__fbufsize.c create mode 100644 libc/stdio/__flbf.c create mode 100644 libc/stdio/__fpending.c create mode 100644 libc/stdio/__fpurge.c create mode 100644 libc/stdio/__freadable.c create mode 100644 libc/stdio/__freading.c create mode 100644 libc/stdio/__fsetlocking.c create mode 100644 libc/stdio/__fwritable.c create mode 100644 libc/stdio/__fwriting.c create mode 100644 libc/stdio/_adjust_pos.c create mode 100644 libc/stdio/_cs_funcs.c create mode 100644 libc/stdio/_flushlbf.c create mode 100644 libc/stdio/_fopen.c create mode 100644 libc/stdio/_fpmaxtostr.c create mode 100644 libc/stdio/_fwrite.c create mode 100644 libc/stdio/_load_inttype.c create mode 100644 libc/stdio/_rfill.c create mode 100644 libc/stdio/_stdio.c create mode 100644 libc/stdio/_stdio.h create mode 100644 libc/stdio/_store_inttype.c create mode 100644 libc/stdio/_trans2r.c create mode 100644 libc/stdio/_trans2w.c create mode 100644 libc/stdio/_uintmaxtostr.c create mode 100644 libc/stdio/_wcommit.c create mode 100644 libc/stdio/_wfwrite.c create mode 100644 libc/stdio/asprintf.c create mode 100644 libc/stdio/clearerr.c create mode 100644 libc/stdio/dprintf.c create mode 100644 libc/stdio/fclose.c create mode 100644 libc/stdio/fcloseall.c create mode 100644 libc/stdio/fdopen.c create mode 100644 libc/stdio/feof.c create mode 100644 libc/stdio/ferror.c create mode 100644 libc/stdio/fflush.c create mode 100644 libc/stdio/fgetc.c create mode 100644 libc/stdio/fgetpos.c create mode 100644 libc/stdio/fgets.c create mode 100644 libc/stdio/fgetwc.c create mode 100644 libc/stdio/fgetws.c create mode 100644 libc/stdio/fileno.c create mode 100644 libc/stdio/flockfile.c create mode 100644 libc/stdio/fmemopen.c create mode 100644 libc/stdio/fopen.c create mode 100644 libc/stdio/fopencookie.c create mode 100644 libc/stdio/fprintf.c create mode 100644 libc/stdio/fputc.c create mode 100644 libc/stdio/fputs.c create mode 100644 libc/stdio/fputwc.c create mode 100644 libc/stdio/fputws.c create mode 100644 libc/stdio/fread.c create mode 100644 libc/stdio/freopen.c create mode 100644 libc/stdio/fseeko.c create mode 100644 libc/stdio/fsetpos.c create mode 100644 libc/stdio/ftello.c create mode 100644 libc/stdio/ftrylockfile.c create mode 100644 libc/stdio/funlockfile.c create mode 100644 libc/stdio/fwide.c create mode 100644 libc/stdio/fwprintf.c create mode 100644 libc/stdio/fwrite.c create mode 100644 libc/stdio/getchar.c create mode 100644 libc/stdio/gets.c create mode 100644 libc/stdio/getw.c create mode 100644 libc/stdio/getwchar.c create mode 100644 libc/stdio/open_memstream.c create mode 100644 libc/stdio/perror.c create mode 100644 libc/stdio/putchar.c create mode 100644 libc/stdio/puts.c create mode 100644 libc/stdio/putw.c create mode 100644 libc/stdio/putwchar.c create mode 100644 libc/stdio/remove.c create mode 100644 libc/stdio/rewind.c create mode 100644 libc/stdio/setbuf.c create mode 100644 libc/stdio/setbuffer.c create mode 100644 libc/stdio/setlinebuf.c create mode 100644 libc/stdio/setvbuf.c create mode 100644 libc/stdio/snprintf.c create mode 100644 libc/stdio/sprintf.c delete mode 100644 libc/stdio/stdio.c create mode 100644 libc/stdio/swprintf.c create mode 100644 libc/stdio/ungetc.c create mode 100644 libc/stdio/ungetwc.c create mode 100644 libc/stdio/vasprintf.c create mode 100644 libc/stdio/vdprintf.c create mode 100644 libc/stdio/vfprintf.c create mode 100644 libc/stdio/vprintf.c create mode 100644 libc/stdio/vsnprintf.c create mode 100644 libc/stdio/vsprintf.c create mode 100644 libc/stdio/vswprintf.c create mode 100644 libc/stdio/vwprintf.c create mode 100644 libc/stdio/wprintf.c (limited to 'libc/stdio') 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_BUFFER