1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
30 typedef unsigned short WORD
;
31 #define PRIMARYLANGID(lgid) ((WORD)(lgid) & 0x3ff)
32 #define SUBLANGID(lgid) ((WORD)(lgid) >> 10)
33 #define LANG_SPANISH 0x0a
34 #define SUBLANG_NEUTRAL 0x00
35 #define SUBLANG_SPANISH 0x01
38 #include "cmdline.hxx"
40 #include <comphelper/string.hxx>
41 #include "osl/thread.h"
42 #include "osl/process.h"
43 #include "osl/file.hxx"
46 #include "tools/config.hxx"
47 #include "i18nlangtag/languagetag.hxx"
61 namespace /* private */
67 std::cout
<< "Usage: -ulf ulf_file -rc rc_output_file -rct rc_template_file -rch rch_file -rcf rcf_file" << std::endl
;
68 std::cout
<< "-ulf Name of the ulf file" << std::endl
;
69 std::cout
<< "-rc Name of the resulting resource file" << std::endl
;
70 std::cout
<< "-rct Name of the resource template file" << std::endl
;
71 std::cout
<< "-rch Name of the resource file header" << std::endl
;
72 std::cout
<< "-rcf Name of the resource file footer" << std::endl
;
75 inline OUString
OStringToOUString(const OString
& str
)
76 { return OStringToOUString(str
, osl_getThreadTextEncoding()); }
78 inline OString
OUStringToOString(const OUString
& str
)
79 { return OUStringToOString(str
, osl_getThreadTextEncoding()); }
81 /** Get the directory where the module
82 is located as system directory, the
83 returned directory has a trailing '\' */
84 OUString
get_module_path()
88 if (osl_Process_E_None
== osl_getProcessWorkingDir(&cwd_url
.pData
))
89 osl::FileBase::getSystemPathFromFileURL(cwd_url
, module_path
);
94 /** Make the absolute directory of a base and
95 a relative directory, if the relative
96 directory is absolute the relative
97 directory will be returned unchanged.
98 Base and relative directory should be
99 system paths the returned directory is
101 OUString
get_absolute_path(
102 const OUString
& BaseDir
, const OUString
& RelDir
)
107 osl::FileBase::getFileURLFromSystemPath(BaseDir
, base_url
);
108 osl::FileBase::getFileURLFromSystemPath(RelDir
, rel_url
);
111 osl::FileBase::getAbsoluteFileURL(base_url
, rel_url
, abs_url
);
113 OUString abs_sys_path
;
114 osl::FileBase::getSystemPathFromFileURL(abs_url
, abs_sys_path
);
119 OString
get_absolute_file_path(const std::string
& file_name
)
121 OUString fp
= get_absolute_path(
122 get_module_path(), OStringToOUString(file_name
.c_str()));
123 return OUStringToOString(fp
);
126 /** A helper class, enables stream exceptions
127 on construction, restors the old exception
128 state on destruction */
129 class StreamExceptionsEnabler
132 explicit StreamExceptionsEnabler(
134 std::ios::iostate NewIos
= std::ios::failbit
| std::ios::badbit
) :
136 m_OldIos(m_IoStrm
.exceptions())
138 m_IoStrm
.exceptions(NewIos
);
141 ~StreamExceptionsEnabler()
143 m_IoStrm
.exceptions(m_OldIos
);
147 std::ios::iostate m_OldIos
;
150 typedef std::vector
<std::string
> string_container_t
;
152 class iso_lang_identifier
155 iso_lang_identifier() {};
157 iso_lang_identifier(const OString
& str
) :
161 iso_lang_identifier(const std::string
& str
) :
165 OUString
make_OUString() const
166 { return OStringToOUString( maBcp47
, RTL_TEXTENCODING_ASCII_US
); }
168 std::string
make_std_string() const
169 { return maBcp47
.getStr(); }
175 /** Convert a OUString to the MS resource
176 file format string e.g.
177 OUString -> L"\x1A00\x2200\x3400" */
178 std::string
make_winrc_unicode_string(const OUString
& str
)
180 std::ostringstream oss
;
183 size_t length
= str
.getLength();
184 const sal_Unicode
* pchr
= str
.getStr();
186 for (size_t i
= 0; i
< length
; i
++)
187 oss
<< "\\x" << std::hex
<< (int)*pchr
++;
193 std::string
make_winrc_unicode_string(const std::string
& str
)
195 return make_winrc_unicode_string(
196 OUString::createFromAscii(str
.c_str()));
199 /** A replacement table contains pairs of
200 placeholders and the appropriate substitute */
204 typedef std::map
<std::string
, std::string
> replacement_table_t
;
205 typedef std::map
<std::string
, replacement_table_t
*> iso_lang_replacement_table_t
;
208 typedef iso_lang_replacement_table_t::iterator iterator
;
209 typedef iso_lang_replacement_table_t::const_iterator const_iterator
;
212 { return iso_lang_replacement_table_
.begin(); }
215 { return iso_lang_replacement_table_
.end(); }
223 iso_lang_replacement_table_t::iterator iter_end
= iso_lang_replacement_table_
.end();
224 iso_lang_replacement_table_t::iterator iter
= iso_lang_replacement_table_
.begin();
226 for( /* no init */; iter
!= iter_end
; ++iter
)
229 iso_lang_replacement_table_
.clear();
232 void set_language(const iso_lang_identifier
& iso_lang
)
234 active_iso_lang_
= iso_lang
;
237 // If Text is a placeholder substitute it with
238 //its substitute else leave it unchanged
239 void substitute(std::string
& Text
)
241 replacement_table_t
* prt
= get_replacement_table(active_iso_lang_
.make_std_string());
243 replacement_table_t::iterator iter
= prt
->find(Text
);
244 if (iter
!= prt
->end())
248 void add_substitution(
249 const std::string
& Placeholder
, const std::string
& Substitute
)
251 replacement_table_t
* prt
= get_replacement_table(active_iso_lang_
.make_std_string());
253 prt
->insert(std::make_pair(Placeholder
, Substitute
));
258 // Return the replacement table for the iso lang id
259 // create a new one if not already present
260 replacement_table_t
* get_replacement_table(const std::string
& iso_lang
)
262 iso_lang_replacement_table_t::iterator iter
=
263 iso_lang_replacement_table_
.find(iso_lang
);
265 replacement_table_t
* prt
= NULL
;
267 if (iso_lang_replacement_table_
.end() == iter
)
269 prt
= new replacement_table_t();
270 iso_lang_replacement_table_
.insert(std::make_pair(iso_lang
, prt
));
280 iso_lang_replacement_table_t iso_lang_replacement_table_
;
281 iso_lang_identifier active_iso_lang_
;
284 typedef std::map
< unsigned short , std::string
, std::less
< unsigned short > > shortmap
;
286 void add_group_entries(
288 const OString
& GroupName
,
289 Substitutor
& Substitutor
)
291 OSL_ASSERT(aConfig
.HasGroup(GroupName
));
293 aConfig
.SetGroup(GroupName
);
294 size_t key_count
= aConfig
.GetKeyCount();
297 for (size_t i
= 0; i
< key_count
; i
++)
299 OString iso_lang
= aConfig
.GetKeyName(sal::static_int_cast
<sal_uInt16
>(i
));
300 OString key_value_utf8
= aConfig
.ReadKey(sal::static_int_cast
<sal_uInt16
>(i
));
301 iso_lang_identifier
myiso_lang( iso_lang
);
302 LanguageType ltype
= LanguageTag( myiso_lang
.make_OUString()).makeFallback().getLanguageType();
303 if( ( ltype
& 0x0200 ) == 0 && map
[ ltype
].empty() )
305 Substitutor
.set_language(iso_lang_identifier(iso_lang
));
307 key_value_utf8
= comphelper::string::strip(key_value_utf8
, '\"');
309 OUString key_value_utf16
=
310 OStringToOUString(key_value_utf8
, RTL_TEXTENCODING_UTF8
);
312 Substitutor
.add_substitution(
313 GroupName
.getStr(), make_winrc_unicode_string(key_value_utf16
));
314 map
[ static_cast<unsigned short>(ltype
) ] = std::string( iso_lang
.getStr() );
318 if( !map
[ ltype
].empty() )
320 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
.getStr());
327 void read_ulf_file(const std::string
& FileName
, Substitutor
& Substitutor
)
329 // work-around for #i32420#
331 // as the Config class is currently not able to deal correctly with
332 // UTF8 files starting with a byte-order-mark we create a copy of the
333 // original file without the byte-order-mark
334 OUString tmpfile_url
;
335 osl_createTempFile(NULL
, NULL
, &tmpfile_url
.pData
);
337 OUString tmpfile_sys
;
338 osl::FileBase::getSystemPathFromFileURL(tmpfile_url
, tmpfile_sys
);
340 std::ifstream
in(FileName
.c_str());
341 std::ofstream
out(OUStringToOString(tmpfile_sys
).getStr());
345 StreamExceptionsEnabler
sexc_out(out
);
346 StreamExceptionsEnabler
sexc_in(in
);
348 //skip the byte-order-mark 0xEF 0xBB 0xBF, identifying UTF8 files
349 unsigned char BOM
[3] = {0xEF, 0xBB, 0xBF};
351 in
.read(&buff
[0], 3);
353 if (memcmp(buff
, BOM
, 3) != 0)
357 while (std::getline(in
, line
))
358 out
<< line
<< std::endl
;
360 catch (const std::ios::failure
&)
367 // end work-around for #i32420#
369 Config
config(tmpfile_url
.getStr());
370 size_t grpcnt
= config
.GetGroupCount();
371 for (size_t i
= 0; i
< grpcnt
; i
++)
372 add_group_entries(config
, config
.GetGroupName(sal::static_int_cast
<sal_uInt16
>(i
)), Substitutor
);
376 const std::string
& fname
,
377 string_container_t
& string_container
)
379 std::ifstream
file(fname
.c_str());
380 StreamExceptionsEnabler
sexc(file
);
385 while (std::getline(file
, line
))
386 string_container
.push_back(line
);
388 catch(const std::ios::failure
&)
395 /** A simple helper function that appens the
396 content of one file to another one */
397 void concatenate_files(std::ostream
& os
, std::istream
& is
)
399 StreamExceptionsEnabler
os_sexc(os
);
400 StreamExceptionsEnabler
is_sexc(is
);
405 while (std::getline(is
, line
))
406 os
<< line
<< std::endl
;
408 catch(const std::ios::failure
&)
415 bool is_placeholder(const std::string
& str
)
417 return ((str
.length() > 1) &&
419 ('%' == str
[str
.length() - 1]));
422 void start_language_section(
423 std::ostream_iterator
<std::string
>& ostream_iter
, const iso_lang_identifier
& iso_lang
)
425 ostream_iter
= std::string();
427 std::string
lang_section("LANGUAGE ");
429 LanguageType ltype
= LanguageTag( iso_lang
.make_OUString()).makeFallback().getLanguageType();
432 int primLangID
= PRIMARYLANGID(ltype
);
433 int subLangID
= SUBLANGID(ltype
);
434 // Our resources are normally not sub language dependent.
435 // Esp. for spanish we don't want to distinguish between trad.
436 // and internatinal sorting ( which leads to two different sub languages )
437 // Setting the sub language to neutral allows us to use one
438 // stringlist for all spanish variants
439 if ( ( primLangID
== LANG_SPANISH
) &&
440 ( subLangID
== SUBLANG_SPANISH
) )
441 subLangID
= SUBLANG_NEUTRAL
;
444 _itoa(primLangID
, buff
, 16);
446 sprintf(buff
, "%x", primLangID
);
448 lang_section
+= std::string("0x") + std::string(buff
);
450 lang_section
+= std::string(" , ");
453 _itoa(subLangID
, buff
, 16);
455 sprintf(buff
, "%x", subLangID
);
457 lang_section
+= std::string("0x") + std::string(buff
);
458 ostream_iter
= lang_section
;
461 /** Iterate all languages in the substitutor,
462 replace the all placeholder and append the
463 result to the output file */
464 void inflate_rc_template_to_file(
465 std::ostream
& os
, const string_container_t
& rctmpl
, Substitutor
& substitutor
)
467 StreamExceptionsEnabler
sexc(os
);
469 Substitutor::const_iterator iter
= substitutor
.begin();
470 Substitutor::const_iterator iter_end
= substitutor
.end();
472 std::ostream_iterator
<std::string
> oi(os
, "\n");
474 for ( /**/ ;iter
!= iter_end
; ++iter
)
476 substitutor
.set_language(iso_lang_identifier(iter
->first
));
478 string_container_t::const_iterator rct_iter
= rctmpl
.begin();
479 string_container_t::const_iterator rct_iter_end
= rctmpl
.end();
482 start_language_section(oi
, iter
->first
);
484 for ( /**/ ;rct_iter
!= rct_iter_end
; ++rct_iter
)
486 std::istringstream
iss(*rct_iter
);
493 substitutor
.substitute(token
);
495 // HACK for partially merged
496 // *.lng files where some strings have
497 // a particular language that others
498 // don't have in order to keep the
500 if (is_placeholder(token
))
501 token
= make_winrc_unicode_string(token
);
511 } // namespace /* private */
514 The file names provided via command line should be
515 absolute or relative to the directory of this module.
518 1. read the ulf file and initialize the substitutor
519 2. read the resource template file
520 3. create the output file and append the header
521 4. inflate the resource template to the output file
522 for every language using the substitutor
525 #define MAKE_ABSOLUTE(s) (get_absolute_file_path((s)).getStr())
526 #define ULF_FILE(c) MAKE_ABSOLUTE((c).get_arg("-ulf"))
527 #define RC_TEMPLATE(c) MAKE_ABSOLUTE((c).get_arg("-rct"))
528 #define RC_FILE(c) MAKE_ABSOLUTE((c).get_arg("-rc"))
529 #define RC_HEADER(c) MAKE_ABSOLUTE((c).get_arg("-rch"))
530 #define RC_FOOTER(c) MAKE_ABSOLUTE((c).get_arg("-rcf"))
532 SAL_IMPLEMENT_MAIN_WITH_ARGS(argc
, argv
)
536 CommandLine
cmdline(argc
, argv
);
538 Substitutor substitutor
;
539 read_ulf_file(ULF_FILE(cmdline
), substitutor
);
541 string_container_t rc_tmpl
;
542 read_file(RC_TEMPLATE(cmdline
), rc_tmpl
);
544 std::ofstream
rc_file(RC_FILE(cmdline
));
545 std::ifstream
in_header(RC_HEADER(cmdline
));
546 concatenate_files(rc_file
, in_header
);
548 inflate_rc_template_to_file(rc_file
, rc_tmpl
, substitutor
);
550 std::ifstream
in_footer(RC_FOOTER(cmdline
));
551 concatenate_files(rc_file
, in_footer
);
553 catch(const std::ios::failure
& ex
)
555 std::cout
<< ex
.what() << std::endl
;
558 catch(const std::exception
& ex
)
560 std::cout
<< ex
.what() << std::endl
;
566 std::cout
<< "Unexpected error..." << std::endl
;
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */