mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-12-04 12:28:58 +00:00
eeb6d312f5
prevents doing this in one pass.
255 lines
5.4 KiB
C
255 lines
5.4 KiB
C
/*
|
|
* Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers.
|
|
* All rights reserved.
|
|
* Copyright (c) 1992
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* By using this file, you agree to the terms and conditions set
|
|
* forth in the LICENSE file which can be found at the top level of
|
|
* the sendmail distribution.
|
|
*/
|
|
|
|
#include <sm/gen.h>
|
|
SM_IDSTR(id, "@(#)$Id: strto.c,v 1.18 2001/12/30 04:59:37 gshapiro Exp $")
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <sm/limits.h>
|
|
#include <sm/conf.h>
|
|
#include <sm/string.h>
|
|
|
|
/*
|
|
** SM_STRTOLL -- Convert a string to a (signed) long long integer.
|
|
**
|
|
** Ignores `locale' stuff. Assumes that the upper and lower case
|
|
** alphabets and digits are each contiguous.
|
|
**
|
|
** Parameters:
|
|
** nptr -- string containing number
|
|
** endptr -- location of first invalid character
|
|
** base -- numeric base that 'nptr' number is based in
|
|
**
|
|
** Returns:
|
|
** Failure: on underflow LLONG_MIN is returned; on overflow
|
|
** LLONG_MAX is returned and errno is set.
|
|
** When 'endptr' == '\0' then the entire string 'nptr'
|
|
** was valid.
|
|
** Success: returns the converted number
|
|
*/
|
|
|
|
LONGLONG_T
|
|
sm_strtoll(nptr, endptr, base)
|
|
const char *nptr;
|
|
char **endptr;
|
|
register int base;
|
|
{
|
|
register bool neg;
|
|
register const char *s;
|
|
register LONGLONG_T acc, cutoff;
|
|
register int c;
|
|
register int any, cutlim;
|
|
|
|
/*
|
|
** Skip white space and pick up leading +/- sign if any.
|
|
** If base is 0, allow 0x for hex and 0 for octal, else
|
|
** assume decimal; if base is already 16, allow 0x.
|
|
*/
|
|
|
|
s = nptr;
|
|
do
|
|
{
|
|
c = (unsigned char) *s++;
|
|
} while (isascii(c) && isspace(c));
|
|
if (c == '-')
|
|
{
|
|
neg = true;
|
|
c = *s++;
|
|
}
|
|
else
|
|
{
|
|
neg = false;
|
|
if (c == '+')
|
|
c = *s++;
|
|
}
|
|
if ((base == 0 || base == 16) &&
|
|
c == '0' && (*s == 'x' || *s == 'X'))
|
|
{
|
|
c = s[1];
|
|
s += 2;
|
|
base = 16;
|
|
}
|
|
if (base == 0)
|
|
base = c == '0' ? 8 : 10;
|
|
|
|
/*
|
|
** Compute the cutoff value between legal numbers and illegal
|
|
** numbers. That is the largest legal value, divided by the
|
|
** base. An input number that is greater than this value, if
|
|
** followed by a legal input character, is too big. One that
|
|
** is equal to this value may be valid or not; the limit
|
|
** between valid and invalid numbers is then based on the last
|
|
** digit. For instance, if the range for long-long's is
|
|
** [-9223372036854775808..9223372036854775807] and the input base
|
|
** is 10, cutoff will be set to 922337203685477580 and cutlim to
|
|
** either 7 (!neg) or 8 (neg), meaning that if we have
|
|
** accumulated a value > 922337203685477580, or equal but the
|
|
** next digit is > 7 (or 8), the number is too big, and we will
|
|
** return a range error.
|
|
**
|
|
** Set any if any `digits' consumed; make it negative to indicate
|
|
** overflow.
|
|
*/
|
|
|
|
cutoff = neg ? LLONG_MIN : LLONG_MAX;
|
|
cutlim = cutoff % base;
|
|
cutoff /= base;
|
|
if (neg)
|
|
{
|
|
if (cutlim > 0)
|
|
{
|
|
cutlim -= base;
|
|
cutoff += 1;
|
|
}
|
|
cutlim = -cutlim;
|
|
}
|
|
for (acc = 0, any = 0;; c = (unsigned char) *s++)
|
|
{
|
|
if (isascii(c) && isdigit(c))
|
|
c -= '0';
|
|
else if (isascii(c) && isalpha(c))
|
|
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
|
else
|
|
break;
|
|
if (c >= base)
|
|
break;
|
|
if (any < 0)
|
|
continue;
|
|
if (neg)
|
|
{
|
|
if (acc < cutoff || (acc == cutoff && c > cutlim))
|
|
{
|
|
any = -1;
|
|
acc = LLONG_MIN;
|
|
errno = ERANGE;
|
|
}
|
|
else
|
|
{
|
|
any = 1;
|
|
acc *= base;
|
|
acc -= c;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (acc > cutoff || (acc == cutoff && c > cutlim))
|
|
{
|
|
any = -1;
|
|
acc = LLONG_MAX;
|
|
errno = ERANGE;
|
|
}
|
|
else
|
|
{
|
|
any = 1;
|
|
acc *= base;
|
|
acc += c;
|
|
}
|
|
}
|
|
}
|
|
if (endptr != 0)
|
|
*endptr = (char *) (any ? s - 1 : nptr);
|
|
return acc;
|
|
}
|
|
|
|
/*
|
|
** SM_STRTOULL -- Convert a string to an unsigned long long integer.
|
|
**
|
|
** Ignores `locale' stuff. Assumes that the upper and lower case
|
|
** alphabets and digits are each contiguous.
|
|
**
|
|
** Parameters:
|
|
** nptr -- string containing (unsigned) number
|
|
** endptr -- location of first invalid character
|
|
** base -- numeric base that 'nptr' number is based in
|
|
**
|
|
** Returns:
|
|
** Failure: on overflow ULLONG_MAX is returned and errno is set.
|
|
** When 'endptr' == '\0' then the entire string 'nptr'
|
|
** was valid.
|
|
** Success: returns the converted number
|
|
*/
|
|
|
|
ULONGLONG_T
|
|
sm_strtoull(nptr, endptr, base)
|
|
const char *nptr;
|
|
char **endptr;
|
|
register int base;
|
|
{
|
|
register const char *s;
|
|
register ULONGLONG_T acc, cutoff;
|
|
register int c;
|
|
register bool neg;
|
|
register int any, cutlim;
|
|
|
|
/* See sm_strtoll for comments as to the logic used. */
|
|
s = nptr;
|
|
do
|
|
{
|
|
c = (unsigned char) *s++;
|
|
} while (isascii(c) && isspace(c));
|
|
neg = (c == '-');
|
|
if (neg)
|
|
{
|
|
c = *s++;
|
|
}
|
|
else
|
|
{
|
|
if (c == '+')
|
|
c = *s++;
|
|
}
|
|
if ((base == 0 || base == 16) &&
|
|
c == '0' && (*s == 'x' || *s == 'X'))
|
|
{
|
|
c = s[1];
|
|
s += 2;
|
|
base = 16;
|
|
}
|
|
if (base == 0)
|
|
base = c == '0' ? 8 : 10;
|
|
|
|
cutoff = ULLONG_MAX / (ULONGLONG_T)base;
|
|
cutlim = ULLONG_MAX % (ULONGLONG_T)base;
|
|
for (acc = 0, any = 0;; c = (unsigned char) *s++)
|
|
{
|
|
if (isascii(c) && isdigit(c))
|
|
c -= '0';
|
|
else if (isascii(c) && isalpha(c))
|
|
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
|
else
|
|
break;
|
|
if (c >= base)
|
|
break;
|
|
if (any < 0)
|
|
continue;
|
|
if (acc > cutoff || (acc == cutoff && c > cutlim))
|
|
{
|
|
any = -1;
|
|
acc = ULLONG_MAX;
|
|
errno = ERANGE;
|
|
}
|
|
else
|
|
{
|
|
any = 1;
|
|
acc *= (ULONGLONG_T)base;
|
|
acc += c;
|
|
}
|
|
}
|
|
if (neg && any > 0)
|
|
acc = -((LONGLONG_T) acc);
|
|
if (endptr != 0)
|
|
*endptr = (char *) (any ? s - 1 : nptr);
|
|
return acc;
|
|
}
|