-#include <ctype.h>
#include <stdio.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <round.h>
#include <stdint.h>
#include <string.h>
/* 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, ...)
{
{
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);
case INT:
value = va_arg (args, int);
break;
+ case INTMAX:
+ value = va_arg (args, intmax_t);
+ break;
case LONG:
value = va_arg (args, long);
break;
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;
case INT:
value = va_arg (args, unsigned);
break;
+ case INTMAX:
+ value = va_arg (args, uintmax_t);
+ break;
case LONG:
value = va_arg (args, unsigned long);
break;
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);
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;
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;
/* 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)
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
__vprintf (format, args, output, aux);
va_end (args);
}
-
-/* Writes string S to the console, followed by a new-line
- character. */
-int
-puts (const char *s)
-{
- while (*s != '\0')
- putchar (*s++);
- putchar ('\n');
- return 0;
-}
\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. */
+/* Dumps the SIZE bytes in BUF to the console as hex bytes
+ arranged 16 per line. Numeric offsets are also included,
+ starting at OFS for the first byte in BUF. If ASCII is true
+ then the corresponding ASCII characters are also rendered
+ alongside. */
void
-hex_dump (const void *buffer, size_t size, bool ascii)
+hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii)
{
- 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. */
+ const uint8_t *buf = buf_;
+ const size_t per_line = 16; /* Maximum bytes per line. */
- for (p = buffer; p < (uint8_t *) buffer + size; p += n)
+ while (size > 0)
{
+ size_t start, end, n;
size_t i;
-
+
/* Number of bytes on this line. */
- n = (uint8_t *) (buffer + size) - p;
- if (n > n_per_line)
- n = n_per_line;
+ start = ofs % per_line;
+ end = per_line;
+ if (end - start > size)
+ end = start + size;
+ n = end - start;
/* Print line. */
- for (i = 0; i < n; i++)
- printf ("%c%02x", i == n_per_line / 2 ? '-' : ' ', (unsigned) p[i]);
+ printf ("%08jx ", (uintmax_t) ROUND_DOWN (ofs, per_line));
+ for (i = 0; i < start; i++)
+ printf (" ");
+ for (; i < end; i++)
+ printf ("%02hhx%c",
+ buf[i - start], i == per_line / 2 - 1? '-' : ' ');
if (ascii)
{
- for (; i < n_per_line; i++)
+ for (; i < per_line; i++)
printf (" ");
- printf (" |");
- for (i = 0; i < n; i++)
- printf ("%c", isprint (p[i]) ? p[i] : '.');
- for (; i < n_per_line; i++)
+ printf ("|");
+ for (i = 0; i < start; i++)
+ printf (" ");
+ for (; i < end; i++)
+ printf ("%c",
+ isprint (buf[i - start]) ? buf[i - start] : '.');
+ for (; i < per_line; i++)
printf (" ");
printf ("|");
}
printf ("\n");
+
+ ofs += n;
+ buf += n;
+ 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);
}
}