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.c136
1 files changed, 50 insertions, 86 deletions
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 577fc13a31..c895d65eb7 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -1621,11 +1621,13 @@ bool vim_isblankline(char_u *lbuf)
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)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- const char_u *ptr = start;
+ const char *ptr = (const char *)start;
+#define STRING_ENDED(ptr) \
+ (!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen))
int pre = 0; // default is decimal
bool negative = false;
- uvarnumber_T un = 0;
if (ptr[0] == '-') {
negative = true;
@@ -1633,119 +1635,80 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
}
// Recognize hex, octal and bin.
- if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9')
- && (maxlen == 0 || maxlen > 1)) {
+ if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
+ && !STRING_ENDED(ptr + 1)
+ && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') {
pre = ptr[1];
if ((what & STR2NR_HEX)
- && ((pre == 'X') || (pre == 'x'))
- && ascii_isxdigit(ptr[2])
- && (maxlen == 0 || maxlen > 2)) {
+ && !STRING_ENDED(ptr + 2)
+ && (pre == 'X' || pre == 'x')
+ && ascii_isxdigit(ptr[2])) {
// hexadecimal
ptr += 2;
} else if ((what & STR2NR_BIN)
- && ((pre == 'B') || (pre == 'b'))
- && ascii_isbdigit(ptr[2])
- && (maxlen == 0 || maxlen > 2)) {
+ && !STRING_ENDED(ptr + 2)
+ && (pre == 'B' || pre == 'b')
+ && ascii_isbdigit(ptr[2])) {
// binary
ptr += 2;
} else {
// decimal or octal, default is decimal
pre = 0;
- if (what & STR2NR_OCT) {
- // Don't interpret "0", "08" or "0129" as octal.
- for (int n = 1; ascii_isdigit(ptr[n]); ++n) {
- if (ptr[n] > '7') {
- // can't be octal
+ 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;
}
- if (ptr[n] >= '0') {
- // assume octal
- pre = '0';
- }
- if (n == maxlen) {
- break;
- }
}
}
}
}
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
- int n = 1;
- if ((pre == 'B') || (pre == 'b') || what == STR2NR_BIN + STR2NR_FORCE) {
- // bin
- if (pre != 0) {
- n += 2; // skip over "0b"
- }
- while ('0' <= *ptr && *ptr <= '1') {
- // avoid ubsan error for overflow
- if (un < UVARNUMBER_MAX / 2) {
- un = 2 * un + (uvarnumber_T)(*ptr - '0');
- } else {
- un = UVARNUMBER_MAX;
- }
- ptr++;
- if (n++ == maxlen) {
- break;
- }
- }
- } else if ((pre == '0') || what == STR2NR_OCT + STR2NR_FORCE) {
- // octal
- while ('0' <= *ptr && *ptr <= '7') {
- // avoid ubsan error for overflow
- if (un < UVARNUMBER_MAX / 8) {
- un = 8 * un + (uvarnumber_T)(*ptr - '0');
- } else {
- un = UVARNUMBER_MAX;
- }
- ptr++;
- if (n++ == maxlen) {
- break;
- }
- }
- } else if ((pre == 'X') || (pre == 'x')
- || what == STR2NR_HEX + STR2NR_FORCE) {
- // hex
- if (pre != 0) {
- n += 2; // skip over "0x"
- }
- while (ascii_isxdigit(*ptr)) {
- // avoid ubsan error for overflow
- if (un < UVARNUMBER_MAX / 16) {
- un = 16 * un + (uvarnumber_T)hex2nr(*ptr);
- } else {
- un = UVARNUMBER_MAX;
- }
- ptr++;
- if (n++ == maxlen) {
- break;
- }
- }
+ uvarnumber_T un = 0;
+#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)
+ 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
- while (ascii_isdigit(*ptr)) {
- // avoid ubsan error for overflow
- if (un < UVARNUMBER_MAX / 10) {
- un = 10 * un + (uvarnumber_T)(*ptr - '0');
- } else {
- un = UVARNUMBER_MAX;
- }
- ptr++;
- if (n++ == maxlen) {
- break;
- }
- }
+ // Decimal number.
+ PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0'));
}
+#undef PARSE_NUMBER
if (prep != NULL) {
*prep = pre;
}
if (len != NULL) {
- *len = (int)(ptr - start);
+ *len = (int)(ptr - (const char *)start);
}
if (nptr != NULL) {
@@ -1767,6 +1730,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
if (unptr != NULL) {
*unptr = un;
}
+#undef STRING_ENDED
}
/// Return the value of a single hex character.