repl-vsnprintf.c
上传用户:qaz666999
上传日期:2022-08-06
资源大小:2570k
文件大小:10k
- /* __gmp_replacement_vsnprintf -- for systems which don't have vsnprintf, or
- only have a broken one.
- THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY. THEY'RE ALMOST
- CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN
- FUTURE GNU MP RELEASES.
- Copyright 2001, 2002 Free Software Foundation, Inc.
- This file is part of the GNU MP Library.
- The GNU MP Library is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 3 of the License, or (at your
- option) any later version.
- The GNU MP Library 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 Lesser General Public
- License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
- #include "config.h"
- #if ! HAVE_VSNPRINTF /* only need this file if we don't have vsnprintf */
- #define _GNU_SOURCE /* for strnlen prototype */
- #if HAVE_STDARG
- #include <stdarg.h>
- #else
- #include <varargs.h>
- #endif
- #include <ctype.h> /* for isdigit */
- #include <stddef.h> /* for ptrdiff_t */
- #include <string.h>
- #include <stdio.h> /* for NULL */
- #include <stdlib.h>
- #if HAVE_FLOAT_H
- #include <float.h> /* for DBL_MAX_10_EXP etc */
- #endif
- #if HAVE_INTTYPES_H
- # include <inttypes.h> /* for intmax_t */
- #else
- # if HAVE_STDINT_H
- # include <stdint.h>
- # endif
- #endif
- #if HAVE_SYS_TYPES_H
- #include <sys/types.h> /* for quad_t */
- #endif
- #include "gmp.h"
- #include "gmp-impl.h"
- /* Autoconf notes that AIX 4.3 has a broken strnlen, but fortunately it
- doesn't affect us since __gmp_replacement_vsnprintf is not required on
- that system. */
- #if ! HAVE_STRNLEN
- static size_t
- strnlen (const char *s, size_t n)
- {
- size_t i;
- for (i = 0; i < n; i++)
- if (s[i] == ' ')
- break;
- return i;
- }
- #endif
- /* The approach here is to parse the fmt string, and decide how much space
- it requires, then use vsprintf into a big enough buffer. The space
- calculated isn't an exact amount, but it's certainly no less than
- required.
- This code was inspired by GNU libiberty/vasprintf.c but we support more
- datatypes, when available.
- mingw32 - doesn't have vsnprintf, it seems. Because gcc is used a full
- set of types are available, but "long double" is just a plain IEEE
- 64-bit "double" and LDBL_MAX_EXP_10 is correspondingly defined, so we
- avoid the big 15-bit exponent estimate. */
- int
- __gmp_replacement_vsnprintf (char *buf, size_t buf_size,
- const char *orig_fmt, va_list orig_ap)
- {
- va_list ap;
- const char *fmt;
- size_t total_width, integer_sizeof, floating_sizeof, len;
- char fchar, type;
- int width, prec, seen_prec, double_digits, long_double_digits;
- int *value;
- /* preserve orig_ap for use after size estimation */
- va_copy (ap, orig_ap);
- fmt = orig_fmt;
- total_width = strlen (fmt) + 1; /* 1 extra for the ' ' */
- integer_sizeof = sizeof (long);
- #if HAVE_LONG_LONG
- integer_sizeof = MAX (integer_sizeof, sizeof (long long));
- #endif
- #if HAVE_QUAD_T
- integer_sizeof = MAX (integer_sizeof, sizeof (quad_t));
- #endif
- floating_sizeof = sizeof (double);
- #if HAVE_LONG_DOUBLE
- floating_sizeof = MAX (floating_sizeof, sizeof (long double));
- #endif
- /* IEEE double or VAX G floats have an 11 bit exponent, so the default is
- a maximum 308 decimal digits. VAX D floats have only an 8 bit
- exponent, but we don't bother trying to detect that directly. */
- double_digits = 308;
- #ifdef DBL_MAX_10_EXP
- /* but in any case prefer a value the compiler says */
- double_digits = DBL_MAX_10_EXP;
- #endif
- /* IEEE 128-bit quad, Intel 80-bit temporary, or VAX H floats all have 15
- bit exponents, so the default is a maximum 4932 decimal digits. */
- long_double_digits = 4932;
- /* but if double == long double, then go with that size */
- #if HAVE_LONG_DOUBLE
- if (sizeof (double) == sizeof (long double))
- long_double_digits = double_digits;
- #endif
- #ifdef LDBL_MAX_10_EXP
- /* but in any case prefer a value the compiler says */
- long_double_digits = LDBL_MAX_10_EXP;
- #endif
- for (;;)
- {
- fmt = strchr (fmt, '%');
- if (fmt == NULL)
- break;
- fmt++;
- type = ' ';
- width = 0;
- prec = 6;
- seen_prec = 0;
- value = &width;
- for (;;)
- {
- fchar = *fmt++;
- switch (fchar) {
- case 'c':
- /* char, already accounted for by strlen(fmt) */
- goto next;
- case 'd':
- case 'i':
- case 'o':
- case 'x':
- case 'X':
- case 'u':
- /* at most 3 digits per byte in hex, dec or octal, plus a sign */
- total_width += 3 * integer_sizeof + 1;
- switch (type) {
- case 'j':
- /* Let's assume uintmax_t is the same size as intmax_t. */
- #if HAVE_INTMAX_T
- (void) va_arg (ap, intmax_t);
- #else
- ASSERT_FAIL (intmax_t not available);
- #endif
- break;
- case 'l':
- (void) va_arg (ap, long);
- break;
- case 'L':
- #if HAVE_LONG_LONG
- (void) va_arg (ap, long long);
- #else
- ASSERT_FAIL (long long not available);
- #endif
- break;
- case 'q':
- /* quad_t is probably the same as long long, but let's treat
- it separately just to be sure. Also let's assume u_quad_t
- will be the same size as quad_t. */
- #if HAVE_QUAD_T
- (void) va_arg (ap, quad_t);
- #else
- ASSERT_FAIL (quad_t not available);
- #endif
- break;
- case 't':
- #if HAVE_PTRDIFF_T
- (void) va_arg (ap, ptrdiff_t);
- #else
- ASSERT_FAIL (ptrdiff_t not available);
- #endif
- break;
- case 'z':
- (void) va_arg (ap, size_t);
- break;
- default:
- /* default is an "int", and this includes h=short and hh=char
- since they're promoted to int in a function call */
- (void) va_arg (ap, int);
- break;
- }
- goto next;
- case 'E':
- case 'e':
- case 'G':
- case 'g':
- /* Requested decimals, sign, point and e, plus an overestimate
- of exponent digits (the assumption is all the float is
- exponent!). */
- total_width += prec + 3 + floating_sizeof * 3;
- if (type == 'L')
- {
- #if HAVE_LONG_DOUBLE
- (void) va_arg (ap, long double);
- #else
- ASSERT_FAIL (long double not available);
- #endif
- }
- else
- (void) va_arg (ap, double);
- break;
- case 'f':
- /* Requested decimals, sign and point, and a margin for error,
- then add the maximum digits that can be in the integer part,
- based on the maximum exponent value. */
- total_width += prec + 2 + 10;
- if (type == 'L')
- {
- #if HAVE_LONG_DOUBLE
- (void) va_arg (ap, long double);
- total_width += long_double_digits;
- #else
- ASSERT_FAIL (long double not available);
- #endif
- }
- else
- {
- (void) va_arg (ap, double);
- total_width += double_digits;
- }
- break;
- case 'h': /* short or char */
- case 'j': /* intmax_t */
- case 'L': /* long long or long double */
- case 'q': /* quad_t */
- case 't': /* ptrdiff_t */
- set_type:
- type = fchar;
- break;
- case 'l':
- /* long or long long */
- if (type != 'l')
- goto set_type;
- type = 'L'; /* "ll" means "L" */
- break;
- case 'n':
- /* bytes written, no output as such */
- (void) va_arg (ap, void *);
- goto next;
- case 's':
- /* If no precision was given, then determine the string length
- and put it there, to be added to the total under "next". If
- a precision was given then that's already the maximum from
- this field, but see whether the string is shorter than that,
- in case the limit was very big. */
- {
- const char *s = va_arg (ap, const char *);
- prec = (seen_prec ? strnlen (s, prec) : strlen (s));
- }
- goto next;
- case 'p':
- /* pointer, let's assume at worst it's octal with some padding */
- (void) va_arg (ap, const void *);
- total_width += 3 * sizeof (void *) + 16;
- goto next;
- case '%':
- /* literal %, already accounted for by strlen(fmt) */
- goto next;
- case '#':
- /* showbase, at most 2 for "0x" */
- total_width += 2;
- break;
- case '+':
- case ' ':
- /* sign, already accounted for under numerics */
- break;
- case '-':
- /* left justify, no effect on total width */
- break;
- case '.':
- seen_prec = 1;
- value = ≺
- break;
- case '*':
- {
- /* negative width means left justify which can be ignored,
- negative prec would be invalid, just use absolute value */
- int n = va_arg (ap, int);
- *value = ABS (n);
- }
- break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- /* process all digits to form a value */
- {
- int n = 0;
- do {
- n = n * 10 + (fchar-'0');
- fchar = *fmt++;
- } while (isascii (fchar) && isdigit (fchar));
- fmt--; /* unget the non-digit */
- *value = n;
- }
- break;
- default:
- /* incomplete or invalid % sequence */
- ASSERT (0);
- goto next;
- }
- }
- next:
- total_width += width;
- total_width += prec;
- }
- if (total_width <= buf_size)
- {
- vsprintf (buf, orig_fmt, orig_ap);
- len = strlen (buf);
- }
- else
- {
- char *s;
- s = __GMP_ALLOCATE_FUNC_TYPE (total_width, char);
- vsprintf (s, orig_fmt, orig_ap);
- len = strlen (s);
- if (buf_size != 0)
- {
- size_t copylen = MIN (len, buf_size-1);
- memcpy (buf, s, copylen);
- buf[copylen] = ' ';
- }
- (*__gmp_free_func) (s, total_width);
- }
- /* If total_width was somehow wrong then chances are we've already
- clobbered memory, but maybe this check will still work. */
- ASSERT_ALWAYS (len < total_width);
- return len;
- }
- #endif /* ! HAVE_VSNPRINTF */