Improve readability of WINEPRELOADRESERVE parsing code, and also make the parser available for other purposes in future patches.
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com ---
Notes: v3 -> v4: - document parse_ul() function - don't remove constness of preload_reserve() argument
loader/preloader.c | 62 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 16 deletions(-)
diff --git a/loader/preloader.c b/loader/preloader.c index 73df8b591f0..c23f1f087b5 100644 --- a/loader/preloader.c +++ b/loader/preloader.c @@ -68,6 +68,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <limits.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -719,6 +720,42 @@ static inline void *wld_memmove( void *dest, const void *src, size_t len ) return dest; }
+/* + * parse_ul - parse an unsigned long number with given radix + * + * Differences from strtoul(): + * - Does not support radix prefixes ("0x", etc) + * - Does not saturate to ULONG_MAX on overflow, wrap around instead + * - Indicates overflow via output argument, not errno + */ +static inline unsigned long parse_ul( const char *nptr, char **endptr, unsigned int radix, int *overflow ) +{ + const char *p = nptr; + unsigned long value, thresh; + int ovfl = 0; + + value = 0; + thresh = ULONG_MAX / radix; + for (;;) + { + unsigned int digit; + if (*p >= '0' && *p <= '9') digit = *p - '0'; + else if (*p >= 'a' && *p <= 'z') digit = *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'Z') digit = *p - 'A' + 10; + else break; + if (digit >= radix) break; + if (value > thresh) ovfl = 1; + value *= radix; + if (value > value + digit) ovfl = 1; + value += digit; + p++; + } + + if (endptr) *endptr = (char *)p; + if (overflow) *overflow = ovfl; + return value; +} + /* * wld_printf - just the basics * @@ -1385,27 +1422,20 @@ found: */ static void preload_reserve( const char *str ) { - const char *p; + char *p = (char *)str; unsigned long result = 0; void *start = NULL, *end = NULL; - int i, first = 1; + int i;
- for (p = str; *p; p++) + result = parse_ul( p, &p, 16, NULL ); + if (*p == '-') { - if (*p >= '0' && *p <= '9') result = result * 16 + *p - '0'; - else if (*p >= 'a' && *p <= 'f') result = result * 16 + *p - 'a' + 10; - else if (*p >= 'A' && *p <= 'F') result = result * 16 + *p - 'A' + 10; - else if (*p == '-') - { - if (!first) goto error; - start = (void *)(result & ~page_mask); - result = 0; - first = 0; - } - else goto error; + start = (void *)(result & ~page_mask); + result = parse_ul( p + 1, &p, 16, NULL ); + if (*p) goto error; + end = (void *)((result + page_mask) & ~page_mask); } - if (!first) end = (void *)((result + page_mask) & ~page_mask); - else if (result) goto error; /* single value '0' is allowed */ + else if (*p || result) goto error; /* single value '0' is allowed */
/* sanity checks */ if (end <= start) start = end = NULL;