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