Usermode/libc - Actually fix strtoi (passing utest now)
[tpg/acess2.git] / Usermode / Libraries / libc.so_src / strtoi.c
1 /*
2  * Acess2 C Library
3  * - By John Hodge (thePowersGang)
4  *
5  * strtoi.c
6  * - strto[u][l]l/atoi implimentation
7  */
8 #include <ctype.h>
9 #include <errno.h>
10 #include <limits.h>
11 #include <stddef.h>
12
13 unsigned long long strtoull(const char *str, char **end, int base)
14 {
15         unsigned long long      ret = 0;
16         
17         if( !str || base < 0 || base > 36 || base == 1 ) {
18                 if(end)
19                         *end = (char*)str;
20                 errno = EINVAL;
21                 return 0;
22         }
23
24         // Trim leading spaces
25         while( isspace(*str) )
26                 str++;
27         
28         // Handle base detection for hex
29         if( base == 0 || base == 16 ) {
30                 if( *str == '0' && (str[1] == 'x' || str[1] == 'X') && isxdigit(str[2]) ) {
31                         str += 2;
32                         base = 16;
33                 }
34         }
35         
36         // Handle base detection for octal
37         if( base == 0 && *str == '0' ) {
38                 str ++;
39                 base = 8;
40         }
41
42         // Fall back on decimal when unknown
43         if( base == 0 )
44                 base = 10;
45
46         // Value before getting within 1 digit of ULLONG_MAX
47         // - Used to avoid overflow in more accurate check
48         unsigned long long      max_before_ullong_max = ULLONG_MAX / base;
49         unsigned int    space_above = ULLONG_MAX - max_before_ullong_max * base;
50         while( *str )
51         {
52                  int    next = -1;
53                 if( base <= 10 ) {
54                         if( '0' <= *str && *str <= '0'+base-1 )
55                                 next = *str - '0';
56                 }
57                 else {
58                         if( '0' <= *str && *str <= '9' )
59                                 next = *str - '0';
60                         if( 'A' <= *str && *str <= 'A'+base-10-1 )
61                                 next = *str - 'A' + 10;
62                         if( 'a' <= *str && *str <= 'a'+base-10-1 )
63                                 next = *str - 'a' + 10;
64                 }
65                 //_SysDebug("strtoull - ret=0x%llx,next=%i,str='%s'", ret, next, str);
66                 if( next < 0 )
67                         break;
68                 
69                 // If we're already out of range, keep eating
70                 if( ret == ULLONG_MAX ) {
71                         errno = ERANGE;
72                         str ++;
73                         // Keep eating until first unrecognised character
74                         continue;
75                 }
76         
77                 // Rough then accurate check against max value
78                 if( ret >= max_before_ullong_max )
79                 {
80                         //_SysDebug("strtoull - 0x%llx>0x%llx", ret, max_before_ullong_max);
81                         if( (ret - max_before_ullong_max) * base + next > space_above ) {
82                                 //_SysDebug("strtoull - %u*%u+%u (%u) > %u",
83                                 //      (unsigned int)(ret - max_before_ullong_max), base, next, space_above);
84                                 ret = ULLONG_MAX;
85                                 errno = ERANGE;
86                                 str ++;
87                                 continue;
88                         }
89                 }
90                 ret *= base;
91                 ret += next;
92                 str ++;
93         }
94
95         if(end)
96                 *end = (char*)str;
97         return ret;
98 }
99
100 unsigned long strtoul(const char *ptr, char **end, int base)
101 {
102         unsigned long long tmp = strtoull(ptr, end, base);
103         
104         if( tmp > ULONG_MAX ) {
105                 errno = ERANGE;
106                 return ULONG_MAX;
107         }
108         
109         return tmp;
110 }
111
112 long long strtoll(const char *str, char **end, int base)
113 {
114          int    neg = 0;
115
116         if( !str ) {
117                 errno = EINVAL;
118                 return 0;
119         }
120         
121         while( isspace(*str) )
122                 str++;
123         
124         // Check for negative (or positive) sign
125         if(*str == '-' || *str == '+')
126         {
127                 //_SysDebug("strtoll - str[0:1] = '%.2s'", str);
128                 if( !isdigit(str[1]) ) {
129                         // Non-digit, invalid string
130                         if(end) *end = (char*)str;
131                         return 0;
132                 }
133                 neg = (*str == '-');
134                 str++;
135         }
136
137         unsigned long long ret = strtoull(str, end, base);      
138         //_SysDebug("strtoll - neg=%i,ret=%llu", neg, ret);
139
140         if( neg ) {
141                 // Abuses unsigned integer overflow
142                 if( ret + LLONG_MIN < ret ) {
143                         errno = ERANGE;
144                         return LLONG_MIN;
145                 }
146                 return -ret;
147         }
148         else
149         {
150                 if( ret > LLONG_MAX ) {
151                         errno = ERANGE;
152                         return LLONG_MAX;
153                 }
154                 return ret;
155         }
156 }
157
158 long strtol(const char *str, char **end, int base)
159 {
160         long long tmp = strtoll(str, end, base);
161         if( tmp > LONG_MAX || tmp < LONG_MIN ) {
162                 errno = ERANGE;
163                 return (tmp > LONG_MAX) ? LONG_MAX : LONG_MIN;
164         }
165         return tmp;
166 }
167
168 /**
169  * \fn int atoi(const char *str)
170  * \brief Convert a string to an integer
171  */
172 int atoi(const char *str)
173 {
174         long long       tmp = strtoll(str, NULL, 0);
175         if( tmp > INT_MAX || tmp < INT_MIN ) {
176                 errno = ERANGE;
177                 return (tmp > INT_MAX) ? INT_MAX : INT_MIN;
178         }
179         return tmp;
180 }
181
182 long atol(const char *str)
183 {
184         long long       tmp = strtoll(str, NULL, 0);
185         if( tmp > LONG_MAX || tmp < LONG_MIN ) {
186                 errno = ERANGE;
187                 return (tmp > LONG_MAX) ? LONG_MAX : LONG_MIN;
188         }
189         return tmp;
190 }
191
192 long long atoll(const char *str)
193 {
194         long long       tmp = strtoll(str, NULL, 0);
195         return tmp;
196 }

UCC git Repository :: git.ucc.asn.au