lok: add character parameter to renderFont
[LibreOffice.git] / l10ntools / source / cfgmerge.cxx
blob20a6f0deac4710b57eee85a44214d8bc167caa1b
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 void yyerror(char const *);
39 namespace {
41 namespace global {
43 OString inputPathname;
44 std::unique_ptr< CfgParser > parser;
49 extern "C" {
51 FILE * init(int argc, char ** argv) {
53 common::HandledArgs aArgs;
54 if ( !common::handleArguments(argc, argv, aArgs) )
56 common::writeUsage("cfgex","*.xcu");
57 std::exit(EXIT_FAILURE);
59 global::inputPathname = aArgs.m_sInputFile;
61 FILE * pFile = std::fopen(global::inputPathname.getStr(), "r");
62 if (pFile == nullptr) {
63 std::fprintf(
64 stderr, "Error: Cannot open file \"%s\"\n",
65 global::inputPathname.getStr() );
66 std::exit(EXIT_FAILURE);
69 if (aArgs.m_bMergeMode) {
70 global::parser.reset(
71 new CfgMerge(
72 aArgs.m_sMergeSrc, aArgs.m_sOutputFile,
73 global::inputPathname, aArgs.m_sLanguage ));
74 } else {
75 global::parser.reset(
76 new CfgExport(
77 aArgs.m_sOutputFile, global::inputPathname ));
80 return pFile;
83 void workOnTokenSet(int nTyp, char * pTokenText) {
84 global::parser->Execute( nTyp, pTokenText );
90 // class CfgStackData
93 CfgStackData* CfgStack::Push(const OString &rTag, const OString &rId)
95 CfgStackData *pD = new CfgStackData( rTag, rId );
96 maList.push_back( pD );
97 return pD;
101 // class CfgStack
104 CfgStack::~CfgStack()
106 for ( size_t i = 0, n = maList.size(); i < n; i++ )
107 delete maList[ i ];
108 maList.clear();
111 OString CfgStack::GetAccessPath( size_t nPos )
113 OStringBuffer sReturn;
114 for (size_t i = 0; i <= nPos; ++i)
116 if (i)
117 sReturn.append('.');
118 sReturn.append(maList[i]->GetIdentifier());
121 return sReturn.makeStringAndClear();
124 CfgStackData *CfgStack::GetStackData()
126 if (!maList.empty())
127 return maList[maList.size() - 1];
128 else
129 return nullptr;
133 // class CfgParser
136 CfgParser::CfgParser()
137 : pStackData( nullptr ),
138 bLocalize( false )
142 CfgParser::~CfgParser()
146 bool CfgParser::IsTokenClosed(const OString &rToken)
148 return rToken[rToken.getLength() - 2] == '/';
151 void CfgParser::AddText(
152 OString &rText,
153 const OString &rIsoLang,
154 const OString &rResTyp )
156 rText = rText.replaceAll(OString('\n'), OString()).
157 replaceAll(OString('\r'), OString()).
158 replaceAll(OString('\t'), OString());
159 pStackData->sResTyp = rResTyp;
160 WorkOnText( rText, rIsoLang );
161 pStackData->sText[ rIsoLang ] = rText;
164 void CfgParser::ExecuteAnalyzedToken( int nToken, char *pToken )
166 OString sToken( pToken );
168 if ( sToken == " " || sToken == "\t" )
169 sLastWhitespace += sToken;
171 OString sTokenName;
172 OString sTokenId;
174 bool bOutput = true;
176 switch ( nToken ) {
177 case CFG_TOKEN_PACKAGE:
178 case CFG_TOKEN_COMPONENT:
179 case CFG_TOKEN_TEMPLATE:
180 case CFG_TOKEN_CONFIGNAME:
181 case CFG_TOKEN_OORNAME:
182 case CFG_TOKEN_OORVALUE:
183 case CFG_TAG:
184 case ANYTOKEN:
185 case CFG_TEXT_START:
187 sTokenName = sToken.getToken(1, '<').getToken(0, '>').
188 getToken(0, ' ');
190 if ( !IsTokenClosed( sToken )) {
191 OString sSearch;
192 switch ( nToken ) {
193 case CFG_TOKEN_PACKAGE:
194 sSearch = "package-id=";
195 break;
196 case CFG_TOKEN_COMPONENT:
197 sSearch = "component-id=";
198 break;
199 case CFG_TOKEN_TEMPLATE:
200 sSearch = "template-id=";
201 break;
202 case CFG_TOKEN_CONFIGNAME:
203 sSearch = "cfg:name=";
204 break;
205 case CFG_TOKEN_OORNAME:
206 sSearch = "oor:name=";
207 bLocalize = true;
208 break;
209 case CFG_TOKEN_OORVALUE:
210 sSearch = "oor:value=";
211 break;
212 case CFG_TEXT_START: {
213 if ( sCurrentResTyp != sTokenName ) {
214 WorkOnResourceEnd();
216 sCurrentResTyp = sTokenName;
218 OString sTemp = sToken.copy( sToken.indexOf( "xml:lang=" ));
219 sCurrentIsoLang = sTemp.getToken(1, '"');
221 if ( sCurrentIsoLang == NO_TRANSLATE_ISO )
222 bLocalize = false;
224 pStackData->sTextTag = sToken;
226 sCurrentText = "";
228 break;
230 if ( !sSearch.isEmpty())
232 OString sTemp = sToken.copy( sToken.indexOf( sSearch ));
233 sTokenId = sTemp.getToken(1, '"');
235 pStackData = aStack.Push( sTokenName, sTokenId );
237 if ( sSearch == "cfg:name=" ) {
238 OString sTemp( sToken.toAsciiUpperCase() );
239 bLocalize = (( sTemp.indexOf( "CFG:TYPE=\"STRING\"" ) != -1 ) &&
240 ( sTemp.indexOf( "CFG:LOCALIZED=\"sal_True\"" ) != -1 ));
243 else if ( sTokenName == "label" ) {
244 if ( sCurrentResTyp != sTokenName ) {
245 WorkOnResourceEnd();
247 sCurrentResTyp = sTokenName;
250 break;
251 case CFG_CLOSETAG:
253 sTokenName = sToken.getToken(1, '/').getToken(0, '>').
254 getToken(0, ' ');
255 if ( aStack.GetStackData() && ( aStack.GetStackData()->GetTagType() == sTokenName ))
257 if (sCurrentText.isEmpty())
258 WorkOnResourceEnd();
259 aStack.Pop();
260 pStackData = aStack.GetStackData();
262 else
264 OString sError( "Misplaced close tag: " );
265 OString sInFile(" in file ");
266 sError += sToken;
267 sError += sInFile;
268 sError += global::inputPathname;
269 yyerror(sError.getStr());
270 std::exit(EXIT_FAILURE);
273 break;
275 case CFG_TEXTCHAR:
276 sCurrentText += sToken;
277 bOutput = false;
278 break;
280 case CFG_TOKEN_NO_TRANSLATE:
281 bLocalize = false;
282 break;
285 if ( !sCurrentText.isEmpty() && nToken != CFG_TEXTCHAR )
287 AddText( sCurrentText, sCurrentIsoLang, sCurrentResTyp );
288 Output( sCurrentText );
289 sCurrentText.clear();
290 pStackData->sEndTextTag = sToken;
293 if ( bOutput )
294 Output( sToken );
296 if ( sToken != " " && sToken != "\t" )
297 sLastWhitespace = "";
300 void CfgExport::Output(const OString&)
304 void CfgParser::Execute( int nToken, char * pToken )
306 OString sToken( pToken );
308 switch ( nToken ) {
309 case CFG_TAG:
310 if ( sToken.indexOf( "package-id=" ) != -1 ) {
311 ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE, pToken );
312 return;
313 } else if ( sToken.indexOf( "component-id=" ) != -1 ) {
314 ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT, pToken );
315 return;
316 } else if ( sToken.indexOf( "template-id=" ) != -1 ) {
317 ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE, pToken );
318 return;
319 } else if ( sToken.indexOf( "cfg:name=" ) != -1 ) {
320 ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
321 return;
322 } else if ( sToken.indexOf( "oor:name=" ) != -1 ) {
323 ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
324 return;
325 } else if ( sToken.indexOf( "oor:value=" ) != -1 ) {
326 ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE, pToken );
327 return;
329 break;
331 ExecuteAnalyzedToken( nToken, pToken );
335 // class CfgExport
338 CfgExport::CfgExport(
339 const OString &rOutputFile,
340 const OString &rFilePath )
342 : sPath( rFilePath )
344 pOutputStream.open( rOutputFile, PoOfstream::APP );
345 if (!pOutputStream.isOpen())
347 std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
348 std::exit(EXIT_FAILURE);
352 CfgExport::~CfgExport()
354 pOutputStream.close();
358 void CfgExport::WorkOnResourceEnd()
360 if ( bLocalize ) {
361 if ( !pStackData->sText["en-US"].isEmpty() )
363 OString sXComment = pStackData->sText[OString("x-comment")];
364 OString sLocalId = pStackData->sIdentifier;
365 OString sGroupId;
366 if ( aStack.size() == 1 ) {
367 sGroupId = sLocalId;
368 sLocalId = "";
370 else {
371 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
375 OString sText = pStackData->sText[ "en-US" ];
376 sText = helper::UnQuotHTML( sText );
378 common::writePoEntry(
379 "Cfgex", pOutputStream, sPath, pStackData->sResTyp,
380 sGroupId, sLocalId, sXComment, sText);
385 void CfgExport::WorkOnText(
386 OString &rText,
387 const OString &rIsoLang
390 if( !rIsoLang.isEmpty() ) rText = helper::UnQuotHTML( rText );
394 // class CfgMerge
397 CfgMerge::CfgMerge(
398 const OString &rMergeSource, const OString &rOutputFile,
399 const OString &rFilename, const OString &rLanguage )
400 : pMergeDataFile( nullptr ),
401 sFilename( rFilename ),
402 bEnglish( false )
404 pOutputStream.open(
405 rOutputFile.getStr(), std::ios_base::out | std::ios_base::trunc);
406 if (!pOutputStream.is_open())
408 std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
409 std::exit(EXIT_FAILURE);
412 if (!rMergeSource.isEmpty())
414 pMergeDataFile = new MergeDataFile(
415 rMergeSource, global::inputPathname, true );
416 if (rLanguage.equalsIgnoreAsciiCase("ALL") )
418 aLanguages = pMergeDataFile->GetLanguages();
420 else aLanguages.push_back(rLanguage);
422 else
423 aLanguages.push_back(rLanguage);
426 CfgMerge::~CfgMerge()
428 pOutputStream.close();
429 delete pMergeDataFile;
432 void CfgMerge::WorkOnText(OString &, const OString& rLangIndex)
435 if ( pMergeDataFile && bLocalize ) {
436 if ( !pResData ) {
437 OString sLocalId = pStackData->sIdentifier;
438 OString sGroupId;
439 if ( aStack.size() == 1 ) {
440 sGroupId = sLocalId;
441 sLocalId.clear();
443 else {
444 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
447 pResData.reset( new ResData( sGroupId, sFilename ) );
448 pResData->sId = sLocalId;
449 pResData->sResTyp = pStackData->sResTyp;
452 if (rLangIndex.equalsIgnoreAsciiCase("en-US"))
453 bEnglish = true;
457 void CfgMerge::Output(const OString& rOutput)
459 pOutputStream << rOutput.getStr();
462 void CfgMerge::WorkOnResourceEnd()
465 if ( pMergeDataFile && pResData && bLocalize && bEnglish ) {
466 MergeEntrys *pEntrys = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData.get() );
467 if ( pEntrys ) {
468 OString sCur;
470 for( size_t i = 0; i < aLanguages.size(); ++i ){
471 sCur = aLanguages[ i ];
473 OString sContent;
474 pEntrys->GetText( sContent, STRING_TYP_TEXT, sCur , true );
475 if (
476 ( !sCur.equalsIgnoreAsciiCase("en-US") ) && !sContent.isEmpty())
479 OString sText = helper::QuotHTML( sContent);
481 OString sAdditionalLine( "\t" );
483 OString sTextTag = pStackData->sTextTag;
484 OString sTemp = sTextTag.copy( sTextTag.indexOf( "xml:lang=" ));
486 sal_Int32 n = 0;
487 OString sSearch = sTemp.getToken(0, '"', n);
488 sSearch += "\"";
489 sSearch += sTemp.getToken(0, '"', n);
490 sSearch += "\"";
492 OString sReplace = sTemp.getToken(0, '"');
493 sReplace += "\"";
494 sReplace += sCur;
495 sReplace += "\"";
497 sTextTag = sTextTag.replaceFirst(sSearch, sReplace);
499 sAdditionalLine += sTextTag;
500 sAdditionalLine += sText;
501 sAdditionalLine += pStackData->sEndTextTag;
503 sAdditionalLine += "\n";
504 sAdditionalLine += sLastWhitespace;
506 Output( sAdditionalLine );
511 pResData.reset();
512 bEnglish = false;
515 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */