Format null pointers as 0x0.
[pintos-anon] / src / lib / stdio.c
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdint.h>
4 #include <string.h>
5
6 /* Auxiliary data for vsnprintf_helper(). */
7 struct vsnprintf_aux 
8   {
9     char *p;            /* Current output position. */
10     int length;         /* Length of output string. */
11     int max_length;     /* Max length of output string. */
12   };
13
14 static void vsnprintf_helper (char, void *);
15
16 /* Like vprintf(), except that output is stored into BUFFER,
17    which must have space for BUF_SIZE characters.  Writes at most
18    BUF_SIZE - 1 characters to BUFFER, followed by a null
19    terminator.  BUFFER will always be null-terminated unless
20    BUF_SIZE is zero.  Returns the number of characters that would
21    have been written to BUFFER, not including a null terminator,
22    had there been enough room. */
23 int
24 vsnprintf (char *buffer, size_t buf_size, const char *format, va_list args) 
25 {
26   /* Set up aux data for vsnprintf_helper(). */
27   struct vsnprintf_aux aux;
28   aux.p = buffer;
29   aux.length = 0;
30   aux.max_length = buf_size > 0 ? buf_size - 1 : 0;
31
32   /* Do most of the work. */
33   __vprintf (format, args, vsnprintf_helper, &aux);
34
35   /* Add null terminator. */
36   if (buf_size > 0)
37     *aux.p = '\0';
38
39   return aux.length;
40 }
41
42 /* Helper function for vsnprintf(). */
43 static void
44 vsnprintf_helper (char ch, void *aux_)
45 {
46   struct vsnprintf_aux *aux = aux_;
47
48   if (aux->length++ < aux->max_length)
49     *aux->p++ = ch;
50 }
51
52 /* Like printf(), except that output is stored into BUFFER,
53    which must have space for BUF_SIZE characters.  Writes at most
54    BUF_SIZE - 1 characters to BUFFER, followed by a null
55    terminator.  BUFFER will always be null-terminated unless
56    BUF_SIZE is zero.  Returns the number of characters that would
57    have been written to BUFFER, not including a null terminator,
58    had there been enough room. */
59 int
60 snprintf (char *buffer, size_t buf_size, const char *format, ...) 
61 {
62   va_list args;
63   int retval;
64
65   va_start (args, format);
66   retval = vsnprintf (buffer, buf_size, format, args);
67   va_end (args);
68
69   return retval;
70 }
71
72 /* Writes formatted output to the console.
73    In the kernel, the console is both the video display and first
74    serial port.
75    In userspace, the console is file descriptor 1.
76 */
77 int
78 printf (const char *format, ...) 
79 {
80   va_list args;
81   int retval;
82
83   va_start (args, format);
84   retval = vprintf (format, args);
85   va_end (args);
86
87   return retval;
88 }
89 \f
90 /* printf() formatting internals. */
91
92 /* A printf() conversion. */
93 struct printf_conversion 
94   {
95     /* Flags. */
96     enum 
97       {
98         MINUS = 1 << 0,         /* '-' */
99         PLUS = 1 << 1,          /* '+' */
100         SPACE = 1 << 2,         /* ' ' */
101         POUND = 1 << 3,         /* '#' */
102         ZERO = 1 << 4,          /* '0' */
103         GROUP = 1 << 5          /* '\'' */
104       }
105     flags;
106
107     /* Minimum field width. */
108     int width;
109
110     /* Numeric precision.
111        -1 indicates no precision was specified. */
112     int precision;
113
114     /* Type of argument to format. */
115     enum 
116       {
117         CHAR = 1,               /* hh */
118         SHORT = 2,              /* h */
119         INT = 3,                /* (none) */
120         INTMAX = 4,             /* j */
121         LONG = 5,               /* l */
122         LONGLONG = 6,           /* ll */
123         PTRDIFFT = 7,           /* t */
124         SIZET = 8               /* z */
125       }
126     type;
127   };
128
129 struct integer_base 
130   {
131     int base;                   /* Base. */
132     const char *digits;         /* Collection of digits. */
133     const char *signifier;      /* Prefix used with # flag. */
134     int group;                  /* Number of digits to group with ' flag. */
135   };
136
137 static const struct integer_base base_d = {10, "0123456789", "", 3};
138 static const struct integer_base base_o = {8, "01234567", "0", 3};
139 static const struct integer_base base_x = {16, "0123456789abcdef", "0x", 4};
140 static const struct integer_base base_X = {16, "0123456789ABCDEF", "0X", 4};
141
142 static const char *parse_conversion (const char *format,
143                                      struct printf_conversion *,
144                                      va_list *);
145 static void format_integer (uintmax_t value, bool negative,
146                             const struct integer_base *,
147                             const struct printf_conversion *,
148                             void (*output) (char, void *), void *aux);
149 static void output_dup (char ch, size_t cnt,
150                         void (*output) (char, void *), void *aux);
151 static void format_string (const char *string, size_t length,
152                            struct printf_conversion *,
153                            void (*output) (char, void *), void *aux);
154
155 void
156 __vprintf (const char *format, va_list args,
157            void (*output) (char, void *), void *aux)
158 {
159   for (; *format != '\0'; format++)
160     {
161       struct printf_conversion c;
162
163       /* Literally copy non-conversions to output. */
164       if (*format != '%') 
165         {
166           output (*format, aux);
167           continue;
168         }
169       format++;
170
171       /* %% => %. */
172       if (*format == '%') 
173         {
174           output ('%', aux);
175           continue;
176         }
177
178       /* Parse conversion specifiers. */
179       format = parse_conversion (format, &c, &args);
180
181       /* Do conversion. */
182       switch (*format) 
183         {
184         case 'd':
185         case 'i': 
186           {
187             /* Signed integer conversions. */
188             intmax_t value;
189             
190             switch (c.type) 
191               {
192               case CHAR: 
193                 value = (signed char) va_arg (args, int);
194                 break;
195               case SHORT:
196                 value = (short) va_arg (args, int);
197                 break;
198               case INT:
199                 value = va_arg (args, int);
200                 break;
201               case LONG:
202                 value = va_arg (args, long);
203                 break;
204               case LONGLONG:
205                 value = va_arg (args, long long);
206                 break;
207               case PTRDIFFT:
208                 value = va_arg (args, ptrdiff_t);
209                 break;
210               case SIZET:
211                 value = va_arg (args, size_t);
212                 break;
213               default:
214                 NOT_REACHED ();
215               }
216
217             format_integer (value < 0 ? -value : value,
218                             value < 0, &base_d, &c, output, aux);
219           }
220           break;
221           
222         case 'o':
223         case 'u':
224         case 'x':
225         case 'X':
226           {
227             /* Unsigned integer conversions. */
228             uintmax_t value;
229             const struct integer_base *b;
230
231             switch (c.type) 
232               {
233               case CHAR: 
234                 value = (unsigned char) va_arg (args, unsigned);
235                 break;
236               case SHORT:
237                 value = (unsigned short) va_arg (args, unsigned);
238                 break;
239               case INT:
240                 value = va_arg (args, unsigned);
241                 break;
242               case LONG:
243                 value = va_arg (args, unsigned long);
244                 break;
245               case LONGLONG:
246                 value = va_arg (args, unsigned long long);
247                 break;
248               case PTRDIFFT:
249                 value = va_arg (args, ptrdiff_t);
250                 break;
251               case SIZET:
252                 value = va_arg (args, size_t);
253                 break;
254               default:
255                 NOT_REACHED ();
256               }
257
258             switch (*format) 
259               {
260               case 'o': b = &base_o; break;
261               case 'u': b = &base_d; break;
262               case 'x': b = &base_x; break;
263               case 'X': b = &base_X; break;
264               default: NOT_REACHED ();
265               }
266             
267             format_integer (value, false, b, &c, output, aux);
268           }
269           break;
270
271         case 'c': 
272           {
273             /* Treat character as single-character string. */
274             char ch = va_arg (args, int);
275             format_string (&ch, 1, &c, output, aux);
276           }
277           break;
278
279         case 's':
280           {
281             /* String conversion. */
282             const char *s = va_arg (args, char *);
283             if (s == NULL)
284               s = "(null)";
285
286             /* Limit string length according to precision.
287                Note: if c.precision == -1 then strnlen() will get
288                SIZE_MAX for MAXLEN, which is just what we want. */
289             format_string (s, strnlen (s, c.precision), &c, output, aux);
290           }
291           break;
292           
293         case 'p':
294           {
295             /* Pointer conversion.
296                Format pointers as %#x. */
297             void *p = va_arg (args, void *);
298
299             c.flags = POUND;
300             format_integer ((uintptr_t) p, false, &base_x, &c, output, aux);
301           }
302           break;
303       
304         case 'f':
305         case 'e':
306         case 'E':
307         case 'g':
308         case 'G':
309         case 'n':
310           /* We don't support floating-point arithmetic,
311              and %n can be part of a security hole. */
312           __printf ("<<no %%%c in kernel>>", output, aux, *format);
313           break;
314
315         default:
316           __printf ("<<no %%%c conversion>>", output, aux, *format);
317           break;
318         }
319     }
320 }
321
322 /* Parses conversion option characters starting at FORMAT and
323    initializes C appropriately.  Returns the character in FORMAT
324    that indicates the conversion (e.g. the `d' in `%d').  Uses
325    *ARGS for `*' field widths and precisions. */
326 static const char *
327 parse_conversion (const char *format, struct printf_conversion *c,
328                   va_list *args) 
329 {
330   /* Parse flag characters. */
331   c->flags = 0;
332   for (;;) 
333     {
334       switch (*format++) 
335         {
336         case '-':
337           c->flags |= MINUS;
338           break;
339         case '+':
340           c->flags |= PLUS;
341           break;
342         case ' ':
343           c->flags |= SPACE;
344           break;
345         case '#':
346           c->flags |= POUND;
347           break;
348         case '0':
349           c->flags |= ZERO;
350           break;
351         case '\'':
352           c->flags |= GROUP;
353           break;
354         default:
355           format--;
356           goto not_a_flag;
357         }
358     }
359  not_a_flag:
360   if (c->flags & MINUS)
361     c->flags &= ~ZERO;
362   if (c->flags & PLUS)
363     c->flags &= ~SPACE;
364
365   /* Parse field width. */
366   c->width = 0;
367   if (*format == '*')
368     {
369       format++;
370       c->width = va_arg (*args, int);
371     }
372   else 
373     {
374       for (; isdigit (*format); format++)
375         c->width = c->width * 10 + *format - '0';
376     }
377   if (c->width < 0) 
378     {
379       c->width = -c->width;
380       c->flags |= MINUS;
381     }
382       
383   /* Parse precision. */
384   c->precision = -1;
385   if (*format == '.') 
386     {
387       format++;
388       if (*format == '*') 
389         {
390           format++;
391           c->precision = va_arg (*args, int);
392         }
393       else 
394         {
395           c->precision = 0;
396           for (; isdigit (*format); format++)
397             c->precision = c->precision * 10 + *format - '0';
398         }
399       if (c->precision < 0) 
400         c->precision = -1;
401     }
402   if (c->precision >= 0)
403     c->flags &= ~ZERO;
404
405   /* Parse type. */
406   c->type = INT;
407   switch (*format++) 
408     {
409     case 'h':
410       if (*format == 'h') 
411         {
412           format++;
413           c->type = CHAR;
414         }
415       else
416         c->type = SHORT;
417       break;
418       
419     case 'j':
420       c->type = INTMAX;
421       break;
422
423     case 'l':
424       if (*format == 'l')
425         {
426           format++;
427           c->type = LONGLONG;
428         }
429       else
430         c->type = LONG;
431       break;
432
433     case 't':
434       c->type = PTRDIFFT;
435       break;
436
437     case 'z':
438       c->type = SIZET;
439       break;
440
441     default:
442       format--;
443       break;
444     }
445
446   return format;
447 }
448
449 /* Performs an integer conversion, writing output to OUTPUT with
450    auxiliary data AUX.  The integer converted has absolute value
451    VALUE.  If NEGATIVE is true the value is negative, otherwise
452    positive.  The output will use the given DIGITS, with
453    strlen(DIGITS) indicating the output base.  Details of the
454    conversion are in C. */
455 static void
456 format_integer (uintmax_t value, bool negative, const struct integer_base *b,
457                 const struct printf_conversion *c,
458                 void (*output) (char, void *), void *aux)
459 {
460   char buf[64], *cp;            /* Buffer and current position. */
461   const char *signifier;        /* b->signifier or "". */
462   int precision;                /* Rendered precision. */
463   int pad_cnt;                  /* # of pad characters to fill field width. */
464   int group_cnt;                /* # of digits grouped so far. */
465
466   /* Accumulate digits into buffer.
467      This algorithm produces digits in reverse order, so later we
468      will output the buffer's content in reverse.  This is also
469      the reason that later we append zeros and the sign. */
470   cp = buf;
471   group_cnt = 0;
472   while (value > 0) 
473     {
474       if ((c->flags & GROUP) && group_cnt++ == b->group) 
475         {
476           *cp++ = ',';
477           group_cnt = 0; 
478         }
479       *cp++ = b->digits[value % b->base];
480       value /= b->base;
481     }
482
483   /* Append enough zeros to match precision.
484      If requested precision is 0, then a value of zero is
485      rendered as a null string, otherwise as "0". */
486   precision = c->precision < 0 ? 1 : c->precision;
487   if (precision < 0)
488     precision = 1;
489   while (cp - buf < precision && cp - buf < (int) sizeof buf - 8)
490     *cp++ = '0';
491
492   /* Append sign. */
493   if (c->flags & PLUS)
494     *cp++ = negative ? '-' : '+';
495   else if (c->flags & SPACE)
496     *cp++ = negative ? '-' : ' ';
497   else if (negative)
498     *cp++ = '-';
499
500   /* Calculate number of pad characters to fill field width. */
501   signifier = c->flags & POUND ? b->signifier : "";
502   pad_cnt = c->width - (cp - buf) - strlen (signifier);
503   if (pad_cnt < 0)
504     pad_cnt = 0;
505
506   /* Do output. */
507   if ((c->flags & (MINUS | ZERO)) == 0)
508     output_dup (' ', pad_cnt, output, aux);
509   while (*signifier != '\0')
510     output (*signifier++, aux);
511   if (c->flags & ZERO)
512     output_dup ('0', pad_cnt, output, aux);
513   while (cp > buf)
514     output (*--cp, aux);
515   if (c->flags & MINUS)
516     output_dup (' ', pad_cnt, output, aux);
517 }
518
519 /* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */
520 static void
521 output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux) 
522 {
523   while (cnt-- > 0)
524     output (ch, aux);
525 }
526
527 /* Formats the LENGTH characters starting at STRING according to
528    the conversion specified in C.  Writes output to OUTPUT with
529    auxiliary data AUX. */
530 static void
531 format_string (const char *string, size_t length,
532                struct printf_conversion *c,
533                void (*output) (char, void *), void *aux) 
534 {
535   if (c->width > 1 && (c->flags & MINUS) == 0)
536     output_dup (' ', c->width - 1, output, aux);
537   while (length-- > 0)
538     output (*string++, aux);
539   if (c->width > 1 && (c->flags & MINUS) != 0)
540     output_dup (' ', c->width - 1, output, aux);
541 }
542
543 /* Wrapper for __vprintf() that converts varargs into a
544    va_list. */
545 void
546 __printf (const char *format,
547           void (*output) (char, void *), void *aux, ...) 
548 {
549   va_list args;
550
551   va_start (args, aux);
552   __vprintf (format, args, output, aux);
553   va_end (args);
554 }
555
556 /* Writes string S to the console, followed by a new-line
557    character. */
558 int
559 puts (const char *s) 
560 {
561   while (*s != '\0')
562     putchar (*s++);
563   putchar ('\n');
564   return 0;
565 }
566 \f
567 /* Dumps the SIZE bytes in BUFFER to the console as hex bytes
568    arranged 16 per line.  If ASCII is true then the corresponding
569    ASCII characters are also rendered alongside. */   
570 void
571 hex_dump (const void *buffer, size_t size, bool ascii)
572 {
573   const size_t n_per_line = 16; /* Maximum bytes per line. */
574   size_t n;                     /* Number of bytes in this line. */
575   const uint8_t *p;             /* Start of current line in buffer. */
576
577   for (p = buffer; p < (uint8_t *) buffer + size; p += n) 
578     {
579       size_t i;
580
581       /* Number of bytes on this line. */
582       n = (uint8_t *) (buffer + size) - p;
583       if (n > n_per_line)
584         n = n_per_line;
585
586       /* Print line. */
587       for (i = 0; i < n; i++) 
588         printf ("%c%02x", i == n_per_line / 2 ? '-' : ' ', (unsigned) p[i]);
589       if (ascii) 
590         {
591           for (; i < n_per_line; i++)
592             printf ("   ");
593           printf (" |");
594           for (i = 0; i < n; i++)
595             printf ("%c", isprint (p[i]) ? p[i] : '.');
596           for (; i < n_per_line; i++)
597             printf (" ");
598           printf ("|");
599         }
600       printf ("\n");
601     }
602 }