Cuda use streams and async to avoid busywaiting
[blender.git] / intern / locale / msgfmt.cc
1 // Written by Sergey Sharybin <sergey.vfx@gmail.com>
2 // Added support for contexts
3 //
4 // Based on Python script msgfmt.py from Python source
5 // code tree, which was written by Written by
6 // Martin v. Löwis <loewis@informatik.hu-berlin.de>
7 //
8 // Generate binary message catalog from textual translation description.
9 //
10 // This program converts a textual Uniforum-style message catalog (.po file) into
11 // a binary GNU catalog (.mo file).  This is essentially the same function as the
12 // GNU msgfmt program, however, it is a simpler implementation.
13 //
14 // Usage: msgfmt input.po output.po
15
16 #include <algorithm>
17 #include <cctype>
18 #include <fstream>
19 #include <functional>
20 #include <iostream>
21 #include <map>
22 #include <stdlib.h>
23 #include <string>
24 #include <vector>
25
26 namespace {
27
28 std::map<std::string, std::string> MESSAGES;
29
30 bool starts_with(const std::string &string,
31                  const std::string &prefix) {
32   return prefix.size() <= string.size() &&
33          string.compare(0, prefix.size(), prefix) == 0;
34 }
35
36 std::string ltrim(const std::string &s) {
37   std::string result = s;
38   result.erase(result.begin(),
39                std::find_if(result.begin(),
40                             result.end(),
41                             std::not1(std::ptr_fun<int, int>(std::isspace))));
42   return result;
43 }
44
45 std::string rtrim(const std::string &s) {
46   std::string result = s;
47   result.erase(
48     std::find_if(result.rbegin(),
49                  result.rend(),
50                  std::not1(std::ptr_fun<int, int>(std::isspace))).base(),
51     result.end());
52   return result;
53 }
54
55 std::string trim(const std::string &s) {
56   return ltrim(rtrim(s));
57 }
58
59 std::string unescape(const std::string &s) {
60   std::string result;
61   std::string::const_iterator it = s.begin();
62   while (it != s.end()) {
63     char current_char = *it++;
64     if (current_char == '\\' && it != s.end()) {
65       char next_char = *it++;
66       if (next_char == '\\') {
67         current_char = '\\';
68       } else if (next_char == 'n') {
69         current_char = '\n';
70       } else if (next_char == 't') {
71         current_char = '\t';
72       } else {
73         current_char = next_char;
74       }
75     }
76     result += current_char;
77   }
78
79   if (result[0] == '"' && result[result.size() - 1] == '"') {
80     result = result.substr(1, result.size() - 2);
81   }
82
83   return result;
84 }
85
86 // Add a non-fuzzy translation to the dictionary.
87 void add(const std::string &msgctxt,
88          const std::string &msgid,
89          const std::string &msgstr,
90          bool fuzzy) {
91   if (fuzzy == false && msgstr.empty() == false) {
92     if (msgctxt.empty()) {
93       MESSAGES[msgid] = msgstr;
94     } else {
95       MESSAGES[msgctxt + (char)0x04 + msgid] = msgstr;
96     }
97   }
98 }
99
100 template<typename TKey, typename TValue>
101 void get_keys(std::map<TKey, TValue> map,
102               std::vector<TKey> *keys) {
103   for (typename std::map<TKey, TValue>::iterator it = map.begin();
104       it != map.end();
105       it++) {
106     keys->push_back(it->first);
107   }
108 }
109
110 std::string intToBytes(int value) {
111   std::string result;
112   for (unsigned int i = 0; i < sizeof(value); i++) {
113     result += (unsigned char) ((value >> (i * 8)) & 0xff);
114   }
115   return result;
116 }
117
118 typedef enum {
119   SECTION_NONE = 0,
120   SECTION_CTX  = 1,
121   SECTION_ID   = 2,
122   SECTION_STR  = 3
123 } eSectionType;
124
125 struct Offset {
126   unsigned int o1, l1, o2, l2;
127 };
128
129 // Return the generated output.
130 std::string generate(void) {
131   // The keys are sorted in the .mo file
132   std::vector<std::string> keys;
133
134   // Get list of sorted keys.
135   get_keys(MESSAGES, &keys);
136   std::sort(keys.begin(), keys.end());
137
138   std::vector<Offset> offsets;
139   std::string ids = "", strs = "";
140   for (std::vector<std::string>::iterator it = keys.begin();
141        it != keys.end();
142        it++) {
143     std::string &id = *it;
144     // For each string, we need size and file offset.  Each string is NUL
145     // terminated; the NUL does not count into the size.
146     Offset offset = {(unsigned int) ids.size(),
147                      (unsigned int) id.size(),
148                      (unsigned int) strs.size(),
149                      (unsigned int) MESSAGES[id].size()};
150     offsets.push_back(offset);
151     ids += id + '\0';
152     strs += MESSAGES[id] + '\0';
153   }
154
155   // The header is 7 32-bit unsigned integers.  We don't use hash tables, so
156   // the keys start right after the index tables.
157   // translated string.
158   int keystart = 7 * 4 + 16 * keys.size();
159   // and the values start after the keys
160   int valuestart = keystart + ids.size();
161   std::vector<int> koffsets;
162   std::vector<int> voffsets;
163   // The string table first has the list of keys, then the list of values.
164   // Each entry has first the size of the string, then the file offset.
165   for (std::vector<Offset>::iterator it = offsets.begin();
166        it != offsets.end();
167        it++) {
168     Offset &offset = *it;
169     koffsets.push_back(offset.l1);
170     koffsets.push_back(offset.o1 + keystart);
171     voffsets.push_back(offset.l2);
172     voffsets.push_back(offset.o2 + valuestart);
173   }
174
175   std::vector<int> all_offsets;
176   all_offsets.reserve(koffsets.size() + voffsets.size());
177   all_offsets.insert(all_offsets.end(), koffsets.begin(), koffsets.end());
178   all_offsets.insert(all_offsets.end(), voffsets.begin(), voffsets.end());
179
180   std::string output = "";
181   output += intToBytes(0x950412de);  // Magic
182   output += intToBytes(0x0);  // Version
183   output += intToBytes(keys.size());  // # of entries
184   output += intToBytes(7 * 4);  // start of key index
185   output += intToBytes(7 * 4 + keys.size() * 8);  // start of value index
186   output += intToBytes(0);  // Size of hash table
187   output += intToBytes(0);  // Offset of hash table
188
189   for (std::vector<int>::iterator it = all_offsets.begin();
190        it != all_offsets.end();
191        it++) {
192     int offset = *it;
193     output += intToBytes(offset);
194   }
195
196   output += ids;
197   output += strs;
198
199   return output;
200 }
201
202 void make(const char *input_file_name,
203           const char *output_file_name) {
204   std::map<std::string, std::string> messages;
205
206   // Start off assuming Latin-1, so everything decodes without failure,
207   // until we know the exact encoding.
208   // TODO(sergey): Support encoding.
209   // const char *encoding = "latin-1";
210
211   eSectionType section = SECTION_NONE;
212   bool fuzzy = false;
213   bool is_plural = false;
214   std::string msgctxt, msgid, msgstr;
215
216   std::ifstream input_file_stream(input_file_name);
217
218   // Parse the catalog.
219   int lno = 0;
220   for (std::string l; getline(input_file_stream, l); ) {
221     lno++;
222     // If we get a comment line after a msgstr, this is a new entry.
223     if (l[0] == '#' && section == SECTION_STR) {
224       add(msgctxt, msgid, msgstr, fuzzy);
225       section = SECTION_NONE;
226       msgctxt = "";
227       fuzzy = false;
228     }
229     // Record a fuzzy mark.
230     if (starts_with(l, "#,") && l.find("fuzzy") != std::string::npos) {
231       fuzzy = 1;
232     }
233     // Skip comments
234     if (l[0] == '#') {
235       continue;
236     }
237     // Now we are in a msgid section, output previous section.
238     if (starts_with(l, "msgctxt")) {
239       if (section == SECTION_STR) {
240         add(msgctxt, msgid, msgstr, fuzzy);
241       }
242       section = SECTION_CTX;
243       l = l.substr(7, l.size() - 7);
244       msgctxt = msgid = msgstr = "";
245     }
246     else if (starts_with(l, "msgid") && !starts_with(l, "msgid_plural")) {
247       if (section == SECTION_STR) {
248         add(msgctxt, msgid, msgstr, fuzzy);
249         msgctxt = "";
250         if (msgid == "") {
251 #if 0
252           // See whether there is an encoding declaration.
253           p = HeaderParser();
254           charset = p.parsestr(msgstr.decode(encoding)).get_content_charset();
255           if (charset) {
256             encoding = charset;
257           }
258 #else
259           // Not ported to C++ yet.
260           std::cerr << "Encoding declarations are not supported yet.\n"
261                     << std::endl;
262           abort();
263 #endif
264         }
265       }
266       section = SECTION_ID;
267       l = l.substr(5, l.size() - 5);
268       msgid = msgstr = "";
269       is_plural = false;
270     } else if (starts_with(l, "msgid_plural")) {
271       // This is a message with plural forms.
272       if (section != SECTION_ID) {
273         std::cerr << "msgid_plural not preceeded by msgid on"
274                   << input_file_name << ":"
275                   << lno
276                   << std::endl;
277         abort();
278       }
279       l = l.substr(12, l.size() - 12);
280       msgid += '\0';  // separator of singular and plural
281       is_plural = true;
282     } else if (starts_with(l, "msgstr")) {
283       // Now we are in a msgstr section
284       section = SECTION_STR;
285       if (starts_with(l, "msgstr[")) {
286         if (is_plural == false) {
287           std::cerr << "plural without msgid_plural on "
288                     << input_file_name << ":"
289                     << lno
290                     << std::endl;
291           abort();
292         }
293         int bracket_position = l.find(']');
294         if (bracket_position == std::string::npos) {
295           std::cerr << "Syntax error on "
296                     << input_file_name << ":"
297                     << lno
298                     << std::endl;
299           abort();
300         }
301         l = l.substr(bracket_position, l.size() - bracket_position);
302         if (msgstr != "") {
303           msgstr += '\0';  // Separator of the various plural forms;
304         }
305       } else {
306         if (is_plural) {
307           std::cerr << "indexed msgstr required for plural on "
308                     << input_file_name << ":"
309                     << lno
310                     << std::endl;
311           abort();
312         }
313         l = l.substr(6, l.size() - 6);
314       }
315     }
316     // Skip empty lines.
317     l = trim(l);
318     if (l.empty()) {
319       continue;
320     }
321     l = unescape(l);
322     if (section == SECTION_CTX) {
323       // TODO(sergey): Support encoding.
324       // msgid += l.encode(encoding);
325       msgctxt += l;
326     }
327     else if (section == SECTION_ID) {
328       // TODO(sergey): Support encoding.
329       // msgid += l.encode(encoding);
330       msgid += l;
331     } else if (section == SECTION_STR) {
332       // TODO(sergey): Support encoding.
333       // msgstr += l.encode(encoding)
334       msgstr += l;
335     } else {
336       std::cerr << "Syntax error on "
337                 << input_file_name << ":"
338                 << lno
339                 << std::endl;
340       abort();
341     }
342     // Add last entry
343     if (section == SECTION_STR) {
344       add(msgctxt, msgid, msgstr, fuzzy);
345     }
346   }
347
348   // Compute output
349   std::string output = generate();
350
351   std::ofstream output_file_stream(output_file_name,
352     std::ios::out | std::ios::binary);
353   output_file_stream << output;
354 }
355
356 }  // namespace
357
358 int main(int argc, char **argv) {
359   if (argc != 3) {
360     printf("Usage: %s <input.po> <output.mo>\n", argv[0]);
361     return EXIT_FAILURE;
362   }
363   const char *input_file = argv[1];
364   const char *output_file = argv[2];
365
366   make(input_file, output_file);
367
368   return EXIT_SUCCESS;
369 }