On 10/18/2010 11:41 PM, Jim Meyering wrote: > I have to agree. The whole point of inttostr functions is to > provide a minimal and robust mechanism for converting integral > values to strings.
I looked into this some more and came up with a different idea: add a primitive umax2str that automatically allocates a big-enough buffer in the caller's stack frame. So, instead of this: if (repetition) { char buf[INT_BUFSIZE_BOUND (uintmax_t)]; fprintf (stderr, _(" on repetition %s\n"), umaxtostr (repetition, buf)); } the caller can write this: if (repetition) fprintf (stderr, _(" on repetition %s\n"), umax2str (repetition)); Sometimes code still needs umaxtostr, so we'll continue to support that, and do compile-time checking on the buffer if it's easy, but the idea is to prefer umax2str when possible, since it makes code nicer to read and easier to maintain. Some downsides of umax2str, imax2str, etc. are: * They return a pointer to storage whose lifetime is that of the enclosing block. It's possible that someone who doesn't understand how they work, will write code that ends up with dangling pointers. * The calling code (though not gnulib) must assume support for C99 compound literals. Coreutils already assumes a C99 compiler, so I expect this is not a fatal objection for coreutils. * GCC generates slightly worse code for umax2str than for umaxtostr, since it unnecessarily stores zeros into the literals. The number of extra instructions and the pressure on the cache is fairly small, but still, this is annoying. Perhaps someday we can convince GCC to generate better code. * C99 compound literals are not that widely used in this way, and it's possible that we'll tickle compiler bugs. Anyway, if you like this approach despite the downsides, here is a proposed gnulib patch to add support for umax2str etc., while also adding compile-time bounds checking for umaxtostr etc. when the compile-time checking is easy. This implementation does not support VLA buffers but the need for that is practically zero. Attached is a proposed coreutils patch to use this new feature. >From 5d050036e995a9088d0b635bf6352d16424c38c0 Mon Sep 17 00:00:00 2001 From: Paul Eggert <egg...@cs.ucla.edu> Date: Sun, 24 Oct 2010 18:20:58 -0700 Subject: [PATCH] inttostr: add int2str etc., and compile-time checks for inttostr etc. This patch is by Jim Meyering <j...@meyering.net>, Bruno Haible <br...@clisp.org>, and myself. * lib/anytostr.c: Don't include config.h and inttostr.h; that is now the includer's responsibility. * lib/imaxtostr.c (imaxtostr): Undef after including those files. * lib/inttostr.c (inttostr): Likewise. * lib/offtostr.c (offtostr): Likewise. * lib/uinttostr.c (uinttostr): Likewise. * lib/umaxtostr.c (umaxtostr): Likewise. * lib/inttostr.h: Include verify.h, as we need it (again). (__gl_might_be_char_ptr, __gl_size_bound, __gl_inttostr): New macros. (imaxtostr, inttostr, offtostr, uinttostr, umaxtostr): New macros, that check the buffer size at compile time if possible. (int2str_call, imax2str, int2str, off2str, uint2str, umax2str): New macros, which assume C99, and which allocate a big-enough buffer in the caller's frame. * modules/inttostr (Depends-on): Add verify (again). * modules/inttostr-tests (Files): Add tests/test-inttostr2.c. * tests/test-inttostr2.c: New file. --- ChangeLog | 25 +++++++++++++ lib/anytostr.c | 4 +-- lib/imaxtostr.c | 3 ++ lib/inttostr.c | 3 ++ lib/inttostr.h | 42 ++++++++++++++++++++++ lib/offtostr.c | 3 ++ lib/uinttostr.c | 3 ++ lib/umaxtostr.c | 3 ++ modules/inttostr | 1 + modules/inttostr-tests | 1 + tests/test-inttostr2.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 tests/test-inttostr2.c diff --git a/ChangeLog b/ChangeLog index 95328fe..9b4b9cc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2010-10-24 Paul Eggert <egg...@cs.ucla.edu> + + inttostr: add int2str etc., and compile-time checks for inttostr etc. + + This patch is by Jim Meyering <j...@meyering.net>, Bruno Haible + <br...@clisp.org>, and myself. + + * lib/anytostr.c: Don't include config.h and inttostr.h; that is + now the includer's responsibility. + * lib/imaxtostr.c (imaxtostr): Undef after including those files. + * lib/inttostr.c (inttostr): Likewise. + * lib/offtostr.c (offtostr): Likewise. + * lib/uinttostr.c (uinttostr): Likewise. + * lib/umaxtostr.c (umaxtostr): Likewise. + * lib/inttostr.h: Include verify.h, as we need it (again). + (__gl_might_be_char_ptr, __gl_size_bound, __gl_inttostr): New macros. + (imaxtostr, inttostr, offtostr, uinttostr, umaxtostr): New macros, + that check the buffer size at compile time if possible. + (int2str_call, imax2str, int2str, off2str, uint2str, umax2str): + New macros, which assume C99, and which allocate a big-enough + buffer in the caller's frame. + * modules/inttostr (Depends-on): Add verify (again). + * modules/inttostr-tests (Files): Add tests/test-inttostr2.c. + * tests/test-inttostr2.c: New file. + 2010-10-23 Paul Eggert <egg...@cs.ucla.edu> inttostr: simplify by removing unnecessary redundancy diff --git a/lib/anytostr.c b/lib/anytostr.c index eb71553..7681761 100644 --- a/lib/anytostr.c +++ b/lib/anytostr.c @@ -22,9 +22,7 @@ # pragma GCC diagnostic ignored "-Wtype-limits" #endif -#include <config.h> - -#include "inttostr.h" +/* This file is meant to be included; see inttostr.c for an example use. */ /* Convert I to a printable string in BUF, which must be at least INT_BUFSIZE_BOUND (INTTYPE) bytes long. Return the address of the diff --git a/lib/imaxtostr.c b/lib/imaxtostr.c index b91ac98..a944136 100644 --- a/lib/imaxtostr.c +++ b/lib/imaxtostr.c @@ -1,3 +1,6 @@ +#include <config.h> +#include "inttostr.h" +#undef imaxtostr #define anytostr imaxtostr #define inttype intmax_t #include "anytostr.c" diff --git a/lib/inttostr.c b/lib/inttostr.c index c96b5ca..0415ce6 100644 --- a/lib/inttostr.c +++ b/lib/inttostr.c @@ -1,3 +1,6 @@ +#include <config.h> +#include "inttostr.h" +#undef inttostr #define anytostr inttostr #define inttype int #include "anytostr.c" diff --git a/lib/inttostr.h b/lib/inttostr.h index 4f74968..eb6f542 100644 --- a/lib/inttostr.h +++ b/lib/inttostr.h @@ -21,6 +21,7 @@ #include <sys/types.h> #include "intprops.h" +#include "verify.h" #ifndef __GNUC_PREREQ # if defined __GNUC__ && defined __GNUC_MINOR__ @@ -44,3 +45,44 @@ char *inttostr (int, char *) __attribute_warn_unused_result__; char *offtostr (off_t, char *) __attribute_warn_unused_result__; char *uinttostr (unsigned int, char *) __attribute_warn_unused_result__; char *umaxtostr (uintmax_t, char *) __attribute_warn_unused_result__; + +/* Nonzero if P might be a char * pointer, zero if it is definitely not. */ +#if __GNUC_PREREQ (3,1) +# define __gl_might_be_char_ptr(p) \ + __builtin_types_compatible_p (__typeof__ (p), char *) +#else +# define __gl_might_be_char_ptr(p) (sizeof (p) == sizeof (char *)) +#endif + +/* An upper bound on the size of the object that P points at. Avoid + GCC's __builtin_object_size here, because GCC (as of version 4.5.1) + incorrectly rejects its use in an integral constant expression. */ +#define __gl_size_bound(p) \ + (__gl_might_be_char_ptr (p) ? (size_t) -1 : sizeof (p)) + +/* Convert a value of type T to a string, by invoking FUNC on the + value N, and using a char array S to hold the resulting string. + Diagnose obvious size problems at compile-time. */ +#define __gl_inttostr(t, func, n, s) \ + ((void) verify_true (INT_BUFSIZE_BOUND (t) <= __gl_size_bound (s)), \ + (func) (n, s)) + +/* Redefine imaxtostr etc. to check the size of the array, if possible. + S is not allowed to be a variable length array. */ +#define imaxtostr(n, s) __gl_inttostr (intmax_t, imaxtostr, n, s) +#define inttostr(n, s) __gl_inttostr (int, inttostr, n, s) +#define offtostr(n, s) __gl_inttostr (off_t, offtostr, n, s) +#define uinttostr(n, s) __gl_inttostr (unsigned int, uinttostr, n, s) +#define umaxtostr(n, s) __gl_inttostr (uintmax_t, umaxtostr, n, s) + +/* Easier-to-use macros, which don't require a string buffer argument. + They can be used in code that assumes C99-style compound literals. + They return a pointer to storage with scope equal to that of the + enclosing block. */ +#define int2str_call(t, func, n) \ + func (n, ((char [INT_BUFSIZE_BOUND (t)]) {0,})) +#define imax2str(n) int2str_call (intmax_t, imaxtostr, n) +#define int2str(n) int2str_call (int, inttostr, n) +#define off2str(n) int2str_call (off_t, offtostr, n) +#define uint2str(n) int2str_call (unsigned int, uinttostr, n) +#define umax2str(n) int2str_call (uintmax_t, umaxtostr, n) diff --git a/lib/offtostr.c b/lib/offtostr.c index 96082aa..c48fff1 100644 --- a/lib/offtostr.c +++ b/lib/offtostr.c @@ -1,3 +1,6 @@ +#include <config.h> +#include "inttostr.h" +#undef offtostr #define anytostr offtostr #define inttype off_t #include "anytostr.c" diff --git a/lib/uinttostr.c b/lib/uinttostr.c index 48fd98f..f915de9 100644 --- a/lib/uinttostr.c +++ b/lib/uinttostr.c @@ -1,3 +1,6 @@ +#include <config.h> +#include "inttostr.h" +#undef uinttostr #define anytostr uinttostr #define inttype unsigned int #include "anytostr.c" diff --git a/lib/umaxtostr.c b/lib/umaxtostr.c index f95bfc3..10043ae 100644 --- a/lib/umaxtostr.c +++ b/lib/umaxtostr.c @@ -1,3 +1,6 @@ +#include <config.h> +#include "inttostr.h" +#undef umaxtostr #define anytostr umaxtostr #define inttype uintmax_t #include "anytostr.c" diff --git a/modules/inttostr b/modules/inttostr index 6bbec28..495ef25 100644 --- a/modules/inttostr +++ b/modules/inttostr @@ -14,6 +14,7 @@ m4/inttostr.m4 Depends-on: intprops stdint +verify configure.ac: gl_INTTOSTR diff --git a/modules/inttostr-tests b/modules/inttostr-tests index 48dbe50..aa7698f 100644 --- a/modules/inttostr-tests +++ b/modules/inttostr-tests @@ -1,6 +1,7 @@ Files: tests/macros.h tests/test-inttostr.c +tests/test-inttostr2.c Depends-on: intprops diff --git a/tests/test-inttostr2.c b/tests/test-inttostr2.c new file mode 100644 index 0000000..f01d942 --- /dev/null +++ b/tests/test-inttostr2.c @@ -0,0 +1,89 @@ +/* Test Fortify support for inttostr functions. + Copyright (C) 2010 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Compile this with a GCC >= 4.3 on a glibc system, with options -O -S. */ + +/* Defining _FORTIFY_SOURCE, together with option -O, on a glibc system + leads to a definition of __USE_FORTIFY_LEVEL. */ +#undef _FORTIFY_SOURCE +#define _FORTIFY_SOURCE 1 + +#include <config.h> + +#include "inttostr.h" + +#include <string.h> + +/* Buffer too small: warning at compile time. */ + +char * +test_small_1 (int n) +{ + char buf[5]; + return strdup (inttostr (n, buf)); /* expect warning here */ +} + +/* Buffer too small: would like warning at compile time, but cannot + easily do it due to GCC's mishandling of __builtin_object_size. */ + +char * +test_small_2 (int n) +{ + char buf[5]; + return strdup (inttostr (n, &buf[0])); /* would like warning here */ +} + +/* Buffer large enough: no warning, and call inttostr. */ + +char * +test_large_1 (int n) +{ + char buf[12]; + return strdup (inttostr (n, buf)); +} + +char * +test_large_2 (int n) +{ + char buf[12]; + return strdup (inttostr (n, &buf[0])); +} + +/* Buffer is a variable length array; this is not supported. */ + +char * +test_variable_length_array (int n) +{ + char buf[n < 0 ? 21 : 20]; + return strdup (inttostr (n, buf)); /* expect warning here */ +} + +/* Buffer size unknown. + No warning. Call inttostr. */ + +char * +test_variable_length_array_pointer (int n) +{ + char buf[n < 0 ? 21 : 20]; + return strdup (inttostr (n, &buf[0])); +} + +/* + * For Emacs M-x compile + * Local Variables: + * compile-command: "gcc -I. -I.. -I../gllib -O -S test-inttostr2.c" + * End: + */ -- 1.7.2
>From db8a6bc4341e43c060e1b8c62110a866b5c4c661 Mon Sep 17 00:00:00 2001 From: Paul Eggert <egg...@cs.ucla.edu> Date: Sun, 24 Oct 2010 18:21:28 -0700 Subject: [PATCH] inttostr: prefer C99-style stack allocation of buffer This change depends on adding int2str etc. to gnulib. * src/chown-core.c (gid_to_name, uid_to_name): Use int2str instead of inttostr, and similarly for uint2str, imax2str, umax2str, off2str, and time2str. * src/cksum.c (cksum): Likewise. * src/csplit.c (handle_line_error, regexp_error, close_output_file): (parse_patterns): Likewise. * src/date.c (show_date): Likewise. * src/df.c (print_header): Likewise. * src/du.c (show_date): Likewise. * src/expr.c (mpz_get_str, mpz_out_str): Likewise. * src/factor.c (print_factors_single): Likewise. * src/head.c (elide_tail_bytes_pipe, elide_tail_lines_seekable, main): Likewise. * src/ls.c (gobble_file, print_long_format): (length_of_file_name_and_frills): Likewise. * src/shred.c (dopass): Likewise. * src/sort.c (specify_nmerge, key_warnings, check): Likewise. * src/split.c (main): Likewise. * src/tail.c (xlseek): Likewise. * src/test.c (binary_operator): Likewise. * src/wc.c (write_counts): Likewise. * src/ls.c (format_inode_buf): Renamed from format_inode, with a simpler API that doesn't involve buffer length. Omit the assert. (format_inode): New macro, with a simpler API than the old format_inode. All callers changed. * src/system.h (time2str): New macro, in the style of int2str. --- src/chown-core.c | 10 ++++------ src/cksum.c | 3 +-- src/csplit.c | 23 ++++++----------------- src/date.c | 3 +-- src/df.c | 2 +- src/du.c | 3 +-- src/expr.c | 6 ++---- src/factor.c | 5 ++--- src/head.c | 20 ++++++-------------- src/ls.c | 47 +++++++++++++++++++---------------------------- src/shred.c | 3 +-- src/sort.c | 16 ++++++---------- src/split.c | 9 +++------ src/system.h | 1 + src/tail.c | 3 +-- src/test.c | 6 ++---- src/wc.c | 11 +++++------ 17 files changed, 62 insertions(+), 109 deletions(-) diff --git a/src/chown-core.c b/src/chown-core.c index 1d3f74c..8feb0de 100644 --- a/src/chown-core.c +++ b/src/chown-core.c @@ -81,11 +81,10 @@ chopt_free (struct Chown_option *chopt ATTRIBUTE_UNUSED) extern char * gid_to_name (gid_t gid) { - char buf[INT_BUFSIZE_BOUND (intmax_t)]; struct group *grp = getgrgid (gid); return xstrdup (grp ? grp->gr_name - : TYPE_SIGNED (gid_t) ? imaxtostr (gid, buf) - : umaxtostr (gid, buf)); + : TYPE_SIGNED (gid_t) ? imax2str (gid) + : umax2str (gid)); } /* Convert the numeric user-id, UID, to a string stored in xmalloc'd memory, @@ -95,11 +94,10 @@ gid_to_name (gid_t gid) extern char * uid_to_name (uid_t uid) { - char buf[INT_BUFSIZE_BOUND (intmax_t)]; struct passwd *pwd = getpwuid (uid); return xstrdup (pwd ? pwd->pw_name - : TYPE_SIGNED (uid_t) ? imaxtostr (uid, buf) - : umaxtostr (uid, buf)); + : TYPE_SIGNED (uid_t) ? imax2str (uid) + : umax2str (uid)); } /* Tell the user how/if the user and group of FILE have been changed. diff --git a/src/cksum.c b/src/cksum.c index 282c777..a8124c1 100644 --- a/src/cksum.c +++ b/src/cksum.c @@ -186,7 +186,6 @@ cksum (const char *file, bool print_name) uintmax_t length = 0; size_t bytes_read; FILE *fp; - char length_buf[INT_BUFSIZE_BOUND (uintmax_t)]; char const *hp; if (STREQ (file, "-")) @@ -235,7 +234,7 @@ cksum (const char *file, bool print_name) return false; } - hp = umaxtostr (length, length_buf); + hp = umax2str (length); for (; length; length >>= 8) crc = (crc << 8) ^ crctab[((crc >> 24) ^ length) & 0xFF]; diff --git a/src/csplit.c b/src/csplit.c index 40baba8..7b1e31e 100644 --- a/src/csplit.c +++ b/src/csplit.c @@ -685,12 +685,10 @@ static void handle_line_error (const struct control *, uintmax_t) static void handle_line_error (const struct control *p, uintmax_t repetition) { - char buf[INT_BUFSIZE_BOUND (uintmax_t)]; - fprintf (stderr, _("%s: %s: line number out of range"), - program_name, quote (umaxtostr (p->lines_required, buf))); + program_name, quote (umax2str (p->lines_required))); if (repetition) - fprintf (stderr, _(" on repetition %s\n"), umaxtostr (repetition, buf)); + fprintf (stderr, _(" on repetition %s\n"), umax2str (repetition)); else fprintf (stderr, "\n"); @@ -737,10 +735,7 @@ regexp_error (struct control *p, uintmax_t repetition, bool ignore) program_name, quote (global_argv[p->argnum])); if (repetition) - { - char buf[INT_BUFSIZE_BOUND (uintmax_t)]; - fprintf (stderr, _(" on repetition %s\n"), umaxtostr (repetition, buf)); - } + fprintf (stderr, _(" on repetition %s\n"), umax2str (repetition)); else fprintf (stderr, "\n"); @@ -991,10 +986,7 @@ close_output_file (void) else { if (!suppress_count) - { - char buf[INT_BUFSIZE_BOUND (uintmax_t)]; - fprintf (stdout, "%s\n", umaxtostr (bytes_written, buf)); - } + fprintf (stdout, "%s\n", umax2str (bytes_written)); } output_stream = NULL; } @@ -1147,12 +1139,9 @@ parse_patterns (int argc, int start, char **argv) _("%s: line number must be greater than zero"), argv[i]); if (val < last_val) - { - char buf[INT_BUFSIZE_BOUND (uintmax_t)]; - error (EXIT_FAILURE, 0, + error (EXIT_FAILURE, 0, _("line number %s is smaller than preceding line number, %s"), - quote (argv[i]), umaxtostr (last_val, buf)); - } + quote (argv[i]), umax2str (last_val)); if (val == last_val) error (0, 0, diff --git a/src/date.c b/src/date.c index ad9b490..1f2ec36 100644 --- a/src/date.c +++ b/src/date.c @@ -535,8 +535,7 @@ show_date (const char *format, struct timespec when) tm = localtime (&when.tv_sec); if (! tm) { - char buf[INT_BUFSIZE_BOUND (intmax_t)]; - error (0, 0, _("time %s is out of range"), timetostr (when.tv_sec, buf)); + error (0, 0, _("time %s is out of range"), time2str (when.tv_sec)); return false; } diff --git a/src/df.c b/src/df.c index 749ce1a..68a3002 100644 --- a/src/df.c +++ b/src/df.c @@ -170,7 +170,7 @@ print_header (void) } else if (posix_format) printf (_(" %s-blocks Used Available Capacity"), - umaxtostr (output_block_size, buf)); + umax2str (output_block_size)); else { int opts = (human_suppress_point_zero diff --git a/src/du.c b/src/du.c index 4951826..f41eccd 100644 --- a/src/du.c +++ b/src/du.c @@ -350,8 +350,7 @@ show_date (const char *format, struct timespec when) struct tm *tm = localtime (&when.tv_sec); if (! tm) { - char buf[INT_BUFSIZE_BOUND (intmax_t)]; - char *when_str = timetostr (when.tv_sec, buf); + char *when_str = time2str (when.tv_sec); error (0, 0, _("time %s is out of range"), when_str); fputs (when_str, stdout); return; diff --git a/src/expr.c b/src/expr.c index dbeaeba..4c412b5 100644 --- a/src/expr.c +++ b/src/expr.c @@ -117,8 +117,7 @@ static char * mpz_get_str (char const *str, int base, mpz_t z) { (void) str; (void) base; - char buf[INT_BUFSIZE_BOUND (intmax_t)]; - return xstrdup (imaxtostr (z[0], buf)); + return xstrdup (imax2str (z[0])); } static int mpz_sgn (mpz_t z) @@ -139,8 +138,7 @@ static int mpz_out_str (FILE *stream, int base, mpz_t z) { (void) base; - char buf[INT_BUFSIZE_BOUND (intmax_t)]; - return fputs (imaxtostr (z[0], buf), stream) != EOF; + return fputs (imax2str (z[0]), stream) != EOF; } #endif diff --git a/src/factor.c b/src/factor.c index 7291d28..04bd60e 100644 --- a/src/factor.c +++ b/src/factor.c @@ -338,11 +338,10 @@ print_factors_single (uintmax_t n) uintmax_t factors[MAX_N_FACTORS]; size_t n_factors = factor_wheel (n, MAX_N_FACTORS, factors); size_t i; - char buf[INT_BUFSIZE_BOUND (uintmax_t)]; - printf ("%s:", umaxtostr (n, buf)); + printf ("%s:", umax2str (n)); for (i = 0; i < n_factors; i++) - printf (" %s", umaxtostr (factors[i], buf)); + printf (" %s", umax2str (factors[i])); putchar ('\n'); } diff --git a/src/head.c b/src/head.c index dd3dc89..0cb3581 100644 --- a/src/head.c +++ b/src/head.c @@ -225,11 +225,8 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0) #endif if (SIZE_MAX < n_elide_0 + READ_BUFSIZE) - { - char umax_buf[INT_BUFSIZE_BOUND (n_elide_0)]; - error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"), - umaxtostr (n_elide_0, umax_buf)); - } + error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"), + umax2str (n_elide_0)); /* Two cases to consider... 1) n_elide is small enough that we can afford to double-buffer: @@ -611,9 +608,8 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd, pos -= bytes_read; if (lseek (fd, pos, SEEK_SET) < 0) { - char offset_buf[INT_BUFSIZE_BOUND (pos)]; error (0, errno, _("%s: cannot seek to offset %s"), - pretty_filename, offtostr (pos, offset_buf)); + pretty_filename, off2str (pos)); return false; } bytes_read = safe_read (fd, buffer, bytes_read); @@ -682,9 +678,8 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd, pos -= BUFSIZ; if (lseek (fd, pos, SEEK_SET) < 0) { - char offset_buf[INT_BUFSIZE_BOUND (pos)]; error (0, errno, _("%s: cannot seek to offset %s"), - pretty_filename, offtostr (pos, offset_buf)); + pretty_filename, off2str (pos)); return false; } @@ -1041,11 +1036,8 @@ main (int argc, char **argv) print_headers = true; if ( ! count_lines && elide_from_end && OFF_T_MAX < n_units) - { - char umax_buf[INT_BUFSIZE_BOUND (n_units)]; - error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"), - umaxtostr (n_units, umax_buf)); - } + error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"), + umax2str (n_units)); file_list = (optind < argc ? (char const *const *) &argv[optind] diff --git a/src/ls.c b/src/ls.c index f861df9..9d50501 100644 --- a/src/ls.c +++ b/src/ls.c @@ -2976,18 +2976,16 @@ gobble_file (char const *name, enum filetype type, ino_t inode, if (format == long_format) { - char b[INT_BUFSIZE_BOUND (uintmax_t)]; - int b_len = strlen (umaxtostr (f->stat.st_nlink, b)); + int b_len = strlen (umax2str (f->stat.st_nlink)); if (nlink_width < b_len) nlink_width = b_len; if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode)) { - char buf[INT_BUFSIZE_BOUND (uintmax_t)]; - int len = strlen (umaxtostr (major (f->stat.st_rdev), buf)); + int len = strlen (umax2str (major (f->stat.st_rdev))); if (major_device_number_width < len) major_device_number_width = len; - len = strlen (umaxtostr (minor (f->stat.st_rdev), buf)); + len = strlen (umax2str (minor (f->stat.st_rdev))); if (minor_device_number_width < len) minor_device_number_width = len; len = major_device_number_width + 2 + minor_device_number_width; @@ -3009,8 +3007,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, if (print_inode) { - char buf[INT_BUFSIZE_BOUND (uintmax_t)]; - int len = strlen (umaxtostr (f->stat.st_ino, buf)); + int len = strlen (umax2str (f->stat.st_ino)); if (inode_number_width < len) inode_number_width = len; } @@ -3595,16 +3592,17 @@ format_group_width (gid_t g) } /* Return a pointer to a formatted version of F->stat.st_ino, - possibly using buffer, BUF, of length BUFLEN, which must be at least + possibly using buffer, BUF, which must be at least INT_BUFSIZE_BOUND (uintmax_t) bytes. */ static char * -format_inode (char *buf, size_t buflen, const struct fileinfo *f) +format_inode_buf (const struct fileinfo *f, char *buf) { - assert (INT_BUFSIZE_BOUND (uintmax_t) <= buflen); return (f->stat_ok && f->stat.st_ino != NOT_AN_INODE_NUMBER ? umaxtostr (f->stat.st_ino, buf) : (char *) "?"); } +#define format_inode(f) \ + format_inode_buf (f, (char [INT_BUFSIZE_BOUND (uintmax_t)]) {'\0',}) /* Print information about F in long format. */ static void @@ -3661,9 +3659,7 @@ print_long_format (const struct fileinfo *f) if (print_inode) { - char hbuf[INT_BUFSIZE_BOUND (uintmax_t)]; - sprintf (p, "%*s ", inode_number_width, - format_inode (hbuf, sizeof hbuf, f)); + sprintf (p, "%*s ", inode_number_width, format_inode (f)); /* Increment by strlen (p) here, rather than by inode_number_width + 1. The latter is wrong when inode_number_width is zero. */ p += strlen (p); @@ -3687,11 +3683,9 @@ print_long_format (const struct fileinfo *f) /* The last byte of the mode string is the POSIX "optional alternate access method flag". */ - { - char hbuf[INT_BUFSIZE_BOUND (uintmax_t)]; - sprintf (p, "%s %*s ", modebuf, nlink_width, - ! f->stat_ok ? "?" : umaxtostr (f->stat.st_nlink, hbuf)); - } + sprintf (p, "%s %*s ", modebuf, nlink_width, + ! f->stat_ok ? "?" : umax2str (f->stat.st_nlink)); + /* Increment by strlen (p) here, rather than by, e.g., sizeof modebuf - 2 + any_has_acl + 1 + nlink_width + 1. The latter is wrong when nlink_width is zero. */ @@ -3721,16 +3715,14 @@ print_long_format (const struct fileinfo *f) if (f->stat_ok && (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode))) { - char majorbuf[INT_BUFSIZE_BOUND (uintmax_t)]; - char minorbuf[INT_BUFSIZE_BOUND (uintmax_t)]; int blanks_width = (file_size_width - (major_device_number_width + 2 + minor_device_number_width)); sprintf (p, "%*s, %*s ", major_device_number_width + MAX (0, blanks_width), - umaxtostr (major (f->stat.st_rdev), majorbuf), + umax2str (major (f->stat.st_rdev)), minor_device_number_width, - umaxtostr (minor (f->stat.st_rdev), minorbuf)); + umax2str (minor (f->stat.st_rdev))); p += file_size_width + 1; } else @@ -3800,11 +3792,10 @@ print_long_format (const struct fileinfo *f) { /* The time cannot be converted using the desired format, so print it as a huge integer number of seconds. */ - char hbuf[INT_BUFSIZE_BOUND (intmax_t)]; sprintf (p, "%*s ", long_time_expected_width (), (! f->stat_ok ? "?" - : timetostr (when_timespec.tv_sec, hbuf))); + : time2str (when_timespec.tv_sec))); /* FIXME: (maybe) We discarded when_timespec.tv_nsec. */ p += strlen (p); } @@ -4045,13 +4036,13 @@ prep_non_filename_text (void) static size_t print_file_name_and_frills (const struct fileinfo *f, size_t start_col) { - char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))]; + char buf[LONGEST_HUMAN_READABLE + 1]; set_normal_color (); if (print_inode) printf ("%*s ", format == with_commas ? 0 : inode_number_width, - format_inode (buf, sizeof buf, f)); + format_inode (f)); if (print_block_size) printf ("%*s ", format == with_commas ? 0 : block_size_width, @@ -4250,11 +4241,11 @@ length_of_file_name_and_frills (const struct fileinfo *f) { size_t len = 0; size_t name_width; - char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))]; + char buf[LONGEST_HUMAN_READABLE + 1]; if (print_inode) len += 1 + (format == with_commas - ? strlen (umaxtostr (f->stat.st_ino, buf)) + ? strlen (umax2str (f->stat.st_ino)) : inode_number_width); if (print_block_size) diff --git a/src/shred.c b/src/shred.c index 1da197f..d8f4202 100644 --- a/src/shred.c +++ b/src/shred.c @@ -446,7 +446,6 @@ dopass (int fd, char const *qname, off_t *sizep, int type, else { int errnum = errno; - char buf[INT_BUFSIZE_BOUND (uintmax_t)]; /* If the first write of the first pass for a given file has just failed with EINVAL, turn off direct mode I/O @@ -462,7 +461,7 @@ dopass (int fd, char const *qname, off_t *sizep, int type, continue; } error (0, errnum, _("%s: error writing at offset %s"), - qname, umaxtostr (offset + soff, buf)); + qname, umax2str (offset + soff)); /* 'shred' is often used on bad media, before throwing it out. Thus, it shouldn't give up on bad blocks. This diff --git a/src/sort.c b/src/sort.c index 7e25f6a..4f53a6d 100644 --- a/src/sort.c +++ b/src/sort.c @@ -1302,13 +1302,12 @@ specify_nmerge (int oi, char c, char const *s) if (e == LONGINT_OVERFLOW) { - char max_nmerge_buf[INT_BUFSIZE_BOUND (max_nmerge)]; error (0, 0, _("--%s argument %s too large"), long_options[oi].name, quote (s)); error (SORT_FAILURE, 0, _("maximum --%s argument with current rlimit is %s"), long_options[oi].name, - uinttostr (max_nmerge, max_nmerge_buf)); + uint2str (max_nmerge)); } else xstrtol_fatal (e, oi, c, long_options, s); @@ -2320,7 +2319,6 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) { size_t sword = key->sword; size_t eword = key->eword; - char tmp[INT_BUFSIZE_BOUND (uintmax_t)]; /* obsolescent syntax +A.x -B.y is equivalent to: -k A+1.x+1,B.y (when y = 0) -k A+1.x+1,B+1.y (when y > 0) */ @@ -2332,14 +2330,13 @@ key_warnings (struct keyfield const *gkey, bool gkey_only) if (sword == SIZE_MAX) sword++; - po = stpcpy (stpcpy (po, "+"), umaxtostr (sword, tmp)); - pn = stpcpy (stpcpy (pn, "-k "), umaxtostr (sword + 1, tmp)); + po = stpcpy (stpcpy (po, "+"), umax2str (sword)); + pn = stpcpy (stpcpy (pn, "-k "), umax2str (sword + 1)); if (key->eword != SIZE_MAX) { - po = stpcpy (stpcpy (po, " -"), umaxtostr (eword + 1, tmp)); + po = stpcpy (stpcpy (po, " -"), umax2str (eword + 1)); pn = stpcpy (stpcpy (pn, ","), - umaxtostr (eword + 1 - + (key->echar == SIZE_MAX), tmp)); + umax2str (eword + 1 + (key->echar == SIZE_MAX))); } error (0, 0, _("obsolescent key `%s' used; consider `%s' instead"), obuf, nbuf); @@ -2734,10 +2731,9 @@ check (char const *file_name, char checkonly) struct line const *disorder_line = line - 1; uintmax_t disorder_line_number = buffer_linelim (&buf) - disorder_line + line_number; - char hr_buf[INT_BUFSIZE_BOUND (disorder_line_number)]; fprintf (stderr, _("%s: %s:%s: disorder: "), program_name, file_name, - umaxtostr (disorder_line_number, hr_buf)); + umax2str (disorder_line_number)); write_line (disorder_line, stderr, _("standard error")); } diff --git a/src/split.c b/src/split.c index 61ae265..758d6c9 100644 --- a/src/split.c +++ b/src/split.c @@ -479,12 +479,9 @@ main (int argc, char **argv) n_units = 0; /* More than one number given; ignore other. */ digits_optind = this_optind; if (!DECIMAL_DIGIT_ACCUMULATE (n_units, c - '0', uintmax_t)) - { - char buffer[INT_BUFSIZE_BOUND (uintmax_t)]; - error (EXIT_FAILURE, 0, - _("line count option -%s%c... is too large"), - umaxtostr (n_units, buffer), c); - } + error (EXIT_FAILURE, 0, + _("line count option -%s%c... is too large"), + umax2str (n_units), c); break; case 'd': diff --git a/src/system.h b/src/system.h index 9e14681..2ed3e95 100644 --- a/src/system.h +++ b/src/system.h @@ -626,6 +626,7 @@ timetostr (time_t t, char *buf) ? imaxtostr (t, buf) : umaxtostr (t, buf)); } +#define time2str(t) (TYPE_SIGNED (time_t) ? imax2str (t) : umax2str (t)) static inline char * bad_cast (char const *s) diff --git a/src/tail.c b/src/tail.c index 68cc819..8c34b7d 100644 --- a/src/tail.c +++ b/src/tail.c @@ -434,13 +434,12 @@ static off_t xlseek (int fd, off_t offset, int whence, char const *filename) { off_t new_offset = lseek (fd, offset, whence); - char buf[INT_BUFSIZE_BOUND (offset)]; char *s; if (0 <= new_offset) return new_offset; - s = offtostr (offset, buf); + s = off2str (offset); switch (whence) { case SEEK_SET: diff --git a/src/test.c b/src/test.c index e2247d2..460f11e 100644 --- a/src/test.c +++ b/src/test.c @@ -290,13 +290,11 @@ binary_operator (bool l_is_l) || (argv[op][1] == 'n' && argv[op][2] == 'e')) && !argv[op][3]) { - char lbuf[INT_BUFSIZE_BOUND (uintmax_t)]; - char rbuf[INT_BUFSIZE_BOUND (uintmax_t)]; char const *l = (l_is_l - ? umaxtostr (strlen (argv[op - 1]), lbuf) + ? umax2str (strlen (argv[op - 1])) : find_int (argv[op - 1])); char const *r = (r_is_l - ? umaxtostr (strlen (argv[op + 2]), rbuf) + ? umax2str (strlen (argv[op + 2])) : find_int (argv[op + 1])); int cmp = strintcmp (l, r); bool xe_operator = (argv[op][2] == 'e'); diff --git a/src/wc.c b/src/wc.c index a1922ba..1cd6f98 100644 --- a/src/wc.c +++ b/src/wc.c @@ -150,31 +150,30 @@ write_counts (uintmax_t lines, { static char const format_sp_int[] = " %*s"; char const *format_int = format_sp_int + 1; - char buf[INT_BUFSIZE_BOUND (uintmax_t)]; if (print_lines) { - printf (format_int, number_width, umaxtostr (lines, buf)); + printf (format_int, number_width, umax2str (lines)); format_int = format_sp_int; } if (print_words) { - printf (format_int, number_width, umaxtostr (words, buf)); + printf (format_int, number_width, umax2str (words)); format_int = format_sp_int; } if (print_chars) { - printf (format_int, number_width, umaxtostr (chars, buf)); + printf (format_int, number_width, umax2str (chars)); format_int = format_sp_int; } if (print_bytes) { - printf (format_int, number_width, umaxtostr (bytes, buf)); + printf (format_int, number_width, umax2str (bytes)); format_int = format_sp_int; } if (print_linelength) { - printf (format_int, number_width, umaxtostr (linelength, buf)); + printf (format_int, number_width, umax2str (linelength)); } if (file) printf (" %s", file); -- 1.7.2