| /* vsprintf with automatic memory allocation. |
| Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. |
| |
| This program is free software; you can redistribute it and/or modify it |
| under the terms of the GNU Library General Public License as published |
| by the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public |
| License along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| USA. */ |
| |
| #ifndef _WIN32 |
| /* Tell glibc's <stdio.h> to provide a prototype for snprintf(). |
| This must come before <config.h> because <config.h> may include |
| <features.h>, and once <features.h> has been included, it's too late. */ |
| #ifndef _GNU_SOURCE |
| # define _GNU_SOURCE 1 |
| #endif |
| #endif |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include "gst-printf.h" |
| |
| /* Specification. */ |
| #include "vasnprintf.h" |
| |
| #include <stdio.h> /* snprintf(), sprintf() */ |
| #include <stdlib.h> /* abort(), malloc(), realloc(), free() */ |
| #include <string.h> /* memcpy(), strlen() */ |
| #include <errno.h> /* errno */ |
| #include <limits.h> /* CHAR_BIT */ |
| #include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */ |
| #include "printf-parse.h" |
| #include "printf-extension.h" |
| |
| #ifdef HAVE_WCHAR_T |
| # ifdef HAVE_WCSLEN |
| # define local_wcslen wcslen |
| # else |
| /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid |
| a dependency towards this library, here is a local substitute. |
| Define this substitute only once, even if this file is included |
| twice in the same compilation unit. */ |
| # ifndef local_wcslen_defined |
| # define local_wcslen_defined 1 |
| static size_t |
| local_wcslen (const wchar_t * s) |
| { |
| const wchar_t *ptr; |
| |
| for (ptr = s; *ptr != (wchar_t) 0; ptr++); |
| return ptr - s; |
| } |
| # endif |
| # endif |
| #endif |
| |
| /* For those losing systems which don't have 'alloca' we have to add |
| some additional code emulating it. */ |
| #if defined (alloca) || defined (GLIB_HAVE_ALLOCA_H) |
| # define freea(p) /* nothing */ |
| #else |
| # define alloca(n) malloc (n) |
| # define freea(p) free (p) |
| #endif |
| |
| #ifndef HAVE_LONG_LONG_FORMAT |
| static inline int |
| print_long_long (char *buf, |
| int len, |
| int width, |
| int precision, |
| unsigned long flags, char conversion, unsigned long long number) |
| { |
| int negative = FALSE; |
| char buffer[128]; |
| char *bufferend; |
| char *pointer; |
| int base; |
| static const char *upper = "0123456789ABCDEFX"; |
| static const char *lower = "0123456789abcdefx"; |
| const char *digits; |
| int i; |
| char *p; |
| int count; |
| |
| #define EMIT(c) \ |
| if (p - buf == len - 1) \ |
| { \ |
| *p++ = '\0'; \ |
| return len; \ |
| } \ |
| else \ |
| *p++ = c; |
| |
| p = buf; |
| |
| switch (conversion) { |
| case 'o': |
| base = 8; |
| digits = lower; |
| negative = FALSE; |
| break; |
| case 'x': |
| base = 16; |
| digits = lower; |
| negative = FALSE; |
| break; |
| case 'X': |
| base = 16; |
| digits = upper; |
| negative = FALSE; |
| break; |
| case 'u': |
| base = 10; |
| digits = lower; |
| negative = FALSE; |
| break; |
| default: |
| base = 10; |
| digits = lower; |
| negative = (long long) number < 0; |
| if (negative) |
| number = -((long long) number); |
| break; |
| } |
| |
| /* Build number */ |
| pointer = bufferend = &buffer[sizeof (buffer) - 1]; |
| *pointer-- = '\0'; |
| for (i = 1; i < (int) sizeof (buffer); i++) { |
| *pointer-- = digits[number % base]; |
| number /= base; |
| if (number == 0) |
| break; |
| } |
| |
| /* Adjust width */ |
| width -= (bufferend - pointer) - 1; |
| |
| /* Adjust precision */ |
| if (precision != -1) { |
| precision -= (bufferend - pointer) - 1; |
| if (precision < 0) |
| precision = 0; |
| flags |= FLAG_ZERO; |
| } |
| |
| /* Adjust width further */ |
| if (negative || (flags & FLAG_SHOWSIGN) || (flags & FLAG_SPACE)) |
| width--; |
| if (flags & FLAG_ALT) { |
| switch (base) { |
| case 16: |
| width -= 2; |
| break; |
| case 8: |
| width--; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* Output prefixes spaces if needed */ |
| if (!((flags & FLAG_LEFT) || ((flags & FLAG_ZERO) && (precision == -1)))) { |
| count = (precision == -1) ? 0 : precision; |
| while (width-- > count) |
| *p++ = ' '; |
| } |
| |
| /* width has been adjusted for signs and alternatives */ |
| if (negative) { |
| EMIT ('-'); |
| } else if (flags & FLAG_SHOWSIGN) { |
| EMIT ('+'); |
| } else if (flags & FLAG_SPACE) { |
| EMIT (' '); |
| } |
| |
| if (flags & FLAG_ALT) { |
| switch (base) { |
| case 8: |
| EMIT ('0'); |
| break; |
| case 16: |
| EMIT ('0'); |
| EMIT (digits[16]); |
| break; |
| default: |
| break; |
| } /* switch base */ |
| } |
| |
| /* Output prefixed zero padding if needed */ |
| if (flags & FLAG_ZERO) { |
| if (precision == -1) |
| precision = width; |
| while (precision-- > 0) { |
| EMIT ('0'); |
| width--; |
| } |
| } |
| |
| /* Output the number itself */ |
| while (*(++pointer)) { |
| EMIT (*pointer); |
| } |
| |
| /* Output trailing spaces if needed */ |
| if (flags & FLAG_LEFT) { |
| while (width-- > 0) |
| EMIT (' '); |
| } |
| |
| EMIT ('\0'); |
| |
| return p - buf - 1; |
| } |
| #endif |
| |
| static void |
| printf_postprocess_args (char_directives * directives, arguments * arguments) |
| { |
| int i; |
| |
| for (i = 0; i < directives->count; ++i) { |
| char_directive *dp; |
| argument *a; |
| |
| dp = &directives->dir[i]; |
| |
| /* %% has no arguments, for example */ |
| if (dp->arg_index < 0) |
| continue; |
| |
| a = &arguments->arg[dp->arg_index]; |
| |
| if (a->type == TYPE_POINTER_EXT) { |
| char fmt[4]; |
| |
| fmt[0] = 'p'; |
| fmt[1] = POINTER_EXT_SIGNIFIER_CHAR; |
| fmt[2] = dp->ptr_ext_char; |
| fmt[3] = '\0'; |
| |
| a->ext_string = |
| __gst_printf_pointer_extension_serialize (fmt, a->a.a_pointer); |
| } |
| } |
| } |
| |
| char * |
| vasnprintf (char *resultbuf, size_t * lengthp, const char *format, va_list args) |
| { |
| char_directives d; |
| arguments a; |
| |
| if (printf_parse (format, &d, &a) < 0) { |
| errno = EINVAL; |
| return NULL; |
| } |
| #define CLEANUP() \ |
| free (d.dir); \ |
| if (a.arg) { \ |
| while (a.count--) { \ |
| if (a.arg[a.count].ext_string) \ |
| free (a.arg[a.count].ext_string); \ |
| } \ |
| free (a.arg); \ |
| } |
| |
| if (printf_fetchargs (args, &a) < 0) { |
| CLEANUP (); |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| /* collect TYPE_POINTER_EXT argument strings */ |
| printf_postprocess_args (&d, &a); |
| |
| { |
| char *buf = |
| (char *) alloca (7 + d.max_width_length + d.max_precision_length + 6); |
| const char *cp; |
| unsigned int i; |
| char_directive *dp; |
| /* Output string accumulator. */ |
| char *result; |
| size_t allocated; |
| size_t length; |
| |
| if (resultbuf != NULL) { |
| result = resultbuf; |
| allocated = *lengthp; |
| } else { |
| result = NULL; |
| allocated = 0; |
| } |
| length = 0; |
| /* Invariants: |
| result is either == resultbuf or == NULL or malloc-allocated. |
| If length > 0, then result != NULL. */ |
| |
| #define ENSURE_ALLOCATION(needed) \ |
| if ((needed) > allocated) \ |
| { \ |
| char *memory; \ |
| \ |
| allocated = (allocated > 0 ? 2 * allocated : 12); \ |
| if ((needed) > allocated) \ |
| allocated = (needed); \ |
| if (result == resultbuf || result == NULL) \ |
| memory = (char *) malloc (allocated); \ |
| else \ |
| memory = (char *) realloc (result, allocated); \ |
| \ |
| if (memory == NULL) \ |
| { \ |
| if (!(result == resultbuf || result == NULL)) \ |
| free (result); \ |
| freea (buf); \ |
| CLEANUP (); \ |
| errno = ENOMEM; \ |
| return NULL; \ |
| } \ |
| if (result == resultbuf && length > 0) \ |
| memcpy (memory, result, length); \ |
| result = memory; \ |
| } |
| |
| for (cp = format, i = 0, dp = &d.dir[0];; cp = dp->dir_end, i++, dp++) { |
| if (cp != dp->dir_start) { |
| size_t n = dp->dir_start - cp; |
| |
| ENSURE_ALLOCATION (length + n); |
| memcpy (result + length, cp, n); |
| length += n; |
| } |
| if (i == d.count) |
| break; |
| |
| /* Execute a single directive. */ |
| if (dp->conversion == '%') { |
| if (!(dp->arg_index < 0)) |
| abort (); |
| ENSURE_ALLOCATION (length + 1); |
| result[length] = '%'; |
| length += 1; |
| } else { |
| if (!(dp->arg_index >= 0)) |
| abort (); |
| |
| if (dp->conversion == 'n') { |
| switch (a.arg[dp->arg_index].type) { |
| case TYPE_COUNT_SCHAR_POINTER: |
| *a.arg[dp->arg_index].a.a_count_schar_pointer = length; |
| break; |
| case TYPE_COUNT_SHORT_POINTER: |
| *a.arg[dp->arg_index].a.a_count_short_pointer = length; |
| break; |
| case TYPE_COUNT_INT_POINTER: |
| *a.arg[dp->arg_index].a.a_count_int_pointer = length; |
| break; |
| case TYPE_COUNT_LONGINT_POINTER: |
| *a.arg[dp->arg_index].a.a_count_longint_pointer = length; |
| break; |
| #ifdef HAVE_LONG_LONG |
| case TYPE_COUNT_LONGLONGINT_POINTER: |
| *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; |
| break; |
| #endif |
| default: |
| abort (); |
| } |
| } else { |
| arg_type type = a.arg[dp->arg_index].type; |
| char *p; |
| unsigned int prefix_count; |
| int prefixes[2]; |
| #ifndef HAVE_SNPRINTF |
| unsigned int tmp_length; |
| char tmpbuf[700]; |
| char *tmp; |
| |
| /* Allocate a temporary buffer of sufficient size for calling |
| sprintf. */ |
| { |
| unsigned int width; |
| unsigned int precision; |
| |
| width = 0; |
| if (dp->width_start != dp->width_end) { |
| if (dp->width_arg_index >= 0) { |
| int arg; |
| |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) |
| abort (); |
| arg = a.arg[dp->width_arg_index].a.a_int; |
| width = (arg < 0 ? -arg : arg); |
| } else { |
| const char *digitp = dp->width_start; |
| |
| do |
| width = width * 10 + (*digitp++ - '0'); |
| while (digitp != dp->width_end); |
| } |
| } |
| |
| precision = 6; |
| if (dp->precision_start != dp->precision_end) { |
| if (dp->precision_arg_index >= 0) { |
| int arg; |
| |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) |
| abort (); |
| arg = a.arg[dp->precision_arg_index].a.a_int; |
| precision = (arg < 0 ? 0 : arg); |
| } else { |
| const char *digitp = dp->precision_start + 1; |
| |
| precision = 0; |
| while (digitp != dp->precision_end) |
| precision = precision * 10 + (*digitp++ - '0'); |
| } |
| } |
| |
| switch (dp->conversion) { |
| case 'd': |
| case 'i': |
| case 'u': |
| # ifdef HAVE_LONG_LONG |
| if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) |
| tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.30103 /* binary -> decimal */ |
| * 2 /* estimate for FLAG_GROUP */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 1; /* account for leading sign */ |
| else |
| # endif |
| if (type == TYPE_LONGINT || type == TYPE_ULONGINT) |
| tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.30103 /* binary -> decimal */ |
| * 2 /* estimate for FLAG_GROUP */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 1; /* account for leading sign */ |
| else |
| tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.30103 /* binary -> decimal */ |
| * 2 /* estimate for FLAG_GROUP */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 1; /* account for leading sign */ |
| break; |
| |
| case 'o': |
| # ifdef HAVE_LONG_LONG |
| if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) |
| tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.333334 /* binary -> octal */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 1; /* account for leading sign */ |
| else |
| # endif |
| if (type == TYPE_LONGINT || type == TYPE_ULONGINT) |
| tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.333334 /* binary -> octal */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 1; /* account for leading sign */ |
| else |
| tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.333334 /* binary -> octal */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 1; /* account for leading sign */ |
| break; |
| |
| case 'x': |
| case 'X': |
| # ifdef HAVE_LONG_LONG |
| if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) |
| tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 2; /* account for leading sign or alternate form */ |
| else |
| # endif |
| # ifdef HAVE_INT64_AND_I64 |
| if (type == TYPE_INT64 || type == TYPE_UINT64) |
| tmp_length = (unsigned int) (sizeof (unsigned __int64) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 2; /* account for leading sign or alternate form */ |
| else |
| # endif |
| if (type == TYPE_LONGINT || type == TYPE_ULONGINT) |
| tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 2; /* account for leading sign or alternate form */ |
| else |
| tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 2; /* account for leading sign or alternate form */ |
| break; |
| |
| case 'f': |
| case 'F': |
| # ifdef HAVE_LONG_DOUBLE |
| if (type == TYPE_LONGDOUBLE) |
| tmp_length = (unsigned int) (LDBL_MAX_EXP * 0.30103 /* binary -> decimal */ |
| * 2 /* estimate for FLAG_GROUP */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + precision + 10; /* sign, decimal point etc. */ |
| else |
| # endif |
| tmp_length = (unsigned int) (DBL_MAX_EXP * 0.30103 /* binary -> decimal */ |
| * 2 /* estimate for FLAG_GROUP */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + precision + 10; /* sign, decimal point etc. */ |
| break; |
| |
| case 'e': |
| case 'E': |
| case 'g': |
| case 'G': |
| case 'a': |
| case 'A': |
| tmp_length = precision + 12; /* sign, decimal point, exponent etc. */ |
| break; |
| |
| case 'c': |
| # ifdef HAVE_WINT_T |
| if (type == TYPE_WIDE_CHAR) |
| tmp_length = MB_CUR_MAX; |
| else |
| # endif |
| tmp_length = 1; |
| break; |
| |
| case 's': |
| # ifdef HAVE_WCHAR_T |
| if (type == TYPE_WIDE_STRING) |
| tmp_length = (a.arg[dp->arg_index].a.a_wide_string == NULL ? 6 /* wcslen(L"(null)") */ |
| : local_wcslen (a.arg[dp->arg_index].a.a_wide_string)) |
| * MB_CUR_MAX; |
| else |
| # endif |
| tmp_length = a.arg[dp->arg_index].a.a_string == NULL ? 6 /* strlen("(null)") */ |
| : strlen (a.arg[dp->arg_index].a.a_string); |
| break; |
| |
| case 'p': |
| tmp_length = (unsigned int) (sizeof (void *) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ |
| ) |
| + 1 /* turn floor into ceil */ |
| + 2; /* account for leading 0x */ |
| |
| /* make sure we always have enough space for a plain %p, so + */ |
| if (dp->flags & FLAG_PTR_EXT && a.arg[dp->arg_index].ext_string) |
| tmp_length += strlen (a.arg[dp->arg_index].ext_string); |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| if (tmp_length < width) |
| tmp_length = width; |
| |
| tmp_length++; /* account for trailing NUL */ |
| } |
| |
| if (tmp_length <= sizeof (tmpbuf)) |
| tmp = tmpbuf; |
| else { |
| tmp = (char *) malloc (tmp_length); |
| if (tmp == NULL) { |
| /* Out of memory. */ |
| if (!(result == resultbuf || result == NULL)) |
| free (result); |
| freea (buf); |
| CLEANUP (); |
| errno = ENOMEM; |
| return NULL; |
| } |
| } |
| #endif |
| |
| /* Construct the format string for calling snprintf or |
| sprintf. */ |
| p = buf; |
| *p++ = '%'; |
| if (dp->flags & FLAG_GROUP) |
| *p++ = '\''; |
| if (dp->flags & FLAG_LEFT) |
| *p++ = '-'; |
| if (dp->flags & FLAG_SHOWSIGN) |
| *p++ = '+'; |
| if (dp->flags & FLAG_SPACE) |
| *p++ = ' '; |
| if (dp->flags & FLAG_ALT) |
| *p++ = '#'; |
| if (dp->flags & FLAG_ZERO) |
| *p++ = '0'; |
| if (dp->width_start != dp->width_end) { |
| size_t n = dp->width_end - dp->width_start; |
| memcpy (p, dp->width_start, n); |
| p += n; |
| } |
| if (dp->precision_start != dp->precision_end) { |
| size_t n = dp->precision_end - dp->precision_start; |
| memcpy (p, dp->precision_start, n); |
| p += n; |
| } |
| |
| switch (type) { |
| #ifdef HAVE_INT64_AND_I64 |
| case TYPE_INT64: |
| case TYPE_UINT64: |
| *p++ = 'I'; |
| *p++ = '6'; |
| *p++ = '4'; |
| break; |
| #endif |
| #ifdef HAVE_LONG_LONG |
| case TYPE_LONGLONGINT: |
| case TYPE_ULONGLONGINT: |
| #ifdef HAVE_INT64_AND_I64 /* The system (sn)printf uses %I64. Also assume |
| * that long long == __int64. |
| */ |
| *p++ = 'I'; |
| *p++ = '6'; |
| *p++ = '4'; |
| break; |
| #else |
| *p++ = 'l'; |
| /*FALLTHROUGH*/ |
| #endif |
| #endif |
| case TYPE_LONGINT: |
| case TYPE_ULONGINT: |
| #ifdef HAVE_WINT_T |
| case TYPE_WIDE_CHAR: |
| #endif |
| #ifdef HAVE_WCHAR_T |
| case TYPE_WIDE_STRING: |
| #endif |
| *p++ = 'l'; |
| break; |
| #ifdef HAVE_LONG_DOUBLE |
| case TYPE_LONGDOUBLE: |
| *p++ = 'L'; |
| break; |
| #endif |
| default: |
| break; |
| } |
| *p = dp->conversion; |
| #ifdef HAVE_SNPRINTF |
| p[1] = '%'; |
| p[2] = 'n'; |
| p[3] = '\0'; |
| #else |
| p[1] = '\0'; |
| #endif |
| |
| /* Construct the arguments for calling snprintf or sprintf. */ |
| prefix_count = 0; |
| if (dp->width_arg_index >= 0) { |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) |
| abort (); |
| prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int; |
| } |
| if (dp->precision_arg_index >= 0) { |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) |
| abort (); |
| prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int; |
| } |
| #ifdef HAVE_SNPRINTF |
| /* Prepare checking whether snprintf returns the count |
| via %n. */ |
| ENSURE_ALLOCATION (length + 1); |
| result[length] = '\0'; |
| #endif |
| |
| for (;;) { |
| size_t maxlen; |
| int count; |
| #ifdef HAVE_SNPRINTF |
| int retcount; |
| #endif |
| |
| maxlen = allocated - length; |
| count = -1; |
| |
| #ifdef HAVE_SNPRINTF |
| retcount = 0; |
| |
| #define SNPRINTF_BUF(arg) \ |
| switch (prefix_count) \ |
| { \ |
| case 0: \ |
| retcount = snprintf (result + length, maxlen, buf, \ |
| arg, &count); \ |
| break; \ |
| case 1: \ |
| retcount = snprintf (result + length, maxlen, buf, \ |
| prefixes[0], arg, &count); \ |
| break; \ |
| case 2: \ |
| retcount = snprintf (result + length, maxlen, buf, \ |
| prefixes[0], prefixes[1], arg, \ |
| &count); \ |
| break; \ |
| default: \ |
| abort (); \ |
| } |
| #else |
| #define SNPRINTF_BUF(arg) \ |
| switch (prefix_count) \ |
| { \ |
| case 0: \ |
| count = sprintf (tmp, buf, arg); \ |
| break; \ |
| case 1: \ |
| count = sprintf (tmp, buf, prefixes[0], arg); \ |
| break; \ |
| case 2: \ |
| count = sprintf (tmp, buf, prefixes[0], prefixes[1],\ |
| arg); \ |
| break; \ |
| default: \ |
| abort (); \ |
| } |
| #endif |
| |
| switch (type) { |
| case TYPE_SCHAR: |
| { |
| int arg = a.arg[dp->arg_index].a.a_schar; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_UCHAR: |
| { |
| unsigned int arg = a.arg[dp->arg_index].a.a_uchar; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_SHORT: |
| { |
| int arg = a.arg[dp->arg_index].a.a_short; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_USHORT: |
| { |
| unsigned int arg = a.arg[dp->arg_index].a.a_ushort; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_INT: |
| { |
| int arg = a.arg[dp->arg_index].a.a_int; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_UINT: |
| { |
| unsigned int arg = a.arg[dp->arg_index].a.a_uint; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_LONGINT: |
| { |
| long int arg = a.arg[dp->arg_index].a.a_longint; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_ULONGINT: |
| { |
| unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| #ifdef HAVE_INT64_AND_I64 |
| case TYPE_INT64: |
| { |
| __int64 arg = a.arg[dp->arg_index].a.a_int64; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_UINT64: |
| { |
| unsigned __int64 arg = a.arg[dp->arg_index].a.a_uint64; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| #endif |
| #ifdef HAVE_LONG_LONG |
| #ifndef HAVE_LONG_LONG_FORMAT |
| case TYPE_LONGLONGINT: |
| case TYPE_ULONGLONGINT: |
| { |
| unsigned long long int arg = |
| a.arg[dp->arg_index].a.a_ulonglongint; |
| int width; |
| int precision; |
| |
| width = 0; |
| if (dp->width_start != dp->width_end) { |
| if (dp->width_arg_index >= 0) { |
| int arg; |
| |
| if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) |
| abort (); |
| arg = a.arg[dp->width_arg_index].a.a_int; |
| width = (arg < 0 ? -arg : arg); |
| } else { |
| const char *digitp = dp->width_start; |
| |
| do |
| width = width * 10 + (*digitp++ - '0'); |
| while (digitp != dp->width_end); |
| } |
| } |
| |
| precision = -1; |
| if (dp->precision_start != dp->precision_end) { |
| if (dp->precision_arg_index >= 0) { |
| int arg; |
| |
| if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) |
| abort (); |
| arg = a.arg[dp->precision_arg_index].a.a_int; |
| precision = (arg < 0 ? 0 : arg); |
| } else { |
| const char *digitp = dp->precision_start + 1; |
| |
| precision = 0; |
| do |
| precision = precision * 10 + (*digitp++ - '0'); |
| while (digitp != dp->precision_end); |
| } |
| } |
| #ifdef HAVE_SNPRINTF |
| count = print_long_long (result + length, maxlen, |
| width, precision, dp->flags, dp->conversion, arg); |
| #else |
| count = print_long_long (tmp, tmp_length, |
| width, precision, dp->flags, dp->conversion, arg); |
| #endif |
| } |
| break; |
| #else |
| case TYPE_LONGLONGINT: |
| { |
| long long int arg = a.arg[dp->arg_index].a.a_longlongint; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_ULONGLONGINT: |
| { |
| unsigned long long int arg = |
| a.arg[dp->arg_index].a.a_ulonglongint; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| #endif |
| #endif |
| case TYPE_DOUBLE: |
| { |
| double arg = a.arg[dp->arg_index].a.a_double; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| #ifdef HAVE_LONG_DOUBLE |
| case TYPE_LONGDOUBLE: |
| { |
| long double arg = a.arg[dp->arg_index].a.a_longdouble; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| #endif |
| case TYPE_CHAR: |
| { |
| int arg = a.arg[dp->arg_index].a.a_char; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| #ifdef HAVE_WINT_T |
| case TYPE_WIDE_CHAR: |
| { |
| wint_t arg = a.arg[dp->arg_index].a.a_wide_char; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| #endif |
| case TYPE_STRING: |
| { |
| const char *arg = a.arg[dp->arg_index].a.a_string == NULL |
| ? "(null)" : a.arg[dp->arg_index].a.a_string; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| #ifdef HAVE_WCHAR_T |
| case TYPE_WIDE_STRING: |
| { |
| const wchar_t *arg = |
| a.arg[dp->arg_index].a.a_wide_string == |
| NULL ? L"(null)" : a.arg[dp->arg_index].a.a_wide_string; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| #endif |
| case TYPE_POINTER: |
| { |
| void *arg = a.arg[dp->arg_index].a.a_pointer; |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| case TYPE_POINTER_EXT: |
| { |
| void *arg = a.arg[dp->arg_index].a.a_pointer; |
| |
| if (a.arg[dp->arg_index].ext_string != NULL) { |
| arg = a.arg[dp->arg_index].ext_string; |
| *p = 's'; |
| } |
| |
| SNPRINTF_BUF (arg); |
| } |
| break; |
| default: |
| abort (); |
| } |
| |
| #ifdef HAVE_SNPRINTF |
| /* Portability: Not all implementations of snprintf() |
| are ISO C 99 compliant. Determine the number of |
| bytes that snprintf() has produced or would have |
| produced. */ |
| if (count >= 0) { |
| /* Verify that snprintf() has NUL-terminated its |
| result. */ |
| if (count < maxlen && result[length + count] != '\0') |
| abort (); |
| /* Portability hack. */ |
| if (retcount > count) |
| count = retcount; |
| } else { |
| /* snprintf() doesn't understand the '%n' |
| directive. */ |
| if (p[1] != '\0') { |
| /* Don't use the '%n' directive; instead, look |
| at the snprintf() return value. */ |
| p[1] = '\0'; |
| continue; |
| } |
| count = retcount; |
| } |
| #endif |
| |
| /* Attempt to handle failure. */ |
| if (count < 0) { |
| if (!(result == resultbuf || result == NULL)) |
| free (result); |
| freea (buf); |
| CLEANUP (); |
| errno = EINVAL; |
| return NULL; |
| } |
| #ifndef HAVE_SNPRINTF |
| if (count >= tmp_length) |
| /* tmp_length was incorrectly calculated - fix the |
| code above! */ |
| abort (); |
| #endif |
| |
| /* Make room for the result. */ |
| if (count >= maxlen) { |
| /* Need at least count bytes. But allocate |
| proportionally, to avoid looping eternally if |
| snprintf() reports a too small count. */ |
| size_t n = length + count; |
| |
| if (n < 2 * allocated) |
| n = 2 * allocated; |
| |
| ENSURE_ALLOCATION (n); |
| #ifdef HAVE_SNPRINTF |
| continue; |
| #endif |
| } |
| #ifdef HAVE_SNPRINTF |
| /* The snprintf() result did fit. */ |
| #else |
| /* Append the sprintf() result. */ |
| memcpy (result + length, tmp, count); |
| if (tmp != tmpbuf) |
| free (tmp); |
| #endif |
| |
| length += count; |
| break; |
| } |
| } |
| } |
| } |
| |
| /* Add the final NUL. */ |
| ENSURE_ALLOCATION (length + 1); |
| result[length] = '\0'; |
| |
| if (result != resultbuf && length + 1 < allocated) { |
| /* Shrink the allocated memory if possible. */ |
| char *memory; |
| |
| memory = (char *) realloc (result, length + 1); |
| if (memory != NULL) |
| result = memory; |
| } |
| |
| freea (buf); |
| CLEANUP (); |
| *lengthp = length; |
| return result; |
| } |
| } |