mirror of
https://github.com/ctm/executor.git
synced 2024-06-10 10:29:31 +00:00
191 lines
5.0 KiB
C
191 lines
5.0 KiB
C
/* Copyright 1995 by Abacus Research and
|
|
* Development, Inc. All rights reserved.
|
|
*/
|
|
|
|
#if !defined (OMIT_RCSID_STRINGS)
|
|
char ROMlib_rcsid_parsenum[] =
|
|
"$Id: parsenum.c 63 2004-12-24 18:19:43Z ctm $";
|
|
#endif
|
|
|
|
#include "rsys/common.h"
|
|
#include "rsys/parsenum.h"
|
|
#include <ctype.h>
|
|
|
|
|
|
/* If the given string has a 'k', 'K', 'm', or 'M' suffix, returns the
|
|
* log base 2 corresponding to that size (either 10 or 20) and removes
|
|
* the suffix from the string. If no suffix is found, returns 0.
|
|
*/
|
|
static int
|
|
fetch_and_remove_shift_suffix (char *num)
|
|
{
|
|
char *end;
|
|
int shift, last;
|
|
|
|
end = &num[strlen (num) - 1];
|
|
last = *end;
|
|
if (islower (last)) /* Check not actually needed under ANSI C. */
|
|
last = toupper (last);
|
|
|
|
if (last == 'K')
|
|
shift = 10; /* 1K == 1 << 10 */
|
|
else if (last == 'M')
|
|
shift = 20; /* 1M == 1 << 20 */
|
|
else
|
|
shift = 0;
|
|
|
|
if (shift)
|
|
*end = '\0';
|
|
|
|
return shift;
|
|
}
|
|
|
|
|
|
/* Parses the given input string as a number of base strlen(digits)
|
|
* with the specified digits. Returns by reference the parsed value
|
|
* as (*vp / *divisorp), which may be fractional. All letters in the
|
|
* digits string must be uppercase. All input characters are
|
|
* converted to upper case.
|
|
*/
|
|
static boolean_t
|
|
parse_base_number (const char *s, const char *digits, long long *vp,
|
|
long long *divisorp)
|
|
{
|
|
long long v, divisor;
|
|
boolean_t found_dot_p;
|
|
int radix;
|
|
|
|
/* Make sure there's at least one digit. */
|
|
if (s[0] == '\0')
|
|
return FALSE;
|
|
|
|
radix = strlen (digits);
|
|
|
|
for (v = 0, divisor = 1, found_dot_p = FALSE; *s; s++)
|
|
{
|
|
const char *p;
|
|
int n;
|
|
|
|
n = toupper (*s);
|
|
if (n == '.')
|
|
{
|
|
if (found_dot_p) /* Only one decimal point allowed. */
|
|
return FALSE;
|
|
found_dot_p = TRUE;
|
|
}
|
|
else
|
|
{
|
|
p = strchr (digits, n);
|
|
if (p == NULL)
|
|
return FALSE;
|
|
if (v > 0x7FFFFFFFFFFFFFFFLL / radix /* Check overflow. */
|
|
|| divisor > 0x7FFFFFFFFFFFFFFFLL / radix)
|
|
return FALSE;
|
|
v = (v * radix) + (p - digits);
|
|
if (found_dot_p)
|
|
divisor *= radix;
|
|
}
|
|
}
|
|
|
|
*vp = v;
|
|
*divisorp = divisor;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Parse a number specified in a flexible format. Returns TRUE iff
|
|
* the parse was successful and the result didn't exceed 32 bits,
|
|
* else FALSE. Returns by reference the parsed value in *val.
|
|
*
|
|
* Each number may have an optional preceding "+" or "-" sign, which
|
|
* do the obvious thing.
|
|
*
|
|
* Base numbers may be specified in several ways:
|
|
*
|
|
* "123" ; decimal number
|
|
* "146.5" ; floating point value
|
|
* "0x12AC" ; hexadecimal number
|
|
* "0x12AC.12A" ; hexadecimal floating point
|
|
* "0b010010" ; binary number
|
|
* "0b010.10" ; binary floating point
|
|
*
|
|
* I decided not to allow octal because that might confuse
|
|
* unsophisticated users who accidentally precede a number with a
|
|
* leading zero.
|
|
*
|
|
* The number may be followed by an optional case-insensitive suffix:
|
|
*
|
|
* "K" or "k" ; multiply the value by 1024 (1K)
|
|
* "M" or "m" ; multiply the value by 1024*1024 (1M)
|
|
*
|
|
* Floating point results will be rounded toward zero before they are
|
|
* returned. The rounding happens *after* any multiplication suffix
|
|
* is applied, so "1.5K" is exactly equivalent to "1536".
|
|
*
|
|
* The truncated result can then be rounded up to the next highest
|
|
* (farther from zero) multiple of a specified number. If you don't
|
|
* want rounding, just pass "1" for round_up_to_multiple_of.
|
|
*/
|
|
boolean_t
|
|
parse_number (const char *orig_num, int32 *val,
|
|
unsigned round_up_to_multiple_of)
|
|
{
|
|
long long orig_raw_val, raw_val, div;
|
|
int shift_factor;
|
|
boolean_t negate_p, parsed_p;
|
|
int32 v;
|
|
char *num;
|
|
|
|
/* Default value to be safe. */
|
|
*val = 0;
|
|
|
|
/* Empty string is an error. We check this here to simplify checks later. */
|
|
if (orig_num == NULL || orig_num[0] == '\0')
|
|
return FALSE;
|
|
|
|
num = (char *) alloca (strlen (orig_num) + 1);
|
|
strcpy (num, orig_num);
|
|
|
|
/* Check for leading + or -, and advance past it. */
|
|
negate_p = (num[0] == '-');
|
|
num += (num[0] == '-' || num[0] == '+');
|
|
|
|
/* Look for a trailing suffix. */
|
|
shift_factor = fetch_and_remove_shift_suffix (num);
|
|
|
|
/* Figure out which parsing function to use. */
|
|
if (num[0] == '0' && num[1] == 'x')
|
|
parsed_p = parse_base_number (num + 2, "0123456789ABCDEF", &raw_val, &div);
|
|
else if (num[0] == '0' && num[1] == 'b')
|
|
parsed_p = parse_base_number (num + 2, "01", &raw_val, &div);
|
|
else
|
|
parsed_p = parse_base_number (num, "0123456789", &raw_val, &div);
|
|
|
|
/* Make sure we parsed successfully. */
|
|
if (!parsed_p)
|
|
return FALSE;
|
|
|
|
/* Compute the resulting number by applying the scaling factors. */
|
|
orig_raw_val = raw_val;
|
|
raw_val <<= shift_factor;
|
|
if (raw_val >> shift_factor != orig_raw_val) /* exceeded precision? */
|
|
return FALSE;
|
|
raw_val /= div;
|
|
|
|
/* Make sure we didn't exceed the precision of the result. */
|
|
v = raw_val;
|
|
if (v != raw_val)
|
|
return FALSE;
|
|
|
|
/* Round up if so specified. */
|
|
if (round_up_to_multiple_of > 1)
|
|
{
|
|
v += round_up_to_multiple_of - 1;
|
|
v -= v % round_up_to_multiple_of;
|
|
}
|
|
|
|
*val = negate_p ? -v : v;
|
|
|
|
return TRUE;
|
|
}
|