Blame lib/strtol.c

Packit 8f70b4
/* Convert string representation of a number into an integer value.
Packit 8f70b4
Packit 8f70b4
   Copyright (C) 1991-1992, 1994-1999, 2003, 2005-2007, 2009-2018 Free Software
Packit 8f70b4
   Foundation, Inc.
Packit 8f70b4
Packit 8f70b4
   NOTE: The canonical source of this file is maintained with the GNU C
Packit 8f70b4
   Library.  Bugs can be reported to bug-glibc@gnu.org.
Packit 8f70b4
Packit 8f70b4
   This program is free software: you can redistribute it and/or modify it
Packit 8f70b4
   under the terms of the GNU General Public License as published by the
Packit 8f70b4
   Free Software Foundation; either version 3 of the License, or any
Packit 8f70b4
   later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License
Packit 8f70b4
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
#ifdef _LIBC
Packit 8f70b4
# define USE_NUMBER_GROUPING
Packit 8f70b4
#else
Packit 8f70b4
# include <config.h>
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#include <ctype.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#ifndef __set_errno
Packit 8f70b4
# define __set_errno(Val) errno = (Val)
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#include <limits.h>
Packit 8f70b4
#include <stddef.h>
Packit 8f70b4
#include <stdlib.h>
Packit 8f70b4
#include <string.h>
Packit 8f70b4
Packit 8f70b4
#ifdef USE_NUMBER_GROUPING
Packit 8f70b4
# include "../locale/localeinfo.h"
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* Nonzero if we are defining 'strtoul' or 'strtoull', operating on
Packit 8f70b4
   unsigned integers.  */
Packit 8f70b4
#ifndef UNSIGNED
Packit 8f70b4
# define UNSIGNED 0
Packit 8f70b4
# define INT LONG int
Packit 8f70b4
#else
Packit 8f70b4
# define INT unsigned LONG int
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* Determine the name.  */
Packit 8f70b4
#ifdef USE_IN_EXTENDED_LOCALE_MODEL
Packit 8f70b4
# if UNSIGNED
Packit 8f70b4
#  ifdef USE_WIDE_CHAR
Packit 8f70b4
#   ifdef QUAD
Packit 8f70b4
#    define strtol __wcstoull_l
Packit 8f70b4
#   else
Packit 8f70b4
#    define strtol __wcstoul_l
Packit 8f70b4
#   endif
Packit 8f70b4
#  else
Packit 8f70b4
#   ifdef QUAD
Packit 8f70b4
#    define strtol __strtoull_l
Packit 8f70b4
#   else
Packit 8f70b4
#    define strtol __strtoul_l
Packit 8f70b4
#   endif
Packit 8f70b4
#  endif
Packit 8f70b4
# else
Packit 8f70b4
#  ifdef USE_WIDE_CHAR
Packit 8f70b4
#   ifdef QUAD
Packit 8f70b4
#    define strtol __wcstoll_l
Packit 8f70b4
#   else
Packit 8f70b4
#    define strtol __wcstol_l
Packit 8f70b4
#   endif
Packit 8f70b4
#  else
Packit 8f70b4
#   ifdef QUAD
Packit 8f70b4
#    define strtol __strtoll_l
Packit 8f70b4
#   else
Packit 8f70b4
#    define strtol __strtol_l
Packit 8f70b4
#   endif
Packit 8f70b4
#  endif
Packit 8f70b4
# endif
Packit 8f70b4
#else
Packit 8f70b4
# if UNSIGNED
Packit 8f70b4
#  ifdef USE_WIDE_CHAR
Packit 8f70b4
#   ifdef QUAD
Packit 8f70b4
#    define strtol wcstoull
Packit 8f70b4
#   else
Packit 8f70b4
#    define strtol wcstoul
Packit 8f70b4
#   endif
Packit 8f70b4
#  else
Packit 8f70b4
#   ifdef QUAD
Packit 8f70b4
#    define strtol strtoull
Packit 8f70b4
#   else
Packit 8f70b4
#    define strtol strtoul
Packit 8f70b4
#   endif
Packit 8f70b4
#  endif
Packit 8f70b4
# else
Packit 8f70b4
#  ifdef USE_WIDE_CHAR
Packit 8f70b4
#   ifdef QUAD
Packit 8f70b4
#    define strtol wcstoll
Packit 8f70b4
#   else
Packit 8f70b4
#    define strtol wcstol
Packit 8f70b4
#   endif
Packit 8f70b4
#  else
Packit 8f70b4
#   ifdef QUAD
Packit 8f70b4
#    define strtol strtoll
Packit 8f70b4
#   endif
Packit 8f70b4
#  endif
Packit 8f70b4
# endif
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/* If QUAD is defined, we are defining 'strtoll' or 'strtoull',
Packit 8f70b4
   operating on 'long long int's.  */
Packit 8f70b4
#ifdef QUAD
Packit 8f70b4
# define LONG long long
Packit 8f70b4
# define STRTOL_LONG_MIN LLONG_MIN
Packit 8f70b4
# define STRTOL_LONG_MAX LLONG_MAX
Packit 8f70b4
# define STRTOL_ULONG_MAX ULLONG_MAX
Packit 8f70b4
Packit 8f70b4
/* The extra casts in the following macros work around compiler bugs,
Packit 8f70b4
   e.g., in Cray C 5.0.3.0.  */
Packit 8f70b4
Packit 8f70b4
/* True if the arithmetic type T is signed.  */
Packit 8f70b4
# define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
Packit 8f70b4
Packit 8f70b4
/* Minimum and maximum values for integer types.
Packit 8f70b4
   These macros have undefined behavior for signed types that either
Packit 8f70b4
   have padding bits or do not use two's complement.  If this is a
Packit 8f70b4
   problem for you, please let us know how to fix it for your host.  */
Packit 8f70b4
Packit 8f70b4
/* The maximum and minimum values for the integer type T.  */
Packit 8f70b4
# define TYPE_MINIMUM(t) ((t) ~ TYPE_MAXIMUM (t))
Packit 8f70b4
# define TYPE_MAXIMUM(t)                                                 \
Packit 8f70b4
   ((t) (! TYPE_SIGNED (t)                                               \
Packit 8f70b4
         ? (t) -1                                                        \
Packit 8f70b4
         : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1)))
Packit 8f70b4
Packit 8f70b4
# ifndef ULLONG_MAX
Packit 8f70b4
#  define ULLONG_MAX TYPE_MAXIMUM (unsigned long long)
Packit 8f70b4
# endif
Packit 8f70b4
# ifndef LLONG_MAX
Packit 8f70b4
#  define LLONG_MAX TYPE_MAXIMUM (long long int)
Packit 8f70b4
# endif
Packit 8f70b4
# ifndef LLONG_MIN
Packit 8f70b4
#  define LLONG_MIN TYPE_MINIMUM (long long int)
Packit 8f70b4
# endif
Packit 8f70b4
Packit 8f70b4
# if __GNUC__ == 2 && __GNUC_MINOR__ < 7
Packit 8f70b4
   /* Work around gcc bug with using this constant.  */
Packit 8f70b4
   static const unsigned long long int maxquad = ULLONG_MAX;
Packit 8f70b4
#  undef STRTOL_ULONG_MAX
Packit 8f70b4
#  define STRTOL_ULONG_MAX maxquad
Packit 8f70b4
# endif
Packit 8f70b4
#else
Packit 8f70b4
# define LONG long
Packit 8f70b4
# define STRTOL_LONG_MIN LONG_MIN
Packit 8f70b4
# define STRTOL_LONG_MAX LONG_MAX
Packit 8f70b4
# define STRTOL_ULONG_MAX ULONG_MAX
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
/* We use this code also for the extended locale handling where the
Packit 8f70b4
   function gets as an additional argument the locale which has to be
Packit 8f70b4
   used.  To access the values we have to redefine the _NL_CURRENT
Packit 8f70b4
   macro.  */
Packit 8f70b4
#ifdef USE_IN_EXTENDED_LOCALE_MODEL
Packit 8f70b4
# undef _NL_CURRENT
Packit 8f70b4
# define _NL_CURRENT(category, item) \
Packit 8f70b4
  (current->values[_NL_ITEM_INDEX (item)].string)
Packit 8f70b4
# define LOCALE_PARAM , loc
Packit 8f70b4
# define LOCALE_PARAM_PROTO , __locale_t loc
Packit 8f70b4
#else
Packit 8f70b4
# define LOCALE_PARAM
Packit 8f70b4
# define LOCALE_PARAM_PROTO
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#ifdef USE_WIDE_CHAR
Packit 8f70b4
# include <wchar.h>
Packit 8f70b4
# include <wctype.h>
Packit 8f70b4
# define L_(Ch) L##Ch
Packit 8f70b4
# define UCHAR_TYPE wint_t
Packit 8f70b4
# define STRING_TYPE wchar_t
Packit 8f70b4
# ifdef USE_IN_EXTENDED_LOCALE_MODEL
Packit 8f70b4
#  define ISSPACE(Ch) __iswspace_l ((Ch), loc)
Packit 8f70b4
#  define ISALPHA(Ch) __iswalpha_l ((Ch), loc)
Packit 8f70b4
#  define TOUPPER(Ch) __towupper_l ((Ch), loc)
Packit 8f70b4
# else
Packit 8f70b4
#  define ISSPACE(Ch) iswspace (Ch)
Packit 8f70b4
#  define ISALPHA(Ch) iswalpha (Ch)
Packit 8f70b4
#  define TOUPPER(Ch) towupper (Ch)
Packit 8f70b4
# endif
Packit 8f70b4
#else
Packit 8f70b4
# define L_(Ch) Ch
Packit 8f70b4
# define UCHAR_TYPE unsigned char
Packit 8f70b4
# define STRING_TYPE char
Packit 8f70b4
# ifdef USE_IN_EXTENDED_LOCALE_MODEL
Packit 8f70b4
#  define ISSPACE(Ch) __isspace_l ((Ch), loc)
Packit 8f70b4
#  define ISALPHA(Ch) __isalpha_l ((Ch), loc)
Packit 8f70b4
#  define TOUPPER(Ch) __toupper_l ((Ch), loc)
Packit 8f70b4
# else
Packit 8f70b4
#  define ISSPACE(Ch) isspace (Ch)
Packit 8f70b4
#  define ISALPHA(Ch) isalpha (Ch)
Packit 8f70b4
#  define TOUPPER(Ch) toupper (Ch)
Packit 8f70b4
# endif
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#define INTERNAL(X) INTERNAL1(X)
Packit 8f70b4
#define INTERNAL1(X) __##X##_internal
Packit 8f70b4
#define WEAKNAME(X) WEAKNAME1(X)
Packit 8f70b4
Packit 8f70b4
#ifdef USE_NUMBER_GROUPING
Packit 8f70b4
/* This file defines a function to check for correct grouping.  */
Packit 8f70b4
# include "grouping.h"
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
/* Convert NPTR to an 'unsigned long int' or 'long int' in base BASE.
Packit 8f70b4
   If BASE is 0 the base is determined by the presence of a leading
Packit 8f70b4
   zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal.
Packit 8f70b4
   If BASE is < 2 or > 36, it is reset to 10.
Packit 8f70b4
   If ENDPTR is not NULL, a pointer to the character after the last
Packit 8f70b4
   one converted is stored in *ENDPTR.  */
Packit 8f70b4
Packit 8f70b4
INT
Packit 8f70b4
INTERNAL (strtol) (const STRING_TYPE *nptr, STRING_TYPE **endptr,
Packit 8f70b4
                   int base, int group LOCALE_PARAM_PROTO)
Packit 8f70b4
{
Packit 8f70b4
  int negative;
Packit 8f70b4
  register unsigned LONG int cutoff;
Packit 8f70b4
  register unsigned int cutlim;
Packit 8f70b4
  register unsigned LONG int i;
Packit 8f70b4
  register const STRING_TYPE *s;
Packit 8f70b4
  register UCHAR_TYPE c;
Packit 8f70b4
  const STRING_TYPE *save, *end;
Packit 8f70b4
  int overflow;
Packit 8f70b4
Packit 8f70b4
#ifdef USE_NUMBER_GROUPING
Packit 8f70b4
# ifdef USE_IN_EXTENDED_LOCALE_MODEL
Packit 8f70b4
  struct locale_data *current = loc->__locales[LC_NUMERIC];
Packit 8f70b4
# endif
Packit 8f70b4
  /* The thousands character of the current locale.  */
Packit 8f70b4
  wchar_t thousands = L'\0';
Packit 8f70b4
  /* The numeric grouping specification of the current locale,
Packit 8f70b4
     in the format described in <locale.h>.  */
Packit 8f70b4
  const char *grouping;
Packit 8f70b4
Packit 8f70b4
  if (group)
Packit 8f70b4
    {
Packit 8f70b4
      grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
Packit 8f70b4
      if (*grouping <= 0 || *grouping == CHAR_MAX)
Packit 8f70b4
        grouping = NULL;
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          /* Figure out the thousands separator character.  */
Packit 8f70b4
# if defined _LIBC || defined _HAVE_BTOWC
Packit 8f70b4
          thousands = __btowc (*_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP));
Packit 8f70b4
          if (thousands == WEOF)
Packit 8f70b4
            thousands = L'\0';
Packit 8f70b4
# endif
Packit 8f70b4
          if (thousands == L'\0')
Packit 8f70b4
            grouping = NULL;
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    grouping = NULL;
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
  if (base < 0 || base == 1 || base > 36)
Packit 8f70b4
    {
Packit 8f70b4
      __set_errno (EINVAL);
Packit 8f70b4
      return 0;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  save = s = nptr;
Packit 8f70b4
Packit 8f70b4
  /* Skip white space.  */
Packit 8f70b4
  while (ISSPACE (*s))
Packit 8f70b4
    ++s;
Packit 8f70b4
  if (*s == L_('\0'))
Packit 8f70b4
    goto noconv;
Packit 8f70b4
Packit 8f70b4
  /* Check for a sign.  */
Packit 8f70b4
  if (*s == L_('-'))
Packit 8f70b4
    {
Packit 8f70b4
      negative = 1;
Packit 8f70b4
      ++s;
Packit 8f70b4
    }
Packit 8f70b4
  else if (*s == L_('+'))
Packit 8f70b4
    {
Packit 8f70b4
      negative = 0;
Packit 8f70b4
      ++s;
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
    negative = 0;
Packit 8f70b4
Packit 8f70b4
  /* Recognize number prefix and if BASE is zero, figure it out ourselves.  */
Packit 8f70b4
  if (*s == L_('0'))
Packit 8f70b4
    {
Packit 8f70b4
      if ((base == 0 || base == 16) && TOUPPER (s[1]) == L_('X'))
Packit 8f70b4
        {
Packit 8f70b4
          s += 2;
Packit 8f70b4
          base = 16;
Packit 8f70b4
        }
Packit 8f70b4
      else if (base == 0)
Packit 8f70b4
        base = 8;
Packit 8f70b4
    }
Packit 8f70b4
  else if (base == 0)
Packit 8f70b4
    base = 10;
Packit 8f70b4
Packit 8f70b4
  /* Save the pointer so we can check later if anything happened.  */
Packit 8f70b4
  save = s;
Packit 8f70b4
Packit 8f70b4
#ifdef USE_NUMBER_GROUPING
Packit 8f70b4
  if (group)
Packit 8f70b4
    {
Packit 8f70b4
      /* Find the end of the digit string and check its grouping.  */
Packit 8f70b4
      end = s;
Packit 8f70b4
      for (c = *end; c != L_('\0'); c = *++end)
Packit 8f70b4
        if ((wchar_t) c != thousands
Packit 8f70b4
            && ((wchar_t) c < L_('0') || (wchar_t) c > L_('9'))
Packit 8f70b4
            && (!ISALPHA (c) || (int) (TOUPPER (c) - L_('A') + 10) >= base))
Packit 8f70b4
          break;
Packit 8f70b4
      if (*s == thousands)
Packit 8f70b4
        end = s;
Packit 8f70b4
      else
Packit 8f70b4
        end = correctly_grouped_prefix (s, end, thousands, grouping);
Packit 8f70b4
    }
Packit 8f70b4
  else
Packit 8f70b4
#endif
Packit 8f70b4
    end = NULL;
Packit 8f70b4
Packit 8f70b4
  cutoff = STRTOL_ULONG_MAX / (unsigned LONG int) base;
Packit 8f70b4
  cutlim = STRTOL_ULONG_MAX % (unsigned LONG int) base;
Packit 8f70b4
Packit 8f70b4
  overflow = 0;
Packit 8f70b4
  i = 0;
Packit 8f70b4
  for (c = *s; c != L_('\0'); c = *++s)
Packit 8f70b4
    {
Packit 8f70b4
      if (s == end)
Packit 8f70b4
        break;
Packit 8f70b4
      if (c >= L_('0') && c <= L_('9'))
Packit 8f70b4
        c -= L_('0');
Packit 8f70b4
      else if (ISALPHA (c))
Packit 8f70b4
        c = TOUPPER (c) - L_('A') + 10;
Packit 8f70b4
      else
Packit 8f70b4
        break;
Packit 8f70b4
      if ((int) c >= base)
Packit 8f70b4
        break;
Packit 8f70b4
      /* Check for overflow.  */
Packit 8f70b4
      if (i > cutoff || (i == cutoff && c > cutlim))
Packit 8f70b4
        overflow = 1;
Packit 8f70b4
      else
Packit 8f70b4
        {
Packit 8f70b4
          i *= (unsigned LONG int) base;
Packit 8f70b4
          i += c;
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  /* Check if anything actually happened.  */
Packit 8f70b4
  if (s == save)
Packit 8f70b4
    goto noconv;
Packit 8f70b4
Packit 8f70b4
  /* Store in ENDPTR the address of one character
Packit 8f70b4
     past the last character we converted.  */
Packit 8f70b4
  if (endptr != NULL)
Packit 8f70b4
    *endptr = (STRING_TYPE *) s;
Packit 8f70b4
Packit 8f70b4
#if !UNSIGNED
Packit 8f70b4
  /* Check for a value that is within the range of
Packit 8f70b4
     'unsigned LONG int', but outside the range of 'LONG int'.  */
Packit 8f70b4
  if (overflow == 0
Packit 8f70b4
      && i > (negative
Packit 8f70b4
              ? -((unsigned LONG int) (STRTOL_LONG_MIN + 1)) + 1
Packit 8f70b4
              : (unsigned LONG int) STRTOL_LONG_MAX))
Packit 8f70b4
    overflow = 1;
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
  if (overflow)
Packit 8f70b4
    {
Packit 8f70b4
      __set_errno (ERANGE);
Packit 8f70b4
#if UNSIGNED
Packit 8f70b4
      return STRTOL_ULONG_MAX;
Packit 8f70b4
#else
Packit 8f70b4
      return negative ? STRTOL_LONG_MIN : STRTOL_LONG_MAX;
Packit 8f70b4
#endif
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  /* Return the result of the appropriate sign.  */
Packit 8f70b4
  return negative ? -i : i;
Packit 8f70b4
Packit 8f70b4
noconv:
Packit 8f70b4
  /* We must handle a special case here: the base is 0 or 16 and the
Packit 8f70b4
     first two characters are '0' and 'x', but the rest are no
Packit 8f70b4
     hexadecimal digits.  This is no error case.  We return 0 and
Packit 8f70b4
     ENDPTR points to the 'x'.  */
Packit 8f70b4
  if (endptr != NULL)
Packit 8f70b4
    {
Packit 8f70b4
      if (save - nptr >= 2 && TOUPPER (save[-1]) == L_('X')
Packit 8f70b4
          && save[-2] == L_('0'))
Packit 8f70b4
        *endptr = (STRING_TYPE *) &save[-1];
Packit 8f70b4
      else
Packit 8f70b4
        /*  There was no number to convert.  */
Packit 8f70b4
        *endptr = (STRING_TYPE *) nptr;
Packit 8f70b4
    }
Packit 8f70b4
Packit 8f70b4
  return 0L;
Packit 8f70b4
}
Packit 8f70b4

Packit 8f70b4
/* External user entry point.  */
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
INT
Packit 8f70b4
#ifdef weak_function
Packit 8f70b4
weak_function
Packit 8f70b4
#endif
Packit 8f70b4
strtol (const STRING_TYPE *nptr, STRING_TYPE **endptr,
Packit 8f70b4
        int base LOCALE_PARAM_PROTO)
Packit 8f70b4
{
Packit 8f70b4
  return INTERNAL (strtol) (nptr, endptr, base, 0 LOCALE_PARAM);
Packit 8f70b4
}