tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / l10ntools / source / cfgmerge.cxx
blobf1afb41f0cfded198eb9478dee071bb366fb742c
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 <iostream>
28 #include <memory>
29 #include <rtl/strbuf.hxx>
30 #include <o3tl/string_view.hxx>
32 #include <helper.hxx>
33 #include <export.hxx>
34 #include <cfgmerge.hxx>
35 #include <utility>
36 #include <tokens.h>
38 namespace {
40 namespace global {
42 OString inputPathname;
43 std::unique_ptr< CfgParser > parser;
48 extern "C" {
50 FILE * init(int argc, char ** argv) {
52 common::HandledArgs aArgs;
53 if ( !common::handleArguments(argc, argv, aArgs) )
55 common::writeUsage("cfgex"_ostr,"*.xcu"_ostr);
56 std::exit(EXIT_FAILURE);
58 global::inputPathname = aArgs.m_sInputFile;
60 FILE * pFile = std::fopen(global::inputPathname.getStr(), "r");
61 if (pFile == nullptr) {
62 std::fprintf(
63 stderr, "Error: Cannot open file \"%s\"\n",
64 global::inputPathname.getStr() );
65 std::exit(EXIT_FAILURE);
68 if (aArgs.m_bMergeMode) {
69 global::parser.reset(
70 new CfgMerge(
71 aArgs.m_sMergeSrc, aArgs.m_sOutputFile,
72 global::inputPathname, aArgs.m_sLanguage ));
73 } else {
74 global::parser.reset(
75 new CfgExport(
76 aArgs.m_sOutputFile, global::inputPathname ));
79 return pFile;
82 void workOnTokenSet(int nTyp, char * pTokenText) {
83 global::parser->Execute( nTyp, pTokenText );
91 CfgStackData* CfgStack::Push(const OString &rTag, const OString &rId)
93 CfgStackData *pD = new CfgStackData( rTag, rId );
94 maList.push_back( pD );
95 return pD;
101 CfgStack::~CfgStack()
105 OString CfgStack::GetAccessPath( size_t nPos )
107 OStringBuffer sReturn;
108 for (size_t i = 0; i <= nPos; ++i)
110 if (i)
111 sReturn.append('.');
112 sReturn.append(maList[i]->GetIdentifier());
115 return sReturn.makeStringAndClear();
118 CfgStackData *CfgStack::GetStackData()
120 if (!maList.empty())
121 return maList[maList.size() - 1];
122 else
123 return nullptr;
129 CfgParser::CfgParser()
130 : pStackData( nullptr ),
131 bLocalize( false )
135 CfgParser::~CfgParser()
137 // CfgParser::ExecuteAnalyzedToken pushes onto aStack some XML entities (like XML and document
138 // type declarations) that don't have corresponding closing tags, so will never be popped off
139 // aStack again. But not pushing them onto aStack in the first place would change the
140 // identifiers computed in CfgStack::GetAccessPath, which could make the existing translation
141 // mechanisms fail. So, for simplicity, and short of more thorough input error checking, take
142 // into account here all the patterns of such declarations encountered during a build and during
143 // `make translations` (some inputs start with no such declarations at all, some inputs start
144 // with an XML declaration, and some inputs start with an XML declaration followed by a document
145 // type declaration) and pop any corresponding remaining excess elements off aStack:
146 if (aStack.size() == 2 && aStack.GetStackData()->GetTagType() == "!DOCTYPE") {
147 aStack.Pop();
149 if (aStack.size() == 1 && aStack.GetStackData()->GetTagType() == "?xml") {
150 aStack.Pop();
154 bool CfgParser::IsTokenClosed(std::string_view rToken)
156 return rToken[rToken.size() - 2] == '/';
159 void CfgParser::AddText(
160 OString &rText,
161 const OString &rIsoLang,
162 const OString &rResTyp )
164 rText = rText.replaceAll(OString('\n'), OString()).
165 replaceAll(OString('\r'), OString()).
166 replaceAll(OString('\t'), OString());
167 pStackData->sResTyp = rResTyp;
168 WorkOnText( rText, rIsoLang );
169 pStackData->sText[ rIsoLang ] = rText;
172 #if defined _MSC_VER
173 #pragma warning(disable: 4702) // unreachable code, bug in MSVC2015, it thinks the std::exit is unreachable
174 #endif
175 void CfgParser::ExecuteAnalyzedToken( int nToken, char *pToken )
177 OString sToken( pToken );
179 if ( sToken == " " || sToken == "\t" )
180 sLastWhitespace += sToken;
182 OString sTokenName;
184 bool bOutput = true;
186 switch ( nToken ) {
187 case CFG_TOKEN_PACKAGE:
188 case CFG_TOKEN_COMPONENT:
189 case CFG_TOKEN_TEMPLATE:
190 case CFG_TOKEN_CONFIGNAME:
191 case CFG_TOKEN_OORNAME:
192 case CFG_TOKEN_OORVALUE:
193 case CFG_TAG:
194 case ANYTOKEN:
195 case CFG_TEXT_START:
197 sTokenName = sToken.getToken(1, '<').getToken(0, '>').
198 getToken(0, ' ');
200 if ( !IsTokenClosed( sToken )) {
201 OString sSearch;
202 switch ( nToken ) {
203 case CFG_TOKEN_PACKAGE:
204 sSearch = "package-id="_ostr;
205 break;
206 case CFG_TOKEN_COMPONENT:
207 sSearch = "component-id="_ostr;
208 break;
209 case CFG_TOKEN_TEMPLATE:
210 sSearch = "template-id="_ostr;
211 break;
212 case CFG_TOKEN_CONFIGNAME:
213 sSearch = "cfg:name="_ostr;
214 break;
215 case CFG_TOKEN_OORNAME:
216 sSearch = "oor:name="_ostr;
217 bLocalize = true;
218 break;
219 case CFG_TOKEN_OORVALUE:
220 sSearch = "oor:value="_ostr;
221 break;
222 case CFG_TEXT_START: {
223 if ( sCurrentResTyp != sTokenName ) {
224 WorkOnResourceEnd();
226 sCurrentResTyp = sTokenName;
228 OString sTemp = sToken.copy( sToken.indexOf( "xml:lang=" ));
229 sCurrentIsoLang = sTemp.getToken(1, '"');
231 if ( sCurrentIsoLang == NO_TRANSLATE_ISO )
232 bLocalize = false;
234 pStackData->sTextTag = sToken;
236 sCurrentText = ""_ostr;
238 break;
240 OString sTokenId;
241 if ( !sSearch.isEmpty())
243 OString sTemp = sToken.copy( sToken.indexOf( sSearch ));
244 sTokenId = sTemp.getToken(1, '"');
246 pStackData = aStack.Push( sTokenName, sTokenId );
248 if ( sSearch == "cfg:name=" ) {
249 OString sTemp( sToken.toAsciiUpperCase() );
250 bLocalize = sTemp.indexOf("CFG:TYPE=\"STRING\"")>=0
251 && sTemp.indexOf( "CFG:LOCALIZED=\"TRUE\"" )>=0;
254 else if ( sTokenName == "label" ) {
255 if ( sCurrentResTyp != sTokenName ) {
256 WorkOnResourceEnd();
258 sCurrentResTyp = sTokenName;
261 break;
262 case CFG_CLOSETAG:
264 sTokenName = sToken.getToken(1, '/').getToken(0, '>').
265 getToken(0, ' ');
266 if ( aStack.GetStackData() && ( aStack.GetStackData()->GetTagType() == sTokenName ))
268 if (sCurrentText.isEmpty())
269 WorkOnResourceEnd();
270 aStack.Pop();
271 pStackData = aStack.GetStackData();
273 else
275 const OString sError{ "Misplaced close tag: " + sToken + " in file " + global::inputPathname };
276 yyerror(sError.getStr());
277 std::exit(EXIT_FAILURE);
280 break;
282 case CFG_TEXTCHAR:
283 sCurrentText += sToken;
284 bOutput = false;
285 break;
287 case CFG_TOKEN_NO_TRANSLATE:
288 bLocalize = false;
289 break;
292 if ( !sCurrentText.isEmpty() && nToken != CFG_TEXTCHAR )
294 AddText( sCurrentText, sCurrentIsoLang, sCurrentResTyp );
295 Output( sCurrentText );
296 sCurrentText.clear();
297 pStackData->sEndTextTag = sToken;
300 if ( bOutput )
301 Output( sToken );
303 if ( sToken != " " && sToken != "\t" )
304 sLastWhitespace = ""_ostr;
307 void CfgExport::Output(const OString&)
311 void CfgParser::Execute( int nToken, char * pToken )
313 OString sToken( pToken );
315 switch ( nToken ) {
316 case CFG_TAG:
317 if ( sToken.indexOf( "package-id=" ) != -1 ) {
318 ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE, pToken );
319 return;
320 } else if ( sToken.indexOf( "component-id=" ) != -1 ) {
321 ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT, pToken );
322 return;
323 } else if ( sToken.indexOf( "template-id=" ) != -1 ) {
324 ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE, pToken );
325 return;
326 } else if ( sToken.indexOf( "cfg:name=" ) != -1 ) {
327 ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
328 return;
329 } else if ( sToken.indexOf( "oor:name=" ) != -1 ) {
330 ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
331 return;
332 } else if ( sToken.indexOf( "oor:value=" ) != -1 ) {
333 ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE, pToken );
334 return;
336 break;
338 ExecuteAnalyzedToken( nToken, pToken );
344 CfgExport::CfgExport(
345 const OString &rOutputFile,
346 OString sFilePath )
347 : sPath(std::move( sFilePath ))
349 pOutputStream.open( rOutputFile, PoOfstream::APP );
350 if (!pOutputStream.isOpen())
352 std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
353 std::exit(EXIT_FAILURE);
357 CfgExport::~CfgExport()
359 pOutputStream.close();
363 void CfgExport::WorkOnResourceEnd()
365 if ( !bLocalize )
366 return;
368 if ( pStackData->sText["en-US"_ostr].isEmpty() )
369 return;
371 OString sXComment = pStackData->sText["x-comment"_ostr];
372 OString sLocalId = pStackData->sIdentifier;
373 OString sGroupId;
374 if ( aStack.size() == 1 ) {
375 sGroupId = sLocalId;
376 sLocalId = ""_ostr;
378 else {
379 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
383 OString sText = pStackData->sText[ "en-US"_ostr ];
384 sText = helper::UnQuotHTML( sText );
386 common::writePoEntry(
387 "Cfgex"_ostr, pOutputStream, sPath, pStackData->sResTyp,
388 sGroupId, sLocalId, sXComment, sText);
391 void CfgExport::WorkOnText(
392 OString &rText,
393 const OString &rIsoLang
396 if( !rIsoLang.isEmpty() ) rText = helper::UnQuotHTML( rText );
402 CfgMerge::CfgMerge(
403 const OString &rMergeSource, const OString &rOutputFile,
404 OString _sFilename, const OString &rLanguage )
405 : sFilename(std::move( _sFilename )),
406 bEnglish( false )
408 pOutputStream.open(
409 rOutputFile.getStr(), std::ios_base::out | std::ios_base::trunc);
410 if (!pOutputStream.is_open())
412 std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
413 std::exit(EXIT_FAILURE);
416 if (!rMergeSource.isEmpty())
418 pMergeDataFile.reset(new MergeDataFile(
419 rMergeSource, global::inputPathname, true ));
420 if (rLanguage.equalsIgnoreAsciiCase("ALL") )
422 aLanguages = pMergeDataFile->GetLanguages();
424 else aLanguages.push_back(rLanguage);
426 else
427 aLanguages.push_back(rLanguage);
430 CfgMerge::~CfgMerge()
432 pOutputStream.close();
435 void CfgMerge::WorkOnText(OString &, const OString& rLangIndex)
437 if ( !(pMergeDataFile && bLocalize) )
438 return;
440 if ( !pResData ) {
441 OString sLocalId = pStackData->sIdentifier;
442 OString sGroupId;
443 if ( aStack.size() == 1 ) {
444 sGroupId = sLocalId;
445 sLocalId.clear();
447 else {
448 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
451 pResData.reset( new ResData( sGroupId, sFilename ) );
452 pResData->sId = sLocalId;
453 pResData->sResTyp = pStackData->sResTyp;
456 if (rLangIndex.equalsIgnoreAsciiCase("en-US"))
457 bEnglish = true;
460 void CfgMerge::Output(const OString& rOutput)
462 pOutputStream << rOutput;
465 void CfgMerge::WorkOnResourceEnd()
468 if ( pMergeDataFile && pResData && bLocalize && bEnglish ) {
469 MergeEntrys *pEntrys = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData.get() );
470 if ( pEntrys ) {
471 OString sCur;
473 for( size_t i = 0; i < aLanguages.size(); ++i ){
474 sCur = aLanguages[ i ];
476 OString sContent;
477 pEntrys->GetText( sContent, sCur, true );
478 if (
479 ( !sCur.equalsIgnoreAsciiCase("en-US") ) && !sContent.isEmpty())
481 OString sTextTag = pStackData->sTextTag;
482 const sal_Int32 nLangAttributeStart{ sTextTag.indexOf( "xml:lang=" ) };
483 const sal_Int32 nLangStart{ sTextTag.indexOf( '"', nLangAttributeStart )+1 };
484 const sal_Int32 nLangEnd{ sTextTag.indexOf( '"', nLangStart ) };
485 OString sAdditionalLine{ "\t"
486 + sTextTag.replaceAt(nLangStart, nLangEnd-nLangStart, sCur)
487 + helper::QuotHTML(sContent)
488 + pStackData->sEndTextTag
489 + "\n"
490 + sLastWhitespace };
491 Output( sAdditionalLine );
496 pResData.reset();
497 bEnglish = false;
500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */