Fix handling of `+' and ` ' flags for unsigned conversions in
[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, size_t 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                 break;
218               default:
219                 NOT_REACHED ();
220               }
221
222             format_integer (value < 0 ? -value : value,
223                             true, value < 0, &base_d, &c, output, aux);
224           }
225           break;
226           
227         case 'o':
228         case 'u':
229         case 'x':
230         case 'X':
231           {
232             /* Unsigned integer conversions. */
233             uintmax_t value;
234             const struct integer_base *b;
235
236             switch (c.type) 
237               {
238               case CHAR: 
239                 value = (unsigned char) va_arg (args, unsigned);
240                 break;
241               case SHORT:
242                 value = (unsigned short) va_arg (args, unsigned);
243                 break;
244               case INT:
245                 value = va_arg (args, unsigned);
246                 break;
247               case INTMAX:
248                 value = va_arg (args, uintmax_t);
249                 break;
250               case LONG:
251                 value = va_arg (args, unsigned long);
252                 break;
253               case LONGLONG:
254                 value = va_arg (args, unsigned long long);
255                 break;
256               case PTRDIFFT:
257                 value = va_arg (args, ptrdiff_t);
258                 break;
259               case SIZET:
260                 value = va_arg (args, size_t);
261                 break;
262               default:
263                 NOT_REACHED ();
264               }
265
266             switch (*format) 
267               {
268               case 'o': b = &base_o; break;
269               case 'u': b = &base_d; break;
270               case 'x': b = &base_x; break;
271               case 'X': b = &base_X; break;
272               default: NOT_REACHED ();
273               }
274
275             format_integer (value, false, false, b, &c, output, aux);
276           }
277           break;
278
279         case 'c': 
280           {
281             /* Treat character as single-character string. */
282             char ch = va_arg (args, int);
283             format_string (&ch, 1, &c, output, aux);
284           }
285           break;
286
287         case 's':
288           {
289             /* String conversion. */
290             const char *s = va_arg (args, char *);
291             if (s == NULL)
292               s = "(null)";
293
294             /* Limit string length according to precision.
295                Note: if c.precision == -1 then strnlen() will get
296                SIZE_MAX for MAXLEN, which is just what we want. */
297             format_string (s, strnlen (s, c.precision), &c, output, aux);
298           }
299           break;
300           
301         case 'p':
302           {
303             /* Pointer conversion.
304                Format pointers as %#x. */
305             void *p = va_arg (args, void *);
306
307             c.flags = POUND;
308             format_integer ((uintptr_t) p, false, false,
309                             &base_x, &c, output, aux);
310           }
311           break;
312       
313         case 'f':
314         case 'e':
315         case 'E':
316         case 'g':
317         case 'G':
318         case 'n':
319           /* We don't support floating-point arithmetic,
320              and %n can be part of a security hole. */
321           __printf ("<<no %%%c in kernel>>", output, aux, *format);
322           break;
323
324         default:
325           __printf ("<<no %%%c conversion>>", output, aux, *format);
326           break;
327         }
328     }
329 }
330
331 /* Parses conversion option characters starting at FORMAT and
332    initializes C appropriately.  Returns the character in FORMAT
333    that indicates the conversion (e.g. the `d' in `%d').  Uses
334    *ARGS for `*' field widths and precisions. */
335 static const char *
336 parse_conversion (const char *format, struct printf_conversion *c,
337                   va_list *args) 
338 {
339   /* Parse flag characters. */
340   c->flags = 0;
341   for (;;) 
342     {
343       switch (*format++) 
344         {
345         case '-':
346           c->flags |= MINUS;
347           break;
348         case '+':
349           c->flags |= PLUS;
350           break;
351         case ' ':
352           c->flags |= SPACE;
353           break;
354         case '#':
355           c->flags |= POUND;
356           break;
357         case '0':
358           c->flags |= ZERO;
359           break;
360         case '\'':
361           c->flags |= GROUP;
362           break;
363         default:
364           format--;
365           goto not_a_flag;
366         }
367     }
368  not_a_flag:
369   if (c->flags & MINUS)
370     c->flags &= ~ZERO;
371   if (c->flags & PLUS)
372     c->flags &= ~SPACE;
373
374   /* Parse field width. */
375   c->width = 0;
376   if (*format == '*')
377     {
378       format++;
379       c->width = va_arg (*args, int);
380     }
381   else 
382     {
383       for (; isdigit (*format); format++)
384         c->width = c->width * 10 + *format - '0';
385     }
386   if (c->width < 0) 
387     {
388       c->width = -c->width;
389       c->flags |= MINUS;
390     }
391       
392   /* Parse precision. */
393   c->precision = -1;
394   if (*format == '.') 
395     {
396       format++;
397       if (*format == '*') 
398         {
399           format++;
400           c->precision = va_arg (*args, int);
401         }
402       else 
403         {
404           c->precision = 0;
405           for (; isdigit (*format); format++)
406             c->precision = c->precision * 10 + *format - '0';
407         }
408       if (c->precision < 0) 
409         c->precision = -1;
410     }
411   if (c->precision >= 0)
412     c->flags &= ~ZERO;
413
414   /* Parse type. */
415   c->type = INT;
416   switch (*format++) 
417     {
418     case 'h':
419       if (*format == 'h') 
420         {
421           format++;
422           c->type = CHAR;
423         }
424       else
425         c->type = SHORT;
426       break;
427       
428     case 'j':
429       c->type = INTMAX;
430       break;
431
432     case 'l':
433       if (*format == 'l')
434         {
435           format++;
436           c->type = LONGLONG;
437         }
438       else
439         c->type = LONG;
440       break;
441
442     case 't':
443       c->type = PTRDIFFT;
444       break;
445
446     case 'z':
447       c->type = SIZET;
448       break;
449
450     default:
451       format--;
452       break;
453     }
454
455   return format;
456 }
457
458 /* Performs an integer conversion, writing output to OUTPUT with
459    auxiliary data AUX.  The integer converted has absolute value
460    VALUE.  If IS_SIGNED is true, does a signed conversion with
461    NEGATIVE indicating a negative value; otherwise does an
462    unsigned conversion and ignores IS_SIGNED.  The output will
463    use the given DIGITS, with strlen(DIGITS) indicating the
464    output base.  Details of the conversion are in C. */
465 static void
466 format_integer (uintmax_t value, bool is_signed, bool negative, 
467                 const struct integer_base *b,
468                 const struct printf_conversion *c,
469                 void (*output) (char, void *), void *aux)
470 {
471   char buf[64], *cp;            /* Buffer and current position. */
472   const char *signifier;        /* b->signifier or "". */
473   int precision;                /* Rendered precision. */
474   int pad_cnt;                  /* # of pad characters to fill field width. */
475   int digit_cnt;                /* # of digits output so far. */
476
477   /* Accumulate digits into buffer.
478      This algorithm produces digits in reverse order, so later we
479      will output the buffer's content in reverse.  This is also
480      the reason that later we append zeros and the sign. */
481   cp = buf;
482   digit_cnt = 0;
483   while (value > 0) 
484     {
485       if ((c->flags & GROUP) && digit_cnt > 0 && digit_cnt % b->group == 0)
486         *cp++ = ',';
487       *cp++ = b->digits[value % b->base];
488       value /= b->base;
489       digit_cnt++;
490     }
491
492   /* Append enough zeros to match precision.
493      If requested precision is 0, then a value of zero is
494      rendered as a null string, otherwise as "0". */
495   precision = c->precision < 0 ? 1 : c->precision;
496   while (cp - buf < precision && cp - buf < (int) sizeof buf - 8)
497     *cp++ = '0';
498
499   /* Append sign. */
500   if (is_signed) 
501     {
502       if (c->flags & PLUS)
503         *cp++ = negative ? '-' : '+';
504       else if (c->flags & SPACE)
505         *cp++ = negative ? '-' : ' ';
506       else if (negative)
507         *cp++ = '-';
508     }
509   
510   /* Calculate number of pad characters to fill field width. */
511   signifier = c->flags & POUND ? b->signifier : "";
512   pad_cnt = c->width - (cp - buf) - strlen (signifier);
513   if (pad_cnt < 0)
514     pad_cnt = 0;
515
516   /* Do output. */
517   if ((c->flags & (MINUS | ZERO)) == 0)
518     output_dup (' ', pad_cnt, output, aux);
519   while (*signifier != '\0')
520     output (*signifier++, aux);
521   if (c->flags & ZERO)
522     output_dup ('0', pad_cnt, output, aux);
523   while (cp > buf)
524     output (*--cp, aux);
525   if (c->flags & MINUS)
526     output_dup (' ', pad_cnt, output, aux);
527 }
528
529 /* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */
530 static void
531 output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux) 
532 {
533   while (cnt-- > 0)
534     output (ch, aux);
535 }
536
537 /* Formats the LENGTH characters starting at STRING according to
538    the conversion specified in C.  Writes output to OUTPUT with
539    auxiliary data AUX. */
540 static void
541 format_string (const char *string, size_t length,
542                struct printf_conversion *c,
543                void (*output) (char, void *), void *aux) 
544 {
545   if (c->width > 1 && (c->flags & MINUS) == 0)
546     output_dup (' ', c->width - 1, output, aux);
547   while (length-- > 0)
548     output (*string++, aux);
549   if (c->width > 1 && (c->flags & MINUS) != 0)
550     output_dup (' ', c->width - 1, output, aux);
551 }
552
553 /* Wrapper for __vprintf() that converts varargs into a
554    va_list. */
555 void
556 __printf (const char *format,
557           void (*output) (char, void *), void *aux, ...) 
558 {
559   va_list args;
560
561   va_start (args, aux);
562   __vprintf (format, args, output, aux);
563   va_end (args);
564 }
565 \f
566 /* Dumps the SIZE bytes in BUF to the console as hex bytes
567    arranged 16 per line.  Numeric offsets are also included,
568    starting at OFS for the first byte in BUF.  If ASCII is true
569    then the corresponding ASCII characters are also rendered
570    alongside. */   
571 void
572 hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii)
573 {
574   const uint8_t *buf = buf_;
575   const size_t per_line = 16; /* Maximum bytes per line. */
576
577   while (size > 0)
578     {
579       size_t start, end, n;
580       size_t i;
581       
582       /* Number of bytes on this line. */
583       start = ofs % per_line;
584       end = per_line;
585       if (end - start > size)
586         end = start + size;
587       n = end - start;
588
589       /* Print line. */
590       printf ("%08jx  ", (uintmax_t) ROUND_DOWN (ofs, per_line));
591       for (i = 0; i < start; i++)
592         printf ("   ");
593       for (; i < end; i++) 
594         printf ("%02hhx%c",
595                 buf[i - start], i == per_line / 2 - 1? '-' : ' ');
596       if (ascii) 
597         {
598           for (; i < per_line; i++)
599             printf ("   ");
600           printf ("|");
601           for (i = 0; i < start; i++)
602             printf (" ");
603           for (; i < end; i++)
604             printf ("%c",
605                     isprint (buf[i - start]) ? buf[i - start] : '.');
606           for (; i < per_line; i++)
607             printf (" ");
608           printf ("|");
609         }
610       printf ("\n");
611
612       ofs += n;
613       buf += n;
614       size -= n;
615     }
616 }