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