47a23789b7
Change-Id: I9272c244e22d87fbd2547f4fcb6b94493ffaad3a
517 lines
14 KiB
C++
517 lines
14 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include "common.hxx"
|
|
#include "sal/config.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
#include "boost/scoped_ptr.hpp"
|
|
#include "rtl/strbuf.hxx"
|
|
|
|
#include "helper.hxx"
|
|
#include "export.hxx"
|
|
#include "cfgmerge.hxx"
|
|
#include "tokens.h"
|
|
|
|
void yyerror(char const *);
|
|
|
|
namespace {
|
|
|
|
namespace global {
|
|
|
|
OString inputPathname;
|
|
boost::scoped_ptr< CfgParser > parser;
|
|
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
FILE * init(int argc, char ** argv) {
|
|
|
|
common::HandledArgs aArgs;
|
|
if ( !common::handleArguments(argc, argv, aArgs) )
|
|
{
|
|
common::writeUsage("cfgex","*.xcu");
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
global::inputPathname = aArgs.m_sInputFile;
|
|
|
|
FILE * pFile = std::fopen(global::inputPathname.getStr(), "r");
|
|
if (pFile == 0) {
|
|
std::fprintf(
|
|
stderr, "Error: Cannot open file \"%s\"\n",
|
|
global::inputPathname.getStr() );
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (aArgs.m_bMergeMode) {
|
|
global::parser.reset(
|
|
new CfgMerge(
|
|
aArgs.m_sMergeSrc, aArgs.m_sOutputFile,
|
|
global::inputPathname, aArgs.m_sLanguage ));
|
|
} else {
|
|
global::parser.reset(
|
|
new CfgExport(
|
|
aArgs.m_sOutputFile, global::inputPathname ));
|
|
}
|
|
|
|
return pFile;
|
|
}
|
|
|
|
void workOnTokenSet(int nTyp, char * pTokenText) {
|
|
global::parser->Execute( nTyp, pTokenText );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// class CfgStackData
|
|
//
|
|
|
|
CfgStackData* CfgStack::Push(const OString &rTag, const OString &rId)
|
|
{
|
|
CfgStackData *pD = new CfgStackData( rTag, rId );
|
|
maList.push_back( pD );
|
|
return pD;
|
|
}
|
|
|
|
//
|
|
// class CfgStack
|
|
//
|
|
|
|
CfgStack::~CfgStack()
|
|
{
|
|
for ( size_t i = 0, n = maList.size(); i < n; i++ )
|
|
delete maList[ i ];
|
|
maList.clear();
|
|
}
|
|
|
|
OString CfgStack::GetAccessPath( size_t nPos )
|
|
{
|
|
OStringBuffer sReturn;
|
|
for (size_t i = 0; i <= nPos; ++i)
|
|
{
|
|
if (i)
|
|
sReturn.append('.');
|
|
sReturn.append(maList[i]->GetIdentifier());
|
|
}
|
|
|
|
return sReturn.makeStringAndClear();
|
|
}
|
|
|
|
CfgStackData *CfgStack::GetStackData()
|
|
{
|
|
if (!maList.empty())
|
|
return maList[maList.size() - 1];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// class CfgParser
|
|
//
|
|
|
|
CfgParser::CfgParser()
|
|
: pStackData( NULL ),
|
|
bLocalize( sal_False )
|
|
{
|
|
}
|
|
|
|
CfgParser::~CfgParser()
|
|
{
|
|
}
|
|
|
|
sal_Bool CfgParser::IsTokenClosed(const OString &rToken)
|
|
{
|
|
return rToken[rToken.getLength() - 2] == '/';
|
|
}
|
|
|
|
void CfgParser::AddText(
|
|
OString &rText,
|
|
const OString &rIsoLang,
|
|
const OString &rResTyp )
|
|
{
|
|
rText = rText.replaceAll(OString('\n'), OString()).
|
|
replaceAll(OString('\r'), OString()).
|
|
replaceAll(OString('\t'), OString());
|
|
pStackData->sResTyp = rResTyp;
|
|
WorkOnText( rText, rIsoLang );
|
|
pStackData->sText[ rIsoLang ] = rText;
|
|
}
|
|
|
|
int CfgParser::ExecuteAnalyzedToken( int nToken, char *pToken )
|
|
{
|
|
OString sToken( pToken );
|
|
|
|
if ( sToken == " " || sToken == "\t" )
|
|
sLastWhitespace += sToken;
|
|
|
|
OString sTokenName;
|
|
OString sTokenId;
|
|
|
|
sal_Bool bOutput = sal_True;
|
|
|
|
switch ( nToken ) {
|
|
case CFG_TOKEN_PACKAGE:
|
|
case CFG_TOKEN_COMPONENT:
|
|
case CFG_TOKEN_TEMPLATE:
|
|
case CFG_TOKEN_CONFIGNAME:
|
|
case CFG_TOKEN_OORNAME:
|
|
case CFG_TOKEN_OORVALUE:
|
|
case CFG_TAG:
|
|
case ANYTOKEN:
|
|
case CFG_TEXT_START:
|
|
{
|
|
sTokenName = sToken.getToken(1, '<').getToken(0, '>').
|
|
getToken(0, ' ');
|
|
|
|
if ( !IsTokenClosed( sToken )) {
|
|
OString sSearch;
|
|
switch ( nToken ) {
|
|
case CFG_TOKEN_PACKAGE:
|
|
sSearch = "package-id=";
|
|
break;
|
|
case CFG_TOKEN_COMPONENT:
|
|
sSearch = "component-id=";
|
|
break;
|
|
case CFG_TOKEN_TEMPLATE:
|
|
sSearch = "template-id=";
|
|
break;
|
|
case CFG_TOKEN_CONFIGNAME:
|
|
sSearch = "cfg:name=";
|
|
break;
|
|
case CFG_TOKEN_OORNAME:
|
|
sSearch = "oor:name=";
|
|
bLocalize = sal_True;
|
|
break;
|
|
case CFG_TOKEN_OORVALUE:
|
|
sSearch = "oor:value=";
|
|
break;
|
|
case CFG_TEXT_START: {
|
|
if ( sCurrentResTyp != sTokenName ) {
|
|
WorkOnResourceEnd();
|
|
}
|
|
sCurrentResTyp = sTokenName;
|
|
|
|
OString sTemp = sToken.copy( sToken.indexOf( "xml:lang=" ));
|
|
sCurrentIsoLang = sTemp.getToken(1, '"');
|
|
|
|
if ( sCurrentIsoLang == NO_TRANSLATE_ISO )
|
|
bLocalize = sal_False;
|
|
|
|
pStackData->sTextTag = sToken;
|
|
|
|
sCurrentText = "";
|
|
}
|
|
break;
|
|
}
|
|
if ( !sSearch.isEmpty())
|
|
{
|
|
OString sTemp = sToken.copy( sToken.indexOf( sSearch ));
|
|
sTokenId = sTemp.getToken(1, '"');
|
|
}
|
|
pStackData = aStack.Push( sTokenName, sTokenId );
|
|
|
|
if ( sSearch == "cfg:name=" ) {
|
|
OString sTemp( sToken.toAsciiUpperCase() );
|
|
bLocalize = (( sTemp.indexOf( "CFG:TYPE=\"STRING\"" ) != -1 ) &&
|
|
( sTemp.indexOf( "CFG:LOCALIZED=\"sal_True\"" ) != -1 ));
|
|
}
|
|
}
|
|
else if ( sTokenName == "label" ) {
|
|
if ( sCurrentResTyp != sTokenName ) {
|
|
WorkOnResourceEnd();
|
|
}
|
|
sCurrentResTyp = sTokenName;
|
|
}
|
|
}
|
|
break;
|
|
case CFG_CLOSETAG:
|
|
{
|
|
sTokenName = sToken.getToken(1, '/').getToken(0, '>').
|
|
getToken(0, ' ');
|
|
if ( aStack.GetStackData() && ( aStack.GetStackData()->GetTagType() == sTokenName ))
|
|
{
|
|
if (sCurrentText.isEmpty())
|
|
WorkOnResourceEnd();
|
|
aStack.Pop();
|
|
pStackData = aStack.GetStackData();
|
|
}
|
|
else
|
|
{
|
|
OString sError( "Misplaced close tag: " );
|
|
OString sInFile(" in file ");
|
|
sError += sToken;
|
|
sError += sInFile;
|
|
sError += global::inputPathname;
|
|
Error( sError );
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CFG_TEXTCHAR:
|
|
sCurrentText += sToken;
|
|
bOutput = sal_False;
|
|
break;
|
|
|
|
case CFG_TOKEN_NO_TRANSLATE:
|
|
bLocalize = sal_False;
|
|
break;
|
|
}
|
|
|
|
if ( !sCurrentText.isEmpty() && nToken != CFG_TEXTCHAR )
|
|
{
|
|
AddText( sCurrentText, sCurrentIsoLang, sCurrentResTyp );
|
|
Output( sCurrentText );
|
|
sCurrentText = OString();
|
|
pStackData->sEndTextTag = sToken;
|
|
}
|
|
|
|
if ( bOutput )
|
|
Output( sToken );
|
|
|
|
if ( sToken != " " && sToken != "\t" )
|
|
sLastWhitespace = "";
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CfgExport::Output(const OString&)
|
|
{
|
|
}
|
|
|
|
int CfgParser::Execute( int nToken, char * pToken )
|
|
{
|
|
OString sToken( pToken );
|
|
|
|
switch ( nToken ) {
|
|
case CFG_TAG:
|
|
if ( sToken.indexOf( "package-id=" ) != -1 )
|
|
return ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE, pToken );
|
|
else if ( sToken.indexOf( "component-id=" ) != -1 )
|
|
return ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT, pToken );
|
|
else if ( sToken.indexOf( "template-id=" ) != -1 )
|
|
return ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE, pToken );
|
|
else if ( sToken.indexOf( "cfg:name=" ) != -1 )
|
|
return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
|
|
else if ( sToken.indexOf( "oor:name=" ) != -1 )
|
|
return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
|
|
else if ( sToken.indexOf( "oor:value=" ) != -1 )
|
|
return ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE, pToken );
|
|
break;
|
|
}
|
|
return ExecuteAnalyzedToken( nToken, pToken );
|
|
}
|
|
|
|
void CfgParser::Error(const OString& rError)
|
|
{
|
|
yyerror(rError.getStr());
|
|
}
|
|
|
|
//
|
|
// class CfgExport
|
|
//
|
|
|
|
CfgExport::CfgExport(
|
|
const OString &rOutputFile,
|
|
const OString &rFilePath )
|
|
|
|
: sPath( rFilePath )
|
|
{
|
|
pOutputStream.open( rOutputFile, PoOfstream::APP );
|
|
if (!pOutputStream.isOpen())
|
|
{
|
|
std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
CfgExport::~CfgExport()
|
|
{
|
|
pOutputStream.close();
|
|
}
|
|
|
|
|
|
void CfgExport::WorkOnResourceEnd()
|
|
{
|
|
if ( bLocalize ) {
|
|
if ( !pStackData->sText["en-US"].isEmpty() )
|
|
{
|
|
OString sXComment = pStackData->sText[OString(RTL_CONSTASCII_STRINGPARAM("x-comment"))];
|
|
OString sLocalId = pStackData->sIdentifier;
|
|
OString sGroupId;
|
|
if ( aStack.size() == 1 ) {
|
|
sGroupId = sLocalId;
|
|
sLocalId = "";
|
|
}
|
|
else {
|
|
sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
|
|
}
|
|
|
|
|
|
OString sText = pStackData->sText[ "en-US" ];
|
|
sText = helper::UnQuotHTML( sText );
|
|
|
|
common::writePoEntry(
|
|
"Cfgex", pOutputStream, sPath, pStackData->sResTyp,
|
|
sGroupId, sLocalId, sXComment, sText);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CfgExport::WorkOnText(
|
|
OString &rText,
|
|
const OString &rIsoLang
|
|
)
|
|
{
|
|
if( !rIsoLang.isEmpty() ) rText = helper::UnQuotHTML( rText );
|
|
}
|
|
|
|
|
|
//
|
|
// class CfgMerge
|
|
//
|
|
|
|
CfgMerge::CfgMerge(
|
|
const OString &rMergeSource, const OString &rOutputFile,
|
|
const OString &rFilename, const OString &rLanguage )
|
|
: pMergeDataFile( NULL ),
|
|
pResData( NULL ),
|
|
sFilename( rFilename ),
|
|
bEnglish( sal_False )
|
|
{
|
|
pOutputStream.open(
|
|
rOutputFile.getStr(), std::ios_base::out | std::ios_base::trunc);
|
|
if (!pOutputStream.is_open())
|
|
{
|
|
std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
|
|
std::exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (!rMergeSource.isEmpty())
|
|
{
|
|
pMergeDataFile = new MergeDataFile(
|
|
rMergeSource, global::inputPathname, true );
|
|
if (rLanguage.equalsIgnoreAsciiCase("ALL") )
|
|
{
|
|
aLanguages = pMergeDataFile->GetLanguages();
|
|
}
|
|
else aLanguages.push_back(rLanguage);
|
|
}
|
|
else
|
|
aLanguages.push_back(rLanguage);
|
|
}
|
|
|
|
CfgMerge::~CfgMerge()
|
|
{
|
|
pOutputStream.close();
|
|
delete pMergeDataFile;
|
|
delete pResData;
|
|
}
|
|
|
|
void CfgMerge::WorkOnText(OString &, const OString& rLangIndex)
|
|
{
|
|
|
|
if ( pMergeDataFile && bLocalize ) {
|
|
if ( !pResData ) {
|
|
OString sLocalId = pStackData->sIdentifier;
|
|
OString sGroupId;
|
|
if ( aStack.size() == 1 ) {
|
|
sGroupId = sLocalId;
|
|
sLocalId = OString();
|
|
}
|
|
else {
|
|
sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
|
|
}
|
|
|
|
pResData = new ResData( sGroupId, sFilename );
|
|
pResData->sId = sLocalId;
|
|
pResData->sResTyp = pStackData->sResTyp;
|
|
}
|
|
|
|
if (rLangIndex.equalsIgnoreAsciiCase("en-US"))
|
|
bEnglish = sal_True;
|
|
}
|
|
}
|
|
|
|
void CfgMerge::Output(const OString& rOutput)
|
|
{
|
|
pOutputStream << rOutput.getStr();
|
|
}
|
|
|
|
void CfgMerge::WorkOnResourceEnd()
|
|
{
|
|
|
|
if ( pMergeDataFile && pResData && bLocalize && bEnglish ) {
|
|
MergeEntrys *pEntrys = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData );
|
|
if ( pEntrys ) {
|
|
OString sCur;
|
|
|
|
for( unsigned int i = 0; i < aLanguages.size(); ++i ){
|
|
sCur = aLanguages[ i ];
|
|
|
|
OString sContent;
|
|
pEntrys->GetText( sContent, STRING_TYP_TEXT, sCur , sal_True );
|
|
if (
|
|
( !sCur.equalsIgnoreAsciiCase("en-US") ) && !sContent.isEmpty())
|
|
{
|
|
|
|
OString sText = helper::QuotHTML( sContent);
|
|
|
|
OString sAdditionalLine( "\t" );
|
|
|
|
OString sTextTag = pStackData->sTextTag;
|
|
OString sTemp = sTextTag.copy( sTextTag.indexOf( "xml:lang=" ));
|
|
|
|
sal_Int32 n = 0;
|
|
OString sSearch = sTemp.getToken(0, '"', n);
|
|
sSearch += "\"";
|
|
sSearch += sTemp.getToken(0, '"', n);
|
|
sSearch += "\"";
|
|
|
|
OString sReplace = sTemp.getToken(0, '"');
|
|
sReplace += "\"";
|
|
sReplace += sCur;
|
|
sReplace += "\"";
|
|
|
|
sTextTag = sTextTag.replaceFirst(sSearch, sReplace);
|
|
|
|
sAdditionalLine += sTextTag;
|
|
sAdditionalLine += sText;
|
|
sAdditionalLine += pStackData->sEndTextTag;
|
|
|
|
sAdditionalLine += "\n";
|
|
sAdditionalLine += sLastWhitespace;
|
|
|
|
Output( sAdditionalLine );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete pResData;
|
|
pResData = NULL;
|
|
bEnglish = sal_False;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|