summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--lib.c41
-rw-r--r--tests/custom/03_stdlib/27_sprintf21
-rw-r--r--tests/custom/03_stdlib/28_printf17
3 files changed, 72 insertions, 7 deletions
diff --git a/lib.c b/lib.c
index df05250..b5afd99 100644
--- a/lib.c
+++ b/lib.c
@@ -1198,7 +1198,7 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
uint32_t conv, flags, width, precision;
uc_value_t *fmt = uc_fn_arg(0), *arg;
const char *fstr, *last, *p, *cfmt;
- size_t argidx = 1, sfmtlen;
+ size_t argidx = 1, argpos, sfmtlen;
uint64_t u;
int64_t n;
double d;
@@ -1218,6 +1218,26 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
width = 0;
precision = 0;
+ argpos = argidx;
+
+ if (*p >= '1' && *p <= '9') {
+ while (isdigit(*p))
+ width = width * 10 + (*p++ - '0');
+
+ /* if a dollar sign follows, this is an argument index */
+ if (*p == '$') {
+ argpos = width;
+ width = 0;
+ p++;
+ }
+
+ /* otherwise skip to parsing precision, flags can't possibly follow */
+ else {
+ flags |= FMT_F_WIDTH;
+ goto parse_precision;
+ }
+ }
+
while (*p != '\0' && strchr("#0- +", *p)) {
switch (*p++) {
case '#': flags |= FMT_F_ALT; break;
@@ -1235,6 +1255,7 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
flags |= FMT_F_WIDTH;
}
+parse_precision:
if (*p == '.') {
p++;
@@ -1373,7 +1394,8 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
break;
case FMT_C_INT:
- arg = uc_fn_arg(argidx++);
+ argidx++;
+ arg = uc_fn_arg(argpos);
n = ucv_to_integer(arg);
if (errno == ERANGE)
@@ -1383,7 +1405,8 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
break;
case FMT_C_UINT:
- arg = uc_fn_arg(argidx++);
+ argidx++;
+ arg = uc_fn_arg(argpos);
u = ucv_to_unsigned(arg);
if (errno == ERANGE)
@@ -1393,17 +1416,20 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
break;
case FMT_C_DBL:
- d = ucv_to_double(uc_fn_arg(argidx++));
+ argidx++;
+ d = ucv_to_double(uc_fn_arg(argpos));
ucv_stringbuf_printf(buf, sfmt, d);
break;
case FMT_C_CHR:
- n = ucv_to_integer(uc_fn_arg(argidx++));
+ argidx++;
+ n = ucv_to_integer(uc_fn_arg(argpos));
ucv_stringbuf_printf(buf, sfmt, (int)n);
break;
case FMT_C_STR:
- arg = uc_fn_arg(argidx++);
+ argidx++;
+ arg = uc_fn_arg(argpos);
switch (ucv_type(arg)) {
case UC_STRING:
@@ -1423,8 +1449,9 @@ uc_printf_common(uc_vm_t *vm, size_t nargs, uc_stringbuf_t *buf)
break;
case FMT_C_JSON:
+ argidx++;
s = ucv_to_jsonstring_formatted(vm,
- uc_fn_arg(argidx++),
+ uc_fn_arg(argpos),
precision > 0 ? (precision > 1 ? ' ' : '\t') : '\0',
precision > 0 ? (precision > 1 ? precision - 1 : 1) : 0);
diff --git a/tests/custom/03_stdlib/27_sprintf b/tests/custom/03_stdlib/27_sprintf
index 3edcd48..e1a3c5d 100644
--- a/tests/custom/03_stdlib/27_sprintf
+++ b/tests/custom/03_stdlib/27_sprintf
@@ -548,3 +548,24 @@ Supplying a non-string format value will yield an empty string result.
-- Expect stdout --
""
-- End --
+
+
+Prefixing a format directive with `n$` will select the corresponding argument
+with 1 referring to the first argument. Missing or out-of range arguments will
+be treated as `null`.
+
+-- Testcase --
+{%
+ printf("%.J\n", [
+ sprintf("%2$s", "foo", "bar", "baz"),
+ sprintf("%10$s", "foo", "bar", "baz")
+ ]);
+%}
+-- End --
+
+-- Expect stdout --
+[
+ "bar",
+ "(null)"
+]
+-- End --
diff --git a/tests/custom/03_stdlib/28_printf b/tests/custom/03_stdlib/28_printf
index a2a6d27..b4556b3 100644
--- a/tests/custom/03_stdlib/28_printf
+++ b/tests/custom/03_stdlib/28_printf
@@ -524,3 +524,20 @@ Supplying a non-string format value will yield an empty string result.
-- Expect stdout --
-- End --
+
+
+Prefixing a format directive with `n$` will select the corresponding argument
+with 1 referring to the first argument. Missing or out-of range arguments will
+be treated as `null`.
+
+-- Testcase --
+{%
+ printf("%2$s\n", "foo", "bar", "baz");
+ printf("%10$s\n", "foo", "bar", "baz");
+%}
+-- End --
+
+-- Expect stdout --
+bar
+(null)
+-- End --