diff options
Diffstat (limited to 'pintos-progos/lib/stdio.c')
| -rw-r--r-- | pintos-progos/lib/stdio.c | 655 |
1 files changed, 655 insertions, 0 deletions
diff --git a/pintos-progos/lib/stdio.c b/pintos-progos/lib/stdio.c new file mode 100644 index 0000000..8927c50 --- /dev/null +++ b/pintos-progos/lib/stdio.c | |||
| @@ -0,0 +1,655 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <ctype.h> | ||
| 3 | #include <inttypes.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | #include <string.h> | ||
| 7 | |||
| 8 | /* Auxiliary data for vsnprintf_helper(). */ | ||
| 9 | struct vsnprintf_aux | ||
| 10 | { | ||
| 11 | char *p; /* Current output position. */ | ||
| 12 | int length; /* Length of output string. */ | ||
| 13 | int max_length; /* Max length of output string. */ | ||
| 14 | }; | ||
| 15 | |||
| 16 | static void vsnprintf_helper (char, void *); | ||
| 17 | |||
| 18 | /* Like vprintf(), except that output is stored into BUFFER, | ||
| 19 | which must have space for BUF_SIZE characters. Writes at most | ||
| 20 | BUF_SIZE - 1 characters to BUFFER, followed by a null | ||
| 21 | terminator. BUFFER will always be null-terminated unless | ||
| 22 | BUF_SIZE is zero. Returns the number of characters that would | ||
| 23 | have been written to BUFFER, not including a null terminator, | ||
| 24 | had there been enough room. */ | ||
| 25 | int | ||
| 26 | vsnprintf (char *buffer, size_t buf_size, const char *format, va_list args) | ||
| 27 | { | ||
| 28 | /* Set up aux data for vsnprintf_helper(). */ | ||
| 29 | struct vsnprintf_aux aux; | ||
| 30 | aux.p = buffer; | ||
| 31 | aux.length = 0; | ||
| 32 | aux.max_length = buf_size > 0 ? buf_size - 1 : 0; | ||
| 33 | |||
| 34 | /* Do most of the work. */ | ||
| 35 | __vprintf (format, args, vsnprintf_helper, &aux); | ||
| 36 | |||
| 37 | /* Add null terminator. */ | ||
| 38 | if (buf_size > 0) | ||
| 39 | *aux.p = '\0'; | ||
| 40 | |||
| 41 | return aux.length; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* Helper function for vsnprintf(). */ | ||
| 45 | static void | ||
| 46 | vsnprintf_helper (char ch, void *aux_) | ||
| 47 | { | ||
| 48 | struct vsnprintf_aux *aux = aux_; | ||
| 49 | |||
| 50 | if (aux->length++ < aux->max_length) | ||
| 51 | *aux->p++ = ch; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Like printf(), except that output is stored into BUFFER, | ||
| 55 | which must have space for BUF_SIZE characters. Writes at most | ||
| 56 | BUF_SIZE - 1 characters to BUFFER, followed by a null | ||
| 57 | terminator. BUFFER will always be null-terminated unless | ||
| 58 | BUF_SIZE is zero. Returns the number of characters that would | ||
| 59 | have been written to BUFFER, not including a null terminator, | ||
| 60 | had there been enough room. */ | ||
| 61 | int | ||
| 62 | snprintf (char *buffer, size_t buf_size, const char *format, ...) | ||
| 63 | { | ||
| 64 | va_list args; | ||
| 65 | int retval; | ||
| 66 | |||
| 67 | va_start (args, format); | ||
| 68 | retval = vsnprintf (buffer, buf_size, format, args); | ||
| 69 | va_end (args); | ||
| 70 | |||
| 71 | return retval; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Writes formatted output to the console. | ||
| 75 | In the kernel, the console is both the video display and first | ||
| 76 | serial port. | ||
| 77 | In userspace, the console is file descriptor 1. */ | ||
| 78 | int | ||
| 79 | printf (const char *format, ...) | ||
| 80 | { | ||
| 81 | va_list args; | ||
| 82 | int retval; | ||
| 83 | |||
| 84 | va_start (args, format); | ||
| 85 | retval = vprintf (format, args); | ||
| 86 | va_end (args); | ||
| 87 | |||
| 88 | return retval; | ||
| 89 | } | ||
| 90 | |||
| 91 | /* printf() formatting internals. */ | ||
| 92 | |||
| 93 | /* A printf() conversion. */ | ||
| 94 | struct printf_conversion | ||
| 95 | { | ||
| 96 | /* Flags. */ | ||
| 97 | enum | ||
| 98 | { | ||
| 99 | MINUS = 1 << 0, /* '-' */ | ||
| 100 | PLUS = 1 << 1, /* '+' */ | ||
| 101 | SPACE = 1 << 2, /* ' ' */ | ||
| 102 | POUND = 1 << 3, /* '#' */ | ||
| 103 | ZERO = 1 << 4, /* '0' */ | ||
| 104 | GROUP = 1 << 5 /* '\'' */ | ||
| 105 | } | ||
| 106 | flags; | ||
| 107 | |||
| 108 | /* Minimum field width. */ | ||
| 109 | int width; | ||
| 110 | |||
| 111 | /* Numeric precision. | ||
| 112 | -1 indicates no precision was specified. */ | ||
| 113 | int precision; | ||
| 114 | |||
| 115 | /* Type of argument to format. */ | ||
| 116 | enum | ||
| 117 | { | ||
| 118 | CHAR = 1, /* hh */ | ||
| 119 | SHORT = 2, /* h */ | ||
| 120 | INT = 3, /* (none) */ | ||
| 121 | INTMAX = 4, /* j */ | ||
| 122 | LONG = 5, /* l */ | ||
| 123 | LONGLONG = 6, /* ll */ | ||
| 124 | PTRDIFFT = 7, /* t */ | ||
| 125 | SIZET = 8 /* z */ | ||
| 126 | } | ||
| 127 | type; | ||
| 128 | }; | ||
| 129 | |||
| 130 | struct integer_base | ||
| 131 | { | ||
| 132 | int base; /* Base. */ | ||
| 133 | const char *digits; /* Collection of digits. */ | ||
| 134 | int x; /* `x' character to use, for base 16 only. */ | ||
| 135 | int group; /* Number of digits to group with ' flag. */ | ||
| 136 | }; | ||
| 137 | |||
| 138 | static const struct integer_base base_d = {10, "0123456789", 0, 3}; | ||
| 139 | static const struct integer_base base_o = {8, "01234567", 0, 3}; | ||
| 140 | static const struct integer_base base_x = {16, "0123456789abcdef", 'x', 4}; | ||
| 141 | static const struct integer_base base_X = {16, "0123456789ABCDEF", 'X', 4}; | ||
| 142 | |||
| 143 | static const char *parse_conversion (const char *format, | ||
| 144 | struct printf_conversion *, | ||
| 145 | va_list *); | ||
| 146 | static void format_integer (uintmax_t value, bool is_signed, bool negative, | ||
| 147 | const struct integer_base *, | ||
| 148 | const struct printf_conversion *, | ||
| 149 | void (*output) (char, void *), void *aux); | ||
| 150 | static void output_dup (char ch, size_t cnt, | ||
| 151 | void (*output) (char, void *), void *aux); | ||
| 152 | static void format_string (const char *string, int length, | ||
| 153 | struct printf_conversion *, | ||
| 154 | void (*output) (char, void *), void *aux); | ||
| 155 | |||
| 156 | void | ||
| 157 | __vprintf (const char *format, va_list args, | ||
| 158 | void (*output) (char, void *), void *aux) | ||
| 159 | { | ||
| 160 | for (; *format != '\0'; format++) | ||
| 161 | { | ||
| 162 | struct printf_conversion c; | ||
| 163 | |||
| 164 | /* Literally copy non-conversions to output. */ | ||
| 165 | if (*format != '%') | ||
| 166 | { | ||
| 167 | output (*format, aux); | ||
| 168 | continue; | ||
| 169 | } | ||
| 170 | format++; | ||
| 171 | |||
| 172 | /* %% => %. */ | ||
| 173 | if (*format == '%') | ||
| 174 | { | ||
| 175 | output ('%', aux); | ||
| 176 | continue; | ||
| 177 | } | ||
| 178 | |||
| 179 | /* Parse conversion specifiers. */ | ||
| 180 | format = parse_conversion (format, &c, &args); | ||
| 181 | |||
| 182 | /* Do conversion. */ | ||
| 183 | switch (*format) | ||
| 184 | { | ||
| 185 | case 'd': | ||
| 186 | case 'i': | ||
| 187 | { | ||
| 188 | /* Signed integer conversions. */ | ||
| 189 | intmax_t value; | ||
| 190 | |||
| 191 | switch (c.type) | ||
| 192 | { | ||
| 193 | case CHAR: | ||
| 194 | value = (signed char) va_arg (args, int); | ||
| 195 | break; | ||
| 196 | case SHORT: | ||
| 197 | value = (short) va_arg (args, int); | ||
| 198 | break; | ||
| 199 | case INT: | ||
| 200 | value = va_arg (args, int); | ||
| 201 | break; | ||
| 202 | case INTMAX: | ||
| 203 | value = va_arg (args, intmax_t); | ||
| 204 | break; | ||
| 205 | case LONG: | ||
| 206 | value = va_arg (args, long); | ||
| 207 | break; | ||
| 208 | case LONGLONG: | ||
| 209 | value = va_arg (args, long long); | ||
| 210 | break; | ||
| 211 | case PTRDIFFT: | ||
| 212 | value = va_arg (args, ptrdiff_t); | ||
| 213 | break; | ||
| 214 | case SIZET: | ||
| 215 | value = va_arg (args, size_t); | ||
| 216 | if (value > SIZE_MAX / 2) | ||
| 217 | value = value - SIZE_MAX - 1; | ||
| 218 | break; | ||
| 219 | default: | ||
| 220 | NOT_REACHED (); | ||
| 221 | } | ||
| 222 | |||
| 223 | format_integer (value < 0 ? -value : value, | ||
| 224 | true, value < 0, &base_d, &c, output, aux); | ||
| 225 | } | ||
| 226 | break; | ||
| 227 | |||
| 228 | case 'o': | ||
| 229 | case 'u': | ||
| 230 | case 'x': | ||
| 231 | case 'X': | ||
| 232 | { | ||
| 233 | /* Unsigned integer conversions. */ | ||
| 234 | uintmax_t value; | ||
| 235 | const struct integer_base *b; | ||
| 236 | |||
| 237 | switch (c.type) | ||
| 238 | { | ||
| 239 | case CHAR: | ||
| 240 | value = (unsigned char) va_arg (args, unsigned); | ||
| 241 | break; | ||
| 242 | case SHORT: | ||
| 243 | value = (unsigned short) va_arg (args, unsigned); | ||
| 244 | break; | ||
| 245 | case INT: | ||
| 246 | value = va_arg (args, unsigned); | ||
| 247 | break; | ||
| 248 | case INTMAX: | ||
| 249 | value = va_arg (args, uintmax_t); | ||
| 250 | break; | ||
| 251 | case LONG: | ||
| 252 | value = va_arg (args, unsigned long); | ||
| 253 | break; | ||
| 254 | case LONGLONG: | ||
| 255 | value = va_arg (args, unsigned long long); | ||
| 256 | break; | ||
| 257 | case PTRDIFFT: | ||
| 258 | value = va_arg (args, ptrdiff_t); | ||
| 259 | #if UINTMAX_MAX != PTRDIFF_MAX | ||
| 260 | value &= ((uintmax_t) PTRDIFF_MAX << 1) | 1; | ||
| 261 | #endif | ||
| 262 | break; | ||
| 263 | case SIZET: | ||
| 264 | value = va_arg (args, size_t); | ||
| 265 | break; | ||
| 266 | default: | ||
| 267 | NOT_REACHED (); | ||
| 268 | } | ||
| 269 | |||
| 270 | switch (*format) | ||
| 271 | { | ||
| 272 | case 'o': b = &base_o; break; | ||
| 273 | case 'u': b = &base_d; break; | ||
| 274 | case 'x': b = &base_x; break; | ||
| 275 | case 'X': b = &base_X; break; | ||
| 276 | default: NOT_REACHED (); | ||
| 277 | } | ||
| 278 | |||
| 279 | format_integer (value, false, false, b, &c, output, aux); | ||
| 280 | } | ||
| 281 | break; | ||
| 282 | |||
| 283 | case 'c': | ||
| 284 | { | ||
| 285 | /* Treat character as single-character string. */ | ||
| 286 | char ch = va_arg (args, int); | ||
| 287 | format_string (&ch, 1, &c, output, aux); | ||
| 288 | } | ||
| 289 | break; | ||
| 290 | |||
| 291 | case 's': | ||
| 292 | { | ||
| 293 | /* String conversion. */ | ||
| 294 | const char *s = va_arg (args, char *); | ||
| 295 | if (s == NULL) | ||
| 296 | s = "(null)"; | ||
| 297 | |||
| 298 | /* Limit string length according to precision. | ||
| 299 | Note: if c.precision == -1 then strnlen() will get | ||
| 300 | SIZE_MAX for MAXLEN, which is just what we want. */ | ||
| 301 | format_string (s, strnlen (s, c.precision), &c, output, aux); | ||
| 302 | } | ||
| 303 | break; | ||
| 304 | |||
| 305 | case 'p': | ||
| 306 | { | ||
| 307 | /* Pointer conversion. | ||
| 308 | Format pointers as %#x. */ | ||
| 309 | void *p = va_arg (args, void *); | ||
| 310 | |||
| 311 | c.flags = POUND; | ||
| 312 | format_integer ((uintptr_t) p, false, false, | ||
| 313 | &base_x, &c, output, aux); | ||
| 314 | } | ||
| 315 | break; | ||
| 316 | |||
| 317 | case 'f': | ||
| 318 | case 'e': | ||
| 319 | case 'E': | ||
| 320 | case 'g': | ||
| 321 | case 'G': | ||
| 322 | case 'n': | ||
| 323 | /* We don't support floating-point arithmetic, | ||
| 324 | and %n can be part of a security hole. */ | ||
| 325 | __printf ("<<no %%%c in kernel>>", output, aux, *format); | ||
| 326 | break; | ||
| 327 | |||
| 328 | default: | ||
| 329 | __printf ("<<no %%%c conversion>>", output, aux, *format); | ||
| 330 | break; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | /* Parses conversion option characters starting at FORMAT and | ||
| 336 | initializes C appropriately. Returns the character in FORMAT | ||
| 337 | that indicates the conversion (e.g. the `d' in `%d'). Uses | ||
| 338 | *ARGS for `*' field widths and precisions. */ | ||
| 339 | static const char * | ||
| 340 | parse_conversion (const char *format, struct printf_conversion *c, | ||
| 341 | va_list *args) | ||
| 342 | { | ||
| 343 | /* Parse flag characters. */ | ||
| 344 | c->flags = 0; | ||
| 345 | for (;;) | ||
| 346 | { | ||
| 347 | switch (*format++) | ||
| 348 | { | ||
| 349 | case '-': | ||
| 350 | c->flags |= MINUS; | ||
| 351 | break; | ||
| 352 | case '+': | ||
| 353 | c->flags |= PLUS; | ||
| 354 | break; | ||
| 355 | case ' ': | ||
| 356 | c->flags |= SPACE; | ||
| 357 | break; | ||
| 358 | case '#': | ||
| 359 | c->flags |= POUND; | ||
| 360 | break; | ||
| 361 | case '0': | ||
| 362 | c->flags |= ZERO; | ||
| 363 | break; | ||
| 364 | case '\'': | ||
| 365 | c->flags |= GROUP; | ||
| 366 | break; | ||
| 367 | default: | ||
| 368 | format--; | ||
| 369 | goto not_a_flag; | ||
| 370 | } | ||
| 371 | } | ||
| 372 | not_a_flag: | ||
| 373 | if (c->flags & MINUS) | ||
| 374 | c->flags &= ~ZERO; | ||
| 375 | if (c->flags & PLUS) | ||
| 376 | c->flags &= ~SPACE; | ||
| 377 | |||
| 378 | /* Parse field width. */ | ||
| 379 | c->width = 0; | ||
| 380 | if (*format == '*') | ||
| 381 | { | ||
| 382 | format++; | ||
| 383 | c->width = va_arg (*args, int); | ||
| 384 | } | ||
| 385 | else | ||
| 386 | { | ||
| 387 | for (; isdigit (*format); format++) | ||
| 388 | c->width = c->width * 10 + *format - '0'; | ||
| 389 | } | ||
| 390 | if (c->width < 0) | ||
| 391 | { | ||
| 392 | c->width = -c->width; | ||
| 393 | c->flags |= MINUS; | ||
| 394 | } | ||
| 395 | |||
| 396 | /* Parse precision. */ | ||
| 397 | c->precision = -1; | ||
| 398 | if (*format == '.') | ||
| 399 | { | ||
| 400 | format++; | ||
| 401 | if (*format == '*') | ||
| 402 | { | ||
| 403 | format++; | ||
| 404 | c->precision = va_arg (*args, int); | ||
| 405 | } | ||
| 406 | else | ||
| 407 | { | ||
| 408 | c->precision = 0; | ||
| 409 | for (; isdigit (*format); format++) | ||
| 410 | c->precision = c->precision * 10 + *format - '0'; | ||
| 411 | } | ||
| 412 | if (c->precision < 0) | ||
| 413 | c->precision = -1; | ||
| 414 | } | ||
| 415 | if (c->precision >= 0) | ||
| 416 | c->flags &= ~ZERO; | ||
| 417 | |||
| 418 | /* Parse type. */ | ||
| 419 | c->type = INT; | ||
| 420 | switch (*format++) | ||
| 421 | { | ||
| 422 | case 'h': | ||
| 423 | if (*format == 'h') | ||
| 424 | { | ||
| 425 | format++; | ||
| 426 | c->type = CHAR; | ||
| 427 | } | ||
| 428 | else | ||
| 429 | c->type = SHORT; | ||
| 430 | break; | ||
| 431 | |||
| 432 | case 'j': | ||
| 433 | c->type = INTMAX; | ||
| 434 | break; | ||
| 435 | |||
| 436 | case 'l': | ||
| 437 | if (*format == 'l') | ||
| 438 | { | ||
| 439 | format++; | ||
| 440 | c->type = LONGLONG; | ||
| 441 | } | ||
| 442 | else | ||
| 443 | c->type = LONG; | ||
| 444 | break; | ||
| 445 | |||
| 446 | case 't': | ||
| 447 | c->type = PTRDIFFT; | ||
| 448 | break; | ||
| 449 | |||
| 450 | case 'z': | ||
| 451 | c->type = SIZET; | ||
| 452 | break; | ||
| 453 | |||
| 454 | default: | ||
| 455 | format--; | ||
| 456 | break; | ||
| 457 | } | ||
| 458 | |||
| 459 | return format; | ||
| 460 | } | ||
| 461 | |||
| 462 | /* Performs an integer conversion, writing output to OUTPUT with | ||
| 463 | auxiliary data AUX. The integer converted has absolute value | ||
| 464 | VALUE. If IS_SIGNED is true, does a signed conversion with | ||
| 465 | NEGATIVE indicating a negative value; otherwise does an | ||
| 466 | unsigned conversion and ignores NEGATIVE. The output is done | ||
| 467 | according to the provided base B. Details of the conversion | ||
| 468 | are in C. */ | ||
| 469 | static void | ||
| 470 | format_integer (uintmax_t value, bool is_signed, bool negative, | ||
| 471 | const struct integer_base *b, | ||
| 472 | const struct printf_conversion *c, | ||
| 473 | void (*output) (char, void *), void *aux) | ||
| 474 | { | ||
| 475 | char buf[64], *cp; /* Buffer and current position. */ | ||
| 476 | int x; /* `x' character to use or 0 if none. */ | ||
| 477 | int sign; /* Sign character or 0 if none. */ | ||
| 478 | int precision; /* Rendered precision. */ | ||
| 479 | int pad_cnt; /* # of pad characters to fill field width. */ | ||
| 480 | int digit_cnt; /* # of digits output so far. */ | ||
| 481 | |||
| 482 | /* Determine sign character, if any. | ||
| 483 | An unsigned conversion will never have a sign character, | ||
| 484 | even if one of the flags requests one. */ | ||
| 485 | sign = 0; | ||
| 486 | if (is_signed) | ||
| 487 | { | ||
| 488 | if (c->flags & PLUS) | ||
| 489 | sign = negative ? '-' : '+'; | ||
| 490 | else if (c->flags & SPACE) | ||
| 491 | sign = negative ? '-' : ' '; | ||
| 492 | else if (negative) | ||
| 493 | sign = '-'; | ||
| 494 | } | ||
| 495 | |||
| 496 | /* Determine whether to include `0x' or `0X'. | ||
| 497 | It will only be included with a hexadecimal conversion of a | ||
| 498 | nonzero value with the # flag. */ | ||
| 499 | x = (c->flags & POUND) && value ? b->x : 0; | ||
| 500 | |||
| 501 | /* Accumulate digits into buffer. | ||
| 502 | This algorithm produces digits in reverse order, so later we | ||
| 503 | will output the buffer's content in reverse. */ | ||
| 504 | cp = buf; | ||
| 505 | digit_cnt = 0; | ||
| 506 | while (value > 0) | ||
| 507 | { | ||
| 508 | if ((c->flags & GROUP) && digit_cnt > 0 && digit_cnt % b->group == 0) | ||
| 509 | *cp++ = ','; | ||
| 510 | *cp++ = b->digits[value % b->base]; | ||
| 511 | value /= b->base; | ||
| 512 | digit_cnt++; | ||
| 513 | } | ||
| 514 | |||
| 515 | /* Append enough zeros to match precision. | ||
| 516 | If requested precision is 0, then a value of zero is | ||
| 517 | rendered as a null string, otherwise as "0". | ||
| 518 | If the # flag is used with base 8, the result must always | ||
| 519 | begin with a zero. */ | ||
| 520 | precision = c->precision < 0 ? 1 : c->precision; | ||
| 521 | while (cp - buf < precision && cp < buf + sizeof buf - 1) | ||
| 522 | *cp++ = '0'; | ||
| 523 | if ((c->flags & POUND) && b->base == 8 && (cp == buf || cp[-1] != '0')) | ||
| 524 | *cp++ = '0'; | ||
| 525 | |||
| 526 | /* Calculate number of pad characters to fill field width. */ | ||
| 527 | pad_cnt = c->width - (cp - buf) - (x ? 2 : 0) - (sign != 0); | ||
| 528 | if (pad_cnt < 0) | ||
| 529 | pad_cnt = 0; | ||
| 530 | |||
| 531 | /* Do output. */ | ||
| 532 | if ((c->flags & (MINUS | ZERO)) == 0) | ||
| 533 | output_dup (' ', pad_cnt, output, aux); | ||
| 534 | if (sign) | ||
| 535 | output (sign, aux); | ||
| 536 | if (x) | ||
| 537 | { | ||
| 538 | output ('0', aux); | ||
| 539 | output (x, aux); | ||
| 540 | } | ||
| 541 | if (c->flags & ZERO) | ||
| 542 | output_dup ('0', pad_cnt, output, aux); | ||
| 543 | while (cp > buf) | ||
| 544 | output (*--cp, aux); | ||
| 545 | if (c->flags & MINUS) | ||
| 546 | output_dup (' ', pad_cnt, output, aux); | ||
| 547 | } | ||
| 548 | |||
| 549 | /* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */ | ||
| 550 | static void | ||
| 551 | output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux) | ||
| 552 | { | ||
| 553 | while (cnt-- > 0) | ||
| 554 | output (ch, aux); | ||
| 555 | } | ||
| 556 | |||
| 557 | /* Formats the LENGTH characters starting at STRING according to | ||
| 558 | the conversion specified in C. Writes output to OUTPUT with | ||
| 559 | auxiliary data AUX. */ | ||
| 560 | static void | ||
| 561 | format_string (const char *string, int length, | ||
| 562 | struct printf_conversion *c, | ||
| 563 | void (*output) (char, void *), void *aux) | ||
| 564 | { | ||
| 565 | int i; | ||
| 566 | if (c->width > length && (c->flags & MINUS) == 0) | ||
| 567 | output_dup (' ', c->width - length, output, aux); | ||
| 568 | for (i = 0; i < length; i++) | ||
| 569 | output (string[i], aux); | ||
| 570 | if (c->width > length && (c->flags & MINUS) != 0) | ||
| 571 | output_dup (' ', c->width - length, output, aux); | ||
| 572 | } | ||
| 573 | |||
| 574 | /* Wrapper for __vprintf() that converts varargs into a | ||
| 575 | va_list. */ | ||
| 576 | void | ||
| 577 | __printf (const char *format, | ||
| 578 | void (*output) (char, void *), void *aux, ...) | ||
| 579 | { | ||
| 580 | va_list args; | ||
| 581 | |||
| 582 | va_start (args, aux); | ||
| 583 | __vprintf (format, args, output, aux); | ||
| 584 | va_end (args); | ||
| 585 | } | ||
| 586 | |||
| 587 | /* Dumps the SIZE bytes in BUF to the console as hex bytes | ||
| 588 | arranged 16 per line. Numeric offsets are also included, | ||
| 589 | starting at OFS for the first byte in BUF. If ASCII is true | ||
| 590 | then the corresponding ASCII characters are also rendered | ||
| 591 | alongside. */ | ||
| 592 | void | ||
| 593 | hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii) | ||
| 594 | { | ||
| 595 | const uint8_t *buf = buf_; | ||
| 596 | const size_t per_line = 16; /* Maximum bytes per line. */ | ||
| 597 | |||
| 598 | while (size > 0) | ||
| 599 | { | ||
| 600 | size_t start, end, n; | ||
| 601 | size_t i; | ||
| 602 | |||
| 603 | /* Number of bytes on this line. */ | ||
| 604 | start = ofs % per_line; | ||
| 605 | end = per_line; | ||
| 606 | if (end - start > size) | ||
| 607 | end = start + size; | ||
| 608 | n = end - start; | ||
| 609 | |||
| 610 | /* Print line. */ | ||
| 611 | printf ("%08jx ", (uintmax_t) ROUND_DOWN (ofs, per_line)); | ||
| 612 | for (i = 0; i < start; i++) | ||
| 613 | printf (" "); | ||
| 614 | for (; i < end; i++) | ||
| 615 | printf ("%02hhx%c", | ||
| 616 | buf[i - start], i == per_line / 2 - 1? '-' : ' '); | ||
| 617 | if (ascii) | ||
| 618 | { | ||
| 619 | for (; i < per_line; i++) | ||
| 620 | printf (" "); | ||
| 621 | printf ("|"); | ||
| 622 | for (i = 0; i < start; i++) | ||
| 623 | printf (" "); | ||
| 624 | for (; i < end; i++) | ||
| 625 | printf ("%c", | ||
| 626 | isprint (buf[i - start]) ? buf[i - start] : '.'); | ||
| 627 | for (; i < per_line; i++) | ||
| 628 | printf (" "); | ||
| 629 | printf ("|"); | ||
| 630 | } | ||
| 631 | printf ("\n"); | ||
| 632 | |||
| 633 | ofs += n; | ||
| 634 | buf += n; | ||
| 635 | size -= n; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | /* Prints SIZE, which represents a number of bytes, in a | ||
| 640 | human-readable format, e.g. "256 kB". */ | ||
| 641 | void | ||
| 642 | print_human_readable_size (uint64_t size) | ||
| 643 | { | ||
| 644 | if (size == 1) | ||
| 645 | printf ("1 byte"); | ||
| 646 | else | ||
| 647 | { | ||
| 648 | static const char *factors[] = {"bytes", "kB", "MB", "GB", "TB", NULL}; | ||
| 649 | const char **fp; | ||
| 650 | |||
| 651 | for (fp = factors; size >= 1024 && fp[1] != NULL; fp++) | ||
| 652 | size /= 1024; | ||
| 653 | printf ("%"PRIu64" %s", size, *fp); | ||
| 654 | } | ||
| 655 | } | ||
