1 /* Handle list of needed message catalogs
2    Copyright (C) 1995-2022 Free Software Foundation, Inc.
3    Written by Ulrich Drepper <drepper@gnu.org>, 1995.
4 
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU Lesser General Public License as published by
7    the Free Software Foundation; either version 2.1 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #if defined HAVE_UNISTD_H || defined _LIBC
28 # include <unistd.h>
29 #endif
30 
31 #include "gettextP.h"
32 #ifdef _LIBC
33 # include <libintl.h>
34 #else
35 # include "libgnuintl.h"
36 #endif
37 
38 /* Handle multi-threaded applications.  */
39 #ifdef _LIBC
40 # include <libc-lock.h>
41 # define gl_rwlock_define_initialized __libc_rwlock_define_initialized
42 # define gl_rwlock_rdlock __libc_rwlock_rdlock
43 # define gl_rwlock_wrlock __libc_rwlock_wrlock
44 # define gl_rwlock_unlock __libc_rwlock_unlock
45 #else
46 # include "lock.h"
47 #endif
48 
49 /* @@ end of prolog @@ */
50 /* List of already loaded domains.  */
51 static struct loaded_l10nfile *_nl_loaded_domains;
52 
53 
54 /* Return a data structure describing the message catalog described by
55    the DOMAINNAME and CATEGORY parameters with respect to the currently
56    established bindings.  */
57 struct loaded_l10nfile *
_nl_find_domain(const char * dirname,char * locale,const char * domainname,struct binding * domainbinding)58 _nl_find_domain (const char *dirname, char *locale,
59 		 const char *domainname, struct binding *domainbinding)
60 {
61   struct loaded_l10nfile *retval;
62   const char *language;
63   const char *modifier;
64   const char *territory;
65   const char *codeset;
66   const char *normalized_codeset;
67   const char *alias_value;
68   int mask;
69 
70   /* LOCALE can consist of up to four recognized parts for the XPG syntax:
71 
72 		language[_territory][.codeset][@modifier]
73 
74      Beside the first part all of them are allowed to be missing.  If
75      the full specified locale is not found, the less specific one are
76      looked for.  The various parts will be stripped off according to
77      the following order:
78 		(1) codeset
79 		(2) normalized codeset
80 		(3) territory
81 		(4) modifier
82    */
83 
84   /* We need to protect modifying the _NL_LOADED_DOMAINS data.  */
85   gl_rwlock_define_initialized (static, lock);
86   gl_rwlock_rdlock (lock);
87 
88   /* If we have already tested for this locale entry there has to
89      be one data set in the list of loaded domains.  */
90   retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
91 			       strlen (dirname) + 1, 0, locale, NULL, NULL,
92 			       NULL, NULL, domainname, 0);
93 
94   gl_rwlock_unlock (lock);
95 
96   if (retval != NULL)
97     {
98       /* We know something about this locale.  */
99       int cnt;
100 
101       if (retval->decided <= 0)
102 	_nl_load_domain (retval, domainbinding);
103 
104       if (retval->data != NULL)
105 	return retval;
106 
107       for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
108 	{
109 	  if (retval->successor[cnt]->decided <= 0)
110 	    _nl_load_domain (retval->successor[cnt], domainbinding);
111 
112 	  if (retval->successor[cnt]->data != NULL)
113 	    break;
114 	}
115 
116       return retval;
117       /* NOTREACHED */
118     }
119 
120   /* See whether the locale value is an alias.  If yes its value
121      *overwrites* the alias name.  No test for the original value is
122      done.  */
123   alias_value = _nl_expand_alias (locale);
124   if (alias_value != NULL)
125     {
126       size_t len = strlen (alias_value) + 1;
127       locale = (char *) malloc (len);
128       if (locale == NULL)
129 	return NULL;
130 
131       memcpy (locale, alias_value, len);
132     }
133 
134   /* Now we determine the single parts of the locale name.  First
135      look for the language.  Termination symbols are `_', '.', and `@'.  */
136   mask = _nl_explode_name (locale, &language, &modifier, &territory,
137 			   &codeset, &normalized_codeset);
138   if (mask == -1)
139     /* This means we are out of core.  */
140     return NULL;
141 
142   /* We need to protect modifying the _NL_LOADED_DOMAINS data.  */
143   gl_rwlock_wrlock (lock);
144 
145   /* Create all possible locale entries which might be interested in
146      generalization.  */
147   retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
148 			       strlen (dirname) + 1, mask, language, territory,
149 			       codeset, normalized_codeset, modifier,
150 			       domainname, 1);
151 
152   gl_rwlock_unlock (lock);
153 
154   if (retval == NULL)
155     /* This means we are out of core.  */
156     goto out;
157 
158   if (retval->decided <= 0)
159     _nl_load_domain (retval, domainbinding);
160   if (retval->data == NULL)
161     {
162       int cnt;
163       for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
164 	{
165 	  if (retval->successor[cnt]->decided <= 0)
166 	    _nl_load_domain (retval->successor[cnt], domainbinding);
167 	  if (retval->successor[cnt]->data != NULL)
168 	    break;
169 	}
170     }
171 
172   /* The room for an alias was dynamically allocated.  Free it now.  */
173   if (alias_value != NULL)
174     free (locale);
175 
176 out:
177   /* The space for normalized_codeset is dynamically allocated.  Free it.  */
178   if (mask & XPG_NORM_CODESET)
179     free ((void *) normalized_codeset);
180 
181   return retval;
182 }
183 
184 
185 #ifdef _LIBC
186 /* This is called from iconv/gconv_db.c's free_mem, as locales must
187    be freed before freeing gconv steps arrays.  */
188 void __libc_freeres_fn_section
_nl_finddomain_subfreeres(void)189 _nl_finddomain_subfreeres (void)
190 {
191   struct loaded_l10nfile *runp = _nl_loaded_domains;
192 
193   while (runp != NULL)
194     {
195       struct loaded_l10nfile *here = runp;
196       if (runp->data != NULL)
197 	_nl_unload_domain ((struct loaded_domain *) runp->data);
198       runp = runp->next;
199       free ((char *) here->filename);
200       free (here);
201     }
202 }
203 #endif
204