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