summaryrefslogtreecommitdiff
path: root/payloads/libpayload/libc/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/libc/printf.c')
-rw-r--r--payloads/libpayload/libc/printf.c267
1 files changed, 122 insertions, 145 deletions
diff --git a/payloads/libpayload/libc/printf.c b/payloads/libpayload/libc/printf.c
index bc2c41c255..8f76ccf286 100644
--- a/payloads/libpayload/libc/printf.c
+++ b/payloads/libpayload/libc/printf.c
@@ -69,11 +69,10 @@ struct printf_spec {
#define __PRINTF_FLAG_NEGATIVE 0x00000100
/**
- * Buffer big enough for 64-bit number printed in base 2, sign, prefix and 0
- * to terminate string (last one is only for better testing end of buffer by
- * zero-filling subroutine).
+ * Buffer big enough for 64-bit number printed in base 2, sign, and prefix.
+ * Add some more to support sane amounts of zero-padding.
*/
-#define PRINT_NUMBER_BUFFER_SIZE (64 + 5)
+#define PRINT_BUFFER_SIZE (64 + 1 + 2 + 13)
/** Enumeration of possible arguments types. */
typedef enum {
@@ -128,6 +127,26 @@ static int printf_putchar(int c, struct printf_spec *ps)
return ps->write(&ch, 1, ps->data);
}
+/* Print spaces for padding. Ignores negative counts. */
+static int print_spaces(int count, struct printf_spec *ps)
+{
+ int tmp, ret;
+ char buffer[PRINT_BUFFER_SIZE];
+
+ if (count <= 0)
+ return 0;
+
+ memset(buffer, ' ', MIN(PRINT_BUFFER_SIZE, count));
+ for (tmp = count; tmp > PRINT_BUFFER_SIZE; tmp -= PRINT_BUFFER_SIZE)
+ if ((ret = printf_putnchars(buffer, PRINT_BUFFER_SIZE, ps)) < 0)
+ return ret;
+
+ if ((ret = printf_putnchars(buffer, tmp, ps)) < 0)
+ return ret;
+
+ return count;
+}
+
/**
* Print one formatted character.
*
@@ -139,21 +158,24 @@ static int printf_putchar(int c, struct printf_spec *ps)
*/
static int print_char(char c, int width, uint64_t flags, struct printf_spec *ps)
{
- int counter = 0;
+ int retval;
+ int counter = 1;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
- while (--width > 0) {
- if (printf_putchar(' ', ps) > 0)
- ++counter;
- }
+ if ((retval = print_spaces(width - 1, ps)) < 0)
+ return retval;
+ else
+ counter += retval;
}
- if (printf_putchar(c, ps) > 0)
- counter++;
+ if ((retval = printf_putchar(c, ps)) < 0)
+ return retval;
- while (--width > 0) {
- if (printf_putchar(' ', ps) > 0)
- ++counter;
+ if (flags & __PRINTF_FLAG_LEFTALIGNED) {
+ if ((retval = print_spaces(width - 1, ps)) < 0)
+ return retval;
+ else
+ counter += retval;
}
return counter;
@@ -185,19 +207,21 @@ static int print_string(char *s, int width, unsigned int precision,
width -= precision;
if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
- while (width-- > 0) {
- if (printf_putchar(' ', ps) == 1)
- counter++;
- }
+ if ((retval = print_spaces(width, ps)) < 0)
+ return retval;
+ else
+ counter += retval;
}
if ((retval = printf_putnchars(s, MIN(size, precision), ps)) < 0)
- return -counter;
+ return retval;
counter += retval;
- while (width-- > 0) {
- if (printf_putchar(' ', ps) == 1)
- ++counter;
+ if (flags & __PRINTF_FLAG_LEFTALIGNED) {
+ if ((retval = print_spaces(width, ps)) < 0)
+ return retval;
+ else
+ counter += retval;
}
return counter;
@@ -209,7 +233,7 @@ static int print_string(char *s, int width, unsigned int precision,
* Print significant digits of a number in given base.
*
* @param num Number to print.
- * @param width Width modifier.h
+ * @param width Width modifier.
* @param precision Precision modifier.
* @param base Base to print the number in (must be between 2 and 16).
* @param flags Flags that modify the way the number is printed.
@@ -220,49 +244,34 @@ static int print_number(uint64_t num, int width, int precision, int base,
uint64_t flags, struct printf_spec *ps)
{
const char *digits = digits_small;
- char d[PRINT_NUMBER_BUFFER_SIZE];
- char *ptr = &d[PRINT_NUMBER_BUFFER_SIZE - 1];
- int size = 0; /* Size of number with all prefixes and signs. */
- int number_size; /* Size of plain number. */
+ char d[PRINT_BUFFER_SIZE];
+ char *ptr = &d[PRINT_BUFFER_SIZE];
+ int size = 0; /* Size of the string in ptr */
+ int counter = 0; /* Amount of actually printed bytes. */
char sgn;
int retval;
- int counter = 0;
if (flags & __PRINTF_FLAG_BIGCHARS)
digits = digits_big;
- *ptr-- = 0; /* Put zero at end of string. */
-
if (num == 0) {
- *ptr-- = '0';
+ *--ptr = '0';
size++;
} else {
do {
- *ptr-- = digits[num % base];
+ *--ptr = digits[num % base];
size++;
} while (num /= base);
}
- number_size = size;
+ /* Both precision and LEFTALIGNED overrule ZEROPADDED. */
+ if ((flags & __PRINTF_FLAG_LEFTALIGNED) || precision)
+ flags &= ~__PRINTF_FLAG_ZEROPADDED;
- /*
- * Collect the sum of all prefixes/signs/... to calculate padding and
- * leading zeroes.
- */
- if (flags & __PRINTF_FLAG_PREFIX) {
- switch (base) {
- case 2: /* Binary formating is not standard, but useful. */
- size += 2;
- break;
- case 8:
- size++;
- break;
- case 16:
- size += 2;
- break;
- }
- }
+ /* Fix precision now since it doesn't count prefixes/signs. */
+ precision -= size;
+ /* Reserve size for prefixes/signs before filling up padding. */
sgn = 0;
if (flags & __PRINTF_FLAG_SIGNED) {
if (flags & __PRINTF_FLAG_NEGATIVE) {
@@ -276,87 +285,75 @@ static int print_number(uint64_t num, int width, int precision, int base,
size++;
}
}
-
- if (flags & __PRINTF_FLAG_LEFTALIGNED)
- flags &= ~__PRINTF_FLAG_ZEROPADDED;
-
- /*
- * If the number is left-aligned or precision is specified then
- * zero-padding is ignored.
- */
- if (flags & __PRINTF_FLAG_ZEROPADDED) {
- if ((precision == 0) && (width > size))
- precision = width - size + number_size;
- }
-
- /* Print leading spaces. */
- if (number_size > precision) {
- /* Print the whole number not only a part. */
- precision = number_size;
- }
-
- width -= precision + size - number_size;
-
- if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
- while (width-- > 0) {
- if (printf_putchar(' ', ps) == 1)
- counter++;
+ if (flags & __PRINTF_FLAG_PREFIX) {
+ switch (base) {
+ case 2: /* Binary formating is not standard, but useful. */
+ size += 2;
+ break;
+ case 8:
+ size++;
+ break;
+ case 16:
+ size += 2;
+ break;
}
}
- /* Print sign. */
- if (sgn) {
- if (printf_putchar(sgn, ps) == 1)
- counter++;
+ /* If this is still set we didn't have a precision, so repurpose it */
+ if (flags & __PRINTF_FLAG_ZEROPADDED)
+ precision = width - size;
+
+ /* Pad smaller numbers with 0 (larger numbers lead to precision < 0). */
+ if (precision > 0) {
+ precision = MIN(precision, PRINT_BUFFER_SIZE - size);
+ ptr -= precision;
+ size += precision;
+ memset(ptr, '0', precision);
}
- /* Print prefix. */
+ /* Add sign and prefix (we adjusted size for this beforehand). */
if (flags & __PRINTF_FLAG_PREFIX) {
switch (base) {
case 2: /* Binary formating is not standard, but useful. */
- if (printf_putchar('0', ps) == 1)
- counter++;
- if (flags & __PRINTF_FLAG_BIGCHARS) {
- if (printf_putchar('B', ps) == 1)
- counter++;
- } else {
- if (printf_putchar('b', ps) == 1)
- counter++;
- }
+ *--ptr = (flags & __PRINTF_FLAG_BIGCHARS) ? 'B' : 'b';
+ *--ptr = '0';
break;
case 8:
- if (printf_putchar('o', ps) == 1)
- counter++;
+ *--ptr = '0';
break;
case 16:
- if (printf_putchar('0', ps) == 1)
- counter++;
- if (flags & __PRINTF_FLAG_BIGCHARS) {
- if (printf_putchar('X', ps) == 1)
- counter++;
- } else {
- if (printf_putchar('x', ps) == 1)
- counter++;
- }
+ *--ptr = (flags & __PRINTF_FLAG_BIGCHARS) ? 'X' : 'x';
+ *--ptr = '0';
break;
}
}
-
- /* Print leading zeroes. */
- precision -= number_size;
- while (precision-- > 0) {
- if (printf_putchar('0', ps) == 1)
- counter++;
+ if (sgn)
+ *--ptr = sgn;
+
+ /* Pad with spaces up to width, try to avoid extra putnchar if we can */
+ width -= size;
+ if (width > 0 && !(flags & __PRINTF_FLAG_LEFTALIGNED)) {
+ int tmp = MIN(width, PRINT_BUFFER_SIZE - size);
+ ptr -= tmp;
+ size += tmp;
+ memset(ptr, ' ', tmp);
+ if ((retval = print_spaces(width - tmp, ps)) < 0)
+ return retval;
+ else
+ counter += retval;
}
- /* Print number itself. */
- if ((retval = printf_putstr(++ptr, ps)) > 0)
- counter += retval;
+ /* Now print the whole thing at once. */
+ if ((retval = printf_putnchars(ptr, size, ps)) < 0)
+ return retval;
+ counter += retval;
- /* Print ending spaces. */
- while (width-- > 0) {
- if (printf_putchar(' ', ps) == 1)
- counter++;
+ /* Edge case: left-aligned with width (should be rare). */
+ if (flags & __PRINTF_FLAG_LEFTALIGNED) {
+ if ((retval = print_spaces(width, ps)) < 0)
+ return retval;
+ else
+ counter += retval;
}
return counter;
@@ -468,10 +465,8 @@ static int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
/* Print common characters if any processed. */
if (i > j) {
if ((retval = printf_putnchars(&fmt[j],
- (size_t) (i - j), ps)) < 0) {
- counter = -counter;
- goto out; /* Error */
- }
+ (size_t) (i - j), ps)) < 0)
+ return retval;
counter += retval;
}
@@ -574,20 +569,15 @@ static int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
/* String and character conversions */
case 's':
if ((retval = print_string(va_arg(ap, char *),
- width, precision, flags, ps)) < 0) {
- counter = -counter;
- goto out;
- };
+ width, precision, flags, ps)) < 0)
+ return retval;
counter += retval;
j = i + 1;
goto next_char;
case 'c':
c = va_arg(ap, unsigned int);
- retval = print_char(c, width, flags, ps);
- if (retval < 0) {
- counter = -counter;
- goto out;
- };
+ if ((retval = print_char(c, width, flags, ps)) < 0)
+ return retval;
counter += retval;
j = i + 1;
goto next_char;
@@ -654,9 +644,6 @@ static int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
size = sizeof(void *);
number = (uint64_t) (unsigned long)va_arg(ap, void *);
break;
- default: /* Unknown qualifier */
- counter = -counter;
- goto out;
}
if (flags & __PRINTF_FLAG_SIGNED) {
@@ -674,10 +661,8 @@ static int printf_core(const char *fmt, struct printf_spec *ps, va_list ap)
}
if ((retval = print_number(number, width, precision,
- base, flags, ps)) < 0) {
- counter = -counter;
- goto out;
- }
+ base, flags, ps)) < 0)
+ return retval;
counter += retval;
j = i + 1;
@@ -688,15 +673,11 @@ next_char:
if (i > j) {
if ((retval = printf_putnchars(&fmt[j],
- (u64) (i - j), ps)) < 0) {
- counter = -counter;
- goto out; /* Error */
-
- }
+ (u64) (i - j), ps)) < 0)
+ return retval;
counter += retval;
}
-out:
return counter;
}
@@ -830,12 +811,8 @@ int printf(const char *fmt, ...)
static int vprintf_write(const char *str, size_t count, void *unused)
{
- size_t i;
-
- for (i = 0; i < count; i++)
- putchar(str[i]);
-
- return i;
+ console_write(str, count);
+ return count;
}
int vprintf(const char *fmt, va_list ap)