1/* A more useful interface to strtol.
2
3   Copyright (C) 1995-1996, 1998-2001, 2003-2007, 2009-2010 Free Software
4   Foundation, Inc.
5
6   This program is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19/* Written by Jim Meyering. */
20
21#ifndef __strtol
22# define __strtol strtol
23# define __strtol_t long int
24# define __xstrtol xstrtol
25# define STRTOL_T_MINIMUM LONG_MIN
26# define STRTOL_T_MAXIMUM LONG_MAX
27#endif
28
29#include <config.h>
30
31#include "xstrtol.h"
32
33/* Some pre-ANSI implementations (e.g. SunOS 4)
34   need stderr defined if assertion checking is enabled.  */
35#include <stdio.h>
36
37#include <assert.h>
38#include <ctype.h>
39#include <errno.h>
40#include <limits.h>
41#include <stdlib.h>
42#include <string.h>
43
44#include "intprops.h"
45
46static strtol_error
47bkm_scale (__strtol_t *x, int scale_factor)
48{
49  if (TYPE_SIGNED (__strtol_t) && *x < STRTOL_T_MINIMUM / scale_factor)
50    {
51      *x = STRTOL_T_MINIMUM;
52      return LONGINT_OVERFLOW;
53    }
54  if (STRTOL_T_MAXIMUM / scale_factor < *x)
55    {
56      *x = STRTOL_T_MAXIMUM;
57      return LONGINT_OVERFLOW;
58    }
59  *x *= scale_factor;
60  return LONGINT_OK;
61}
62
63static strtol_error
64bkm_scale_by_power (__strtol_t *x, int base, int power)
65{
66  strtol_error err = LONGINT_OK;
67  while (power--)
68    err |= bkm_scale (x, base);
69  return err;
70}
71
72/* FIXME: comment.  */
73
74strtol_error
75__xstrtol (const char *s, char **ptr, int strtol_base,
76           __strtol_t *val, const char *valid_suffixes)
77{
78  char *t_ptr;
79  char **p;
80  __strtol_t tmp;
81  strtol_error err = LONGINT_OK;
82
83  assert (0 <= strtol_base && strtol_base <= 36);
84
85  p = (ptr ? ptr : &t_ptr);
86
87  if (! TYPE_SIGNED (__strtol_t))
88    {
89      const char *q = s;
90      unsigned char ch = *q;
91      while (isspace (ch))
92        ch = *++q;
93      if (ch == '-')
94        return LONGINT_INVALID;
95    }
96
97  errno = 0;
98  tmp = __strtol (s, p, strtol_base);
99
100  if (*p == s)
101    {
102      /* If there is no number but there is a valid suffix, assume the
103         number is 1.  The string is invalid otherwise.  */
104      if (valid_suffixes && **p && strchr (valid_suffixes, **p))
105        tmp = 1;
106      else
107        return LONGINT_INVALID;
108    }
109  else if (errno != 0)
110    {
111      if (errno != ERANGE)
112        return LONGINT_INVALID;
113      err = LONGINT_OVERFLOW;
114    }
115
116  /* Let valid_suffixes == NULL mean `allow any suffix'.  */
117  /* FIXME: update all callers except the ones that allow suffixes
118     after the number, changing last parameter NULL to `""'.  */
119  if (!valid_suffixes)
120    {
121      *val = tmp;
122      return err;
123    }
124
125  if (**p != '\0')
126    {
127      int base = 1024;
128      int suffixes = 1;
129      strtol_error overflow;
130
131      if (!strchr (valid_suffixes, **p))
132        {
133          *val = tmp;
134          return err | LONGINT_INVALID_SUFFIX_CHAR;
135        }
136
137      if (strchr (valid_suffixes, '0'))
138        {
139          /* The ``valid suffix'' '0' is a special flag meaning that
140             an optional second suffix is allowed, which can change
141             the base.  A suffix "B" (e.g. "100MB") stands for a power
142             of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for
143             a power of 1024.  If no suffix (e.g. "100M"), assume
144             power-of-1024.  */
145
146          switch (p[0][1])
147            {
148            case 'i':
149              if (p[0][2] == 'B')
150                suffixes += 2;
151              break;
152
153            case 'B':
154            case 'D': /* 'D' is obsolescent */
155              base = 1000;
156              suffixes++;
157              break;
158            }
159        }
160
161      switch (**p)
162        {
163        case 'b':
164          overflow = bkm_scale (&tmp, 512);
165          break;
166
167        case 'B':
168          overflow = bkm_scale (&tmp, 1024);
169          break;
170
171        case 'c':
172          overflow = 0;
173          break;
174
175        case 'E': /* exa or exbi */
176          overflow = bkm_scale_by_power (&tmp, base, 6);
177          break;
178
179        case 'G': /* giga or gibi */
180        case 'g': /* 'g' is undocumented; for compatibility only */
181          overflow = bkm_scale_by_power (&tmp, base, 3);
182          break;
183
184        case 'k': /* kilo */
185        case 'K': /* kibi */
186          overflow = bkm_scale_by_power (&tmp, base, 1);
187          break;
188
189        case 'M': /* mega or mebi */
190        case 'm': /* 'm' is undocumented; for compatibility only */
191          overflow = bkm_scale_by_power (&tmp, base, 2);
192          break;
193
194        case 'P': /* peta or pebi */
195          overflow = bkm_scale_by_power (&tmp, base, 5);
196          break;
197
198        case 'T': /* tera or tebi */
199        case 't': /* 't' is undocumented; for compatibility only */
200          overflow = bkm_scale_by_power (&tmp, base, 4);
201          break;
202
203        case 'w':
204          overflow = bkm_scale (&tmp, 2);
205          break;
206
207        case 'Y': /* yotta or 2**80 */
208          overflow = bkm_scale_by_power (&tmp, base, 8);
209          break;
210
211        case 'Z': /* zetta or 2**70 */
212          overflow = bkm_scale_by_power (&tmp, base, 7);
213          break;
214
215        default:
216          *val = tmp;
217          return err | LONGINT_INVALID_SUFFIX_CHAR;
218        }
219
220      err |= overflow;
221      *p += suffixes;
222      if (**p)
223        err |= LONGINT_INVALID_SUFFIX_CHAR;
224    }
225
226  *val = tmp;
227  return err;
228}
229