/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2016-2021 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
. */
#if HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#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;
}