Bump for 3.6-28
[LibreOffice.git] / l10ntools / source / cfgmerge.cxx
blobfaaa6d52071cf08ed2c905d9a0f1dea6f21c064b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #include "common.hxx"
30 #include "sal/config.h"
32 #include <cstdio>
33 #include <cstdlib>
34 #include <cstring>
36 #include "boost/scoped_ptr.hpp"
37 #include "rtl/strbuf.hxx"
39 #include "common.hxx"
40 #include "export.hxx"
41 #include "cfgmerge.hxx"
42 #include "tokens.h"
44 void yyerror(char const *);
46 namespace {
48 namespace global {
50 bool mergeMode = false;
51 bool errorLog = true;
52 char const * prj = 0;
53 char const * prjRoot = 0;
54 char const * inputPathname = 0;
55 char const * outputPathname = 0;
56 char const * mergeSrc;
57 boost::scoped_ptr< CfgParser > parser;
61 void handleArguments(int argc, char ** argv) {
62 for (int i = 1; i != argc; ++i) {
63 if (std::strcmp(argv[i], "-e") == 0) {
64 global::errorLog = false;
65 } else if (std::strcmp(argv[i], "-i") == 0) {
66 if (++i == argc) {
67 global::inputPathname = 0; // no valid command line
68 break;
70 global::inputPathname = argv[i];
71 } else if (std::strcmp(argv[i], "-l") == 0) {
72 if (++i == argc) {
73 global::inputPathname = 0; // no valid command line
74 break;
76 Export::sLanguages = argv[i];
77 } else if (std::strcmp(argv[i], "-m") == 0) {
78 if (++i == argc) {
79 global::inputPathname = 0; // no valid command line
80 break;
82 global::mergeSrc = argv[i];
83 global::mergeMode = true;
84 } else if (std::strcmp(argv[i], "-o") == 0) {
85 if (++i == argc) {
86 global::inputPathname = 0; // no valid command line
87 break;
89 global::outputPathname = argv[i];
90 } else if (std::strcmp(argv[i], "-p") == 0) {
91 if (++i == argc) {
92 global::inputPathname = 0; // no valid command line
93 break;
95 global::prj = argv[i];
96 } else if (std::strcmp(argv[i], "-r") == 0) {
97 if (++i == argc) {
98 global::inputPathname = 0; // no valid command line
99 break;
101 global::prjRoot = argv[i];
102 } else {
103 global::inputPathname = 0; // no valid command line
104 break;
107 if (global::inputPathname == 0 || global::outputPathname == 0) {
108 std::fprintf(
109 stderr,
110 ("Syntax: cfgex [-p Prj] [-r PrjRoot] -i FileIn -o FileOut"
111 " [-m DataBase] [-e] [-l l1,l2,...]\n"
112 " Prj: Project\n"
113 " PrjRoot: Path to project root (../.. etc.)\n"
114 " FileIn: Source files (*.src)\n"
115 " FileOut: Destination file (*.*)\n"
116 " DataBase: Mergedata (*.sdf)\n"
117 " -e: Disable writing errorlog\n"
118 " -l: Restrict the handled languages; l1, l2, ... are elements of"
119 " (de, en-US, ...)\n"));
120 std::exit(EXIT_FAILURE);
122 Export::InitLanguages();
127 extern "C" {
129 FILE * init(int argc, char ** argv) {
130 handleArguments(argc, argv);
132 FILE * pFile = std::fopen(global::inputPathname, "r");
133 if (pFile == 0) {
134 std::fprintf(
135 stderr, "Error: Cannot open file \"%s\"\n",
136 global::inputPathname);
137 std::exit(EXIT_FAILURE);
140 if (global::mergeMode) {
141 global::parser.reset(
142 new CfgMerge(
143 global::mergeSrc, global::outputPathname,
144 global::inputPathname));
145 } else {
146 global::parser.reset(
147 new CfgExport(
148 global::outputPathname, global::prj,
149 common::pathnameToken(global::inputPathname, global::prjRoot)));
152 return pFile;
155 void workOnTokenSet(int nTyp, char * pTokenText) {
156 global::parser->Execute( nTyp, pTokenText );
162 // class CfgStackData
165 CfgStackData* CfgStack::Push(const rtl::OString &rTag, const rtl::OString &rId)
167 CfgStackData *pD = new CfgStackData( rTag, rId );
168 maList.push_back( pD );
169 return pD;
173 // class CfgStack
176 /*****************************************************************************/
177 CfgStack::~CfgStack()
178 /*****************************************************************************/
180 for ( size_t i = 0, n = maList.size(); i < n; i++ )
181 delete maList[ i ];
182 maList.clear();
185 rtl::OString CfgStack::GetAccessPath( size_t nPos )
187 rtl::OStringBuffer sReturn;
188 for (size_t i = 0; i <= nPos; ++i)
190 if (i)
191 sReturn.append('.');
192 sReturn.append(maList[i]->GetIdentifier());
195 return sReturn.makeStringAndClear();
198 /*****************************************************************************/
199 CfgStackData *CfgStack::GetStackData()
200 /*****************************************************************************/
202 if (!maList.empty())
203 return maList[maList.size() - 1];
204 else
205 return 0;
209 // class CfgParser
212 /*****************************************************************************/
213 CfgParser::CfgParser()
214 /*****************************************************************************/
215 : pStackData( NULL ),
216 bLocalize( sal_False )
220 CfgParser::~CfgParser()
224 sal_Bool CfgParser::IsTokenClosed(const rtl::OString &rToken)
226 return rToken[rToken.getLength() - 2] == '/';
229 /*****************************************************************************/
230 void CfgParser::AddText(
231 rtl::OString &rText,
232 const rtl::OString &rIsoLang,
233 const rtl::OString &rResTyp
235 /*****************************************************************************/
237 rText = rText.replaceAll(rtl::OString('\n'), rtl::OString()).
238 replaceAll(rtl::OString('\r'), rtl::OString()).
239 replaceAll(rtl::OString('\t'), rtl::OString());
240 pStackData->sResTyp = rResTyp;
241 WorkOnText( rText, rIsoLang );
242 pStackData->sText[ rIsoLang ] = rText;
245 /*****************************************************************************/
246 int CfgParser::ExecuteAnalyzedToken( int nToken, char *pToken )
247 /*****************************************************************************/
249 rtl::OString sToken( pToken );
251 if ( sToken == " " || sToken == "\t" )
252 sLastWhitespace += sToken;
254 rtl::OString sTokenName;
255 rtl::OString sTokenId;
257 sal_Bool bOutput = sal_True;
259 switch ( nToken ) {
260 case CFG_TOKEN_PACKAGE:
261 case CFG_TOKEN_COMPONENT:
262 case CFG_TOKEN_TEMPLATE:
263 case CFG_TOKEN_CONFIGNAME:
264 case CFG_TOKEN_OORNAME:
265 case CFG_TOKEN_OORVALUE:
266 case CFG_TAG:
267 case ANYTOKEN:
268 case CFG_TEXT_START:
270 sTokenName = sToken.getToken(1, '<').getToken(0, '>').
271 getToken(0, ' ');
273 if ( !IsTokenClosed( sToken )) {
274 rtl::OString sSearch;
275 switch ( nToken ) {
276 case CFG_TOKEN_PACKAGE:
277 sSearch = "package-id=";
278 break;
279 case CFG_TOKEN_COMPONENT:
280 sSearch = "component-id=";
281 break;
282 case CFG_TOKEN_TEMPLATE:
283 sSearch = "template-id=";
284 break;
285 case CFG_TOKEN_CONFIGNAME:
286 sSearch = "cfg:name=";
287 break;
288 case CFG_TOKEN_OORNAME:
289 sSearch = "oor:name=";
290 bLocalize = sal_True;
291 break;
292 case CFG_TOKEN_OORVALUE:
293 sSearch = "oor:value=";
294 break;
295 case CFG_TEXT_START: {
296 if ( sCurrentResTyp != sTokenName ) {
297 WorkOnRessourceEnd();
298 rtl::OString sCur;
299 for( unsigned int i = 0; i < aLanguages.size(); ++i ){
300 sCur = aLanguages[ i ];
301 pStackData->sText[ sCur ] = rtl::OString();
304 sCurrentResTyp = sTokenName;
306 rtl::OString sTemp = sToken.copy( sToken.indexOf( "xml:lang=" ));
307 sCurrentIsoLang = sTemp.getToken(1, '"');
309 if ( sCurrentIsoLang == NO_TRANSLATE_ISO )
310 bLocalize = sal_False;
312 pStackData->sTextTag = sToken;
314 sCurrentText = "";
316 break;
318 if ( !sSearch.isEmpty())
320 rtl::OString sTemp = sToken.copy( sToken.indexOf( sSearch ));
321 sTokenId = sTemp.getToken(1, '"');
323 pStackData = aStack.Push( sTokenName, sTokenId );
325 if ( sSearch == "cfg:name=" ) {
326 rtl::OString sTemp( sToken.toAsciiUpperCase() );
327 bLocalize = (( sTemp.indexOf( "CFG:TYPE=\"STRING\"" ) != -1 ) &&
328 ( sTemp.indexOf( "CFG:LOCALIZED=\"sal_True\"" ) != -1 ));
331 else if ( sTokenName == "label" ) {
332 if ( sCurrentResTyp != sTokenName ) {
333 WorkOnRessourceEnd();
334 rtl::OString sCur;
335 for( unsigned int i = 0; i < aLanguages.size(); ++i ){
336 sCur = aLanguages[ i ];
337 pStackData->sText[ sCur ] = rtl::OString();
340 sCurrentResTyp = sTokenName;
343 break;
344 case CFG_CLOSETAG:
346 sTokenName = sToken.getToken(1, '/').getToken(0, '>').
347 getToken(0, ' ');
348 if ( aStack.GetStackData() && ( aStack.GetStackData()->GetTagType() == sTokenName ))
350 if (sCurrentText.isEmpty())
351 WorkOnRessourceEnd();
352 aStack.Pop();
353 pStackData = aStack.GetStackData();
355 else
357 rtl::OString sError( "Misplaced close tag: " );
358 rtl::OString sInFile(" in file ");
359 sError += sToken;
360 sError += sInFile;
361 sError += global::inputPathname;
362 Error( sError );
363 std::exit(EXIT_FAILURE);
366 break;
368 case CFG_TEXTCHAR:
369 sCurrentText += sToken;
370 bOutput = sal_False;
371 break;
373 case CFG_TOKEN_NO_TRANSLATE:
374 bLocalize = sal_False;
375 break;
378 if ( !sCurrentText.isEmpty() && nToken != CFG_TEXTCHAR )
380 AddText( sCurrentText, sCurrentIsoLang, sCurrentResTyp );
381 Output( sCurrentText );
382 sCurrentText = rtl::OString();
383 pStackData->sEndTextTag = sToken;
386 if ( bOutput )
387 Output( sToken );
389 if ( sToken != " " && sToken != "\t" )
390 sLastWhitespace = "";
392 return 1;
395 void CfgExport::Output(const rtl::OString&)
399 /*****************************************************************************/
400 int CfgParser::Execute( int nToken, char * pToken )
401 /*****************************************************************************/
403 rtl::OString sToken( pToken );
405 switch ( nToken ) {
406 case CFG_TAG:
407 if ( sToken.indexOf( "package-id=" ) != -1 )
408 return ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE, pToken );
409 else if ( sToken.indexOf( "component-id=" ) != -1 )
410 return ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT, pToken );
411 else if ( sToken.indexOf( "template-id=" ) != -1 )
412 return ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE, pToken );
413 else if ( sToken.indexOf( "cfg:name=" ) != -1 )
414 return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
415 else if ( sToken.indexOf( "oor:name=" ) != -1 )
416 return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
417 else if ( sToken.indexOf( "oor:value=" ) != -1 )
418 return ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE, pToken );
419 break;
421 return ExecuteAnalyzedToken( nToken, pToken );
424 void CfgParser::Error(const rtl::OString& rError)
426 yyerror(rError.getStr());
430 // class CfgOutputParser
433 CfgOutputParser::CfgOutputParser(const rtl::OString &rOutputFile)
435 pOutputStream.open(
436 rOutputFile.getStr(), std::ios_base::out | std::ios_base::trunc);
437 if (!pOutputStream.is_open())
439 rtl::OStringBuffer sError(RTL_CONSTASCII_STRINGPARAM("ERROR: Unable to open output file: "));
440 sError.append(rOutputFile);
441 Error(sError.makeStringAndClear());
442 std::exit(EXIT_FAILURE);
446 /*****************************************************************************/
447 CfgOutputParser::~CfgOutputParser()
448 /*****************************************************************************/
450 pOutputStream.close();
454 // class CfgExport
457 /*****************************************************************************/
458 CfgExport::CfgExport(
459 const rtl::OString &rOutputFile,
460 const rtl::OString &rProject,
461 const rtl::OString &rFilePath
463 /*****************************************************************************/
464 : CfgOutputParser( rOutputFile ),
465 sPrj( rProject ),
466 sPath( rFilePath )
468 Export::InitLanguages( false );
469 aLanguages = Export::GetLanguages();
472 /*****************************************************************************/
473 CfgExport::~CfgExport()
474 /*****************************************************************************/
478 /*****************************************************************************/
479 void CfgExport::WorkOnRessourceEnd()
480 /*****************************************************************************/
482 if ( bLocalize ) {
483 if ( pStackData->sText[rtl::OString(RTL_CONSTASCII_STRINGPARAM("en-US"))].getLength() )
485 rtl::OString sFallback = pStackData->sText[rtl::OString(RTL_CONSTASCII_STRINGPARAM("en-US"))];
486 rtl::OString sXComment = pStackData->sText[rtl::OString(RTL_CONSTASCII_STRINGPARAM("x-comment"))];
487 rtl::OString sLocalId = pStackData->sIdentifier;
488 rtl::OString sGroupId;
489 if ( aStack.size() == 1 ) {
490 sGroupId = sLocalId;
491 sLocalId = "";
493 else {
494 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
497 for (size_t n = 0; n < aLanguages.size(); n++)
499 rtl::OString sCur = aLanguages[ n ];
501 rtl::OString sText = pStackData->sText[ sCur ];
502 if ( sText.isEmpty())
503 sText = sFallback;
505 sText = Export::UnquoteHTML( sText );
507 rtl::OString sOutput( sPrj ); sOutput += "\t";
508 sOutput += sPath;
509 sOutput += "\t0\t";
510 sOutput += pStackData->sResTyp; sOutput += "\t";
511 sOutput += sGroupId; sOutput += "\t";
512 sOutput += sLocalId; sOutput += "\t\t\t0\t";
513 sOutput += sCur;
514 sOutput += "\t";
516 sOutput += sText; sOutput += "\t";
517 sOutput += sXComment; sOutput += "\t\t\t";
519 pOutputStream << sOutput.getStr() << '\n';
525 void CfgExport::WorkOnText(
526 rtl::OString &rText,
527 const rtl::OString &rIsoLang
530 if( rIsoLang.getLength() ) rText = Export::UnquoteHTML( rText );
535 // class CfgMerge
538 CfgMerge::CfgMerge(
539 const rtl::OString &rMergeSource, const rtl::OString &rOutputFile,
540 const rtl::OString &rFilename)
541 : CfgOutputParser( rOutputFile ),
542 pMergeDataFile( NULL ),
543 pResData( NULL ),
544 sFilename( rFilename ),
545 bEnglish( sal_False )
547 if (rMergeSource.getLength())
549 pMergeDataFile = new MergeDataFile(
550 rMergeSource, global::inputPathname, global::errorLog, true );
551 if (Export::sLanguages.equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("ALL")))
553 Export::SetLanguages( pMergeDataFile->GetLanguages() );
554 aLanguages = pMergeDataFile->GetLanguages();
556 else aLanguages = Export::GetLanguages();
558 else
559 aLanguages = Export::GetLanguages();
562 /*****************************************************************************/
563 CfgMerge::~CfgMerge()
564 /*****************************************************************************/
566 delete pMergeDataFile;
567 delete pResData;
570 void CfgMerge::WorkOnText(rtl::OString &rText, const rtl::OString& rLangIndex)
573 if ( pMergeDataFile && bLocalize ) {
574 if ( !pResData ) {
575 rtl::OString sLocalId = pStackData->sIdentifier;
576 rtl::OString sGroupId;
577 if ( aStack.size() == 1 ) {
578 sGroupId = sLocalId;
579 sLocalId = rtl::OString();
581 else {
582 sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
585 rtl::OString sPlatform;
587 pResData = new ResData( sPlatform, sGroupId , sFilename );
588 pResData->sId = sLocalId;
589 pResData->sResTyp = pStackData->sResTyp;
592 if (rLangIndex.equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("en-US")))
593 bEnglish = sal_True;
595 PFormEntrys *pEntrys = pMergeDataFile->GetPFormEntrysCaseSensitive( pResData );
596 if ( pEntrys ) {
597 rtl::OString sContent;
598 pEntrys->GetText( sContent, STRING_TYP_TEXT, rLangIndex );
600 if ( Export::isAllowed( rLangIndex ) &&
601 ( sContent != "-" ) && !sContent.isEmpty())
603 rText = Export::QuoteHTML( rText );
609 void CfgMerge::Output(const rtl::OString& rOutput)
611 pOutputStream << rOutput.getStr();
614 /*****************************************************************************/
615 void CfgMerge::WorkOnRessourceEnd()
616 /*****************************************************************************/
619 if ( pMergeDataFile && pResData && bLocalize && bEnglish ) {
620 PFormEntrys *pEntrys = pMergeDataFile->GetPFormEntrysCaseSensitive( pResData );
621 if ( pEntrys ) {
622 rtl::OString sCur;
624 for( unsigned int i = 0; i < aLanguages.size(); ++i ){
625 sCur = aLanguages[ i ];
627 rtl::OString sContent;
628 pEntrys->GetText( sContent, STRING_TYP_TEXT, sCur , sal_True );
629 if (
630 ( !sCur.equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("en-US")) ) &&
632 ( sContent != "-" ) && !sContent.isEmpty())
635 rtl::OString sText = Export::QuoteHTML( sContent);
637 rtl::OString sAdditionalLine( "\t" );
639 rtl::OString sTextTag = pStackData->sTextTag;
640 rtl::OString sTemp = sTextTag.copy( sTextTag.indexOf( "xml:lang=" ));
642 sal_Int32 n = 0;
643 rtl::OString sSearch = sTemp.getToken(0, '"', n);
644 sSearch += "\"";
645 sSearch += sTemp.getToken(0, '"', n);
646 sSearch += "\"";
648 rtl::OString sReplace = sTemp.getToken(0, '"');
649 sReplace += "\"";
650 sReplace += sCur;
651 sReplace += "\"";
653 sTextTag = sTextTag.replaceFirst(sSearch, sReplace);
655 sAdditionalLine += sTextTag;
656 sAdditionalLine += sText;
657 sAdditionalLine += pStackData->sEndTextTag;
659 sAdditionalLine += "\n";
660 sAdditionalLine += sLastWhitespace;
662 Output( sAdditionalLine );
667 delete pResData;
668 pResData = NULL;
669 bEnglish = sal_False;
672 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */