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