Commit 0e67eec2 authored by Niklas Haas's avatar Niklas Haas

osdep: add locale-invariant printf wrappers

Since nobody can agree on adding `printf_l` to the standard, we have to
do this using OS-dependent mechanisms. On glibc, the best hack-around is
to use `uselocale` to set the thread-specific locale. This requires some
roundabout locale initialization, but works.

For other platforms (e.g. BSD and Win32), there are saner methods of
accomplishing this, which is why it's inside osdep/.
parent c9c9c33b
......@@ -24,6 +24,7 @@
#include <stdio.h>
#include "ta/talloc.h"
#include "osdep/printf.h"
#include "bstr.h"
#include "ctype.h"
......@@ -315,7 +316,7 @@ void bstr_xappend_vasprintf(void *talloc_ctx, bstr *s, const char *fmt,
char c;
if (avail < 1)
dest = &c;
size = vsnprintf(dest, MAX(avail, 1), fmt, copy);
size = vsnprintf_c(dest, MAX(avail, 1), fmt, copy);
va_end(copy);
if (size < 0)
......@@ -323,7 +324,7 @@ void bstr_xappend_vasprintf(void *talloc_ctx, bstr *s, const char *fmt,
if (avail < 1 || size + 1 > avail) {
resize_append(talloc_ctx, s, size + 1);
vsnprintf(s->start + s->len, size + 1, fmt, ap);
vsnprintf_c(s->start + s->len, size + 1, fmt, ap);
}
s->len += size;
}
......
......@@ -24,6 +24,7 @@
#include <stdint.h>
#include "ta/talloc.h"
#include "osdep/printf.h"
#include "config.h"
// Include all of the symbols that should be public in a way that marks them
......
......@@ -21,42 +21,6 @@
#include "common.h"
#include "context.h"
// We mostly care about LC_NUMERIC, and how "." vs. "," is treated,
// Other locale stuff might break too, but probably isn't too bad.
static bool check_locale(struct pl_context *ctx)
{
char *name = setlocale(LC_NUMERIC, NULL);
bool seems_ok = !name || strcmp(name, "C") == 0;
// Since `setlocale` doesn't necessarily have any correlation with the
// calling thread's locale, also do a check to see if `printf` succeeds.
char buf[8];
snprintf(buf, sizeof(buf), "%f", 1234.0);
bool works = strncmp(buf, "1234.00", sizeof(buf)) == 0;
if (works && seems_ok) {
return true;
} else if (works && !seems_ok) {
pl_warn(ctx, "LC_NUMERIC is specified as '%s'. This *seems* to work "
"okay, but if this is a result of user configuration, then "
"libplacebo could randomly break on some platforms. It's "
"recommended to explicitly set LC_NUMERIC to 'C'. Note that "
"you may ignore this warning if you are using `uselocale` to "
"set the correct locale, as this cannot be queried.", name);
return true;
} else if (!works && seems_ok) {
pl_fatal(ctx, "LC_NUMERIC value seems okay, but the result of "
"printf('%%f') appears to contain garbage: '%s'. Unable to "
"continue. This situation may be the case if using `uselocale` "
"to override the locale of the calling thread.", buf);
return false;
} else { // if (!works && !seems_ok)
pl_fatal(ctx, "LC_NUMERIC is specified as '%s', which produces garbage "
"for printf('%%f'): '%s'. Unable to continue.", name, buf);
return false;
}
}
struct pl_context *pl_context_create(int api_ver,
const struct pl_context_params *params)
{
......@@ -74,11 +38,6 @@ struct pl_context *pl_context_create(int api_ver,
struct pl_context *ctx = talloc_zero(NULL, struct pl_context);
ctx->params = *params;
if (!check_locale(ctx)) {
pl_context_destroy(&ctx);
return NULL;
}
#ifdef NDEBUG
const char *disable_dbg = getenv("LIBPLACEBO_ENABLE_SECURITY_BUGS");
if (!disable_dbg || strcmp(disable_dbg, "1") != 0) {
......
......@@ -259,7 +259,7 @@ static void generate_shaders(struct pl_dispatch *dp, struct pass *pass,
const char *type = va->fmt->glsl_type;
char loc[32];
snprintf(loc, sizeof(loc), "layout(location=%d) ", va->location);
snprintf_c(loc, sizeof(loc), "layout(location=%d) ", va->location);
ADD(vert_head, "%s%s %s vert%s;\n", loc, vert_in, type, va->name);
if (strcmp(va->name, vert_pos) == 0) {
......@@ -609,7 +609,7 @@ static void translate_compute_shader(struct pl_dispatch *dp,
ident_t points[4];
for (int i = 0; i < PL_ARRAY_SIZE(points); i++) {
char name[4];
snprintf(name, sizeof(name), "p%d", i);
snprintf_c(name, sizeof(name), "p%d", i);
points[i] = sh_var_from_va(sh, name, &sva->attr, sva->data[i]);
}
......
......@@ -5,22 +5,28 @@ fixver = '0'
version = majorver + '.' + apiver + '.' + fixver
# Build options mostly taken from mpv
add_global_arguments([
build_opts = [
'-D_ISOC99_SOURCE', '-D_GNU_SOURCE',
'-fvisibility=hidden',
# Warnings
'-Wall', '-Wundef', '-Wmissing-prototypes', '-Wshadow', '-Wparentheses',
'-Wpointer-arith', '-Wno-pointer-sign',
], language: 'c')
]
cc = meson.get_compiler('c')
# clang's version of -Wmissing-braces rejects the common {0} initializers
if (cc.get_id() == 'clang')
add_global_arguments('-Wno-missing-braces', language : 'c')
if cc.get_id() == 'clang'
build_opts += ['-Wno-missing-braces']
endif
# Global dependencies
build_deps = [
dependency('threads'),
cc.find_library('m', required: false),
]
# Source files
sources = [
'colorspace.c',
......@@ -69,6 +75,20 @@ components = [
]
]
# OS-dependent files and feature checks
has_useloc = cc.has_header_symbol(
'locale.h', 'uselocale',
prefix: '#define _XOPEN_SOURCE 700'
)
## osdep/printf.h
if has_useloc
sources += 'osdep/printf_useloc.c'
build_opts += ['-D_XOPEN_SOURCE=700']
else
error('Could not find any suitable implementation of `osdep/printf.h`!')
endif
# Configuration
conf = configuration_data()
conf.set('majorver', majorver)
......@@ -83,7 +103,6 @@ else
endif
# Build process
bdeps = [ cc.find_library('m', required: false) ]
defs = ''
foreach c : components
......@@ -100,7 +119,7 @@ foreach c : components
pretty = name.underscorify().to_upper()
defs += '#define PL_HAVE_@0@ @1@\n'.format(pretty, has_dep ? 1 : 0)
if has_dep
bdeps += dep
build_deps += dep
if (c.length() > 2)
sources += c[2]
endif
......@@ -119,9 +138,10 @@ configure_file(
configuration: conf,
)
add_project_arguments(build_opts, language: 'c')
lib = library('placebo', sources,
install: true,
dependencies: bdeps,
dependencies: build_deps,
soversion: apiver,
)
......@@ -142,7 +162,7 @@ tdeps = [ declare_dependency(link_with: lib) ]
if get_option('tests')
foreach t : tests
e = executable('test.' + t, 'tests/' + t, dependencies: bdeps + tdeps)
e = executable('test.' + t, 'tests/' + t, dependencies: build_deps + tdeps)
test(t, e)
endforeach
endif
/*
* This file is part of libplacebo.
*
* libplacebo 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 2.1 of the License, or (at your option) any later version.
*
* libplacebo 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 libplacebo. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdio.h>
#include <stdarg.h>
#define PRINTF_WRAP(name) \
__typeof__(name) name##_c;
// These printf wrappers (named printf_c etc.) must perform locale-invariant
// versions of their equivalent functions without the prefix. (The _c suffix
// stands for the "C" locale)
PRINTF_WRAP(printf);
PRINTF_WRAP(fprintf);
PRINTF_WRAP(sprintf);
PRINTF_WRAP(snprintf);
PRINTF_WRAP(vprintf);
PRINTF_WRAP(vfprintf);
PRINTF_WRAP(vsprintf);
PRINTF_WRAP(vsnprintf);
/*
* This file is part of libplacebo.
*
* libplacebo 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 2.1 of the License, or (at your option) any later version.
*
* libplacebo 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 libplacebo. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <locale.h>
#include <pthread.h>
#include "osdep/printf.h"
static locale_t cloc;
static pthread_once_t once = PTHREAD_ONCE_INIT;
static void init_cloc()
{
cloc = newlocale(0, "C", (locale_t) 0);
if (!cloc)
abort();
}
#define WRAP_VA(fn, ...) \
({ \
pthread_once(&once, init_cloc); \
locale_t oldloc = uselocale((locale_t) 0); \
uselocale(cloc); \
int ret_va = fn(__VA_ARGS__); \
uselocale(oldloc); \
ret_va; \
})
#define WRAP(fn, ...) \
({ \
va_list ap; \
va_start(ap, format); \
int ret = WRAP_VA(v##fn, __VA_ARGS__, ap); \
va_end(ap); \
ret; \
})
int printf_c(const char *format, ...)
{
return WRAP(printf, format);
}
int fprintf_c(FILE *stream, const char *format, ...)
{
return WRAP(fprintf, stream, format);
}
int sprintf_c(char *str, const char *format, ...)
{
return WRAP(sprintf, str, format);
}
int snprintf_c(char *str, size_t size, const char *format, ...)
{
return WRAP(snprintf, str, size, format);
}
int vprintf_c(const char *format, va_list ap)
{
return WRAP_VA(vprintf, format, ap);
}
int vfprintf_c(FILE *stream, const char *format, va_list ap)
{
return WRAP_VA(vfprintf, stream, format, ap);
}
int vsprintf_c(char *str, const char *format, va_list ap)
{
return WRAP_VA(vsprintf, str, format, ap);
}
int vsnprintf_c(char *str, size_t size, const char *format, va_list ap)
{
return WRAP_VA(vsnprintf, str, size, format, ap);
}
......@@ -18,6 +18,8 @@
#include <stdio.h>
#include <assert.h>
#include "osdep/printf.h"
#define TA_NO_WRAPPERS
#include "ta.h"
......@@ -192,7 +194,7 @@ static bool ta_vasprintf_append_at(char **str, size_t at, const char *fmt,
va_list copy;
va_copy(copy, ap);
char c;
size = vsnprintf(&c, 1, fmt, copy);
size = vsnprintf_c(&c, 1, fmt, copy);
va_end(copy);
if (size < 0)
......@@ -204,7 +206,7 @@ static bool ta_vasprintf_append_at(char **str, size_t at, const char *fmt,
return false;
*str = t;
}
vsnprintf(*str + at, size + 1, fmt, ap);
vsnprintf_c(*str + at, size + 1, fmt, ap);
ta_dbg_mark_as_string(*str);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment