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