[libc] Rewrite strtoul()
[ipxe.git] / src / core / string.c
1 /*
2 * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * 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
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27
28 /** @file
29 *
30 * String functions
31 *
32 */
33
34 /**
35 * Fill memory region
36 *
37 * @v dest Destination region
38 * @v character Fill character
39 * @v len Length
40 * @ret dest Destination region
41 */
42 void * generic_memset ( void *dest, int character, size_t len ) {
43 uint8_t *dest_bytes = dest;
44
45 while ( len-- )
46 *(dest_bytes++) = character;
47 return dest;
48 }
49
50 /**
51 * Copy memory region
52 *
53 * @v dest Destination region
54 * @v src Source region
55 * @v len Length
56 * @ret dest Destination region
57 */
58 void * generic_memcpy ( void *dest, const void *src, size_t len ) {
59 const uint8_t *src_bytes = src;
60 uint8_t *dest_bytes = dest;
61
62 while ( len-- )
63 *(dest_bytes++) = *(src_bytes++);
64 return dest;
65 }
66
67 /**
68 * Copy (possibly overlapping) memory region
69 *
70 * @v dest Destination region
71 * @v src Source region
72 * @v len Length
73 * @ret dest Destination region
74 */
75 void * generic_memmove ( void *dest, const void *src, size_t len ) {
76 const uint8_t *src_bytes = ( src + len );
77 uint8_t *dest_bytes = ( dest + len );
78
79 if ( dest < src )
80 return memcpy ( dest, src, len );
81 while ( len-- )
82 *(--dest_bytes) = *(--src_bytes);
83 return dest;
84 }
85
86 /**
87 * Compare memory regions
88 *
89 * @v first First region
90 * @v second Second region
91 * @v len Length
92 * @ret diff Difference
93 */
94 int memcmp ( const void *first, const void *second, size_t len ) {
95 const uint8_t *first_bytes = first;
96 const uint8_t *second_bytes = second;
97 int diff;
98
99 while ( len-- ) {
100 diff = ( *(second_bytes++) - *(first_bytes++) );
101 if ( diff )
102 return diff;
103 }
104 return 0;
105 }
106
107 /**
108 * Find character within a memory region
109 *
110 * @v src Source region
111 * @v character Character to find
112 * @v len Length
113 * @ret found Found character, or NULL if not found
114 */
115 void * memchr ( const void *src, int character, size_t len ) {
116 const uint8_t *src_bytes = src;
117
118 for ( ; len-- ; src_bytes++ ) {
119 if ( *src_bytes == character )
120 return ( ( void * ) src_bytes );
121 }
122 return NULL;
123 }
124
125 /**
126 * Swap memory regions
127 *
128 * @v first First region
129 * @v second Second region
130 * @v len Length
131 * @ret first First region
132 */
133 void * memswap ( void *first, void *second, size_t len ) {
134 uint8_t *first_bytes = first;
135 uint8_t *second_bytes = second;
136 uint8_t temp;
137
138 for ( ; len-- ; first_bytes++, second_bytes++ ) {
139 temp = *first_bytes;
140 *first_bytes = *second_bytes;
141 *second_bytes = temp;
142 }
143 return first;
144 }
145
146 /**
147 * Compare strings
148 *
149 * @v first First string
150 * @v second Second string
151 * @ret diff Difference
152 */
153 int strcmp ( const char *first, const char *second ) {
154
155 return strncmp ( first, second, ~( ( size_t ) 0 ) );
156 }
157
158 /**
159 * Compare strings
160 *
161 * @v first First string
162 * @v second Second string
163 * @v max Maximum length to compare
164 * @ret diff Difference
165 */
166 int strncmp ( const char *first, const char *second, size_t max ) {
167 const uint8_t *first_bytes = ( ( const uint8_t * ) first );
168 const uint8_t *second_bytes = ( ( const uint8_t * ) second );
169 int diff;
170
171 for ( ; max-- ; first_bytes++, second_bytes++ ) {
172 diff = ( *second_bytes - *first_bytes );
173 if ( diff )
174 return diff;
175 if ( ! *first_bytes )
176 return 0;
177 }
178 return 0;
179 }
180
181 /**
182 * Compare case-insensitive strings
183 *
184 * @v first First string
185 * @v second Second string
186 * @ret diff Difference
187 */
188 int strcasecmp ( const char *first, const char *second ) {
189 const uint8_t *first_bytes = ( ( const uint8_t * ) first );
190 const uint8_t *second_bytes = ( ( const uint8_t * ) second );
191 int diff;
192
193 for ( ; ; first_bytes++, second_bytes++ ) {
194 diff = ( toupper ( *second_bytes ) -
195 toupper ( *first_bytes ) );
196 if ( diff )
197 return diff;
198 if ( ! *first_bytes )
199 return 0;
200 }
201 }
202
203 /**
204 * Get length of string
205 *
206 * @v src String
207 * @ret len Length
208 */
209 size_t strlen ( const char *src ) {
210
211 return strnlen ( src, ~( ( size_t ) 0 ) );
212 }
213
214 /**
215 * Get length of string
216 *
217 * @v src String
218 * @v max Maximum length
219 * @ret len Length
220 */
221 size_t strnlen ( const char *src, size_t max ) {
222 const uint8_t *src_bytes = ( ( const uint8_t * ) src );
223 size_t len = 0;
224
225 while ( max-- && *(src_bytes++) )
226 len++;
227 return len;
228 }
229
230 /**
231 * Find character within a string
232 *
233 * @v src String
234 * @v character Character to find
235 * @ret found Found character, or NULL if not found
236 */
237 char * strchr ( const char *src, int character ) {
238 const uint8_t *src_bytes = ( ( const uint8_t * ) src );
239
240 for ( ; ; src_bytes++ ) {
241 if ( *src_bytes == character )
242 return ( ( char * ) src_bytes );
243 if ( ! *src_bytes )
244 return NULL;
245 }
246 }
247
248 /**
249 * Find rightmost character within a string
250 *
251 * @v src String
252 * @v character Character to find
253 * @ret found Found character, or NULL if not found
254 */
255 char * strrchr ( const char *src, int character ) {
256 const uint8_t *src_bytes = ( ( const uint8_t * ) src );
257 const uint8_t *start = src_bytes;
258
259 while ( *src_bytes )
260 src_bytes++;
261 for ( src_bytes-- ; src_bytes >= start ; src_bytes-- ) {
262 if ( *src_bytes == character )
263 return ( ( char * ) src_bytes );
264 }
265 return NULL;
266 }
267
268 /**
269 * Find substring
270 *
271 * @v haystack String
272 * @v needle Substring
273 * @ret found Found substring, or NULL if not found
274 */
275 char * strstr ( const char *haystack, const char *needle ) {
276 size_t len = strlen ( needle );
277
278 for ( ; *haystack ; haystack++ ) {
279 if ( memcmp ( haystack, needle, len ) == 0 )
280 return ( ( char * ) haystack );
281 }
282 return NULL;
283 }
284
285 /**
286 * Copy string
287 *
288 * @v dest Destination string
289 * @v src Source string
290 * @ret dest Destination string
291 */
292 char * strcpy ( char *dest, const char *src ) {
293 const uint8_t *src_bytes = ( ( const uint8_t * ) src );
294 uint8_t *dest_bytes = ( ( uint8_t * ) dest );
295
296 /* We cannot use strncpy(), since that would pad the destination */
297 for ( ; ; src_bytes++, dest_bytes++ ) {
298 *dest_bytes = *src_bytes;
299 if ( ! *dest_bytes )
300 break;
301 }
302 return dest;
303 }
304
305 /**
306 * Copy string
307 *
308 * @v dest Destination string
309 * @v src Source string
310 * @v max Maximum length
311 * @ret dest Destination string
312 */
313 char * strncpy ( char *dest, const char *src, size_t max ) {
314 const uint8_t *src_bytes = ( ( const uint8_t * ) src );
315 uint8_t *dest_bytes = ( ( uint8_t * ) dest );
316
317 for ( ; max ; max--, src_bytes++, dest_bytes++ ) {
318 *dest_bytes = *src_bytes;
319 if ( ! *dest_bytes )
320 break;
321 }
322 while ( max-- )
323 *(dest_bytes++) = '\0';
324 return dest;
325 }
326
327 /**
328 * Concatenate string
329 *
330 * @v dest Destination string
331 * @v src Source string
332 * @ret dest Destination string
333 */
334 char * strcat ( char *dest, const char *src ) {
335
336 strcpy ( ( dest + strlen ( dest ) ), src );
337 return dest;
338 }
339
340 /**
341 * Duplicate string
342 *
343 * @v src Source string
344 * @ret dup Duplicated string, or NULL if allocation failed
345 */
346 char * strdup ( const char *src ) {
347
348 return strndup ( src, ~( ( size_t ) 0 ) );
349 }
350
351 /**
352 * Duplicate string
353 *
354 * @v src Source string
355 * @v max Maximum length
356 * @ret dup Duplicated string, or NULL if allocation failed
357 */
358 char * strndup ( const char *src, size_t max ) {
359 size_t len = strnlen ( src, max );
360 char *dup;
361
362 dup = malloc ( len + 1 /* NUL */ );
363 if ( dup ) {
364 memcpy ( dup, src, len );
365 dup[len] = '\0';
366 }
367 return dup;
368 }
369
370 /**
371 * Calculate digit value
372 *
373 * @v character Digit character
374 * @ret digit Digit value
375 *
376 * Invalid digits will be returned as a value greater than or equal to
377 * the numeric base.
378 */
379 unsigned int digit_value ( unsigned int character ) {
380
381 if ( character >= 'a' )
382 return ( character - ( 'a' - 10 ) );
383 if ( character >= 'A' )
384 return ( character - ( 'A' - 10 ) );
385 if ( character <= '9' )
386 return ( character - '0' );
387 return character;
388 }
389
390 /**
391 * Preprocess string for strtoul() or strtoull()
392 *
393 * @v string String
394 * @v negate Final value should be negated
395 * @v base Numeric base
396 * @ret string Remaining string
397 */
398 static const char * strtoul_pre ( const char *string, int *negate, int *base ) {
399
400 /* Skip any leading whitespace */
401 while ( isspace ( *string ) )
402 string++;
403
404 /* Process arithmetic sign, if present */
405 *negate = 0;
406 if ( *string == '-' ) {
407 string++;
408 *negate = 1;
409 } else if ( *string == '+' ) {
410 string++;
411 }
412
413 /* Process base, if present */
414 if ( *base == 0 ) {
415 *base = 10;
416 if ( *string == '0' ) {
417 string++;
418 *base = 8;
419 if ( ( *string & ~0x20 ) == 'X' ) {
420 string++;
421 *base = 16;
422 }
423 }
424 }
425
426 return string;
427 }
428
429 /**
430 * Convert string to numeric value
431 *
432 * @v string String
433 * @v endp End pointer (or NULL)
434 * @v base Numeric base (or zero to autodetect)
435 * @ret value Numeric value
436 */
437 unsigned long strtoul ( const char *string, char **endp, int base ) {
438 unsigned long value = 0;
439 unsigned int digit;
440 int negate;
441
442 /* Preprocess string */
443 string = strtoul_pre ( string, &negate, &base );
444
445 /* Process digits */
446 for ( ; ; string++ ) {
447 digit = digit_value ( *string );
448 if ( digit >= ( unsigned int ) base )
449 break;
450 value = ( ( value * base ) + digit );
451 }
452
453 /* Negate value if, applicable */
454 if ( negate )
455 value = -value;
456
457 /* Fill in end pointer, if applicable */
458 if ( endp )
459 *endp = ( ( char * ) string );
460
461 return value;
462 }
463
464 /**
465 * Convert string to numeric value
466 *
467 * @v string String
468 * @v endp End pointer (or NULL)
469 * @v base Numeric base (or zero to autodetect)
470 * @ret value Numeric value
471 */
472 unsigned long long strtoull ( const char *string, char **endp, int base ) {
473 unsigned long long value = 0;
474 unsigned int digit;
475 int negate;
476
477 /* Preprocess string */
478 string = strtoul_pre ( string, &negate, &base );
479
480 /* Process digits */
481 for ( ; ; string++ ) {
482 digit = digit_value ( *string );
483 if ( digit >= ( unsigned int ) base )
484 break;
485 value = ( ( value * base ) + digit );
486 }
487
488 /* Negate value if, applicable */
489 if ( negate )
490 value = -value;
491
492 /* Fill in end pointer, if applicable */
493 if ( endp )
494 *endp = ( ( char * ) string );
495
496 return value;
497 }