diff --git a/examples/basic.c b/examples/basic.c index 6438970..4801ee8 100644 --- a/examples/basic.c +++ b/examples/basic.c @@ -46,6 +46,11 @@ int main(void) { // format specifiers crprintf("Number: %d, String: %s\n", 42, "hello"); crprintf("Hex: 0x%x, Float: %.2f\n", 255, 3.14159); + crprintf("Precision: %.*s, Width: '%*s', Dynamic float: %*.*f\n", + 4, "abcdef", + 6, "hi", + 7, 2, 3.14159 + ); // reset crprintf("red back to normal immediately\n"); diff --git a/meson.build b/meson.build index ddb648a..2ef4a2c 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('crprintf', 'c', - version: '0.0.11', + version: '0.0.12', license: 'MIT', default_options: ['warning_level=2'] ) diff --git a/src/crprintf.c b/src/crprintf.c index 3588a05..59f5f2d 100644 --- a/src/crprintf.c +++ b/src/crprintf.c @@ -740,12 +740,16 @@ typedef enum { static arg_class_t classify_arg(const char *spec, int len) { char conv = spec[len - 1]; - if (conv == '%' || conv == 'n') return ARG_NONE; - if (conv == 's') return ARG_CSTR; - if (conv == 'p') return ARG_PTR; - if (conv == 'f' || conv == 'F' || conv == 'e' || conv == 'E' || - conv == 'g' || conv == 'G' || conv == 'a' || conv == 'A') - return ARG_DOUBLE; + + if (conv == '%') return ARG_NONE; + if (conv == 'n') return ARG_PTR; + if (conv == 's') return ARG_CSTR; + if (conv == 'p') return ARG_PTR; + + if ( + conv == 'f' || conv == 'F' || conv == 'e' || conv == 'E' || + conv == 'g' || conv == 'G' || conv == 'a' || conv == 'A' + ) return ARG_DOUBLE; const char *p = spec + 1; while (*p == '-' || *p == '+' || *p == ' ' || *p == '#' || *p == '0') p++; @@ -762,6 +766,39 @@ static arg_class_t classify_arg(const char *spec, int len) { return ARG_INT; } +static void consume_arg(va_list *ap, arg_class_t cls) { +switch (cls) { + case ARG_INT: (void)va_arg(*ap, int); break; + case ARG_LONG: (void)va_arg(*ap, long); break; + case ARG_LLONG: (void)va_arg(*ap, long long); break; + case ARG_SIZE: (void)va_arg(*ap, size_t); break; + case ARG_DOUBLE: (void)va_arg(*ap, double); break; + case ARG_CSTR: (void)va_arg(*ap, const char *); break; + case ARG_PTR: (void)va_arg(*ap, void *); break; + case ARG_WINT: (void)va_arg(*ap, wint_t); break; + case ARG_WSTR: (void)va_arg(*ap, wchar_t *); break; + case ARG_NONE: break; +}} + +static void advance_format_args(const char *spec, va_list *ap) { + const char *p = spec + 1; + while (*p == '-' || *p == '+' || *p == ' ' || *p == '#' || *p == '0') p++; + if (*p == '*') { + (void)va_arg(*ap, int); + p++; + } else while (*p >= '0' && *p <= '9') p++; + + if (*p == '.') { + p++; + if (*p == '*') { + (void)va_arg(*ap, int); + p++; + } else while (*p >= '0' && *p <= '9') p++; + } + + consume_arg(ap, classify_arg(spec, (int)strlen(spec))); +} + static const char *scan_fmt(crprintf_compiled *p, const char *ptr, const char **lit) { flush_lit(p, *lit, ptr); @@ -1022,7 +1059,6 @@ static vm_output_t crprintf_vm_run_ex(crprintf_compiled *prog, va_list ap, crpri op_emit_fmt: { uint32_t lit_off = ip->operand & 0x0FFFFFFF; - arg_class_t cls = (arg_class_t)(ip->operand >> 28); const char *spec = prog->literals + lit_off; char tmp[256]; @@ -1045,18 +1081,7 @@ static vm_output_t crprintf_vm_run_ex(crprintf_compiled *prog, va_list ap, crpri } #pragma GCC diagnostic pop - switch (cls) { - case ARG_INT: (void)va_arg(ap, int); break; - case ARG_LONG: (void)va_arg(ap, long); break; - case ARG_LLONG: (void)va_arg(ap, long long); break; - case ARG_SIZE: (void)va_arg(ap, size_t); break; - case ARG_DOUBLE: (void)va_arg(ap, double); break; - case ARG_CSTR: (void)va_arg(ap, const char *); break; - case ARG_PTR: (void)va_arg(ap, void *); break; - case ARG_WINT: (void)va_arg(ap, wint_t); break; - case ARG_WSTR: (void)va_arg(ap, wchar_t *); break; - case ARG_NONE: break; - } + advance_format_args(spec, &ap); NEXT(); } diff --git a/tests/test.c b/tests/test.c index e2110af..0e8ed29 100644 --- a/tests/test.c +++ b/tests/test.c @@ -51,6 +51,25 @@ TEST(format_specifiers) { ASSERT_STR_EQ(buf, "float: 3.14"); } +TEST(dynamic_width_and_precision) { + char buf[256]; + crprintf_set_color(false); + + crsprintf(buf, sizeof(buf), "one: %.*s two: %s", 3, "abcdef", "done"); + ASSERT_STR_EQ(buf, "one: abc two: done"); + + crsprintf(buf, sizeof(buf), "wide: '%*s' next: %d", 5, "hi", 7); + ASSERT_STR_EQ(buf, "wide: ' hi' next: 7"); + + crsprintf(buf, sizeof(buf), "float: %*.*f next: %s", 6, 2, 3.14159, "done"); + ASSERT_STR_EQ(buf, "float: 3.14 next: done"); + + crsprintf(buf, sizeof(buf), "%.*s %s", 4, "abcdef", "ok"); + ASSERT_STR_EQ(buf, "abcd ok"); + + crprintf_set_color(true); +} + TEST(color_tags_stripped_in_no_color_mode) { char buf[256]; crprintf_set_color(false); @@ -334,6 +353,7 @@ int main(void) { RUN_TEST(basic_string); RUN_TEST(format_specifiers); + RUN_TEST(dynamic_width_and_precision); RUN_TEST(color_tags_stripped_in_no_color_mode); RUN_TEST(padding_right); RUN_TEST(padding_left);