From 04933b1ea968f958d2541dd65fd33ebb503caac3 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:08:16 +0200 Subject: refactor: remove redundant casts --- src/nvim/strings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 34b3c38103..4ef60f4ab8 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1468,7 +1468,7 @@ char *reverse_text(char *s) /// @return [allocated] Copy of the string. char *strrep(const char *src, const char *what, const char *rep) { - char *pos = (char *)src; + const char *pos = src; size_t whatlen = strlen(what); // Count occurrences -- cgit From 2d78e656b715119ca11d131a1a932f22f1b4ad36 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:43:00 +0200 Subject: refactor: remove redundant casts --- src/nvim/strings.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 4ef60f4ab8..14aa4ddc1a 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -946,10 +946,10 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t - str_arg); } if (fmt_spec == 'S') { - char *p1; + const char *p1; size_t i; - for (i = 0, p1 = (char *)str_arg; *p1; p1 += utfc_ptr2len(p1)) { + for (i = 0, p1 = str_arg; *p1; p1 += utfc_ptr2len(p1)) { size_t cell = (size_t)utf_ptr2cells(p1); if (precision_specified && i + cell > precision) { break; -- cgit From 907018e547c9b989781667d2cf951e1abf99ab9d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Apr 2023 00:23:11 +0800 Subject: vim-patch:8.2.3139: functions for string manipulation are spread out (#23316) Problem: Functions for string manipulation are spread out. Solution: Move string related functions to a new source file. (Yegappan Lakshmanan, closes vim/vim#8470) https://github.com/vim/vim/commit/a2438132a675be4dde3acbdf03ba1fdb2f09427c Co-authored-by: Yegappan Lakshmanan --- src/nvim/strings.c | 593 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 593 insertions(+) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 14aa4ddc1a..d5d7d62c38 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -19,6 +19,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_docmd.h" +#include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/macros.h" #include "nvim/math.h" @@ -26,6 +27,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/plines.h" #include "nvim/strings.h" #include "nvim/types.h" #include "nvim/vim.h" @@ -1499,3 +1501,594 @@ char *strrep(const char *src, const char *what, const char *rep) return ret; } + +static void byteidx(typval_T *argvars, typval_T *rettv, int comp) +{ + const char *const str = tv_get_string_chk(&argvars[0]); + varnumber_T idx = tv_get_number_chk(&argvars[1], NULL); + rettv->vval.v_number = -1; + if (str == NULL || idx < 0) { + return; + } + + const char *t = str; + for (; idx > 0; idx--) { + if (*t == NUL) { // EOL reached. + return; + } + if (comp) { + t += utf_ptr2len(t); + } else { + t += utfc_ptr2len(t); + } + } + rettv->vval.v_number = (varnumber_T)(t - str); +} + +/// "byteidx()" function +void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + byteidx(argvars, rettv, false); +} + +/// "byteidxcomp()" function +void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + byteidx(argvars, rettv, true); +} + +/// "charidx()" function +void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + + if ((tv_check_for_string_arg(argvars, 0) == FAIL + || tv_check_for_number_arg(argvars, 1) == FAIL + || tv_check_for_opt_bool_arg(argvars, 2) == FAIL)) { + return; + } + + const char *str = tv_get_string_chk(&argvars[0]); + varnumber_T idx = tv_get_number_chk(&argvars[1], NULL); + if (str == NULL || idx < 0) { + return; + } + int countcc = 0; + if (argvars[2].v_type != VAR_UNKNOWN) { + countcc = (int)tv_get_number(&argvars[2]); + } + if (countcc < 0 || countcc > 1) { + semsg(_(e_using_number_as_bool_nr), countcc); + return; + } + + int (*ptr2len)(const char *); + if (countcc) { + ptr2len = utf_ptr2len; + } else { + ptr2len = utfc_ptr2len; + } + + const char *p; + int len; + for (p = str, len = 0; p <= str + idx; len++) { + if (*p == NUL) { + return; + } + p += ptr2len(p); + } + + rettv->vval.v_number = len > 0 ? len - 1 : 0; +} + +/// "str2list()" function +void f_str2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_list_alloc_ret(rettv, kListLenUnknown); + const char *p = tv_get_string(&argvars[0]); + + for (; *p != NUL; p += utf_ptr2len(p)) { + tv_list_append_number(rettv->vval.v_list, utf_ptr2char(p)); + } +} + +/// "str2nr()" function +void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + int base = 10; + int what = 0; + + if (argvars[1].v_type != VAR_UNKNOWN) { + base = (int)tv_get_number(&argvars[1]); + if (base != 2 && base != 8 && base != 10 && base != 16) { + emsg(_(e_invarg)); + return; + } + if (argvars[2].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[2])) { + what |= STR2NR_QUOTE; + } + } + + char *p = skipwhite(tv_get_string(&argvars[0])); + bool isneg = (*p == '-'); + if (*p == '+' || *p == '-') { + p = skipwhite(p + 1); + } + switch (base) { + case 2: + what |= STR2NR_BIN | STR2NR_FORCE; + break; + case 8: + what |= STR2NR_OCT | STR2NR_OOCT | STR2NR_FORCE; + break; + case 16: + what |= STR2NR_HEX | STR2NR_FORCE; + break; + } + varnumber_T n; + vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false, NULL); + // Text after the number is silently ignored. + if (isneg) { + rettv->vval.v_number = -n; + } else { + rettv->vval.v_number = n; + } +} + +/// "strgetchar()" function +void f_strgetchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + + const char *const str = tv_get_string_chk(&argvars[0]); + if (str == NULL) { + return; + } + bool error = false; + varnumber_T charidx = tv_get_number_chk(&argvars[1], &error); + if (error) { + return; + } + + const size_t len = strlen(str); + size_t byteidx = 0; + + while (charidx >= 0 && byteidx < len) { + if (charidx == 0) { + rettv->vval.v_number = utf_ptr2char(str + byteidx); + break; + } + charidx--; + byteidx += (size_t)utf_ptr2len(str + byteidx); + } +} + +/// "stridx()" function +void f_stridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *haystack = tv_get_string_buf_chk(&argvars[0], buf); + const char *const haystack_start = haystack; + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } + + if (argvars[2].v_type != VAR_UNKNOWN) { + bool error = false; + + const ptrdiff_t start_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], + &error); + if (error || start_idx >= (ptrdiff_t)strlen(haystack)) { + return; + } + if (start_idx >= 0) { + haystack += start_idx; + } + } + + const char *pos = strstr(haystack, needle); + if (pos != NULL) { + rettv->vval.v_number = (varnumber_T)(pos - haystack_start); + } +} + +/// "string()" function +void f_string(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = encode_tv2string(&argvars[0], NULL); +} + +/// "strlen()" function +void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0])); +} + +static void strchar_common(typval_T *argvars, typval_T *rettv, bool skipcc) +{ + const char *s = tv_get_string(&argvars[0]); + varnumber_T len = 0; + int (*func_mb_ptr2char_adv)(const char **pp); + + func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; + while (*s != NUL) { + func_mb_ptr2char_adv(&s); + len++; + } + rettv->vval.v_number = len; +} + +/// "strcharlen()" function +void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + strchar_common(argvars, rettv, true); +} + +/// "strchars()" function +void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + int skipcc = false; + + if (argvars[1].v_type != VAR_UNKNOWN) { + skipcc = (int)tv_get_bool(&argvars[1]); + } + if (skipcc < 0 || skipcc > 1) { + semsg(_(e_using_number_as_bool_nr), skipcc); + } else { + strchar_common(argvars, rettv, skipcc); + } +} + +/// "strdisplaywidth()" function +void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const char *const s = tv_get_string(&argvars[0]); + int col = 0; + + if (argvars[1].v_type != VAR_UNKNOWN) { + col = (int)tv_get_number(&argvars[1]); + } + + rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char *)s) - col); +} + +/// "strwidth()" function +void f_strwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const char *const s = tv_get_string(&argvars[0]); + + rettv->vval.v_number = (varnumber_T)mb_string2cells(s); +} + +/// "strcharpart()" function +void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const char *const p = tv_get_string(&argvars[0]); + const size_t slen = strlen(p); + + int nbyte = 0; + bool error = false; + varnumber_T nchar = tv_get_number_chk(&argvars[1], &error); + if (!error) { + if (nchar > 0) { + while (nchar > 0 && (size_t)nbyte < slen) { + nbyte += utf_ptr2len(p + nbyte); + nchar--; + } + } else { + nbyte = (int)nchar; + } + } + int len = 0; + if (argvars[2].v_type != VAR_UNKNOWN) { + int charlen = (int)tv_get_number(&argvars[2]); + while (charlen > 0 && nbyte + len < (int)slen) { + int off = nbyte + len; + + if (off < 0) { + len += 1; + } else { + len += utf_ptr2len(p + off); + } + charlen--; + } + } else { + len = (int)slen - nbyte; // default: all bytes that are available. + } + + // Only return the overlap between the specified part and the actual + // string. + if (nbyte < 0) { + len += nbyte; + nbyte = 0; + } else if ((size_t)nbyte > slen) { + nbyte = (int)slen; + } + if (len < 0) { + len = 0; + } else if (nbyte + len > (int)slen) { + len = (int)slen - nbyte; + } + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = xstrndup(p + nbyte, (size_t)len); +} + +/// "strpart()" function +void f_strpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + bool error = false; + + const char *const p = tv_get_string(&argvars[0]); + const size_t slen = strlen(p); + + varnumber_T n = tv_get_number_chk(&argvars[1], &error); + varnumber_T len; + if (error) { + len = 0; + } else if (argvars[2].v_type != VAR_UNKNOWN) { + len = tv_get_number(&argvars[2]); + } else { + len = (varnumber_T)slen - n; // Default len: all bytes that are available. + } + + // Only return the overlap between the specified part and the actual + // string. + if (n < 0) { + len += n; + n = 0; + } else if (n > (varnumber_T)slen) { + n = (varnumber_T)slen; + } + if (len < 0) { + len = 0; + } else if (n + len > (varnumber_T)slen) { + len = (varnumber_T)slen - n; + } + + if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) { + int off; + + // length in characters + for (off = (int)n; off < (int)slen && len > 0; len--) { + off += utfc_ptr2len(p + off); + } + len = off - n; + } + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = xmemdupz(p + n, (size_t)len); +} + +/// "strridx()" function +void f_strridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *const haystack = tv_get_string_buf_chk(&argvars[0], buf); + + rettv->vval.v_number = -1; + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } + + const size_t haystack_len = strlen(haystack); + ptrdiff_t end_idx; + if (argvars[2].v_type != VAR_UNKNOWN) { + // Third argument: upper limit for index. + end_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], NULL); + if (end_idx < 0) { + return; // Can never find a match. + } + } else { + end_idx = (ptrdiff_t)haystack_len; + } + + const char *lastmatch = NULL; + if (*needle == NUL) { + // Empty string matches past the end. + lastmatch = haystack + end_idx; + } else { + for (const char *rest = haystack; *rest != NUL; rest++) { + rest = strstr(rest, needle); + if (rest == NULL || rest > haystack + end_idx) { + break; + } + lastmatch = rest; + } + } + + if (lastmatch != NULL) { + rettv->vval.v_number = (varnumber_T)(lastmatch - haystack); + } +} + +/// "strtrans()" function +void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = transstr(tv_get_string(&argvars[0]), true); +} + +/// "tolower(string)" function +void f_tolower(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), false); +} + +/// "toupper(string)" function +void f_toupper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), true); +} + +/// "tr(string, fromstr, tostr)" function +void f_tr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + char buf[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + + const char *in_str = tv_get_string(&argvars[0]); + const char *fromstr = tv_get_string_buf_chk(&argvars[1], buf); + const char *tostr = tv_get_string_buf_chk(&argvars[2], buf2); + + // Default return value: empty string. + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (fromstr == NULL || tostr == NULL) { + return; // Type error; errmsg already given. + } + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + + // fromstr and tostr have to contain the same number of chars. + bool first = true; + while (*in_str != NUL) { + const char *cpstr = in_str; + const int inlen = utfc_ptr2len(in_str); + int cplen = inlen; + int idx = 0; + int fromlen; + for (const char *p = fromstr; *p != NUL; p += fromlen) { + fromlen = utfc_ptr2len(p); + if (fromlen == inlen && strncmp(in_str, p, (size_t)inlen) == 0) { + int tolen; + for (p = tostr; *p != NUL; p += tolen) { + tolen = utfc_ptr2len(p); + if (idx-- == 0) { + cplen = tolen; + cpstr = p; + break; + } + } + if (*p == NUL) { // tostr is shorter than fromstr. + goto error; + } + break; + } + idx++; + } + + if (first && cpstr == in_str) { + // Check that fromstr and tostr have the same number of + // (multi-byte) characters. Done only once when a character + // of in_str doesn't appear in fromstr. + first = false; + int tolen; + for (const char *p = tostr; *p != NUL; p += tolen) { + tolen = utfc_ptr2len(p); + idx--; + } + if (idx != 0) { + goto error; + } + } + + ga_grow(&ga, cplen); + memmove((char *)ga.ga_data + ga.ga_len, cpstr, (size_t)cplen); + ga.ga_len += cplen; + + in_str += inlen; + } + + // add a terminating NUL + ga_append(&ga, NUL); + + rettv->vval.v_string = ga.ga_data; + return; +error: + semsg(_(e_invarg2), fromstr); + ga_clear(&ga); +} + +/// "trim({expr})" function +void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *head = tv_get_string_buf_chk(&argvars[0], buf1); + const char *mask = NULL; + const char *prev; + const char *p; + int dir = 0; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (head == NULL) { + return; + } + + if (tv_check_for_opt_string_arg(argvars, 1) == FAIL) { + return; + } + + if (argvars[1].v_type == VAR_STRING) { + mask = tv_get_string_buf_chk(&argvars[1], buf2); + if (argvars[2].v_type != VAR_UNKNOWN) { + bool error = false; + // leading or trailing characters to trim + dir = (int)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; + } + if (dir < 0 || dir > 2) { + semsg(_(e_invarg2), tv_get_string(&argvars[2])); + return; + } + } + } + + int c1; + if (dir == 0 || dir == 1) { + // Trim leading characters + while (*head != NUL) { + c1 = utf_ptr2char(head); + if (mask == NULL) { + if (c1 > ' ' && c1 != 0xa0) { + break; + } + } else { + for (p = mask; *p != NUL; MB_PTR_ADV(p)) { + if (c1 == utf_ptr2char(p)) { + break; + } + } + if (*p == NUL) { + break; + } + } + MB_PTR_ADV(head); + } + } + + const char *tail = head + strlen(head); + if (dir == 0 || dir == 2) { + // Trim trailing characters + for (; tail > head; tail = prev) { + prev = tail; + MB_PTR_BACK(head, prev); + c1 = utf_ptr2char(prev); + if (mask == NULL) { + if (c1 > ' ' && c1 != 0xa0) { + break; + } + } else { + for (p = mask; *p != NUL; MB_PTR_ADV(p)) { + if (c1 == utf_ptr2char(p)) { + break; + } + } + if (*p == NUL) { + break; + } + } + } + } + rettv->vval.v_string = xstrnsave(head, (size_t)(tail - head)); +} -- cgit From 191e8b40625731a652bade7000911554834afe5f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 26 Apr 2023 09:50:37 +0800 Subject: vim-patch:9.0.1485: no functions for converting from/to UTF-16 index (#23318) Problem: no functions for converting from/to UTF-16 index. Solution: Add UTF-16 flag to existing funtions and add strutf16len() and utf16idx(). (Yegappan Lakshmanan, closes vim/vim#12216) https://github.com/vim/vim/commit/67672ef097dd708244ff042a8364994da2b91e75 Co-authored-by: Yegappan Lakshmanan --- src/nvim/strings.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 134 insertions(+), 16 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index d5d7d62c38..e8c04aa5c7 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1504,22 +1504,44 @@ char *strrep(const char *src, const char *what, const char *rep) static void byteidx(typval_T *argvars, typval_T *rettv, int comp) { + rettv->vval.v_number = -1; + const char *const str = tv_get_string_chk(&argvars[0]); varnumber_T idx = tv_get_number_chk(&argvars[1], NULL); - rettv->vval.v_number = -1; if (str == NULL || idx < 0) { return; } + varnumber_T utf16idx = false; + if (argvars[2].v_type != VAR_UNKNOWN) { + utf16idx = tv_get_bool(&argvars[2]); + if (utf16idx < 0 || utf16idx > 1) { + semsg(_(e_using_number_as_bool_nr), utf16idx); + return; + } + } + + int (*ptr2len)(const char *); + if (comp) { + ptr2len = utf_ptr2len; + } else { + ptr2len = utfc_ptr2len; + } + const char *t = str; for (; idx > 0; idx--) { if (*t == NUL) { // EOL reached. return; } - if (comp) { - t += utf_ptr2len(t); - } else { - t += utfc_ptr2len(t); + if (utf16idx) { + const int clen = ptr2len(t); + const int c = (clen > 1) ? utf_ptr2char(t) : *t; + if (c > 0xFFFF) { + idx--; + } + } + if (idx > 0) { + t += ptr2len(t); } } rettv->vval.v_number = (varnumber_T)(t - str); @@ -1542,24 +1564,27 @@ void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = -1; - if ((tv_check_for_string_arg(argvars, 0) == FAIL - || tv_check_for_number_arg(argvars, 1) == FAIL - || tv_check_for_opt_bool_arg(argvars, 2) == FAIL)) { + if (tv_check_for_string_arg(argvars, 0) == FAIL + || tv_check_for_number_arg(argvars, 1) == FAIL + || tv_check_for_opt_bool_arg(argvars, 2) == FAIL + || (argvars[2].v_type != VAR_UNKNOWN + && tv_check_for_opt_bool_arg(argvars, 3) == FAIL)) { return; } - const char *str = tv_get_string_chk(&argvars[0]); + const char *const str = tv_get_string_chk(&argvars[0]); varnumber_T idx = tv_get_number_chk(&argvars[1], NULL); if (str == NULL || idx < 0) { return; } - int countcc = 0; + + varnumber_T countcc = false; + varnumber_T utf16idx = false; if (argvars[2].v_type != VAR_UNKNOWN) { - countcc = (int)tv_get_number(&argvars[2]); - } - if (countcc < 0 || countcc > 1) { - semsg(_(e_using_number_as_bool_nr), countcc); - return; + countcc = tv_get_bool(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) { + utf16idx = tv_get_bool(&argvars[3]); + } } int (*ptr2len)(const char *); @@ -1571,10 +1596,18 @@ void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const char *p; int len; - for (p = str, len = 0; p <= str + idx; len++) { + for (p = str, len = 0; utf16idx ? idx >= 0 : p <= str + idx; len++) { if (*p == NUL) { return; } + if (utf16idx) { + idx--; + const int clen = ptr2len(p); + const int c = (clen > 1) ? utf_ptr2char(p) : *p; + if (c > 0xFFFF) { + idx--; + } + } p += ptr2len(p); } @@ -1743,6 +1776,36 @@ void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } +/// "strutf16len()" function +void f_strutf16len(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + + if (tv_check_for_string_arg(argvars, 0) == FAIL + || tv_check_for_opt_bool_arg(argvars, 1) == FAIL) { + return; + } + + varnumber_T countcc = false; + if (argvars[1].v_type != VAR_UNKNOWN) { + countcc = tv_get_bool(&argvars[1]); + } + + const char *s = tv_get_string(&argvars[0]); + varnumber_T len = 0; + int (*func_mb_ptr2char_adv)(const char **pp); + + func_mb_ptr2char_adv = countcc ? mb_cptr2char_adv : mb_ptr2char_adv; + while (*s != NUL) { + const int ch = func_mb_ptr2char_adv(&s); + if (ch > 0xFFFF) { + len++; + } + len++; + } + rettv->vval.v_number = len; +} + /// "strdisplaywidth()" function void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -1914,6 +1977,61 @@ void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_string = transstr(tv_get_string(&argvars[0]), true); } +/// "utf16idx()" function +void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + + if (tv_check_for_string_arg(argvars, 0) == FAIL + || tv_check_for_opt_number_arg(argvars, 1) == FAIL + || tv_check_for_opt_bool_arg(argvars, 2) == FAIL + || (argvars[2].v_type != VAR_UNKNOWN + && tv_check_for_opt_bool_arg(argvars, 3) == FAIL)) { + return; + } + + const char *const str = tv_get_string_chk(&argvars[0]); + varnumber_T idx = tv_get_number_chk(&argvars[1], NULL); + if (str == NULL || idx < 0) { + return; + } + + varnumber_T countcc = false; + varnumber_T charidx = false; + if (argvars[2].v_type != VAR_UNKNOWN) { + countcc = tv_get_bool(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) { + charidx = tv_get_bool(&argvars[3]); + } + } + + int (*ptr2len)(const char *); + if (countcc) { + ptr2len = utf_ptr2len; + } else { + ptr2len = utfc_ptr2len; + } + + const char *p; + int len; + for (p = str, len = 0; charidx ? idx >= 0 : p <= str + idx; len++) { + if (*p == NUL) { + return; + } + const int clen = ptr2len(p); + const int c = (clen > 1) ? utf_ptr2char(p) : *p; + if (c > 0xFFFF) { + len++; + } + p += ptr2len(p); + if (charidx) { + idx--; + } + } + + rettv->vval.v_number = len > 0 ? len - 1 : 0; +} + /// "tolower(string)" function void f_tolower(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { -- cgit From 3b0df1780e2c8526bda5dead18ee7cc45925caba Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:23:44 +0200 Subject: refactor: uncrustify Notable changes: replace all infinite loops to `while(true)` and remove `int` from `unsigned int`. --- src/nvim/strings.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index e8c04aa5c7..61e00f85dc 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -373,7 +373,7 @@ int vim_stricmp(const char *s1, const char *s2) { int i; - for (;;) { + while (true) { i = (int)TOLOWER_LOC((uint8_t)(*s1)) - (int)TOLOWER_LOC((uint8_t)(*s2)); if (i != 0) { return i; // this character different @@ -831,10 +831,10 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we treat // argument like common implementations do - unsigned int uj = (unsigned)(*p++ - '0'); + unsigned uj = (unsigned)(*p++ - '0'); while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned int)(*p++ - '0'); + uj = 10 * uj + (unsigned)(*p++ - '0'); } min_field_width = uj; } @@ -855,10 +855,10 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we // treat argument like common implementations do - unsigned int uj = (unsigned)(*p++ - '0'); + unsigned uj = (unsigned)(*p++ - '0'); while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned int)(*p++ - '0'); + uj = 10 * uj + (unsigned)(*p++ - '0'); } precision = uj; } @@ -1030,10 +1030,10 @@ 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 int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int)); + uarg = (unsigned)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned)); break; case 'h': - uarg = (uint16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int)); + uarg = (uint16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned)); break; case 'l': uarg = (tvs ? (unsigned long)tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned long)); -- cgit From 3724e65c30f7d1ef803ac4e34eab7d4a9531b1a8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 17:42:34 +0800 Subject: vim-patch:8.2.2607: strcharpart() cannot include composing characters Problem: strcharpart() cannot include composing characters. Solution: Add the {skipcc} argument. https://github.com/vim/vim/commit/02b4d9b18a03549b68e364e428392b7a62766c74 Co-authored-by: Bram Moolenaar --- src/nvim/strings.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 61e00f85dc..5231ec0841 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1834,12 +1834,26 @@ void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const size_t slen = strlen(p); int nbyte = 0; + varnumber_T skipcc = false; bool error = false; varnumber_T nchar = tv_get_number_chk(&argvars[1], &error); if (!error) { + if (argvars[2].v_type != VAR_UNKNOWN + && argvars[3].v_type != VAR_UNKNOWN) { + skipcc = tv_get_bool(&argvars[3]); + if (skipcc < 0 || skipcc > 1) { + semsg(_(e_using_number_as_bool_nr), skipcc); + return; + } + } + if (nchar > 0) { while (nchar > 0 && (size_t)nbyte < slen) { - nbyte += utf_ptr2len(p + nbyte); + if (skipcc) { + nbyte += utfc_ptr2len(p + nbyte); + } else { + nbyte += utf_ptr2len(p + nbyte); + } nchar--; } } else { @@ -1855,7 +1869,11 @@ void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (off < 0) { len += 1; } else { - len += utf_ptr2len(p + off); + if (skipcc) { + len += utfc_ptr2len(p + off); + } else { + len += utf_ptr2len(p + off); + } } charlen--; } -- cgit From d745433817499c34ccf230469417fb0ea29b7ab9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 May 2023 22:40:18 +0800 Subject: vim-patch:9.0.1522: some functions give two error messages Problem: Some functions give two error messages. Solution: Do not give a second error message. (closes vim/vim#12352) https://github.com/vim/vim/commit/e4098457ab9c94225b1b0e3c5e06b82b75587971 It seems that tv_get_bool() is actually not exactly the same as tv_get_number(), so change it to a function instead. Co-authored-by: Bram Moolenaar --- src/nvim/strings.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 5231ec0841..acbff51e49 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1502,7 +1502,8 @@ char *strrep(const char *src, const char *what, const char *rep) return ret; } -static void byteidx(typval_T *argvars, typval_T *rettv, int comp) +/// Implementation of "byteidx()" and "byteidxcomp()" functions +static void byteidx_common(typval_T *argvars, typval_T *rettv, int comp) { rettv->vval.v_number = -1; @@ -1516,7 +1517,9 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp) if (argvars[2].v_type != VAR_UNKNOWN) { utf16idx = tv_get_bool(&argvars[2]); if (utf16idx < 0 || utf16idx > 1) { - semsg(_(e_using_number_as_bool_nr), utf16idx); + if (utf16idx != -1) { + semsg(_(e_using_number_as_bool_nr), utf16idx); + } return; } } @@ -1550,13 +1553,13 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp) /// "byteidx()" function void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - byteidx(argvars, rettv, false); + byteidx_common(argvars, rettv, false); } /// "byteidxcomp()" function void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - byteidx(argvars, rettv, true); + byteidx_common(argvars, rettv, true); } /// "charidx()" function @@ -1770,7 +1773,9 @@ void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) skipcc = (int)tv_get_bool(&argvars[1]); } if (skipcc < 0 || skipcc > 1) { - semsg(_(e_using_number_as_bool_nr), skipcc); + if (skipcc != -1) { + semsg(_(e_using_number_as_bool_nr), skipcc); + } } else { strchar_common(argvars, rettv, skipcc); } @@ -1842,7 +1847,9 @@ void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) && argvars[3].v_type != VAR_UNKNOWN) { skipcc = tv_get_bool(&argvars[3]); if (skipcc < 0 || skipcc > 1) { - semsg(_(e_using_number_as_bool_nr), skipcc); + if (skipcc != -1) { + semsg(_(e_using_number_as_bool_nr), skipcc); + } return; } } -- cgit From 625926f729137658d0a8a73a55aeefc5583488c3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 May 2023 22:49:53 +0800 Subject: vim-patch:9.0.1524: passing -1 for bool is not always rejected Problem: Passing -1 for bool is not always rejected. Solution: Check for error in a better way. (closes vim/vim#12358) https://github.com/vim/vim/commit/8cf51376b842e0060edf08bd2e5bd9933c552ecf --- src/nvim/strings.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index acbff51e49..4e521b14f7 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1515,11 +1515,13 @@ static void byteidx_common(typval_T *argvars, typval_T *rettv, int comp) varnumber_T utf16idx = false; if (argvars[2].v_type != VAR_UNKNOWN) { - utf16idx = tv_get_bool(&argvars[2]); + bool error = false; + utf16idx = tv_get_bool_chk(&argvars[2], &error); + if (error) { + return; + } if (utf16idx < 0 || utf16idx > 1) { - if (utf16idx != -1) { - semsg(_(e_using_number_as_bool_nr), utf16idx); - } + semsg(_(e_using_number_as_bool_nr), utf16idx); return; } } @@ -1767,18 +1769,21 @@ void f_strcharlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "strchars()" function void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - int skipcc = false; + varnumber_T skipcc = false; if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = (int)tv_get_bool(&argvars[1]); - } - if (skipcc < 0 || skipcc > 1) { - if (skipcc != -1) { + bool error = false; + skipcc = tv_get_bool_chk(&argvars[1], &error); + if (error) { + return; + } + if (skipcc < 0 || skipcc > 1) { semsg(_(e_using_number_as_bool_nr), skipcc); + return; } - } else { - strchar_common(argvars, rettv, skipcc); } + + strchar_common(argvars, rettv, skipcc); } /// "strutf16len()" function @@ -1845,11 +1850,12 @@ void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (!error) { if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN) { - skipcc = tv_get_bool(&argvars[3]); + skipcc = tv_get_bool_chk(&argvars[3], &error); + if (error) { + return; + } if (skipcc < 0 || skipcc > 1) { - if (skipcc != -1) { - semsg(_(e_using_number_as_bool_nr), skipcc); - } + semsg(_(e_using_number_as_bool_nr), skipcc); return; } } -- cgit From 106922898ad1510954737d38e7f8db78559ae6bd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 9 Jun 2023 17:43:46 +0800 Subject: vim-patch:9.0.1617: charidx() result is not consistent with byteidx() (#23963) Problem: charidx() and utf16idx() result is not consistent with byteidx(). Solution: When the index is equal to the length of the text return the lenght of the text instead of -1. (Yegappan Lakshmanan, closes vim/vim#12503) https://github.com/vim/vim/commit/577922b917e48285a7a312daf7b5bbc6e272939c Co-authored-by: Yegappan Lakshmanan --- src/nvim/strings.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 4e521b14f7..a0d62f5df5 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1603,6 +1603,11 @@ void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) int len; for (p = str, len = 0; utf16idx ? idx >= 0 : p <= str + idx; len++) { if (*p == NUL) { + // If the index is exactly the number of bytes or utf-16 code units + // in the string then return the length of the string in characters. + if (utf16idx ? (idx == 0) : (p == (str + idx))) { + rettv->vval.v_number = len; + } return; } if (utf16idx) { @@ -2047,6 +2052,11 @@ void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) int len; for (p = str, len = 0; charidx ? idx >= 0 : p <= str + idx; len++) { if (*p == NUL) { + // If the index is exactly the number of bytes or characters in the + // string then return the length of the string in utf-16 code units. + if (charidx ? (idx == 0) : (p == (str + idx))) { + rettv->vval.v_number = len; + } return; } const int clen = ptr2len(p); -- cgit From bbb934e7755a3b6f14c4d94334b8f54c63daebf1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 14 Jun 2023 20:54:11 +0800 Subject: vim-patch:9.0.1629: having utf16idx() rounding up is inconvenient (#24019) Problem: Having utf16idx() rounding up is inconvenient. Solution: Make utf16idx() round down. (Yegappan Lakshmanan, closes vim/vim#12523) https://github.com/vim/vim/commit/95707037afa1aeae4f3494dc623a721ceed7fc4e Co-authored-by: Yegappan Lakshmanan --- src/nvim/strings.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index a0d62f5df5..ada3a45d60 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -2014,6 +2014,9 @@ void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } /// "utf16idx()" function +/// +/// Converts a byte or character offset in a string to the corresponding UTF-16 +/// code unit offset. void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = -1; @@ -2050,6 +2053,7 @@ void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const char *p; int len; + int utf16idx = 0; for (p = str, len = 0; charidx ? idx >= 0 : p <= str + idx; len++) { if (*p == NUL) { // If the index is exactly the number of bytes or characters in the @@ -2059,6 +2063,7 @@ void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } return; } + utf16idx = len; const int clen = ptr2len(p); const int c = (clen > 1) ? utf_ptr2char(p) : *p; if (c > 0xFFFF) { @@ -2070,7 +2075,7 @@ void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } - rettv->vval.v_number = len > 0 ? len - 1 : 0; + rettv->vval.v_number = utf16idx; } /// "tolower(string)" function -- cgit From 2f17ef1fc4b96cf1106fd95ba090d34a2e4b977b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 22 Jun 2023 04:09:14 -0700 Subject: fix(messages): use "Vimscript" instead of "VimL" #24111 followup to #24109 fix #16150 --- src/nvim/strings.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index ada3a45d60..6fe2fd8ff3 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -500,13 +500,10 @@ static const char *const e_printf = /// Get number argument from idxp entry in tvs /// -/// Will give an error message for VimL entry with invalid type or for -/// insufficient entries. +/// Will give an error message for Vimscript entry with invalid type or for insufficient entries. /// -/// @param[in] tvs List of VimL values. List is terminated by VAR_UNKNOWN -/// value. -/// @param[in,out] idxp Index in a list. Will be incremented. Indexing starts -/// at 1. +/// @param[in] tvs List of Vimscript values. List is terminated by VAR_UNKNOWN value. +/// @param[in,out] idxp Index in a list. Will be incremented. Indexing starts at 1. /// /// @return Number value or 0 in case of error. static varnumber_T tv_nr(typval_T *tvs, int *idxp) @@ -530,10 +527,10 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) /// Get string argument from idxp entry in tvs /// -/// Will give an error message for VimL entry with invalid type or for +/// Will give an error message for Vimscript entry with invalid type or for /// insufficient entries. /// -/// @param[in] tvs List of VimL values. List is terminated by VAR_UNKNOWN +/// @param[in] tvs List of Vimscript values. List is terminated by VAR_UNKNOWN /// value. /// @param[in,out] idxp Index in a list. Will be incremented. /// @param[out] tofree If the idxp entry in tvs is not a String or a Number, @@ -564,7 +561,7 @@ static const char *tv_str(typval_T *tvs, int *idxp, char **const tofree) /// Get pointer argument from the next entry in tvs /// -/// Will give an error message for VimL entry with invalid type or for +/// Will give an error message for Vimscript entry with invalid type or for /// insufficient entries. /// /// @param[in] tvs List of typval_T values. @@ -595,11 +592,10 @@ static const void *tv_ptr(const typval_T *const tvs, int *const idxp) /// Get float argument from idxp entry in tvs /// -/// Will give an error message for VimL entry with invalid type or for +/// Will give an error message for Vimscript entry with invalid type or for /// insufficient entries. /// -/// @param[in] tvs List of VimL values. List is terminated by VAR_UNKNOWN -/// value. +/// @param[in] tvs List of Vimscript values. List is terminated by VAR_UNKNOWN value. /// @param[in,out] idxp Index in a list. Will be incremented. /// /// @return Floating-point value or zero in case of error. @@ -727,7 +723,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) /// @param[in] str_m String length. /// @param[in] fmt String format. /// @param[in] ap Values that should be formatted. Ignored if tvs is not NULL. -/// @param[in] tvs Values that should be formatted, for printf() VimL +/// @param[in] tvs Values that should be formatted, for printf() Vimscript /// function. Must be NULL in other cases. /// /// @return Number of bytes excluding NUL byte that would be written to the -- cgit From 516b173780e39de3ce1e4525f0a8f0ff250c992b Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 13 Jul 2023 10:17:19 +0100 Subject: perf(rtp): reduce rtp scans (#24191) * perf(rtp): reduce rtp scans Problem: Scanning the filesystem is expensive and particularly affects startuptime. Solution: Reduce the amount of redundant directory scans by relying less on glob patterns and handle vim and lua sourcing lower down. --- src/nvim/strings.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 6fe2fd8ff3..52a803a3cc 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -436,6 +436,25 @@ char *vim_strchr(const char *const string, const int c) } } +/// Test if "str" ends with "suffix" +/// +/// @param[in] str +/// @param[in] suffix to match +/// +/// @return [allocated] Copy of the string. +bool str_ends_with(const char *str, const char *suffix) +{ + if (!str || !suffix) { + return false; + } + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + if (lensuffix > lenstr) { + return false; + } + return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; +} + // Sort an array of strings. #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From 9176b5e10a6b32ff65c8ba3f532e3bd55c168ec6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Jul 2023 07:57:13 +0800 Subject: fix(runtime): respect 'fileignorecase' when sourcing (#24344) --- src/nvim/strings.c | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 52a803a3cc..6fe2fd8ff3 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -436,25 +436,6 @@ char *vim_strchr(const char *const string, const int c) } } -/// Test if "str" ends with "suffix" -/// -/// @param[in] str -/// @param[in] suffix to match -/// -/// @return [allocated] Copy of the string. -bool str_ends_with(const char *str, const char *suffix) -{ - if (!str || !suffix) { - return false; - } - size_t lenstr = strlen(str); - size_t lensuffix = strlen(suffix); - if (lensuffix > lenstr) { - return false; - } - return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; -} - // Sort an array of strings. #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From 842a47d6a4103a75e33c2c0023dbae5ad2c0f534 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Aug 2023 19:16:19 +0800 Subject: 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 https://github.com/vim/vim/commit/0c6181fec4c362eb9682d5af583341eb20cb1af5 Co-authored-by: Christ van Willegen --- src/nvim/strings.c | 789 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 749 insertions(+), 40 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 6fe2fd8ff3..2e603b8c9f 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -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. -- cgit From fc14928719df12826397b46b1765b82f1fc7d1d8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Aug 2023 20:54:28 +0800 Subject: fix(printf): make positional %zd and %zu work (#24722) --- src/nvim/strings.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 2e603b8c9f..cfc7738ad7 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -49,9 +49,11 @@ 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_signedsizet[] = N_("signed size_t"); 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_sizet[] = N_("size_t"); static char typename_pointer[] = N_("pointer"); static char typename_percent[] = N_("percent"); static char typename_char[] = N_("char"); @@ -748,9 +750,11 @@ enum { TYPE_INT, TYPE_LONGINT, TYPE_LONGLONGINT, + TYPE_SIGNEDSIZET, TYPE_UNSIGNEDINT, TYPE_UNSIGNEDLONGINT, TYPE_UNSIGNEDLONGLONGINT, + TYPE_SIZET, TYPE_POINTER, TYPE_PERCENT, TYPE_CHAR, @@ -768,8 +772,8 @@ static int format_typeof(const char *type, bool usetvs) // current conversion specifier character char fmt_spec = '\0'; - // parse 'h', 'l' and 'll' length modifiers - if (*type == 'h' || *type == 'l') { + // parse 'h', 'l', 'll' and 'z' length modifiers + if (*type == 'h' || *type == 'l' || *type == 'z') { length_modifier = *type; type++; if (length_modifier == 'l' && *type == 'l') { @@ -855,6 +859,8 @@ static int format_typeof(const char *type, bool usetvs) return TYPE_LONGINT; case 'L': return TYPE_LONGLONGINT; + case 'z': + return TYPE_SIGNEDSIZET; } } else { // unsigned @@ -866,6 +872,8 @@ static int format_typeof(const char *type, bool usetvs) return TYPE_UNSIGNEDLONGINT; case 'L': return TYPE_UNSIGNEDLONGLONGINT; + case 'z': + return TYPE_SIZET; } } break; @@ -894,10 +902,14 @@ static char *format_typename(const char *type) return _(typename_longlongint); case TYPE_UNSIGNEDINT: return _(typename_unsignedint); + case TYPE_SIGNEDSIZET: + return _(typename_signedsizet); case TYPE_UNSIGNEDLONGINT: return _(typename_unsignedlongint); case TYPE_UNSIGNEDLONGLONGINT: return _(typename_unsignedlonglongint); + case TYPE_SIZET: + return _(typename_sizet); case TYPE_POINTER: return _(typename_pointer); case TYPE_PERCENT: @@ -1147,8 +1159,8 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * ptype = p; } - // parse 'h', 'l' and 'll' length modifiers - if (*p == 'h' || *p == 'l') { + // parse 'h', 'l', 'll' and 'z' length modifiers + if (*p == 'h' || *p == 'l' || *p == 'z') { length_modifier = *p; p++; if (length_modifier == 'l' && *p == 'l') { @@ -1283,6 +1295,10 @@ static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, in va_arg(*ap, long long); // NOLINT(runtime/int) break; + case TYPE_SIGNEDSIZET: // implementation-defined, usually ptrdiff_t + va_arg(*ap, ptrdiff_t); + break; + case TYPE_UNSIGNEDINT: va_arg(*ap, unsigned); break; @@ -1295,6 +1311,10 @@ static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, in va_arg(*ap, unsigned long long); // NOLINT(runtime/int) break; + case TYPE_SIZET: + va_arg(*ap, size_t); + break; + case TYPE_FLOAT: va_arg(*ap, double); break; @@ -1699,7 +1719,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), va_arg(ap, long long))); // NOLINT(runtime/int) break; - case 'z': + case 'z': // implementation-defined, usually ptrdiff_t arg = (tvs ? (ptrdiff_t)tv_nr(tvs, &arg_idx) : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), -- cgit From 4c7df98e4eeed20f8a9c461729935b79743d7752 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 19 Aug 2023 17:44:19 +0800 Subject: vim-patch:9.0.1515: reverse() does not work for a String Problem: reverse() does not work for a String. Solution: Implement reverse() for a String. (Yegappan Lakshmanan, closes vim/vim#12179) https://github.com/vim/vim/commit/03ff1c2dde7f15eca5c9baa6dafbda9b49bedc3b vim-patch:9.0.1738: Duplicate code to reverse a string Problem: Duplicate code to reverse a string Solution: Move reverse_text() to strings.c and remove string_reverse(). closes: vim/vim#12847 https://github.com/vim/vim/commit/4dd266cb66d901cf5324f09405cfea3f004bd29f Co-authored-by: Yegappan Lakshmanan --- src/nvim/strings.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index cfc7738ad7..ec770de4a0 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -2168,20 +2168,17 @@ int kv_do_printf(StringBuilder *str, const char *fmt, ...) /// /// @return the allocated string. char *reverse_text(char *s) - FUNC_ATTR_NONNULL_RET + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { - // Reverse the pattern. size_t len = strlen(s); char *rev = xmalloc(len + 1); - size_t rev_i = len; - for (size_t s_i = 0; s_i < len; s_i++) { + for (size_t s_i = 0, rev_i = len; s_i < len; s_i++) { const int mb_len = utfc_ptr2len(s + s_i); rev_i -= (size_t)mb_len; memmove(rev + rev_i, s + s_i, (size_t)mb_len); s_i += (size_t)mb_len - 1; } rev[len] = NUL; - return rev; } -- cgit From c431d820e7be1c511d3d16e89cdffaa21b7909fa Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 4 Sep 2023 08:49:50 +0800 Subject: vim-patch:9.0.1856: issues with formatting positional arguments (#25013) Problem: issues with formatting positional arguments Solution: fix them, add tests and documentation closes: vim/vim#12140 closes: vim/vim#12985 Tentatively fix message_test. Check NULL ptr. https://github.com/vim/vim/commit/aa90d4f031f73a34aaef5746931ea746849a2231 Co-authored-by: Christ van Willegen --- src/nvim/strings.c | 123 ++++++++++++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 57 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index ec770de4a0..b66dff3c18 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -32,33 +32,35 @@ #include "nvim/types.h" #include "nvim/vim.h" -static char e_cannot_mix_positional_and_non_positional_str[] +static const 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[] +static const 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[] +static const 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[] +static const 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[] +static const 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[] +static const 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_signedsizet[] = N_("signed size_t"); -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_sizet[] = N_("size_t"); -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"); +static const char e_aptypes_is_null_str_nr[] + = "E1408: Internal error: ap_types or ap_types[idx] is NULL: %s: %d"; + +static const char typename_unknown[] = N_("unknown"); +static const char typename_int[] = N_("int"); +static const char typename_longint[] = N_("long int"); +static const char typename_longlongint[] = N_("long long int"); +static const char typename_signedsizet[] = N_("signed size_t"); +static const char typename_unsignedint[] = N_("unsigned int"); +static const char typename_unsignedlongint[] = N_("unsigned long int"); +static const char typename_unsignedlonglongint[] = N_("unsigned long long int"); +static const char typename_sizet[] = N_("size_t"); +static const char typename_pointer[] = N_("pointer"); +static const char typename_percent[] = N_("percent"); +static const char typename_char[] = N_("char"); +static const char typename_string[] = N_("string"); +static const 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 @@ -763,7 +765,7 @@ enum { }; /// Types that can be used in a format string -static int format_typeof(const char *type, bool usetvs) +static int format_typeof(const char *type) FUNC_ATTR_NONNULL_ALL { // allowed values: \0, h, l, L @@ -800,19 +802,6 @@ static int format_typeof(const char *type, bool usetvs) 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 @@ -847,7 +836,7 @@ static int format_typeof(const char *type, bool usetvs) if (fmt_spec == 'p') { return TYPE_POINTER; } else if (fmt_spec == 'b' || fmt_spec == 'B') { - return TYPE_UNSIGNEDINT; + return TYPE_UNSIGNEDLONGLONGINT; } else if (fmt_spec == 'd') { // signed switch (length_modifier) { @@ -893,7 +882,7 @@ static int format_typeof(const char *type, bool usetvs) static char *format_typename(const char *type) FUNC_ATTR_NONNULL_ALL { - switch (format_typeof(type, false)) { + switch (format_typeof(type)) { case TYPE_INT: return _(typename_int); case TYPE_LONGINT: @@ -960,7 +949,7 @@ static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const } } } else { - if (format_typeof(type, false) != format_typeof((*ap_types)[arg - 1], false)) { + if (format_typeof(type) != format_typeof((*ap_types)[arg - 1])) { semsg(_(e_positional_arg_num_type_inconsistent_str_str), arg, format_typename(type), format_typename((*ap_types)[arg - 1])); return FAIL; @@ -1239,7 +1228,7 @@ error: } static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, int *arg_idx, - int *arg_cur) + int *arg_cur, const char *fmt) FUNC_ATTR_NONNULL_ARG(3, 4, 5) { int arg_min = 0; @@ -1260,10 +1249,14 @@ static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, in } for (*arg_cur = arg_min; *arg_cur < *arg_idx - 1; (*arg_cur)++) { - assert(ap_types != NULL); + if (ap_types == NULL || ap_types[*arg_cur] == NULL) { + semsg(e_aptypes_is_null_str_nr, fmt, *arg_cur); + return; + } + const char *p = ap_types[*arg_cur]; - int fmt_type = format_typeof(p, true); + int fmt_type = format_typeof(p); // get parameter value, do initial processing switch (fmt_type) { @@ -1477,7 +1470,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st const int j = (tvs ? (int)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, int))); if (j >= 0) { @@ -1528,7 +1522,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st const int j = (tvs ? (int)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, int))); if (j >= 0) { @@ -1600,7 +1595,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st case 'c': { const int j = (tvs ? (int)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, int))); // standard demands unsigned char @@ -1613,7 +1609,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st case 'S': str_arg = (tvs ? tv_str(tvs, &arg_idx, &tofree) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, const char *))); if (!str_arg) { @@ -1684,7 +1681,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st if (fmt_spec == 'p') { ptr_arg = (tvs ? tv_ptr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, void *))); if (ptr_arg) { @@ -1696,7 +1694,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st case '\0': arg = (tvs ? (int)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, int))); break; case 'h': @@ -1704,25 +1703,29 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st arg = (int16_t) (tvs ? (int)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, int))); break; case 'l': arg = (tvs ? (long)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, long))); break; 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), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, long long))); // NOLINT(runtime/int) break; case 'z': // implementation-defined, usually ptrdiff_t arg = (tvs ? (ptrdiff_t)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, ptrdiff_t))); break; } @@ -1737,32 +1740,37 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st case '\0': uarg = (tvs ? (unsigned)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, unsigned))); break; case 'h': uarg = (uint16_t) (tvs ? (unsigned)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, unsigned))); break; case 'l': uarg = (tvs ? (unsigned long)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, unsigned long))); break; 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), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, unsigned long long))); // NOLINT(runtime/int) break; case 'z': uarg = (tvs ? (size_t)tv_nr(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, size_t))); break; } @@ -1900,7 +1908,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st double f = (tvs ? tv_float(tvs, &arg_idx) - : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur), + : (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, + &arg_cur, fmt), va_arg(ap, double))); double abs_f = f < 0 ? -f : f; -- cgit From 069fad6e2df25e6b079879670cf6c68ae7ddb012 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 4 Sep 2023 15:55:16 +0800 Subject: vim-patch:9.0.1863: wrong format specifiers in e_aptypes_is_null_str_nr (#25015) Problem: wrong format specifiers in e_aptypes_is_null_str_nr Solution: Fix the wrong format specifier closes: vim/vim#13020 https://github.com/vim/vim/commit/7db89bdc23e53c7bc43af6f1c7281bc69a6a3098 --- src/nvim/strings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index b66dff3c18..bc877ec1cc 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1250,7 +1250,7 @@ static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, in for (*arg_cur = arg_min; *arg_cur < *arg_idx - 1; (*arg_cur)++) { if (ap_types == NULL || ap_types[*arg_cur] == NULL) { - semsg(e_aptypes_is_null_str_nr, fmt, *arg_cur); + siemsg(e_aptypes_is_null_str_nr, fmt, *arg_cur); return; } -- cgit From 82150ca51bf396e5795b49f6e1cb2faaaa07b28b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 9 Sep 2023 07:15:41 +0800 Subject: vim-patch:9.0.1884: Wrong order of arguments for error messages (#25055) Problem: Wrong order of arguments for error messages Solution: Reverse order or arguments for e_aptypes_is_null_nr_str closes: vim/vim#13051 https://github.com/vim/vim/commit/1bd2cb11694690a77e4141bce2e34d9dfb882f1c Co-authored-by: Christ van Willegen --- src/nvim/strings.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index bc877ec1cc..661dae7592 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -44,8 +44,8 @@ static const char e_positional_arg_num_type_inconsistent_str_str[] = N_("E1404: Positional argument %d type used inconsistently: %s/%s"); static const char e_invalid_format_specifier_str[] = N_("E1405: Invalid format specifier: %s"); -static const char e_aptypes_is_null_str_nr[] - = "E1408: Internal error: ap_types or ap_types[idx] is NULL: %s: %d"; +static const char e_aptypes_is_null_nr_str[] + = "E1408: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"; static const char typename_unknown[] = N_("unknown"); static const char typename_int[] = N_("int"); @@ -1250,7 +1250,7 @@ static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, in for (*arg_cur = arg_min; *arg_cur < *arg_idx - 1; (*arg_cur)++) { if (ap_types == NULL || ap_types[*arg_cur] == NULL) { - siemsg(e_aptypes_is_null_str_nr, fmt, *arg_cur); + siemsg(e_aptypes_is_null_nr_str, fmt, *arg_cur); return; } -- cgit From 7ec20a4082f2e62e0ae90d464e0d62aa4ffdacf4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 29 Sep 2023 06:52:02 +0800 Subject: vim-patch:9.0.1950: Vim9: error codes spread out (#25405) Problem: Vim9: error codes spread out Solution: group them together and reserve 100 more for future use Reserve 100 error codes for future enhancements to the Vim9 class support closes: vim/vim#13207 https://github.com/vim/vim/commit/413f83990f15d5d59d27ab741670f527a7a3feb8 Co-authored-by: Yegappan Lakshmanan --- src/nvim/strings.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 661dae7592..7e4d07bd00 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -33,19 +33,19 @@ #include "nvim/vim.h" static const char e_cannot_mix_positional_and_non_positional_str[] - = N_("E1400: Cannot mix positional and non-positional arguments: %s"); + = N_("E1500: Cannot mix positional and non-positional arguments: %s"); static const char e_fmt_arg_nr_unused_str[] - = N_("E1401: format argument %d unused in $-style format: %s"); + = N_("E1501: format argument %d unused in $-style format: %s"); static const char e_positional_num_field_spec_reused_str_str[] - = N_("E1402: Positional argument %d used as field width reused as different type: %s/%s"); + = N_("E1502: Positional argument %d used as field width reused as different type: %s/%s"); static const char e_positional_nr_out_of_bounds_str[] - = N_("E1403: Positional argument %d out of bounds: %s"); + = N_("E1503: Positional argument %d out of bounds: %s"); static const char e_positional_arg_num_type_inconsistent_str_str[] - = N_("E1404: Positional argument %d type used inconsistently: %s/%s"); + = N_("E1504: Positional argument %d type used inconsistently: %s/%s"); static const char e_invalid_format_specifier_str[] - = N_("E1405: Invalid format specifier: %s"); + = N_("E1505: Invalid format specifier: %s"); static const char e_aptypes_is_null_nr_str[] - = "E1408: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"; + = "E1520: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"; static const char typename_unknown[] = N_("unknown"); static const char typename_int[] = N_("int"); -- cgit From cf8b2c0e74fd5e723b0c15c2ce84e6900fd322d3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 12:05:28 +0800 Subject: build(iwyu): add a few more _defs.h mappings (#25435) --- src/nvim/strings.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 7e4d07bd00..0d307a760f 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -21,6 +21,7 @@ #include "nvim/ex_docmd.h" #include "nvim/garray.h" #include "nvim/gettext.h" +#include "nvim/globals.h" #include "nvim/macros.h" #include "nvim/math.h" #include "nvim/mbyte.h" -- cgit From 8bb0878013a4507620201fa5aceb4c18e413bebe Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 6 Oct 2023 07:21:40 +0800 Subject: vim-patch:9.0.1990: strange error number Problem: strange error number Solution: change error number, add doc tag for E1507 closes: vim/vim#13270 https://github.com/vim/vim/commit/ea746f9e862092aef3d4e95c64d116759b9fabe0 Co-authored-by: Christ van Willegen --- src/nvim/strings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 0d307a760f..7ba11ed8e5 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -46,7 +46,7 @@ static const char e_positional_arg_num_type_inconsistent_str_str[] static const char e_invalid_format_specifier_str[] = N_("E1505: Invalid format specifier: %s"); static const char e_aptypes_is_null_nr_str[] - = "E1520: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"; + = "E1507: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"; static const char typename_unknown[] = N_("unknown"); static const char typename_int[] = N_("int"); -- cgit From 7f65431d90e18819009c30aa3ee2a680d3b3958d Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 7 Oct 2023 00:15:09 +0200 Subject: vim-patch:9.0.1997: Some unused code in move.c and string.c Problem: Some unused code in move.c and string.c Solution: Remove it closes: vim/vim#13288 https://github.com/vim/vim/commit/580c1fcb4ad85360cd3a361c3c8e37b534153d60 Co-authored-by: dundargoc --- src/nvim/strings.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 7ba11ed8e5..cc66f917f8 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1079,7 +1079,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * any_arg = 1; CHECK_POS_ARG; } - } else if (ascii_isdigit((int)(*(arg = p)))) { + } else 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'); @@ -1126,7 +1126,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * any_arg = 1; CHECK_POS_ARG; } - } else if (ascii_isdigit((int)(*(arg = p)))) { + } else 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'); @@ -1155,7 +1155,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * p++; if (length_modifier == 'l' && *p == 'l') { // double l = long long - length_modifier = 'L'; + // length_modifier = 'L'; p++; } } -- cgit From bc5dfda441dd1b3de56bd64059b0a1df4075f050 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 18 Oct 2023 06:08:14 +0800 Subject: vim-patch:9.0.2041: trim(): hard to use default mask (#25692) Problem: trim(): hard to use default mask (partly revert v9.0.2040) Solution: use default mask when it is empty The default 'mask' value is pretty complex, as it includes many characters. Yet, if one needs to specify the trimming direction, the third argument, 'trim()' currently requires the 'mask' value to be provided explicitly. Currently, an empty 'mask' will make 'trim()' call return 'text' value that is passed in unmodified. It is unlikely that someone is using it, so the chances of scripts being broken by this change are low. Also, this reverts commit 9.0.2040 (which uses v:none for the default and requires to use an empty string instead). closes: vim/vim#13358 https://github.com/vim/vim/commit/8079917447e7436dccc2e4cd4a4a56ae0a4712f2 vim-patch:9.0.2040: trim(): hard to use default mask Problem: trim(): hard to use default mask Solution: Use default 'mask' when it is v:none The default 'mask' value is pretty complex, as it includes many characters. Yet, if one needs to specify the trimming direction, the third argument, 'trim()' currently requires the 'mask' value to be provided explicitly. 'v:none' is already used to mean "use the default argument value" in user defined functions. See |none-function_argument| in help. closes: vim/vim#13363 https://github.com/vim/vim/commit/6e6386716f9494ae86027c6d34f657fd03dfec42 Co-authored-by: Illia Bobyr --- src/nvim/strings.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index cc66f917f8..af82f5e578 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -2926,6 +2926,10 @@ void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (argvars[1].v_type == VAR_STRING) { mask = tv_get_string_buf_chk(&argvars[1], buf2); + if (*mask == NUL) { + mask = NULL; + } + if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; // leading or trailing characters to trim -- cgit From cd63a9addd6e1114c3524fa041ece560550cfe7b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 10 Nov 2023 08:39:21 +0800 Subject: refactor: change some xstrndup() and xstrnsave() to xmemdupz() (#25959) When the given length is exactly the number of bytes to copy, xmemdupz() makes the intention clearer. --- src/nvim/strings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index af82f5e578..26da38f284 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -2646,7 +2646,7 @@ void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } rettv->v_type = VAR_STRING; - rettv->vval.v_string = xstrndup(p + nbyte, (size_t)len); + rettv->vval.v_string = xmemdupz(p + nbyte, (size_t)len); } /// "strpart()" function -- cgit From 353a4be7e84fdc101318215bdcc8a7e780d737fe Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 12 Nov 2023 13:13:58 +0100 Subject: build: remove PVS We already have an extensive suite of static analysis tools we use, which causes a fair bit of redundancy as we get duplicate warnings. PVS is also prone to give false warnings which creates a lot of work to identify and disable. --- src/nvim/strings.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 26da38f284..007d088bbb 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1,6 +1,3 @@ -// 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 #include #include @@ -603,7 +600,7 @@ 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) // -V568 + 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) -- cgit From 28f4f3c48498086307ed825d1761edb5789ca0e8 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 12 Nov 2023 15:54:54 +0100 Subject: refactor: follow style guide - reduce variable scope - prefer initialization over declaration and assignment - use bool to represent boolean values --- src/nvim/strings.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 007d088bbb..eb851a4c1b 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -485,10 +485,8 @@ void sort_strings(char **files, int count) bool has_non_ascii(const char *s) FUNC_ATTR_PURE { - const char *p; - if (s != NULL) { - for (p = s; *p != NUL; p++) { + for (const char *p = s; *p != NUL; p++) { if ((uint8_t)(*p) >= 128) { return true; } @@ -1964,7 +1962,6 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st assert(str_arg_l < sizeof(tmp)); if (remove_trailing_zeroes) { - int i; char *tp; // using %g or %G: remove superfluous zeroes @@ -1979,7 +1976,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st STRMOVE(tp + 1, tp + 2); str_arg_l--; } - i = (tp[1] == '-') ? 2 : 1; + int i = (tp[1] == '-') ? 2 : 1; while (tp[i] == '0') { // change "1.0e07" to "1.0e7" STRMOVE(tp + i, tp + i + 1); -- cgit From ac1113ded5f8f09dd99a9894d7a7e795626fb728 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 13 Nov 2023 23:40:37 +0100 Subject: refactor: follow style guide - reduce variable scope - prefer initialization over declaration and assignment --- src/nvim/strings.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index eb851a4c1b..4dda332b9f 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -2938,11 +2938,10 @@ void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } - int c1; if (dir == 0 || dir == 1) { // Trim leading characters while (*head != NUL) { - c1 = utf_ptr2char(head); + int c1 = utf_ptr2char(head); if (mask == NULL) { if (c1 > ' ' && c1 != 0xa0) { break; @@ -2967,7 +2966,7 @@ void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) for (; tail > head; tail = prev) { prev = tail; MB_PTR_BACK(head, prev); - c1 = utf_ptr2char(prev); + int c1 = utf_ptr2char(prev); if (mask == NULL) { if (c1 > ' ' && c1 != 0xa0) { break; -- cgit From a6e3d93421ba13c407a96fac9cc01fa41ec7ad98 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 16 Nov 2023 10:59:11 +0100 Subject: refactor: enable formatting for ternaries This requires removing the "Inner expression should be aligned" rule from clint as it prevents essentially any formatting regarding ternary operators. --- src/nvim/strings.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 4dda332b9f..edb463416f 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -915,8 +915,8 @@ static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const { 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 *)); + ? 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; -- cgit From 6c14ae6bfaf51415b555e9a6b85d1d280976358d Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 20:27:32 +0100 Subject: refactor: rename types.h to types_defs.h --- src/nvim/strings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index edb463416f..06f5166864 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -27,7 +27,7 @@ #include "nvim/option.h" #include "nvim/plines.h" #include "nvim/strings.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/vim.h" static const char e_cannot_mix_positional_and_non_positional_str[] -- cgit From 79b6ff28ad1204fbb4199b9092f5c578d88cb28e Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 28 Nov 2023 20:31:00 +0100 Subject: refactor: fix headers with IWYU --- src/nvim/strings.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 06f5166864..1533a486bc 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -9,8 +9,8 @@ #include #include "auto/config.h" -#include "nvim/ascii.h" -#include "nvim/assert.h" +#include "nvim/ascii_defs.h" +#include "nvim/assert_defs.h" #include "nvim/charset.h" #include "nvim/eval/encode.h" #include "nvim/eval/typval.h" @@ -19,7 +19,7 @@ #include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/globals.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/math.h" #include "nvim/mbyte.h" #include "nvim/memory.h" @@ -28,7 +28,7 @@ #include "nvim/plines.h" #include "nvim/strings.h" #include "nvim/types_defs.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" static const char e_cannot_mix_positional_and_non_positional_str[] = N_("E1500: Cannot mix positional and non-positional arguments: %s"); -- cgit From 86cc791debba09c8ed1aa0d863be844108866a38 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 29 Nov 2023 23:10:21 +0800 Subject: refactor: move function macros out of vim_defs.h (#26300) --- src/nvim/strings.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'src/nvim/strings.c') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 1533a486bc..a439d11818 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -380,18 +380,6 @@ void del_trailing_spaces(char *ptr) } } -#if !defined(HAVE_STRNLEN) -size_t xstrnlen(const char *s, size_t n) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE -{ - const char *end = memchr(s, '\0', n); - if (end == NULL) { - return n; - } - return (size_t)(end - s); -} -#endif - #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) // Compare two strings, ignoring case, using current locale. // Doesn't work for multi-byte characters. @@ -441,6 +429,13 @@ int vim_strnicmp(const char *s1, const char *s2, size_t len) } #endif +/// Case-insensitive `strequal`. +bool striequal(const char *a, const char *b) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (a == NULL && b == NULL) || (a && b && STRICMP(a, b) == 0); +} + /// strchr() version which handles multibyte strings /// /// @param[in] string String to search in. -- cgit