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