Fix 3 distinct bugs reported by Cristian Cadar.
[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     const char *signifier;      /* Prefix used with # flag. */
136     int group;                  /* Number of digits to group with ' flag. */
137   };
138
139 static const struct integer_base base_d = {10, "0123456789", "", 3};
140 static const struct integer_base base_o = {8, "01234567", "0", 3};
141 static const struct integer_base base_x = {16, "0123456789abcdef", "0x", 4};
142 static const struct integer_base base_X = {16, "0123456789ABCDEF", "0X", 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 will
468    use the given DIGITS, with strlen(DIGITS) indicating the
469    output base.  Details of the conversion 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   const char *signifier;        /* b->signifier or "". */
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   /* Accumulate digits into buffer.
483      This algorithm produces digits in reverse order, so later we
484      will output the buffer's content in reverse.  This is also
485      the reason that later we append zeros and the sign. */
486   cp = buf;
487   digit_cnt = 0;
488   while (value > 0) 
489     {
490       if ((c->flags & GROUP) && digit_cnt > 0 && digit_cnt % b->group == 0)
491         *cp++ = ',';
492       *cp++ = b->digits[value % b->base];
493       value /= b->base;
494       digit_cnt++;
495     }
496
497   /* Append enough zeros to match precision.
498      If requested precision is 0, then a value of zero is
499      rendered as a null string, otherwise as "0". */
500   precision = c->precision < 0 ? 1 : c->precision;
501   while (cp - buf < precision && cp - buf < (int) sizeof buf - 8)
502     *cp++ = '0';
503
504   /* Append sign. */
505   if (is_signed) 
506     {
507       if (c->flags & PLUS)
508         *cp++ = negative ? '-' : '+';
509       else if (c->flags & SPACE)
510         *cp++ = negative ? '-' : ' ';
511       else if (negative)
512         *cp++ = '-';
513     }
514   
515   /* Calculate number of pad characters to fill field width. */
516   signifier = c->flags & POUND ? b->signifier : "";
517   pad_cnt = c->width - (cp - buf) - strlen (signifier);
518   if (pad_cnt < 0)
519     pad_cnt = 0;
520
521   /* Do output. */
522   if ((c->flags & (MINUS | ZERO)) == 0)
523     output_dup (' ', pad_cnt, output, aux);
524   while (*signifier != '\0')
525     output (*signifier++, aux);
526   if (c->flags & ZERO)
527     output_dup ('0', pad_cnt, output, aux);
528   while (cp > buf)
529     output (*--cp, aux);
530   if (c->flags & MINUS)
531     output_dup (' ', pad_cnt, output, aux);
532 }
533
534 /* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */
535 static void
536 output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux) 
537 {
538   while (cnt-- > 0)
539     output (ch, aux);
540 }
541
542 /* Formats the LENGTH characters starting at STRING according to
543    the conversion specified in C.  Writes output to OUTPUT with
544    auxiliary data AUX. */
545 static void
546 format_string (const char *string, int length,
547                struct printf_conversion *c,
548                void (*output) (char, void *), void *aux) 
549 {
550   if (c->width > length && (c->flags & MINUS) == 0)
551     output_dup (' ', c->width - length, output, aux);
552   while (length-- > 0)
553     output (*string++, aux);
554   if (c->width > length && (c->flags & MINUS) != 0)
555     output_dup (' ', c->width - length, output, aux);
556 }
557
558 /* Wrapper for __vprintf() that converts varargs into a
559    va_list. */
560 void
561 __printf (const char *format,
562           void (*output) (char, void *), void *aux, ...) 
563 {
564   va_list args;
565
566   va_start (args, aux);
567   __vprintf (format, args, output, aux);
568   va_end (args);
569 }
570 \f
571 /* Dumps the SIZE bytes in BUF to the console as hex bytes
572    arranged 16 per line.  Numeric offsets are also included,
573    starting at OFS for the first byte in BUF.  If ASCII is true
574    then the corresponding ASCII characters are also rendered
575    alongside. */   
576 void
577 hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii)
578 {
579   const uint8_t *buf = buf_;
580   const size_t per_line = 16; /* Maximum bytes per line. */
581
582   while (size > 0)
583     {
584       size_t start, end, n;
585       size_t i;
586       
587       /* Number of bytes on this line. */
588       start = ofs % per_line;
589       end = per_line;
590       if (end - start > size)
591         end = start + size;
592       n = end - start;
593
594       /* Print line. */
595       printf ("%08jx  ", (uintmax_t) ROUND_DOWN (ofs, per_line));
596       for (i = 0; i < start; i++)
597         printf ("   ");
598       for (; i < end; i++) 
599         printf ("%02hhx%c",
600                 buf[i - start], i == per_line / 2 - 1? '-' : ' ');
601       if (ascii) 
602         {
603           for (; i < per_line; i++)
604             printf ("   ");
605           printf ("|");
606           for (i = 0; i < start; i++)
607             printf (" ");
608           for (; i < end; i++)
609             printf ("%c",
610                     isprint (buf[i - start]) ? buf[i - start] : '.');
611           for (; i < per_line; i++)
612             printf (" ");
613           printf ("|");
614         }
615       printf ("\n");
616
617       ofs += n;
618       buf += n;
619       size -= n;
620     }
621 }