1/* A more useful interface to strtol.
2
3   Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
4   2006, 2007 Free Software 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