From 34cb0879551b8217db820f684d913a393f4f38cb Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sun, 30 May 2021 22:50:09 +0100 Subject: vim-patch:8.1.1355: obvious mistakes are accepted as valid expressions Problem: Obvious mistakes are accepted as valid expressions. Solution: Be more strict about parsing numbers. (Yasuhiro Matsumoto, closes vim/vim#3981) https://github.com/vim/vim/commit/16e9b85113e0b354ece1cb4f5fcc7866850f3685 Update vim_str2nr_spec.lua to add more tests that use strict = true. --- src/nvim/charset.c | 18 ++++++++++++++++-- src/nvim/eval.c | 7 ++++++- src/nvim/eval/decode.c | 6 +++--- src/nvim/eval/funcs.c | 3 ++- src/nvim/eval/typval.c | 3 ++- src/nvim/ex_cmds.c | 2 +- src/nvim/ex_getln.c | 4 ++-- src/nvim/keymap.c | 12 ++++++++++-- src/nvim/ops.c | 2 +- src/nvim/option.c | 6 +++--- src/nvim/testdir/test_expr.vim | 11 +++++++++++ src/nvim/viml/parser/expressions.c | 4 ++-- 12 files changed, 59 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 4725c0d08f..9f11e85b01 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1397,6 +1397,7 @@ bool vim_isblankline(char_u *lbuf) /// If "what" contains STR2NR_HEX recognize hex numbers. /// If "what" contains STR2NR_FORCE always assume bin/oct/hex. /// If maxlen > 0, check at a maximum maxlen chars. +/// If strict is true, check the number strictly. return *len = 0 if fail. /// /// @param start /// @param prep Returns guessed type of number 0 = decimal, 'x' or 'X' is @@ -1407,9 +1408,12 @@ bool vim_isblankline(char_u *lbuf) /// @param nptr Returns the signed result. /// @param unptr Returns the unsigned result. /// @param maxlen Max length of string to check. +/// @param strict If true, fail if the number has unexpected trailing +/// alpha-numeric chars: *len is set to 0 and nothing else is +/// returned. void vim_str2nr(const char_u *const start, int *const prep, int *const len, const int what, varnumber_T *const nptr, - uvarnumber_T *const unptr, const int maxlen) + uvarnumber_T *const unptr, const int maxlen, const bool strict) FUNC_ATTR_NONNULL_ARG(1) { const char *ptr = (const char *)start; @@ -1419,6 +1423,10 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, const bool negative = (ptr[0] == '-'); uvarnumber_T un = 0; + if (len != NULL) { + *len = 0; + } + if (negative) { ptr++; } @@ -1492,7 +1500,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, goto vim_str2nr_dec; } - // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. + // Do the conversion manually to avoid sscanf() quirks. abort(); // Should’ve used goto earlier. #define PARSE_NUMBER(base, cond, conv) \ do { \ @@ -1524,6 +1532,12 @@ vim_str2nr_hex: #undef PARSE_NUMBER vim_str2nr_proceed: + // Check for an alpha-numeric character immediately following, that is + // most likely a typo. + if (strict && ptr - (const char *)start != maxlen && ASCII_ISALNUM(*ptr)) { + return; + } + if (prep != NULL) { *prep = pre; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5603fbb082..fbafe42eb2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3950,7 +3950,12 @@ static int eval7( rettv->vval.v_float = f; } } else { - vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0); + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true); + if (len == 0) { + EMSG2(_(e_invexpr2), *arg); + ret = FAIL; + break; + } *arg += len; if (evaluate) { rettv->v_type = VAR_NUMBER; diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index bd4dc87d31..89e1f04bfd 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -437,7 +437,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, t += 4; uvarnumber_T ch; vim_str2nr((char_u *)ubuf, NULL, NULL, - STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4); + STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4, true); if (ch == 0) { hasnul = true; } @@ -611,8 +611,8 @@ parse_json_number_check: // Convert integer varnumber_T nr; int num_len; - vim_str2nr((char_u *) s, NULL, &num_len, 0, &nr, NULL, (int) (p - s)); - if ((int) exp_num_len != num_len) { + vim_str2nr((char_u *)s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true); + if ((int)exp_num_len != num_len) { emsgf(_("E685: internal error: while converting number \"%.*s\" " "to integer vim_str2nr consumed %i bytes in place of %zu"), (int) exp_num_len, s, num_len, exp_num_len); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 99f9f17e0a..519b268bd1 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -10030,7 +10030,8 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) what = 0; } } - vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); + vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false); + // Text after the number is silently ignored. if (isneg) { rettv->vval.v_number = -n; } else { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 5cb0058ec6..22b3bf026b 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2990,7 +2990,8 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_STRING: { varnumber_T n = 0; if (tv->vval.v_string != NULL) { - vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0); + vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, + false); } return n; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index bd8d3973d5..2ea16f0d50 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -609,7 +609,7 @@ void ex_sort(exarg_T *eap) } else { nrs[lnum - eap->line1].st_u.num.is_number = true; vim_str2nr(s, NULL, NULL, sort_what, - &nrs[lnum - eap->line1].st_u.num.value, NULL, 0); + &nrs[lnum - eap->line1].st_u.num.value, NULL, 0, false); } } else { s = skipwhite(p); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 0f98c9cd34..b90773ce83 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6056,7 +6056,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range - vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); *str += len; *num1 = (int)num; first = true; @@ -6064,7 +6064,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == ',') { // parse "to" part of range *str = skipwhite(*str + 1); - vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); if (len > 0) { *num2 = (int)num; *str = skipwhite(*str + len); diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 277b9ade89..c6966ff9fa 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -628,7 +628,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') { bp += 3; // skip t_xx, xx may be '-' or '>' } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) { - vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0); + vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, true); + if (l == 0) { + EMSG(_(e_invarg)); + return 0; + } bp += l + 5; break; } @@ -654,7 +658,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, if (STRNICMP(last_dash + 1, "char-", 5) == 0 && ascii_isdigit(last_dash[6])) { // or or - vim_str2nr(last_dash + 6, NULL, NULL, STR2NR_ALL, NULL, &n, 0); + vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, true); + if (l == 0) { + EMSG(_(e_invarg)); + return 0; + } key = (int)n; } else { int off = 1; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3f0d85dc88..77432149ce 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -5017,7 +5017,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) 0 + (do_bin ? STR2NR_BIN : 0) + (do_oct ? STR2NR_OCT : 0) + (do_hex ? STR2NR_HEX : 0), - NULL, &n, maxlen); + NULL, &n, maxlen, false); // ignore leading '-' for hex, octal and bin numbers if (pre && negative) { diff --git a/src/nvim/option.c b/src/nvim/option.c index fdfb409c5e..06389c0ef1 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1276,9 +1276,9 @@ int do_set( } } else if (*arg == '-' || ascii_isdigit(*arg)) { // Allow negative, octal and hex numbers. - vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0); - if (arg[i] != NUL && !ascii_iswhite(arg[i])) { - errmsg = e_invarg; + vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true); + if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) { + errmsg = (char_u *)N_("E521: Number required after ="); goto skip; } } else { diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 0b41a1127a..c49285621a 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -502,6 +502,17 @@ func Test_empty_concatenate() call assert_equal('b', 'b' . 'a'[4:0]) endfunc +func Test_broken_number() + let X = 'bad' + call assert_fails('echo 1X', 'E15:') + call assert_fails('echo 0b1X', 'E15:') + call assert_fails('echo 0b12', 'E15:') + call assert_fails('echo 0x1X', 'E15:') + call assert_fails('echo 011X', 'E15:') + call assert_equal(2, str2nr('2a')) + call assert_fails('inoremap b', 'E474:') +endfunc + func Test_eval_after_if() let s:val = '' func SetVal(x) diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index e9d82ca87d..f5bd5479c4 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -351,7 +351,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) } if (exp_start) { vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part, - (int)(ret.len - exp_start)); + (int)(ret.len - exp_start), false); } if (exp_negative) { exp_part += frac_size; @@ -369,7 +369,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) int len; int prep; vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL, - &ret.data.num.val.integer, (int)pline.size); + &ret.data.num.val.integer, (int)pline.size, false); ret.len = (size_t)len; const uint8_t bases[] = { [0] = 10, -- cgit From dda977f5c4d2fc81a0582fbaec7a6397aaf7aebf Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 10 Sep 2021 21:30:49 +0100 Subject: vim-patch:8.1.1722: error when scriptversion is 2 a making a dictionary access Problem: Error when scriptversion is 2 a making a dictionary access. Solution: Parse the subscript even when not evaluating the sub-expression. (closes vim/vim#4704) https://github.com/vim/vim/commit/61343f0c44c8e71df04918d033e0a744c0b7f8aa :scriptversion is N/A. --- src/nvim/eval.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fbafe42eb2..dfbb187b5b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1456,21 +1456,18 @@ static void ex_let_const(exarg_T *eap, const bool is_const) /* * Assign the typevalue "tv" to the variable or variables at "arg_start". * Handles both "var" with any type and "[var, var; var]" with a list type. - * When "nextchars" is not NULL it points to a string with characters that + * When "op" is not NULL it points to a string with characters that * must appear after the variable(s). Use "+", "-" or "." for add, subtract * or concatenate. * Returns OK or FAIL; */ -static int -ex_let_vars( - char_u *arg_start, - typval_T *tv, - int copy, // copy values from "tv", don't move - int semicolon, // from skip_var_list() - int var_count, // from skip_var_list() - int is_const, // lock variables for :const - char_u *nextchars -) +static int ex_let_vars(char_u *arg_start, + typval_T *tv, + int copy, // copy values from "tv", don't move + int semicolon, // from skip_var_list() + int var_count, // from skip_var_list() + int is_const, // lock variables for :const + char_u *op) { char_u *arg = arg_start; typval_T ltv; @@ -1479,7 +1476,7 @@ ex_let_vars( /* * ":let var = expr" or ":for var in list" */ - if (ex_let_one(arg, tv, copy, is_const, nextchars, nextchars) == NULL) { + if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { return FAIL; } return OK; @@ -1510,7 +1507,7 @@ ex_let_vars( while (*arg != ']') { arg = skipwhite(arg + 1); arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, - (const char_u *)",;]", nextchars); + (const char_u *)",;]", op); if (arg == NULL) { return FAIL; } @@ -1532,8 +1529,8 @@ ex_let_vars( ltv.vval.v_list = rest_list; tv_list_ref(rest_list); - arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, - (char_u *)"]", nextchars); + arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, (char_u *)"]", + op); tv_clear(<v); if (arg == NULL) { return FAIL; @@ -8675,6 +8672,7 @@ handle_subscript( } } + // "." is ".name" lookup when we found a dict. while (ret == OK && (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) -- cgit From 6617629ad6eceeb77d49633780a7213eeb17a2c9 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 22 May 2021 19:44:53 +0100 Subject: vim-patch:8.1.2035: recognizing octal numbers is confusing Problem: Recognizing octal numbers is confusing. Solution: Introduce scriptversion 4: do not use octal and allow for single quote inside numbers. https://github.com/vim/vim/commit/60a8de28d11595f4df0419ece8afa7d6accc9fbd :scriptversion is N/A. Cherry-pick Test_readfile_binary() from v8.1.0742. Note that this patch was missing vim_str2nr() changes, and so fails the tests; this was fixed in v8.1.2036. --- src/nvim/charset.h | 4 +++- src/nvim/eval.lua | 2 +- src/nvim/eval/funcs.c | 14 +++++++------- src/nvim/testdir/test_eval_stuff.vim | 36 +++++++++++++++++++++++++++--------- src/nvim/testdir/test_functions.vim | 6 ++++++ 5 files changed, 44 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/charset.h b/src/nvim/charset.h index e657ce19b6..b5f1f8506b 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -27,9 +27,11 @@ typedef enum { /// /// STR2NR_FORCE|STR2NR_DEC is actually not different from supplying zero /// as flags, but still present for completeness. - STR2NR_FORCE = (1 << 3), + STR2NR_FORCE = (1 << 7), /// Recognize all formats vim_str2nr() can recognize. STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX, + STR2NR_NO_OCT = STR2NR_BIN | STR2NR_HEX, ///< Disallow octal numbers. + STR2NR_QUOTE = (1 << 4), ///< Ignore embedded single quotes. } ChStr2NrFlags; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index faff29b268..a7242ba73a 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -344,7 +344,7 @@ return { stdpath={args=1}, str2float={args=1, base=1}, str2list={args={1, 2}, base=1}, - str2nr={args={1, 2}}, + str2nr={args={1, 3}}, strcharpart={args={2, 3}}, strchars={args={1,2}}, strdisplaywidth={args={1, 2}}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 519b268bd1..61f535e32e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -9998,7 +9998,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int base = 10; varnumber_T n; - int what; + int what = 0; if (argvars[1].v_type != VAR_UNKNOWN) { base = tv_get_number(&argvars[1]); @@ -10006,6 +10006,9 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } + if (argvars[2].v_type != VAR_UNKNOWN && tv_get_number(&argvars[2])) { + what |= STR2NR_QUOTE; + } } char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); @@ -10015,20 +10018,17 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } switch (base) { case 2: { - what = STR2NR_BIN | STR2NR_FORCE; + what |= STR2NR_BIN | STR2NR_FORCE; break; } case 8: { - what = STR2NR_OCT | STR2NR_FORCE; + what |= STR2NR_OCT | STR2NR_FORCE; break; } case 16: { - what = STR2NR_HEX | STR2NR_FORCE; + what |= STR2NR_HEX | STR2NR_FORCE; break; } - default: { - what = 0; - } } vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false); // Text after the number is silently ignored. diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 4870b9a60a..084c856ba0 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -33,6 +33,24 @@ func Test_for_invalid() redraw endfunc +func Test_readfile_binary() + new + call setline(1, ['one', 'two', 'three']) + setlocal ff=dos + silent write XReadfile + let lines = readfile('XReadfile') + call assert_equal(['one', 'two', 'three'], lines) + let lines = readfile('XReadfile', '', 2) + call assert_equal(['one', 'two'], lines) + let lines = readfile('XReadfile', 'b') + call assert_equal(["one\r", "two\r", "three\r", ""], lines) + let lines = readfile('XReadfile', 'b', 2) + call assert_equal(["one\r", "two\r"], lines) + + bwipe! + call delete('XReadfile') +endfunc + func Test_mkdir_p() call mkdir('Xmkdir/nested', 'p') call assert_true(isdirectory('Xmkdir/nested')) @@ -90,6 +108,15 @@ func Test_string_concatenation() call assert_equal('ab', a) endfunc +" Test fix for issue #4507 +func Test_skip_after_throw() + try + throw 'something' + let x = wincol() || &ts + catch /something/ + endtry +endfunc + func Test_nocatch_restore_silent_emsg() silent! try throw 1 @@ -111,15 +138,6 @@ func Test_let_errmsg() let v:errmsg = '' endfunc -" Test fix for issue #4507 -func Test_skip_after_throw() - try - throw 'something' - let x = wincol() || &ts - catch /something/ - endtry -endfunc - func Test_number_max_min_size() " This will fail on systems without 64 bit number support or when not " configured correctly. diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 6cb3e24201..a78e617572 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -161,6 +161,12 @@ func Test_str2nr() call assert_equal(11259375, str2nr('0XABCDEF', 16)) call assert_equal(-11259375, str2nr('-0xABCDEF', 16)) + call assert_equal(1, str2nr("1'000'000", 10, 0)) + call assert_equal(256, str2nr("1'0000'0000", 2, 1)) + call assert_equal(262144, str2nr("1'000'000", 8, 1)) + call assert_equal(1000000, str2nr("1'000'000", 10, 1)) + call assert_equal(65536, str2nr("1'00'00", 16, 1)) + call assert_equal(0, str2nr('0x10')) call assert_equal(0, str2nr('0b10')) call assert_equal(1, str2nr('12', 2)) -- cgit From b6d9e92805b4cf37680be61c1c22ddf7a061d649 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 22 May 2021 20:21:44 +0100 Subject: vim-patch:8.1.2036: the str2nr() tests fail Problem: The str2nr() tests fail. Solution: Add missing part of patch. https://github.com/vim/vim/commit/1ac90b4fa63414d56750559506a3e076df6923b0 Add extra tests for quoted numbers in vim_str2nr_spec.lua, as the included ones in this patch are somewhat lacking. --- src/nvim/charset.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 9f11e85b01..7b08488925 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1396,6 +1396,7 @@ bool vim_isblankline(char_u *lbuf) /// If "what" contains STR2NR_OCT recognize octal numbers. /// If "what" contains STR2NR_HEX recognize hex numbers. /// If "what" contains STR2NR_FORCE always assume bin/oct/hex. +/// If "what" contains STR2NR_QUOTE ignore embedded single quotes /// If maxlen > 0, check at a maximum maxlen chars. /// If strict is true, check the number strictly. return *len = 0 if fail. /// @@ -1434,7 +1435,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, if (what & STR2NR_FORCE) { // When forcing main consideration is skipping the prefix. Octal and decimal // numbers have no prefixes to skip. pre is not set. - switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) { + switch (what & ~(STR2NR_FORCE | STR2NR_QUOTE)) { case STR2NR_HEX: { if (!STRING_ENDED(ptr + 2) && ptr[0] == '0' @@ -1504,7 +1505,18 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, abort(); // Should’ve used goto earlier. #define PARSE_NUMBER(base, cond, conv) \ do { \ - while (!STRING_ENDED(ptr) && (cond)) { \ + const char *const after_prefix = ptr; \ + while (!STRING_ENDED(ptr)) { \ + if ((what & STR2NR_QUOTE) && ptr > after_prefix && *ptr == '\'') { \ + ptr++; \ + if (!STRING_ENDED(ptr) && (cond)) { \ + continue; \ + } \ + ptr--; \ + } \ + if (!(cond)) { \ + break; \ + } \ const uvarnumber_T digit = (uvarnumber_T)(conv); \ /* avoid ubsan error for overflow */ \ if (un < UVARNUMBER_MAX / base \ -- cgit From 10018958d860780c3766f4e785710201c07f3684 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 11 Sep 2021 15:33:49 +0100 Subject: vim-patch:8.1.2043: not sufficient testing for quoted numbers Problem: Not sufficient testing for quoted numbers. Solution: Add a few more test cases. https://github.com/vim/vim/commit/ea8dcf8346f488786023fd03ec1c013cda243040 :scriptversion is N/A. Already added more quote tests in the port of v8.1.2036. --- src/nvim/testdir/test_functions.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index a78e617572..2e838f73b6 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -165,6 +165,7 @@ func Test_str2nr() call assert_equal(256, str2nr("1'0000'0000", 2, 1)) call assert_equal(262144, str2nr("1'000'000", 8, 1)) call assert_equal(1000000, str2nr("1'000'000", 10, 1)) + call assert_equal(1000, str2nr("1'000''000", 10, 1)) call assert_equal(65536, str2nr("1'00'00", 16, 1)) call assert_equal(0, str2nr('0x10')) -- cgit From 90a4cf92d21b730fea7099fb3e12a9ef791a1a57 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 3 Jun 2021 01:55:18 +0100 Subject: vim-patch:8.2.0886: cannot use octal numbers in scriptversion 4 Problem: Cannot use octal numbers in scriptversion 4. Solution: Add the "0o" notation. (Ken Takata, closes vim/vim#5304) https://github.com/vim/vim/commit/c17e66c5c0acd5038f1eb3d7b3049b64bb6ea30b :scriptversion is N/A. Cherry-pick latest str2nr() doc changes from v8.1.2035. Cherry-pick various mentions of the 0o prefix from: - v8.2.2324 - https://github.com/vim/vim/commit/2346a6378483c9871016f9fc821ec5cbea638f13 - https://github.com/vim/vim/commit/11e3c5ba820325b69cb56f70e13c21d7b8808d33 - https://github.com/vim/vim/commit/82be4849eed0b8fbee45bc8da99b685ec89af59a Patch used ascii_isbdigit() by mistake, which was fixed in v8.2.2309. Make STR2NR_OOCT work the same as STR2NR_OCT when forcing. In Vim, STR2NR_FORCE | STR2NR_OOCT isn't handled, and doesn't actually force anything. Rather than abort(), make it work as STR2NR_OCT. This means STR2NR_FORCE | STR2NR_OCT works the same as STR2NR_FORCE | STR2NR_OOCT and STR2NR_FORCE | STR2NR_OCT | STR2NR_OOCT. --- src/nvim/charset.c | 37 ++++++++++++++++++++++++++++--------- src/nvim/charset.h | 9 +++++++-- src/nvim/eval/funcs.c | 2 +- src/nvim/testdir/test_functions.vim | 5 +++++ 4 files changed, 41 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 7b08488925..94b6b7a8d7 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1385,6 +1385,8 @@ bool vim_isblankline(char_u *lbuf) /// If "prep" is not NULL, returns a flag to indicate the type of the number: /// 0 decimal /// '0' octal +/// 'O' octal +/// 'o' octal /// 'B' bin /// 'b' bin /// 'X' hex @@ -1402,8 +1404,8 @@ bool vim_isblankline(char_u *lbuf) /// /// @param start /// @param prep Returns guessed type of number 0 = decimal, 'x' or 'X' is -/// hexadecimal, '0' = octal, 'b' or 'B' is binary. When using -/// STR2NR_FORCE is always zero. +/// hexadecimal, '0', 'o' or 'O' is octal, 'b' or 'B' is binary. +/// When using STR2NR_FORCE is always zero. /// @param len Returns the detected length of number. /// @param what Recognizes what number passed, @see ChStr2NrFlags. /// @param nptr Returns the signed result. @@ -1433,8 +1435,8 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } if (what & STR2NR_FORCE) { - // When forcing main consideration is skipping the prefix. Octal and decimal - // numbers have no prefixes to skip. pre is not set. + // When forcing main consideration is skipping the prefix. Decimal numbers + // have no prefixes to skip. pre is not set. switch (what & ~(STR2NR_FORCE | STR2NR_QUOTE)) { case STR2NR_HEX: { if (!STRING_ENDED(ptr + 2) @@ -1454,7 +1456,16 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } goto vim_str2nr_bin; } - case STR2NR_OCT: { + // Make STR2NR_OOCT work the same as STR2NR_OCT when forcing. + case STR2NR_OCT: + case STR2NR_OOCT: + case STR2NR_OCT | STR2NR_OOCT: { + if (!STRING_ENDED(ptr + 2) + && ptr[0] == '0' + && (ptr[1] == 'o' || ptr[1] == 'O') + && ascii_isbdigit(ptr[2])) { + ptr += 2; + } goto vim_str2nr_oct; } case 0: { @@ -1464,9 +1475,9 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, abort(); } } - } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) - && !STRING_ENDED(ptr + 1) - && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { + } else if ((what & (STR2NR_HEX | STR2NR_OCT | STR2NR_OOCT | STR2NR_BIN)) + && !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8' + && ptr[1] != '9') { pre = ptr[1]; // Detect hexadecimal: 0x or 0X followed by hex digit. if ((what & STR2NR_HEX) @@ -1484,7 +1495,15 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, ptr += 2; goto vim_str2nr_bin; } - // Detect octal number: zero followed by octal digits without '8' or '9'. + // Detect octal: 0o or 0O followed by octal digits (without '8' or '9'). + if ((what & STR2NR_OOCT) + && !STRING_ENDED(ptr + 2) + && (pre == 'O' || pre == 'o') + && ascii_isbdigit(ptr[2])) { + ptr += 2; + goto vim_str2nr_oct; + } + // Detect old octal format: 0 followed by octal digits. pre = 0; if (!(what & STR2NR_OCT) || !('0' <= ptr[1] && ptr[1] <= '7')) { diff --git a/src/nvim/charset.h b/src/nvim/charset.h index b5f1f8506b..2fef4d78a2 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -23,14 +23,19 @@ typedef enum { STR2NR_BIN = (1 << 0), ///< Allow binary numbers. STR2NR_OCT = (1 << 1), ///< Allow octal numbers. STR2NR_HEX = (1 << 2), ///< Allow hexadecimal numbers. + STR2NR_OOCT = (1 << 3), ///< Octal with prefix "0o": 0o777 /// Force one of the above variants. /// /// STR2NR_FORCE|STR2NR_DEC is actually not different from supplying zero /// as flags, but still present for completeness. + /// + /// STR2NR_FORCE|STR2NR_OCT|STR2NR_OOCT is the same as STR2NR_FORCE|STR2NR_OCT + /// or STR2NR_FORCE|STR2NR_OOCT. STR2NR_FORCE = (1 << 7), /// Recognize all formats vim_str2nr() can recognize. - STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX, - STR2NR_NO_OCT = STR2NR_BIN | STR2NR_HEX, ///< Disallow octal numbers. + STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX | STR2NR_OOCT, + /// Disallow octals numbers without the 0o prefix. + STR2NR_NO_OCT = STR2NR_BIN | STR2NR_HEX | STR2NR_OOCT, STR2NR_QUOTE = (1 << 4), ///< Ignore embedded single quotes. } ChStr2NrFlags; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 61f535e32e..801b0f9d1c 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -10022,7 +10022,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } case 8: { - what |= STR2NR_OCT | STR2NR_FORCE; + what |= STR2NR_OCT | STR2NR_OOCT | STR2NR_FORCE; break; } case 16: { diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 2e838f73b6..ed46730cbb 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -152,6 +152,10 @@ func Test_str2nr() call assert_equal(65, str2nr('0101', 8)) call assert_equal(-65, str2nr('-101', 8)) call assert_equal(-65, str2nr('-0101', 8)) + call assert_equal(65, str2nr('0o101', 8)) + call assert_equal(65, str2nr('0O0101', 8)) + call assert_equal(-65, str2nr('-0O101', 8)) + call assert_equal(-65, str2nr('-0o0101', 8)) call assert_equal(11259375, str2nr('abcdef', 16)) call assert_equal(11259375, str2nr('ABCDEF', 16)) @@ -170,6 +174,7 @@ func Test_str2nr() call assert_equal(0, str2nr('0x10')) call assert_equal(0, str2nr('0b10')) + call assert_equal(0, str2nr('0o10')) call assert_equal(1, str2nr('12', 2)) call assert_equal(1, str2nr('18', 8)) call assert_equal(1, str2nr('1g', 16)) -- cgit From 26733dd488874fee8dfc70d54167f427d5f50516 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 3 Jun 2021 02:45:36 +0100 Subject: vim-patch:8.2.2309: 0o777 not recognized as octal Problem: 0o777 not recognized as octal. Solution: Use vim_isodigit(). (Ken Takata, closes vim/vim#7633, closes vim/vim#7631) https://github.com/vim/vim/commit/c37b655443e0a11a77a9f0707e3259ab4b8b3dda :scriptversion is N/A. --- src/nvim/ascii.h | 8 ++++++++ src/nvim/charset.c | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index f41068ea70..7b5e82cd3f 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -169,6 +169,14 @@ static inline bool ascii_isbdigit(int c) return (c == '0' || c == '1'); } +/// Checks if `c` is an octal digit, that is, 0-7. +/// +/// @see {ascii_isdigit} +static inline bool ascii_isodigit(int c) +{ + return (c >= '0' && c <= '7'); +} + /// Checks if `c` is a white-space character, that is, /// one of \f, \n, \r, \t, \v. /// diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 94b6b7a8d7..ab4e4ad4bd 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1463,7 +1463,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, if (!STRING_ENDED(ptr + 2) && ptr[0] == '0' && (ptr[1] == 'o' || ptr[1] == 'O') - && ascii_isbdigit(ptr[2])) { + && ascii_isodigit(ptr[2])) { ptr += 2; } goto vim_str2nr_oct; @@ -1499,14 +1499,14 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, if ((what & STR2NR_OOCT) && !STRING_ENDED(ptr + 2) && (pre == 'O' || pre == 'o') - && ascii_isbdigit(ptr[2])) { + && ascii_isodigit(ptr[2])) { ptr += 2; goto vim_str2nr_oct; } // Detect old octal format: 0 followed by octal digits. pre = 0; if (!(what & STR2NR_OCT) - || !('0' <= ptr[1] && ptr[1] <= '7')) { + || !ascii_isodigit(ptr[1])) { goto vim_str2nr_dec; } for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) { @@ -1552,7 +1552,7 @@ vim_str2nr_bin: PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0')); goto vim_str2nr_proceed; vim_str2nr_oct: - PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0')); + PARSE_NUMBER(8, (ascii_isodigit(*ptr)), (*ptr - '0')); goto vim_str2nr_proceed; vim_str2nr_dec: PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0')); -- cgit From 7175efb518d09aad59f7917c15b7c9752e9e320e Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 11 Sep 2021 15:28:24 +0100 Subject: fix(get_showbreak_value): remove FUNC_ATTR_UNUSED --- src/nvim/option.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/option.c b/src/nvim/option.c index 06389c0ef1..23b488ea68 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -7486,7 +7486,7 @@ unsigned int get_bkc_value(buf_T *buf) /// /// @param win If not NULL, the window to get the local option from; global /// otherwise. -char_u *get_showbreak_value(win_T *const win FUNC_ATTR_UNUSED) +char_u *get_showbreak_value(win_T *const win) FUNC_ATTR_WARN_UNUSED_RESULT { if (win->w_p_sbr == NULL || *win->w_p_sbr == NUL) { -- cgit