blob: 1897ff6349a3c6631c0055579e10c92ecd01f7ce [file]
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: LGPL-3.0-only WITH LGPL-3.0-linking-exception
#include "include/a2i.h"
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/param.h>
#define ISSPACE(c) isspace((unsigned char) ({(char){c};}))
#define ISALNUM(c) isalnum((unsigned char) ({(char){c};}))
#define streq(a,b) (strcmp(a,b) == 0)
intmax_t
atosimax(const char *restrict s, char **restrict endp, int base,
intmax_t min, intmax_t max, int *restrict status)
{
__label__ clamp;
int errno_saved;
intmax_t n;
endp = endp ?: &(char *){NULL};
status = status ?: &(int){0};
_Pragma("GCC diagnostic push");
_Pragma("GCC diagnostic ignored \"-Wcast-qual\"");
*endp = (char *) s;
_Pragma("GCC diagnostic pop");
n = 0;
if (base < 0 || base == 1 || base > 36) {
*status = EINVAL;
goto clamp;
}
if (ISSPACE(*s)) {
*status = ECANCELED;
goto clamp;
}
errno_saved = errno;
errno = 0;
n = strtoimax(s, endp, base);
if (errno != 0 && errno != EINVAL)
*status = errno;
else if (*endp == s)
*status = ECANCELED;
else if (n < min || n > max)
*status = ERANGE;
else if (!streq(*endp, ""))
*status = ENOTSUP;
else
*status = 0;
errno = errno_saved;
clamp:
return MAX(min, MIN(max, n));
}
uintmax_t
atouimax(const char *restrict s, char **restrict endp, int base,
uintmax_t min, uintmax_t max, int *restrict status)
{
__label__ clamp;
int errno_saved;
uintmax_t n;
endp = endp ?: &(char *){NULL};
status = status ?: &(int){0};
_Pragma("GCC diagnostic push");
_Pragma("GCC diagnostic ignored \"-Wcast-qual\"");
*endp = (char *) s;
_Pragma("GCC diagnostic pop");
n = 0;
if (base < 0 || base == 1 || base > 36) {
*status = EINVAL;
goto clamp;
}
if (ISSPACE(*s)) {
*status = ECANCELED;
goto clamp;
}
if (!ISALNUM(*s)) {
*status = ECANCELED;
goto clamp;
}
errno_saved = errno;
errno = 0;
n = strtoumax(s, endp, base);
if (errno != 0 && errno != EINVAL)
*status = errno;
else if (*endp == s)
*status = ECANCELED;
else if (n < min || n > max)
*status = ERANGE;
else if (!streq(*endp, ""))
*status = ENOTSUP;
else
*status = 0;
errno = errno_saved;
clamp:
return MAX(min, MIN(max, n));
}