-
Simon McVittie authored
We'll be running these with the GLib that we located via the RPATH, which (in general) will not be new enough to load GIO modules from the host OS; so in every executable that is linked to GIO and has our RPATH, we will want to turn off external modules. This edits the global environment variables, so in programs that wrap another command (such as steam-runtime-urlopen since steamrt/tasks#489), we need to save the original environment first, and base the environment of the wrapped command on the saved copy. steamrt/tasks#505 Signed-off-by:
Simon McVittie <smcv@collabora.com>
Simon McVittie authoredWe'll be running these with the GLib that we located via the RPATH, which (in general) will not be new enough to load GIO modules from the host OS; so in every executable that is linked to GIO and has our RPATH, we will want to turn off external modules. This edits the global environment variables, so in programs that wrap another command (such as steam-runtime-urlopen since steamrt/tasks#489), we need to save the original environment first, and base the environment of the wrapped command on the saved copy. steamrt/tasks#505 Signed-off-by:
Simon McVittie <smcv@collabora.com>
check-locale.c 7.37 KiB
/*
* Copyright © 2019 Collabora Ltd.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <locale.h>
#include <glib.h>
#include <json-glib/json-glib.h>
#include "steam-runtime-tools/glib-backports-internal.h"
#include "steam-runtime-tools/utils-internal.h"
#ifdef MOCK_CHECK_LOCALE
typedef enum
{
MOCK_LOCALE_C,
#ifdef MOCK_CHECK_LOCALE_HAVE_C_UTF8
MOCK_LOCALE_C_UTF8,
#endif
#ifdef MOCK_CHECK_LOCALE_HAVE_EN_US
MOCK_LOCALE_EN_US,
MOCK_LOCALE_EN_US_UTF8,
#endif
MOCK_LOCALE_EN_GB_UTF8,
MOCK_LOCALE_COMPLICATED
} MockLocale;
static struct
{
const char *name;
const char *alias;
} mock_locales[] = {
{ "C", "POSIX" },
#ifdef MOCK_CHECK_LOCALE_HAVE_C_UTF8
{ "C.UTF-8", NULL },
#endif
#ifdef MOCK_CHECK_LOCALE_HAVE_EN_US
{ "en_US", NULL },
{ "en_US.UTF-8", NULL },
#endif
{ "en_GB.UTF-8", NULL },
/* This is what smcv's en_GB installation of Debian returns from
* setlocale(LC_ALL, ""). Why are some of them utf8 and some UTF-8?
* We just don't know. */
{ "LC_CTYPE=en_GB.UTF-8;"
"LC_NUMERIC=en_GB.utf8;"
"LC_TIME=en_GB.utf8;"
"LC_COLLATE=en_GB.UTF-8;"
"LC_MONETARY=en_GB.utf8;"
"LC_MESSAGES=en_GB.UTF-8;"
"LC_PAPER=en_GB.utf8;"
"LC_NAME=en_GB.UTF-8;"
"LC_ADDRESS=en_GB.UTF-8;"
"LC_TELEPHONE=en_GB.UTF-8;"
"LC_MEASUREMENT=en_GB.utf8;"
"LC_IDENTIFICATION=en_GB.UTF-8",
NULL },
};
G_STATIC_ASSERT (G_N_ELEMENTS (mock_locales) == MOCK_LOCALE_COMPLICATED + 1);
static MockLocale mock_locale = MOCK_LOCALE_C;
static const char *
mock_setlocale (const char *locale_name)
{
gsize i;
if (locale_name == NULL)
return mock_locales[mock_locale].name;
if (g_strcmp0 (locale_name, "") == 0)
{
#ifdef MOCK_CHECK_LOCALE_LEGACY
mock_locale = MOCK_LOCALE_EN_US;
#else
mock_locale = MOCK_LOCALE_COMPLICATED;
#endif
return mock_locales[mock_locale].name;
}
for (i = 0; i < G_N_ELEMENTS (mock_locales); i++)
{
if (g_strcmp0 (locale_name, mock_locales[i].name) == 0
|| g_strcmp0 (locale_name, mock_locales[i].alias) == 0)
{
mock_locale = i;
return mock_locales[mock_locale].name;
}
}
errno = ENOENT;
return NULL;
}
static gboolean
mock_get_charset (const char **charset)
{
switch (mock_locale)
{
case MOCK_LOCALE_C:
if (charset != NULL)
*charset = "ANSI_X3.4-1968";
return FALSE;
#ifdef MOCK_CHECK_LOCALE_HAVE_EN_US
case MOCK_LOCALE_EN_US:
if (charset != NULL)
*charset = "ISO-8859-1";
return FALSE;
#endif
#ifdef MOCK_CHECK_LOCALE_HAVE_C_UTF8
case MOCK_LOCALE_C_UTF8:
#endif
#ifdef MOCK_CHECK_LOCALE_HAVE_EN_US
case MOCK_LOCALE_EN_US_UTF8:
#endif
case MOCK_LOCALE_EN_GB_UTF8:
case MOCK_LOCALE_COMPLICATED:
default:
if (charset != NULL)
*charset = "UTF-8";
return TRUE;
}
}
#endif
static gchar *opt_locale = NULL;
static gboolean opt_print_version = FALSE;
static gboolean
opt_locale_cb (const char *name,
const char *value,
gpointer data,
GError **error)
{
if (opt_locale != NULL)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"At most one locale is expected");
return FALSE;
}
opt_locale = g_strdup (value);
return TRUE;
}
static const GOptionEntry option_entries[] =
{
{ "version", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_print_version,
"Print version number and exit", NULL },
{ G_OPTION_REMAINING, 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK,
opt_locale_cb,
"The locale to test [default: use environment variables]",
"[LOCALE]" },
{ NULL }
};
int
main (int argc,
char **argv)
{
GOptionContext *option_context = NULL;
GError *local_error = NULL;
const char *locale_result;
const char *locale_name;
const char *charset;
gchar *json = NULL;
JsonNode *root = NULL;
JsonBuilder *builder = NULL;
JsonGenerator *generator = NULL;
int ret = 1;
gboolean is_utf8;
option_context = g_option_context_new ("");
g_option_context_add_main_entries (option_context, option_entries, NULL);
if (!g_option_context_parse (option_context, &argc, &argv, &local_error))
{
ret = 2;
goto out;
}
if (opt_print_version)
{
/* Output version number as YAML for machine-readability,
* inspired by `ostree --version` and `docker version` */
g_print (
"%s:\n"
" Package: steam-runtime-tools\n"
" Version: %s\n",
argv[0], VERSION);
ret = 0;
goto out;
}
_srt_setenv_disable_gio_modules ();
if (opt_locale == NULL)
locale_name = "";
else
locale_name = opt_locale;
builder = json_builder_new ();
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "requested");
json_builder_add_string_value (builder, locale_name);
#ifdef MOCK_CHECK_LOCALE
locale_result = mock_setlocale (locale_name);
#else
locale_result = setlocale (LC_ALL, locale_name);
#endif
if (locale_result == NULL)
{
int saved_errno = errno;
json_builder_set_member_name (builder, "error");
json_builder_add_string_value (builder,
g_strerror (saved_errno));
ret = 1;
}
else
{
#ifdef MOCK_CHECK_LOCALE
is_utf8 = mock_get_charset (&charset);
#else
is_utf8 = g_get_charset (&charset);
#endif
json_builder_set_member_name (builder, "result");
json_builder_add_string_value (builder, locale_result);
json_builder_set_member_name (builder, "charset");
json_builder_add_string_value (builder, charset);
json_builder_set_member_name (builder, "is_utf8");
json_builder_add_boolean_value (builder, is_utf8);
ret = 0;
}
json_builder_end_object (builder);
root = json_builder_get_root (builder);
generator = json_generator_new ();
json_generator_set_pretty (generator, TRUE);
json_generator_set_root (generator, root);
json = json_generator_to_data (generator, NULL);
g_print ("%s\n", json);
out:
if (local_error != NULL)
g_printerr ("%s: %s\n", g_get_prgname (), local_error->message);
g_clear_object (&generator);
g_clear_object (&builder);
g_clear_error (&local_error);
g_clear_pointer (&root, json_node_free);
g_clear_pointer (&option_context, g_option_context_free);
g_free (opt_locale);
g_free (json);
return ret;
}