• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *   http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <libintl.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <limits.h>
21 #include <sys/stat.h>
22 #include <sys/mman.h>
23 #include <ctype.h>
24 #include "locale_impl.h"
25 #include "atomic.h"
26 #include "pleval.h"
27 #include "lock.h"
28 
29 #define __DIGIT_SEVEN__ 7
30 #define __DIGIT_NINE__ 9
31 #define __DIGIT_TEN__ 10
32 #define __DIGIT_THIRTEEN__ 13
33 
34 struct binding {
35     struct binding *next;
36     int dirlen;
37     volatile int active;
38     char *domainname;
39     char *dirname;
40     char buf[];
41 };
42 
43 static void *volatile bindings;
44 
gettextdir(const char * domainname,size_t * dirlen)45 static char *gettextdir(const char *domainname, size_t *dirlen)
46 {
47     struct binding *p;
48     for (p=bindings; p; p=p->next) {
49         if (!strcmp(p->domainname, domainname) && p->active) {
50             *dirlen = p->dirlen;
51             return (char *)p->dirname;
52         }
53     }
54     return 0;
55 }
56 
bindtextdomain(const char * domainname,const char * dirname)57 char *bindtextdomain(const char *domainname, const char *dirname)
58 {
59     static volatile int lock[1];
60     struct binding *p, *q;
61 
62     if (!domainname) {
63         return 0;
64     }
65     if (!dirname) {
66         return gettextdir(domainname, &(size_t){0});
67     }
68 
69     size_t domlen = strnlen(domainname, NAME_MAX+1);
70     size_t dirlen = strnlen(dirname, PATH_MAX);
71     if (domlen > NAME_MAX || dirlen >= PATH_MAX) {
72         errno = EINVAL;
73         return 0;
74     }
75 
76     LOCK(lock);
77 
78     for (p=bindings; p; p=p->next) {
79         if (!strcmp(p->domainname, domainname) &&
80             !strcmp(p->dirname, dirname)) {
81             break;
82         }
83     }
84 
85     if (!p) {
86         p = calloc(sizeof *p + domlen + dirlen + 2, 1);
87         if (!p) {
88             UNLOCK(lock);
89             return 0;
90         }
91         p->next = bindings;
92         p->dirlen = dirlen;
93         p->domainname = p->buf;
94         p->dirname = p->buf + domlen + 1;
95         memcpy(p->domainname, domainname, domlen+1);
96         memcpy(p->dirname, dirname, dirlen+1);
97         a_cas_p(&bindings, bindings, p);
98     }
99 
100     a_store(&p->active, 1);
101 
102     for (q=bindings; q; q=q->next) {
103         if (!strcmp(q->domainname, domainname) && q != p) {
104             a_store(&q->active, 0);
105         }
106     }
107 
108     UNLOCK(lock);
109 
110     return (char *)p->dirname;
111 }
112 
113 static const char catnames[][18] = {
114     "LC_CTYPE",
115     "LC_NUMERIC",
116     "LC_TIME",
117     "LC_COLLATE",
118     "LC_MONETARY",
119     "LC_MESSAGES",
120     "LC_PAPER",
121     "LC_NAME",
122     "LC_ADDRESS",
123     "LC_TELEPHONE",
124     "LC_MEASUREMENT",
125     "LC_IDENTIFICATION",
126 };
127 
128 static const char catlens[] = { 8, 10, 7, 10, 11, 11, 8, 7, 10, 12, 14, 17 };
129 
130 struct msgcat {
131     struct msgcat *next;
132     const void *map;
133     size_t map_size;
134     const char *plural_rule;
135     int nplurals;
136     struct binding *binding;
137     const struct __locale_map *lm;
138     int cat;
139 };
140 
dummy_gettextdomain()141 static char *dummy_gettextdomain()
142 {
143     return "messages";
144 }
145 
146 weak_alias(dummy_gettextdomain, __gettextdomain);
147 
dcngettext(const char * domainname,const char * msgid1,const char * msgid2,unsigned long int n,int category)148 char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n, int category)
149 {
150     static struct msgcat *volatile cats;
151     struct msgcat *p;
152     struct __locale_struct *loc = CURRENT_LOCALE;
153     const struct __locale_map *lm;
154     size_t domlen;
155     struct binding *q;
156     int old_errno = errno;
157 
158     if ((unsigned)category >= LC_ALL) {
159         goto notrans;
160     }
161 
162     if (!domainname) {
163         domainname = __gettextdomain();
164     }
165 
166     domlen = strnlen(domainname, NAME_MAX+1);
167     if (domlen > NAME_MAX) {
168         goto notrans;
169     }
170 
171     for (q=bindings; q; q=q->next) {
172         if (!strcmp(q->domainname, domainname) && q->active) {
173             break;
174         }
175     }
176     if (!q) {
177         goto notrans;
178     }
179 
180     lm = loc->cat[category];
181     if (!lm) {
182 notrans:
183         errno = old_errno;
184         return (char *) ((n == 1) ? msgid1 : msgid2);
185     }
186 
187     for (p=cats; p; p=p->next) {
188         if (p->binding == q && p->lm == lm && p->cat == category) {
189             break;
190         }
191     }
192 
193     if (!p) {
194         const char *dirname, *locname, *catname, *modname, *locp;
195         size_t dirlen, loclen, catlen, modlen, alt_modlen;
196         void *old_cats;
197         size_t map_size;
198 
199         dirname = q->dirname;
200         locname = lm->name;
201         catname = catnames[category];
202 
203         dirlen = q->dirlen;
204         loclen = strlen(locname);
205         catlen = catlens[category];
206 
207         /* Logically split @mod suffix from locale name. */
208         modname = memchr(locname, '@', loclen);
209         if (!modname) {
210             modname = locname + loclen;
211         }
212         alt_modlen = modlen = loclen - (modname-locname);
213         loclen = modname-locname;
214 
215         /* Drop .charset identifier; it is not used. */
216         const char *csp = memchr(locname, '.', loclen);
217         if (csp) {
218             loclen = csp-locname;
219         }
220 
221         char name[dirlen+1 + loclen+modlen+1 + catlen+1 + domlen+3 + 1];
222         const void *map;
223 
224         for (;;) {
225             snprintf(name, sizeof name, "%s/%.*s%.*s/%s/%s.mo\0",
226                 dirname, (int)loclen, locname,
227                 (int)alt_modlen, modname, catname, domainname);
228             if (map = __map_file(name, &map_size)) {
229                 break;
230             }
231 
232             /* Try dropping @mod, _YY, then both. */
233             if (alt_modlen) {
234                 alt_modlen = 0;
235             } else if ((locp = memchr(locname, '_', loclen))) {
236                 loclen = locp-locname;
237                 alt_modlen = modlen;
238             } else {
239                 break;
240             }
241         }
242         if (!map) {
243             goto notrans;
244         }
245 
246         p = calloc(sizeof *p, 1);
247         if (!p) {
248             __munmap((void *)map, map_size);
249             goto notrans;
250         }
251         p->cat = category;
252         p->binding = q;
253         p->lm = lm;
254         p->map = map;
255         p->map_size = map_size;
256 
257         const char *rule = "n!=1;";
258         unsigned long np = 2;
259         const char *r = __mo_lookup(p->map, p->map_size, "");
260         char *z;
261         while (r && strncmp(r, "Plural-Forms:", __DIGIT_THIRTEEN__)) {
262             z = strchr(r, '\n');
263             r = z ? z+1 : 0;
264         }
265         if (r) {
266             r += __DIGIT_THIRTEEN__;
267             while (isspace(*r)) {
268                 r++;
269             }
270             if (!strncmp(r, "nplurals=", __DIGIT_NINE__)) {
271                 np = strtoul(r+__DIGIT_NINE__, &z, __DIGIT_TEN__);
272                 r = z;
273             }
274             while (*r && *r != ';') {
275                 r++;
276             }
277             if (*r) {
278                 r++;
279                 while (isspace(*r)) {
280                     r++;
281                 }
282                 if (!strncmp(r, "plural=", __DIGIT_SEVEN__)) {
283                     rule = r+__DIGIT_SEVEN__;
284                 }
285             }
286         }
287         p->nplurals = np;
288         p->plural_rule = rule;
289 
290         do {
291             old_cats = cats;
292             p->next = old_cats;
293         } while (a_cas_p(&cats, old_cats, p) != old_cats);
294     }
295 
296     const char *trans = __mo_lookup(p->map, p->map_size, msgid1);
297     if (!trans) {
298         goto notrans;
299     }
300 
301     /* Non-plural-processing gettext forms pass a null pointer as
302      * msgid2 to request that dcngettext suppress plural processing. */
303 
304     if (msgid2 && p->nplurals) {
305         unsigned long plural = __pleval(p->plural_rule, n);
306         if (plural > p->nplurals) {
307             goto notrans;
308         }
309         while (plural--) {
310             size_t rem = p->map_size - (trans - (char *)p->map);
311             size_t l = strnlen(trans, rem);
312             if (l+1 >= rem) {
313                 goto notrans;
314             }
315             trans += l+1;
316         }
317     }
318     errno = old_errno;
319     return (char *)trans;
320 }
321 
dcgettext(const char * domainname,const char * msgid,int category)322 char *dcgettext(const char *domainname, const char *msgid, int category)
323 {
324     return dcngettext(domainname, msgid, 0, 1, category);
325 }
326 
dngettext(const char * domainname,const char * msgid1,const char * msgid2,unsigned long int n)327 char *dngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n)
328 {
329     return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES);
330 }
331 
dgettext(const char * domainname,const char * msgid)332 char *dgettext(const char *domainname, const char *msgid)
333 {
334     return dcngettext(domainname, msgid, 0, 1, LC_MESSAGES);
335 }
336