Blame utils/strtol.c

Packit 67cb25
/* Copyright (C) 1991, 1992, 1994 Free Software Foundation, Inc.
Packit 67cb25
Packit 67cb25
This file is part of the GNU C Library.
Packit 67cb25
Packit 67cb25
The GNU C Library is free software; you can redistribute it and/or
Packit 67cb25
modify it under the terms of the GNU Library General Public License as
Packit 67cb25
published by the Free Software Foundation; either version 3 of the
Packit 67cb25
License, or (at your option) any later version.
Packit 67cb25
Packit 67cb25
The GNU C Library is distributed in the hope that it will be useful,
Packit 67cb25
but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67cb25
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 67cb25
Library General Public License for more details.
Packit 67cb25
Packit 67cb25
You should have received a copy of the GNU Library General Public
Packit 67cb25
License along with the GNU C Library; see the file COPYING.LIB.  If
Packit 67cb25
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Packit 67cb25
Cambridge, MA 02110-1301, USA.  */
Packit 67cb25
Packit 67cb25
#include <ctype.h>
Packit 67cb25
#include <limits.h>
Packit 67cb25
#include <stddef.h>
Packit 67cb25
#include <stdlib.h>
Packit 67cb25
#include <errno.h>
Packit 67cb25
Packit 67cb25
#ifndef UNSIGNED
Packit 67cb25
#define UNSIGNED        0
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
/* Convert NPTR to an `unsigned long int' or `long int' in base BASE.
Packit 67cb25
   If BASE is 0 the base is determined by the presence of a leading
Packit 67cb25
   zero, indicating octal or a leading "0x" or "0X", indicating hexadecimal.
Packit 67cb25
   If BASE is < 2 or > 36, it is reset to 10.
Packit 67cb25
   If ENDPTR is not NULL, a pointer to the character after the last
Packit 67cb25
   one converted is stored in *ENDPTR.  */
Packit 67cb25
#if     UNSIGNED
Packit 67cb25
unsigned long int
Packit 67cb25
#define strtol  strtoul
Packit 67cb25
#else
Packit 67cb25
long int
Packit 67cb25
#endif
Packit 67cb25
strtol (nptr, endptr, base)
Packit 67cb25
     const char *nptr;
Packit 67cb25
     char **endptr;
Packit 67cb25
     int base;
Packit 67cb25
{
Packit 67cb25
  int negative;
Packit 67cb25
  register unsigned long int cutoff;
Packit 67cb25
  register unsigned int cutlim;
Packit 67cb25
  register unsigned long int i;
Packit 67cb25
  register const char *s;
Packit 67cb25
  register unsigned char c;
Packit 67cb25
  const char *save;
Packit 67cb25
  int overflow;
Packit 67cb25
Packit 67cb25
  if (base < 0 || base == 1 || base > 36)
Packit 67cb25
    base = 10;
Packit 67cb25
Packit 67cb25
  s = nptr;
Packit 67cb25
Packit 67cb25
  /* Skip white space.  */
Packit 67cb25
  while (isspace (*s))
Packit 67cb25
    ++s;
Packit 67cb25
  if (*s == '\0')
Packit 67cb25
    goto noconv;
Packit 67cb25
Packit 67cb25
  /* Check for a sign.  */
Packit 67cb25
  if (*s == '-')
Packit 67cb25
    {
Packit 67cb25
      negative = 1;
Packit 67cb25
      ++s;
Packit 67cb25
    }
Packit 67cb25
  else if (*s == '+')
Packit 67cb25
    {
Packit 67cb25
      negative = 0;
Packit 67cb25
      ++s;
Packit 67cb25
    }
Packit 67cb25
  else
Packit 67cb25
    negative = 0;
Packit 67cb25
Packit 67cb25
  if (base == 16 && s[0] == '0' && toupper (s[1]) == 'X')
Packit 67cb25
    s += 2;
Packit 67cb25
Packit 67cb25
  /* If BASE is zero, figure it out ourselves.  */
Packit 67cb25
  if (base == 0)
Packit 67cb25
    if (*s == '0')
Packit 67cb25
      {
Packit 67cb25
        if (toupper (s[1]) == 'X')
Packit 67cb25
          {
Packit 67cb25
            s += 2;
Packit 67cb25
            base = 16;
Packit 67cb25
          }
Packit 67cb25
        else
Packit 67cb25
          base = 8;
Packit 67cb25
      }
Packit 67cb25
    else
Packit 67cb25
      base = 10;
Packit 67cb25
Packit 67cb25
  /* Save the pointer so we can check later if anything happened.  */
Packit 67cb25
  save = s;
Packit 67cb25
Packit 67cb25
  cutoff = ULONG_MAX / (unsigned long int) base;
Packit 67cb25
  cutlim = ULONG_MAX % (unsigned long int) base;
Packit 67cb25
Packit 67cb25
  overflow = 0;
Packit 67cb25
  i = 0;
Packit 67cb25
  for (c = *s; c != '\0'; c = *++s)
Packit 67cb25
    {
Packit 67cb25
      if (isdigit (c))
Packit 67cb25
        c -= '0';
Packit 67cb25
      else if (isalpha (c))
Packit 67cb25
        c = toupper (c) - 'A' + 10;
Packit 67cb25
      else
Packit 67cb25
        break;
Packit 67cb25
      if (c >= base)
Packit 67cb25
        break;
Packit 67cb25
      /* Check for overflow.  */
Packit 67cb25
      if (i > cutoff || (i == cutoff && c > cutlim))
Packit 67cb25
        overflow = 1;
Packit 67cb25
      else
Packit 67cb25
        {
Packit 67cb25
          i *= (unsigned long int) base;
Packit 67cb25
          i += c;
Packit 67cb25
        }
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* Check if anything actually happened.  */
Packit 67cb25
  if (s == save)
Packit 67cb25
    goto noconv;
Packit 67cb25
Packit 67cb25
  /* Store in ENDPTR the address of one character
Packit 67cb25
     past the last character we converted.  */
Packit 67cb25
  if (endptr != NULL)
Packit 67cb25
    *endptr = (char *) s;
Packit 67cb25
Packit 67cb25
#if     !UNSIGNED
Packit 67cb25
  /* Check for a value that is within the range of
Packit 67cb25
     `unsigned long int', but outside the range of `long int'.  */
Packit 67cb25
  if (i > (negative ?
Packit 67cb25
           -(unsigned long int) LONG_MIN : (unsigned long int) LONG_MAX))
Packit 67cb25
    overflow = 1;
Packit 67cb25
#endif
Packit 67cb25
Packit 67cb25
  if (overflow)
Packit 67cb25
    {
Packit 67cb25
      errno = ERANGE;
Packit 67cb25
#if     UNSIGNED
Packit 67cb25
      return ULONG_MAX;
Packit 67cb25
#else
Packit 67cb25
      return negative ? LONG_MIN : LONG_MAX;
Packit 67cb25
#endif
Packit 67cb25
    }
Packit 67cb25
Packit 67cb25
  /* Return the result of the appropriate sign.  */
Packit 67cb25
  return (negative ? -i : i);
Packit 67cb25
Packit 67cb25
noconv:
Packit 67cb25
  /* There was no number to convert.  */
Packit 67cb25
  if (endptr != NULL)
Packit 67cb25
    *endptr = (char *) nptr;
Packit 67cb25
  return 0L;
Packit 67cb25
}