naev 0.11.5
msgcat.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
4
13/* musl as a whole is licensed under the following standard MIT license:
14 *
15 * ----------------------------------------------------------------------
16 * Copyright © 2005-2020 Rich Felker, et al.
17 *
18 * Permission is hereby granted, free of charge, to any person obtaining
19 * a copy of this software and associated documentation files (the
20 * "Software"), to deal in the Software without restriction, including
21 * without limitation the rights to use, copy, modify, merge, publish,
22 * distribute, sublicense, and/or sell copies of the Software, and to
23 * permit persons to whom the Software is furnished to do so, subject to
24 * the following conditions:
25 *
26 * The above copyright notice and this permission notice shall be
27 * included in all copies or substantial portions of the Software.
28 *
29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
32 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
33 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
34 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
35 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 * ----------------------------------------------------------------------
37 * */
38
40#include <ctype.h>
41#include <stdlib.h>
42#include <string.h>
45#include "msgcat.h"
46
47
48/* Internal implementations, corresponding to Musl's __pleval and __mo_lookup. */
49static uint64_t msgcat_plural_eval( const char *, uint64_t );
50static const char* msgcat_mo_lookup( const void *p, size_t size, const char *s );
51
52
53/* ==================== https://git.musl-libc.org/cgit/musl/tree/src/locale/dcngettext.c ===================== */
54/* (The code in this section is heavily modified for Naev's use, but that's its origin.) */
55
59void msgcat_init( msgcat_t* p, const void* map, size_t map_size )
60{
61 p->map = map;
62 p->map_size = map_size;
63
64 const char *rule = "n!=1;";
65 uint64_t np = 2;
66 const char *r = msgcat_mo_lookup(p->map, p->map_size, "");
67 char *z;
68 while (r && strncmp(r, "Plural-Forms:", 13)) {
69 z = strchr(r, '\n');
70 r = z ? z+1 : 0;
71 }
72 if (r) {
73 r += 13;
74 while (isspace(*r)) r++;
75 if (!strncmp(r, "nplurals=", 9)) {
76 np = strtoul(r+9, &z, 10);
77 r = z;
78 }
79 while (*r && *r != ';') r++;
80 if (*r) {
81 r++;
82 while (isspace(*r)) r++;
83 if (!strncmp(r, "plural=", 7))
84 rule = r+7;
85 }
86 }
87 p->nplurals = np;
88 p->plural_rule = rule;
89}
90
101const char* msgcat_ngettext( const msgcat_t* p, const char* msgid1, const char* msgid2, uint64_t n )
102{
103 const char *trans = msgcat_mo_lookup(p->map, p->map_size, msgid1);
104 if (!trans) return NULL;
105
106 /* Non-plural-processing gettext forms pass a null pointer as
107 * msgid2 to request that dcngettext suppress plural processing. */
108
109 if (msgid2 && p->nplurals) {
110 uint64_t plural = msgcat_plural_eval(p->plural_rule, n);
111 if (plural > p->nplurals) return NULL;
112 while (plural--) {
113 size_t rem = p->map_size - (trans - (char *)p->map);
114 size_t l = strnlen(trans, rem);
115 if (l+1 >= rem)
116 return NULL;
117 trans += l+1;
118 }
119 }
120 return trans;
121}
122
123
124/* ===================== https://git.musl-libc.org/cgit/musl/tree/src/locale/__mo_lookup.c =================== */
125static inline uint32_t swapc(uint32_t x, int c)
126{
127 return c ? (x>>24) | (x>>8&0xff00) | (x<<8&0xff0000) | (x<<24) : x;
128}
129
130const char *msgcat_mo_lookup(const void *p, size_t size, const char *s)
131{
132 const uint32_t *mo = p;
133 int sw = *mo - 0x950412de;
134 uint32_t b = 0, n = swapc(mo[2], sw);
135 uint32_t o = swapc(mo[3], sw);
136 uint32_t t = swapc(mo[4], sw);
137 if (n>=size/4 || o>=size-4*n || t>=size-4*n || ((o|t)%4))
138 return 0;
139 o/=4;
140 t/=4;
141 for (;;) {
142 uint32_t ol = swapc(mo[o+2*(b+n/2)], sw);
143 uint32_t os = swapc(mo[o+2*(b+n/2)+1], sw);
144 if (os >= size || ol >= size-os || ((char *)p)[os+ol])
145 return 0;
146 int sign = strcmp(s, (char *)p + os);
147 if (!sign) {
148 uint32_t tl = swapc(mo[t+2*(b+n/2)], sw);
149 uint32_t ts = swapc(mo[t+2*(b+n/2)+1], sw);
150 if (ts >= size || tl >= size-ts || ((char *)p)[ts+tl])
151 return 0;
152 return (char *)p + ts;
153 }
154 else if (n == 1) return 0;
155 else if (sign < 0)
156 n /= 2;
157 else {
158 b += n/2;
159 n -= n/2;
160 }
161 }
162 return 0;
163}
164
165
169uint32_t msgcat_nstringsFromHeader( const char buf[12] )
170{
171 const uint32_t *mo = (uint32_t*) buf;
172 int sw = *mo - 0x950412de;
173 return swapc(mo[2], sw);
174}
175
176
177/* ===================== https://git.musl-libc.org/cgit/musl/tree/src/locale/pleval.c ======================== */
178/*
179grammar:
180
181Start = Expr ';'
182Expr = Or | Or '?' Expr ':' Expr
183Or = And | Or '||' And
184And = Eq | And '&&' Eq
185Eq = Rel | Eq '==' Rel | Eq '!=' Rel
186Rel = Add | Rel '<=' Add | Rel '>=' Add | Rel '<' Add | Rel '>' Add
187Add = Mul | Add '+' Mul | Add '-' Mul
188Mul = Prim | Mul '*' Prim | Mul '/' Prim | Mul '%' Prim
189Prim = '(' Expr ')' | '!' Prim | decimal | 'n'
190
191internals:
192
193recursive descent expression evaluator with stack depth limit.
194for binary operators an operator-precedence parser is used.
195eval* functions store the result of the parsed subexpression
196and return a pointer to the next non-space character.
197*/
198
199struct st {
200 uint64_t r;
201 uint64_t n;
202 int op;
203};
204
205static const char *skipspace(const char *s)
206{
207 while (isspace(*s)) s++;
208 return s;
209}
210
211static const char *evalexpr(struct st *st, const char *s, int d);
212
213static const char *evalprim(struct st *st, const char *s, int d)
214{
215 char *e;
216 if (--d < 0) return "";
217 s = skipspace(s);
218 if (isdigit(*s)) {
219 st->r = strtoul(s, &e, 10);
220 if (e == s || st->r == UINT64_MAX) return "";
221 return skipspace(e);
222 }
223 if (*s == 'n') {
224 st->r = st->n;
225 return skipspace(s+1);
226 }
227 if (*s == '(') {
228 s = evalexpr(st, s+1, d);
229 if (*s != ')') return "";
230 return skipspace(s+1);
231 }
232 if (*s == '!') {
233 s = evalprim(st, s+1, d);
234 st->r = !st->r;
235 return s;
236 }
237 return "";
238}
239
240static int binop(struct st *st, int op, uint64_t left)
241{
242 uint64_t a = left, b = st->r;
243 switch (op) {
244 case 0: st->r = a||b; return 0;
245 case 1: st->r = a&&b; return 0;
246 case 2: st->r = a==b; return 0;
247 case 3: st->r = a!=b; return 0;
248 case 4: st->r = a>=b; return 0;
249 case 5: st->r = a<=b; return 0;
250 case 6: st->r = a>b; return 0;
251 case 7: st->r = a<b; return 0;
252 case 8: st->r = a+b; return 0;
253 case 9: st->r = a-b; return 0;
254 case 10: st->r = a*b; return 0;
255 case 11: if (b) {st->r = a%b; return 0;} return 1;
256 case 12: if (b) {st->r = a/b; return 0;} return 1;
257 }
258 return 1;
259}
260
261static const char *parseop(struct st *st, const char *s)
262{
263 static const char opch[11] = "|&=!><+-*%/";
264 static const char opch2[6] = "|&====";
265 int i;
266 for (i=0; i<11; i++)
267 if (*s == opch[i]) {
268 /* note: >,< are accepted with or without = */
269 if (i<6 && s[1] == opch2[i]) {
270 st->op = i;
271 return s+2;
272 }
273 if (i>=4) {
274 st->op = i+2;
275 return s+1;
276 }
277 break;
278 }
279 st->op = 13;
280 return s;
281}
282
283static const char *evalbinop(struct st *st, const char *s, int minprec, int d)
284{
285 static const char prec[14] = {1,2,3,3,4,4,4,4,5,5,6,6,6,0};
286 uint64_t left;
287 int op;
288 d--;
289 s = evalprim(st, s, d);
290 s = parseop(st, s);
291 for (;;) {
292 /*
293 st->r (left hand side value) and st->op are now set,
294 get the right hand side or back out if op has low prec,
295 if op was missing then prec[op]==0
296 */
297 op = st->op;
298 if (prec[op] <= minprec)
299 return s;
300 left = st->r;
301 s = evalbinop(st, s, prec[op], d);
302 if (binop(st, op, left))
303 return "";
304 }
305}
306
307static const char *evalexpr(struct st *st, const char *s, int d)
308{
309 uint64_t a, b;
310 if (--d < 0)
311 return "";
312 s = evalbinop(st, s, 0, d);
313 if (*s != '?')
314 return s;
315 a = st->r;
316 s = evalexpr(st, s+1, d);
317 if (*s != ':')
318 return "";
319 b = st->r;
320 s = evalexpr(st, s+1, d);
321 st->r = a ? b : st->r;
322 return s;
323}
324
325uint64_t msgcat_plural_eval(const char *s, uint64_t n)
326{
327 struct st st;
328 st.n = n;
329 s = evalexpr(&st, s, 100);
330 return *s == ';' ? st.r : UINT64_MAX;
331}
const char * msgcat_ngettext(const msgcat_t *p, const char *msgid1, const char *msgid2, uint64_t n)
Return a translation, if present, from the given message catalog.
Definition msgcat.c:101
void msgcat_init(msgcat_t *p, const void *map, size_t map_size)
Initialize a msgcat_t, given the contents and content-length of a .mo file.
Definition msgcat.c:59
uint32_t msgcat_nstringsFromHeader(const char buf[12])
Return the number of strings in a message catalog, given its first 12 bytes.
Definition msgcat.c:169
static const double c[]
Definition rng.c:264
static const double d[]
Definition rng.c:273
Definition msgcat.c:199