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