/* Implementation of the bindtextdomain(3) function 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 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include "gettextP.h" #ifdef _LIBC # include #else # include "libgnuintl.h" #endif /* Handle multi-threaded applications. */ #ifdef _LIBC # include # define gl_rwlock_define __libc_rwlock_define # define gl_rwlock_wrlock __libc_rwlock_wrlock # define gl_rwlock_unlock __libc_rwlock_unlock #else # include "lock.h" #endif /* Some compilers, like SunOS4 cc, don't have offsetof in . */ #ifndef offsetof # define offsetof(type,ident) ((size_t)&(((type*)0)->ident)) #endif /* @@ end of prolog @@ */ /* Lock variable to protect the global data in the gettext implementation. */ gl_rwlock_define (extern, _nl_state_lock attribute_hidden) /* Names for the libintl functions are a problem. They must not clash with existing names and they should follow ANSI C. But this source code is also used in GNU C Library where the names have a __ prefix. So we have to make a difference here. */ #ifdef _LIBC # define BINDTEXTDOMAIN __bindtextdomain # define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset # ifndef strdup # define strdup(str) __strdup (str) # endif #else # define BINDTEXTDOMAIN libintl_bindtextdomain # define BIND_TEXTDOMAIN_CODESET libintl_bind_textdomain_codeset #endif /* 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 wchar_t **wdirnamep, const char **codesetp) { struct binding *binding; int modified; /* Some sanity checks. */ if (domainname == NULL || domainname[0] == '\0') { if (dirnamep) *dirnamep = NULL; #if defined _WIN32 && !defined __CYGWIN__ if (wdirnamep) *wdirnamep = NULL; #endif if (codesetp) *codesetp = NULL; return; } gl_rwlock_wrlock (_nl_state_lock); modified = 0; for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next) { int compare = strcmp (domainname, binding->domainname); if (compare == 0) /* We found it! */ break; if (compare < 0) { /* It is not in the list. */ binding = NULL; break; } } if (binding != NULL) { if (dirnamep) { const char *dirname = *dirnamep; if (dirname == NULL) /* The current binding has be to returned. */ *dirnamep = binding->dirname; 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. */ char *result = binding->dirname; if (result == NULL || strcmp (dirname, result) != 0) { if (strcmp (dirname, _nl_default_dirname) == 0) result = (char *) _nl_default_dirname; else { #if defined _LIBC || defined HAVE_STRDUP result = strdup (dirname); #else size_t len = strlen (dirname) + 1; result = (char *) malloc (len); if (__builtin_expect (result != NULL, 1)) memcpy (result, dirname, len); #endif } if (__builtin_expect (result != NULL, 1)) { 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; } } *dirnamep = result; } } #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; if (codeset == NULL) /* The current binding has be to returned. */ *codesetp = binding->codeset; 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. */ char *result = binding->codeset; if (result == NULL || strcmp (codeset, result) != 0) { #if defined _LIBC || defined HAVE_STRDUP result = strdup (codeset); #else size_t len = strlen (codeset) + 1; result = (char *) malloc (len); if (__builtin_expect (result != NULL, 1)) memcpy (result, codeset, len); #endif if (__builtin_expect (result != NULL, 1)) { free (binding->codeset); binding->codeset = result; modified = 1; } } *codesetp = result; } } } 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; } else { /* We have to create a new binding. */ size_t len = strlen (domainname) + 1; struct binding *new_binding = (struct binding *) malloc (offsetof (struct binding, domainname) + len); if (__builtin_expect (new_binding == NULL, 0)) goto failed; memcpy (new_binding->domainname, domainname, len); if (dirnamep) { const char *dirname = *dirnamep; if (dirname == NULL) { #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) dirname = _nl_default_dirname; else { char *result; #if defined _LIBC || defined HAVE_STRDUP result = strdup (dirname); if (__builtin_expect (result == NULL, 0)) goto failed_dirname; #else size_t len = strlen (dirname) + 1; result = (char *) malloc (len); if (__builtin_expect (result == NULL, 0)) goto failed_dirname; memcpy (result, dirname, len); #endif dirname = result; } } *dirnamep = dirname; new_binding->dirname = (char *) dirname; } else { #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) { const char *codeset = *codesetp; if (codeset != NULL) { char *result; #if defined _LIBC || defined HAVE_STRDUP result = strdup (codeset); if (__builtin_expect (result == NULL, 0)) goto failed_codeset; #else size_t len = strlen (codeset) + 1; result = (char *) malloc (len); if (__builtin_expect (result == NULL, 0)) goto failed_codeset; memcpy (result, codeset, len); #endif codeset = result; } *codesetp = codeset; new_binding->codeset = (char *) codeset; } else new_binding->codeset = NULL; /* Now enqueue it. */ if (_nl_domain_bindings == NULL || strcmp (domainname, _nl_domain_bindings->domainname) < 0) { new_binding->next = _nl_domain_bindings; _nl_domain_bindings = new_binding; } else { binding = _nl_domain_bindings; while (binding->next != NULL && strcmp (domainname, binding->next->domainname) > 0) binding = binding->next; new_binding->next = binding->next; binding->next = new_binding; } modified = 1; /* Here we deal with memory allocation failures. */ 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: free (new_binding); failed: if (dirnamep) *dirnamep = NULL; #if defined _WIN32 && !defined __CYGWIN__ if (wdirnamep) *wdirnamep = NULL; #endif if (codesetp) *codesetp = NULL; } } /* If we modified any binding, we flush the caches. */ if (modified) ++_nl_msg_cat_cntr; gl_rwlock_unlock (_nl_state_lock); } /* Specify that the DOMAINNAME message catalog will be found in DIRNAME rather than in the system locale data base. */ char * BINDTEXTDOMAIN (const char *domainname, const char *dirname) { #ifdef __EMX__ const char *saved_dirname = dirname; char dirname_with_drive[_MAX_PATH]; # ifdef __KLIBC__ if (dirname && strncmp (dirname, "/@unixroot", 10) == 0 && (dirname[10] == '\0' || dirname[10] == '/' || dirname[10] == '\\')) /* kLIBC itself processes /@unixroot prefix */; else # endif /* Resolve UNIXROOT into dirname if it is not resolved by os2compat.[ch]. */ if (dirname && (dirname[0] == '/' || dirname[0] == '\\' )) { const char *unixroot = getenv ("UNIXROOT"); size_t len = strlen (dirname) + 1; if (unixroot && unixroot[0] != '\0' && unixroot[1] == ':' && unixroot[2] == '\0' && 2 + len <= _MAX_PATH) { memcpy (dirname_with_drive, unixroot, 2); memcpy (dirname_with_drive + 2, dirname, len); dirname = dirname_with_drive; } } #endif 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, NULL, &codeset); return (char *) codeset; } #ifdef _LIBC /* Aliases for function names in GNU C Library. */ weak_alias (__bindtextdomain, bindtextdomain); weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset); #endif