Blame lib/xstrtol.c

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