mirror of
https://github.com/rizinorg/rizin.git
synced 2026-10-12 02:21:57 +00:00
rz-util: skip the expression parser for bare integer literals
This commit is contained in:
committed by
NOT XVilka
parent
78b903c5f7
commit
f5bb3a9a41
@@ -449,6 +449,8 @@ typedef enum {
|
||||
|
||||
RZ_API RzNumBasePrefix rz_num_base_prefix(RZ_NONNULL const char *p, RZ_NULLABLE RZ_OUT ut32 *base,
|
||||
RZ_NULLABLE RZ_OUT size_t *prefix_len);
|
||||
RZ_API bool rz_num_is_int_literal(RZ_NONNULL const char *str, RZ_NULLABLE RZ_OUT ut64 *value,
|
||||
RZ_NULLABLE RZ_OUT bool *overflow);
|
||||
|
||||
/**
|
||||
* \brief Absolute value of a 64-bit number. Store result in `ut64`
|
||||
|
||||
+20
-35
@@ -216,45 +216,30 @@ static bool literal_is_float(const char *t) {
|
||||
|
||||
// Parse an unsigned integer literal. Returns true on success. On
|
||||
// ERANGE, sets *overflow=true so the caller can promote to bignum.
|
||||
// Thin wrapper over rz_num_is_int_literal() adding the evaluator's own
|
||||
// diagnostics. The three outcomes the caller distinguishes are: nothing after
|
||||
// the base prefix, no valid digit at all, and a digit run followed by a digit
|
||||
// invalid for the base (the '8' in 08 / 0187, which the grammar's permissive
|
||||
// decimal digit class lets through to here). The last one leaves *err unset so
|
||||
// the caller categorises it as a plain parse error.
|
||||
static bool parse_int_literal(const char *t, ut64 *out, bool *overflow, char **err) {
|
||||
const char *p = t;
|
||||
if (rz_num_is_int_literal(t, out, overflow)) {
|
||||
return true;
|
||||
}
|
||||
if (!err || *err || (overflow && *overflow)) {
|
||||
return false;
|
||||
}
|
||||
ut32 base = 10;
|
||||
size_t prefix_len = 0;
|
||||
// A C style leading zero reports prefix_len 0: the zero is an octal
|
||||
// digit, so strtoull() must still see it.
|
||||
rz_num_base_prefix(p, &base, &prefix_len);
|
||||
p += prefix_len;
|
||||
if (!*p) {
|
||||
if (err && !*err) {
|
||||
*err = rz_str_newf("empty integer literal: %s", t);
|
||||
}
|
||||
return false;
|
||||
rz_num_base_prefix(t, &base, &prefix_len);
|
||||
const char *digits = t + prefix_len;
|
||||
ut8 first = 0;
|
||||
if (!*digits) {
|
||||
*err = rz_str_newf("empty integer literal: %s", t);
|
||||
} else if (rz_hex_to_byte(&first, (ut8)*digits) || first >= base) {
|
||||
*err = rz_str_newf("not an integer: %s", t);
|
||||
}
|
||||
errno = 0;
|
||||
char *endp = NULL;
|
||||
unsigned long long v = strtoull(p, &endp, base);
|
||||
if (errno == ERANGE) {
|
||||
if (overflow) {
|
||||
*overflow = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (endp == p) {
|
||||
if (err && !*err) {
|
||||
*err = rz_str_newf("not an integer: %s", t);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (*endp != '\0') {
|
||||
// Trailing junk after the digit run: a digit not valid for the
|
||||
// detected base. This catches C-style octal with an out-of-range
|
||||
// digit (the '8' in 08 / 0187), which the permissive decimal
|
||||
// digit class in the grammar lets through to here. Leave *err
|
||||
// unset so the caller can categorise it as a parse error.
|
||||
return false;
|
||||
}
|
||||
*out = (ut64)v;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool parse_float_literal(const char *t, double *out, char **err) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2007-2020 pancake <[email protected]>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <errno.h>
|
||||
#include <math.h> /* for ceill */
|
||||
#include <rz_util.h>
|
||||
|
||||
@@ -580,6 +581,59 @@ static bool num_is_flagname_token(const char *str) {
|
||||
return has_colon;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check whether \p str is a bare integer literal, and parse it.
|
||||
*
|
||||
* A bare integer literal is an optional base prefix (see \ref rz_num_base_prefix)
|
||||
* followed only by digits valid in that base, and nothing else. Signs,
|
||||
* whitespace, operators, radix points, exponents, bit-vector widths, unit
|
||||
* suffixes and the deprecated trailing base suffixes all make \p str not a
|
||||
* bare literal.
|
||||
*
|
||||
* This is the cheap path around rz_num_math(): it needs no expression parser.
|
||||
*
|
||||
* \param str Text to examine.
|
||||
* \param value Set to the parsed value when the function returns true.
|
||||
* \param overflow Set to true when \p str is a valid integer literal too large
|
||||
* for a ut64. The function still returns false; a caller that
|
||||
* supports arbitrary precision can use this to promote it.
|
||||
*
|
||||
* \return true if \p str is a bare integer literal that fits in a ut64.
|
||||
*/
|
||||
RZ_API bool rz_num_is_int_literal(RZ_NONNULL const char *str, RZ_NULLABLE RZ_OUT ut64 *value,
|
||||
RZ_NULLABLE RZ_OUT bool *overflow) {
|
||||
rz_return_val_if_fail(str, false);
|
||||
ut32 base = 10;
|
||||
size_t prefix_len = 0;
|
||||
rz_num_base_prefix(str, &base, &prefix_len);
|
||||
const char *digits = str + prefix_len;
|
||||
if (!*digits) {
|
||||
return false;
|
||||
}
|
||||
for (const char *p = digits; *p; p++) {
|
||||
ut8 v = 0;
|
||||
if (rz_hex_to_byte(&v, (ut8)*p) || v >= base) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
errno = 0;
|
||||
char *endp = NULL;
|
||||
unsigned long long v = strtoull(digits, &endp, base);
|
||||
if (errno == ERANGE) {
|
||||
if (overflow) {
|
||||
*overflow = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (*endp) {
|
||||
return false;
|
||||
}
|
||||
if (value) {
|
||||
*value = (ut64)v;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Compute a numerical expression yielding a ut64.
|
||||
*
|
||||
@@ -621,6 +675,16 @@ RZ_API ut64 rz_num_math_ut64(RzNum *num, const char *str) {
|
||||
num->nc.calc_buf = str;
|
||||
}
|
||||
|
||||
ut64 plain = 0;
|
||||
// Bare integer literals are the common input here and need no
|
||||
// expression parser; see rz_num_is_int_literal().
|
||||
if (rz_num_is_int_literal(str, &plain, NULL)) {
|
||||
if (num) {
|
||||
num->value = plain;
|
||||
}
|
||||
return plain;
|
||||
}
|
||||
|
||||
// Legacy `[addr]` dereference. The bracket read predates the typed
|
||||
// `addr:width` syntax but is still widely used - both on its own
|
||||
// (`s [ptr]`) and as a sub-term (`[0]/2`, `5+[0]`, `[0]+[4]`) - and
|
||||
|
||||
Reference in New Issue
Block a user