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 .
20 #include <sal/config.h>
22 #include <o3tl/string_view.hxx>
31 #include <lngmerge.hxx>
36 bool lcl_isNextGroup(OString
&sGroup_out
, std::string_view sLineTrim
)
38 if (o3tl::starts_with(sLineTrim
, "[") && o3tl::ends_with(sLineTrim
, "]"))
40 sLineTrim
= o3tl::getToken(sLineTrim
, 1, '[');
41 sLineTrim
= o3tl::getToken(sLineTrim
, 0, ']');
42 sGroup_out
= OString(o3tl::trim(sLineTrim
));
48 void lcl_RemoveUTF8ByteOrderMarker( OString
&rString
)
50 if( rString
.getLength() >= 3 && rString
[0] == '\xEF' &&
51 rString
[1] == '\xBB' && rString
[2] == '\xBF' )
53 rString
= rString
.copy(3);
61 LngParser::LngParser(OString sLngFile
)
62 : sSource(std::move( sLngFile
))
64 std::ifstream
aStream(sSource
.getStr());
65 if (!aStream
.is_open())
68 bool bFirstLine
= true;
70 while (std::getline(aStream
, s
))
72 OString
sLine(s
.data(), s
.length());
76 // Always remove UTF8 BOM from the first line
77 lcl_RemoveUTF8ByteOrderMarker( sLine
);
81 mvLines
.push_back( sLine
);
83 mvLines
.push_back( OString() );
86 LngParser::~LngParser()
90 void LngParser::CreatePO( const OString
&rPOFile
)
92 PoOfstream
aPOStream( rPOFile
, PoOfstream::APP
);
93 if (!aPOStream
.isOpen()) {
94 std::cerr
<< "Ulfex error: Can't open po file:" << rPOFile
<< "\n";
99 OString sGroup
, sLine
;
103 while( nPos
< mvLines
.size() ) {
104 sLine
= mvLines
[ nPos
++ ];
105 while( nPos
< mvLines
.size() && !isNextGroup( sGroup
, sLine
) ) {
106 ReadLine( sLine
, Text
);
108 sLine
= mvLines
[ nPos
++ ];
115 WritePO( aPOStream
, Text
, sSource
, sID
);
117 Text
.erase("x-comment"_ostr
);
122 void LngParser::WritePO(PoOfstream
&aPOStream
,
123 OStringHashMap
&rText_inout
, const OString
&rActFileName
,
126 common::writePoEntry(
127 "Ulfex"_ostr
, aPOStream
, rActFileName
, "LngText",
128 rID
, OString(), rText_inout
.count("x-comment"_ostr
) ? rText_inout
["x-comment"_ostr
] : OString(), rText_inout
["en-US"_ostr
]);
131 bool LngParser::isNextGroup(OString
&sGroup_out
, std::string_view sLine_in
)
133 return lcl_isNextGroup(sGroup_out
, o3tl::trim(sLine_in
));
136 void LngParser::ReadLine(std::string_view rLine_in
,
137 OStringHashMap
&rText_inout
)
139 if (!o3tl::starts_with(rLine_in
, " *") && !o3tl::starts_with(rLine_in
, "/*"))
141 OString
sLang(o3tl::trim(o3tl::getToken(rLine_in
, 0, '=')));
142 if (!sLang
.isEmpty()) {
143 rText_inout
[sLang
] = OString(o3tl::getToken(rLine_in
, 1, '"'));
148 void LngParser::Merge(
149 const OString
&rPOFile
,
150 const OString
&rDestinationFile
,
151 std::string_view rLanguage
)
153 std::ofstream
aDestination(
154 rDestinationFile
.getStr(), std::ios_base::out
| std::ios_base::trunc
);
156 MergeDataFile
aMergeDataFile( rPOFile
, sSource
, false, true );
157 if( o3tl::equalsIgnoreAsciiCase(rLanguage
, "ALL") )
158 aLanguages
= aMergeDataFile
.GetLanguages();
164 // seek to next group
165 while ( nPos
< mvLines
.size() && !bGroup
)
166 bGroup
= lcl_isNextGroup(sGroup
, o3tl::trim(mvLines
[nPos
++]));
168 while ( nPos
< mvLines
.size()) {
170 OString
sID( sGroup
);
171 std::size_t nLastLangPos
= 0;
173 ResData
aResData( sID
, sSource
);
174 aResData
.sResTyp
= "LngText"_ostr
;
175 MergeEntrys
*pEntrys
= aMergeDataFile
.GetMergeEntrys( &aResData
);
179 OString sLanguagesDone
;
181 while ( nPos
< mvLines
.size() && !bGroup
)
183 const OString sLine
{ mvLines
[nPos
].trim() };
184 if ( lcl_isNextGroup(sGroup
, sLine
) )
188 sLanguagesDone
= ""_ostr
;
193 OString
sLang(sLine
.getToken(0, '=', n
));
194 if (n
== -1 || static_cast<bool>(sLine
.match("/*")))
200 sLang
= sLang
.trim();
202 OString sSearch
{ ";" + sLang
+ ";" };
204 if ( sLanguagesDone
.indexOf( sSearch
) != -1 ) {
205 mvLines
.erase( mvLines
.begin() + nPos
);
209 if( !sLang
.isEmpty() )
212 pEntrys
->GetText( sNewText
, sLang
, true );
216 if ( !sNewText
.isEmpty()) {
217 mvLines
[ nPos
] = sLang
219 // escape quotes, unescape double escaped quotes fdo#56648
220 + sNewText
.replaceAll("\""_ostr
,"\\\""_ostr
).replaceAll("\\\\\""_ostr
,"\\\""_ostr
)
222 Text
[ sLang
] = sNewText
;
227 sLanguagesDone
+= sSearch
;
232 sLanguagesDone
+= sSearch
;
240 for(size_t n
= 0; n
< aLanguages
.size(); ++n
)
242 sCur
= aLanguages
[ n
];
243 if( !sCur
.equalsIgnoreAsciiCase("en-US") && Text
[sCur
].isEmpty() && pEntrys
)
247 pEntrys
->GetText( sNewText
, sCur
, true );
250 if ( !sNewText
.isEmpty() && sCur
!= "x-comment")
252 const OString sLine
{ sCur
254 // escape quotes, unescape double escaped quotes fdo#56648
255 + sNewText
.replaceAll("\""_ostr
,"\\\""_ostr
).replaceAll("\\\\\""_ostr
,"\\\""_ostr
)
261 if ( nLastLangPos
< mvLines
.size() ) {
262 mvLines
.insert( mvLines
.begin() + nLastLangPos
, sLine
);
264 mvLines
.push_back( sLine
);
272 for ( size_t i
= 0; i
< mvLines
.size(); ++i
)
273 aDestination
<< mvLines
[i
] << '\n';
275 aDestination
.close();
278 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */