Line data Source code
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 :
46 : static strtol_error
47 310 : bkm_scale (__strtol_t *x, int scale_factor)
48 : {
49 0 : if (TYPE_SIGNED (__strtol_t) && *x < STRTOL_T_MINIMUM / scale_factor)
50 : {
51 0 : *x = STRTOL_T_MINIMUM;
52 0 : return LONGINT_OVERFLOW;
53 : }
54 310 : if (STRTOL_T_MAXIMUM / scale_factor < *x)
55 : {
56 20 : *x = STRTOL_T_MAXIMUM;
57 20 : return LONGINT_OVERFLOW;
58 : }
59 290 : *x *= scale_factor;
60 290 : return LONGINT_OK;
61 : }
62 :
63 : static strtol_error
64 60 : bkm_scale_by_power (__strtol_t *x, int base, int power)
65 : {
66 60 : strtol_error err = LONGINT_OK;
67 404 : while (power--)
68 284 : err |= bkm_scale (x, base);
69 60 : return err;
70 : }
71 :
72 : /* FIXME: comment. */
73 :
74 : strtol_error
75 803 : __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 803 : strtol_error err = LONGINT_OK;
82 :
83 803 : assert (0 <= strtol_base && strtol_base <= 36);
84 :
85 803 : p = (ptr ? ptr : &t_ptr);
86 :
87 : if (! TYPE_SIGNED (__strtol_t))
88 : {
89 712 : const char *q = s;
90 712 : unsigned char ch = *q;
91 1524 : while (isspace (ch))
92 100 : ch = *++q;
93 712 : if (ch == '-')
94 170 : return LONGINT_INVALID;
95 : }
96 :
97 633 : errno = 0;
98 633 : tmp = __strtol (s, p, strtol_base);
99 :
100 633 : 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 244 : if (valid_suffixes && **p && strchr (valid_suffixes, **p))
105 69 : tmp = 1;
106 : else
107 175 : return LONGINT_INVALID;
108 : }
109 389 : else if (errno != 0)
110 : {
111 0 : if (errno != ERANGE)
112 0 : return LONGINT_INVALID;
113 0 : 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 458 : if (!valid_suffixes)
120 : {
121 63 : *val = tmp;
122 63 : return err;
123 : }
124 :
125 395 : if (**p != '\0')
126 : {
127 181 : int base = 1024;
128 181 : int suffixes = 1;
129 : strtol_error overflow;
130 :
131 181 : if (!strchr (valid_suffixes, **p))
132 : {
133 88 : *val = tmp;
134 88 : return err | LONGINT_INVALID_SUFFIX_CHAR;
135 : }
136 :
137 93 : 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 87 : switch (p[0][1])
147 : {
148 6 : case 'i':
149 6 : if (p[0][2] == 'B')
150 2 : suffixes += 2;
151 6 : break;
152 :
153 17 : case 'B':
154 : case 'D': /* 'D' is obsolescent */
155 17 : base = 1000;
156 17 : suffixes++;
157 17 : break;
158 : }
159 : }
160 :
161 93 : switch (**p)
162 : {
163 21 : case 'b':
164 21 : overflow = bkm_scale (&tmp, 512);
165 21 : break;
166 :
167 4 : case 'B':
168 4 : overflow = bkm_scale (&tmp, 1024);
169 4 : break;
170 :
171 3 : case 'c':
172 3 : overflow = 0;
173 3 : break;
174 :
175 12 : case 'E': /* exa or exbi */
176 12 : overflow = bkm_scale_by_power (&tmp, base, 6);
177 12 : break;
178 :
179 6 : case 'G': /* giga or gibi */
180 : case 'g': /* 'g' is undocumented; for compatibility only */
181 6 : overflow = bkm_scale_by_power (&tmp, base, 3);
182 6 : break;
183 :
184 8 : case 'k': /* kilo */
185 : case 'K': /* kibi */
186 8 : overflow = bkm_scale_by_power (&tmp, base, 1);
187 8 : break;
188 :
189 5 : case 'M': /* mega or mebi */
190 : case 'm': /* 'm' is undocumented; for compatibility only */
191 5 : overflow = bkm_scale_by_power (&tmp, base, 2);
192 5 : break;
193 :
194 7 : case 'P': /* peta or pebi */
195 7 : overflow = bkm_scale_by_power (&tmp, base, 5);
196 7 : break;
197 :
198 7 : case 'T': /* tera or tebi */
199 : case 't': /* 't' is undocumented; for compatibility only */
200 7 : overflow = bkm_scale_by_power (&tmp, base, 4);
201 7 : break;
202 :
203 1 : case 'w':
204 1 : overflow = bkm_scale (&tmp, 2);
205 1 : break;
206 :
207 8 : case 'Y': /* yotta or 2**80 */
208 8 : overflow = bkm_scale_by_power (&tmp, base, 8);
209 8 : break;
210 :
211 7 : case 'Z': /* zetta or 2**70 */
212 7 : overflow = bkm_scale_by_power (&tmp, base, 7);
213 7 : break;
214 :
215 4 : default:
216 4 : *val = tmp;
217 4 : return err | LONGINT_INVALID_SUFFIX_CHAR;
218 : }
219 :
220 89 : err |= overflow;
221 89 : *p += suffixes;
222 89 : if (**p)
223 6 : err |= LONGINT_INVALID_SUFFIX_CHAR;
224 : }
225 :
226 303 : *val = tmp;
227 303 : return err;
228 : }
|