/* printf(). Supports most of the normal integer and string formats.
- * - %[#-+ 0][width][{l,t,z,ll,L,j,q}]{c,d,i,u,x,X,p,s,m,%}
+ * - %[#0-+ ][width|*[.precision|*}][{l,t,z,ll,L,j,q}]{c,d,i,u,x,X,p,s,m,%}
* - %% generates a single %
* - %m outputs strerror(errno).
* - %X outputs a..f the same as %x.
- * - The modifiers [-0] are currently ignored.
- * - No support for precision or variable widths.
* - No support for floating point or wide characters.
* - Invalid formats are copied to the output buffer.
*
char ch;
unsigned long long v;
long long signed_v;
- int written, width, len;
+ int written, width, precision, len;
unsigned int flags, ch_flag;
- char outbuf[2 + 22 + 1];
+ char outbuf[2 + 31 + 22 + 1];
char *out;
const char *outstr;
unsigned int sign_prefix;
flags |= ch_flag;
}
- /* width */
- while (ch >= '0' && ch <= '9') {
- width *= 10;
- width += ch - '0';
-
- ch = *fmt++;
+ /* Width and precision */
+ for (;; ch = *fmt++) {
+ if (ch == '*') {
+ precision = va_arg(args, unsigned int);
+ ch = *fmt++;
+ } else {
+ for (precision = 0; ch >= '0' && ch <= '9'; ch = *fmt++)
+ precision = precision * 10 + (ch - '0');
+ }
+ if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '.'))
+ break;
+ width = precision;
+ if (ch != '.') {
+ /* Default precision for strings */
+ precision = INT_MAX;
+ break;
+ }
+ flags |= _NOLIBC_PF_FLAG('.');
}
/* Length modifier.
if (ch == 's') {
/* "%s" - character string. */
outstr = (const char *)(uintptr_t)v;
- if (!outstr)
+ if (!outstr) {
outstr = "(null)";
+ /* Match glibc, nothing output if precision too small */
+ len = precision >= 6 ? 6 : 0;
+ goto do_output;
+ }
goto do_strlen_output;
}
}
/* The value is converted offset into the buffer so that
- * the sign/prefix can be added in front.
+ * 31 zero pad characters and the sign/prefix can be added in front.
* The longest digit string is 22 + 1 for octal conversions, the
* space is reserved even though octal isn't currently supported.
*/
- out = outbuf + 2;
+ out = outbuf + 2 + 31;
if (v == 0) {
/* There are special rules for zero. */
len = 5;
goto do_output;
}
+ if (!precision) {
+ /* Explicit %nn.0d, no digits output */
+ len = 0;
+ goto prepend_sign;
+ }
/* All other formats (including "%#x") just output "0". */
out[0] = '0';
len = 1;
}
}
+ /* Add zero padding */
+ if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '0', '.')) {
+ if (!_NOLIBC_PF_FLAGS_CONTAIN(flags, '.')) {
+ if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '-'))
+ /* Left justify overrides zero pad */
+ goto prepend_sign;
+ /* eg "%05d", Zero pad to field width less sign.
+ * Note that precision can end up negative so all
+ * the variables have to be 'signed int'.
+ */
+ precision = width;
+ if (sign_prefix) {
+ precision--;
+ if (sign_prefix >= 256)
+ precision--;
+ }
+ }
+ if (precision > 31)
+ /* Don't run off the start of outbuf[], arbitrary limit
+ * longer than the longest number field. */
+ precision = 31;
+ for (; len < precision; len++) {
+ /* Stop gcc generating horrid code and memset(). */
+ _NOLIBC_OPTIMIZER_HIDE_VAR(len);
+ *--out = '0';
+ }
+ }
+
+prepend_sign:
/* Add the 0, 1 or 2 ("0x") sign/prefix characters at the front. */
for (; sign_prefix; sign_prefix >>= 8) {
/* Force gcc to increment len inside the loop. */
goto do_output;
do_strlen_output:
- /* Open coded strlen() (slightly smaller). */
- for (len = 0;; len++)
+ /* Open coded strnlen() (slightly smaller). */
+ for (len = 0; len < precision; len++)
if (!outstr[len])
break;
CASE_TEST(signed_max); EXPECT_VFPRINTF(1, "2147483647", "%i", ~0u >> 1); break;
CASE_TEST(signed_min); EXPECT_VFPRINTF(1, "-2147483648", "%i", (~0u >> 1) + 1); break;
CASE_TEST(unsigned_max); EXPECT_VFPRINTF(1, "4294967295", "%u", ~0u); break;
- CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break;
+ CASE_TEST(char); EXPECT_VFPRINTF(1, "|c|d| e|", "|%c|%.0c|%4c|", 'c', 'd', 'e'); break;
CASE_TEST(hex_nolibc); EXPECT_VFPRINTF(is_nolibc, "|f|d|", "|%x|%X|", 0xf, 0xd); break;
CASE_TEST(hex_libc); EXPECT_VFPRINTF(!is_nolibc, "|f|D|", "|%x|%X|", 0xf, 0xd); break;
CASE_TEST(hex_alt); EXPECT_VFPRINTF(1, "|0x1| 0x2| 0|", "|%#x|%#5x|%#5x|", 1, 2, 0); break;
+ CASE_TEST(hex_alt_prec); EXPECT_VFPRINTF(1, "| 0x02|0x03| 0x123|", "|%#5.2x|%#04x|%#6.2x|", 2, 3, 0x123); break;
+ CASE_TEST(hex_0_alt); EXPECT_VFPRINTF(1, "|0|0000| 00|", "|%#x|%#04x|%#5.2x|", 0, 0, 0); break;
CASE_TEST(pointer); EXPECT_VFPRINTF(1, "0x1", "%p", (void *) 0x1); break;
- CASE_TEST(pointer_NULL); EXPECT_VFPRINTF(is_nolibc || is_glibc, "(nil)", "%p", (void *)0); break;
+ CASE_TEST(pointer_NULL); EXPECT_VFPRINTF(is_nolibc || is_glibc, "|(nil)|(nil)|", "|%p|%.4p|", (void *)0, (void *)0); break;
+ CASE_TEST(string_NULL); EXPECT_VFPRINTF(is_nolibc || is_glibc, "|(null)||(null)|", "|%s|%.5s|%.6s|", (void *)0, (void *)0, (void *)0); break;
CASE_TEST(percent); EXPECT_VFPRINTF(1, "a%d42%69%", "a%%d%d%%%d%%", 42, 69); break;
CASE_TEST(perc_qual); EXPECT_VFPRINTF(is_nolibc || is_glibc, "a%d2", "a%-14l%d%d", 2); break;
CASE_TEST(invalid); EXPECT_VFPRINTF(is_nolibc || is_glibc, "a%12yx3%y42%P", "a%12yx%d%y%d%P", 3, 42); break;
CASE_TEST(uintmax_max); EXPECT_VFPRINTF(1, "18446744073709551615", "%ju", ~0ULL); break;
CASE_TEST(truncation); EXPECT_VFPRINTF(1, "0123456789012345678901234", "%s", "0123456789012345678901234"); break;
CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); break;
+ CASE_TEST(string_trunc); EXPECT_VFPRINTF(1, " 12345", "%10.5s", "1234567890"); break;
CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); break;
CASE_TEST(number_left); EXPECT_VFPRINTF(1, "|-5 |", "|%-8d|", -5); break;
CASE_TEST(string_align); EXPECT_VFPRINTF(1, "|foo |", "|%-8s|", "foo"); break;
CASE_TEST(width_trunc); EXPECT_VFPRINTF(1, " 1", "%25d", 1); break;
+ CASE_TEST(width_tr_lft); EXPECT_VFPRINTF(1, "1 ", "%-30d", 1); break;
+ CASE_TEST(number_pad); EXPECT_VFPRINTF(1, "0000000005", "%010d", 5); break;
+ CASE_TEST(number_pad); EXPECT_VFPRINTF(1, "|0000000005|0x1234|", "|%010d|%#01x|", 5, 0x1234); break;
+ CASE_TEST(num_pad_neg); EXPECT_VFPRINTF(1, "-000000005", "%010d", -5); break;
+ CASE_TEST(num_pad_hex); EXPECT_VFPRINTF(1, "00fffffffb", "%010x", -5); break;
+ CASE_TEST(num_pad_trunc);EXPECT_VFPRINTF(is_nolibc, " 0000000000000000000000000000005", "%035d", 5); break; /* max 31 '0' can be added */
+ CASE_TEST(num_p_tr_libc);EXPECT_VFPRINTF(!is_nolibc, "00000000000000000000000000000000005", "%035d", 5); break;
+ CASE_TEST(number_prec); EXPECT_VFPRINTF(1, " 00005", "%10.5d", 5); break;
+ CASE_TEST(num_prec_neg); EXPECT_VFPRINTF(1, " -00005", "%10.5d", -5); break;
+ CASE_TEST(num_prec_var); EXPECT_VFPRINTF(1, " -00005", "%*.*d", 10, 5, -5); break;
+ CASE_TEST(num_0_prec_0); EXPECT_VFPRINTF(1, "|| |+||||", "|%.0d|% .0d|%+.0d|%.0u|%.0x|%#.0x|", 0, 0, 0, 0, 0, 0); break;
CASE_TEST(errno); errno = 22; EXPECT_VFPRINTF(is_nolibc, "errno=22", "%m"); break;
CASE_TEST(errno-neg); errno = -22; EXPECT_VFPRINTF(is_nolibc, "errno=-22 ", "%-12m"); break;
CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break;