cfgmerge.cxx 13.4 KB
Newer Older
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
Michael Meeks's avatar
Michael Meeks committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * 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 .
 */
19

20
#include <sal/config.h>
21

22 23
#include <cfglex.hxx>
#include <common.hxx>
24

25 26 27 28
#include <cstdio>
#include <cstdlib>
#include <cstring>

Caolán McNamara's avatar
Caolán McNamara committed
29
#include <memory>
30
#include <rtl/strbuf.hxx>
31

32 33 34 35
#include <helper.hxx>
#include <export.hxx>
#include <cfgmerge.hxx>
#include <tokens.h>
36

37 38 39 40
namespace {

namespace global {

41
OString inputPathname;
Caolán McNamara's avatar
Caolán McNamara committed
42
std::unique_ptr< CfgParser > parser;
43 44

}
45 46
}

47
extern "C" {
48

49
FILE * init(int argc, char ** argv) {
50

Zolnai Tamás's avatar
Zolnai Tamás committed
51 52
    common::HandledArgs aArgs;
    if ( !common::handleArguments(argc, argv, aArgs) )
53
    {
Zolnai Tamás's avatar
Zolnai Tamás committed
54
        common::writeUsage("cfgex","*.xcu");
55 56
        std::exit(EXIT_FAILURE);
    }
57
    global::inputPathname = aArgs.m_sInputFile;
58

59
    FILE * pFile = std::fopen(global::inputPathname.getStr(), "r");
60
    if (pFile == nullptr) {
61 62
        std::fprintf(
            stderr, "Error: Cannot open file \"%s\"\n",
63
            global::inputPathname.getStr() );
64
        std::exit(EXIT_FAILURE);
65
    }
66

67
    if (aArgs.m_bMergeMode) {
68 69
        global::parser.reset(
            new CfgMerge(
70 71
                aArgs.m_sMergeSrc, aArgs.m_sOutputFile,
                global::inputPathname, aArgs.m_sLanguage ));
72 73
    } else {
        global::parser.reset(
74
            new CfgExport(
75
                aArgs.m_sOutputFile, global::inputPathname ));
76 77
    }

78
    return pFile;
79 80
}

81 82
void workOnTokenSet(int nTyp, char * pTokenText) {
    global::parser->Execute( nTyp, pTokenText );
83 84 85 86
}

}

87

88
// class CfgStackData
89

90

91
CfgStackData* CfgStack::Push(const OString &rTag, const OString &rId)
92 93
{
    CfgStackData *pD = new CfgStackData( rTag, rId );
94
    maList.push_back( pD );
95 96 97
    return pD;
}

98

99
// class CfgStack
100

101 102 103 104 105

CfgStack::~CfgStack()
{
}

106
OString CfgStack::GetAccessPath( size_t nPos )
107
{
108
    OStringBuffer sReturn;
109 110 111 112
    for (size_t i = 0; i <= nPos; ++i)
    {
        if (i)
            sReturn.append('.');
113
        sReturn.append(maList[i]->GetIdentifier());
114 115
    }

116
    return sReturn.makeStringAndClear();
117
}
118

119
CfgStackData *CfgStack::GetStackData()
120
{
121 122 123
    if (!maList.empty())
        return maList[maList.size() - 1];
    else
124
        return nullptr;
125 126
}

127

128
// class CfgParser
129

130 131

CfgParser::CfgParser()
132
                : pStackData( nullptr ),
133
                bLocalize( false )
134 135 136 137 138 139 140
{
}

CfgParser::~CfgParser()
{
}

141
bool CfgParser::IsTokenClosed(const OString &rToken)
142
{
143
    return rToken[rToken.getLength() - 2] == '/';
144 145
}

146
void CfgParser::AddText(
147 148
    OString &rText,
    const OString &rIsoLang,
Zolnai Tamás's avatar
Zolnai Tamás committed
149
    const OString &rResTyp )
150
{
151 152 153
    rText = rText.replaceAll(OString('\n'), OString()).
        replaceAll(OString('\r'), OString()).
        replaceAll(OString('\t'), OString());
154 155 156
    pStackData->sResTyp = rResTyp;
    WorkOnText( rText, rIsoLang );
    pStackData->sText[ rIsoLang ] = rText;
157 158
}

159 160 161
#if defined _MSC_VER
#pragma warning(disable: 4702) // unreachable code, bug in MSVC2015, it thinks the std::exit is unreachable
#endif
162
void CfgParser::ExecuteAnalyzedToken( int nToken, char *pToken )
163
{
164
    OString sToken( pToken );
165

166 167 168
    if ( sToken == " " || sToken == "\t" )
        sLastWhitespace += sToken;

169 170
    OString sTokenName;
    OString sTokenId;
171

172
    bool bOutput = true;
173

174 175 176 177 178
    switch ( nToken ) {
        case CFG_TOKEN_PACKAGE:
        case CFG_TOKEN_COMPONENT:
        case CFG_TOKEN_TEMPLATE:
        case CFG_TOKEN_CONFIGNAME:
179
        case CFG_TOKEN_OORNAME:
180
        case CFG_TOKEN_OORVALUE:
181 182 183 184
        case CFG_TAG:
        case ANYTOKEN:
        case CFG_TEXT_START:
        {
185 186
            sTokenName = sToken.getToken(1, '<').getToken(0, '>').
                getToken(0, ' ');
187

188
            if ( !IsTokenClosed( sToken )) {
189
                OString sSearch;
190 191 192 193 194 195 196 197 198 199 200 201 202
                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;
203 204
                    case CFG_TOKEN_OORNAME:
                        sSearch = "oor:name=";
205
                        bLocalize = true;
206
                    break;
207 208 209
                    case CFG_TOKEN_OORVALUE:
                        sSearch = "oor:value=";
                    break;
210
                    case CFG_TEXT_START: {
211
                        if ( sCurrentResTyp != sTokenName ) {
212
                            WorkOnResourceEnd();
213
                         }
214 215
                        sCurrentResTyp = sTokenName;

216
                        OString sTemp = sToken.copy( sToken.indexOf( "xml:lang=" ));
217
                        sCurrentIsoLang = sTemp.getToken(1, '"');
218

219
                        if ( sCurrentIsoLang == NO_TRANSLATE_ISO )
220
                            bLocalize = false;
221

222 223 224 225 226 227
                        pStackData->sTextTag = sToken;

                        sCurrentText = "";
                    }
                    break;
                }
228
                if ( !sSearch.isEmpty())
229
                {
230
                    OString sTemp = sToken.copy( sToken.indexOf( sSearch ));
231
                    sTokenId = sTemp.getToken(1, '"');
232 233
                }
                pStackData = aStack.Push( sTokenName, sTokenId );
234

Nils Fuhrmann's avatar
Nils Fuhrmann committed
235
                if ( sSearch == "cfg:name=" ) {
236
                    OString sTemp( sToken.toAsciiUpperCase() );
237 238
                    bLocalize = sTemp.indexOf("CFG:TYPE=\"STRING\"")>=0
                        && sTemp.indexOf( "CFG:LOCALIZED=\"TRUE\"" )>=0;
Nils Fuhrmann's avatar
Nils Fuhrmann committed
239
                }
240
            }
241 242
            else if ( sTokenName == "label" ) {
                if ( sCurrentResTyp != sTokenName ) {
243
                    WorkOnResourceEnd();
244 245 246
                }
                sCurrentResTyp = sTokenName;
            }
247 248 249
        }
        break;
        case CFG_CLOSETAG:
250
        {
251 252
            sTokenName = sToken.getToken(1, '/').getToken(0, '>').
                getToken(0, ' ');
253 254
            if ( aStack.GetStackData() && ( aStack.GetStackData()->GetTagType() == sTokenName ))
            {
255
                if (sCurrentText.isEmpty())
256
                    WorkOnResourceEnd();
257 258 259
                aStack.Pop();
                pStackData = aStack.GetStackData();
            }
260 261
            else
            {
262
                const OString sError{ "Misplaced close tag: " + sToken + " in file " + global::inputPathname };
263
                yyerror(sError.getStr());
264
                std::exit(EXIT_FAILURE);
265
            }
266
        }
267 268 269 270
        break;

        case CFG_TEXTCHAR:
            sCurrentText += sToken;
271
            bOutput = false;
272
        break;
273 274

        case CFG_TOKEN_NO_TRANSLATE:
275
            bLocalize = false;
276
        break;
277 278
    }

279
    if ( !sCurrentText.isEmpty() && nToken != CFG_TEXTCHAR )
280
    {
281 282
        AddText( sCurrentText, sCurrentIsoLang, sCurrentResTyp );
        Output( sCurrentText );
283
        sCurrentText.clear();
284 285 286
        pStackData->sEndTextTag = sToken;
    }

287 288 289
    if ( bOutput )
        Output( sToken );

290 291
    if ( sToken != " " && sToken != "\t" )
        sLastWhitespace = "";
292 293
}

294
void CfgExport::Output(const OString&)
295 296 297
{
}

298
void CfgParser::Execute( int nToken, char * pToken )
299
{
300
    OString sToken( pToken );
301 302 303

    switch ( nToken ) {
        case CFG_TAG:
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
            if ( sToken.indexOf( "package-id=" ) != -1 ) {
                ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE, pToken );
                return;
            } else if ( sToken.indexOf( "component-id=" ) != -1 ) {
                ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT, pToken );
                return;
            } else if ( sToken.indexOf( "template-id=" ) != -1 ) {
                ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE, pToken );
                return;
            } else if ( sToken.indexOf( "cfg:name=" ) != -1 ) {
                ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
                return;
            } else if ( sToken.indexOf( "oor:name=" ) != -1 ) {
                ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
                return;
            } else if ( sToken.indexOf( "oor:value=" ) != -1 ) {
                ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE, pToken );
                return;
            }
323 324
        break;
    }
325
    ExecuteAnalyzedToken( nToken, pToken );
326 327
}

328

329
// class CfgExport
330

331 332

CfgExport::CfgExport(
333
        const OString &rOutputFile,
Zolnai Tamás's avatar
Zolnai Tamás committed
334 335
        const OString &rFilePath )

336
                : sPath( rFilePath )
337
{
338 339 340 341 342 343
    pOutputStream.open( rOutputFile, PoOfstream::APP );
    if (!pOutputStream.isOpen())
    {
        std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
        std::exit(EXIT_FAILURE);
    }
344 345 346 347
}

CfgExport::~CfgExport()
{
348
    pOutputStream.close();
349 350
}

Zolnai Tamás's avatar
Zolnai Tamás committed
351

352
void CfgExport::WorkOnResourceEnd()
353
{
354
    if ( bLocalize ) {
355
    if ( !pStackData->sText["en-US"].isEmpty() )
356
        {
357
            OString sXComment = pStackData->sText[OString("x-comment")];
358 359
            OString sLocalId = pStackData->sIdentifier;
            OString sGroupId;
360
            if ( aStack.size() == 1 ) {
361 362 363 364
                sGroupId = sLocalId;
                sLocalId = "";
            }
            else {
365
                sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
366 367 368
            }


369 370
            OString sText = pStackData->sText[ "en-US" ];
            sText = helper::UnQuotHTML( sText );
371

372 373 374
            common::writePoEntry(
                "Cfgex", pOutputStream, sPath, pStackData->sResTyp,
                sGroupId, sLocalId, sXComment, sText);
375 376
        }
    }
377
}
378 379

void CfgExport::WorkOnText(
380 381
    OString &rText,
    const OString &rIsoLang
382 383
)
{
384
    if( !rIsoLang.isEmpty() ) rText = helper::UnQuotHTML( rText );
385 386 387 388
}


// class CfgMerge
389

390 391

CfgMerge::CfgMerge(
392 393
    const OString &rMergeSource, const OString &rOutputFile,
    const OString &rFilename, const OString &rLanguage )
394
                : sFilename( rFilename ),
395
                bEnglish( false )
396
{
397 398 399 400 401 402 403 404
    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);
    }

405
    if (!rMergeSource.isEmpty())
406
    {
407 408
        pMergeDataFile.reset(new MergeDataFile(
            rMergeSource, global::inputPathname, true ));
409
        if (rLanguage.equalsIgnoreAsciiCase("ALL") )
410
        {
411 412
            aLanguages = pMergeDataFile->GetLanguages();
        }
413
        else aLanguages.push_back(rLanguage);
414 415
    }
    else
416
        aLanguages.push_back(rLanguage);
417 418 419 420
}

CfgMerge::~CfgMerge()
{
421
    pOutputStream.close();
422 423
}

424
void CfgMerge::WorkOnText(OString &, const OString& rLangIndex)
425
{
426

Nils Fuhrmann's avatar
Nils Fuhrmann committed
427
    if ( pMergeDataFile && bLocalize ) {
428
        if ( !pResData ) {
429 430
            OString sLocalId = pStackData->sIdentifier;
            OString sGroupId;
431
            if ( aStack.size() == 1 ) {
432
                sGroupId = sLocalId;
433
                sLocalId.clear();
434 435
            }
            else {
436
                sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
437 438
            }

Noel Grandin's avatar
Noel Grandin committed
439
            pResData.reset( new ResData( sGroupId, sFilename ) );
440 441 442 443
            pResData->sId = sLocalId;
            pResData->sResTyp = pStackData->sResTyp;
        }

444
        if (rLangIndex.equalsIgnoreAsciiCase("en-US"))
445
            bEnglish = true;
446 447 448
    }
}

449
void CfgMerge::Output(const OString& rOutput)
450
{
451
    pOutputStream << rOutput;
452 453
}

454
void CfgMerge::WorkOnResourceEnd()
455
{
456

457
    if ( pMergeDataFile && pResData && bLocalize && bEnglish ) {
Noel Grandin's avatar
Noel Grandin committed
458
        MergeEntrys *pEntrys = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData.get() );
459
        if ( pEntrys ) {
460
            OString sCur;
461

462
            for( size_t i = 0; i < aLanguages.size(); ++i ){
463
                sCur = aLanguages[ i ];
464

465
                OString sContent;
Noel Grandin's avatar
Noel Grandin committed
466
                pEntrys->GetText( sContent, sCur, true );
467
                if (
468
                    ( !sCur.equalsIgnoreAsciiCase("en-US") ) && !sContent.isEmpty())
469
                {
470
                    OString sTextTag = pStackData->sTextTag;
471 472 473 474 475 476 477 478 479
                    const sal_Int32 nLangAttributeStart{ sTextTag.indexOf( "xml:lang=" ) };
                    const sal_Int32 nLangStart{ sTextTag.indexOf( '"', nLangAttributeStart )+1 };
                    const sal_Int32 nLangEnd{ sTextTag.indexOf( '"', nLangStart ) };
                    OString sAdditionalLine{ "\t"
                        + sTextTag.replaceAt(nLangStart, nLangEnd-nLangStart, sCur)
                        + helper::QuotHTML(sContent)
                        + pStackData->sEndTextTag
                        + "\n"
                        + sLastWhitespace };
480 481 482 483 484
                    Output( sAdditionalLine );
                }
            }
        }
    }
Noel Grandin's avatar
Noel Grandin committed
485
    pResData.reset();
486
    bEnglish = false;
487
}
488 489

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */