Reported at <https://savannah.gnu.org/bugs/?57714>.
* gettext-runtime/intl/libgnuintl.in.h (libintl_wbindtextdomain): New
declaration.
(wbindtextdomain): New redirect.
* gettext-runtime/intl/bindtextdom.c (set_binding_values): Accept a wdirnamep
argument. Set not only binding->dirname but also binding->wdirname.
(BINDTEXTDOMAIN, BIND_TEXTDOMAIN_CODESET): Pass NULL as wdirnamep.
(libintl_wbindtextdomain): New function.
* gettext-runtime/intl/loadinfo.h (struct loaded_l10nfile): Add a wfilename
field.
(_nl_make_l10nflist): On native Windows, accept wdirlist, wdirlist_len
parameters.
* gettext-runtime/intl/loadmsgcat.c (_nl_load_domain): On native Windows, use
_wopen() instead of open() to open a file with a name given as wchar_t[].
* gettext-runtime/intl/l10nflist.c: Include <wchar.h>.
(_nl_make_l10nflist): On native Windows, accept wdirlist, wdirlist_len
parameters. Construct abs_wfilename and use in the search for existing
'struct loaded_l10nfile' and when allocating a fresh 'struct loaded_l10nfile'.
* gettext-runtime/intl/gettextP.h (struct binding): Add a wdirname field.
(_nl_find_domain): On native Windows, accept a wdirname parameter.
* gettext-runtime/intl/finddomain.c (_nl_find_domain): On native Windows, accept
a wdirname parameter. Pass it to _nl_make_l10nflist.
* gettext-runtime/intl/dcigettext.c (DCIGETTEXT): Consider not only
binding->dirname but also binding->wdirname. On native Windows, use _wgetcwd
instead of getcwd. Pass also wdirname to _nl_find_domain.
* gettext-tools/tests/intl-6: New file, based on gettext-tools/tests/intl-1.
* gettext-tools/tests/intl-6-prg.c: New file, based on
gettext-tools/tests/intl-1-prg.c.
* gettext-tools/tests/Makefile.am (TESTS): Add intl-6.
(check_PROGRAMS): Add intl-6-prg.
(intl_6_prg_SOURCES, intl_6_prg_LDADD): New variables.
* gettext-tools/doc/gettext.texi (Ambiguities, src/Makefile,
Language Implementors, C): Document wbindtextdomain.
* gettext-runtime/NEWS: Mention the change.
* NEWS: Likewise.
- JavaScript:
xgettext parses JSX expressions more reliably.
+* Runtime behaviour:
+ - On native Windows platforms, the directory that contains the message
+ catalogs may now contain arbitrary Unicode characters. To make use of
+ this feature, use the new function 'wbindtextdomain' instead of
+ 'bindtextdomain'. It allows to pass a directory name in wchar_t[] encoding.
+ Note: 'wbindtextdomain' exists only on native Windows platforms.
+
* Libtextstyle:
- Added support for emitting hyperlinks.
- New API for doing formatted output.
-Version 0.21 - December 2019
+Version 0.21 - April 2020
+
+* On native Windows platforms, the directory that contains the message
+ catalogs may now contain arbitrary Unicode characters. To make use of
+ this feature, use the new function 'wbindtextdomain' instead of
+ 'bindtextdomain'. It allows to pass a directory name in wchar_t[] encoding.
+ Note: 'wbindtextdomain' exists only on native Windows platforms.
+
+Version 0.20.2 - April 2020
* The interpretation of the language preferences on macOS has been improved,
especially in the case where a system locale does not exist for the
/* Implementation of the bindtextdomain(3) function
- Copyright (C) 1995-2016 Free Software Foundation, Inc.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
# define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset
#endif
-/* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
- to be used for the DOMAINNAME message catalog.
- If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
- modified, only the current value is returned.
- If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
- modified nor returned. */
+/* Specifies the directory name *DIRNAMEP, the directory name *WDIRNAMEP
+ (only on native Windows), and the output codeset *CODESETP to be used
+ for the DOMAINNAME message catalog.
+ If *DIRNAMEP or *WDIRNAMEP or *CODESETP is NULL, the corresponding attribute
+ is not modified, only the current value is returned.
+ If DIRNAMEP or WDIRNAMEP or CODESETP is NULL, the corresponding attribute is
+ neither modified nor returned, except that setting WDIRNAME erases DIRNAME
+ and vice versa. */
static void
set_binding_values (const char *domainname,
- const char **dirnamep, const char **codesetp)
+ const char **dirnamep, const wchar_t **wdirnamep,
+ const char **codesetp)
{
struct binding *binding;
int modified;
{
if (dirnamep)
*dirnamep = NULL;
+#if defined _WIN32 && !defined __CYGWIN__
+ if (wdirnamep)
+ *wdirnamep = NULL;
+#endif
if (codesetp)
*codesetp = NULL;
return;
one are equal we simply do nothing. Otherwise replace the
old binding. */
char *result = binding->dirname;
- if (strcmp (dirname, result) != 0)
+ if (result == NULL || strcmp (dirname, result) != 0)
{
if (strcmp (dirname, _nl_default_dirname) == 0)
result = (char *) _nl_default_dirname;
{
if (binding->dirname != _nl_default_dirname)
free (binding->dirname);
-
binding->dirname = result;
+
+#if defined _WIN32 && !defined __CYGWIN__
+ free (binding->wdirname);
+ binding->wdirname = NULL;
+#endif
+
modified = 1;
}
}
}
}
+#if defined _WIN32 && !defined __CYGWIN__
+ if (wdirnamep)
+ {
+ const wchar_t *wdirname = *wdirnamep;
+
+ if (wdirname == NULL)
+ /* The current binding has be to returned. */
+ *wdirnamep = binding->wdirname;
+ else
+ {
+ /* The domain is already bound. If the new value and the old
+ one are equal we simply do nothing. Otherwise replace the
+ old binding. */
+ wchar_t *result = binding->wdirname;
+ if (result == NULL || wcscmp (wdirname, result) != 0)
+ {
+ result = _wcsdup (wdirname);
+
+ if (__builtin_expect (result != NULL, 1))
+ {
+ if (binding->dirname != _nl_default_dirname)
+ free (binding->dirname);
+ binding->dirname = NULL;
+
+ free (binding->wdirname);
+ binding->wdirname = result;
+
+ modified = 1;
+ }
+ }
+ *wdirnamep = result;
+ }
+ }
+#endif
+
if (codesetp)
{
const char *codeset = *codesetp;
}
}
else if ((dirnamep == NULL || *dirnamep == NULL)
+#if defined _WIN32 && !defined __CYGWIN__
+ && (wdirnamep == NULL || *wdirnamep == NULL)
+#endif
&& (codesetp == NULL || *codesetp == NULL))
{
/* Simply return the default values. */
if (dirnamep)
*dirnamep = _nl_default_dirname;
+#if defined _WIN32 && !defined __CYGWIN__
+ if (wdirnamep)
+ *wdirnamep = NULL;
+#endif
if (codesetp)
*codesetp = NULL;
}
const char *dirname = *dirnamep;
if (dirname == NULL)
- /* The default value. */
- dirname = _nl_default_dirname;
+ {
+#if defined _WIN32 && !defined __CYGWIN__
+ if (wdirnamep && *wdirnamep != NULL)
+ dirname = NULL;
+ else
+#endif
+ /* The default value. */
+ dirname = _nl_default_dirname;
+ }
else
{
if (strcmp (dirname, _nl_default_dirname) == 0)
new_binding->dirname = (char *) dirname;
}
else
- /* The default value. */
- new_binding->dirname = (char *) _nl_default_dirname;
+ {
+#if defined _WIN32 && !defined __CYGWIN__
+ if (wdirnamep && *wdirnamep != NULL)
+ new_binding->dirname = NULL;
+ else
+#endif
+ /* The default value. */
+ new_binding->dirname = (char *) _nl_default_dirname;
+ }
+
+#if defined _WIN32 && !defined __CYGWIN__
+ if (wdirnamep)
+ {
+ const wchar_t *wdirname = *wdirnamep;
+
+ if (wdirname != NULL)
+ {
+ wchar_t *result = _wcsdup (wdirname);
+ if (__builtin_expect (result == NULL, 0))
+ goto failed_wdirname;
+ wdirname = result;
+ }
+ *wdirnamep = wdirname;
+ new_binding->wdirname = (wchar_t *) wdirname;
+ }
+ else
+ new_binding->wdirname = NULL;
+#endif
if (codesetp)
{
if (0)
{
failed_codeset:
+#if defined _WIN32 && !defined __CYGWIN__
+ free (new_binding->wdirname);
+ failed_wdirname:
+#endif
if (new_binding->dirname != _nl_default_dirname)
free (new_binding->dirname);
failed_dirname:
failed:
if (dirnamep)
*dirnamep = NULL;
+#if defined _WIN32 && !defined __CYGWIN__
+ if (wdirnamep)
+ *wdirnamep = NULL;
+#endif
if (codesetp)
*codesetp = NULL;
}
}
}
#endif
- set_binding_values (domainname, &dirname, NULL);
+ set_binding_values (domainname, &dirname, NULL, NULL);
#ifdef __EMX__
dirname = saved_dirname;
#endif
return (char *) dirname;
}
+#if defined _WIN32 && !defined __CYGWIN__
+/* Specify that the DOMAINNAME message catalog will be found
+ in WDIRNAME rather than in the system locale data base. */
+wchar_t *
+libintl_wbindtextdomain (const char *domainname, const wchar_t *wdirname)
+{
+ set_binding_values (domainname, NULL, &wdirname, NULL);
+ return (wchar_t *) wdirname;
+}
+#endif
+
/* Specify the character encoding in which the messages from the
DOMAINNAME message catalog will be returned. */
char *
BIND_TEXTDOMAIN_CODESET (const char *domainname, const char *codeset)
{
- set_binding_values (domainname, NULL, &codeset);
+ set_binding_values (domainname, NULL, NULL, &codeset);
return (char *) codeset;
}
const char *categoryname;
const char *categoryvalue;
const char *dirname;
+#if defined _WIN32 && !defined __CYGWIN__
+ const wchar_t *wdirname;
+#endif
char *xdomainname;
char *single_locale;
char *retval;
and _nl_load_domain and _nl_find_domain just pass it through. */
binding = NULL;
dirname = bindtextdomain (domainname, NULL);
+# if defined _WIN32 && !defined __CYGWIN__
+ wdirname = wbindtextdomain (domainname, NULL);
+# endif
#else
for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
{
}
if (binding == NULL)
- dirname = _nl_default_dirname;
+ {
+ dirname = _nl_default_dirname;
+# if defined _WIN32 && !defined __CYGWIN__
+ wdirname = NULL;
+# endif
+ }
else
{
dirname = binding->dirname;
+# if defined _WIN32 && !defined __CYGWIN__
+ wdirname = binding->wdirname;
+# endif
#endif
+#if defined _WIN32 && !defined __CYGWIN__
+ if (wdirname != NULL
+ ? IS_RELATIVE_FILE_NAME (wdirname)
+ : IS_RELATIVE_FILE_NAME (dirname))
+ {
+ /* We have a relative path. Make it absolute now. */
+ size_t wdirname_len;
+ size_t path_max;
+ wchar_t *resolved_wdirname;
+ wchar_t *ret;
+ wchar_t *p;
+
+ if (wdirname != NULL)
+ wdirname_len = wcslen (wdirname);
+ else
+ {
+ wdirname_len = mbstowcs (NULL, dirname, 0);
+
+ if (wdirname_len == (size_t)(-1))
+ /* dirname contains invalid multibyte characters. Don't signal
+ an error but simply return the default string. */
+ goto return_untranslated;
+ }
+ wdirname_len++;
+
+ path_max = (unsigned int) PATH_MAX;
+ path_max += 2; /* The getcwd docs say to do this. */
+
+ for (;;)
+ {
+ resolved_wdirname =
+ (wchar_t *)
+ alloca ((path_max + wdirname_len) * sizeof (wchar_t));
+ ADD_BLOCK (block_list, resolved_wdirname);
+
+ __set_errno (0);
+ ret = _wgetcwd (resolved_wdirname, path_max);
+ if (ret != NULL || errno != ERANGE)
+ break;
+
+ path_max += path_max / 2;
+ path_max += PATH_INCR;
+ }
+
+ if (ret == NULL)
+ /* We cannot get the current working directory. Don't signal an
+ error but simply return the default string. */
+ goto return_untranslated;
+
+ p = wcschr (resolved_wdirname, L'\0');
+ *p++ = L'/';
+ if (wdirname != NULL)
+ wcscpy (p, wdirname);
+ else
+ mbstowcs (p, dirname, wdirname_len);
+
+ wdirname = resolved_wdirname;
+ dirname = NULL;
+ }
+#else
if (IS_RELATIVE_FILE_NAME (dirname))
{
/* We have a relative path. Make it absolute now. */
stpcpy (stpcpy (strchr (resolved_dirname, '\0'), "/"), dirname);
dirname = resolved_dirname;
}
+#endif
#ifndef IN_LIBGLOCALE
}
#endif
/* Find structure describing the message catalog matching the
DOMAINNAME and CATEGORY. */
- domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
+ domain = _nl_find_domain (dirname,
+#if defined _WIN32 && !defined __CYGWIN__
+ wdirname,
+#endif
+ single_locale, xdomainname, binding);
if (domain != NULL)
{
/* Return a data structure describing the message catalog described by
- the DIRNAME, LOCALE, and DOMAINNAME parameters with respect to the
- currently established bindings. */
+ the DIRNAME or WDIRNAME, LOCALE, and DOMAINNAME parameters with respect
+ to the currently established bindings. */
struct loaded_l10nfile *
internal_function
-_nl_find_domain (const char *dirname, char *locale,
+_nl_find_domain (const char *dirname,
+#if defined _WIN32 && !defined __CYGWIN__
+ const wchar_t *wdirname,
+#endif
+ char *locale,
const char *domainname, struct binding *domainbinding)
{
struct loaded_l10nfile *retval;
/* If we have already tested for this locale entry there has to
be one data set in the list of loaded domains. */
- retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
- strlen (dirname) + 1, 0, locale, NULL, NULL,
- NULL, NULL, domainname, 0);
+ retval = _nl_make_l10nflist (&_nl_loaded_domains,
+ dirname,
+ dirname != NULL ? strlen (dirname) + 1 : 0,
+#if defined _WIN32 && !defined __CYGWIN__
+ wdirname,
+ wdirname != NULL ? wcslen (wdirname) + 1 : 0,
+#endif
+ 0, locale, NULL, NULL, NULL, NULL,
+ domainname, 0);
gl_rwlock_unlock (lock);
/* Create all possible locale entries which might be interested in
generalization. */
- retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
- strlen (dirname) + 1, mask, language, territory,
+ retval = _nl_make_l10nflist (&_nl_loaded_domains,
+ dirname,
+ dirname != NULL ? strlen (dirname) + 1 : 0,
+#if defined _WIN32 && !defined __CYGWIN__
+ wdirname,
+ wdirname != NULL ? wcslen (wdirname) + 1 : 0,
+#endif
+ mask, language, territory,
codeset, normalized_codeset, modifier,
domainname, 1);
/* Header describing internals of libintl library.
- Copyright (C) 1995-2016 Free Software Foundation, Inc.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
Written by Ulrich Drepper <drepper@cygnus.com>, 1995.
This program is free software: you can redistribute it and/or modify
{
struct binding *next;
char *dirname;
+#if defined _WIN32 && !defined __CYGWIN__
+ wchar_t *wdirname;
+#endif
char *codeset;
char domainname[ZERO];
};
const char *categoryname); */
#endif
-struct loaded_l10nfile *_nl_find_domain (const char *__dirname, char *__locale,
+struct loaded_l10nfile *_nl_find_domain (const char *__dirname,
+#if defined _WIN32 && !defined __CYGWIN__
+ const wchar_t *__wdirname,
+#endif
+ char *__locale,
const char *__domainname,
struct binding *__domainbinding)
internal_function;
-/* Copyright (C) 1995-2016, 2018, 2020 Free Software Foundation, Inc.
+/* Copyright (C) 1995-2020 Free Software Foundation, Inc.
Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
This program is free software: you can redistribute it and/or modify
#include <ctype.h>
#include <sys/types.h>
#include <stdlib.h>
+#if defined _WIN32 && !defined __CYGWIN__
+# include <wchar.h>
+#endif
#include "loadinfo.h"
struct loaded_l10nfile *
_nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
const char *dirlist, size_t dirlist_len,
+#if defined _WIN32 && !defined __CYGWIN__
+ const wchar_t *wdirlist, size_t wdirlist_len,
+#endif
int mask, const char *language, const char *territory,
const char *codeset, const char *normalized_codeset,
const char *modifier,
const char *filename, int do_allocate)
{
char *abs_filename;
+#if defined _WIN32 && !defined __CYGWIN__
+ wchar_t *abs_wfilename;
+#endif
struct loaded_l10nfile **lastp;
struct loaded_l10nfile *retval;
- char *cp;
size_t dirlist_count;
size_t entries;
int cnt;
/* If LANGUAGE contains an absolute directory specification, we ignore
- DIRLIST. */
+ DIRLIST and WDIRLIST. */
if (!IS_RELATIVE_FILE_NAME (language))
- dirlist_len = 0;
+ {
+ dirlist_len = 0;
+#if defined _WIN32 && !defined __CYGWIN__
+ wdirlist_len = 0;
+#endif
+ }
/* Allocate room for the full file name. */
abs_filename = (char *) malloc (dirlist_len
return NULL;
/* Construct file name. */
- cp = abs_filename;
- if (dirlist_len > 0)
- {
- memcpy (cp, dirlist, dirlist_len);
+ {
+ char *cp;
+
+ cp = abs_filename;
+ if (dirlist_len > 0)
+ {
+ memcpy (cp, dirlist, dirlist_len);
#ifdef _LIBC
- __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
+ __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
#endif
- cp += dirlist_len;
- cp[-1] = '/';
- }
+ cp += dirlist_len;
+ cp[-1] = '/';
+ }
- cp = stpcpy (cp, language);
+ cp = stpcpy (cp, language);
- if ((mask & XPG_TERRITORY) != 0)
- {
- *cp++ = '_';
- cp = stpcpy (cp, territory);
- }
- if ((mask & XPG_CODESET) != 0)
- {
- *cp++ = '.';
- cp = stpcpy (cp, codeset);
- }
- if ((mask & XPG_NORM_CODESET) != 0)
- {
- *cp++ = '.';
- cp = stpcpy (cp, normalized_codeset);
- }
- if ((mask & XPG_MODIFIER) != 0)
+ if ((mask & XPG_TERRITORY) != 0)
+ {
+ *cp++ = '_';
+ cp = stpcpy (cp, territory);
+ }
+ if ((mask & XPG_CODESET) != 0)
+ {
+ *cp++ = '.';
+ cp = stpcpy (cp, codeset);
+ }
+ if ((mask & XPG_NORM_CODESET) != 0)
+ {
+ *cp++ = '.';
+ cp = stpcpy (cp, normalized_codeset);
+ }
+ if ((mask & XPG_MODIFIER) != 0)
+ {
+ *cp++ = '@';
+ cp = stpcpy (cp, modifier);
+ }
+
+ *cp++ = '/';
+ stpcpy (cp, filename);
+ }
+
+#if defined _WIN32 && !defined __CYGWIN__
+ /* Construct wide-char file name. */
+ if (wdirlist_len > 0)
{
- *cp++ = '@';
- cp = stpcpy (cp, modifier);
- }
+ /* Since dirlist_len == 0, just concatenate wdirlist and abs_filename. */
+ /* An upper bound for wcslen (mbstowcs (abs_filename)). */
+ size_t abs_filename_bound = mbstowcs (NULL, abs_filename, 0);
+ if (abs_filename_bound == (size_t)-1)
+ {
+ free (abs_filename);
+ return NULL;
+ }
+
+ /* Allocate and fill abs_wfilename. */
+ abs_wfilename =
+ (wchar_t *)
+ malloc ((wdirlist_len + abs_filename_bound + 1) * sizeof (wchar_t));
+ if (abs_wfilename == NULL)
+ {
+ free (abs_filename);
+ return NULL;
+ }
+ wmemcpy (abs_wfilename, wdirlist, wdirlist_len - 1);
+ abs_wfilename[wdirlist_len - 1] = L'/';
+ if (mbstowcs (abs_wfilename + wdirlist_len, abs_filename,
+ abs_filename_bound + 1)
+ > abs_filename_bound)
+ {
+ free (abs_filename);
+ free (abs_wfilename);
+ return NULL;
+ }
- *cp++ = '/';
- stpcpy (cp, filename);
+ free (abs_filename);
+ abs_filename = NULL;
+ }
+ else
+ abs_wfilename = NULL;
+#endif
/* Look in list of already loaded domains whether it is already
available. */
lastp = l10nfile_list;
for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
- if (retval->filename != NULL)
+ if (retval->filename != NULL
+#if defined _WIN32 && !defined __CYGWIN__
+ || retval->wfilename != NULL
+#endif
+ )
{
- int compare = strcmp (retval->filename, abs_filename);
+ int compare =
+#if defined _WIN32 && !defined __CYGWIN__
+ abs_wfilename != NULL
+ ? retval->wfilename != NULL && wcscmp (retval->wfilename, abs_wfilename)
+ : retval->filename != NULL && strcmp (retval->filename, abs_filename);
+#else
+ strcmp (retval->filename, abs_filename);
+#endif
if (compare == 0)
/* We found it! */
break;
if (retval != NULL || do_allocate == 0)
{
free (abs_filename);
+#if defined _WIN32 && !defined __CYGWIN__
+ free (abs_wfilename);
+#endif
return retval;
}
if (retval == NULL)
{
free (abs_filename);
+#if defined _WIN32 && !defined __CYGWIN__
+ free (abs_wfilename);
+#endif
return NULL;
}
retval->filename = abs_filename;
+#if defined _WIN32 && !defined __CYGWIN__
+ retval->wfilename = abs_wfilename;
+#endif
/* We set retval->data to NULL here; it is filled in later.
Setting retval->decided to 1 here means that retval does not
else
#endif
retval->successor[entries++]
- = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
+ = _nl_make_l10nflist (l10nfile_list,
+ dirlist, dirlist_len,
+#if defined _WIN32 && !defined __CYGWIN__
+ wdirlist, wdirlist_len,
+#endif
cnt, language, territory, codeset,
normalized_codeset, modifier, filename, 1);
}
_INTL_ASM (libintl_bindtextdomain);
#endif
+#if defined _WIN32 && !defined __CYGWIN__
+/* Specify that the DOMAINNAME message catalog will be found
+ in WDIRNAME rather than in the system locale data base. */
+#ifdef _INTL_REDIRECT_INLINE
+extern wchar_t *libintl_wbindtextdomain (const char *__domainname,
+ const wchar_t *__wdirname);
+static inline wchar_t *wbindtextdomain (const char *__domainname,
+ const wchar_t *__wdirname)
+{
+ return libintl_wbindtextdomain (__domainname, __wdirname);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+# define wbindtextdomain libintl_wbindtextdomain
+#endif
+extern wchar_t *wbindtextdomain (const char *__domainname,
+ const wchar_t *__wdirname)
+ _INTL_ASM (libintl_wbindtextdomain);
+#endif
+#endif
+
/* Specify the character encoding in which the messages from the
DOMAINNAME message catalog will be returned. */
#ifdef _INTL_REDIRECT_INLINE
struct loaded_l10nfile
{
const char *filename;
+#if defined _WIN32 && !defined __CYGWIN__
+ const wchar_t *wfilename;
+#endif
int decided;
const void *data;
/* Lookup a locale dependent file.
*L10NFILE_LIST denotes a pool of lookup results of locale dependent
files of the same kind, sorted in decreasing order of ->filename.
+
DIRLIST and DIRLIST_LEN are an argz list of directories in which to
- look, containing at least one directory (i.e. DIRLIST_LEN > 0).
+ look.
+ Likewise, on native Windows, WDIRLIST and WDIRLIST_LEN are wide-char
+ list of directories in which to look. Only one of DIRLIST, WDIRLIST
+ is non-NULL.
+ DIRLIST and WDIRLIST contain at least one directory, i.e.
+ DIRLIST_LEN + WDIRLIST_LEN > 0.
Outside glibc, only one directory is used, i.e.
- DIRLIST_LEN == strlen (DIRLIST) + 1.
+ DIRLIST_LEN == strlen (DIRLIST) + 1, WDIRLIST_LEN == 0, or
+ DIRLIST_LEN == 0, WDIRLIST_LEN == wcslen (WDIRLIST) + 1.
+
MASK, LANGUAGE, TERRITORY, CODESET, NORMALIZED_CODESET, MODIFIER
are the pieces of the locale name, as produced by _nl_explode_name().
FILENAME is the filename suffix.
+
The return value is the lookup result, either found in *L10NFILE_LIST,
or - if DO_ALLOCATE is nonzero - freshly allocated, or possibly NULL.
If the return value is non-NULL, it is added to *L10NFILE_LIST, and
results from which this lookup result inherits. */
extern struct loaded_l10nfile *
_nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
- const char *dirlist, size_t dirlist_len, int mask,
+ const char *dirlist, size_t dirlist_len,
+#if defined _WIN32 && !defined __CYGWIN__
+ const wchar_t *wdirlist, size_t wdirlist_len,
+#endif
+ int mask,
const char *language, const char *territory,
const char *codeset, const char *normalized_codeset,
const char *modifier,
/* Load needed message catalogs.
- Copyright (C) 1995-2019 Free Software Foundation, Inc.
+ Copyright (C) 1995-2020 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
might be NULL. This can happen when according to the given
specification the locale file name is different for XPG and CEN
syntax. */
- if (domain_file->filename == NULL)
- goto out;
-
- /* Try to open the addressed file. */
- fd = open (domain_file->filename, O_RDONLY | O_BINARY);
- if (fd == -1)
+ if (domain_file->filename != NULL)
+ {
+ /* Try to open the addressed file. */
+ fd = open (domain_file->filename, O_RDONLY | O_BINARY);
+ if (fd == -1)
+ goto out;
+ }
+#if defined _WIN32 && !defined __CYGWIN__
+ else if (domain_file->wfilename != NULL)
+ {
+ /* Try to open the addressed file. */
+ fd = _wopen (domain_file->wfilename, O_RDONLY | O_BINARY);
+ if (fd == -1)
+ goto out;
+ }
+#endif
+ else
goto out;
/* We must know about the size of the file. */
paths should always be avoided to avoid dependencies and
unreliabilities.
+@example
+wchar_t *wbindtextdomain (const char *domain_name,
+ const wchar_t *dir_name);
+@end example
+
+This function is provided only on native Windows platforms. It is like
+@code{bindtextdomain}, except that the @var{dir_name} parameter is a
+wide string (in UTF-16 encoding, as usual on Windows).
+
@node Locating Catalogs
@subsection Locating Message Catalog Files
@cindex message catalog files location
textdomain (@var{PACKAGE});
@end example
+On native Windows platforms, the @code{main} function may call
+@code{wbindtextdomain} instead of @code{bindtextdomain}.
+
To make LOCALEDIR known to the program, add the following lines to
@file{Makefile.in}:
language, or by introducing a magic variable called @code{TEXTDOMAIN}.
Similarly, you should allow the programmer to designate where to search
for message catalogs, by providing access to the @code{bindtextdomain}
+function or --- on native Windows platforms --- to the @code{wbindtextdomain}
function.
@item
@code{textdomain} function
@item bindtextdomain
-@code{bindtextdomain} function
+@code{bindtextdomain} and @code{wbindtextdomain} functions
@item setlocale
Programmer must call @code{setlocale (LC_ALL, "")}
## Makefile for the gettext-tools/tests subdirectory of GNU gettext
-## Copyright (C) 1995-1997, 2001-2010, 2012-2016, 2018-2019 Free Software Foundation, Inc.
+## Copyright (C) 1995-1997, 2001-2010, 2012-2016, 2018-2020 Free Software Foundation, Inc.
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
MOSTLYCLEANFILES = core *.stackdump
TESTS = gettext-1 gettext-2 \
- intl-1 intl-2 intl-3 intl-4 intl-5 \
+ intl-1 intl-2 intl-3 intl-4 intl-5 intl-6 \
intl-setlocale-1 intl-setlocale-2 \
intl-thread-1 intl-thread-2 intl-thread-3 \
intl-version \
LDADD = $(LDADD_@USE_INCLUDED_LIBINTL@) @INTL_MACOSX_LIBS@
LDADD_yes = ../intl/libintl.la @LTLIBTHREAD@
LDADD_no = ../intl/libgnuintl.la @LTLIBTHREAD@ @LTLIBINTL@
-check_PROGRAMS = tstgettext tstngettext testlocale intl-1-prg intl-3-prg intl-4-prg intl-5-prg intl-setlocale-1-prg intl-setlocale-2-prg intl-thread-1-prg intl-thread-2-prg intl-thread-3-prg intl-version-prg cake fc3 fc4 fc5 gettextpo-1-prg sentence-1-prg
+check_PROGRAMS = tstgettext tstngettext testlocale intl-1-prg intl-3-prg intl-4-prg intl-5-prg intl-6-prg intl-setlocale-1-prg intl-setlocale-2-prg intl-thread-1-prg intl-thread-2-prg intl-thread-3-prg intl-version-prg cake fc3 fc4 fc5 gettextpo-1-prg sentence-1-prg
tstgettext_SOURCES = \
tstgettext.c ../../gettext-runtime/src/escapes.h \
setlocale.c
intl_4_prg_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD)
intl_5_prg_SOURCES = intl-5-prg.c
intl_5_prg_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD)
+intl_6_prg_SOURCES = intl-6-prg.c
+intl_6_prg_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD)
intl_setlocale_1_prg_SOURCES = intl-setlocale-1-prg.c
intl_setlocale_1_prg_LDADD = ../gnulib-lib/libgettextlib.la $(LDADD)
intl_setlocale_2_prg_SOURCES = intl-setlocale-2-prg.c
--- /dev/null
+#! /bin/sh
+. "${srcdir=.}/init.sh"; path_prepend_ . ../src
+
+# Test that gettext() does basic translation lookup, even when the directory
+# with the message catalogs contains arbitrary Unicode characters.
+
+test -d in-6 || mkdir in-6
+test -d in-6/fr || mkdir in-6/fr
+test -d in-6/fr/LC_MESSAGES || mkdir in-6/fr/LC_MESSAGES
+
+: ${MSGFMT=msgfmt}
+${MSGFMT} -o in-6/fr/LC_MESSAGES/tstprog.mo "$wabs_srcdir"/intl-1.po
+
+: ${DIFF=diff}
+cat <<EOF > in-6.ok
+fromage
+EOF
+
+: ${LOCALE_FR=fr_FR}
+: ${LOCALE_FR_UTF8=fr_FR.UTF-8}
+if test $LOCALE_FR != none; then
+ prepare_locale_ in-6/fr in-6/$LOCALE_FR
+ ../intl-6-prg in-6 $LOCALE_FR > in-6.tmp || Exit 1
+ LC_ALL=C tr -d '\r' < in-6.tmp > in-6.out || Exit 1
+ ${DIFF} in-6.ok in-6.out || Exit 1
+fi
+if test $LOCALE_FR_UTF8 != none; then
+ prepare_locale_ in-6/fr in-6/$LOCALE_FR_UTF8
+ ../intl-6-prg in-6 $LOCALE_FR_UTF8 > in-6.tmp || Exit 1
+ LC_ALL=C tr -d '\r' < in-6.tmp > in-6.out || Exit 1
+ ${DIFF} in-6.ok in-6.out || Exit 1
+fi
+if test $LOCALE_FR = none && test $LOCALE_FR_UTF8 = none; then
+ if test -f /usr/bin/localedef; then
+ echo "Skipping test: no french locale is installed"
+ else
+ echo "Skipping test: no french locale is supported"
+ fi
+ Exit 77
+fi
+
+Exit 0
--- /dev/null
+/* Test program, used by the intl-6 test.
+ Copyright (C) 2000, 2005, 2007, 2013, 2018, 2020 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <locale.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#if defined _WIN32 && !defined __CYGWIN__
+# include <wchar.h>
+#endif
+
+#include "xsetenv.h"
+/* Make sure we use the included libintl, not the system's one. */
+#undef _LIBINTL_H
+#include "libgnuintl.h"
+
+const char unicodedir[] = "русский…日本語…हिंदी…😷";
+#if defined _WIN32 && !defined __CYGWIN__
+const wchar_t wunicodedir[] = /* the same string in UTF-16 encoding */
+ { 0x0440, 0x0443, 0x0441, 0x0441, 0x043A, 0x0438, 0x0439, 0x2026,
+ 0x65E5, 0x672C, 0x8A9E, 0x2026,
+ 0x0939, 0x093F, 0x0902, 0x0926, 0x0940, 0x2026,
+ 0xD83D, 0xDE37, 0
+ };
+#endif
+
+int
+main (int argc, char *argv[])
+{
+ const char *dir = argv[1];
+ const char *locale = argv[2];
+ wchar_t *wdir;
+ int ret;
+
+ wdir = (wchar_t *) malloc ((strlen (dir) + 1) * sizeof (wchar_t));
+ mbstowcs (wdir, dir, strlen (dir) + 1);
+
+ /* Rename the directory. */
+#if defined _WIN32 && !defined __CYGWIN__
+ ret = _wrename (wdir, wunicodedir);
+#else
+ ret = rename (dir, unicodedir);
+#endif
+ if (ret != 0)
+ {
+ fprintf (stderr, "Initial rename failed.\n");
+ exit (1);
+ }
+
+ /* Clean up environment. */
+ unsetenv ("LANGUAGE");
+ unsetenv ("OUTPUT_CHARSET");
+
+ textdomain ("tstprog");
+
+ xsetenv ("LC_ALL", locale, 1);
+ if (setlocale (LC_ALL, "") == NULL)
+ setlocale (LC_ALL, "C");
+
+#if defined _WIN32 && !defined __CYGWIN__
+ wbindtextdomain ("tstprog", wunicodedir);
+#else
+ bindtextdomain ("tstprog", unicodedir);
+#endif
+
+ printf ("%s\n", gettext ("cheese"));
+
+ /* Rename the directory back. */
+#if defined _WIN32 && !defined __CYGWIN__
+ ret = _wrename (wunicodedir, wdir);
+#else
+ ret = rename (unicodedir, dir);
+#endif
+ if (ret != 0)
+ {
+ fprintf (stderr, "Final rename failed.\n");
+ exit (1);
+ }
+
+ return 0;
+}