diff options
Diffstat (limited to 'libmailutils/string/strtosize.c')
-rw-r--r-- | libmailutils/string/strtosize.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/libmailutils/string/strtosize.c b/libmailutils/string/strtosize.c new file mode 100644 index 000000000..a1146a46b --- /dev/null +++ b/libmailutils/string/strtosize.c @@ -0,0 +1,132 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2016-2024 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, see + <http://www.gnu.org/licenses/>. */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif +#include <mailutils/errno.h> +#include <mailutils/cctype.h> +#include <mailutils/cstr.h> + +#ifndef SIZE_MAX +# define SIZE_MAX (~((size_t)0)) +#endif + +/* + * Converts the initial part of the string in STR to a size_t value. + * A valid input consists of a sequence of decimal digits, and optional + * size suffix: K, M, or G. Any amount of whitespace is allowed at the + * beginning of STR and between the last digit and size suffix. + * + * On success, the result is stored in RET_VAL and 0 is returned. + * On error, RET_VAL remains untouched and one of the following error + * codes is returned: + * EINVAL - STR is NULL. + * MU_ERR_OUT_PTR_NULL + * RET_VAL is NULL. + * ERANGE - Result is out of allowed range for size_t. + * MU_ERR_PARSE - (0) Unexpected character encountered or + * (1) STR is empty or + * (2) STR contains only whitespace characters or + * (3) ENDP is NULL and the portion of STR that remains + * after conversion contains characters other than + * \0 and whitespace, + * + * If ENDP is not NULL, a pointer to the character in STR at which + * the conversion has stopped is stored in *ENDP. + */ +int +mu_strtosize (char const *str, char **endp, size_t *ret_val) +{ + size_t n = 0; + int rc = 0; + char const *start, *s; + + if (!str) + return EINVAL; + if (!ret_val) + return MU_ERR_OUT_PTR_NULL; + + s = start = mu_str_skip_class (str, MU_CTYPE_SPACE); + + while (*s && mu_isdigit (*s)) + { + size_t x = n * 10 + *s - '0'; + if (x < n) + { + rc = ERANGE; + goto end; + } + n = x; + s++; + } + + if (s == start) + { + rc = MU_ERR_PARSE; + goto end; + } + + str = s; + s = mu_str_skip_class (s, MU_CTYPE_SPACE); + + switch (*s++) + { + case 'g': + case 'G': + if (SIZE_MAX / n < 1024) + { + rc = ERANGE; + break; + } + n <<= 10; + case 'm': + case 'M': + if (SIZE_MAX / n < 1024) + { + rc = ERANGE; + break; + } + n <<= 10; + case 'k': + case 'K': + if (SIZE_MAX / n < 1024) + { + rc = ERANGE; + break; + } + n <<= 10; + break; + + case 0: + s = str; + break; + + default: + s = str; + rc = MU_ERR_PARSE; + } + end: + if (endp) + *endp = (char*) s; + else if (rc == 0 && *mu_str_skip_class (s, MU_CTYPE_SPACE)) + rc = MU_ERR_PARSE; + + if (rc == 0) + *ret_val = n; + return rc; +} |