strtoi.h revision 34f9b3eef6fdadbda0a846aa4d68691ac40eace5
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2009 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.opensource.org/licenses/cpl1.0.txt *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
/*
* AT&T Research
* Glenn Fowler
* Phong Vo
*
* common header and implementation for
*
* strtol strtoul strton
* strtoll strtoull strtonll
* strntol strntoul strnton
* strntoll strntoull strntonll
*
* define these macros to instantiate an implementation:
*
* S2I_function the function name
* S2I_number the signed number type
* S2I_unumber the unsigned number type
* S2I_unsigned 1 for unsigned, 0 for signed
* S2I_qualifier 1 for optional qualifier suffix, 0 otherwise
* S2I_multiplier 1 for optional multiplier suffix, 0 otherwise
* S2I_size the second argument is the input string size
*
* convert string to number
* errno=ERANGE on overflow (LONG_MAX) or underflow (LONG_MIN)
* if non-null e will point to first unrecognized char in s
* if basep!=0 it points to the default base on input and
* will point to the explicit base on return
* a default base of 0 will determine the base from the input
* a default base of 1 will determine the base from the input using bb#*
* a base prefix in the string overrides *b
* *b will not be set if the string has no base prefix
* if m>1 and no multipler was specified then the result is multiplied by m
* if m<0 then multipliers are not consumed
* if a base arg or prefix is specified then multiplier is not consumed
*
* integer numbers are of the form:
*
* [sign][base][number[qualifier]][multiplier]
*
* base: nnn# base nnn
* 0[xX] hex
* 0 octal
* [1-9] decimal
*
* number: [0-9a-zA-Z]*
*
* qualifier: [lL]
* [uU]
* [uU][lL]
* [lL][uU]
* [lL][lL][uU]
* [uU][lL][lL]
*
* multiplier: . pseudo-float if m>1
* [bB] block (512)
* [cC] char (1)
* [gG] giga (1024*1024*1024)
* [kK] kilo (1024)
* [mM] mega (1024*1024)
*/
#include <ast.h>
#include <ctype.h>
#include "sfhdr.h"
#if !__STD_C && !defined(const)
#define const
#endif
#ifndef ERANGE
#define ERANGE EINVAL
#endif
#define QL 01
#define QU 02
#define S2I_umax (~((S2I_unumber)0))
#if S2I_unsigned
#define S2I_type S2I_unumber
#define S2I_min 0
#define S2I_max S2I_umax
#else
#define S2I_type S2I_number
#define S2I_min (-S2I_max-1)
#define S2I_max (S2I_umax>>1)
#endif
#if S2I_size
#define S2I_valid(s) ((s)<(z))
#else
#define S2I_valid(s) 1
#endif
#define ADDOVER(n,c,s) ((S2I_umax-(n))<((S2I_unumber)((c)+(s))))
#define MPYOVER(n,c) (((S2I_unumber)(n))>(S2I_umax/(c)))
static const S2I_unumber mm[] =
{
0,
S2I_umax / 1,
S2I_umax / 2,
S2I_umax / 3,
S2I_umax / 4,
S2I_umax / 5,
S2I_umax / 6,
S2I_umax / 7,
S2I_umax / 8,
S2I_umax / 9,
S2I_umax / 10,
S2I_umax / 11,
S2I_umax / 12,
S2I_umax / 13,
S2I_umax / 14,
S2I_umax / 15,
S2I_umax / 16,
S2I_umax / 17,
S2I_umax / 18,
S2I_umax / 19,
S2I_umax / 20,
S2I_umax / 21,
S2I_umax / 22,
S2I_umax / 23,
S2I_umax / 24,
S2I_umax / 25,
S2I_umax / 26,
S2I_umax / 27,
S2I_umax / 28,
S2I_umax / 29,
S2I_umax / 30,
S2I_umax / 31,
S2I_umax / 32,
S2I_umax / 33,
S2I_umax / 34,
S2I_umax / 35,
S2I_umax / 36,
S2I_umax / 37,
S2I_umax / 38,
S2I_umax / 39,
S2I_umax / 40,
S2I_umax / 41,
S2I_umax / 42,
S2I_umax / 43,
S2I_umax / 44,
S2I_umax / 45,
S2I_umax / 46,
S2I_umax / 47,
S2I_umax / 48,
S2I_umax / 49,
S2I_umax / 50,
S2I_umax / 51,
S2I_umax / 52,
S2I_umax / 53,
S2I_umax / 54,
S2I_umax / 55,
S2I_umax / 56,
S2I_umax / 57,
S2I_umax / 58,
S2I_umax / 59,
S2I_umax / 60,
S2I_umax / 61,
S2I_umax / 62,
S2I_umax / 63,
S2I_umax / 64,
};
#if defined(__EXPORT__)
#define extern __EXPORT__
#endif
extern S2I_type
#undef extern
#if S2I_size
#if S2I_multiplier
#if __STD_C
S2I_function(const char* a, size_t size, char** e, char* basep, int m)
#else
S2I_function(a, size, e, basep, m) const char* a; size_t size; char** e; char* basep; int m;
#endif
#else
#if __STD_C
S2I_function(const char* a, size_t size, char** e, int base)
#else
S2I_function(a, size, e, base) const char* a; size_t size; char** e; int base;
#endif
#endif
#else
#if S2I_multiplier
#if __STD_C
S2I_function(const char* a, char** e, char* basep, int m)
#else
S2I_function(a, e, basep, m) const char* a; char** e; char* basep; int m;
#endif
#else
#if __STD_C
S2I_function(const char* a, char** e, int base)
#else
S2I_function(a, e, base) const char* a; char** e; int base;
#endif
#endif
#endif
{
register unsigned char* s = (unsigned char*)a;
#if S2I_size
register unsigned char* z = s + size;
#endif
register S2I_unumber n;
register S2I_unumber x;
register int c;
register int shift;
register unsigned char* p;
register unsigned char* cv;
unsigned char* b;
unsigned char* k;
S2I_unumber v;
#if S2I_multiplier
register int base;
#endif
int negative;
int overflow = 0;
int decimal = 0;
int thousand = 0;
#if !S2I_unsigned
int qualifier = 0;
#endif
#if S2I_multiplier
base = basep ? *((unsigned char*)basep) : 0;
#else
if (base > 36 && base <= SF_RADIX)
{
static int conformance = -1;
if (conformance < 0)
conformance = !strcmp(astconf("CONFORMANCE", NiL, NiL), "standard");
if (conformance)
base = 1;
}
#endif
if (base && (base < 2 || base > SF_RADIX))
{
errno = EINVAL;
return 0;
}
while (S2I_valid(s) && isspace(*s))
s++;
if ((negative = S2I_valid(s) && (*s == '-')) || S2I_valid(s) && *s == '+')
k = ++s;
else
k = 0;
p = s;
if (!base)
{
if (S2I_valid(p) && (c = *p++) >= '0' && c <= '9')
{
n = c - '0';
if (S2I_valid(p) && (c = *p) >= '0' && c <= '9')
{
n = (n << 3) + (n << 1) + c - '0';
p++;
}
if (S2I_valid(p) && *p == '#')
{
if (n >= 2 && n <= 64)
{
k = s = p + 1;
base = n;
}
}
else if (base)
base = 0;
else if (S2I_valid(s) && *s == '0' && S2I_valid(s + 1))
{
if ((c = *(s + 1)) == 'x' || c == 'X')
{
k = s += 2;
base = 16;
}
else if (c >= '0' && c <= '7')
{
s++;
base = 8;
}
}
}
if (!base)
base = 10;
else if (base < 2 || base > SF_RADIX)
{
errno = EINVAL;
return 0;
}
#if S2I_multiplier
else
{
if (basep)
*basep = base;
m = -1;
}
#endif
}
#if S2I_multiplier
else
m = -1;
#endif
/*
* this part transcribed from sfvscanf()
*/
SFSETLOCALE(&decimal, &thousand);
x = mm[base];
n = 0;
if (base == 10)
{
b = s;
p = 0;
for (;;)
{
if (S2I_valid(s) && (c = *s++) >= '0' && c <= '9')
{
if (n > x)
overflow = 1;
else
{
n = (n << 3) + (n << 1);
c -= '0';
if (ADDOVER(n, c, negative))
overflow = 1;
n += c;
}
}
else if (p && (s - p) != (3 + S2I_valid(s)))
{
s = p;
n = v;
c = 0;
break;
}
else if (!S2I_valid(s) || c != thousand)
break;
else if (!p && (s - b) > 4)
{
if (e)
*e = (char*)s - 1;
if (overflow)
{
errno = ERANGE;
#if S2I_unsigned
n = S2I_max;
#else
n = negative ? S2I_min : S2I_max;
#endif
}
return n;
}
else
{
p = s;
v = n;
}
}
}
else
{
SFCVINIT();
cv = base <= 36 ? _Sfcv36 : _Sfcv64;
if ((base & ~(base - 1)) == base)
{
#if !S2I_unsigned
qualifier |= QU;
#endif
if (base < 8)
shift = base < 4 ? 1 : 2;
else if (base < 32)
shift = base < 16 ? 3 : 4;
else
shift = base < 64 ? 5 : 6;
while (S2I_valid(s) && (c = cv[*s++]) < base)
{
if (n > x)
overflow = 1;
else
{
n <<= shift;
if (ADDOVER(n, c, negative))
overflow = 1;
n += c;
}
}
}
else
while (S2I_valid(s) && (c = cv[*s++]) < base)
{
if (n > x)
overflow = 1;
else
{
n *= base;
if (ADDOVER(n, c, negative))
overflow = 1;
n += c;
}
}
c = *(s - 1);
}
#if S2I_qualifier
/*
* optional qualifier suffix
*/
if (S2I_valid(s) && s > (unsigned char*)(a + 1))
{
base = 0;
for (;;)
{
if (!(base & QL) && (c == 'l' || c == 'L'))
{
base |= QL;
if (!S2I_valid(s))
break;
c = *s++;
if (c == 'l' || c == 'L')
{
if (!S2I_valid(s))
break;
c = *s++;
}
}
else if (!(base & QU) && (c == 'u' || c == 'U'))
{
base |= QU;
#if !S2I_unsigned
qualifier |= QU;
#endif
if (!S2I_valid(s))
break;
c = *s++;
}
else
break;
}
}
#endif
if (S2I_valid(s))
{
#if S2I_multiplier
/*
* optional multiplier suffix
*/
if (m < 0 || s == (unsigned char*)(a + 1))
s--;
else
{
switch (c)
{
case 'b':
case 'B':
shift = 9;
break;
case 'k':
case 'K':
shift = 10;
break;
case 'm':
case 'M':
shift = 20;
break;
case 'g':
case 'G':
shift = 30;
break;
case 't':
case 'T':
shift = 40;
break;
case 'p':
case 'P':
shift = 50;
break;
case 'e':
case 'E':
shift = 60;
break;
default:
if (m <= 1)
v = 0;
else if (c == decimal && S2I_valid(s))
{
if (MPYOVER(n, m))
overflow = 1;
n *= m;
v = 0;
while (S2I_valid(s) && (c = *s++) >= '0' && c <= '9')
v += (m /= 10) * (c - '0');
if (ADDOVER(n, v, negative))
overflow = 1;
n += v;
v = 0;
}
else
v = m;
s--;
shift = 0;
break;
}
if (shift)
{
if (S2I_valid(s))
switch (*s)
{
case 'b':
case 'B':
case 'i':
case 'I':
s++;
break;
}
#if S2I_unsigned
if (shift >= (sizeof(S2I_type) * CHAR_BIT))
#else
if (shift >= (sizeof(S2I_type) * CHAR_BIT - 1))
#endif
{
v = 0;
overflow = 1;
}
else
v = ((S2I_unumber)1) << shift;
}
if (v)
{
if (MPYOVER(n, v))
overflow = 1;
n *= v;
}
}
#else
s--;
#endif
}
if (s == k)
{
s--;
#if S2I_multiplier
if (basep)
*basep = 10;
#endif
}
#if !S2I_unsigned
else if (!(qualifier & QU))
{
if (negative)
{
if (!n)
{
b = k;
do
{
if (b >= s)
{
negative = 0;
break;
}
} while (*b++ == '0');
}
if (negative && (n - 1) > S2I_max)
overflow = 1;
}
else if (n > S2I_max)
overflow = 1;
}
#endif
if (e)
*e = (char*)s;
if (overflow)
{
#if !S2I_unsigned
if (negative)
{
if (x << 1)
errno = ERANGE;
return (S2I_type)S2I_min;
}
#endif
errno = ERANGE;
return (S2I_type)S2I_max;
}
return negative ? -n : n;
}