Workshop o mikrokontrolérech na SKSP 2024.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
3.4 KiB

3 months ago
/*
* UCW Library -- Configuration files: parsers for basic types
*
* (c) 2001--2006 Robert Spalek <robert@ucw.cz>
* (c) 2003--2006 Martin Mares <mj@ucw.cz>
* (c) 2019 Pavel Charvat <pchar@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* of the GNU Lesser General Public License.
*/
#include <ucw/lib.h>
#include <ucw/conf.h>
#include <ucw/chartype.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
struct unit {
uint name; // one-letter name of the unit
uint den; // fraction
u64 num;
};
static const struct unit units[] = {
{ 'd', 1, 86400 },
{ 'h', 1, 3600 },
{ 'k', 1, 1000 },
{ 'm', 1, 1000000 },
{ 'g', 1, 1000000000 },
{ 't', 1, 1000000000000LLU },
{ 'K', 1, 1<<10 },
{ 'M', 1, 1<<20 },
{ 'G', 1, 1<<30 },
{ 'T', 1, 1LLU<<40 },
{ '%', 100, 1 },
{ 0, 0, 0 }
};
static const struct unit *
lookup_unit(const char *value, const char *end, char **msg)
{
if (end && *end) {
if (end == value || end[1] || *end >= '0' && *end <= '9')
*msg = "Invalid number";
else {
for (const struct unit *u=units; u->name; u++)
if ((char)u->name == *end)
return u;
*msg = "Invalid unit";
}
}
return NULL;
}
static char cf_rngerr[] = "Number out of range";
char *
cf_parse_int(const char *str, int *ptr)
{
char *msg = NULL;
if (!*str)
msg = "Missing number";
else {
const struct unit *u;
char *end;
errno = 0;
uint x = strtoul(str, &end, 0);
if (errno == ERANGE)
msg = cf_rngerr;
else if (u = lookup_unit(str, end, &msg)) {
u64 y = (u64)x * u->num;
if (y % u->den)
msg = "Number is not an integer";
else {
y /= u->den;
if (y > 0xffffffff)
msg = cf_rngerr;
*ptr = y;
}
} else
*ptr = x;
}
return msg;
}
char *
cf_parse_u64(const char *str, u64 *ptr)
{
char *msg = NULL;
if (!*str)
msg = "Missing number";
else {
const struct unit *u;
char *end;
errno = 0;
u64 x = strtoull(str, &end, 0);
if (errno == ERANGE)
msg = cf_rngerr;
else if (u = lookup_unit(str, end, &msg)) {
if (x > ~(u64)0 / u->num)
msg = "Number out of range";
else {
x *= u->num;
if (x % u->den)
msg = "Number is not an integer";
else
*ptr = x / u->den;
}
} else
*ptr = x;
}
return msg;
}
char *
cf_parse_double(const char *str, double *ptr)
{
char *msg = NULL;
if (!*str)
msg = "Missing number";
else {
const struct unit *u;
double x;
uint read_chars;
if (sscanf(str, "%lf%n", &x, &read_chars) != 1)
msg = "Invalid number";
else if (u = lookup_unit(str, str + read_chars, &msg))
*ptr = x * u->num / u->den;
else
*ptr = x;
}
return msg;
}
char *
cf_parse_ip(const char *p, u32 *varp)
{
if (!*p)
return "Missing IP address";
uint x = 0;
char *p2;
if (*p == '0' && (p[1] | 32) == 'x' && Cxdigit(p[2])) {
errno = 0;
x = strtoul(p, &p2, 16);
if (errno == ERANGE || x > 0xffffffff)
goto error;
p = p2;
}
else
for (uint i = 0; i < 4; i++) {
if (i) {
if (*p++ != '.')
goto error;
}
if (!Cdigit(*p))
goto error;
errno = 0;
uint y = strtoul(p, &p2, 10);
if (errno == ERANGE || p2 == (char*) p || y > 255)
goto error;
p = p2;
x = (x << 8) + y;
}
*varp = x;
return *p ? "Trailing characters" : NULL;
error:
return "Invalid IP address";
}