/*
 * 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