Use o3tl::convert in Math
[LibreOffice.git] / l10ntools / source / cfgmerge.cxx
blob3fc24d50d5ca5373e42b5829383953d40648bda3
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 <cfglex.hxx>
23 #include <common.hxx>
25 #include <cstdio>
26 #include <cstdlib>
27 #include <cstring>
29 #include <memory>
30 #include <rtl/strbuf.hxx>
32 #include <helper.hxx>
33 #include <export.hxx>
34 #include <cfgmerge.hxx>
35 #include <tokens.h>
37 namespace {
39 namespace global {
41 OString inputPathname;
42 std::unique_ptr< CfgParser > parser;
47 extern "C" {
49 FILE * init(int argc, char ** argv) {
51 common::HandledArgs aArgs;
52 if ( !common::handleArguments(argc, argv, aArgs) )
54 common::writeUsage("cfgex","*.xcu");
55 std::exit(EXIT_FAILURE);
57 global::inputPathname = aArgs.m_sInputFile;
59 FILE * pFile = std::fopen(global::inputPathname.getStr(), "r");
60 if (pFile == nullptr) {
61 std::fprintf(
62 stderr, "Error: Cannot open file \"%s\"\n",
63 global::inputPathname.getStr() );
64 std::exit(EXIT_FAILURE);
67 if (aArgs.m_bMergeMode) {
68 global::parser.reset(
69 new CfgMerge(
70 aArgs.m_sMergeSrc, aArgs.m_sOutputFile,
71 global::inputPathname, aArgs.m_sLanguage ));
72 } else {
73 global::parser.reset(
74 new CfgExport(
75 aArgs.m_sOutputFile, global::inputPathname ));
78 return pFile;
81 void workOnTokenSet(int nTyp, char * pTokenText) {
82 global::parser->Execute( nTyp, pTokenText );
90 CfgStackData* CfgStack::Push(const OString &rTag, const OString &rId)
92 CfgStackData *pD = new CfgStackData( rTag, rId );
93 maList.push_back( pD );
94 return pD;
100 CfgStack::~CfgStack()
104 OString CfgStack::GetAccessPath( size_t nPos )
106 OStringBuffer sReturn;
107 for (size_t i = 0; i <= nPos; ++i)
109 if (i)
110 sReturn.append('.');
111 sReturn.append(maList[i]->GetIdentifier());
114 return sReturn.makeStringAndClear();
117 CfgStackData *CfgStack::GetStackData()
119 if (!maList.empty())
120 return maList[maList.size() - 1];
121 else
122 return nullptr;
128 CfgParser::CfgParser()
129 : pStackData( nullptr ),
130 bLocalize( false )
134 CfgParser::~CfgParser()
138 bool CfgParser::IsTokenClosed(const OString &rToken)
140 return rToken[rToken.getLength() - 2] == '/';
143 void CfgParser::AddText(
144 OString &rText,
145 const OString &rIsoLang,
146 const OString &rResTyp )
148 rText = rText.replaceAll(OString('\n'), OString()).
149 replaceAll(OString('\r'), OString()).
150 replaceAll(OString('\t'), OString());
151 pStackData->sResTyp = rResTyp;
152 WorkOnText( rText, rIsoLang );
153 pStackData->sText[ rIsoLang ] = rText;
156 #if defined _MSC_VER
157 #pragma warning(disable: 4702) // unreachable code, bug in MSVC2015, it thinks the std::exit is unreachable
158 #endif
159 void CfgParser::ExecuteAnalyzedToken( int nToken, char *pToken )
161 OString sToken( pToken );
163 if ( sToken == " " || sToken == "\t" )
164 sLastWhitespace += sToken;
166 OString sTokenName;
168 bool bOutput = true;
170 switch ( nToken ) {
171 case CFG_TOKEN_PACKAGE:
172 case CFG_TOKEN_COMPONENT:
173 case CFG_TOKEN_TEMPLATE:
174 case CFG_TOKEN_CONFIGNAME:
175 case CFG_TOKEN_OORNAME:
176 case CFG_TOKEN_OORVALUE:
177 case CFG_TAG:
178 case ANYTOKEN:
179 case CFG_TEXT_START:
181 sTokenName = sToken.getToken(1, '<').getToken(0, '>').
182 getToken(0, ' ');
184 if ( !IsTokenClosed( sToken )) {
185 OString sSearch;
186 switch ( nToken ) {
187 case CFG_TOKEN_PACKAGE:
188 sSearch = "package-id=";
189 break;
190 case CFG_TOKEN_COMPONENT:
191 sSearch = "component-id=";
192 break;
193 case CFG_TOKEN_TEMPLATE:
194 sSearch = "template-id=";
195 break;
196 case CFG_TOKEN_CONFIGNAME:
197 sSearch = "cfg:name=";
198 break;
199 case CFG_TOKEN_OORNAME:
200 sSearch = "oor:name=";
201 bLocalize = true;
202 break;
203 case CFG_TOKEN_OORVALUE:
204 sSearch = "oor:value=";
205 break;
206 case CFG_TEXT_START: {
207 if ( sCurrentResTyp != sTokenName ) {
208 WorkOnResourceEnd();
210 sCurrentResTyp = sTokenName;
212 OString sTemp = sToken.copy( sToken.indexOf( "xml:lang=" ));
213 sCurrentIsoLang = sTemp.getToken(1, '"');
215 if ( sCurrentIsoLang == NO_TRANSLATE_ISO )
216 bLocalize = false;
218 pStackData->sTextTag = sToken;
220 sCurrentText = "";
222 break;
224 OString sTokenId;
225 if ( !sSearch.isEmpty())
227 OString sTemp = sToken.copy( sToken.indexOf( sSearch ));
228 sTokenId = sTemp.getToken(1, '"');
230 pStackData = aStack.Push( sTokenName, sTokenId );
232 if ( sSearch == "cfg:name=" ) {
233 OString sTemp( sToken.toAsciiUpperCase() );
234 bLocalize = sTemp.indexOf("CFG:TYPE=\"STRING\"")>=0
235 && sTemp.indexOf( "CFG:LOCALIZED=\"TRUE\"" )>=0;
238 else if ( sTokenName == "label" ) {
239 if ( sCurrentResTyp != sTokenName ) {
240 WorkOnResourceEnd();
242 sCurrentResTyp = sTokenName;
245 break;
246 case CFG_CLOSETAG:
248 sTokenName = sToken.getToken(1, '/').getToken(0, '>').
249 getToken(0, ' ');
250 if ( aStack.GetStackData() && ( aStack.GetStackData()->GetTagType() == sTokenName ))
252 if (sCurrentText.isEmpty())
253 WorkOnResourceEnd();
254 aStack.Pop();
255 pStackData = aStack.GetStackData();
257 else
259 const OString sError{ "Misplaced close tag: " + sToken + " in file " + global::inputPathname };
260 yyerror(sError.getStr());
261 std::exit(EXIT_FAILURE);
264 break;
266 case CFG_TEXTCHAR:
267 sCurrentText += sToken;
268 bOutput = false;
269 break;
271 case CFG_TOKEN_NO_TRANSLATE:
272 bLocalize = false;
273 break;
276 if ( !sCurrentText.isEmpty() && nToken != CFG_TEXTCHAR )
278 AddText( sCurrentText, sCurrentIsoLang, sCurrentResTyp );
279 Output( sCurrentText );
280 sCurrentText.clear();
281 pStackData->sEndTextTag = sToken;
284 if ( bOutput )
285 Output( sToken );
287 if ( sToken != " " && sToken != "\t" )
288 sLastWhitespace = "";
291 void CfgExport::Output(const OString&)
295 void CfgParser::Execute( int nToken, char * pToken )
297 OString sToken( pToken );
299 switch ( nToken ) {
300 case CFG_TAG:
301 if ( sToken.indexOf( "package-id=" ) != -1 ) {
302 ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE, pToken );
303 return;
304 } else if ( sToken.indexOf( "component-id=" ) != -1 ) {
305 ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT, pToken );
306 return;
307 } else if ( sToken.indexOf( "template-id=" ) != -1 ) {
308 ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE, pToken );
309 return;
310 } else if ( sToken.indexOf( "cfg:name=" ) != -1 ) {
311 ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
312 return;
313 } else if ( sToken.indexOf( "oor:name=" ) != -1 ) {
314 ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
315 return;
316 } else if ( sToken.indexOf( "oor:value=" ) != -1 ) {
317 ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE, pToken );
318 return;
320 break;
322 ExecuteAnalyzedToken( nToken, pToken );
328 CfgExport::CfgExport(
329 const OString &rOutputFile,
330 const OString &rFilePath )
332 : sPath( rFilePath )
334 pOutputStream.open( rOutputFile, PoOfstream::APP );
335 if (!pOutputStream.isOpen())
337 std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
338 std::exit(EXIT_FAILURE);
342 CfgExport::~CfgExport()
344 pOutputStream.close();
348 void CfgExport::WorkOnResourceEnd()
350 if ( !bLocalize )
351 return;
353 if ( pStackData->sText["en-US"].isEmpty() )
354 return;
356 OString sXComment = pStackData->sText[OString("x-comment")];
357 OString sLocalId = pStackData->sIdentifier;
358 OString sGroupId;
359 if ( aStack.size() == 1 ) {
360 sGroupId = sLocalId;
361 sLocalId = "";
363 else {
364 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
368 OString sText = pStackData->sText[ "en-US" ];
369 sText = helper::UnQuotHTML( sText );
371 common::writePoEntry(
372 "Cfgex", pOutputStream, sPath, pStackData->sResTyp,
373 sGroupId, sLocalId, sXComment, sText);
376 void CfgExport::WorkOnText(
377 OString &rText,
378 const OString &rIsoLang
381 if( !rIsoLang.isEmpty() ) rText = helper::UnQuotHTML( rText );
387 CfgMerge::CfgMerge(
388 const OString &rMergeSource, const OString &rOutputFile,
389 const OString &rFilename, const OString &rLanguage )
390 : sFilename( rFilename ),
391 bEnglish( false )
393 pOutputStream.open(
394 rOutputFile.getStr(), std::ios_base::out | std::ios_base::trunc);
395 if (!pOutputStream.is_open())
397 std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
398 std::exit(EXIT_FAILURE);
401 if (!rMergeSource.isEmpty())
403 pMergeDataFile.reset(new MergeDataFile(
404 rMergeSource, global::inputPathname, true ));
405 if (rLanguage.equalsIgnoreAsciiCase("ALL") )
407 aLanguages = pMergeDataFile->GetLanguages();
409 else aLanguages.push_back(rLanguage);
411 else
412 aLanguages.push_back(rLanguage);
415 CfgMerge::~CfgMerge()
417 pOutputStream.close();
420 void CfgMerge::WorkOnText(OString &, const OString& rLangIndex)
422 if ( !(pMergeDataFile && bLocalize) )
423 return;
425 if ( !pResData ) {
426 OString sLocalId = pStackData->sIdentifier;
427 OString sGroupId;
428 if ( aStack.size() == 1 ) {
429 sGroupId = sLocalId;
430 sLocalId.clear();
432 else {
433 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
436 pResData.reset( new ResData( sGroupId, sFilename ) );
437 pResData->sId = sLocalId;
438 pResData->sResTyp = pStackData->sResTyp;
441 if (rLangIndex.equalsIgnoreAsciiCase("en-US"))
442 bEnglish = true;
445 void CfgMerge::Output(const OString& rOutput)
447 pOutputStream << rOutput;
450 void CfgMerge::WorkOnResourceEnd()
453 if ( pMergeDataFile && pResData && bLocalize && bEnglish ) {
454 MergeEntrys *pEntrys = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData.get() );
455 if ( pEntrys ) {
456 OString sCur;
458 for( size_t i = 0; i < aLanguages.size(); ++i ){
459 sCur = aLanguages[ i ];
461 OString sContent;
462 pEntrys->GetText( sContent, sCur, true );
463 if (
464 ( !sCur.equalsIgnoreAsciiCase("en-US") ) && !sContent.isEmpty())
466 OString sTextTag = pStackData->sTextTag;
467 const sal_Int32 nLangAttributeStart{ sTextTag.indexOf( "xml:lang=" ) };
468 const sal_Int32 nLangStart{ sTextTag.indexOf( '"', nLangAttributeStart )+1 };
469 const sal_Int32 nLangEnd{ sTextTag.indexOf( '"', nLangStart ) };
470 OString sAdditionalLine{ "\t"
471 + sTextTag.replaceAt(nLangStart, nLangEnd-nLangStart, sCur)
472 + helper::QuotHTML(sContent)
473 + pStackData->sEndTextTag
474 + "\n"
475 + sLastWhitespace };
476 Output( sAdditionalLine );
481 pResData.reset();
482 bEnglish = false;
485 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */