1/* A more useful interface to strtol.
2
3   Copyright (C) 1995, 1996, 1998, 1999, 2000, 2001, 2003, 2004, 2005
4   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 2, or (at your option)
9   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, write to the Free Software Foundation,
18   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20/* Written by Jim Meyering. */
21
22#ifdef HAVE_CONFIG_H
23# include <config.h>
24#endif
25
26#include "xstrtol.h"
27
28#ifndef __strtol
29# define __strtol strtol
30# define __strtol_t long int
31# define __xstrtol xstrtol
32# define STRTOL_T_MINIMUM LONG_MIN
33# define STRTOL_T_MAXIMUM LONG_MAX
34#endif
35
36/* Some pre-ANSI implementations (e.g. SunOS 4)
37   need stderr defined if assertion checking is enabled.  */
38#include <stdio.h>
39
40#include <assert.h>
41#include <ctype.h>
42#include <errno.h>
43#include <limits.h>
44#include <stdlib.h>
45#include <string.h>
46
47#include "intprops.h"
48
49#ifndef STRTOL_T_MINIMUM
50# define STRTOL_T_MINIMUM TYPE_MINIMUM (__strtol_t)
51# define STRTOL_T_MAXIMUM TYPE_MAXIMUM (__strtol_t)
52#endif
53
54#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
55# define IN_CTYPE_DOMAIN(c) 1
56#else
57# define IN_CTYPE_DOMAIN(c) isascii(c)
58#endif
59
60#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
61
62#if !HAVE_DECL_STRTOIMAX && !defined strtoimax
63intmax_t strtoimax ();
64#endif
65
66#if !HAVE_DECL_STRTOUMAX && !defined strtoumax
67uintmax_t strtoumax ();
68#endif
69
70static strtol_error
71bkm_scale (__strtol_t *x, int scale_factor)
72{
73  if (TYPE_SIGNED (__strtol_t) && *x < STRTOL_T_MINIMUM / scale_factor)
74    {
75      *x = STRTOL_T_MINIMUM;
76      return LONGINT_OVERFLOW;
77    }
78  if (STRTOL_T_MAXIMUM / scale_factor < *x)
79    {
80      *x = STRTOL_T_MAXIMUM;
81      return LONGINT_OVERFLOW;
82    }
83  *x *= scale_factor;
84  return LONGINT_OK;
85}
86
87static strtol_error
88bkm_scale_by_power (__strtol_t *x, int base, int power)
89{
90  strtol_error err = LONGINT_OK;
91  while (power--)
92    err |= bkm_scale (x, base);
93  return err;
94}
95
96/* FIXME: comment.  */
97
98strtol_error
99__xstrtol (const char *s, char **ptr, int strtol_base,
100	   __strtol_t *val, const char *valid_suffixes)
101{
102  char *t_ptr;
103  char **p;
104  __strtol_t tmp;
105  strtol_error err = LONGINT_OK;
106
107  assert (0 <= strtol_base && strtol_base <= 36);
108
109  p = (ptr ? ptr : &t_ptr);
110
111  if (! TYPE_SIGNED (__strtol_t))
112    {
113      const char *q = s;
114      unsigned char ch = *q;
115      while (ISSPACE (ch))
116	ch = *++q;
117      if (ch == '-')
118	return LONGINT_INVALID;
119    }
120
121  errno = 0;
122  tmp = __strtol (s, p, strtol_base);
123
124  if (*p == s)
125    {
126      /* If there is no number but there is a valid suffix, assume the
127	 number is 1.  The string is invalid otherwise.  */
128      if (valid_suffixes && **p && strchr (valid_suffixes, **p))
129	tmp = 1;
130      else
131	return LONGINT_INVALID;
132    }
133  else if (errno != 0)
134    {
135      if (errno != ERANGE)
136	return LONGINT_INVALID;
137      err = LONGINT_OVERFLOW;
138    }
139
140  /* Let valid_suffixes == NULL mean `allow any suffix'.  */
141  /* FIXME: update all callers except the ones that allow suffixes
142     after the number, changing last parameter NULL to `""'.  */
143  if (!valid_suffixes)
144    {
145      *val = tmp;
146      return err;
147    }
148
149  if (**p != '\0')
150    {
151      int base = 1024;
152      int suffixes = 1;
153      strtol_error overflow;
154
155      if (!strchr (valid_suffixes, **p))
156	{
157	  *val = tmp;
158	  return err | LONGINT_INVALID_SUFFIX_CHAR;
159	}
160
161      if (strchr (valid_suffixes, '0'))
162	{
163	  /* The ``valid suffix'' '0' is a special flag meaning that
164	     an optional second suffix is allowed, which can change
165	     the base.  A suffix "B" (e.g. "100MB") stands for a power
166	     of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for
167	     a power of 1024.  If no suffix (e.g. "100M"), assume
168	     power-of-1024.  */
169
170	  switch (p[0][1])
171	    {
172	    case 'i':
173	      if (p[0][2] == 'B')
174		suffixes += 2;
175	      break;
176
177	    case 'B':
178	    case 'D': /* 'D' is obsolescent */
179	      base = 1000;
180	      suffixes++;
181	      break;
182	    }
183	}
184
185      switch (**p)
186	{
187	case 'b':
188	  overflow = bkm_scale (&tmp, 512);
189	  break;
190
191	case 'B':
192	  overflow = bkm_scale (&tmp, 1024);
193	  break;
194
195	case 'c':
196	  overflow = 0;
197	  break;
198
199	case 'E': /* exa or exbi */
200	  overflow = bkm_scale_by_power (&tmp, base, 6);
201	  break;
202
203	case 'G': /* giga or gibi */
204	case 'g': /* 'g' is undocumented; for compatibility only */
205	  overflow = bkm_scale_by_power (&tmp, base, 3);
206	  break;
207
208	case 'k': /* kilo */
209	case 'K': /* kibi */
210	  overflow = bkm_scale_by_power (&tmp, base, 1);
211	  break;
212
213	case 'M': /* mega or mebi */
214	case 'm': /* 'm' is undocumented; for compatibility only */
215	  overflow = bkm_scale_by_power (&tmp, base, 2);
216	  break;
217
218	case 'P': /* peta or pebi */
219	  overflow = bkm_scale_by_power (&tmp, base, 5);
220	  break;
221
222	case 'T': /* tera or tebi */
223	case 't': /* 't' is undocumented; for compatibility only */
224	  overflow = bkm_scale_by_power (&tmp, base, 4);
225	  break;
226
227	case 'w':
228	  overflow = bkm_scale (&tmp, 2);
229	  break;
230
231	case 'Y': /* yotta or 2**80 */
232	  overflow = bkm_scale_by_power (&tmp, base, 8);
233	  break;
234
235	case 'Z': /* zetta or 2**70 */
236	  overflow = bkm_scale_by_power (&tmp, base, 7);
237	  break;
238
239	default:
240	  *val = tmp;
241	  return err | LONGINT_INVALID_SUFFIX_CHAR;
242	}
243
244      err |= overflow;
245      *p += suffixes;
246      if (**p)
247	err |= LONGINT_INVALID_SUFFIX_CHAR;
248    }
249
250  *val = tmp;
251  return err;
252}
253
254#ifdef TESTING_XSTRTO
255
256# include <stdio.h>
257# include "error.h"
258
259char *program_name;
260
261int
262main (int argc, char **argv)
263{
264  strtol_error s_err;
265  int i;
266
267  program_name = argv[0];
268  for (i=1; i<argc; i++)
269    {
270      char *p;
271      __strtol_t val;
272
273      s_err = __xstrtol (argv[i], &p, 0, &val, "bckmw");
274      if (s_err == LONGINT_OK)
275	{
276	  printf ("%s->%lu (%s)\n", argv[i], val, p);
277	}
278      else
279	{
280	  STRTOL_FATAL_ERROR (argv[i], "arg", s_err);
281	}
282    }
283  exit (0);
284}
285
286#endif /* TESTING_XSTRTO */
287