OLD | NEW |
1 #include <libintl.h> | 1 #include <libintl.h> |
2 #include <stdlib.h> | 2 #include <stdlib.h> |
3 #include <string.h> | 3 #include <string.h> |
4 #include <errno.h> | 4 #include <errno.h> |
5 #include <limits.h> | 5 #include <limits.h> |
6 #include <sys/stat.h> | 6 #include <sys/stat.h> |
7 #include <ctype.h> | 7 #include <ctype.h> |
8 #include "locale_impl.h" | 8 #include "locale_impl.h" |
9 #include "libc.h" | 9 #include "libc.h" |
10 #include "atomic.h" | 10 #include "atomic.h" |
11 | 11 |
12 struct binding { | 12 struct binding { |
13 » struct binding *next; | 13 struct binding* next; |
14 » int dirlen; | 14 int dirlen; |
15 » volatile int active; | 15 volatile int active; |
16 » char *domainname; | 16 char* domainname; |
17 » char *dirname; | 17 char* dirname; |
18 » char buf[]; | 18 char buf[]; |
19 }; | 19 }; |
20 | 20 |
21 static void *volatile bindings; | 21 static void* volatile bindings; |
22 | 22 |
23 static char *gettextdir(const char *domainname, size_t *dirlen) | 23 static char* gettextdir(const char* domainname, size_t* dirlen) { |
24 { | 24 struct binding* p; |
25 » struct binding *p; | 25 for (p = bindings; p; p = p->next) { |
26 » for (p=bindings; p; p=p->next) { | 26 if (!strcmp(p->domainname, domainname) && p->active) { |
27 » » if (!strcmp(p->domainname, domainname) && p->active) { | 27 *dirlen = p->dirlen; |
28 » » » *dirlen = p->dirlen; | 28 return (char*)p->dirname; |
29 » » » return (char *)p->dirname; | 29 } |
30 » » } | 30 } |
31 » } | 31 return 0; |
32 » return 0; | 32 } |
33 } | 33 |
34 | 34 char* bindtextdomain(const char* domainname, const char* dirname) { |
35 char *bindtextdomain(const char *domainname, const char *dirname) | 35 static volatile int lock[2]; |
36 { | 36 struct binding *p, *q; |
37 » static volatile int lock[2]; | 37 |
38 » struct binding *p, *q; | 38 if (!domainname) |
39 | 39 return 0; |
40 » if (!domainname) return 0; | 40 if (!dirname) |
41 » if (!dirname) return gettextdir(domainname, &(size_t){0}); | 41 return gettextdir(domainname, &(size_t){0}); |
42 | 42 |
43 » size_t domlen = strlen(domainname); | 43 size_t domlen = strlen(domainname); |
44 » size_t dirlen = strlen(dirname); | 44 size_t dirlen = strlen(dirname); |
45 » if (domlen > NAME_MAX || dirlen >= PATH_MAX) { | 45 if (domlen > NAME_MAX || dirlen >= PATH_MAX) { |
46 » » errno = EINVAL; | 46 errno = EINVAL; |
47 » » return 0; | 47 return 0; |
48 » } | 48 } |
49 | 49 |
50 » LOCK(lock); | 50 LOCK(lock); |
51 | 51 |
52 » for (p=bindings; p; p=p->next) { | 52 for (p = bindings; p; p = p->next) { |
53 » » if (!strcmp(p->domainname, domainname) && | 53 if (!strcmp(p->domainname, domainname) && !strcmp(p->dirname, dirname)) { |
54 » » !strcmp(p->dirname, dirname)) { | 54 break; |
55 » » » break; | 55 } |
56 » » } | 56 } |
57 » } | 57 |
58 | 58 if (!p) { |
59 » if (!p) { | 59 p = malloc(sizeof *p + domlen + dirlen + 2); |
60 » » p = malloc(sizeof *p + domlen + dirlen + 2); | 60 if (!p) { |
61 » » if (!p) { | 61 UNLOCK(lock); |
62 » » » UNLOCK(lock); | 62 return 0; |
63 » » » return 0; | 63 } |
64 » » } | 64 p->next = bindings; |
65 » » p->next = bindings; | 65 p->dirlen = dirlen; |
66 » » p->dirlen = dirlen; | 66 p->domainname = p->buf; |
67 » » p->domainname = p->buf; | 67 p->dirname = p->buf + domlen + 1; |
68 » » p->dirname = p->buf + domlen + 1; | 68 memcpy(p->domainname, domainname, domlen + 1); |
69 » » memcpy(p->domainname, domainname, domlen+1); | 69 memcpy(p->dirname, dirname, dirlen + 1); |
70 » » memcpy(p->dirname, dirname, dirlen+1); | 70 a_cas_p(&bindings, bindings, p); |
71 » » a_cas_p(&bindings, bindings, p); | 71 } |
72 » } | 72 |
73 | 73 a_store(&p->active, 1); |
74 » a_store(&p->active, 1); | 74 |
75 | 75 for (q = bindings; q; q = q->next) { |
76 » for (q=bindings; q; q=q->next) { | 76 if (!strcmp(p->domainname, domainname) && q != p) |
77 » » if (!strcmp(p->domainname, domainname) && q != p) | 77 a_store(&q->active, 0); |
78 » » » a_store(&q->active, 0); | 78 } |
79 » } | 79 |
80 | 80 UNLOCK(lock); |
81 » UNLOCK(lock); | 81 |
82 » | 82 return (char*)p->dirname; |
83 » return (char *)p->dirname; | |
84 } | 83 } |
85 | 84 |
86 static const char catnames[][12] = { | 85 static const char catnames[][12] = { |
87 » "LC_CTYPE", | 86 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", |
88 » "LC_NUMERIC", | 87 "LC_COLLATE", "LC_MONETARY", "LC_MESSAGES", |
89 » "LC_TIME", | |
90 » "LC_COLLATE", | |
91 » "LC_MONETARY", | |
92 » "LC_MESSAGES", | |
93 }; | 88 }; |
94 | 89 |
95 static const char catlens[] = { 8, 10, 7, 10, 11, 11 }; | 90 static const char catlens[] = {8, 10, 7, 10, 11, 11}; |
96 | 91 |
97 struct msgcat { | 92 struct msgcat { |
98 » struct msgcat *next; | 93 struct msgcat* next; |
99 » const void *map; | 94 const void* map; |
100 » size_t map_size; | 95 size_t map_size; |
101 » void *volatile plural_rule; | 96 void* volatile plural_rule; |
102 » volatile int nplurals; | 97 volatile int nplurals; |
103 » char name[]; | 98 char name[]; |
104 }; | 99 }; |
105 | 100 |
106 static char *dummy_gettextdomain() | 101 static char* dummy_gettextdomain() { |
107 { | 102 return "messages"; |
108 » return "messages"; | |
109 } | 103 } |
110 | 104 |
111 weak_alias(dummy_gettextdomain, __gettextdomain); | 105 weak_alias(dummy_gettextdomain, __gettextdomain); |
112 | 106 |
113 const unsigned char *__map_file(const char *, size_t *); | 107 const unsigned char* __map_file(const char*, size_t*); |
114 int __munmap(void *, size_t); | 108 int __munmap(void*, size_t); |
115 unsigned long __pleval(const char *, unsigned long); | 109 unsigned long __pleval(const char*, unsigned long); |
116 | 110 |
117 char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2,
unsigned long int n, int category) | 111 char* dcngettext(const char* domainname, |
118 { | 112 const char* msgid1, |
119 » static struct msgcat *volatile cats; | 113 const char* msgid2, |
120 » struct msgcat *p; | 114 unsigned long int n, |
121 » struct __locale_struct *loc = CURRENT_LOCALE; | 115 int category) { |
122 » const struct __locale_map *lm; | 116 static struct msgcat* volatile cats; |
123 » const char *dirname, *locname, *catname; | 117 struct msgcat* p; |
124 » size_t dirlen, loclen, catlen, domlen; | 118 struct __locale_struct* loc = CURRENT_LOCALE; |
125 | 119 const struct __locale_map* lm; |
126 » if ((unsigned)category >= LC_ALL) goto notrans; | 120 const char *dirname, *locname, *catname; |
127 | 121 size_t dirlen, loclen, catlen, domlen; |
128 » if (!domainname) domainname = __gettextdomain(); | 122 |
129 | 123 if ((unsigned)category >= LC_ALL) |
130 » domlen = strlen(domainname); | 124 goto notrans; |
131 » if (domlen > NAME_MAX) goto notrans; | 125 |
132 | 126 if (!domainname) |
133 » dirname = gettextdir(domainname, &dirlen); | 127 domainname = __gettextdomain(); |
134 » if (!dirname) goto notrans; | 128 |
135 | 129 domlen = strlen(domainname); |
136 » lm = loc->cat[category]; | 130 if (domlen > NAME_MAX) |
137 » if (!lm) { | 131 goto notrans; |
138 notrans: | 132 |
139 » » return (char *) ((n == 1) ? msgid1 : msgid2); | 133 dirname = gettextdir(domainname, &dirlen); |
140 » } | 134 if (!dirname) |
141 » locname = lm->name; | 135 goto notrans; |
142 | 136 |
143 » catname = catnames[category]; | 137 lm = loc->cat[category]; |
144 » catlen = catlens[category]; | 138 if (!lm) { |
145 » loclen = strlen(locname); | 139 notrans: |
146 | 140 return (char*)((n == 1) ? msgid1 : msgid2); |
147 » size_t namelen = dirlen+1 + loclen+1 + catlen+1 + domlen+3; | 141 } |
148 » char name[namelen+1], *s = name; | 142 locname = lm->name; |
149 | 143 |
150 » memcpy(s, dirname, dirlen); | 144 catname = catnames[category]; |
151 » s[dirlen] = '/'; | 145 catlen = catlens[category]; |
152 » s += dirlen + 1; | 146 loclen = strlen(locname); |
153 » memcpy(s, locname, loclen); | 147 |
154 » s[loclen] = '/'; | 148 size_t namelen = dirlen + 1 + loclen + 1 + catlen + 1 + domlen + 3; |
155 » s += loclen + 1; | 149 char name[namelen + 1], *s = name; |
156 » memcpy(s, catname, catlen); | 150 |
157 » s[catlen] = '/'; | 151 memcpy(s, dirname, dirlen); |
158 » s += catlen + 1; | 152 s[dirlen] = '/'; |
159 » memcpy(s, domainname, domlen); | 153 s += dirlen + 1; |
160 » s[domlen] = '.'; | 154 memcpy(s, locname, loclen); |
161 » s[domlen+1] = 'm'; | 155 s[loclen] = '/'; |
162 » s[domlen+2] = 'o'; | 156 s += loclen + 1; |
163 » s[domlen+3] = 0; | 157 memcpy(s, catname, catlen); |
164 | 158 s[catlen] = '/'; |
165 » for (p=cats; p; p=p->next) | 159 s += catlen + 1; |
166 » » if (!strcmp(p->name, name)) | 160 memcpy(s, domainname, domlen); |
167 » » » break; | 161 s[domlen] = '.'; |
168 | 162 s[domlen + 1] = 'm'; |
169 » if (!p) { | 163 s[domlen + 2] = 'o'; |
170 » » void *old_cats; | 164 s[domlen + 3] = 0; |
171 » » size_t map_size; | 165 |
172 » » const void *map = __map_file(name, &map_size); | 166 for (p = cats; p; p = p->next) |
173 » » if (!map) goto notrans; | 167 if (!strcmp(p->name, name)) |
174 » » p = malloc(sizeof *p + namelen + 1); | 168 break; |
175 » » if (!p) { | 169 |
176 » » » __munmap((void *)map, map_size); | 170 if (!p) { |
177 » » » goto notrans; | 171 void* old_cats; |
178 » » } | 172 size_t map_size; |
179 » » p->map = map; | 173 const void* map = __map_file(name, &map_size); |
180 » » p->map_size = map_size; | 174 if (!map) |
181 » » memcpy(p->name, name, namelen+1); | 175 goto notrans; |
182 » » do { | 176 p = malloc(sizeof *p + namelen + 1); |
183 » » » old_cats = cats; | 177 if (!p) { |
184 » » » p->next = old_cats; | 178 __munmap((void*)map, map_size); |
185 » » } while (a_cas_p(&cats, old_cats, p) != old_cats); | 179 goto notrans; |
186 » } | 180 } |
187 | 181 p->map = map; |
188 » const char *trans = __mo_lookup(p->map, p->map_size, msgid1); | 182 p->map_size = map_size; |
189 » if (!trans) goto notrans; | 183 memcpy(p->name, name, namelen + 1); |
190 | 184 do { |
191 » /* Non-plural-processing gettext forms pass a null pointer as | 185 old_cats = cats; |
192 » * msgid2 to request that dcngettext suppress plural processing. */ | 186 p->next = old_cats; |
193 » if (!msgid2) return (char *)trans; | 187 } while (a_cas_p(&cats, old_cats, p) != old_cats); |
194 | 188 } |
195 » if (!p->plural_rule) { | 189 |
196 » » const char *rule = "n!=1;"; | 190 const char* trans = __mo_lookup(p->map, p->map_size, msgid1); |
197 » » unsigned long np = 2; | 191 if (!trans) |
198 » » const char *r = __mo_lookup(p->map, p->map_size, ""); | 192 goto notrans; |
199 » » char *z; | 193 |
200 » » while (r && strncmp(r, "Plural-Forms:", 13)) { | 194 /* Non-plural-processing gettext forms pass a null pointer as |
201 » » » z = strchr(r, '\n'); | 195 * msgid2 to request that dcngettext suppress plural processing. */ |
202 » » » r = z ? z+1 : 0; | 196 if (!msgid2) |
203 » » } | 197 return (char*)trans; |
204 » » if (r) { | 198 |
205 » » » r += 13; | 199 if (!p->plural_rule) { |
206 » » » while (isspace(*r)) r++; | 200 const char* rule = "n!=1;"; |
207 » » » if (!strncmp(r, "nplurals=", 9)) { | 201 unsigned long np = 2; |
208 » » » » np = strtoul(r+9, &z, 10); | 202 const char* r = __mo_lookup(p->map, p->map_size, ""); |
209 » » » » r = z; | 203 char* z; |
210 » » » } | 204 while (r && strncmp(r, "Plural-Forms:", 13)) { |
211 » » » while (*r && *r != ';') r++; | 205 z = strchr(r, '\n'); |
212 » » » if (*r) { | 206 r = z ? z + 1 : 0; |
213 » » » » r++; | 207 } |
214 » » » » while (isspace(*r)) r++; | 208 if (r) { |
215 » » » » if (!strncmp(r, "plural=", 7)) | 209 r += 13; |
216 » » » » » rule = r+7; | 210 while (isspace(*r)) |
217 » » » } | 211 r++; |
218 » » } | 212 if (!strncmp(r, "nplurals=", 9)) { |
219 » » a_store(&p->nplurals, np); | 213 np = strtoul(r + 9, &z, 10); |
220 » » a_cas_p(&p->plural_rule, 0, (void *)rule); | 214 r = z; |
221 » } | 215 } |
222 » if (p->nplurals) { | 216 while (*r && *r != ';') |
223 » » unsigned long plural = __pleval(p->plural_rule, n); | 217 r++; |
224 » » if (plural > p->nplurals) goto notrans; | 218 if (*r) { |
225 » » while (plural--) { | 219 r++; |
226 » » » size_t rem = p->map_size - (trans - (char *)p->map); | 220 while (isspace(*r)) |
227 » » » size_t l = strnlen(trans, rem); | 221 r++; |
228 » » » if (l+1 >= rem) | 222 if (!strncmp(r, "plural=", 7)) |
229 » » » » goto notrans; | 223 rule = r + 7; |
230 » » » trans += l+1; | 224 } |
231 » » } | 225 } |
232 » } | 226 a_store(&p->nplurals, np); |
233 » return (char *)trans; | 227 a_cas_p(&p->plural_rule, 0, (void*)rule); |
234 } | 228 } |
235 | 229 if (p->nplurals) { |
236 char *dcgettext(const char *domainname, const char *msgid, int category) | 230 unsigned long plural = __pleval(p->plural_rule, n); |
237 { | 231 if (plural > p->nplurals) |
238 » return dcngettext(domainname, msgid, 0, 1, category); | 232 goto notrans; |
239 } | 233 while (plural--) { |
240 | 234 size_t rem = p->map_size - (trans - (char*)p->map); |
241 char *dngettext(const char *domainname, const char *msgid1, const char *msgid2,
unsigned long int n) | 235 size_t l = strnlen(trans, rem); |
242 { | 236 if (l + 1 >= rem) |
243 » return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES); | 237 goto notrans; |
244 } | 238 trans += l + 1; |
245 | 239 } |
246 char *dgettext(const char *domainname, const char *msgid) | 240 } |
247 { | 241 return (char*)trans; |
248 » return dcngettext(domainname, msgid, 0, 1, LC_MESSAGES); | 242 } |
249 } | 243 |
| 244 char* dcgettext(const char* domainname, const char* msgid, int category) { |
| 245 return dcngettext(domainname, msgid, 0, 1, category); |
| 246 } |
| 247 |
| 248 char* dngettext(const char* domainname, |
| 249 const char* msgid1, |
| 250 const char* msgid2, |
| 251 unsigned long int n) { |
| 252 return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES); |
| 253 } |
| 254 |
| 255 char* dgettext(const char* domainname, const char* msgid) { |
| 256 return dcngettext(domainname, msgid, 0, 1, LC_MESSAGES); |
| 257 } |
OLD | NEW |