Merge #2490: Add support for '%zu' in vim_snprintf().

Reviewed-by: Gustaf Lindstedt <gustaflindstedt@gmail.com>
Reviewed-by: Scott Prager <splinterofchaos@gmail.com>
This commit is contained in:
Eliseo Martínez 2015-05-01 18:50:40 +02:00
commit 4fb79b1f76

View File

@ -148,7 +148,7 @@ int msg_attr(char_u *s, int attr) FUNC_ATTR_NONNULL_ARG(1)
return msg_attr_keep(s, attr, FALSE); return msg_attr_keep(s, attr, FALSE);
} }
int int
msg_attr_keep ( msg_attr_keep (
char_u *s, char_u *s,
int attr, int attr,
@ -674,7 +674,7 @@ char_u *msg_may_trunc(int force, char_u *s)
return s; return s;
} }
static void static void
add_msg_hist ( add_msg_hist (
char_u *s, char_u *s,
int len, /* -1 for undetermined length */ int len, /* -1 for undetermined length */
@ -1231,7 +1231,7 @@ void msg_make(char_u *arg)
* This function is used to show mappings, where we want to see how to type * This function is used to show mappings, where we want to see how to type
* the character/string -- webb * the character/string -- webb
*/ */
int int
msg_outtrans_special ( msg_outtrans_special (
char_u *strstart, char_u *strstart,
int from /* TRUE for lhs of a mapping */ int from /* TRUE for lhs of a mapping */
@ -1824,7 +1824,7 @@ static int do_clear_sb_text = FALSE; /* clear text on next msg */
/* /*
* Store part of a printed message for displaying when scrolling back. * Store part of a printed message for displaying when scrolling back.
*/ */
static void static void
store_sb_text ( store_sb_text (
char_u **sb_str, /* start of string */ char_u **sb_str, /* start of string */
char_u *s, /* just after string */ char_u *s, /* just after string */
@ -2641,7 +2641,7 @@ void msg_advance(int col)
* A '&' in a button name becomes a shortcut, so each '&' should be before a * A '&' in a button name becomes a shortcut, so each '&' should be before a
* different letter. * different letter.
*/ */
int int
do_dialog ( do_dialog (
int type, int type,
char_u *title, char_u *title,
@ -2733,7 +2733,7 @@ do_dialog (
* Copy one character from "*from" to "*to", taking care of multi-byte * Copy one character from "*from" to "*to", taking care of multi-byte
* characters. Return the length of the character in bytes. * characters. Return the length of the character in bytes.
*/ */
static int static int
copy_char ( copy_char (
char_u *from, char_u *from,
char_u *to, char_u *to,
@ -3117,14 +3117,14 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
{ {
size_t str_l = 0; size_t str_l = 0;
bool str_avail = str_l < str_m; bool str_avail = str_l < str_m;
char *p = fmt; char *p = fmt;
int arg_idx = 1; int arg_idx = 1;
if (p == NULL) if (!p)
p = ""; p = "";
while (*p != NUL) { while (*p) {
if (*p != '%') { if (*p != '%') {
/* Copy up to the next '%' or NUL without any changes. */ // copy up to the next '%' or NUL without any changes
size_t n = (size_t)(xstrchrnul(p + 1, '%') - p); size_t n = (size_t)(xstrchrnul(p + 1, '%') - p);
if (str_avail) { if (str_avail) {
size_t avail = str_m - str_l; size_t avail = str_m - str_l;
@ -3139,68 +3139,60 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
int zero_padding = 0, precision_specified = 0, justify_left = 0; int zero_padding = 0, precision_specified = 0, justify_left = 0;
int alternate_form = 0, force_sign = 0; int alternate_form = 0, force_sign = 0;
/* If both the ' ' and '+' flags appear, the ' ' flag should be // if both ' ' and '+' flags appear, ' ' flag should be ignored
* ignored. */
int space_for_positive = 1; int space_for_positive = 1;
/* allowed values: \0, h, l, L */ // allowed values: \0, h, l, 2 (for ll), z, L
char length_modifier = '\0'; char length_modifier = '\0';
/* temporary buffer for simple numeric->string conversion */ // temporary buffer for simple numeric->string conversion
# define TMP_LEN 350 /* On my system 1e308 is the biggest number possible. # define TMP_LEN 350 // 1e308 seems reasonable as the maximum printable
* That sounds reasonable to use as the maximum
* printable. */
char tmp[TMP_LEN]; char tmp[TMP_LEN];
/* string address in case of string argument */ // string address in case of string argument
char *str_arg; char *str_arg;
/* natural field width of arg without padding and sign */ // natural field width of arg without padding and sign
size_t str_arg_l; size_t str_arg_l;
/* unsigned char argument value - only defined for c conversion. // unsigned char argument value (only defined for c conversion);
* N.B. standard explicitly states the char argument for the c // standard explicitly states the char argument for the c
* conversion is unsigned */ // conversion is unsigned
unsigned char uchar_arg; unsigned char uchar_arg;
/* number of zeros to be inserted for numeric conversions as // number of zeros to be inserted for numeric conversions as
* required by the precision or minimal field width */ // required by the precision or minimal field width
size_t number_of_zeros_to_pad = 0; size_t number_of_zeros_to_pad = 0;
/* index into tmp where zero padding is to be inserted */ // index into tmp where zero padding is to be inserted
size_t zero_padding_insertion_ind = 0; size_t zero_padding_insertion_ind = 0;
/* current conversion specifier character */ // current conversion specifier character
char fmt_spec = '\0'; char fmt_spec = '\0';
str_arg = NULL; str_arg = NULL;
p++; /* skip '%' */ p++; // skip '%'
/* parse flags */ // parse flags
while (*p == '0' || *p == '-' || *p == '+' || *p == ' ' while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
|| *p == '#' || *p == '\'') { || *p == '#' || *p == '\'') {
switch (*p) { switch (*p) {
case '0': zero_padding = 1; break; case '0': zero_padding = 1; break;
case '-': justify_left = 1; break; case '-': justify_left = 1; break;
case '+': force_sign = 1; space_for_positive = 0; break; // if both '0' and '-' flags appear, '0' should be ignored
case ' ': force_sign = 1; case '+': force_sign = 1; space_for_positive = 0; break;
/* If both the ' ' and '+' flags appear, the ' ' case ' ': force_sign = 1; break;
* flag should be ignored */ // if both ' ' and '+' flags appear, ' ' should be ignored
break; case '#': alternate_form = 1; break;
case '#': alternate_form = 1; break; case '\'': break;
case '\'': break;
} }
p++; p++;
} }
/* If the '0' and '-' flags both appear, the '0' flag should be
* ignored. */
/* parse field width */ // parse field width
if (*p == '*') { if (*p == '*') {
int j;
p++; p++;
j = tvs != NULL ? tv_nr(tvs, &arg_idx) : va_arg(ap, int); int j = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int);
if (j >= 0) if (j >= 0)
min_field_width = j; min_field_width = j;
else { else {
@ -3208,8 +3200,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
justify_left = 1; justify_left = 1;
} }
} else if (ascii_isdigit((int)(*p))) { } else if (ascii_isdigit((int)(*p))) {
/* size_t could be wider than unsigned int; make sure we treat // size_t could be wider than unsigned int; make sure we treat
* argument like common implementations do */ // argument like common implementations do
unsigned int uj = *p++ - '0'; unsigned int uj = *p++ - '0';
while (ascii_isdigit((int)(*p))) while (ascii_isdigit((int)(*p)))
@ -3217,14 +3209,12 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
min_field_width = uj; min_field_width = uj;
} }
/* parse precision */ // parse precision
if (*p == '.') { if (*p == '.') {
p++; p++;
precision_specified = 1; precision_specified = 1;
if (*p == '*') { if (*p == '*') {
int j; int j = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int);
j = tvs != NULL ? tv_nr(tvs, &arg_idx) : va_arg(ap, int);
p++; p++;
if (j >= 0) if (j >= 0)
precision = j; precision = j;
@ -3233,8 +3223,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
precision = 0; precision = 0;
} }
} else if (ascii_isdigit((int)(*p))) { } else if (ascii_isdigit((int)(*p))) {
/* size_t could be wider than unsigned int; make sure we // size_t could be wider than unsigned int; make sure we
* treat argument like common implementations do */ // treat argument like common implementations do
unsigned int uj = *p++ - '0'; unsigned int uj = *p++ - '0';
while (ascii_isdigit((int)(*p))) while (ascii_isdigit((int)(*p)))
@ -3243,46 +3233,41 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
} }
/* parse 'h', 'l' and 'll' length modifiers */ // parse 'h', 'l', 'll' and 'z' length modifiers
if (*p == 'h' || *p == 'l') { if (*p == 'h' || *p == 'l' || *p == 'z') {
length_modifier = *p; length_modifier = *p;
p++; p++;
if (length_modifier == 'l' && *p == 'l') { /* double l = long long */ if (length_modifier == 'l' && *p == 'l') { // ll, encoded as 2
length_modifier = '2'; /* double l encoded as '2' */ length_modifier = '2';
p++; p++;
} }
} }
fmt_spec = *p; fmt_spec = *p;
/* common synonyms: */ // common synonyms
switch (fmt_spec) { switch (fmt_spec) {
case 'i': fmt_spec = 'd'; break; case 'i': fmt_spec = 'd'; break;
case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
case 'F': fmt_spec = 'f'; break; case 'F': fmt_spec = 'f'; break;
default: break; default: break;
} }
/* get parameter value, do initial processing */ // get parameter value, do initial processing
switch (fmt_spec) { switch (fmt_spec) {
/* '%' and 'c' behave similar to 's' regarding flags and field widths */ // '%' and 'c' behave similar to 's' regarding flags and field widths
case '%': case '%': case 'c': case 's': case 'S':
case 'c':
case 's':
case 'S':
str_arg_l = 1; str_arg_l = 1;
switch (fmt_spec) { switch (fmt_spec) {
case '%': case '%':
str_arg = p; str_arg = p;
break; break;
case 'c': case 'c': {
{ int j = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int);
int j; // standard demands unsigned char
j = tvs != NULL ? tv_nr(tvs, &arg_idx) : va_arg(ap, int);
/* standard demands unsigned char */
uchar_arg = (unsigned char)j; uchar_arg = (unsigned char)j;
str_arg = (char *)&uchar_arg; str_arg = (char *)&uchar_arg;
break; break;
@ -3290,39 +3275,33 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
case 's': case 's':
case 'S': case 'S':
str_arg = tvs != NULL ? tv_str(tvs, &arg_idx) : va_arg(ap, char *); str_arg = tvs ? tv_str(tvs, &arg_idx) : va_arg(ap, char *);
if (str_arg == NULL) { if (!str_arg) {
str_arg = "[NULL]"; str_arg = "[NULL]";
str_arg_l = 6; str_arg_l = 6;
} }
/* make sure not to address string beyond the specified // make sure not to address string beyond the specified precision
* precision !!! */
else if (!precision_specified) else if (!precision_specified)
str_arg_l = strlen(str_arg); str_arg_l = strlen(str_arg);
/* truncate string if necessary as requested by precision */ // truncate string if necessary as requested by precision
else if (precision == 0) else if (precision == 0)
str_arg_l = 0; str_arg_l = 0;
else { else {
/* Don't put the #if inside memchr(), it can be a // memchr on HP does not like n > 2^31
* macro. */ // TODO(elmart): check if this still holds / is relevant
/* memchr on HP does not like n > 2^31 !!! */ str_arg_l = (size_t)((char *)xmemscan(str_arg,
char *q = memchr(str_arg, '\0', NUL,
precision <= (size_t)0x7fffffffL ? precision MIN(precision, 0x7fffffff))
: (size_t)0x7fffffffL); - str_arg);
str_arg_l = (q == NULL) ? precision
: (size_t)(q - str_arg);
} }
if (fmt_spec == 'S') { if (fmt_spec == 'S') {
if (min_field_width != 0) if (min_field_width != 0)
min_field_width += STRLEN(str_arg) min_field_width += strlen(str_arg)
- mb_string2cells((char_u *) str_arg); - mb_string2cells((char_u *) str_arg);
if (precision) { if (precision) {
char_u *p1 = (char_u *)str_arg; char_u *p1 = (char_u *)str_arg;
size_t i; for (size_t i = 0; i < precision && *p1; i++)
for (i = 0; i < precision && *p1; i++)
p1 += mb_ptr2len(p1); p1 += mb_ptr2len(p1);
str_arg_l = precision = p1 - (char_u *)str_arg; str_arg_l = precision = p1 - (char_u *)str_arg;
} }
} }
@ -3333,64 +3312,60 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
break; break;
case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
{ // u, o, x, X and p conversion specifiers imply the value is unsigned;
/* NOTE: the u, o, x, X and p conversion specifiers // d implies a signed value
* imply the value is unsigned; d implies a signed
* value */
/* 0 if numeric argument is zero (or if pointer is // 0 if numeric argument is zero (or if pointer is NULL for 'p'),
* NULL for 'p'), +1 if greater than zero (or nonzero // +1 if greater than zero (or non NULL for 'p'),
* for unsigned arguments), -1 if negative (unsigned // -1 if negative (unsigned argument is never negative)
* argument is never negative) */
int arg_sign = 0; int arg_sign = 0;
/* only defined for length modifier h, or for no // only defined for length modifier h, or for no length modifiers
* length modifiers */
int int_arg = 0; int int_arg = 0;
unsigned int uint_arg = 0; unsigned int uint_arg = 0;
/* only defined for length modifier l */ // only defined for length modifier l
long int long_arg = 0; long int long_arg = 0;
unsigned long int ulong_arg = 0; unsigned long int ulong_arg = 0;
/* only defined for length modifier ll */ // only defined for length modifier ll
long long int long_long_arg = 0; long long int long_long_arg = 0;
unsigned long long int ulong_long_arg = 0; unsigned long long int ulong_long_arg = 0;
/* pointer argument value -only defined for p // only defined for length modifier z
* conversion */ size_t size_t_arg = 0;
// only defined for p conversion
void *ptr_arg = NULL; void *ptr_arg = NULL;
if (fmt_spec == 'p') { if (fmt_spec == 'p') {
length_modifier = '\0'; length_modifier = '\0';
ptr_arg = tvs != NULL ? (void *)tv_str(tvs, &arg_idx) ptr_arg = tvs ? (void *)tv_str(tvs, &arg_idx) : va_arg(ap, void *);
: va_arg(ap, void *); if (ptr_arg)
if (ptr_arg != NULL)
arg_sign = 1; arg_sign = 1;
} else if (fmt_spec == 'd') { } else if (fmt_spec == 'd') {
/* signed */ // signed
switch (length_modifier) { switch (length_modifier) {
case '\0': case '\0':
case 'h': case 'h':
/* char and short arguments are passed as int. */ // char and short arguments are passed as int
int_arg = tvs != NULL ? tv_nr(tvs, &arg_idx) : va_arg(ap, int); int_arg = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int);
if (int_arg > 0) if (int_arg > 0)
arg_sign = 1; arg_sign = 1;
else if (int_arg < 0) else if (int_arg < 0)
arg_sign = -1; arg_sign = -1;
break; break;
case 'l': case 'l':
long_arg = tvs != NULL ? tv_nr(tvs, &arg_idx) long_arg = tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, long int);
: va_arg(ap, long int);
if (long_arg > 0) if (long_arg > 0)
arg_sign = 1; arg_sign = 1;
else if (long_arg < 0) else if (long_arg < 0)
arg_sign = -1; arg_sign = -1;
break; break;
case '2': case '2':
long_long_arg = tvs != NULL ? tv_nr(tvs, &arg_idx) long_long_arg = tvs ? tv_nr(tvs, &arg_idx)
: va_arg(ap, long long int); : va_arg(ap, long long int);
if (long_long_arg > 0) if (long_long_arg > 0)
arg_sign = 1; arg_sign = 1;
else if (long_long_arg < 0) else if (long_long_arg < 0)
@ -3398,68 +3373,67 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
break; break;
} }
} else { } else {
/* unsigned */ // unsigned
switch (length_modifier) { switch (length_modifier) {
case '\0': case '\0':
case 'h': case 'h':
uint_arg = tvs != NULL ? (unsigned)tv_nr(tvs, &arg_idx) uint_arg = tvs ? (unsigned)tv_nr(tvs, &arg_idx)
: va_arg(ap, unsigned int); : va_arg(ap, unsigned int);
if (uint_arg != 0) if (uint_arg != 0)
arg_sign = 1; arg_sign = 1;
break; break;
case 'l': case 'l':
ulong_arg = tvs != NULL ? (unsigned long)tv_nr(tvs, &arg_idx) ulong_arg = tvs ? (unsigned long)tv_nr(tvs, &arg_idx)
: va_arg(ap, unsigned long int); : va_arg(ap, unsigned long int);
if (ulong_arg != 0) if (ulong_arg != 0)
arg_sign = 1; arg_sign = 1;
break; break;
case '2': case '2':
ulong_long_arg = tvs != NULL ? ulong_long_arg = tvs ? (unsigned long long)tv_nr(tvs, &arg_idx)
(unsigned long long)tv_nr(tvs, &arg_idx) : : va_arg(ap, unsigned long long int);
va_arg(ap, unsigned long long int);
if (ulong_long_arg) arg_sign = 1; if (ulong_long_arg) arg_sign = 1;
break; break;
case 'z':
size_t_arg = tvs ? (size_t)tv_nr(tvs, &arg_idx)
: va_arg(ap, size_t);
if (size_t_arg) arg_sign = 1;
break;
} }
} }
str_arg = tmp; str_arg = tmp;
str_arg_l = 0; str_arg_l = 0;
/* NOTE: // For d, i, u, o, x, and X conversions, if precision is specified,
* For d, i, u, o, x, and X conversions, if precision is // '0' flag should be ignored. This is so with Solaris 2.6, Digital UNIX
* specified, the '0' flag should be ignored. This is so // 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
* with Solaris 2.6, Digital UNIX 4.0, HPUX 10, Linux,
* FreeBSD, NetBSD; but not with Perl.
*/
if (precision_specified) if (precision_specified)
zero_padding = 0; zero_padding = 0;
if (fmt_spec == 'd') { if (fmt_spec == 'd') {
if (force_sign && arg_sign >= 0) if (force_sign && arg_sign >= 0)
tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
/* leave negative numbers for sprintf to handle, to // leave negative numbers for sprintf to handle, to
* avoid handling tricky cases like (short int)-32768 */ // avoid handling tricky cases like (short int)-32768
} else if (alternate_form) { } else if (alternate_form) {
if (arg_sign != 0 if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') ) {
&& (fmt_spec == 'x' || fmt_spec == 'X') ) {
tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = '0';
tmp[str_arg_l++] = fmt_spec; tmp[str_arg_l++] = fmt_spec;
} }
/* alternate form should have no effect for p // alternate form should have no effect for p * conversion, but ...
* conversion, but ... */
} }
zero_padding_insertion_ind = str_arg_l; zero_padding_insertion_ind = str_arg_l;
if (!precision_specified) if (!precision_specified)
precision = 1; /* default precision is 1 */ precision = 1; // default precision is 1
if (precision == 0 && arg_sign == 0) { if (precision == 0 && arg_sign == 0) {
/* When zero value is formatted with an explicit // when zero value is formatted with an explicit precision 0,
* precision 0, the resulting formatted string is // resulting formatted string is empty (d, i, u, o, x, X, p)
* empty (d, i, u, o, x, X, p). */
} else { } else {
char f[5]; char f[5];
int f_l = 0; int f_l = 0;
/* construct a simple format string for sprintf */ // construct a simple format string for sprintf
f[f_l++] = '%'; f[f_l++] = '%';
if (!length_modifier) if (!length_modifier)
; ;
@ -3474,38 +3448,33 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
if (fmt_spec == 'p') if (fmt_spec == 'p')
str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg); str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg);
else if (fmt_spec == 'd') { else if (fmt_spec == 'd') {
/* signed */ // signed
switch (length_modifier) { switch (length_modifier) {
case '\0': case '\0':
case 'h': str_arg_l += sprintf( case 'h': str_arg_l += sprintf(tmp + str_arg_l, f, int_arg);
tmp + str_arg_l, f, int_arg); break;
break; case 'l': str_arg_l += sprintf(tmp + str_arg_l, f, long_arg);
case 'l': str_arg_l += sprintf( break;
tmp + str_arg_l, f, long_arg); case '2': str_arg_l += sprintf(tmp + str_arg_l, f, long_long_arg);
break; break;
case '2': str_arg_l += sprintf(
tmp + str_arg_l, f, long_long_arg);
break;
} }
} else { } else {
/* unsigned */ // unsigned
switch (length_modifier) { switch (length_modifier) {
case '\0': case '\0':
case 'h': str_arg_l += sprintf( case 'h': str_arg_l += sprintf(tmp + str_arg_l, f, uint_arg);
tmp + str_arg_l, f, uint_arg); break;
break; case 'l': str_arg_l += sprintf(tmp + str_arg_l, f, ulong_arg);
case 'l': str_arg_l += sprintf( break;
tmp + str_arg_l, f, ulong_arg); case '2': str_arg_l += sprintf(tmp + str_arg_l, f, ulong_long_arg);
break; break;
case '2': str_arg_l += sprintf( case 'z': str_arg_l += sprintf(tmp + str_arg_l, f, size_t_arg);
tmp + str_arg_l, f, ulong_long_arg); break;
break;
} }
} }
/* include the optional minus sign and possible // include the optional minus sign and possible "0x" in the region
* "0x" in the region before the zero padding // before the zero padding insertion point
* insertion point */
if (zero_padding_insertion_ind < str_arg_l if (zero_padding_insertion_ind < str_arg_l
&& tmp[zero_padding_insertion_ind] == '-') && tmp[zero_padding_insertion_ind] == '-')
zero_padding_insertion_ind++; zero_padding_insertion_ind++;
@ -3517,30 +3486,26 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
{ {
size_t num_of_digits = str_arg_l size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
- zero_padding_insertion_ind;
if (alternate_form && fmt_spec == 'o' if (alternate_form && fmt_spec == 'o'
/* unless zero is already the first // unless zero is already the first character
* character */
&& !(zero_padding_insertion_ind < str_arg_l && !(zero_padding_insertion_ind < str_arg_l
&& tmp[zero_padding_insertion_ind] == '0')) { && tmp[zero_padding_insertion_ind] == '0')) {
/* assure leading zero for alternate-form // assure leading zero for alternate-form octal numbers
* octal numbers */
if (!precision_specified if (!precision_specified
|| precision < num_of_digits + 1) { || precision < num_of_digits + 1) {
/* precision is increased to force the // precision is increased to force the first character to be zero,
* first character to be zero, except if a // except if a zero value is formatted with an explicit precision
* zero value is formatted with an // of zero
* explicit precision of zero */
precision = num_of_digits + 1; precision = num_of_digits + 1;
} }
} }
/* zero padding to specified precision? */ // zero padding to specified precision?
if (num_of_digits < precision) if (num_of_digits < precision)
number_of_zeros_to_pad = precision - num_of_digits; number_of_zeros_to_pad = precision - num_of_digits;
} }
/* zero padding to specified minimal field width? */ // zero padding to specified minimal field width?
if (!justify_left && zero_padding) { if (!justify_left && zero_padding) {
int n = (int)(min_field_width - (str_arg_l int n = (int)(min_field_width - (str_arg_l
+ number_of_zeros_to_pad)); + number_of_zeros_to_pad));
@ -3556,21 +3521,17 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
case 'g': case 'g':
case 'G': case 'G':
{ {
/* Floating point. */ // floating point
double f;
double abs_f;
char format[40]; char format[40];
int l; int l;
int remove_trailing_zeroes = FALSE; int remove_trailing_zeroes = FALSE;
f = tvs != NULL ? tv_float(tvs, &arg_idx) : va_arg(ap, double); double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double);
abs_f = f < 0 ? -f : f; double abs_f = f < 0 ? -f : f;
if (fmt_spec == 'g' || fmt_spec == 'G') { if (fmt_spec == 'g' || fmt_spec == 'G') {
/* Would be nice to use %g directly, but it prints // can't use %g directly, cause it prints "1.0" as "1"
* "1.0" as "1", we don't want that. */ if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0)
if ((abs_f >= 0.001 && abs_f < 10000000.0)
|| abs_f == 0.0)
fmt_spec = 'f'; fmt_spec = 'f';
else else
fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; fmt_spec = fmt_spec == 'g' ? 'e' : 'E';
@ -3578,7 +3539,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
if (fmt_spec == 'f' && abs_f > 1.0e307) { if (fmt_spec == 'f' && abs_f > 1.0e307) {
/* Avoid a buffer overflow */ // avoid a buffer overflow
strcpy(tmp, "inf"); strcpy(tmp, "inf");
str_arg_l = 3; str_arg_l = 3;
} else { } else {
@ -3587,8 +3548,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
if (precision_specified) { if (precision_specified) {
size_t max_prec = TMP_LEN - 10; size_t max_prec = TMP_LEN - 10;
/* Make sure we don't get more digits than we // make sure we don't get more digits than we have room for
* have room for. */
if (fmt_spec == 'f' && abs_f > 1.0) if (fmt_spec == 'f' && abs_f > 1.0)
max_prec -= (size_t)log10(abs_f); max_prec -= (size_t)log10(abs_f);
if (precision > max_prec) if (precision > max_prec)
@ -3603,23 +3563,22 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
int i; int i;
char *tp; char *tp;
/* Using %g or %G: remove superfluous zeroes. */ // using %g or %G: remove superfluous zeroes
if (fmt_spec == 'f') if (fmt_spec == 'f')
tp = tmp + str_arg_l - 1; tp = tmp + str_arg_l - 1;
else { else {
tp = (char *)vim_strchr((char_u *)tmp, tp = (char *)vim_strchr((char_u *)tmp,
fmt_spec == 'e' ? 'e' : 'E'); fmt_spec == 'e' ? 'e' : 'E');
if (tp != NULL) { if (tp) {
/* Remove superfluous '+' and leading // remove superfluous '+' and leading zeroes from exponent
* zeroes from the exponent. */
if (tp[1] == '+') { if (tp[1] == '+') {
/* Change "1.0e+07" to "1.0e07" */ // change "1.0e+07" to "1.0e07"
STRMOVE(tp + 1, tp + 2); STRMOVE(tp + 1, tp + 2);
--str_arg_l; --str_arg_l;
} }
i = (tp[1] == '-') ? 2 : 1; i = (tp[1] == '-') ? 2 : 1;
while (tp[i] == '0') { while (tp[i] == '0') {
/* Change "1.0e07" to "1.0e7" */ // change "1.0e07" to "1.0e7"
STRMOVE(tp + i, tp + i + 1); STRMOVE(tp + i, tp + i + 1);
--str_arg_l; --str_arg_l;
} }
@ -3628,26 +3587,19 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
if (tp != NULL && !precision_specified) if (tp != NULL && !precision_specified)
/* Remove trailing zeroes, but keep the one // remove trailing zeroes, but keep the one just after a dot
* just after a dot. */ while (tp > tmp + 2 && *tp == '0' && tp[-1] != '.') {
while (tp > tmp + 2 && *tp == '0'
&& tp[-1] != '.') {
STRMOVE(tp, tp + 1); STRMOVE(tp, tp + 1);
--tp; --tp;
--str_arg_l; --str_arg_l;
} }
} else { } else {
char *tp; // be consistent: some printf("%e") use 1.0e+12 and some 1.0e+012;
// remove one zero in the last case
/* Be consistent: some printf("%e") use 1.0e+12 char *tp = (char *)vim_strchr((char_u *)tmp,
* and some 1.0e+012. Remove one zero in the last fmt_spec == 'e' ? 'e' : 'E');
* case. */ if (tp && (tp[1] == '+' || tp[1] == '-') && tp[2] == '0'
tp = (char *)vim_strchr((char_u *)tmp, && ascii_isdigit(tp[3]) && ascii_isdigit(tp[4])) {
fmt_spec == 'e' ? 'e' : 'E');
if (tp != NULL && (tp[1] == '+' || tp[1] == '-')
&& tp[2] == '0'
&& ascii_isdigit(tp[3])
&& ascii_isdigit(tp[4])) {
STRMOVE(tp + 2, tp + 3); STRMOVE(tp + 2, tp + 3);
--str_arg_l; --str_arg_l;
} }
@ -3658,33 +3610,30 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
default: default:
/* unrecognized conversion specifier, keep format string // unrecognized conversion specifier, keep format string as-is
* as-is */ zero_padding = 0; // turn zero padding off for non-numeric conversion
zero_padding = 0; /* turn zero padding off for non-numeric
conversion */
justify_left = 1; justify_left = 1;
min_field_width = 0; /* reset flags */ min_field_width = 0; // reset flags
/* discard the unrecognized conversion, just keep * // discard the unrecognized conversion, just keep
* the unrecognized conversion character */ // the unrecognized conversion character
str_arg = p; str_arg = p;
str_arg_l = 0; str_arg_l = 0;
if (*p != NUL) if (*p)
str_arg_l++; /* include invalid conversion specifier str_arg_l++; // include invalid conversion specifier
unchanged if not at end-of-string */ // unchanged if not at end-of-string
break; break;
} }
if (*p != NUL) if (*p)
p++; /* step over the just processed conversion specifier */ p++; // step over the just processed conversion specifier
/* insert padding to the left as requested by min_field_width; // insert padding to the left as requested by min_field_width;
* this does not include the zero padding in case of numerical // this does not include the zero padding in case of numerical conversions
* conversions*/
if (!justify_left) { if (!justify_left) {
assert(str_arg_l <= SIZE_MAX - number_of_zeros_to_pad); assert(str_arg_l <= SIZE_MAX - number_of_zeros_to_pad);
if (min_field_width > str_arg_l + number_of_zeros_to_pad) { if (min_field_width > str_arg_l + number_of_zeros_to_pad) {
/* left padding with blank or zero */ // left padding with blank or zero
size_t pn = min_field_width - (str_arg_l + number_of_zeros_to_pad); size_t pn = min_field_width - (str_arg_l + number_of_zeros_to_pad);
if (str_avail) { if (str_avail) {
size_t avail = str_m - str_l; size_t avail = str_m - str_l;
@ -3696,14 +3645,14 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
} }
/* zero padding as requested by the precision or by the minimal // zero padding as requested by the precision or by the minimal
* field width for numeric conversions required? */ // field width for numeric conversions required?
if (number_of_zeros_to_pad == 0) { if (number_of_zeros_to_pad == 0) {
/* will not copy first part of numeric right now, * // will not copy first part of numeric right now,
* force it to be copied later in its entirety */ // force it to be copied later in its entirety
zero_padding_insertion_ind = 0; zero_padding_insertion_ind = 0;
} else { } else {
/* insert first part of numerics (sign or '0x') before zero padding */ // insert first part of numerics (sign or '0x') before zero padding
if (zero_padding_insertion_ind > 0) { if (zero_padding_insertion_ind > 0) {
size_t zn = zero_padding_insertion_ind; size_t zn = zero_padding_insertion_ind;
if (str_avail) { if (str_avail) {
@ -3715,7 +3664,7 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
str_l += zn; str_l += zn;
} }
/* insert zero padding as requested by precision or min field width */ // insert zero padding as requested by precision or min field width
if (number_of_zeros_to_pad > 0) { if (number_of_zeros_to_pad > 0) {
size_t zn = number_of_zeros_to_pad; size_t zn = number_of_zeros_to_pad;
if (str_avail) { if (str_avail) {
@ -3728,8 +3677,8 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
} }
/* insert formatted string // insert formatted string
* (or as-is conversion specifier for unknown conversions) */ // (or as-is conversion specifier for unknown conversions)
if (str_arg_l > zero_padding_insertion_ind) { if (str_arg_l > zero_padding_insertion_ind) {
size_t sn = str_arg_l - zero_padding_insertion_ind; size_t sn = str_arg_l - zero_padding_insertion_ind;
if (str_avail) { if (str_avail) {
@ -3743,11 +3692,11 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
str_l += sn; str_l += sn;
} }
/* insert right padding */ // insert right padding
if (justify_left) { if (justify_left) {
assert(str_arg_l <= SIZE_MAX - number_of_zeros_to_pad); assert(str_arg_l <= SIZE_MAX - number_of_zeros_to_pad);
if (min_field_width > str_arg_l + number_of_zeros_to_pad) { if (min_field_width > str_arg_l + number_of_zeros_to_pad) {
/* right blank padding to the field width */ // right blank padding to the field width
size_t pn = min_field_width - (str_arg_l + number_of_zeros_to_pad); size_t pn = min_field_width - (str_arg_l + number_of_zeros_to_pad);
if (str_avail) { if (str_avail) {
size_t avail = str_m - str_l; size_t avail = str_m - str_l;
@ -3762,18 +3711,16 @@ int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap, typval_T *tvs)
} }
if (str_m > 0) { if (str_m > 0) {
/* make sure the string is nul-terminated even at the expense of // make sure the string is nul-terminated even at the expense of
* overwriting the last character (shouldn't happen, but just in case) // overwriting the last character (shouldn't happen, but just in case)
* */
str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0'; str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
} }
if (tvs != NULL && tvs[arg_idx - 1].v_type != VAR_UNKNOWN) if (tvs && tvs[arg_idx - 1].v_type != VAR_UNKNOWN)
EMSG(_("E767: Too many arguments to printf()")); EMSG(_("E767: Too many arguments to printf()"));
/* Return the number of characters formatted (excluding trailing nul // return the number of characters formatted (excluding trailing nul
* character), that is, the number of characters that would have been // character); that is, the number of characters that would have been
* written to the buffer if it were large enough. */ // written to the buffer if it were large enough.
return (int)str_l; return (int)str_l;
} }