1/* __gmp_replacement_vsnprintf -- for systems which don't have vsnprintf, or 2 only have a broken one. 3 4 THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY. THEY'RE ALMOST 5 CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN 6 FUTURE GNU MP RELEASES. 7 8Copyright 2001, 2002 Free Software Foundation, Inc. 9 10This file is part of the GNU MP Library. 11 12The GNU MP Library is free software; you can redistribute it and/or modify 13it under the terms of the GNU Lesser General Public License as published by 14the Free Software Foundation; either version 3 of the License, or (at your 15option) any later version. 16 17The GNU MP Library is distributed in the hope that it will be useful, but 18WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 19or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 20License for more details. 21 22You should have received a copy of the GNU Lesser General Public License 23along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */ 24 25#include "config.h" 26 27#if ! HAVE_VSNPRINTF /* only need this file if we don't have vsnprintf */ 28 29 30#define _GNU_SOURCE /* for strnlen prototype */ 31 32#if HAVE_STDARG 33#include <stdarg.h> 34#else 35#include <varargs.h> 36#endif 37 38#include <ctype.h> /* for isdigit */ 39#include <stddef.h> /* for ptrdiff_t */ 40#include <string.h> 41#include <stdio.h> /* for NULL */ 42#include <stdlib.h> 43 44#if HAVE_FLOAT_H 45#include <float.h> /* for DBL_MAX_10_EXP etc */ 46#endif 47 48#if HAVE_INTTYPES_H 49# include <inttypes.h> /* for intmax_t */ 50#else 51# if HAVE_STDINT_H 52# include <stdint.h> 53# endif 54#endif 55 56#if HAVE_SYS_TYPES_H 57#include <sys/types.h> /* for quad_t */ 58#endif 59 60#include "gmp.h" 61#include "gmp-impl.h" 62 63 64/* Autoconf notes that AIX 4.3 has a broken strnlen, but fortunately it 65 doesn't affect us since __gmp_replacement_vsnprintf is not required on 66 that system. */ 67#if ! HAVE_STRNLEN 68static size_t 69strnlen (const char *s, size_t n) 70{ 71 size_t i; 72 for (i = 0; i < n; i++) 73 if (s[i] == '\0') 74 break; 75 return i; 76} 77#endif 78 79 80/* The approach here is to parse the fmt string, and decide how much space 81 it requires, then use vsprintf into a big enough buffer. The space 82 calculated isn't an exact amount, but it's certainly no less than 83 required. 84 85 This code was inspired by GNU libiberty/vasprintf.c but we support more 86 datatypes, when available. 87 88 mingw32 - doesn't have vsnprintf, it seems. Because gcc is used a full 89 set of types are available, but "long double" is just a plain IEEE 90 64-bit "double" and LDBL_MAX_EXP_10 is correspondingly defined, so we 91 avoid the big 15-bit exponent estimate. */ 92 93int 94__gmp_replacement_vsnprintf (char *buf, size_t buf_size, 95 const char *orig_fmt, va_list orig_ap) 96{ 97 va_list ap; 98 const char *fmt; 99 size_t total_width, integer_sizeof, floating_sizeof, len; 100 char fchar, type; 101 int width, prec, seen_prec, double_digits, long_double_digits; 102 int *value; 103 104 /* preserve orig_ap for use after size estimation */ 105 va_copy (ap, orig_ap); 106 107 fmt = orig_fmt; 108 total_width = strlen (fmt) + 1; /* 1 extra for the '\0' */ 109 110 integer_sizeof = sizeof (long); 111#if HAVE_LONG_LONG 112 integer_sizeof = MAX (integer_sizeof, sizeof (long long)); 113#endif 114#if HAVE_QUAD_T 115 integer_sizeof = MAX (integer_sizeof, sizeof (quad_t)); 116#endif 117 118 floating_sizeof = sizeof (double); 119#if HAVE_LONG_DOUBLE 120 floating_sizeof = MAX (floating_sizeof, sizeof (long double)); 121#endif 122 123 /* IEEE double or VAX G floats have an 11 bit exponent, so the default is 124 a maximum 308 decimal digits. VAX D floats have only an 8 bit 125 exponent, but we don't bother trying to detect that directly. */ 126 double_digits = 308; 127#ifdef DBL_MAX_10_EXP 128 /* but in any case prefer a value the compiler says */ 129 double_digits = DBL_MAX_10_EXP; 130#endif 131 132 /* IEEE 128-bit quad, Intel 80-bit temporary, or VAX H floats all have 15 133 bit exponents, so the default is a maximum 4932 decimal digits. */ 134 long_double_digits = 4932; 135 /* but if double == long double, then go with that size */ 136#if HAVE_LONG_DOUBLE 137 if (sizeof (double) == sizeof (long double)) 138 long_double_digits = double_digits; 139#endif 140#ifdef LDBL_MAX_10_EXP 141 /* but in any case prefer a value the compiler says */ 142 long_double_digits = LDBL_MAX_10_EXP; 143#endif 144 145 for (;;) 146 { 147 fmt = strchr (fmt, '%'); 148 if (fmt == NULL) 149 break; 150 fmt++; 151 152 type = '\0'; 153 width = 0; 154 prec = 6; 155 seen_prec = 0; 156 value = &width; 157 158 for (;;) 159 { 160 fchar = *fmt++; 161 switch (fchar) { 162 163 case 'c': 164 /* char, already accounted for by strlen(fmt) */ 165 goto next; 166 167 case 'd': 168 case 'i': 169 case 'o': 170 case 'x': 171 case 'X': 172 case 'u': 173 /* at most 3 digits per byte in hex, dec or octal, plus a sign */ 174 total_width += 3 * integer_sizeof + 1; 175 176 switch (type) { 177 case 'j': 178 /* Let's assume uintmax_t is the same size as intmax_t. */ 179#if HAVE_INTMAX_T 180 (void) va_arg (ap, intmax_t); 181#else 182 ASSERT_FAIL (intmax_t not available); 183#endif 184 break; 185 case 'l': 186 (void) va_arg (ap, long); 187 break; 188 case 'L': 189#if HAVE_LONG_LONG 190 (void) va_arg (ap, long long); 191#else 192 ASSERT_FAIL (long long not available); 193#endif 194 break; 195 case 'q': 196 /* quad_t is probably the same as long long, but let's treat 197 it separately just to be sure. Also let's assume u_quad_t 198 will be the same size as quad_t. */ 199#if HAVE_QUAD_T 200 (void) va_arg (ap, quad_t); 201#else 202 ASSERT_FAIL (quad_t not available); 203#endif 204 break; 205 case 't': 206#if HAVE_PTRDIFF_T 207 (void) va_arg (ap, ptrdiff_t); 208#else 209 ASSERT_FAIL (ptrdiff_t not available); 210#endif 211 break; 212 case 'z': 213 (void) va_arg (ap, size_t); 214 break; 215 default: 216 /* default is an "int", and this includes h=short and hh=char 217 since they're promoted to int in a function call */ 218 (void) va_arg (ap, int); 219 break; 220 } 221 goto next; 222 223 case 'E': 224 case 'e': 225 case 'G': 226 case 'g': 227 /* Requested decimals, sign, point and e, plus an overestimate 228 of exponent digits (the assumption is all the float is 229 exponent!). */ 230 total_width += prec + 3 + floating_sizeof * 3; 231 if (type == 'L') 232 { 233#if HAVE_LONG_DOUBLE 234 (void) va_arg (ap, long double); 235#else 236 ASSERT_FAIL (long double not available); 237#endif 238 } 239 else 240 (void) va_arg (ap, double); 241 break; 242 243 case 'f': 244 /* Requested decimals, sign and point, and a margin for error, 245 then add the maximum digits that can be in the integer part, 246 based on the maximum exponent value. */ 247 total_width += prec + 2 + 10; 248 if (type == 'L') 249 { 250#if HAVE_LONG_DOUBLE 251 (void) va_arg (ap, long double); 252 total_width += long_double_digits; 253#else 254 ASSERT_FAIL (long double not available); 255#endif 256 } 257 else 258 { 259 (void) va_arg (ap, double); 260 total_width += double_digits; 261 } 262 break; 263 264 case 'h': /* short or char */ 265 case 'j': /* intmax_t */ 266 case 'L': /* long long or long double */ 267 case 'q': /* quad_t */ 268 case 't': /* ptrdiff_t */ 269 set_type: 270 type = fchar; 271 break; 272 273 case 'l': 274 /* long or long long */ 275 if (type != 'l') 276 goto set_type; 277 type = 'L'; /* "ll" means "L" */ 278 break; 279 280 case 'n': 281 /* bytes written, no output as such */ 282 (void) va_arg (ap, void *); 283 goto next; 284 285 case 's': 286 /* If no precision was given, then determine the string length 287 and put it there, to be added to the total under "next". If 288 a precision was given then that's already the maximum from 289 this field, but see whether the string is shorter than that, 290 in case the limit was very big. */ 291 { 292 const char *s = va_arg (ap, const char *); 293 prec = (seen_prec ? strnlen (s, prec) : strlen (s)); 294 } 295 goto next; 296 297 case 'p': 298 /* pointer, let's assume at worst it's octal with some padding */ 299 (void) va_arg (ap, const void *); 300 total_width += 3 * sizeof (void *) + 16; 301 goto next; 302 303 case '%': 304 /* literal %, already accounted for by strlen(fmt) */ 305 goto next; 306 307 case '#': 308 /* showbase, at most 2 for "0x" */ 309 total_width += 2; 310 break; 311 312 case '+': 313 case ' ': 314 /* sign, already accounted for under numerics */ 315 break; 316 317 case '-': 318 /* left justify, no effect on total width */ 319 break; 320 321 case '.': 322 seen_prec = 1; 323 value = ≺ 324 break; 325 326 case '*': 327 { 328 /* negative width means left justify which can be ignored, 329 negative prec would be invalid, just use absolute value */ 330 int n = va_arg (ap, int); 331 *value = ABS (n); 332 } 333 break; 334 335 case '0': case '1': case '2': case '3': case '4': 336 case '5': case '6': case '7': case '8': case '9': 337 /* process all digits to form a value */ 338 { 339 int n = 0; 340 do { 341 n = n * 10 + (fchar-'0'); 342 fchar = *fmt++; 343 } while (isascii (fchar) && isdigit (fchar)); 344 fmt--; /* unget the non-digit */ 345 *value = n; 346 } 347 break; 348 349 default: 350 /* incomplete or invalid % sequence */ 351 ASSERT (0); 352 goto next; 353 } 354 } 355 356 next: 357 total_width += width; 358 total_width += prec; 359 } 360 361 if (total_width <= buf_size) 362 { 363 vsprintf (buf, orig_fmt, orig_ap); 364 len = strlen (buf); 365 } 366 else 367 { 368 char *s; 369 370 s = __GMP_ALLOCATE_FUNC_TYPE (total_width, char); 371 vsprintf (s, orig_fmt, orig_ap); 372 len = strlen (s); 373 if (buf_size != 0) 374 { 375 size_t copylen = MIN (len, buf_size-1); 376 memcpy (buf, s, copylen); 377 buf[copylen] = '\0'; 378 } 379 (*__gmp_free_func) (s, total_width); 380 } 381 382 /* If total_width was somehow wrong then chances are we've already 383 clobbered memory, but maybe this check will still work. */ 384 ASSERT_ALWAYS (len < total_width); 385 386 return len; 387} 388 389#endif /* ! HAVE_VSNPRINTF */ 390