xref: /trunk/main/shell/source/tools/lngconvex/lngconvex.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_shell.hxx"
30 
31 #include <tools/presys.h>
32 #if defined _MSC_VER
33 #pragma warning(push, 1)
34 #endif
35 #include <windows.h>
36 #if defined _MSC_VER
37 #pragma warning(pop)
38 #endif
39 #include <tools/postsys.h>
40 
41 #define VCL_NEED_BASETSD
42 
43 #include "cmdline.hxx"
44 
45 #include "osl/thread.h"
46 #include "osl/process.h"
47 #include "osl/file.hxx"
48 #include "sal/main.h"
49 
50 #include "tools/config.hxx"
51 #include "i18npool/mslangid.hxx"
52 
53 #include <iostream>
54 #include <fstream>
55 #include <map>
56 #include <sstream>
57 #include <iterator>
58 #include <algorithm>
59 #include <string>
60 
61 namespace /* private */
62 {
63 
64 using rtl::OUString;
65 using rtl::OString;
66 
67 //###########################################
68 void ShowUsage()
69 {
70     std::cout << "Usage: -ulf ulf_file -rc rc_output_file -rct rc_template_file -rch rch_file -rcf rcf_file" << std::endl;
71     std::cout << "-ulf Name of the ulf file" << std::endl;
72     std::cout << "-rc  Name of the resulting resource file" << std::endl;
73     std::cout << "-rct Name of the resource template file" << std::endl;
74     std::cout << "-rch Name of the resource file header" << std::endl;
75     std::cout << "-rcf Name of the resource file footer" << std::endl;
76 }
77 
78 //###########################################
79 inline OUString OStringToOUString(const OString& str)
80 { return rtl::OStringToOUString(str, osl_getThreadTextEncoding()); }
81 
82 //###########################################
83 inline OString OUStringToOString(const OUString& str)
84 { return rtl::OUStringToOString(str, osl_getThreadTextEncoding()); }
85 
86 //###########################################
87 /** Get the directory where the module
88     is located as system directory, the
89     returned directory has a trailing '\'  */
90 OUString get_module_path()
91 {
92     OUString cwd_url;
93     OUString module_path;
94     if (osl_Process_E_None == osl_getProcessWorkingDir(&cwd_url.pData))
95         osl::FileBase::getSystemPathFromFileURL(cwd_url, module_path);
96 
97     return module_path;
98 }
99 
100 //###########################################
101 /** Make the absolute directory of a base and
102     a relative directory, if the relative
103     directory is absolute the the relative
104     directory will be returned unchanged.
105     Base and relative directory should be
106     system paths the returned directory is
107     a system path too */
108 OUString get_absolute_path(
109     const OUString& BaseDir, const OUString& RelDir)
110 {
111     OUString base_url;
112     OUString rel_url;
113 
114     osl::FileBase::getFileURLFromSystemPath(BaseDir, base_url);
115     osl::FileBase::getFileURLFromSystemPath(RelDir, rel_url);
116 
117     OUString abs_url;
118     osl::FileBase::getAbsoluteFileURL(base_url, rel_url, abs_url);
119 
120     OUString abs_sys_path;
121     osl::FileBase::getSystemPathFromFileURL(abs_url, abs_sys_path);
122 
123     return abs_sys_path;
124 }
125 
126 //###########################################
127 OString get_absolute_file_path(const std::string& file_name)
128 {
129     OUString fp = get_absolute_path(
130         get_module_path(), OStringToOUString(file_name.c_str()));
131     return OUStringToOString(fp);
132 }
133 
134 //###########################################
135 /** A helper class, enables stream exceptions
136     on construction, restors the old exception
137     state on destruction */
138 class StreamExceptionsEnabler
139 {
140 public:
141     explicit StreamExceptionsEnabler(
142         std::ios& iostrm,
143         std::ios::iostate NewIos = std::ios::failbit | std::ios::badbit) :
144         m_IoStrm(iostrm),
145         m_OldIos(m_IoStrm.exceptions())
146     {
147         m_IoStrm.exceptions(NewIos);
148     }
149 
150     ~StreamExceptionsEnabler()
151     {
152         m_IoStrm.exceptions(m_OldIos);
153     }
154 private:
155     std::ios& m_IoStrm;
156     std::ios::iostate m_OldIos;
157 };
158 
159 typedef std::vector<std::string> string_container_t;
160 
161 //###########################################
162 class iso_lang_identifier
163 {
164 public:
165     iso_lang_identifier() {};
166 
167     iso_lang_identifier(const OString& str) :
168         lang_(str)
169     { init(); }
170 
171     iso_lang_identifier(const std::string& str) :
172         lang_(str.c_str())
173     { init(); }
174 
175     OString language() const
176     { return lang_; }
177 
178     OString country() const
179     { return country_; }
180 
181     OString make_OString() const
182     { return lang_ + "-" + country_; }
183 
184     std::string make_std_string() const
185     {
186         OString tmp(lang_ + "-" + country_);
187         return tmp.getStr();
188     }
189 
190 private:
191     void init()
192     {
193         sal_Int32 idx = lang_.indexOf("-");
194 
195         if (idx > -1)
196         {
197             country_ = lang_.copy(idx + 1);
198             lang_ = lang_.copy(0, idx);
199         }
200     }
201 
202 private:
203     OString lang_;
204     OString country_;
205 };
206 
207 //###########################################
208 /** Convert a OUString to the MS resource
209     file format string e.g.
210     OUString -> L"\x1A00\x2200\x3400" */
211 std::string make_winrc_unicode_string(const OUString& str)
212 {
213     std::ostringstream oss;
214     oss << "L\"";
215 
216     size_t length = str.getLength();
217     const sal_Unicode* pchr = str.getStr();
218 
219     for (size_t i = 0; i < length; i++)
220         oss << "\\x" << std::hex << (int)*pchr++;
221 
222     oss << "\"";
223     return oss.str();
224 }
225 
226 //###########################################
227 std::string make_winrc_unicode_string(const std::string& str)
228 {
229     return make_winrc_unicode_string(
230         OUString::createFromAscii(str.c_str()));
231 }
232 
233 //################################################
234 /** A replacement table contains pairs of
235     placeholders and the appropriate substitute */
236 class Substitutor
237 {
238 private:
239     typedef std::map<std::string, std::string> replacement_table_t;
240     typedef std::map<std::string, replacement_table_t*> iso_lang_replacement_table_t;
241 
242 public:
243     typedef iso_lang_replacement_table_t::iterator iterator;
244     typedef iso_lang_replacement_table_t::const_iterator const_iterator;
245 
246     iterator begin()
247     { return iso_lang_replacement_table_.begin(); }
248 
249     iterator end()
250     { return iso_lang_replacement_table_.end(); }
251 
252 public:
253 
254     Substitutor() {};
255 
256     ~Substitutor()
257     {
258         iso_lang_replacement_table_t::iterator iter_end = iso_lang_replacement_table_.end();
259         iso_lang_replacement_table_t::iterator iter = iso_lang_replacement_table_.begin();
260 
261         for( /* no init */; iter != iter_end; ++iter)
262             delete iter->second;
263 
264         iso_lang_replacement_table_.clear();
265     }
266 
267     void set_language(const iso_lang_identifier& iso_lang)
268     {
269         active_iso_lang_ = iso_lang;
270     }
271 
272     // If Text is a placeholder substitute it with
273     //its substitute else leave it unchanged
274     void substitute(std::string& Text)
275     {
276         replacement_table_t* prt = get_replacement_table(active_iso_lang_.make_std_string());
277         OSL_ASSERT(prt);
278         replacement_table_t::iterator iter = prt->find(Text);
279         if (iter != prt->end())
280             Text = iter->second;
281     }
282 
283     void add_substitution(
284         const std::string& Placeholder, const std::string& Substitute)
285     {
286         replacement_table_t* prt = get_replacement_table(active_iso_lang_.make_std_string());
287         OSL_ASSERT(prt);
288         prt->insert(std::make_pair(Placeholder, Substitute));
289     }
290 
291 
292 private:
293     // Return the replacement table for the iso lang id
294     // create a new one if not already present
295     replacement_table_t* get_replacement_table(const std::string& iso_lang)
296     {
297         iso_lang_replacement_table_t::iterator iter =
298             iso_lang_replacement_table_.find(iso_lang);
299 
300         replacement_table_t* prt = NULL;
301 
302         if (iso_lang_replacement_table_.end() == iter)
303         {
304             prt = new replacement_table_t();
305             iso_lang_replacement_table_.insert(std::make_pair(iso_lang, prt));
306         }
307         else
308         {
309             prt = iter->second;
310         }
311         return prt;
312     }
313 
314 private:
315     iso_lang_replacement_table_t iso_lang_replacement_table_;
316     iso_lang_identifier active_iso_lang_;
317 };
318 
319 typedef std::map< unsigned short , std::string , std::less< unsigned short > > shortmap;
320 
321 //###########################################
322 void add_group_entries(
323     Config& aConfig,
324     const ByteString& GroupName,
325     Substitutor& Substitutor)
326 {
327     OSL_ASSERT(aConfig.HasGroup(GroupName));
328 
329     aConfig.SetGroup(GroupName);
330     size_t key_count = aConfig.GetKeyCount();
331     shortmap map;
332 
333     for (size_t i = 0; i < key_count; i++)
334     {
335         ByteString iso_lang = aConfig.GetKeyName(sal::static_int_cast<USHORT>(i));
336         ByteString key_value_utf8 = aConfig.ReadKey(sal::static_int_cast<USHORT>(i));
337         iso_lang_identifier myiso_lang( iso_lang );
338         LanguageType ltype = MsLangId::convertIsoNamesToLanguage(myiso_lang.language(), myiso_lang.country());
339         if(  ( ltype & 0x0200 ) == 0 && map[ ltype ].empty()  )
340         {
341             Substitutor.set_language(iso_lang_identifier(iso_lang));
342 
343             key_value_utf8.EraseLeadingAndTrailingChars('\"');
344 
345             OUString key_value_utf16 =
346                 rtl::OStringToOUString(key_value_utf8, RTL_TEXTENCODING_UTF8);
347 
348             Substitutor.add_substitution(
349                 GroupName.GetBuffer(), make_winrc_unicode_string(key_value_utf16));
350             map[ static_cast<unsigned short>(ltype) ] = std::string( iso_lang.GetBuffer() );
351         }
352         else
353         {
354             if( !map[ ltype ].empty() )
355             {
356                 printf("ERROR: Duplicated ms id %d found for the languages %s and %s !!!! This does not work in microsoft resources\nPlease remove one!\n", ltype , map[ ltype ].c_str() , iso_lang.GetBuffer());
357                 exit( -1 );
358             }
359         }
360     }
361 }
362 
363 //###########################################
364 void read_ulf_file(const std::string& FileName, Substitutor& Substitutor)
365 {
366     // work-around for #i32420#
367 
368     // as the Config class is currently not able to deal correctly with
369     // UTF8 files starting with a byte-order-mark we create a copy of the
370     // original file without the byte-order-mark
371     rtl::OUString tmpfile_url;
372     osl_createTempFile(NULL, NULL, &tmpfile_url.pData);
373 
374     rtl::OUString tmpfile_sys;
375     osl::FileBase::getSystemPathFromFileURL(tmpfile_url, tmpfile_sys);
376 
377     std::ifstream in(FileName.c_str());
378     std::ofstream out(OUStringToOString(tmpfile_sys).getStr());
379 
380     try
381     {
382         StreamExceptionsEnabler sexc_out(out);
383         StreamExceptionsEnabler sexc_in(in);
384 
385         //skip the byte-order-mark 0xEF 0xBB 0xBF, identifying UTF8 files
386         unsigned char BOM[3] = {0xEF, 0xBB, 0xBF};
387         char buff[3];
388         in.read(&buff[0], 3);
389 
390         if (memcmp(buff, BOM, 3) != 0)
391             in.seekg(0);
392 
393         std::string line;
394         while (std::getline(in, line))
395             out << line << std::endl;
396     }
397     catch (const std::ios::failure&)
398     {
399         if (!in.eof())
400             throw;
401     }
402 
403     //Config config(OStringToOUString(FileName.c_str()).getStr());
404 
405     // end work-around for #i32420#
406 
407     Config config(tmpfile_url.getStr());
408     size_t grpcnt = config.GetGroupCount();
409     for (size_t i = 0; i < grpcnt; i++)
410         add_group_entries(config, config.GetGroupName(sal::static_int_cast<USHORT>(i)), Substitutor);
411 }
412 
413 //###########################################
414 void read_file(
415     const std::string& fname,
416     string_container_t& string_container)
417 {
418     std::ifstream file(fname.c_str());
419     StreamExceptionsEnabler sexc(file);
420 
421     try
422     {
423         std::string line;
424         while (std::getline(file, line))
425             string_container.push_back(line);
426     }
427     catch(const std::ios::failure&)
428     {
429         if (!file.eof())
430             throw;
431     }
432 }
433 
434 //###########################################
435 /** A simple helper function that appens the
436     content of one file to another one  */
437 void concatenate_files(std::ostream& os, std::istream& is)
438 {
439     StreamExceptionsEnabler os_sexc(os);
440     StreamExceptionsEnabler is_sexc(is);
441 
442     try
443     {
444         std::string line;
445         while (std::getline(is, line))
446             os << line << std::endl;
447     }
448     catch(const std::ios::failure&)
449     {
450         if (!is.eof())
451             throw;
452     }
453 }
454 
455 //###########################################
456 bool is_placeholder(const std::string& str)
457 {
458     return ((str.length() > 1) &&
459             ('%' == str[0]) &&
460             ('%' == str[str.length() - 1]));
461 }
462 
463 //###########################################
464 void start_language_section(
465     std::ostream_iterator<std::string>& ostream_iter, const iso_lang_identifier& iso_lang)
466 {
467     ostream_iter = std::string();
468 
469     std::string lang_section("LANGUAGE ");
470 
471     LanguageType ltype = MsLangId::convertIsoNamesToLanguage(iso_lang.language(), iso_lang.country());
472 
473     char buff[10];
474     int primLangID = PRIMARYLANGID(ltype);
475     int subLangID = SUBLANGID(ltype);
476     // Our resources are normaly not sub language dependant.
477     // Esp. for spanish we don't want to distinguish between trad.
478     // and internatinal sorting ( which leads to two different sub languages )
479     // Setting the sub language to neutral allows us to use one
480     // stringlist for all spanish variants ( see #123126# )
481     if ( ( primLangID == LANG_SPANISH ) &&
482          ( subLangID == SUBLANG_SPANISH ) )
483         subLangID = SUBLANG_NEUTRAL;
484 
485     _itoa(primLangID, buff, 16);
486     lang_section += std::string("0x") + std::string(buff);
487 
488     lang_section += std::string(" , ");
489 
490     _itoa(subLangID, buff, 16);
491 
492     lang_section += std::string("0x") + std::string(buff);
493     ostream_iter = lang_section;
494 }
495 
496 //###########################################
497 /** Iterate all languages in the substitutor,
498     replace the all placeholder and append the
499     result to the output file */
500 void inflate_rc_template_to_file(
501     std::ostream& os, const string_container_t& rctmpl, Substitutor& substitutor)
502 {
503     StreamExceptionsEnabler sexc(os);
504 
505     Substitutor::const_iterator iter = substitutor.begin();
506     Substitutor::const_iterator iter_end = substitutor.end();
507 
508     std::ostream_iterator<std::string> oi(os, "\n");
509 
510     for ( /**/ ;iter != iter_end; ++iter)
511     {
512         substitutor.set_language(iso_lang_identifier(iter->first));
513 
514         string_container_t::const_iterator rct_iter = rctmpl.begin();
515         string_container_t::const_iterator rct_iter_end = rctmpl.end();
516 
517         if (!rctmpl.empty())
518             start_language_section(oi, iter->first);
519 
520         for ( /**/ ;rct_iter != rct_iter_end; ++rct_iter)
521         {
522             std::istringstream iss(*rct_iter);
523             std::string line;
524 
525             while (iss)
526             {
527                 std::string token;
528                 iss >> token;
529                 substitutor.substitute(token);
530 
531                 // #110274# HACK for partially merged
532                 // *.lng files where some strings have
533                 // a particular language that others
534                 // don't have in order to keep the
535                 // build
536                 if (is_placeholder(token))
537                     token = make_winrc_unicode_string(token);
538 
539                 line += token;
540                 line += " ";
541             }
542             oi = line;
543         }
544     }
545 }
546 
547 } // namespace /* private */
548 
549 //####################################################
550 /* MAIN
551    The file names provided via command line should be
552    absolute or relative to the directory of this module.
553 
554    Algo:
555    1. read the ulf file and initialize the substitutor
556    2. read the resource template file
557    3. create the output file and append the header
558    4. inflate the resource template to the output file
559       for every language using the substitutor
560    5. append the footer
561 */
562 #define MAKE_ABSOLUTE(s) (get_absolute_file_path((s)).getStr())
563 #define ULF_FILE(c)    MAKE_ABSOLUTE((c).get_arg("-ulf"))
564 #define RC_TEMPLATE(c) MAKE_ABSOLUTE((c).get_arg("-rct"))
565 #define RC_FILE(c)     MAKE_ABSOLUTE((c).get_arg("-rc"))
566 #define RC_HEADER(c)   MAKE_ABSOLUTE((c).get_arg("-rch"))
567 #define RC_FOOTER(c)   MAKE_ABSOLUTE((c).get_arg("-rcf"))
568 
569 SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
570 {
571     try
572     {
573         CommandLine cmdline(argc, argv);
574 
575         Substitutor substitutor;
576         read_ulf_file(ULF_FILE(cmdline), substitutor);
577 
578         string_container_t rc_tmpl;
579         read_file(RC_TEMPLATE(cmdline), rc_tmpl);
580 
581         std::ofstream rc_file(RC_FILE(cmdline));
582         std::ifstream in_header(RC_HEADER(cmdline));
583         concatenate_files(rc_file, in_header);
584 
585         inflate_rc_template_to_file(rc_file, rc_tmpl, substitutor);
586 
587         std::ifstream in_footer(RC_FOOTER(cmdline));
588         concatenate_files(rc_file, in_footer);
589     }
590     catch(const std::ios::failure& ex)
591     {
592         std::cout << ex.what() << std::endl;
593     }
594     catch(std::exception& ex)
595     {
596         std::cout << ex.what() << std::endl;
597         ShowUsage();
598     }
599     catch(...)
600     {
601         std::cout << "Unexpected error..." << std::endl;
602     }
603     return 0;
604 }
605 
606