|
0
|
1 /**
|
|
|
2 * "Portable" printf implementation
|
|
|
3 *
|
|
|
4 * Copyright (c) 2025 Paper
|
|
|
5 * Copyright (c) 2005-2012 Rich Felker
|
|
|
6 *
|
|
|
7 * Permission is hereby granted, free of charge, to any person obtaining
|
|
|
8 * a copy of this software and associated documentation files (the
|
|
|
9 * "Software"), to deal in the Software without restriction, including
|
|
|
10 * without limitation the rights to use, copy, modify, merge, publish,
|
|
|
11 * distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
12 * permit persons to whom the Software is furnished to do so, subject to
|
|
|
13 * the following conditions:
|
|
|
14 *
|
|
|
15 * The above copyright notice and this permission notice shall be
|
|
|
16 * included in all copies or substantial portions of the Software.
|
|
|
17 *
|
|
|
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
25 *
|
|
|
26 * NOTE: Floating point support is very scuffed, and doesn't
|
|
|
27 * work for many numbers. It also depends on math.h. If you don't
|
|
|
28 * care or don't want floating point support, toggle the #define
|
|
|
29 * to disable it.
|
|
|
30 *
|
|
|
31 * EXTRA NOTE: You don't need malloc() if you turn off wide character
|
|
|
32 * support :)
|
|
|
33 **/
|
|
|
34
|
|
|
35 #include "printf.h"
|
|
|
36
|
|
|
37 /* Since C89, but optional because locale shit sucks */
|
|
|
38 #define HAVE_WCTOMB 1
|
|
|
39
|
|
|
40 /* strlen has been here since the beginning... */
|
|
|
41 #define HAVE_STRLEN 1
|
|
|
42 /* ... but strnlen is a POSIX invention, added in C23. */
|
|
|
43 /*#define HAVE_STRNLEN 1*/
|
|
|
44
|
|
|
45 #define HAVE_FLOATING_POINT 1
|
|
|
46 #define HAVE_INTMAX_T 1
|
|
|
47 #define HAVE_LONG_LONG 1
|
|
|
48 #define HAVE_UINTPTR_T 1
|
|
|
49 #define HAVE_SIZE_T 1
|
|
|
50 #define HAVE_PTRDIFF_T 1
|
|
|
51 #define HAVE_LONG_DOUBLE 1
|
|
|
52
|
|
|
53 /* #define to use sprintf for floats. */
|
|
|
54 #define MY_PRINTF_USE_SPRINTF 1
|
|
|
55
|
|
|
56 #include <stdarg.h>
|
|
|
57 #include <stdio.h> /* stdout, FILE */
|
|
|
58 #include <limits.h>
|
|
|
59 #if defined(HAVE_STRLEN) || defined(HAVE_STRNLEN)
|
|
|
60 # include <string.h>
|
|
|
61 #endif
|
|
|
62 #ifdef HAVE_WCTOMB
|
|
|
63 # include <stdlib.h>
|
|
|
64 #endif
|
|
|
65 #ifdef HAVE_INTMAX_T
|
|
|
66 # include <stdint.h>
|
|
|
67 typedef intmax_t my_printf_intmax;
|
|
|
68 typedef uintmax_t my_printf_uintmax;
|
|
|
69 #elif defined(HAVE_LONG_LONG)
|
|
|
70 typedef long long my_printf_intmax;
|
|
|
71 typedef unsigned long long my_printf_uintmax;
|
|
|
72 #else
|
|
|
73 typedef long my_printf_intmax;
|
|
|
74 typedef unsigned long my_printf_uintmax;
|
|
|
75 #endif
|
|
|
76 #ifdef HAVE_WCTOMB
|
|
|
77 # include <wchar.h>
|
|
|
78 #endif
|
|
|
79 #ifdef HAVE_FLOATING_POINT
|
|
|
80 # include <math.h> /* fmod */
|
|
|
81 # include <float.h>
|
|
|
82 #endif
|
|
|
83 #ifdef HAVE_UINTPTR_T
|
|
|
84 # include <stdint.h>
|
|
|
85 typedef uintptr_t my_printf_uintptr;
|
|
|
86 #else
|
|
|
87 /* uintmax can probably hold a pointer?? hopefully :) */
|
|
|
88 typedef my_printf_uintmax my_printf_uintptr;
|
|
|
89 #endif
|
|
|
90 #ifdef HAVE_SIZE_T
|
|
|
91 # include <stddef.h>
|
|
|
92 typedef size_t my_printf_size;
|
|
|
93 #else
|
|
|
94 typedef my_printf_uintptr my_printf_size;
|
|
|
95 #endif
|
|
|
96
|
|
|
97 #ifdef HAVE_LONG_DOUBLE
|
|
|
98 /* Everything is long double */
|
|
|
99 typedef long double floatmax;
|
|
|
100
|
|
|
101 # define frexpm frexpl
|
|
|
102 #else
|
|
|
103 typedef double floatmax;
|
|
|
104
|
|
|
105 # define frexpm frexp
|
|
|
106 #endif
|
|
|
107
|
|
|
108 #ifdef HAVE_STRNLEN
|
|
|
109 # define my_strnlen strnlen
|
|
|
110 #else
|
|
|
111 static my_printf_size my_strnlen(const char *s, my_printf_size maxlen)
|
|
|
112 {
|
|
|
113 my_printf_size len;
|
|
|
114 for (len = 0; len < maxlen && *s; len++, s++);
|
|
|
115 return len;
|
|
|
116 }
|
|
|
117 #endif
|
|
|
118
|
|
|
119 #ifdef HAVE_STRLEN
|
|
|
120 # define my_strlen strlen
|
|
|
121 #else
|
|
|
122 static my_printf_size my_strlen(const char *s)
|
|
|
123 {
|
|
|
124 my_printf_size len;
|
|
|
125 for (len = 0; *s; len++, s++);
|
|
|
126 return len;
|
|
|
127 }
|
|
|
128 #endif
|
|
|
129
|
|
|
130 #ifdef TEST_MEMORY
|
|
|
131 static int my_mem_counter = 0;
|
|
|
132
|
|
|
133 /* Make sure our memory allocation code still works,
|
|
|
134 * even in out of memory conditions */
|
|
|
135 static void *my_realloc(void *x, my_printf_size sz)
|
|
|
136 {
|
|
|
137 if (my_mem_counter++ > 4)
|
|
|
138 return NULL;
|
|
|
139
|
|
|
140 return realloc(x, sz);
|
|
|
141 }
|
|
|
142
|
|
|
143 static void *my_calloc(my_printf_size sz, my_printf_size c)
|
|
|
144 {
|
|
|
145 if (my_mem_counter++ > 4)
|
|
|
146 return NULL;
|
|
|
147
|
|
|
148 return calloc(sz, c);
|
|
|
149 }
|
|
|
150
|
|
|
151 static void *my_malloc(my_printf_size sz)
|
|
|
152 {
|
|
|
153 if (my_mem_counter++ > 4)
|
|
|
154 return NULL;
|
|
|
155
|
|
|
156 return malloc(sz);
|
|
|
157 }
|
|
|
158 #else
|
|
|
159 # define my_realloc realloc
|
|
|
160 # define my_calloc calloc
|
|
|
161 # define my_malloc malloc
|
|
|
162 #endif
|
|
|
163
|
|
|
164 void my_free(void *x)
|
|
|
165 {
|
|
|
166 if (x) free(x);
|
|
|
167 }
|
|
|
168
|
|
|
169 /* ------------------------------------------------------------------------ */
|
|
|
170 /* ERRORS */
|
|
|
171
|
|
|
172 const char *my_strerror(int err)
|
|
|
173 {
|
|
|
174 static const char *errs[] = {
|
|
|
175 "Out of memory",
|
|
|
176 "Invalid format string",
|
|
|
177 "Invalid or incomplete multibyte or wide character",
|
|
|
178 "Value too large to be stored in data type",
|
|
|
179 };
|
|
|
180
|
|
|
181 err = abs(err) - 1;
|
|
|
182
|
|
|
183 if (err < 0 || err >= sizeof(errs))
|
|
|
184 return NULL;
|
|
|
185
|
|
|
186 return errs[err];
|
|
|
187 }
|
|
|
188
|
|
|
189 /* ------------------------------------------------------------------------ */
|
|
|
190 /* FLAGS */
|
|
|
191
|
|
|
192 enum flags {
|
|
|
193 FLAG_JUSTIFY_LEFT = 0x01,
|
|
|
194 FLAG_PLUS = 0x02,
|
|
|
195 FLAG_SPACE = 0x04,
|
|
|
196 FLAG_HASH = 0x08,
|
|
|
197 FLAG_ZERO = 0x10
|
|
|
198 };
|
|
|
199
|
|
|
200 /* ------------------------------------------------------------------------ */
|
|
|
201 /* CONVERT SIZE_T TO SIGNED
|
|
|
202 * - this is required for handling %zd correctly on odd systems
|
|
|
203 * - basically any compiler ever (besides msvc) will do dead code removal
|
|
|
204 * because sizeof() is a compile time constant
|
|
|
205 * - we cannot do this in the preprocessor unless SIZE_MAX is defined... */
|
|
|
206
|
|
|
207 #ifdef HAVE_SIZE_T
|
|
|
208 static my_printf_intmax my_size_t_sign(size_t x)
|
|
|
209 {
|
|
|
210 #define IF(type) \
|
|
|
211 if (sizeof(size_t) == sizeof(type)) { \
|
|
|
212 union { type s; size_t ss; } u; \
|
|
|
213 u.ss = x; \
|
|
|
214 return u.s; \
|
|
|
215 } else \
|
|
|
216
|
|
|
217 IF(signed char)
|
|
|
218 IF(signed short)
|
|
|
219 IF(signed int)
|
|
|
220 IF(signed long)
|
|
|
221 #ifdef HAVE_LONG_LONG
|
|
|
222 IF(signed long long)
|
|
|
223 #endif
|
|
|
224 #ifdef HAVE_PTRDIFF_T
|
|
|
225 IF(ptrdiff_t)
|
|
|
226 #endif
|
|
|
227 /* hope this works */
|
|
|
228 return (my_printf_intmax)x;
|
|
|
229 #undef IF
|
|
|
230 }
|
|
|
231 #endif
|
|
|
232
|
|
|
233 /* ------------------------------------------------------------------------ */
|
|
|
234 /* CHAR PRINTING */
|
|
|
235
|
|
|
236 static void print_chars(put_spec put,
|
|
|
237 void *opaque, my_printf_uintmax *pnum, unsigned char c, unsigned long width)
|
|
|
238 {
|
|
|
239 *pnum += width;
|
|
|
240 while (width-- > 0)
|
|
|
241 put(opaque, c);
|
|
|
242 }
|
|
|
243
|
|
|
244 #define print_spaces(a,b,c,d) print_chars(a,b,c,' ',d)
|
|
|
245
|
|
|
246 /* ------------------------------------------------------------------------ */
|
|
|
247 /* STRING PRINTING */
|
|
|
248
|
|
|
249 static void putstring(put_spec put, void *opaque, const char *s,
|
|
|
250 my_printf_size len, my_printf_uintmax *num, my_printf_uintmax width, int justify_left)
|
|
|
251 {
|
|
|
252 my_printf_size i;
|
|
|
253
|
|
|
254 /* Handle width */
|
|
|
255 if (!justify_left && width > len)
|
|
|
256 print_spaces(put, opaque, num, width - len);
|
|
|
257
|
|
|
258 for (i = 0; i < len; i++)
|
|
|
259 put(opaque, s[i]);
|
|
|
260 *num += len;
|
|
|
261
|
|
|
262 if (justify_left && width > len)
|
|
|
263 print_spaces(put, opaque, num, width - len);
|
|
|
264 }
|
|
|
265
|
|
|
266 /* ------------------------------------------------------------------------ */
|
|
|
267 /* NUMBER PRINTING */
|
|
|
268
|
|
|
269 static my_printf_uintmax my_numput_width(my_printf_uintmax d, int radix)
|
|
|
270 {
|
|
|
271 my_printf_uintmax x;
|
|
|
272 my_printf_uintmax width;
|
|
|
273
|
|
|
274 /* ... */
|
|
|
275 for (width = 0, x = d; x >= 1; width++, x /= radix);
|
|
|
276 width--; /* ;) */
|
|
|
277
|
|
|
278 return width;
|
|
|
279 }
|
|
|
280
|
|
|
281 static void my_numput_ex(put_spec put,
|
|
|
282 void *opaque, my_printf_uintmax d, int radix, const char trans[36],
|
|
|
283 my_printf_uintmax *num)
|
|
|
284 {
|
|
|
285 /* This is terrible but it doesn't need a intermediate buffer */
|
|
|
286 my_printf_uintmax x;
|
|
|
287 my_printf_uintmax width, i;
|
|
|
288
|
|
|
289 /* ... */
|
|
|
290 width = my_numput_width(d, radix);
|
|
|
291
|
|
|
292 for (i = 0, x = 1; i < width; x *= radix, i++);
|
|
|
293
|
|
|
294 while (x >= 1) {
|
|
|
295 put(opaque, trans[d / x]);
|
|
|
296 ++*num;
|
|
|
297 d %= x;
|
|
|
298 x /= radix;
|
|
|
299 }
|
|
|
300 }
|
|
|
301
|
|
|
302 /* note: for radix 2-10, the translation table doesn't actually matter. */
|
|
|
303 static void my_numput_lower(put_spec put,
|
|
|
304 void *opaque, my_printf_uintmax d, int radix, my_printf_uintmax *num)
|
|
|
305 {
|
|
|
306 static const char trans[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
|
307 my_numput_ex(put, opaque, d, radix, trans, num);
|
|
|
308 }
|
|
|
309
|
|
|
310 static void my_numput_upper(put_spec put,
|
|
|
311 void *opaque, my_printf_uintmax d, int radix, my_printf_uintmax *num)
|
|
|
312 {
|
|
|
313 static const char trans[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
314 my_numput_ex(put, opaque, d, radix, trans, num);
|
|
|
315 }
|
|
|
316
|
|
|
317 static void my_numput(put_spec put,
|
|
|
318 void *opaque, my_printf_uintmax d, int radix, my_printf_uintmax *num,
|
|
|
319 int hash, const char *hash_str, my_printf_size hash_strlen,
|
|
|
320 my_printf_uintmax width, int justify_left, int zero,
|
|
|
321 int upper)
|
|
|
322 {
|
|
|
323 if (hash) {
|
|
|
324 putstring(put, opaque, hash_str, hash_strlen, num, 0, 0);
|
|
|
325 if (width >= 2) width -= 2;
|
|
|
326 }
|
|
|
327
|
|
|
328 if (width > 0) {
|
|
|
329 my_printf_uintmax len;
|
|
|
330
|
|
|
331 /* This is disgusting. */
|
|
|
332 len = my_numput_width(d, 16);
|
|
|
333
|
|
|
334 if ((!justify_left && !zero) && len + 1 < width)
|
|
|
335 print_chars(put, opaque, num, ' ', width - len - 1);
|
|
|
336
|
|
|
337 if (zero && len + 1 < width)
|
|
|
338 print_chars(put, opaque, num, '0', width - len - 1);
|
|
|
339
|
|
|
340
|
|
|
341 (upper ? my_numput_upper : my_numput_lower)(put, opaque, d, radix, num);
|
|
|
342
|
|
|
343 if ((justify_left) && len + 1 < width)
|
|
|
344 print_spaces(put, opaque, num, width - len - 1);
|
|
|
345 } else {
|
|
|
346 (upper ? my_numput_upper : my_numput_lower)(put, opaque, d, radix, num);
|
|
|
347 }
|
|
|
348 }
|
|
|
349
|
|
|
350 /* ------------------------------------------------------------------------ */
|
|
|
351 /* FLOATING POINT
|
|
|
352 *
|
|
|
353 * The following code was taken from musl libc. I won't even pretend to
|
|
|
354 * understand it. */
|
|
|
355
|
|
|
356 #ifdef HAVE_FLOATING_POINT
|
|
|
357 # ifndef isnan
|
|
|
358 static int isnan(floatmax d)
|
|
|
359 {
|
|
|
360 return (d != d);
|
|
|
361 }
|
|
|
362 # endif
|
|
|
363
|
|
|
364 # ifndef isfinite
|
|
|
365 static int isfinite(floatmax d)
|
|
|
366 {
|
|
|
367 return !(d >= HUGE_VAL);
|
|
|
368 }
|
|
|
369 # endif
|
|
|
370
|
|
|
371 # ifndef signbit
|
|
|
372 static int signbit(floatmax d)
|
|
|
373 {
|
|
|
374 /* NOTE: this doesn't work for -NAN */
|
|
|
375 return (d < 0.0 || d == -0.0);
|
|
|
376 }
|
|
|
377 # endif
|
|
|
378
|
|
|
379 #define MARK_POS FLAG_PLUS
|
|
|
380 #define PAD_POS FLAG_SPACE
|
|
|
381 #define ALT_FORM FLAG_HASH
|
|
|
382 #define LEFT_ADJ FLAG_JUSTIFY_LEFT
|
|
|
383 #define ZERO_PAD FLAG_ZERO
|
|
|
384
|
|
|
385 #define MIN(x,y) ((x)<(y)?(x):(y))
|
|
|
386 #define MAX(x,y) ((x)>(y)?(x):(y))
|
|
|
387 #define CONCAT2(x,y) x ## y
|
|
|
388 #define CONCAT(x,y) CONCAT2(x,y)
|
|
|
389
|
|
|
390 static const char xdigits[16] = {
|
|
|
391 "0123456789ABCDEF"
|
|
|
392 };
|
|
|
393
|
|
|
394 static void out(put_spec put, void *opaque, const char *s, size_t l, my_printf_uintmax *num)
|
|
|
395 {
|
|
|
396 putstring(put, opaque, s, l, num, 0, 0);
|
|
|
397 }
|
|
|
398
|
|
|
399 static void pad(put_spec put, void *opaque, char c, int w, int l, enum flags fl,
|
|
|
400 my_printf_uintmax *num)
|
|
|
401 {
|
|
|
402 char pad[256];
|
|
|
403 if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
|
|
|
404 l = w - l;
|
|
|
405 memset(pad, c, l>sizeof pad ? sizeof pad : l);
|
|
|
406 for (; l >= sizeof pad; l -= sizeof pad)
|
|
|
407 out(put, opaque, pad, sizeof pad, num);
|
|
|
408 out(put, opaque, pad, l, num);
|
|
|
409 }
|
|
|
410
|
|
|
411 static char *fmt_u(uintmax_t x, char *s)
|
|
|
412 {
|
|
|
413 unsigned long y;
|
|
|
414 for ( ; x>ULONG_MAX; x/=10) *--s = '0' + x%10;
|
|
|
415 for (y=x; y; y/=10) *--s = '0' + y%10;
|
|
|
416 return s;
|
|
|
417 }
|
|
|
418
|
|
|
419 static int my_floatput(put_spec put, void *opaque, floatmax y,
|
|
|
420 int w, int p, enum flags fl, my_printf_uintmax *num, int t)
|
|
|
421 {
|
|
|
422 /* Floating point implementation borrowed from musl libc */
|
|
|
423 uint32_t big[(LDBL_MAX_EXP+LDBL_MANT_DIG)/9+1];
|
|
|
424 uint32_t *a, *d, *r, *z;
|
|
|
425 int e2=0, e, i, j, l;
|
|
|
426 char buf[9+LDBL_MANT_DIG/4], *s;
|
|
|
427 const char *prefix="-0X+0X 0X-0x+0x 0x";
|
|
|
428 int pl;
|
|
|
429 char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
|
|
|
430
|
|
|
431 pl=1;
|
|
|
432 if (signbit(y)) {
|
|
|
433 y=-y;
|
|
|
434 } else if (fl & MARK_POS) {
|
|
|
435 prefix+=3;
|
|
|
436 } else if (fl & PAD_POS) {
|
|
|
437 prefix+=6;
|
|
|
438 } else prefix++, pl=0;
|
|
|
439
|
|
|
440 if (!isfinite(y)) {
|
|
|
441 char *s;
|
|
|
442 if (isnan(y)) {
|
|
|
443 s = (t & 32) ? "nan" : "NAN";
|
|
|
444 } else {
|
|
|
445 s = (t & 32) ? "inf" : "INF";
|
|
|
446 }
|
|
|
447 pad(put, opaque, ' ', w, 3+pl, fl&~ZERO_PAD, num);
|
|
|
448 out(put, opaque, prefix, pl, num);
|
|
|
449 out(put, opaque, s, 3, num);
|
|
|
450 pad(put, opaque, ' ', w, 3+pl, fl^LEFT_ADJ, num);
|
|
|
451 return MAX(w, 3+pl);
|
|
|
452 }
|
|
|
453
|
|
|
454 y = frexpm(y, &e2) * 2;
|
|
|
455 if (y) e2--;
|
|
|
456
|
|
|
457 if ((t|32)=='a') {
|
|
|
458 long double round = 8.0;
|
|
|
459 int re;
|
|
|
460
|
|
|
461 if (t&32) prefix += 9;
|
|
|
462 pl += 2;
|
|
|
463
|
|
|
464 if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
|
|
|
465 else re=LDBL_MANT_DIG/4-1-p;
|
|
|
466
|
|
|
467 if (re) {
|
|
|
468 while (re--) round*=16;
|
|
|
469 if (*prefix=='-') {
|
|
|
470 y=-y;
|
|
|
471 y-=round;
|
|
|
472 y+=round;
|
|
|
473 y=-y;
|
|
|
474 } else {
|
|
|
475 y+=round;
|
|
|
476 y-=round;
|
|
|
477 }
|
|
|
478 }
|
|
|
479
|
|
|
480 estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
|
|
|
481 if (estr==ebuf) *--estr='0';
|
|
|
482 *--estr = (e2<0 ? '-' : '+');
|
|
|
483 *--estr = t+('p'-'a');
|
|
|
484
|
|
|
485 s=buf;
|
|
|
486 do {
|
|
|
487 int x=y;
|
|
|
488 *s++=xdigits[x]|(t&32);
|
|
|
489 y=16*(y-x);
|
|
|
490 if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
|
|
|
491 } while (y);
|
|
|
492
|
|
|
493 if (p && s-buf-2 < p)
|
|
|
494 l = (p+2) + (ebuf-estr);
|
|
|
495 else
|
|
|
496 l = (s-buf) + (ebuf-estr);
|
|
|
497
|
|
|
498 pad(put, opaque, ' ', w, pl+l, fl, num);
|
|
|
499 out(put, opaque, prefix, pl, num);
|
|
|
500 pad(put, opaque, '0', w, pl+l, fl^ZERO_PAD, num);
|
|
|
501 out(put, opaque, buf, s-buf, num);
|
|
|
502 pad(put, opaque, '0', l-(ebuf-estr)-(s-buf), 0, 0, num);
|
|
|
503 out(put, opaque, estr, ebuf-estr, num);
|
|
|
504 pad(put, opaque, ' ', w, pl+l, fl^LEFT_ADJ, num);
|
|
|
505 return MAX(w, pl+l);
|
|
|
506 }
|
|
|
507 if (p<0) p=6;
|
|
|
508
|
|
|
509 if (y) y *= 0x1p28, e2-=28;
|
|
|
510
|
|
|
511 if (e2<0) a=r=z=big;
|
|
|
512 else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
|
|
|
513
|
|
|
514 do {
|
|
|
515 *z = y;
|
|
|
516 y = 1000000000*(y-*z++);
|
|
|
517 } while (y);
|
|
|
518
|
|
|
519 while (e2>0) {
|
|
|
520 uint32_t carry=0;
|
|
|
521 int sh=MIN(29,e2);
|
|
|
522 for (d=z-1; d>=a; d--) {
|
|
|
523 uint64_t x = ((uint64_t)*d<<sh)+carry;
|
|
|
524 *d = x % 1000000000;
|
|
|
525 carry = x / 1000000000;
|
|
|
526 }
|
|
|
527 if (!z[-1] && z>a) z--;
|
|
|
528 if (carry) *--a = carry;
|
|
|
529 e2-=sh;
|
|
|
530 }
|
|
|
531 while (e2<0) {
|
|
|
532 uint32_t carry=0, *b;
|
|
|
533 int sh=MIN(9,-e2);
|
|
|
534 for (d=a; d<z; d++) {
|
|
|
535 uint32_t rm = *d & (1<<sh)-1;
|
|
|
536 *d = (*d>>sh) + carry;
|
|
|
537 carry = (1000000000>>sh) * rm;
|
|
|
538 }
|
|
|
539 if (!*a) a++;
|
|
|
540 if (carry) *z++ = carry;
|
|
|
541 /* Avoid (slow!) computation past requested precision */
|
|
|
542 b = (t|32)=='f' ? r : a;
|
|
|
543 if (z-b > 2+p/9) z = b+2+p/9;
|
|
|
544 e2+=sh;
|
|
|
545 }
|
|
|
546
|
|
|
547 if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
|
|
|
548 else e=0;
|
|
|
549
|
|
|
550 /* Perform rounding: j is precision after the radix (possibly neg) */
|
|
|
551 j = p - ((t|32)!='f')*e - ((t|32)=='g' && p);
|
|
|
552 if (j < 9*(z-r-1)) {
|
|
|
553 uint32_t x;
|
|
|
554 /* We avoid C's broken division of negative numbers */
|
|
|
555 d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
|
|
|
556 j += 9*LDBL_MAX_EXP;
|
|
|
557 j %= 9;
|
|
|
558 for (i=10, j++; j<9; i*=10, j++);
|
|
|
559 x = *d % i;
|
|
|
560 /* Are there any significant digits past j? */
|
|
|
561 if (x || d+1!=z) {
|
|
|
562 long double round = CONCAT(0x1p,LDBL_MANT_DIG);
|
|
|
563 long double small;
|
|
|
564 if (*d/i & 1) round += 2;
|
|
|
565 if (x<i/2) small=0x0.8p0;
|
|
|
566 else if (x==i/2 && d+1==z) small=0x1.0p0;
|
|
|
567 else small=0x1.8p0;
|
|
|
568 if (pl && *prefix=='-') round*=-1, small*=-1;
|
|
|
569 *d -= x;
|
|
|
570 /* Decide whether to round by probing round+small */
|
|
|
571 if (round+small != round) {
|
|
|
572 *d = *d + i;
|
|
|
573 while (*d > 999999999) {
|
|
|
574 *d--=0;
|
|
|
575 (*d)++;
|
|
|
576 }
|
|
|
577 if (d<a) a=d;
|
|
|
578 for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
|
|
|
579 }
|
|
|
580 }
|
|
|
581 if (z>d+1) z=d+1;
|
|
|
582 for (; !z[-1] && z>a; z--);
|
|
|
583 }
|
|
|
584
|
|
|
585 if ((t|32)=='g') {
|
|
|
586 if (!p) p++;
|
|
|
587 if (p>e && e>=-4) {
|
|
|
588 t--;
|
|
|
589 p-=e+1;
|
|
|
590 } else {
|
|
|
591 t-=2;
|
|
|
592 p--;
|
|
|
593 }
|
|
|
594 if (!(fl&ALT_FORM)) {
|
|
|
595 /* Count trailing zeros in last place */
|
|
|
596 if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
|
|
|
597 else j=9;
|
|
|
598 if ((t|32)=='f')
|
|
|
599 p = MIN(p,MAX(0,9*(z-r-1)-j));
|
|
|
600 else
|
|
|
601 p = MIN(p,MAX(0,9*(z-r-1)+e-j));
|
|
|
602 }
|
|
|
603 }
|
|
|
604 l = 1 + p + (p || (fl&ALT_FORM));
|
|
|
605 if ((t|32)=='f') {
|
|
|
606 if (e>0) l+=e;
|
|
|
607 } else {
|
|
|
608 estr=fmt_u(e<0 ? -e : e, ebuf);
|
|
|
609 while(ebuf-estr<2) *--estr='0';
|
|
|
610 *--estr = (e<0 ? '-' : '+');
|
|
|
611 *--estr = t;
|
|
|
612 l += ebuf-estr;
|
|
|
613 }
|
|
|
614
|
|
|
615 pad(put, opaque, ' ', w, pl+l, fl, num);
|
|
|
616 out(put, opaque, prefix, pl, num);
|
|
|
617 pad(put, opaque, '0', w, pl+l, fl^ZERO_PAD, num);
|
|
|
618
|
|
|
619 if ((t|32)=='f') {
|
|
|
620 if (a>r) a=r;
|
|
|
621 for (d=a; d<=r; d++) {
|
|
|
622 char *s = fmt_u(*d, buf+9);
|
|
|
623 if (d!=a) while (s>buf) *--s='0';
|
|
|
624 else if (s==buf+9) *--s='0';
|
|
|
625 out(put, opaque, s, buf+9-s, num);
|
|
|
626 }
|
|
|
627 if (p || (fl&ALT_FORM)) out(put, opaque, ".", 1, num);
|
|
|
628 for (; d<z && p>0; d++, p-=9) {
|
|
|
629 char *s = fmt_u(*d, buf+9);
|
|
|
630 while (s>buf) *--s='0';
|
|
|
631 out(put, opaque, s, MIN(9,p), num);
|
|
|
632 }
|
|
|
633 pad(put, opaque, '0', p+9, 9, 0, num);
|
|
|
634 } else {
|
|
|
635 if (z<=a) z=a+1;
|
|
|
636 for (d=a; d<z && p>=0; d++) {
|
|
|
637 char *s = fmt_u(*d, buf+9);
|
|
|
638 if (s==buf+9) *--s='0';
|
|
|
639 if (d!=a) while (s>buf) *--s='0';
|
|
|
640 else {
|
|
|
641 out(put, opaque, s++, 1, num);
|
|
|
642 if (p>0||(fl&ALT_FORM)) out(put, opaque, ".", 1, num);
|
|
|
643 }
|
|
|
644 out(put, opaque, s, MIN(buf+9-s, p), num);
|
|
|
645 p -= buf+9-s;
|
|
|
646 }
|
|
|
647 pad(put, opaque, '0', p+18, 18, 0, num);
|
|
|
648 out(put, opaque, estr, ebuf-estr, num);
|
|
|
649 }
|
|
|
650
|
|
|
651 pad(put, opaque, ' ', w, pl+l, fl^LEFT_ADJ, num);
|
|
|
652
|
|
|
653 return MAX(w, pl+l);
|
|
|
654 }
|
|
|
655 #endif
|
|
|
656
|
|
|
657 /* ------------------------------------------------------------------------ */
|
|
|
658 /* NUMBER PARSER */
|
|
|
659
|
|
|
660 static void parse_num(my_printf_uintmax *pnum, const char **pfmt)
|
|
|
661 {
|
|
|
662 /* Width */
|
|
|
663 (*pnum) = 0;
|
|
|
664 while (**pfmt >= '0' && **pfmt <= '9') {
|
|
|
665 /* Parse width */
|
|
|
666 (*pnum) *= 10;
|
|
|
667 (*pnum) += **pfmt - '0';
|
|
|
668
|
|
|
669 (*pfmt)++;
|
|
|
670 }
|
|
|
671 }
|
|
|
672
|
|
|
673 /* ------------------------------------------------------------------------ */
|
|
|
674
|
|
|
675 /* required for extrmeely scuffed %ls handling */
|
|
|
676 int my_iprintf(put_spec put, void *opaque, const char *format, ...);
|
|
|
677
|
|
|
678 int my_viprintf(put_spec put, void *opaque, const char *format, va_list ap)
|
|
|
679 {
|
|
|
680 const char *fmt = format;
|
|
|
681 my_printf_uintmax num;
|
|
|
682
|
|
|
683 num = 0;
|
|
|
684
|
|
|
685 while (*fmt) {
|
|
|
686 if (fmt[0] == '%' && fmt[1] == '%') {
|
|
|
687 put(opaque, '%');
|
|
|
688 num++;
|
|
|
689 fmt += 2;
|
|
|
690 } else if (*fmt == '%') {
|
|
|
691 my_printf_uintmax width, precision;
|
|
|
692 int have_precision;
|
|
|
693 enum flags flags = 0;
|
|
|
694 enum {
|
|
|
695 TYPE_NONE,
|
|
|
696 TYPE_hh,
|
|
|
697 TYPE_h,
|
|
|
698 TYPE_ll,
|
|
|
699 TYPE_l,
|
|
|
700 TYPE_j,
|
|
|
701 TYPE_z,
|
|
|
702 TYPE_t,
|
|
|
703 TYPE_L
|
|
|
704 } type = TYPE_NONE;
|
|
|
705
|
|
|
706 fmt++;
|
|
|
707
|
|
|
708 /* Flags */
|
|
|
709 while (*fmt) {
|
|
|
710 int end = 0;
|
|
|
711
|
|
|
712 switch (*fmt) {
|
|
|
713 case '-':
|
|
|
714 fmt++;
|
|
|
715 flags |= FLAG_JUSTIFY_LEFT;
|
|
|
716 /* Left justify (unimplemented) */
|
|
|
717 break;
|
|
|
718 case '+':
|
|
|
719 fmt++;
|
|
|
720 flags |= FLAG_PLUS;
|
|
|
721 /* Forces plus/minus sign */
|
|
|
722 break;
|
|
|
723 case ' ':
|
|
|
724 fmt++;
|
|
|
725 /* If no negative sign, add a space */
|
|
|
726 flags |= FLAG_SPACE;
|
|
|
727 break;
|
|
|
728 case '#':
|
|
|
729 fmt++;
|
|
|
730 /* For hex and octal specifiers, this prepends with
|
|
|
731 * the C literal notation (such as 0x08 for "%#x", 8) */
|
|
|
732 flags |= FLAG_HASH;
|
|
|
733 break;
|
|
|
734 case '0':
|
|
|
735 fmt++;
|
|
|
736 /* Left-pads the number with zeroes */
|
|
|
737 flags |= FLAG_ZERO;
|
|
|
738 break;
|
|
|
739 default:
|
|
|
740 end = 1;
|
|
|
741 break;
|
|
|
742 }
|
|
|
743
|
|
|
744 if (end) break;
|
|
|
745 }
|
|
|
746
|
|
|
747 /* Width */
|
|
|
748 parse_num(&width, &fmt);
|
|
|
749
|
|
|
750 /* Precision */
|
|
|
751 if (*fmt == '.') {
|
|
|
752 have_precision = 1;
|
|
|
753 fmt++;
|
|
|
754 if (*fmt == '*') {
|
|
|
755 /* Precision is in the va_list */
|
|
|
756 precision = va_arg(ap, int);
|
|
|
757 fmt++;
|
|
|
758 } else {
|
|
|
759 /* NOTE this was not intentional, but we actually handle this right.
|
|
|
760 *
|
|
|
761 * "If the period is specified without an explicit value for precision,
|
|
|
762 * 0 is assumed." */
|
|
|
763 parse_num(&precision, &fmt);
|
|
|
764 }
|
|
|
765 } else {
|
|
|
766 have_precision = 0;
|
|
|
767 }
|
|
|
768
|
|
|
769 /* Length specifier (could be condensed to a switch) */
|
|
|
770 switch (*fmt) {
|
|
|
771 case 'h':
|
|
|
772 fmt++;
|
|
|
773 if (*fmt == 'h') {
|
|
|
774 /* char */
|
|
|
775 fmt++;
|
|
|
776 type = TYPE_hh;
|
|
|
777 } else {
|
|
|
778 /* short */
|
|
|
779 type = TYPE_h;
|
|
|
780 }
|
|
|
781 break;
|
|
|
782 case 'l':
|
|
|
783 fmt++;
|
|
|
784 if (*fmt == 'l') {
|
|
|
785 /* long long */
|
|
|
786 fmt++;
|
|
|
787 type = TYPE_ll;
|
|
|
788 } else {
|
|
|
789 /* long, wint_t (c), and wchar_t * (s) */
|
|
|
790 type = TYPE_l;
|
|
|
791 }
|
|
|
792 break;
|
|
|
793 case 'j':
|
|
|
794 /* intmax_t */
|
|
|
795 fmt++;
|
|
|
796 type = TYPE_j;
|
|
|
797 break;
|
|
|
798 case 'z':
|
|
|
799 /* size_t */
|
|
|
800 fmt++;
|
|
|
801 type = TYPE_z;
|
|
|
802 break;
|
|
|
803 case 't':
|
|
|
804 /* ptrdiff_t */
|
|
|
805 fmt++;
|
|
|
806 type = TYPE_t;
|
|
|
807 break;
|
|
|
808 case 'L':
|
|
|
809 /* long double */
|
|
|
810 fmt++;
|
|
|
811 type = TYPE_L;
|
|
|
812 break;
|
|
|
813 }
|
|
|
814
|
|
|
815 #define TYPE_CASE(TYPE, SIGN, T, X) \
|
|
|
816 case TYPE: \
|
|
|
817 d = (T)va_arg(ap, X); \
|
|
|
818 break;
|
|
|
819
|
|
|
820 #ifdef HAVE_LONG_LONG
|
|
|
821 # define LONG_LONG_CASE(sign) \
|
|
|
822 TYPE_CASE(TYPE_ll, sign, sign long long, long long)
|
|
|
823 #else
|
|
|
824 # define LONG_LONG_CASE(sign)
|
|
|
825 #endif
|
|
|
826
|
|
|
827 #ifdef HAVE_INTMAX_T
|
|
|
828 # define INTMAX_T_CASE(sign) \
|
|
|
829 TYPE_CASE(TYPE_j, sign, intmax_t, intmax_t)
|
|
|
830 #else
|
|
|
831 # define INTMAX_T_CASE(sign)
|
|
|
832 #endif
|
|
|
833
|
|
|
834 #ifdef HAVE_SIZE_T
|
|
|
835 # define SIZE_T_CASE(sign) \
|
|
|
836 case TYPE_z: \
|
|
|
837 d = my_size_t_sign(va_arg(ap, size_t)); \
|
|
|
838 break;
|
|
|
839 #else
|
|
|
840 # define SIZE_T_CASE(sign)
|
|
|
841 #endif
|
|
|
842
|
|
|
843 #ifdef HAVE_PTRDIFF_T
|
|
|
844 # define PTRDIFF_T_CASE(sign) \
|
|
|
845 TYPE_CASE(TYPE_t, sign, ptrdiff_t, ptrdiff_t)
|
|
|
846 #else
|
|
|
847 # define PTRDIFF_T_CASE(sign)
|
|
|
848 #endif
|
|
|
849
|
|
|
850 /* For numbers, these are basically all the same besides the sign.
|
|
|
851 * Note that we always(-ish) interpret numbers as signed, to prevent
|
|
|
852 * signed integer overflow (which is undefined behavior). The problem
|
|
|
853 * here is that if we, say, pass a size_t, there is no signed equivalent.
|
|
|
854 * If someone were to pass a size_t that's bigger than INTMAX_MAX, then
|
|
|
855 * we'll be in big trouble. But who's even going to use %zd anyway??
|
|
|
856 *
|
|
|
857 * NOTE: ubsan doesn't scream at us when we do that. So I think it's
|
|
|
858 * probably fine. */
|
|
|
859 #define TYPE_SWITCH(sign) \
|
|
|
860 switch (type) { \
|
|
|
861 TYPE_CASE(TYPE_hh, sign, sign char, int) \
|
|
|
862 TYPE_CASE(TYPE_h, sign, sign short, int) \
|
|
|
863 TYPE_CASE(TYPE_NONE, sign, sign int, int) \
|
|
|
864 TYPE_CASE(TYPE_l, sign, sign long, long) \
|
|
|
865 LONG_LONG_CASE(sign) \
|
|
|
866 INTMAX_T_CASE(sign) \
|
|
|
867 PTRDIFF_T_CASE(sign) \
|
|
|
868 SIZE_T_CASE(sign) \
|
|
|
869 default: \
|
|
|
870 /* Bad type specifier */ \
|
|
|
871 return MY_EINVAL; \
|
|
|
872 }
|
|
|
873
|
|
|
874 switch (*fmt) {
|
|
|
875 case 'd': {
|
|
|
876 /* Need special handling for some signed shit, so we can't
|
|
|
877 * do my_numput exactly.
|
|
|
878 *
|
|
|
879 * This is also the only place this needs to be... */
|
|
|
880 my_printf_intmax d;
|
|
|
881 my_printf_uintmax ad;
|
|
|
882 my_printf_uintmax len;
|
|
|
883
|
|
|
884 TYPE_SWITCH(signed)
|
|
|
885
|
|
|
886 /* TODO handle width specifiers */
|
|
|
887
|
|
|
888 /* inline llabs with defined behavior for LLONG_MIN */
|
|
|
889 ad = (d < 0) ? (~(my_printf_uintmax)d + 1) : d;
|
|
|
890
|
|
|
891 if (width > 0) {
|
|
|
892 len = my_numput_width(ad, 10);
|
|
|
893
|
|
|
894 if ((d < 0) || (flags & FLAG_PLUS) || (flags & FLAG_SPACE))
|
|
|
895 len++;
|
|
|
896
|
|
|
897 if (!(flags & (FLAG_JUSTIFY_LEFT|FLAG_ZERO)) && len + 1 < width) {
|
|
|
898 print_chars(put, opaque, &num, ' ', width - len - 1);
|
|
|
899 }
|
|
|
900
|
|
|
901 if (d < 0) {
|
|
|
902 put(opaque, '-');
|
|
|
903 num++;
|
|
|
904 } else if (flags & FLAG_PLUS) {
|
|
|
905 put(opaque, '+');
|
|
|
906 num++;
|
|
|
907 } else if (flags & FLAG_SPACE) {
|
|
|
908 put(opaque, ' ');
|
|
|
909 num++;
|
|
|
910 }
|
|
|
911
|
|
|
912 if ((flags & FLAG_ZERO) && len + 1 < width)
|
|
|
913 print_chars(put, opaque, &num, '0', width - len - 1);
|
|
|
914
|
|
|
915 my_numput_lower(put, opaque, ad, 10, &num);
|
|
|
916
|
|
|
917 if ((flags & FLAG_JUSTIFY_LEFT) && len + 1 < width) {
|
|
|
918 print_spaces(put, opaque, &num, width - len - 1);
|
|
|
919 }
|
|
|
920 } else {
|
|
|
921 /* Faster */
|
|
|
922 if (d < 0) {
|
|
|
923 put(opaque, '-');
|
|
|
924 num++;
|
|
|
925 } else if (flags & FLAG_PLUS) {
|
|
|
926 put(opaque, '+');
|
|
|
927 num++;
|
|
|
928 } else if (flags & FLAG_SPACE) {
|
|
|
929 put(opaque, ' ');
|
|
|
930 num++;
|
|
|
931 }
|
|
|
932
|
|
|
933 my_numput_lower(put, opaque, ad, 10, &num);
|
|
|
934 }
|
|
|
935
|
|
|
936 fmt++;
|
|
|
937
|
|
|
938 break;
|
|
|
939 }
|
|
|
940 case 'u': {
|
|
|
941 my_printf_uintmax d;
|
|
|
942 TYPE_SWITCH(unsigned)
|
|
|
943 my_numput(put, opaque, d, 10, &num, 0, NULL, 0,
|
|
|
944 width, flags & FLAG_JUSTIFY_LEFT, flags & FLAG_ZERO, 0);
|
|
|
945 fmt++;
|
|
|
946 break;
|
|
|
947 }
|
|
|
948 case 'x': {
|
|
|
949 my_printf_uintmax d;
|
|
|
950 TYPE_SWITCH(unsigned)
|
|
|
951
|
|
|
952 /* stinky */
|
|
|
953 my_numput(put, opaque, d, 16, &num, flags & FLAG_HASH, "0x", 2,
|
|
|
954 width, flags & FLAG_JUSTIFY_LEFT, flags & FLAG_ZERO, 0);
|
|
|
955
|
|
|
956 fmt++;
|
|
|
957 break;
|
|
|
958 }
|
|
|
959 case 'X': {
|
|
|
960 my_printf_uintmax d;
|
|
|
961 TYPE_SWITCH(unsigned)
|
|
|
962
|
|
|
963 /* stinky */
|
|
|
964 my_numput(put, opaque, d, 16, &num, flags & FLAG_HASH, "0X", 2,
|
|
|
965 width, flags & FLAG_JUSTIFY_LEFT, flags & FLAG_ZERO, 1);
|
|
|
966
|
|
|
967 fmt++;
|
|
|
968 break;
|
|
|
969 }
|
|
|
970 case 'o': {
|
|
|
971 my_printf_uintmax d;
|
|
|
972 TYPE_SWITCH(unsigned)
|
|
|
973 /* stinky */
|
|
|
974 my_numput(put, opaque, d, 8, &num, flags & FLAG_HASH, "0", 1,
|
|
|
975 width, flags & FLAG_JUSTIFY_LEFT, flags & FLAG_ZERO, 0);
|
|
|
976 fmt++;
|
|
|
977 break;
|
|
|
978 }
|
|
|
979
|
|
|
980 #undef TYPE_CASE
|
|
|
981 #undef LONG_LONG_CASE
|
|
|
982 #undef INTMAX_T_CASE
|
|
|
983 #undef SIZE_T_CASE
|
|
|
984 #undef PTRDIFF_T_CASE
|
|
|
985 #undef TYPE_SWITCH
|
|
|
986
|
|
|
987 #ifdef HAVE_FLOATING_POINT
|
|
|
988 case 'a': case 'A': case 'e': case 'E':
|
|
|
989 case 'G': case 'g': case 'F': case 'f': {
|
|
|
990 /* balls */
|
|
|
991 floatmax d;
|
|
|
992 switch (type) {
|
|
|
993 #ifdef HAVE_LONG_DOUBLE
|
|
|
994 case TYPE_L:
|
|
|
995 d = va_arg(ap, long double);
|
|
|
996 break;
|
|
|
997 #endif
|
|
|
998 case TYPE_NONE:
|
|
|
999 d = va_arg(ap, double);
|
|
|
1000 break;
|
|
|
1001 default:
|
|
|
1002 /* Invalid */
|
|
|
1003 return MY_EINVAL;
|
|
|
1004 }
|
|
|
1005 if (!have_precision) precision = 6;
|
|
|
1006 my_floatput(put, opaque, d, width, precision, flags, &num, *fmt);
|
|
|
1007 fmt++;
|
|
|
1008 break;
|
|
|
1009 }
|
|
|
1010 #endif
|
|
|
1011 case 's': {
|
|
|
1012 switch (type) {
|
|
|
1013 case TYPE_l: {
|
|
|
1014 wchar_t *s;
|
|
|
1015 s = va_arg(ap, wchar_t *);
|
|
|
1016
|
|
|
1017 /* This stinks and isn't right for width nor precision */
|
|
|
1018 while (*s && (!have_precision || precision-- > 0)) {
|
|
|
1019 my_iprintf(put, opaque, "%lc", *(s++));
|
|
|
1020 num++;
|
|
|
1021 }
|
|
|
1022
|
|
|
1023 break;
|
|
|
1024 }
|
|
|
1025 case TYPE_NONE: {
|
|
|
1026 char *s;
|
|
|
1027 my_printf_size len;
|
|
|
1028
|
|
|
1029 s = va_arg(ap, char *);
|
|
|
1030
|
|
|
1031 if (width > 0) {
|
|
|
1032 if (have_precision) {
|
|
|
1033 /* Use strnlen to avoid overflow */
|
|
|
1034 len = my_strnlen(s, precision);
|
|
|
1035 } else {
|
|
|
1036 len = my_strlen(s);
|
|
|
1037 }
|
|
|
1038
|
|
|
1039 putstring(put, opaque, s, len, &num, width, !!(flags & FLAG_JUSTIFY_LEFT));
|
|
|
1040 } else {
|
|
|
1041 /* Simpler implementation (only one pass of the string) */
|
|
|
1042 if (have_precision) {
|
|
|
1043 while (*s && precision-- > 0) {
|
|
|
1044 put(opaque, *(s++));
|
|
|
1045 num++;
|
|
|
1046 }
|
|
|
1047 } else {
|
|
|
1048 while (*s) {
|
|
|
1049 put(opaque, *(s++));
|
|
|
1050 num++;
|
|
|
1051 }
|
|
|
1052 }
|
|
|
1053 }
|
|
|
1054 break;
|
|
|
1055 }
|
|
|
1056 default:
|
|
|
1057 /* Invalid */
|
|
|
1058 return MY_EINVAL;
|
|
|
1059 }
|
|
|
1060
|
|
|
1061 fmt++;
|
|
|
1062 break;
|
|
|
1063 }
|
|
|
1064 case 'c': {
|
|
|
1065 switch (type) {
|
|
|
1066 #ifdef HAVE_WCTOMB
|
|
|
1067 case TYPE_l: {
|
|
|
1068 /* XXX this doesn't work on Windows. */
|
|
|
1069 char *c;
|
|
|
1070
|
|
|
1071 /* TODO would make more sense to move the full buffer
|
|
|
1072 * handling stuff out of here, instead of having it all
|
|
|
1073 * intermixed like this */
|
|
|
1074 c = my_calloc(MB_CUR_MAX + 1, 1);
|
|
|
1075 if (!c)
|
|
|
1076 return MY_ENOMEM;
|
|
|
1077
|
|
|
1078 /* handle wide char -> multi byte */
|
|
|
1079 if (wctomb(c, va_arg(ap, wint_t)) == -1) {
|
|
|
1080 my_free(c);
|
|
|
1081 return MY_EENCOD;
|
|
|
1082 }
|
|
|
1083 putstring(put, opaque, c, my_strlen(c), &num, width, !!(flags & FLAG_JUSTIFY_LEFT));
|
|
|
1084 my_free(c);
|
|
|
1085 break;
|
|
|
1086 }
|
|
|
1087 #endif
|
|
|
1088 case TYPE_NONE: {
|
|
|
1089 /* Balls simple */
|
|
|
1090 char c = (char)va_arg(ap, int);
|
|
|
1091 putstring(put, opaque, &c, 1, &num, width, !!(flags & FLAG_JUSTIFY_LEFT));
|
|
|
1092 break;
|
|
|
1093 }
|
|
|
1094 default:
|
|
|
1095 /* Invalid type */
|
|
|
1096 return MY_EINVAL;
|
|
|
1097 }
|
|
|
1098
|
|
|
1099 fmt++;
|
|
|
1100 break;
|
|
|
1101 }
|
|
|
1102 case 'n': {
|
|
|
1103 #define STORE(TYPE, type) case TYPE: *va_arg(ap, type *) = num; break;
|
|
|
1104
|
|
|
1105 switch (type) {
|
|
|
1106 STORE(TYPE_hh, signed char)
|
|
|
1107 STORE(TYPE_h, short)
|
|
|
1108 STORE(TYPE_NONE, int)
|
|
|
1109 STORE(TYPE_l, long)
|
|
|
1110 #ifdef HAVE_LONG_LONG
|
|
|
1111 STORE(TYPE_ll, long long)
|
|
|
1112 #endif
|
|
|
1113 #ifdef HAVE_INTMAX_T
|
|
|
1114 STORE(TYPE_j, intmax_t)
|
|
|
1115 #endif
|
|
|
1116 #ifdef HAVE_SIZE_T
|
|
|
1117 STORE(TYPE_z, size_t)
|
|
|
1118 #endif
|
|
|
1119 #ifdef HAVE_PTRDIFF_T
|
|
|
1120 STORE(TYPE_t, ptrdiff_t)
|
|
|
1121 #endif
|
|
|
1122 default:
|
|
|
1123 return -1;
|
|
|
1124 }
|
|
|
1125 #undef STORE
|
|
|
1126
|
|
|
1127 fmt++;
|
|
|
1128 break;
|
|
|
1129 }
|
|
|
1130 case 'p': {
|
|
|
1131 /* This can pretty much be whatever we want, but I'm going to replicate
|
|
|
1132 * the glibc result. */
|
|
|
1133 void *x;
|
|
|
1134
|
|
|
1135 x = va_arg(ap, void *);
|
|
|
1136
|
|
|
1137 put(opaque, '0');
|
|
|
1138 put(opaque, 'x');
|
|
|
1139 num += 2;
|
|
|
1140
|
|
|
1141 my_numput_lower(put, opaque, (my_printf_uintptr)x, 16, &num);
|
|
|
1142 fmt++;
|
|
|
1143 break;
|
|
|
1144 }
|
|
|
1145 default:
|
|
|
1146 /* Invalid/unknown selector */
|
|
|
1147 return MY_EINVAL;
|
|
|
1148 }
|
|
|
1149 } else {
|
|
|
1150 /* Just append the character. */
|
|
|
1151 put(opaque, *(fmt++));
|
|
|
1152 num++;
|
|
|
1153 }
|
|
|
1154 }
|
|
|
1155
|
|
|
1156 if (num > (my_printf_uintmax)INT_MAX)
|
|
|
1157 return MY_EOVERFLOW;
|
|
|
1158
|
|
|
1159 return num;
|
|
|
1160 }
|
|
|
1161
|
|
|
1162 int my_iprintf(put_spec put, void *opaque, const char *format, ...)
|
|
|
1163 {
|
|
|
1164 va_list ap;
|
|
|
1165 int r;
|
|
|
1166
|
|
|
1167 va_start(ap, format);
|
|
|
1168 r = my_viprintf(put, opaque, format, ap);
|
|
|
1169 va_end(ap);
|
|
|
1170
|
|
|
1171 return r;
|
|
|
1172 }
|
|
|
1173
|
|
|
1174 /* ------------------------------------------------------------------------ */
|
|
|
1175 /* vsnprintf */
|
|
|
1176
|
|
|
1177 struct put_vsnprintf_data {
|
|
|
1178 char *out;
|
|
|
1179 my_printf_size n;
|
|
|
1180 int c;
|
|
|
1181 };
|
|
|
1182
|
|
|
1183 static void put_vsnprintf(void *opaque, unsigned char c)
|
|
|
1184 {
|
|
|
1185 struct put_vsnprintf_data *d = opaque;
|
|
|
1186
|
|
|
1187 if (d->out && d->c < d->n)
|
|
|
1188 d->out[d->c] = c;
|
|
|
1189
|
|
|
1190 d->c++;
|
|
|
1191 }
|
|
|
1192
|
|
|
1193 int my_vsnprintf(char *out, my_printf_size n, const char *fmt, va_list ap)
|
|
|
1194 {
|
|
|
1195 int r;
|
|
|
1196 struct put_vsnprintf_data d;
|
|
|
1197
|
|
|
1198 d.out = out;
|
|
|
1199 d.n = n;
|
|
|
1200 d.c = 0;
|
|
|
1201
|
|
|
1202 r = my_viprintf(put_vsnprintf, &d, fmt, ap);
|
|
|
1203
|
|
|
1204 /* case: returned size is smaller than buffer. */
|
|
|
1205 put_vsnprintf(&d, 0);
|
|
|
1206
|
|
|
1207 /* case: returned size is larger than buffer.
|
|
|
1208 * we still have to NUL terminate. */
|
|
|
1209 if (out && n > 0)
|
|
|
1210 out[n-1] = 0; /* NUL terminate */
|
|
|
1211
|
|
|
1212 if (r < 0)
|
|
|
1213 return r;
|
|
|
1214
|
|
|
1215 /* subtract one because of NUL termination above */
|
|
|
1216 return d.c - 1;
|
|
|
1217 }
|
|
|
1218
|
|
|
1219 int my_snprintf(char *out, my_printf_size n, const char *fmt, ...)
|
|
|
1220 {
|
|
|
1221 int r;
|
|
|
1222 va_list ap;
|
|
|
1223
|
|
|
1224 va_start(ap, fmt);
|
|
|
1225 r = my_vsnprintf(out, n, fmt, ap);
|
|
|
1226 va_end(ap);
|
|
|
1227
|
|
|
1228 return r;
|
|
|
1229 }
|
|
|
1230
|
|
|
1231 /* ------------------------------------------------------------------------ */
|
|
|
1232 /* vasprintf
|
|
|
1233 * we do this in one pass, instead of two (like the usual vsnprintf impl)
|
|
|
1234 * why? well, it's a bit easier to implement, and doesn't require va_copy. */
|
|
|
1235
|
|
|
1236 struct put_vasprintf_data {
|
|
|
1237 char *x;
|
|
|
1238 my_printf_size len;
|
|
|
1239 my_printf_size alloc;
|
|
|
1240
|
|
|
1241 int dead;
|
|
|
1242 };
|
|
|
1243
|
|
|
1244 static void put_vasprintf(void *opaque, unsigned char c)
|
|
|
1245 {
|
|
|
1246 struct put_vasprintf_data *d = opaque;
|
|
|
1247
|
|
|
1248 if (d->dead)
|
|
|
1249 return;
|
|
|
1250
|
|
|
1251 if (!d->x || d->len >= d->alloc) {
|
|
|
1252 char *old;
|
|
|
1253
|
|
|
1254 /* make a guesstimate */
|
|
|
1255 if (!d->alloc) {
|
|
|
1256 #ifdef TEST
|
|
|
1257 /* Stress test reallocation */
|
|
|
1258 d->alloc = 2;
|
|
|
1259 #else
|
|
|
1260 d->alloc = 128;
|
|
|
1261 #endif
|
|
|
1262 } else {
|
|
|
1263 d->alloc *= 2;
|
|
|
1264 }
|
|
|
1265
|
|
|
1266 old = d->x;
|
|
|
1267 d->x = my_realloc(old, d->alloc);
|
|
|
1268 if (!d->x) {
|
|
|
1269 my_free(old);
|
|
|
1270 d->dead = 1;
|
|
|
1271 return;
|
|
|
1272 }
|
|
|
1273 }
|
|
|
1274
|
|
|
1275 d->x[d->len++] = c;
|
|
|
1276 }
|
|
|
1277
|
|
|
1278 int my_vasprintf(char **out, const char *fmt, va_list ap)
|
|
|
1279 {
|
|
|
1280 int r;
|
|
|
1281 struct put_vasprintf_data d;
|
|
|
1282
|
|
|
1283 d.x = NULL;
|
|
|
1284 d.len = 0;
|
|
|
1285 d.alloc = 0;
|
|
|
1286 d.dead = 0;
|
|
|
1287
|
|
|
1288 r = my_viprintf(put_vasprintf, &d, fmt, ap);
|
|
|
1289
|
|
|
1290 if (d.dead) /* memory allocation failed, punt */
|
|
|
1291 return MY_ENOMEM;
|
|
|
1292
|
|
|
1293 /* Add NUL terminator */
|
|
|
1294 put_vasprintf(&d, 0);
|
|
|
1295
|
|
|
1296 if (d.x) {
|
|
|
1297 /* Trim the fat
|
|
|
1298 * Even if the realloc calls fails, this is still okay
|
|
|
1299 * because we still have the original allocation */
|
|
|
1300 void *x = my_realloc(d.x, d.len);
|
|
|
1301 if (x)
|
|
|
1302 d.x = x;
|
|
|
1303 }
|
|
|
1304
|
|
|
1305 if (r >= 0)
|
|
|
1306 *out = d.x;
|
|
|
1307
|
|
|
1308 return r;
|
|
|
1309 }
|
|
|
1310
|
|
|
1311 int my_asprintf(char **out, const char *fmt, ...)
|
|
|
1312 {
|
|
|
1313 int r;
|
|
|
1314 va_list ap;
|
|
|
1315
|
|
|
1316 va_start(ap, fmt);
|
|
|
1317 r = my_vasprintf(out, fmt, ap);
|
|
|
1318 va_end(ap);
|
|
|
1319
|
|
|
1320 return r;
|
|
|
1321 }
|
|
|
1322
|
|
|
1323 /* ------------------------------------------------------------------------ */
|
|
|
1324 /* Finally, functions for outputting to stdio. */
|
|
|
1325
|
|
|
1326 static void put_fprintf(void *opaque, unsigned char c)
|
|
|
1327 {
|
|
|
1328 putc(c, opaque);
|
|
|
1329 }
|
|
|
1330
|
|
|
1331 int my_vfprintf(FILE *f, const char *fmt, va_list ap)
|
|
|
1332 {
|
|
|
1333 return my_viprintf(put_fprintf, f, fmt, ap);
|
|
|
1334 }
|
|
|
1335
|
|
|
1336 int my_fprintf(FILE *f, const char *fmt, ...)
|
|
|
1337 {
|
|
|
1338 int r;
|
|
|
1339 va_list ap;
|
|
|
1340
|
|
|
1341 va_start(ap, fmt);
|
|
|
1342 r = my_viprintf(put_fprintf, f, fmt, ap);
|
|
|
1343 va_end(ap);
|
|
|
1344
|
|
|
1345 return r;
|
|
|
1346 }
|
|
|
1347
|
|
|
1348 /* ------------------------------------------------------------------------ */
|
|
|
1349
|
|
|
1350 int my_vprintf(const char *fmt, va_list ap)
|
|
|
1351 {
|
|
|
1352 return my_vfprintf(stdout, fmt, ap);
|
|
|
1353 }
|
|
|
1354
|
|
|
1355 int my_printf(const char *fmt, ...)
|
|
|
1356 {
|
|
|
1357 int r;
|
|
|
1358 va_list ap;
|
|
|
1359
|
|
|
1360 va_start(ap, fmt);
|
|
|
1361 r = my_vprintf(fmt, ap);
|
|
|
1362 va_end(ap);
|
|
|
1363
|
|
|
1364 return r;
|
|
|
1365 }
|