protoize
[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 (__unsigned long int *x, int scale_factor)
75 {
76   __unsigned long int product = *x * scale_factor;
77   if (*x != product / scale_factor)
78     return 1;
79   *x = product;
80   return 0;
81 }
82
83 static int
84 bkm_scale_by_power (__unsigned long int *x, int base, int power)
85 {
86   while (power--)
87     if (bkm_scale (x, base))
88       return 1;
89
90   return 0;
91 }
92
93 /* FIXME: comment.  */
94
95 strtol_error
96 __xstrtol (const char *s, char **ptr, int strtol_base,
97            __unsigned long int *val, const char *valid_suffixes)
98 {
99   char *t_ptr;
100   char **p;
101   __unsigned long int tmp;
102
103   assert (0 <= strtol_base && strtol_base <= 36);
104
105   p = (ptr ? ptr : &t_ptr);
106
107   errno = 0;
108   tmp = __strtol (s, p, strtol_base);
109   if (errno != 0)
110     return LONGINT_OVERFLOW;
111   if (*p == s)
112     return LONGINT_INVALID;
113
114   /* Let valid_suffixes == NULL mean `allow any suffix'.  */
115   /* FIXME: update all callers except the one in tail.c changing
116      last parameter NULL to `""'.  */
117   if (!valid_suffixes)
118     {
119       *val = tmp;
120       return LONGINT_OK;
121     }
122
123   if (**p != '\0')
124     {
125       int base = 1024;
126       int suffixes = 1;
127       int overflow;
128
129       if (!strchr (valid_suffixes, **p))
130         return LONGINT_INVALID_SUFFIX_CHAR;
131
132       if (strchr (valid_suffixes, '0'))
133         {
134           /* The ``valid suffix'' '0' is a special flag meaning that
135              an optional second suffix is allowed, which can change
136              the base, e.g. "100MD" for 100 megabytes decimal.  */
137
138           switch (p[0][1])
139             {
140             case 'B':
141               suffixes++;
142               break;
143
144             case 'D':
145               base = 1000;
146               suffixes++;
147               break;
148             }
149         }
150
151       switch (**p)
152         {
153         case 'b':
154           overflow = bkm_scale (&tmp, 512);
155           break;
156
157         case 'B':
158           overflow = bkm_scale (&tmp, 1024);
159           break;
160
161         case 'c':
162           overflow = 0;
163           break;
164
165         case 'E': /* Exa */
166           overflow = bkm_scale_by_power (&tmp, base, 6);
167           break;
168
169         case 'G': /* Giga */
170           overflow = bkm_scale_by_power (&tmp, base, 3);
171           break;
172
173         case 'k': /* kilo */
174           overflow = bkm_scale_by_power (&tmp, base, 1);
175           break;
176
177         case 'M': /* Mega */
178         case 'm': /* 'm' is undocumented; for backward compatibility only */
179           overflow = bkm_scale_by_power (&tmp, base, 2);
180           break;
181
182         case 'P': /* Peta */
183           overflow = bkm_scale_by_power (&tmp, base, 5);
184           break;
185
186         case 'T': /* Tera */
187           overflow = bkm_scale_by_power (&tmp, base, 4);
188           break;
189
190         case 'w':
191           overflow = bkm_scale (&tmp, 2);
192           break;
193
194         case 'Y': /* Yotta */
195           overflow = bkm_scale_by_power (&tmp, base, 8);
196           break;
197
198         case 'Z': /* Zetta */
199           overflow = bkm_scale_by_power (&tmp, base, 7);
200           break;
201
202         default:
203           return LONGINT_INVALID_SUFFIX_CHAR;
204           break;
205         }
206
207       if (overflow)
208         return LONGINT_OVERFLOW;
209
210       (*p) += suffixes;
211     }
212
213   *val = tmp;
214   return LONGINT_OK;
215 }
216
217 #ifdef TESTING_XSTRTO
218
219 # include <stdio.h>
220 # include "error.h"
221
222 char *program_name;
223
224 int
225 main (int argc, char** argv)
226 {
227   strtol_error s_err;
228   int i;
229
230   program_name = argv[0];
231   for (i=1; i<argc; i++)
232     {
233       char *p;
234       __unsigned long int val;
235
236       s_err = __xstrtol (argv[i], &p, 0, &val, "bckmw");
237       if (s_err == LONGINT_OK)
238         {
239           printf ("%s->%lu (%s)\n", argv[i], val, p);
240         }
241       else
242         {
243           STRTOL_FATAL_ERROR (argv[i], "arg", s_err);
244         }
245     }
246   exit (0);
247 }
248
249 #endif /* TESTING_XSTRTO */