0,0 → 1,1489 |
/* |
* Copyright (c) 1995 Patrick Powell. |
* |
* This code is based on code written by Patrick Powell <papowell@astart.com>. |
* It may be used for any purpose as long as this notice remains intact on all |
* source code distributions. |
*/ |
|
/* |
* Copyright (c) 2008 Holger Weiss. |
* |
* This version of the code is maintained by Holger Weiss <holger@jhweiss.de>. |
* My changes to the code may freely be used, modified and/or redistributed for |
* any purpose. It would be nice if additions and fixes to this file (including |
* trivial code cleanups) would be sent back in order to let me include them in |
* the version available at <http://www.jhweiss.de/software/snprintf.html>. |
* However, this is not a requirement for using or redistributing (possibly |
* modified) versions of this file, nor is leaving this notice intact mandatory. |
*/ |
|
/* |
* History |
* |
* 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1: |
* |
* Fixed the detection of infinite floating point values on IRIX (and |
* possibly other systems) and applied another few minor cleanups. |
* |
* 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0: |
* |
* Added a lot of new features, fixed many bugs, and incorporated various |
* improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery |
* <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller |
* <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH |
* projects. The additions include: support the "e", "E", "g", "G", and |
* "F" conversion specifiers (and use conversion style "f" or "F" for the |
* still unsupported "a" and "A" specifiers); support the "hh", "ll", "j", |
* "t", and "z" length modifiers; support the "#" flag and the (non-C99) |
* "'" flag; use localeconv(3) (if available) to get both the current |
* locale's decimal point character and the separator between groups of |
* digits; fix the handling of various corner cases of field width and |
* precision specifications; fix various floating point conversion bugs; |
* handle infinite and NaN floating point values; don't attempt to write to |
* the output buffer (which may be NULL) if a size of zero was specified; |
* check for integer overflow of the field width, precision, and return |
* values and during the floating point conversion; use the OUTCHAR() macro |
* instead of a function for better performance; provide asprintf(3) and |
* vasprintf(3) functions; add new test cases. The replacement functions |
* have been renamed to use an "rpl_" prefix, the function calls in the |
* main project (and in this file) must be redefined accordingly for each |
* replacement function which is needed (by using Autoconf or other means). |
* Various other minor improvements have been applied and the coding style |
* was cleaned up for consistency. |
* |
* 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13: |
* |
* C99 compliant snprintf(3) and vsnprintf(3) functions return the number |
* of characters that would have been written to a sufficiently sized |
* buffer (excluding the '\0'). The original code simply returned the |
* length of the resulting output string, so that's been fixed. |
* |
* 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8: |
* |
* The original code assumed that both snprintf(3) and vsnprintf(3) were |
* missing. Some systems only have snprintf(3) but not vsnprintf(3), so |
* the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. |
* |
* 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i: |
* |
* The PGP code was using unsigned hexadecimal formats. Unfortunately, |
* unsigned formats simply didn't work. |
* |
* 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1: |
* |
* Ok, added some minimal floating point support, which means this probably |
* requires libm on most operating systems. Don't yet support the exponent |
* (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just |
* wasn't being exercised in ways which showed it, so that's been fixed. |
* Also, formatted the code to Mutt conventions, and removed dead code left |
* over from the original. Also, there is now a builtin-test, run with: |
* gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf |
* |
* 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43: |
* |
* This was ugly. It is still ugly. I opted out of floating point |
* numbers, but the formatter understands just about everything from the |
* normal C string format, at least as far as I can tell from the Solaris |
* 2.5 printf(3S) man page. |
*/ |
|
/* |
* ToDo |
* |
* - Add wide character support. |
* - Add support for "%a" and "%A" conversions. |
* - Create test routines which predefine the expected results. Our test cases |
* usually expose bugs in system implementations rather than in ours :-) |
*/ |
|
/* |
* Usage |
* |
* 1) The following preprocessor macros should be defined to 1 if the feature or |
* file in question is available on the target system (by using Autoconf or |
* other means), though basic functionality should be available as long as |
* HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly: |
* |
* HAVE_VSNPRINTF |
* HAVE_SNPRINTF |
* HAVE_VASPRINTF |
* HAVE_ASPRINTF |
* HAVE_STDARG_H |
* HAVE_STDDEF_H |
* HAVE_STDINT_H |
* HAVE_STDLIB_H |
* HAVE_INTTYPES_H |
* HAVE_LOCALE_H |
* HAVE_LOCALECONV |
* HAVE_LCONV_DECIMAL_POINT |
* HAVE_LCONV_THOUSANDS_SEP |
* HAVE_LONG_DOUBLE |
* HAVE_LONG_LONG_INT |
* HAVE_UNSIGNED_LONG_LONG_INT |
* HAVE_INTMAX_T |
* HAVE_UINTMAX_T |
* HAVE_UINTPTR_T |
* HAVE_PTRDIFF_T |
* HAVE_VA_COPY |
* HAVE___VA_COPY |
* |
* 2) The calls to the functions which should be replaced must be redefined |
* throughout the project files (by using Autoconf or other means): |
* |
* #define vsnprintf rpl_vsnprintf |
* #define snprintf rpl_snprintf |
* #define vasprintf rpl_vasprintf |
* #define asprintf rpl_asprintf |
* |
* 3) The required replacement functions should be declared in some header file |
* included throughout the project files: |
* |
* #if HAVE_CONFIG_H |
* #include <config.h> |
* #endif |
* #if HAVE_STDARG_H |
* #include <stdarg.h> |
* #if !HAVE_VSNPRINTF |
* int rpl_vsnprintf(char *, size_t, const char *, va_list); |
* #endif |
* #if !HAVE_SNPRINTF |
* int rpl_snprintf(char *, size_t, const char *, ...); |
* #endif |
* #if !HAVE_VASPRINTF |
* int rpl_vasprintf(char **, const char *, va_list); |
* #endif |
* #if !HAVE_ASPRINTF |
* int rpl_asprintf(char **, const char *, ...); |
* #endif |
* #endif |
* |
* Autoconf macros for handling step 1 and step 2 are available at |
* <http://www.jhweiss.de/software/snprintf.html>. |
*/ |
|
#include "pipe/p_config.h" |
|
#if HAVE_CONFIG_H |
#include <config.h> |
#else |
#ifdef _MSC_VER |
#define vsnprintf util_vsnprintf |
#define snprintf util_snprintf |
#define HAVE_VSNPRINTF 0 |
#define HAVE_SNPRINTF 0 |
#define HAVE_VASPRINTF 1 /* not needed */ |
#define HAVE_ASPRINTF 1 /* not needed */ |
#define HAVE_STDARG_H 1 |
#define HAVE_STDDEF_H 1 |
#define HAVE_STDINT_H 0 |
#define HAVE_STDLIB_H 1 |
#define HAVE_INTTYPES_H 0 |
#define HAVE_LOCALE_H 0 |
#define HAVE_LOCALECONV 0 |
#define HAVE_LCONV_DECIMAL_POINT 0 |
#define HAVE_LCONV_THOUSANDS_SEP 0 |
#define HAVE_LONG_DOUBLE 0 |
#define HAVE_LONG_LONG_INT 1 |
#define HAVE_UNSIGNED_LONG_LONG_INT 1 |
#define HAVE_INTMAX_T 0 |
#define HAVE_UINTMAX_T 0 |
#define HAVE_UINTPTR_T 1 |
#define HAVE_PTRDIFF_T 1 |
#define HAVE_VA_COPY 0 |
#define HAVE___VA_COPY 0 |
#else |
#define HAVE_VSNPRINTF 1 |
#define HAVE_SNPRINTF 1 |
#define HAVE_VASPRINTF 1 |
#define HAVE_ASPRINTF 1 |
#endif |
#endif /* HAVE_CONFIG_H */ |
|
#if !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || !HAVE_VASPRINTF |
#include <stdio.h> /* For NULL, size_t, vsnprintf(3), and vasprintf(3). */ |
#ifdef VA_START |
#undef VA_START |
#endif /* defined(VA_START) */ |
#ifdef VA_SHIFT |
#undef VA_SHIFT |
#endif /* defined(VA_SHIFT) */ |
#if HAVE_STDARG_H |
#include <stdarg.h> |
#define VA_START(ap, last) va_start(ap, last) |
#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */ |
#else /* Assume <varargs.h> is available. */ |
#include <varargs.h> |
#define VA_START(ap, last) va_start(ap) /* "last" is ignored. */ |
#define VA_SHIFT(ap, value, type) value = va_arg(ap, type) |
#endif /* HAVE_STDARG_H */ |
|
#if !HAVE_VASPRINTF |
#if HAVE_STDLIB_H |
#include <stdlib.h> /* For malloc(3). */ |
#endif /* HAVE_STDLIB_H */ |
#ifdef VA_COPY |
#undef VA_COPY |
#endif /* defined(VA_COPY) */ |
#ifdef VA_END_COPY |
#undef VA_END_COPY |
#endif /* defined(VA_END_COPY) */ |
#if HAVE_VA_COPY |
#define VA_COPY(dest, src) va_copy(dest, src) |
#define VA_END_COPY(ap) va_end(ap) |
#elif HAVE___VA_COPY |
#define VA_COPY(dest, src) __va_copy(dest, src) |
#define VA_END_COPY(ap) va_end(ap) |
#else |
#define VA_COPY(dest, src) (void)mymemcpy(&dest, &src, sizeof(va_list)) |
#define VA_END_COPY(ap) /* No-op. */ |
#define NEED_MYMEMCPY 1 |
static void *mymemcpy(void *, void *, size_t); |
#endif /* HAVE_VA_COPY */ |
#endif /* !HAVE_VASPRINTF */ |
|
#if !HAVE_VSNPRINTF |
#include <limits.h> /* For *_MAX. */ |
#if HAVE_INTTYPES_H |
#include <inttypes.h> /* For intmax_t (if not defined in <stdint.h>). */ |
#endif /* HAVE_INTTYPES_H */ |
#if HAVE_LOCALE_H |
#include <locale.h> /* For localeconv(3). */ |
#endif /* HAVE_LOCALE_H */ |
#if HAVE_STDDEF_H |
#include <stddef.h> /* For ptrdiff_t. */ |
#endif /* HAVE_STDDEF_H */ |
#if HAVE_STDINT_H |
#include <stdint.h> /* For intmax_t. */ |
#endif /* HAVE_STDINT_H */ |
|
/* Support for unsigned long long int. We may also need ULLONG_MAX. */ |
#ifndef ULONG_MAX /* We may need ULONG_MAX as a fallback. */ |
#ifdef UINT_MAX |
#define ULONG_MAX UINT_MAX |
#else |
#define ULONG_MAX INT_MAX |
#endif /* defined(UINT_MAX) */ |
#endif /* !defined(ULONG_MAX) */ |
#ifdef ULLONG |
#undef ULLONG |
#endif /* defined(ULLONG) */ |
#if HAVE_UNSIGNED_LONG_LONG_INT |
#define ULLONG unsigned long long int |
#ifndef ULLONG_MAX |
#define ULLONG_MAX ULONG_MAX |
#endif /* !defined(ULLONG_MAX) */ |
#else |
#define ULLONG unsigned long int |
#ifdef ULLONG_MAX |
#undef ULLONG_MAX |
#endif /* defined(ULLONG_MAX) */ |
#define ULLONG_MAX ULONG_MAX |
#endif /* HAVE_LONG_LONG_INT */ |
|
/* Support for uintmax_t. We also need UINTMAX_MAX. */ |
#ifdef UINTMAX_T |
#undef UINTMAX_T |
#endif /* defined(UINTMAX_T) */ |
#if HAVE_UINTMAX_T || defined(uintmax_t) |
#define UINTMAX_T uintmax_t |
#ifndef UINTMAX_MAX |
#define UINTMAX_MAX ULLONG_MAX |
#endif /* !defined(UINTMAX_MAX) */ |
#else |
#define UINTMAX_T ULLONG |
#ifdef UINTMAX_MAX |
#undef UINTMAX_MAX |
#endif /* defined(UINTMAX_MAX) */ |
#define UINTMAX_MAX ULLONG_MAX |
#endif /* HAVE_UINTMAX_T || defined(uintmax_t) */ |
|
/* Support for long double. */ |
#ifndef LDOUBLE |
#if HAVE_LONG_DOUBLE |
#define LDOUBLE long double |
#else |
#define LDOUBLE double |
#endif /* HAVE_LONG_DOUBLE */ |
#endif /* !defined(LDOUBLE) */ |
|
/* Support for long long int. */ |
#ifndef LLONG |
#if HAVE_LONG_LONG_INT |
#define LLONG long long int |
#else |
#define LLONG long int |
#endif /* HAVE_LONG_LONG_INT */ |
#endif /* !defined(LLONG) */ |
|
/* Support for intmax_t. */ |
#ifndef INTMAX_T |
#if HAVE_INTMAX_T || defined(intmax_t) |
#define INTMAX_T intmax_t |
#else |
#define INTMAX_T LLONG |
#endif /* HAVE_INTMAX_T || defined(intmax_t) */ |
#endif /* !defined(INTMAX_T) */ |
|
/* Support for uintptr_t. */ |
#ifndef UINTPTR_T |
#if HAVE_UINTPTR_T || defined(uintptr_t) |
#define UINTPTR_T uintptr_t |
#else |
#define UINTPTR_T unsigned long int |
#endif /* HAVE_UINTPTR_T || defined(uintptr_t) */ |
#endif /* !defined(UINTPTR_T) */ |
|
/* WinCE5.0 does not have uintptr_t defined */ |
#if (_WIN32_WCE < 600) |
#ifdef UINTPTR_T |
#undef UINTPTR_T |
#endif |
#define UINTPTR_T unsigned long int |
#endif |
|
|
/* Support for ptrdiff_t. */ |
#ifndef PTRDIFF_T |
#if HAVE_PTRDIFF_T || defined(ptrdiff_t) |
#define PTRDIFF_T ptrdiff_t |
#else |
#define PTRDIFF_T long int |
#endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */ |
#endif /* !defined(PTRDIFF_T) */ |
|
/* |
* We need an unsigned integer type corresponding to ptrdiff_t (cf. C99: |
* 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an |
* unsigned type if necessary. This should work just fine in practice. |
*/ |
#ifndef UPTRDIFF_T |
#define UPTRDIFF_T PTRDIFF_T |
#endif /* !defined(UPTRDIFF_T) */ |
|
/* |
* We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7). |
* However, we'll simply use size_t and convert it to a signed type if |
* necessary. This should work just fine in practice. |
*/ |
#ifndef SSIZE_T |
#define SSIZE_T size_t |
#endif /* !defined(SSIZE_T) */ |
|
/* Either ERANGE or E2BIG should be available everywhere. */ |
#ifndef ERANGE |
#define ERANGE E2BIG |
#endif /* !defined(ERANGE) */ |
#ifndef EOVERFLOW |
#define EOVERFLOW ERANGE |
#endif /* !defined(EOVERFLOW) */ |
|
/* |
* Buffer size to hold the octal string representation of UINT128_MAX without |
* nul-termination ("3777777777777777777777777777777777777777777"). |
*/ |
#ifdef MAX_CONVERT_LENGTH |
#undef MAX_CONVERT_LENGTH |
#endif /* defined(MAX_CONVERT_LENGTH) */ |
#define MAX_CONVERT_LENGTH 43 |
|
/* Format read states. */ |
#define PRINT_S_DEFAULT 0 |
#define PRINT_S_FLAGS 1 |
#define PRINT_S_WIDTH 2 |
#define PRINT_S_DOT 3 |
#define PRINT_S_PRECISION 4 |
#define PRINT_S_MOD 5 |
#define PRINT_S_CONV 6 |
|
/* Format flags. */ |
#define PRINT_F_MINUS (1 << 0) |
#define PRINT_F_PLUS (1 << 1) |
#define PRINT_F_SPACE (1 << 2) |
#define PRINT_F_NUM (1 << 3) |
#define PRINT_F_ZERO (1 << 4) |
#define PRINT_F_QUOTE (1 << 5) |
#define PRINT_F_UP (1 << 6) |
#define PRINT_F_UNSIGNED (1 << 7) |
#define PRINT_F_TYPE_G (1 << 8) |
#define PRINT_F_TYPE_E (1 << 9) |
|
/* Conversion flags. */ |
#define PRINT_C_CHAR 1 |
#define PRINT_C_SHORT 2 |
#define PRINT_C_LONG 3 |
#define PRINT_C_LLONG 4 |
#define PRINT_C_LDOUBLE 5 |
#define PRINT_C_SIZE 6 |
#define PRINT_C_PTRDIFF 7 |
#define PRINT_C_INTMAX 8 |
|
#ifndef MAX |
#define MAX(x, y) ((x >= y) ? x : y) |
#endif /* !defined(MAX) */ |
#ifndef CHARTOINT |
#define CHARTOINT(ch) (ch - '0') |
#endif /* !defined(CHARTOINT) */ |
#ifndef ISDIGIT |
#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9') |
#endif /* !defined(ISDIGIT) */ |
#ifndef ISNAN |
#define ISNAN(x) (x != x) |
#endif /* !defined(ISNAN) */ |
#ifndef ISINF |
#define ISINF(x) (x != 0.0 && x + x == x) |
#endif /* !defined(ISINF) */ |
|
#ifdef OUTCHAR |
#undef OUTCHAR |
#endif /* defined(OUTCHAR) */ |
#define OUTCHAR(str, len, size, ch) \ |
do { \ |
if (len + 1 < size) \ |
str[len] = ch; \ |
(len)++; \ |
} while (/* CONSTCOND */ 0) |
|
static void fmtstr(char *, size_t *, size_t, const char *, int, int, int); |
static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int); |
static void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *); |
static void printsep(char *, size_t *, size_t); |
static int getnumsep(int); |
static int getexponent(LDOUBLE); |
static int convert(UINTMAX_T, char *, size_t, int, int); |
static UINTMAX_T cast(LDOUBLE); |
static UINTMAX_T myround(LDOUBLE); |
static LDOUBLE mypow10(int); |
|
int |
util_vsnprintf(char *str, size_t size, const char *format, va_list args) |
{ |
LDOUBLE fvalue; |
INTMAX_T value; |
unsigned char cvalue; |
const char *strvalue; |
INTMAX_T *intmaxptr; |
PTRDIFF_T *ptrdiffptr; |
SSIZE_T *sizeptr; |
LLONG *llongptr; |
long int *longptr; |
int *intptr; |
short int *shortptr; |
signed char *charptr; |
size_t len = 0; |
int overflow = 0; |
int base = 0; |
int cflags = 0; |
int flags = 0; |
int width = 0; |
int precision = -1; |
int state = PRINT_S_DEFAULT; |
char ch = *format++; |
|
/* |
* C99 says: "If `n' is zero, nothing is written, and `s' may be a null |
* pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer |
* even if a size larger than zero was specified. At least NetBSD's |
* snprintf(3) does the same, as well as other versions of this file. |
* (Though some of these versions will write to a non-NULL buffer even |
* if a size of zero was specified, which violates the standard.) |
*/ |
if (str == NULL && size != 0) |
size = 0; |
|
while (ch != '\0') |
switch (state) { |
case PRINT_S_DEFAULT: |
if (ch == '%') |
state = PRINT_S_FLAGS; |
else |
OUTCHAR(str, len, size, ch); |
ch = *format++; |
break; |
case PRINT_S_FLAGS: |
switch (ch) { |
case '-': |
flags |= PRINT_F_MINUS; |
ch = *format++; |
break; |
case '+': |
flags |= PRINT_F_PLUS; |
ch = *format++; |
break; |
case ' ': |
flags |= PRINT_F_SPACE; |
ch = *format++; |
break; |
case '#': |
flags |= PRINT_F_NUM; |
ch = *format++; |
break; |
case '0': |
flags |= PRINT_F_ZERO; |
ch = *format++; |
break; |
case '\'': /* SUSv2 flag (not in C99). */ |
flags |= PRINT_F_QUOTE; |
ch = *format++; |
break; |
default: |
state = PRINT_S_WIDTH; |
break; |
} |
break; |
case PRINT_S_WIDTH: |
if (ISDIGIT(ch)) { |
ch = CHARTOINT(ch); |
if (width > (INT_MAX - ch) / 10) { |
overflow = 1; |
goto out; |
} |
width = 10 * width + ch; |
ch = *format++; |
} else if (ch == '*') { |
/* |
* C99 says: "A negative field width argument is |
* taken as a `-' flag followed by a positive |
* field width." (7.19.6.1, 5) |
*/ |
if ((width = va_arg(args, int)) < 0) { |
flags |= PRINT_F_MINUS; |
width = -width; |
} |
ch = *format++; |
state = PRINT_S_DOT; |
} else |
state = PRINT_S_DOT; |
break; |
case PRINT_S_DOT: |
if (ch == '.') { |
state = PRINT_S_PRECISION; |
ch = *format++; |
} else |
state = PRINT_S_MOD; |
break; |
case PRINT_S_PRECISION: |
if (precision == -1) |
precision = 0; |
if (ISDIGIT(ch)) { |
ch = CHARTOINT(ch); |
if (precision > (INT_MAX - ch) / 10) { |
overflow = 1; |
goto out; |
} |
precision = 10 * precision + ch; |
ch = *format++; |
} else if (ch == '*') { |
/* |
* C99 says: "A negative precision argument is |
* taken as if the precision were omitted." |
* (7.19.6.1, 5) |
*/ |
if ((precision = va_arg(args, int)) < 0) |
precision = -1; |
ch = *format++; |
state = PRINT_S_MOD; |
} else |
state = PRINT_S_MOD; |
break; |
case PRINT_S_MOD: |
switch (ch) { |
case 'h': |
ch = *format++; |
if (ch == 'h') { /* It's a char. */ |
ch = *format++; |
cflags = PRINT_C_CHAR; |
} else |
cflags = PRINT_C_SHORT; |
break; |
case 'l': |
ch = *format++; |
if (ch == 'l') { /* It's a long long. */ |
ch = *format++; |
cflags = PRINT_C_LLONG; |
} else |
cflags = PRINT_C_LONG; |
break; |
case 'L': |
cflags = PRINT_C_LDOUBLE; |
ch = *format++; |
break; |
case 'j': |
cflags = PRINT_C_INTMAX; |
ch = *format++; |
break; |
case 't': |
cflags = PRINT_C_PTRDIFF; |
ch = *format++; |
break; |
case 'z': |
cflags = PRINT_C_SIZE; |
ch = *format++; |
break; |
} |
state = PRINT_S_CONV; |
break; |
case PRINT_S_CONV: |
switch (ch) { |
case 'd': |
/* FALLTHROUGH */ |
case 'i': |
switch (cflags) { |
case PRINT_C_CHAR: |
value = (signed char)va_arg(args, int); |
break; |
case PRINT_C_SHORT: |
value = (short int)va_arg(args, int); |
break; |
case PRINT_C_LONG: |
value = va_arg(args, long int); |
break; |
case PRINT_C_LLONG: |
value = va_arg(args, LLONG); |
break; |
case PRINT_C_SIZE: |
value = va_arg(args, SSIZE_T); |
break; |
case PRINT_C_INTMAX: |
value = va_arg(args, INTMAX_T); |
break; |
case PRINT_C_PTRDIFF: |
value = va_arg(args, PTRDIFF_T); |
break; |
default: |
value = va_arg(args, int); |
break; |
} |
fmtint(str, &len, size, value, 10, width, |
precision, flags); |
break; |
case 'X': |
flags |= PRINT_F_UP; |
/* FALLTHROUGH */ |
case 'x': |
base = 16; |
/* FALLTHROUGH */ |
case 'o': |
if (base == 0) |
base = 8; |
/* FALLTHROUGH */ |
case 'u': |
if (base == 0) |
base = 10; |
flags |= PRINT_F_UNSIGNED; |
switch (cflags) { |
case PRINT_C_CHAR: |
value = (unsigned char)va_arg(args, |
unsigned int); |
break; |
case PRINT_C_SHORT: |
value = (unsigned short int)va_arg(args, |
unsigned int); |
break; |
case PRINT_C_LONG: |
value = va_arg(args, unsigned long int); |
break; |
case PRINT_C_LLONG: |
value = va_arg(args, ULLONG); |
break; |
case PRINT_C_SIZE: |
value = va_arg(args, size_t); |
break; |
case PRINT_C_INTMAX: |
value = va_arg(args, UINTMAX_T); |
break; |
case PRINT_C_PTRDIFF: |
value = va_arg(args, UPTRDIFF_T); |
break; |
default: |
value = va_arg(args, unsigned int); |
break; |
} |
fmtint(str, &len, size, value, base, width, |
precision, flags); |
break; |
case 'A': |
/* Not yet supported, we'll use "%F". */ |
/* FALLTHROUGH */ |
case 'F': |
flags |= PRINT_F_UP; |
case 'a': |
/* Not yet supported, we'll use "%f". */ |
/* FALLTHROUGH */ |
case 'f': |
if (cflags == PRINT_C_LDOUBLE) |
fvalue = va_arg(args, LDOUBLE); |
else |
fvalue = va_arg(args, double); |
fmtflt(str, &len, size, fvalue, width, |
precision, flags, &overflow); |
if (overflow) |
goto out; |
break; |
case 'E': |
flags |= PRINT_F_UP; |
/* FALLTHROUGH */ |
case 'e': |
flags |= PRINT_F_TYPE_E; |
if (cflags == PRINT_C_LDOUBLE) |
fvalue = va_arg(args, LDOUBLE); |
else |
fvalue = va_arg(args, double); |
fmtflt(str, &len, size, fvalue, width, |
precision, flags, &overflow); |
if (overflow) |
goto out; |
break; |
case 'G': |
flags |= PRINT_F_UP; |
/* FALLTHROUGH */ |
case 'g': |
flags |= PRINT_F_TYPE_G; |
if (cflags == PRINT_C_LDOUBLE) |
fvalue = va_arg(args, LDOUBLE); |
else |
fvalue = va_arg(args, double); |
/* |
* If the precision is zero, it is treated as |
* one (cf. C99: 7.19.6.1, 8). |
*/ |
if (precision == 0) |
precision = 1; |
fmtflt(str, &len, size, fvalue, width, |
precision, flags, &overflow); |
if (overflow) |
goto out; |
break; |
case 'c': |
cvalue = (unsigned char)va_arg(args, int); |
OUTCHAR(str, len, size, cvalue); |
break; |
case 's': |
strvalue = va_arg(args, char *); |
fmtstr(str, &len, size, strvalue, width, |
precision, flags); |
break; |
case 'p': |
/* |
* C99 says: "The value of the pointer is |
* converted to a sequence of printing |
* characters, in an implementation-defined |
* manner." (C99: 7.19.6.1, 8) |
*/ |
if ((strvalue = va_arg(args, void *)) == NULL) |
/* |
* We use the glibc format. BSD prints |
* "0x0", SysV "0". |
*/ |
fmtstr(str, &len, size, "(nil)", width, |
-1, flags); |
else { |
/* |
* We use the BSD/glibc format. SysV |
* omits the "0x" prefix (which we emit |
* using the PRINT_F_NUM flag). |
*/ |
flags |= PRINT_F_NUM; |
flags |= PRINT_F_UNSIGNED; |
fmtint(str, &len, size, |
(UINTPTR_T)strvalue, 16, width, |
precision, flags); |
} |
break; |
case 'n': |
switch (cflags) { |
case PRINT_C_CHAR: |
charptr = va_arg(args, signed char *); |
*charptr = (signed char)len; |
break; |
case PRINT_C_SHORT: |
shortptr = va_arg(args, short int *); |
*shortptr = (short int)len; |
break; |
case PRINT_C_LONG: |
longptr = va_arg(args, long int *); |
*longptr = (long int)len; |
break; |
case PRINT_C_LLONG: |
llongptr = va_arg(args, LLONG *); |
*llongptr = (LLONG)len; |
break; |
case PRINT_C_SIZE: |
/* |
* C99 says that with the "z" length |
* modifier, "a following `n' conversion |
* specifier applies to a pointer to a |
* signed integer type corresponding to |
* size_t argument." (7.19.6.1, 7) |
*/ |
sizeptr = va_arg(args, SSIZE_T *); |
*sizeptr = len; |
break; |
case PRINT_C_INTMAX: |
intmaxptr = va_arg(args, INTMAX_T *); |
*intmaxptr = len; |
break; |
case PRINT_C_PTRDIFF: |
ptrdiffptr = va_arg(args, PTRDIFF_T *); |
*ptrdiffptr = len; |
break; |
default: |
intptr = va_arg(args, int *); |
*intptr = (int)len; |
break; |
} |
break; |
case '%': /* Print a "%" character verbatim. */ |
OUTCHAR(str, len, size, ch); |
break; |
default: /* Skip other characters. */ |
break; |
} |
ch = *format++; |
state = PRINT_S_DEFAULT; |
base = cflags = flags = width = 0; |
precision = -1; |
break; |
} |
out: |
if (len < size) |
str[len] = '\0'; |
else if (size > 0) |
str[size - 1] = '\0'; |
|
if (overflow || len >= INT_MAX) { |
return -1; |
} |
return (int)len; |
} |
|
static void |
fmtstr(char *str, size_t *len, size_t size, const char *value, int width, |
int precision, int flags) |
{ |
int padlen, strln; /* Amount to pad. */ |
int noprecision = (precision == -1); |
|
if (value == NULL) /* We're forgiving. */ |
value = "(null)"; |
|
/* If a precision was specified, don't read the string past it. */ |
for (strln = 0; value[strln] != '\0' && |
(noprecision || strln < precision); strln++) |
continue; |
|
if ((padlen = width - strln) < 0) |
padlen = 0; |
if (flags & PRINT_F_MINUS) /* Left justify. */ |
padlen = -padlen; |
|
while (padlen > 0) { /* Leading spaces. */ |
OUTCHAR(str, *len, size, ' '); |
padlen--; |
} |
while (*value != '\0' && (noprecision || precision-- > 0)) { |
OUTCHAR(str, *len, size, *value); |
value++; |
} |
while (padlen < 0) { /* Trailing spaces. */ |
OUTCHAR(str, *len, size, ' '); |
padlen++; |
} |
} |
|
static void |
fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width, |
int precision, int flags) |
{ |
UINTMAX_T uvalue; |
char iconvert[MAX_CONVERT_LENGTH]; |
char sign = 0; |
char hexprefix = 0; |
int spadlen = 0; /* Amount to space pad. */ |
int zpadlen = 0; /* Amount to zero pad. */ |
int pos; |
int separators = (flags & PRINT_F_QUOTE); |
int noprecision = (precision == -1); |
|
if (flags & PRINT_F_UNSIGNED) |
uvalue = value; |
else { |
uvalue = (value >= 0) ? value : -value; |
if (value < 0) |
sign = '-'; |
else if (flags & PRINT_F_PLUS) /* Do a sign. */ |
sign = '+'; |
else if (flags & PRINT_F_SPACE) |
sign = ' '; |
} |
|
pos = convert(uvalue, iconvert, sizeof(iconvert), base, |
flags & PRINT_F_UP); |
|
if (flags & PRINT_F_NUM && uvalue != 0) { |
/* |
* C99 says: "The result is converted to an `alternative form'. |
* For `o' conversion, it increases the precision, if and only |
* if necessary, to force the first digit of the result to be a |
* zero (if the value and precision are both 0, a single 0 is |
* printed). For `x' (or `X') conversion, a nonzero result has |
* `0x' (or `0X') prefixed to it." (7.19.6.1, 6) |
*/ |
switch (base) { |
case 8: |
if (precision <= pos) |
precision = pos + 1; |
break; |
case 16: |
hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x'; |
break; |
} |
} |
|
if (separators) /* Get the number of group separators we'll print. */ |
separators = getnumsep(pos); |
|
zpadlen = precision - pos - separators; |
spadlen = width /* Minimum field width. */ |
- separators /* Number of separators. */ |
- MAX(precision, pos) /* Number of integer digits. */ |
- ((sign != 0) ? 1 : 0) /* Will we print a sign? */ |
- ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */ |
|
if (zpadlen < 0) |
zpadlen = 0; |
if (spadlen < 0) |
spadlen = 0; |
|
/* |
* C99 says: "If the `0' and `-' flags both appear, the `0' flag is |
* ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a |
* precision is specified, the `0' flag is ignored." (7.19.6.1, 6) |
*/ |
if (flags & PRINT_F_MINUS) /* Left justify. */ |
spadlen = -spadlen; |
else if (flags & PRINT_F_ZERO && noprecision) { |
zpadlen += spadlen; |
spadlen = 0; |
} |
while (spadlen > 0) { /* Leading spaces. */ |
OUTCHAR(str, *len, size, ' '); |
spadlen--; |
} |
if (sign != 0) /* Sign. */ |
OUTCHAR(str, *len, size, sign); |
if (hexprefix != 0) { /* A "0x" or "0X" prefix. */ |
OUTCHAR(str, *len, size, '0'); |
OUTCHAR(str, *len, size, hexprefix); |
} |
while (zpadlen > 0) { /* Leading zeros. */ |
OUTCHAR(str, *len, size, '0'); |
zpadlen--; |
} |
while (pos > 0) { /* The actual digits. */ |
pos--; |
OUTCHAR(str, *len, size, iconvert[pos]); |
if (separators > 0 && pos > 0 && pos % 3 == 0) |
printsep(str, len, size); |
} |
while (spadlen < 0) { /* Trailing spaces. */ |
OUTCHAR(str, *len, size, ' '); |
spadlen++; |
} |
} |
|
static void |
fmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width, |
int precision, int flags, int *overflow) |
{ |
LDOUBLE ufvalue; |
UINTMAX_T intpart; |
UINTMAX_T fracpart; |
UINTMAX_T mask; |
const char *infnan = NULL; |
char iconvert[MAX_CONVERT_LENGTH]; |
char fconvert[MAX_CONVERT_LENGTH]; |
char econvert[4]; /* "e-12" (without nul-termination). */ |
char esign = 0; |
char sign = 0; |
int leadfraczeros = 0; |
int exponent = 0; |
int emitpoint = 0; |
int omitzeros = 0; |
int omitcount = 0; |
int padlen = 0; |
int epos = 0; |
int fpos = 0; |
int ipos = 0; |
int separators = (flags & PRINT_F_QUOTE); |
int estyle = (flags & PRINT_F_TYPE_E); |
#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT |
struct lconv *lc = localeconv(); |
#endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */ |
|
/* |
* AIX' man page says the default is 0, but C99 and at least Solaris' |
* and NetBSD's man pages say the default is 6, and sprintf(3) on AIX |
* defaults to 6. |
*/ |
if (precision == -1) |
precision = 6; |
|
if (fvalue < 0.0) |
sign = '-'; |
else if (flags & PRINT_F_PLUS) /* Do a sign. */ |
sign = '+'; |
else if (flags & PRINT_F_SPACE) |
sign = ' '; |
|
if (ISNAN(fvalue)) |
infnan = (flags & PRINT_F_UP) ? "NAN" : "nan"; |
else if (ISINF(fvalue)) |
infnan = (flags & PRINT_F_UP) ? "INF" : "inf"; |
|
if (infnan != NULL) { |
if (sign != 0) |
iconvert[ipos++] = sign; |
while (*infnan != '\0') |
iconvert[ipos++] = *infnan++; |
fmtstr(str, len, size, iconvert, width, ipos, flags); |
return; |
} |
|
/* "%e" (or "%E") or "%g" (or "%G") conversion. */ |
if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) { |
if (flags & PRINT_F_TYPE_G) { |
/* |
* For "%g" (and "%G") conversions, the precision |
* specifies the number of significant digits, which |
* includes the digits in the integer part. The |
* conversion will or will not be using "e-style" (like |
* "%e" or "%E" conversions) depending on the precision |
* and on the exponent. However, the exponent can be |
* affected by rounding the converted value, so we'll |
* leave this decision for later. Until then, we'll |
* assume that we're going to do an "e-style" conversion |
* (in order to get the exponent calculated). For |
* "e-style", the precision must be decremented by one. |
*/ |
precision--; |
/* |
* For "%g" (and "%G") conversions, trailing zeros are |
* removed from the fractional portion of the result |
* unless the "#" flag was specified. |
*/ |
if (!(flags & PRINT_F_NUM)) |
omitzeros = 1; |
} |
exponent = getexponent(fvalue); |
estyle = 1; |
} |
|
again: |
/* |
* Sorry, we only support 9, 19, or 38 digits (that is, the number of |
* digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value |
* minus one) past the decimal point due to our conversion method. |
*/ |
switch (sizeof(UINTMAX_T)) { |
case 16: |
if (precision > 38) |
precision = 38; |
break; |
case 8: |
if (precision > 19) |
precision = 19; |
break; |
default: |
if (precision > 9) |
precision = 9; |
break; |
} |
|
ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue; |
if (estyle) /* We want exactly one integer digit. */ |
ufvalue /= mypow10(exponent); |
|
if ((intpart = cast(ufvalue)) == UINTMAX_MAX) { |
*overflow = 1; |
return; |
} |
|
/* |
* Factor of ten with the number of digits needed for the fractional |
* part. For example, if the precision is 3, the mask will be 1000. |
*/ |
mask = (UINTMAX_T)mypow10(precision); |
/* |
* We "cheat" by converting the fractional part to integer by |
* multiplying by a factor of ten. |
*/ |
if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) { |
/* |
* For example, ufvalue = 2.99962, intpart = 2, and mask = 1000 |
* (because precision = 3). Now, myround(1000 * 0.99962) will |
* return 1000. So, the integer part must be incremented by one |
* and the fractional part must be set to zero. |
*/ |
intpart++; |
fracpart = 0; |
if (estyle && intpart == 10) { |
/* |
* The value was rounded up to ten, but we only want one |
* integer digit if using "e-style". So, the integer |
* part must be set to one and the exponent must be |
* incremented by one. |
*/ |
intpart = 1; |
exponent++; |
} |
} |
|
/* |
* Now that we know the real exponent, we can check whether or not to |
* use "e-style" for "%g" (and "%G") conversions. If we don't need |
* "e-style", the precision must be adjusted and the integer and |
* fractional parts must be recalculated from the original value. |
* |
* C99 says: "Let P equal the precision if nonzero, 6 if the precision |
* is omitted, or 1 if the precision is zero. Then, if a conversion |
* with style `E' would have an exponent of X: |
* |
* - if P > X >= -4, the conversion is with style `f' (or `F') and |
* precision P - (X + 1). |
* |
* - otherwise, the conversion is with style `e' (or `E') and precision |
* P - 1." (7.19.6.1, 8) |
* |
* Note that we had decremented the precision by one. |
*/ |
if (flags & PRINT_F_TYPE_G && estyle && |
precision + 1 > exponent && exponent >= -4) { |
precision -= exponent; |
estyle = 0; |
goto again; |
} |
|
if (estyle) { |
if (exponent < 0) { |
exponent = -exponent; |
esign = '-'; |
} else |
esign = '+'; |
|
/* |
* Convert the exponent. The sizeof(econvert) is 4. So, the |
* econvert buffer can hold e.g. "e+99" and "e-99". We don't |
* support an exponent which contains more than two digits. |
* Therefore, the following stores are safe. |
*/ |
epos = convert(exponent, econvert, 2, 10, 0); |
/* |
* C99 says: "The exponent always contains at least two digits, |
* and only as many more digits as necessary to represent the |
* exponent." (7.19.6.1, 8) |
*/ |
if (epos == 1) |
econvert[epos++] = '0'; |
econvert[epos++] = esign; |
econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e'; |
} |
|
/* Convert the integer part and the fractional part. */ |
ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0); |
if (fracpart != 0) /* convert() would return 1 if fracpart == 0. */ |
fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0); |
|
leadfraczeros = precision - fpos; |
|
if (omitzeros) { |
if (fpos > 0) /* Omit trailing fractional part zeros. */ |
while (omitcount < fpos && fconvert[omitcount] == '0') |
omitcount++; |
else { /* The fractional part is zero, omit it completely. */ |
omitcount = precision; |
leadfraczeros = 0; |
} |
precision -= omitcount; |
} |
|
/* |
* Print a decimal point if either the fractional part is non-zero |
* and/or the "#" flag was specified. |
*/ |
if (precision > 0 || flags & PRINT_F_NUM) |
emitpoint = 1; |
if (separators) /* Get the number of group separators we'll print. */ |
separators = getnumsep(ipos); |
|
padlen = width /* Minimum field width. */ |
- ipos /* Number of integer digits. */ |
- epos /* Number of exponent characters. */ |
- precision /* Number of fractional digits. */ |
- separators /* Number of group separators. */ |
- (emitpoint ? 1 : 0) /* Will we print a decimal point? */ |
- ((sign != 0) ? 1 : 0); /* Will we print a sign character? */ |
|
if (padlen < 0) |
padlen = 0; |
|
/* |
* C99 says: "If the `0' and `-' flags both appear, the `0' flag is |
* ignored." (7.19.6.1, 6) |
*/ |
if (flags & PRINT_F_MINUS) /* Left justifty. */ |
padlen = -padlen; |
else if (flags & PRINT_F_ZERO && padlen > 0) { |
if (sign != 0) { /* Sign. */ |
OUTCHAR(str, *len, size, sign); |
sign = 0; |
} |
while (padlen > 0) { /* Leading zeros. */ |
OUTCHAR(str, *len, size, '0'); |
padlen--; |
} |
} |
while (padlen > 0) { /* Leading spaces. */ |
OUTCHAR(str, *len, size, ' '); |
padlen--; |
} |
if (sign != 0) /* Sign. */ |
OUTCHAR(str, *len, size, sign); |
while (ipos > 0) { /* Integer part. */ |
ipos--; |
OUTCHAR(str, *len, size, iconvert[ipos]); |
if (separators > 0 && ipos > 0 && ipos % 3 == 0) |
printsep(str, len, size); |
} |
if (emitpoint) { /* Decimal point. */ |
#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT |
if (lc->decimal_point != NULL && *lc->decimal_point != '\0') |
OUTCHAR(str, *len, size, *lc->decimal_point); |
else /* We'll always print some decimal point character. */ |
#endif /* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */ |
OUTCHAR(str, *len, size, '.'); |
} |
while (leadfraczeros > 0) { /* Leading fractional part zeros. */ |
OUTCHAR(str, *len, size, '0'); |
leadfraczeros--; |
} |
while (fpos > omitcount) { /* The remaining fractional part. */ |
fpos--; |
OUTCHAR(str, *len, size, fconvert[fpos]); |
} |
while (epos > 0) { /* Exponent. */ |
epos--; |
OUTCHAR(str, *len, size, econvert[epos]); |
} |
while (padlen < 0) { /* Trailing spaces. */ |
OUTCHAR(str, *len, size, ' '); |
padlen++; |
} |
} |
|
static void |
printsep(char *str, size_t *len, size_t size) |
{ |
#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP |
struct lconv *lc = localeconv(); |
int i; |
|
if (lc->thousands_sep != NULL) |
for (i = 0; lc->thousands_sep[i] != '\0'; i++) |
OUTCHAR(str, *len, size, lc->thousands_sep[i]); |
else |
#endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */ |
OUTCHAR(str, *len, size, ','); |
} |
|
static int |
getnumsep(int digits) |
{ |
int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3; |
#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP |
int strln; |
struct lconv *lc = localeconv(); |
|
/* We support an arbitrary separator length (including zero). */ |
if (lc->thousands_sep != NULL) { |
for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++) |
continue; |
separators *= strln; |
} |
#endif /* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */ |
return separators; |
} |
|
static int |
getexponent(LDOUBLE value) |
{ |
LDOUBLE tmp = (value >= 0.0) ? value : -value; |
int exponent = 0; |
|
/* |
* We check for 99 > exponent > -99 in order to work around possible |
* endless loops which could happen (at least) in the second loop (at |
* least) if we're called with an infinite value. However, we checked |
* for infinity before calling this function using our ISINF() macro, so |
* this might be somewhat paranoid. |
*/ |
while (tmp < 1.0 && tmp > 0.0 && --exponent > -99) |
tmp *= 10; |
while (tmp >= 10.0 && ++exponent < 99) |
tmp /= 10; |
|
return exponent; |
} |
|
static int |
convert(UINTMAX_T value, char *buf, size_t size, int base, int caps) |
{ |
const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef"; |
size_t pos = 0; |
|
/* We return an unterminated buffer with the digits in reverse order. */ |
do { |
buf[pos++] = digits[value % base]; |
value /= base; |
} while (value != 0 && pos < size); |
|
return (int)pos; |
} |
|
static UINTMAX_T |
cast(LDOUBLE value) |
{ |
UINTMAX_T result; |
|
/* |
* We check for ">=" and not for ">" because if UINTMAX_MAX cannot be |
* represented exactly as an LDOUBLE value (but is less than LDBL_MAX), |
* it may be increased to the nearest higher representable value for the |
* comparison (cf. C99: 6.3.1.4, 2). It might then equal the LDOUBLE |
* value although converting the latter to UINTMAX_T would overflow. |
*/ |
if (value >= UINTMAX_MAX) |
return UINTMAX_MAX; |
|
result = (UINTMAX_T)value; |
/* |
* At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to |
* an integer type converts e.g. 1.9 to 2 instead of 1 (which violates |
* the standard). Sigh. |
*/ |
return (result <= value) ? result : result - 1; |
} |
|
static UINTMAX_T |
myround(LDOUBLE value) |
{ |
UINTMAX_T intpart = cast(value); |
|
return ((value -= intpart) < 0.5) ? intpart : intpart + 1; |
} |
|
static LDOUBLE |
mypow10(int exponent) |
{ |
LDOUBLE result = 1; |
|
while (exponent > 0) { |
result *= 10; |
exponent--; |
} |
while (exponent < 0) { |
result /= 10; |
exponent++; |
} |
return result; |
} |
#endif /* !HAVE_VSNPRINTF */ |
|
#if !HAVE_VASPRINTF |
#if NEED_MYMEMCPY |
void * |
mymemcpy(void *dst, void *src, size_t len) |
{ |
const char *from = src; |
char *to = dst; |
|
/* No need for optimization, we use this only to replace va_copy(3). */ |
while (len-- > 0) |
*to++ = *from++; |
return dst; |
} |
#endif /* NEED_MYMEMCPY */ |
|
int |
util_vasprintf(char **ret, const char *format, va_list ap) |
{ |
size_t size; |
int len; |
va_list aq; |
|
VA_COPY(aq, ap); |
len = vsnprintf(NULL, 0, format, aq); |
VA_END_COPY(aq); |
if (len < 0 || (*ret = malloc(size = len + 1)) == NULL) |
return -1; |
return vsnprintf(*ret, size, format, ap); |
} |
#endif /* !HAVE_VASPRINTF */ |
|
#if !HAVE_SNPRINTF |
#if HAVE_STDARG_H |
int |
util_snprintf(char *str, size_t size, const char *format, ...) |
#else |
int |
util_snprintf(va_alist) va_dcl |
#endif /* HAVE_STDARG_H */ |
{ |
#if !HAVE_STDARG_H |
char *str; |
size_t size; |
char *format; |
#endif /* HAVE_STDARG_H */ |
va_list ap; |
int len; |
|
VA_START(ap, format); |
VA_SHIFT(ap, str, char *); |
VA_SHIFT(ap, size, size_t); |
VA_SHIFT(ap, format, const char *); |
len = vsnprintf(str, size, format, ap); |
va_end(ap); |
return len; |
} |
#endif /* !HAVE_SNPRINTF */ |
|
#if !HAVE_ASPRINTF |
#if HAVE_STDARG_H |
int |
util_asprintf(char **ret, const char *format, ...) |
#else |
int |
util_asprintf(va_alist) va_dcl |
#endif /* HAVE_STDARG_H */ |
{ |
#if !HAVE_STDARG_H |
char **ret; |
char *format; |
#endif /* HAVE_STDARG_H */ |
va_list ap; |
int len; |
|
VA_START(ap, format); |
VA_SHIFT(ap, ret, char **); |
VA_SHIFT(ap, format, const char *); |
len = vasprintf(ret, format, ap); |
va_end(ap); |
return len; |
} |
#endif /* !HAVE_ASPRINTF */ |
#else /* Dummy declaration to avoid empty translation unit warnings. */ |
int main(void); |
#endif /* !HAVE_SNPRINTF || !HAVE_VSNPRINTF || !HAVE_ASPRINTF || [...] */ |
|
|
/* vim: set joinspaces textwidth=80: */ |