Browse Source

libobs: Use locale-independent double conversion

Prevents issues on specific locales, especially where decimal points are
represented by commas rather than periods.
jp9000 10 years ago
parent
commit
d44d3b1f0a

+ 2 - 1
libobs/graphics/effect-parser.c

@@ -16,6 +16,7 @@
 ******************************************************************************/
 
 #include <assert.h>
+#include "../util/platform.h"
 #include "effect-parser.h"
 #include "effect.h"
 
@@ -735,7 +736,7 @@ static inline int ep_parse_param_assign_intfloat(struct effect_parser *ep,
 		return code;
 
 	if (is_float) {
-		float f = (float)strtod(ep->cfp.cur_token->str.array, NULL);
+		float f = (float)os_strtod(ep->cfp.cur_token->str.array);
 		if (is_negative) f = -f;
 		da_push_back_array(param->default_val, &f, sizeof(float));
 	} else {

+ 2 - 1
libobs/graphics/shader-parser.c

@@ -15,6 +15,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
 
+#include "../util/platform.h"
 #include "shader-parser.h"
 
 enum gs_shader_param_type get_shader_param_type(const char *type)
@@ -489,7 +490,7 @@ static inline int sp_parse_param_assign_intfloat(struct shader_parser *sp,
 		return code;
 
 	if (is_float) {
-		float f = (float)strtod(sp->cfp.cur_token->str.array, NULL);
+		float f = (float)os_strtod(sp->cfp.cur_token->str.array);
 		if (is_negative) f = -f;
 		da_push_back_array(param->default_val, &f, sizeof(float));
 	} else {

+ 5 - 6
libobs/util/config-file.c

@@ -472,10 +472,9 @@ void config_set_bool(config_t *config, const char *section,
 void config_set_double(config_t *config, const char *section,
 		const char *name, double value)
 {
-	struct dstr str;
-	dstr_init(&str);
-	dstr_printf(&str, "%g", value);
-	config_set_item(&config->sections, section, name, str.array);
+	char *str = bzalloc(64);
+	os_dtostr(value, str, 64);
+	config_set_item(&config->sections, section, name, str);
 }
 
 void config_set_default_string(config_t *config, const char *section,
@@ -591,7 +590,7 @@ double config_get_double(const config_t *config, const char *section,
 {
 	const char *value = config_get_string(config, section, name);
 	if (value)
-		return strtod(value, NULL);
+		return os_strtod(value);
 
 	return 0.0;
 }
@@ -644,7 +643,7 @@ double config_get_default_double(const config_t *config, const char *section,
 {
 	const char *value = config_get_default_string(config, section, name);
 	if (value)
-		return strtod(value, NULL);
+		return os_strtod(value);
 
 	return 0.0;
 }

+ 98 - 0
libobs/util/platform.c

@@ -18,6 +18,7 @@
 
 #include <errno.h>
 #include <stdlib.h>
+#include <locale.h>
 #include "c99defs.h"
 #include "platform.h"
 #include "bmem.h"
@@ -414,3 +415,100 @@ size_t os_mbs_to_utf8_ptr(const char *str, size_t len, char **pstr)
 	*pstr = dst;
 	return out_len;
 }
+
+/* locale independent double conversion from jansson, credit goes to them */
+
+static inline void to_locale(char *str)
+{
+	const char *point;
+	char *pos;
+
+	point = localeconv()->decimal_point;
+	if(*point == '.') {
+		/* No conversion needed */
+		return;
+	}
+
+	pos = strchr(str, '.');
+	if(pos)
+		*pos = *point;
+}
+
+static inline void from_locale(char *buffer)
+{
+	const char *point;
+	char *pos;
+
+	point = localeconv()->decimal_point;
+	if(*point == '.') {
+		/* No conversion needed */
+		return;
+	}
+
+	pos = strchr(buffer, *point);
+	if(pos)
+		*pos = '.';
+}
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
+
+double os_strtod(const char *str)
+{
+	char buf[64];
+	snprintf(buf, 64, "%s", str);
+	to_locale(buf);
+	return strtod(buf, NULL);
+}
+
+int os_dtostr(double value, char *dst, size_t size)
+{
+	int ret;
+	char *start, *end;
+	size_t length;
+
+	ret = snprintf(dst, size, "%.17g", value);
+	if(ret < 0)
+		return -1;
+
+	length = (size_t)ret;
+	if(length >= size)
+		return -1;
+
+	from_locale(dst);
+
+	/* Make sure there's a dot or 'e' in the output. Otherwise
+	   a real is converted to an integer when decoding */
+	if(strchr(dst, '.') == NULL && strchr(dst, 'e') == NULL) {
+		if(length + 3 >= size) {
+			/* No space to append ".0" */
+			return -1;
+		}
+		dst[length] = '.';
+		dst[length + 1] = '0';
+		dst[length + 2] = '\0';
+		length += 2;
+	}
+
+	/* Remove leading '+' from positive exponent. Also remove leading
+	   zeros from exponents (added by some printf() implementations) */
+	start = strchr(dst, 'e');
+	if(start) {
+		start++;
+		end = start + 1;
+
+		if(*start == '-')
+			start++;
+
+		while(*end == '0')
+			end++;
+
+		if(end != start) {
+			memmove(start, end, length - (size_t)(end - dst));
+			length -= (size_t)(end - start);
+		}
+	}
+
+	return (int)length;
+}

+ 3 - 0
libobs/util/platform.h

@@ -65,6 +65,9 @@ EXPORT size_t os_wcs_to_utf8_ptr(const wchar_t *str, size_t len, char **pstr);
 EXPORT size_t os_utf8_to_mbs_ptr(const char *str, size_t len, char **pstr);
 EXPORT size_t os_mbs_to_utf8_ptr(const char *str, size_t len, char **pstr);
 
+EXPORT double os_strtod(const char *str);
+EXPORT int os_dtostr(double value, char *dst, size_t size);
+
 EXPORT void *os_dlopen(const char *path);
 EXPORT void *os_dlsym(void *module, const char *func);
 EXPORT void os_dlclose(void *module);