1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cfgmerge.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_transex3.hxx"
34 #include <tools/string.hxx>
35 #include <tools/fsys.hxx>
39 #include "cfgmerge.hxx"
41 #include "utf8conv.hxx"
43 extern "C" { int yyerror( char * ); }
44 extern "C" { int YYWarning( char * ); }
46 // defines to parse command line
47 #define STATE_NON 0x0001
48 #define STATE_INPUT 0x0002
49 #define STATE_OUTPUT 0x0003
50 #define STATE_PRJ 0x0004
51 #define STATE_ROOT 0x0005
52 #define STATE_MERGESRC 0x0006
53 #define STATE_ERRORLOG 0x0007
54 #define STATE_UTF8 0x0008
55 #define STATE_LANGUAGES 0X0009
56 #define STATE_ISOCODE99 0x000A
57 #define STATE_FORCE 0x000B
59 // set of global variables
68 ByteString sInputFileName
;
69 ByteString sActFileName
;
70 ByteString sFullEntry
;
71 ByteString sOutputFile
;
78 // the whole interface to lexer is in this extern "C" section
80 /*****************************************************************************/
81 extern char *GetOutputFile( int argc
, char* argv
[])
82 /*****************************************************************************/
84 bEnableExport
= FALSE
;
95 USHORT nState
= STATE_NON
;
99 for( int i
= 1; i
< argc
; i
++ ) {
100 ByteString
sSwitch( argv
[ i
] );
101 sSwitch
.ToUpperAscii();
103 if ( sSwitch
== "-I" ) {
104 nState
= STATE_INPUT
; // next token specifies source file
106 else if ( sSwitch
== "-O" ) {
107 nState
= STATE_OUTPUT
; // next token specifies the dest file
109 else if ( sSwitch
== "-P" ) {
110 nState
= STATE_PRJ
; // next token specifies the cur. project
112 else if ( sSwitch
== "-R" ) {
113 nState
= STATE_ROOT
; // next token specifies path to project root
115 else if ( sSwitch
== "-M" ) {
116 nState
= STATE_MERGESRC
; // next token specifies the merge database
118 else if ( sSwitch
== "-E" ) {
119 nState
= STATE_ERRORLOG
;
122 else if ( sSwitch
== "-UTF8" ) {
126 else if ( sSwitch
== "-NOUTF8" ) {
130 else if ( sSwitch
== "-F" ) {
131 nState
= STATE_FORCE
;
134 else if ( sSwitch
== "-QQ" ) {
137 else if ( sSwitch
== "-L" ) {
138 nState
= STATE_LANGUAGES
;
140 else if ( sSwitch
.ToUpperAscii() == "-ISO99" ) {
141 nState
= STATE_ISOCODE99
;
146 return NULL
; // no valid command line
149 sInputFileName
= argv
[ i
];
150 bInput
= TRUE
; // source file found
154 sOutputFile
= argv
[ i
]; // the dest. file
158 sPrj
= ByteString( argv
[ i
]);
159 // sPrj.ToLowerAscii(); // the project
163 sPrjRoot
= ByteString( argv
[ i
]); // path to project root
166 case STATE_MERGESRC
: {
167 sMergeSrc
= ByteString( argv
[ i
]);
168 bMergeMode
= TRUE
; // activate merge mode, cause merge database found
171 case STATE_LANGUAGES
: {
172 Export::sLanguages
= ByteString( argv
[ i
]);
175 case STATE_ISOCODE99
: {
176 Export::sIsoCode99
= ByteString( argv
[ i
]);
184 // command line is valid
185 bEnableExport
= TRUE
;
186 char *pReturn
= new char[ sOutputFile
.Len() + 1 ];
187 strcpy( pReturn
, sOutputFile
.GetBuffer()); // #100211# - checked
191 // command line is not valid
195 if( bQuiet
) return 1;
198 /*****************************************************************************/
199 int InitCfgExport( char *pOutput
, char* pFilename
)
200 /*****************************************************************************/
202 // instanciate Export
203 ByteString
sOutput( pOutput
);
204 ByteString
sFilename( pFilename
);
205 Export::InitLanguages();
208 pParser
= new CfgMerge( sMergeSrc
, sOutputFile
, sFilename
);
209 else if ( sOutputFile
.Len())
210 pParser
= new CfgExport( sOutputFile
, sPrj
, sActFileName
);
215 /*****************************************************************************/
217 /*****************************************************************************/
224 void removeTempFile(){
225 if( !sUsedTempFile
.EqualsIgnoreCaseAscii( "" ) ){
226 DirEntry
aTempFile( sUsedTempFile
);
230 extern const char* getFilename()
232 return sInputFileName
.GetBuffer();
234 /*****************************************************************************/
235 extern FILE *GetCfgFile()
236 /*****************************************************************************/
239 // look for valid filename
240 if ( sInputFileName
.Len()) {
241 if( Export::fileHasUTF8ByteOrderMarker( sInputFileName
) ){
242 DirEntry aTempFile
= Export::GetTempFile();
243 DirEntry
aSourceFile( String( sInputFileName
, RTL_TEXTENCODING_ASCII_US
) );
244 aSourceFile
.CopyTo( aTempFile
, FSYS_ACTION_COPYFILE
);
245 String sTempFile
= aTempFile
.GetFull();
246 Export::RemoveUTF8ByteOrderMarkerFromFile( ByteString( sTempFile
, RTL_TEXTENCODING_ASCII_US
) );
247 pFile
= fopen( ByteString( sTempFile
, RTL_TEXTENCODING_ASCII_US
).GetBuffer(), "r" );
248 sUsedTempFile
= sTempFile
;
250 // able to open file?
251 pFile
= fopen( sInputFileName
.GetBuffer(), "r" );
252 sUsedTempFile
= String::CreateFromAscii("");
255 fprintf( stderr
, "Error: Could not open file %s\n",
256 sInputFileName
.GetBuffer());
260 // this is a valid file which can be opened, so
261 // create path to project root
262 DirEntry
aEntry( String( sInputFileName
, RTL_TEXTENCODING_ASCII_US
));
264 sFullEntry
= ByteString( aEntry
.GetFull(), RTL_TEXTENCODING_ASCII_US
);
265 aEntry
+= DirEntry( String( "..", RTL_TEXTENCODING_ASCII_US
));
266 aEntry
+= DirEntry( sPrjRoot
);
267 ByteString
sPrjEntry( aEntry
.GetFull(), RTL_TEXTENCODING_ASCII_US
);
269 // create file name, beginnig with project root
270 // (e.g.: source\ui\src\menue.src)
271 // printf("sFullEntry = %s\n",sFullEntry.GetBuffer());
272 sActFileName
= sFullEntry
.Copy( sPrjEntry
.Len() + 1 );
273 // printf("sActFileName = %s\n",sActFileName.GetBuffer());
275 fprintf( stdout
, "\nProcessing File %s ...\n", sInputFileName
.GetBuffer());
277 sActFileName
.SearchAndReplaceAll( "/", "\\" );
282 // this means the file could not be opened
286 /*****************************************************************************/
287 int WorkOnTokenSet( int nTyp
, char *pTokenText
)
288 /*****************************************************************************/
290 pParser
->Execute( nTyp
, pTokenText
);
296 /*****************************************************************************/
298 /*****************************************************************************/
303 /*****************************************************************************/
305 /*****************************************************************************/
312 // class CfgStackData
315 CfgStackData
* CfgStack::Push( const ByteString
&rTag
, const ByteString
&rId
)
317 CfgStackData
*pD
= new CfgStackData( rTag
, rId
);
318 Insert( pD
, LIST_APPEND
);
326 /*****************************************************************************/
327 CfgStack::~CfgStack()
328 /*****************************************************************************/
330 for ( ULONG i
= 0; i
< Count(); i
++ )
331 delete GetObject( i
);
334 /*****************************************************************************/
335 ByteString
CfgStack::GetAccessPath( ULONG nPos
)
336 /*****************************************************************************/
338 if ( nPos
== LIST_APPEND
)
342 for ( ULONG i
= 0; i
<= nPos
; i
++ ) {
345 sReturn
+= GetStackData( i
)->GetIdentifier();
351 /*****************************************************************************/
352 CfgStackData
*CfgStack::GetStackData( ULONG nPos
)
353 /*****************************************************************************/
355 if ( nPos
== LIST_APPEND
)
358 return GetObject( nPos
);
365 /*****************************************************************************/
366 CfgParser::CfgParser()
367 /*****************************************************************************/
368 : pStackData( NULL
),
373 /*****************************************************************************/
374 CfgParser::~CfgParser()
375 /*****************************************************************************/
380 /*****************************************************************************/
381 BOOL
CfgParser::IsTokenClosed( const ByteString
&rToken
)
382 /*****************************************************************************/
384 return rToken
.GetChar( rToken
.Len() - 2 ) == '/';
387 /*****************************************************************************/
388 void CfgParser::AddText(
390 const ByteString
&rIsoLang
,
391 const ByteString
&rResTyp
393 /*****************************************************************************/
396 while ( rText
.Len() != nTextLen
) {
397 nTextLen
= rText
.Len();
398 rText
.SearchAndReplaceAll( "\n", " " );
399 rText
.SearchAndReplaceAll( "\r", " " );
400 rText
.SearchAndReplaceAll( "\t", " " );
401 rText
.SearchAndReplaceAll( " ", " " );
403 pStackData
->sResTyp
= rResTyp
;
404 WorkOnText( rText
, rIsoLang
);
406 pStackData
->sText
[ rIsoLang
] = rText
;
410 /*****************************************************************************/
411 void CfgParser::WorkOnRessourceEnd()
412 /*****************************************************************************/
416 /*****************************************************************************/
417 int CfgParser::ExecuteAnalyzedToken( int nToken
, char *pToken
)
418 /*****************************************************************************/
420 ByteString
sToken( pToken
);
422 if ( sToken
== " " || sToken
== "\t" )
423 sLastWhitespace
+= sToken
;
425 ByteString sTokenName
;
431 case CFG_TOKEN_PACKAGE
:
432 case CFG_TOKEN_COMPONENT
:
433 case CFG_TOKEN_TEMPLATE
:
434 case CFG_TOKEN_CONFIGNAME
:
435 case CFG_TOKEN_OORNAME
:
436 case CFG_TOKEN_OORVALUE
:
441 sTokenName
= sToken
.GetToken( 1, '<' ).GetToken( 0, '>' ).GetToken( 0, ' ' );
443 if ( !IsTokenClosed( sToken
)) {
446 case CFG_TOKEN_PACKAGE
:
447 sSearch
= "package-id=";
449 case CFG_TOKEN_COMPONENT
:
450 sSearch
= "component-id=";
452 case CFG_TOKEN_TEMPLATE
:
453 sSearch
= "template-id=";
455 case CFG_TOKEN_CONFIGNAME
:
456 sSearch
= "cfg:name=";
458 case CFG_TOKEN_OORNAME
:
459 sSearch
= "oor:name=";
462 case CFG_TOKEN_OORVALUE
:
463 sSearch
= "oor:value=";
465 case CFG_TEXT_START
: {
466 if ( sCurrentResTyp
!= sTokenName
) {
467 WorkOnRessourceEnd();
469 for( unsigned int n
= 0; n
< aLanguages
.size(); n
++ ){
470 sCur
= aLanguages
[ n
];
471 pStackData
->sText
[ sCur
] = ByteString("");
474 sCurrentResTyp
= sTokenName
;
476 ByteString sTemp
= sToken
.Copy( sToken
.Search( "xml:lang=" ));
477 sCurrentIsoLang
= sTemp
.GetToken( 1, '\"' ).GetToken( 0, '\"' );
479 if ( sCurrentIsoLang
== NO_TRANSLATE_ISO
)
482 pStackData
->sTextTag
= sToken
;
488 if ( sSearch
.Len()) {
489 ByteString sTemp
= sToken
.Copy( sToken
.Search( sSearch
));
490 sTokenId
= sTemp
.GetToken( 1, '\"' ).GetToken( 0, '\"' );
492 pStackData
= aStack
.Push( sTokenName
, sTokenId
);
494 if ( sSearch
== "cfg:name=" ) {
495 ByteString
sTemp( sToken
);
496 sTemp
.ToUpperAscii();
497 bLocalize
= (( sTemp
.Search( "CFG:TYPE=\"STRING\"" ) != STRING_NOTFOUND
) &&
498 ( sTemp
.Search( "CFG:LOCALIZED=\"TRUE\"" ) != STRING_NOTFOUND
));
501 else if ( sTokenName
== "label" ) {
502 if ( sCurrentResTyp
!= sTokenName
) {
503 WorkOnRessourceEnd();
505 for( unsigned int n
= 0; n
< aLanguages
.size(); n
++ ){
506 sCur
= aLanguages
[ n
];
507 pStackData
->sText
[ sCur
] = ByteString("");
510 sCurrentResTyp
= sTokenName
;
515 sTokenName
= sToken
.GetToken( 1, '/' ).GetToken( 0, '>' ).GetToken( 0, ' ' );
516 if ( aStack
.GetStackData() && ( aStack
.GetStackData()->GetTagType() == sTokenName
)) {
517 if ( ! sCurrentText
.Len())
518 WorkOnRessourceEnd();
520 pStackData
= aStack
.GetStackData();
523 ByteString
sError( "Missplaced close tag: " );
524 ByteString
sInFile(" in file ");
527 sError
+= sFullEntry
;
534 sCurrentText
+= sToken
;
538 case CFG_TOKEN_NO_TRANSLATE
:
543 if ( sCurrentText
.Len() && nToken
!= CFG_TEXTCHAR
) {
544 AddText( sCurrentText
, sCurrentIsoLang
, sCurrentResTyp
);
545 Output( sCurrentText
);
547 pStackData
->sEndTextTag
= sToken
;
553 if ( sToken
!= " " && sToken
!= "\t" )
554 sLastWhitespace
= "";
559 /*****************************************************************************/
560 void CfgExport::Output( const ByteString
& rOutput
)
561 /*****************************************************************************/
563 // Dummy operation to suppress warnings caused by poor class design
564 ByteString
a( rOutput
);
567 /*****************************************************************************/
568 int CfgParser::Execute( int nToken
, char * pToken
)
569 /*****************************************************************************/
571 ByteString
sToken( pToken
);
575 if ( sToken
.Search( "package-id=" ) != STRING_NOTFOUND
)
576 return ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE
, pToken
);
577 else if ( sToken
.Search( "component-id=" ) != STRING_NOTFOUND
)
578 return ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT
, pToken
);
579 else if ( sToken
.Search( "template-id=" ) != STRING_NOTFOUND
)
580 return ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE
, pToken
);
581 else if ( sToken
.Search( "cfg:name=" ) != STRING_NOTFOUND
)
582 return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME
, pToken
);
583 else if ( sToken
.Search( "oor:name=" ) != STRING_NOTFOUND
)
584 return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME
, pToken
);
585 else if ( sToken
.Search( "oor:value=" ) != STRING_NOTFOUND
)
586 return ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE
, pToken
);
589 return ExecuteAnalyzedToken( nToken
, pToken
);
593 /*****************************************************************************/
594 void CfgParser::Error( const ByteString
&rError
)
595 /*****************************************************************************/
597 // ByteString sError( rError );
598 // sError.Append("Error: In file ");
599 // sError.Append( sActFileName );
600 yyerror(( char * ) rError
.GetBuffer());
605 // class CfgOutputParser
608 /*****************************************************************************/
609 CfgOutputParser::CfgOutputParser( const ByteString
&rOutputFile
)
610 /*****************************************************************************/
614 String( rOutputFile
, RTL_TEXTENCODING_ASCII_US
),
615 STREAM_STD_WRITE
| STREAM_TRUNC
617 pOutputStream
->SetStreamCharSet( RTL_TEXTENCODING_UTF8
);
619 if ( !pOutputStream
->IsOpen()) {
620 ByteString
sError( "ERROR: Unable to open output file: " );
621 sError
+= rOutputFile
;
623 delete pOutputStream
;
624 pOutputStream
= NULL
;
629 /*****************************************************************************/
630 CfgOutputParser::~CfgOutputParser()
631 /*****************************************************************************/
633 if ( pOutputStream
) {
634 pOutputStream
->Close();
635 delete pOutputStream
;
643 /*****************************************************************************/
644 CfgExport::CfgExport(
645 const ByteString
&rOutputFile
,
646 const ByteString
&rProject
,
647 const ByteString
&rFilePath
649 /*****************************************************************************/
650 : CfgOutputParser( rOutputFile
),
654 Export::InitLanguages( false );
655 aLanguages
= Export::GetLanguages();
658 /*****************************************************************************/
659 CfgExport::~CfgExport()
660 /*****************************************************************************/
664 /*****************************************************************************/
665 void CfgExport::WorkOnRessourceEnd()
666 /*****************************************************************************/
668 if ( pOutputStream
&& bLocalize
) {
669 if (( pStackData
->sText
[ ByteString("en-US") ].Len()
672 ( pStackData
->sText
[ ByteString("de") ].Len() ||
673 pStackData
->sText
[ ByteString("en-US") ].Len() )))
675 ByteString sFallback
= pStackData
->sText
[ ByteString("en-US") ];
677 //if ( pStackData->sText[ ByteString("en-US") ].Len())
678 // sFallback = pStackData->sText[ ByteString("en-US") ];
680 ByteString sLocalId
= pStackData
->sIdentifier
;
682 if ( aStack
.Count() == 1 ) {
687 sGroupId
= aStack
.GetAccessPath( aStack
.Count() - 2 );
690 ByteString
sTimeStamp( Export::GetTimeStamp());
693 for( unsigned int n
= 0; n
< aLanguages
.size(); n
++ ){
694 sCur
= aLanguages
[ n
];
696 ByteString sText
= pStackData
->sText
[ sCur
];
700 Export::UnquotHTML( sText
);
702 ByteString
sOutput( sPrj
); sOutput
+= "\t";
705 sOutput
+= pStackData
->sResTyp
; sOutput
+= "\t";
706 sOutput
+= sGroupId
; sOutput
+= "\t";
707 sOutput
+= sLocalId
; sOutput
+= "\t\t\t0\t";
711 sOutput
+= sText
; sOutput
+= "\t\t\t\t";
712 sOutput
+= sTimeStamp
;
714 //if( !sCur.EqualsIgnoreCaseAscii("de") ||( sCur.EqualsIgnoreCaseAscii("de") && !Export::isMergingGermanAllowed( sPrj ) ) )
715 pOutputStream
->WriteLine( sOutput
);
721 /*****************************************************************************/
722 void CfgExport::WorkOnText(
724 const ByteString
&rIsoLang
726 /*****************************************************************************/
728 if( rIsoLang
.Len() ) Export::UnquotHTML( rText
);
736 /*****************************************************************************/
738 const ByteString
&rMergeSource
, const ByteString
&rOutputFile
,
739 ByteString
&rFilename
)
740 /*****************************************************************************/
741 : CfgOutputParser( rOutputFile
),
742 pMergeDataFile( NULL
),
745 sFilename( rFilename
),
748 if ( rMergeSource
.Len()){
749 pMergeDataFile
= new MergeDataFile(
750 rMergeSource
, sInputFileName
, bErrorLog
, RTL_TEXTENCODING_MS_1252
, true );
751 if( Export::sLanguages
.EqualsIgnoreCaseAscii("ALL") ){
752 Export::SetLanguages( pMergeDataFile
->GetLanguages() );
753 aLanguages
= pMergeDataFile
->GetLanguages();
755 else aLanguages
= Export::GetLanguages();
757 aLanguages
= Export::GetLanguages();
760 /*****************************************************************************/
761 CfgMerge::~CfgMerge()
762 /*****************************************************************************/
764 delete pMergeDataFile
;
768 /*****************************************************************************/
769 void CfgMerge::WorkOnText(
771 const ByteString
& nLangIndex
773 /*****************************************************************************/
776 if ( pMergeDataFile
&& bLocalize
) {
778 ByteString sLocalId
= pStackData
->sIdentifier
;
780 if ( aStack
.Count() == 1 ) {
785 sGroupId
= aStack
.GetAccessPath( aStack
.Count() - 2 );
788 ByteString
sPlatform( "" );
790 pResData
= new ResData( sPlatform
, sGroupId
, sFilename
);
791 pResData
->sId
= sLocalId
;
792 pResData
->sResTyp
= pStackData
->sResTyp
;
795 //if ( nLangIndex.EqualsIgnoreCaseAscii("de") )
797 if (( nLangIndex
.EqualsIgnoreCaseAscii("en-US") ))
800 PFormEntrys
*pEntrys
= pMergeDataFile
->GetPFormEntrysCaseSensitive( pResData
);
803 pEntrys
->GetText( sContent
, STRING_TYP_TEXT
, nLangIndex
);
805 if ( Export::isAllowed( nLangIndex
) &&
806 ( sContent
!= "-" ) && ( sContent
.Len()))
808 #ifdef MERGE_SOURCE_LANGUAGES
809 if( nLangIndex
.EqualsIgnoreCaseAscii("de") || nLangIndex
.EqualsIgnoreCaseAscii("en-US") )
812 Export::QuotHTML( rText
);
818 /*****************************************************************************/
819 void CfgMerge::Output( const ByteString
& rOutput
)
820 /*****************************************************************************/
823 pOutputStream
->Write( rOutput
.GetBuffer(), rOutput
.Len());
826 ULONG
CfgStack::Push( CfgStackData
*pStackData
)
828 Insert( pStackData
, LIST_APPEND
);
832 /*****************************************************************************/
833 void CfgMerge::WorkOnRessourceEnd()
834 /*****************************************************************************/
837 if ( pMergeDataFile
&& pResData
&& bLocalize
&& (( bEnglish
) || bForce
)) {
838 PFormEntrys
*pEntrys
= pMergeDataFile
->GetPFormEntrysCaseSensitive( pResData
);
842 for( unsigned int n
= 0; n
< aLanguages
.size(); n
++ ){
843 sCur
= aLanguages
[ n
];
846 pEntrys
->GetText( sContent
, STRING_TYP_TEXT
, sCur
, TRUE
);
848 // (!sCur.EqualsIgnoreCaseAscii("de") ) &&
849 ( !sCur
.EqualsIgnoreCaseAscii("en-US") ) &&
851 ( sContent
!= "-" ) && ( sContent
.Len()))
854 ByteString sText
= sContent
;
855 Export::QuotHTML( sText
);
857 ByteString
sAdditionalLine( "\t" );
859 ByteString sTextTag
= pStackData
->sTextTag
;
860 ByteString sTemp
= sTextTag
.Copy( sTextTag
.Search( "xml:lang=" ));
862 ByteString sSearch
= sTemp
.GetToken( 0, '\"' );
864 sSearch
+= sTemp
.GetToken( 1, '\"' );
867 ByteString sReplace
= sTemp
.GetToken( 0, '\"' );
872 sTextTag
.SearchAndReplace( sSearch
, sReplace
);
874 sAdditionalLine
+= sTextTag
;
875 sAdditionalLine
+= sText
;
876 sAdditionalLine
+= pStackData
->sEndTextTag
;
878 sAdditionalLine
+= "\n";
879 sAdditionalLine
+= sLastWhitespace
;
881 Output( sAdditionalLine
);