Blame lib/xstrtol.c

Packit 709fb3
/* A more useful interface to strtol.
Packit 709fb3
Packit 709fb3
   Copyright (C) 1995-1996, 1998-2001, 2003-2007, 2009-2017 Free Software
Packit 709fb3
   Foundation, Inc.
Packit 709fb3
Packit 709fb3
   This program is free software: you can redistribute it and/or modify
Packit 709fb3
   it under the terms of the GNU General Public License as published by
Packit 709fb3
   the Free Software Foundation; either version 3 of the License, or
Packit 709fb3
   (at your option) any later version.
Packit 709fb3
Packit 709fb3
   This program is distributed in the hope that it will be useful,
Packit 709fb3
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 709fb3
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 709fb3
   GNU General Public License for more details.
Packit 709fb3
Packit 709fb3
   You should have received a copy of the GNU General Public License
Packit 709fb3
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 709fb3
Packit 709fb3
/* Written by Jim Meyering. */
Packit 709fb3
Packit 709fb3
#ifndef __strtol
Packit 709fb3
# define __strtol strtol
Packit 709fb3
# define __strtol_t long int
Packit 709fb3
# define __xstrtol xstrtol
Packit 709fb3
# define STRTOL_T_MINIMUM LONG_MIN
Packit 709fb3
# define STRTOL_T_MAXIMUM LONG_MAX
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#include <config.h>
Packit 709fb3
Packit 709fb3
#include "xstrtol.h"
Packit 709fb3
Packit 709fb3
/* Some pre-ANSI implementations (e.g. SunOS 4)
Packit 709fb3
   need stderr defined if assertion checking is enabled.  */
Packit 709fb3
#include <stdio.h>
Packit 709fb3
Packit 709fb3
#include <ctype.h>
Packit 709fb3
#include <errno.h>
Packit 709fb3
#include <limits.h>
Packit 709fb3
#include <stdlib.h>
Packit 709fb3
#include <string.h>
Packit 709fb3
Packit 709fb3
#include "assure.h"
Packit 709fb3
#include "intprops.h"
Packit 709fb3
Packit 709fb3
/* xstrtoll.c and xstrtoull.c, which include this file, require that
Packit 709fb3
   ULLONG_MAX, LLONG_MAX, LLONG_MIN are defined, but <limits.h> does not
Packit 709fb3
   define them on all platforms.  */
Packit 709fb3
#ifndef ULLONG_MAX
Packit 709fb3
# define ULLONG_MAX TYPE_MAXIMUM (unsigned long long)
Packit 709fb3
#endif
Packit 709fb3
#ifndef LLONG_MAX
Packit 709fb3
# define LLONG_MAX TYPE_MAXIMUM (long long int)
Packit 709fb3
#endif
Packit 709fb3
#ifndef LLONG_MIN
Packit 709fb3
# define LLONG_MIN TYPE_MINIMUM (long long int)
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
static strtol_error
Packit 709fb3
bkm_scale (__strtol_t *x, int scale_factor)
Packit 709fb3
{
Packit 709fb3
  if (TYPE_SIGNED (__strtol_t) && *x < STRTOL_T_MINIMUM / scale_factor)
Packit 709fb3
    {
Packit 709fb3
      *x = STRTOL_T_MINIMUM;
Packit 709fb3
      return LONGINT_OVERFLOW;
Packit 709fb3
    }
Packit 709fb3
  if (STRTOL_T_MAXIMUM / scale_factor < *x)
Packit 709fb3
    {
Packit 709fb3
      *x = STRTOL_T_MAXIMUM;
Packit 709fb3
      return LONGINT_OVERFLOW;
Packit 709fb3
    }
Packit 709fb3
  *x *= scale_factor;
Packit 709fb3
  return LONGINT_OK;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
static strtol_error
Packit 709fb3
bkm_scale_by_power (__strtol_t *x, int base, int power)
Packit 709fb3
{
Packit 709fb3
  strtol_error err = LONGINT_OK;
Packit 709fb3
  while (power--)
Packit 709fb3
    err |= bkm_scale (x, base);
Packit 709fb3
  return err;
Packit 709fb3
}
Packit 709fb3
Packit 709fb3
/* FIXME: comment.  */
Packit 709fb3
Packit 709fb3
strtol_error
Packit 709fb3
__xstrtol (const char *s, char **ptr, int strtol_base,
Packit 709fb3
           __strtol_t *val, const char *valid_suffixes)
Packit 709fb3
{
Packit 709fb3
  char *t_ptr;
Packit 709fb3
  char **p;
Packit 709fb3
  __strtol_t tmp;
Packit 709fb3
  strtol_error err = LONGINT_OK;
Packit 709fb3
Packit 709fb3
  assure (0 <= strtol_base && strtol_base <= 36);
Packit 709fb3
Packit 709fb3
  p = (ptr ? ptr : &t_ptr);
Packit 709fb3
Packit 709fb3
  errno = 0;
Packit 709fb3
Packit 709fb3
  if (! TYPE_SIGNED (__strtol_t))
Packit 709fb3
    {
Packit 709fb3
      const char *q = s;
Packit 709fb3
      unsigned char ch = *q;
Packit 709fb3
      while (isspace (ch))
Packit 709fb3
        ch = *++q;
Packit 709fb3
      if (ch == '-')
Packit 709fb3
        return LONGINT_INVALID;
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  tmp = __strtol (s, p, strtol_base);
Packit 709fb3
Packit 709fb3
  if (*p == s)
Packit 709fb3
    {
Packit 709fb3
      /* If there is no number but there is a valid suffix, assume the
Packit 709fb3
         number is 1.  The string is invalid otherwise.  */
Packit 709fb3
      if (valid_suffixes && **p && strchr (valid_suffixes, **p))
Packit 709fb3
        tmp = 1;
Packit 709fb3
      else
Packit 709fb3
        return LONGINT_INVALID;
Packit 709fb3
    }
Packit 709fb3
  else if (errno != 0)
Packit 709fb3
    {
Packit 709fb3
      if (errno != ERANGE)
Packit 709fb3
        return LONGINT_INVALID;
Packit 709fb3
      err = LONGINT_OVERFLOW;
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  /* Let valid_suffixes == NULL mean "allow any suffix".  */
Packit 709fb3
  /* FIXME: update all callers except the ones that allow suffixes
Packit 709fb3
     after the number, changing last parameter NULL to "".  */
Packit 709fb3
  if (!valid_suffixes)
Packit 709fb3
    {
Packit 709fb3
      *val = tmp;
Packit 709fb3
      return err;
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  if (**p != '\0')
Packit 709fb3
    {
Packit 709fb3
      int base = 1024;
Packit 709fb3
      int suffixes = 1;
Packit 709fb3
      strtol_error overflow;
Packit 709fb3
Packit 709fb3
      if (!strchr (valid_suffixes, **p))
Packit 709fb3
        {
Packit 709fb3
          *val = tmp;
Packit 709fb3
          return err | LONGINT_INVALID_SUFFIX_CHAR;
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
      switch (**p)
Packit 709fb3
        {
Packit 709fb3
        case 'E': case 'G': case 'g': case 'k': case 'K': case 'M': case 'm':
Packit 709fb3
        case 'P': case 'T': case 't': case 'Y': case 'Z':
Packit 709fb3
Packit 709fb3
          /* The "valid suffix" '0' is a special flag meaning that
Packit 709fb3
             an optional second suffix is allowed, which can change
Packit 709fb3
             the base.  A suffix "B" (e.g. "100MB") stands for a power
Packit 709fb3
             of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for
Packit 709fb3
             a power of 1024.  If no suffix (e.g. "100M"), assume
Packit 709fb3
             power-of-1024.  */
Packit 709fb3
Packit 709fb3
          if (strchr (valid_suffixes, '0'))
Packit 709fb3
            switch (p[0][1])
Packit 709fb3
              {
Packit 709fb3
              case 'i':
Packit 709fb3
                if (p[0][2] == 'B')
Packit 709fb3
                  suffixes += 2;
Packit 709fb3
                break;
Packit 709fb3
Packit 709fb3
              case 'B':
Packit 709fb3
              case 'D': /* 'D' is obsolescent */
Packit 709fb3
                base = 1000;
Packit 709fb3
                suffixes++;
Packit 709fb3
                break;
Packit 709fb3
              }
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
      switch (**p)
Packit 709fb3
        {
Packit 709fb3
        case 'b':
Packit 709fb3
          overflow = bkm_scale (&tmp, 512);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'B':
Packit 709fb3
          /* This obsolescent first suffix is distinct from the 'B'
Packit 709fb3
             second suffix above.  E.g., 'tar -L 1000B' means change
Packit 709fb3
             the tape after writing 1000 KiB of data.  */
Packit 709fb3
          overflow = bkm_scale (&tmp, 1024);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'c':
Packit 709fb3
          overflow = LONGINT_OK;
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'E': /* exa or exbi */
Packit 709fb3
          overflow = bkm_scale_by_power (&tmp, base, 6);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'G': /* giga or gibi */
Packit 709fb3
        case 'g': /* 'g' is undocumented; for compatibility only */
Packit 709fb3
          overflow = bkm_scale_by_power (&tmp, base, 3);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'k': /* kilo */
Packit 709fb3
        case 'K': /* kibi */
Packit 709fb3
          overflow = bkm_scale_by_power (&tmp, base, 1);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'M': /* mega or mebi */
Packit 709fb3
        case 'm': /* 'm' is undocumented; for compatibility only */
Packit 709fb3
          overflow = bkm_scale_by_power (&tmp, base, 2);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'P': /* peta or pebi */
Packit 709fb3
          overflow = bkm_scale_by_power (&tmp, base, 5);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'T': /* tera or tebi */
Packit 709fb3
        case 't': /* 't' is undocumented; for compatibility only */
Packit 709fb3
          overflow = bkm_scale_by_power (&tmp, base, 4);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'w':
Packit 709fb3
          overflow = bkm_scale (&tmp, 2);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'Y': /* yotta or 2**80 */
Packit 709fb3
          overflow = bkm_scale_by_power (&tmp, base, 8);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        case 'Z': /* zetta or 2**70 */
Packit 709fb3
          overflow = bkm_scale_by_power (&tmp, base, 7);
Packit 709fb3
          break;
Packit 709fb3
Packit 709fb3
        default:
Packit 709fb3
          *val = tmp;
Packit 709fb3
          return err | LONGINT_INVALID_SUFFIX_CHAR;
Packit 709fb3
        }
Packit 709fb3
Packit 709fb3
      err |= overflow;
Packit 709fb3
      *p += suffixes;
Packit 709fb3
      if (**p)
Packit 709fb3
        err |= LONGINT_INVALID_SUFFIX_CHAR;
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
  *val = tmp;
Packit 709fb3
  return err;
Packit 709fb3
}