From 1b3c1f6c06d73e881bfc2a46e5ee3e0b24ba96d8 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 27 Feb 2023 19:37:43 +0100 Subject: refactor(build): graduate HAVE_LOCALE_H feature Merge locale.h into os/lang.h Having a source file with the same name as a system header we use is considered an anti-pattern. --- src/nvim/os/lang.c | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 337 insertions(+), 6 deletions(-) (limited to 'src/nvim/os/lang.c') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 57c82bba86..8ca2aa3a4b 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -7,16 +7,347 @@ # include # undef Boolean # undef FileInfo +#endif -# include "auto/config.h" -# ifdef HAVE_LOCALE_H -# include -# endif -# include "nvim/os/os.h" +#include +#include +#include + +#include "auto/config.h" +#include "nvim/ascii.h" +#include "nvim/buffer.h" +#include "nvim/charset.h" +#include "nvim/eval.h" +#include "nvim/ex_cmds_defs.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/macros.h" +#include "nvim/memory.h" +#include "nvim/message.h" +#include "nvim/option.h" +#include "nvim/os/lang.h" +#include "nvim/os/os.h" +#include "nvim/os/shell.h" +#include "nvim/path.h" +#include "nvim/profile.h" +#include "nvim/types.h" +#include "nvim/vim.h" +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/lang.c.generated.h" #endif -#include "nvim/os/lang.h" +static char *get_locale_val(int what) +{ + // Obtain the locale value from the libraries. + char *loc = setlocale(what, NULL); + + return loc; +} + +/// @return true when "lang" starts with a valid language name. +/// Rejects NULL, empty string, "C", "C.UTF-8" and others. +static bool is_valid_mess_lang(const char *lang) +{ + return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]); +} + +/// Obtain the current messages language. Used to set the default for +/// 'helplang'. May return NULL or an empty string. +char *get_mess_lang(void) +{ + char *p; + +#if defined(LC_MESSAGES) + p = get_locale_val(LC_MESSAGES); +#else + // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG + // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME + // and LC_MONETARY may be set differently for a Japanese working in the + // US. + p = get_locale_val(LC_COLLATE); +#endif + return is_valid_mess_lang(p) ? p : NULL; +} + +// Complicated #if; matches with where get_mess_env() is used below. +#ifdef HAVE_WORKING_LIBINTL +/// Get the language used for messages from the environment. +static char *get_mess_env(void) +{ + char *p; + + p = (char *)os_getenv("LC_ALL"); + if (p == NULL) { + p = (char *)os_getenv("LC_MESSAGES"); + if (p == NULL) { + p = (char *)os_getenv("LANG"); + if (p != NULL && ascii_isdigit(*p)) { + p = NULL; // ignore something like "1043" + } + if (p == NULL) { + p = get_locale_val(LC_CTYPE); + } + } + } + return p; +} +#endif + +/// Set the "v:lang" variable according to the current locale setting. +/// Also do "v:lc_time"and "v:ctype". +void set_lang_var(void) +{ + const char *loc; + + loc = get_locale_val(LC_CTYPE); + set_vim_var_string(VV_CTYPE, loc, -1); + + // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall + // back to LC_CTYPE if it's empty. +#ifdef HAVE_WORKING_LIBINTL + loc = get_mess_env(); +#elif defined(LC_MESSAGES) + loc = get_locale_val(LC_MESSAGES); +#else + // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE + loc = get_locale_val(LC_CTYPE); +#endif + set_vim_var_string(VV_LANG, loc, -1); + + loc = get_locale_val(LC_TIME); + set_vim_var_string(VV_LC_TIME, loc, -1); + + loc = get_locale_val(LC_COLLATE); + set_vim_var_string(VV_COLLATE, loc, -1); +} + +/// Setup to use the current locale (for ctype() and many other things). +void init_locale(void) +{ + setlocale(LC_ALL, ""); + +#ifdef LC_NUMERIC + // Make sure strtod() uses a decimal point, not a comma. + setlocale(LC_NUMERIC, "C"); +#endif + + char localepath[MAXPATHL] = { 0 }; + snprintf(localepath, sizeof(localepath), "%s", get_vim_var_str(VV_PROGPATH)); + char *tail = path_tail_with_sep(localepath); + *tail = NUL; + tail = path_tail(localepath); + xstrlcpy(tail, "share/locale", + sizeof(localepath) - (size_t)(tail - localepath)); + bindtextdomain(PROJECT_NAME, localepath); + textdomain(PROJECT_NAME); + TIME_MSG("locale set"); +} + +#ifdef HAVE_WORKING_LIBINTL + +/// ":language": Set the language (locale). +/// +/// @param eap +void ex_language(exarg_T *eap) +{ + char *loc; + char *p; + char *name; + int what = LC_ALL; + char *whatstr = ""; +# ifdef LC_MESSAGES +# define VIM_LC_MESSAGES LC_MESSAGES +# else +# define VIM_LC_MESSAGES 6789 +# endif + + name = eap->arg; + + // Check for "messages {name}", "ctype {name}" or "time {name}" argument. + // Allow abbreviation, but require at least 3 characters to avoid + // confusion with a two letter language name "me" or "ct". + p = skiptowhite(eap->arg); + if ((*p == NUL || ascii_iswhite(*p)) && p - eap->arg >= 3) { + if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0) { + what = VIM_LC_MESSAGES; + name = skipwhite(p); + whatstr = "messages "; + } else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0) { + what = LC_CTYPE; + name = skipwhite(p); + whatstr = "ctype "; + } else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0) { + what = LC_TIME; + name = skipwhite(p); + whatstr = "time "; + } else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0) { + what = LC_COLLATE; + name = skipwhite(p); + whatstr = "collate "; + } + } + + if (*name == NUL) { + if (what == VIM_LC_MESSAGES) { + p = get_mess_env(); + } else { + p = setlocale(what, NULL); + } + if (p == NULL || *p == NUL) { + p = "Unknown"; + } + smsg(_("Current %slanguage: \"%s\""), whatstr, p); + } else { +# ifndef LC_MESSAGES + if (what == VIM_LC_MESSAGES) { + loc = ""; + } else { +# endif + loc = setlocale(what, name); +# ifdef LC_NUMERIC + // Make sure strtod() uses a decimal point, not a comma. + setlocale(LC_NUMERIC, "C"); +# endif +# ifndef LC_MESSAGES + } +# endif + if (loc == NULL) { + semsg(_("E197: Cannot set language to \"%s\""), name); + } else { +# ifdef HAVE_NL_MSG_CAT_CNTR + // Need to do this for GNU gettext, otherwise cached translations + // will be used again. + extern int _nl_msg_cat_cntr; + + _nl_msg_cat_cntr++; +# endif + // Reset $LC_ALL, otherwise it would overrule everything. + os_setenv("LC_ALL", "", 1); + + if (what != LC_TIME && what != LC_COLLATE) { + // Tell gettext() what to translate to. It apparently doesn't + // use the currently effective locale. + if (what == LC_ALL) { + os_setenv("LANG", name, 1); + + // Clear $LANGUAGE because GNU gettext uses it. + os_setenv("LANGUAGE", "", 1); + } + if (what != LC_CTYPE) { + os_setenv("LC_MESSAGES", name, 1); + set_helplang_default(name); + } + } + + // Set v:lang, v:lc_time, v:collate and v:ctype to the final result. + set_lang_var(); + maketitle(); + } + } +} + +static char **locales = NULL; // Array of all available locales + +# ifndef MSWIN +static bool did_init_locales = false; + +/// @return an array of strings for all available locales + NULL for the +/// last element or, +/// NULL in case of error. +static char **find_locales(void) +{ + garray_T locales_ga; + char *loc; + char *saveptr = NULL; + + // Find all available locales by running command "locale -a". If this + // doesn't work we won't have completion. + char *locale_a = get_cmd_output("locale -a", NULL, kShellOptSilent, NULL); + if (locale_a == NULL) { + return NULL; + } + ga_init(&locales_ga, sizeof(char *), 20); + + // Transform locale_a string where each locale is separated by "\n" + // into an array of locale strings. + loc = os_strtok(locale_a, "\n", &saveptr); + + while (loc != NULL) { + loc = xstrdup(loc); + GA_APPEND(char *, &locales_ga, loc); + loc = os_strtok(NULL, "\n", &saveptr); + } + xfree(locale_a); + // Guarantee that .ga_data is NULL terminated + ga_grow(&locales_ga, 1); + ((char **)locales_ga.ga_data)[locales_ga.ga_len] = NULL; + return locales_ga.ga_data; +} +# endif + +/// Lazy initialization of all available locales. +static void init_locales(void) +{ +# ifndef MSWIN + if (did_init_locales) { + return; + } + + did_init_locales = true; + locales = find_locales(); +# endif +} + +# if defined(EXITFREE) +void free_locales(void) +{ + if (locales == NULL) { + return; + } + + for (int i = 0; locales[i] != NULL; i++) { + xfree(locales[i]); + } + XFREE_CLEAR(locales); +} +# endif + +/// Function given to ExpandGeneric() to obtain the possible arguments of the +/// ":language" command. +char *get_lang_arg(expand_T *xp, int idx) +{ + if (idx == 0) { + return "messages"; + } + if (idx == 1) { + return "ctype"; + } + if (idx == 2) { + return "time"; + } + if (idx == 3) { + return "collate"; + } + + init_locales(); + if (locales == NULL) { + return NULL; + } + return locales[idx - 4]; +} + +/// Function given to ExpandGeneric() to obtain the available locales. +char *get_locales(expand_T *xp, int idx) +{ + init_locales(); + if (locales == NULL) { + return NULL; + } + return locales[idx]; +} + +#endif void lang_init(void) { -- cgit From bc13bc154aa574e0bb58a50f2e0ca4570efa57c3 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 29 Sep 2023 16:10:54 +0200 Subject: refactor(message): smsg_attr -> smsg --- src/nvim/os/lang.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/os/lang.c') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 8ca2aa3a4b..652b851903 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -197,7 +197,7 @@ void ex_language(exarg_T *eap) if (p == NULL || *p == NUL) { p = "Unknown"; } - smsg(_("Current %slanguage: \"%s\""), whatstr, p); + smsg(0, _("Current %slanguage: \"%s\""), whatstr, p); } else { # ifndef LC_MESSAGES if (what == VIM_LC_MESSAGES) { -- cgit From 09a17f91d0d362c6e58bfdbe3ccdeacffb0b44b9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 2 Oct 2023 10:45:33 +0800 Subject: refactor: move cmdline completion types to cmdexpand_defs.h (#25465) --- src/nvim/os/lang.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/os/lang.c') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 652b851903..c3958cb3f2 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -5,6 +5,7 @@ # define Boolean CFBoolean // Avoid conflict with API's Boolean # define FileInfo CSFileInfo // Avoid conflict with API's Fileinfo # include + # undef Boolean # undef FileInfo #endif @@ -17,6 +18,7 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval.h" #include "nvim/ex_cmds_defs.h" #include "nvim/garray.h" @@ -30,7 +32,6 @@ #include "nvim/os/shell.h" #include "nvim/path.h" #include "nvim/profile.h" -#include "nvim/types.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From 3a44db510b1661eb92689e328d5191acfcb95433 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 11 Oct 2023 22:16:25 +0200 Subject: refactor(lang): reduce scope of HAVE_WORKING_LIBINTL #ifdefs A lot of code inside HAVE_WORKING_LIBINTL doesn't really depend on a "working libintl". For instance ex_language is also used for ":lang collate" and ":lang time". Also ":lang C" should not fail just because translations aren't available (it just means use the default text). References: https://github.com/neovim/neovim/pull/1 2d00ead2e57653b771354a564f9a760c2ce0d18e separate ifdefs for locale and gettext got merged together. https://github.com/neovim/neovim/commit/8253e29971c54310434ee93d987a99994769d717 Unmotivated switcharoo of get_mess_env() logic. If available, get_locale_val(LC_MESSAGES) is the correct implementation. --- src/nvim/os/lang.c | 68 +++++++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 39 deletions(-) (limited to 'src/nvim/os/lang.c') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index c3958cb3f2..93c200f6e8 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -71,20 +71,23 @@ char *get_mess_lang(void) return is_valid_mess_lang(p) ? p : NULL; } -// Complicated #if; matches with where get_mess_env() is used below. -#ifdef HAVE_WORKING_LIBINTL /// Get the language used for messages from the environment. +/// +/// This uses LC_MESSAGES when available, which it is for most systems we build for +/// except for windows. Then fallback to get the value from the envirionment +/// ourselves, and use LC_CTYPE as a last resort. static char *get_mess_env(void) { - char *p; - - p = (char *)os_getenv("LC_ALL"); +#ifdef LC_MESSAGES + return get_locale_val(LC_MESSAGES); +#else + char *p = (char *)os_getenv("LC_ALL"); if (p == NULL) { p = (char *)os_getenv("LC_MESSAGES"); if (p == NULL) { p = (char *)os_getenv("LANG"); if (p != NULL && ascii_isdigit(*p)) { - p = NULL; // ignore something like "1043" + p = NULL; // ignore something like "1043" } if (p == NULL) { p = get_locale_val(LC_CTYPE); @@ -92,8 +95,8 @@ static char *get_mess_env(void) } } return p; -} #endif +} /// Set the "v:lang" variable according to the current locale setting. /// Also do "v:lc_time"and "v:ctype". @@ -104,16 +107,7 @@ void set_lang_var(void) loc = get_locale_val(LC_CTYPE); set_vim_var_string(VV_CTYPE, loc, -1); - // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall - // back to LC_CTYPE if it's empty. -#ifdef HAVE_WORKING_LIBINTL loc = get_mess_env(); -#elif defined(LC_MESSAGES) - loc = get_locale_val(LC_MESSAGES); -#else - // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE - loc = get_locale_val(LC_CTYPE); -#endif set_vim_var_string(VV_LANG, loc, -1); loc = get_locale_val(LC_TIME); @@ -145,8 +139,6 @@ void init_locale(void) TIME_MSG("locale set"); } -#ifdef HAVE_WORKING_LIBINTL - /// ":language": Set the language (locale). /// /// @param eap @@ -157,11 +149,11 @@ void ex_language(exarg_T *eap) char *name; int what = LC_ALL; char *whatstr = ""; -# ifdef LC_MESSAGES -# define VIM_LC_MESSAGES LC_MESSAGES -# else -# define VIM_LC_MESSAGES 6789 -# endif +#ifdef LC_MESSAGES +# define VIM_LC_MESSAGES LC_MESSAGES +#else +# define VIM_LC_MESSAGES 6789 +#endif name = eap->arg; @@ -200,29 +192,29 @@ void ex_language(exarg_T *eap) } smsg(0, _("Current %slanguage: \"%s\""), whatstr, p); } else { -# ifndef LC_MESSAGES +#ifndef LC_MESSAGES if (what == VIM_LC_MESSAGES) { loc = ""; } else { -# endif +#endif loc = setlocale(what, name); -# ifdef LC_NUMERIC +#ifdef LC_NUMERIC // Make sure strtod() uses a decimal point, not a comma. setlocale(LC_NUMERIC, "C"); -# endif -# ifndef LC_MESSAGES +#endif +#ifndef LC_MESSAGES } -# endif +#endif if (loc == NULL) { semsg(_("E197: Cannot set language to \"%s\""), name); } else { -# ifdef HAVE_NL_MSG_CAT_CNTR +#ifdef HAVE_NL_MSG_CAT_CNTR // Need to do this for GNU gettext, otherwise cached translations // will be used again. extern int _nl_msg_cat_cntr; _nl_msg_cat_cntr++; -# endif +#endif // Reset $LC_ALL, otherwise it would overrule everything. os_setenv("LC_ALL", "", 1); @@ -250,7 +242,7 @@ void ex_language(exarg_T *eap) static char **locales = NULL; // Array of all available locales -# ifndef MSWIN +#ifndef MSWIN static bool did_init_locales = false; /// @return an array of strings for all available locales + NULL for the @@ -285,22 +277,22 @@ static char **find_locales(void) ((char **)locales_ga.ga_data)[locales_ga.ga_len] = NULL; return locales_ga.ga_data; } -# endif +#endif /// Lazy initialization of all available locales. static void init_locales(void) { -# ifndef MSWIN +#ifndef MSWIN if (did_init_locales) { return; } did_init_locales = true; locales = find_locales(); -# endif +#endif } -# if defined(EXITFREE) +#if defined(EXITFREE) void free_locales(void) { if (locales == NULL) { @@ -312,7 +304,7 @@ void free_locales(void) } XFREE_CLEAR(locales); } -# endif +#endif /// Function given to ExpandGeneric() to obtain the possible arguments of the /// ":language" command. @@ -348,8 +340,6 @@ char *get_locales(expand_T *xp, int idx) return locales[idx]; } -#endif - void lang_init(void) { #ifdef __APPLE__ -- cgit From 2dc9ceb99c018b15dcf0c443cad46efecccaf94e Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 29 Oct 2023 09:02:32 +0100 Subject: docs: small fixes (#25585) Co-authored-by: tmummert Co-authored-by: parikshit adhikari --- src/nvim/os/lang.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/os/lang.c') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 93c200f6e8..602dcc9bd3 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -74,7 +74,7 @@ char *get_mess_lang(void) /// Get the language used for messages from the environment. /// /// This uses LC_MESSAGES when available, which it is for most systems we build for -/// except for windows. Then fallback to get the value from the envirionment +/// except for windows. Then fallback to get the value from the environment /// ourselves, and use LC_CTYPE as a last resort. static char *get_mess_env(void) { -- cgit From 353a4be7e84fdc101318215bdcc8a7e780d737fe Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 12 Nov 2023 13:13:58 +0100 Subject: build: remove PVS We already have an extensive suite of static analysis tools we use, which causes a fair bit of redundancy as we get duplicate warnings. PVS is also prone to give false warnings which creates a lot of work to identify and disable. --- src/nvim/os/lang.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/os/lang.c') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 602dcc9bd3..d14c086a4f 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifdef __APPLE__ # define Boolean CFBoolean // Avoid conflict with API's Boolean # define FileInfo CSFileInfo // Avoid conflict with API's Fileinfo -- cgit From 488038580934f301c1528a14548ec0cabd16c2cd Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 10 Nov 2023 14:06:04 +0100 Subject: build: adjust clang-tidy warning exclusion logic Enable all clang-tidy warnings by default instead of disabling them. This ensures that we don't miss useful warnings on each clang-tidy version upgrade. A drawback of this is that it will force us to either fix or adjust the warnings as soon as possible. --- src/nvim/os/lang.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/os/lang.c') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index d14c086a4f..e6db41a8ac 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -208,7 +208,7 @@ void ex_language(exarg_T *eap) #ifdef HAVE_NL_MSG_CAT_CNTR // Need to do this for GNU gettext, otherwise cached translations // will be used again. - extern int _nl_msg_cat_cntr; + extern int _nl_msg_cat_cntr; // NOLINT(bugprone-reserved-identifier) _nl_msg_cat_cntr++; #endif -- cgit From 79b6ff28ad1204fbb4199b9092f5c578d88cb28e Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 28 Nov 2023 20:31:00 +0100 Subject: refactor: fix headers with IWYU --- src/nvim/os/lang.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/os/lang.c') diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index e6db41a8ac..17d179a56a 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -12,7 +12,7 @@ #include #include "auto/config.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" @@ -20,7 +20,7 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/garray.h" #include "nvim/gettext.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" @@ -29,7 +29,7 @@ #include "nvim/os/shell.h" #include "nvim/path.h" #include "nvim/profile.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/lang.c.generated.h" -- cgit