shithub: riscv

ref: 578af37678b2ea16e29d950cbc352823cd2491ab
dir: /sys/src/ape/lib/ap/gen/strtoull.c/

View raw version
#include <stdlib.h>
#include <errno.h>
#include <limits.h>

#define UVLONG_MAX	(1LL<<63)

unsigned long long
strtoull(char *nptr, char **endptr, int base)
{
	char *p;
	unsigned long long n, nn, m;
	int c, ovfl, v, neg, ndig;

	p = nptr;
	neg = 0;
	n = 0;
	ndig = 0;
	ovfl = 0;

	/*
	 * White space
	 */
	for(;; p++) {
		switch(*p) {
		case ' ':
		case '\t':
		case '\n':
		case '\f':
		case '\r':
		case '\v':
			continue;
		}
		break;
	}

	/*
	 * Sign
	 */
	if(*p == '-' || *p == '+')
		if(*p++ == '-')
			neg = 1;

	/*
	 * Base
	 */
	if(base == 0) {
		base = 10;
		if(*p == '0') {
			base = 8;
			if(p[1] == 'x' || p[1] == 'X'){
				p += 2;
				base = 16;
			}
		}
	} else
	if(base == 16 && *p == '0') {
		if(p[1] == 'x' || p[1] == 'X')
			p += 2;
	} else
	if(base < 0 || 36 < base)
		goto Return;

	/*
	 * Non-empty sequence of digits
	 */
	m = UVLONG_MAX/base;
	for(;; p++,ndig++) {
		c = *p;
		v = base;
		if('0' <= c && c <= '9')
			v = c - '0';
		else
		if('a' <= c && c <= 'z')
			v = c - 'a' + 10;
		else
		if('A' <= c && c <= 'Z')
			v = c - 'A' + 10;
		if(v >= base)
			break;
		if(n > m)
			ovfl = 1;
		nn = n*base + v;
		if(nn < n)
			ovfl = 1;
		n = nn;
	}

Return:
	if(ndig == 0)
		p = nptr;
	if(endptr)
		*endptr = p;
	if(ovfl){
		errno = ERANGE;
		return UVLONG_MAX;
	}
	if(neg)
		return -n;
	return n;
}