* lib/xstrtol.c (bkm_scale): Don't assume that you can convert
[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 #if STDC_HEADERS
25 # include <stdlib.h>
26 #endif
27
28 #if HAVE_STRING_H
29 # include <string.h>
30 #else
31 # include <strings.h>
32 # ifndef strchr
33 #  define strchr index
34 # endif
35 #endif
36
37 #define NDEBUG
38 #include <assert.h>
39
40 #include <errno.h>
41 #ifndef errno
42 extern int errno;
43 #endif
44
45 #if HAVE_LIMITS_H
46 # include <limits.h>
47 #endif
48
49 #ifndef CHAR_BIT
50 # define CHAR_BIT 8
51 #endif
52
53 /* The extra casts work around common compiler bugs.  */
54 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
55 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
56    It is necessary at least when t == time_t.  */
57 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
58                               ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
59 #define TYPE_MAXIMUM(t) (~ (t) 0 - TYPE_MINIMUM (t))
60
61 #ifndef ULONG_MAX
62 # define ULONG_MAX TYPE_MAXIMUM (unsigned long int)
63 #endif
64
65 #ifndef LONG_MAX
66 # define LONG_MAX TYPE_MAXIMUM (long int)
67 #endif
68
69 #include "xstrtol.h"
70
71 __unsigned long int __strtol ();
72
73 static int
74 bkm_scale (x, scale_factor)
75      __unsigned long int *x;
76      int scale_factor;
77 {
78   __unsigned long int product = *x * scale_factor;
79   if (*x != product / scale_factor)
80     return 1;
81   *x = product;
82   return 0;
83 }
84
85 static int
86 bkm_scale_by_power (x, base, power)
87      __unsigned long int *x;
88      int base;
89      int power;
90 {
91   while (power--)
92     if (bkm_scale (x, base))
93       return 1;
94
95   return 0;
96 }
97
98 /* FIXME: comment.  */
99
100 strtol_error
101 __xstrtol (const char *s, char **ptr, int strtol_base,
102            __unsigned long int *val, const char *valid_suffixes)
103 {
104   char *t_ptr;
105   char **p;
106   __unsigned long int tmp;
107
108   assert (0 <= strtol_base && strtol_base <= 36);
109
110   p = (ptr ? ptr : &t_ptr);
111
112   errno = 0;
113   tmp = __strtol (s, p, strtol_base);
114   if (errno != 0)
115     return LONGINT_OVERFLOW;
116   if (*p == s)
117     return LONGINT_INVALID;
118
119   /* Let valid_suffixes == NULL mean `allow any suffix'.  */
120   /* FIXME: update all callers except the one in tail.c changing
121      last parameter NULL to `""'.  */
122   if (!valid_suffixes)
123     {
124       *val = tmp;
125       return LONGINT_OK;
126     }
127
128   if (**p != '\0')
129     {
130       int base = 1024;
131       int suffixes = 1;
132       int overflow;
133
134       if (!strchr (valid_suffixes, **p))
135         return LONGINT_INVALID_SUFFIX_CHAR;
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, e.g. "100MD" for 100 megabytes decimal.  */
142
143           switch (p[0][1])
144             {
145             case 'B':
146               suffixes++;
147               break;
148
149             case 'D':
150               base = 1000;
151               suffixes++;
152               break;
153             }
154         }
155
156       switch (**p)
157         {
158         case 'b':
159           overflow = bkm_scale (&tmp, 512);
160           break;
161
162         case 'B':
163           overflow = bkm_scale (&tmp, 1024);
164           break;
165
166         case 'c':
167           overflow = 0;
168           break;
169
170         case 'E': /* Exa */
171           overflow = bkm_scale_by_power (&tmp, base, 6);
172           break;
173
174         case 'G': /* Giga */
175           overflow = bkm_scale_by_power (&tmp, base, 3);
176           break;
177
178         case 'k': /* kilo */
179           overflow = bkm_scale_by_power (&tmp, base, 1);
180           break;
181
182         case 'M': /* Mega */
183         case 'm': /* 'm' is undocumented; for backward compatibility only */
184           overflow = bkm_scale_by_power (&tmp, base, 2);
185           break;
186
187         case 'P': /* Peta */
188           overflow = bkm_scale_by_power (&tmp, base, 5);
189           break;
190
191         case 'T': /* Tera */
192           overflow = bkm_scale_by_power (&tmp, base, 4);
193           break;
194
195         case 'w':
196           overflow = bkm_scale (&tmp, 2);
197           break;
198
199         case 'Y': /* Yotta */
200           overflow = bkm_scale_by_power (&tmp, base, 8);
201           break;
202
203         case 'Z': /* Zetta */
204           overflow = bkm_scale_by_power (&tmp, base, 7);
205           break;
206
207         default:
208           return LONGINT_INVALID_SUFFIX_CHAR;
209           break;
210         }
211
212       if (overflow)
213         return LONGINT_OVERFLOW;
214
215       (*p) += suffixes;
216     }
217
218   *val = tmp;
219   return LONGINT_OK;
220 }
221
222 #ifdef TESTING_XSTRTO
223
224 # include <stdio.h>
225 # include "error.h"
226
227 char *program_name;
228
229 int
230 main (int argc, char** argv)
231 {
232   strtol_error s_err;
233   int i;
234
235   program_name = argv[0];
236   for (i=1; i<argc; i++)
237     {
238       char *p;
239       __unsigned long int val;
240
241       s_err = __xstrtol (argv[i], &p, 0, &val, "bckmw");
242       if (s_err == LONGINT_OK)
243         {
244           printf ("%s->%lu (%s)\n", argv[i], val, p);
245         }
246       else
247         {
248           STRTOL_FATAL_ERROR (argv[i], "arg", s_err);
249         }
250     }
251   exit (0);
252 }
253
254 #endif /* TESTING_XSTRTO */