]> git.lyx.org Git - lyx.git/blob - src/support/Messages.cpp
Prevent zombie processes (#8774)
[lyx.git] / src / support / Messages.cpp
1 /* \file Messages.cpp
2  * This file is part of LyX, the document processor.
3  * Licence details can be found in the file COPYING.
4  *
5  * \author Lars Gullik Bjønnes
6  *
7  * Full author contact details are available in file CREDITS.
8  */
9
10 /*
11   This is a limited parser for gettext's po files. Several features are
12   not handled for now:
13    * encoding is supposed to be UTF-8 (the charset parameter is not honored)
14    * context is not handled (implemented differently in LyX)
15    * plural forms not implemented (not used for now in LyX).
16    * The byte endianness of the machine on which the .mo file have been
17      built is expected to be the same as the one of the machine where this
18      code is run.
19
20   The data is loaded in a std::map object for simplicity.
21  */
22
23 /*
24   Format of a MO file. Source: http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
25
26              byte
27                   +------------------------------------------+
28                0  | magic number = 0x950412de                |
29                   |                                          |
30                4  | file format revision = 0                 |
31                   |                                          |
32                8  | number of strings                        |  == N
33                   |                                          |
34               12  | offset of table with original strings    |  == O
35                   |                                          |
36               16  | offset of table with translation strings |  == T
37                   |                                          |
38               20  | size of hashing table                    |  == S
39                   |                                          |
40               24  | offset of hashing table                  |  == H
41                   |                                          |
42                   .                                          .
43                   .    (possibly more entries later)         .
44                   .                                          .
45                   |                                          |
46                O  | length & offset 0th string  ----------------.
47            O + 8  | length & offset 1st string  ------------------.
48                    ...                                    ...   | |
49      O + ((N-1)*8)| length & offset (N-1)th string           |  | |
50                   |                                          |  | |
51                T  | length & offset 0th translation  ---------------.
52            T + 8  | length & offset 1st translation  -----------------.
53                    ...                                    ...   | | | |
54      T + ((N-1)*8)| length & offset (N-1)th translation      |  | | | |
55                   |                                          |  | | | |
56                H  | start hash table                         |  | | | |
57                    ...                                    ...   | | | |
58        H + S * 4  | end hash table                           |  | | | |
59                   |                                          |  | | | |
60                   | NUL terminated 0th string  <----------------' | | |
61                   |                                          |    | | |
62                   | NUL terminated 1st string  <------------------' | |
63                   |                                          |      | |
64                    ...                                    ...       | |
65                   |                                          |      | |
66                   | NUL terminated 0th translation  <---------------' |
67                   |                                          |        |
68                   | NUL terminated 1st translation  <-----------------'
69                   |                                          |
70                    ...                                    ...
71                   |                                          |
72                   +------------------------------------------+
73
74  */
75
76 #include <config.h>
77
78 #include "support/Messages.h"
79
80 #include "support/debug.h"
81 #include "support/docstring.h"
82 #include "support/lstrings.h"
83 #include "support/Package.h"
84 #include "support/unicode.h"
85
86 #include "support/lassert.h"
87
88 #include <boost/cstdint.hpp>
89
90 #include <cerrno>
91 #include <fstream>
92
93 #ifdef HAVE_SYS_STAT_H
94 # include <sys/stat.h>
95 #endif
96
97 using namespace std;
98 using boost::uint32_t;
99
100 namespace lyx {
101
102 void cleanTranslation(docstring & trans)
103 {
104         /*
105           Some english words have different translations, depending on
106           context. In these cases the original string is augmented by
107           context information (e.g. "To:[[as in 'From page x to page
108           y']]" and "To:[[as in 'From format x to format y']]". Also, 
109           when placeholders are used, the context can indicate what will
110           be substituted for the placeholder (e.g. "%1$s[[date]], %1$s
111           [[time]]). This means that we need to filter out everything 
112           in double square brackets at the end of the string, otherwise
113           the user sees bogus messages. If we are unable to honour the
114           request we just return what we got in.
115         */
116         static docstring const ctx_start = from_ascii("[[");
117         static docstring const ctx_end = from_ascii("]]");
118         while (true) {
119                 size_t const pos1 = trans.find(ctx_start);
120                 if (pos1 != docstring::npos) {
121                         size_t const pos2 = trans.find(ctx_end, pos1);
122                         if (pos2 != docstring::npos) {
123                                 trans.erase(pos1, pos2 - pos1 + 2);
124                                 continue;
125                         }
126                 }
127                 break;
128         }
129 }
130
131 } // lyx
132
133
134 #ifdef ENABLE_NLS
135
136 using namespace lyx::support;
137
138 namespace lyx {
139
140 std::string Messages::gui_lang_;
141
142
143 Messages::Messages(string const & l)
144         : lang_(l)
145 {
146         // strip off any encoding suffix, i.e., assume 8-bit po files
147         size_t i = lang_.find(".");
148         lang_ = lang_.substr(0, i);
149         LYXERR(Debug::LOCALE, "language(" << lang_ << ")");
150
151         readMoFile();
152 }
153
154
155 namespace {
156
157 // Find the code we have for a given language code. Return empty if not found.
158 string realCode(string code)
159 {
160         // this loops at most twice
161         while (true) {
162                 if (package().messages_file(code).isReadableFile())
163                         return code;
164                 if (contains(code, '_'))
165                         code = token(code, '_', 0);
166                 else
167                         break;
168         }
169         return string();
170 }
171 }
172
173
174 bool Messages::available(string const & c)
175 {
176         return !realCode(c).empty();
177 }
178
179
180 string Messages::language() const
181 {
182         return realCode(lang_);
183 }
184
185
186 struct MoHeader
187 {
188         // magic number = 0x950412de
189         uint32_t magic;
190         // file format revision = 0
191         uint32_t rev;
192         // number of strings
193         uint32_t N;
194         // offset of table with original strings
195         uint32_t O;
196         // offset of table with translation strings
197         uint32_t T;
198         // there is a hashing table afterwrds, but we ignore it
199 };
200
201
202 struct StringTable
203 {
204         // string length
205         uint32_t length;
206         // string offset
207         uint32_t offset;
208 };
209
210
211 bool Messages::readMoFile()
212 {
213         // FIXME:remove
214         if (lang_.empty()) {
215                 LYXERR0("No language given, nothing to load.");
216                 return false;
217         }
218
219         string const code = realCode(lang_);
220         if (code.empty()) {
221                 LYXERR0("Cannot find translation for language " << lang_);
222                 return false;
223         }
224
225         string const filen = package().messages_file(code).toSafeFilesystemEncoding();
226
227         // get file size
228         struct stat buf;
229         if (stat(filen.c_str(), &buf)) {
230                 LYXERR0("Cannot get information for file " << filen);
231                 return false;
232         }
233
234         vector<char> moData(buf.st_size);
235
236         ifstream is(filen.c_str(), ios::in | ios::binary);
237         if (!is.read(&moData[0], buf.st_size)) {
238                 LYXERR0("Cannot read file " << filen);
239                 return false;
240         }
241
242         MoHeader const * header = reinterpret_cast<MoHeader const *>(&moData[0]);
243         if (header->magic != 0x950412de) {
244                 LYXERR0("Wrong magic number for file " << filen
245                         << ".\nExpected 0x950412de, got " << std::hex << header->magic);
246                 return false;
247         }
248
249         StringTable const * orig = reinterpret_cast<StringTable const *>(&moData[0] + header->O);
250         StringTable const * trans = reinterpret_cast<StringTable const *>(&moData[0] + header->T);
251         // First the header
252         string const info = string(&moData[0] + trans[0].offset, trans[0].length);
253         size_t pos = info.find("charset=");
254         if (pos != string::npos) {
255                 pos += 8;
256                 string charset;
257                 size_t pos2 = info.find("\n", pos);
258                 if (pos2 == string::npos)
259                         charset = info.substr(pos);
260                 else
261                         charset = info.substr(pos, pos2 - pos);
262                 charset = ascii_lowercase(trim(charset));
263                 if (charset != "utf-8") {
264                         LYXERR0("Wrong encoding " << charset << " for file " << filen);
265                         return false;
266                 }
267         } else {
268                 LYXERR0("Cannot find encoding encoding for file " << filen);
269                 return false;
270         }
271
272         for (size_t i = 1; i < header->N; ++i) {
273                 // Note that in theory the strings may contain NUL characters.
274                 // This may be the case with plural forms
275                 string const ostr(&moData[0] + orig[i].offset, orig[i].length);
276                 docstring tstr = from_utf8(string(&moData[0] + trans[i].offset,
277                                                   trans[i].length));
278                 cleanTranslation(tstr);
279                 trans_map_[ostr] = tstr;
280                 //lyxerr << ostr << " ==> " << tstr << endl;
281         }
282
283         return true;
284 }
285
286 docstring const Messages::get(string const & m) const
287 {
288         if (m.empty())
289                 return docstring();
290
291         TranslationMap::const_iterator it = trans_map_.find(m);
292         if (it != trans_map_.end())
293                 return it->second;
294         else {
295                 docstring res = from_utf8(m);
296                 cleanTranslation(res);
297                 return res;
298         }
299 }
300
301 } // namespace lyx
302
303 #else // ENABLE_NLS
304 // This is the dummy variant.
305
306 namespace lyx {
307
308 Messages::Messages(string const & /* l */) {}
309
310 docstring const Messages::get(string const & m) const
311 {
312         docstring trans = from_ascii(m);
313         cleanTranslation(trans);
314         return trans;
315 }
316
317 std::string Messages::language() const
318     {
319         return string();
320     }
321
322 bool Messages::available(string const & /* c */)
323 {
324         return false;
325 }
326
327 } // namespace lyx
328
329 #endif