>From db8a6bc4341e43c060e1b8c62110a866b5c4c661 Mon Sep 17 00:00:00 2001 From: Paul Eggert 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