150 lines
3.7 KiB
C
150 lines
3.7 KiB
C
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include "../internal/strtox.h"
|
|
|
|
/*
|
|
* Shared integer subject-sequence scan (todo 11). See src/internal/strtox.h
|
|
* for the contract; this file is the engine the strtol/strtoul wrappers and
|
|
* todo 14's strtoimax/strtoumax build on.
|
|
*
|
|
* Prefix rules (C23 7.24.1.4p3-4):
|
|
* - base 0: "0x"/"0X" followed by a hex digit selects base 16 and the
|
|
* prefix is consumed; a bare leading 0 selects base 8 and the 0 is the
|
|
* subject itself ("0" is a valid integer constant, so the subject is
|
|
* never empty after an octal prefix); anything else is base 10.
|
|
* - base 16: "0x"/"0X" followed by a hex digit is consumed; without a
|
|
* hex digit the "0" is itself a valid hex digit and the x terminates
|
|
* the subject.
|
|
* - "0x" without a hex digit under base 0 is no integer-constant form,
|
|
* so there is no subject sequence at all.
|
|
*
|
|
* Endptr always lands at the first character that is not part of the
|
|
* subject sequence, or at nptr when there is no subject (C23 7.24.1.4p5,
|
|
* even when whitespace or a sign was consumed).
|
|
*/
|
|
|
|
int
|
|
__strtox_scan(const char *nptr, char **endptr, int base, int *neg, unsigned long long *mag,
|
|
int *over)
|
|
{
|
|
const char *s = nptr;
|
|
int sg = 0;
|
|
int ov = 0;
|
|
int digits = 0;
|
|
int consumed_prefix = 0;
|
|
unsigned long long m = 0;
|
|
|
|
while (strtox_isspace(*s))
|
|
{
|
|
s++;
|
|
}
|
|
if (*s == '+' || *s == '-')
|
|
{
|
|
sg = (*s == '-');
|
|
s++;
|
|
}
|
|
if (base != 0 && (base < 2 || base > 36))
|
|
{
|
|
if (endptr)
|
|
{
|
|
*endptr = (char *)nptr;
|
|
}
|
|
*neg = 0;
|
|
*mag = 0;
|
|
*over = 0;
|
|
return STRTOX_BADBASE;
|
|
}
|
|
|
|
if (base == 0 || base == 16)
|
|
{
|
|
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
|
|
{
|
|
int d2 = strtox_digit(s[2]);
|
|
|
|
if (d2 >= 0 && d2 < 16)
|
|
{
|
|
base = 16;
|
|
s += 2;
|
|
}
|
|
else if (base == 0)
|
|
{
|
|
/* "0x" with no hex digit is no integer-constant form. */
|
|
if (endptr)
|
|
{
|
|
*endptr = (char *)nptr;
|
|
}
|
|
*neg = 0;
|
|
*mag = 0;
|
|
*over = 0;
|
|
return STRTOX_EMPTY;
|
|
}
|
|
/* base 16: the 0 itself is the subject; the x ends it. */
|
|
}
|
|
else if (base == 0 && s[0] == '0')
|
|
{
|
|
/* Octal prefix: the 0 opens the subject even when no octal
|
|
* digit follows ("0" is itself a valid integer constant). */
|
|
base = 8;
|
|
consumed_prefix = 1;
|
|
s++;
|
|
}
|
|
else if (base == 0)
|
|
{
|
|
/* No prefix: a decimal subject (base 0 auto-detection). */
|
|
base = 10;
|
|
}
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
int d = strtox_digit(*s);
|
|
|
|
if (d < 0 || d >= base)
|
|
{
|
|
break;
|
|
}
|
|
if (ov == 0)
|
|
{
|
|
unsigned long long lim =
|
|
(ULLONG_MAX - (unsigned long long)d) / (unsigned long long)base;
|
|
|
|
if (m > lim)
|
|
{
|
|
ov = 1;
|
|
}
|
|
else
|
|
{
|
|
m = (m * (unsigned long long)base) + (unsigned long long)d;
|
|
}
|
|
}
|
|
digits++;
|
|
s++;
|
|
}
|
|
|
|
if (digits == 0 && consumed_prefix == 0)
|
|
{
|
|
if (endptr)
|
|
{
|
|
*endptr = (char *)nptr;
|
|
}
|
|
*neg = 0;
|
|
*mag = 0;
|
|
*over = 0;
|
|
return STRTOX_EMPTY;
|
|
}
|
|
if (endptr)
|
|
{
|
|
*endptr = (char *)s;
|
|
}
|
|
*neg = sg;
|
|
*mag = m;
|
|
*over = ov;
|
|
return STRTOX_OK;
|
|
}
|