Blame lib/xstrtol.c

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