1 /***************************************************************************
2 * Copyright (C) 2012~2013 by Yichao Yu *
3 * yyc1992@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <stdint.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <fcitx/fcitx.h>
29 #include <fcitx-utils/utils.h>
30 #include <fcitx-utils/log.h>
31 #include <fcitx-utils/utarray.h>
32 #include "config.h"
33
34 #ifndef _FCITX_DISABLE_GETTEXT
35
36 #include <gettext-po.h>
37
38 static void
_xerror_handler(int severity,po_message_t message,const char * filename,size_t lineno,size_t column,int multiline_p,const char * message_text)39 _xerror_handler(int severity, po_message_t message, const char *filename,
40 size_t lineno, size_t column, int multiline_p,
41 const char *message_text)
42 {
43 FCITX_UNUSED(message);
44 FCITX_UNUSED(filename);
45 FCITX_UNUSED(lineno);
46 FCITX_UNUSED(column);
47 FCITX_UNUSED(multiline_p);
48 FCITX_UNUSED(message_text);
49 if (severity == PO_SEVERITY_FATAL_ERROR) {
50 exit(1);
51 }
52 }
53
54 static void
_xerror2_handler(int severity,po_message_t message1,const char * filename1,size_t lineno1,size_t column1,int multiline_p1,const char * message_text1,po_message_t message2,const char * filename2,size_t lineno2,size_t column2,int multiline_p2,const char * message_text2)55 _xerror2_handler(int severity, po_message_t message1, const char *filename1,
56 size_t lineno1, size_t column1, int multiline_p1,
57 const char *message_text1, po_message_t message2,
58 const char *filename2, size_t lineno2, size_t column2,
59 int multiline_p2, const char *message_text2)
60 {
61 FCITX_UNUSED(message1);
62 FCITX_UNUSED(filename1);
63 FCITX_UNUSED(lineno1);
64 FCITX_UNUSED(column1);
65 FCITX_UNUSED(multiline_p1);
66 FCITX_UNUSED(message_text1);
67 FCITX_UNUSED(message2);
68 FCITX_UNUSED(filename2);
69 FCITX_UNUSED(lineno2);
70 FCITX_UNUSED(column2);
71 FCITX_UNUSED(multiline_p2);
72 FCITX_UNUSED(message_text2);
73 if (severity == PO_SEVERITY_FATAL_ERROR) {
74 exit(1);
75 }
76 }
77 static const struct po_xerror_handler handler = {
78 .xerror = _xerror_handler,
79 .xerror2 = _xerror2_handler
80 };
81
82 #endif
83
84 static inline void
encode_char(char * out,char c)85 encode_char(char *out, char c)
86 {
87 unsigned char *uout = (unsigned char*)out;
88 unsigned char uc = (unsigned char)c;
89 uout[0] = 'a' + (uc & 0x0f);
90 uout[1] = 'a' + (uc >> 4);
91 }
92
93 static void
encode_string(const char * in,char * out,size_t l)94 encode_string(const char *in, char *out, size_t l)
95 {
96 size_t i;
97 for (i = 0;i < l;i++) {
98 encode_char(out + i * 2, in[i]);
99 }
100 out[l * 2] = '\0';
101 }
102
103 #ifndef _FCITX_DISABLE_GETTEXT
104
105 static int
lang_to_prefix(const char * lang,char ** out)106 lang_to_prefix(const char *lang, char **out)
107 {
108 int lang_len = strlen(lang);
109 char *res = malloc(lang_len * 2 + 4);
110 *out = res;
111 int len = lang_len * 2;
112 encode_string(lang, res, lang_len);
113 strcpy(res + len, "___");
114 return len + 3;
115 }
116
117 static void
parse_po(const char * lang,const char * fname)118 parse_po(const char *lang, const char *fname)
119 {
120 po_file_t po_file = po_file_read(fname, &handler);
121 po_message_iterator_t msg_iter = po_message_iterator(po_file, NULL);
122 po_message_t msg;
123 char *buff = NULL;
124 size_t buff_len = 0;
125 int prefix_len = lang_to_prefix(lang, &buff);
126
127 while ((msg = po_next_message(msg_iter))) {
128 const char *msg_id = po_message_msgid(msg);
129 const char *msg_str = po_message_msgstr(msg);
130 int fuzzy = po_message_is_fuzzy(msg);
131 int obsolete = po_message_is_obsolete(msg);
132 if (fuzzy || obsolete || !msg_id || !*msg_id || !msg_str || !*msg_str)
133 continue;
134 int id_len = strlen(msg_id);
135 int str_len = strlen(msg_str);
136 unsigned int len = (id_len + str_len) * 2 + prefix_len + 2;
137 if (len > buff_len) {
138 buff_len = len;
139 buff = realloc(buff, buff_len);
140 }
141 char *p = buff + prefix_len;
142 encode_string(msg_id, p, id_len);
143 p += id_len * 2;
144 p[0] = '=';
145 encode_string(msg_str, p + 1, str_len);
146 fwrite(buff, 1, len - 1, stdout);
147 fwrite("\n", 1, 1, stdout);
148 }
149
150 fcitx_utils_free(buff);
151 po_message_iterator_free(msg_iter);
152 po_file_free(po_file);
153 }
154
155 static void
fix_header(const char * header,const char * fname)156 fix_header(const char *header, const char *fname)
157 {
158 po_file_t po_file = po_file_read(fname, &handler);
159 po_message_iterator_t msg_iter = po_message_iterator(po_file, NULL);
160 po_message_t msg;
161
162 while ((msg = po_next_message(msg_iter))) {
163 const char *msg_id = po_message_msgid(msg);
164 int fuzzy = po_message_is_fuzzy(msg);
165 if (*msg_id || !fuzzy)
166 continue;
167 po_message_set_msgstr(msg, header);
168 }
169
170 po_message_iterator_free(msg_iter);
171 po_file_write(po_file, "-", &handler);
172 po_file_free(po_file);
173 }
174
175 #else
176
177 static void
parse_po(const char * lang,const char * fname)178 parse_po(const char *lang, const char *fname)
179 {
180 FCITX_UNUSED(lang);
181 FCITX_UNUSED(fname);
182 }
183
184 static void
fix_header(const char * header,const char * fname)185 fix_header(const char *header, const char *fname)
186 {
187 FCITX_UNUSED(header);
188 FCITX_UNUSED(fname);
189 }
190
191 #endif
192
193 static void
encode_stdin()194 encode_stdin()
195 {
196 #define ENCODE_BUF_SIZE (1024)
197 char in_buff[ENCODE_BUF_SIZE];
198 char out_buff[ENCODE_BUF_SIZE * 2];
199 size_t len;
200 while ((len = fread(in_buff, 1, ENCODE_BUF_SIZE, stdin))) {
201 encode_string(in_buff, out_buff, len);
202 fwrite(out_buff, 2, len, stdout);
203 }
204 }
205
206 static inline int
check_char(char c)207 check_char(char c)
208 {
209 return c >= 'a' && c < 'a' + 0x10;
210 }
211
212 static inline size_t
check_buff(char * buff,size_t len)213 check_buff(char *buff, size_t len)
214 {
215 size_t i;
216 for (i = 0;i < len;i++) {
217 if (check_char(buff[i]))
218 continue;
219 len--;
220 if (len <= i)
221 continue;
222 memmove(buff + i, buff + i + 1, len - i);
223 i--;
224 }
225 return len;
226 }
227
228 static inline char
hex_to_char(const char c[2])229 hex_to_char(const char c[2])
230 {
231 return (c[0] - 'a') + ((c[1] - 'a') << 4);
232 }
233
234 static inline void
decode_stdin()235 decode_stdin()
236 {
237 #define DECODE_BUF_SIZE (1024)
238 char out_buff[DECODE_BUF_SIZE];
239 char in_buff[DECODE_BUF_SIZE * 2];
240 size_t len;
241 size_t left = 0;
242 size_t count = 0;
243 size_t i;
244 while ((len = fread(in_buff + left, 1, DECODE_BUF_SIZE * 2, stdin))) {
245 len = check_buff(in_buff, len + left);
246 count = len / 2;
247 for (i = 0;i < count;i++) {
248 out_buff[i] = hex_to_char(in_buff + i * 2);
249 }
250 fwrite(out_buff, count, 1, stdout);
251 if ((left = len % 2)) {
252 in_buff[0] = in_buff[len - 1];
253 }
254 }
255 }
256
257 static inline const char*
std_filename(const char * fname)258 std_filename(const char *fname)
259 {
260 return (fname && *fname) ? fname : "-";
261 }
262
263 int
main(int argc,char ** argv)264 main(int argc, char **argv)
265 {
266 if (argc <= 1)
267 return 1;
268 const char *action = argv[1];
269 if (strcmp(action, "--parse-po") == 0) {
270 const char *lang = argv[2];
271 if (!lang)
272 return 1;
273 parse_po(lang, std_filename(argv[3]));
274 } else if (strcmp(action, "--encode") == 0) {
275 encode_stdin();
276 } else if (strcmp(action, "--decode") == 0) {
277 decode_stdin();
278 } else if (strcmp(action, "--fix-header") == 0) {
279 const char *header = argv[2];
280 if (!header)
281 return 1;
282 fix_header(header, std_filename(argv[3]));
283 } else if (strcmp(action, "--gettext-support") == 0) {
284 #ifndef _FCITX_DISABLE_GETTEXT
285 return 0;
286 #else
287 return 1;
288 #endif
289 } else {
290 return 1;
291 }
292 return 0;
293 }
294