Blame lib/xstrtol.c

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