Include stdio.h. Required on some systems when using assert.
[pspp] / lib / xstrtol.c
1 /* A more useful interface to strtol.
2    Copyright (C) 1995, 1996, 1998 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by Jim Meyering. */
19
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <stdio.h>
25
26 #if STDC_HEADERS
27 # include <stdlib.h>
28 #endif
29
30 #if HAVE_STRING_H
31 # include <string.h>
32 #else
33 # include <strings.h>
34 # ifndef strchr
35 #  define strchr index
36 # endif
37 #endif
38
39 #include <assert.h>
40
41 #include <errno.h>
42 #ifndef errno
43 extern int errno;
44 #endif
45
46 #if HAVE_LIMITS_H
47 # include <limits.h>
48 #endif
49
50 #ifndef CHAR_BIT
51 # define CHAR_BIT 8
52 #endif
53
54 /* The extra casts work around common compiler bugs.  */
55 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
56 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
57    It is necessary at least when t == time_t.  */
58 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
59                               ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
60 #define TYPE_MAXIMUM(t) (~ (t) 0 - TYPE_MINIMUM (t))
61
62 #ifndef ULONG_MAX
63 # define ULONG_MAX TYPE_MAXIMUM (unsigned long int)
64 #endif
65
66 #ifndef LONG_MAX
67 # define LONG_MAX TYPE_MAXIMUM (long int)
68 #endif
69
70 #include "xstrtol.h"
71
72 __unsigned long int __strtol ();
73
74 static int
75 bkm_scale (__unsigned long int *x, int scale_factor)
76 {
77   __unsigned long int product = *x * scale_factor;
78   if (*x != product / scale_factor)
79     return 1;
80   *x = product;
81   return 0;
82 }
83
84 static int
85 bkm_scale_by_power (__unsigned long int *x, int base, int power)
86 {
87   while (power--)
88     if (bkm_scale (x, base))
89       return 1;
90
91   return 0;
92 }
93
94 /* FIXME: comment.  */
95
96 strtol_error
97 __xstrtol (const char *s, char **ptr, int strtol_base,
98            __unsigned long int *val, const char *valid_suffixes)
99 {
100   char *t_ptr;
101   char **p;
102   __unsigned long int tmp;
103
104   assert (0 <= strtol_base && strtol_base <= 36);
105
106   p = (ptr ? ptr : &t_ptr);
107
108   errno = 0;
109   tmp = __strtol (s, p, strtol_base);
110   if (errno != 0)
111     return LONGINT_OVERFLOW;
112   if (*p == s)
113     return LONGINT_INVALID;
114
115   /* Let valid_suffixes == NULL mean `allow any suffix'.  */
116   /* FIXME: update all callers except the one in tail.c changing
117      last parameter NULL to `""'.  */
118   if (!valid_suffixes)
119     {
120       *val = tmp;
121       return LONGINT_OK;
122     }
123
124   if (**p != '\0')
125     {
126       int base = 1024;
127       int suffixes = 1;
128       int overflow;
129
130       if (!strchr (valid_suffixes, **p))
131         return LONGINT_INVALID_SUFFIX_CHAR;
132
133       if (strchr (valid_suffixes, '0'))
134         {
135           /* The ``valid suffix'' '0' is a special flag meaning that
136              an optional second suffix is allowed, which can change
137              the base, e.g. "100MD" for 100 megabytes decimal.  */
138
139           switch (p[0][1])
140             {
141             case 'B':
142               suffixes++;
143               break;
144
145             case 'D':
146               base = 1000;
147               suffixes++;
148               break;
149             }
150         }
151
152       switch (**p)
153         {
154         case 'b':
155           overflow = bkm_scale (&tmp, 512);
156           break;
157
158         case 'B':
159           overflow = bkm_scale (&tmp, 1024);
160           break;
161
162         case 'c':
163           overflow = 0;
164           break;
165
166         case 'E': /* Exa */
167           overflow = bkm_scale_by_power (&tmp, base, 6);
168           break;
169
170         case 'G': /* Giga */
171           overflow = bkm_scale_by_power (&tmp, base, 3);
172           break;
173
174         case 'k': /* kilo */
175           overflow = bkm_scale_by_power (&tmp, base, 1);
176           break;
177
178         case 'M': /* Mega */
179         case 'm': /* 'm' is undocumented; for backward compatibility only */
180           overflow = bkm_scale_by_power (&tmp, base, 2);
181           break;
182
183         case 'P': /* Peta */
184           overflow = bkm_scale_by_power (&tmp, base, 5);
185           break;
186
187         case 'T': /* Tera */
188           overflow = bkm_scale_by_power (&tmp, base, 4);
189           break;
190
191         case 'w':
192           overflow = bkm_scale (&tmp, 2);
193           break;
194
195         case 'Y': /* Yotta */
196           overflow = bkm_scale_by_power (&tmp, base, 8);
197           break;
198
199         case 'Z': /* Zetta */
200           overflow = bkm_scale_by_power (&tmp, base, 7);
201           break;
202
203         default:
204           return LONGINT_INVALID_SUFFIX_CHAR;
205           break;
206         }
207
208       if (overflow)
209         return LONGINT_OVERFLOW;
210
211       (*p) += suffixes;
212     }
213
214   *val = tmp;
215   return LONGINT_OK;
216 }
217
218 #ifdef TESTING_XSTRTO
219
220 # include <stdio.h>
221 # include "error.h"
222
223 char *program_name;
224
225 int
226 main (int argc, char** argv)
227 {
228   strtol_error s_err;
229   int i;
230
231   program_name = argv[0];
232   for (i=1; i<argc; i++)
233     {
234       char *p;
235       __unsigned long int val;
236
237       s_err = __xstrtol (argv[i], &p, 0, &val, "bckmw");
238       if (s_err == LONGINT_OK)
239         {
240           printf ("%s->%lu (%s)\n", argv[i], val, p);
241         }
242       else
243         {
244           STRTOL_FATAL_ERROR (argv[i], "arg", s_err);
245         }
246     }
247   exit (0);
248 }
249
250 #endif /* TESTING_XSTRTO */