Redo makefiles.
[pintos-anon] / src / lib / lib.c
index 42ca2fd69bdd25c66b33d1d5601fcea1a6b46cf8..91fb318f2c8ebaf5898019c32f1b802e1d429147 100644 (file)
@@ -1,64 +1,88 @@
+#include "lib.h"
 #include <stdint.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include "debug.h"
-#include "interrupt.h"
-#include "lib.h"
-#include "serial.h"
-#include "vga.h"
+#include "devices/serial.h"
+#include "devices/vga.h"
+#include "threads/interrupt.h"
+
+static void
+vprintf_core (const char *format, va_list args,
+              void (*output) (char, void *), void *aux);
+\f
+/* <string.h> */
 
+/* Sets the SIZE bytes in DST to VALUE. */
 void *
-memset (void *dst_, int value, size_t cnt
+memset (void *dst_, int value, size_t size
 {
   unsigned char *dst = dst_;
+
+  ASSERT (dst != NULL || size == 0);
   
-  while (cnt-- > 0)
+  while (size-- > 0)
     *dst++ = value;
 
   return dst_;
 }
 
+/* Copies SIZE bytes from SRC to DST, which must not overlap.
+   Returns DST. */
 void *
-memcpy (void *dst_, const void *src_, size_t cnt
+memcpy (void *dst_, const void *src_, size_t size
 {
   unsigned char *dst = dst_;
   const unsigned char *src = src_;
 
-  while (cnt-- > 0)
+  ASSERT (dst != NULL || size == 0);
+  ASSERT (src != NULL || size == 0);
+
+  while (size-- > 0)
     *dst++ = *src++;
 
   return dst_;
 }
 
+/* Copies SIZE bytes from SRC to DST, which are allowed to
+   overlap.  Returns DST. */
 void *
-memmove (void *dst_, const void *src_, size_t cnt
+memmove (void *dst_, const void *src_, size_t size
 {
   unsigned char *dst = dst_;
   const unsigned char *src = src_;
 
+  ASSERT (dst != NULL || size == 0);
+  ASSERT (src != NULL || size == 0);
+
   if (dst < src) 
     {
-      while (cnt-- > 0)
+      while (size-- > 0)
         *dst++ = *src++;
     }
   else 
     {
-      dst += cnt;
-      src += cnt;
-      while (cnt-- > 0)
+      dst += size;
+      src += size;
+      while (size-- > 0)
         *--dst = *--src;
     }
 
   return dst;
 }
 
+/* Returns a pointer to the first occurrence of CH in the first
+   SIZE bytes starting at BLOCK.  Returns a null pointer if CH
+   does not occur in BLOCK. */
 void *
 memchr (const void *block_, int ch_, size_t size) 
 {
   const unsigned char *block = block_;
   unsigned char ch = ch_;
 
+  ASSERT (block != NULL || size == 0);
+
   for (; size-- > 0; block++)
     if (*block == ch)
       return (void *) block;
@@ -66,120 +90,289 @@ memchr (const void *block_, int ch_, size_t size)
   return NULL;
 }
 
+/* Find the first differing byte in the two blocks of SIZE bytes
+   at A and B.  Returns a positive value if the byte in A is
+   greater, a negative value if the byte in B is greater, or zero
+   if blocks A and B are equal. */
 int
 memcmp (const void *a_, const void *b_, size_t size) 
 {
   const unsigned char *a = a_;
   const unsigned char *b = b_;
 
+  ASSERT (a != NULL || size == 0);
+  ASSERT (b != NULL || size == 0);
+
   for (; size-- > 0; a++, b++)
     if (*a != *b)
       return *a > *b ? +1 : -1;
   return 0;
 }
 
+/* Finds and returns the first occurrence of C in STRING, or a
+   null pointer if C does not appear in STRING.  If C == '\0'
+   then returns a pointer to the null terminator at the end of
+   STRING. */
+char *
+strchr (const char *string, int c_) 
+{
+  char c = c_;
+
+  ASSERT (string != NULL);
+
+  for (;;) 
+    if (*string == c)
+      return (char *) string;
+    else if (*string == '\0')
+      return NULL;
+    else
+      string++;
+}
+
+/* Copies string SRC to DST.  If SRC is longer than SIZE - 1
+   characters, only SIZE - 1 characters are copied.  A null
+   terminator is always written to DST, unless SIZE is 0.
+   Returns the length of SRC.
+
+   strlcpy() is not in the standard C library, but it is an
+   increasingly popular extension.  See
+   http://www.courtesan.com/todd/papers/strlcpy.html for
+   information on strlcpy(). */
 size_t
 strlcpy (char *dst, const char *src, size_t size) 
 {
-  size_t src_len = strlen (src);
+  size_t src_len;
+
+  ASSERT (dst != NULL);
+  ASSERT (src != NULL);
+
+  src_len = strlen (src);
   if (size > 0) 
     {
-      size_t dst_len_max = size - 1;
-      size_t dst_len = src_len < dst_len_max ? src_len : dst_len_max;
+      size_t dst_len = size - 1;
+      if (src_len < dst_len)
+        src_len = dst_len;
       memcpy (dst, src, dst_len);
       dst[dst_len] = '\0';
     }
   return src_len;
 }
 
+/* Returns the length of STRING. */
 size_t
 strlen (const char *string) 
 {
   const char *p;
 
+  ASSERT (string != NULL);
+
   for (p = string; *p != '\0'; p++)
     continue;
   return p - string;
 }
 
-char *
-strchr (const char *string, int c_) 
+/* If STRING is less than MAXLEN characters in length, returns
+   its actual length.  Otherwise, returns MAXLEN. */
+size_t
+strnlen (const char *string, size_t maxlen) 
 {
-  char c = c_;
+  size_t length;
 
-  for (;;) 
-    if (*string == c)
-      return (char *) string;
-    else if (*string == '\0')
-      return NULL;
-    else string++;
+  for (length = 0; string[length] != '\0' && length < maxlen; length++)
+    continue;
+  return length;
 }
-\f
-static void
-vprintf_core (const char *format, va_list args,
-              void (*output) (char, void *), void *aux);
 
-static void
-vprintk_helper (char ch, void *aux __attribute__ ((unused))) 
+/* Finds the first differing characters in strings A and B.
+   Returns a positive value if the character in A (as an unsigned
+   char) is greater, a negative value if the character in B (as
+   an unsigned char) is greater, or zero if strings A and B are
+   equal. */
+int
+strcmp (const char *a_, const char *b_) 
 {
-  vga_putc (ch);
-  serial_outb (ch);
+  const unsigned char *a = (const unsigned char *) a_;
+  const unsigned char *b = (const unsigned char *) b_;
+
+  ASSERT (a != NULL);
+  ASSERT (b != NULL);
+
+  while (*a != '\0' && *a == *b) 
+    {
+      a++;
+      b++;
+    }
+
+  return *a < *b ? -1 : *a > *b;
 }
 
-void
-vprintk (const char *format, va_list args) 
+/* Breaks a string into tokens separated by DELIMITERS.  The
+   first time this function is called, S should be the string to
+   tokenize, and in subsequent calls it must be a null pointer.
+   SAVE_PTR is the address of a `char *' variable used to keep
+   track of the tokenizer's position.  The return value each time
+   is the next token in the string, or a null pointer if no
+   tokens remain.
+
+   This function treats multiple adjacent delimiters as a single
+   delimiter.  The returned tokens will never be length 0.
+   DELIMITERS may change from one call to the next within a
+   single string.
+
+   strtok_r() modifies the string S, changing delimiters to null
+   bytes.  Thus, S must be a modifiable string.
+
+   Example usage:
+
+   char s[] = "  String to  tokenize. ";
+   char *token, *save_ptr;
+
+   for (token = strtok_r (s, " ", &save_ptr); token != NULL;
+        token = strtok_r (NULL, " ", &save_ptr))
+     printf ("'%s'\n", token);
+
+   outputs:
+
+     'String'
+     'to'
+     'tokenize.'
+*/
+char *
+strtok_r (char *s, const char *delimiters, char **save_ptr) 
 {
-  enum if_level old_level = intr_disable ();
-  vprintf_core (format, args, vprintk_helper, NULL);
-  intr_set_level (old_level);
+  char *token;
+  
+  ASSERT (delimiters != NULL);
+  ASSERT (save_ptr != NULL);
+
+  /* If S is nonnull, start from it.
+     If S is null, start from saved position. */
+  if (s == NULL)
+    s = *save_ptr;
+  ASSERT (s != NULL);
+
+  /* Skip any DELIMITERS at our current position. */
+  while (strchr (delimiters, *s) != NULL) 
+    {
+      /* strchr() will always return nonnull if we're searching
+         for a null byte, because every string contains a null
+         byte (at the end). */
+      if (*s == '\0')
+        {
+          *save_ptr = s;
+          return NULL;
+        }
+
+      s++;
+    }
+
+  /* Skip any non-DELIMITERS up to the end of the string. */
+  token = s;
+  while (strchr (delimiters, *s) == NULL)
+    s++;
+  if (*s != '\0') 
+    {
+      *s = '\0';
+      *save_ptr = s + 1;
+    }
+  else 
+    *save_ptr = s;
+  return token;
 }
+\f
+/* <stdlib.h> */
 
-void
-printk (const char *format, ...) 
+/* Converts a string representation of a signed decimal integer
+   in S into an `int', which is returned. */
+int
+atoi (const char *s) 
 {
-  va_list args;
+  bool negative;
+  int value;
+
+  /* Skip white space. */
+  while (isspace (*s))
+    s++;
+
+  /* Parse sign. */
+  negative = false;
+  if (*s == '+')
+    s++;
+  else if (*s == '-')
+    {
+      negative = true;
+      s++;
+    }
 
-  va_start (args, format);
-  vprintk (format, args);
-  va_end (args);
+  /* Parse digits.  We always initially parse the value as
+     negative, and then make it positive later, because the
+     negative range of an int is bigger than the positive range
+     on a 2's complement system. */
+  for (value = 0; isdigit (*s); s++)
+    value = value * 10 - (*s - '0');
+  if (!negative)
+    value = -value;
+
+  return value;
 }
 \f
+/* <stdio.h> */
+
+/* Auxiliary data for vsnprintf_helper(). */
 struct vsnprintf_aux 
   {
-    char *p;
-    int length;
-    int max_length;
+    char *p;            /* Current output position. */
+    int length;         /* Length of output string. */
+    int max_length;     /* Max length of output string. */
   };
 
-static void
-vsnprintf_helper (char ch, void *aux_) 
-{
-  struct vsnprintf_aux *aux = aux_;
-
-  if (aux->length++ < aux->max_length)
-    *aux->p++ = ch;
-}
+static void vsnprintf_helper (char, void *);
 
+/* Like vprintf(), except that output is stored into BUFFER,
+   which must have space for BUF_SIZE characters.  Writes at most
+   BUF_SIZE - 1 characters to BUFFER, followed by a null
+   terminator.  BUFFER will always be null-terminated unless
+   BUF_SIZE is zero.  Returns the number of characters that would
+   have been written to BUFFER, not including a null terminator,
+   had there been enough room. */
 int
-vsnprintf (char *buffer, size_t buf_size,
-           const char *format, va_list args) 
+vsnprintf (char *buffer, size_t buf_size, const char *format, va_list args) 
 {
+  /* Set up aux data for vsnprintf_helper(). */
   struct vsnprintf_aux aux;
   aux.p = buffer;
   aux.length = 0;
   aux.max_length = buf_size > 0 ? buf_size - 1 : 0;
-  
+
+  /* Do most of the work. */
   vprintf_core (format, args, vsnprintf_helper, &aux);
 
+  /* Add null terminator. */
   if (buf_size > 0)
     *aux.p = '\0';
 
   return aux.length;
 }
 
+/* Helper function for vsnprintf(). */
+static void
+vsnprintf_helper (char ch, void *aux_)
+{
+  struct vsnprintf_aux *aux = aux_;
+
+  if (aux->length++ < aux->max_length)
+    *aux->p++ = ch;
+}
+
+/* Like printf(), except that output is stored into BUFFER,
+   which must have space for BUF_SIZE characters.  Writes at most
+   BUF_SIZE - 1 characters to BUFFER, followed by a null
+   terminator.  BUFFER will always be null-terminated unless
+   BUF_SIZE is zero.  Returns the number of characters that would
+   have been written to BUFFER, not including a null terminator,
+   had there been enough room. */
 int
-snprintf (char *buffer, size_t buf_size,
-          const char *format, ...) 
+snprintf (char *buffer, size_t buf_size, const char *format, ...) 
 {
   va_list args;
   int retval;
@@ -191,39 +384,283 @@ snprintf (char *buffer, size_t buf_size,
   return retval;
 }
 \f
-/* printf() and friends internals.  You do not need to understand
-   this code. */
+/* Nonstandard functions. */
+
+static void vprintk_helper (char, void *);
+
+/* Like vprintf(), except that output is written to the system
+   console, which is defined as the video display and the first
+   serial port (at the same time). */
+void
+vprintk (const char *format, va_list args) 
+{
+  enum intr_level old_level = intr_disable ();
+  vprintf_core (format, args, vprintk_helper, NULL);
+  intr_set_level (old_level);
+}
+
+/* Helper function for vprintk(). */
+static void
+vprintk_helper (char ch, void *aux UNUSED) 
+{
+  vga_putc (ch);
+  serial_outb (ch);
+}
+
+/* Like printf(), except that output is written to the system
+   console, which is defined as the video display and the first
+   serial port (at the same time). */
+void
+printk (const char *format, ...) 
+{
+  va_list args;
+
+  va_start (args, format);
+  vprintk (format, args);
+  va_end (args);
+}
+\f
+/* printf() formatting internals. */
 
+/* A printf() conversion. */
 struct printf_conversion 
   {
+    /* Flags. */
     enum 
       {
-        MINUS = 1 << 0,
-        PLUS = 1 << 1,
-        SPACE = 1 << 2,
-        POUND = 1 << 3,
-        ZERO = 1 << 4,
-        GROUP = 1 << 5
+        MINUS = 1 << 0,         /* '-' */
+        PLUS = 1 << 1,          /* '+' */
+        SPACE = 1 << 2,         /* ' ' */
+        POUND = 1 << 3,         /* '#' */
+        ZERO = 1 << 4,          /* '0' */
+        GROUP = 1 << 5          /* '\'' */
       }
     flags;
 
+    /* Minimum field width. */
     int width;
+
+    /* Numeric precision.
+       -1 indicates no precision was specified. */
     int precision;
 
+    /* Type of argument to format. */
     enum 
       {
-        CHAR = 1,
-        SHORT = 2,
-        INT = 3,
-        INTMAX = 4,
-        LONG = 5,
-        LONGLONG = 6,
-        PTRDIFFT = 7,
-        SIZET = 8
+        CHAR = 1,               /* hh */
+        SHORT = 2,              /* h */
+        INT = 3,                /* (none) */
+        INTMAX = 4,             /* j */
+        LONG = 5,               /* l */
+        LONGLONG = 6,           /* ll */
+        PTRDIFFT = 7,           /* t */
+        SIZET = 8               /* z */
       }
     type;
   };
 
+struct integer_base 
+  {
+    int base;                   /* Base. */
+    const char *digits;         /* Collection of digits. */
+    const char *signifier;      /* Prefix used with # flag. */
+    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 char *parse_conversion (const char *format,
+                                     struct printf_conversion *,
+                                     va_list *);
+static void format_integer (uintmax_t value, 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,
+                           struct printf_conversion *,
+                           void (*output) (char, void *), void *aux);
+static void printf_core (const char *format,
+                         void (*output) (char, void *), void *aux, ...);
+
+static void
+vprintf_core (const char *format, va_list args,
+              void (*output) (char, void *), void *aux)
+{
+  for (; *format != '\0'; format++)
+    {
+      struct printf_conversion c;
+
+      /* Literally copy non-conversions to output. */
+      if (*format != '%') 
+        {
+          output (*format, aux);
+          continue;
+        }
+      format++;
+
+      /* %% => %. */
+      if (*format == '%') 
+        {
+          output ('%', aux);
+          continue;
+        }
+
+      /* Parse conversion specifiers. */
+      format = parse_conversion (format, &c, &args);
+
+      /* Do conversion. */
+      switch (*format) 
+        {
+        case 'd':
+        case 'i': 
+          {
+            /* Signed integer conversions. */
+            intmax_t value;
+            
+            switch (c.type) 
+              {
+              case CHAR: 
+                value = (signed char) va_arg (args, int);
+                break;
+              case SHORT:
+                value = (short) va_arg (args, int);
+                break;
+              case INT:
+                value = va_arg (args, int);
+                break;
+              case LONG:
+                value = va_arg (args, long);
+                break;
+              case LONGLONG:
+                value = va_arg (args, long long);
+                break;
+              case PTRDIFFT:
+                value = va_arg (args, ptrdiff_t);
+                break;
+              case SIZET:
+                value = va_arg (args, size_t);
+                break;
+              default:
+                NOT_REACHED ();
+              }
+
+            format_integer (value < 0 ? -value : value,
+                            value < 0, &base_d, &c, output, aux);
+          }
+          break;
+          
+        case 'o':
+        case 'u':
+        case 'x':
+        case 'X':
+          {
+            /* Unsigned integer conversions. */
+            uintmax_t value;
+            const struct integer_base *b;
+
+            switch (c.type) 
+              {
+              case CHAR: 
+                value = (unsigned char) va_arg (args, unsigned);
+                break;
+              case SHORT:
+                value = (unsigned short) va_arg (args, unsigned);
+                break;
+              case INT:
+                value = va_arg (args, unsigned);
+                break;
+              case LONG:
+                value = va_arg (args, unsigned long);
+                break;
+              case LONGLONG:
+                value = va_arg (args, unsigned long long);
+                break;
+              case PTRDIFFT:
+                value = va_arg (args, ptrdiff_t);
+                break;
+              case SIZET:
+                value = va_arg (args, size_t);
+                break;
+              default:
+                NOT_REACHED ();
+              }
+
+            switch (*format) 
+              {
+              case 'o': b = &base_o; break;
+              case 'u': b = &base_d; break;
+              case 'x': b = &base_x; break;
+              case 'X': b = &base_X; break;
+              default: NOT_REACHED ();
+              }
+            
+            format_integer (value, false, b, &c, output, aux);
+          }
+          break;
+
+        case 'c': 
+          {
+            /* Treat character as single-character string. */
+            char ch = va_arg (args, int);
+            format_string (&ch, 1, &c, output, aux);
+          }
+          break;
+
+        case 's':
+          {
+            /* String conversion. */
+            const char *s = va_arg (args, char *);
+            if (s == NULL)
+              s = "(null)";
+
+            /* Limit string length according to precision.
+               Note: if c.precision == -1 then strnlen() will get
+               SIZE_MAX for MAXLEN, which is just what we want. */
+            format_string (s, strnlen (s, c.precision), &c, output, aux);
+          }
+          break;
+          
+        case 'p':
+          {
+            /* Pointer conversion.
+               Format non-null pointers as %#x. */
+            void *p = va_arg (args, void *);
+
+            c.flags = POUND;
+            if (p != NULL) 
+              format_integer ((uintptr_t) p, false, &base_x, &c, output, aux);
+            else
+              format_string ("(nil)", 5, &c, output, aux); 
+          }
+          break;
+      
+        case 'f':
+        case 'e':
+        case 'E':
+        case 'g':
+        case 'G':
+        case 'n':
+          /* We don't support floating-point arithmetic,
+             and %n can be part of a security hole. */
+          printf_core ("<<no %%%c in kernel>>", output, aux, *format);
+          break;
+
+        default:
+          printf_core ("<<no %%%c conversion>>", output, aux, *format);
+          break;
+        }
+    }
+}
+
+/* Parses conversion option characters starting at FORMAT and
+   initializes C appropriately.  Returns the character in FORMAT
+   that indicates the conversion (e.g. the `d' in `%d').  Uses
+   *ARGS for `*' field widths and precisions. */
 static const char *
 parse_conversion (const char *format, struct printf_conversion *c,
                   va_list *args) 
@@ -331,17 +768,11 @@ parse_conversion (const char *format, struct printf_conversion *c,
         c->type = LONG;
       break;
 
-    case 'L':
-    case 'q':
-      c->type = LONGLONG;
-      break;
-
     case 't':
       c->type = PTRDIFFT;
       break;
 
     case 'z':
-    case 'Z':
       c->type = SIZET;
       break;
 
@@ -353,45 +784,47 @@ parse_conversion (const char *format, struct printf_conversion *c,
   return format;
 }
 
+/* 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. */
 static void
-output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux) 
-{
-  while (cnt-- > 0)
-    output (ch, aux);
-}
-
-static void
-printf_integer (uintmax_t value, bool negative, const char *digits,
-                struct printf_conversion *c,
+format_integer (uintmax_t value, bool negative, const struct integer_base *b,
+                const struct printf_conversion *c,
                 void (*output) (char, void *), void *aux)
-
 {
-  char buf[64], *cp;
-  int base;
-  const char *base_name;
-  int pad_cnt;
-
-  base = strlen (digits);
+  char buf[64], *cp;            /* Buffer and current position. */
+  const char *signifier;        /* b->signifier or "". */
+  int precision;                /* Rendered precision. */
+  int pad_cnt;                  /* # of pad characters to fill field width. */
+  int group_cnt;                /* # of digits grouped so far. */
 
   /* 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. */
   cp = buf;
+  group_cnt = 0;
   while (value > 0) 
     {
-      if ((c->flags & GROUP) && cp > buf && (cp - buf) % 3 == 0)
-        *cp++ = ',';
-      *cp++ = digits[value % base];
-      value /= base;
+      if ((c->flags & GROUP) && group_cnt++ == b->group) 
+        {
+          *cp++ = ',';
+          group_cnt = 0; 
+        }
+      *cp++ = b->digits[value % b->base];
+      value /= b->base;
     }
 
   /* Append enough zeros to match precision.
-     If precision is 0, then a value of zero is rendered as a
-     null string.  Otherwise at least one digit is presented. */
-  if (c->precision < 0)
-    c->precision = 1;
-  while (cp - buf < c->precision && cp - buf < (int) sizeof buf - 8)
+     If requested precision is 0, then a value of zero is
+     rendered as a null string, otherwise as "0". */
+  precision = c->precision < 0 ? 1 : c->precision;
+  if (precision < 0)
+    precision = 1;
+  while (cp - buf < precision && cp - buf < (int) sizeof buf - 8)
     *cp++ = '0';
 
   /* Append sign. */
@@ -402,26 +835,17 @@ printf_integer (uintmax_t value, bool negative, const char *digits,
   else if (negative)
     *cp++ = '-';
 
-  /* Get name of base. */
-  base_name = "";
-  if (c->flags & POUND) 
-    {
-      if (base == 8)
-        base_name = "0";
-      else if (base == 16)
-        base_name = digits[0xa] == 'a' ? "0x" : "0X";
-    }
-
   /* Calculate number of pad characters to fill field width. */
-  pad_cnt = c->width - (cp - buf) - strlen (base_name);
+  signifier = c->flags & POUND ? b->signifier : "";
+  pad_cnt = c->width - (cp - buf) - strlen (signifier);
   if (pad_cnt < 0)
     pad_cnt = 0;
 
   /* Do output. */
   if ((c->flags & (MINUS | ZERO)) == 0)
     output_dup (' ', pad_cnt, output, aux);
-  while (*base_name != '\0')
-    output (*base_name++, aux);
+  while (*signifier != '\0')
+    output (*signifier++, aux);
   if (c->flags & ZERO)
     output_dup ('0', pad_cnt, output, aux);
   while (cp > buf)
@@ -430,8 +854,19 @@ printf_integer (uintmax_t value, bool negative, const char *digits,
     output_dup (' ', pad_cnt, output, aux);
 }
 
+/* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */
 static void
-printf_string (const char *string, size_t length,
+output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux) 
+{
+  while (cnt-- > 0)
+    output (ch, aux);
+}
+
+/* Formats the LENGTH characters starting at STRING according to
+   the conversion specified in C.  Writes output to OUTPUT with
+   auxiliary data AUX. */
+static void
+format_string (const char *string, size_t length,
                struct printf_conversion *c,
                void (*output) (char, void *), void *aux) 
 {
@@ -443,6 +878,8 @@ printf_string (const char *string, size_t length,
     output_dup (' ', c->width - 1, output, aux);
 }
 
+/* Wrapper for vprintf_core() that converts varargs into a
+   va_list. */
 static void
 printf_core (const char *format,
              void (*output) (char, void *), void *aux, ...) 
@@ -453,224 +890,40 @@ printf_core (const char *format,
   vprintf_core (format, args, output, aux);
   va_end (args);
 }
-
-static void
-vprintf_core (const char *format, va_list args,
-              void (*output) (char, void *), void *aux)
-{
-  for (; *format != '\0'; format++)
-    {
-      struct printf_conversion c;
-
-      /* Literally copy non-conversions to output. */
-      if (*format != '%') 
-        {
-          output (*format, aux);
-          continue;
-        }
-      format++;
-
-      /* %% => %. */
-      if (*format == '%') 
-        {
-          output ('%', aux);
-          continue;
-        }
-
-      format = parse_conversion (format, &c, &args);
-      switch (*format) 
-        {
-        case 'd':
-        case 'i': 
-          {
-            intmax_t value;
-            uintmax_t abs_value;
-            bool negative = false;
-
-            switch (c.type) 
-              {
-              case CHAR: 
-                value = (signed char) va_arg (args, int);
-                break;
-              case SHORT:
-                value = (short) va_arg (args, int);
-                break;
-              case INT:
-                value = va_arg (args, int);
-                break;
-              case LONG:
-                value = va_arg (args, long);
-                break;
-              case LONGLONG:
-                value = va_arg (args, long long);
-                break;
-              case PTRDIFFT:
-                value = va_arg (args, ptrdiff_t);
-                break;
-              case SIZET:
-                value = va_arg (args, size_t);
-                break;
-              default:
-                NOT_REACHED ();
-              }
-
-            if (value < 0) 
-              {
-                negative = true;
-                abs_value = -value;
-              }
-            else
-              abs_value = value;
-
-            printf_integer (abs_value, negative, "0123456789",
-                            &c, output, aux);
-          }
-          break;
-          
-        case 'o':
-        case 'u':
-        case 'x':
-        case 'X':
-          {
-            uintmax_t value;
-            const char *digits;
-
-            switch (c.type) 
-              {
-              case CHAR: 
-                value = (unsigned char) va_arg (args, unsigned);
-                break;
-              case SHORT:
-                value = (unsigned short) va_arg (args, unsigned);
-                break;
-              case INT:
-                value = va_arg (args, unsigned);
-                break;
-              case LONG:
-                value = va_arg (args, unsigned long);
-                break;
-              case LONGLONG:
-                value = va_arg (args, unsigned long long);
-                break;
-              case PTRDIFFT:
-                value = va_arg (args, ptrdiff_t);
-                break;
-              case SIZET:
-                value = va_arg (args, size_t);
-                break;
-              default:
-                NOT_REACHED ();
-              }
-
-            switch (*format) 
-              {
-              case 'o':
-                digits = "01234567";
-                break;
-              case 'u':
-                digits = "0123456789";
-                break;
-              case 'x':
-                digits = "0123456789abcdef";
-                break;
-              case 'X':
-                digits = "0123456789ABCDEF";
-                break;
-              default:
-                NOT_REACHED ();
-              }
-            
-            printf_integer (value, false, digits, &c, output, aux);
-          }
-          break;
-
-        case 'c': 
-          {
-            char ch = va_arg (args, int);
-            printf_string (&ch, 1, &c, output, aux);
-          }
-          break;
-
-        case 's':
-          {
-            const char *s;
-            size_t length;
-            
-            s = va_arg (args, char *);
-            if (s == NULL)
-              s = "(null)";
-
-            if (c.precision >= 0) 
-              {
-                const char *zero = memchr (s, '\0', c.precision);
-                if (zero != NULL)
-                  length = zero - s;
-                else
-                  length = c.precision;
-              }
-            else
-              length = strlen (s);
-
-            printf_string (s, length, &c, output, aux);
-          }
-          break;
-          
-        case 'p':
-          {
-            void *p = va_arg (args, void *);
-
-            c.flags = POUND;
-            if (p != NULL) 
-              printf_integer ((uintptr_t) p,
-                              false, "0123456789abcdef", &c,
-                              output, aux); 
-            else
-              printf_string ("(nil)", 5, &c, output, aux); 
-          }
-          break;
-      
-        case 'f':
-        case 'e':
-        case 'E':
-        case 'g':
-        case 'G':
-        case 'n':
-          printf_core ("<<no %%%c in kernel>>", output, aux, *format);
-          break;
-
-        default:
-          printf_core ("<<no %%%c conversion>>", output, aux, *format);
-          break;
-        }
-    }
-}
 \f
+/* Dumps the SIZE bytes in BUFFER to the console as hex bytes
+   arranged 16 per line.  If ASCII is true then the corresponding
+   ASCII characters are also rendered alongside. */   
 void
-hex_dump (const void *buffer, size_t size
+hex_dump (const void *buffer, size_t size, bool ascii)
 {
-  const size_t n_per_line = 16;
-  const uint8_t *p = buffer;
-  size_t ofs = 0;
+  const size_t n_per_line = 16; /* Maximum bytes per line. */
+  size_t n;                     /* Number of bytes in this line. */
+  const uint8_t *p;             /* Start of current line in buffer. */
 
-  while (size > 0
+  for (p = buffer; p < (uint8_t *) buffer + size; p += n
     {
-      size_t n, i;
+      size_t i;
 
-      printk ("%08zx", ofs);
-      n = size >= n_per_line ? n_per_line : size;
+      /* Number of bytes on this line. */
+      n = (uint8_t *) (buffer + size) - p;
+      if (n > n_per_line)
+        n = n_per_line;
+
+      /* Print line. */
       for (i = 0; i < n; i++) 
-        printk ("%c%02x", i == n / 2 ? '-' : ' ', (unsigned) p[i]);
-      for (; i < n_per_line; i++)
-        printk ("   ");
-      printk (" |");
-      for (i = 0; i < n; i++)
-        printk ("%c", isprint (p[i]) ? p[i] : '.');
-      for (; i < n_per_line; i++)
-        printk (" ");
-      printk ("|\n");
-
-      p += n;
-      ofs += n;
-      size -= n;
+        printk ("%c%02x", i == n_per_line / 2 ? '-' : ' ', (unsigned) p[i]);
+      if (ascii) 
+        {
+          for (; i < n_per_line; i++)
+            printk ("   ");
+          printk (" |");
+          for (i = 0; i < n; i++)
+            printk ("%c", isprint (p[i]) ? p[i] : '.');
+          for (; i < n_per_line; i++)
+            printk (" ");
+          printk ("|");
+        }
+      printk ("\n");
     }
 }