/*
 * $QNXLicenseC:
 * Copyright 2007, QNX Software Systems. All Rights Reserved.
 * 
 * You must obtain a written license from and pay applicable license fees to QNX 
 * Software Systems before you may reproduce, modify or distribute this software, 
 * or any work that includes all or part of this software.   Free development 
 * licenses are available for evaluation and non-commercial purposes.  For more 
 * information visit http://licensing.qnx.com or email licensing@qnx.com.
 *  
 * This file may contain contributions from others.  Please review this entire 
 * file for other proprietary rights or license notices, as well as the QNX 
 * Development Suite License Guide at http://licensing.qnx.com/license-guide/ 
 * for other information.
 * $
 */




/*
 * This convert a string to a constant using the POSIX locale
 */

#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <ctype.h>

#define _STOINT_SIGNED          0x1
#define _STOINT_LLONG           0x2

#define LLONG_MAX    9223372036854775807LL
#define LLONG_MIN    (-LLONG_MAX - 1LL)
#define ULLONG_MAX   18446744073709551615ULL

u_int64_t _Stoint(const char *nptr, char **endptr, int base, int flags) {
	int            neg, overflow, once;
	const char     *p;
	u_int64_t			ll_value;
	unsigned long		l_value;
	int					n;

	/* Part 1, skip white space characters */
	p = nptr;
	while(isspace(*p)) {
		p++;
	}

	/* Part 2, Optional sign */
	neg = 0;
	if(*p == '+') {
		p++;
	} else if(*p == '-') {
		neg = 1;
		p++;
	}

	/* start with 0 for octal, 0x or 0X for hex, rest is decimal */
	if(base == 0) {
		if(*p == '0') {
			if(*(p+1) == 'x' || *(p+1) == 'X') {
				p += 2;
				base = 16;
			} else {
				base = 8;
			}
		} else {
			base = 10;
		}
	} else if(base == 16 && *p == '0' && (*(p+1) == 'x' || *(p+1) == 'X')) {
		/* Base 16 is allowed to start with 0x or 0X if there */
		p += 2;
	}

	ll_value = 0;
	l_value = 0;
	once = overflow = 0;
	/* Only valid bases are 2 to 36 */
	if(base >= 2 && base <= 36) {
		while(n = *p) {
			u_int64_t					ll_save;
			unsigned long				l_save;

			/* Adjust character to from a char to an integer */
			if(n >= '0' && n <= '9') {
				n -= '0';
			} else if(n >= 'a' && n <= 'z') {
				n -= 'a' - 10;
			} else if(n >= 'A' && n <= 'Z') {
				n -= 'A' - 10;
			} else {
				/* Unknown, done */
				break;
			}
			if(n >= base) {
				/* Out of radix range, done */
				break;
			}

			/* Character is valid */
			p++;

			/* Scale, and add digit, check for overflow */
			if (flags & _STOINT_LLONG) {

				ll_save = ll_value;
				ll_value = ll_value * base + n;
				if(ll_value < ll_save) {
					overflow = 1;
				}
			} else {
				l_save = l_value;
				l_value = l_value * base + n;
				if(l_value < l_save) {
					overflow = 1;
				}
			}
			once = 1;
		}

		/* Check for a signed number overflow */
		if(flags & _STOINT_SIGNED) {
		   if(flags & _STOINT_LLONG) {
			  if(((int64_t)ll_value < 0) && !(neg && (ll_value == -(u_int64_t)LLONG_MIN))) {
				 overflow = 1;
			  }
		   } else {
			  if(((long)l_value < 0) && !(neg && (l_value == -(unsigned long)LONG_MIN))) {
				 overflow = 1;
			  }
		   }
		}
	}

	/* Store the end of part 2, so caller can get to part 3 */
	if(endptr) {
		*endptr = (char *)(once ? p : nptr);
	}

	/* If base not supported or no conversion was done, return 0 setting errno to EINVAL (UNIX98) */
	if(!once) {
		errno = EINVAL;
		return 0;
	}

	/* If no conversion was done, return respective max setting errno to ERANGE (UNIX98) */
	if(overflow) {
		errno = ERANGE;
		if (flags & _STOINT_LLONG) {
			return (flags & _STOINT_SIGNED) ? (neg ? LLONG_MIN : LLONG_MAX) : ULLONG_MAX;
		} else {
			return (flags & _STOINT_SIGNED) ? (neg ? LONG_MIN : LONG_MAX) : ULONG_MAX;
		}
	}

	/* Everything OK, return the number */
	if (flags & _STOINT_LLONG) {
		return neg ? -ll_value : ll_value;
	} else {
		return neg ? -l_value : l_value;
	}
}

