Branch libreoffice-5-0-4
[LibreOffice.git] / l10ntools / source / lngmerge.cxx
blob959a93c2961f5e19c3f06452e6a846ccc89b8475
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <cstddef>
23 #include <fstream>
24 #include <iterator>
25 #include <string>
27 #include "po.hxx"
28 #include "lngmerge.hxx"
30 namespace {
32 OString getBracketedContent(const OString& text) {
33 return text.getToken(1, '[').getToken(0, ']');
36 static void lcl_RemoveUTF8ByteOrderMarker( OString &rString )
38 if( rString.getLength() >= 3 && rString[0] == '\xEF' &&
39 rString[1] == '\xBB' && rString[2] == '\xBF' )
41 rString = rString.copy(3);
48 // class LngParser
50 LngParser::LngParser(const OString &rLngFile)
51 : nError( LNG_OK )
52 , pLines( NULL )
53 , sSource( rLngFile )
55 pLines = new LngLineList();
56 std::ifstream aStream(sSource.getStr());
57 if (aStream.is_open())
59 bool bFirstLine = true;
60 std::string s;
61 std::getline(aStream, s);
62 while (!aStream.eof())
64 OString sLine(s.data(), s.length());
66 if( bFirstLine )
68 // Always remove UTF8 BOM from the first line
69 lcl_RemoveUTF8ByteOrderMarker( sLine );
70 bFirstLine = false;
73 pLines->push_back( new OString(sLine) );
74 std::getline(aStream, s);
76 pLines->push_back( new OString() );
78 else
79 nError = LNG_COULD_NOT_OPEN;
82 LngParser::~LngParser()
84 for ( size_t i = 0, n = pLines->size(); i < n; ++i )
85 delete (*pLines)[ i ];
86 pLines->clear();
87 delete pLines;
90 bool 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.getStr() << "\n";
97 size_t nPos = 0;
98 bool bStart = true;
99 OString sGroup, sLine;
100 OStringHashMap Text;
101 OString sID;
103 while( nPos < pLines->size() ) {
104 sLine = *(*pLines)[ nPos++ ];
105 while( nPos < pLines->size() && !isNextGroup( sGroup , sLine ) ) {
106 ReadLine( sLine , Text );
107 sID = sGroup;
108 sLine = *(*pLines)[ nPos++ ];
110 if( bStart ) {
111 bStart = false;
112 sID = sGroup;
114 else {
115 WritePO( aPOStream , Text , sSource , sID );
117 Text.erase("x-comment");
119 aPOStream.close();
120 return true;
123 void LngParser::WritePO(PoOfstream &aPOStream,
124 OStringHashMap &rText_inout, const OString &rActFileName,
125 const OString &rID)
128 bool bExport = true;
129 if ( bExport )
131 common::writePoEntry(
132 "Ulfex", aPOStream, rActFileName, "LngText",
133 rID, OString(), rText_inout.count("x-comment") ? rText_inout["x-comment"] : OString(), rText_inout["en-US"]);
137 bool LngParser::isNextGroup(OString &sGroup_out, const OString &sLine_in)
139 const OString sLineTrim = sLine_in.trim();
140 if (sLineTrim.startsWith("[") && sLineTrim.endsWith("]"))
142 sGroup_out = getBracketedContent(sLineTrim).trim();
143 return true;
145 return false;
148 void LngParser::ReadLine(const OString &rLine_in,
149 OStringHashMap &rText_inout)
151 if (!rLine_in.match(" *") && !rLine_in.match("/*"))
153 OString sLang(rLine_in.getToken(0, '=').trim());
154 if (!sLang.isEmpty()) {
155 OString sText(rLine_in.getToken(1, '"'));
156 rText_inout[sLang] = sText;
161 bool LngParser::Merge(
162 const OString &rPOFile,
163 const OString &rDestinationFile,
164 const OString &rLanguage )
166 std::ofstream aDestination(
167 rDestinationFile.getStr(), std::ios_base::out | std::ios_base::trunc);
168 if (!aDestination.is_open()) {
169 nError = LNG_COULD_NOT_OPEN;
171 nError = LNG_OK;
173 MergeDataFile aMergeDataFile( rPOFile, sSource, false, true );
174 if( rLanguage.equalsIgnoreAsciiCase("ALL") )
175 aLanguages = aMergeDataFile.GetLanguages();
177 size_t nPos = 0;
178 bool bGroup = false;
179 OString sGroup;
181 // seek to next group
182 while ( nPos < pLines->size() && !bGroup )
184 OString sLine( *(*pLines)[ nPos ] );
185 sLine = sLine.trim();
186 if ( sLine.startsWith("[") && sLine.endsWith("]") )
188 sGroup = getBracketedContent(sLine).trim();
189 bGroup = true;
191 nPos ++;
194 while ( nPos < pLines->size()) {
195 OStringHashMap Text;
196 OString sID( sGroup );
197 std::size_t nLastLangPos = 0;
199 ResData *pResData = new ResData( sID, sSource );
200 pResData->sResTyp = "LngText";
201 MergeEntrys *pEntrys = aMergeDataFile.GetMergeEntrys( pResData );
202 // read languages
203 bGroup = false;
205 OString sLanguagesDone;
207 while ( nPos < pLines->size() && !bGroup )
209 OString sLine( *(*pLines)[ nPos ] );
210 sLine = sLine.trim();
211 if ( sLine.startsWith("[") && sLine.endsWith("]") )
213 sGroup = getBracketedContent(sLine).trim();
214 bGroup = true;
215 nPos ++;
216 sLanguagesDone = "";
218 else
220 sal_Int32 n = 0;
221 OString sLang(sLine.getToken(0, '=', n));
222 if (n == -1 || static_cast<bool>(sLine.match("/*")))
224 ++nPos;
226 else
228 sLang = sLang.trim();
230 OString sSearch( ";" );
231 sSearch += sLang;
232 sSearch += ";";
234 if (( sLanguagesDone.indexOf( sSearch ) != -1 )) {
235 LngLineList::iterator it = pLines->begin();
236 std::advance( it, nPos );
237 pLines->erase( it );
239 if( pEntrys )
241 if( !sLang.isEmpty() )
243 OString sNewText;
244 pEntrys->GetText( sNewText, STRING_TYP_TEXT, sLang, true );
245 if( sLang == "qtz" )
246 continue;
248 if ( !sNewText.isEmpty()) {
249 OString *pLine = (*pLines)[ nPos ];
251 OString sText1( sLang );
252 sText1 += " = \"";
253 // escape quotes, unescape double escaped quotes fdo#56648
254 sText1 += sNewText.replaceAll("\"","\\\"").replaceAll("\\\\\"","\\\"");
255 sText1 += "\"";
256 *pLine = sText1;
257 Text[ sLang ] = sNewText;
260 nLastLangPos = nPos;
261 nPos ++;
262 sLanguagesDone += sSearch;
264 else {
265 nLastLangPos = nPos;
266 nPos ++;
267 sLanguagesDone += sSearch;
272 OString sCur;
273 if ( nLastLangPos )
275 for(size_t n = 0; n < aLanguages.size(); ++n)
277 sCur = aLanguages[ n ];
278 if( !sCur.equalsIgnoreAsciiCase("en-US") && Text[sCur].isEmpty() && pEntrys )
281 OString sNewText;
282 pEntrys->GetText( sNewText, STRING_TYP_TEXT, sCur, true );
283 if( sCur == "qtz" )
284 continue;
285 if ( !sNewText.isEmpty() && sCur != "x-comment")
287 OString sLine;
288 sLine += sCur;
289 sLine += " = \"";
290 // escape quotes, unescape double escaped quotes fdo#56648
291 sLine += sNewText.replaceAll("\"","\\\"").replaceAll("\\\\\"","\\\"");
292 sLine += "\"";
294 nLastLangPos++;
295 nPos++;
297 if ( nLastLangPos < pLines->size() ) {
298 LngLineList::iterator it = pLines->begin();
299 std::advance( it, nLastLangPos );
300 pLines->insert( it, new OString(sLine) );
301 } else {
302 pLines->push_back( new OString(sLine) );
309 delete pResData;
312 for ( size_t i = 0; i < pLines->size(); ++i )
313 aDestination << (*pLines)[i]->getStr() << '\n';
315 aDestination.close();
316 return true;
319 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */