Translated using Weblate (Chinese (Simplified))
[oweals/luci.git] / modules / luci-base / src / po2lmo.c
1 /*
2  * lmo - Lua Machine Objects - PO to LMO conversion tool
3  *
4  *   Copyright (C) 2009-2012 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #include "template_lmo.h"
20
21 static void die(const char *msg)
22 {
23         fprintf(stderr, "Error: %s\n", msg);
24         exit(1);
25 }
26
27 static void usage(const char *name)
28 {
29         fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
30         exit(1);
31 }
32
33 static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
34 {
35         int i;
36
37         if (fwrite(ptr, size, nmemb, stream) == 0)
38                 die("Failed to write");
39
40         for (i = 0; i < ((4 - (size % 4)) % 4); i++)
41                 if (fputc(0, stream))
42                         die("Failed to write");
43 }
44
45 static int extract_string(const char *src, char *dest, int len)
46 {
47         int pos = 0;
48         int esc = 0;
49         int off = -1;
50
51         if (*src == '#')
52                 return -1;
53
54         for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
55         {
56                 if( (off == -1) && (src[pos] == '"') )
57                 {
58                         off = pos + 1;
59                 }
60                 else if( off >= 0 )
61                 {
62                         if( esc == 1 )
63                         {
64                                 switch (src[pos])
65                                 {
66                                 case '"':
67                                 case '\\':
68                                         off++;
69                                         break;
70                                 }
71                                 dest[pos-off] = src[pos];
72                                 esc = 0;
73                         }
74                         else if( src[pos] == '\\' )
75                         {
76                                 dest[pos-off] = src[pos];
77                                 esc = 1;
78                         }
79                         else if( src[pos] != '"' )
80                         {
81                                 dest[pos-off] = src[pos];
82                         }
83                         else
84                         {
85                                 dest[pos-off] = '\0';
86                                 break;
87                         }
88                 }
89         }
90
91         return (off > -1) ? strlen(dest) : -1;
92 }
93
94 static int cmp_index(const void *a, const void *b)
95 {
96         uint32_t x = ((const lmo_entry_t *)a)->key_id;
97         uint32_t y = ((const lmo_entry_t *)b)->key_id;
98
99         if (x < y)
100                 return -1;
101         else if (x > y)
102                 return 1;
103
104         return 0;
105 }
106
107 static void print_uint32(uint32_t x, FILE *out)
108 {
109         uint32_t y = htonl(x);
110         print(&y, sizeof(uint32_t), 1, out);
111 }
112
113 static void print_index(void *array, int n, FILE *out)
114 {
115         lmo_entry_t *e;
116
117         qsort(array, n, sizeof(*e), cmp_index);
118
119         for (e = array; n > 0; n--, e++)
120         {
121                 print_uint32(e->key_id, out);
122                 print_uint32(e->val_id, out);
123                 print_uint32(e->offset, out);
124                 print_uint32(e->length, out);
125         }
126 }
127
128 enum fieldtype {
129         UNSPEC        = 0,
130         MSG_CTXT      = 1,
131         MSG_ID        = 2,
132         MSG_ID_PLURAL = 3,
133         MSG_STR       = 4
134 };
135
136 struct msg {
137         int plural_num;
138         char *ctxt;
139         char *id;
140         char *id_plural;
141         char *val[10];
142         size_t len;
143         char **cur;
144 };
145
146 static void *array = NULL;
147 static int n_entries = 0;
148 static size_t offset = 0;
149
150 static void print_msg(struct msg *msg, FILE *out)
151 {
152         char key[4096], *field, *p;
153         uint32_t key_id, val_id;
154         lmo_entry_t *entry;
155         size_t len;
156         int esc, i;
157
158         if (msg->id && msg->val[0]) {
159                 for (i = 0; i <= msg->plural_num; i++) {
160                         if (!msg->val[i])
161                                 continue;
162
163                         if (msg->ctxt && msg->id_plural)
164                                 snprintf(key, sizeof(key), "%s\1%s\2%d", msg->ctxt, msg->id, i);
165                         else if (msg->ctxt)
166                                 snprintf(key, sizeof(key), "%s\1%s", msg->ctxt, msg->id);
167                         else if (msg->id_plural)
168                                 snprintf(key, sizeof(key), "%s\2%d", msg->id, i);
169                         else
170                                 snprintf(key, sizeof(key), "%s", msg->id);
171
172                         key_id = sfh_hash(key, strlen(key));
173                         val_id = sfh_hash(msg->val[i], strlen(msg->val[i]));
174
175                         if (key_id != val_id) {
176                                 n_entries++;
177                                 array = realloc(array, n_entries * sizeof(lmo_entry_t));
178
179                                 if (!array)
180                                         die("Out of memory");
181
182                                 entry = (lmo_entry_t *)array + n_entries - 1;
183                                 entry->key_id = key_id;
184                                 entry->val_id = msg->plural_num + 1;
185                                 entry->offset = offset;
186                                 entry->length = strlen(msg->val[i]);
187
188                                 len = entry->length + ((4 - (entry->length % 4)) % 4);
189
190                                 print(msg->val[i], entry->length, 1, out);
191                                 offset += len;
192                         }
193                 }
194         }
195         else if (msg->val[0]) {
196                 for (field = msg->val[0], p = field, esc = 0; *p; p++) {
197                         if (esc) {
198                                 if (*p == 'n') {
199                                         p[-1] = 0;
200
201                                         if (!strncasecmp(field, "Plural-Forms: ", 14)) {
202                                                 field += 14;
203
204                                                 n_entries++;
205                                                 array = realloc(array, n_entries * sizeof(lmo_entry_t));
206
207                                                 if (!array)
208                                                         die("Out of memory");
209
210                                                 entry = (lmo_entry_t *)array + n_entries - 1;
211                                                 entry->key_id = 0;
212                                                 entry->val_id = 0;
213                                                 entry->offset = offset;
214                                                 entry->length = strlen(field);
215
216                                                 len = entry->length + ((4 - (entry->length % 4)) % 4);
217
218                                                 print(field, entry->length, 1, out);
219                                                 offset += len;
220                                                 break;
221                                         }
222
223                                         field = p + 1;
224                                 }
225
226                                 esc = 0;
227                         }
228                         else if (*p == '\\') {
229                                 esc = 1;
230                         }
231                 }
232         }
233
234         free(msg->ctxt);
235         free(msg->id);
236         free(msg->id_plural);
237
238         for (i = 0; i < sizeof(msg->val) / sizeof(msg->val[0]); i++)
239                 free(msg->val[i]);
240
241         memset(msg, 0, sizeof(*msg));
242 }
243
244 int main(int argc, char *argv[])
245 {
246         struct msg msg = { .plural_num = -1 };
247         char line[4096], tmp[4096];
248         FILE *in, *out;
249         ssize_t len;
250         int eof;
251
252         if ((argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL))
253                 usage(argv[0]);
254
255         while (1) {
256                 line[0] = 0;
257                 eof = !fgets(line, sizeof(line), in);
258
259                 if (!strncmp(line, "msgctxt \"", 9)) {
260                         if (msg.id || msg.val[0])
261                                 print_msg(&msg, out);
262                         else
263                                 free(msg.ctxt);
264
265                         msg.ctxt = NULL;
266                         msg.cur = &msg.ctxt;
267                         msg.len = 0;
268                 }
269                 else if (eof || !strncmp(line, "msgid \"", 7)) {
270                         if (msg.id || msg.val[0])
271                                 print_msg(&msg, out);
272                         else
273                                 free(msg.id);
274
275                         msg.id = NULL;
276                         msg.cur = &msg.id;
277                         msg.len = 0;
278                 }
279                 else if (!strncmp(line, "msgid_plural \"", 14)) {
280                         free(msg.id_plural);
281                         msg.id_plural = NULL;
282                         msg.cur = &msg.id_plural;
283                         msg.len = 0;
284                 }
285                 else if (!strncmp(line, "msgstr \"", 8) || !strncmp(line, "msgstr[", 7)) {
286                         if (line[6] == '[')
287                                 msg.plural_num = strtoul(line + 7, NULL, 10);
288                         else
289                                 msg.plural_num = 0;
290
291                         if (msg.plural_num >= 10)
292                                 die("Too many plural forms");
293
294                         free(msg.val[msg.plural_num]);
295                         msg.val[msg.plural_num] = NULL;
296                         msg.cur = &msg.val[msg.plural_num];
297                         msg.len = 0;
298                 }
299
300                 if (eof)
301                         break;
302
303                 if (msg.cur) {
304                         len = extract_string(line, tmp, sizeof(tmp));
305
306                         if (len > 0) {
307                                 *msg.cur = realloc(*msg.cur, msg.len + len + 1);
308
309                                 if (!*msg.cur)
310                                         die("Out of memory");
311
312                                 memcpy(*msg.cur + msg.len, tmp, len + 1);
313                                 msg.len += len;
314                         }
315                 }
316         }
317
318         print_index(array, n_entries, out);
319
320         if (offset > 0) {
321                 print_uint32(offset, out);
322                 fsync(fileno(out));
323                 fclose(out);
324         }
325         else {
326                 fclose(out);
327                 unlink(argv[2]);
328         }
329
330         fclose(in);
331         return(0);
332 }