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