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