diff options
Diffstat (limited to 'lib/sh/snprintf.c')
-rw-r--r-- | lib/sh/snprintf.c | 178 |
1 files changed, 112 insertions, 66 deletions
diff --git a/lib/sh/snprintf.c b/lib/sh/snprintf.c index d46b2d9..87ca217 100644 --- a/lib/sh/snprintf.c +++ b/lib/sh/snprintf.c @@ -9,7 +9,7 @@ Unix snprintf implementation. derived from inetutils/libinetutils/snprintf.c Version 1.1 - Copyright (C) 2001,2006,2010 Free Software Foundation, Inc. + Copyright (C) 2001,2006,2010,2012 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -26,7 +26,7 @@ You should have received a copy of the GNU General Public License along with Bash. If not, see <http://www.gnu.org/licenses/>. - Revision History: + Original (pre-bash) Revision History: 1.1: * added changes from Miles Bader @@ -50,7 +50,6 @@ * Currently doesn't handle (and bash/readline doesn't use): * * *M$ width, precision specifications * * %N$ numbered argument conversions - * * inf, nan floating values imperfect (if isinf(), isnan() not in libc) * * support for `F' is imperfect with ldfallback(), since underlying * printf may not handle it -- should ideally have another autoconf test */ @@ -303,11 +302,30 @@ static void dfallback __P((struct DATA *, const char *, const char *, double)); static char *groupnum __P((char *)); -#ifndef HAVE_ISINF_IN_LIBC -static int isinf __P((double)); +#if defined (HAVE_LONG_DOUBLE) +# define LONGDOUBLE long double +#else +# define LONGDOUBLE double +#endif + +#ifndef isnan + static inline int isnan_f (float x) { return x != x; } + static inline int isnan_d (double x) { return x != x; } + static inline int isnan_ld (LONGDOUBLE x) { return x != x; } + # define isnan(x) \ + (sizeof (x) == sizeof (LONGDOUBLE) ? isnan_ld (x) \ + : sizeof (x) == sizeof (double) ? isnan_d (x) \ + : isnan_f (x)) #endif -#ifndef HAVE_ISNAN_IN_LIBC -static int isnan __P((double)); + +#ifndef isinf + static inline int isinf_f (float x) { return !isnan (x) && isnan (x - x); } + static inline int isinf_d (double x) { return !isnan (x) && isnan (x - x); } + static inline int isinf_ld (LONGDOUBLE x) { return !isnan (x) && isnan (x - x); } + # define isinf(x) \ + (sizeof (x) == sizeof (LONGDOUBLE) ? isinf_ld (x) \ + : sizeof (x) == sizeof (double) ? isinf_d (x) \ + : isinf_f (x)) #endif #ifdef DRIVER @@ -371,7 +389,7 @@ static void xfree __P((void *)); while (0) #define PUT_PLUS(d, p, zero) \ - if ((d) > zero && (p)->justify == RIGHT) \ + if (((p)->flags & PF_PLUS) && (d) > zero) \ PUT_CHAR('+', p) #define PUT_SPACE(d, p, zero) \ @@ -431,9 +449,9 @@ static void xfree __P((void *)); if (lv) \ { \ if (lv->decimal_point && lv->decimal_point[0]) \ - (d) = lv->decimal_point[0]; \ + (d) = lv->decimal_point[0]; \ if (lv->thousands_sep && lv->thousands_sep[0]) \ - (t) = lv->thousands_sep[0]; \ + (t) = lv->thousands_sep[0]; \ (g) = lv->grouping ? lv->grouping : ""; \ if (*(g) == '\0' || *(g) == CHAR_MAX || (t) == -1) (g) = 0; \ } \ @@ -574,7 +592,7 @@ integral(real, ip) /* * return an ascii representation of the integral part of the number * and set fract to be an ascii representation of the fraction part - * the container for the fraction and the integral part or staticly + * the container for the fraction and the integral part or statically * declare with fix size */ static char * @@ -586,10 +604,9 @@ numtoa(number, base, precision, fract) register int i, j; double ip, fp; /* integer and fraction part */ double fraction; - int digits = MAX_INT - 1; + int digits, sign; static char integral_part[MAX_INT]; static char fraction_part[MAX_FRACT]; - double sign; int ch; /* taking care of the obvious case: 0.0 */ @@ -607,8 +624,12 @@ numtoa(number, base, precision, fract) return integral_part; } + /* -0 is tricky */ + sign = (number == -0.) ? '-' : ((number < 0.) ? '-' : '+'); + digits = MAX_INT - 1; + /* for negative numbers */ - if ((sign = number) < 0.) + if (sign == '-') { number = -number; digits--; /* sign consume one digit */ @@ -643,7 +664,7 @@ numtoa(number, base, precision, fract) integral_part[i] = '9'; /* put the sign ? */ - if (sign < 0.) + if (sign == '-') integral_part[i++] = '-'; integral_part[i] = '\0'; @@ -682,9 +703,13 @@ number(p, d, base) long sd; int flags; - /* An explicit precision turns off the zero-padding flag. */ + /* An explicit precision turns off the zero-padding flag and sets the + pad character back to space. */ if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT)) - p->flags &= ~PF_ZEROPAD; + { + p->flags &= ~PF_ZEROPAD; + p->pad = ' '; + } sd = d; /* signed for ' ' padding in base 10 */ flags = 0; @@ -698,10 +723,11 @@ number(p, d, base) { GETLOCALEDATA(decpoint, thoussep, grouping); if (grouping && (t = groupnum (tmp))) - tmp = t; + tmp = t; } - p->width -= strlen(tmp); + /* need to add one for any `+', but we only add one in base 10 */ + p->width -= strlen(tmp) + (base == 10 && d > 0 && (p->flags & PF_PLUS)); PAD_RIGHT(p); if ((p->flags & PF_DOT) && p->precision > 0) @@ -753,9 +779,13 @@ lnumber(p, d, base) long long sd; int flags; - /* An explicit precision turns off the zero-padding flag. */ + /* An explicit precision turns off the zero-padding flag and sets the + pad character back to space. */ if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT)) - p->flags &= ~PF_ZEROPAD; + { + p->flags &= ~PF_ZEROPAD; + p->pad = ' '; + } sd = d; /* signed for ' ' padding in base 10 */ flags = (*p->pf == 'x' || *p->pf == 'X' || *p->pf == 'o' || *p->pf == 'u' || *p->pf == 'U') ? FL_UNSIGNED : 0; @@ -768,10 +798,11 @@ lnumber(p, d, base) { GETLOCALEDATA(decpoint, thoussep, grouping); if (grouping && (t = groupnum (tmp))) - tmp = t; + tmp = t; } - p->width -= strlen(tmp); + /* need to add one for any `+', but we only add one in base 10 */ + p->width -= strlen(tmp) + (base == 10 && d > 0 && (p->flags & PF_PLUS)); PAD_RIGHT(p); if ((p->flags & PF_DOT) && p->precision > 0) @@ -875,11 +906,11 @@ wstrings(p, tmp) { len = wcsrtombs (NULL, &ws, 0, &mbs); if (len != (size_t)-1) - { + { memset (&mbs, '\0', sizeof (mbstate_t)); os = (char *)xmalloc (len + 1); (void)wcsrtombs (os, &ws, len + 1, &mbs); - } + } } if (len == (size_t)-1) { @@ -919,32 +950,6 @@ wchars (p, wc) #ifdef FLOATING_POINT -#ifndef HAVE_ISINF_IN_LIBC -/* Half-assed versions, since we don't want to link with libm. */ -static int -isinf(d) - double d; -{ -#ifdef DBL_MAX - if (d < DBL_MIN) - return -1; - else if (d > DBL_MAX) - return 1; - else -#endif - return 0; -} -#endif - -#ifndef HAVE_ISNAN_IN_LIBC -static int -isnan(d) - double d; -{ - return 0; -} -#endif - /* Check for [+-]infinity and NaN. If MODE == 1, we check for Infinity, else (mode == 2) we check for NaN. This does the necessary printing. Returns 1 if Inf or Nan, 0 if not. */ @@ -1002,19 +1007,35 @@ floating(p, d) { /* smash the trailing zeros unless altform */ for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--) - tmp2[i] = '\0'; + tmp2[i] = '\0'; if (tmp2[0] == '\0') p->precision = 0; } /* calculate the padding. 1 for the dot */ p->width = p->width - + /* XXX - should this be d>0. && (p->flags & PF_PLUS) ? */ +#if 0 ((d > 0. && p->justify == RIGHT) ? 1:0) - +#else + ((d > 0. && (p->flags & PF_PLUS)) ? 1:0) - +#endif ((p->flags & PF_SPACE) ? 1:0) - strlen(tmp) - p->precision - ((p->precision != 0 || (p->flags & PF_ALTFORM)) ? 1 : 0); /* radix char */ - PAD_RIGHT(p); - PUT_PLUS(d, p, 0.); + + if (p->pad == ' ') + { + PAD_RIGHT(p); + PUT_PLUS(d, p, 0.); + } + else + { + if (*tmp == '-') + PUT_CHAR(*tmp++, p); + PUT_PLUS(d, p, 0.); + PAD_RIGHT(p); + } PUT_SPACE(d, p, 0.); while (*tmp) @@ -1058,14 +1079,30 @@ exponent(p, d) tmp = dtoa(d, p->precision, &tmp2); /* 1 for unit, 1 for the '.', 1 for 'e|E', - * 1 for '+|-', 2 for 'exp' */ + * 1 for '+|-', 2 for 'exp' (but no `.' if precision == 0 */ /* calculate how much padding need */ p->width = p->width - + /* XXX - should this be d>0. && (p->flags & PF_PLUS) ? */ +#if 0 ((d > 0. && p->justify == RIGHT) ? 1:0) - - ((p->flags & PF_SPACE) ? 1:0) - p->precision - 6; +#else + ((d > 0. && (p->flags & PF_PLUS)) ? 1:0) - +#endif + (p->precision != 0 || (p->flags & PF_ALTFORM)) - + ((p->flags & PF_SPACE) ? 1:0) - p->precision - 5; - PAD_RIGHT(p); - PUT_PLUS(d, p, 0.); + if (p->pad == ' ') + { + PAD_RIGHT(p); + PUT_PLUS(d, p, 0.); + } + else + { + if (*tmp == '-') + PUT_CHAR(*tmp++, p); + PUT_PLUS(d, p, 0.); + PAD_RIGHT(p); + } PUT_SPACE(d, p, 0.); while (*tmp) @@ -1163,7 +1200,7 @@ groupnum (s) else if (*g == CHAR_MAX) { do - *--re = *--se; + *--re = *--se; while (se > s); break; } @@ -1295,10 +1332,6 @@ vsnprintf_internal(data, string, length, format, args) case '#': data->flags |= PF_ALTFORM; continue; - case '0': - data->flags |= PF_ZEROPAD; - data->pad = '0'; - continue; case '*': if (data->flags & PF_DOT) data->flags |= PF_STAR_P; @@ -1322,13 +1355,25 @@ vsnprintf_internal(data, string, length, format, args) if ((data->flags & PF_DOT) == 0) { data->flags |= PF_PLUS; - data->justify = RIGHT; + if ((data->flags & PF_LADJUST) == 0) + data->justify = RIGHT; } continue; case '\'': data->flags |= PF_THOUSANDS; continue; + case '0': + /* If we're not specifying precision (in which case we've seen + a `.') and we're not performing left-adjustment (in which + case the `0' is ignored), a `0' is taken as the zero-padding + flag. */ + if ((data->flags & (PF_DOT|PF_LADJUST)) == 0) + { + data->flags |= PF_ZEROPAD; + data->pad = '0'; + continue; + } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -1410,8 +1455,9 @@ conv_break: else { /* reduce precision by 1 because of leading digit before - decimal point in e format. */ - data->precision--; + decimal point in e format, unless specified as 0. */ + if (data->precision > 0) + data->precision--; exponent(data, d); } state = 0; |