From 0026c400629cafc20ae16dc0ae98665927663275 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 26 Sep 2023 16:22:48 -0700 Subject: [PATCH] Simplify setlocale logic to rely on libc to fail if invalid Instead of checking if a locale is valid, let's change this logic: 1. We first try setlocale to inherit from env vars, system default. 2. Next, we fall back to unsetting LANG if it was set manually, allowing us to fall back to system defaults. 3. We fall back to en_US.UTF-8. --- src/os/locale.zig | 58 ++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/src/os/locale.zig b/src/os/locale.zig index e6a354f76..5f0a960bc 100644 --- a/src/os/locale.zig +++ b/src/os/locale.zig @@ -24,42 +24,31 @@ pub fn ensureLocale() void { } } - // If we have a locale set in the env, we verify it is valid. If it isn't - // valid, then we set it to a default value of en_US.UTF-8. We don't touch - // the existing LANG value in the env so that child processes can use it, - // though. - const locale: []const u8 = locale: { - // If we don't have lang set, we use "" and let the default happen. - const new_lang = std.os.getenv("LANG") orelse break :locale ""; - - // If the locale is valid, we also use "" because LANG is already set. - if (localeIsValid(new_lang)) break :locale ""; - - // If the locale is not valid we fall back to en_US.UTF-8 and need to - // set LANG to this value too. - const default_locale = "en_US.UTF-8"; - log.info("LANG is not valid according to libc, will use en_US.UTF-8", .{}); - - if (setenv("LANG", default_locale.ptr, 1) < 0) { - log.err("error setting LANG env var to {s}", .{default_locale}); - } - - // Use it as locale - break :locale default_locale; - }; - - // Set the locale - if (setlocale(LC_ALL, locale.ptr)) |v| { + // Set the locale to whatever is set in env vars. + if (setlocale(LC_ALL, "")) |v| { log.debug("setlocale result={s}", .{v}); - } else log.warn("setlocale failed, locale may be incorrect", .{}); -} + return; + } -/// Returns true if the given locale is valid according to libc. This value -/// can be `en_US` or `en_US.utf-8` style of formatting. -fn localeIsValid(locale: []const u8) bool { - const v = newlocale(LC_ALL_MASK, locale.ptr, null) orelse return false; - defer freelocale(v); - return true; + // setlocale failed. This is probably because the LANG env var is + // invalid. Try to set it without the LANG var set to use the system + // default. + if (std.os.getenv("LANG")) |old_lang| { + _ = unsetenv("LANG"); + defer _ = setenv("LANG", old_lang.ptr, 1); + + if (setlocale(LC_ALL, "")) |v| { + log.debug("setlocale result={s}", .{v}); + return; + } + } + + // Failure again... fallback to en_US.UTF-8 + log.warn("setlocale failed with LANG and system default. Falling back to en_US.UTF-8", .{}); + if (setlocale(LC_ALL, "en_US.UTF-8")) |v| { + log.debug("setlocale result={s}", .{v}); + return; + } else log.err("setlocale failed even with the fallback, uncertain results", .{}); } /// This sets the LANG environment variable based on the macOS system @@ -107,6 +96,7 @@ const LC_ALL: c_int = 6; // from locale.h const LC_ALL_MASK: c_int = 0x7fffffff; // from locale.h const locale_t = ?*anyopaque; extern "c" fn setenv(name: ?[*]const u8, value: ?[*]const u8, overwrite: c_int) c_int; +extern "c" fn unsetenv(name: ?[*]const u8) c_int; extern "c" fn setlocale(category: c_int, locale: ?[*]const u8) ?[*:0]u8; extern "c" fn newlocale(category: c_int, locale: ?[*]const u8, base: locale_t) locale_t; extern "c" fn freelocale(v: locale_t) void;