diff options
author | Roland McGrath <roland@gnu.org> | 1995-10-17 00:41:39 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 1995-10-17 00:41:39 +0000 |
commit | deab9deadc372fe1a367aef2e78c0d8f2885bf23 (patch) | |
tree | 367eaf76a86934f3883f950da5719ca581185ca5 /stdio-common | |
parent | 10fd03c6014206b3775be893727f282c4c709ebf (diff) | |
download | glibc-deab9deadc372fe1a367aef2e78c0d8f2885bf23.tar.gz |
* Makefile (subdirs): Replace stdio with stdio-common and $(stdio).
* configure.in: Grok arg --enable-libio.
($stdio = libio): Define USE_IN_LIBIO.
* config.h.in (USE_IN_LIBIO): Add #undef.
* config.make.in (stdio): New variable, set by configure.
* Makeconfig (stdio): New variable.
* stdio.h [USE_IN_LIBIO]: Include libio/stdio.h instead of
stdio/stdio.h.
* stdio-common/Makefile: New file.
* stdio/Makefile: Half the contents moved to stdio-common/Makefile.
* stdio/_itoa.c: Moved to stdio-common.
* stdio/_itoa.h: Moved to stdio-common.
* stdio/asprintf.c: Moved to stdio-common.
* stdio/bug1.c: Moved to stdio-common.
* stdio/bug1.input: Moved to stdio-common.
* stdio/bug2.c: Moved to stdio-common.
* stdio/bug3.c: Moved to stdio-common.
* stdio/bug4.c: Moved to stdio-common.
* stdio/bug5.c: Moved to stdio-common.
* stdio/bug6.c: Moved to stdio-common.
* stdio/bug6.input: Moved to stdio-common.
* stdio/bug7.c: Moved to stdio-common.
* stdio/dprintf.c: Moved to stdio-common.
* stdio/errnobug.c: Moved to stdio-common.
* stdio/getline.c: Moved to stdio-common.
* stdio/getw.c: Moved to stdio-common.
* stdio/perror.c: Moved to stdio-common.
* stdio/printf-parse.h: Moved to stdio-common.
* stdio/printf-prs.c: Moved to stdio-common.
* stdio/printf.c: Moved to stdio-common.
* stdio/printf.h: Moved to stdio-common.
* stdio/printf_fp.c: Moved to stdio-common.
* stdio/psignal.c: Moved to stdio-common.
* stdio/putw.c: Moved to stdio-common.
* stdio/reg-printf.c: Moved to stdio-common.
* stdio/scanf.c: Moved to stdio-common.
* stdio/snprintf.c: Moved to stdio-common.
* stdio/sprintf.c: Moved to stdio-common.
* stdio/sscanf.c: Moved to stdio-common.
* stdio/tempnam.c: Moved to stdio-common.
* stdio/temptest.c: Moved to stdio-common.
* stdio/test-fseek.c: Moved to stdio-common.
* stdio/test-fwrite.c: Moved to stdio-common.
* stdio/test-popen.c: Moved to stdio-common.
* stdio/test_rdwr.c: Moved to stdio-common.
* stdio/tmpfile.c: Moved to stdio-common.
* stdio/tmpnam.c: Moved to stdio-common.
* stdio/tst-fileno.c: Moved to stdio-common.
* stdio/tst-printf.c: Moved to stdio-common.
* stdio/tstgetln.c: Moved to stdio-common.
* stdio/tstgetln.input: Moved to stdio-common.
* stdio/tstscanf.c: Moved to stdio-common.
* stdio/tstscanf.input: Moved to stdio-common.
* stdio/vfprintf.c: Moved to stdio-common.
* stdio/vfscanf.c: Moved to stdio-common.
* stdio/vprintf.c: Moved to stdio-common.
* stdio/xbug.c: Moved to stdio-common.
* sysdeps/generic/Makefile (siglist.c rules): Do this in subdir
stdio-common instead of stdio.
* sysdeps/unix/Makefile (errlist.c rules): Likewise.
* stdio-common/asprintf.c [USE_IN_LIBIO]: Call libio primitive
function.
* stdio-common/dprintf.c: Likewise.
* stdio-common/printf.c: Likewise.
* stdio-common/scanf.c: Likewise.
* stdio-common/snprintf.c: Likewise.
* stdio-common/sprintf.c: Likewise.
* stdio-common/sscanf.c: Likewise.
* stdio-common/vprintf.c: Likewise.
* Makerules: Include $(+depfiles) directly instead of generating
depend-$(subdir).
(depend-$(subdir)): Target removed.
(common-clean): Don't remove depend-$(subdir).
Diffstat (limited to 'stdio-common')
56 files changed, 5918 insertions, 0 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile new file mode 100644 index 0000000000..6ca6c7d1d3 --- /dev/null +++ b/stdio-common/Makefile @@ -0,0 +1,47 @@ +# Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. +# This file is part of the GNU C Library. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. + +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public +# License along with the GNU C Library; see the file COPYING.LIB. If +# not, write to the Free Software Foundation, Inc., 675 Mass Ave, +# Cambridge, MA 02139, USA. + +# +# Specific makefile for stdio-common. +# +subdir := stdio-common + +headers := stdio_lim.h printf.h + +routines := \ + ctermid cuserid \ + vfprintf vprintf printf_fp reg-printf printf-prs _itoa \ + vsnprintf vsprintf vasprintf \ + fprintf printf snprintf sprintf asprintf \ + dprintf vdprintf \ + vfscanf vscanf vsscanf \ + fscanf scanf sscanf \ + perror psignal \ + tmpfile tmpnam tempnam tempname \ + getline getw putw \ + remove rename +aux := errlist siglist +distribute := _itoa.h printf-parse.h + +tests := tst-printf tstscanf test_rdwr test-popen tstgetln test-fseek \ + temptest tst-fileno test-fwrite \ + xbug errnobug \ + bug1 bug2 bug3 bug4 bug5 bug6 bug7 + + +include ../Rules diff --git a/stdio-common/_itoa.c b/stdio-common/_itoa.c new file mode 100644 index 0000000000..caa8179624 --- /dev/null +++ b/stdio-common/_itoa.c @@ -0,0 +1,418 @@ +/* Internal function for converting integers to ASCII. +Copyright (C) 1994, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. +Contributed by Torbjorn Granlund <tege@matematik.su.se> +and Ulrich Drepper <drepper@gnu.ai.mit.edu>. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <gmp-mparam.h> +#include "../stdlib/gmp.h" +#include "../stdlib/gmp-impl.h" +#include "../stdlib/longlong.h" + +#include "_itoa.h" + + +/* Canonize environment. For some architectures not all values might + be defined in the GMP header files. */ +#ifndef UMUL_TIME +# define UMUL_TIME 1 +#endif +#ifndef UDIV_TIME +# define UDIV_TIME 1 +#endif + +/* Control memory layout. */ +#ifdef PACK +# undef PACK +# define PACK __attribute__ ((packed)) +#else +# define PACK +#endif + + +/* Declare local types. */ +struct base_table_t +{ +#if (UDIV_TIME > 2 * UMUL_TIME) + mp_limb base_multiplier; +#endif + char flag; + char post_shift; +#if BITS_PER_MP_LIMB == 32 + struct + { + char normalization_steps; + char ndigits; + mp_limb base PACK; +#if UDIV_TIME > 2 * UMUL_TIME + mp_limb base_ninv PACK; +#endif + } big; +#endif +}; + +/* To reduce the memory needed we include some fields of the tables + only confitionally. */ +#if BITS_PER_MP_LIMB == 32 +# if UDIV_TIME > 2 * UMUL_TIME +# define SEL1(X) X, +# define SEL2(X) ,X +# else +# define SEL1(X) +# define SEL2(X) +# endif +#endif + + +/* Local variables. */ +static const struct base_table_t base_table[] = +{ +#if BITS_PER_MP_LIMB == 64 + /* 2 */ {0ul, 1, 1}, + /* 3 */ {0xaaaaaaaaaaaaaaabul, 0, 1}, + /* 4 */ {0ul, 1, 2}, + /* 5 */ {0xcccccccccccccccdul, 0, 2}, + /* 6 */ {0xaaaaaaaaaaaaaaabul, 0, 2}, + /* 7 */ {0x2492492492492493ul, 1, 3}, + /* 8 */ {0ul, 1, 3}, + /* 9 */ {0xe38e38e38e38e38ful, 0, 3}, + /* 10 */ {0xcccccccccccccccdul, 0, 3}, + /* 11 */ {0x2e8ba2e8ba2e8ba3ul, 0, 1}, + /* 12 */ {0xaaaaaaaaaaaaaaabul, 0, 3}, + /* 13 */ {0x4ec4ec4ec4ec4ec5ul, 0, 2}, + /* 14 */ {0x2492492492492493ul, 1, 4}, + /* 15 */ {0x8888888888888889ul, 0, 3}, + /* 16 */ {0ul, 1, 4}, + /* 17 */ {0xf0f0f0f0f0f0f0f1ul, 0, 4}, + /* 18 */ {0xe38e38e38e38e38ful, 0, 4}, + /* 19 */ {0xd79435e50d79435ful, 0, 4}, + /* 20 */ {0xcccccccccccccccdul, 0, 4}, + /* 21 */ {0x8618618618618619ul, 1, 5}, + /* 22 */ {0x2e8ba2e8ba2e8ba3ul, 0, 2}, + /* 23 */ {0x642c8590b21642c9ul, 1, 5}, + /* 24 */ {0xaaaaaaaaaaaaaaabul, 0, 4}, + /* 25 */ {0x47ae147ae147ae15ul, 1, 5}, + /* 26 */ {0x4ec4ec4ec4ec4ec5ul, 0, 3}, + /* 27 */ {0x97b425ed097b425ful, 0, 4}, + /* 28 */ {0x2492492492492493ul, 1, 5}, + /* 29 */ {0x1a7b9611a7b9611bul, 1, 5}, + /* 30 */ {0x8888888888888889ul, 0, 4}, + /* 31 */ {0x0842108421084211ul, 1, 5}, + /* 32 */ {0ul, 1, 5}, + /* 33 */ {0x0f83e0f83e0f83e1ul, 0, 1}, + /* 34 */ {0xf0f0f0f0f0f0f0f1ul, 0, 5}, + /* 35 */ {0xea0ea0ea0ea0ea0ful, 0, 5}, + /* 36 */ {0xe38e38e38e38e38ful, 0, 5} +#endif +#if BITS_PER_MP_LIMB == 32 + /* 2 */ {SEL1(0ul) 1, 1, {0, 31, 0x80000000ul SEL2(0xfffffffful)}}, + /* 3 */ {SEL1(0xaaaaaaabul) 0, 1, {0, 20, 0xcfd41b91ul SEL2(0x3b563c24ul)}}, + /* 4 */ {SEL1(0ul) 1, 2, {1, 15, 0x40000000ul SEL2(0xfffffffful)}}, + /* 5 */ {SEL1(0xcccccccdul) 0, 2, {1, 13, 0x48c27395ul SEL2(0xc25c2684ul)}}, + /* 6 */ {SEL1(0xaaaaaaabul) 0, 2, {0, 12, 0x81bf1000ul SEL2(0xf91bd1b6ul)}}, + /* 7 */ {SEL1(0x24924925ul) 1, 3, {1, 11, 0x75db9c97ul SEL2(0x1607a2cbul)}}, + /* 8 */ {SEL1(0ul) 1, 3, {1, 10, 0x40000000ul SEL2(0xfffffffful)}}, + /* 9 */ {SEL1(0x38e38e39ul) 0, 1, {0, 10, 0xcfd41b91ul SEL2(0x3b563c24ul)}}, + /* 10 */ {SEL1(0xcccccccdul) 0, 3, {2, 9, 0x3b9aca00ul SEL2(0x12e0be82ul)}}, + /* 11 */ {SEL1(0xba2e8ba3ul) 0, 3, {0, 9, 0x8c8b6d2bul SEL2(0xd24cde04ul)}}, + /* 12 */ {SEL1(0xaaaaaaabul) 0, 3, {3, 8, 0x19a10000ul SEL2(0x3fa39ab5ul)}}, + /* 13 */ {SEL1(0x4ec4ec4ful) 0, 2, {2, 8, 0x309f1021ul SEL2(0x50f8ac5ful)}}, + /* 14 */ {SEL1(0x24924925ul) 1, 4, {1, 8, 0x57f6c100ul SEL2(0x74843b1eul)}}, + /* 15 */ {SEL1(0x88888889ul) 0, 3, {0, 8, 0x98c29b81ul SEL2(0xad0326c2ul)}}, + /* 16 */ {SEL1(0ul) 1, 4, {3, 7, 0x10000000ul SEL2(0xfffffffful)}}, + /* 17 */ {SEL1(0xf0f0f0f1ul) 0, 4, {3, 7, 0x18754571ul SEL2(0x4ef0b6bdul)}}, + /* 18 */ {SEL1(0x38e38e39ul) 0, 2, {2, 7, 0x247dbc80ul SEL2(0xc0fc48a1ul)}}, + /* 19 */ {SEL1(0xaf286bcbul) 1, 5, {2, 7, 0x3547667bul SEL2(0x33838942ul)}}, + /* 20 */ {SEL1(0xcccccccdul) 0, 4, {1, 7, 0x4c4b4000ul SEL2(0xad7f29abul)}}, + /* 21 */ {SEL1(0x86186187ul) 1, 5, {1, 7, 0x6b5a6e1dul SEL2(0x313c3d15ul)}}, + /* 22 */ {SEL1(0xba2e8ba3ul) 0, 4, {0, 7, 0x94ace180ul SEL2(0xb8cca9e0ul)}}, + /* 23 */ {SEL1(0xb21642c9ul) 0, 4, {0, 7, 0xcaf18367ul SEL2(0x42ed6de9ul)}}, + /* 24 */ {SEL1(0xaaaaaaabul) 0, 4, {4, 6, 0x0b640000ul SEL2(0x67980e0bul)}}, + /* 25 */ {SEL1(0x51eb851ful) 0, 3, {4, 6, 0x0e8d4a51ul SEL2(0x19799812ul)}}, + /* 26 */ {SEL1(0x4ec4ec4ful) 0, 3, {3, 6, 0x1269ae40ul SEL2(0xbce85396ul)}}, + /* 27 */ {SEL1(0x2f684bdbul) 1, 5, {3, 6, 0x17179149ul SEL2(0x62c103a9ul)}}, + /* 28 */ {SEL1(0x24924925ul) 1, 5, {3, 6, 0x1cb91000ul SEL2(0x1d353d43ul)}}, + /* 29 */ {SEL1(0x8d3dcb09ul) 0, 4, {2, 6, 0x23744899ul SEL2(0xce1deceaul)}}, + /* 30 */ {SEL1(0x88888889ul) 0, 4, {2, 6, 0x2b73a840ul SEL2(0x790fc511ul)}}, + /* 31 */ {SEL1(0x08421085ul) 1, 5, {2, 6, 0x34e63b41ul SEL2(0x35b865a0ul)}}, + /* 32 */ {SEL1(0ul) 1, 5, {1, 6, 0x40000000ul SEL2(0xfffffffful)}}, + /* 33 */ {SEL1(0x3e0f83e1ul) 0, 3, {1, 6, 0x4cfa3cc1ul SEL2(0xa9aed1b3ul)}}, + /* 34 */ {SEL1(0xf0f0f0f1ul) 0, 5, {1, 6, 0x5c13d840ul SEL2(0x63dfc229ul)}}, + /* 35 */ {SEL1(0xd41d41d5ul) 1, 6, {1, 6, 0x6d91b519ul SEL2(0x2b0fee30ul)}}, + /* 36 */ {SEL1(0x38e38e39ul) 0, 3, {0, 6, 0x81bf1000ul SEL2(0xf91bd1b6ul)}} +#endif +}; + +/* Lower-case digits. */ +static const char _itoa_lower_digits[] + = "0123456789abcdefghijklmnopqrstuvwxyz"; +/* Upper-case digits. */ +static const char _itoa_upper_digits[] + = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + +char * +_itoa (value, buflim, base, upper_case) + unsigned long long int value; + char *buflim; + unsigned int base; + int upper_case; +{ + const char *digits = upper_case ? _itoa_upper_digits : _itoa_lower_digits; + char *bp = buflim; + const struct base_table_t *brec = &base_table[base - 2]; + + switch (base) + { +#define RUN_2N(BITS) \ + do \ + { \ + /* `unsigned long long int' always has 64 bits. */ \ + mp_limb work_hi = value >> (64 - BITS_PER_MP_LIMB); \ + \ + if (BITS_PER_MP_LIMB == 32) \ + if (work_hi != 0) \ + { \ + mp_limb work_lo; \ + int cnt; \ + \ + work_lo = value & 0xfffffffful; \ + for (cnt = BITS_PER_MP_LIMB / BITS; cnt > 0; --cnt) \ + { \ + *--bp = digits[work_lo & ((1ul << BITS) - 1)]; \ + work_lo >>= BITS; \ + } \ + if (BITS_PER_MP_LIMB % BITS != 0) \ + { \ + work_lo |= ((work_hi \ + & ((1 << BITS - BITS_PER_MP_LIMB % BITS) \ + - 1)) \ + << BITS_PER_MP_LIMB % BITS); \ + *--bp = digits[work_lo]; \ + work_hi >>= BITS - BITS_PER_MP_LIMB % BITS; \ + } \ + } \ + else \ + work_hi = value & 0xfffffffful; \ + do \ + { \ + *--bp = digits[work_hi & ((1 << BITS) - 1)]; \ + work_hi >>= BITS; \ + } \ + while (work_hi != 0); \ + } \ + while (0) + case 8: + RUN_2N (3); + break; + + case 16: + RUN_2N (4); + break; + + default: + { +#if BITS_PER_MP_LIMB == 64 + mp_limb base_multiplier = brec->base_multiplier; + if (brec->flag) + while (value != 0) + { + mp_limb quo, rem, x, dummy; + + umul_ppmm (x, dummy, value, base_multiplier); + quo = (x + ((value - x) >> 1)) >> (brec->post_shift - 1); + rem = value - quo * base; + *--bp = digits[rem]; + value = quo; + } + else + while (value != 0) + { + mp_limb quo, rem, x, dummy; + + umul_ppmm (x, dummy, value, base_multiplier); + quo = x >> brec->post_shift; + rem = value - quo * base; + *--bp = digits[rem]; + value = quo; + } +#endif +#if BITS_PER_MP_LIMB == 32 + mp_limb t[3]; + int n; + + /* First convert x0 to 1-3 words in base s->big.base. + Optimize for frequent cases of 32 bit numbers. */ + if ((mp_limb) (value >> 32) >= 1) + { + int big_normalization_steps = brec->big.normalization_steps; + mp_limb big_base_norm = brec->big.base << big_normalization_steps; + + if ((mp_limb) (value >> 32) >= brec->big.base) + { + mp_limb x1hi, x1lo, r; + /* If you want to optimize this, take advantage of + that the quotient in the first udiv_qrnnd will + always be very small. It might be faster just to + subtract in a tight loop. */ + +#if UDIV_TIME > 2 * UMUL_TIME + mp_limb x, xh, xl; + + if (big_normalization_steps == 0) + xh = 0; + else + xh = (mp_limb) (value >> 64 - big_normalization_steps); + xl = (mp_limb) (value >> 32 - big_normalization_steps); + udiv_qrnnd_preinv (x1hi, r, xh, xl, big_base_norm, + brec->big.base_ninv); + + xl = ((mp_limb) value) << big_normalization_steps; + udiv_qrnnd_preinv (x1lo, x, r, xl, big_base_norm, + big_normalization_steps); + t[2] = x >> big_normalization_steps; + + if (big_normalization_steps == 0) + xh = x1hi; + else + xh = ((x1hi << big_normalization_steps) + | (x1lo >> 32 - big_normalization_steps)); + xl = x1lo << big_normalization_steps; + udiv_qrnnd_preinv (t[0], x, xh, xl, big_base_norm, + big_normalization_steps); + t[1] = x >> big_normalization_steps; +#elif UDIV_NEEDS_NORMALIZATION + mp_limb x, xh, xl; + + if (big_normalization_steps == 0) + xh = 0; + else + xh = (mp_limb) (value >> 64 - big_normalization_steps); + xl = (mp_limb) (value >> 32 - big_normalization_steps); + udiv_qrnnd (x1hi, r, xh, xl, big_base_norm); + + xl = ((mp_limb) value) << big_normalization_steps; + udiv_qrnnd (x1lo, x, r, xl, big_base_norm); + t[2] = x >> big_normalization_steps; + + if (big_normalization_steps == 0) + xh = x1hi; + else + xh = ((x1hi << big_normalization_steps) + | (x1lo >> 32 - big_normalization_steps)); + xl = x1lo << big_normalization_steps; + udiv_qrnnd (t[0], x, xh, xl, big_base_norm); + t[1] = x >> big_normalization_steps; +#else + udiv_qrnnd (x1hi, r, 0, (mp_limb) (value >> 32), + brec->big.base); + udiv_qrnnd (x1lo, t[2], r, (mp_limb) value, brec->big.base); + udiv_qrnnd (t[0], t[1], x1hi, x1lo, brec->big.base); +#endif + n = 3; + } + else + { +#if (UDIV_TIME > 2 * UMUL_TIME) + mp_limb x; + + value <<= brec->big.normalization_steps; + udiv_qrnnd_preinv (t[0], x, (mp_limb) (value >> 32), + (mp_limb) value, big_base_norm, + brec->big.base_ninv); + t[1] = x >> brec->big.normalization_steps; +#elif UDIV_NEEDS_NORMALIZATION + mp_limb x; + + value <<= big_normalization_steps; + udiv_qrnnd (t[0], x, (mp_limb) (value >> 32), + (mp_limb) value, big_base_norm); + t[1] = x >> big_normalization_steps; +#else + udiv_qrnnd (t[0], t[1], (mp_limb) (value >> 32), + (mp_limb) value, brec->big.base); +#endif + n = 2; + } + } + else + { + t[0] = value; + n = 1; + } + + /* Convert the 1-3 words in t[], word by word, to ASCII. */ + do + { + mp_limb ti = t[--n]; + int ndig_for_this_limb = 0; + +#if UDIV_TIME > 2 * UMUL_TIME + mp_limb base_multiplier = brec->base_multiplier; + if (brec->flag) + while (ti != 0) + { + mp_limb quo, rem, x, dummy; + + umul_ppmm (x, dummy, ti, base_multiplier); + quo = (x + ((ti - x) >> 1)) >> (brec->post_shift - 1); + rem = ti - quo * base; + *--bp = digits[rem]; + ti = quo; + ++ndig_for_this_limb; + } + else + while (ti != 0) + { + mp_limb quo, rem, x, dummy; + + umul_ppmm (x, dummy, ti, base_multiplier); + quo = x >> brec->post_shift; + rem = ti - quo * base; + *--bp = digits[rem]; + ti = quo; + ++ndig_for_this_limb; + } +#else + while (ti != 0) + { + mp_limb quo, rem; + + quo = ti / base; + rem = ti % base; + *--bp = digits[rem]; + ti = quo; + ++ndig_for_this_limb; + } +#endif + /* If this wasn't the most significant word, pad with zeros. */ + if (n != 0) + while (ndig_for_this_limb < brec->big.ndigits) + { + *--bp = '0'; + ++ndig_for_this_limb; + } + } + while (n != 0); +#endif + } + break; + } + + return bp; +} diff --git a/stdio-common/_itoa.h b/stdio-common/_itoa.h new file mode 100644 index 0000000000..ab3d1d1d3a --- /dev/null +++ b/stdio-common/_itoa.h @@ -0,0 +1,32 @@ +/* Internal function for converting integers to ASCII. +Copyright (C) 1994, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifndef _ITOA_H +#define _ITOA_H +#include <sys/cdefs.h> + +/* Convert VALUE into ASCII in base BASE (2..36). + Write backwards starting the character just before BUFLIM. + Return the address of the first (left-to-right) character in the number. + Use upper case letters iff UPPER_CASE is nonzero. */ + +extern char *_itoa __P ((unsigned long long int value, char *buflim, + unsigned int base, int upper_case)); + +#endif /* itoa.h */ diff --git a/stdio-common/asprintf.c b/stdio-common/asprintf.c new file mode 100644 index 0000000000..85ab7b1041 --- /dev/null +++ b/stdio-common/asprintf.c @@ -0,0 +1,42 @@ +/* Copyright (C) 1991, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <stdarg.h> +#include <stdio.h> + +#ifdef USE_IN_LIBIO +# define vasprintf _IO_vasprintf +#endif + +/* Write formatted output from FORMAT to a string which is + allocated with malloc and stored in *STRING_PTR. */ +/* VARARGS2 */ +int +asprintf (string_ptr, format) + char **string_ptr; + const char *format; +{ + va_list arg; + int done; + + va_start (arg, format); + done = vasprintf (string_ptr, format, arg); + va_end (arg); + + return done; +} diff --git a/stdio-common/bug1.c b/stdio-common/bug1.c new file mode 100644 index 0000000000..755bc4231b --- /dev/null +++ b/stdio-common/bug1.c @@ -0,0 +1,28 @@ +#include <ansidecl.h> +#include <stdio.h> +#include <string.h> + +int +DEFUN_VOID(main) +{ + char *bp; + size_t size; + FILE *stream; + int lose = 0; + + stream = open_memstream (&bp, &size); + fprintf (stream, "hello"); + fflush (stream); + printf ("buf = %s, size = %d\n", bp, size); + lose |= size != 5; + lose |= strncmp (bp, "hello", size); + fprintf (stream, ", world"); + fclose (stream); + printf ("buf = %s, size = %d\n", bp, size); + lose |= size != 12; + lose |= strncmp (bp, "hello, world", 12); + + puts (lose ? "Test FAILED!" : "Test succeeded."); + + return lose; +} diff --git a/stdio-common/bug1.input b/stdio-common/bug1.input new file mode 100644 index 0000000000..5595fa46c0 --- /dev/null +++ b/stdio-common/bug1.input @@ -0,0 +1 @@ +95 diff --git a/stdio-common/bug2.c b/stdio-common/bug2.c new file mode 100644 index 0000000000..2b34c890bf --- /dev/null +++ b/stdio-common/bug2.c @@ -0,0 +1,12 @@ +#include <ansidecl.h> +#include <stdio.h> + +int +DEFUN_VOID(main) +{ + int i; + puts ("This should print \"wow = I\" for I from 0 to 39 inclusive."); + for (i = 0; i < 40; i++) + printf ("%s = %d\n", "wow", i); + return 0; +} diff --git a/stdio-common/bug3.c b/stdio-common/bug3.c new file mode 100644 index 0000000000..1684720b9f --- /dev/null +++ b/stdio-common/bug3.c @@ -0,0 +1,52 @@ +#include <ansidecl.h> +#include <stdio.h> +#include <string.h> + +int +DEFUN_VOID(main) +{ + FILE *f; + int i; + + f = fopen("/tmp/bugtest", "w+"); + for (i=0; i<9000; i++) + putc ('x', f); + fseek (f, 8180L, 0); + fwrite ("Where does this text go?", 1, 24, f); + fflush (f); + + rewind (f); + for (i=0; i<9000; i++) + { + int j; + + if ((j = getc(f)) != 'x') + { + if (i != 8180) + { + printf ("Test FAILED!"); + return 1; + } + else + { + char buf[25]; + + buf[0] = j; + fread (buf + 1, 1, 23, f); + buf[24] = '\0'; + if (strcmp (buf, "Where does this text go?") != 0) + { + printf ("%s\nTest FAILED!\n", buf); + return 1; + } + i += 23; + } + } + } + + fclose(f); + + puts ("Test succeeded."); + + return 0; +} diff --git a/stdio-common/bug4.c b/stdio-common/bug4.c new file mode 100644 index 0000000000..00abf3c502 --- /dev/null +++ b/stdio-common/bug4.c @@ -0,0 +1,50 @@ +#ifdef _LIBC +#include <ansidecl.h> +#endif +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +int stdio_block_read = 1, stdio_block_write = 1; + +int +DEFUN(main, (argc, argv), + int argc AND char **argv) +{ + FILE *f; + int i; + char buffer[31]; + + while ((i = getopt (argc, argv, "rw")) != EOF) + switch (i) + { + case 'r': + stdio_block_read = 0; + break; + case 'w': + stdio_block_write = 0; + break; + } + + f = fopen("/tmp/bugtest", "w+"); + for (i=0; i<9000; i++) { + putc('x', f); + } + fseek(f, 8180L, 0); + fwrite("Where does this text come from?", 1, 31, f); + fseek(f, 8180L, 0); + fread(buffer, 1, 31, f); + fwrite(buffer, 1, 31, stdout); + fclose(f); + + if (!memcmp (buffer, "Where does this text come from?", 31)) + { + puts ("\nTest succeeded."); + return 0; + } + else + { + puts ("\nTest FAILED!"); + return 1; + } +} diff --git a/stdio-common/bug5.c b/stdio-common/bug5.c new file mode 100644 index 0000000000..18f069ae29 --- /dev/null +++ b/stdio-common/bug5.c @@ -0,0 +1,60 @@ +/* If stdio is working correctly, after this is run infile and outfile + will have the same contents. If the bug (found in GNU C library 0.3) + exhibits itself, outfile will be missing the 2nd through 1023rd + characters. */ + +#include <ansidecl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static char buf[8192]; + +int +DEFUN_VOID(main) +{ + FILE *in; + FILE *out; + static char inname[] = "/tmp/bug5.in"; + static char outname[] = "/tmp/bug5.out"; + int i; + + /* Create a test file. */ + in = fopen (inname, "w+"); + if (in == NULL) + { + perror (inname); + return 1; + } + for (i = 0; i < 1000; ++i) + fprintf (in, "%d\n", i); + + out = fopen (outname, "w"); + if (out == NULL) + { + perror (outname); + return 1; + } + if (fseek (in, 0L, SEEK_SET) != 0) + abort (); + putc (getc (in), out); + i = fread (buf, 1, sizeof (buf), in); + if (i == 0) + { + perror ("fread"); + return 1; + } + if (fwrite (buf, 1, i, out) != i) + { + perror ("fwrite"); + return 1; + } + fclose (in); + fclose (out); + + puts ("There should be no further output from this test."); + fflush (stdout); + execlp ("cmp", "cmp", inname, outname, (char *) NULL); + perror ("execlp: cmp"); + exit (1); +} diff --git a/stdio-common/bug6.c b/stdio-common/bug6.c new file mode 100644 index 0000000000..4a37ab2584 --- /dev/null +++ b/stdio-common/bug6.c @@ -0,0 +1,27 @@ +#include <ansidecl.h> +#include <stdio.h> + +int +DEFUN_VOID(main) +{ + char buf[80]; + int i; + int lost = 0; + + scanf ("%2s", buf); + lost |= (buf[0] != 'X' || buf[1] != 'Y' || buf[2] != '\0'); + if (lost) + puts ("test of %2s failed."); + scanf (" "); + scanf ("%d", &i); + lost |= (i != 1234); + if (lost) + puts ("test of %d failed."); + scanf ("%c", buf); + lost |= (buf[0] != 'L'); + if (lost) + puts ("test of %c failed.\n"); + + puts (lost ? "Test FAILED!" : "Test succeeded."); + return lost; +} diff --git a/stdio-common/bug6.input b/stdio-common/bug6.input new file mode 100644 index 0000000000..d996e399c3 --- /dev/null +++ b/stdio-common/bug6.input @@ -0,0 +1 @@ +XY 1234L diff --git a/stdio-common/bug7.c b/stdio-common/bug7.c new file mode 100644 index 0000000000..af06f8d6a5 --- /dev/null +++ b/stdio-common/bug7.c @@ -0,0 +1,53 @@ +/* Regression test for fseek and freopen bugs. */ + +#include <stdio.h> + +int +main () +{ + int lose = 0; + char filename[] = "/tmp/foo"; + FILE *fp; + + fp = fopen (filename, "w+"); + fprintf (fp, "Hello world!\n"); + fflush (fp); + fseek (fp, 5L, SEEK_SET); + if (fseek (fp, -1L, SEEK_CUR) < 0) + { + printf ("seek failed\n"); + lose = 1; + } + fclose (fp); + remove (filename); + + { + FILE *file1; + FILE *file2; + char filename1[] = "/tmp/foo"; + char filename2[] = "/tmp/bar"; + int ch; + + file1 = fopen (filename1, "w"); + fclose (file1); + + file2 = fopen (filename2, "w"); + fputc ('x', file2); + fclose (file2); + + file1 = fopen (filename1, "r"); + file2 = freopen (filename2, "r", file1); + if ((ch = fgetc (file2)) != 'x') + { + printf ("wrong character in reopened file, value = %d\n", ch); + lose = 1; + } + fclose (file1); + fclose (file2); + remove (filename1); + remove (filename2); + } + + puts (lose ? "Test FAILED!" : "Test succeeded."); + return lose; +} diff --git a/stdio-common/dprintf.c b/stdio-common/dprintf.c new file mode 100644 index 0000000000..5746d49841 --- /dev/null +++ b/stdio-common/dprintf.c @@ -0,0 +1,41 @@ +/* Copyright (C) 1991, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <stdarg.h> +#include <stdio.h> + +#ifdef USE_IN_LIBIO +# define vdprintf _IO_vdprintf +#endif + +/* Write formatted output to D, according to the format string FORMAT. */ +/* VARARGS2 */ +int +dprintf (d, format) + int d; + const char *format; +{ + va_list arg; + int done; + + va_start (arg, format); + done = vdprintf (d, format, arg); + va_end (arg); + + return done; +} diff --git a/stdio-common/errnobug.c b/stdio-common/errnobug.c new file mode 100644 index 0000000000..cf17be30a2 --- /dev/null +++ b/stdio-common/errnobug.c @@ -0,0 +1,60 @@ +/* Regression test for reported old bug that errno is clobbered + by the first successful output to a stream on an unseekable object. +Copyright (C) 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> + +int +main (void) +{ + int fd[2]; + FILE *f; + + /* Get a stream that cannot seek. */ + + if (pipe (fd)) + { + perror ("pipe"); + return 1; + } + f = fdopen (fd[1], "w"); + if (f == NULL) + { + perror ("fdopen"); + return 1; + } + + errno = 0; + if (fputs ("fnord", f)) + { + perror ("fputs"); + return 1; + } + + if (errno) + { + perror ("errno gratuitously set -- TEST FAILED"); + return 1; + } + + puts ("Test succeeded."); + return 0; +} diff --git a/stdio-common/fprintf.c b/stdio-common/fprintf.c new file mode 100644 index 0000000000..bc6d1003b7 --- /dev/null +++ b/stdio-common/fprintf.c @@ -0,0 +1,38 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdarg.h> +#include <stdio.h> + + +/* Write formatted output to STREAM from the format string FORMAT. */ +/* VARARGS2 */ +int +DEFUN(fprintf, (stream, format), + FILE *stream AND CONST char *format DOTS) +{ + va_list arg; + int done; + + va_start(arg, format); + done = vfprintf(stream, format, arg); + va_end(arg); + + return done; +} diff --git a/stdio-common/fscanf.c b/stdio-common/fscanf.c new file mode 100644 index 0000000000..cbe0103368 --- /dev/null +++ b/stdio-common/fscanf.c @@ -0,0 +1,38 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdarg.h> +#include <stdio.h> + + +/* Read formatted input from STREAM according to the format string FORMAT. */ +/* VARARGS2 */ +int +DEFUN(fscanf, (stream, format), + FILE *stream AND CONST char *format DOTS) +{ + va_list arg; + int done; + + va_start(arg, format); + done = __vfscanf(stream, format, arg); + va_end(arg); + + return done; +} diff --git a/stdio-common/getline.c b/stdio-common/getline.c new file mode 100644 index 0000000000..1a2f975c75 --- /dev/null +++ b/stdio-common/getline.c @@ -0,0 +1,33 @@ +/* Copyright (C) 1991, 1992, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stddef.h> +#include <stdio.h> + +#undef __getline + +/* Like getdelim, but always looks for a newline. */ +ssize_t +DEFUN(__getline, (lineptr, n, stream), + char **lineptr AND size_t *n AND FILE *stream) +{ + return __getdelim (lineptr, n, '\n', stream); +} + +weak_alias (__getline, getline) diff --git a/stdio-common/getw.c b/stdio-common/getw.c new file mode 100644 index 0000000000..45d4d8875d --- /dev/null +++ b/stdio-common/getw.c @@ -0,0 +1,33 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdio.h> + + +/* Read a word (int) from STREAM. */ +int +DEFUN(getw, (stream), FILE *stream) +{ + int w; + + /* Is there a better way? */ + if (fread((PTR) &w, sizeof(w), 1, stream) != 1) + return(EOF); + return(w); +} diff --git a/stdio-common/perror.c b/stdio-common/perror.c new file mode 100644 index 0000000000..1054acaa7d --- /dev/null +++ b/stdio-common/perror.c @@ -0,0 +1,42 @@ +/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdio.h> +#include <errno.h> + +extern char *_strerror_internal __P ((int, char *buf, size_t)); + +/* Print a line on stderr consisting of the text in S, a colon, a space, + a message describing the meaning of the contents of `errno' and a newline. + If S is NULL or "", the colon and space are omitted. */ +void +DEFUN(perror, (s), register CONST char *s) +{ + char buf[1024]; + int errnum = errno; + CONST char *colon; + + if (s == NULL || *s == '\0') + s = colon = ""; + else + colon = ": "; + + (void) fprintf (stderr, "%s%s%s\n", + s, colon, _strerror_internal (errnum, buf, sizeof buf)); +} diff --git a/stdio-common/printf-parse.h b/stdio-common/printf-parse.h new file mode 100644 index 0000000000..0f6e9e287b --- /dev/null +++ b/stdio-common/printf-parse.h @@ -0,0 +1,388 @@ +/* Internal header for parsing printf format strings. +Copyright (C) 1995 Free Software Foundation, Inc. +This file is part of th GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ctype.h> +#include <printf.h> +#include <string.h> +#include <stddef.h> + +#define NDEBUG 1 +#include <assert.h> + +#define MAX(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); \ + _a > _b ? _a : _b; }) +#define MIN(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); \ + _a < _b ? _a : _b; }) + +struct printf_spec + { + /* Information parsed from the format spec. */ + struct printf_info info; + + /* Pointers into the format string for the end of this format + spec and the next (or to the end of the string if no more). */ + const char *end_of_fmt, *next_fmt; + + /* Position of arguments for precision and width, or -1 if `info' has + the constant value. */ + int prec_arg, width_arg; + + int data_arg; /* Position of data argument. */ + int data_arg_type; /* Type of first argument. */ + /* Number of arguments consumed by this format specifier. */ + size_t ndata_args; + }; + + +/* The various kinds off arguments that can be passed to printf. */ +union printf_arg + { + unsigned char pa_char; + short int pa_short_int; + int pa_int; + long int pa_long_int; + long long int pa_long_long_int; + unsigned short int pa_u_short_int; + unsigned int pa_u_int; + unsigned long int pa_u_long_int; + unsigned long long int pa_u_long_long_int; + float pa_float; + double pa_double; + long double pa_long_double; + const char *pa_string; + void *pa_pointer; + }; + + +/* Read a simple integer from a string and update the string pointer. + It is assumed that the first character is a digit. */ +static inline unsigned int +read_int (const char * *pstr) +{ + unsigned int retval = **pstr - '0'; + + while (isdigit (*++(*pstr))) + { + retval *= 10; + retval += **pstr - '0'; + } + + return retval; +} + + + +/* Find the next spec in FORMAT, or the end of the string. Returns + a pointer into FORMAT, to a '%' or a '\0'. */ +static inline const char * +find_spec (const char *format) +{ + while (*format != '\0' && *format != '%') + { + int len; + + if (isascii (*format) || (len = mblen (format, MB_CUR_MAX)) <= 0) + ++format; + else + format += len; + } + return format; +} + + +/* This is defined in reg-printf.c. */ +extern printf_arginfo_function **__printf_arginfo_table; + + +/* FORMAT must point to a '%' at the beginning of a spec. Fills in *SPEC + with the parsed details. POSN is the number of arguments already + consumed. At most MAXTYPES - POSN types are filled in TYPES. Return + the number of args consumed by this spec; *MAX_REF_ARG is updated so it + remains the highest argument index used. */ +static inline size_t +parse_one_spec (const char *format, size_t posn, struct printf_spec *spec, + size_t *max_ref_arg) +{ + unsigned int n; + size_t nargs = 0; + + /* Skip the '%'. */ + ++format; + + /* Clear information structure. */ + spec->data_arg = -1; + spec->info.alt = 0; + spec->info.space = 0; + spec->info.left = 0; + spec->info.showsign = 0; + spec->info.group = 0; + spec->info.pad = ' '; + + /* Test for positional argument. */ + if (isdigit (*format)) + { + const char *begin = format; + + n = read_int (&format); + + if (n > 0 && *format == '$') + /* Is positional parameter. */ + { + ++format; /* Skip the '$'. */ + spec->data_arg = n - 1; + *max_ref_arg = MAX (*max_ref_arg, n); + } + else + /* Oops; that was actually the width and/or 0 padding flag. + Step back and read it again. */ + format = begin; + } + + /* Check for spec modifiers. */ + while (*format == ' ' || *format == '+' || *format == '-' || + *format == '#' || *format == '0' || *format == '\'') + switch (*format++) + { + case ' ': + /* Output a space in place of a sign, when there is no sign. */ + spec->info.space = 1; + break; + case '+': + /* Always output + or - for numbers. */ + spec->info.showsign = 1; + break; + case '-': + /* Left-justify things. */ + spec->info.left = 1; + break; + case '#': + /* Use the "alternate form": + Hex has 0x or 0X, FP always has a decimal point. */ + spec->info.alt = 1; + break; + case '0': + /* Pad with 0s. */ + spec->info.pad = '0'; + break; + case '\'': + /* Show grouping in numbers if the locale information + indicates any. */ + spec->info.group = 1; + break; + } + if (spec->info.left) + spec->info.pad = ' '; + + /* Get the field width. */ + spec->width_arg = -1; + spec->info.width = 0; + if (*format == '*') + { + /* The field width is given in an argument. + A negative field width indicates left justification. */ + const char *begin = ++format; + + if (isdigit (*format)) + { + /* The width argument might be found in a positional parameter. */ + n = read_int (&format); + + if (n > 0 && *format == '$') + { + spec->width_arg = n - 1; + *max_ref_arg = MAX (*max_ref_arg, n); + ++format; /* Skip '$'. */ + } + } + + if (spec->width_arg < 0) + { + /* Not in a positional parameter. Consume one argument. */ + spec->width_arg = posn++; + ++nargs; + format = begin; /* Step back and reread. */ + } + } + else if (isdigit (*format)) + /* Constant width specification. */ + spec->info.width = read_int (&format); + + /* Get the precision. */ + spec->prec_arg = -1; + /* -1 means none given; 0 means explicit 0. */ + spec->info.prec = -1; + if (*format == '.') + { + ++format; + if (*format == '*') + { + /* The precision is given in an argument. */ + const char *begin = ++format; + + if (isdigit (*format)) + { + n = read_int (&format); + + if (n > 0 && *format == '$') + { + spec->prec_arg = n - 1; + *max_ref_arg = MAX (*max_ref_arg, n); + ++format; + } + } + + if (spec->prec_arg < 0) + { + /* Not in a positional parameter. */ + spec->prec_arg = posn++; + ++nargs; + format = begin; + } + } + else if (isdigit (*format)) + spec->info.prec = read_int (&format); + else + /* "%.?" is treated like "%.0?". */ + spec->info.prec = 0; + + /* If there was a precision specified, ignore the 0 flag and always + pad with spaces. */ + spec->info.pad = ' '; + } + + /* Check for type modifiers. */ +#define is_longlong is_long_double + spec->info.is_long_double = 0; + spec->info.is_short = 0; + spec->info.is_long = 0; + + while (*format == 'h' || *format == 'l' || *format == 'L' || + *format == 'Z' || *format == 'q') + switch (*format++) + { + case 'h': + /* int's are short int's. */ + spec->info.is_short = 1; + break; + case 'l': + if (spec->info.is_long) + /* A double `l' is equivalent to an `L'. */ + spec->info.is_longlong = 1; + else + /* int's are long int's. */ + spec->info.is_long = 1; + break; + case 'L': + /* double's are long double's, and int's are long long int's. */ + spec->info.is_long_double = 1; + break; + case 'Z': + /* int's are size_t's. */ + assert (sizeof(size_t) <= sizeof(unsigned long long int)); + spec->info.is_longlong = sizeof(size_t) > sizeof(unsigned long int); + spec->info.is_long = sizeof(size_t) > sizeof(unsigned int); + break; + case 'q': + /* 4.4 uses this for long long. */ + spec->info.is_longlong = 1; + break; + } + + /* Get the format specification. */ + spec->info.spec = *format++; + if (__printf_arginfo_table != NULL && + __printf_arginfo_table[spec->info.spec] != NULL) + /* We don't try to get the types for all arguments if the format + uses more than one. The normal case is covered though. */ + spec->ndata_args = (*__printf_arginfo_table[spec->info.spec]) + (&spec->info, 1, &spec->data_arg_type); + else + { + /* Find the data argument types of a built-in spec. */ + spec->ndata_args = 1; + + switch (spec->info.spec) + { + case 'i': + case 'd': + case 'u': + case 'o': + case 'X': + case 'x': + if (spec->info.is_longlong) + spec->data_arg_type = PA_INT|PA_FLAG_LONG_LONG; + else if (spec->info.is_long) + spec->data_arg_type = PA_INT|PA_FLAG_LONG; + else if (spec->info.is_short) + spec->data_arg_type = PA_INT|PA_FLAG_SHORT; + else + spec->data_arg_type = PA_INT; + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (spec->info.is_long_double) + spec->data_arg_type = PA_DOUBLE|PA_FLAG_LONG_DOUBLE; + else + spec->data_arg_type = PA_DOUBLE; + break; + case 'c': + spec->data_arg_type = PA_CHAR; + break; + case 's': + spec->data_arg_type = PA_STRING; + break; + case 'p': + spec->data_arg_type = PA_POINTER; + break; + case 'n': + spec->data_arg_type = PA_INT|PA_FLAG_PTR; + break; + + case 'm': + default: + /* An unknown spec will consume no args. */ + spec->ndata_args = 0; + break; + } + + if (spec->data_arg == -1 && spec->ndata_args > 0) + { + /* There are args consumed, but no positional spec. + Use the next sequential arg position. */ + spec->data_arg = posn; + posn += spec->ndata_args; + nargs += spec->ndata_args; + } + } + + if (spec->info.spec == '\0') + /* Format ended before this spec was complete. */ + spec->end_of_fmt = spec->next_fmt = format - 1; + else + { + /* Find the next format spec. */ + spec->end_of_fmt = format; + spec->next_fmt = find_spec (format); + } + + return nargs; +} diff --git a/stdio-common/printf-prs.c b/stdio-common/printf-prs.c new file mode 100644 index 0000000000..811a9cb7eb --- /dev/null +++ b/stdio-common/printf-prs.c @@ -0,0 +1,72 @@ +/* Copyright (C) 1991, 1992, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <printf.h> +#include <stdlib.h> +#include <string.h> + +#include "printf-parse.h" + + +size_t +parse_printf_format (fmt, n, argtypes) + const char *fmt; + size_t n; + int *argtypes; +{ + size_t nargs; /* Number of arguments. */ + size_t max_ref_arg; /* Highest index used in a positional arg. */ + struct printf_spec spec; + + nargs = 0; + max_ref_arg = 0; + + /* Search for format specifications. */ + for (fmt = find_spec (fmt); *fmt != '\0'; fmt = spec.next_fmt) + { + /* Parse this spec. */ + nargs += parse_one_spec (fmt, nargs, &spec, &max_ref_arg); + + /* If the width is determined by an argument this is an int. */ + if (spec.width_arg != -1 && spec.width_arg < n) + argtypes[spec.width_arg] = PA_INT; + + /* If the precision is determined by an argument this is an int. */ + if (spec.prec_arg != -1 && spec.prec_arg < n) + argtypes[spec.prec_arg] = PA_INT; + + if (spec.data_arg < n) + switch (spec.ndata_args) + { + case 0: /* No arguments. */ + break; + case 1: /* One argument; we already have the type. */ + argtypes[spec.data_arg] = spec.data_arg_type; + break; + default: + /* We have more than one argument for this format spec. We must + call the arginfo function again to determine all the types. */ + (void) (*__printf_arginfo_table[spec.info.spec]) + (&spec.info, n - spec.data_arg, &argtypes[spec.data_arg]); + break; + } + } + + return MAX (nargs, max_ref_arg); +} diff --git a/stdio-common/printf.c b/stdio-common/printf.c new file mode 100644 index 0000000000..d8aa895a77 --- /dev/null +++ b/stdio-common/printf.c @@ -0,0 +1,40 @@ +/* Copyright (C) 1991, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <stdarg.h> +#include <stdio.h> + +#ifdef USE_IN_LIBIO +# define vprintf _IO_vprintf +#endif + +/* Write formatted output to stdout from the format string FORMAT. */ +/* VARARGS1 */ +int +printf (format) + const char *format; +{ + va_list arg; + int done; + + va_start (arg, format); + done = vprintf (format, arg); + va_end (arg); + + return done; +} diff --git a/stdio-common/printf.h b/stdio-common/printf.h new file mode 100644 index 0000000000..0f381c77f4 --- /dev/null +++ b/stdio-common/printf.h @@ -0,0 +1,124 @@ +/* Copyright (C) 1991, 1992, 1993, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the, 1992 Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifndef _PRINTF_H + +#define _PRINTF_H 1 +#include <features.h> + +__BEGIN_DECLS + +#define __need_FILE +#include <stdio.h> +#define __need_size_t +#include <stddef.h> + + +struct printf_info +{ + int prec; /* Precision. */ + int width; /* Width. */ + unsigned char spec; /* Format letter. */ + unsigned int is_long_double:1;/* L flag. */ + unsigned int is_short:1; /* h flag. */ + unsigned int is_long:1; /* l flag. */ + unsigned int alt:1; /* # flag. */ + unsigned int space:1; /* Space flag. */ + unsigned int left:1; /* - flag. */ + unsigned int showsign:1; /* + flag. */ + unsigned int group:1; /* ' flag. */ + char pad; /* Padding character. */ +}; + + +/* Type of a printf specifier-handler function. + STREAM is the FILE on which to write output. + INFO gives information about the format specification. + ARGS is a vector of pointers to the argument data; + the number of pointers will be the number returned + by the associated arginfo function for the same INFO. + + The function should return the number of characters written, + or -1 for errors. */ + +typedef int printf_function __P ((FILE *__stream, + __const struct printf_info *__info, + __const void **__const __args)); + +/* Type of a printf specifier-arginfo function. + INFO gives information about the format specification. + N, ARGTYPES, and return value are as for printf_parse_format. */ + +typedef int printf_arginfo_function __P ((__const struct printf_info * __info, + size_t __n, + int *__argtypes)); + + +/* Register FUNC to be called to format SPEC specifiers; ARGINFO must be + specified to determine how many arguments a SPEC conversion requires and + what their types are, even if your program never calls + `parse_printf_format'. */ + +extern int register_printf_function __P ((int __spec, printf_function __func, + printf_arginfo_function __arginfo)); + + +/* Parse FMT, and fill in N elements of ARGTYPES with the + types needed for the conversions FMT specifies. Returns + the number of arguments required by FMT. + + The ARGINFO function registered with a user-defined format is passed a + `struct printf_info' describing the format spec being parsed. A width + or precision of INT_MIN means a `*' was used to indicate that the + width/precision will come from an arg. The function should fill in the + array it is passed with the types of the arguments it wants, and return + the number of arguments it wants. */ + +extern size_t parse_printf_format __P ((__const char *__fmt, + size_t __n, + int *__argtypes)); + + +/* Codes returned by `parse_printf_format' for basic types. + + These values cover all the standard format specifications. + Users can add new values after PA_LAST for their own types. */ + +enum +{ /* C type: */ + PA_INT, /* int */ + PA_CHAR, /* int, cast to char */ + PA_STRING, /* const char *, a '\0'-terminated string */ + PA_POINTER, /* void * */ + PA_FLOAT, /* float */ + PA_DOUBLE, /* double */ + PA_LAST +}; + +/* Flag bits that can be set in a type returned by `parse_printf_format'. */ +#define PA_FLAG_MASK 0xff00 +#define PA_FLAG_LONG_LONG (1 << 8) +#define PA_FLAG_LONG_DOUBLE PA_FLAG_LONG_LONG +#define PA_FLAG_LONG (1 << 9) +#define PA_FLAG_SHORT (1 << 10) +#define PA_FLAG_PTR (1 << 11) + + +__END_DECLS + +#endif /* printf.h */ diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c new file mode 100644 index 0000000000..28d13d61b8 --- /dev/null +++ b/stdio-common/printf_fp.c @@ -0,0 +1,990 @@ +/* Floating point output for `printf'. +Copyright (C) 1995 Free Software Foundation, Inc. +Written by Ulrich Drepper. + +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifdef USE_IN_LIBIO +# include <libioP.h> +#else +# include <stdio.h> +#endif +#include <alloca.h> +#include <ansidecl.h> +#include <ctype.h> +#include <float.h> +#include <gmp-mparam.h> +#include "../stdlib/gmp.h" +#include "../stdlib/gmp-impl.h" +#include "../stdlib/longlong.h" +#include "../stdlib/fpioconst.h" +#include "../locale/localeinfo.h" +#include <limits.h> +#include <math.h> +#include <printf.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#define NDEBUG /* Undefine this for debugging assertions. */ +#include <assert.h> + +/* This defines make it possible to use the same code for GNU C library and + the GNU I/O library. */ +#ifdef USE_IN_LIBIO +# define PUT(f, s, n) _IO_sputn (f, s, n) +# define PAD(f, c, n) _IO_padn (f, c, n) +/* We use this file GNU C library and GNU I/O library. So make + names equal. */ +# undef putc +# define putc(c, f) _IO_putc (c, f) +# define size_t _IO_size_t +# define FILE _IO_FILE +#else /* ! USE_IN_LIBIO */ +# define PUT(f, s, n) fwrite (s, 1, n, f) +# define PAD(f, c, n) __printf_pad (f, c, n) +ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c. */ +#endif /* USE_IN_LIBIO */ + +/* Macros for doing the actual output. */ + +#define outchar(ch) \ + do \ + { \ + register CONST int outc = (ch); \ + if (putc (outc, fp) == EOF) \ + return -1; \ + ++done; \ + } while (0) + +#define PRINT(ptr, len) \ + do \ + { \ + register size_t outlen = (len); \ + if (len > 20) \ + { \ + if (PUT (fp, ptr, outlen) != outlen) \ + return -1; \ + ptr += outlen; \ + done += outlen; \ + } \ + else \ + { \ + while (outlen-- > 0) \ + outchar (*ptr++); \ + } \ + } while (0) + +#define PADN(ch, len) \ + do \ + { \ + if (PAD (fp, ch, len) != len) \ + return -1; \ + done += len; \ + } \ + while (0) + +/* We use the GNU MP library to handle large numbers. + + An MP variable occupies a varying number of entries in its array. We keep + track of this number for efficiency reasons. Otherwise we would always + have to process the whole array. */ +#define MPN_VAR(name) mp_limb *name; mp_size_t name##size + +#define MPN_ASSIGN(dst,src) \ + memcpy (dst, src, (dst##size = src##size) * sizeof (mp_limb)) +#define MPN_GE(u,v) \ + (u##size > v##size || (u##size == v##size && __mpn_cmp (u, v, u##size) >= 0)) + +extern int __isinfl (long double), __isnanl (long double); + +extern mp_size_t __mpn_extract_double (mp_ptr res_ptr, mp_size_t size, + int *expt, int *is_neg, + double value); +extern mp_size_t __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size, + int *expt, int *is_neg, + long double value); + + +static unsigned int guess_grouping (unsigned int intdig_max, + const char *grouping, wchar_t sepchar); +static char *group_number (char *buf, char *bufend, unsigned int intdig_no, + const char *grouping, wchar_t thousands_sep); + + +int +__printf_fp (fp, info, args) + FILE *fp; + const struct printf_info *info; + const **const args; +{ + /* The floating-point value to output. */ + union + { + double dbl; + LONG_DOUBLE ldbl; + } + fpnum; + + /* Locale-dependent representation of decimal point. */ + wchar_t decimal; + + /* Locale-dependent thousands separator and grouping specification. */ + wchar_t thousands_sep; + const char *grouping; + + /* "NaN" or "Inf" for the special cases. */ + CONST char *special = NULL; + + /* We need just a few limbs for the input before shifting to the right + position. */ + mp_limb fp_input[(LDBL_MANT_DIG + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB]; + /* We need to shift the contents of fp_input by this amount of bits. */ + int to_shift; + + /* The significant of the floting-point value in question */ + MPN_VAR(frac); + /* and the exponent. */ + int exponent; + /* Sign of the exponent. */ + int expsign = 0; + /* Sign of float number. */ + int is_neg = 0; + + /* Scaling factor. */ + MPN_VAR(scale); + + /* Temporary bignum value. */ + MPN_VAR(tmp); + + /* Digit which is result of last hack_digit() call. */ + int digit; + + /* The type of output format that will be used: 'e'/'E' or 'f'. */ + int type; + + /* Counter for number of written characters. */ + int done = 0; + + /* General helper (carry limb). */ + mp_limb cy; + + char hack_digit (void) + { + mp_limb hi; + + if (expsign != 0 && type == 'f' && exponent-- > 0) + hi = 0; + else if (scalesize == 0) + { + hi = frac[fracsize - 1]; + cy = __mpn_mul_1 (frac, frac, fracsize - 1, 10); + frac[fracsize - 1] = cy; + } + else + { + if (fracsize < scalesize) + hi = 0; + else + { + hi = __mpn_divmod (tmp, frac, fracsize, scale, scalesize); + tmp[fracsize - scalesize] = hi; + hi = tmp[0]; + + fracsize = __mpn_normal_size (frac, scalesize); + if (fracsize == 0) + { + /* We're not prepared for an mpn variable with zero + limbs. */ + fracsize = 1; + return '0' + hi; + } + } + + cy = __mpn_mul_1 (frac, frac, fracsize, 10); + if (cy != 0) + frac[fracsize++] = cy; + } + + return '0' + hi; + } + + + /* Figure out the decimal point character. */ + if (mbtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT), + strlen (_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT))) <= 0) + decimal = (wchar_t) *_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT); + + + if (info->group) + { + grouping = _NL_CURRENT (LC_NUMERIC, GROUPING); + if (*grouping <= 0 || *grouping == CHAR_MAX) + grouping = NULL; + else + { + /* Figure out the thousands seperator character. */ + if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP), + strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0) + thousands_sep = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); + if (thousands_sep == L'\0') + grouping = NULL; + } + } + else + grouping = NULL; + + /* Fetch the argument value. */ + if (info->is_long_double && sizeof (long double) > sizeof (double)) + { + fpnum.ldbl = *(const long double *) args[0]; + + /* Check for special values: not a number or infinity. */ + if (__isnanl (fpnum.ldbl)) + { + special = "NaN"; + is_neg = 0; + } + else if (__isinfl (fpnum.ldbl)) + { + special = "Inf"; + is_neg = fpnum.ldbl < 0; + } + else + { + fracsize = __mpn_extract_long_double (fp_input, + (sizeof (fp_input) / + sizeof (fp_input[0])), + &exponent, &is_neg, + fpnum.ldbl); + to_shift = 1 + fracsize * BITS_PER_MP_LIMB - LDBL_MANT_DIG; + } + } + else + { + fpnum.dbl = *(const double *) args[0]; + + /* Check for special values: not a number or infinity. */ + if (__isnan (fpnum.dbl)) + { + special = "NaN"; + is_neg = 0; + } + else if (__isinf (fpnum.dbl)) + { + special = "Inf"; + is_neg = fpnum.dbl < 0; + } + else + { + fracsize = __mpn_extract_double (fp_input, + (sizeof (fp_input) + / sizeof (fp_input[0])), + &exponent, &is_neg, fpnum.dbl); + to_shift = 1 + fracsize * BITS_PER_MP_LIMB - DBL_MANT_DIG; + } + } + + if (special) + { + int width = info->prec > info->width ? info->prec : info->width; + + if (is_neg || info->showsign || info->space) + --width; + width -= 3; + + if (!info->left && width > 0) + PADN (' ', width); + + if (is_neg) + outchar ('-'); + else if (info->showsign) + outchar ('+'); + else if (info->space) + outchar (' '); + + PRINT (special, 3); + + if (info->left && width > 0) + PADN (' ', width); + + return done; + } + + + /* We need three multiprecision variables. Now that we have the exponent + of the number we can allocate the needed memory. It would be more + efficient to use variables of the fixed maximum size but because this + would be really big it could lead to memory problems. */ + { + mp_size_t bignum_size = ((ABS (exponent) + BITS_PER_MP_LIMB - 1) + / BITS_PER_MP_LIMB + 3) * sizeof (mp_limb); + frac = (mp_limb *) alloca (bignum_size); + tmp = (mp_limb *) alloca (bignum_size); + scale = (mp_limb *) alloca (bignum_size); + } + + /* We now have to distinguish between numbers with positive and negative + exponents because the method used for the one is not applicable/efficient + for the other. */ + scalesize = 0; + if (exponent > 2) + { + /* |FP| >= 1.0. */ + int scaleexpo = 0; + int explog = LDBL_MAX_10_EXP_LOG; + int exp10 = 0; + const struct mp_power *tens = &_fpioconst_pow10[explog + 1]; + int cnt_h, cnt_l, i; + + if ((exponent + to_shift) % BITS_PER_MP_LIMB == 0) + { + MPN_COPY_DECR (frac + (exponent + to_shift) / BITS_PER_MP_LIMB, + fp_input, fracsize); + fracsize += (exponent + to_shift) / BITS_PER_MP_LIMB; + } + else + { + cy = __mpn_lshift (frac + (exponent + to_shift) / BITS_PER_MP_LIMB, + fp_input, fracsize, + (exponent + to_shift) % BITS_PER_MP_LIMB); + fracsize += (exponent + to_shift) / BITS_PER_MP_LIMB; + if (cy) + frac[fracsize++] = cy; + } + MPN_ZERO (frac, (exponent + to_shift) / BITS_PER_MP_LIMB); + + assert (tens > &_fpioconst_pow10[0]); + do + { + --tens; + + /* The number of the product of two binary numbers with n and m + bits respectively has m+n or m+n-1 bits. */ + if (exponent >= scaleexpo + tens->p_expo - 1) + { + if (scalesize == 0) + MPN_ASSIGN (tmp, tens->array); + else + { + cy = __mpn_mul (tmp, scale, scalesize, + tens->array + 2, tens->arraysize - 2); + tmpsize = scalesize + tens->arraysize - 2; + if (cy == 0) + --tmpsize; + } + + if (MPN_GE (frac, tmp)) + { + int cnt; + MPN_ASSIGN (scale, tmp); + count_leading_zeros (cnt, scale[scalesize - 1]); + scaleexpo = (scalesize - 2) * BITS_PER_MP_LIMB - cnt - 1; + exp10 |= 1 << explog; + } + } + --explog; + } + while (tens > &_fpioconst_pow10[0]); + exponent = exp10; + + /* Optimize number representations. We want to represent the numbers + with the lowest number of bytes possible without losing any + bytes. Also the highest bit in the scaling factor has to be set + (this is a requirement of the MPN division routines). */ + if (scalesize > 0) + { + /* Determine minimum number of zero bits at the end of + both numbers. */ + for (i = 0; scale[i] == 0 && frac[i] == 0; i++) + ; + + /* Determine number of bits the scaling factor is misplaced. */ + count_leading_zeros (cnt_h, scale[scalesize - 1]); + + if (cnt_h == 0) + { + /* The highest bit of the scaling factor is already set. So + we only have to remove the trailing empty limbs. */ + if (i > 0) + { + MPN_COPY_INCR (scale, scale + i, scalesize - i); + scalesize -= i; + MPN_COPY_INCR (frac, frac + i, fracsize - i); + fracsize -= i; + } + } + else + { + if (scale[i] != 0) + { + count_trailing_zeros (cnt_l, scale[i]); + if (frac[i] != 0) + { + int cnt_l2; + count_trailing_zeros (cnt_l2, frac[i]); + if (cnt_l2 < cnt_l) + cnt_l = cnt_l2; + } + } + else + count_trailing_zeros (cnt_l, frac[i]); + + /* Now shift the numbers to their optimal position. */ + if (i == 0 && BITS_PER_MP_LIMB - cnt_h > cnt_l) + { + /* We cannot save any memory. So just roll both numbers + so that the scaling factor has its highest bit set. */ + + (void) __mpn_lshift (scale, scale, scalesize, cnt_h); + cy = __mpn_lshift (frac, frac, fracsize, cnt_h); + if (cy != 0) + frac[fracsize++] = cy; + } + else if (BITS_PER_MP_LIMB - cnt_h <= cnt_l) + { + /* We can save memory by removing the trailing zero limbs + and by packing the non-zero limbs which gain another + free one. */ + + (void) __mpn_rshift (scale, scale + i, scalesize - i, + BITS_PER_MP_LIMB - cnt_h); + scalesize -= i + 1; + (void) __mpn_rshift (frac, frac + i, fracsize - i, + BITS_PER_MP_LIMB - cnt_h); + fracsize -= frac[fracsize - i - 1] == 0 ? i + 1 : i; + } + else + { + /* We can only save the memory of the limbs which are zero. + The non-zero parts occupy the same number of limbs. */ + + (void) __mpn_rshift (scale, scale + (i - 1), + scalesize - (i - 1), + BITS_PER_MP_LIMB - cnt_h); + scalesize -= i; + (void) __mpn_rshift (frac, frac + (i - 1), + fracsize - (i - 1), + BITS_PER_MP_LIMB - cnt_h); + fracsize -= frac[fracsize - (i - 1) - 1] == 0 ? i : i - 1; + } + } + } + } + else if (exponent < 0) + { + /* |FP| < 1.0. */ + int exp10 = 0; + int explog = LDBL_MAX_10_EXP_LOG; + const struct mp_power *tens = &_fpioconst_pow10[explog + 1]; + mp_size_t used_limbs = fracsize - 1; + + /* Now shift the input value to its right place. */ + cy = __mpn_lshift (frac, fp_input, fracsize, to_shift); + frac[fracsize++] = cy; + assert (cy == 1 || (frac[fracsize - 2] == 0 && frac[0] == 0)); + + expsign = 1; + exponent = -exponent; + + assert (tens != &_fpioconst_pow10[0]); + do + { + --tens; + + if (exponent >= tens->m_expo) + { + int i, incr, cnt_h, cnt_l; + mp_limb topval[2]; + + /* The __mpn_mul function expects the first argument to be + bigger than the second. */ + if (fracsize < tens->arraysize - 2) + cy = __mpn_mul (tmp, &tens->array[2], tens->arraysize - 2, + frac, fracsize); + else + cy = __mpn_mul (tmp, frac, fracsize, + &tens->array[2], tens->arraysize - 2); + tmpsize = fracsize + tens->arraysize - 2; + if (cy == 0) + --tmpsize; + + count_leading_zeros (cnt_h, tmp[tmpsize - 1]); + incr = (tmpsize - fracsize) * BITS_PER_MP_LIMB + + BITS_PER_MP_LIMB - 1 - cnt_h; + + assert (incr <= tens->p_expo); + + /* If we increased the exponent by exactly 3 we have to test + for overflow. This is done by comparing with 10 shifted + to the right position. */ + if (incr == exponent + 3) + if (cnt_h <= BITS_PER_MP_LIMB - 4) + { + topval[0] = 0; + topval[1] = 10 << (BITS_PER_MP_LIMB - 4 - cnt_h); + } + else + { + topval[0] = 10 << (BITS_PER_MP_LIMB - 4); + topval[1] = 0; + (void) __mpn_lshift (topval, topval, 2, + BITS_PER_MP_LIMB - cnt_h); + } + + /* We have to be careful when multiplying the last factor. + If the result is greater than 1.0 be have to test it + against 10.0. If it is greater or equal to 10.0 the + multiplication was not valid. This is because we cannot + determine the number of bits in the result in advance. */ + if (incr < exponent + 3 + || (incr == exponent + 3 && + (tmp[tmpsize - 1] < topval[1] + || (tmp[tmpsize - 1] == topval[1] + && tmp[tmpsize - 2] < topval[0])))) + { + /* The factor is right. Adapt binary and decimal + exponents. */ + exponent -= incr; + exp10 |= 1 << explog; + + /* If this factor yields a number greater or equal to + 1.0, we must not shift the non-fractional digits down. */ + if (exponent < 0) + cnt_h += -exponent; + + /* Now we optimize the number representation. */ + for (i = 0; tmp[i] == 0; ++i); + if (cnt_h == BITS_PER_MP_LIMB - 1) + { + MPN_COPY (frac, tmp + i, tmpsize - i); + fracsize = tmpsize - i; + } + else + { + count_trailing_zeros (cnt_l, tmp[i]); + + /* Now shift the numbers to their optimal position. */ + if (i == 0 && BITS_PER_MP_LIMB - 1 - cnt_h > cnt_l) + { + /* We cannot save any memory. Just roll the + number so that the leading digit is in a + seperate limb. */ + + cy = __mpn_lshift (frac, tmp, tmpsize, cnt_h + 1); + fracsize = tmpsize + 1; + frac[fracsize - 1] = cy; + } + else if (BITS_PER_MP_LIMB - 1 - cnt_h <= cnt_l) + { + (void) __mpn_rshift (frac, tmp + i, tmpsize - i, + BITS_PER_MP_LIMB - 1 - cnt_h); + fracsize = tmpsize - i; + } + else + { + /* We can only save the memory of the limbs which + are zero. The non-zero parts occupy the same + number of limbs. */ + + (void) __mpn_rshift (frac, tmp + (i - 1), + tmpsize - (i - 1), + BITS_PER_MP_LIMB - 1 - cnt_h); + fracsize = tmpsize - (i - 1); + } + } + used_limbs = fracsize - 1; + } + } + --explog; + } + while (tens != &_fpioconst_pow10[1] && exponent > 0); + /* All factors but 10^-1 are tested now. */ + if (exponent > 0) + { + cy = __mpn_mul_1 (tmp, frac, fracsize, 10); + tmpsize = fracsize; + assert (cy == 0 || tmp[tmpsize - 1] < 20); + + (void) __mpn_rshift (frac, tmp, tmpsize, MIN (4, exponent)); + fracsize = tmpsize; + exp10 |= 1; + assert (frac[fracsize - 1] < 10); + } + exponent = exp10; + } + else + { + /* This is a special case. We don't need a factor because the + numbers are in the range of 0.0 <= fp < 8.0. We simply + shift it to the right place and divide it by 1.0 to get the + leading digit. (Of course this division is not really made.) */ + assert (0 <= exponent && exponent < 3 && + exponent + to_shift < BITS_PER_MP_LIMB); + + /* Now shift the input value to its right place. */ + cy = __mpn_lshift (frac, fp_input, fracsize, (exponent + to_shift)); + frac[fracsize++] = cy; + exponent = 0; + } + + { + int width = info->width; + char *buffer, *startp, *cp; + int chars_needed; + int expscale; + int intdig_max, intdig_no = 0; + int fracdig_min, fracdig_max, fracdig_no = 0; + int dig_max; + int significant; + + if (tolower (info->spec) == 'e') + { + type = info->spec; + intdig_max = 1; + fracdig_min = fracdig_max = info->prec < 0 ? 6 : info->prec; + chars_needed = 1 + 1 + fracdig_max + 1 + 1 + 4; + /* d . ddd e +- ddd */ + dig_max = INT_MAX; /* Unlimited. */ + significant = 1; /* Does not matter here. */ + } + else if (info->spec == 'f') + { + type = 'f'; + fracdig_min = fracdig_max = info->prec < 0 ? 6 : info->prec; + if (expsign == 0) + { + intdig_max = exponent + 1; + /* This can be really big! */ /* XXX Maybe malloc if too big? */ + chars_needed = exponent + 1 + 1 + fracdig_max; + } + else + { + intdig_max = 1; + chars_needed = 1 + 1 + fracdig_max; + } + dig_max = INT_MAX; /* Unlimited. */ + significant = 1; /* Does not matter here. */ + } + else + { + dig_max = info->prec < 0 ? 6 : (info->prec == 0 ? 1 : info->prec); + if ((expsign == 0 && exponent >= dig_max) + || (expsign != 0 && exponent > 4)) + { + type = isupper (info->spec) ? 'E' : 'e'; + fracdig_max = dig_max - 1; + intdig_max = 1; + chars_needed = 1 + 1 + fracdig_max + 1 + 1 + 4; + } + else + { + type = 'f'; + intdig_max = expsign == 0 ? exponent + 1 : 0; + fracdig_max = dig_max - intdig_max; + /* We need space for the significant digits and perhaps for + leading zeros when < 1.0. Pessimistic guess: dig_max. */ + chars_needed = dig_max + dig_max + 1; + } + fracdig_min = info->alt ? fracdig_max : 0; + significant = 0; /* We count significant digits. */ + } + + if (grouping) + /* Guess the number of groups we will make, and thus how + many spaces we need for separator characters. */ + chars_needed += guess_grouping (intdig_max, grouping, thousands_sep); + + /* Allocate buffer for output. We need two more because while rounding + it is possible that we need two more characters in front of all the + other output. */ + buffer = alloca (2 + chars_needed); + cp = startp = buffer + 2; /* Let room for rounding. */ + + /* Do the real work: put digits in allocated buffer. */ + if (expsign == 0 || type != 'f') + { + assert (expsign == 0 || intdig_max == 1); + while (intdig_no < intdig_max) + { + ++intdig_no; + *cp++ = hack_digit (); + } + significant = 1; + if (info->alt + || fracdig_min > 0 + || (fracdig_max > 0 && (fracsize > 1 || frac[0] != 0))) + *cp++ = decimal; + } + else + { + /* |fp| < 1.0 and the selected type is 'f', so put "0." + in the buffer. */ + *cp++ = '0'; + --exponent; + *cp++ = decimal; + } + + /* Generate the needed number of fractional digits. */ + while (fracdig_no < fracdig_min + || (fracdig_no < fracdig_max && (fracsize > 1 || frac[0] != 0))) + { + ++fracdig_no; + *cp = hack_digit (); + if (*cp != '0') + significant = 1; + else if (significant == 0) + { + ++fracdig_max; + if (fracdig_min > 0) + ++fracdig_min; + } + ++cp; + } + + /* Do rounding. */ + digit = hack_digit (); + if (digit > '4') + { + char *tp = cp; + + if (digit == '5') + /* This is the critical case. */ + if (fracsize == 1 && frac[0] == 0) + /* Rest of the number is zero -> round to even. + (IEEE 754-1985 4.1 says this is the default rounding.) */ + if ((*(cp - 1) & 1) == 0) + goto do_expo; + + if (fracdig_no > 0) + { + /* Process fractional digits. Terminate if not rounded or + radix character is reached. */ + while (*--tp != decimal && *tp == '9') + *tp = '0'; + if (*tp != decimal) + /* Round up. */ + (*tp)++; + } + + if (fracdig_no == 0 || *tp == decimal) + { + /* Round the integer digits. */ + if (*(tp - 1) == decimal) + --tp; + + while (--tp >= startp && *tp == '9') + *tp = '0'; + + if (tp >= startp) + /* Round up. */ + (*tp)++; + else + /* It is more citical. All digits were 9's. */ + { + if (type != 'f') + { + *startp = '1'; + exponent += expsign == 0 ? 1 : -1; + } + else if (intdig_no == dig_max) + { + /* This is the case where for type %g the number fits + really in the range for %f output but after rounding + the number of digits is too big. */ + *--startp = decimal; + *--startp = '1'; + + if (info->alt || fracdig_no > 0) + { + /* Overwrite the old radix character. */ + startp[intdig_no + 2] = '0'; + ++fracdig_no; + } + + fracdig_no += intdig_no; + intdig_no = 1; + fracdig_max = intdig_max - intdig_no; + ++exponent; + /* Now we must print the exponent. */ + type = isupper (info->spec) ? 'E' : 'e'; + } + else + { + /* We can simply add another another digit before the + radix. */ + *--startp = '1'; + ++intdig_no; + } + + /* While rounding the number of digits can change. + If the number now exceeds the limits remove some + fractional digits. */ + if (intdig_no + fracdig_no > dig_max) + { + cp -= intdig_no + fracdig_no - dig_max; + fracdig_no -= intdig_no + fracdig_no - dig_max; + } + } + } + } + + do_expo: + /* Now remove unnecessary '0' at the end of the string. */ + while (fracdig_no > fracdig_min && *(cp - 1) == '0') + { + --cp; + --fracdig_no; + } + /* If we eliminate all fractional digits we perhaps also can remove + the radix character. */ + if (fracdig_no == 0 && !info->alt && *(cp - 1) == decimal) + --cp; + + if (grouping) + /* Add in separator characters, overwriting the same buffer. */ + cp = group_number (startp, cp, intdig_no, grouping, thousands_sep); + + /* Write the exponent if it is needed. */ + if (type != 'f') + { + *cp++ = type; + *cp++ = expsign ? '-' : '+'; + + /* Find the magnitude of the exponent. */ + expscale = 10; + while (expscale <= exponent) + expscale *= 10; + + if (exponent < 10) + /* Exponent always has at least two digits. */ + *cp++ = '0'; + else + do + { + expscale /= 10; + *cp++ = '0' + (exponent / expscale); + exponent %= expscale; + } + while (expscale > 10); + *cp++ = '0' + exponent; + } + + /* Compute number of characters which must be filled with the padding + character. */ + if (is_neg || info->showsign || info->space) + --width; + width -= cp - startp; + + if (!info->left && info->pad != '0' && width > 0) + PADN (info->pad, width); + + if (is_neg) + outchar ('-'); + else if (info->showsign) + outchar ('+'); + else if (info->space) + outchar (' '); + + if (!info->left && info->pad == '0' && width > 0) + PADN ('0', width); + + PRINT (startp, cp - startp); + + if (info->left && width > 0) + PADN (info->pad, width); + } + return done; +} + +/* Return the number of extra grouping characters that will be inserted + into a number with INTDIG_MAX integer digits. */ + +static unsigned int +guess_grouping (unsigned int intdig_max, const char *grouping, wchar_t sepchar) +{ + unsigned int groups; + + /* We treat all negative values like CHAR_MAX. */ + + if (*grouping == CHAR_MAX || *grouping <= 0) + /* No grouping should be done. */ + return 0; + + groups = 0; + while (intdig_max > (unsigned int) *grouping) + { + ++groups; + intdig_max -= *grouping++; + + if (*grouping == CHAR_MAX || *grouping < 0) + /* No more grouping should be done. */ + break; + else if (*grouping == 0) + { + /* Same grouping repeats. */ + groups += intdig_max / grouping[-1]; + break; + } + } + + return groups; +} + +/* Group the INTDIG_NO integer digits of the number in [BUF,BUFEND). + There is guaranteed enough space past BUFEND to extend it. + Return the new end of buffer. */ + +static char * +group_number (char *buf, char *bufend, unsigned int intdig_no, + const char *grouping, wchar_t thousands_sep) +{ + unsigned int groups = guess_grouping (intdig_no, grouping, thousands_sep); + char *p; + + if (groups == 0) + return bufend; + + /* Move the fractional part down. */ + memmove (buf + intdig_no + groups, buf + intdig_no, + bufend - (buf + intdig_no)); + + p = buf + intdig_no + groups - 1; + do + { + unsigned int len = *grouping++; + do + *p-- = buf[--intdig_no]; + while (--len > 0); + *p-- = thousands_sep; + + if (*grouping == CHAR_MAX || *grouping < 0) + /* No more grouping should be done. */ + break; + else if (*grouping == 0) + /* Same grouping repeats. */ + --grouping; + } while (intdig_no > (unsigned int) *grouping); + + /* Copy the remaining ungrouped digits. */ + do + *p-- = buf[--intdig_no]; + while (p > buf); + + return bufend + groups; +} diff --git a/stdio-common/psignal.c b/stdio-common/psignal.c new file mode 100644 index 0000000000..8997a2ecdf --- /dev/null +++ b/stdio-common/psignal.c @@ -0,0 +1,49 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdio.h> +#include <signal.h> + + +#ifndef HAVE_GNU_LD +#define _sys_siglist sys_siglist +#endif + +/* Defined in sys_siglist.c. */ +extern CONST char *CONST _sys_siglist[]; + + +/* Print out on stderr a line consisting of the test in S, a colon, a space, + a message describing the meaning of the signal number SIG and a newline. + If S is NULL or "", the colon and space are omitted. */ +void +DEFUN(psignal, (sig, s), int sig AND register CONST char *s) +{ + CONST char *colon; + + if (s == NULL || s == '\0') + s = colon = ""; + else + colon = ": "; + + if (sig >= 0 && sig < NSIG) + (void) fprintf(stderr, "%s%s%s\n", s, colon, _sys_siglist[sig]); + else + (void) fprintf(stderr, "%s%sUnknown signal %d\n", s, colon, sig); +} diff --git a/stdio-common/putw.c b/stdio-common/putw.c new file mode 100644 index 0000000000..1b70baeeaf --- /dev/null +++ b/stdio-common/putw.c @@ -0,0 +1,31 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdio.h> + + +/* Write the word (int) W to STREAM. */ +int +DEFUN(putw, (w, stream), int w AND FILE *stream) +{ + /* Is there a better way? */ + if (fwrite((CONST PTR) &w, sizeof(w), 1, stream) < 1) + return(EOF); + return(0); +} diff --git a/stdio-common/reg-printf.c b/stdio-common/reg-printf.c new file mode 100644 index 0000000000..95d7a1f3c9 --- /dev/null +++ b/stdio-common/reg-printf.c @@ -0,0 +1,47 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <errno.h> +#include <limits.h> +#include <printf.h> + +/* Array of functions indexed by format character. */ +static printf_function *printf_funcs[UCHAR_MAX + 1]; +printf_arginfo_function *__printf_arginfo_table[UCHAR_MAX + 1]; + +printf_function **__printf_function_table; + +/* Register FUNC to be called to format SPEC specifiers. */ +int +DEFUN(register_printf_function, (spec, converter, arginfo), + int spec AND printf_function converter AND + printf_arginfo_function arginfo) +{ + if (spec < 0 || spec > (int) UCHAR_MAX) + { + errno = EINVAL; + return -1; + } + + __printf_function_table = printf_funcs; + __printf_arginfo_table[spec] = arginfo; + printf_funcs[spec] = converter; + + return 0; +} diff --git a/stdio-common/scanf.c b/stdio-common/scanf.c new file mode 100644 index 0000000000..cf43363958 --- /dev/null +++ b/stdio-common/scanf.c @@ -0,0 +1,40 @@ +/* Copyright (C) 1991, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <stdarg.h> +#include <stdio.h> + +#ifdef USE_IN_LIBIO +# define vscanf _IO_vscanf +#endif + +/* Read formatted input from stdin according to the format string FORMAT. */ +/* VARARGS1 */ +int +scanf (format) + const char *format; +{ + va_list arg; + int done; + + va_start (arg, format); + done = vscanf (format, arg); + va_end (arg); + + return done; +} diff --git a/stdio-common/snprintf.c b/stdio-common/snprintf.c new file mode 100644 index 0000000000..00b85f3175 --- /dev/null +++ b/stdio-common/snprintf.c @@ -0,0 +1,43 @@ +/* Copyright (C) 1991, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <stdarg.h> +#include <stdio.h> + +#ifdef USE_IN_LIBIO +# define vsnprintf _IO_vsnprintf +#endif + +/* Write formatted output into S, according to the format + string FORMAT, writing no more than MAXLEN characters. */ +/* VARARGS3 */ +int +snprintf (s, maxlen, format) + char *s; + size_t maxlen; + const char *format; +{ + va_list arg; + int done; + + va_start (arg, format); + done = vsnprintf (s, maxlen, format, arg); + va_end (arg); + + return done; +} diff --git a/stdio-common/sprintf.c b/stdio-common/sprintf.c new file mode 100644 index 0000000000..9cfc89cb84 --- /dev/null +++ b/stdio-common/sprintf.c @@ -0,0 +1,41 @@ +/* Copyright (C) 1991, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <stdarg.h> +#include <stdio.h> + +#ifdef USE_IN_LIBIO +# define vsprintf _IO_vsprintf +#endif + +/* Write formatted output into S, according to the format string FORMAT. */ +/* VARARGS2 */ +int +sprintf (s, format) + char *s; + const char *format; +{ + va_list arg; + int done; + + va_start (arg, format); + done = vsprintf (s, format, arg); + va_end (arg); + + return done; +} diff --git a/stdio-common/sscanf.c b/stdio-common/sscanf.c new file mode 100644 index 0000000000..794a3ce628 --- /dev/null +++ b/stdio-common/sscanf.c @@ -0,0 +1,41 @@ +/* Copyright (C) 1991, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <stdarg.h> +#include <stdio.h> + +#ifdef USE_IN_LIBIO +# define __vsscanf _IO_vsscanf +#endif + +/* Read formatted input from S, according to the format string FORMAT. */ +/* VARARGS2 */ +int +sscanf (s, format) + const char *s; + const char *format; +{ + va_list arg; + int done; + + va_start (arg, format); + done = __vsscanf (s, format, arg); + va_end (arg); + + return done; +} diff --git a/stdio-common/tempnam.c b/stdio-common/tempnam.c new file mode 100644 index 0000000000..14988a8656 --- /dev/null +++ b/stdio-common/tempnam.c @@ -0,0 +1,50 @@ +/* Copyright (C) 1991, 1993 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +/* Generate a unique temporary filename using up to five characters of PFX + if it is not NULL. The directory to put this file in is searched for + as follows: First the environment variable "TMPDIR" is checked. + If it contains the name of a writable directory, that directory is used. + If not and if DIR is not NULL, that value is checked. If that fails, + P_tmpdir is tried and finally "/tmp". The storage for the filename + is allocated by `malloc'. */ +char * +DEFUN(tempnam, (dir, pfx), CONST char *dir AND CONST char *pfx) +{ + size_t len; + register char *s; + register char *t = __stdio_gen_tempname(dir, pfx, 1, &len, (FILE **) NULL); + + if (t == NULL) + return NULL; + + s = (char *) malloc(len); + if (s == NULL) + return NULL; + + (void) memcpy(s, t, len); + return s; +} diff --git a/stdio-common/temptest.c b/stdio-common/temptest.c new file mode 100644 index 0000000000..374719896a --- /dev/null +++ b/stdio-common/temptest.c @@ -0,0 +1,31 @@ +#include <ansidecl.h> +#include <stdio.h> +#include <string.h> + +char *files[500]; + +int +main () +{ + char *fn; + FILE *fp; + int i; + + for (i = 0; i < 500; i++) { + fn = __stdio_gen_tempname((CONST char *) NULL, + "file", 0, (size_t *) NULL, (FILE **) NULL); + if (fn == NULL) { + printf ("__stdio_gen_tempname failed\n"); + exit (1); + } + files[i] = strdup (fn); + printf ("file: %s\n", fn); + fp = fopen (fn, "w"); + fclose (fp); + } + + for (i = 0; i < 500; i++) + remove (files[i]); + + exit (0); +} diff --git a/stdio-common/test-fseek.c b/stdio-common/test-fseek.c new file mode 100644 index 0000000000..d56c669a54 --- /dev/null +++ b/stdio-common/test-fseek.c @@ -0,0 +1,67 @@ +#include <ansidecl.h> +#include <stdio.h> + +#define TESTFILE "/tmp/test.dat" + +int +main __P((void)) +{ + FILE *fp; + int i, j; + + puts ("\nFile seek test"); + fp = fopen (TESTFILE, "w"); + if (fp == NULL) + { + perror (TESTFILE); + return 1; + } + + for (i = 0; i < 256; i++) + putc (i, fp); + if (freopen (TESTFILE, "r", fp) != fp) + { + perror ("Cannot open file for reading"); + return 1; + } + + for (i = 1; i <= 255; i++) + { + printf ("%3d\n", i); + fseek (fp, (long) -i, SEEK_END); + if ((j = getc (fp)) != 256 - i) + { + printf ("SEEK_END failed %d\n", j); + break; + } + if (fseek (fp, (long) i, SEEK_SET)) + { + puts ("Cannot SEEK_SET"); + break; + } + if ((j = getc (fp)) != i) + { + printf ("SEEK_SET failed %d\n", j); + break; + } + if (fseek (fp, (long) i, SEEK_SET)) + { + puts ("Cannot SEEK_SET"); + break; + } + if (fseek (fp, (long) (i >= 128 ? -128 : 128), SEEK_CUR)) + { + puts ("Cannot SEEK_CUR"); + break; + } + if ((j = getc (fp)) != (i >= 128 ? i - 128 : i + 128)) + { + printf ("SEEK_CUR failed %d\n", j); + break; + } + } + fclose (fp); + + puts ((i > 255) ? "Test succeeded." : "Test FAILED!"); + return (i > 255) ? 0 : 1; +} diff --git a/stdio-common/test-fwrite.c b/stdio-common/test-fwrite.c new file mode 100644 index 0000000000..cc6cdf038e --- /dev/null +++ b/stdio-common/test-fwrite.c @@ -0,0 +1,68 @@ +#include <stdio.h> +#include <string.h> + +int +main () +{ + FILE *f = tmpfile (); + char obuf[99999], ibuf[sizeof obuf]; + char *line; + size_t linesz; + + if (! f) + { + perror ("tmpfile"); + return 1; + } + + if (fputs ("line\n", f) == EOF) + { + perror ("fputs"); + return 1; + } + + memset (obuf, 'z', sizeof obuf); + memset (ibuf, 'y', sizeof ibuf); + + if (fwrite (obuf, sizeof obuf, 1, f) != 1) + { + perror ("fwrite"); + return 1; + } + + rewind (f); + + line = NULL; + linesz = 0; + if (getline (&line, &linesz, f) != 5) + { + perror ("getline"); + return 1; + } + if (strcmp (line, "line\n")) + { + puts ("Lines differ. Test FAILED!"); + return 1; + } + + if (fread (ibuf, sizeof ibuf, 1, f) != 1) + { + perror ("fread"); + return 1; + } + + if (memcmp (ibuf, obuf, sizeof ibuf)) + { + puts ("Buffers differ. Test FAILED!"); + return 1; + } + + asprintf (&line, "\ +GDB is free software and you are welcome to distribute copies of it\n\ + under certain conditions; type \"show copying\" to see the conditions.\n\ +There is absolutely no warranty for GDB; type \"show warranty\" for details.\n\ +"); + + puts ("Test succeeded."); + return 0; +} diff --git a/stdio-common/test-popen.c b/stdio-common/test-popen.c new file mode 100644 index 0000000000..b452f3f63c --- /dev/null +++ b/stdio-common/test-popen.c @@ -0,0 +1,67 @@ +#include <ansidecl.h> +#include <stdio.h> +#include <stdlib.h> + +void +DEFUN(write_data, (stream), FILE *stream) +{ + int i; + for (i=0; i<100; i++) + fprintf (stream, "%d\n", i); + if (ferror (stream)) { + fprintf (stderr, "Output to stream failed.\n"); + exit (1); + } +} + +void +DEFUN(read_data, (stream), FILE *stream) +{ + int i, j; + + for (i=0; i<100; i++) + { + if (fscanf (stream, "%d\n", &j) != 1 || j != i) + { + if (ferror (stream)) + perror ("fscanf"); + puts ("Test FAILED!"); + exit (1); + } + } +} + +int +DEFUN_VOID(main) +{ + FILE *output, *input; + int wstatus, rstatus; + + output = popen ("/bin/cat >/tmp/tstpopen.tmp", "w"); + if (output == NULL) + { + perror ("popen"); + puts ("Test FAILED!"); + exit (1); + } + write_data (output); + wstatus = pclose (output); + printf ("writing pclose returned %d\n", wstatus); + input = popen ("/bin/cat /tmp/tstpopen.tmp", "r"); + if (input == NULL) + { + perror ("/tmp/tstpopen.tmp"); + puts ("Test FAILED!"); + exit (1); + } + read_data (input); + rstatus = pclose (input); + printf ("reading pclose returned %d\n", rstatus); + + puts (wstatus | rstatus ? "Test FAILED!" : "Test succeeded."); + exit (wstatus | rstatus); +} + + + + diff --git a/stdio-common/test_rdwr.c b/stdio-common/test_rdwr.c new file mode 100644 index 0000000000..f987f16cd4 --- /dev/null +++ b/stdio-common/test_rdwr.c @@ -0,0 +1,130 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +int +DEFUN(main, (argc, argv), int argc AND char **argv) +{ + static CONST char hello[] = "Hello, world.\n"; + static CONST char replace[] = "Hewwo, world.\n"; + static CONST size_t replace_from = 2, replace_to = 4; + char filename[FILENAME_MAX]; + char *name = strrchr(*argv, '/'); + char buf[BUFSIZ]; + FILE *f; + int lose = 0; + + if (name != NULL) + ++name; + else + name = *argv; + + (void) sprintf(filename, "/tmp/%s.test", name); + + f = fopen(filename, "w+"); + if (f == NULL) + { + perror(filename); + exit(1); + } + + (void) fputs(hello, f); + rewind(f); + (void) fgets(buf, sizeof(buf), f); + rewind(f); + (void) fputs(buf, f); + rewind(f); + { + register size_t i; + for (i = 0; i < replace_from; ++i) + { + int c = getc(f); + if (c == EOF) + { + printf("EOF at %u.\n", i); + lose = 1; + break; + } + else if (c != hello[i]) + { + printf("Got '%c' instead of '%c' at %u.\n", + (unsigned char) c, hello[i], i); + lose = 1; + break; + } + } + } + + { + long int where = ftell(f); + if (where == replace_from) + { + register size_t i; + for (i = replace_from; i < replace_to; ++i) + if (putc(replace[i], f) == EOF) + { + printf("putc('%c') got %s at %u.\n", + replace[i], strerror(errno), i); + lose = 1; + break; + } + } + else if (where == -1L) + { + printf("ftell got %s (should be at %u).\n", + strerror(errno), replace_from); + lose = 1; + } + else + { + printf("ftell returns %u; should be %u.\n", where, replace_from); + lose = 1; + } + } + + if (!lose) + { + rewind(f); + if (fgets(buf, sizeof(buf), f) == NULL) + { + printf("fgets got %s.\n", strerror(errno)); + lose = 1; + } + else if (strcmp(buf, replace)) + { + printf("Read \"%s\" instead of \"%s\".\n", buf, replace); + lose = 1; + } + } + + if (lose) + printf("Test FAILED! Losing file is \"%s\".\n", filename); + else + { + (void) remove(filename); + puts("Test succeeded."); + } + + exit(lose ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/stdio-common/tmpfile.c b/stdio-common/tmpfile.c new file mode 100644 index 0000000000..dfe11ada50 --- /dev/null +++ b/stdio-common/tmpfile.c @@ -0,0 +1,43 @@ +/* Copyright (C) 1991, 1993 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdio.h> + + +/* This returns a new stream opened on a temporary file (generated + by tmpnam) The file is opened with mode "w+b" (binary read/write). + If we couldn't generate a unique filename or the file couldn't + be opened, NULL is returned. */ +FILE * +DEFUN_VOID(tmpfile) +{ + char *filename; + FILE *f; + + filename = __stdio_gen_tempname ((char *) NULL, "tmpf", 0, + (size_t *) NULL, &f); + if (filename == NULL) + return NULL; + + /* Note that this relies on the Unix semantics that + a file is not really removed until it is closed. */ + (void) remove (filename); + + return f; +} diff --git a/stdio-common/tmpnam.c b/stdio-common/tmpnam.c new file mode 100644 index 0000000000..88dd0a4ca5 --- /dev/null +++ b/stdio-common/tmpnam.c @@ -0,0 +1,42 @@ +/* Copyright (C) 1991, 1993 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + + +/* Generate a unique filename in P_tmpdir. */ +char * +DEFUN(tmpnam, (s), register char *s) +{ + register char *t = __stdio_gen_tempname((CONST char *) NULL, + (CONST char *) NULL, 0, + (size_t *) NULL, (FILE **) NULL); + + if (t == NULL) + return NULL; + + if (s != NULL) + (void) strcpy(s, t); + else + s = t; + + return s; +} diff --git a/stdio-common/tst-fileno.c b/stdio-common/tst-fileno.c new file mode 100644 index 0000000000..81945f7b44 --- /dev/null +++ b/stdio-common/tst-fileno.c @@ -0,0 +1,37 @@ +/* Copyright (C) 1994 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdio.h> +#include <unistd.h> + +static int +DEFUN(check, (name, stream, fd), CONST char *name AND FILE *stream AND int fd) +{ + int sfd = fileno (stream); + printf ("(fileno (%s) = %d) %c= %d\n", name, sfd, sfd == fd ? '=' : '!', fd); + return sfd != fd; +} + +int +DEFUN_VOID(main) +{ + exit (check ("stdin", stdin, STDIN_FILENO) || + check ("stdout", stdout, STDOUT_FILENO) || + check ("stderr", stderr, STDERR_FILENO)); +} diff --git a/stdio-common/tst-printf.c b/stdio-common/tst-printf.c new file mode 100644 index 0000000000..c177da18b2 --- /dev/null +++ b/stdio-common/tst-printf.c @@ -0,0 +1,298 @@ +/* Copyright (C) 1991, 1992, 1993, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#ifdef BSD +#include </usr/include/stdio.h> +#define EXIT_SUCCESS 0 +#else +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#endif + +#include <float.h> + + +void +DEFUN(fmtchk, (fmt), CONST char *fmt) +{ + (void) fputs(fmt, stdout); + (void) printf(":\t`"); + (void) printf(fmt, 0x12); + (void) printf("'\n"); +} + +void +DEFUN(fmtst1chk, (fmt), CONST char *fmt) +{ + (void) fputs(fmt, stdout); + (void) printf(":\t`"); + (void) printf(fmt, 4, 0x12); + (void) printf("'\n"); +} + +void +DEFUN(fmtst2chk, (fmt), CONST char *fmt) +{ + (void) fputs(fmt, stdout); + (void) printf(":\t`"); + (void) printf(fmt, 4, 4, 0x12); + (void) printf("'\n"); +} + +/* This page is covered by the following copyright: */ + +/* (C) Copyright C E Chew + * + * Feel free to copy, use and distribute this software provided: + * + * 1. you do not pretend that you wrote it + * 2. you leave this copyright notice intact. + */ + +/* + * Extracted from exercise.c for glibc-1.05 bug report by Bruce Evans. + */ + +#define DEC -123 +#define INT 255 +#define UNS (~0) + +/* Formatted Output Test + * + * This exercises the output formatting code. + */ + +void +DEFUN_VOID(fp_test) +{ + int i, j, k, l; + char buf[7]; + char *prefix = buf; + char tp[20]; + + puts("\nFormatted output test"); + printf("prefix 6d 6o 6x 6X 6u\n"); + strcpy(prefix, "%"); + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + for (k = 0; k < 2; k++) { + for (l = 0; l < 2; l++) { + strcpy(prefix, "%"); + if (i == 0) strcat(prefix, "-"); + if (j == 0) strcat(prefix, "+"); + if (k == 0) strcat(prefix, "#"); + if (l == 0) strcat(prefix, "0"); + printf("%5s |", prefix); + strcpy(tp, prefix); + strcat(tp, "6d |"); + printf(tp, DEC); + strcpy(tp, prefix); + strcat(tp, "6o |"); + printf(tp, INT); + strcpy(tp, prefix); + strcat(tp, "6x |"); + printf(tp, INT); + strcpy(tp, prefix); + strcat(tp, "6X |"); + printf(tp, INT); + strcpy(tp, prefix); + strcat(tp, "6u |"); + printf(tp, UNS); + printf("\n"); + } + } + } + } + printf("%10s\n", (char *) NULL); + printf("%-10s\n", (char *) NULL); +} + +int +DEFUN_VOID(main) +{ + static char shortstr[] = "Hi, Z."; + static char longstr[] = "Good morning, Doctor Chandra. This is Hal. \ +I am ready for my first lesson today."; + + fmtchk("%.4x"); + fmtchk("%04x"); + fmtchk("%4.4x"); + fmtchk("%04.4x"); + fmtchk("%4.3x"); + fmtchk("%04.3x"); + + fmtst1chk("%.*x"); + fmtst1chk("%0*x"); + fmtst2chk("%*.*x"); + fmtst2chk("%0*.*x"); + +#ifndef BSD + printf("bad format:\t\"%z\"\n"); + printf("nil pointer (padded):\t\"%10p\"\n", (PTR) NULL); +#endif + + printf("decimal negative:\t\"%d\"\n", -2345); + printf("octal negative:\t\"%o\"\n", -2345); + printf("hex negative:\t\"%x\"\n", -2345); + printf("long decimal number:\t\"%ld\"\n", -123456L); + printf("long octal negative:\t\"%lo\"\n", -2345L); + printf("long unsigned decimal number:\t\"%lu\"\n", -123456L); + printf("zero-padded LDN:\t\"%010ld\"\n", -123456L); + printf("left-adjusted ZLDN:\t\"%-010ld\"\n", -123456); + printf("space-padded LDN:\t\"%10ld\"\n", -123456L); + printf("left-adjusted SLDN:\t\"%-10ld\"\n", -123456L); + + printf("zero-padded string:\t\"%010s\"\n", shortstr); + printf("left-adjusted Z string:\t\"%-010s\"\n", shortstr); + printf("space-padded string:\t\"%10s\"\n", shortstr); + printf("left-adjusted S string:\t\"%-10s\"\n", shortstr); + printf("null string:\t\"%s\"\n", (char *)NULL); + printf("limited string:\t\"%.22s\"\n", longstr); + + printf("e-style >= 1:\t\"%e\"\n", 12.34); + printf("e-style >= .1:\t\"%e\"\n", 0.1234); + printf("e-style < .1:\t\"%e\"\n", 0.001234); + printf("e-style big:\t\"%.60e\"\n", 1e20); + printf ("e-style == .1:\t\"%e\"\n", 0.1); + printf("f-style >= 1:\t\"%f\"\n", 12.34); + printf("f-style >= .1:\t\"%f\"\n", 0.1234); + printf("f-style < .1:\t\"%f\"\n", 0.001234); + printf("g-style >= 1:\t\"%g\"\n", 12.34); + printf("g-style >= .1:\t\"%g\"\n", 0.1234); + printf("g-style < .1:\t\"%g\"\n", 0.001234); + printf("g-style big:\t\"%.60g\"\n", 1e20); + + printf (" %6.5f\n", .099999999860301614); + printf (" %6.5f\n", .1); + printf ("x%5.4fx\n", .5); + + printf ("%#03x\n", 1); + + { + double d = FLT_MIN; + int niter = 17; + + while (niter-- != 0) + printf ("%.17e\n", d / 2); + fflush (stdout); + } + + printf ("%15.5e\n", 4.9406564584124654e-324); + +#define FORMAT "|%12.4f|%12.4e|%12.4g|\n" + printf (FORMAT, 0.0, 0.0, 0.0); + printf (FORMAT, 1.0, 1.0, 1.0); + printf (FORMAT, -1.0, -1.0, -1.0); + printf (FORMAT, 100.0, 100.0, 100.0); + printf (FORMAT, 1000.0, 1000.0, 1000.0); + printf (FORMAT, 10000.0, 10000.0, 10000.0); + printf (FORMAT, 12345.0, 12345.0, 12345.0); + printf (FORMAT, 100000.0, 100000.0, 100000.0); + printf (FORMAT, 123456.0, 123456.0, 123456.0); +#undef FORMAT + + { + char buf[20]; + printf ("snprintf (\"%%30s\", \"foo\") == %d, \"%.*s\"\n", + snprintf (buf, sizeof (buf), "%30s", "foo"), sizeof (buf), buf); + } + + fp_test (); + + printf ("%e should be 1.234568e+06\n", 1234567.8); + printf ("%f should be 1234567.800000\n", 1234567.8); + printf ("%g should be 1.23457e+06\n", 1234567.8); + printf ("%g should be 123.456\n", 123.456); + printf ("%g should be 1e+06\n", 1000000.0); + printf ("%g should be 10\n", 10.0); + printf ("%g should be 0.02\n", 0.02); + + { + double x=1.0; + printf("%.17f\n",(1.0/x/10.0+1.0)*x-x); + } + + puts ("--- Should be no further output. ---"); + rfg1 (); + rfg2 (); + + exit(EXIT_SUCCESS); +} + +rfg1 () +{ + char buf[100]; + + sprintf (buf, "%5.s", "xyz"); + if (strcmp (buf, " ") != 0) + printf ("got: '%s', expected: '%s'\n", buf, " "); + sprintf (buf, "%5.f", 33.3); + if (strcmp (buf, " 33") != 0) + printf ("got: '%s', expected: '%s'\n", buf, " 33"); + sprintf (buf, "%8.e", 33.3e7); + if (strcmp (buf, " 3e+08") != 0) + printf ("got: '%s', expected: '%s'\n", buf, " 3e+08"); + sprintf (buf, "%8.E", 33.3e7); + if (strcmp (buf, " 3E+08") != 0) + printf ("got: '%s', expected: '%s'\n", buf, " 3E+08"); + sprintf (buf, "%.g", 33.3); + if (strcmp (buf, "3e+01") != 0) + printf ("got: '%s', expected: '%s'\n", buf, "3e+01"); + sprintf (buf, "%.G", 33.3); + if (strcmp (buf, "3E+01") != 0) + printf ("got: '%s', expected: '%s'\n", buf, "3E+01"); + return 0; +} + +rfg2 () +{ + int prec; + char buf[100]; + + prec = 0; + sprintf (buf, "%.*g", prec, 3.3); + if (strcmp (buf, "3") != 0) + printf ("got: '%s', expected: '%s'\n", buf, "3"); + prec = 0; + sprintf (buf, "%.*G", prec, 3.3); + if (strcmp (buf, "3") != 0) + printf ("got: '%s', expected: '%s'\n", buf, "3"); + prec = 0; + sprintf (buf, "%7.*G", prec, 3.33); + if (strcmp (buf, " 3") != 0) + printf ("got: '%s', expected: '%s'\n", buf, " 3"); + prec = 3; + sprintf (buf, "%04.*o", prec, 33); + if (strcmp (buf, " 041") != 0) + printf ("got: '%s', expected: '%s'\n", buf, " 041"); + prec = 7; + sprintf (buf, "%09.*u", prec, 33); + if (strcmp (buf, " 0000033") != 0) + printf ("got: '%s', expected: '%s'\n", buf, " 0000033"); + prec = 3; + sprintf (buf, "%04.*x", prec, 33); + if (strcmp (buf, " 021") != 0) + printf ("got: '%s', expected: '%s'\n", buf, " 021"); + prec = 3; + sprintf (buf, "%04.*X", prec, 33); + if (strcmp (buf, " 021") != 0) + printf ("got: '%s', expected: '%s'\n", buf, " 021"); + return 0; +} diff --git a/stdio-common/tstgetln.c b/stdio-common/tstgetln.c new file mode 100644 index 0000000000..ea8ea817da --- /dev/null +++ b/stdio-common/tstgetln.c @@ -0,0 +1,46 @@ +/* Copyright (C) 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdio.h> + +int +DEFUN_VOID(main) +{ + char *buf = NULL; + size_t size = 0; + ssize_t len; + + while ((len = getline (&buf, &size, stdin)) != -1) + { + printf ("bufsize %u; read %d: ", size, len); + if (fwrite (buf, len, 1, stdout) != 1) + { + perror ("fwrite"); + return 1; + } + } + + if (ferror (stdin)) + { + perror ("getline"); + return 1; + } + + return 0; +} diff --git a/stdio-common/tstgetln.input b/stdio-common/tstgetln.input new file mode 100644 index 0000000000..d04ed5bf78 --- /dev/null +++ b/stdio-common/tstgetln.input @@ -0,0 +1,3 @@ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy +z diff --git a/stdio-common/tstscanf.c b/stdio-common/tstscanf.c new file mode 100644 index 0000000000..53d4b0ac47 --- /dev/null +++ b/stdio-common/tstscanf.c @@ -0,0 +1,100 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#ifdef BSD +#include </usr/include/stdio.h> +#else +#include <stdio.h> +#endif +#include <stdlib.h> +#include <string.h> + + +int +DEFUN(main, (argc, argv), int argc AND char **argv) +{ + char buf[BUFSIZ]; + FILE *in = stdin, *out = stdout; + + if (argc == 2 && !strcmp (argv[1], "-opipe")) + { + out = popen ("/bin/cat", "w"); + if (out == NULL) + { + perror ("popen: /bin/cat"); + exit (EXIT_FAILURE); + } + } + else if (argc == 3 && !strcmp (argv[1], "-ipipe")) + { + sprintf (buf, "/bin/cat %s", argv[2]); + in = popen (buf, "r"); + } + + { + char name[50]; + fprintf (out, + "sscanf (\"thompson\", \"%%s\", name) == %d, name == \"%s\"\n", + sscanf ("thompson", "%s", name), + name); + } + + fputs ("Testing scanf (vfscanf)\n", out); + + fputs ("Test 1:\n", out); + { + int n, i; + float x; + char name[50]; + n = fscanf (in, "%d%f%s", &i, &x, name); + fprintf (out, "n = %d, i = %d, x = %f, name = \"%.50s\"\n", + n, i, x, name); + } + fprintf (out, "Residual: \"%s\"\n", fgets (buf, sizeof (buf), in)); + fputs ("Test 2:\n", out); + { + int i; + float x; + char name[50]; + (void) fscanf (in, "%2d%f%*d %[0123456789]", &i, &x, name); + fprintf (out, "i = %d, x = %f, name = \"%.50s\"\n", i, x, name); + } + fprintf (out, "Residual: \"%s\"\n", fgets (buf, sizeof (buf), in)); + fputs ("Test 3:\n", out); + { + float quant; + char units[21], item[21]; + while (!feof (in) && !ferror (in)) + { + int count; + quant = 0.0; + units[0] = item[0] = '\0'; + count = fscanf (in, "%f%20s of %20s", &quant, units, item); + (void) fscanf (in, "%*[^\n]"); + fprintf (out, "count = %d, quant = %f, item = %.21s, units = %.21s\n", + count, quant, item, units); + } + } + fprintf (out, "Residual: \"%s\"\n", fgets (buf, sizeof (buf), in)); + + if (out != stdout) + pclose (out); + + exit(EXIT_SUCCESS); +} diff --git a/stdio-common/tstscanf.input b/stdio-common/tstscanf.input new file mode 100644 index 0000000000..26158652dd --- /dev/null +++ b/stdio-common/tstscanf.input @@ -0,0 +1,7 @@ +25 54.32E-1 thompson +56789 0123 56a72 +2 quarts of oil +-12.8degrees Celsius +lots of luck +10.0LBS of fertilizer +100ergs of energy diff --git a/stdio-common/vasprintf.c b/stdio-common/vasprintf.c new file mode 100644 index 0000000000..d2ad6b1da6 --- /dev/null +++ b/stdio-common/vasprintf.c @@ -0,0 +1,86 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + + +/* Enlarge STREAM's buffer. */ +static void +DEFUN(enlarge_buffer, (stream, c), + register FILE *stream AND int c) +{ + ptrdiff_t bufp_offset = stream->__bufp - stream->__buffer; + char *newbuf; + + stream->__bufsize += 100; + newbuf = (char *) realloc ((PTR) stream->__buffer, stream->__bufsize); + if (newbuf == NULL) + { + free ((PTR) stream->__buffer); + stream->__buffer = stream->__bufp + = stream->__put_limit = stream->__get_limit = NULL; + stream->__error = 1; + } + else + { + stream->__buffer = newbuf; + stream->__bufp = stream->__buffer + bufp_offset; + stream->__get_limit = stream->__put_limit; + stream->__put_limit = stream->__buffer + stream->__bufsize; + if (c != EOF) + *stream->__bufp++ = (unsigned char) c; + } +} + +/* Write formatted output from FORMAT to a string which is + allocated with malloc and stored in *STRING_PTR. */ +int +DEFUN(vasprintf, (string_ptr, format, args), + char **string_ptr AND CONST char *format AND va_list args) +{ + FILE f; + int done; + + memset ((PTR) &f, 0, sizeof (f)); + f.__magic = _IOMAGIC; + f.__bufsize = 100; + f.__buffer = (char *) malloc (f.__bufsize); + if (f.__buffer == NULL) + return -1; + f.__bufp = f.__buffer; + f.__put_limit = f.__buffer + f.__bufsize; + f.__mode.__write = 1; + f.__room_funcs.__output = enlarge_buffer; + f.__seen = 1; + + done = vfprintf (&f, format, args); + if (done < 0) + return done; + + *string_ptr = realloc (f.__buffer, (f.__bufp - f.__buffer) + 1); + if (*string_ptr == NULL) + *string_ptr = f.__buffer; + (*string_ptr)[f.__bufp - f.__buffer] = '\0'; + return done; +} diff --git a/stdio-common/vdprintf.c b/stdio-common/vdprintf.c new file mode 100644 index 0000000000..9df4e537bc --- /dev/null +++ b/stdio-common/vdprintf.c @@ -0,0 +1,51 @@ +/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + + +/* Write formatted output to file descriptor D according to the format string + FORMAT, using the argument list in ARG. */ +int +DEFUN(vdprintf, (d, format, arg), + int d AND CONST char *format AND va_list arg) +{ + int done; + FILE f; + + /* Create an unbuffered stream talking to D on the stack. */ + memset ((PTR) &f, 0, sizeof(f)); + f.__magic = _IOMAGIC; + f.__mode.__write = 1; + f.__cookie = (PTR) (long int) d; /* Casting to long quiets GCC on Alpha. */ + f.__room_funcs = __default_room_functions; + f.__io_funcs = __default_io_functions; + f.__seen = 1; + f.__userbuf = 1; + + /* vfprintf will use a buffer on the stack for the life of the call, + and flush it when finished. */ + done = vfprintf (&f, format, arg); + + return done; +} diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c new file mode 100644 index 0000000000..63a5148463 --- /dev/null +++ b/stdio-common/vfprintf.c @@ -0,0 +1,858 @@ +/* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ctype.h> +#include <errno.h> +#include <float.h> +#include <limits.h> +#include <math.h> +#include <printf.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <printf.h> +#include <stddef.h> +#include "_itoa.h" +#include "../locale/localeinfo.h" + +/* Include the shared code for parsing the format string. */ +#include "printf-parse.h" + + +/* This function from the GNU C library is also used in libio. + To compile for use in libio, compile with -DUSE_IN_LIBIO. */ + +#ifdef USE_IN_LIBIO +/* This code is for use in libio. */ +#include <libioP.h> +#define PUT(f, s, n) _IO_sputn (f, s, n) +#define PAD(padchar) \ + if (specs[cnt].info.width > 0) \ + done += _IO_padn (s, padchar, specs[cnt].info.width) +#define PUTC(c, f) _IO_putc (c, f) +#define vfprintf _IO_vfprintf +#define size_t _IO_size_t +#define FILE _IO_FILE +#define va_list _IO_va_list +#undef BUFSIZ +#define BUFSIZ _IO_BUFSIZ +#define ARGCHECK(s, format) \ + do \ + { \ + /* Check file argument for consistence. */ \ + CHECK_FILE (s, -1); \ + if (s->_flags & _IO_NO_WRITES || format == NULL) \ + { \ + MAYBE_SET_EINVAL; \ + return -1; \ + } \ + } while (0) +#define UNBUFFERED_P(s) ((s)->_IO_file_flags & _IO_UNBUFFERED) +#else /* ! USE_IN_LIBIO */ +/* This code is for use in the GNU C library. */ +#include <stdio.h> +#define PUTC(c, f) putc (c, f) +#define PUT(f, s, n) fwrite (s, 1, n, f) +ssize_t __printf_pad __P ((FILE *, char pad, size_t n)); +#define PAD(padchar) \ + if (specs[cnt].info.width > 0) \ + { if (__printf_pad (s, padchar, specs[cnt].info.width) == -1) \ + return -1; else done += specs[cnt].info.width; } +#define ARGCHECK(s, format) \ + do \ + { \ + /* Check file argument for consistence. */ \ + if (!__validfp(s) || !s->__mode.__write || format == NULL) \ + { \ + errno = EINVAL; \ + return -1; \ + } \ + if (!s->__seen) \ + { \ + if (__flshfp (s, EOF) == EOF) \ + return -1; \ + } \ + } while (0) +#define UNBUFFERED_P(s) ((s)->__buffer == NULL) +#endif /* USE_IN_LIBIO */ + + +#define outchar(x) \ + do \ + { \ + register const int outc = (x); \ + if (putc (outc, s) == EOF) \ + return -1; \ + else \ + ++done; \ + } while (0) + +#define outstring(string, len) \ + do \ + { \ + if (len > 20) \ + { \ + if (PUT (s, string, len) != len) \ + return -1; \ + done += len; \ + } \ + else \ + { \ + register const char *cp = string; \ + register int l = len; \ + while (l-- > 0) \ + outchar (*cp++); \ + } \ + } while (0) + +/* Helper function to provide temporary buffering for unbuffered streams. */ +static int buffered_vfprintf __P ((FILE *stream, const char *fmt, va_list)); + +static printf_function printf_unknown; + +extern printf_function **__printf_function_table; + +static char *group_number __P ((char *, char *, const char *, wchar_t)); + + +int +vfprintf (s, format, ap) + register FILE *s; + const char *format; + va_list ap; +{ + /* The character used as thousands separator. */ + wchar_t thousands_sep; + + /* The string describing the size of groups of digits. */ + const char *grouping; + + /* Array with information about the needed arguments. This has to be + dynamically extendable. */ + size_t nspecs; + size_t nspecs_max; + struct printf_spec *specs; + + /* The number of arguments the format string requests. This will + determine the size of the array needed to store the argument + attributes. */ + size_t nargs; + int *args_type; + union printf_arg *args_value; + + /* Positional parameters refer to arguments directly. This could also + determine the maximum number of arguments. Track the maximum number. */ + size_t max_ref_arg; + + /* End of leading constant string. */ + const char *lead_str_end; + + /* Number of characters written. */ + register size_t done = 0; + + /* Running pointer through format string. */ + const char *f; + + /* Just a counter. */ + int cnt; + + ARGCHECK (s, format); + + if (UNBUFFERED_P (s)) + /* Use a helper function which will allocate a local temporary buffer + for the stream and then call us again. */ + return buffered_vfprintf (s, format, ap); + + /* Reset multibyte characters to their initial state. */ + (void) mblen ((char *) NULL, 0); + + /* Figure out the thousands separator character. */ + if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP), + strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0) + thousands_sep = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); + grouping = _NL_CURRENT (LC_NUMERIC, GROUPING); + if (*grouping == '\0' || *grouping == CHAR_MAX || thousands_sep == L'\0') + grouping = NULL; + + nspecs_max = 32; /* A more or less arbitrary start value. */ + specs = alloca (nspecs_max * sizeof (struct printf_spec)); + nspecs = 0; + nargs = 0; + max_ref_arg = 0; + + /* Find the first format specifier. */ + lead_str_end = find_spec (format); + + for (f = lead_str_end; *f != '\0'; f = specs[nspecs++].next_fmt) + { + if (nspecs >= nspecs_max) + { + /* Extend the array of format specifiers. */ + struct printf_spec *old = specs; + + nspecs_max *= 2; + specs = alloca (nspecs_max * sizeof (struct printf_spec)); + if (specs == &old[nspecs]) + /* Stack grows up, OLD was the last thing allocated; extend it. */ + nspecs_max += nspecs_max / 2; + else + { + /* Copy the old array's elements to the new space. */ + memcpy (specs, old, nspecs * sizeof (struct printf_spec)); + if (old == &specs[nspecs]) + /* Stack grows down, OLD was just below the new SPECS. + We can use that space when the new space runs out. */ + nspecs_max += nspecs_max / 2; + } + } + + /* Parse the format specifier. */ + nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg); + } + + /* Determine the number of arguments the format string consumes. */ + nargs = MAX (nargs, max_ref_arg); + + /* Allocate memory for the argument descriptions. */ + args_type = alloca (nargs * sizeof (int)); + args_value = alloca (nargs * sizeof (union printf_arg)); + + /* XXX Could do sanity check here: + Initialize args_type elts to zero. + If any is still zero after this loop, format is invalid. */ + + /* Fill in the types of all the arguments. */ + for (cnt = 0; cnt < nspecs; ++cnt) + { + /* If the width is determined by an argument this is an int. */ + if (specs[cnt].width_arg != -1) + args_type[specs[cnt].width_arg] = PA_INT; + + /* If the precision is determined by an argument this is an int. */ + if (specs[cnt].prec_arg != -1) + args_type[specs[cnt].prec_arg] = PA_INT; + + switch (specs[cnt].ndata_args) + { + case 0: /* No arguments. */ + break; + case 1: /* One argument; we already have the type. */ + args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type; + break; + default: + /* We have more than one argument for this format spec. We must + call the arginfo function again to determine all the types. */ + (void) (*__printf_arginfo_table[specs[cnt].info.spec]) + (&specs[cnt].info, + specs[cnt].ndata_args, &args_type[specs[cnt].data_arg]); + break; + } + } + + /* Now we know all the types and the order. Fill in the argument values. */ + for (cnt = 0; cnt < nargs; ++cnt) + switch (args_type[cnt]) + { +#define T(tag, mem, type) \ + case tag: \ + args_value[cnt].mem = va_arg (ap, type); \ + break + + T (PA_CHAR, pa_char, int); /* Promoted. */ + T (PA_INT|PA_FLAG_SHORT, pa_short_int, int); /* Promoted. */ + T (PA_INT, pa_int, int); + T (PA_INT|PA_FLAG_LONG, pa_long_int, long int); + T (PA_INT|PA_FLAG_LONG_LONG, pa_long_long_int, long long int); + T (PA_FLOAT, pa_float, double); /* Promoted. */ + T (PA_DOUBLE, pa_double, double); + T (PA_DOUBLE|PA_FLAG_LONG_DOUBLE, pa_long_double, long double); + T (PA_STRING, pa_string, const char *); + T (PA_POINTER, pa_pointer, void *); +#undef T + default: + if ((args_type[cnt] & PA_FLAG_PTR) != 0) + args_value[cnt].pa_pointer = va_arg (ap, void *); + break; + } + + /* Write the literal text before the first format. */ + outstring (format, lead_str_end - format); + + /* Now walk through all format specifiers and process them. */ + for (cnt = 0; cnt < nspecs; ++cnt) + { + printf_function *function; /* Auxiliary function to do output. */ + int is_neg; /* Decimal integer is negative. */ + int base; /* Base of a number to be written. */ + unsigned long long int num; /* Integral number to be written. */ + const char *str; /* String to be written. */ + char errorbuf[1024]; /* Buffer sometimes used by %m. */ + + if (specs[cnt].width_arg != -1) + { + /* Extract the field width from an argument. */ + specs[cnt].info.width = args_value[specs[cnt].width_arg].pa_int; + + if (specs[cnt].info.width < 0) + /* If the width value is negative left justification is selected + and the value is taken as being positive. */ + { + specs[cnt].info.width = -specs[cnt].info.width; + specs[cnt].info.left = 1; + } + } + + if (specs[cnt].prec_arg != -1) + { + /* Extract the precision from an argument. */ + specs[cnt].info.prec = args_value[specs[cnt].prec_arg].pa_int; + + if (specs[cnt].info.prec < 0) + /* If the precision is negative the precision is omitted. */ + specs[cnt].info.prec = -1; + } + + /* Check for a user-defined handler for this spec. */ + function = (__printf_function_table == NULL ? NULL : + __printf_function_table[specs[cnt].info.spec]); + + if (function != NULL) + use_function: /* Built-in formats with helpers use this. */ + { + int function_done; + unsigned int i; + const void *ptr[specs[cnt].ndata_args]; + + /* Fill in an array of pointers to the argument values. */ + for (i = 0; i < specs[cnt].ndata_args; ++i) + ptr[i] = &args_value[specs[cnt].data_arg + i]; + + /* Call the function. */ + function_done = (*function) (s, &specs[cnt].info, ptr); + + /* If an error occured don't do any further work. */ + if (function_done < 0) + return -1; + + done += function_done; + } + else + switch (specs[cnt].info.spec) + { + case '%': + /* Write a literal "%". */ + outchar ('%'); + break; + case 'i': + case 'd': + { + long long int signed_num; + + /* Decimal integer. */ + base = 10; + if (specs[cnt].info.is_longlong) + signed_num = args_value[specs[cnt].data_arg].pa_long_long_int; + else if (specs[cnt].info.is_long) + signed_num = args_value[specs[cnt].data_arg].pa_long_int; + else if (!specs[cnt].info.is_short) + signed_num = args_value[specs[cnt].data_arg].pa_int; + else + signed_num = args_value[specs[cnt].data_arg].pa_short_int; + + is_neg = signed_num < 0; + num = is_neg ? (- signed_num) : signed_num; + goto number; + } + + case 'u': + /* Decimal unsigned integer. */ + base = 10; + goto unsigned_number; + + case 'o': + /* Octal unsigned integer. */ + base = 8; + goto unsigned_number; + + case 'X': + /* Hexadecimal unsigned integer. */ + case 'x': + /* Hex with lower-case digits. */ + base = 16; + + unsigned_number: + /* Unsigned number of base BASE. */ + + if (specs[cnt].info.is_longlong) + num = args_value[specs[cnt].data_arg].pa_u_long_long_int; + else if (specs[cnt].info.is_long) + num = args_value[specs[cnt].data_arg].pa_u_long_int; + else if (!specs[cnt].info.is_short) + num = args_value[specs[cnt].data_arg].pa_u_int; + else + num = args_value[specs[cnt].data_arg].pa_u_short_int; + + /* ANSI only specifies the `+' and + ` ' flags for signed conversions. */ + is_neg = 0; + specs[cnt].info.showsign = 0; + specs[cnt].info.space = 0; + + number: + /* Number of base BASE. */ + { + char work[BUFSIZ]; + char *const workend = &work[sizeof(work) - 1]; + register char *w; + + /* Supply a default precision if none was given. */ + if (specs[cnt].info.prec == -1) + specs[cnt].info.prec = 1; + + /* Put the number in WORK. */ + w = _itoa (num, workend + 1, base, specs[cnt].info.spec == 'X'); + w -= 1; + if (specs[cnt].info.group && grouping) + w = group_number (w, workend, grouping, thousands_sep); + specs[cnt].info.width -= workend - w; + specs[cnt].info.prec -= workend - w; + + if (num != 0 && specs[cnt].info.alt && base == 8 + && specs[cnt].info.prec <= 0) + { + /* Add octal marker. */ + *w-- = '0'; + --specs[cnt].info.width; + } + + if (specs[cnt].info.prec > 0) + { + /* Add zeros to the precision. */ + specs[cnt].info.width -= specs[cnt].info.prec; + while (specs[cnt].info.prec-- > 0) + *w-- = '0'; + } + + if (num != 0 && specs[cnt].info.alt && base == 16) + /* Account for 0X hex marker. */ + specs[cnt].info.width -= 2; + + if (is_neg || specs[cnt].info.showsign || specs[cnt].info.space) + --specs[cnt].info.width; + + if (!specs[cnt].info.left && specs[cnt].info.pad == ' ') + PAD (' '); + + if (is_neg) + outchar ('-'); + else if (specs[cnt].info.showsign) + outchar ('+'); + else if (specs[cnt].info.space) + outchar (' '); + + if (num != 0 && specs[cnt].info.alt && base == 16) + { + outchar ('0'); + outchar (specs[cnt].info.spec); + } + + if (!specs[cnt].info.left && specs[cnt].info.pad == '0') + PAD ('0'); + + /* Write the number. */ + while (++w <= workend) + outchar (*w); + + if (specs[cnt].info.left) + PAD (' '); + } + break; + + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + { + /* Floating-point number. This is handled by printf_fp.c. */ + extern printf_function __printf_fp; + function = __printf_fp; + goto use_function; + } + + case 'c': + /* Character. */ + if (!specs[cnt].info.left) + { + --specs[cnt].info.width; + PAD (' '); + } + outchar ((unsigned char) args_value[specs[cnt].data_arg].pa_char); + if (specs[cnt].info.left) + PAD (' '); + break; + + case 's': + { + static const char null[] = "(null)"; + size_t len; + + str = args_value[specs[cnt].data_arg].pa_string; + + string: + + if (str == NULL) + { + /* Write "(null)" if there's space. */ + if (specs[cnt].info.prec == -1 + || specs[cnt].info.prec >= (int) sizeof (null) - 1) + { + str = null; + len = sizeof (null) - 1; + } + else + { + str = ""; + len = 0; + } + } + else if (specs[cnt].info.prec != -1) + { + /* Search for the end of the string, but don't search + past the length specified by the precision. */ + const char *end = memchr (str, '\0', specs[cnt].info.prec); + if (end) + len = end - str; + else + len = specs[cnt].info.prec; + } + else + len = strlen (str); + + specs[cnt].info.width -= len; + + if (!specs[cnt].info.left) + PAD (' '); + outstring (str, len); + if (specs[cnt].info.left) + PAD (' '); + } + break; + + case 'p': + /* Generic pointer. */ + { + const void *ptr; + ptr = args_value[specs[cnt].data_arg].pa_pointer; + if (ptr != NULL) + { + /* If the pointer is not NULL, write it as a %#x spec. */ + base = 16; + num = (unsigned long long int) (unsigned long int) ptr; + is_neg = 0; + specs[cnt].info.alt = 1; + specs[cnt].info.spec = 'x'; + specs[cnt].info.group = 0; + goto number; + } + else + { + /* Write "(nil)" for a nil pointer. */ + str = "(nil)"; + /* Make sure the full string "(nil)" is printed. */ + if (specs[cnt].info.prec < 5) + specs[cnt].info.prec = 5; + goto string; + } + } + break; + + case 'n': + /* Answer the count of characters written. */ + if (specs[cnt].info.is_longlong) + *(long long int *) + args_value[specs[cnt].data_arg].pa_pointer = done; + else if (specs[cnt].info.is_long) + *(long int *) + args_value[specs[cnt].data_arg].pa_pointer = done; + else if (!specs[cnt].info.is_short) + *(int *) + args_value[specs[cnt].data_arg].pa_pointer = done; + else + *(short int *) + args_value[specs[cnt].data_arg].pa_pointer = done; + break; + + case 'm': + { + extern char *_strerror_internal __P ((int, char *buf, size_t)); + str = _strerror_internal (errno, errorbuf, sizeof errorbuf); + goto string; + } + + default: + /* Unrecognized format specifier. */ + function = printf_unknown; + goto use_function; + } + + /* Write the following constant string. */ + outstring (specs[cnt].end_of_fmt, + specs[cnt].next_fmt - specs[cnt].end_of_fmt); + } + + return done; +} + + +/* Handle an unknown format specifier. This prints out a canonicalized + representation of the format spec itself. */ + +static int +printf_unknown (s, info, args) + FILE *s; + const struct printf_info *info; + const void **const args; +{ + int done = 0; + char work[BUFSIZ]; + char *const workend = &work[sizeof(work) - 1]; + register char *w; + + outchar ('%'); + + if (info->alt) + outchar ('#'); + if (info->group) + outchar ('\''); + if (info->showsign) + outchar ('+'); + else if (info->space) + outchar (' '); + if (info->left) + outchar ('-'); + if (info->pad == '0') + outchar ('0'); + + if (info->width != 0) + { + w = _itoa (info->width, workend + 1, 10, 0); + while (++w <= workend) + outchar (*w); + } + + if (info->prec != -1) + { + outchar ('.'); + w = _itoa (info->prec, workend + 1, 10, 0); + while (++w <= workend) + outchar (*w); + } + + if (info->spec != '\0') + outchar (info->spec); + + return done; +} + +/* Group the digits according to the grouping rules of the current locale. + The interpretation of GROUPING is as in `struct lconv' from <locale.h>. */ + +static char * +group_number (char *w, char *workend, const char *grouping, + wchar_t thousands_sep) +{ + int len; + char *src, *s; + + /* We treat all negative values like CHAR_MAX. */ + + if (*grouping == CHAR_MAX || *grouping < 0) + /* No grouping should be done. */ + return w; + + len = *grouping; + + /* Copy existing string so that nothing gets overwritten. */ + src = (char *) alloca (workend - w); + memcpy (src, w + 1, workend - w); + s = &src[workend - w - 1]; + w = workend; + + /* Process all characters in the string. */ + while (s >= src) + { + *w-- = *s--; + + if (--len == 0 && s >= src) + { + /* A new group begins. */ + *w-- = thousands_sep; + + len = *grouping++; + if (*grouping == '\0') + /* The previous grouping repeats ad infinitum. */ + --grouping; + else if (*grouping == CHAR_MAX || *grouping < 0) + { + /* No further grouping to be done. + Copy the rest of the number. */ + do + *w-- = *s--; + while (s >= src); + break; + } + } + } + return w; +} + +#ifdef USE_IN_LIBIO +/* Helper "class" for `fprintf to unbuffered': creates a temporary buffer. */ +struct helper_file + { + struct _IO_FILE_plus _f; + _IO_FILE *_put_stream; + }; + +static int +_IO_helper_overflow (s, c) + _IO_FILE *s; + int c; +{ + _IO_FILE *target = ((struct helper_file*) s)->_put_stream; + int used = s->_IO_write_ptr - s->_IO_write_base; + if (used) + { + _IO_size_t written = _IO_sputn (target, s->_IO_write_base, used); + s->_IO_write_ptr -= written; + } + return _IO_putc (c, s); +} + +static const struct _IO_jump_t _IO_helper_jumps = + { + _IO_helper_overflow, + _IO_default_underflow, + _IO_default_xsputn, + _IO_default_xsgetn, + _IO_default_read, + _IO_default_write, + _IO_default_doallocate, + _IO_default_pbackfail, + _IO_default_setbuf, + _IO_default_sync, + _IO_default_finish, + _IO_default_close, + _IO_default_stat, + _IO_default_seek, + _IO_default_seekoff, + _IO_default_seekpos, + _IO_default_uflow + }; + +static int +buffered_vfprintf (s, format, args) + register _IO_FILE *s; + char const *format; + _IO_va_list args; +{ + char buf[_IO_BUFSIZ]; + struct helper_file helper; + register _IO_FILE *hp = (_IO_FILE *) &helper; + int result, to_flush; + + /* Initialize helper. */ + helper._put_stream = s; + hp->_IO_write_base = buf; + hp->_IO_write_ptr = buf; + hp->_IO_write_end = buf + sizeof buf; + hp->_IO_file_flags = _IO_MAGIC|_IO_NO_READS; + hp->_jumps = (struct _IO_jump_t *) &_IO_helper_jumps; + + /* Now print to helper instead. */ + result = _IO_vfprintf (hp, format, args); + + /* Now flush anything from the helper to the S. */ + if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0) + { + if (_IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush) + return -1; + } + + return result; +} + +#else /* !USE_IN_LIBIO */ + +static int +buffered_vfprintf (s, format, args) + register FILE *s; + char const *format; + va_list args; +{ + char buf[BUFSIZ]; + int result; + + s->__bufp = s->__buffer = buf; + s->__bufsize = sizeof buf; + s->__put_limit = s->__buffer + s->__bufsize; + s->__get_limit = s->__buffer; + + /* Now use buffer to print. */ + result = vfprintf (s, format, args); + + if (fflush (s) == EOF) + result = -1; + s->__buffer = s->__bufp = s->__get_limit = s->__put_limit = NULL; + s->__bufsize = 0; + + return result; +} + + +/* Pads string with given number of a specified character. + This code is taken from iopadn.c of the GNU I/O library. */ +#define PADSIZE 16 +static const char blanks[PADSIZE] = +{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; +static const char zeroes[PADSIZE] = +{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + +ssize_t +__printf_pad (s, pad, count) + FILE *s; + char pad; + size_t count; +{ + const char *padptr; + register size_t i; + + padptr = pad == ' ' ? blanks : zeroes; + + for (i = count; i >= PADSIZE; i -= PADSIZE) + if (PUT (s, padptr, PADSIZE) != PADSIZE) + return -1; + if (i > 0) + if (PUT (s, padptr, i) != i) + return -1; + + return count; +} +#undef PADSIZE +#endif /* USE_IN_LIBIO */ diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c new file mode 100644 index 0000000000..a778346287 --- /dev/null +++ b/stdio-common/vfscanf.c @@ -0,0 +1,624 @@ +/* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include "../locale/localeinfo.h" +#include <errno.h> +#include <limits.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +#ifdef __GNUC__ +#define HAVE_LONGLONG +#define LONGLONG long long +#else +#define LONGLONG long +#endif + + +#define inchar() ((c = getc(s)) == EOF ? EOF : (++read_in, c)) +#define conv_error() return (ungetc(c, s), done) +#define input_error() return (done == 0 ? EOF : done) +#define memory_error() return ((errno = ENOMEM), EOF) + + +/* Read formatted input from S according to the format string + FORMAT, using the argument list in ARG. + Return the number of assignments made, or -1 for an input error. */ +int +DEFUN(__vfscanf, (s, format, arg), + FILE *s AND CONST char *format AND va_list argptr) +{ + va_list arg = (va_list) argptr; + + register CONST char *f = format; + register char fc; /* Current character of the format. */ + register size_t done = 0; /* Assignments done. */ + register size_t read_in = 0; /* Chars read in. */ + register int c; /* Last char read. */ + register int do_assign; /* Whether to do an assignment. */ + register int width; /* Maximum field width. */ + int group_flag; /* %' modifier flag. */ + + /* Type modifiers. */ + int is_short, is_long, is_long_double; +#ifdef HAVE_LONGLONG + /* We use the `L' modifier for `long long int'. */ +#define is_longlong is_long_double +#else +#define is_longlong 0 +#endif + int malloc_string; /* Args are char ** to be filled in. */ + /* Status for reading F-P nums. */ + char got_dot, got_e; + /* If a [...] is a [^...]. */ + char not_in; + /* Base for integral numbers. */ + int base; + /* Signedness for integral numbers. */ + int number_signed; + /* Integral holding variables. */ + union + { + long long int q; + unsigned long long int uq; + long int l; + unsigned long int ul; + } num; + /* Character-buffer pointer. */ + register char *str, **strptr; + size_t strsize; + /* Workspace. */ + char work[200]; + char *w; /* Pointer into WORK. */ + wchar_t decimal; /* Decimal point character. */ + + if (!__validfp(s) || !s->__mode.__read || format == NULL) + { + errno = EINVAL; + return EOF; + } + + /* Figure out the decimal point character. */ + if (mbtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT), + strlen (_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT))) <= 0) + decimal = (wchar_t) *_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT); + + c = inchar(); + + /* Run through the format string. */ + while (*f != '\0') + { + unsigned int argpos; + /* Extract the next argument, which is of type TYPE. + For a %N$... spec, this is the Nth argument from the beginning; + otherwise it is the next argument after the state now in ARG. */ +#define ARG(type) (argpos == 0 ? va_arg (arg, type) : \ + ({ unsigned int pos = argpos; \ + va_list arg = (va_list) argptr; \ + while (--pos > 0) \ + (void) va_arg (arg, void *); \ + va_arg (arg, type); \ + })) + + if (!isascii (*f)) + { + /* Non-ASCII, may be a multibyte. */ + int len = mblen (f, strlen(f)); + if (len > 0) + { + while (len-- > 0) + if (c == EOF) + input_error(); + else if (c == *f++) + (void) inchar(); + else + conv_error(); + continue; + } + } + + fc = *f++; + if (fc != '%') + { + /* Characters other than format specs must just match. */ + if (c == EOF) + input_error(); + if (isspace(fc)) + { + /* Whitespace characters match any amount of whitespace. */ + while (isspace (c)) + inchar (); + continue; + } + else if (c == fc) + (void) inchar(); + else + conv_error(); + continue; + } + + /* Initialize state of modifiers. */ + argpos = 0; + do_assign = 1; + group_flag = 0; + is_short = is_long = is_long_double = malloc_string = 0; + + /* Check for a positional parameter specification. */ + if (isdigit (*f)) + { + argpos = *f++ - '0'; + while (isdigit (*f)) + argpos = argpos * 10 + (*f++ - '0'); + if (*f == '$') + ++f; + else + { + /* Oops; that was actually the field width. */ + width = argpos; + argpos = 0; + goto got_width; + } + } + + /* Check for the assignment-suppressant and the number grouping flag. */ + while (*f == '*' || *f == '\'') + switch (*f++) + { + case '*': + do_assign = 0; + break; + case '\'': + group_flag = 1; + break; + } + + /* Find the maximum field width. */ + width = 0; + while (isdigit(*f)) + { + width *= 10; + width += *f++ - '0'; + } + got_width: + if (width == 0) + width = -1; + + /* Check for type modifiers. */ + while (*f == 'h' || *f == 'l' || *f == 'L' || *f == 'a' || *f == 'q') + switch (*f++) + { + case 'h': + /* int's are short int's. */ + is_short = 1; + break; + case 'l': + if (is_long) + /* A double `l' is equivalent to an `L'. */ + is_longlong = 1; + else + /* int's are long int's. */ + is_long = 1; + break; + case 'q': + case 'L': + /* double's are long double's, and int's are long long int's. */ + is_long_double = 1; + break; + case 'a': + /* String conversions (%s, %[) take a `char **' + arg and fill it in with a malloc'd pointer. */ + malloc_string = 1; + break; + } + + /* End of the format string? */ + if (*f == '\0') + conv_error(); + + /* Find the conversion specifier. */ + w = work; + fc = *f++; + if (fc != '[' && fc != 'c' && fc != 'n') + /* Eat whitespace. */ + while (isspace(c)) + (void) inchar(); + switch (fc) + { + case '%': /* Must match a literal '%'. */ + if (c != fc) + conv_error(); + break; + + case 'n': /* Answer number of assignments done. */ + if (do_assign) + *ARG (int *) = read_in - 1; /* Don't count the read-ahead. */ + break; + + case 'c': /* Match characters. */ + if (do_assign) + { + str = ARG (char *); + if (str == NULL) + conv_error (); + } + + if (c == EOF) + input_error(); + + if (width == -1) + width = 1; + + if (do_assign) + { + do + *str++ = c; + while (inchar() != EOF && --width > 0); + } + else + while (inchar() != EOF && --width > 0); + + if (do_assign) + ++done; + + break; + + case 's': /* Read a string. */ +#define STRING_ARG \ + if (do_assign) \ + { \ + if (malloc_string) \ + { \ + /* The string is to be stored in a malloc'd buffer. */ \ + strptr = ARG (char **); \ + if (strptr == NULL) \ + conv_error (); \ + /* Allocate an initial buffer. */ \ + strsize = 100; \ + *strptr = str = malloc (strsize); \ + } \ + else \ + str = ARG (char *); \ + if (str == NULL) \ + conv_error (); \ + } + STRING_ARG; + + if (c == EOF) + input_error (); + + do + { + if (isspace (c)) + break; +#define STRING_ADD_CHAR(c) \ + if (do_assign) \ + { \ + *str++ = c; \ + if (malloc_string && str == *strptr + strsize) \ + { \ + /* Enlarge the buffer. */ \ + str = realloc (*strptr, strsize * 2); \ + if (str == NULL) \ + { \ + /* Can't allocate that much. Last-ditch effort. */\ + str = realloc (*strptr, strsize + 1); \ + if (str == NULL) \ + { \ + /* We lose. Oh well. \ + Terminate the string and stop converting, \ + so at least we don't swallow any input. */ \ + (*strptr)[strsize] = '\0'; \ + ++done; \ + conv_error (); \ + } \ + else \ + { \ + *strptr = str; \ + str += strsize; \ + ++strsize; \ + } \ + } \ + else \ + { \ + *strptr = str; \ + str += strsize; \ + strsize *= 2; \ + } \ + } \ + } + STRING_ADD_CHAR (c); + } while (inchar () != EOF && (width <= 0 || --width > 0)); + + if (do_assign) + { + *str = '\0'; + ++done; + } + break; + + case 'x': /* Hexadecimal integer. */ + case 'X': /* Ditto. */ + base = 16; + number_signed = 0; + goto number; + + case 'o': /* Octal integer. */ + base = 8; + number_signed = 0; + goto number; + + case 'u': /* Unsigned decimal integer. */ + base = 10; + number_signed = 0; + goto number; + + case 'd': /* Signed decimal integer. */ + base = 10; + number_signed = 1; + goto number; + + case 'i': /* Generic number. */ + base = 0; + number_signed = 1; + + number: + if (c == EOF) + input_error(); + + /* Check for a sign. */ + if (c == '-' || c == '+') + { + *w++ = c; + if (width > 0) + --width; + (void) inchar(); + } + + /* Look for a leading indication of base. */ + if (c == '0') + { + if (width > 0) + --width; + *w++ = '0'; + + (void) inchar(); + + if (tolower(c) == 'x') + { + if (base == 0) + base = 16; + if (base == 16) + { + if (width > 0) + --width; + (void) inchar(); + } + } + else if (base == 0) + base = 8; + } + + if (base == 0) + base = 10; + + /* Read the number into WORK. */ + while (width != 0 && c != EOF) + { + if (base == 16 ? !isxdigit(c) : + (!isdigit(c) || c - '0' >= base)) + break; + *w++ = c; + if (width > 0) + --width; + (void) inchar (); + } + + if (w == work || + (w - work == 1 && (work[0] == '+' || work[0] == '-'))) + /* There was no number. */ + conv_error(); + + /* Convert the number. */ + *w = '\0'; + if (is_longlong) + { + if (number_signed) + num.q = __strtoq_internal (work, &w, base, group_flag); + else + num.uq = __strtouq_internal (work, &w, base, group_flag); + } + else + { + if (number_signed) + num.l = __strtol_internal (work, &w, base, group_flag); + else + num.ul = __strtoul_internal (work, &w, base, group_flag); + } + if (w == work) + conv_error (); + + if (do_assign) + { + if (! number_signed) + { + if (is_longlong) + *ARG (unsigned LONGLONG int *) = num.uq; + else if (is_long) + *ARG (unsigned long int *) = num.ul; + else if (is_short) + *ARG (unsigned short int *) + = (unsigned short int) num.ul; + else + *ARG (unsigned int *) = (unsigned int) num.ul; + } + else + { + if (is_longlong) + *ARG (LONGLONG int *) = num.q; + else if (is_long) + *ARG (long int *) = num.l; + else if (is_short) + *ARG (short int *) = (short int) num.l; + else + *ARG (int *) = (int) num.l; + } + ++done; + } + break; + + case 'e': /* Floating-point numbers. */ + case 'E': + case 'f': + case 'g': + case 'G': + if (c == EOF) + input_error(); + + /* Check for a sign. */ + if (c == '-' || c == '+') + { + *w++ = c; + if (inchar() == EOF) + /* EOF is only an input error before we read any chars. */ + conv_error(); + if (width > 0) + --width; + } + + got_dot = got_e = 0; + do + { + if (isdigit(c)) + *w++ = c; + else if (got_e && w[-1] == 'e' && (c == '-' || c == '+')) + *w++ = c; + else if (!got_e && tolower(c) == 'e') + { + *w++ = 'e'; + got_e = got_dot = 1; + } + else if (c == decimal && !got_dot) + { + *w++ = c; + got_dot = 1; + } + else + break; + if (width > 0) + --width; + } while (inchar() != EOF && width != 0); + + if (w == work) + conv_error(); + if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e') + conv_error(); + + /* Convert the number. */ + *w = '\0'; + if (is_long_double) + { + long double d = __strtold_internal (work, &w, group_flag); + if (do_assign && w != work) + *ARG (long double *) = d; + } + else if (is_long) + { + double d = __strtod_internal (work, &w, group_flag); + if (do_assign && w != work) + *ARG (double *) = d; + } + else + { + float d = __strtof_internal (work, &w, group_flag); + if (do_assign && w != work) + *ARG (float *) = d; + } + + if (w == work) + conv_error (); + + if (do_assign) + ++done; + break; + + case '[': /* Character class. */ + STRING_ARG; + + if (c == EOF) + input_error(); + + if (*f == '^') + { + ++f; + not_in = 1; + } + else + not_in = 0; + + while ((fc = *f++) != '\0' && fc != ']') + { + if (fc == '-' && *f != '\0' && *f != ']' && + w > work && w[-1] <= *f) + /* Add all characters from the one before the '-' + up to (but not including) the next format char. */ + for (fc = w[-1] + 1; fc < *f; ++fc) + *w++ = fc; + else + /* Add the character to the list. */ + *w++ = fc; + } + if (fc == '\0') + conv_error(); + + *w = '\0'; + num.ul = read_in; + do + { + if ((strchr (work, c) == NULL) != not_in) + break; + STRING_ADD_CHAR (c); + if (width > 0) + --width; + } while (inchar () != EOF && width != 0); + if (read_in == num.ul) + conv_error (); + + if (do_assign) + { + *str = '\0'; + ++done; + } + break; + + case 'p': /* Generic pointer. */ + base = 16; + /* A PTR must be the same size as a `long int'. */ + is_long = 1; + goto number; + } + } + + conv_error(); +} + +weak_alias (__vfscanf, vfscanf) diff --git a/stdio-common/vprintf.c b/stdio-common/vprintf.c new file mode 100644 index 0000000000..77f1da47e2 --- /dev/null +++ b/stdio-common/vprintf.c @@ -0,0 +1,37 @@ +/* Copyright (C) 1991, 1993, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <stdarg.h> +#undef __OPTIMIZE__ /* Avoid inline `vprintf' function. */ +#include <stdio.h> + +#undef vprintf + +#ifdef USE_IN_LIBIO +# define vfprintf _IO_vfprintf +#endif + +/* Write formatted output to stdout according to the + format string FORMAT, using the argument list in ARG. */ +int +vprintf (format, arg) + const char *format; + __gnuc_va_list arg; +{ + return vfprintf (stdout, format, arg); +} diff --git a/stdio-common/vscanf.c b/stdio-common/vscanf.c new file mode 100644 index 0000000000..0d829440e9 --- /dev/null +++ b/stdio-common/vscanf.c @@ -0,0 +1,32 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdarg.h> +#include <stdio.h> + +#undef vscanf + + +/* Read formatted input from stdin according to the format + string in FORMAT, using the argument list in ARG. */ +int +DEFUN(vscanf, (format, arg), CONST char *format AND va_list arg) +{ + return vfscanf (stdin, format, arg); +} diff --git a/stdio-common/vsnprintf.c b/stdio-common/vsnprintf.c new file mode 100644 index 0000000000..a02c259131 --- /dev/null +++ b/stdio-common/vsnprintf.c @@ -0,0 +1,56 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + + +/* + * Write formatted output to S according to the format string + * FORMAT, using the argument list in ARG, writing no more + * than MAXLEN characters. + */ +int +DEFUN(vsnprintf, (s, maxlen, format, arg), + char *s AND size_t maxlen AND CONST char *format AND va_list arg) +{ + int done; + FILE f; + + memset((PTR) &f, 0, sizeof(f)); + f.__magic = _IOMAGIC; + f.__mode.__write = 1; + /* The buffer size is one less than MAXLEN + so we have space for the null terminator. */ + f.__bufp = f.__buffer = (char *) s; + f.__bufsize = maxlen - 1; + f.__put_limit = f.__buffer + f.__bufsize; + f.__get_limit = f.__buffer; + /* After the buffer is full (MAXLEN characters have been written), + any more characters written will go to the bit bucket. */ + f.__room_funcs = __default_room_functions; + f.__io_funcs.__write = NULL; + f.__seen = 1; + + done = vfprintf(&f, format, arg); + *f.__bufp = '\0'; + + return done; +} diff --git a/stdio-common/vsprintf.c b/stdio-common/vsprintf.c new file mode 100644 index 0000000000..82be90f1fa --- /dev/null +++ b/stdio-common/vsprintf.c @@ -0,0 +1,50 @@ +/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + + +/* Write formatted output to S according to the format string + FORMAT, using the argument list in ARG. */ +int +DEFUN(vsprintf, (s, format, arg), + char *s AND CONST char *format AND va_list arg) +{ + int done; + FILE f; + + memset((PTR) &f, 0, sizeof(f)); + f.__magic = _IOMAGIC; + f.__mode.__write = 1; + f.__bufp = f.__buffer = (char *) s; + f.__put_limit = (char *) ULONG_MAX; + f.__bufsize = (size_t) (f.__put_limit - f.__bufp); + f.__get_limit = f.__buffer; + f.__room_funcs.__output = NULL; + f.__seen = 1; + + done = vfprintf(&f, format, arg); + *f.__bufp = '\0'; + + return done; +} diff --git a/stdio-common/vsscanf.c b/stdio-common/vsscanf.c new file mode 100644 index 0000000000..6f027d5065 --- /dev/null +++ b/stdio-common/vsscanf.c @@ -0,0 +1,58 @@ +/* Copyright (C) 1991, 1992, 1995 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <ansidecl.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#undef vsscanf + + +/* Read formatted input from S according to the format + string FORMAT, using the argument list in ARG. */ +int +DEFUN(__vsscanf, (s, format, arg), + CONST char *s AND CONST char *format AND va_list arg) +{ + FILE f; + + if (s == NULL) + { + errno = EINVAL; + return -1; + } + + memset((PTR) &f, 0, sizeof(f)); + f.__magic = _IOMAGIC; + f.__mode.__read = 1; + f.__bufp = f.__buffer = (char *) s; + f.__bufsize = strlen(s); + f.__get_limit = f.__buffer + f.__bufsize; + f.__put_limit = f.__buffer; + /* After the buffer is empty (strlen(S) characters have been read), + any more read attempts will get EOF. */ + f.__room_funcs.__input = NULL; + f.__seen = 1; + + return __vfscanf(&f, format, arg); +} + + +weak_alias (__vsscanf, vsscanf) diff --git a/stdio-common/xbug.c b/stdio-common/xbug.c new file mode 100644 index 0000000000..ec648f5566 --- /dev/null +++ b/stdio-common/xbug.c @@ -0,0 +1,63 @@ +#include <stdio.h> + +typedef struct _Buffer { + char *buff; + int room, used; +} Buffer; + +#define INIT_BUFFER_SIZE 10000 + +void InitBuffer(b) + Buffer *b; +{ + b->room = INIT_BUFFER_SIZE; + b->used = 0; + b->buff = (char *)malloc(INIT_BUFFER_SIZE*sizeof(char)); +} + +void AppendToBuffer(b, str, len) + register Buffer *b; + char *str; + register int len; +{ + while (b->used + len > b->room) { + b->buff = (char *)realloc(b->buff, 2*b->room*(sizeof(char))); + b->room *= 2; + } + strncpy(b->buff + b->used, str, len); + b->used += len; +} + +void ReadFile(buffer, input) + register Buffer *buffer; + FILE *input; +{ + char buf[BUFSIZ + 1]; + register int bytes; + + buffer->used = 0; + while (!feof(input) && (bytes = fread(buf, 1, BUFSIZ, input)) > 0) { + AppendToBuffer(buffer, buf, bytes); + } + AppendToBuffer(buffer, "", 1); +} + +main() +{ + char * filename = "xbug.c"; + FILE *input; + Buffer buffer; + + InitBuffer(&buffer); + + if (!freopen (filename, "r", stdin)) + fprintf(stderr, "cannot open file\n"); + + if (!(input = popen("/bin/cat", "r"))) + fprintf(stderr, "cannot run \n"); + + ReadFile(&buffer, input); + pclose(input); + + return 0; +} |