aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/charset.c135
-rw-r--r--test/symbolic/klee/nvim/charset.c129
-rw-r--r--test/unit/charset/vim_str2nr_spec.lua1
3 files changed, 173 insertions, 92 deletions
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index c895d65eb7..5aebf90194 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -1611,8 +1611,9 @@ bool vim_isblankline(char_u *lbuf)
/// If maxlen > 0, check at a maximum maxlen chars.
///
/// @param start
-/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex,
-/// '0' = octal, 'b' or 'B' is bin
+/// @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.
/// @param len Returns the detected length of number.
/// @param what Recognizes what number passed.
/// @param nptr Returns the signed result.
@@ -1627,55 +1628,84 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
#define STRING_ENDED(ptr) \
(!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
int pre = 0; // default is decimal
- bool negative = false;
+ const bool negative = (ptr[0] == '-');
+ uvarnumber_T un = 0;
- if (ptr[0] == '-') {
- negative = true;
+ if (negative) {
ptr++;
}
- // Recognize hex, octal and bin.
- if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
- && !STRING_ENDED(ptr + 1)
- && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
+ 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)) {
+ case STR2NR_HEX: {
+ if (!STRING_ENDED(ptr + 2)
+ && ptr[0] == '0'
+ && (ptr[1] == 'x' || ptr[1] == 'X')
+ && ascii_isxdigit(ptr[2])) {
+ ptr += 2;
+ }
+ goto vim_str2nr_hex;
+ }
+ case STR2NR_BIN: {
+ if (!STRING_ENDED(ptr + 2)
+ && ptr[0] == '0'
+ && (ptr[1] == 'b' || ptr[1] == 'B')
+ && ascii_isbdigit(ptr[2])) {
+ ptr += 2;
+ }
+ goto vim_str2nr_bin;
+ }
+ case STR2NR_OCT: {
+ goto vim_str2nr_oct;
+ }
+ case 0: {
+ goto vim_str2nr_dec;
+ }
+ default: {
+ assert(false);
+ }
+ }
+ } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
+ && !STRING_ENDED(ptr + 1)
+ && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
pre = ptr[1];
-
+ // Detect hexadecimal: 0x or 0X follwed by hex digit
if ((what & STR2NR_HEX)
&& !STRING_ENDED(ptr + 2)
&& (pre == 'X' || pre == 'x')
&& ascii_isxdigit(ptr[2])) {
- // hexadecimal
ptr += 2;
- } else if ((what & STR2NR_BIN)
- && !STRING_ENDED(ptr + 2)
- && (pre == 'B' || pre == 'b')
- && ascii_isbdigit(ptr[2])) {
- // binary
+ goto vim_str2nr_hex;
+ }
+ // Detect binary: 0b or 0B follwed by 0 or 1
+ if ((what & STR2NR_BIN)
+ && !STRING_ENDED(ptr + 2)
+ && (pre == 'B' || pre == 'b')
+ && ascii_isbdigit(ptr[2])) {
ptr += 2;
- } else {
- // decimal or octal, default is decimal
- pre = 0;
-
- if (what & STR2NR_OCT
- && !STRING_ENDED(ptr + 1)
- && ('0' <= ptr[1] && ptr[1] <= '7')) {
- // Assume octal now: what we already know is that string starts with
- // zero and some octal digit.
- pre = '0';
- // Don’t interpret "0", "008" or "0129" as octal.
- for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
- if (ptr[i] > '7') {
- // Can’t be octal.
- pre = 0;
- break;
- }
- }
+ goto vim_str2nr_bin;
+ }
+ // Detect octal number: zero followed by octal digits without '8' or '9'
+ pre = 0;
+ if (!(what & STR2NR_OCT)
+ || !('0' <= ptr[1] && ptr[1] <= '7')) {
+ goto vim_str2nr_dec;
+ }
+ for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
+ if (ptr[i] > '7') {
+ goto vim_str2nr_dec;
}
}
+ pre = '0';
+ goto vim_str2nr_oct;
+ } else {
+ goto vim_str2nr_dec;
}
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
- uvarnumber_T un = 0;
+ assert(false); // Should’ve used goto earlier.
#define PARSE_NUMBER(base, cond, conv) \
do { \
while (!STRING_ENDED(ptr) && (cond)) { \
@@ -1688,18 +1718,29 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
ptr++; \
} \
} while (0)
- if (pre == 'B' || pre == 'b' || what == (STR2NR_BIN|STR2NR_FORCE)) {
- // Binary number.
- PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0'));
- } else if (pre == '0' || what == (STR2NR_OCT|STR2NR_FORCE)) {
- // Octal number.
- PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0'));
- } else if (pre == 'X' || pre == 'x' || what == (STR2NR_HEX|STR2NR_FORCE)) {
- // Hexadecimal number.
- PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr)));
- } else {
- // Decimal number.
- PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
+ switch (pre) {
+ case 'b':
+ case 'B': {
+vim_str2nr_bin:
+ PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0'));
+ break;
+ }
+ case '0': {
+vim_str2nr_oct:
+ PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0'));
+ break;
+ }
+ case 0: {
+vim_str2nr_dec:
+ PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
+ break;
+ }
+ case 'x':
+ case 'X': {
+vim_str2nr_hex:
+ PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr)));
+ break;
+ }
}
#undef PARSE_NUMBER
diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c
index f3a218949f..77f690f08d 100644
--- a/test/symbolic/klee/nvim/charset.c
+++ b/test/symbolic/klee/nvim/charset.c
@@ -31,55 +31,83 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
#define STRING_ENDED(ptr) \
(!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
int pre = 0; // default is decimal
- bool negative = false;
+ const bool negative = (ptr[0] == '-');
+ uvarnumber_T un = 0;
- if (ptr[0] == '-') {
- negative = true;
+ if (negative) {
ptr++;
}
- // Recognize hex, octal and bin.
- if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
- && !STRING_ENDED(ptr + 1)
- && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
+ 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)) {
+ case STR2NR_HEX: {
+ if (!STRING_ENDED(ptr + 2)
+ && ptr[0] == '0'
+ && (ptr[1] == 'x' || ptr[1] == 'X')
+ && ascii_isxdigit(ptr[2])) {
+ ptr += 2;
+ }
+ goto vim_str2nr_hex;
+ }
+ case STR2NR_BIN: {
+ if (!STRING_ENDED(ptr + 2)
+ && ptr[0] == '0'
+ && (ptr[1] == 'b' || ptr[1] == 'B')
+ && ascii_isbdigit(ptr[2])) {
+ ptr += 2;
+ }
+ goto vim_str2nr_bin;
+ }
+ case STR2NR_OCT: {
+ goto vim_str2nr_oct;
+ }
+ case 0: {
+ goto vim_str2nr_dec;
+ }
+ default: {
+ assert(false);
+ }
+ }
+ } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
+ && !STRING_ENDED(ptr + 1)
+ && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
pre = ptr[1];
-
+ // Detect hexadecimal: 0x or 0X follwed by hex digit
if ((what & STR2NR_HEX)
&& !STRING_ENDED(ptr + 2)
&& (pre == 'X' || pre == 'x')
&& ascii_isxdigit(ptr[2])) {
- // hexadecimal
ptr += 2;
- } else if ((what & STR2NR_BIN)
- && !STRING_ENDED(ptr + 2)
- && (pre == 'B' || pre == 'b')
- && ascii_isbdigit(ptr[2])) {
- // binary
+ goto vim_str2nr_hex;
+ }
+ // Detect binary: 0b or 0B follwed by 0 or 1
+ if ((what & STR2NR_BIN)
+ && !STRING_ENDED(ptr + 2)
+ && (pre == 'B' || pre == 'b')
+ && ascii_isbdigit(ptr[2])) {
ptr += 2;
- } else {
- // decimal or octal, default is decimal
- pre = 0;
-
- if (what & STR2NR_OCT
- && !STRING_ENDED(ptr + 1)
- && ('0' <= ptr[1] && ptr[1] <= '7')) {
- // Assume octal now: what we already know is that string starts with
- // zero and some octal digit.
- pre = '0';
- // Don’t interpret "0", "008" or "0129" as octal.
- for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
- if (ptr[i] > '7') {
- // Can’t be octal.
- pre = 0;
- break;
- }
- }
+ goto vim_str2nr_bin;
+ }
+ // Detect octal number: zero followed by octal digits without '8' or '9'
+ pre = 0;
+ if (!(what & STR2NR_OCT)) {
+ goto vim_str2nr_dec;
+ }
+ for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) {
+ if (ptr[i] > '7') {
+ goto vim_str2nr_dec;
}
}
+ pre = '0';
+ goto vim_str2nr_oct;
+ } else {
+ goto vim_str2nr_dec;
}
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
- uvarnumber_T un = 0;
+ assert(false); // Should’ve used goto earlier.
#define PARSE_NUMBER(base, cond, conv) \
do { \
while (!STRING_ENDED(ptr) && (cond)) { \
@@ -92,18 +120,29 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
ptr++; \
} \
} while (0)
- if (pre == 'B' || pre == 'b' || what == (STR2NR_BIN|STR2NR_FORCE)) {
- // Binary number.
- PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0'));
- } else if (pre == '0' || what == (STR2NR_OCT|STR2NR_FORCE)) {
- // Octal number.
- PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0'));
- } else if (pre == 'X' || pre == 'x' || what == (STR2NR_HEX|STR2NR_FORCE)) {
- // Hexadecimal number.
- PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr)));
- } else {
- // Decimal number.
- PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
+ switch (pre) {
+ case 'b':
+ case 'B': {
+vim_str2nr_bin:
+ PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0'));
+ break;
+ }
+ case '0': {
+vim_str2nr_oct:
+ PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0'));
+ break;
+ }
+ case 0: {
+vim_str2nr_dec:
+ PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
+ break;
+ }
+ case 'x':
+ case 'X': {
+vim_str2nr_hex:
+ PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr)));
+ break;
+ }
}
#undef PARSE_NUMBER
diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua
new file mode 100644
index 0000000000..cfbc77bc2a
--- /dev/null
+++ b/test/unit/charset/vim_str2nr_spec.lua
@@ -0,0 +1 @@
+-- FIXME