comparison printf.c @ 0:e3088565a6b8 default tip

*: initial commit kinda dumb, but wifi was out and I was bored. most of this code is shit.
author Paper <paper@tflc.us>
date Wed, 03 Dec 2025 03:04:39 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:e3088565a6b8
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 }