xv6, line by line
user/printf.c

user/printf.c

C · 135 lines · annotated 100% · user program / library · upstream

About this file

Formatted output for user programs: printf writes to standard output, fprintf to any file descriptor (most often 2, standard error, for error messages), and vprintf does the work for both.

The formatting code is almost a copy of the kernel’s printk (kernel/printk.c). The difference is where the characters go. The kernel can hand each character to the console driver directly. A user program cannot touch any device; it can only ask the kernel to write bytes to a file descriptor. So putc here makes one write system call per character: printf("hello\n") enters the kernel six times. That is slow, and it has a visible effect: when two processes print at the same time, their output is mixed character by character, because nothing groups a whole printf into one write.

Supported directives, in both files: %d, %u, %x (32-bit), the same with l or ll (64-bit), %p, %c, %s and %%. There is no field width, padding or precision, and no floating point. See format string and variadic function.

Read before: kernel/printk.c. Read next: user/umalloc.c.

5#include <stdarg.h>
7static char digits[] = "0123456789ABCDEF";
9static void
10putc(int fd, char c)
12 write(fd, &c, 1);
15static void
16printint(int fd, long long xx, int base, int sgn)
18 char buf[20];
19 int i, neg;
20 unsigned long long x;
22 neg = 0;
23 if (sgn && xx < 0) {
24 neg = 1;
25 x = -xx;
26 } else {
27 x = xx;
28 }
30 i = 0;
31 do {
32 buf[i++] = digits[x % base];
33 } while ((x /= base) != 0);
34 if (neg)
35 buf[i++] = '-';
37 while (--i >= 0)
38 putc(fd, buf[i]);
41static void
44 int i;
45 putc(fd, '0');
46 putc(fd, 'x');
47 for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)
48 putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]);
51// Print to the given fd. Only understands %d, %x, %p, %c, %s.
52void
53vprintf(int fd, const char *fmt, va_list ap)
55 char *s;
56 int c0, c1, c2, i, state;
58 state = 0;
59 for (i = 0; fmt[i]; i++) {
60 c0 = fmt[i] & 0xff;
61 if (state == 0) {
62 if (c0 == '%') {
63 state = '%';
64 } else {
66 }
67 } else if (state == '%') {
68 c1 = c2 = 0;
69 if (c0)
70 c1 = fmt[i + 1] & 0xff;
71 if (c1)
72 c2 = fmt[i + 2] & 0xff;
73 if (c0 == 'd') {
74 printint(fd, va_arg(ap, int), 10, 1);
75 } else if (c0 == 'l' && c1 == 'd') {
76 printint(fd, va_arg(ap, uint64), 10, 1);
77 i += 1;
78 } else if (c0 == 'l' && c1 == 'l' && c2 == 'd') {
79 printint(fd, va_arg(ap, uint64), 10, 1);
80 i += 2;
81 } else if (c0 == 'u') {
82 printint(fd, va_arg(ap, uint32), 10, 0);
83 } else if (c0 == 'l' && c1 == 'u') {
84 printint(fd, va_arg(ap, uint64), 10, 0);
85 i += 1;
86 } else if (c0 == 'l' && c1 == 'l' && c2 == 'u') {
87 printint(fd, va_arg(ap, uint64), 10, 0);
88 i += 2;
89 } else if (c0 == 'x') {
90 printint(fd, va_arg(ap, uint32), 16, 0);
91 } else if (c0 == 'l' && c1 == 'x') {
92 printint(fd, va_arg(ap, uint64), 16, 0);
93 i += 1;
94 } else if (c0 == 'l' && c1 == 'l' && c2 == 'x') {
95 printint(fd, va_arg(ap, uint64), 16, 0);
96 i += 2;
97 } else if (c0 == 'p') {
98 printptr(fd, va_arg(ap, uint64));
99 } else if (c0 == 'c') {
100 putc(fd, va_arg(ap, uint32));
101 } else if (c0 == 's') {
102 if ((s = va_arg(ap, char *)) == 0)
103 s = "(null)";
104 for (; *s; s++)
105 putc(fd, *s);
106 } else if (c0 == '%') {
107 putc(fd, '%');
108 } else {
109 // Unknown % sequence. Print it to draw attention.
110 putc(fd, '%');
112 }
114 state = 0;
115 }
116 }
119void
120fprintf(int fd, const char *fmt, ...)
122 va_list ap;
124 va_start(ap, fmt);
128void
129printf(const char *fmt, ...)
131 va_list ap;
133 va_start(ap, fmt);