| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 1 | /* vi: set sw=4 ts=4: */ | 
 | 2 | /* | 
 | 3 |  * Utility routines. | 
 | 4 |  * | 
 | 5 |  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> | 
 | 6 |  * | 
 | 7 |  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 
 | 8 |  */ | 
 | 9 |  | 
 | 10 | #include "libbb.h" | 
 | 11 |  | 
 | 12 | /* On exit: errno = 0 only if there was non-empty, '\0' terminated value | 
| Denis Vlasenko | 49a5eba | 2008-07-23 08:41:08 +0000 | [diff] [blame] | 13 |  * errno = EINVAL if value was not '\0' terminated, but otherwise ok | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 14 |  *    Return value is still valid, caller should just check whether end[0] | 
 | 15 |  *    is a valid terminating char for particular case. OTOH, if caller | 
 | 16 |  *    requires '\0' terminated input, [s]he can just check errno == 0. | 
 | 17 |  * errno = ERANGE if value had alphanumeric terminating char ("1234abcg"). | 
 | 18 |  * errno = ERANGE if value is out of range, missing, etc. | 
 | 19 |  * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok ) | 
| Denis Vlasenko | 666da5e | 2006-12-26 18:17:42 +0000 | [diff] [blame] | 20 |  *    return value is all-ones in this case. | 
| Denis Vlasenko | f19817d | 2008-07-18 18:17:10 +0000 | [diff] [blame] | 21 |  * | 
 | 22 |  * Test code: | 
 | 23 |  * char *endptr; | 
 | 24 |  * const char *minus = "-"; | 
 | 25 |  * errno = 0; | 
 | 26 |  * bb_strtoi(minus, &endptr, 0); // must set ERANGE | 
 | 27 |  * printf("minus:%p endptr:%p errno:%d EINVAL:%d\n", minus, endptr, errno, EINVAL); | 
 | 28 |  * errno = 0; | 
 | 29 |  * bb_strtoi("-0-", &endptr, 0); // must set EINVAL and point to second '-' | 
 | 30 |  * printf("endptr[0]:%c errno:%d EINVAL:%d\n", endptr[0], errno, EINVAL); | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 31 |  */ | 
 | 32 |  | 
 | 33 | static unsigned long long ret_ERANGE(void) | 
 | 34 | { | 
 | 35 | 	errno = ERANGE; /* this ain't as small as it looks (on glibc) */ | 
 | 36 | 	return ULLONG_MAX; | 
 | 37 | } | 
 | 38 |  | 
 | 39 | static unsigned long long handle_errors(unsigned long long v, char **endp, char *endptr) | 
 | 40 | { | 
 | 41 | 	if (endp) *endp = endptr; | 
 | 42 |  | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 43 | 	/* errno is already set to ERANGE by strtoXXX if value overflowed */ | 
 | 44 | 	if (endptr[0]) { | 
 | 45 | 		/* "1234abcg" or out-of-range? */ | 
 | 46 | 		if (isalnum(endptr[0]) || errno) | 
 | 47 | 			return ret_ERANGE(); | 
 | 48 | 		/* good number, just suspicious terminator */ | 
 | 49 | 		errno = EINVAL; | 
 | 50 | 	} | 
 | 51 | 	return v; | 
 | 52 | } | 
 | 53 |  | 
 | 54 |  | 
| Denis Vlasenko | defc1ea | 2008-06-27 02:52:20 +0000 | [diff] [blame] | 55 | unsigned long long FAST_FUNC bb_strtoull(const char *arg, char **endp, int base) | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 56 | { | 
| Denis Vlasenko | df38188 | 2006-11-28 10:54:16 +0000 | [diff] [blame] | 57 | 	unsigned long long v; | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 58 | 	char *endptr; | 
 | 59 |  | 
 | 60 | 	/* strtoul("  -4200000000") returns 94967296, errno 0 (!) */ | 
 | 61 | 	/* I don't think that this is right. Preventing this... */ | 
 | 62 | 	if (!isalnum(arg[0])) return ret_ERANGE(); | 
 | 63 |  | 
 | 64 | 	/* not 100% correct for lib func, but convenient for the caller */ | 
 | 65 | 	errno = 0; | 
| Denis Vlasenko | df38188 | 2006-11-28 10:54:16 +0000 | [diff] [blame] | 66 | 	v = strtoull(arg, &endptr, base); | 
 | 67 | 	return handle_errors(v, endp, endptr); | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 68 | } | 
 | 69 |  | 
| Denis Vlasenko | defc1ea | 2008-06-27 02:52:20 +0000 | [diff] [blame] | 70 | long long FAST_FUNC bb_strtoll(const char *arg, char **endp, int base) | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 71 | { | 
| Denis Vlasenko | df38188 | 2006-11-28 10:54:16 +0000 | [diff] [blame] | 72 | 	unsigned long long v; | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 73 | 	char *endptr; | 
 | 74 |  | 
| Denis Vlasenko | f19817d | 2008-07-18 18:17:10 +0000 | [diff] [blame] | 75 | 	/* Check for the weird "feature": | 
 | 76 | 	 * a "-" string is apparently a valid "number" for strto[u]l[l]! | 
 | 77 | 	 * It returns zero and errno is 0! :( */ | 
 | 78 | 	char first = (arg[0] != '-' ? arg[0] : arg[1]); | 
 | 79 | 	if (!isalnum(first)) return ret_ERANGE(); | 
 | 80 |  | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 81 | 	errno = 0; | 
| Denis Vlasenko | df38188 | 2006-11-28 10:54:16 +0000 | [diff] [blame] | 82 | 	v = strtoll(arg, &endptr, base); | 
 | 83 | 	return handle_errors(v, endp, endptr); | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 84 | } | 
 | 85 |  | 
 | 86 | #if ULONG_MAX != ULLONG_MAX | 
| Denis Vlasenko | defc1ea | 2008-06-27 02:52:20 +0000 | [diff] [blame] | 87 | unsigned long FAST_FUNC bb_strtoul(const char *arg, char **endp, int base) | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 88 | { | 
| Denis Vlasenko | df38188 | 2006-11-28 10:54:16 +0000 | [diff] [blame] | 89 | 	unsigned long v; | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 90 | 	char *endptr; | 
 | 91 |  | 
 | 92 | 	if (!isalnum(arg[0])) return ret_ERANGE(); | 
 | 93 | 	errno = 0; | 
| Denis Vlasenko | df38188 | 2006-11-28 10:54:16 +0000 | [diff] [blame] | 94 | 	v = strtoul(arg, &endptr, base); | 
 | 95 | 	return handle_errors(v, endp, endptr); | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 96 | } | 
 | 97 |  | 
| Denis Vlasenko | defc1ea | 2008-06-27 02:52:20 +0000 | [diff] [blame] | 98 | long FAST_FUNC bb_strtol(const char *arg, char **endp, int base) | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 99 | { | 
| Denis Vlasenko | df38188 | 2006-11-28 10:54:16 +0000 | [diff] [blame] | 100 | 	long v; | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 101 | 	char *endptr; | 
 | 102 |  | 
| Denis Vlasenko | f19817d | 2008-07-18 18:17:10 +0000 | [diff] [blame] | 103 | 	char first = (arg[0] != '-' ? arg[0] : arg[1]); | 
 | 104 | 	if (!isalnum(first)) return ret_ERANGE(); | 
 | 105 |  | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 106 | 	errno = 0; | 
| Denis Vlasenko | df38188 | 2006-11-28 10:54:16 +0000 | [diff] [blame] | 107 | 	v = strtol(arg, &endptr, base); | 
 | 108 | 	return handle_errors(v, endp, endptr); | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 109 | } | 
 | 110 | #endif | 
 | 111 |  | 
 | 112 | #if UINT_MAX != ULONG_MAX | 
| Denis Vlasenko | defc1ea | 2008-06-27 02:52:20 +0000 | [diff] [blame] | 113 | unsigned FAST_FUNC bb_strtou(const char *arg, char **endp, int base) | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 114 | { | 
 | 115 | 	unsigned long v; | 
 | 116 | 	char *endptr; | 
 | 117 |  | 
 | 118 | 	if (!isalnum(arg[0])) return ret_ERANGE(); | 
 | 119 | 	errno = 0; | 
 | 120 | 	v = strtoul(arg, &endptr, base); | 
 | 121 | 	if (v > UINT_MAX) return ret_ERANGE(); | 
 | 122 | 	return handle_errors(v, endp, endptr); | 
 | 123 | } | 
 | 124 |  | 
| Denis Vlasenko | defc1ea | 2008-06-27 02:52:20 +0000 | [diff] [blame] | 125 | int FAST_FUNC bb_strtoi(const char *arg, char **endp, int base) | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 126 | { | 
 | 127 | 	long v; | 
 | 128 | 	char *endptr; | 
 | 129 |  | 
| Denis Vlasenko | f19817d | 2008-07-18 18:17:10 +0000 | [diff] [blame] | 130 | 	char first = (arg[0] != '-' ? arg[0] : arg[1]); | 
 | 131 | 	if (!isalnum(first)) return ret_ERANGE(); | 
 | 132 |  | 
| Denis Vlasenko | 3ece72d | 2006-11-27 15:12:16 +0000 | [diff] [blame] | 133 | 	errno = 0; | 
 | 134 | 	v = strtol(arg, &endptr, base); | 
 | 135 | 	if (v > INT_MAX) return ret_ERANGE(); | 
 | 136 | 	if (v < INT_MIN) return ret_ERANGE(); | 
 | 137 | 	return handle_errors(v, endp, endptr); | 
 | 138 | } | 
 | 139 | #endif |