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