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/inet/addr.c | 3 +- libc/misc/assert/__assert.c | 33 +- libc/misc/locale/locale.c | 4 +- libc/misc/time/time.c | 2 +- libc/misc/wchar/Makefile | 18 +- libc/misc/wchar/wchar.c | 41 +- libc/misc/wchar/wstdio.c | 14 +- libc/pwd_grp/pwd_grp.c | 15 +- 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 + libc/stdlib/abort.c | 8 - libc/stdlib/ptsname.c | 3 +- libc/stdlib/stdlib.c | 10 +- libc/string/wstring.c | 9 +- libc/sysdeps/linux/common/bits/uClibc_locale.h | 1 + libc/sysdeps/linux/common/bits/uClibc_stdio.h | 716 ++-- .../linux/common/bits/uClibc_uintmaxtostr.h | 115 + 115 files changed, 9165 insertions(+), 7717 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 create mode 100644 libc/sysdeps/linux/common/bits/uClibc_uintmaxtostr.h (limited to 'libc') diff --git a/libc/inet/addr.c b/libc/inet/addr.c index a75916e69..75ebc191e 100644 --- a/libc/inet/addr.c +++ b/libc/inet/addr.c @@ -16,13 +16,14 @@ * Changed to use _int10tostr. */ +#define _GNU_SOURCE #define __FORCE_GLIBC #include <features.h> -#define _STDIO_UTILITY /* For _int10tostr. */ #include <stdio.h> #include <string.h> #include <ctype.h> #include <netinet/in.h> +#include <bits/uClibc_uintmaxtostr.h> int inet_aton(const char *cp, struct in_addr *addrptr); diff --git a/libc/misc/assert/__assert.c b/libc/misc/assert/__assert.c index 74b5ee86f..2949b75f0 100644 --- a/libc/misc/assert/__assert.c +++ b/libc/misc/assert/__assert.c @@ -27,10 +27,11 @@ * and is useful in debugging the stdio code. */ -#define _STDIO_UTILITY /* For _stdio_fdout and _int10tostr. */ +#define _ISOC99_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> +#include <bits/uClibc_uintmaxtostr.h> /* Get the prototype from assert.h as a double-check. */ #undef NDEBUG @@ -44,8 +45,6 @@ extern const char *__progname; #endif -#if 1 - static int in_assert; /* bss inits to 0. */ void __assert(const char *assertion, const char * filename, @@ -69,31 +68,3 @@ void __assert(const char *assertion, const char * filename, } abort(); } - -#else - -void __assert(const char *assertion, const char * filename, - int linenumber, register const char * function) -{ - char buf[__BUFLEN_INT10TOSTR]; - - _stdio_fdout(STDERR_FILENO, -#ifdef ASSERT_SHOW_PROGNAME - __progname, - ": ", -#endif - filename, - ":", - _int10tostr(buf+sizeof(buf)-1, linenumber), - ": ", - /* Function name isn't available with some compilers. */ - ((function == NULL) ? "?function?" : function), - ": Assertion `", - assertion, - "' failed.\n", - NULL - ); - abort(); -} - -#endif diff --git a/libc/misc/locale/locale.c b/libc/misc/locale/locale.c index cf058ce3e..c6a1a2dd0 100644 --- a/libc/misc/locale/locale.c +++ b/libc/misc/locale/locale.c @@ -346,7 +346,7 @@ struct lconv *localeconv(void) /**********************************************************************/ #if defined(L__locale_init) && !defined(__LOCALE_C_ONLY) -static __uclibc_locale_t __global_locale_data; +__uclibc_locale_t __global_locale_data; __locale_t __global_locale = &__global_locale_data; @@ -1420,7 +1420,7 @@ int __locale_mbrtowc_l(wchar_t *__restrict dst, mbstate_t ps; const char *p = src; size_t r; - ps.mask = 0; + ps.__mask = 0; r = _wchar_utf8sntowcs(dst, 1, &p, SIZE_MAX, &ps, 1); return (r == 1) ? (p-src) : r; /* Need to return 0 if nul char. */ } diff --git a/libc/misc/time/time.c b/libc/misc/time/time.c index e2f1f6c6d..8176e071a 100644 --- a/libc/misc/time/time.c +++ b/libc/misc/time/time.c @@ -135,7 +135,6 @@ */ #define _GNU_SOURCE -#define _STDIO_UTILITY #include <stdio.h> #include <stdlib.h> #include <stddef.h> @@ -147,6 +146,7 @@ #include <ctype.h> #include <langinfo.h> #include <locale.h> +#include <bits/uClibc_uintmaxtostr.h> #ifdef __UCLIBC_HAS_XLOCALE__ #include <xlocale.h> diff --git a/libc/misc/wchar/Makefile b/libc/misc/wchar/Makefile index a96dae2b4..b1db37293 100644 --- a/libc/misc/wchar/Makefile +++ b/libc/misc/wchar/Makefile @@ -33,17 +33,17 @@ ifeq ($(UCLIBC_HAS_LOCALE),y) MOBJ1 += iconv.o endif -MSRC2= wstdio.c -MOBJ2= fwide.o \ - fgetwc.o getwchar.o fgetws.o \ - fputwc.o putwchar.o fputws.o \ - ungetwc.o -# getwc (fgetwc alias) getwc_unlocked (fgetwc_unlocked alias) -# putwc (fputwc alias) putwc_unlocked (fputwc_unlocked alias) - +# The stdio and time related wide functions are now built in the normal +# directories. +# +# stdio: +# fwide fgetwc getwchar fgetws fputwc putwchar fputws ungetwc +# getwc (fgetwc alias) getwc_unlocked (fgetwc_unlocked alias) +# putwc (fputwc alias) putwc_unlocked (fputwc_unlocked alias) +# time: # wcsftime -OBJS=$(MOBJ1) $(MOBJ2) +OBJS=$(MOBJ1) all: $(OBJS) $(LIBC) diff --git a/libc/misc/wchar/wchar.c b/libc/misc/wchar/wchar.c index 4fc96f430..d1383c99f 100644 --- a/libc/misc/wchar/wchar.c +++ b/libc/misc/wchar/wchar.c @@ -1,5 +1,5 @@ -/* Copyright (C) 2002 Manuel Novoa III +/* Copyright (C) 2002, 2003, 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 @@ -92,6 +92,9 @@ * Aug 18, 2003 * Bug fix: _wchar_utf8sntowcs and _wchar_wcsntoutf8s now set errno if EILSEQ. * + * Feb 11, 2004 + * Bug fix: Fix size check for remaining output space in iconv(). + * * Manuel */ @@ -193,7 +196,7 @@ wint_t btowc(int c) if (c != EOF) { *buf = (unsigned char) c; - mbstate.mask = 0; /* Initialize the mbstate. */ + mbstate.__mask = 0; /* Initialize the mbstate. */ if (mbrtowc(&wc, buf, 1, &mbstate) <= 1) { return wc; } @@ -251,7 +254,7 @@ int wctob(wint_t c) int mbsinit(const mbstate_t *ps) { - return !ps || !ps->mask; + return !ps || !ps->__mask; } #endif @@ -291,7 +294,8 @@ size_t mbrtowc(wchar_t *__restrict pwc, const char *__restrict s, s = empty_string; n = 1; } else if (!n) { - return (ps->mask && (ps->wc == 0xffffU)) /* TODO: change error code? */ + /* TODO: change error code? */ + return (ps->__mask && (ps->__wc == 0xffffU)) ? ((size_t) -1) : ((size_t) -2); } @@ -434,15 +438,15 @@ size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn, return 0; } - if ((mask = (__uwchar_t) ps->mask) != 0) { /* A continuation... */ + if ((mask = (__uwchar_t) ps->__mask) != 0) { /* A continuation... */ #ifdef DECODER - wc = (__uwchar_t) ps->wc; + wc = (__uwchar_t) ps->__wc; if (n) { goto CONTINUE; } goto DONE; #else - if ((wc = (__uwchar_t) ps->wc) != 0xffffU) { + if ((wc = (__uwchar_t) ps->__wc) != 0xffffU) { /* TODO: change error code here and below? */ if (n) { goto CONTINUE; @@ -472,8 +476,8 @@ size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn, wc = 0xfffdU; goto COMPLETE; #else - ps->mask = mask; - ps->wc = 0xffffU; + ps->__mask = mask; + ps->__wc = 0xffffU; __set_errno(EILSEQ); return (size_t) -1; /* Illegal start byte! */ #endif @@ -532,8 +536,8 @@ size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn, } while ((mask >>= 5) >= 0x40); goto DONE; } - ps->mask = (wchar_t) mask; - ps->wc = (wchar_t) wc; + ps->__mask = (wchar_t) mask; + ps->__wc = (wchar_t) wc; *src = s; return (size_t) -2; } @@ -552,8 +556,8 @@ size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn, #endif DONE: - /* ps->wc is irrelavent here. */ - ps->mask = 0; + /* ps->__wc is irrelavent here. */ + ps->__mask = 0; if (pwc != wcbuf) { *src = s; } @@ -1037,7 +1041,7 @@ int wcswidth(const wchar_t *pwcs, size_t n) else if (ENCODING == __ctype_encoding_8_bit) { mbstate_t mbstate; - mbstate.mask = 0; /* Initialize the mbstate. */ + mbstate.__mask = 0; /* Initialize the mbstate. */ if (__wcsnrtombs(NULL, &pwcs, n, SIZE_MAX, &mbstate) == ((size_t) - 1)) { return -1; } @@ -1282,7 +1286,8 @@ iconv_t weak_function iconv_open(const char *tocode, const char *fromcode) px->tobom0 = px->tobom = (tocodeset & 0x10) >> 4; px->fromcodeset0 = px->fromcodeset = fromcodeset; px->frombom0 = px->frombom = (fromcodeset & 0x10) >> 4; - px->skip_invalid_input = px->tostate.mask = px->fromstate.mask = 0; + px->skip_invalid_input = px->tostate.__mask + = px->fromstate.__mask = 0; return (iconv_t) px; } } else { @@ -1316,7 +1321,7 @@ size_t weak_function iconv(iconv_t cd, char **__restrict inbuf, * shift sequence to return to initial state! */ if ((px->fromcodeset & 0xf0) == 0xe0) { } - px->tostate.mask = px->fromstate.mask = 0; + px->tostate.__mask = px->fromstate.__mask = 0; px->fromcodeset = px->fromcodeset0; px->tobom = px->tobom0; px->frombom = px->frombom0; @@ -1398,7 +1403,7 @@ size_t weak_function iconv(iconv_t cd, char **__restrict inbuf, INVALID: __set_errno(EINVAL); } else { - px->fromstate.mask = 0; + px->fromstate.__mask = 0; inci = 1; ILLEGAL: if (px->skip_invalid_input) { @@ -1444,7 +1449,7 @@ size_t weak_function iconv(iconv_t cd, char **__restrict inbuf, if (px->tocodeset >= IC_MULTIBYTE) { inco = (px->tocodeset == IC_WCHAR_T) ? 4: (px->tocodeset & 6); - if (*outbytesleft < inci) goto TOO_BIG; + if (*outbytesleft < inco) goto TOO_BIG; if (px->tocodeset != IC_WCHAR_T) { if (((__uwchar_t) wc) > (((px->tocodeset & IC_UCS_4) == IC_UCS_4) ? 0x7fffffffUL : 0x10ffffUL) diff --git a/libc/misc/wchar/wstdio.c b/libc/misc/wchar/wstdio.c index e984bf837..b49494f35 100644 --- a/libc/misc/wchar/wstdio.c +++ b/libc/misc/wchar/wstdio.c @@ -57,7 +57,7 @@ #include <errno.h> #include <assert.h> -#ifndef __STDIO_THREADSAFE +#ifndef __UCLIBC_HAS_THREADS__ #ifdef __BCC__ #define UNLOCKED_STREAM(RETURNTYPE,NAME,PARAMS,ARGS,STREAM) \ @@ -85,7 +85,7 @@ void NAME PARAMS #define __STDIO_THREADLOCK_OPENLIST #define __STDIO_THREADUNLOCK_OPENLIST -#else /* __STDIO_THREADSAFE */ +#else /* __UCLIBC_HAS_THREADS__ */ #include <pthread.h> @@ -121,7 +121,7 @@ void NAME##_unlocked PARAMS #define __STDIO_THREADTRYLOCK_OPENLIST \ __pthread_mutex_trylock(&_stdio_openlist_lock) -#endif /* __STDIO_THREADSAFE */ +#endif /* __UCLIBC_HAS_THREADS__ */ #ifndef __STDIO_BUFFERS #error stdio buffers are currently required for wide i/o @@ -404,9 +404,9 @@ wint_t ungetwc(wint_t c, register FILE *stream) /* If can't read or c == WEOF or ungot slots already filled, then fail. */ if ((stream->modeflags & (__MASK_UNGOT2|__FLAG_WRITEONLY -#ifndef __STDIO_AUTO_RW_TRANSITION +#ifndef __UCLIBC_HAS_STDIO_AUTO_RW_TRANSITION__ |__FLAG_WRITING /* Note: technically no, but yes in spirit */ -#endif /* __STDIO_AUTO_RW_TRANSITION */ +#endif /* __UCLIBC_HAS_STDIO_AUTO_RW_TRANSITION__ */ )) || ((stream->modeflags & __MASK_UNGOT1) && (stream->ungot[1])) || (c == WEOF) ) { @@ -417,11 +417,11 @@ wint_t ungetwc(wint_t c, register FILE *stream) /* ungot_width */ #ifdef __STDIO_BUFFERS -#ifdef __STDIO_AUTO_RW_TRANSITION +#ifdef __UCLIBC_HAS_STDIO_AUTO_RW_TRANSITION__ if (stream->modeflags & __FLAG_WRITING) { fflush_unlocked(stream); /* Commit any write-buffered chars. */ } -#endif /* __STDIO_AUTO_RW_TRANSITION */ +#endif /* __UCLIBC_HAS_STDIO_AUTO_RW_TRANSITION__ */ #endif /* __STDIO_BUFFERS */ /* Clear EOF and WRITING flags, and set READING FLAG */ diff --git a/libc/pwd_grp/pwd_grp.c b/libc/pwd_grp/pwd_grp.c index d3047e760..90b4eafd6 100644 --- a/libc/pwd_grp/pwd_grp.c +++ b/libc/pwd_grp/pwd_grp.c @@ -777,11 +777,12 @@ int putgrent(const struct group *__restrict p, FILE *__restrict f) char **m; const char *fmt; int rv = -1; + __STDIO_AUTO_THREADLOCK_VAR; if (!p || !f) { /* Sigh... glibc checks. */ __set_errno(EINVAL); } else { - __STDIO_THREADLOCK(f); + __STDIO_AUTO_THREADLOCK(f); if (fprintf(f, "%s:%s:%lu:", p->gr_name, p->gr_passwd, @@ -809,7 +810,7 @@ int putgrent(const struct group *__restrict p, FILE *__restrict f) } - __STDIO_THREADUNLOCK(f); + __STDIO_AUTO_THREADUNLOCK(f); } return rv; @@ -835,10 +836,11 @@ int putspent(const struct spwd *p, FILE *stream) long int x; int i; int rv = -1; + __STDIO_AUTO_THREADLOCK_VAR; /* Unlike putpwent and putgrent, glibc does not check the args. */ - __STDIO_THREADLOCK(stream); + __STDIO_AUTO_THREADLOCK(stream); if (fprintf(stream, "%s:%s:", p->sp_namp, (p->sp_pwdp ? p->sp_pwdp : "")) < 0 @@ -865,7 +867,7 @@ int putspent(const struct spwd *p, FILE *stream) } DO_UNLOCK: - __STDIO_THREADUNLOCK(stream); + __STDIO_AUTO_THREADUNLOCK(stream); return rv; } @@ -1117,11 +1119,12 @@ int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data, int line_len; int skip; int rv = ERANGE; + __STDIO_AUTO_THREADLOCK_VAR; if (buflen < PWD_BUFFER_SIZE) { __set_errno(rv); } else { - __STDIO_THREADLOCK(f); + __STDIO_AUTO_THREADLOCK(f); skip = 0; do { @@ -1165,7 +1168,7 @@ int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data, } } while (1); - __STDIO_THREADUNLOCK(f); + __STDIO_AUTO_THREADUNLOCK(f); } return rv; 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) * up