diff options
Diffstat (limited to 'src/nvim/strings.c')
-rw-r--r-- | src/nvim/strings.c | 916 |
1 files changed, 467 insertions, 449 deletions
diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 79a3db4843..11c1aa1760 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1,28 +1,26 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include <assert.h> #include <inttypes.h> +#include <math.h> #include <stdarg.h> #include <stdbool.h> #include <string.h> -#include <math.h> -#include <assert.h> -#include "nvim/assert.h" -#include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/strings.h" -#include "nvim/file_search.h" +#include "nvim/assert.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/encode.h" #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" +#include "nvim/file_search.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/fold.h" #include "nvim/func_attr.h" #include "nvim/getchar.h" @@ -35,8 +33,10 @@ #include "nvim/message.h" #include "nvim/misc1.h" #include "nvim/move.h" -#include "nvim/option.h" #include "nvim/ops.h" +#include "nvim/option.h" +#include "nvim/os/os.h" +#include "nvim/os/shell.h" #include "nvim/os_unix.h" #include "nvim/path.h" #include "nvim/quickfix.h" @@ -44,12 +44,11 @@ #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/spell.h" +#include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/os/os.h" -#include "nvim/os/shell.h" -#include "nvim/eval/encode.h" /// Copy "string" into newly allocated memory. char_u *vim_strsave(const char_u *string) @@ -84,8 +83,7 @@ char_u *vim_strsave_escaped(const char_u *string, const char_u *esc_chars) * characters where rem_backslash() would remove the backslash. * Escape the characters with "cc". */ -char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, - char_u cc, bool bsl) +char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, char_u cc, bool bsl) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { /* @@ -100,9 +98,10 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, p += l - 1; continue; } - if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) - ++length; /* count a backslash */ - ++length; /* count an ordinary char */ + if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) { + ++length; // count a backslash + } + ++length; // count an ordinary char } char_u *escaped_string = xmalloc(length); @@ -112,11 +111,12 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, if (l > 1) { memcpy(p2, p, l); p2 += l; - p += l - 1; /* skip multibyte char */ + p += l - 1; // skip multibyte char continue; } - if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) + if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) { *p2++ = cc; + } *p2++ = *p; } *p2 = NUL; @@ -182,12 +182,11 @@ char *vim_strnsave_unquoted(const char *const string, const size_t length) * When "do_newline" is false do not escape newline unless it is csh shell. * Returns the result in allocated memory. */ -char_u *vim_strsave_shellescape(const char_u *string, - bool do_special, bool do_newline) +char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_newline) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *d; - char_u *escaped_string; + char_u *d; + char_u *escaped_string; size_t l; int csh_like; bool fish_like; @@ -202,7 +201,7 @@ char_u *vim_strsave_shellescape(const char_u *string, // itself must be escaped to get a literal '\'. fish_like = fish_like_shell(); - /* First count the number of extra bytes required. */ + // First count the number of extra bytes required. size_t length = STRLEN(string) + 3; // two quotes and a trailing NUL for (const char_u *p = string; *p != NUL; MB_PTR_ADV(p)) { #ifdef WIN32 @@ -217,12 +216,13 @@ char_u *vim_strsave_shellescape(const char_u *string, } if ((*p == '\n' && (csh_like || do_newline)) || (*p == '!' && (csh_like || do_special))) { - ++length; /* insert backslash */ - if (csh_like && do_special) - ++length; /* insert backslash */ + ++length; // insert backslash + if (csh_like && do_special) { + ++length; // insert backslash + } } if (do_special && find_cmdline_var(p, &l) >= 0) { - ++length; /* insert backslash */ + ++length; // insert backslash p += l - 1; } if (*p == '\\' && fish_like) { @@ -230,7 +230,7 @@ char_u *vim_strsave_shellescape(const char_u *string, } } - /* Allocate memory for the result and fill it. */ + // Allocate memory for the result and fill it. escaped_string = xmalloc(length); d = escaped_string; @@ -264,15 +264,17 @@ char_u *vim_strsave_shellescape(const char_u *string, if ((*p == '\n' && (csh_like || do_newline)) || (*p == '!' && (csh_like || do_special))) { *d++ = '\\'; - if (csh_like && do_special) + if (csh_like && do_special) { *d++ = '\\'; + } *d++ = *p++; continue; } if (do_special && find_cmdline_var(p, &l) >= 0) { - *d++ = '\\'; /* insert backslash */ - while (--l != SIZE_MAX) /* copy the var */ + *d++ = '\\'; // insert backslash + while (--l != SIZE_MAX) { // copy the var *d++ = *p++; + } continue; } if (*p == '\\' && fish_like) { @@ -285,11 +287,11 @@ char_u *vim_strsave_shellescape(const char_u *string, } // add terminating quote and finish with a NUL -# ifdef WIN32 +#ifdef WIN32 if (!p_ssl) { *d++ = '"'; } else -# endif +#endif *d++ = '\''; *d = NUL; @@ -384,11 +386,12 @@ char *strcase_save(const char *const orig, bool upper) void del_trailing_spaces(char_u *ptr) FUNC_ATTR_NONNULL_ALL { - char_u *q; + char_u *q; q = ptr + STRLEN(ptr); - while (--q > ptr && ascii_iswhite(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V) + while (--q > ptr && ascii_iswhite(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V) { *q = NUL; + } } #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) @@ -404,14 +407,16 @@ int vim_stricmp(const char *s1, const char *s2) for (;; ) { i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2); - if (i != 0) - return i; /* this character different */ - if (*s1 == NUL) - break; /* strings match until NUL */ + if (i != 0) { + return i; // this character different + } + if (*s1 == NUL) { + break; // strings match until NUL + } ++s1; ++s2; } - return 0; /* strings match */ + return 0; // strings match } #endif @@ -428,15 +433,17 @@ int vim_strnicmp(const char *s1, const char *s2, size_t len) while (len > 0) { i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2); - if (i != 0) - return i; /* this character different */ - if (*s1 == NUL) - break; /* strings match until NUL */ + if (i != 0) { + return i; // this character different + } + if (*s1 == NUL) { + break; // strings match until NUL + } ++s1; ++s2; --len; } - return 0; /* strings match */ + return 0; // strings match } #endif @@ -490,10 +497,13 @@ bool has_non_ascii(const char_u *s) { const char_u *p; - if (s != NULL) - for (p = s; *p != NUL; ++p) - if (*p >= 128) + if (s != NULL) { + for (p = s; *p != NUL; ++p) { + if (*p >= 128) { return true; + } + } + } return false; } @@ -504,7 +514,7 @@ bool has_non_ascii_len(const char *const s, const size_t len) { if (s != NULL) { for (size_t i = 0; i < len; i++) { - if ((uint8_t) s[i] >= 128) { + if ((uint8_t)s[i] >= 128) { return true; } } @@ -527,7 +537,7 @@ char_u *concat_str(const char_u *restrict str1, const char_u *restrict str2) static const char *const e_printf = - N_("E766: Insufficient arguments for printf()"); + N_("E766: Insufficient arguments for printf()"); /// Get number argument from idxp entry in tvs /// @@ -606,15 +616,14 @@ static const void *tv_ptr(const typval_T *const tvs, int *const idxp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { #define OFF(attr) offsetof(union typval_vval_union, attr) - STATIC_ASSERT( - OFF(v_string) == OFF(v_list) - && OFF(v_string) == OFF(v_dict) - && OFF(v_string) == OFF(v_partial) - && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_list) - && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_dict) - && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_partial), - "Strings, dictionaries, lists and partials are expected to be pointers, " - "so that all three of them can be accessed via v_string"); + STATIC_ASSERT(OFF(v_string) == OFF(v_list) + && OFF(v_string) == OFF(v_dict) + && OFF(v_string) == OFF(v_partial) + && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_list) + && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_dict) + && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_partial), + "Strings, dictionaries, lists and partials are expected to be pointers, " + "so that all three of them can be accessed via v_string"); #undef OFF const int idx = *idxp - 1; if (tvs[idx].v_type == VAR_UNKNOWN) { @@ -735,8 +744,8 @@ int vim_snprintf(char *str, size_t str_m, const char *fmt, ...) // Return the representation of infinity for printf() function: // "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF". -static const char *infinity_str(bool positive, char fmt_spec, - int force_sign, int space_for_positive) +static const char *infinity_str(bool positive, char fmt_spec, int force_sign, + int space_for_positive) { static const char *table[] = { "-inf", "inf", "+inf", " inf", @@ -765,8 +774,7 @@ 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, typval_T *const tvs) { size_t str_l = 0; bool str_avail = str_l < str_m; @@ -800,7 +808,7 @@ int vim_vsnprintf_typval( char length_modifier = '\0'; // temporary buffer for simple numeric->string conversion -# define TMP_LEN 350 // 1e308 seems reasonable as the maximum printable +#define TMP_LEN 350 // 1e308 seems reasonable as the maximum printable char tmp[TMP_LEN]; // string address in case of string argument @@ -832,15 +840,22 @@ int vim_vsnprintf_typval( // parse flags while (true) { switch (*p) { - case '0': zero_padding = 1; p++; continue; - case '-': justify_left = 1; p++; continue; - // if both '0' and '-' flags appear, '0' should be ignored - case '+': force_sign = 1; space_for_positive = 0; p++; continue; - case ' ': force_sign = 1; p++; continue; - // if both ' ' and '+' flags appear, ' ' should be ignored - case '#': alternate_form = 1; p++; continue; - case '\'': p++; continue; - default: break; + case '0': + zero_padding = 1; p++; continue; + case '-': + justify_left = 1; p++; continue; + // if both '0' and '-' flags appear, '0' should be ignored + case '+': + force_sign = 1; space_for_positive = 0; p++; continue; + case ' ': + force_sign = 1; p++; continue; + // if both ' ' and '+' flags appear, ' ' should be ignored + case '#': + alternate_form = 1; p++; continue; + case '\'': + p++; continue; + default: + break; } break; } @@ -905,444 +920,447 @@ int vim_vsnprintf_typval( // common synonyms switch (fmt_spec) { - case 'i': fmt_spec = 'd'; 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; + case 'i': + fmt_spec = 'd'; 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; } switch (fmt_spec) { - case 'b': case 'B': - case 'd': case 'u': case 'o': case 'x': case 'X': - if (tvs && length_modifier == '\0') { - length_modifier = '2'; - } + case 'b': + case 'B': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + if (tvs && length_modifier == '\0') { + length_modifier = '2'; + } } // get parameter value, do initial processing switch (fmt_spec) { - // '%' and 'c' behave similar to 's' regarding flags and field widths - case '%': case 'c': case 's': case 'S': - str_arg_l = 1; - switch (fmt_spec) { - case '%': - str_arg = p; - break; - - case 'c': { - const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int); - // standard demands unsigned char - uchar_arg = (unsigned char)j; - str_arg = (char *)&uchar_arg; - break; - } + // '%' and 'c' behave similar to 's' regarding flags and field widths + case '%': + case 'c': + case 's': + case 'S': + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; + break; - case 's': - case 'S': - str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree) - : va_arg(ap, const char *); - if (!str_arg) { - str_arg = "[NULL]"; - str_arg_l = 6; - } else if (!precision_specified) { - // make sure not to address string beyond the specified - // precision - str_arg_l = strlen(str_arg); - } else if (precision == 0) { - // truncate string if necessary as requested by precision - str_arg_l = 0; - } else { - // memchr on HP does not like n > 2^31 - // TODO(elmart): check if this still holds / is relevant - str_arg_l = (size_t)((char *)xmemscan(str_arg, - NUL, - MIN(precision, - 0x7fffffff)) - - str_arg); - } - if (fmt_spec == 'S') { - if (min_field_width != 0) { - min_field_width += (strlen(str_arg) - - mb_string2cells((char_u *)str_arg)); - } - if (precision) { - char_u *p1; - size_t i = 0; - - for (p1 = (char_u *)str_arg; *p1; - p1 += mb_ptr2len(p1)) { - i += (size_t)utf_ptr2cells(p1); - if (i > precision) { - break; - } - } - str_arg_l = (size_t)(p1 - (char_u *)str_arg); + case 'c': { + const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int); + // standard demands unsigned char + uchar_arg = (unsigned char)j; + str_arg = (char *)&uchar_arg; + break; + } + + case 's': + case 'S': + str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree) + : va_arg(ap, const char *); + if (!str_arg) { + str_arg = "[NULL]"; + str_arg_l = 6; + } else if (!precision_specified) { + // make sure not to address string beyond the specified + // precision + str_arg_l = strlen(str_arg); + } else if (precision == 0) { + // truncate string if necessary as requested by precision + str_arg_l = 0; + } else { + // memchr on HP does not like n > 2^31 + // TODO(elmart): check if this still holds / is relevant + str_arg_l = (size_t)((char *)xmemscan(str_arg, + NUL, + MIN(precision, + 0x7fffffff)) + - str_arg); + } + if (fmt_spec == 'S') { + if (min_field_width != 0) { + min_field_width += (strlen(str_arg) + - mb_string2cells((char_u *)str_arg)); + } + if (precision) { + char_u *p1; + size_t i = 0; + + for (p1 = (char_u *)str_arg; *p1; + p1 += mb_ptr2len(p1)) { + i += (size_t)utf_ptr2cells(p1); + if (i > precision) { + break; } } - break; - - default: - break; + str_arg_l = (size_t)(p1 - (char_u *)str_arg); + } } break; - case 'd': - case 'u': - case 'b': case 'B': - case 'o': - case 'x': case 'X': - case 'p': { - // u, b, 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 non NULL for 'p'), - // -1 if negative (unsigned argument is never negative) - int arg_sign = 0; - - intmax_t arg = 0; - uintmax_t uarg = 0; - - // only defined for p conversion - const void *ptr_arg = NULL; - - if (fmt_spec == 'p') { - ptr_arg = tvs ? tv_ptr(tvs, &arg_idx) : va_arg(ap, void *); - if (ptr_arg) { - arg_sign = 1; - } - } else if (fmt_spec == 'd') { - // signed - switch (length_modifier) { - case '\0': { - arg = (int)(tvs ? tv_nr(tvs, &arg_idx) : 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)); - break; - } - case 'l': { - arg = (tvs ? (long)tv_nr(tvs, &arg_idx) : va_arg(ap, long)); - break; - } - case '2': { - arg = ( - tvs + default: + break; + } + break; + + case 'd': + case 'u': + case 'b': + case 'B': + case 'o': + case 'x': + case 'X': + case 'p': { + // u, b, 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 non NULL for 'p'), + // -1 if negative (unsigned argument is never negative) + int arg_sign = 0; + + intmax_t arg = 0; + uintmax_t uarg = 0; + + // only defined for p conversion + const void *ptr_arg = NULL; + + if (fmt_spec == 'p') { + ptr_arg = tvs ? tv_ptr(tvs, &arg_idx) : va_arg(ap, void *); + if (ptr_arg) { + arg_sign = 1; + } + } else if (fmt_spec == 'd') { + // signed + switch (length_modifier) { + case '\0': + arg = (int)(tvs ? tv_nr(tvs, &arg_idx) : 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)); + break; + case 'l': + arg = (tvs ? (long)tv_nr(tvs, &arg_idx) : 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) - break; - } - case 'z': { - arg = (tvs + break; + case 'z': + arg = (tvs ? (ptrdiff_t)tv_nr(tvs, &arg_idx) : va_arg(ap, ptrdiff_t)); - break; - } - } - if (arg > 0) { - arg_sign = 1; - } else if (arg < 0) { - arg_sign = -1; - } - } else { - // unsigned - switch (length_modifier) { - case '\0': { - uarg = (unsigned int)(tvs + break; + } + if (arg > 0) { + arg_sign = 1; + } else if (arg < 0) { + arg_sign = -1; + } + } else { + // unsigned + switch (length_modifier) { + case '\0': + uarg = (unsigned int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int)); - break; - } - case 'h': { - uarg = (uint16_t)(tvs + break; + case 'h': + uarg = (uint16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int)); - break; - } - case 'l': { - uarg = (tvs + break; + case 'l': + uarg = (tvs ? (unsigned long)tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned long)); - break; - } - case '2': { - uarg = (uintmax_t)(unsigned long long)( // NOLINT (runtime/int) - tvs + 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) - break; - } - case 'z': { - uarg = (tvs + break; + case 'z': + uarg = (tvs ? (size_t)tv_nr(tvs, &arg_idx) : va_arg(ap, size_t)); - break; - } - } - arg_sign = (uarg != 0); + break; } + arg_sign = (uarg != 0); + } - str_arg = tmp; - str_arg_l = 0; + str_arg = tmp; + str_arg_l = 0; - // For d, i, u, o, x, and X conversions, if precision is specified, - // '0' flag should be ignored. This is so with Solaris 2.6, Digital - // UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. - if (precision_specified) { - zero_padding = 0; - } + // For d, i, u, o, x, and X conversions, if precision is specified, + // '0' flag should be ignored. This is so with Solaris 2.6, Digital + // UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. + if (precision_specified) { + zero_padding = 0; + } - if (fmt_spec == 'd') { - if (force_sign && arg_sign >= 0) { - tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; - } - // leave negative numbers for snprintf to handle, to - // avoid handling tricky cases like (short int)-32768 - } else if (alternate_form) { - if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' - || fmt_spec == 'b' || fmt_spec == 'B')) { - tmp[str_arg_l++] = '0'; - tmp[str_arg_l++] = fmt_spec; - } - // alternate form should have no effect for p * conversion, but ... + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) { + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; } - - zero_padding_insertion_ind = str_arg_l; - if (!precision_specified) { - precision = 1; // default precision is 1 + // leave negative numbers for snprintf to handle, to + // avoid handling tricky cases like (short int)-32768 + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' + || fmt_spec == 'b' || fmt_spec == 'B')) { + tmp[str_arg_l++] = '0'; + tmp[str_arg_l++] = fmt_spec; } - if (precision == 0 && arg_sign == 0) { - // when zero value is formatted with an explicit precision 0, - // resulting formatted string is empty (d, i, u, b, B, o, x, X, p) - } else { - switch (fmt_spec) { - case 'p': { // pointer - str_arg_l += (size_t)snprintf(tmp + str_arg_l, - sizeof(tmp) - str_arg_l, - "%p", ptr_arg); - break; - } - case 'd': { // signed - str_arg_l += (size_t)snprintf(tmp + str_arg_l, - sizeof(tmp) - str_arg_l, - "%" PRIdMAX, arg); - break; - } - case 'b': case 'B': { // binary - size_t bits = 0; - for (bits = sizeof(uintmax_t) * 8; bits > 0; bits--) { - if ((uarg >> (bits - 1)) & 0x1) { - break; - } - } + // alternate form should have no effect for p * conversion, but ... + } - while (bits > 0) { - tmp[str_arg_l++] = ((uarg >> --bits) & 0x1) ? '1' : '0'; - } - break; - } - default: { // unsigned - // construct a simple format string for snprintf - char f[] = "%" PRIuMAX; - f[sizeof("%" PRIuMAX) - 1 - 1] = fmt_spec; - assert(PRIuMAX[sizeof(PRIuMAX) - 1 - 1] == 'u'); - str_arg_l += (size_t)snprintf(tmp + str_arg_l, - sizeof(tmp) - str_arg_l, - f, uarg); + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) { + precision = 1; // default precision is 1 + } + if (precision == 0 && arg_sign == 0) { + // when zero value is formatted with an explicit precision 0, + // resulting formatted string is empty (d, i, u, b, B, o, x, X, p) + } else { + switch (fmt_spec) { + case 'p': // pointer + str_arg_l += (size_t)snprintf(tmp + str_arg_l, + sizeof(tmp) - str_arg_l, + "%p", ptr_arg); + break; + case 'd': // signed + str_arg_l += (size_t)snprintf(tmp + str_arg_l, + sizeof(tmp) - str_arg_l, + "%" PRIdMAX, arg); + break; + case 'b': + case 'B': { // binary + size_t bits = 0; + for (bits = sizeof(uintmax_t) * 8; bits > 0; bits--) { + if ((uarg >> (bits - 1)) & 0x1) { break; } } - assert(str_arg_l < sizeof(tmp)); - // include the optional minus sign and possible "0x" in the region - // before the zero padding insertion point - if (zero_padding_insertion_ind < str_arg_l - && tmp[zero_padding_insertion_ind] == '-') { - zero_padding_insertion_ind++; - } - if (zero_padding_insertion_ind + 1 < str_arg_l - && tmp[zero_padding_insertion_ind] == '0' - && (tmp[zero_padding_insertion_ind + 1] == 'x' - || tmp[zero_padding_insertion_ind + 1] == 'X' - || tmp[zero_padding_insertion_ind + 1] == 'b' - || tmp[zero_padding_insertion_ind + 1] == 'B')) { - zero_padding_insertion_ind += 2; + while (bits > 0) { + tmp[str_arg_l++] = ((uarg >> --bits) & 0x1) ? '1' : '0'; } + break; + } + default: { // unsigned + // construct a simple format string for snprintf + char f[] = "%" PRIuMAX; + f[sizeof("%" PRIuMAX) - 1 - 1] = fmt_spec; + assert(PRIuMAX[sizeof(PRIuMAX) - 1 - 1] == 'u'); + str_arg_l += (size_t)snprintf(tmp + str_arg_l, + sizeof(tmp) - str_arg_l, + f, uarg); + break; + } } + assert(str_arg_l < sizeof(tmp)); - { - size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; - - if (alternate_form && fmt_spec == 'o' - // unless zero is already the first character - && !(zero_padding_insertion_ind < str_arg_l - && tmp[zero_padding_insertion_ind] == '0')) { - // assure leading zero for alternate-form octal numbers - if (!precision_specified - || precision < num_of_digits + 1) { - // precision is increased to force the first character to be - // zero, except if a zero value is formatted with an explicit - // precision of zero - precision = num_of_digits + 1; - } - } - // zero padding to specified precision? - if (num_of_digits < precision) { - number_of_zeros_to_pad = precision - num_of_digits; - } + // include the optional minus sign and possible "0x" in the region + // before the zero padding insertion point + if (zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; } - // zero padding to specified minimal field width? - if (!justify_left && zero_padding) { - const int n = (int)(min_field_width - (str_arg_l - + number_of_zeros_to_pad)); - if (n > 0) { - number_of_zeros_to_pad += (size_t)n; - } + if (zero_padding_insertion_ind + 1 < str_arg_l + && tmp[zero_padding_insertion_ind] == '0' + && (tmp[zero_padding_insertion_ind + 1] == 'x' + || tmp[zero_padding_insertion_ind + 1] == 'X' + || tmp[zero_padding_insertion_ind + 1] == 'b' + || tmp[zero_padding_insertion_ind + 1] == 'B')) { + zero_padding_insertion_ind += 2; } - break; } - case 'f': - case 'F': - case 'e': - case 'E': - case 'g': - case 'G': - { - // floating point - char format[40]; - int remove_trailing_zeroes = false; - - double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double); - double abs_f = f < 0 ? -f : f; - - if (fmt_spec == 'g' || fmt_spec == 'G') { - // can't use %g directly, cause it prints "1.0" as "1" - if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) { - fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f'; - } else { - fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; - } - remove_trailing_zeroes = true; + { + size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; + + if (alternate_form && fmt_spec == 'o' + // unless zero is already the first character + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0')) { + // assure leading zero for alternate-form octal numbers + if (!precision_specified + || precision < num_of_digits + 1) { + // precision is increased to force the first character to be + // zero, except if a zero value is formatted with an explicit + // precision of zero + precision = num_of_digits + 1; } + } + // zero padding to specified precision? + if (num_of_digits < precision) { + number_of_zeros_to_pad = precision - num_of_digits; + } + } + // zero padding to specified minimal field width? + if (!justify_left && zero_padding) { + const int n = (int)(min_field_width - (str_arg_l + + number_of_zeros_to_pad)); + if (n > 0) { + number_of_zeros_to_pad += (size_t)n; + } + } + break; + } - if (xisinf(f) - || (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) { - xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec, - force_sign, space_for_positive), - sizeof(tmp)); - str_arg_l = strlen(tmp); - zero_padding = 0; - } else if (xisnan(f)) { - // Not a number: nan or NAN - memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4); - str_arg_l = 3; - zero_padding = 0; - } else { - // Regular float number - format[0] = '%'; - size_t l = 1; - if (force_sign) { - format[l++] = space_for_positive ? ' ' : '+'; - } - if (precision_specified) { - size_t max_prec = TMP_LEN - 10; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': { + // floating point + char format[40]; + int remove_trailing_zeroes = false; + + double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double); + double abs_f = f < 0 ? -f : f; + + if (fmt_spec == 'g' || fmt_spec == 'G') { + // can't use %g directly, cause it prints "1.0" as "1" + if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) { + fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f'; + } else { + fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; + } + remove_trailing_zeroes = true; + } - // make sure we don't get more digits than we have room for - if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { - max_prec -= (size_t)log10(abs_f); - } - if (precision > max_prec) { - precision = max_prec; - } - l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d", - (int)precision); - } + if (xisinf(f) + || (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) { + xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec, + force_sign, space_for_positive), + sizeof(tmp)); + str_arg_l = strlen(tmp); + zero_padding = 0; + } else if (xisnan(f)) { + // Not a number: nan or NAN + memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4); + str_arg_l = 3; + zero_padding = 0; + } else { + // Regular float number + format[0] = '%'; + size_t l = 1; + if (force_sign) { + format[l++] = space_for_positive ? ' ' : '+'; + } + if (precision_specified) { + size_t max_prec = TMP_LEN - 10; - // Cast to char to avoid a conversion warning on Ubuntu 12.04. - assert(l + 1 < sizeof(format)); - format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); - format[l + 1] = NUL; + // make sure we don't get more digits than we have room for + if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { + max_prec -= (size_t)log10(abs_f); + } + if (precision > max_prec) { + precision = max_prec; + } + l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d", + (int)precision); + } - str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); - assert(str_arg_l < sizeof(tmp)); + // Cast to char to avoid a conversion warning on Ubuntu 12.04. + assert(l + 1 < sizeof(format)); + format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); + format[l + 1] = NUL; - if (remove_trailing_zeroes) { - int i; - char *tp; + str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); + assert(str_arg_l < sizeof(tmp)); - // using %g or %G: remove superfluous zeroes - if (fmt_spec == 'f' || fmt_spec == 'F') { - tp = tmp + str_arg_l - 1; - } else { - tp = (char *)vim_strchr((char_u *)tmp, - fmt_spec == 'e' ? 'e' : 'E'); - if (tp) { - // remove superfluous '+' and leading zeroes from exponent - if (tp[1] == '+') { - // change "1.0e+07" to "1.0e07" - STRMOVE(tp + 1, tp + 2); - str_arg_l--; - } - i = (tp[1] == '-') ? 2 : 1; - while (tp[i] == '0') { - // change "1.0e07" to "1.0e7" - STRMOVE(tp + i, tp + i + 1); - str_arg_l--; - } - tp--; - } - } + if (remove_trailing_zeroes) { + int i; + char *tp; - if (tp != NULL && !precision_specified) { - // remove trailing zeroes, but keep the one just after a dot - while (tp > tmp + 2 && *tp == '0' && tp[-1] != '.') { - STRMOVE(tp, tp + 1); - tp--; - str_arg_l--; - } + // using %g or %G: remove superfluous zeroes + if (fmt_spec == 'f' || fmt_spec == 'F') { + tp = tmp + str_arg_l - 1; + } else { + tp = (char *)vim_strchr((char_u *)tmp, + fmt_spec == 'e' ? 'e' : 'E'); + if (tp) { + // remove superfluous '+' and leading zeroes from exponent + if (tp[1] == '+') { + // change "1.0e+07" to "1.0e07" + STRMOVE(tp + 1, tp + 2); + str_arg_l--; } - } else { - // Be consistent: some printf("%e") use 1.0e+12 and some - // 1.0e+012; remove one zero in the last case. - char *tp = (char *)vim_strchr((char_u *)tmp, - fmt_spec == 'e' ? 'e' : 'E'); - if (tp && (tp[1] == '+' || tp[1] == '-') && tp[2] == '0' - && ascii_isdigit(tp[3]) && ascii_isdigit(tp[4])) { - STRMOVE(tp + 2, tp + 3); + i = (tp[1] == '-') ? 2 : 1; + while (tp[i] == '0') { + // change "1.0e07" to "1.0e7" + STRMOVE(tp + i, tp + i + 1); str_arg_l--; } + tp--; } } - if (zero_padding && min_field_width > str_arg_l - && (tmp[0] == '-' || force_sign)) { - // Padding 0's should be inserted after the sign. - number_of_zeros_to_pad = min_field_width - str_arg_l; - zero_padding_insertion_ind = 1; + + if (tp != NULL && !precision_specified) { + // remove trailing zeroes, but keep the one just after a dot + while (tp > tmp + 2 && *tp == '0' && tp[-1] != '.') { + STRMOVE(tp, tp + 1); + tp--; + str_arg_l--; + } + } + } else { + // Be consistent: some printf("%e") use 1.0e+12 and some + // 1.0e+012; remove one zero in the last case. + char *tp = (char *)vim_strchr((char_u *)tmp, + fmt_spec == 'e' ? 'e' : 'E'); + if (tp && (tp[1] == '+' || tp[1] == '-') && tp[2] == '0' + && ascii_isdigit(tp[3]) && ascii_isdigit(tp[4])) { + STRMOVE(tp + 2, tp + 3); + str_arg_l--; } - str_arg = tmp; - break; } + } + if (zero_padding && min_field_width > str_arg_l + && (tmp[0] == '-' || force_sign)) { + // Padding 0's should be inserted after the sign. + number_of_zeros_to_pad = min_field_width - str_arg_l; + zero_padding_insertion_ind = 1; + } + str_arg = tmp; + break; + } - default: - // unrecognized conversion specifier, keep format string as-is - zero_padding = 0; // turn zero padding off for non-numeric conversion - justify_left = 1; - min_field_width = 0; // reset flags - - // discard the unrecognized conversion, just keep - // the unrecognized conversion character - str_arg = p; - str_arg_l = 0; - if (*p) { - str_arg_l++; // include invalid conversion specifier - } - // unchanged if not at end-of-string - break; + default: + // unrecognized conversion specifier, keep format string as-is + zero_padding = 0; // turn zero padding off for non-numeric conversion + justify_left = 1; + min_field_width = 0; // reset flags + + // discard the unrecognized conversion, just keep + // the unrecognized conversion character + str_arg = p; + str_arg_l = 0; + if (*p) { + str_arg_l++; // include invalid conversion specifier + } + // unchanged if not at end-of-string + break; } if (*p) { |