aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/charset.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/charset.c')
-rw-r--r--src/nvim/charset.c75
1 files changed, 60 insertions, 15 deletions
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;
}