/* 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);
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;
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 IS_SIGNED. 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 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;
digit_cnt = 0;
while (value > 0)
/* 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 0, the result must always
+ begin with a zero. */
precision = c->precision < 0 ? 1 : c->precision;
- 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