mirror of
https://github.com/neovim/neovim.git
synced 2024-12-27 14:21:31 -07:00
27a7a4d384
assert() is compiled out for release builds, but we don't want to continue running in these impossible situations. This also resolves the "implicit fallthrough" warnings for the asserts in switch cases.
173 lines
4.1 KiB
C
173 lines
4.1 KiB
C
#include <stdbool.h>
|
||
|
||
#include "nvim/ascii.h"
|
||
#include "nvim/macros.h"
|
||
#include "nvim/charset.h"
|
||
#include "nvim/eval/typval.h"
|
||
#include "nvim/vim.h"
|
||
|
||
int hex2nr(int c)
|
||
{
|
||
if ((c >= 'a') && (c <= 'f')) {
|
||
return c - 'a' + 10;
|
||
}
|
||
|
||
if ((c >= 'A') && (c <= 'F')) {
|
||
return c - 'A' + 10;
|
||
}
|
||
return c - '0';
|
||
}
|
||
|
||
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)
|
||
{
|
||
const char *ptr = (const char *)start;
|
||
#define STRING_ENDED(ptr) \
|
||
(!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
|
||
int pre = 0; // default is decimal
|
||
const bool negative = (ptr[0] == '-');
|
||
uvarnumber_T un = 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)) {
|
||
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: {
|
||
abort();
|
||
}
|
||
}
|
||
} 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 followed by hex digit
|
||
if ((what & STR2NR_HEX)
|
||
&& !STRING_ENDED(ptr + 2)
|
||
&& (pre == 'X' || pre == 'x')
|
||
&& ascii_isxdigit(ptr[2])) {
|
||
ptr += 2;
|
||
goto vim_str2nr_hex;
|
||
}
|
||
// Detect binary: 0b or 0B followed by 0 or 1
|
||
if ((what & STR2NR_BIN)
|
||
&& !STRING_ENDED(ptr + 2)
|
||
&& (pre == 'B' || pre == 'b')
|
||
&& ascii_isbdigit(ptr[2])) {
|
||
ptr += 2;
|
||
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.
|
||
abort(); // Should’ve used goto earlier.
|
||
#define PARSE_NUMBER(base, cond, conv) \
|
||
do { \
|
||
while (!STRING_ENDED(ptr) && (cond)) { \
|
||
/* avoid ubsan error for overflow */ \
|
||
if (un < UVARNUMBER_MAX / base) { \
|
||
un = base * un + (uvarnumber_T)(conv); \
|
||
} else { \
|
||
un = UVARNUMBER_MAX; \
|
||
} \
|
||
ptr++; \
|
||
} \
|
||
} while (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
|
||
|
||
if (prep != NULL) {
|
||
*prep = pre;
|
||
}
|
||
|
||
if (len != NULL) {
|
||
*len = (int)(ptr - (const char *)start);
|
||
}
|
||
|
||
if (nptr != NULL) {
|
||
if (negative) { // account for leading '-' for decimal numbers
|
||
// avoid ubsan error for overflow
|
||
if (un > VARNUMBER_MAX) {
|
||
*nptr = VARNUMBER_MIN;
|
||
} else {
|
||
*nptr = -(varnumber_T)un;
|
||
}
|
||
} else {
|
||
if (un > VARNUMBER_MAX) {
|
||
un = VARNUMBER_MAX;
|
||
}
|
||
*nptr = (varnumber_T)un;
|
||
}
|
||
}
|
||
|
||
if (unptr != NULL) {
|
||
*unptr = un;
|
||
}
|
||
#undef STRING_ENDED
|
||
}
|