Eric Blake <ebb9 <at> byu.net> writes:
Thus, I am proposing a _WANT_IO_C99 flag (optionally
_WANT_IO_C99_FORMATS). The default is no for all platforms except for
*-linux and Cygwin where it will default to yes. The flag will remove
the 'a', 'A', 'z', etc... specifiers that do not appear in C90.
This would be implemented the same as long_long and long_double are
today and have a configure option.
OK, I'll work on that next. It may be a day or two before I have time to
complete it.
How about this?
2007-05-11 Eric Blake <ebb9@byu.net>
Minimize printf/scanf size on platforms that don't need C99.
* acconfig.h (_WANT_IO_C99_FORMATS): New macro.
* newlib.hin (_WANT_IO_C99_FORMATS): Likewise.
* configure.in (newlib-io-c99-formats): New configure option.
(_WANT_IO_C99_FORMATS): Define appropriately.
* configure.host (*-linux*, cygwin): Default c99-formats to yes.
* libc/stdio/vfprintf.c (_VFPRINTF_R) [!_WANT_IO_C99_FORMATS]:
Cripple ' flag; hh, z, j, t sizes; a, A, F, C, S specifiers.
* libc/stdio/vfscanf.c (_VFSCANF_R) [!_WANT_IO_C99_FORMATS]:
Likewise.
* configure: Regenerate.
Index: acconfig.h
===================================================================
RCS file: /cvs/src/src/newlib/acconfig.h,v
retrieving revision 1.3
diff -u -p -r1.3 acconfig.h
--- acconfig.h 15 Mar 2007 21:32:09 -0000 1.3
+++ acconfig.h 11 May 2007 16:28:35 -0000
@@ -9,6 +9,10 @@
/* Newlib version */
#undef _NEWLIB_VERSION
+/* C99 formats support (such as %a, %zu, ...) in IO functions like
+ * printf/scanf enabled */
+#undef _WANT_IO_C99_FORMATS
+
/* long long type support in IO functions like printf/scanf enabled */
#undef _WANT_IO_LONG_LONG
Index: configure.host
===================================================================
RCS file: /cvs/src/src/newlib/configure.host,v
retrieving revision 1.93
diff -u -p -r1.93 configure.host
--- configure.host 20 Feb 2007 21:02:36 -0000 1.93
+++ configure.host 11 May 2007 16:28:35 -0000
@@ -24,6 +24,7 @@
# target_optspace --enable-target-optspace ("yes", "no", "")
# newlib_multithread --enable-newlib-multithread ("yes", "no", "yes")
# newlib_elix_level --enable-newlib-elix-level ("1","2","3","4") ("4")
+# newlib_io_c99_formats --enable-newlib-io-c99-formats ("yes", "no", "")
# newlib_io_long_long --enable-newlib-io-long-long ("yes", "no", "")
# newlib_io_long_double --enable-newlib-io-long-double ("yes", "no", "")
@@ -62,6 +63,7 @@ crt1_dir=
have_crt0=
use_libtool=no
have_sys_mach_dir=no
+default_newlib_io_c99_formats=no
default_newlib_io_long_long=no
default_newlib_io_long_double=no
default_newlib_io_pos_args=no
@@ -398,6 +400,7 @@ case "${host}" in
crt1=crt1.o
crt1_dir=libc/sys/${sys_dir}
gcc_dir=`gcc -print-search-dirs | awk '/^install:/{print $2}'`
+ default_newlib_io_c99_formats="yes"
default_newlib_io_long_double="yes"
default_newlib_io_long_long="yes"
default_newlib_io_pos_args="yes"
@@ -498,6 +501,7 @@ case "${host}" in
*-*-cygwin*)
test -z "$cygwin_srcdir" && cygwin_srcdir=`cd
${srcdir}/../winsup/cygwin; pwd`
export cygwin_srcdir
+ default_newlib_io_c99_formats="yes"
default_newlib_io_long_long="yes"
default_newlib_io_long_double="yes"
default_newlib_io_pos_args="yes"
@@ -744,6 +748,13 @@ esac
# Use defaults for certain settings if not specified by user
+# Enable C99 format support in I/O routines if requested.
+if [ "x${newlib_io_c99_formats}" = "x" ]; then
+ if [ ${default_newlib_io_c99_formats} = "yes" ]; then
+ newlib_io_c99_formats="yes";
+ fi
+fi
+
# Enable long long support in I/O routines if requested.
if [ "x${newlib_io_long_long}" = "x" ]; then
if [ ${default_newlib_io_long_long} = "yes" ]; then
Index: configure.in
===================================================================
RCS file: /cvs/src/src/newlib/configure.in,v
retrieving revision 1.35
diff -u -p -r1.35 configure.in
--- configure.in 15 Mar 2007 21:32:12 -0000 1.35
+++ configure.in 11 May 2007 16:28:35 -0000
@@ -20,6 +20,15 @@ AC_ARG_ENABLE(newlib-io-pos-args,
*) AC_MSG_ERROR(bad value ${enableval} for newlib-io-pos-args option) ;;
esac], [newlib_io_pos_args=])dnl
+dnl Support --enable-newlib-io-c99-formats
+AC_ARG_ENABLE(newlib-io-c99-formats,
+[ --enable-newlib-io-c99-formats enable C99 support in IO functions like
printf/scanf],
+[case "${enableval}" in
+ yes) newlib_io_c99_formats=yes;;
+ no) newlib_io_c99_formats=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for newlib-io-c99-formats option) ;;
+ esac], [newlib_io_c99_formats=])dnl
+
dnl Support --enable-newlib-io-long-long
AC_ARG_ENABLE(newlib-io-long-long,
[ --enable-newlib-io-long-long enable long long type support in IO
functions like printf/scanf],
@@ -229,6 +238,10 @@ if test "${newlib_elix_level}" -gt "0";
AC_DEFINE_UNQUOTED(_ELIX_LEVEL,${newlib_elix_level})
fi
+if test "${newlib_io_c99_formats}" = "yes"; then
+AC_DEFINE_UNQUOTED(_WANT_IO_C99_FORMATS)
+fi
+
if test "${newlib_io_long_long}" = "yes"; then
AC_DEFINE_UNQUOTED(_WANT_IO_LONG_LONG)
fi
Index: newlib.hin
===================================================================
RCS file: /cvs/src/src/newlib/newlib.hin,v
retrieving revision 1.13
diff -u -p -r1.13 newlib.hin
--- newlib.hin 15 Mar 2007 21:32:12 -0000 1.13
+++ newlib.hin 11 May 2007 16:28:35 -0000
@@ -9,6 +9,10 @@
/* Newlib version */
#undef _NEWLIB_VERSION
+/* C99 formats support (such as %a, %zu, ...) in IO functions like
+ * printf/scanf enabled */
+#undef _WANT_IO_C99_FORMATS
+
/* long long type support in IO functions like printf/scanf enabled */
#undef _WANT_IO_LONG_LONG
Index: libc/stdio/vfprintf.c
===================================================================
RCS file: /cvs/src/src/newlib/libc/stdio/vfprintf.c,v
retrieving revision 1.57
diff -u -p -r1.57 vfprintf.c
--- libc/stdio/vfprintf.c 11 May 2007 13:09:44 -0000 1.57
+++ libc/stdio/vfprintf.c 11 May 2007 16:28:35 -0000
@@ -319,16 +319,20 @@ _EXFUN(get_arg, (struct _reent *data, in
#define LONGDBL 0x008 /* long double */
#define LONGINT 0x010 /* long integer */
#ifndef _NO_LONGLONG
-#define QUADINT 0x020 /* quad integer */
+# define QUADINT 0x020 /* quad integer */
#else /* ifdef _NO_LONGLONG, make QUADINT equivalent to LONGINT, so
that %lld behaves the same as %ld, not as %d, as expected if:
sizeof (long long) = sizeof long > sizeof int */
-#define QUADINT LONGINT
+# define QUADINT LONGINT
#endif
#define SHORTINT 0x040 /* short integer */
#define ZEROPAD 0x080 /* zero (as opposed to blank)
pad */
#define FPT 0x100 /* Floating point number */
-#define CHARINT 0x200 /* char as integer */
+#ifdef _WANT_IO_C99_FORMATS
+# define CHARINT 0x200 /* char as integer */
+#else /* define as 0, to make SARG and UARG occupy fewer instructions */
+# define CHARINT 0
+#endif
int _EXFUN(_VFPRINTF_R, (struct _reent *, FILE *, _CONST char *, va_list));
@@ -565,12 +569,15 @@ _DEFUN(_VFPRINTF_R, (data, fp, fmt0, ap)
rflag: ch = *fmt++;
reswitch: switch (ch) {
+#ifdef _WANT_IO_C99_FORMATS
case '\'':
- /* In the C locale, LC_NUMERIC requires
+ /* The ' flag is required by POSIX, but not C99.
+ In the C locale, LC_NUMERIC requires
thousands_sep to be the empty string. And since
no other locales are supported (yet), this flag
is currently a no-op. */
goto rflag;
+#endif
case ' ':
/*
* ``If the space and + flags both appear, the space
@@ -714,24 +721,27 @@ reswitch: switch (ch) {
goto rflag;
#endif
case 'h':
+#ifdef _WANT_IO_C99_FORMATS
if (*fmt == 'h') {
fmt++;
flags |= CHARINT;
- } else {
+ } else
+#endif
flags |= SHORTINT;
- }
goto rflag;
case 'l':
+#if defined _WANT_IO_C99_FORMATS || !defined _NO_LONGLONG
if (*fmt == 'l') {
fmt++;
flags |= QUADINT;
- } else {
+ } else
+#endif
flags |= LONGINT;
- }
goto rflag;
- case 'q':
+ case 'q': /* extension */
flags |= QUADINT;
goto rflag;
+#ifdef _WANT_IO_C99_FORMATS
case 'j':
if (sizeof (intmax_t) == sizeof (long))
flags |= LONGINT;
@@ -769,8 +779,9 @@ reswitch: switch (ch) {
have ptrdiff_t as wide as long long. */
flags |= QUADINT;
goto rflag;
- case 'c':
case 'C':
+#endif /* _WANT_IO_C99_FORMATS */
+ case 'c':
cp = buf;
#ifdef _MB_CAPABLE
if (ch == 'C' || (flags & LONGINT)) {
@@ -792,7 +803,7 @@ reswitch: switch (ch) {
}
sign = '\0';
break;
- case 'D':
+ case 'D': /* extension */
flags |= LONGINT;
/*FALLTHROUGH*/
case 'd':
@@ -811,12 +822,14 @@ reswitch: switch (ch) {
base = DEC;
goto number;
#ifdef FLOATING_POINT
+# ifdef _WANT_IO_C99_FORMATS
case 'a':
case 'A':
+ case 'F':
+# endif
case 'e':
case 'E':
case 'f':
- case 'F':
case 'g':
case 'G':
# ifdef _NO_LONGDBL
@@ -835,7 +848,7 @@ reswitch: switch (ch) {
if (isinf (_fpvalue)) {
if (_fpvalue < 0)
sign = '-';
- if (ch <= 'G') /* 'E', 'F', or 'G' */
+ if (ch <= 'G') /* 'A', 'E', 'F', or 'G' */
cp = "INF";
else
cp = "inf";
@@ -844,7 +857,7 @@ reswitch: switch (ch) {
break;
}
if (isnan (_fpvalue)) {
- if (ch <= 'G') /* 'E', 'F', or 'G' */
+ if (ch <= 'G') /* 'A', 'E', 'F', or 'G' */
cp = "NAN";
else
cp = "nan";
@@ -866,7 +879,7 @@ reswitch: switch (ch) {
if (tmp == 2) {
if (_fpvalue < 0)
sign = '-';
- if (ch <= 'G') /* 'E', 'F', or 'G' */
+ if (ch <= 'G') /* 'A', 'E', 'F', or 'G' */
cp = "INF";
else
cp = "inf";
@@ -875,7 +888,7 @@ reswitch: switch (ch) {
break;
}
if (tmp == 1) {
- if (ch <= 'G') /* 'E', 'F', or 'G' */
+ if (ch <= 'G') /* 'A', 'E', 'F', or 'G' */
cp = "NAN";
else
cp = "nan";
@@ -885,6 +898,7 @@ reswitch: switch (ch) {
}
# endif /* !_NO_LONGDBL */
+# ifdef _WANT_IO_C99_FORMATS
if (ch == 'a' || ch == 'A') {
ox[0] = '0';
ox[1] = ch == 'a' ? 'x' : 'X';
@@ -902,7 +916,9 @@ reswitch: switch (ch) {
}
else
cp = buf;
- } else if (prec == -1) {
+ } else
+# endif /* _WANT_IO_C99_FORMATS */
+ if (prec == -1) {
prec = DEFPREC;
} else if ((ch == 'g' || ch == 'G') && prec == 0) {
prec = 1;
@@ -919,8 +935,10 @@ reswitch: switch (ch) {
else
ch = 'g';
}
+# ifdef _WANT_IO_C99_FORMATS
else if (ch == 'F')
ch = 'f';
+# endif
if (ch <= 'e') { /* 'a', 'A', 'e', or 'E' fmt */
--expt;
expsize = exponent (expstr, expt, ch);
@@ -958,12 +976,14 @@ reswitch: switch (ch) {
*GET_ARG (N, ap, long_ptr_t) = ret;
else if (flags & SHORTINT)
*GET_ARG (N, ap, short_ptr_t) = ret;
+#ifdef _WANT_IO_C99_FORMATS
else if (flags & CHARINT)
*GET_ARG (N, ap, char_ptr_t) = ret;
+#endif
else
*GET_ARG (N, ap, int_ptr_t) = ret;
continue; /* no output */
- case 'O':
+ case 'O': /* extension */
flags |= LONGINT;
/*FALLTHROUGH*/
case 'o':
@@ -986,7 +1006,9 @@ reswitch: switch (ch) {
ch = 'x';
goto nosign;
case 's':
+#ifdef _WANT_IO_C99_FORMATS
case 'S':
+#endif
sign = '\0';
if ((cp = GET_ARG (N, ap, char_ptr_t)) == NULL) {
cp = "(null)";
@@ -1068,7 +1090,7 @@ reswitch: switch (ch) {
size = strlen (cp);
break;
- case 'U':
+ case 'U': /* extension */
flags |= LONGINT;
/*FALLTHROUGH*/
case 'u':
@@ -1357,6 +1379,7 @@ _DEFUN(cvt, (data, value, ndigits, flags
*sign = '\000';
# endif /* !_NO_LONGDBL */
+# ifdef _WANT_IO_C99_FORMATS
if (ch == 'a' || ch == 'A') {
/* This code assumes FLT_RADIX is a power of 2. The initial
division ensures the digit before the decimal will be less
@@ -1387,7 +1410,9 @@ _DEFUN(cvt, (data, value, ndigits, flags
}
*length = bp - buf;
return buf;
- } else if (ch == 'f' || ch == 'F') {
+ }
+# endif /* _WANT_IO_C99_FORMATS */
+ if (ch == 'f' || ch == 'F') {
mode = 3; /* ndigits after the decimal point */
} else {
/* To obtain ndigits after the decimal point for the 'e'
@@ -1426,7 +1451,11 @@ _DEFUN(exponent, (p0, exp, fmtch),
{
register char *p, *t;
char expbuf[10];
+# ifdef _WANT_IO_C99_FORMATS
int isa = fmtch == 'a' || fmtch == 'A';
+# else
+# define isa 0
+# endif
p = p0;
*p++ = isa ? 'p' - 'a' + fmtch : fmtch;
@@ -1682,6 +1711,7 @@ _DEFUN(get_arg, (data, n, fmt, ap, numar
case 'q':
flags |= QUADINT;
break;
+# ifdef _WANT_IO_C99_FORMATS
case 'j':
if (sizeof (intmax_t) == sizeof (long))
flags |= LONGINT;
@@ -1712,14 +1742,17 @@ _DEFUN(get_arg, (data, n, fmt, ap, numar
have ptrdiff_t as wide as long long. */
flags |= QUADINT;
break;
+# endif /* _WANT_IO_C99_FORMATS */
case 'l':
default:
+# if defined _WANT_IO_C99_FORMATS || !defined _NO_LONGLONG
if (*fmt == 'l')
{
flags |= QUADINT;
++fmt;
}
else
+# endif
flags |= LONGINT;
break;
}
@@ -1750,36 +1783,44 @@ _DEFUN(get_arg, (data, n, fmt, ap, numar
case 'O':
spec_type = LONG_INT;
break;
+# ifdef _WANT_IO_C99_FORMATS
case 'a':
case 'A':
- case 'f':
case 'F':
+# endif
+ case 'f':
case 'g':
case 'G':
case 'E':
case 'e':
-#ifndef _NO_LONGDBL
+# ifndef _NO_LONGDBL
if (flags & LONGDBL)
spec_type = LONG_DOUBLE;
else
-#endif
+# endif
spec_type = DOUBLE;
break;
case 's':
+# ifdef _WANT_IO_C99_FORMATS
case 'S':
+# endif
case 'p':
case 'n':
spec_type = CHAR_PTR;
break;
case 'c':
+# ifdef _WANT_IO_C99_FORMATS
if (flags & LONGINT)
spec_type = WIDE_CHAR;
else
+# endif
spec_type = INT;
break;
+# ifdef _WANT_IO_C99_FORMATS
case 'C':
spec_type = WIDE_CHAR;
break;
+# endif
}
/* if we have a positional parameter, just store the type,
otherwise
Index: libc/stdio/vfscanf.c
===================================================================
RCS file: /cvs/src/src/newlib/libc/stdio/vfscanf.c,v
retrieving revision 1.34
diff -u -p -r1.34 vfscanf.c
--- libc/stdio/vfscanf.c 17 Apr 2007 20:53:24 -0000 1.34
+++ libc/stdio/vfscanf.c 11 May 2007 16:28:35 -0000
@@ -361,26 +361,31 @@ _DEFUN(__SVFSCANF_R, (rptr, fp, fmt0, ap
flags |= SUPPRESS;
goto again;
case 'l':
+#if defined _WANT_IO_C99_FORMATS || !defined _NO_LONGLONG
if (*fmt == 'l') /* Check for 'll' = long long (SUSv3) */
{
++fmt;
flags |= LONGDBL;
}
else
+#endif
flags |= LONG;
goto again;
case 'L':
flags |= LONGDBL;
goto again;
case 'h':
+#ifdef _WANT_IO_C99_FORMATS
if (*fmt == 'h') /* Check for 'hh' = char int (SUSv3) */
{
++fmt;
flags |= CHAR;
}
else
+#endif
flags |= SHORT;
goto again;
+#ifdef _WANT_IO_C99_FORMATS
case 'j': /* intmax_t */
if (sizeof (intmax_t) == sizeof (long))
flags |= LONG;
@@ -418,6 +423,7 @@ _DEFUN(__SVFSCANF_R, (rptr, fp, fmt0, ap
have size_t as wide as long long. */
flags |= LONGDBL;
goto again;
+#endif /* _WANT_IO_C99_FORMATS */
case '0':
case '1':
@@ -470,7 +476,7 @@ _DEFUN(__SVFSCANF_R, (rptr, fp, fmt0, ap
base = 10;
break;
- case 'X': /* compat XXX */
+ case 'X':
case 'x':
flags |= PFXOK; /* enable 0x prefixing */
c = CT_INT;
@@ -479,19 +485,25 @@ _DEFUN(__SVFSCANF_R, (rptr, fp, fmt0, ap
break;
#ifdef FLOATING_POINT
- case 'E': /* compat XXX */
- case 'G': /* compat XXX */
-/* ANSI says that E,G and X behave the same way as e,g,x */
- /* FALLTHROUGH */
+# ifdef _WANT_IO_C99_FORMATS
+ case 'a':
+ case 'A':
+ case 'F':
+# endif
+ case 'E':
+ case 'G':
case 'e':
case 'f':
case 'g':
c = CT_FLOAT;
break;
#endif
+
+#ifdef _WANT_IO_C99_FORMATS
case 'S':
flags |= LONG;
/* FALLTHROUGH */
+#endif
case 's':
c = CT_STRING;
@@ -503,9 +515,11 @@ _DEFUN(__SVFSCANF_R, (rptr, fp, fmt0, ap
c = CT_CCL;
break;
+#ifdef _WANT_IO_C99_FORMATS
case 'C':
flags |= LONG;
/* FALLTHROUGH */
+#endif
case 'c':
flags |= NOSKIP;
@@ -522,12 +536,15 @@ _DEFUN(__SVFSCANF_R, (rptr, fp, fmt0, ap
case 'n':
if (flags & SUPPRESS) /* ??? */
continue;
+#ifdef _WANT_IO_C99_FORMATS
if (flags & CHAR)
{
cp = va_arg (ap, char *);
*cp = nread;
}
- else if (flags & SHORT)
+ else
+#endif
+ if (flags & SHORT)
{
sp = va_arg (ap, short *);
*sp = nread;
@@ -976,11 +993,13 @@ _DEFUN(__SVFSCANF_R, (rptr, fp, fmt0, ap
res = (*ccfn) (rptr, buf, (char **) NULL, base);
if (flags & POINTER)
*(va_arg (ap, _PTR *)) = (_PTR) (unsigned _POINTER_INT) res;
+#ifdef _WANT_IO_C99_FORMATS
else if (flags & CHAR)
{
cp = va_arg (ap, char *);
*cp = res;
}
+#endif
else if (flags & SHORT)
{
sp = va_arg (ap, short *);