source: mainline/uspace/lib/posix/stdlib/strtol.c@ 4cf8ca6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4cf8ca6 was 102a729, checked in by Petr Koupy <petr.koupy@…>, 14 years ago

Various bugfixes in stdlib.h functions.

  • Property mode set to 100644
File size: 8.3 KB
Line 
1/*
2 * Copyright (c) 2011 Jiri Zarevucky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libposix
30 * @{
31 */
32/** @file Backend for integer conversions.
33 */
34
35#define LIBPOSIX_INTERNAL
36
37#include "../internal/common.h"
38#include "../stdlib.h"
39
40#include "../limits.h"
41#include "../ctype.h"
42#include "../errno.h"
43
44/**
45 * Decides whether a digit belongs to a particular base.
46 *
47 * @param c Character representation of the digit.
48 * @param base Base against which the digit shall be tested.
49 * @return True if the digit belongs to the base, false otherwise.
50 */
51static inline bool is_digit_in_base(int c, int base)
52{
53 if (base <= 10) {
54 return c >= '0' && c < '0' + base;
55 } else {
56 return isdigit(c) ||
57 (tolower(c) >= 'a' && tolower(c) < ('a' + base - 10));
58 }
59}
60
61/**
62 * Derive a digit from its character representation.
63 *
64 * @param c Character representation of the digit.
65 * @param base Base into which the digit belongs to.
66 * @return Digit value represented by an integer.
67 */
68static inline int get_digit_in_base(int c, int base)
69{
70 if (c <= '9') {
71 return c - '0';
72 } else {
73 return 10 + tolower(c) - 'a';
74 }
75}
76
77/**
78 * Backend for all integer conversion functions. Can be configured by arguments
79 * to convert both signed and unsigned integers of various sizes.
80 *
81 * @param nptr Input string.
82 * @param endptr If non-NULL, *endptr is set to the position of the first
83 * unrecognized character.
84 * @param base Expected base of the string representation.
85 * @param min_value Lower bound for the resulting conversion.
86 * @param max_value Upper bound for the resulting conversion.
87 * @param out_negative Either NULL for unsigned conversion or a pointer to the
88 * bool variable into which shall be placed the negativity of the resulting
89 * converted value.
90 * @return Result of the conversion.
91 */
92static inline unsigned long long internal_strtol(
93 const char *restrict nptr, char **restrict endptr, int base,
94 const long long min_value, const unsigned long long max_value,
95 bool *restrict out_negative)
96{
97 if (nptr == NULL) {
98 errno = EINVAL;
99 return 0;
100 }
101
102 if (base < 0 || base == 1 || base > 36) {
103 errno = EINVAL;
104 return 0;
105 }
106
107 unsigned long long real_max_value = max_value;
108
109 /* Current index in the input string. */
110 int i = 0;
111 bool negative = false;
112
113 /* Skip whitespace. */
114 while (isspace(nptr[i])) {
115 i++;
116 }
117
118 /* Parse sign. */
119 switch (nptr[i]) {
120 case '-':
121 negative = true;
122 real_max_value = -min_value;
123 /* fallthrough */
124 case '+':
125 i++;
126 }
127
128 /* Figure out the base. */
129 switch (base) {
130 case 0:
131 if (nptr[i] == '0') {
132 if (tolower(nptr[i + 1]) == 'x') {
133 base = 16;
134 i += 2;
135 } else {
136 base = 8;
137 }
138 } else {
139 base = 10;
140 }
141 break;
142 case 16:
143 if (nptr[i] == '0' && tolower(nptr[i + 1]) == 'x') {
144 i += 2;
145 }
146 break;
147 }
148
149 if (!is_digit_in_base(nptr[i], base)) {
150 /* No digits to parse, invalid input. */
151
152 errno = EINVAL;
153 if (endptr != NULL) {
154 *endptr = (char *) nptr;
155 }
156 return 0;
157 }
158
159 /* Maximal value to which a digit can be added without a risk
160 * of overflow.
161 */
162 unsigned long long max_safe_value = (real_max_value - base + 1) / base;
163
164 unsigned long long result = 0;
165
166 if (real_max_value == 0) {
167 /* Special case when a negative number is parsed as
168 * unsigned integer. Only -0 is accepted.
169 */
170
171 while (is_digit_in_base(nptr[i], base)) {
172 if (nptr[i] != '0') {
173 errno = ERANGE;
174 result = max_value;
175 }
176 i++;
177 }
178 }
179
180 while (is_digit_in_base(nptr[i], base)) {
181 int digit = get_digit_in_base(nptr[i], base);
182
183 if (result > max_safe_value) {
184 /* corner case, check for overflow */
185
186 unsigned long long
187 boundary = (real_max_value - digit) / base;
188
189 if (result > boundary) {
190 /* overflow */
191 errno = ERANGE;
192 result = real_max_value;
193 break;
194 }
195 }
196
197 result = result * base + digit;
198 i++;
199 }
200
201 if (endptr != NULL) {
202 /* Move the pointer to the end of the number,
203 * in case it isn't there already.
204 */
205 while (is_digit_in_base(nptr[i], base)) {
206 i++;
207 }
208
209 *endptr = (char *) &nptr[i];
210 }
211 if (out_negative != NULL) {
212 *out_negative = negative;
213 }
214 return result;
215}
216
217/**
218 * Convert a string to an integer.
219 *
220 * @param nptr Input string.
221 * @return Result of the conversion.
222 */
223int posix_atoi(const char *nptr)
224{
225 bool neg = false;
226 unsigned long long result =
227 internal_strtol(nptr, NULL, 10, INT_MIN, INT_MAX, &neg);
228
229 return (int) (neg ? -result : result);
230}
231
232/**
233 * Convert a string to a long integer.
234 *
235 * @param nptr Input string.
236 * @return Result of the conversion.
237 */
238long posix_atol(const char *nptr)
239{
240 bool neg = false;
241 unsigned long long result =
242 internal_strtol(nptr, NULL, 10, LONG_MIN, LONG_MAX, &neg);
243
244 return (long) (neg ? -result : result);
245}
246
247/**
248 * Convert a string to a long long integer.
249 *
250 * @param nptr Input string.
251 * @return Result of the conversion.
252 */
253long long posix_atoll(const char *nptr)
254{
255 bool neg = false;
256 unsigned long long result =
257 internal_strtol(nptr, NULL, 10, LLONG_MIN, LLONG_MAX, &neg);
258
259 return (long long) (neg ? -result : result);
260}
261
262/**
263 * Convert a string to a long integer.
264 *
265 * @param nptr Input string.
266 * @param endptr Pointer to the final part of the string which
267 * was not used for conversion.
268 * @param base Expected base of the string representation.
269 * @return Result of the conversion.
270 */
271long posix_strtol(const char *restrict nptr, char **restrict endptr, int base)
272{
273 bool neg = false;
274 unsigned long long result =
275 internal_strtol(nptr, endptr, base, LONG_MIN, LONG_MAX, &neg);
276
277 return (long) (neg ? -result : result);
278}
279
280/**
281 * Convert a string to a long long integer.
282 *
283 * @param nptr Input string.
284 * @param endptr Pointer to the final part of the string which
285 * was not used for conversion.
286 * @param base Expected base of the string representation.
287 * @return Result of the conversion.
288 */
289long long posix_strtoll(
290 const char *restrict nptr, char **restrict endptr, int base)
291{
292 bool neg = false;
293 unsigned long long result =
294 internal_strtol(nptr, endptr, base, LLONG_MIN, LLONG_MAX, &neg);
295
296 return (long long) (neg ? -result : result);
297}
298
299/**
300 * Convert a string to an unsigned long integer.
301 *
302 * @param nptr Input string.
303 * @param endptr Pointer to the final part of the string which
304 * was not used for conversion.
305 * @param base Expected base of the string representation.
306 * @return Result of the conversion.
307 */
308unsigned long posix_strtoul(
309 const char *restrict nptr, char **restrict endptr, int base)
310{
311 unsigned long long result =
312 internal_strtol(nptr, endptr, base, 0, ULONG_MAX, NULL);
313
314 return (unsigned long) result;
315}
316
317/**
318 * Convert a string to a an unsigned long long integer.
319 *
320 * @param nptr Input string.
321 * @param endptr Pointer to the final part of the string which
322 * was not used for conversion.
323 * @param base Expected base of the string representation.
324 * @return Result of the conversion.
325 */
326unsigned long long posix_strtoull(
327 const char *restrict nptr, char **restrict endptr, int base)
328{
329 return internal_strtol(nptr, endptr, base, 0, ULLONG_MAX, NULL);
330}
331
332/** @}
333 */
Note: See TracBrowser for help on using the repository browser.