PKUOS - Pintos
Pintos source browser for PKU Operating System course
stdio.c
Go to the documentation of this file.
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(). */
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
16static 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. */
25int
26vsnprintf (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(). */
45static void
46vsnprintf_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. */
61int
62snprintf (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. */
78int
79printf (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. */
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 /**< '\'' */
107
108 /* Minimum field width. */
109 int width;
110
111 /* Numeric precision.
112 -1 indicates no precision was specified. */
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 */
128 };
129
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
138static const struct integer_base base_d = {10, "0123456789", 0, 3};
139static const struct integer_base base_o = {8, "01234567", 0, 3};
140static const struct integer_base base_x = {16, "0123456789abcdef", 'x', 4};
141static const struct integer_base base_X = {16, "0123456789ABCDEF", 'X', 4};
142
143static const char *parse_conversion (const char *format,
144 struct printf_conversion *,
145 va_list *);
146static 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);
150static void output_dup (char ch, size_t cnt,
151 void (*output) (char, void *), void *aux);
152static void format_string (const char *string, int length,
153 struct printf_conversion *,
154 void (*output) (char, void *), void *aux);
155
156void
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. */
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
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. */
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. */
339static const char *
340parse_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. */
469static void
470format_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. */
550static void
551output_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. */
560static void
561format_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. */
576void
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. */
592void
593hex_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". */
641void
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}
static char buf[BUF_SIZE]
static int isprint(int c)
Definition: ctype.h:18
static int isdigit(int c)
Definition: ctype.h:7
#define NOT_REACHED()
lib/debug.h
Definition: debug.h:35
static struct intq buffer
Stores keys from the keyboard and serial port.
Definition: input.c:7
char * start[]
Insult.c.
Definition: insult.c:13
#define PRIu64
Definition: inttypes.h:30
int vprintf(const char *format, va_list args)
The standard vprintf() function, which is like printf() but uses a va_list.
Definition: console.c:126
static const struct integer_base base_x
Definition: stdio.c:140
int vsnprintf(char *buffer, size_t buf_size, const char *format, va_list args)
Like vprintf(), except that output is stored into BUFFER, which must have space for BUF_SIZE characte...
Definition: stdio.c:26
void __printf(const char *format, void(*output)(char, void *), void *aux,...)
Wrapper for __vprintf() that converts varargs into a va_list.
Definition: stdio.c:577
void print_human_readable_size(uint64_t size)
Prints SIZE, which represents a number of bytes, in a human-readable format, e.g.
Definition: stdio.c:642
void __vprintf(const char *format, va_list args, void(*output)(char, void *), void *aux)
Internal functions.
Definition: stdio.c:157
static const struct integer_base base_o
Definition: stdio.c:139
static const char * parse_conversion(const char *format, struct printf_conversion *, va_list *)
Parses conversion option characters starting at FORMAT and initializes C appropriately.
Definition: stdio.c:340
int snprintf(char *buffer, size_t buf_size, const char *format,...)
Like printf(), except that output is stored into BUFFER, which must have space for BUF_SIZE character...
Definition: stdio.c:62
static void output_dup(char ch, size_t cnt, void(*output)(char, void *), void *aux)
Writes CH to OUTPUT with auxiliary data AUX, CNT times.
Definition: stdio.c:551
int printf(const char *format,...)
Writes formatted output to the console.
Definition: stdio.c:79
static void vsnprintf_helper(char, void *)
Helper function for vsnprintf().
Definition: stdio.c:46
static const struct integer_base base_X
Definition: stdio.c:141
void hex_dump(uintptr_t ofs, const void *buf_, size_t size, bool ascii)
Dumps the SIZE bytes in BUF to the console as hex bytes arranged 16 per line.
Definition: stdio.c:593
static const struct integer_base base_d
Definition: stdio.c:138
static void format_string(const char *string, int length, struct printf_conversion *, void(*output)(char, void *), void *aux)
Formats the LENGTH characters starting at STRING according to the conversion specified in C.
Definition: stdio.c:561
static void format_integer(uintmax_t value, bool is_signed, bool negative, const struct integer_base *, const struct printf_conversion *, void(*output)(char, void *), void *aux)
Performs an integer conversion, writing output to OUTPUT with auxiliary data AUX.
Definition: stdio.c:470
static char x
Verifies that mapping over the data segment is disallowed.
Definition: mmap-over-data.c:9
static uint8_t s[256]
RC4-based pseudo-random number generator (PRNG).
Definition: random.c:17
#define ROUND_DOWN(X, STEP)
Yields X rounded down to the nearest multiple of STEP.
Definition: round.h:14
#define va_end(LIST)
Definition: stdarg.h:10
#define va_arg(LIST, TYPE)
Definition: stdarg.h:11
#define va_start(LIST, ARG)
Definition: stdarg.h:9
__builtin_va_list va_list
GCC has <stdarg.h> functionality as built-ins, so all we need is to use it.
Definition: stdarg.h:7
#define NULL
Definition: stddef.h:4
__PTRDIFF_TYPE__ ptrdiff_t
GCC predefines the types we need for ptrdiff_t and size_t, so that we don't have to guess.
Definition: stddef.h:9
int64_t intmax_t
Definition: stdint.h:39
uint32_t uintptr_t
Definition: stdint.h:36
uint64_t uintmax_t
Definition: stdint.h:43
#define SIZE_MAX
lib/stdint.h
Definition: stdint.h:49
unsigned char uint8_t
Definition: stdint.h:20
unsigned long long int uint64_t
Definition: stdint.h:29
#define PTRDIFF_MAX
Definition: stdint.h:47
size_t strnlen(const char *string, size_t maxlen)
If STRING is less than MAXLEN characters in length, returns its actual length.
Definition: string.c:307
int x
‘x’ character to use, for base 16 only.
Definition: stdio.c:134
int base
Base.
Definition: stdio.c:132
int group
Number of digits to group with ' flag.
Definition: stdio.c:135
const char * digits
Collection of digits.
Definition: stdio.c:133
printf() formatting internals.
Definition: stdio.c:95
enum printf_conversion::@1 flags
enum printf_conversion::@2 type
@ INT
(none)
Definition: stdio.c:120
A linked list element.
Definition: list.c:23
Auxiliary data for vsnprintf_helper().
Definition: stdio.c:10
char * p
Current output position.
Definition: stdio.c:11
int max_length
Max length of output string.
Definition: stdio.c:13
int length
Length of output string.
Definition: stdio.c:12