mirror of
https://github.com/neovim/neovim.git
synced 2026-02-20 01:09:56 +10:00
Allow Include What You Use to remove unnecessary includes and only include what is necessary. This helps with reducing compilation times and makes it easier to visualise which dependencies are actually required. Work on https://github.com/neovim/neovim/issues/549, but doesn't close it since this only works fully for .c files and not headers.
376 lines
9.2 KiB
C
376 lines
9.2 KiB
C
// 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
|
|
|
|
// locale.c: functions for language/locale configuration
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
|
|
#include "auto/config.h"
|
|
#ifdef HAVE_LOCALE_H
|
|
# include <locale.h>
|
|
#endif
|
|
|
|
#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/locale.h"
|
|
#include "nvim/macros.h"
|
|
#include "nvim/memory.h"
|
|
#include "nvim/message.h"
|
|
#include "nvim/option.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 "locale.c.generated.h"
|
|
#endif
|
|
|
|
#if defined(HAVE_LOCALE_H)
|
|
# define HAVE_GET_LOCALE_VAL
|
|
|
|
static char *get_locale_val(int what)
|
|
{
|
|
// Obtain the locale value from the libraries.
|
|
char *loc = setlocale(what, NULL);
|
|
|
|
return loc;
|
|
}
|
|
#endif
|
|
|
|
/// @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;
|
|
|
|
#ifdef HAVE_GET_LOCALE_VAL
|
|
# 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
|
|
#else
|
|
p = os_getenv("LC_ALL");
|
|
if (!is_valid_mess_lang(p)) {
|
|
p = os_getenv("LC_MESSAGES");
|
|
if (!is_valid_mess_lang(p)) {
|
|
p = os_getenv("LANG");
|
|
}
|
|
}
|
|
#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"
|
|
}
|
|
# ifdef HAVE_GET_LOCALE_VAL
|
|
if (p == NULL) {
|
|
p = get_locale_val(LC_CTYPE);
|
|
}
|
|
# endif
|
|
}
|
|
}
|
|
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;
|
|
|
|
#ifdef HAVE_GET_LOCALE_VAL
|
|
loc = get_locale_val(LC_CTYPE);
|
|
#else
|
|
// setlocale() not supported: use the default value
|
|
loc = "C";
|
|
#endif
|
|
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);
|
|
|
|
#ifdef HAVE_GET_LOCALE_VAL
|
|
loc = get_locale_val(LC_TIME);
|
|
#endif
|
|
set_vim_var_string(VV_LC_TIME, loc, -1);
|
|
|
|
#ifdef HAVE_GET_LOCALE_VAL
|
|
loc = get_locale_val(LC_COLLATE);
|
|
#else
|
|
// setlocale() not supported: use the default value
|
|
loc = "C";
|
|
#endif
|
|
set_vim_var_string(VV_COLLATE, loc, -1);
|
|
}
|
|
|
|
#if defined(HAVE_LOCALE_H)
|
|
/// 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");
|
|
}
|
|
#endif
|
|
|
|
#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 = (char *)get_cmd_output((char_u *)"locale -a", NULL,
|
|
kShellOptSilent, NULL);
|
|
if (locale_a == NULL) {
|
|
return NULL;
|
|
}
|
|
ga_init(&locales_ga, sizeof(char_u *), 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_u **)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) {
|
|
did_init_locales = true;
|
|
locales = find_locales();
|
|
}
|
|
# endif
|
|
}
|
|
|
|
# if defined(EXITFREE)
|
|
void free_locales(void)
|
|
{
|
|
int i;
|
|
if (locales != NULL) {
|
|
for (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
|