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