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>
30 #include <rtl/strbuf.hxx>
34 #include <cfgmerge.hxx>
41 OString inputPathname
;
42 std::unique_ptr
< CfgParser
> parser
;
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) {
62 stderr
, "Error: Cannot open file \"%s\"\n",
63 global::inputPathname
.getStr() );
64 std::exit(EXIT_FAILURE
);
67 if (aArgs
.m_bMergeMode
) {
70 aArgs
.m_sMergeSrc
, aArgs
.m_sOutputFile
,
71 global::inputPathname
, aArgs
.m_sLanguage
));
75 aArgs
.m_sOutputFile
, global::inputPathname
));
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
);
100 CfgStack::~CfgStack()
104 OString
CfgStack::GetAccessPath( size_t nPos
)
106 OStringBuffer sReturn
;
107 for (size_t i
= 0; i
<= nPos
; ++i
)
111 sReturn
.append(maList
[i
]->GetIdentifier());
114 return sReturn
.makeStringAndClear();
117 CfgStackData
*CfgStack::GetStackData()
120 return maList
[maList
.size() - 1];
128 CfgParser::CfgParser()
129 : pStackData( nullptr ),
134 CfgParser::~CfgParser()
138 bool CfgParser::IsTokenClosed(const OString
&rToken
)
140 return rToken
[rToken
.getLength() - 2] == '/';
143 void CfgParser::AddText(
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
;
157 #pragma warning(disable: 4702) // unreachable code, bug in MSVC2015, it thinks the std::exit is unreachable
159 void CfgParser::ExecuteAnalyzedToken( int nToken
, char *pToken
)
161 OString
sToken( pToken
);
163 if ( sToken
== " " || sToken
== "\t" )
164 sLastWhitespace
+= sToken
;
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
:
181 sTokenName
= sToken
.getToken(1, '<').getToken(0, '>').
184 if ( !IsTokenClosed( sToken
)) {
187 case CFG_TOKEN_PACKAGE
:
188 sSearch
= "package-id=";
190 case CFG_TOKEN_COMPONENT
:
191 sSearch
= "component-id=";
193 case CFG_TOKEN_TEMPLATE
:
194 sSearch
= "template-id=";
196 case CFG_TOKEN_CONFIGNAME
:
197 sSearch
= "cfg:name=";
199 case CFG_TOKEN_OORNAME
:
200 sSearch
= "oor:name=";
203 case CFG_TOKEN_OORVALUE
:
204 sSearch
= "oor:value=";
206 case CFG_TEXT_START
: {
207 if ( sCurrentResTyp
!= sTokenName
) {
210 sCurrentResTyp
= sTokenName
;
212 OString sTemp
= sToken
.copy( sToken
.indexOf( "xml:lang=" ));
213 sCurrentIsoLang
= sTemp
.getToken(1, '"');
215 if ( sCurrentIsoLang
== NO_TRANSLATE_ISO
)
218 pStackData
->sTextTag
= sToken
;
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
) {
242 sCurrentResTyp
= sTokenName
;
248 sTokenName
= sToken
.getToken(1, '/').getToken(0, '>').
250 if ( aStack
.GetStackData() && ( aStack
.GetStackData()->GetTagType() == sTokenName
))
252 if (sCurrentText
.isEmpty())
255 pStackData
= aStack
.GetStackData();
259 const OString sError
{ "Misplaced close tag: " + sToken
+ " in file " + global::inputPathname
};
260 yyerror(sError
.getStr());
261 std::exit(EXIT_FAILURE
);
267 sCurrentText
+= sToken
;
271 case CFG_TOKEN_NO_TRANSLATE
:
276 if ( !sCurrentText
.isEmpty() && nToken
!= CFG_TEXTCHAR
)
278 AddText( sCurrentText
, sCurrentIsoLang
, sCurrentResTyp
);
279 Output( sCurrentText
);
280 sCurrentText
.clear();
281 pStackData
->sEndTextTag
= 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
);
301 if ( sToken
.indexOf( "package-id=" ) != -1 ) {
302 ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE
, pToken
);
304 } else if ( sToken
.indexOf( "component-id=" ) != -1 ) {
305 ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT
, pToken
);
307 } else if ( sToken
.indexOf( "template-id=" ) != -1 ) {
308 ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE
, pToken
);
310 } else if ( sToken
.indexOf( "cfg:name=" ) != -1 ) {
311 ExecuteAnalyzedToken( CFG_TOKEN_OORNAME
, pToken
);
313 } else if ( sToken
.indexOf( "oor:name=" ) != -1 ) {
314 ExecuteAnalyzedToken( CFG_TOKEN_OORNAME
, pToken
);
316 } else if ( sToken
.indexOf( "oor:value=" ) != -1 ) {
317 ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE
, pToken
);
322 ExecuteAnalyzedToken( nToken
, pToken
);
328 CfgExport::CfgExport(
329 const OString
&rOutputFile
,
330 const OString
&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()
353 if ( pStackData
->sText
["en-US"].isEmpty() )
356 OString sXComment
= pStackData
->sText
[OString("x-comment")];
357 OString sLocalId
= pStackData
->sIdentifier
;
359 if ( aStack
.size() == 1 ) {
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(
378 const OString
&rIsoLang
381 if( !rIsoLang
.isEmpty() ) rText
= helper::UnQuotHTML( rText
);
388 const OString
&rMergeSource
, const OString
&rOutputFile
,
389 const OString
&rFilename
, const OString
&rLanguage
)
390 : sFilename( rFilename
),
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
);
412 aLanguages
.push_back(rLanguage
);
415 CfgMerge::~CfgMerge()
417 pOutputStream
.close();
420 void CfgMerge::WorkOnText(OString
&, const OString
& rLangIndex
)
422 if ( !(pMergeDataFile
&& bLocalize
) )
426 OString sLocalId
= pStackData
->sIdentifier
;
428 if ( aStack
.size() == 1 ) {
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"))
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() );
458 for( size_t i
= 0; i
< aLanguages
.size(); ++i
){
459 sCur
= aLanguages
[ i
];
462 pEntrys
->GetText( sContent
, sCur
, true );
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
476 Output( sAdditionalLine
);
485 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */