| /* SPDX-License-Identifier: BSD-2-Clause */ |
| /* Copyright 1996-2025 The NASM Authors - All Rights Reserved */ |
| |
| /* |
| * nasmlib.c library routines for the Netwide Assembler |
| */ |
| |
| #include "compiler.h" |
| |
| #include "nctype.h" |
| |
| #include "nasmlib.h" |
| #include "error.h" |
| #include "nasm.h" /* For globl.dollarhex */ |
| |
| #define lib_isnumchar(c) (nasm_isalnum(c) || (c) == '$' || (c) == '_') |
| |
| void warn_dollar_hex(void) |
| { |
| nasm_warn(WARN_NUMBER_DEPRECATED_HEX, |
| "$ prefix for hexadecimal is deprecated"); |
| } |
| |
| int64_t readnum(const char *str, bool *error) |
| { |
| const char *r = str, *q; |
| int32_t pradix, sradix, radix; |
| int plen, slen, len; |
| uint64_t result, checklimit; |
| int digit, last; |
| bool warn = false; |
| int sign = 1; |
| |
| if (error) |
| *error = true; |
| |
| while (nasm_isspace(*r)) |
| r++; /* find start of number */ |
| |
| /* |
| * If the number came from make_tok_num (as a result of an %assign), it |
| * might have a '-' built into it (rather than in a preceding token). |
| */ |
| if (*r == '-') { |
| r++; |
| sign = -1; |
| } |
| |
| q = r; |
| |
| while (lib_isnumchar(*q)) |
| q++; /* find end of number */ |
| |
| len = q-r; |
| if (!len) { |
| /* Not numeric */ |
| return 0; |
| } |
| |
| /* |
| * Handle radix formats: |
| * |
| * 0<radix-letter><string> |
| * $<string> (hexadecimal) |
| * <string><radix-letter> |
| */ |
| pradix = sradix = 0; |
| plen = slen = 0; |
| |
| if (len > 2 && *r == '0' && (pradix = radix_letter(r[1])) != 0) { |
| plen = 2; |
| } else if (len > 1 && *r == '$' && globl.dollarhex) { |
| /* Warning here would probably duplicate warnings */ |
| pradix = 16, plen = 1; |
| } |
| |
| if (len > 1 && (sradix = radix_letter(q[-1])) != 0) |
| slen = 1; |
| |
| if (pradix > sradix) { |
| radix = pradix; |
| r += plen; |
| } else if (sradix > pradix) { |
| radix = sradix; |
| q -= slen; |
| } else { |
| /* Either decimal, or invalid -- if invalid, we'll trip up |
| further down. */ |
| radix = 10; |
| } |
| |
| /* |
| * `checklimit' must be 2**64 / radix. We can't do that in |
| * 64-bit arithmetic, which we're (probably) using, so we |
| * cheat: since we know that all radices we use are even, we |
| * can divide 2**63 by radix/2 instead. |
| */ |
| checklimit = UINT64_C(0x8000000000000000) / (radix >> 1); |
| |
| /* |
| * Calculate the highest allowable value for the last digit of a |
| * 64-bit constant... in radix 10, it is 6, otherwise it is 0 |
| */ |
| last = (radix == 10 ? 6 : 0); |
| |
| result = 0; |
| while (*r && r < q) { |
| if (*r != '_') { |
| if (*r < '0' || (*r > '9' && *r < 'A') |
| || (digit = numvalue(*r)) >= radix) { |
| return 0; |
| } |
| if (result > checklimit || |
| (result == checklimit && digit >= last)) { |
| warn = true; |
| } |
| |
| result = radix * result + digit; |
| } |
| r++; |
| } |
| |
| if (warn) { |
| nasm_warn(WARN_NUMBER_OVERFLOW, |
| "numeric constant %s does not fit in 64 bits", |
| str); |
| } |
| |
| if (error) |
| *error = false; |
| return result * sign; |
| } |