Mercurial > printf
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 } |
