Implement a proper block layer with partition support.
[pintos-anon] / src / lib / stdio.c
index 4b0ba1967f2698bbce124fd4cad9bfb3ac17ff95..8927c50555d9993ae1ac952e21bc3fe7deb93f7e 100644 (file)
@@ -74,8 +74,7 @@ snprintf (char *buffer, size_t buf_size, const char *format, ...)
 /* Writes formatted output to the console.
    In the kernel, the console is both the video display and first
    serial port.
-   In userspace, the console is file descriptor 1.
-*/
+   In userspace, the console is file descriptor 1. */
 int
 printf (const char *format, ...) 
 {
@@ -132,25 +131,25 @@ struct integer_base
   {
     int base;                   /* Base. */
     const char *digits;         /* Collection of digits. */
-    const char *signifier;      /* Prefix used with # flag. */
+    int x;                      /* `x' character to use, for base 16 only. */
     int group;                  /* Number of digits to group with ' flag. */
   };
 
-static const struct integer_base base_d = {10, "0123456789", "", 3};
-static const struct integer_base base_o = {8, "01234567", "0", 3};
-static const struct integer_base base_x = {16, "0123456789abcdef", "0x", 4};
-static const struct integer_base base_X = {16, "0123456789ABCDEF", "0X", 4};
+static const struct integer_base base_d = {10, "0123456789", 0, 3};
+static const struct integer_base base_o = {8, "01234567", 0, 3};
+static const struct integer_base base_x = {16, "0123456789abcdef", 'x', 4};
+static const struct integer_base base_X = {16, "0123456789ABCDEF", 'X', 4};
 
 static const char *parse_conversion (const char *format,
                                      struct printf_conversion *,
                                      va_list *);
-static void format_integer (uintmax_t value, bool negative,
+static void format_integer (uintmax_t value, bool is_signed, bool negative, 
                             const struct integer_base *,
                             const struct printf_conversion *,
                             void (*output) (char, void *), void *aux);
 static void output_dup (char ch, size_t cnt,
                         void (*output) (char, void *), void *aux);
-static void format_string (const char *string, size_t length,
+static void format_string (const char *string, int length,
                            struct printf_conversion *,
                            void (*output) (char, void *), void *aux);
 
@@ -214,13 +213,15 @@ __vprintf (const char *format, va_list args,
                 break;
               case SIZET:
                 value = va_arg (args, size_t);
+                if (value > SIZE_MAX / 2)
+                  value = value - SIZE_MAX - 1;
                 break;
               default:
                 NOT_REACHED ();
               }
 
             format_integer (value < 0 ? -value : value,
-                            value < 0, &base_d, &c, output, aux);
+                            true, value < 0, &base_d, &c, output, aux);
           }
           break;
           
@@ -255,6 +256,9 @@ __vprintf (const char *format, va_list args,
                 break;
               case PTRDIFFT:
                 value = va_arg (args, ptrdiff_t);
+#if UINTMAX_MAX != PTRDIFF_MAX
+                value &= ((uintmax_t) PTRDIFF_MAX << 1) | 1;
+#endif
                 break;
               case SIZET:
                 value = va_arg (args, size_t);
@@ -271,8 +275,8 @@ __vprintf (const char *format, va_list args,
               case 'X': b = &base_X; break;
               default: NOT_REACHED ();
               }
-            
-            format_integer (value, false, b, &c, output, aux);
+
+            format_integer (value, false, false, b, &c, output, aux);
           }
           break;
 
@@ -305,7 +309,8 @@ __vprintf (const char *format, va_list args,
             void *p = va_arg (args, void *);
 
             c.flags = POUND;
-            format_integer ((uintptr_t) p, false, &base_x, &c, output, aux);
+            format_integer ((uintptr_t) p, false, false,
+                            &base_x, &c, output, aux);
           }
           break;
       
@@ -456,66 +461,83 @@ parse_conversion (const char *format, struct printf_conversion *c,
 
 /* Performs an integer conversion, writing output to OUTPUT with
    auxiliary data AUX.  The integer converted has absolute value
-   VALUE.  If NEGATIVE is true the value is negative, otherwise
-   positive.  The output will use the given DIGITS, with
-   strlen(DIGITS) indicating the output base.  Details of the
-   conversion are in C. */
+   VALUE.  If IS_SIGNED is true, does a signed conversion with
+   NEGATIVE indicating a negative value; otherwise does an
+   unsigned conversion and ignores NEGATIVE.  The output is done
+   according to the provided base B.  Details of the conversion
+   are in C. */
 static void
-format_integer (uintmax_t value, bool negative, const struct integer_base *b,
+format_integer (uintmax_t value, bool is_signed, bool negative, 
+                const struct integer_base *b,
                 const struct printf_conversion *c,
                 void (*output) (char, void *), void *aux)
 {
   char buf[64], *cp;            /* Buffer and current position. */
-  const char *signifier;        /* b->signifier or "". */
+  int x;                        /* `x' character to use or 0 if none. */
+  int sign;                     /* Sign character or 0 if none. */
   int precision;                /* Rendered precision. */
   int pad_cnt;                  /* # of pad characters to fill field width. */
-  int group_cnt;                /* # of digits grouped so far. */
+  int digit_cnt;                /* # of digits output so far. */
+
+  /* Determine sign character, if any.
+     An unsigned conversion will never have a sign character,
+     even if one of the flags requests one. */
+  sign = 0;
+  if (is_signed) 
+    {
+      if (c->flags & PLUS)
+        sign = negative ? '-' : '+';
+      else if (c->flags & SPACE)
+        sign = negative ? '-' : ' ';
+      else if (negative)
+        sign = '-';
+    }
+
+  /* Determine whether to include `0x' or `0X'.
+     It will only be included with a hexadecimal conversion of a
+     nonzero value with the # flag. */
+  x = (c->flags & POUND) && value ? b->x : 0;
 
   /* Accumulate digits into buffer.
      This algorithm produces digits in reverse order, so later we
-     will output the buffer's content in reverse.  This is also
-     the reason that later we append zeros and the sign. */
+     will output the buffer's content in reverse. */
   cp = buf;
-  group_cnt = 0;
+  digit_cnt = 0;
   while (value > 0) 
     {
-      if ((c->flags & GROUP) && group_cnt++ == b->group) 
-        {
-          *cp++ = ',';
-          group_cnt = 0; 
-        }
+      if ((c->flags & GROUP) && digit_cnt > 0 && digit_cnt % b->group == 0)
+        *cp++ = ',';
       *cp++ = b->digits[value % b->base];
       value /= b->base;
+      digit_cnt++;
     }
 
   /* Append enough zeros to match precision.
      If requested precision is 0, then a value of zero is
-     rendered as a null string, otherwise as "0". */
+     rendered as a null string, otherwise as "0".
+     If the # flag is used with base 8, the result must always
+     begin with a zero. */
   precision = c->precision < 0 ? 1 : c->precision;
-  if (precision < 0)
-    precision = 1;
-  while (cp - buf < precision && cp - buf < (int) sizeof buf - 8)
+  while (cp - buf < precision && cp < buf + sizeof buf - 1)
+    *cp++ = '0';
+  if ((c->flags & POUND) && b->base == 8 && (cp == buf || cp[-1] != '0'))
     *cp++ = '0';
-
-  /* Append sign. */
-  if (c->flags & PLUS)
-    *cp++ = negative ? '-' : '+';
-  else if (c->flags & SPACE)
-    *cp++ = negative ? '-' : ' ';
-  else if (negative)
-    *cp++ = '-';
 
   /* Calculate number of pad characters to fill field width. */
-  signifier = c->flags & POUND ? b->signifier : "";
-  pad_cnt = c->width - (cp - buf) - strlen (signifier);
+  pad_cnt = c->width - (cp - buf) - (x ? 2 : 0) - (sign != 0);
   if (pad_cnt < 0)
     pad_cnt = 0;
 
   /* Do output. */
   if ((c->flags & (MINUS | ZERO)) == 0)
     output_dup (' ', pad_cnt, output, aux);
-  while (*signifier != '\0')
-    output (*signifier++, aux);
+  if (sign)
+    output (sign, aux);
+  if (x) 
+    {
+      output ('0', aux);
+      output (x, aux); 
+    }
   if (c->flags & ZERO)
     output_dup ('0', pad_cnt, output, aux);
   while (cp > buf)
@@ -536,16 +558,17 @@ output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux)
    the conversion specified in C.  Writes output to OUTPUT with
    auxiliary data AUX. */
 static void
-format_string (const char *string, size_t length,
+format_string (const char *string, int length,
                struct printf_conversion *c,
                void (*output) (char, void *), void *aux) 
 {
-  if (c->width > 1 && (c->flags & MINUS) == 0)
-    output_dup (' ', c->width - 1, output, aux);
-  while (length-- > 0)
-    output (*string++, aux);
-  if (c->width > 1 && (c->flags & MINUS) != 0)
-    output_dup (' ', c->width - 1, output, aux);
+  int i;
+  if (c->width > length && (c->flags & MINUS) == 0)
+    output_dup (' ', c->width - length, output, aux);
+  for (i = 0; i < length; i++)
+    output (string[i], aux);
+  if (c->width > length && (c->flags & MINUS) != 0)
+    output_dup (' ', c->width - length, output, aux);
 }
 
 /* Wrapper for __vprintf() that converts varargs into a
@@ -612,3 +635,21 @@ hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii)
       size -= n;
     }
 }
+
+/* Prints SIZE, which represents a number of bytes, in a
+   human-readable format, e.g. "256 kB". */
+void
+print_human_readable_size (uint64_t size) 
+{
+  if (size == 1)
+    printf ("1 byte");
+  else 
+    {
+      static const char *factors[] = {"bytes", "kB", "MB", "GB", "TB", NULL};
+      const char **fp;
+
+      for (fp = factors; size >= 1024 && fp[1] != NULL; fp++)
+        size /= 1024;
+      printf ("%"PRIu64" %s", size, *fp);
+    }
+}