To: vim_dev@googlegroups.com Subject: Patch 8.2.1269 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1269 Problem: Language and locale code spread out. Solution: Move relevant code to src/locale.c. (Yegappan Lakshmanan, closes #6509) Files: Filelist, src/Make_cyg_ming.mak, src/Make_morph.mak, src/Make_mvc.mak, src/Make_vms.mms, src/Makefile, src/README.md, src/ex_cmds2.c, src/locale.c, src/main.c, src/proto.h, src/proto/ex_cmds2.pro, src/proto/locale.pro *** ../vim-8.2.1268/Filelist 2020-07-21 21:07:00.716496755 +0200 --- Filelist 2020-07-22 19:03:10.315786917 +0200 *************** *** 76,81 **** --- 76,82 ---- src/json_test.c \ src/kword_test.c \ src/list.c \ + src/locale.c \ src/keymap.h \ src/macros.h \ src/main.c \ *************** *** 247,252 **** --- 248,254 ---- src/proto/insexpand.pro \ src/proto/json.pro \ src/proto/list.pro \ + src/proto/locale.pro \ src/proto/main.pro \ src/proto/map.pro \ src/proto/mark.pro \ *** ../vim-8.2.1268/src/Make_cyg_ming.mak 2020-07-21 21:07:00.716496755 +0200 --- src/Make_cyg_ming.mak 2020-07-22 19:03:10.315786917 +0200 *************** *** 751,756 **** --- 751,757 ---- $(OUTDIR)/insexpand.o \ $(OUTDIR)/json.o \ $(OUTDIR)/list.o \ + $(OUTDIR)/locale.o \ $(OUTDIR)/main.o \ $(OUTDIR)/map.o \ $(OUTDIR)/mark.o \ *** ../vim-8.2.1268/src/Make_morph.mak 2020-07-21 21:07:00.716496755 +0200 --- src/Make_morph.mak 2020-07-22 19:03:10.315786917 +0200 *************** *** 70,75 **** --- 70,76 ---- insexpand.c \ json.c \ list.c \ + locale.c \ main.c \ map.c \ mark.c \ *** ../vim-8.2.1268/src/Make_mvc.mak 2020-07-21 21:07:00.716496755 +0200 --- src/Make_mvc.mak 2020-07-22 19:03:10.315786917 +0200 *************** *** 773,778 **** --- 773,779 ---- $(OUTDIR)\insexpand.obj \ $(OUTDIR)\json.obj \ $(OUTDIR)\list.obj \ + $(OUTDIR)\locale.obj \ $(OUTDIR)\main.obj \ $(OUTDIR)\map.obj \ $(OUTDIR)\mark.obj \ *************** *** 1669,1674 **** --- 1670,1677 ---- $(OUTDIR)/list.obj: $(OUTDIR) list.c $(INCL) + $(OUTDIR)/locale.obj: $(OUTDIR) locale.c $(INCL) + $(OUTDIR)/main.obj: $(OUTDIR) main.c $(INCL) $(CUI_INCL) $(OUTDIR)/map.obj: $(OUTDIR) map.c $(INCL) *************** *** 1939,1944 **** --- 1942,1948 ---- proto/insexpand.pro \ proto/json.pro \ proto/list.pro \ + proto/locale.pro \ proto/main.pro \ proto/map.pro \ proto/mark.pro \ *** ../vim-8.2.1268/src/Make_vms.mms 2020-07-21 21:07:00.716496755 +0200 --- src/Make_vms.mms 2020-07-22 19:03:10.315786917 +0200 *************** *** 345,350 **** --- 345,351 ---- insexpand.c \ json.c \ list.c \ + locale.c \ main.c \ map.c \ mark.c \ *************** *** 460,465 **** --- 461,467 ---- insexpand.obj \ json.obj \ list.obj \ + locale.obj \ main.obj \ map.obj \ mark.obj \ *************** *** 865,870 **** --- 867,876 ---- ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ globals.h + locale.obj : locale.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ + globals.h main.obj : main.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \ [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h \ *** ../vim-8.2.1268/src/Makefile 2020-07-21 22:34:37.909099597 +0200 --- src/Makefile 2020-07-22 19:03:10.315786917 +0200 *************** *** 1647,1652 **** --- 1647,1653 ---- insexpand.c \ json.c \ list.c \ + locale.c \ main.c \ map.c \ mark.c \ *************** *** 1798,1803 **** --- 1799,1805 ---- objects/indent.o \ objects/insexpand.o \ objects/list.o \ + objects/locale.o \ objects/map.o \ objects/mark.o \ objects/match.o \ *************** *** 1973,1978 **** --- 1975,1981 ---- insexpand.pro \ json.pro \ list.pro \ + locale.pro \ main.pro \ map.pro \ mark.pro \ *************** *** 3378,3383 **** --- 3381,3389 ---- objects/list.o: list.c $(CCC) -o $@ list.c + objects/locale.o: locale.c + $(CCC) -o $@ locale.c + objects/main.o: main.c $(CCC) -o $@ main.c *************** *** 3968,3973 **** --- 3974,3983 ---- auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h + objects/locale.o: locale.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h objects/main.o: main.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ *** ../vim-8.2.1268/src/README.md 2020-07-21 21:07:00.716496755 +0200 --- src/README.md 2020-07-22 19:03:10.315786917 +0200 *************** *** 52,57 **** --- 52,58 ---- highlight.c | syntax highlighting indent.c | text indentation insexpand.c | Insert mode completion + locale.c | locale/language handling map.c | mapping and abbreviations mark.c | marks match.c | highlight matching *** ../vim-8.2.1268/src/ex_cmds2.c 2020-06-16 20:03:38.747351038 +0200 --- src/ex_cmds2.c 2020-07-22 19:03:10.315786917 +0200 *************** *** 996,1500 **** } no_check_timestamps = save_no_check_timestamps; } - - #if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ - && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG)) - # define HAVE_GET_LOCALE_VAL - static char_u * - get_locale_val(int what) - { - char_u *loc; - - // Obtain the locale value from the libraries. - loc = (char_u *)setlocale(what, NULL); - - # ifdef MSWIN - if (loc != NULL) - { - char_u *p; - - // setocale() returns something like "LC_COLLATE=;LC_..." when - // one of the values (e.g., LC_CTYPE) differs. - p = vim_strchr(loc, '='); - if (p != NULL) - { - loc = ++p; - while (*p != NUL) // remove trailing newline - { - if (*p < ' ' || *p == ';') - { - *p = NUL; - break; - } - ++p; - } - } - } - # endif - - return loc; - } - #endif - - - #ifdef MSWIN - /* - * On MS-Windows locale names are strings like "German_Germany.1252", but - * gettext expects "de". Try to translate one into another here for a few - * supported languages. - */ - static char_u * - gettext_lang(char_u *name) - { - int i; - static char *(mtable[]) = { - "afrikaans", "af", - "czech", "cs", - "dutch", "nl", - "german", "de", - "english_united kingdom", "en_GB", - "spanish", "es", - "french", "fr", - "italian", "it", - "japanese", "ja", - "korean", "ko", - "norwegian", "no", - "polish", "pl", - "russian", "ru", - "slovak", "sk", - "swedish", "sv", - "ukrainian", "uk", - "chinese_china", "zh_CN", - "chinese_taiwan", "zh_TW", - NULL}; - - for (i = 0; mtable[i] != NULL; i += 2) - if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0) - return (char_u *)mtable[i + 1]; - return name; - } - #endif - - #if defined(FEAT_MULTI_LANG) || defined(PROTO) - /* - * Return TRUE when "lang" starts with a valid language name. - * Rejects NULL, empty string, "C", "C.UTF-8" and others. - */ - static int - is_valid_mess_lang(char_u *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_u * - get_mess_lang(void) - { - char_u *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 = mch_getenv((char_u *)"LC_ALL"); - if (!is_valid_mess_lang(p)) - { - p = mch_getenv((char_u *)"LC_MESSAGES"); - if (!is_valid_mess_lang(p)) - p = mch_getenv((char_u *)"LANG"); - } - # endif - # ifdef MSWIN - p = gettext_lang(p); - # endif - return is_valid_mess_lang(p) ? p : NULL; - } - #endif - - // Complicated #if; matches with where get_mess_env() is used below. - #if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ - && defined(LC_MESSAGES))) \ - || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ - && !defined(LC_MESSAGES)) - /* - * Get the language used for messages from the environment. - */ - static char_u * - get_mess_env(void) - { - char_u *p; - - p = mch_getenv((char_u *)"LC_ALL"); - if (p == NULL || *p == NUL) - { - p = mch_getenv((char_u *)"LC_MESSAGES"); - if (p == NULL || *p == NUL) - { - p = mch_getenv((char_u *)"LANG"); - if (p != NULL && VIM_ISDIGIT(*p)) - p = NULL; // ignore something like "1043" - # ifdef HAVE_GET_LOCALE_VAL - if (p == NULL || *p == NUL) - p = get_locale_val(LC_CTYPE); - # endif - } - } - return p; - } - #endif - - #if defined(FEAT_EVAL) || defined(PROTO) - - /* - * Set the "v:lang" variable according to the current locale setting. - * Also do "v:lc_time"and "v:ctype". - */ - void - set_lang_var(void) - { - char_u *loc; - - # ifdef HAVE_GET_LOCALE_VAL - loc = get_locale_val(LC_CTYPE); - # else - // setlocale() not supported: use the default value - loc = (char_u *)"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. - # if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES) - loc = get_locale_val(LC_MESSAGES); - # else - loc = get_mess_env(); - # 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 = (char_u *)"C"; - # endif - set_vim_var_string(VV_COLLATE, loc, -1); - } - #endif - - #if defined(HAVE_LOCALE_H) || defined(X_LOCALE) - /* - * ":language": Set the language (locale). - */ - void - ex_language(exarg_T *eap) - { - char *loc; - char_u *p; - char_u *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 || VIM_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) - { - # ifndef LC_MESSAGES - if (what == VIM_LC_MESSAGES) - p = get_mess_env(); - else - # endif - p = (char_u *)setlocale(what, NULL); - if (p == NULL || *p == NUL) - p = (char_u *)"Unknown"; - smsg(_("Current %slanguage: \"%s\""), whatstr, p); - } - else - { - # ifndef LC_MESSAGES - if (what == VIM_LC_MESSAGES) - loc = ""; - else - # endif - { - loc = setlocale(what, (char *)name); - # if defined(FEAT_FLOAT) && defined(LC_NUMERIC) - // Make sure strtod() uses a decimal point, not a comma. - setlocale(LC_NUMERIC, "C"); - # 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. - vim_setenv((char_u *)"LC_ALL", (char_u *)""); - - if (what != LC_TIME && what != LC_COLLATE) - { - // Tell gettext() what to translate to. It apparently doesn't - // use the currently effective locale. Also do this when - // FEAT_GETTEXT isn't defined, so that shell commands use this - // value. - if (what == LC_ALL) - { - vim_setenv((char_u *)"LANG", name); - - // Clear $LANGUAGE because GNU gettext uses it. - vim_setenv((char_u *)"LANGUAGE", (char_u *)""); - # ifdef MSWIN - // Apparently MS-Windows printf() may cause a crash when - // we give it 8-bit text while it's expecting text in the - // current locale. This call avoids that. - setlocale(LC_CTYPE, "C"); - # endif - } - if (what != LC_CTYPE) - { - char_u *mname; - # ifdef MSWIN - mname = gettext_lang(name); - # else - mname = name; - # endif - vim_setenv((char_u *)"LC_MESSAGES", mname); - # ifdef FEAT_MULTI_LANG - set_helplang_default(mname); - # endif - } - } - - # ifdef FEAT_EVAL - // Set v:lang, v:lc_time, v:collate and v:ctype to the final result. - set_lang_var(); - # endif - # ifdef FEAT_TITLE - maketitle(); - # endif - } - } - } - - static char_u **locales = NULL; // Array of all available locales - - static int did_init_locales = FALSE; - - /* - * Return an array of strings for all available locales + NULL for the - * last element. Return NULL in case of error. - */ - static char_u ** - find_locales(void) - { - garray_T locales_ga; - char_u *loc; - char_u *locale_list; - # ifdef MSWIN - size_t len = 0; - # endif - - // Find all available locales by running command "locale -a". If this - // doesn't work we won't have completion. - # ifndef MSWIN - locale_list = get_cmd_output((char_u *)"locale -a", - NULL, SHELL_SILENT, NULL); - # else - // Find all available locales by examining the directories in - // $VIMRUNTIME/lang/ - { - int options = WILD_SILENT|WILD_USE_NL|WILD_KEEP_ALL; - expand_T xpc; - char_u *p; - - ExpandInit(&xpc); - xpc.xp_context = EXPAND_DIRECTORIES; - locale_list = ExpandOne(&xpc, (char_u *)"$VIMRUNTIME/lang/*", - NULL, options, WILD_ALL); - ExpandCleanup(&xpc); - if (locale_list == NULL) - // Add a dummy input, that will be skipped lated but we need to - // have something in locale_list so that the C locale is added at - // the end. - locale_list = vim_strsave((char_u *)".\n"); - p = locale_list; - // find the last directory delimiter - while (p != NULL && *p != NUL) - { - if (*p == '\n') - break; - if (*p == '\\') - len = p - locale_list; - p++; - } - } - # endif - if (locale_list == NULL) - return NULL; - ga_init2(&locales_ga, sizeof(char_u *), 20); - - // Transform locale_list string where each locale is separated by "\n" - // into an array of locale strings. - loc = (char_u *)strtok((char *)locale_list, "\n"); - - while (loc != NULL) - { - int ignore = FALSE; - - # ifdef MSWIN - if (len > 0) - loc += len + 1; - // skip locales with a dot (which indicates the charset) - if (vim_strchr(loc, '.') != NULL) - ignore = TRUE; - # endif - if (!ignore) - { - if (ga_grow(&locales_ga, 1) == FAIL) - break; - - loc = vim_strsave(loc); - if (loc == NULL) - break; - - ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc; - } - loc = (char_u *)strtok(NULL, "\n"); - } - - # ifdef MSWIN - // Add the C locale - if (ga_grow(&locales_ga, 1) == OK) - ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = - vim_strsave((char_u *)"C"); - # endif - - vim_free(locale_list); - if (ga_grow(&locales_ga, 1) == FAIL) - { - ga_clear(&locales_ga); - return NULL; - } - ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL; - return (char_u **)locales_ga.ga_data; - } - - /* - * Lazy initialization of all available locales. - */ - static void - init_locales(void) - { - if (!did_init_locales) - { - did_init_locales = TRUE; - locales = find_locales(); - } - } - - # if defined(EXITFREE) || defined(PROTO) - void - free_locales(void) - { - int i; - if (locales != NULL) - { - for (i = 0; locales[i] != NULL; i++) - vim_free(locales[i]); - VIM_CLEAR(locales); - } - } - # endif - - /* - * Function given to ExpandGeneric() to obtain the possible arguments of the - * ":language" command. - */ - char_u * - get_lang_arg(expand_T *xp UNUSED, int idx) - { - if (idx == 0) - return (char_u *)"messages"; - if (idx == 1) - return (char_u *)"ctype"; - if (idx == 2) - return (char_u *)"time"; - if (idx == 3) - return (char_u *)"collate"; - - init_locales(); - if (locales == NULL) - return NULL; - return locales[idx - 4]; - } - - /* - * Function given to ExpandGeneric() to obtain the available locales. - */ - char_u * - get_locales(expand_T *xp UNUSED, int idx) - { - init_locales(); - if (locales == NULL) - return NULL; - return locales[idx]; - } - - #endif --- 996,998 ---- *** ../vim-8.2.1268/src/locale.c 2020-07-22 19:10:21.069268953 +0200 --- src/locale.c 2020-07-22 19:03:10.315786917 +0200 *************** *** 0 **** --- 1,564 ---- + /* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + + /* + * locale.c: functions for language/locale configuration + */ + + #include "vim.h" + + #if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ + && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG)) + # define HAVE_GET_LOCALE_VAL + static char_u * + get_locale_val(int what) + { + char_u *loc; + + // Obtain the locale value from the libraries. + loc = (char_u *)setlocale(what, NULL); + + # ifdef MSWIN + if (loc != NULL) + { + char_u *p; + + // setocale() returns something like "LC_COLLATE=;LC_..." when + // one of the values (e.g., LC_CTYPE) differs. + p = vim_strchr(loc, '='); + if (p != NULL) + { + loc = ++p; + while (*p != NUL) // remove trailing newline + { + if (*p < ' ' || *p == ';') + { + *p = NUL; + break; + } + ++p; + } + } + } + # endif + + return loc; + } + #endif + + + #ifdef MSWIN + /* + * On MS-Windows locale names are strings like "German_Germany.1252", but + * gettext expects "de". Try to translate one into another here for a few + * supported languages. + */ + static char_u * + gettext_lang(char_u *name) + { + int i; + static char *(mtable[]) = { + "afrikaans", "af", + "czech", "cs", + "dutch", "nl", + "german", "de", + "english_united kingdom", "en_GB", + "spanish", "es", + "french", "fr", + "italian", "it", + "japanese", "ja", + "korean", "ko", + "norwegian", "no", + "polish", "pl", + "russian", "ru", + "slovak", "sk", + "swedish", "sv", + "ukrainian", "uk", + "chinese_china", "zh_CN", + "chinese_taiwan", "zh_TW", + NULL}; + + for (i = 0; mtable[i] != NULL; i += 2) + if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0) + return (char_u *)mtable[i + 1]; + return name; + } + #endif + + #if defined(FEAT_MULTI_LANG) || defined(PROTO) + /* + * Return TRUE when "lang" starts with a valid language name. + * Rejects NULL, empty string, "C", "C.UTF-8" and others. + */ + static int + is_valid_mess_lang(char_u *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_u * + get_mess_lang(void) + { + char_u *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 = mch_getenv((char_u *)"LC_ALL"); + if (!is_valid_mess_lang(p)) + { + p = mch_getenv((char_u *)"LC_MESSAGES"); + if (!is_valid_mess_lang(p)) + p = mch_getenv((char_u *)"LANG"); + } + # endif + # ifdef MSWIN + p = gettext_lang(p); + # endif + return is_valid_mess_lang(p) ? p : NULL; + } + #endif + + // Complicated #if; matches with where get_mess_env() is used below. + #if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ + && defined(LC_MESSAGES))) \ + || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \ + && !defined(LC_MESSAGES)) + /* + * Get the language used for messages from the environment. + */ + static char_u * + get_mess_env(void) + { + char_u *p; + + p = mch_getenv((char_u *)"LC_ALL"); + if (p == NULL || *p == NUL) + { + p = mch_getenv((char_u *)"LC_MESSAGES"); + if (p == NULL || *p == NUL) + { + p = mch_getenv((char_u *)"LANG"); + if (p != NULL && VIM_ISDIGIT(*p)) + p = NULL; // ignore something like "1043" + # ifdef HAVE_GET_LOCALE_VAL + if (p == NULL || *p == NUL) + p = get_locale_val(LC_CTYPE); + # endif + } + } + return p; + } + #endif + + #if defined(FEAT_EVAL) || defined(PROTO) + + /* + * Set the "v:lang" variable according to the current locale setting. + * Also do "v:lc_time"and "v:ctype". + */ + void + set_lang_var(void) + { + char_u *loc; + + # ifdef HAVE_GET_LOCALE_VAL + loc = get_locale_val(LC_CTYPE); + # else + // setlocale() not supported: use the default value + loc = (char_u *)"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. + # if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES) + loc = get_locale_val(LC_MESSAGES); + # else + loc = get_mess_env(); + # 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 = (char_u *)"C"; + # endif + set_vim_var_string(VV_COLLATE, loc, -1); + } + #endif + + #if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + /* + * Setup to use the current locale (for ctype() and many other things). + */ + void + init_locale(void) + { + setlocale(LC_ALL, ""); + + # ifdef FEAT_GUI_GTK + // Tell Gtk not to change our locale settings. + gtk_disable_setlocale(); + # endif + # if defined(FEAT_FLOAT) && defined(LC_NUMERIC) + // Make sure strtod() uses a decimal point, not a comma. + setlocale(LC_NUMERIC, "C"); + # endif + + # ifdef MSWIN + // Apparently MS-Windows printf() may cause a crash when we give it 8-bit + // text while it's expecting text in the current locale. This call avoids + // that. + setlocale(LC_CTYPE, "C"); + # endif + + # ifdef FEAT_GETTEXT + { + int mustfree = FALSE; + char_u *p; + + # ifdef DYNAMIC_GETTEXT + // Initialize the gettext library + dyn_libintl_init(); + # endif + // expand_env() doesn't work yet, because g_chartab[] is not + // initialized yet, call vim_getenv() directly + p = vim_getenv((char_u *)"VIMRUNTIME", &mustfree); + if (p != NULL && *p != NUL) + { + vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p); + bindtextdomain(VIMPACKAGE, (char *)NameBuff); + } + if (mustfree) + vim_free(p); + textdomain(VIMPACKAGE); + } + # endif + } + + /* + * ":language": Set the language (locale). + */ + void + ex_language(exarg_T *eap) + { + char *loc; + char_u *p; + char_u *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 || VIM_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) + { + # ifndef LC_MESSAGES + if (what == VIM_LC_MESSAGES) + p = get_mess_env(); + else + # endif + p = (char_u *)setlocale(what, NULL); + if (p == NULL || *p == NUL) + p = (char_u *)"Unknown"; + smsg(_("Current %slanguage: \"%s\""), whatstr, p); + } + else + { + # ifndef LC_MESSAGES + if (what == VIM_LC_MESSAGES) + loc = ""; + else + # endif + { + loc = setlocale(what, (char *)name); + # if defined(FEAT_FLOAT) && defined(LC_NUMERIC) + // Make sure strtod() uses a decimal point, not a comma. + setlocale(LC_NUMERIC, "C"); + # 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. + vim_setenv((char_u *)"LC_ALL", (char_u *)""); + + if (what != LC_TIME && what != LC_COLLATE) + { + // Tell gettext() what to translate to. It apparently doesn't + // use the currently effective locale. Also do this when + // FEAT_GETTEXT isn't defined, so that shell commands use this + // value. + if (what == LC_ALL) + { + vim_setenv((char_u *)"LANG", name); + + // Clear $LANGUAGE because GNU gettext uses it. + vim_setenv((char_u *)"LANGUAGE", (char_u *)""); + # ifdef MSWIN + // Apparently MS-Windows printf() may cause a crash when + // we give it 8-bit text while it's expecting text in the + // current locale. This call avoids that. + setlocale(LC_CTYPE, "C"); + # endif + } + if (what != LC_CTYPE) + { + char_u *mname; + # ifdef MSWIN + mname = gettext_lang(name); + # else + mname = name; + # endif + vim_setenv((char_u *)"LC_MESSAGES", mname); + # ifdef FEAT_MULTI_LANG + set_helplang_default(mname); + # endif + } + } + + # ifdef FEAT_EVAL + // Set v:lang, v:lc_time, v:collate and v:ctype to the final result. + set_lang_var(); + # endif + # ifdef FEAT_TITLE + maketitle(); + # endif + } + } + } + + static char_u **locales = NULL; // Array of all available locales + + static int did_init_locales = FALSE; + + /* + * Return an array of strings for all available locales + NULL for the + * last element. Return NULL in case of error. + */ + static char_u ** + find_locales(void) + { + garray_T locales_ga; + char_u *loc; + char_u *locale_list; + # ifdef MSWIN + size_t len = 0; + # endif + + // Find all available locales by running command "locale -a". If this + // doesn't work we won't have completion. + # ifndef MSWIN + locale_list = get_cmd_output((char_u *)"locale -a", + NULL, SHELL_SILENT, NULL); + # else + // Find all available locales by examining the directories in + // $VIMRUNTIME/lang/ + { + int options = WILD_SILENT|WILD_USE_NL|WILD_KEEP_ALL; + expand_T xpc; + char_u *p; + + ExpandInit(&xpc); + xpc.xp_context = EXPAND_DIRECTORIES; + locale_list = ExpandOne(&xpc, (char_u *)"$VIMRUNTIME/lang/*", + NULL, options, WILD_ALL); + ExpandCleanup(&xpc); + if (locale_list == NULL) + // Add a dummy input, that will be skipped lated but we need to + // have something in locale_list so that the C locale is added at + // the end. + locale_list = vim_strsave((char_u *)".\n"); + p = locale_list; + // find the last directory delimiter + while (p != NULL && *p != NUL) + { + if (*p == '\n') + break; + if (*p == '\\') + len = p - locale_list; + p++; + } + } + # endif + if (locale_list == NULL) + return NULL; + ga_init2(&locales_ga, sizeof(char_u *), 20); + + // Transform locale_list string where each locale is separated by "\n" + // into an array of locale strings. + loc = (char_u *)strtok((char *)locale_list, "\n"); + + while (loc != NULL) + { + int ignore = FALSE; + + # ifdef MSWIN + if (len > 0) + loc += len + 1; + // skip locales with a dot (which indicates the charset) + if (vim_strchr(loc, '.') != NULL) + ignore = TRUE; + # endif + if (!ignore) + { + if (ga_grow(&locales_ga, 1) == FAIL) + break; + + loc = vim_strsave(loc); + if (loc == NULL) + break; + + ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc; + } + loc = (char_u *)strtok(NULL, "\n"); + } + + # ifdef MSWIN + // Add the C locale + if (ga_grow(&locales_ga, 1) == OK) + ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = + vim_strsave((char_u *)"C"); + # endif + + vim_free(locale_list); + if (ga_grow(&locales_ga, 1) == FAIL) + { + ga_clear(&locales_ga); + return NULL; + } + ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL; + return (char_u **)locales_ga.ga_data; + } + + /* + * Lazy initialization of all available locales. + */ + static void + init_locales(void) + { + if (!did_init_locales) + { + did_init_locales = TRUE; + locales = find_locales(); + } + } + + # if defined(EXITFREE) || defined(PROTO) + void + free_locales(void) + { + int i; + if (locales != NULL) + { + for (i = 0; locales[i] != NULL; i++) + vim_free(locales[i]); + VIM_CLEAR(locales); + } + } + # endif + + /* + * Function given to ExpandGeneric() to obtain the possible arguments of the + * ":language" command. + */ + char_u * + get_lang_arg(expand_T *xp UNUSED, int idx) + { + if (idx == 0) + return (char_u *)"messages"; + if (idx == 1) + return (char_u *)"ctype"; + if (idx == 2) + return (char_u *)"time"; + if (idx == 3) + return (char_u *)"collate"; + + init_locales(); + if (locales == NULL) + return NULL; + return locales[idx - 4]; + } + + /* + * Function given to ExpandGeneric() to obtain the available locales. + */ + char_u * + get_locales(expand_T *xp UNUSED, int idx) + { + init_locales(); + if (locales == NULL) + return NULL; + return locales[idx]; + } + + #endif *** ../vim-8.2.1268/src/main.c 2020-06-13 15:47:21.070282268 +0200 --- src/main.c 2020-07-22 19:03:10.315786917 +0200 *************** *** 34,42 **** static int file_owned(char *fname); #endif static void mainerr(int, char_u *); - # if defined(HAVE_LOCALE_H) || defined(X_LOCALE) - static void init_locale(void); - # endif static void early_arg_scan(mparm_T *parmp); #ifndef NO_VIM_MAIN static void usage(void); --- 34,39 ---- *************** *** 1716,1771 **** mch_exit(exitval); } - #if defined(HAVE_LOCALE_H) || defined(X_LOCALE) - /* - * Setup to use the current locale (for ctype() and many other things). - */ - static void - init_locale(void) - { - setlocale(LC_ALL, ""); - - # ifdef FEAT_GUI_GTK - // Tell Gtk not to change our locale settings. - gtk_disable_setlocale(); - # endif - # if defined(FEAT_FLOAT) && defined(LC_NUMERIC) - // Make sure strtod() uses a decimal point, not a comma. - setlocale(LC_NUMERIC, "C"); - # endif - - # ifdef MSWIN - // Apparently MS-Windows printf() may cause a crash when we give it 8-bit - // text while it's expecting text in the current locale. This call avoids - // that. - setlocale(LC_CTYPE, "C"); - # endif - - # ifdef FEAT_GETTEXT - { - int mustfree = FALSE; - char_u *p; - - # ifdef DYNAMIC_GETTEXT - // Initialize the gettext library - dyn_libintl_init(); - # endif - // expand_env() doesn't work yet, because g_chartab[] is not - // initialized yet, call vim_getenv() directly - p = vim_getenv((char_u *)"VIMRUNTIME", &mustfree); - if (p != NULL && *p != NUL) - { - vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p); - bindtextdomain(VIMPACKAGE, (char *)NameBuff); - } - if (mustfree) - vim_free(p); - textdomain(VIMPACKAGE); - } - # endif - } - #endif - /* * Get the name of the display, before gui_prepare() removes it from * argv[]. Used for the xterm-clipboard display. --- 1713,1718 ---- *** ../vim-8.2.1268/src/proto.h 2020-07-21 21:07:00.720496743 +0200 --- src/proto.h 2020-07-22 19:03:10.315786917 +0200 *************** *** 101,106 **** --- 101,107 ---- # include "insexpand.pro" # include "json.pro" # include "list.pro" + # include "locale.pro" # include "blob.pro" # include "main.pro" # include "map.pro" *** ../vim-8.2.1268/src/proto/ex_cmds2.pro 2020-02-14 13:21:55.646197062 +0100 --- src/proto/ex_cmds2.pro 2020-07-22 19:03:10.315786917 +0200 *************** *** 15,24 **** void ex_pyx(exarg_T *eap); void ex_pyxdo(exarg_T *eap); void ex_checktime(exarg_T *eap); - char_u *get_mess_lang(void); - void set_lang_var(void); - void ex_language(exarg_T *eap); - void free_locales(void); - char_u *get_lang_arg(expand_T *xp, int idx); - char_u *get_locales(expand_T *xp, int idx); /* vim: set ft=c : */ --- 15,18 ---- *** ../vim-8.2.1268/src/proto/locale.pro 2020-07-22 19:10:21.085268870 +0200 --- src/proto/locale.pro 2020-07-22 19:03:10.315786917 +0200 *************** *** 0 **** --- 1,9 ---- + /* locale.c */ + char_u *get_mess_lang(void); + void set_lang_var(void); + void init_locale(void); + void ex_language(exarg_T *eap); + void free_locales(void); + char_u *get_lang_arg(expand_T *xp, int idx); + char_u *get_locales(expand_T *xp, int idx); + /* vim: set ft=c : */ *** ../vim-8.2.1268/src/version.c 2020-07-22 18:17:04.235863872 +0200 --- src/version.c 2020-07-22 19:06:29.514532644 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1269, /**/ -- hundred-and-one symptoms of being an internet addict: 57. You begin to wonder how on earth your service provider is allowed to call 200 hours per month "unlimited." /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///