common: validate and properly parse floats

Fixes: #1665.
This commit is contained in:
Andrew J. Hesford 2024-03-27 22:13:50 -04:00
parent d68376f2ac
commit a457542fb1
5 changed files with 138 additions and 3 deletions

View file

@ -10,6 +10,7 @@ labwc_sources += files(
'mem.c',
'nodename.c',
'parse-bool.c',
'parse-double.c',
'scaled_font_buffer.c',
'scaled_scene_buffer.c',
'scene-helpers.c',

99
src/common/parse-double.c Normal file
View file

@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/util/log.h>
#include "common/mem.h"
#include "common/parse-double.h"
struct dec_separator {
int index;
bool multiple;
};
struct converted_double {
double value;
bool valid;
};
static struct dec_separator
find_dec_separator(const char *str)
{
struct dec_separator loc = {
.index = -1,
.multiple = false
};
for (int i = 0; *str; i++, str++) {
switch (*str) {
case ',':
case '.':
if (loc.index >= 0) {
loc.multiple = true;
return loc;
} else {
loc.index = i;
}
break;
}
}
return loc;
}
static struct converted_double
convert_double(const char *str)
{
struct converted_double result = {
.value = 0,
.valid = true,
};
char *eptr = NULL;
errno = 0;
result.value = strtod(str, &eptr);
if (errno) {
wlr_log(WLR_ERROR, "value '%s' is out of range", str);
result.valid = false;
}
if (*eptr) {
wlr_log(WLR_ERROR, "value '%s' contains trailing garbage", str);
result.valid = false;
}
return result;
}
bool
set_double(const char *str, double *val)
{
assert(str);
assert(val);
struct dec_separator dloc = find_dec_separator(str);
if (dloc.multiple) {
wlr_log(WLR_ERROR,
"value '%s' contains multiple decimal markers", str);
return false;
}
char *lstr = NULL;
if (dloc.index >= 0) {
lstr = xstrdup(str);
struct lconv *lc = localeconv();
lstr[dloc.index] = *lc->decimal_point;
str = lstr;
}
struct converted_double conv = convert_double(str);
if (conv.valid) {
*val = conv.value;
}
free(lstr);
return conv.valid;
}