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);