aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/ascii.h8
-rw-r--r--src/nvim/charset.c75
-rw-r--r--src/nvim/charset.h11
-rw-r--r--src/nvim/eval.c35
-rw-r--r--src/nvim/eval.lua2
-rw-r--r--src/nvim/eval/decode.c6
-rw-r--r--src/nvim/eval/funcs.c17
-rw-r--r--src/nvim/eval/typval.c3
-rw-r--r--src/nvim/ex_cmds.c2
-rw-r--r--src/nvim/ex_getln.c4
-rw-r--r--src/nvim/keymap.c12
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c8
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim36
-rw-r--r--src/nvim/testdir/test_expr.vim11
-rw-r--r--src/nvim/testdir/test_functions.vim12
-rw-r--r--src/nvim/viml/parser/expressions.c4
17 files changed, 181 insertions, 67 deletions
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 4725c0d08f..ab4e4ad4bd 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
@@ -1396,20 +1398,25 @@ 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.
///
/// @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.
/// @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,14 +1426,18 @@ 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++;
}
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)) {
+ // 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)
&& ptr[0] == '0'
@@ -1445,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_isodigit(ptr[2])) {
+ ptr += 2;
+ }
goto vim_str2nr_oct;
}
case 0: {
@@ -1455,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)
@@ -1475,10 +1495,18 @@ 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_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++) {
@@ -1492,11 +1520,22 @@ 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 { \
- 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 \
@@ -1513,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'));
@@ -1524,6 +1563,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/charset.h b/src/nvim/charset.h
index e657ce19b6..2fef4d78a2 100644
--- a/src/nvim/charset.h
+++ b/src/nvim/charset.h
@@ -23,13 +23,20 @@ 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 = (1 << 3),
+ ///
+ /// 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_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;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 5603fbb082..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), &ltv, false, is_const,
- (char_u *)"]", nextchars);
+ arg = ex_let_one(skipwhite(arg + 1), &ltv, false, is_const, (char_u *)"]",
+ op);
tv_clear(&ltv);
if (arg == NULL) {
return FAIL;
@@ -3950,7 +3947,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;
@@ -8670,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))))
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/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..801b0f9d1c 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,22 +10018,20 @@ 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_OOCT | 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);
+ 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])) {
// <Char-123> or <Char-033> or <Char-0x33>
- 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..23b488ea68 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 {
@@ -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) {
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_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 <Char-0b1z> b', 'E474:')
+endfunc
+
func Test_eval_after_if()
let s:val = ''
func SetVal(x)
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 6cb3e24201..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))
@@ -161,8 +165,16 @@ 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(1000, 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(0, str2nr('0o10'))
call assert_equal(1, str2nr('12', 2))
call assert_equal(1, str2nr('18', 8))
call assert_equal(1, str2nr('1g', 16))
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,