Branch libreoffice-5-0-4
[LibreOffice.git] / l10ntools / source / export.cxx
blobced58e3c473a70c591e4f70a4c23369a18d2660d
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 <cstddef>
23 #include <cstring>
25 #include "helper.hxx"
26 #include "srclex.hxx"
28 #include "boost/scoped_ptr.hpp"
29 #include <cstdio>
30 #include <cstdlib>
31 #include "common.hxx"
32 #include "export.hxx"
33 #include "tokens.h"
34 #include <iostream>
35 #include <rtl/strbuf.hxx>
37 void yyerror( const char * );
38 void YYWarning( const char * );
40 namespace {
42 MergeDataFile * pMergeDataFile = 0; //TODO
44 namespace global {
46 OString inputPathname;
47 boost::scoped_ptr< Export > exporter;
51 static OString lcl_GetListTyp( const sal_uInt16 nTyp, const bool bUpperCamelCase )
53 OString sType;
54 switch (nTyp)
56 case LIST_STRING:
57 sType = bUpperCamelCase ? "StringList" : "stringlist";
58 break;
59 case LIST_FILTER:
60 sType = bUpperCamelCase ? "FilterList" : "filterlist";
61 break;
62 case LIST_ITEM:
63 sType = bUpperCamelCase ? "ItemList" : "itemlist";
64 break;
65 case LIST_PAIRED:
66 sType = bUpperCamelCase ? "PairedList" : "pairedlist";
67 break;
68 default: break;
70 return sType;
75 extern "C" {
77 FILE * init(int argc, char ** argv)
79 common::HandledArgs aArgs;
80 if ( !common::handleArguments(argc, argv, aArgs) )
82 common::writeUsage("transex3","*.src/*.hrc");
83 std::exit(EXIT_FAILURE);
85 global::inputPathname = aArgs.m_sInputFile;
87 FILE * pFile = std::fopen(global::inputPathname.getStr(), "r");
88 if (pFile == 0) {
89 std::fprintf(
90 stderr, "Error: Cannot open file \"%s\"\n",
91 global::inputPathname.getStr());
92 std::exit(EXIT_FAILURE);
95 if (aArgs.m_bMergeMode) {
96 global::exporter.reset(new Export(aArgs.m_sMergeSrc, aArgs.m_sOutputFile,
97 aArgs.m_sLanguage, aArgs.m_bUTF8BOM));
98 } else {
99 global::exporter.reset(new Export(aArgs.m_sOutputFile));
102 global::exporter->Init();
104 return pFile;
107 int Parse( int nTyp, const char *pTokenText ){
108 global::exporter->Execute( nTyp , pTokenText );
109 return 1;
112 void Close()
114 global::exporter->GetParseQueue()->Close();
115 global::exporter.reset();
116 // avoid nontrivial Export dtor being executed during exit
119 int WorkOnTokenSet( int nTyp, char *pTokenText )
121 global::exporter->GetParseQueue()->Push( QueueEntry( nTyp , OString(pTokenText) ) );
122 return 1;
125 int SetError()
127 // set error at global instance of class Export
128 global::exporter->SetError();
129 return 1;
132 int GetError()
134 // get error at global instance of class Export
135 if (global::exporter->GetError())
136 return 1;
137 return sal_False;
140 } // extern "C"
143 // class ResData
146 bool ResData::SetId( const OString& rId, sal_uInt16 nLevel )
148 if ( nLevel > nIdLevel )
150 nIdLevel = nLevel;
151 sId = rId;
153 if ( bChild && bChildWithText )
155 OString sError("ResId after child definition");
156 yyerror(sError.getStr());
157 SetError();
160 if ( sId.getLength() > 255 )
162 YYWarning( "LocalId > 255 chars, truncating..." );
163 sId = sId.copy(0, 255).trim();
166 return true;
169 return false;
173 // class Export
176 namespace
179 static sal_Int32 lcl_countOccurrences(const OString& text, char c)
181 sal_Int32 n = 0;
182 for (sal_Int32 i = 0;; ++i) {
183 i = text.indexOf(c, i);
184 if (i == -1) {
185 break;
187 ++n;
189 return n;
194 Export::Export(const OString &rOutput)
196 bDefine( false ),
197 bNextMustBeDefineEOL( false ),
198 nLevel( 0 ),
199 nList( LIST_NON ),
200 nListIndex( 0 ),
201 nListLevel( 0 ),
202 bMergeMode( false ),
203 bError( false ),
204 bReadOver( false ),
205 sFilename( global::inputPathname ),
206 sLanguages( OString() ),
207 pParseQueue( new ParserQueue( *this ) )
209 aOutput.mPo = new PoOfstream( rOutput, PoOfstream::APP );
210 if (!aOutput.mPo->isOpen())
212 std::fprintf(stderr, "ERROR : Can't open file %s\n", rOutput.getStr());
213 std::exit(EXIT_FAILURE);
217 Export::Export(
218 const OString &rMergeSource, const OString &rOutput,
219 const OString &rLanguage, bool bUTF8BOM)
221 bDefine( false ),
222 bNextMustBeDefineEOL( false ),
223 nLevel( 0 ),
224 nList( LIST_NON ),
225 nListIndex( 0 ),
226 nListLevel( 0 ),
227 bMergeMode( true ),
228 sMergeSrc( rMergeSource ),
229 bError( false ),
230 bReadOver( false ),
231 sFilename( global::inputPathname ),
232 sLanguages( rLanguage ),
233 pParseQueue( new ParserQueue( *this ) )
235 aOutput.mSimple = new std::ofstream();
236 aOutput.mSimple->open(rOutput.getStr(), std::ios_base::out | std::ios_base::trunc);
237 if (!aOutput.mSimple->is_open())
239 std::fprintf(stderr, "ERROR : Can't open file %s\n", rOutput.getStr());
240 std::exit(EXIT_FAILURE);
243 if ( bUTF8BOM ) WriteUTF8ByteOrderMarkToOutput();
246 void Export::Init()
248 // resets the internal status, used before parseing another file
249 bDefine = false;
250 bNextMustBeDefineEOL = false;
251 nLevel = 0;
252 nList = LIST_NON;
253 nListIndex = 0;
254 for ( size_t i = 0, n = aResStack.size(); i < n; ++i )
255 delete aResStack[ i ];
256 aResStack.clear();
259 Export::~Export()
261 if( pParseQueue )
262 delete pParseQueue;
263 if ( bMergeMode )
265 aOutput.mSimple->close();
266 delete aOutput.mSimple;
268 else
270 aOutput.mPo->close();
271 delete aOutput.mPo;
273 for ( size_t i = 0, n = aResStack.size(); i < n; ++i )
274 delete aResStack[ i ];
275 aResStack.clear();
277 if ( bMergeMode ) {
278 if ( !pMergeDataFile )
279 pMergeDataFile = new MergeDataFile(sMergeSrc, global::inputPathname, true);
281 delete pMergeDataFile;
285 int Export::Execute( int nToken, const char * pToken )
288 OString sToken( pToken );
289 OString sOrig( sToken );
291 if ( nToken == CONDITION )
293 OString sTestToken(pToken);
294 sTestToken = sTestToken.replaceAll("\t", OString()).
295 replaceAll(" ", OString());
296 if (( !bReadOver ) && ( sTestToken.startsWith("#ifndef__RSC_PARSER")))
297 bReadOver = true;
298 else if (( bReadOver ) && ( sTestToken.startsWith("#endif") ))
299 bReadOver = false;
301 if ((( nToken < FILTER_LEVEL ) || ( bReadOver )) &&
302 (!(( bNextMustBeDefineEOL ) && ( sOrig == "\n" )))) {
303 // this tokens are not mandatory for parsing, so ignore them ...
304 if ( bMergeMode )
305 WriteToMerged( sOrig , false ); // ... or write them directly to dest.
306 return 0;
309 ResData *pResData = NULL;
310 if ( nLevel ) {
311 // res. exists at cur. level
312 pResData = ( (nLevel-1) < aResStack.size() ) ? aResStack[ nLevel-1 ] : NULL;
314 else if (( nToken != RESOURCE ) &&
315 ( nToken != RESOURCEEXPR ) &&
316 ( nToken != SMALRESOURCE ) &&
317 ( nToken != LEVELUP ) &&
318 ( nToken != NORMDEFINE ) &&
319 ( nToken != RSCDEFINE ) &&
320 ( nToken != CONDITION ) &&
321 ( nToken != PRAGMA ))
323 // no res. exists at cur. level so return
324 if ( bMergeMode )
325 WriteToMerged( sOrig , false );
326 return 0;
329 if ( bDefine ) {
330 if (( nToken != EMPTYLINE ) && ( nToken != LEVELDOWN ) && ( nToken != LEVELUP )) {
331 // cur. res. defined in macro
332 if ( bNextMustBeDefineEOL ) {
333 if ( nToken != RSCDEFINELEND ) {
334 // end of macro found, so destroy res.
335 bDefine = false;
336 Execute( LEVELDOWN, "" );
337 bNextMustBeDefineEOL = false;
339 else {
340 // next line also in macro definition
341 bNextMustBeDefineEOL = false;
342 if ( bMergeMode )
343 WriteToMerged( sOrig , false );
344 return 1;
350 bool bExecuteDown = false;
351 if ( nToken != LEVELDOWN ) {
352 sal_uInt16 nOpen = 0;
353 sal_uInt16 nClose = 0;
354 bool bReadOver1 = false;
355 sal_uInt16 i = 0;
356 for ( i = 0; i < sToken.getLength(); i++ ) {
357 if ( sToken[i] == '"' )
358 bReadOver1 = !bReadOver1;
359 if ( !bReadOver1 && ( sToken[i] == '{' ))
360 nOpen++;
363 bReadOver1 = false;
364 for ( i = 0; i < sToken.getLength(); i++ ) {
365 if ( sToken[i] == '"' )
366 bReadOver1 = !bReadOver1;
367 if ( !bReadOver1 && ( sToken[i] == '}' ))
368 nClose++;
371 if ( nOpen < nClose )
372 bExecuteDown = true;
375 bool bWriteToMerged = bMergeMode;
376 switch ( nToken ) {
378 case NORMDEFINE:
379 if ( bMergeMode )
380 WriteToMerged( sOrig , false );
381 return 0;
382 case RSCDEFINE:
383 bDefine = true; // res. defined in macro
385 case RESOURCE:
386 case RESOURCEEXPR: {
387 if ( nToken != RSCDEFINE )
388 bNextMustBeDefineEOL = false;
389 // this is the beginning of a new res.
390 nLevel++;
391 if ( nLevel > 1 ) {
392 aResStack[ nLevel - 2 ]->bChild = true;
395 // create new instance for this res. and fill mandatory fields
397 pResData = new ResData( FullId() , sFilename );
398 aResStack.push_back( pResData );
399 sToken = sToken.replaceAll("\n", OString()).
400 replaceAll("\r", OString()).
401 replaceAll("{", OString()).replace('\t', ' ');
402 sToken = sToken.trim();
403 OString sTLower = sToken.getToken(0, ' ').toAsciiLowerCase();
404 pResData->sResTyp = sTLower;
405 OString sId( sToken.copy( pResData->sResTyp.getLength() + 1 ));
406 OString sCondition;
407 if ( sId.indexOf( '#' ) != -1 )
409 // between ResTyp, Id and paranthes is a precomp. condition
410 sCondition = "#";
411 sal_Int32 n = 0;
412 sId = sId.getToken(0, '#', n);
413 sCondition += sId.getToken(0, '#', n);
415 sId = sId.getToken(0, '/');
416 CleanValue( sId );
417 sId = sId.replaceAll("\t", OString());
418 pResData->SetId( sId, ID_LEVEL_IDENTIFIER );
419 if (!sCondition.isEmpty())
421 Execute( CONDITION, ""); // execute the precomp. condition
424 break;
425 case SMALRESOURCE: {
426 // this is the beginning of a new res.
427 bNextMustBeDefineEOL = false;
428 nLevel++;
429 if ( nLevel > 1 ) {
430 aResStack[ nLevel - 2 ]->bChild = true;
433 // create new instance for this res. and fill mandatory fields
435 pResData = new ResData( FullId() , sFilename );
436 aResStack.push_back( pResData );
437 sToken = sToken.replaceAll("\n", OString()).
438 replaceAll("\r", OString()).
439 replaceAll("{", OString()).
440 replaceAll("\t", OString()).
441 replaceAll(" ", OString()).
442 replaceAll("\\", OString()).toAsciiLowerCase();
443 pResData->sResTyp = sToken;
445 break;
446 case LEVELUP: {
447 // push
448 if ( nList )
450 nListLevel++;
451 break;
454 OString sLowerTyp;
455 if ( pResData )
456 sLowerTyp = "unknown";
457 nLevel++;
458 if ( nLevel > 1 ) {
459 aResStack[ nLevel - 2 ]->bChild = true;
462 ResData *pNewData = new ResData( FullId() , sFilename );
463 pNewData->sResTyp = sLowerTyp;
464 aResStack.push_back( pNewData );
466 break;
467 case LEVELDOWN: {
468 // pop
469 if ( !nList || !nListLevel ) {
470 if ( nLevel ) {
471 if ( bDefine && (nLevel == 1 )) {
472 bDefine = false;
473 bNextMustBeDefineEOL = false;
475 WriteData( pResData );
476 ResStack::iterator it = aResStack.begin();
477 ::std::advance( it, nLevel-1 );
478 delete *it;
479 aResStack.erase( it );
480 nLevel--;
482 if( nList )
484 nList = LIST_NON;
485 nListLevel = 1;
488 else
490 if ( bDefine )
491 bNextMustBeDefineEOL = true;
492 nListLevel--;
495 break;
496 case ASSIGNMENT:
498 // interpret different types of assignement
499 sal_Int32 n = 0;
500 OString sKey = sToken.getToken(0, '=', n).
501 replaceAll(" ", OString()).
502 replaceAll("\t", OString());
503 OString sValue = sToken.getToken(0, '=', n);
504 CleanValue( sValue );
505 sKey = sKey.toAsciiUpperCase();
506 if (sKey == "IDENTIFIER")
508 OString sId(
509 sValue.replaceAll("\t", OString()).
510 replaceAll(" ", OString()));
511 pResData->SetId(sId, ID_LEVEL_IDENTIFIER);
513 else if (sKey =="STRINGLIST")
515 nList = LIST_STRING;
516 nListIndex = 0;
517 nListLevel = 1;
519 else if (sKey == "FILTERLIST")
521 nList = LIST_FILTER;
522 nListIndex = 0;
523 nListLevel = 1;
525 if (sToken.indexOf( '{' ) != -1
526 && (lcl_countOccurrences(sToken, '{')
527 > lcl_countOccurrences(sToken, '}')))
529 Parse( LEVELUP, "" );
532 break;
533 case LISTASSIGNMENT:
535 OString sTmpToken(
536 sToken.replaceAll(" ", OString()).toAsciiLowerCase());
537 sal_Int32 nPos = sTmpToken.indexOf("[en-us]=");
538 if (nPos != -1) {
539 OString sKey(
540 sTmpToken.copy(0 , nPos).replaceAll(" ", OString()).
541 replaceAll("\t", OString()));
542 OString sValue = sToken.getToken(1, '=');
543 CleanValue( sValue );
544 sKey = sKey.toAsciiUpperCase();
545 if (sKey == "STRINGLIST")
547 nList = LIST_STRING;
549 else if (sKey == "FILTERLIST")
551 nList = LIST_FILTER;
553 else if (sKey == "PAIREDLIST")
555 nList = LIST_PAIRED;
557 else if (sKey == "ITEMLIST")
559 nList = LIST_ITEM;
561 if( nList )
563 nListIndex = 0;
564 nListLevel = 1;
568 break;
569 case TEXT:
570 case _LISTTEXT:
571 case LISTTEXT: {
572 // this is an entry for a List
573 if ( nList )
575 SetChildWithText();
576 InsertListEntry( sOrig );
579 break;
580 case LONGTEXTLINE:
581 case TEXTLINE:
582 if ( nLevel )
584 CutComment( sToken );
586 // this is a text line!!!
587 OString t(sToken.getToken(0, '='));
588 OString sKey(
589 t.getToken(0, '[').replaceAll(" ", OString()).
590 replaceAll("\t", OString()));
591 OString sText( GetText( sToken, nToken ));
592 OString sLang;
593 if ( sToken.getToken(0, '=').indexOf('[') != -1 )
595 sLang = sToken.getToken(0, '=').getToken(1, '[').
596 getToken(0, ']');
597 CleanValue( sLang );
599 OString sLangIndex = sLang;
600 OString sOrigKey = sKey;
601 if ( !sText.isEmpty() && !sLang.isEmpty() )
603 sKey = sKey.toAsciiUpperCase();
604 if (sKey == "TEXT" || sKey == "MESSAGE" || sKey == "CUSTOMUNITTEXT")
606 SetChildWithText();
607 if ( sLangIndex.equalsIgnoreAsciiCase("en-US") )
608 pResData->SetId( sText, ID_LEVEL_TEXT );
610 pResData->bText = true;
611 pResData->sTextTyp = sOrigKey;
612 if ( !bMergeMode )
614 pResData->sText[ sLangIndex ] = sText;
617 else if ( sKey == "QUICKHELPTEXT" ) {
618 SetChildWithText();
619 pResData->bQuickHelpText = true;
620 if ( !bMergeMode )
622 pResData->sQuickHelpText[ sLangIndex ] = sText;
625 else if ( sKey == "TITLE" ) {
626 SetChildWithText();
627 pResData->bTitle = true;
628 if ( !bMergeMode )
630 pResData->sTitle[ sLangIndex ] = sText;
635 break;
636 case APPFONTMAPPING:
637 break;
638 case RSCDEFINELEND:
639 break;
640 case CONDITION: {
641 if ( nLevel && pResData ) {
642 WriteData( pResData, true );
645 break;
646 case EMPTYLINE : {
647 if ( bDefine ) {
648 bNextMustBeDefineEOL = false;
649 bDefine = false;
650 while ( nLevel )
651 Parse( LEVELDOWN, "" );
654 break;
655 case PRAGMA : {
656 std::fprintf(stderr, "ERROR: archaic PRAGMA %s\n", sToken.getStr());
657 std::exit(EXIT_FAILURE);
659 break;
661 if ( bWriteToMerged ) {
662 // the current token must be written to dest. without merging
664 if( bDefine && sOrig.getLength() > 2 ){
665 for( sal_uInt16 n = 0 ; n < sOrig.getLength() ; n++ ){
666 if( sOrig[n] == '\n' && sOrig[n-1] != '\\'){
667 sOrig = sOrig.replaceAt(n++, 0, "\\");
671 WriteToMerged( sOrig , false);
674 if ( bExecuteDown ) {
675 Parse( LEVELDOWN, "" );
678 return 1;
681 void Export::CutComment( OString &rText )
683 if (rText.indexOf("//") != -1) {
684 OString sWork(rText.replaceAll("\\\"", "XX"));
685 bool bInner = false;
686 for (sal_Int32 i = 0; i < sWork.getLength() - 1; ++i) {
687 if (sWork[i] == '"') {
688 bInner = !bInner;
689 } else if (sWork[i] == '/' && !bInner && sWork[i + 1] == '/' ) {
690 rText = rText.copy(0, i);
691 break;
697 bool Export::WriteData( ResData *pResData, bool bCreateNew )
699 if ( bMergeMode ) {
700 MergeRest( pResData );
701 return true;
704 // mandatory to export: en-US
706 if (( !pResData->sText[ SOURCE_LANGUAGE ].isEmpty())
708 ( !pResData->sQuickHelpText[ SOURCE_LANGUAGE ].isEmpty())
710 ( !pResData->sTitle[ SOURCE_LANGUAGE ].isEmpty()))
713 OString sGID = pResData->sGId;
714 OString sLID;
715 if (sGID.isEmpty())
716 sGID = pResData->sId;
717 else
718 sLID = pResData->sId;
720 OString sXText = pResData->sText[ SOURCE_LANGUAGE ];
721 OString sXHText = pResData->sText[ X_COMMENT ];
722 OString sXQHText = pResData->sQuickHelpText[ SOURCE_LANGUAGE ];
723 OString sXTitle = pResData->sTitle[ SOURCE_LANGUAGE ];
725 if( !sXText.isEmpty() )
727 ConvertExportContent(sXText);
728 ConvertExportContent(sXHText);
729 common::writePoEntry(
730 "Transex3", *aOutput.mPo, global::inputPathname,
731 pResData->sResTyp, sGID, sLID, sXHText, sXText);
733 if( !sXQHText.isEmpty() )
735 ConvertExportContent(sXQHText);
736 common::writePoEntry(
737 "Transex3", *aOutput.mPo, global::inputPathname, pResData->sResTyp,
738 sGID, sLID, OString(), sXQHText, PoEntry::TQUICKHELPTEXT );
740 if( !sXTitle.isEmpty() )
742 ConvertExportContent(sXTitle);
743 common::writePoEntry(
744 "Transex3", *aOutput.mPo, global::inputPathname, pResData->sResTyp,
745 sGID, sLID, OString(), sXTitle, PoEntry::TTITLE );
748 if ( bCreateNew ) {
749 pResData->sText[ SOURCE_LANGUAGE ] = "";
750 pResData->sQuickHelpText[ SOURCE_LANGUAGE ]= "";
751 pResData->sTitle[ SOURCE_LANGUAGE ] = "";
755 if( nList )
757 WriteExportList( pResData, pResData->m_aList, nList );
758 if ( bCreateNew )
759 pResData->m_aList.clear();
761 return true;
764 OString Export::GetPairedListID(const OString& rText)
766 // < "STRING" ; IDENTIFIER ; > ;
767 return rText.getToken(1, ';').toAsciiUpperCase().replace('\t', ' ').trim();
770 OString Export::GetPairedListString(const OString& rText)
772 // < "STRING" ; IDENTIFIER ; > ;
773 OString sString(rText.getToken(0, ';').replace('\t', ' '));
774 sString = sString.trim();
775 OString s1(sString.copy(sString.indexOf('"') + 1));
776 sString = s1.copy(0, s1.lastIndexOf('"'));
777 return sString.trim();
780 OString Export::StripList(const OString & rText)
782 OString s1 = rText.copy( rText.indexOf('\"') + 1);
783 return s1.copy( 0 , s1.lastIndexOf('\"'));
786 bool Export::WriteExportList(ResData *pResData, ExportList& rExportList,
787 const sal_uInt16 nTyp)
789 OString sGID(pResData->sGId);
790 if (sGID.isEmpty())
791 sGID = pResData->sId;
792 else {
793 sGID += ".";
794 sGID += pResData->sId;
795 while (sGID.endsWith(".")) {
796 sGID = sGID.copy(0, sGID.getLength() - 1);
800 for ( size_t i = 0; i < rExportList.size(); i++ )
802 OString sLID;
803 OString sText(rExportList[ i ]);
805 // Strip PairList Line String
806 if (nTyp == LIST_PAIRED)
808 sLID = GetPairedListID( sText );
809 sText = GetPairedListString( sText );
811 else
813 sText = StripList( sText );
814 if( sText == "\\\"" )
815 sText = "\"";
817 ConvertExportContent(sText);
819 if (nTyp != LIST_PAIRED)
820 sLID = sText;
822 OString sType = lcl_GetListTyp( nList, false );
824 common::writePoEntry(
825 "Transex3", *aOutput.mPo, global::inputPathname,
826 sType, sGID, sLID, OString(), sText);
829 return true;
832 OString Export::FullId()
834 OStringBuffer sFull;
835 if ( nLevel > 1 )
837 sFull.append(aResStack[ 0 ]->sId);
838 for ( size_t i = 1; i < nLevel - 1; ++i )
840 OString sToAdd = aResStack[ i ]->sId;
841 if (!sToAdd.isEmpty())
842 sFull.append('.').append(sToAdd);
845 if (sFull.getLength() > 255)
847 OString sError("GroupId > 255 chars");
848 printf("GroupID = %s\n", sFull.getStr());
849 yyerror(sError.getStr());
852 return sFull.makeStringAndClear();
855 void Export::InsertListEntry(const OString &rLine)
857 ResData *pResData = ( nLevel-1 < aResStack.size() ) ? aResStack[ nLevel-1 ] : NULL;
859 if (!pResData)
860 std::exit(EXIT_FAILURE);
862 if( pResData->m_aList.empty() )
863 nListIndex = 0;
865 pResData->m_aList.push_back(rLine);
868 void Export::CleanValue( OString &rValue )
870 while ( !rValue.isEmpty()) {
871 if (( rValue[0] == ' ' ) || ( rValue[0] == '\t' ))
872 rValue = rValue.copy( 1 );
873 else
874 break;
877 if ( !rValue.isEmpty()) {
878 for ( sal_Int32 i = rValue.getLength() - 1; i > 0; i-- ) {
879 if (( rValue[i] == ' ' ) || ( rValue[i] == '\t' ) ||
880 ( rValue[i] == '\n' ) || ( rValue[i] == ';' ) ||
881 ( rValue[i] == '{' ) || ( rValue[i] == '\\' ) ||
882 ( rValue[i] == '\r' ))
883 rValue = rValue.copy(0, i);
884 else
885 break;
890 #define TXT_STATE_TEXT 0x001
891 #define TXT_STATE_MACRO 0x002
893 OString Export::GetText(const OString &rSource, int nToken)
895 OString sReturn;
896 switch ( nToken )
898 case TEXTLINE:
899 case LONGTEXTLINE:
901 OString sTmp(rSource.copy(rSource.indexOf('=')));
902 CleanValue( sTmp );
903 sTmp = sTmp.replaceAll("\n", OString()).
904 replaceAll("\r", OString()).
905 replaceAll("\\\\\"", "-=<[BSlashBSlashHKom]>=-\"").
906 replaceAll("\\\"", "-=<[Hochkomma]>=-").
907 replaceAll("\\\x7f", "-=<[0x7F]>=-").
908 replaceAll("\\0x7F", "-=<[0x7F]>=-");
910 sal_uInt16 nState = TXT_STATE_TEXT;
911 for (sal_Int32 i = 1; i <= lcl_countOccurrences(sTmp, '"'); ++i)
913 OString sToken(sTmp.getToken(i, '"'));
914 if (!sToken.isEmpty()) {
915 if ( nState == TXT_STATE_TEXT ) {
916 sReturn += sToken;
917 nState = TXT_STATE_MACRO;
919 else {
920 sToken = sToken.replace('\t', ' ');
921 for (;;) {
922 sal_Int32 n = 0;
923 sToken = sToken.replaceFirst(" ", " ", &n);
924 if (n == -1) {
925 break;
928 sToken = sToken.trim();
929 if (!sToken.isEmpty()) {
930 sReturn += "\\\" ";
931 sReturn += sToken;
932 sReturn += " \\\"";
934 nState = TXT_STATE_TEXT;
939 sReturn = sReturn.replaceAll("-=<[0x7F]>=-", "\x7f").
940 replaceAll("-=<[Hochkomma]>=-", "\"").
941 replaceAll("-=<[BSlashBSlashHKom]>=-", "\\\\").
942 replaceAll("\\\\", "-=<[BSlashBSlash]>=-").
943 replaceAll("-=<[BSlashBSlash]>=-", "\\");
945 break;
947 return sReturn;
950 void Export::WriteToMerged(const OString &rText , bool bSDFContent)
952 OString sText(rText);
953 for (;;) {
954 sal_Int32 n = 0;
955 sText = sText.replaceFirst(" \n", "\n", &n);
956 if (n == -1) {
957 break;
960 if (pParseQueue->bNextIsM && bSDFContent && sText.getLength() > 2) {
961 for (sal_Int32 n = 0; n < sText.getLength(); ++n) {
962 if (sText[n] == '\n' && sText[n - 1] != '\\') {
963 sText = sText.replaceAt(n++, 0, "\\");
966 } else if (pParseQueue->bLastWasM && sText.getLength() > 2) {
967 for (sal_Int32 n = 0; n < sText.getLength(); ++n) {
968 if (sText[n] == '\n' && sText[n - 1] != '\\') {
969 sText = sText.replaceAt(n++, 0, "\\");
971 if (sText[n] == '\n') {
972 pParseQueue->bMflag = true;
975 } else if (pParseQueue->bCurrentIsM && bSDFContent && sText.getLength() > 2)
977 for (sal_Int32 n = 0; n < sText.getLength(); ++n) {
978 if (sText[n] == '\n' && sText[n - 1] != '\\') {
979 sText = sText.replaceAt(n++, 0, "\\");
980 pParseQueue->bMflag = true;
983 } else if (pParseQueue->bMflag) {
984 for (sal_Int32 n = 1; n < sText.getLength(); ++n) {
985 if (sText[n] == '\n' && sText[n - 1] != '\\') {
986 sText = sText.replaceAt(n++, 0, "\\");
989 } for (sal_Int32 i = 0; i < sText.getLength(); ++i) {
990 if (sText[i] == '\n') {
991 *aOutput.mSimple << '\n';
992 } else {
993 char cChar = sText[i];
994 *aOutput.mSimple << cChar;
999 void Export::ConvertMergeContent( OString &rText )
1001 rText = rText.replaceAll("\\\'","\'"); // Temporary: until PO files contain escaped single quotes
1002 // (Maybe next PO update solve this)
1003 rText =
1004 helper::escapeAll(
1005 rText.replaceAll("\x7f","\\0x7F"),
1006 "\n""\t""\\""\"","\\n""\\t""\\\\""\\\"");
1008 rText = "\"" + rText + "\"";
1011 void Export::ConvertExportContent( OString& rText )
1013 rText = helper::unEscapeAll(rText,"\\n""\\t""\\\\""\\\"","\n""\t""\\""\"");
1016 void Export::ResData2Output( MergeEntrys *pEntry, sal_uInt16 nType, const OString& rTextType )
1018 bool bAddSemicolon = false;
1019 bool bFirst = true;
1020 OString sCur;
1022 for( unsigned int n = 0; n < aLanguages.size(); n++ ){
1023 sCur = aLanguages[ n ];
1025 OString sText;
1026 bool bText = pEntry->GetText( sText, nType, sCur , true );
1027 if ( bText && !sText.isEmpty() ) {
1028 OStringBuffer sOutput;
1029 if ( bNextMustBeDefineEOL) {
1030 if ( bFirst )
1031 sOutput.append("\t\\\n");
1032 else
1033 sOutput.append(";\t\\\n");
1035 bFirst=false;
1036 sOutput.append("\t" + rTextType);
1038 if ( !sCur.equalsIgnoreAsciiCase("en-US") ) {
1039 sOutput.append("[ " + sCur + " ] ");
1042 ConvertMergeContent( sText );
1043 sOutput.append("= " + sText);
1045 if ( bDefine )
1046 sOutput.append(";\\\n");
1047 else if ( !bNextMustBeDefineEOL )
1048 sOutput.append(";\n");
1049 else
1050 bAddSemicolon = true;
1051 for ( sal_uInt16 j = 1; j < nLevel; j++ )
1052 sOutput.append("\t");
1053 WriteToMerged( sOutput.makeStringAndClear() , true );
1058 if ( bAddSemicolon ) {
1059 WriteToMerged( ";" , false );
1063 void Export::MergeRest( ResData *pResData )
1065 if ( !pMergeDataFile ){
1066 pMergeDataFile = new MergeDataFile( sMergeSrc, global::inputPathname, true );
1067 aLanguages = pMergeDataFile->GetLanguages();
1071 MergeEntrys *pEntry = 0;
1072 if( pResData->bText || pResData->bQuickHelpText || pResData->bTitle )
1073 pEntry = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData );
1075 if ( pEntry )
1077 if ( pResData->bText )
1078 ResData2Output( pEntry, STRING_TYP_TEXT, pResData->sTextTyp );
1080 if ( pResData->bQuickHelpText )
1081 ResData2Output( pEntry, STRING_TYP_QUICKHELPTEXT, OString("QuickHelpText") );
1083 if ( pResData->bTitle )
1084 ResData2Output( pEntry, STRING_TYP_TITLE, OString("Title") );
1087 // Merge Lists
1088 if ( nList )
1090 OString sOldId = pResData->sId;
1091 OString sOldGId = pResData->sGId;
1092 OString sOldTyp = pResData->sResTyp;
1094 // Set pResData so we can find the corresponding string
1095 if (!pResData->sGId.isEmpty())
1096 pResData->sGId = pResData->sGId + OString('.');
1097 pResData->sGId = pResData->sGId + pResData->sId;
1099 pResData->sResTyp = lcl_GetListTyp( nList, false );
1101 OString sSpace;
1102 for ( sal_uInt16 i = 1; i < nLevel-1; i++ )
1103 sSpace += "\t";
1105 OString sCur;
1106 for( unsigned int n = 0; n < aLanguages.size(); n++ )
1108 sCur = aLanguages[ n ];
1110 sal_uInt16 nLIndex = 0;
1111 sal_uInt16 nMaxIndex = pResData->m_aList.size();
1112 while( nLIndex < nMaxIndex )
1114 if ( nLIndex == 0 )
1116 OStringBuffer sHead;
1117 if ( bNextMustBeDefineEOL )
1118 sHead.append("\\\n\t");
1119 sHead.append(sSpace + lcl_GetListTyp( nList, true ) + " [ " + sCur + " ] ");
1121 if ( bDefine || bNextMustBeDefineEOL )
1123 sHead.append("= \\\n" + sSpace + "\t{\\\n\t");
1125 else
1127 sHead.append("= \n" + sSpace + "\t{\n\t");
1129 WriteToMerged(sHead.makeStringAndClear() , true);
1132 OString sLine = pResData->m_aList[ nLIndex ];
1133 if ( sLine.indexOf( '>' ) != -1 )
1135 if ((( sLine.indexOf( '{' ) == -1 ) ||
1136 ( sLine.indexOf( '{' ) >= sLine.indexOf( '"' ))) &&
1137 (( sLine.indexOf( '<' ) == -1 ) ||
1138 ( sLine.indexOf( '<' ) >= sLine.indexOf( '"' ))))
1140 sLine = sLine.replaceFirst("\"", "< \"" );
1144 // Set matching identifier
1145 if ( nList == LIST_PAIRED )
1147 pResData->sId = GetPairedListID ( sLine );
1149 else
1151 pResData->sId =
1152 sLine.copy(
1153 sLine.indexOf('"')+1,
1154 sLine.lastIndexOf('"')-sLine.indexOf('"')-1);
1155 ConvertExportContent( pResData->sId );
1158 MergeEntrys* pEntrys = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData );
1160 if( pEntrys )
1162 OString sText;
1163 pEntrys->GetText( sText, STRING_TYP_TEXT, sCur, false );
1164 if( !sText.isEmpty())
1166 ConvertMergeContent( sText );
1167 sLine =
1168 sLine.copy( 0 , sLine.indexOf('"') ) +
1169 sText +
1170 sLine.copy( sLine.lastIndexOf('"') + 1 );
1174 OString sText1( "\t" );
1175 sText1 += sLine;
1176 if ( bDefine || bNextMustBeDefineEOL )
1177 sText1 += " ;\\\n";
1178 else
1179 sText1 += " ;\n";
1180 sText1 += sSpace;
1181 sText1 += "\t";
1182 WriteToMerged( sText1 ,true );
1183 ++nLIndex;
1186 if ( nLIndex > 0 )
1188 OString sFooter;
1189 if (!sSpace.isEmpty())
1190 sFooter = sSpace.copy(1);
1192 if ( bNextMustBeDefineEOL )
1193 sFooter += "};";
1194 else if ( !bDefine )
1195 sFooter += "};\n\t";
1196 else
1197 sFooter += "\n\n";
1198 WriteToMerged( sFooter ,true );
1201 pResData->sId = sOldId;
1202 pResData->sGId = sOldGId;
1203 pResData->sResTyp = sOldTyp;
1205 pParseQueue->bMflag = false;
1208 void Export::SetChildWithText()
1210 if ( aResStack.size() > 1 ) {
1211 for ( size_t i = 0; i < aResStack.size() - 1; i++ ) {
1212 aResStack[ i ]->bChildWithText = true;
1217 void ParserQueue::Push( const QueueEntry& aEntry )
1219 sal_Int32 nLen = aEntry.sLine.getLength();
1221 if( !bStart ){
1222 aQueueCur->push( aEntry );
1223 if( nLen > 1 && aEntry.sLine[nLen-1] == '\n' )
1224 bStart = true;
1225 else if ( aEntry.nTyp != IGNOREDTOKENS ){
1226 if( nLen > 1 && ( aEntry.sLine[nLen-1] == '\\') ){
1227 // Next is Macro
1228 bCurrentIsM = true;
1229 }else{
1230 // Next is no Macro
1231 bCurrentIsM = false;
1235 else{
1236 aQueueNext->push( aEntry );
1237 if( nLen > 1 && aEntry.sLine[nLen-1] != '\n' ){
1238 if( nLen > 1 && ( aEntry.sLine[nLen-1] == '\\') ){
1239 // Next is Macro
1240 bNextIsM = true;
1242 else{
1243 // Next is no Macro
1244 bNextIsM = false;
1246 }else if( nLen > 2 && aEntry.sLine[nLen-1] == '\n' ){
1247 if( aEntry.nTyp != IGNOREDTOKENS ){
1248 if( nLen > 2 && ( aEntry.sLine[nLen-2] == '\\') ){
1249 // Next is Macro
1250 bNextIsM = true;
1252 else{
1253 // Next is no Macro
1254 bNextIsM = false;
1257 // Pop current
1258 Pop( *aQueueCur );
1259 bLastWasM = bCurrentIsM;
1260 // next -> current
1261 bCurrentIsM = bNextIsM;
1262 std::queue<QueueEntry>* aQref = aQueueCur;
1263 aQueueCur = aQueueNext;
1264 aQueueNext = aQref;
1268 else{
1269 // Pop current
1270 Pop( *aQueueCur );
1271 bLastWasM = bCurrentIsM;
1272 // next -> current
1273 bCurrentIsM = bNextIsM;
1274 std::queue<QueueEntry>* aQref = aQueueCur;
1275 aQueueCur = aQueueNext;
1276 aQueueNext = aQref;
1281 void ParserQueue::Close(){
1282 // Pop current
1283 Pop( *aQueueCur );
1284 // next -> current
1285 bLastWasM = bCurrentIsM;
1286 bCurrentIsM = bNextIsM;
1287 std::queue<QueueEntry>* aQref = aQueueCur;
1288 aQueueCur = aQueueNext;
1289 aQueueNext = aQref;
1290 bNextIsM = false;
1291 Pop( *aQueueNext );
1294 void ParserQueue::Pop( std::queue<QueueEntry>& aQueue )
1296 while (!aQueue.empty())
1298 QueueEntry aEntry = aQueue.front();
1299 aQueue.pop();
1300 aExport.Execute(aEntry.nTyp, aEntry.sLine.getStr());
1304 ParserQueue::ParserQueue( Export& aExportObj )
1306 bCurrentIsM( false ),
1307 bNextIsM( false ) ,
1308 bLastWasM( false ),
1309 bMflag( false ) ,
1310 aExport( aExportObj ) ,
1311 bStart( false )
1313 aQueueNext = new std::queue<QueueEntry>;
1314 aQueueCur = new std::queue<QueueEntry>;
1318 ParserQueue::~ParserQueue(){
1319 if( aQueueNext ) delete aQueueNext;
1320 if( aQueueCur ) delete aQueueCur;
1323 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */