Blame lib/xstrtol.c

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