blob: 6055337a413d35532236210a6d0d6fd2f17a891a [file] [log] [blame]
#include <limits.h>
#include <errno.h>
#include <ctype.h>
#include "shgetc.h"
/* Lookup table for digit values. -1==255>=36 -> invalid */
static const unsigned char table[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1,
-1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
unsigned long long __intscan(FILE* f,
unsigned base,
int pok,
unsigned long long lim) {
const unsigned char* val = table + 1;
int c, neg = 0;
unsigned x;
unsigned long long y;
if (base > 36) {
errno = EINVAL;
return 0;
}
while (isspace((c = shgetc(f))))
;
if (c == '+' || c == '-') {
neg = -(c == '-');
c = shgetc(f);
}
if ((base == 0 || base == 16) && c == '0') {
c = shgetc(f);
if ((c | 32) == 'x') {
c = shgetc(f);
if (val[c] >= 16) {
shunget(f);
if (pok)
shunget(f);
else
shlim(f, 0);
return 0;
}
base = 16;
} else if (base == 0) {
base = 8;
}
} else {
if (base == 0)
base = 10;
if (val[c] >= base) {
shunget(f);
shlim(f, 0);
errno = EINVAL;
return 0;
}
}
if (base == 10) {
for (x = 0; c - '0' < 10U && x <= UINT_MAX / 10 - 1; c = shgetc(f))
x = x * 10 + (c - '0');
for (y = x; c - '0' < 10U && y <= ULLONG_MAX / 10 &&
10 * y <= ULLONG_MAX - (c - '0');
c = shgetc(f))
y = y * 10 + (c - '0');
if (c - '0' >= 10U)
goto done;
} else if (!(base & base - 1)) {
int bs = "\0\1\2\4\7\3\6\5"[(0x17 * base) >> 5 & 7];
for (x = 0; val[c] < base && x <= UINT_MAX / 32; c = shgetc(f))
x = x << bs | val[c];
for (y = x; val[c] < base && y <= ULLONG_MAX >> bs; c = shgetc(f))
y = y << bs | val[c];
} else {
for (x = 0; val[c] < base && x <= UINT_MAX / 36 - 1; c = shgetc(f))
x = x * base + val[c];
for (y = x; val[c] < base && y <= ULLONG_MAX / base &&
base * y <= ULLONG_MAX - val[c];
c = shgetc(f))
y = y * base + val[c];
}
if (val[c] < base) {
for (; val[c] < base; c = shgetc(f))
;
errno = ERANGE;
y = lim;
if (lim & 1)
neg = 0;
}
done:
shunget(f);
if (y >= lim) {
if (!(lim & 1) && !neg) {
errno = ERANGE;
return lim - 1;
} else if (y > lim) {
errno = ERANGE;
return lim;
}
}
return (y ^ neg) - neg;
}