/*
* Copyright (c) 2003 - 2006, Nils R. Weller
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Functions to determine the real type, and possible overflows,
* of constants
*/
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "token.h"
#if 0
#include "misc.h"
#endif
#include "error.h"
#if 0
#include "debug.h"
#endif
#include "n_libc.h"
static char *max_hex_int;
static char *max_hex_uint;
static char *max_hex_long;
static char *max_hex_ulong;
static char *max_hex_llong;
static char *max_hex_ullong;
static char *max_oct_int;
static char *max_oct_uint;
static char *max_oct_long;
static char *max_oct_ulong;
static char *max_oct_llong;
static char *max_oct_ullong;
static char *max_dec_int;
static char *max_dec_uint;
static char *max_dec_long;
static char *max_dec_ulong;
static char *max_dec_llong;
static char *max_dec_ullong;
static int min_hex_digits;
static int min_oct_digits;
static int min_dec_digits;
#ifndef LLONG_MAX
#ifdef __i386__
# define LLONG_MAX 9223372036854775807LL
# define LLONG_MIN (-LLONG_MAX - 1LL)
# define ULLONG_MAX 18446744073709551615ULL
#endif
#endif
static int using_llong;
/*
* XXX this is super slow!
* instead use something like
* struct val_limit {
* char *value;
* size_t digits;
* } limits[x];
* limits[x].digits = get_digits(val);
* limits[x].value = n_xmalloc(limits[x].digits + 1);
* sprintf(limits[x].value, "%...", val);
* ... tehn
* #define OVERFLOW(limit, valstr) \
* if (valstr->len != limit.value.len) {
*/
void
init_max_digits(void) {
static int has_inited;
char buf[256];
if (has_inited == 1) {
return;
}
has_inited = 1;
/* XXX the 0x ... stuff SUCKS! that should be stripped */
sprintf(buf, "0x%x", (unsigned)INT_MAX);
max_hex_int = n_xstrdup(buf);
min_hex_digits = strlen(buf);
sprintf(buf, "0x%x", (unsigned)UINT_MAX);
max_hex_uint = n_xstrdup(buf);
sprintf(buf, "0x%lx", (unsigned long)LONG_MAX);
max_hex_long = n_xstrdup(buf);
sprintf(buf, "0x%lx", (unsigned long)ULONG_MAX);
max_hex_ulong = n_xstrdup(buf);
#if defined(LLONG_MAX) && defined(ULLONG_MAX)
using_llong = 1;
sprintf(buf, "0x%llx", (unsigned long long)LLONG_MAX);
max_hex_llong = n_xstrdup(buf);
sprintf(buf, "0x%llx", (unsigned long long)ULLONG_MAX);
max_hex_ullong = n_xstrdup(buf);
#else
using_llong = 0;
#endif
sprintf(buf, "%o", (unsigned)INT_MAX);
max_oct_int = n_xstrdup(buf);
min_oct_digits = strlen(buf);
sprintf(buf, "%o", (unsigned)UINT_MAX);
max_oct_uint = n_xstrdup(buf);
sprintf(buf, "%lo", (unsigned long)LONG_MAX);
max_oct_long = n_xstrdup(buf);
sprintf(buf, "%lo", (unsigned long)ULONG_MAX);
max_oct_ulong = n_xstrdup(buf);
#if defined(LLONG_MAX) && defined(ULLONG_MAX)
sprintf(buf, "%llo", (unsigned long long)LLONG_MAX);
max_oct_llong = n_xstrdup(buf);
sprintf(buf, "%llo", (unsigned long long)ULLONG_MAX);
max_oct_ullong = n_xstrdup(buf);
#endif
sprintf(buf, "%d", INT_MAX);
max_dec_int = n_xstrdup(buf);
min_dec_digits = strlen(buf);
sprintf(buf, "%u", (unsigned)UINT_MAX);
max_dec_uint = n_xstrdup(buf);
sprintf(buf, "%ld", (long)LONG_MAX);
max_dec_long = n_xstrdup(buf);
sprintf(buf, "%lu", (unsigned long)ULONG_MAX);
max_dec_ulong = n_xstrdup(buf);
#if defined(LLONG_MAX) && defined(ULLONG_MAX)
sprintf(buf, "%lld", (long long)LLONG_MAX);
max_dec_llong = n_xstrdup(buf);
sprintf(buf, "%llu", (unsigned long long)ULLONG_MAX);
max_dec_ullong = n_xstrdup(buf);
#endif
}
/*
* Returns 1 if the ascii number pointed to by input overflows the
* ascii number pointed to by limit, else 0
*/
static int
overflow(const char *input, const char *limit) {
int is_larger = 0;
int sofar_even = 1;
while (*input && *limit) {
if (!isdigit((unsigned char)*input)
&& strchr("abcdefx", tolower((unsigned char)*input))
== NULL) {
break;
}
if (*input > *limit && sofar_even == 1) {
/*
* is_larger is significant if both have the same
* number of digits
*/
is_larger = 1;
} else if (*input < *limit) {
sofar_even = 0;
}
++input;
++limit;
}
if (*limit) {
/* Limit not exceeded */
is_larger = 0;
} else if (isdigit((unsigned char)*input)
|| (*input && strchr("abcdef", *input))) {
/* Limit exceeded */
is_larger = 1;
}
return is_larger;
}
/*
* The following two functions - check_hex_oct() and
* check_dec() - determine the type of an integer constant and return
* the value of that as success, -1 on failure (overflow). Decimal
* integer constants are by default of type ``int''. If they do not fit
* into an int, they are promoted to ``long''. If that's not enough
* either, C99 specifies that ``long long'' shall be used.
* A similar concept is applied to constants with unsigned and long/
* long long specifiers or different source representation (hexadecimal,
* octal constants), as will be seen shortly.
*/
static int
check_hex_oct(char *num, int is_hex, int signfl, int long_flag) {
/*
* Since octal and hexadecimal constants are handled EXACTLY the
* same in this respect, the same code can be used. Only a
* different working set must be chosen depending on whether we are
* dealing with an octal or a hexadecimal constant
*/
char *ws[6];
if (is_hex) {
ws[0] = max_hex_int;
ws[1] = max_hex_uint;
ws[2] = max_hex_long;
ws[3] = max_hex_ulong;
ws[4] = max_hex_llong;
ws[5] = max_hex_ullong;
} else {
ws[0] = max_oct_int;
ws[1] = max_oct_uint;
ws[2] = max_oct_long;
ws[3] = max_oct_ulong;
ws[4] = max_oct_llong;
ws[5] = max_oct_ullong;
}
/* For syntactic convenience when dealing with ws... */
#define WS_INT 0
#define WS_UINT 1
#define WS_LONG 2
#define WS_ULONG 3
#define WS_LLONG 4
#define WS_ULLONG 5
if (signfl) {
/* Unsigned */
if (long_flag) {
/*
* 123lu
* unsigned long int
* unsigned long long int
*/
if (overflow(num, ws[WS_ULONG])) {
if (using_llong) {
/* C99 */
if (overflow(num, ws[WS_ULLONG])) {
return -1;
} else {
return TY_ULLONG;
}
} else {
/* C89 */
return -1;
}
} else {
return TY_ULONG;
}
} else {
/*
* 123u
* unsigned int
* unsigned long int
* unsigned long long int
*/
if (overflow(num, ws[WS_UINT])) {
if (overflow(num, ws[WS_ULONG])) {
if (using_llong) {
/* C99 */
if (overflow(num,
ws[WS_ULLONG])) {
return -1;
} else {
return TY_ULLONG;
}
} else {
/* C89 */
return -1;
}
} else {
return TY_ULONG;
}
} else {
return TY_UINT;
}
}
} else {
/* Is signed */
if (long_flag) {
/*
* 123l
* long int
* unsigned long int
* long long int
* unsigned long long int
*/
if (overflow(num, ws[WS_LONG])) {
if (overflow(num, ws[WS_ULONG])) {
if (using_llong) {
/* C99 */
if (overflow(num,
ws[WS_LLONG])) {
if (overflow(num,
ws[WS_ULLONG])) {
return -1;
} else {
return
TY_ULLONG;
}
} else {
return TY_LLONG;
}
} else {
/* C89 */
return -1;
}
} else {
return TY_ULONG;
}
} else {
return TY_LONG;
}
} else {
/*
* 123
* int
* unsigned int
* long int
* unsigned long int
* long long int
* unsigned long long int
*/
if (overflow(num, ws[WS_INT])) {
if (overflow(num, ws[WS_UINT])) {
if (overflow(num, ws[WS_LONG])) {
if (overflow(num,
ws[WS_ULONG])) {
if (using_llong) {
/* C99 */
if (
overflow(num, ws[WS_LLONG])) {
if (overflow(num, ws[WS_ULLONG])) {
return 1;
} else {
return TY_ULLONG;
}
} else {
return TY_LLONG;
}
} else {
/* C89 */
return -1;
}
} else {
return TY_ULONG;
}
} else {
return TY_LONG;
}
} else {
return TY_UINT;
}
} else {
return TY_INT;
}
}
}
/* NOTREACHED */
return -1;
}
static int
check_dec(char *num, int signfl, int long_flag) {
if (signfl) {
/* Unsigned */
if (long_flag) {
/*
* 10lu
* unsigned long int
* unsigned long long int
*/
if (overflow(num, max_dec_ulong)) {
if (using_llong) {
/* C99 */
if (overflow(num, max_dec_ullong)) {
return -1;
} else {
return TY_ULLONG;
}
} else {
/* C89 */
return -1;
}
} else {
return TY_ULONG;
}
} else {
/*
* 10u
* unsigned int
* unsigned long int
* unsigned long long int
*/
if (overflow(num, max_dec_uint)) {
if (overflow(num, max_dec_ulong)) {
if (using_llong) {
/* C99 */
if (overflow(num,
max_dec_ullong)) {
return -1;
} else {
return TY_ULLONG;
}
} else {
/* C89 */
return -1;
}
} else {
return TY_ULONG;
}
} else {
return TY_UINT;
}
}
} else {
/* Signed */
if (long_flag) {
/*
* 10l
* long int
* unsigned long int
* long long int
*/
if (overflow(num, max_dec_long)) {
if (overflow(num, max_dec_ulong)) {
if (using_llong) {
/* C99 */
if (overflow(num,
max_dec_llong)) {
return -1;
} else {
return TY_LLONG;
}
} else {
/* C89 */
return -1;
}
} else {
return TY_ULONG;
}
} else {
return TY_LONG;
}
} else {
/*
* 10
* int
* long int
* unsigned long int
* long long int
*/
if (overflow(num, max_dec_int)) {
if (overflow(num, max_dec_long)) {
if (overflow(num, max_dec_ulong)) {
if (using_llong) {
/* C99 */
if (overflow(num,
max_dec_llong)) {
return -1;
} else {
return TY_LLONG;
}
} else {
return -1;
}
} else {
return TY_ULONG;
}
} else {
return TY_LONG;
}
} else {
return TY_INT;
}
}
}
/* NOTREACHED */
return -1;
}
/*
* Returns the real type of the ascii contanst specified by num, based on
* hexa_flag, octal_flag, signfl, long_flag, if the type recorded by those
* variables had to be adjusted to make the value fit. Returns -1 if an
* overflow was detected. Returns 0 if the type did not have to be
* adjusted
*/
int
range_check(char *num,
int hexa_flag,
int octal_flag,
int signfl,
int long_flag,
int ndigits) {
char *mode = NULL;
char *start = NULL;
int rc;
if (hexa_flag == 1 || octal_flag == 1) {
if (hexa_flag) {
if (ndigits < min_hex_digits) {
return 0;
}
mode = "hexadecimal";
start = num + 2; /* 0x... */
} else {
if (ndigits < min_oct_digits) {
return 0;
}
mode = "octal";
start = num + 1; /* 0... */
}
while (*start == '0')
++start;
rc = check_hex_oct(num, hexa_flag, signfl, long_flag);
} else {
if (ndigits < min_dec_digits) {
return 0;
}
mode = "decimal";
rc = check_dec(num, signfl, long_flag);
}
if (rc == -1) {
#if 0
lexerror("Overflow in %s constant", mode);
#endif
return 0;
}
return rc;
}
/* XXX write new regression test */
syntax highlighted by Code2HTML, v. 0.9.1