LibreOffice Module l10ntools (master) 1
cfgmerge.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <cfglex.hxx>
23#include <common.hxx>
24
25#include <cstdio>
26#include <cstdlib>
27#include <iostream>
28#include <memory>
29#include <rtl/strbuf.hxx>
30#include <o3tl/string_view.hxx>
31
32#include <helper.hxx>
33#include <export.hxx>
34#include <cfgmerge.hxx>
35#include <utility>
36#include <tokens.h>
37
38namespace {
39
40namespace global {
41
42OString inputPathname;
43std::unique_ptr< CfgParser > parser;
44
45}
46}
47
48extern "C" {
49
50FILE * init(int argc, char ** argv) {
51
53 if ( !common::handleArguments(argc, argv, aArgs) )
54 {
55 common::writeUsage("cfgex","*.xcu");
56 std::exit(EXIT_FAILURE);
57 }
58 global::inputPathname = aArgs.m_sInputFile;
59
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);
66 }
67
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 ));
77 }
78
79 return pFile;
80}
81
82void workOnTokenSet(int nTyp, char * pTokenText) {
83 global::parser->Execute( nTyp, pTokenText );
84}
85
86}
87
88
89
90
91CfgStackData* CfgStack::Push(const OString &rTag, const OString &rId)
92{
93 CfgStackData *pD = new CfgStackData( rTag, rId );
94 maList.push_back( pD );
95 return pD;
96}
97
98
99
100
102{
103}
104
105OString CfgStack::GetAccessPath( size_t nPos )
106{
107 OStringBuffer sReturn;
108 for (size_t i = 0; i <= nPos; ++i)
109 {
110 if (i)
111 sReturn.append('.');
112 sReturn.append(maList[i]->GetIdentifier());
113 }
114
115 return sReturn.makeStringAndClear();
116}
117
119{
120 if (!maList.empty())
121 return maList[maList.size() - 1];
122 else
123 return nullptr;
124}
125
126
127
128
130 : pStackData( nullptr ),
131 bLocalize( false )
132{
133}
134
136{
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();
148 }
149 if (aStack.size() == 1 && aStack.GetStackData()->GetTagType() == "?xml") {
150 aStack.Pop();
151 }
152}
153
154bool CfgParser::IsTokenClosed(std::string_view rToken)
155{
156 return rToken[rToken.size() - 2] == '/';
157}
158
160 OString &rText,
161 const OString &rIsoLang,
162 const OString &rResTyp )
163{
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;
170}
171
172#if defined _MSC_VER
173#pragma warning(disable: 4702) // unreachable code, bug in MSVC2015, it thinks the std::exit is unreachable
174#endif
175void CfgParser::ExecuteAnalyzedToken( int nToken, char *pToken )
176{
177 OString sToken( pToken );
178
179 if ( sToken == " " || sToken == "\t" )
180 sLastWhitespace += sToken;
181
182 OString sTokenName;
183
184 bool bOutput = true;
185
186 switch ( nToken ) {
193 case CFG_TAG:
194 case ANYTOKEN:
195 case CFG_TEXT_START:
196 {
197 sTokenName = sToken.getToken(1, '<').getToken(0, '>').
198 getToken(0, ' ');
199
200 if ( !IsTokenClosed( sToken )) {
201 OString sSearch;
202 switch ( nToken ) {
204 sSearch = "package-id=";
205 break;
207 sSearch = "component-id=";
208 break;
210 sSearch = "template-id=";
211 break;
213 sSearch = "cfg:name=";
214 break;
216 sSearch = "oor:name=";
217 bLocalize = true;
218 break;
220 sSearch = "oor:value=";
221 break;
222 case CFG_TEXT_START: {
223 if ( sCurrentResTyp != sTokenName ) {
225 }
226 sCurrentResTyp = sTokenName;
227
228 OString sTemp = sToken.copy( sToken.indexOf( "xml:lang=" ));
229 sCurrentIsoLang = sTemp.getToken(1, '"');
230
232 bLocalize = false;
233
234 pStackData->sTextTag = sToken;
235
236 sCurrentText = "";
237 }
238 break;
239 }
240 OString sTokenId;
241 if ( !sSearch.isEmpty())
242 {
243 OString sTemp = sToken.copy( sToken.indexOf( sSearch ));
244 sTokenId = sTemp.getToken(1, '"');
245 }
246 pStackData = aStack.Push( sTokenName, sTokenId );
247
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;
252 }
253 }
254 else if ( sTokenName == "label" ) {
255 if ( sCurrentResTyp != sTokenName ) {
257 }
258 sCurrentResTyp = sTokenName;
259 }
260 }
261 break;
262 case CFG_CLOSETAG:
263 {
264 sTokenName = sToken.getToken(1, '/').getToken(0, '>').
265 getToken(0, ' ');
266 if ( aStack.GetStackData() && ( aStack.GetStackData()->GetTagType() == sTokenName ))
267 {
268 if (sCurrentText.isEmpty())
270 aStack.Pop();
272 }
273 else
274 {
275 const OString sError{ "Misplaced close tag: " + sToken + " in file " + global::inputPathname };
276 yyerror(sError.getStr());
277 std::exit(EXIT_FAILURE);
278 }
279 }
280 break;
281
282 case CFG_TEXTCHAR:
283 sCurrentText += sToken;
284 bOutput = false;
285 break;
286
288 bLocalize = false;
289 break;
290 }
291
292 if ( !sCurrentText.isEmpty() && nToken != CFG_TEXTCHAR )
293 {
296 sCurrentText.clear();
297 pStackData->sEndTextTag = sToken;
298 }
299
300 if ( bOutput )
301 Output( sToken );
302
303 if ( sToken != " " && sToken != "\t" )
304 sLastWhitespace = "";
305}
306
307void CfgExport::Output(const OString&)
308{
309}
310
311void CfgParser::Execute( int nToken, char * pToken )
312{
313 OString sToken( pToken );
314
315 switch ( nToken ) {
316 case CFG_TAG:
317 if ( sToken.indexOf( "package-id=" ) != -1 ) {
319 return;
320 } else if ( sToken.indexOf( "component-id=" ) != -1 ) {
322 return;
323 } else if ( sToken.indexOf( "template-id=" ) != -1 ) {
325 return;
326 } else if ( sToken.indexOf( "cfg:name=" ) != -1 ) {
328 return;
329 } else if ( sToken.indexOf( "oor:name=" ) != -1 ) {
331 return;
332 } else if ( sToken.indexOf( "oor:value=" ) != -1 ) {
334 return;
335 }
336 break;
337 }
338 ExecuteAnalyzedToken( nToken, pToken );
339}
340
341
342
343
345 const OString &rOutputFile,
346 OString sFilePath )
347 : sPath(std::move( sFilePath ))
348{
349 pOutputStream.open( rOutputFile, PoOfstream::APP );
350 if (!pOutputStream.isOpen())
351 {
352 std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
353 std::exit(EXIT_FAILURE);
354 }
355}
356
358{
360}
361
362
364{
365 if ( !bLocalize )
366 return;
367
368 if ( pStackData->sText["en-US"].isEmpty() )
369 return;
370
371 OString sXComment = pStackData->sText[OString("x-comment")];
372 OString sLocalId = pStackData->sIdentifier;
373 OString sGroupId;
374 if ( aStack.size() == 1 ) {
375 sGroupId = sLocalId;
376 sLocalId = "";
377 }
378 else {
379 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
380 }
381
382
383 OString sText = pStackData->sText[ "en-US" ];
384 sText = helper::UnQuotHTML( sText );
385
388 sGroupId, sLocalId, sXComment, sText);
389}
390
392 OString &rText,
393 const OString &rIsoLang
394)
395{
396 if( !rIsoLang.isEmpty() ) rText = helper::UnQuotHTML( rText );
397}
398
399
400
401
403 const OString &rMergeSource, const OString &rOutputFile,
404 OString _sFilename, const OString &rLanguage )
405 : sFilename(std::move( _sFilename )),
406 bEnglish( false )
407{
408 pOutputStream.open(
409 rOutputFile.getStr(), std::ios_base::out | std::ios_base::trunc);
410 if (!pOutputStream.is_open())
411 {
412 std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
413 std::exit(EXIT_FAILURE);
414 }
415
416 if (!rMergeSource.isEmpty())
417 {
419 rMergeSource, global::inputPathname, true ));
420 if (rLanguage.equalsIgnoreAsciiCase("ALL") )
421 {
422 aLanguages = pMergeDataFile->GetLanguages();
423 }
424 else aLanguages.push_back(rLanguage);
425 }
426 else
427 aLanguages.push_back(rLanguage);
428}
429
431{
432 pOutputStream.close();
433}
434
435void CfgMerge::WorkOnText(OString &, const OString& rLangIndex)
436{
437 if ( !(pMergeDataFile && bLocalize) )
438 return;
439
440 if ( !pResData ) {
441 OString sLocalId = pStackData->sIdentifier;
442 OString sGroupId;
443 if ( aStack.size() == 1 ) {
444 sGroupId = sLocalId;
445 sLocalId.clear();
446 }
447 else {
448 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
449 }
450
451 pResData.reset( new ResData( sGroupId, sFilename ) );
452 pResData->sId = sLocalId;
453 pResData->sResTyp = pStackData->sResTyp;
454 }
455
456 if (rLangIndex.equalsIgnoreAsciiCase("en-US"))
457 bEnglish = true;
458}
459
460void CfgMerge::Output(const OString& rOutput)
461{
462 pOutputStream << rOutput;
463}
464
466{
467
468 if ( pMergeDataFile && pResData && bLocalize && bEnglish ) {
469 MergeEntrys *pEntrys = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData.get() );
470 if ( pEntrys ) {
471 OString sCur;
472
473 for( size_t i = 0; i < aLanguages.size(); ++i ){
474 sCur = aLanguages[ i ];
475
476 OString sContent;
477 pEntrys->GetText( sContent, sCur, true );
478 if (
479 ( !sCur.equalsIgnoreAsciiCase("en-US") ) && !sContent.isEmpty())
480 {
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)
489 + "\n"
490 + sLastWhitespace };
491 Output( sAdditionalLine );
492 }
493 }
494 }
495 }
496 pResData.reset();
497 bEnglish = false;
498}
499
500/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void yyerror(const char *s)
Definition: cfglex.l:150
void workOnTokenSet(int nTyp, char *pTokenText)
Definition: cfgmerge.cxx:82
FILE * init(int argc, char **argv)
Definition: cfgmerge.cxx:50
Export strings from *.xcu files.
Definition: cfgmerge.hxx:133
PoOfstream pOutputStream
Definition: cfgmerge.hxx:136
virtual void WorkOnText(OString &rText, const OString &rIsoLang) override
Definition: cfgmerge.cxx:391
virtual ~CfgExport() override
Definition: cfgmerge.cxx:357
OString sPath
Definition: cfgmerge.hxx:135
void Output(const OString &rOutput) override
Definition: cfgmerge.cxx:307
void WorkOnResourceEnd() override
Definition: cfgmerge.cxx:363
CfgExport(const OString &rOutputFile, OString sFilePath)
Definition: cfgmerge.cxx:344
Merge strings to *.xcu files.
Definition: cfgmerge.hxx:156
std::vector< OString > aLanguages
Definition: cfgmerge.hxx:159
virtual void WorkOnText(OString &rText, const OString &rLangIndex) override
Definition: cfgmerge.cxx:435
CfgMerge(const OString &rMergeSource, const OString &rOutputFile, OString sFilename, const OString &rLanguage)
Definition: cfgmerge.cxx:402
OString sFilename
Definition: cfgmerge.hxx:162
virtual ~CfgMerge() override
Definition: cfgmerge.cxx:430
void Output(const OString &rOutput) override
Definition: cfgmerge.cxx:460
std::unique_ptr< ResData > pResData
Definition: cfgmerge.hxx:160
bool bEnglish
Definition: cfgmerge.hxx:163
std::ofstream pOutputStream
Definition: cfgmerge.hxx:165
std::unique_ptr< MergeDataFile > pMergeDataFile
Definition: cfgmerge.hxx:158
void WorkOnResourceEnd() override
Definition: cfgmerge.cxx:465
void ExecuteAnalyzedToken(int nToken, char *pToken)
Definition: cfgmerge.cxx:175
OString sLastWhitespace
Definition: cfgmerge.hxx:100
virtual void WorkOnText(OString &rText, const OString &rLangIndex)=0
virtual ~CfgParser()
Definition: cfgmerge.cxx:135
virtual void Output(const OString &rOutput)=0
OString sCurrentResTyp
Definition: cfgmerge.hxx:96
CfgStackData * pStackData
Definition: cfgmerge.hxx:103
CfgStack aStack
Definition: cfgmerge.hxx:102
OString sCurrentIsoLang
Definition: cfgmerge.hxx:97
static bool IsTokenClosed(std::string_view rToken)
Definition: cfgmerge.cxx:154
bool bLocalize
Definition: cfgmerge.hxx:105
virtual void WorkOnResourceEnd()=0
void AddText(OString &rText, const OString &rIsoLang, const OString &rResTyp)
Definition: cfgmerge.cxx:159
OString sCurrentText
Definition: cfgmerge.hxx:98
void Execute(int nToken, char *pToken)
Definition: cfgmerge.cxx:311
const OString & GetTagType() const
Definition: cfgmerge.hxx:58
OStringHashMap sText
Definition: cfgmerge.hxx:52
OString sTextTag
Definition: cfgmerge.hxx:49
OString sIdentifier
Definition: cfgmerge.hxx:45
OString sResTyp
Definition: cfgmerge.hxx:47
OString sEndTextTag
Definition: cfgmerge.hxx:50
void Pop()
Definition: cfgmerge.hxx:76
CfgStackData * GetStackData()
Definition: cfgmerge.cxx:118
OString GetAccessPath(size_t nPos)
Definition: cfgmerge.cxx:105
size_t size() const
Definition: cfgmerge.hxx:89
std::vector< CfgStackData * > maList
Definition: cfgmerge.hxx:69
CfgStackData * Push(const OString &rTag, const OString &rId)
Definition: cfgmerge.cxx:91
Purpose: holds information of data to merge, read from PO file.
Definition: export.hxx:114
Purpose: holds information of data to merge.
Definition: export.hxx:77
bool GetText(OString &rReturn, const OString &nLangIndex, bool bDel=false)
Definition: merge.cxx:87
void open(const OString &rFileName, OpenMode aMode=TRUNC)
Definition: po.cxx:498
bool isOpen() const
Definition: po.hxx:116
void close()
Definition: po.cxx:515
@ APP
Definition: po.hxx:109
Purpose: holds mandatory data to export a single res.
Definition: export.hxx:55
#define NO_TRANSLATE_ISO
Definition: export.hxx:37
sal_uInt16 nPos
sal_Int32 getToken(const Context &rContext, const char *pToken)
void writeUsage(const OString &rName, const OString &rFileType)
Write out a help about usage.
Definition: common.cxx:97
void writePoEntry(const OString &rExecutable, PoOfstream &rPoStream, const OString &rSourceFile, std::string_view rResType, const OString &rGroupId, const OString &rLocalId, const OString &rHelpText, const OString &rText, const PoEntry::TYPE eType)
Write out a PoEntry with attention to exceptions.
Definition: common.cxx:110
bool handleArguments(int argc, char *argv[], HandledArgs &o_aHandledArgs)
Handle command line parameters.
Definition: common.cxx:25
int i
OString QuotHTML(std::string_view rString)
Convert special characters to XML entity references.
Definition: helper.cxx:69
OString UnQuotHTML(std::string_view rString)
Convert XML entity references to single characters.
Definition: helper.cxx:102
parser
DefTokenId nToken
Result type of handleArguments()
Definition: common.hxx:26
OString m_sMergeSrc
Definition: common.hxx:29
OString m_sOutputFile
Definition: common.hxx:28
OString m_sLanguage
Definition: common.hxx:30
OString m_sInputFile
Definition: common.hxx:27
#define CFG_TOKEN_OORVALUE
Definition: tokens.h:73
#define CFG_TOKEN_COMPONENT
Definition: tokens.h:69
#define CFG_TOKEN_PACKAGE
Definition: tokens.h:68
#define CFG_TEXT_START
Definition: tokens.h:63
#define CFG_TOKEN_TEMPLATE
Definition: tokens.h:71
#define CFG_TOKEN_CONFIGNAME
Definition: tokens.h:70
#define CFG_TOKEN_OORNAME
Definition: tokens.h:72
#define ANYTOKEN
Definition: tokens.h:31
#define CFG_TEXTCHAR
Definition: tokens.h:65
#define CFG_CLOSETAG
Definition: tokens.h:66
#define CFG_TAG
Definition: tokens.h:62
#define CFG_TOKEN_NO_TRANSLATE
Definition: tokens.h:74