| /* snprintf - formatted output to strings, with bounds checking and allocation */ |
| |
| /* |
| build a test version with |
| gcc -g -DDRIVER -I../.. -I../../include -o test-snprintf snprintf.c fmtu*long.o |
| */ |
| |
| /* |
| Unix snprintf implementation. |
| derived from inetutils/libinetutils/snprintf.c Version 1.1 |
| |
| Copyright (C) 2001,2006 Free Software Foundation, Inc. |
| |
| This file is part of GNU Bash, the Bourne Again SHell. |
| |
| Bash 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. |
| |
| Bash 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 Bash. If not, see <http://www.gnu.org/licenses/>. |
| |
| Revision History: |
| |
| 1.1: |
| * added changes from Miles Bader |
| * corrected a bug with %f |
| * added support for %#g |
| * added more comments :-) |
| 1.0: |
| * supporting must ANSI syntaxic_sugars |
| 0.0: |
| * support %s %c %d |
| |
| THANKS(for the patches and ideas): |
| Miles Bader |
| Cyrille Rustom |
| Jacek Slabocewiz |
| Mike Parker(mouse) |
| |
| */ |
| |
| /* |
| * 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 |
| */ |
| |
| #define FLOATING_POINT |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| /* GCC 4.2 on Snow Leopard doesn't like the snprintf prototype */ |
| #if defined(DEBUG) && !defined (MACOSX) |
| # undef HAVE_SNPRINTF |
| # undef HAVE_ASPRINTF |
| |
| # define HAVE_SNPRINTF 0 |
| # define HAVE_ASPRINTF 0 |
| #endif |
| |
| #if defined(DRIVER) && !defined(HAVE_CONFIG_H) |
| #define HAVE_LONG_LONG |
| #define HAVE_LONG_DOUBLE |
| #ifdef __linux__ |
| #define HAVE_PRINTF_A_FORMAT |
| #endif |
| #define HAVE_ISINF_IN_LIBC |
| #define HAVE_ISNAN_IN_LIBC |
| #define PREFER_STDARG |
| #define HAVE_STRINGIZE |
| #define HAVE_LIMITS_H |
| #define HAVE_STDDEF_H |
| #define HAVE_LOCALE_H |
| #define intmax_t long |
| #endif |
| |
| #if !HAVE_SNPRINTF || !HAVE_ASPRINTF |
| |
| #include <bashtypes.h> |
| |
| #if defined(PREFER_STDARG) |
| # include <stdarg.h> |
| #else |
| # include <varargs.h> |
| #endif |
| |
| #ifdef HAVE_LIMITS_H |
| # include <limits.h> |
| #endif |
| #include <bashansi.h> |
| #ifdef HAVE_STDDEF_H |
| # include <stddef.h> |
| #endif |
| #include <chartypes.h> |
| |
| #ifdef HAVE_STDINT_H |
| # include <stdint.h> |
| #endif |
| |
| #ifdef FLOATING_POINT |
| # include <float.h> /* for manifest constants */ |
| # include <stdio.h> /* for sprintf */ |
| #endif |
| |
| #include <typemax.h> |
| |
| #ifdef HAVE_LOCALE_H |
| # include <locale.h> |
| #endif |
| |
| #include "stdc.h" |
| #include <shmbutil.h> |
| |
| #ifndef DRIVER |
| # include "shell.h" |
| #else |
| # define FL_PREFIX 0x01 /* add 0x, 0X, or 0 prefix as appropriate */ |
| # define FL_ADDBASE 0x02 /* add base# prefix to converted value */ |
| # define FL_HEXUPPER 0x04 /* use uppercase when converting to hex */ |
| # define FL_UNSIGNED 0x08 /* don't add any sign */ |
| extern char *fmtulong __P((unsigned long int, int, char *, size_t, int)); |
| extern char *fmtullong __P((unsigned long long int, int, char *, size_t, int)); |
| #endif |
| |
| #ifndef FREE |
| # define FREE(x) if (x) free (x) |
| #endif |
| |
| /* Bound on length of the string representing an integer value of type T. |
| Subtract one for the sign bit if T is signed; |
| 302 / 1000 is log10 (2) rounded up; |
| add one for integer division truncation; |
| add one more for a minus sign if t is signed. */ |
| #define INT_STRLEN_BOUND(t) \ |
| ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 \ |
| + 1 + TYPE_SIGNED (t)) |
| |
| /* conversion flags */ |
| #define PF_ALTFORM 0x00001 /* # */ |
| #define PF_HEXPREFIX 0x00002 /* 0[Xx] */ |
| #define PF_LADJUST 0x00004 /* - */ |
| #define PF_ZEROPAD 0x00008 /* 0 */ |
| #define PF_PLUS 0x00010 /* + */ |
| #define PF_SPACE 0x00020 /* ' ' */ |
| #define PF_THOUSANDS 0x00040 /* ' */ |
| |
| #define PF_DOT 0x00080 /* `.precision' */ |
| #define PF_STAR_P 0x00100 /* `*' after precision */ |
| #define PF_STAR_W 0x00200 /* `*' before or without precision */ |
| |
| /* length modifiers */ |
| #define PF_SIGNEDCHAR 0x00400 /* hh */ |
| #define PF_SHORTINT 0x00800 /* h */ |
| #define PF_LONGINT 0x01000 /* l */ |
| #define PF_LONGLONG 0x02000 /* ll */ |
| #define PF_LONGDBL 0x04000 /* L */ |
| #define PF_INTMAX_T 0x08000 /* j */ |
| #define PF_SIZE_T 0x10000 /* z */ |
| #define PF_PTRDIFF_T 0x20000 /* t */ |
| |
| #define PF_ALLOCBUF 0x40000 /* for asprintf, vasprintf */ |
| |
| #define PFM_SN 0x01 /* snprintf, vsnprintf */ |
| #define PFM_AS 0x02 /* asprintf, vasprintf */ |
| |
| #define ASBUFSIZE 128 |
| |
| #define x_digs "0123456789abcdef" |
| #define X_digs "0123456789ABCDEF" |
| |
| static char intbuf[INT_STRLEN_BOUND(unsigned long) + 1]; |
| |
| static int decpoint; |
| static int thoussep; |
| static char *grouping; |
| |
| /* |
| * For the FLOATING POINT FORMAT : |
| * the challenge was finding a way to |
| * manipulate the Real numbers without having |
| * to resort to mathematical function(it |
| * would require to link with -lm) and not |
| * going down to the bit pattern(not portable) |
| * |
| * so a number, a real is: |
| |
| real = integral + fraction |
| |
| integral = ... + a(2)*10^2 + a(1)*10^1 + a(0)*10^0 |
| fraction = b(1)*10^-1 + b(2)*10^-2 + ... |
| |
| where: |
| 0 <= a(i) => 9 |
| 0 <= b(i) => 9 |
| |
| from then it was simple math |
| */ |
| |
| /* |
| * size of the buffer for the integral part |
| * and the fraction part |
| */ |
| #define MAX_INT 99 + 1 /* 1 for the null */ |
| #define MAX_FRACT 307 + 1 |
| |
| /* |
| * These functions use static buffers to store the results, |
| * and so are not reentrant |
| */ |
| #define itoa(n) fmtulong(n, 10, intbuf, sizeof(intbuf), 0); |
| #define dtoa(n, p, f) numtoa(n, 10, p, f) |
| |
| #define SWAP_INT(a,b) {int t; t = (a); (a) = (b); (b) = t;} |
| |
| #define GETARG(type) (va_arg(args, type)) |
| |
| /* Macros that do proper sign extension and handle length modifiers. Used |
| for the integer conversion specifiers. */ |
| #define GETSIGNED(p) \ |
| (((p)->flags & PF_LONGINT) \ |
| ? GETARG (long) \ |
| : (((p)->flags & PF_SHORTINT) ? (long)(short)GETARG (int) \ |
| : (long)GETARG (int))) |
| |
| #define GETUNSIGNED(p) \ |
| (((p)->flags & PF_LONGINT) \ |
| ? GETARG (unsigned long) \ |
| : (((p)->flags & PF_SHORTINT) ? (unsigned long)(unsigned short)GETARG (int) \ |
| : (unsigned long)GETARG (unsigned int))) |
| |
| |
| #ifdef HAVE_LONG_DOUBLE |
| #define GETLDOUBLE(p) GETARG (long double) |
| #endif |
| #define GETDOUBLE(p) GETARG (double) |
| |
| #define SET_SIZE_FLAGS(p, type) \ |
| if (sizeof (type) > sizeof (int)) \ |
| (p)->flags |= PF_LONGINT; \ |
| if (sizeof (type) > sizeof (long)) \ |
| (p)->flags |= PF_LONGLONG; |
| |
| /* this struct holds everything we need */ |
| struct DATA |
| { |
| int length; |
| char *base; /* needed for [v]asprintf */ |
| char *holder; |
| int counter; |
| const char *pf; |
| |
| /* FLAGS */ |
| int flags; |
| int justify; |
| int width, precision; |
| char pad; |
| }; |
| |
| /* the floating point stuff */ |
| #ifdef FLOATING_POINT |
| static double pow_10 __P((int)); |
| static int log_10 __P((double)); |
| static double integral __P((double, double *)); |
| static char *numtoa __P((double, int, int, char **)); |
| #endif |
| |
| static void init_data __P((struct DATA *, char *, size_t, const char *, int)); |
| static void init_conv_flag __P((struct DATA *)); |
| |
| /* for the format */ |
| #ifdef FLOATING_POINT |
| static void floating __P((struct DATA *, double)); |
| static void exponent __P((struct DATA *, double)); |
| #endif |
| static void number __P((struct DATA *, unsigned long, int)); |
| #ifdef HAVE_LONG_LONG |
| static void lnumber __P((struct DATA *, unsigned long long, int)); |
| #endif |
| static void pointer __P((struct DATA *, unsigned long)); |
| static void strings __P((struct DATA *, char *)); |
| |
| #ifdef FLOATING_POINT |
| # define FALLBACK_FMTSIZE 32 |
| # define FALLBACK_BASE 4096 |
| # define LFALLBACK_BASE 5120 |
| # ifdef HAVE_LONG_DOUBLE |
| static void ldfallback __P((struct DATA *, const char *, const char *, long double)); |
| # endif |
| static void dfallback __P((struct DATA *, const char *, const char *, double)); |
| #endif |
| |
| static char *groupnum __P((char *)); |
| |
| #ifndef HAVE_ISINF_IN_LIBC |
| static int isinf __P((double)); |
| #endif |
| #ifndef HAVE_ISNAN_IN_LIBC |
| static int isnan __P((double)); |
| #endif |
| |
| #ifdef DRIVER |
| static void memory_error_and_abort (); |
| static void *xmalloc __P((size_t)); |
| static void *xrealloc __P((void *, size_t)); |
| static void xfree __P((void *)); |
| #else |
| # include <xmalloc.h> |
| #endif |
| |
| /* those are defines specific to snprintf to hopefully |
| * make the code clearer :-) |
| */ |
| #define RIGHT 1 |
| #define LEFT 0 |
| #define NOT_FOUND -1 |
| #define FOUND 1 |
| #define MAX_FIELD 15 |
| |
| /* round off to the precision */ |
| #define ROUND(d, p) \ |
| (d < 0.) ? \ |
| d - pow_10(-(p)->precision) * 0.5 : \ |
| d + pow_10(-(p)->precision) * 0.5 |
| |
| /* set default precision */ |
| #define DEF_PREC(p) \ |
| if ((p)->precision == NOT_FOUND) \ |
| (p)->precision = 6 |
| |
| /* put a char. increment the number of chars written even if we've exceeded |
| the vsnprintf/snprintf buffer size (for the return value) */ |
| #define PUT_CHAR(c, p) \ |
| do \ |
| { \ |
| if (((p)->flags & PF_ALLOCBUF) && ((p)->counter >= (p)->length - 1)) \ |
| { \ |
| (p)->length += ASBUFSIZE; \ |
| (p)->base = (char *)xrealloc((p)->base, (p)->length); \ |
| (p)->holder = (p)->base + (p)->counter; /* in case reallocated */ \ |
| } \ |
| if ((p)->counter < (p)->length) \ |
| *(p)->holder++ = (c); \ |
| (p)->counter++; \ |
| } \ |
| while (0) |
| |
| /* Output a string. P->WIDTH has already been adjusted for padding. */ |
| #define PUT_STRING(string, len, p) \ |
| do \ |
| { \ |
| PAD_RIGHT (p); \ |
| while ((len)-- > 0) \ |
| { \ |
| PUT_CHAR (*(string), (p)); \ |
| (string)++; \ |
| } \ |
| PAD_LEFT (p); \ |
| } \ |
| while (0) |
| |
| #define PUT_PLUS(d, p, zero) \ |
| if ((d) > zero && (p)->justify == RIGHT) \ |
| PUT_CHAR('+', p) |
| |
| #define PUT_SPACE(d, p, zero) \ |
| if (((p)->flags & PF_SPACE) && (d) > zero) \ |
| PUT_CHAR(' ', p) |
| |
| /* pad right */ |
| #define PAD_RIGHT(p) \ |
| if ((p)->width > 0 && (p)->justify != LEFT) \ |
| for (; (p)->width > 0; (p)->width--) \ |
| PUT_CHAR((p)->pad, p) |
| |
| /* pad left */ |
| #define PAD_LEFT(p) \ |
| if ((p)->width > 0 && (p)->justify == LEFT) \ |
| for (; (p)->width > 0; (p)->width--) \ |
| PUT_CHAR((p)->pad, p) |
| |
| /* pad with zeros from decimal precision */ |
| #define PAD_ZERO(p) \ |
| if ((p)->precision > 0) \ |
| for (; (p)->precision > 0; (p)->precision--) \ |
| PUT_CHAR('0', p) |
| |
| /* if width and prec. in the args */ |
| #define STAR_ARGS(p) \ |
| do { \ |
| if ((p)->flags & PF_STAR_W) \ |
| { \ |
| (p)->width = GETARG (int); \ |
| if ((p)->width < 0) \ |
| { \ |
| (p)->flags |= PF_LADJUST; \ |
| (p)->justify = LEFT; \ |
| (p)->width = -(p)->width; \ |
| } \ |
| } \ |
| if ((p)->flags & PF_STAR_P) \ |
| { \ |
| (p)->precision = GETARG (int); \ |
| if ((p)->precision < 0) \ |
| { \ |
| (p)->flags &= ~PF_STAR_P; \ |
| (p)->precision = NOT_FOUND; \ |
| } \ |
| } \ |
| } while (0) |
| |
| #if defined (HAVE_LOCALE_H) && defined (HAVE_LOCALECONV) |
| # define GETLOCALEDATA(d, t, g) \ |
| do \ |
| { \ |
| struct lconv *lv; \ |
| if ((d) == 0) { \ |
| (d) = '.'; (t) = -1; (g) = 0; /* defaults */ \ |
| lv = localeconv(); \ |
| if (lv) \ |
| { \ |
| if (lv->decimal_point && lv->decimal_point[0]) \ |
| (d) = lv->decimal_point[0]; \ |
| if (lv->thousands_sep && lv->thousands_sep[0]) \ |
| (t) = lv->thousands_sep[0]; \ |
| (g) = lv->grouping ? lv->grouping : ""; \ |
| if (*(g) == '\0' || *(g) == CHAR_MAX || (t) == -1) (g) = 0; \ |
| } \ |
| } \ |
| } \ |
| while (0); |
| #else |
| # define GETLOCALEDATA(d, t, g) \ |
| ( (d) = '.', (t) = ',', g = "\003" ) |
| #endif |
| |
| #ifdef FLOATING_POINT |
| /* |
| * Find the nth power of 10 |
| */ |
| static double |
| pow_10(n) |
| int n; |
| { |
| double P; |
| |
| /* handle common cases with fast switch statement. */ |
| switch (n) |
| { |
| case -3: return .001; |
| case -2: return .01; |
| case -1: return .1; |
| case 0: return 1.; |
| case 1: return 10.; |
| case 2: return 100.; |
| case 3: return 1000.; |
| } |
| |
| if (n < 0) |
| { |
| P = .0001; |
| for (n += 4; n < 0; n++) |
| P /= 10.; |
| } |
| else |
| { |
| P = 10000.; |
| for (n -= 4; n > 0; n--) |
| P *= 10.; |
| } |
| |
| return P; |
| } |
| |
| /* |
| * Find the integral part of the log in base 10 |
| * Note: this not a real log10() |
| I just need and approximation(integerpart) of x in: |
| 10^x ~= r |
| * log_10(200) = 2; |
| * log_10(250) = 2; |
| * |
| * NOTE: do not call this with r == 0 -- an infinite loop results. |
| */ |
| static int |
| log_10(r) |
| double r; |
| { |
| int i = 0; |
| double result = 1.; |
| |
| if (r < 0.) |
| r = -r; |
| |
| if (r < 1.) |
| { |
| while (result >= r) |
| { |
| result /= 10.; |
| i++; |
| } |
| return (-i); |
| } |
| else |
| { |
| while (result <= r) |
| { |
| result *= 10.; |
| i++; |
| } |
| return (i - 1); |
| } |
| } |
| |
| /* |
| * This function return the fraction part of a double |
| * and set in ip the integral part. |
| * In many ways it resemble the modf() found on most Un*x |
| */ |
| static double |
| integral(real, ip) |
| double real; |
| double *ip; |
| { |
| int j; |
| double i, s, p; |
| double real_integral = 0.; |
| |
| /* take care of the obvious */ |
| /* equal to zero ? */ |
| if (real == 0.) |
| { |
| *ip = 0.; |
| return (0.); |
| } |
| |
| /* negative number ? */ |
| if (real < 0.) |
| real = -real; |
| |
| /* a fraction ? */ |
| if ( real < 1.) |
| { |
| *ip = 0.; |
| return real; |
| } |
| |
| /* the real work :-) */ |
| for (j = log_10(real); j >= 0; j--) |
| { |
| p = pow_10(j); |
| s = (real - real_integral)/p; |
| i = 0.; |
| while (i + 1. <= s) |
| i++; |
| real_integral += i*p; |
| } |
| *ip = real_integral; |
| return (real - real_integral); |
| } |
| |
| #define PRECISION 1.e-6 |
| /* |
| * 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 |
| * declare with fix size |
| */ |
| static char * |
| numtoa(number, base, precision, fract) |
| double number; |
| int base, precision; |
| char **fract; |
| { |
| register int i, j; |
| double ip, fp; /* integer and fraction part */ |
| double fraction; |
| int digits = MAX_INT - 1; |
| static char integral_part[MAX_INT]; |
| static char fraction_part[MAX_FRACT]; |
| double sign; |
| int ch; |
| |
| /* taking care of the obvious case: 0.0 */ |
| if (number == 0.) |
| { |
| integral_part[0] = '0'; |
| integral_part[1] = '\0'; |
| /* The fractional part has to take the precision into account */ |
| for (ch = 0; ch < precision-1; ch++) |
| fraction_part[ch] = '0'; |
| fraction_part[ch] = '0'; |
| fraction_part[ch+1] = '\0'; |
| if (fract) |
| *fract = fraction_part; |
| return integral_part; |
| } |
| |
| /* for negative numbers */ |
| if ((sign = number) < 0.) |
| { |
| number = -number; |
| digits--; /* sign consume one digit */ |
| } |
| |
| fraction = integral(number, &ip); |
| number = ip; |
| |
| /* do the integral part */ |
| if (ip == 0.) |
| { |
| integral_part[0] = '0'; |
| i = 1; |
| } |
| else |
| { |
| for ( i = 0; i < digits && number != 0.; ++i) |
| { |
| number /= base; |
| fp = integral(number, &ip); |
| ch = (int)((fp + PRECISION)*base); /* force to round */ |
| integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10; |
| if (! ISXDIGIT((unsigned char)integral_part[i])) |
| break; /* bail out overflow !! */ |
| number = ip; |
| } |
| } |
| |
| /* Oh No !! out of bound, ho well fill it up ! */ |
| if (number != 0.) |
| for (i = 0; i < digits; ++i) |
| integral_part[i] = '9'; |
| |
| /* put the sign ? */ |
| if (sign < 0.) |
| integral_part[i++] = '-'; |
| |
| integral_part[i] = '\0'; |
| |
| /* reverse every thing */ |
| for ( i--, j = 0; j < i; j++, i--) |
| SWAP_INT(integral_part[i], integral_part[j]); |
| |
| /* the fractional part */ |
| for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision--) |
| { |
| fraction_part[i] = (int)((fp + PRECISION)*10. + '0'); |
| if (! DIGIT(fraction_part[i])) /* underflow ? */ |
| break; |
| fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.); |
| } |
| fraction_part[i] = '\0'; |
| |
| if (fract != (char **)0) |
| *fract = fraction_part; |
| |
| return integral_part; |
| } |
| #endif |
| |
| /* for %d and friends, it puts in holder |
| * the representation with the right padding |
| */ |
| static void |
| number(p, d, base) |
| struct DATA *p; |
| unsigned long d; |
| int base; |
| { |
| char *tmp, *t; |
| long sd; |
| int flags; |
| |
| /* An explicit precision turns off the zero-padding flag. */ |
| if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT)) |
| p->flags &= ~PF_ZEROPAD; |
| |
| sd = d; /* signed for ' ' padding in base 10 */ |
| flags = 0; |
| flags = (*p->pf == 'x' || *p->pf == 'X' || *p->pf == 'o' || *p->pf == 'u' || *p->pf == 'U') ? FL_UNSIGNED : 0; |
| if (*p->pf == 'X') |
| flags |= FL_HEXUPPER; |
| |
| tmp = fmtulong (d, base, intbuf, sizeof(intbuf), flags); |
| t = 0; |
| if ((p->flags & PF_THOUSANDS)) |
| { |
| GETLOCALEDATA(decpoint, thoussep, grouping); |
| if (grouping && (t = groupnum (tmp))) |
| tmp = t; |
| } |
| |
| p->width -= strlen(tmp); |
| PAD_RIGHT(p); |
| |
| if ((p->flags & PF_DOT) && p->precision > 0) |
| { |
| p->precision -= strlen(tmp); |
| PAD_ZERO(p); |
| } |
| |
| switch (base) |
| { |
| case 10: |
| PUT_PLUS(sd, p, 0); |
| PUT_SPACE(sd, p, 0); |
| break; |
| case 8: |
| if (p->flags & PF_ALTFORM) |
| PUT_CHAR('0', p); |
| break; |
| case 16: |
| if (p->flags & PF_ALTFORM) |
| { |
| PUT_CHAR('0', p); |
| PUT_CHAR(*p->pf, p); |
| } |
| break; |
| } |
| |
| while (*tmp) |
| { |
| PUT_CHAR(*tmp, p); |
| tmp++; |
| } |
| |
| PAD_LEFT(p); |
| FREE (t); |
| } |
| |
| #ifdef HAVE_LONG_LONG |
| /* |
| * identical to number() but works for `long long' |
| */ |
| static void |
| lnumber(p, d, base) |
| struct DATA *p; |
| unsigned long long d; |
| int base; |
| { |
| char *tmp, *t; |
| long long sd; |
| int flags; |
| |
| /* An explicit precision turns off the zero-padding flag. */ |
| if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT)) |
| p->flags &= ~PF_ZEROPAD; |
| |
| 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; |
| if (*p->pf == 'X') |
| flags |= FL_HEXUPPER; |
| |
| tmp = fmtullong (d, base, intbuf, sizeof(intbuf), flags); |
| t = 0; |
| if ((p->flags & PF_THOUSANDS)) |
| { |
| GETLOCALEDATA(decpoint, thoussep, grouping); |
| if (grouping && (t = groupnum (tmp))) |
| tmp = t; |
| } |
| |
| p->width -= strlen(tmp); |
| PAD_RIGHT(p); |
| |
| if ((p->flags & PF_DOT) && p->precision > 0) |
| { |
| p->precision -= strlen(tmp); |
| PAD_ZERO(p); |
| } |
| |
| switch (base) |
| { |
| case 10: |
| PUT_PLUS(sd, p, 0); |
| PUT_SPACE(sd, p, 0); |
| break; |
| case 8: |
| if (p->flags & PF_ALTFORM) |
| PUT_CHAR('0', p); |
| break; |
| case 16: |
| if (p->flags & PF_ALTFORM) |
| { |
| PUT_CHAR('0', p); |
| PUT_CHAR(*p->pf, p); |
| } |
| break; |
| } |
| |
| while (*tmp) |
| { |
| PUT_CHAR(*tmp, p); |
| tmp++; |
| } |
| |
| PAD_LEFT(p); |
| FREE (t); |
| } |
| #endif |
| |
| static void |
| pointer(p, d) |
| struct DATA *p; |
| unsigned long d; |
| { |
| char *tmp; |
| |
| tmp = fmtulong(d, 16, intbuf, sizeof(intbuf), 0); |
| p->width -= strlen(tmp); |
| PAD_RIGHT(p); |
| |
| /* prefix '0x' for pointers */ |
| PUT_CHAR('0', p); |
| PUT_CHAR('x', p); |
| |
| while (*tmp) |
| { |
| PUT_CHAR(*tmp, p); |
| tmp++; |
| } |
| |
| PAD_LEFT(p); |
| } |
| |
| /* %s strings */ |
| static void |
| strings(p, tmp) |
| struct DATA *p; |
| char *tmp; |
| { |
| size_t len; |
| |
| len = strlen(tmp); |
| if (p->precision != NOT_FOUND) /* the smallest number */ |
| len = (len < p->precision ? len : p->precision); |
| p->width -= len; |
| |
| PUT_STRING (tmp, len, p); |
| } |
| |
| #if HANDLE_MULTIBYTE |
| /* %ls wide-character strings */ |
| static void |
| wstrings(p, tmp) |
| struct DATA *p; |
| wchar_t *tmp; |
| { |
| size_t len; |
| mbstate_t mbs; |
| char *os; |
| const wchar_t *ws; |
| |
| memset (&mbs, '\0', sizeof (mbstate_t)); |
| ws = (const wchar_t *)tmp; |
| |
| os = (char *)NULL; |
| if (p->precision != NOT_FOUND) |
| { |
| os = (char *)xmalloc (p->precision + 1); |
| len = wcsrtombs (os, &ws, p->precision, &mbs); |
| } |
| else |
| { |
| 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) |
| { |
| /* invalid multibyte sequence; bail now. */ |
| FREE (os); |
| return; |
| } |
| |
| p->width -= len; |
| PUT_STRING (os, len, p); |
| free (os); |
| } |
| |
| static void |
| wchars (p, wc) |
| struct DATA *p; |
| wint_t wc; |
| { |
| char *lbuf, *l; |
| mbstate_t mbs; |
| size_t len; |
| |
| lbuf = (char *)malloc (MB_CUR_MAX+1); |
| if (lbuf == 0) |
| return; |
| memset (&mbs, '\0', sizeof (mbstate_t)); |
| len = wcrtomb (lbuf, wc, &mbs); |
| if (len == (size_t)-1) |
| /* conversion failed; bail now. */ |
| return; |
| p->width -= len; |
| l = lbuf; |
| PUT_STRING (l, len, p); |
| free (lbuf); |
| } |
| #endif /* HANDLE_MULTIBYTE */ |
| |
| #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. */ |
| static int |
| chkinfnan(p, d, mode) |
| struct DATA *p; |
| double d; |
| int mode; /* == 1 for inf, == 2 for nan */ |
| { |
| int i; |
| char *tmp; |
| char *big, *small; |
| |
| i = (mode == 1) ? isinf(d) : isnan(d); |
| if (i == 0) |
| return 0; |
| big = (mode == 1) ? "INF" : "NAN"; |
| small = (mode == 1) ? "inf" : "nan"; |
| |
| tmp = (*p->pf == 'F' || *p->pf == 'G' || *p->pf == 'E') ? big : small; |
| |
| if (i < 0) |
| PUT_CHAR('-', p); |
| |
| while (*tmp) |
| { |
| PUT_CHAR (*tmp, p); |
| tmp++; |
| } |
| |
| return 1; |
| } |
| |
| /* %f %F %g %G floating point representation */ |
| static void |
| floating(p, d) |
| struct DATA *p; |
| double d; |
| { |
| char *tmp, *tmp2, *t; |
| int i; |
| |
| if (d != 0 && (chkinfnan(p, d, 1) || chkinfnan(p, d, 2))) |
| return; /* already printed nan or inf */ |
| |
| GETLOCALEDATA(decpoint, thoussep, grouping); |
| DEF_PREC(p); |
| d = ROUND(d, p); |
| tmp = dtoa(d, p->precision, &tmp2); |
| t = 0; |
| if ((p->flags & PF_THOUSANDS) && grouping && (t = groupnum (tmp))) |
| tmp = t; |
| |
| if ((*p->pf == 'g' || *p->pf == 'G') && (p->flags & PF_ALTFORM) == 0) |
| { |
| /* smash the trailing zeros unless altform */ |
| for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--) |
| tmp2[i] = '\0'; |
| if (tmp2[0] == '\0') |
| p->precision = 0; |
| } |
| |
| /* calculate the padding. 1 for the dot */ |
| p->width = p->width - |
| ((d > 0. && p->justify == RIGHT) ? 1:0) - |
| ((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.); |
| PUT_SPACE(d, p, 0.); |
| |
| while (*tmp) |
| { |
| PUT_CHAR(*tmp, p); /* the integral */ |
| tmp++; |
| } |
| FREE (t); |
| |
| if (p->precision != 0 || (p->flags & PF_ALTFORM)) |
| PUT_CHAR(decpoint, p); /* put the '.' */ |
| |
| for (; *tmp2; tmp2++) |
| PUT_CHAR(*tmp2, p); /* the fraction */ |
| |
| PAD_LEFT(p); |
| } |
| |
| /* %e %E %g %G exponent representation */ |
| static void |
| exponent(p, d) |
| struct DATA *p; |
| double d; |
| { |
| char *tmp, *tmp2; |
| int j, i; |
| |
| if (d != 0 && (chkinfnan(p, d, 1) || chkinfnan(p, d, 2))) |
| return; /* already printed nan or inf */ |
| |
| GETLOCALEDATA(decpoint, thoussep, grouping); |
| DEF_PREC(p); |
| if (d == 0.) |
| j = 0; |
| else |
| { |
| j = log_10(d); |
| d = d / pow_10(j); /* get the Mantissa */ |
| d = ROUND(d, p); |
| } |
| tmp = dtoa(d, p->precision, &tmp2); |
| |
| /* 1 for unit, 1 for the '.', 1 for 'e|E', |
| * 1 for '+|-', 2 for 'exp' */ |
| /* calculate how much padding need */ |
| p->width = p->width - |
| ((d > 0. && p->justify == RIGHT) ? 1:0) - |
| ((p->flags & PF_SPACE) ? 1:0) - p->precision - 6; |
| |
| PAD_RIGHT(p); |
| PUT_PLUS(d, p, 0.); |
| PUT_SPACE(d, p, 0.); |
| |
| while (*tmp) |
| { |
| PUT_CHAR(*tmp, p); |
| tmp++; |
| } |
| |
| if (p->precision != 0 || (p->flags & PF_ALTFORM)) |
| PUT_CHAR(decpoint, p); /* the '.' */ |
| |
| if ((*p->pf == 'g' || *p->pf == 'G') && (p->flags & PF_ALTFORM) == 0) |
| /* smash the trailing zeros unless altform */ |
| for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--) |
| tmp2[i] = '\0'; |
| |
| for (; *tmp2; tmp2++) |
| PUT_CHAR(*tmp2, p); /* the fraction */ |
| |
| /* the exponent put the 'e|E' */ |
| if (*p->pf == 'g' || *p->pf == 'e') |
| PUT_CHAR('e', p); |
| else |
| PUT_CHAR('E', p); |
| |
| /* the sign of the exp */ |
| if (j >= 0) |
| PUT_CHAR('+', p); |
| else |
| { |
| PUT_CHAR('-', p); |
| j = -j; |
| } |
| |
| tmp = itoa(j); |
| /* pad out to at least two spaces. pad with `0' if the exponent is a |
| single digit. */ |
| if (j <= 9) |
| PUT_CHAR('0', p); |
| |
| /* the exponent */ |
| while (*tmp) |
| { |
| PUT_CHAR(*tmp, p); |
| tmp++; |
| } |
| |
| PAD_LEFT(p); |
| } |
| #endif |
| |
| /* Return a new string with the digits in S grouped according to the locale's |
| grouping info and thousands separator. If no grouping should be performed, |
| this returns NULL; the caller needs to check for it. */ |
| static char * |
| groupnum (s) |
| char *s; |
| { |
| char *se, *ret, *re, *g; |
| int len, slen; |
| |
| if (grouping == 0 || *grouping <= 0 || *grouping == CHAR_MAX) |
| return ((char *)NULL); |
| |
| /* find min grouping to size returned string */ |
| for (len = *grouping, g = grouping; *g; g++) |
| if (*g > 0 && *g < len) |
| len = *g; |
| |
| slen = strlen (s); |
| len = slen / len + 1; |
| ret = (char *)xmalloc (slen + len + 1); |
| re = ret + slen + len; |
| *re = '\0'; |
| |
| g = grouping; |
| se = s + slen; |
| len = *g; |
| |
| while (se > s) |
| { |
| *--re = *--se; |
| |
| /* handle `-' inserted by numtoa() and the fmtu* family here. */ |
| if (se > s && se[-1] == '-') |
| continue; |
| |
| /* begin new group. */ |
| if (--len == 0 && se > s) |
| { |
| *--re = thoussep; |
| len = *++g; /* was g++, but that uses first char twice (glibc bug, too) */ |
| if (*g == '\0') |
| len = *--g; /* use previous grouping */ |
| else if (*g == CHAR_MAX) |
| { |
| do |
| *--re = *--se; |
| while (se > s); |
| break; |
| } |
| } |
| } |
| |
| if (re > ret) |
| #ifdef HAVE_MEMMOVE |
| memmove (ret, re, strlen (re) + 1); |
| #else |
| strcpy (ret, re); |
| #endif |
| |
| return ret; |
| } |
| |
| /* initialize the conversion specifiers */ |
| static void |
| init_conv_flag (p) |
| struct DATA *p; |
| { |
| p->flags &= PF_ALLOCBUF; /* preserve PF_ALLOCBUF flag */ |
| p->precision = p->width = NOT_FOUND; |
| p->justify = NOT_FOUND; |
| p->pad = ' '; |
| } |
| |
| static void |
| init_data (p, string, length, format, mode) |
| struct DATA *p; |
| char *string; |
| size_t length; |
| const char *format; |
| int mode; |
| { |
| p->length = length - 1; /* leave room for '\0' */ |
| p->holder = p->base = string; |
| p->pf = format; |
| p->counter = 0; |
| p->flags = (mode == PFM_AS) ? PF_ALLOCBUF : 0; |
| } |
| |
| static int |
| #if defined (__STDC__) |
| vsnprintf_internal(struct DATA *data, char *string, size_t length, const char *format, va_list args) |
| #else |
| vsnprintf_internal(data, string, length, format, args) |
| struct DATA *data; |
| char *string; |
| size_t length; |
| const char *format; |
| va_list args; |
| #endif |
| { |
| double d; /* temporary holder */ |
| #ifdef HAVE_LONG_DOUBLE |
| long double ld; /* for later */ |
| #endif |
| unsigned long ul; |
| #ifdef HAVE_LONG_LONG |
| unsigned long long ull; |
| #endif |
| int state, i, c, n; |
| char *s; |
| #if HANDLE_MULTIBYTE |
| wchar_t *ws; |
| wint_t wc; |
| #endif |
| const char *convstart; |
| int negprec; |
| |
| /* Sanity check, the string length must be >= 0. C99 actually says that |
| LENGTH can be zero here, in the case of snprintf/vsnprintf (it's never |
| 0 in the case of asprintf/vasprintf), and the return value is the number |
| of characters that would have been written. */ |
| if (length < 0) |
| return -1; |
| |
| if (format == 0) |
| return 0; |
| |
| /* Reset these for each call because the locale might have changed. */ |
| decpoint = thoussep = 0; |
| grouping = 0; |
| |
| negprec = 0; |
| for (; c = *(data->pf); data->pf++) |
| { |
| if (c != '%') |
| { |
| PUT_CHAR (c, data); |
| continue; |
| } |
| |
| convstart = data->pf; |
| init_conv_flag (data); /* initialise format flags */ |
| |
| state = 1; |
| for (state = 1; state && *data->pf; ) |
| { |
| c = *(++data->pf); |
| /* fmtend = data->pf */ |
| #if defined (FLOATING_POINT) && defined (HAVE_LONG_DOUBLE) |
| if (data->flags & PF_LONGDBL) |
| { |
| switch (c) |
| { |
| case 'f': case 'F': |
| case 'e': case 'E': |
| case 'g': case 'G': |
| # ifdef HAVE_PRINTF_A_FORMAT |
| case 'a': case 'A': |
| # endif |
| STAR_ARGS (data); |
| ld = GETLDOUBLE (data); |
| ldfallback (data, convstart, data->pf, ld); |
| goto conv_break; |
| } |
| } |
| #endif /* FLOATING_POINT && HAVE_LONG_DOUBLE */ |
| |
| switch (c) |
| { |
| /* Parse format flags */ |
| case '\0': /* a NULL here ? ? bail out */ |
| *data->holder = '\0'; |
| return data->counter; |
| break; |
| 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; |
| else |
| data->flags |= PF_STAR_W; |
| continue; |
| case '-': |
| if ((data->flags & PF_DOT) == 0) |
| { |
| data->flags |= PF_LADJUST; |
| data->justify = LEFT; |
| } |
| else |
| negprec = 1; |
| continue; |
| case ' ': |
| if ((data->flags & PF_PLUS) == 0) |
| data->flags |= PF_SPACE; |
| continue; |
| case '+': |
| if ((data->flags & PF_DOT) == 0) |
| { |
| data->flags |= PF_PLUS; |
| data->justify = RIGHT; |
| } |
| continue; |
| case '\'': |
| data->flags |= PF_THOUSANDS; |
| continue; |
| |
| case '1': case '2': case '3': |
| case '4': case '5': case '6': |
| case '7': case '8': case '9': |
| n = 0; |
| do |
| { |
| n = n * 10 + TODIGIT(c); |
| c = *(++data->pf); |
| } |
| while (DIGIT(c)); |
| data->pf--; /* went too far */ |
| if (n < 0) |
| n = 0; |
| if (data->flags & PF_DOT) |
| data->precision = negprec ? NOT_FOUND : n; |
| else |
| data->width = n; |
| continue; |
| |
| /* optional precision */ |
| case '.': |
| data->flags |= PF_DOT; |
| data->precision = 0; |
| continue; |
| |
| /* length modifiers */ |
| case 'h': |
| data->flags |= (data->flags & PF_SHORTINT) ? PF_SIGNEDCHAR : PF_SHORTINT; |
| continue; |
| case 'l': |
| data->flags |= (data->flags & PF_LONGINT) ? PF_LONGLONG : PF_LONGINT; |
| continue; |
| case 'L': |
| data->flags |= PF_LONGDBL; |
| continue; |
| case 'q': |
| data->flags |= PF_LONGLONG; |
| continue; |
| case 'j': |
| data->flags |= PF_INTMAX_T; |
| SET_SIZE_FLAGS(data, intmax_t); |
| continue; |
| case 'z': |
| data->flags |= PF_SIZE_T; |
| SET_SIZE_FLAGS(data, size_t); |
| continue; |
| case 't': |
| data->flags |= PF_PTRDIFF_T; |
| SET_SIZE_FLAGS(data, ptrdiff_t); |
| continue; |
| |
| /* Conversion specifiers */ |
| #ifdef FLOATING_POINT |
| case 'f': /* float, double */ |
| case 'F': |
| STAR_ARGS(data); |
| d = GETDOUBLE(data); |
| floating(data, d); |
| conv_break: |
| state = 0; |
| break; |
| case 'g': |
| case 'G': |
| STAR_ARGS(data); |
| DEF_PREC(data); |
| d = GETDOUBLE(data); |
| i = (d != 0.) ? log_10(d) : -1; |
| /* |
| * for '%g|%G' ANSI: use f if exponent |
| * is in the range or [-4,p] exclusively |
| * else use %e|%E |
| */ |
| if (-4 < i && i < data->precision) |
| { |
| /* reset precision */ |
| data->precision -= i + 1; |
| floating(data, d); |
| } |
| else |
| { |
| /* reduce precision by 1 because of leading digit before |
| decimal point in e format. */ |
| data->precision--; |
| exponent(data, d); |
| } |
| state = 0; |
| break; |
| case 'e': |
| case 'E': /* Exponent double */ |
| STAR_ARGS(data); |
| d = GETDOUBLE(data); |
| exponent(data, d); |
| state = 0; |
| break; |
| # ifdef HAVE_PRINTF_A_FORMAT |
| case 'a': |
| case 'A': |
| STAR_ARGS(data); |
| d = GETDOUBLE(data); |
| dfallback(data, convstart, data->pf, d); |
| state = 0; |
| break; |
| # endif /* HAVE_PRINTF_A_FORMAT */ |
| #endif /* FLOATING_POINT */ |
| case 'U': |
| data->flags |= PF_LONGINT; |
| /* FALLTHROUGH */ |
| case 'u': |
| STAR_ARGS(data); |
| #ifdef HAVE_LONG_LONG |
| if (data->flags & PF_LONGLONG) |
| { |
| ull = GETARG (unsigned long long); |
| lnumber(data, ull, 10); |
| } |
| else |
| #endif |
| { |
| ul = GETUNSIGNED(data); |
| number(data, ul, 10); |
| } |
| state = 0; |
| break; |
| case 'D': |
| data->flags |= PF_LONGINT; |
| /* FALLTHROUGH */ |
| case 'd': /* decimal */ |
| case 'i': |
| STAR_ARGS(data); |
| #ifdef HAVE_LONG_LONG |
| if (data->flags & PF_LONGLONG) |
| { |
| ull = GETARG (long long); |
| lnumber(data, ull, 10); |
| } |
| else |
| #endif |
| { |
| ul = GETSIGNED(data); |
| number(data, ul, 10); |
| } |
| state = 0; |
| break; |
| case 'o': /* octal */ |
| STAR_ARGS(data); |
| #ifdef HAVE_LONG_LONG |
| if (data->flags & PF_LONGLONG) |
| { |
| ull = GETARG (unsigned long long); |
| lnumber(data, ull, 8); |
| } |
| else |
| #endif |
| { |
| ul = GETUNSIGNED(data); |
| number(data, ul, 8); |
| } |
| state = 0; |
| break; |
| case 'x': |
| case 'X': /* hexadecimal */ |
| STAR_ARGS(data); |
| #ifdef HAVE_LONG_LONG |
| if (data->flags & PF_LONGLONG) |
| { |
| ull = GETARG (unsigned long long); |
| lnumber(data, ull, 16); |
| } |
| else |
| #endif |
| { |
| ul = GETUNSIGNED(data); |
| number(data, ul, 16); |
| } |
| state = 0; |
| break; |
| case 'p': |
| STAR_ARGS(data); |
| ul = (unsigned long)GETARG (void *); |
| pointer(data, ul); |
| state = 0; |
| break; |
| #if HANDLE_MULTIBYTE |
| case 'C': |
| data->flags |= PF_LONGINT; |
| /* FALLTHROUGH */ |
| #endif |
| case 'c': /* character */ |
| STAR_ARGS(data); |
| #if HANDLE_MULTIBYTE |
| if (data->flags & PF_LONGINT) |
| { |
| wc = GETARG (wint_t); |
| wchars (data, wc); |
| } |
| else |
| #endif |
| { |
| ul = GETARG (int); |
| PUT_CHAR(ul, data); |
| } |
| state = 0; |
| break; |
| #if HANDLE_MULTIBYTE |
| case 'S': |
| data->flags |= PF_LONGINT; |
| /* FALLTHROUGH */ |
| #endif |
| case 's': /* string */ |
| STAR_ARGS(data); |
| #if HANDLE_MULTIBYTE |
| if (data->flags & PF_LONGINT) |
| { |
| ws = GETARG (wchar_t *); |
| wstrings (data, ws); |
| } |
| else |
| #endif |
| { |
| s = GETARG (char *); |
| strings(data, s); |
| } |
| state = 0; |
| break; |
| case 'n': |
| #ifdef HAVE_LONG_LONG |
| if (data->flags & PF_LONGLONG) |
| *(GETARG (long long *)) = data->counter; |
| else |
| #endif |
| if (data->flags & PF_LONGINT) |
| *(GETARG (long *)) = data->counter; |
| else if (data->flags & PF_SHORTINT) |
| *(GETARG (short *)) = data->counter; |
| else |
| *(GETARG (int *)) = data->counter; |
| state = 0; |
| break; |
| case '%': /* nothing just % */ |
| PUT_CHAR('%', data); |
| state = 0; |
| break; |
| default: |
| /* is this an error ? maybe bail out */ |
| state = 0; |
| break; |
| } /* end switch */ |
| } /* end of `%' for loop */ |
| } /* end of format string for loop */ |
| |
| if (data->length >= 0) |
| *data->holder = '\0'; /* the end ye ! */ |
| |
| return data->counter; |
| } |
| |
| #if defined (FLOATING_POINT) && defined (HAVE_LONG_DOUBLE) |
| /* |
| * Printing floating point numbers accurately is an art. I'm not good |
| * at it. Fall back to sprintf for long double formats. |
| */ |
| static void |
| ldfallback (data, fs, fe, ld) |
| struct DATA *data; |
| const char *fs, *fe; |
| long double ld; |
| { |
| register char *x; |
| char fmtbuf[FALLBACK_FMTSIZE], *obuf; |
| int fl; |
| |
| fl = LFALLBACK_BASE + (data->precision < 6 ? 6 : data->precision) + 2; |
| obuf = (char *)xmalloc (fl); |
| fl = fe - fs + 1; |
| strncpy (fmtbuf, fs, fl); |
| fmtbuf[fl] = '\0'; |
| |
| if ((data->flags & PF_STAR_W) && (data->flags & PF_STAR_P)) |
| sprintf (obuf, fmtbuf, data->width, data->precision, ld); |
| else if (data->flags & PF_STAR_W) |
| sprintf (obuf, fmtbuf, data->width, ld); |
| else if (data->flags & PF_STAR_P) |
| sprintf (obuf, fmtbuf, data->precision, ld); |
| else |
| sprintf (obuf, fmtbuf, ld); |
| |
| for (x = obuf; *x; x++) |
| PUT_CHAR (*x, data); |
| xfree (obuf); |
| } |
| #endif /* FLOATING_POINT && HAVE_LONG_DOUBLE */ |
| |
| #ifdef FLOATING_POINT |
| /* Used for %a, %A if the libc printf supports them. */ |
| static void |
| dfallback (data, fs, fe, d) |
| struct DATA *data; |
| const char *fs, *fe; |
| double d; |
| { |
| register char *x; |
| char fmtbuf[FALLBACK_FMTSIZE], obuf[FALLBACK_BASE]; |
| int fl; |
| |
| fl = fe - fs + 1; |
| strncpy (fmtbuf, fs, fl); |
| fmtbuf[fl] = '\0'; |
| |
| if ((data->flags & PF_STAR_W) && (data->flags & PF_STAR_P)) |
| sprintf (obuf, fmtbuf, data->width, data->precision, d); |
| else if (data->flags & PF_STAR_W) |
| sprintf (obuf, fmtbuf, data->width, d); |
| else if (data->flags & PF_STAR_P) |
| sprintf (obuf, fmtbuf, data->precision, d); |
| else |
| sprintf (obuf, fmtbuf, d); |
| |
| for (x = obuf; *x; x++) |
| PUT_CHAR (*x, data); |
| } |
| #endif /* FLOATING_POINT */ |
| |
| #if !HAVE_SNPRINTF |
| |
| int |
| #if defined (__STDC__) |
| vsnprintf(char *string, size_t length, const char *format, va_list args) |
| #else |
| vsnprintf(string, length, format, args) |
| char *string; |
| size_t length; |
| const char *format; |
| va_list args; |
| #endif |
| { |
| struct DATA data; |
| |
| if (string == 0 && length != 0) |
| return 0; |
| init_data (&data, string, length, format, PFM_SN); |
| return (vsnprintf_internal(&data, string, length, format, args)); |
| } |
| |
| int |
| #if defined(PREFER_STDARG) |
| snprintf(char *string, size_t length, const char * format, ...) |
| #else |
| snprintf(string, length, format, va_alist) |
| char *string; |
| size_t length; |
| const char *format; |
| va_dcl |
| #endif |
| { |
| struct DATA data; |
| int rval; |
| va_list args; |
| |
| SH_VA_START(args, format); |
| |
| if (string == 0 && length != 0) |
| return 0; |
| init_data (&data, string, length, format, PFM_SN); |
| rval = vsnprintf_internal (&data, string, length, format, args); |
| |
| va_end(args); |
| |
| return rval; |
| } |
| |
| #endif /* HAVE_SNPRINTF */ |
| |
| #if !HAVE_ASPRINTF |
| |
| int |
| #if defined (__STDC__) |
| vasprintf(char **stringp, const char *format, va_list args) |
| #else |
| vasprintf(stringp, format, args) |
| char **stringp; |
| const char *format; |
| va_list args; |
| #endif |
| { |
| struct DATA data; |
| char *string; |
| int r; |
| |
| string = (char *)xmalloc(ASBUFSIZE); |
| init_data (&data, string, ASBUFSIZE, format, PFM_AS); |
| r = vsnprintf_internal(&data, string, ASBUFSIZE, format, args); |
| *stringp = data.base; /* not string in case reallocated */ |
| return r; |
| } |
| |
| int |
| #if defined(PREFER_STDARG) |
| asprintf(char **stringp, const char * format, ...) |
| #else |
| asprintf(stringp, format, va_alist) |
| char **stringp; |
| const char *format; |
| va_dcl |
| #endif |
| { |
| int rval; |
| va_list args; |
| |
| SH_VA_START(args, format); |
| |
| rval = vasprintf (stringp, format, args); |
| |
| va_end(args); |
| |
| return rval; |
| } |
| |
| #endif /* !HAVE_ASPRINTF */ |
| |
| #endif /* !HAVE_SNPRINTF || !HAVE_ASPRINTF */ |
| |
| #ifdef DRIVER |
| |
| static void |
| memory_error_and_abort () |
| { |
| write (2, "out of virtual memory\n", 22); |
| abort (); |
| } |
| |
| static void * |
| xmalloc(bytes) |
| size_t bytes; |
| { |
| void *ret; |
| |
| ret = malloc(bytes); |
| if (ret == 0) |
| memory_error_and_abort (); |
| return ret; |
| } |
| |
| static void * |
| xrealloc (pointer, bytes) |
| void *pointer; |
| size_t bytes; |
| { |
| void *ret; |
| |
| ret = pointer ? realloc(pointer, bytes) : malloc(bytes); |
| if (ret == 0) |
| memory_error_and_abort (); |
| return ret; |
| } |
| |
| static void |
| xfree(x) |
| void *x; |
| { |
| if (x) |
| free (x); |
| } |
| |
| /* set of small tests for snprintf() */ |
| main() |
| { |
| char holder[100]; |
| char *h; |
| int i, si, ai; |
| |
| #ifdef HAVE_LOCALE_H |
| setlocale(LC_ALL, ""); |
| #endif |
| |
| #if 1 |
| si = snprintf((char *)NULL, 0, "abcde\n"); |
| printf("snprintf returns %d with NULL first argument and size of 0\n", si); |
| si = snprintf(holder, 0, "abcde\n"); |
| printf("snprintf returns %d with non-NULL first argument and size of 0\n", si); |
| si = snprintf((char *)NULL, 16, "abcde\n"); |
| printf("snprintf returns %d with NULL first argument and non-zero size\n", si); |
| |
| /* |
| printf("Suite of test for snprintf:\n"); |
| printf("a_format\n"); |
| printf("printf() format\n"); |
| printf("snprintf() format\n\n"); |
| */ |
| /* Checking the field widths */ |
| |
| printf("/%%ld %%ld/, 336, 336\n"); |
| snprintf(holder, sizeof holder, "/%ld %ld/\n", 336, 336); |
| asprintf(&h, "/%ld %ld/\n", 336, 336); |
| printf("/%ld %ld/\n", 336, 336); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%d/, 336\n"); |
| snprintf(holder, sizeof holder, "/%d/\n", 336); |
| asprintf(&h, "/%d/\n", 336); |
| printf("/%d/\n", 336); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%2d/, 336\n"); |
| snprintf(holder, sizeof holder, "/%2d/\n", 336); |
| asprintf(&h, "/%2d/\n", 336); |
| printf("/%2d/\n", 336); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%10d/, 336\n"); |
| snprintf(holder, sizeof holder, "/%10d/\n", 336); |
| asprintf(&h, "/%10d/\n", 336); |
| printf("/%10d/\n", 336); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%-10d/, 336\n"); |
| snprintf(holder, sizeof holder, "/%-10d/\n", 336); |
| asprintf(&h, "/%-10d/\n", 336); |
| printf("/%-10d/\n", 336); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| |
| /* floating points */ |
| |
| printf("/%%f/, 1234.56\n"); |
| snprintf(holder, sizeof holder, "/%f/\n", 1234.56); |
| asprintf(&h, "/%f/\n", 1234.56); |
| printf("/%f/\n", 1234.56); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%e/, 1234.56\n"); |
| snprintf(holder, sizeof holder, "/%e/\n", 1234.56); |
| asprintf(&h, "/%e/\n", 1234.56); |
| printf("/%e/\n", 1234.56); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%4.2f/, 1234.56\n"); |
| snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56); |
| asprintf(&h, "/%4.2f/\n", 1234.56); |
| printf("/%4.2f/\n", 1234.56); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%3.1f/, 1234.56\n"); |
| snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56); |
| asprintf(&h, "/%3.1f/\n", 1234.56); |
| printf("/%3.1f/\n", 1234.56); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%10.3f/, 1234.56\n"); |
| snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56); |
| asprintf(&h, "/%10.3f/\n", 1234.56); |
| printf("/%10.3f/\n", 1234.56); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%10.3e/, 1234.56\n"); |
| snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56); |
| asprintf(&h, "/%10.3e/\n", 1234.56); |
| printf("/%10.3e/\n", 1234.56); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%+4.2f/, 1234.56\n"); |
| snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56); |
| asprintf(&h, "/%+4.2f/\n", 1234.56); |
| printf("/%+4.2f/\n", 1234.56); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%010.2f/, 1234.56\n"); |
| snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56); |
| asprintf(&h, "/%010.2f/\n", 1234.56); |
| printf("/%010.2f/\n", 1234.56); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| #define BLURB "Outstanding acting !" |
| /* strings precisions */ |
| |
| printf("/%%2s/, \"%s\"\n", BLURB); |
| snprintf(holder, sizeof holder, "/%2s/\n", BLURB); |
| asprintf(&h, "/%2s/\n", BLURB); |
| printf("/%2s/\n", BLURB); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%22s/ %s\n", BLURB); |
| snprintf(holder, sizeof holder, "/%22s/\n", BLURB); |
| asprintf(&h, "/%22s/\n", BLURB); |
| printf("/%22s/\n", BLURB); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%22.5s/ %s\n", BLURB); |
| snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB); |
| asprintf(&h, "/%22.5s/\n", BLURB); |
| printf("/%22.5s/\n", BLURB); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%-22.5s/ %s\n", BLURB); |
| snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB); |
| asprintf(&h, "/%-22.5s/\n", BLURB); |
| printf("/%-22.5s/\n", BLURB); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| /* see some flags */ |
| |
| printf("%%x %%X %%#x, 31, 31, 31\n"); |
| snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31); |
| asprintf(&h, "%x %X %#x\n", 31, 31, 31); |
| printf("%x %X %#x\n", 31, 31, 31); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("**%%d**%% d**%% d**, 42, 42, -42\n"); |
| snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42); |
| asprintf(&h, "**%d**% d**% d**\n", 42, 42, -42); |
| printf("**%d**% d**% d**\n", 42, 42, -42); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| /* other flags */ |
| |
| printf("/%%g/, 31.4\n"); |
| snprintf(holder, sizeof holder, "/%g/\n", 31.4); |
| asprintf(&h, "/%g/\n", 31.4); |
| printf("/%g/\n", 31.4); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%.6g/, 31.4\n"); |
| snprintf(holder, sizeof holder, "/%.6g/\n", 31.4); |
| asprintf(&h, "/%.6g/\n", 31.4); |
| printf("/%.6g/\n", 31.4); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%.1G/, 31.4\n"); |
| snprintf(holder, sizeof holder, "/%.1G/\n", 31.4); |
| asprintf(&h, "/%.1G/\n", 31.4); |
| printf("/%.1G/\n", 31.4); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%.1G/, 3100000000.4\n"); |
| snprintf(holder, sizeof holder, "/%.1G/\n", 3100000000.4); |
| asprintf(&h, "/%.1G/\n", 3100000000.4); |
| printf("/%.1G/\n", 3100000000.4); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("abc%%n\n"); |
| printf("abc%n", &i); printf("%d\n", i); |
| snprintf(holder, sizeof holder, "abc%n", &i); |
| printf("%s", holder); printf("%d\n\n", i); |
| asprintf(&h, "abc%n", &i); |
| printf("%s", h); printf("%d\n\n", i); |
| |
| printf("%%*.*s --> 10.10\n"); |
| snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB); |
| asprintf(&h, "%*.*s\n", 10, 10, BLURB); |
| printf("%*.*s\n", 10, 10, BLURB); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("%%%%%%%%\n"); |
| snprintf(holder, sizeof holder, "%%%%\n"); |
| asprintf(&h, "%%%%\n"); |
| printf("%%%%\n"); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| #define BIG "Hello this is a too big string for the buffer" |
| /* printf("A buffer to small of 10, trying to put this:\n");*/ |
| printf("<%%>, %s\n", BIG); |
| i = snprintf(holder, 10, "%s\n", BIG); |
| i = asprintf(&h, "%s", BIG); |
| printf("<%s>\n", BIG); |
| printf("<%s>\n", holder); |
| printf("<%s>\n\n", h); |
| |
| printf ("<%%p> vsnprintf\n"); |
| i = snprintf(holder, 100, "%p", vsnprintf); |
| i = asprintf(&h, "%p", vsnprintf); |
| printf("<%p>\n", vsnprintf); |
| printf("<%s>\n", holder); |
| printf("<%s>\n\n", h); |
| |
| printf ("<%%lu> LONG_MAX+1\n"); |
| i = snprintf(holder, 100, "%lu", (unsigned long)(LONG_MAX)+1); |
| i = asprintf(&h, "%lu", (unsigned long)(LONG_MAX)+1); |
| printf("<%lu>\n", (unsigned long)(LONG_MAX)+1); |
| printf("<%s>\n", holder); |
| printf("<%s>\n\n", h); |
| |
| #ifdef HAVE_LONG_LONG |
| printf ("<%%llu> LLONG_MAX+1\n"); |
| i = snprintf(holder, 100, "%llu", (unsigned long long)(LLONG_MAX)+1); |
| i = asprintf(&h, "%llu", (unsigned long long)(LLONG_MAX)+1); |
| printf("<%llu>\n", (unsigned long long)(LLONG_MAX)+1); |
| printf("<%s>\n", holder); |
| printf("<%s>\n\n", h); |
| #endif |
| |
| #ifdef HAVE_LONG_DOUBLE |
| printf ("<%%6.2LE> 42.42\n"); |
| i = snprintf(holder, 100, "%6.2LE", (long double)42.42); |
| i = asprintf(&h, "%6.2LE", (long double)42.42); |
| printf ("<%6.2LE>\n", (long double)42.42); |
| printf ("<%s>\n", holder); |
| printf ("<%s>\n\n", h); |
| #endif |
| |
| #ifdef HAVE_PRINTF_A_FORMAT |
| printf ("<%%6.2A> 42.42\n"); |
| i = snprintf(holder, 100, "%6.2A", 42.42); |
| i = asprintf(&h, "%6.2A", 42.42); |
| printf ("<%6.2A>\n", 42.42); |
| printf ("<%s>\n", holder); |
| printf ("<%s>\n\n", h); |
| |
| printf ("<%%6.2LA> 42.42\n"); |
| i = snprintf(holder, 100, "%6.2LA", (long double)42.42); |
| i = asprintf(&h, "%6.2LA", (long double)42.42); |
| printf ("<%6.2LA>\n", (long double)42.42); |
| printf ("<%s>\n", holder); |
| printf ("<%s>\n\n", h); |
| #endif |
| |
| printf ("<%%.10240f> DBL_MAX\n"); |
| si = snprintf(holder, 100, "%.10240f", DBL_MAX); |
| ai = asprintf(&h, "%.10240f", DBL_MAX); |
| printf ("<%.10240f>\n", DBL_MAX); |
| printf ("<%d> <%s>\n", si, holder); |
| printf ("<%d> <%s>\n\n", ai, h); |
| |
| printf ("<%%.10240Lf> LDBL_MAX\n"); |
| si = snprintf(holder, 100, "%.10240Lf", (long double)LDBL_MAX); |
| ai = asprintf(&h, "%.10240Lf", (long double)LDBL_MAX); |
| printf ("<%.10240Lf>\n", (long double)LDBL_MAX); |
| printf ("<%d> <%s>\n", si, holder); |
| printf ("<%d> <%s>\n\n", ai, h); |
| |
| /* huh? */ |
| printf("/%%g/, 421.2345\n"); |
| snprintf(holder, sizeof holder, "/%g/\n", 421.2345); |
| asprintf(&h, "/%g/\n", 421.2345); |
| printf("/%g/\n", 421.2345); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%g/, 4214.2345\n"); |
| snprintf(holder, sizeof holder, "/%g/\n", 4214.2345); |
| asprintf(&h, "/%g/\n", 4214.2345); |
| printf("/%g/\n", 4214.2345); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%.5g/, 4214.2345\n"); |
| snprintf(holder, sizeof holder, "/%.5g/\n", 4214.2345); |
| asprintf(&h, "/%.5g/\n", 4214.2345); |
| printf("/%.5g/\n", 4214.2345); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%.4g/, 4214.2345\n"); |
| snprintf(holder, sizeof holder, "/%.4g/\n", 4214.2345); |
| asprintf(&h, "/%.4g/\n", 4214.2345); |
| printf("/%.4g/\n", 4214.2345); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%'ld %%'ld/, 12345, 1234567\n"); |
| snprintf(holder, sizeof holder, "/%'ld %'ld/\n", 12345, 1234567); |
| asprintf(&h, "/%'ld %'ld/\n", 12345, 1234567); |
| printf("/%'ld %'ld/\n", 12345, 1234567); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%'ld %%'ld/, 336, 3336\n"); |
| snprintf(holder, sizeof holder, "/%'ld %'ld/\n", 336, 3336); |
| asprintf(&h, "/%'ld %'ld/\n", 336, 3336); |
| printf("/%'ld %'ld/\n", 336, 3336); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%'ld %%'ld/, -42786, -142786\n"); |
| snprintf(holder, sizeof holder, "/%'ld %'ld/\n", -42786, -142786); |
| asprintf(&h, "/%'ld %'ld/\n", -42786, -142786); |
| printf("/%'ld %'ld/\n", -42786, -142786); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%'f %%'f/, 421.2345, 421234.56789\n"); |
| snprintf(holder, sizeof holder, "/%'f %'f/\n", 421.2345, 421234.56789); |
| asprintf(&h, "/%'f %'f/\n", 421.2345, 421234.56789); |
| printf("/%'f %'f/\n", 421.2345, 421234.56789); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%'f %%'f/, -421.2345, -421234.56789\n"); |
| snprintf(holder, sizeof holder, "/%'f %'f/\n", -421.2345, -421234.56789); |
| asprintf(&h, "/%'f %'f/\n", -421.2345, -421234.56789); |
| printf("/%'f %'f/\n", -421.2345, -421234.56789); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%'g %%'g/, 421.2345, 421234.56789\n"); |
| snprintf(holder, sizeof holder, "/%'g %'g/\n", 421.2345, 421234.56789); |
| asprintf(&h, "/%'g %'g/\n", 421.2345, 421234.56789); |
| printf("/%'g %'g/\n", 421.2345, 421234.56789); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| printf("/%%'g %%'g/, -421.2345, -421234.56789\n"); |
| snprintf(holder, sizeof holder, "/%'g %'g/\n", -421.2345, -421234.56789); |
| asprintf(&h, "/%'g %'g/\n", -421.2345, -421234.56789); |
| printf("/%'g %'g/\n", -421.2345, -421234.56789); |
| printf("%s", holder); |
| printf("%s\n", h); |
| #endif |
| |
| printf("/%%'g/, 4213455.8392\n"); |
| snprintf(holder, sizeof holder, "/%'g/\n", 4213455.8392); |
| asprintf(&h, "/%'g/\n", 4213455.8392); |
| printf("/%'g/\n", 4213455.8392); |
| printf("%s", holder); |
| printf("%s\n", h); |
| |
| exit (0); |
| } |
| #endif |