update credits
[LibreOffice.git] / l10ntools / source / cfgmerge.cxx
blob98778a78b19e8322f34b11ee305af9e13855a720
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 "common.hxx"
21 #include "sal/config.h"
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cstring>
27 #include "boost/scoped_ptr.hpp"
28 #include "rtl/strbuf.hxx"
30 #include "helper.hxx"
31 #include "export.hxx"
32 #include "cfgmerge.hxx"
33 #include "tokens.h"
35 void yyerror(char const *);
37 namespace {
39 namespace global {
41 OString inputPathname;
42 boost::scoped_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 == 0) {
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 );
88 // class CfgStackData
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;
99 // class CfgStack
102 CfgStack::~CfgStack()
104 for ( size_t i = 0, n = maList.size(); i < n; i++ )
105 delete maList[ i ];
106 maList.clear();
109 OString CfgStack::GetAccessPath( size_t nPos )
111 OStringBuffer sReturn;
112 for (size_t i = 0; i <= nPos; ++i)
114 if (i)
115 sReturn.append('.');
116 sReturn.append(maList[i]->GetIdentifier());
119 return sReturn.makeStringAndClear();
122 CfgStackData *CfgStack::GetStackData()
124 if (!maList.empty())
125 return maList[maList.size() - 1];
126 else
127 return 0;
131 // class CfgParser
134 CfgParser::CfgParser()
135 : pStackData( NULL ),
136 bLocalize( sal_False )
140 CfgParser::~CfgParser()
144 sal_Bool CfgParser::IsTokenClosed(const OString &rToken)
146 return rToken[rToken.getLength() - 2] == '/';
149 void CfgParser::AddText(
150 OString &rText,
151 const OString &rIsoLang,
152 const OString &rResTyp )
154 rText = rText.replaceAll(OString('\n'), OString()).
155 replaceAll(OString('\r'), OString()).
156 replaceAll(OString('\t'), OString());
157 pStackData->sResTyp = rResTyp;
158 WorkOnText( rText, rIsoLang );
159 pStackData->sText[ rIsoLang ] = rText;
162 int CfgParser::ExecuteAnalyzedToken( int nToken, char *pToken )
164 OString sToken( pToken );
166 if ( sToken == " " || sToken == "\t" )
167 sLastWhitespace += sToken;
169 OString sTokenName;
170 OString sTokenId;
172 sal_Bool bOutput = sal_True;
174 switch ( nToken ) {
175 case CFG_TOKEN_PACKAGE:
176 case CFG_TOKEN_COMPONENT:
177 case CFG_TOKEN_TEMPLATE:
178 case CFG_TOKEN_CONFIGNAME:
179 case CFG_TOKEN_OORNAME:
180 case CFG_TOKEN_OORVALUE:
181 case CFG_TAG:
182 case ANYTOKEN:
183 case CFG_TEXT_START:
185 sTokenName = sToken.getToken(1, '<').getToken(0, '>').
186 getToken(0, ' ');
188 if ( !IsTokenClosed( sToken )) {
189 OString sSearch;
190 switch ( nToken ) {
191 case CFG_TOKEN_PACKAGE:
192 sSearch = "package-id=";
193 break;
194 case CFG_TOKEN_COMPONENT:
195 sSearch = "component-id=";
196 break;
197 case CFG_TOKEN_TEMPLATE:
198 sSearch = "template-id=";
199 break;
200 case CFG_TOKEN_CONFIGNAME:
201 sSearch = "cfg:name=";
202 break;
203 case CFG_TOKEN_OORNAME:
204 sSearch = "oor:name=";
205 bLocalize = sal_True;
206 break;
207 case CFG_TOKEN_OORVALUE:
208 sSearch = "oor:value=";
209 break;
210 case CFG_TEXT_START: {
211 if ( sCurrentResTyp != sTokenName ) {
212 WorkOnResourceEnd();
214 sCurrentResTyp = sTokenName;
216 OString sTemp = sToken.copy( sToken.indexOf( "xml:lang=" ));
217 sCurrentIsoLang = sTemp.getToken(1, '"');
219 if ( sCurrentIsoLang == NO_TRANSLATE_ISO )
220 bLocalize = sal_False;
222 pStackData->sTextTag = sToken;
224 sCurrentText = "";
226 break;
228 if ( !sSearch.isEmpty())
230 OString sTemp = sToken.copy( sToken.indexOf( sSearch ));
231 sTokenId = sTemp.getToken(1, '"');
233 pStackData = aStack.Push( sTokenName, sTokenId );
235 if ( sSearch == "cfg:name=" ) {
236 OString sTemp( sToken.toAsciiUpperCase() );
237 bLocalize = (( sTemp.indexOf( "CFG:TYPE=\"STRING\"" ) != -1 ) &&
238 ( sTemp.indexOf( "CFG:LOCALIZED=\"sal_True\"" ) != -1 ));
241 else if ( sTokenName == "label" ) {
242 if ( sCurrentResTyp != sTokenName ) {
243 WorkOnResourceEnd();
245 sCurrentResTyp = sTokenName;
248 break;
249 case CFG_CLOSETAG:
251 sTokenName = sToken.getToken(1, '/').getToken(0, '>').
252 getToken(0, ' ');
253 if ( aStack.GetStackData() && ( aStack.GetStackData()->GetTagType() == sTokenName ))
255 if (sCurrentText.isEmpty())
256 WorkOnResourceEnd();
257 aStack.Pop();
258 pStackData = aStack.GetStackData();
260 else
262 OString sError( "Misplaced close tag: " );
263 OString sInFile(" in file ");
264 sError += sToken;
265 sError += sInFile;
266 sError += global::inputPathname;
267 Error( sError );
268 std::exit(EXIT_FAILURE);
271 break;
273 case CFG_TEXTCHAR:
274 sCurrentText += sToken;
275 bOutput = sal_False;
276 break;
278 case CFG_TOKEN_NO_TRANSLATE:
279 bLocalize = sal_False;
280 break;
283 if ( !sCurrentText.isEmpty() && nToken != CFG_TEXTCHAR )
285 AddText( sCurrentText, sCurrentIsoLang, sCurrentResTyp );
286 Output( sCurrentText );
287 sCurrentText = OString();
288 pStackData->sEndTextTag = sToken;
291 if ( bOutput )
292 Output( sToken );
294 if ( sToken != " " && sToken != "\t" )
295 sLastWhitespace = "";
297 return 1;
300 void CfgExport::Output(const OString&)
304 int 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 return ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE, pToken );
312 else if ( sToken.indexOf( "component-id=" ) != -1 )
313 return ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT, pToken );
314 else if ( sToken.indexOf( "template-id=" ) != -1 )
315 return ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE, pToken );
316 else if ( sToken.indexOf( "cfg:name=" ) != -1 )
317 return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
318 else if ( sToken.indexOf( "oor:name=" ) != -1 )
319 return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
320 else if ( sToken.indexOf( "oor:value=" ) != -1 )
321 return ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE, pToken );
322 break;
324 return ExecuteAnalyzedToken( nToken, pToken );
327 void CfgParser::Error(const OString& rError)
329 yyerror(rError.getStr());
333 // class CfgExport
336 CfgExport::CfgExport(
337 const OString &rOutputFile,
338 const OString &rFilePath )
340 : sPath( rFilePath )
342 pOutputStream.open( rOutputFile, PoOfstream::APP );
343 if (!pOutputStream.isOpen())
345 std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
346 std::exit(EXIT_FAILURE);
350 CfgExport::~CfgExport()
352 pOutputStream.close();
356 void CfgExport::WorkOnResourceEnd()
358 if ( bLocalize ) {
359 if ( pStackData->sText[OString(RTL_CONSTASCII_STRINGPARAM("en-US"))].getLength() )
361 OString sXComment = pStackData->sText[OString(RTL_CONSTASCII_STRINGPARAM("x-comment"))];
362 OString sLocalId = pStackData->sIdentifier;
363 OString sGroupId;
364 if ( aStack.size() == 1 ) {
365 sGroupId = sLocalId;
366 sLocalId = "";
368 else {
369 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
373 OString sText = pStackData->sText[ "en-US" ];
374 sText = helper::UnQuotHTML( sText );
376 common::writePoEntry(
377 "Cfgex", pOutputStream, sPath, pStackData->sResTyp,
378 sGroupId, sLocalId, sXComment, sText);
383 void CfgExport::WorkOnText(
384 OString &rText,
385 const OString &rIsoLang
388 if( rIsoLang.getLength() ) rText = helper::UnQuotHTML( rText );
393 // class CfgMerge
396 CfgMerge::CfgMerge(
397 const OString &rMergeSource, const OString &rOutputFile,
398 const OString &rFilename, const OString &rLanguage )
399 : pMergeDataFile( NULL ),
400 pResData( NULL ),
401 sFilename( rFilename ),
402 bEnglish( sal_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.getLength())
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;
430 delete pResData;
433 void CfgMerge::WorkOnText(OString &, const OString& rLangIndex)
436 if ( pMergeDataFile && bLocalize ) {
437 if ( !pResData ) {
438 OString sLocalId = pStackData->sIdentifier;
439 OString sGroupId;
440 if ( aStack.size() == 1 ) {
441 sGroupId = sLocalId;
442 sLocalId = OString();
444 else {
445 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
448 pResData = new ResData( sGroupId, sFilename );
449 pResData->sId = sLocalId;
450 pResData->sResTyp = pStackData->sResTyp;
453 if (rLangIndex.equalsIgnoreAsciiCase("en-US"))
454 bEnglish = sal_True;
458 void CfgMerge::Output(const OString& rOutput)
460 pOutputStream << rOutput.getStr();
463 void CfgMerge::WorkOnResourceEnd()
466 if ( pMergeDataFile && pResData && bLocalize && bEnglish ) {
467 MergeEntrys *pEntrys = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData );
468 if ( pEntrys ) {
469 OString sCur;
471 for( unsigned int i = 0; i < aLanguages.size(); ++i ){
472 sCur = aLanguages[ i ];
474 OString sContent;
475 pEntrys->GetText( sContent, STRING_TYP_TEXT, sCur , sal_True );
476 if (
477 ( !sCur.equalsIgnoreAsciiCase("en-US") ) && !sContent.isEmpty())
480 OString sText = helper::QuotHTML( sContent);
482 OString sAdditionalLine( "\t" );
484 OString sTextTag = pStackData->sTextTag;
485 OString sTemp = sTextTag.copy( sTextTag.indexOf( "xml:lang=" ));
487 sal_Int32 n = 0;
488 OString sSearch = sTemp.getToken(0, '"', n);
489 sSearch += "\"";
490 sSearch += sTemp.getToken(0, '"', n);
491 sSearch += "\"";
493 OString sReplace = sTemp.getToken(0, '"');
494 sReplace += "\"";
495 sReplace += sCur;
496 sReplace += "\"";
498 sTextTag = sTextTag.replaceFirst(sSearch, sReplace);
500 sAdditionalLine += sTextTag;
501 sAdditionalLine += sText;
502 sAdditionalLine += pStackData->sEndTextTag;
504 sAdditionalLine += "\n";
505 sAdditionalLine += sLastWhitespace;
507 Output( sAdditionalLine );
512 delete pResData;
513 pResData = NULL;
514 bEnglish = sal_False;
517 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */