vim-patch:9.0.1704: Cannot use positional arguments for printf() (#24719)

Problem: Cannot use positional arguments for printf()
Solution: Support positional arguments in string formatting

closes: vim/vim#12140

0c6181fec4

Co-authored-by: Christ van Willegen <cvwillegen@gmail.com>
This commit is contained in:
zeertzjq
2023-08-15 19:16:19 +08:00
committed by GitHub
parent 7aad4643f9
commit 842a47d6a4
9 changed files with 1529 additions and 77 deletions

View File

@@ -32,6 +32,32 @@
#include "nvim/types.h"
#include "nvim/vim.h"
static char e_cannot_mix_positional_and_non_positional_str[]
= N_("E1400: Cannot mix positional and non-positional arguments: %s");
static char e_fmt_arg_nr_unused_str[]
= N_("E1401: format argument %d unused in $-style format: %s");
static char e_positional_num_field_spec_reused_str_str[]
= N_("E1402: Positional argument %d used as field width reused as different type: %s/%s");
static char e_positional_nr_out_of_bounds_str[]
= N_("E1403: Positional argument %d out of bounds: %s");
static char e_positional_arg_num_type_inconsistent_str_str[]
= N_("E1404: Positional argument %d type used inconsistently: %s/%s");
static char e_invalid_format_specifier_str[]
= N_("E1405: Invalid format specifier: %s");
static char typename_unknown[] = N_("unknown");
static char typename_int[] = N_("int");
static char typename_longint[] = N_("long int");
static char typename_longlongint[] = N_("long long int");
static char typename_unsignedint[] = N_("unsigned int");
static char typename_unsignedlongint[] = N_("unsigned long int");
static char typename_unsignedlonglongint[] = N_("unsigned long long int");
static char typename_pointer[] = N_("pointer");
static char typename_percent[] = N_("percent");
static char typename_char[] = N_("char");
static char typename_string[] = N_("string");
static char typename_float[] = N_("float");
/// Copy up to `len` bytes of `string` into newly allocated memory and
/// terminate with a NUL. The allocated memory always has size `len + 1`, even
/// when `string` is shorter.
@@ -717,6 +743,571 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap)
return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
}
enum {
TYPE_UNKNOWN = -1,
TYPE_INT,
TYPE_LONGINT,
TYPE_LONGLONGINT,
TYPE_UNSIGNEDINT,
TYPE_UNSIGNEDLONGINT,
TYPE_UNSIGNEDLONGLONGINT,
TYPE_POINTER,
TYPE_PERCENT,
TYPE_CHAR,
TYPE_STRING,
TYPE_FLOAT,
};
/// Types that can be used in a format string
static int format_typeof(const char *type, bool usetvs)
FUNC_ATTR_NONNULL_ALL
{
// allowed values: \0, h, l, L
char length_modifier = '\0';
// current conversion specifier character
char fmt_spec = '\0';
// parse 'h', 'l' and 'll' length modifiers
if (*type == 'h' || *type == 'l') {
length_modifier = *type;
type++;
if (length_modifier == 'l' && *type == 'l') {
// double l = long long
length_modifier = 'L';
type++;
}
}
fmt_spec = *type;
// common synonyms:
switch (fmt_spec) {
case 'i':
fmt_spec = 'd'; break;
case '*':
fmt_spec = 'd'; length_modifier = 'h'; break;
case 'D':
fmt_spec = 'd'; length_modifier = 'l'; break;
case 'U':
fmt_spec = 'u'; length_modifier = 'l'; break;
case 'O':
fmt_spec = 'o'; length_modifier = 'l'; break;
default:
break;
}
if (usetvs) {
switch (fmt_spec) {
case 'd':
case 'u':
case 'o':
case 'x':
case 'X':
if (length_modifier == '\0') {
length_modifier = 'L';
}
}
}
// get parameter value, do initial processing
switch (fmt_spec) {
// '%' and 'c' behave similar to 's' regarding flags and field
// widths
case '%':
return TYPE_PERCENT;
case 'c':
return TYPE_CHAR;
case 's':
case 'S':
return TYPE_STRING;
case 'd':
case 'u':
case 'b':
case 'B':
case 'o':
case 'x':
case 'X':
case 'p':
// NOTE: the u, b, o, x, X and p conversion specifiers
// imply the value is unsigned; d implies a signed
// value
// 0 if numeric argument is zero (or if pointer is
// NULL for 'p'), +1 if greater than zero (or nonzero
// for unsigned arguments), -1 if negative (unsigned
// argument is never negative)
if (fmt_spec == 'p') {
return TYPE_POINTER;
} else if (fmt_spec == 'b' || fmt_spec == 'B') {
return TYPE_UNSIGNEDINT;
} else if (fmt_spec == 'd') {
// signed
switch (length_modifier) {
case '\0':
case 'h':
// char and short arguments are passed as int.
return TYPE_INT;
case 'l':
return TYPE_LONGINT;
case 'L':
return TYPE_LONGLONGINT;
}
} else {
// unsigned
switch (length_modifier) {
case '\0':
case 'h':
return TYPE_UNSIGNEDINT;
case 'l':
return TYPE_UNSIGNEDLONGINT;
case 'L':
return TYPE_UNSIGNEDLONGLONGINT;
}
}
break;
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
return TYPE_FLOAT;
}
return TYPE_UNKNOWN;
}
static char *format_typename(const char *type)
FUNC_ATTR_NONNULL_ALL
{
switch (format_typeof(type, false)) {
case TYPE_INT:
return _(typename_int);
case TYPE_LONGINT:
return _(typename_longint);
case TYPE_LONGLONGINT:
return _(typename_longlongint);
case TYPE_UNSIGNEDINT:
return _(typename_unsignedint);
case TYPE_UNSIGNEDLONGINT:
return _(typename_unsignedlongint);
case TYPE_UNSIGNEDLONGLONGINT:
return _(typename_unsignedlonglongint);
case TYPE_POINTER:
return _(typename_pointer);
case TYPE_PERCENT:
return _(typename_percent);
case TYPE_CHAR:
return _(typename_char);
case TYPE_STRING:
return _(typename_string);
case TYPE_FLOAT:
return _(typename_float);
}
return _(typename_unknown);
}
static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const char *type)
FUNC_ATTR_NONNULL_ALL
{
if (*ap_types == NULL || *num_posarg < arg) {
const char **new_types = *ap_types == NULL
? xcalloc(sizeof(const char *), (size_t)arg)
: xrealloc(*ap_types, (size_t)arg * sizeof(const char *));
for (int idx = *num_posarg; idx < arg; idx++) {
new_types[idx] = NULL;
}
*ap_types = new_types;
*num_posarg = arg;
}
if ((*ap_types)[arg - 1] != NULL) {
if ((*ap_types)[arg - 1][0] == '*' || type[0] == '*') {
const char *pt = type;
if (pt[0] == '*') {
pt = (*ap_types)[arg - 1];
}
if (pt[0] != '*') {
switch (pt[0]) {
case 'd':
case 'i':
break;
default:
semsg(_(e_positional_num_field_spec_reused_str_str), arg,
format_typename((*ap_types)[arg - 1]), format_typename(type));
return FAIL;
}
}
} else {
if (format_typeof(type, false) != format_typeof((*ap_types)[arg - 1], false)) {
semsg(_(e_positional_arg_num_type_inconsistent_str_str), arg,
format_typename(type), format_typename((*ap_types)[arg - 1]));
return FAIL;
}
}
}
(*ap_types)[arg - 1] = type;
return OK;
}
static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *fmt, typval_T *tvs)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
const char *p = fmt;
const char *arg = NULL;
int any_pos = 0;
int any_arg = 0;
#define CHECK_POS_ARG \
do { \
if (any_pos && any_arg) { \
semsg(_(e_cannot_mix_positional_and_non_positional_str), fmt); \
goto error; \
} \
} while (0);
if (p == NULL) {
return OK;
}
while (*p != NUL) {
if (*p != '%') {
char *q = strchr(p + 1, '%');
size_t n = (q == NULL) ? strlen(p) : (size_t)(q - p);
p += n;
} else {
// allowed values: \0, h, l, L
char length_modifier = '\0';
// variable for positional arg
int pos_arg = -1;
p++; // skip '%'
// First check to see if we find a positional
// argument specifier
const char *ptype = p;
while (ascii_isdigit(*ptype)) {
ptype++;
}
if (*ptype == '$') {
if (*p == '0') {
// 0 flag at the wrong place
semsg(_(e_invalid_format_specifier_str), fmt);
goto error;
}
// Positional argument
unsigned uj = (unsigned)(*p++ - '0');
while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned)(*p++ - '0');
}
pos_arg = (int)uj;
any_pos = 1;
CHECK_POS_ARG;
p++;
}
// parse flags
while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
|| *p == '#' || *p == '\'') {
switch (*p) {
case '0':
break;
case '-':
break;
case '+':
break;
case ' ': // If both the ' ' and '+' flags appear, the ' '
// flag should be ignored
break;
case '#':
break;
case '\'':
break;
}
p++;
}
// If the '0' and '-' flags both appear, the '0' flag should be
// ignored.
// parse field width
if (*(arg = p) == '*') {
p++;
if (ascii_isdigit((int)(*p))) {
// Positional argument field width
unsigned uj = (unsigned)(*p++ - '0');
while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned)(*p++ - '0');
}
if (*p != '$') {
semsg(_(e_invalid_format_specifier_str), fmt);
goto error;
} else {
p++;
any_pos = 1;
CHECK_POS_ARG;
if (adjust_types(ap_types, (int)uj, num_posarg, arg) == FAIL) {
goto error;
}
}
} else {
any_arg = 1;
CHECK_POS_ARG;
}
} else if (ascii_isdigit((int)(*(arg = p)))) {
// size_t could be wider than unsigned int; make sure we treat
// argument like common implementations do
unsigned uj = (unsigned)(*p++ - '0');
while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned)(*p++ - '0');
}
if (*p == '$') {
semsg(_(e_invalid_format_specifier_str), fmt);
goto error;
}
}
// parse precision
if (*p == '.') {
p++;
if (*(arg = p) == '*') {
p++;
if (ascii_isdigit((int)(*p))) {
// Parse precision
unsigned uj = (unsigned)(*p++ - '0');
while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned)(*p++ - '0');
}
if (*p == '$') {
any_pos = 1;
CHECK_POS_ARG;
p++;
if (adjust_types(ap_types, (int)uj, num_posarg, arg) == FAIL) {
goto error;
}
} else {
semsg(_(e_invalid_format_specifier_str), fmt);
goto error;
}
} else {
any_arg = 1;
CHECK_POS_ARG;
}
} else if (ascii_isdigit((int)(*(arg = p)))) {
// size_t could be wider than unsigned int; make sure we
// treat argument like common implementations do
unsigned uj = (unsigned)(*p++ - '0');
while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned)(*p++ - '0');
}
if (*p == '$') {
semsg(_(e_invalid_format_specifier_str), fmt);
goto error;
}
}
}
if (pos_arg != -1) {
any_pos = 1;
CHECK_POS_ARG;
ptype = p;
}
// parse 'h', 'l' and 'll' length modifiers
if (*p == 'h' || *p == 'l') {
length_modifier = *p;
p++;
if (length_modifier == 'l' && *p == 'l') {
// double l = long long
length_modifier = 'L';
p++;
}
}
switch (*p) {
// Check for known format specifiers. % is special!
case 'i':
case '*':
case 'd':
case 'u':
case 'o':
case 'D':
case 'U':
case 'O':
case 'x':
case 'X':
case 'b':
case 'B':
case 'c':
case 's':
case 'S':
case 'p':
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
if (pos_arg != -1) {
if (adjust_types(ap_types, pos_arg, num_posarg, ptype) == FAIL) {
goto error;
}
} else {
any_arg = 1;
CHECK_POS_ARG;
}
break;
default:
if (pos_arg != -1) {
semsg(_(e_cannot_mix_positional_and_non_positional_str), fmt);
goto error;
}
}
if (*p != NUL) {
p++; // step over the just processed conversion specifier
}
}
}
for (int arg_idx = 0; arg_idx < *num_posarg; arg_idx++) {
if ((*ap_types)[arg_idx] == NULL) {
semsg(_(e_fmt_arg_nr_unused_str), arg_idx + 1, fmt);
goto error;
}
if (tvs != NULL && tvs[arg_idx].v_type == VAR_UNKNOWN) {
semsg(_(e_positional_nr_out_of_bounds_str), arg_idx + 1, fmt);
goto error;
}
}
return OK;
error:
xfree(*ap_types);
*ap_types = NULL;
*num_posarg = 0;
return FAIL;
}
static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, int *arg_idx,
int *arg_cur)
FUNC_ATTR_NONNULL_ARG(3, 4, 5)
{
int arg_min = 0;
if (*arg_cur + 1 == *arg_idx) {
(*arg_cur)++;
(*arg_idx)++;
return;
}
if (*arg_cur >= *arg_idx) {
// Reset ap to ap_start and skip arg_idx - 1 types
va_end(*ap);
va_copy(*ap, ap_start);
} else {
// Skip over any we should skip
arg_min = *arg_cur;
}
for (*arg_cur = arg_min; *arg_cur < *arg_idx - 1; (*arg_cur)++) {
assert(ap_types != NULL);
const char *p = ap_types[*arg_cur];
int fmt_type = format_typeof(p, true);
// get parameter value, do initial processing
switch (fmt_type) {
case TYPE_PERCENT:
case TYPE_UNKNOWN:
break;
case TYPE_CHAR:
va_arg(*ap, int);
break;
case TYPE_STRING:
va_arg(*ap, const char *);
break;
case TYPE_POINTER:
va_arg(*ap, void *);
break;
case TYPE_INT:
va_arg(*ap, int);
break;
case TYPE_LONGINT:
va_arg(*ap, long);
break;
case TYPE_LONGLONGINT:
va_arg(*ap, long long); // NOLINT(runtime/int)
break;
case TYPE_UNSIGNEDINT:
va_arg(*ap, unsigned);
break;
case TYPE_UNSIGNEDLONGINT:
va_arg(*ap, unsigned long);
break;
case TYPE_UNSIGNEDLONGLONGINT:
va_arg(*ap, unsigned long long); // NOLINT(runtime/int)
break;
case TYPE_FLOAT:
va_arg(*ap, double);
break;
}
}
// Because we know that after we return from this call,
// a va_arg() call is made, we can pre-emptively
// increment the current argument index.
(*arg_cur)++;
(*arg_idx)++;
}
/// Write formatted value to the string
///
/// @param[out] str String to write to.
@@ -728,12 +1319,23 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap)
///
/// @return Number of bytes excluding NUL byte that would be written to the
/// string if str_m was greater or equal to the return value.
int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, typval_T *const tvs)
int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_start,
typval_T *const tvs)
{
size_t str_l = 0;
bool str_avail = str_l < str_m;
const char *p = fmt;
int arg_cur = 0;
int num_posarg = 0;
int arg_idx = 1;
va_list ap;
const char **ap_types = NULL;
if (parse_fmt_types(&ap_types, &num_posarg, fmt, tvs) == FAIL) {
return 0;
}
va_copy(ap, ap_start);
if (!p) {
p = "";
@@ -789,8 +1391,31 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
// buffer for 's' and 'S' specs
char *tofree = NULL;
// variable for positional arg
int pos_arg = -1;
p++; // skip '%'
// First check to see if we find a positional
// argument specifier
const char *ptype = p;
while (ascii_isdigit(*ptype)) {
ptype++;
}
if (*ptype == '$') {
// Positional argument
unsigned uj = (unsigned)(*p++ - '0');
while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned)(*p++ - '0');
}
pos_arg = (int)uj;
p++;
}
// parse flags
while (true) {
switch (*p) {
@@ -817,7 +1442,24 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
// parse field width
if (*p == '*') {
p++;
const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int);
if (ascii_isdigit((int)(*p))) {
// Positional argument field width
unsigned uj = (unsigned)(*p++ - '0');
while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned)(*p++ - '0');
}
arg_idx = (int)uj;
p++;
}
const int j = (tvs
? (int)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, int)));
if (j >= 0) {
min_field_width = (size_t)j;
} else {
@@ -839,16 +1481,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
if (*p == '.') {
p++;
precision_specified = 1;
if (*p == '*') {
const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int);
p++;
if (j >= 0) {
precision = (size_t)j;
} else {
precision_specified = 0;
precision = 0;
}
} else if (ascii_isdigit((int)(*p))) {
if (ascii_isdigit((int)(*p))) {
// size_t could be wider than unsigned int; make sure we
// treat argument like common implementations do
unsigned uj = (unsigned)(*p++ - '0');
@@ -857,6 +1491,32 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
uj = 10 * uj + (unsigned)(*p++ - '0');
}
precision = uj;
} else if (*p == '*') {
p++;
if (ascii_isdigit((int)(*p))) {
// positional argument
unsigned uj = (unsigned)(*p++ - '0');
while (ascii_isdigit((int)(*p))) {
uj = 10 * uj + (unsigned)(*p++ - '0');
}
arg_idx = (int)uj;
p++;
}
const int j = (tvs
? (int)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, int)));
if (j >= 0) {
precision = (size_t)j;
} else {
precision_specified = 0;
precision = 0;
}
}
}
@@ -864,8 +1524,9 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
if (*p == 'h' || *p == 'l' || *p == 'z') {
length_modifier = *p;
p++;
if (length_modifier == 'l' && *p == 'l') { // ll, encoded as 2
length_modifier = '2';
if (length_modifier == 'l' && *p == 'l') {
// double l = long long
length_modifier = 'L';
p++;
}
}
@@ -895,10 +1556,14 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
case 'x':
case 'X':
if (tvs && length_modifier == '\0') {
length_modifier = '2';
length_modifier = 'L';
}
}
if (pos_arg != -1) {
arg_idx = pos_arg;
}
// get parameter value, do initial processing
switch (fmt_spec) {
// '%' and 'c' behave similar to 's' regarding flags and field widths
@@ -913,7 +1578,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
break;
case 'c': {
const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int);
const int j = (tvs
? (int)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, int)));
// standard demands unsigned char
uchar_arg = (unsigned char)j;
str_arg = (char *)&uchar_arg;
@@ -922,8 +1591,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
case 's':
case 'S':
str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree)
: va_arg(ap, const char *);
str_arg = (tvs
? tv_str(tvs, &arg_idx, &tofree)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, const char *)));
if (!str_arg) {
str_arg = "[NULL]";
str_arg_l = 6;
@@ -990,7 +1662,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
const void *ptr_arg = NULL;
if (fmt_spec == 'p') {
ptr_arg = tvs ? tv_ptr(tvs, &arg_idx) : va_arg(ap, void *);
ptr_arg = (tvs
? tv_ptr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, void *)));
if (ptr_arg) {
arg_sign = 1;
}
@@ -998,23 +1674,36 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
// signed
switch (length_modifier) {
case '\0':
arg = (int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int));
arg = (tvs
? (int)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, int)));
break;
case 'h':
// char and short arguments are passed as int16_t
arg = (int16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int));
arg = (int16_t)
(tvs
? (int)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, int)));
break;
case 'l':
arg = (tvs ? (long)tv_nr(tvs, &arg_idx) : va_arg(ap, long));
arg = (tvs
? (long)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, long)));
break;
case '2':
arg = (
tvs
? (long long)tv_nr(tvs, &arg_idx) // NOLINT (runtime/int)
: va_arg(ap, long long)); // NOLINT (runtime/int)
case 'L':
arg = (tvs
? (long long)tv_nr(tvs, &arg_idx) // NOLINT(runtime/int)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, long long))); // NOLINT(runtime/int)
break;
case 'z':
arg = (tvs ? (ptrdiff_t)tv_nr(tvs, &arg_idx) : va_arg(ap, ptrdiff_t));
arg = (tvs
? (ptrdiff_t)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, ptrdiff_t)));
break;
}
if (arg > 0) {
@@ -1026,23 +1715,35 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
// unsigned
switch (length_modifier) {
case '\0':
uarg = (unsigned)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned));
uarg = (tvs
? (unsigned)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, unsigned)));
break;
case 'h':
uarg = (uint16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned));
uarg = (uint16_t)
(tvs
? (unsigned)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, unsigned)));
break;
case 'l':
uarg = (tvs ? (unsigned long)tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned long));
uarg = (tvs
? (unsigned long)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, unsigned long)));
break;
case '2':
uarg = (uintmax_t)(unsigned long long)( // NOLINT (runtime/int)
tvs
? ((unsigned long long) // NOLINT (runtime/int)
tv_nr(tvs, &arg_idx))
: va_arg(ap, unsigned long long)); // NOLINT (runtime/int)
case 'L':
uarg = (tvs
? (unsigned long long)tv_nr(tvs, &arg_idx) // NOLINT(runtime/int)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, unsigned long long))); // NOLINT(runtime/int)
break;
case 'z':
uarg = (tvs ? (size_t)tv_nr(tvs, &arg_idx) : va_arg(ap, size_t));
uarg = (tvs
? (size_t)tv_nr(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, size_t)));
break;
}
arg_sign = (uarg != 0);
@@ -1177,7 +1878,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
char format[40];
int remove_trailing_zeroes = false;
double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double);
double f = (tvs
? tv_float(tvs, &arg_idx)
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
va_arg(ap, double)));
double abs_f = f < 0 ? -f : f;
if (fmt_spec == 'g' || fmt_spec == 'G') {
@@ -1395,10 +2100,14 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
}
if (tvs && tvs[arg_idx - 1].v_type != VAR_UNKNOWN) {
if (tvs != NULL
&& tvs[num_posarg != 0 ? num_posarg : arg_idx - 1].v_type != VAR_UNKNOWN) {
emsg(_("E767: Too many arguments to printf()"));
}
xfree(ap_types);
va_end(ap);
// return the number of characters formatted (excluding trailing nul
// character); that is, the number of characters that would have been
// written to the buffer if it were large enough.