Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "sal/config.h"
21 :
22 : #include "cfglex.hxx"
23 : #include "common.hxx"
24 :
25 : #include <cstdio>
26 : #include <cstdlib>
27 : #include <cstring>
28 :
29 : #include "boost/scoped_ptr.hpp"
30 : #include "rtl/strbuf.hxx"
31 :
32 : #include "helper.hxx"
33 : #include "export.hxx"
34 : #include "cfgmerge.hxx"
35 : #include "tokens.h"
36 :
37 : void yyerror(char const *);
38 :
39 : namespace {
40 :
41 : namespace global {
42 :
43 0 : OString inputPathname;
44 0 : boost::scoped_ptr< CfgParser > parser;
45 :
46 : }
47 : }
48 :
49 : extern "C" {
50 :
51 0 : FILE * init(int argc, char ** argv) {
52 :
53 0 : common::HandledArgs aArgs;
54 0 : if ( !common::handleArguments(argc, argv, aArgs) )
55 : {
56 0 : common::writeUsage("cfgex","*.xcu");
57 0 : std::exit(EXIT_FAILURE);
58 : }
59 0 : global::inputPathname = aArgs.m_sInputFile;
60 :
61 0 : FILE * pFile = std::fopen(global::inputPathname.getStr(), "r");
62 0 : if (pFile == 0) {
63 : std::fprintf(
64 : stderr, "Error: Cannot open file \"%s\"\n",
65 0 : global::inputPathname.getStr() );
66 0 : std::exit(EXIT_FAILURE);
67 : }
68 :
69 0 : if (aArgs.m_bMergeMode) {
70 : global::parser.reset(
71 : new CfgMerge(
72 : aArgs.m_sMergeSrc, aArgs.m_sOutputFile,
73 0 : global::inputPathname, aArgs.m_sLanguage ));
74 : } else {
75 : global::parser.reset(
76 : new CfgExport(
77 0 : aArgs.m_sOutputFile, global::inputPathname ));
78 : }
79 :
80 0 : return pFile;
81 : }
82 :
83 0 : void workOnTokenSet(int nTyp, char * pTokenText) {
84 0 : global::parser->Execute( nTyp, pTokenText );
85 0 : }
86 :
87 : }
88 :
89 :
90 : // class CfgStackData
91 :
92 :
93 0 : CfgStackData* CfgStack::Push(const OString &rTag, const OString &rId)
94 : {
95 0 : CfgStackData *pD = new CfgStackData( rTag, rId );
96 0 : maList.push_back( pD );
97 0 : return pD;
98 : }
99 :
100 :
101 : // class CfgStack
102 :
103 :
104 0 : CfgStack::~CfgStack()
105 : {
106 0 : for ( size_t i = 0, n = maList.size(); i < n; i++ )
107 0 : delete maList[ i ];
108 0 : maList.clear();
109 0 : }
110 :
111 0 : OString CfgStack::GetAccessPath( size_t nPos )
112 : {
113 0 : OStringBuffer sReturn;
114 0 : for (size_t i = 0; i <= nPos; ++i)
115 : {
116 0 : if (i)
117 0 : sReturn.append('.');
118 0 : sReturn.append(maList[i]->GetIdentifier());
119 : }
120 :
121 0 : return sReturn.makeStringAndClear();
122 : }
123 :
124 0 : CfgStackData *CfgStack::GetStackData()
125 : {
126 0 : if (!maList.empty())
127 0 : return maList[maList.size() - 1];
128 : else
129 0 : return 0;
130 : }
131 :
132 :
133 : // class CfgParser
134 :
135 :
136 0 : CfgParser::CfgParser()
137 : : pStackData( NULL ),
138 0 : bLocalize( false )
139 : {
140 0 : }
141 :
142 0 : CfgParser::~CfgParser()
143 : {
144 0 : }
145 :
146 0 : bool CfgParser::IsTokenClosed(const OString &rToken)
147 : {
148 0 : return rToken[rToken.getLength() - 2] == '/';
149 : }
150 :
151 0 : void CfgParser::AddText(
152 : OString &rText,
153 : const OString &rIsoLang,
154 : const OString &rResTyp )
155 : {
156 0 : rText = rText.replaceAll(OString('\n'), OString()).
157 : replaceAll(OString('\r'), OString()).
158 0 : replaceAll(OString('\t'), OString());
159 0 : pStackData->sResTyp = rResTyp;
160 0 : WorkOnText( rText, rIsoLang );
161 0 : pStackData->sText[ rIsoLang ] = rText;
162 0 : }
163 :
164 0 : int CfgParser::ExecuteAnalyzedToken( int nToken, char *pToken )
165 : {
166 0 : OString sToken( pToken );
167 :
168 0 : if ( sToken == " " || sToken == "\t" )
169 0 : sLastWhitespace += sToken;
170 :
171 0 : OString sTokenName;
172 0 : OString sTokenId;
173 :
174 0 : bool bOutput = true;
175 :
176 0 : switch ( nToken ) {
177 : case CFG_TOKEN_PACKAGE:
178 : case CFG_TOKEN_COMPONENT:
179 : case CFG_TOKEN_TEMPLATE:
180 : case CFG_TOKEN_CONFIGNAME:
181 : case CFG_TOKEN_OORNAME:
182 : case CFG_TOKEN_OORVALUE:
183 : case CFG_TAG:
184 : case ANYTOKEN:
185 : case CFG_TEXT_START:
186 : {
187 0 : sTokenName = sToken.getToken(1, '<').getToken(0, '>').
188 0 : getToken(0, ' ');
189 :
190 0 : if ( !IsTokenClosed( sToken )) {
191 0 : OString sSearch;
192 0 : switch ( nToken ) {
193 : case CFG_TOKEN_PACKAGE:
194 0 : sSearch = "package-id=";
195 0 : break;
196 : case CFG_TOKEN_COMPONENT:
197 0 : sSearch = "component-id=";
198 0 : break;
199 : case CFG_TOKEN_TEMPLATE:
200 0 : sSearch = "template-id=";
201 0 : break;
202 : case CFG_TOKEN_CONFIGNAME:
203 0 : sSearch = "cfg:name=";
204 0 : break;
205 : case CFG_TOKEN_OORNAME:
206 0 : sSearch = "oor:name=";
207 0 : bLocalize = true;
208 0 : break;
209 : case CFG_TOKEN_OORVALUE:
210 0 : sSearch = "oor:value=";
211 0 : break;
212 : case CFG_TEXT_START: {
213 0 : if ( sCurrentResTyp != sTokenName ) {
214 0 : WorkOnResourceEnd();
215 : }
216 0 : sCurrentResTyp = sTokenName;
217 :
218 0 : OString sTemp = sToken.copy( sToken.indexOf( "xml:lang=" ));
219 0 : sCurrentIsoLang = sTemp.getToken(1, '"');
220 :
221 0 : if ( sCurrentIsoLang == NO_TRANSLATE_ISO )
222 0 : bLocalize = false;
223 :
224 0 : pStackData->sTextTag = sToken;
225 :
226 0 : sCurrentText = "";
227 : }
228 0 : break;
229 : }
230 0 : if ( !sSearch.isEmpty())
231 : {
232 0 : OString sTemp = sToken.copy( sToken.indexOf( sSearch ));
233 0 : sTokenId = sTemp.getToken(1, '"');
234 : }
235 0 : pStackData = aStack.Push( sTokenName, sTokenId );
236 :
237 0 : if ( sSearch == "cfg:name=" ) {
238 0 : OString sTemp( sToken.toAsciiUpperCase() );
239 0 : bLocalize = (( sTemp.indexOf( "CFG:TYPE=\"STRING\"" ) != -1 ) &&
240 0 : ( sTemp.indexOf( "CFG:LOCALIZED=\"sal_True\"" ) != -1 ));
241 0 : }
242 : }
243 0 : else if ( sTokenName == "label" ) {
244 0 : if ( sCurrentResTyp != sTokenName ) {
245 0 : WorkOnResourceEnd();
246 : }
247 0 : sCurrentResTyp = sTokenName;
248 : }
249 : }
250 0 : break;
251 : case CFG_CLOSETAG:
252 : {
253 0 : sTokenName = sToken.getToken(1, '/').getToken(0, '>').
254 0 : getToken(0, ' ');
255 0 : if ( aStack.GetStackData() && ( aStack.GetStackData()->GetTagType() == sTokenName ))
256 : {
257 0 : if (sCurrentText.isEmpty())
258 0 : WorkOnResourceEnd();
259 0 : aStack.Pop();
260 0 : pStackData = aStack.GetStackData();
261 : }
262 : else
263 : {
264 0 : OString sError( "Misplaced close tag: " );
265 0 : OString sInFile(" in file ");
266 0 : sError += sToken;
267 0 : sError += sInFile;
268 0 : sError += global::inputPathname;
269 0 : Error( sError );
270 0 : std::exit(EXIT_FAILURE);
271 : }
272 : }
273 0 : break;
274 :
275 : case CFG_TEXTCHAR:
276 0 : sCurrentText += sToken;
277 0 : bOutput = false;
278 0 : break;
279 :
280 : case CFG_TOKEN_NO_TRANSLATE:
281 0 : bLocalize = false;
282 0 : break;
283 : }
284 :
285 0 : if ( !sCurrentText.isEmpty() && nToken != CFG_TEXTCHAR )
286 : {
287 0 : AddText( sCurrentText, sCurrentIsoLang, sCurrentResTyp );
288 0 : Output( sCurrentText );
289 0 : sCurrentText.clear();
290 0 : pStackData->sEndTextTag = sToken;
291 : }
292 :
293 0 : if ( bOutput )
294 0 : Output( sToken );
295 :
296 0 : if ( sToken != " " && sToken != "\t" )
297 0 : sLastWhitespace = "";
298 :
299 0 : return 1;
300 : }
301 :
302 0 : void CfgExport::Output(const OString&)
303 : {
304 0 : }
305 :
306 0 : int CfgParser::Execute( int nToken, char * pToken )
307 : {
308 0 : OString sToken( pToken );
309 :
310 0 : switch ( nToken ) {
311 : case CFG_TAG:
312 0 : if ( sToken.indexOf( "package-id=" ) != -1 )
313 0 : return ExecuteAnalyzedToken( CFG_TOKEN_PACKAGE, pToken );
314 0 : else if ( sToken.indexOf( "component-id=" ) != -1 )
315 0 : return ExecuteAnalyzedToken( CFG_TOKEN_COMPONENT, pToken );
316 0 : else if ( sToken.indexOf( "template-id=" ) != -1 )
317 0 : return ExecuteAnalyzedToken( CFG_TOKEN_TEMPLATE, pToken );
318 0 : else if ( sToken.indexOf( "cfg:name=" ) != -1 )
319 0 : return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
320 0 : else if ( sToken.indexOf( "oor:name=" ) != -1 )
321 0 : return ExecuteAnalyzedToken( CFG_TOKEN_OORNAME, pToken );
322 0 : else if ( sToken.indexOf( "oor:value=" ) != -1 )
323 0 : return ExecuteAnalyzedToken( CFG_TOKEN_OORVALUE, pToken );
324 0 : break;
325 : }
326 0 : return ExecuteAnalyzedToken( nToken, pToken );
327 : }
328 :
329 0 : void CfgParser::Error(const OString& rError)
330 : {
331 0 : yyerror(rError.getStr());
332 0 : }
333 :
334 :
335 : // class CfgExport
336 :
337 :
338 0 : CfgExport::CfgExport(
339 : const OString &rOutputFile,
340 : const OString &rFilePath )
341 :
342 0 : : sPath( rFilePath )
343 : {
344 0 : pOutputStream.open( rOutputFile, PoOfstream::APP );
345 0 : if (!pOutputStream.isOpen())
346 : {
347 0 : std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
348 0 : std::exit(EXIT_FAILURE);
349 : }
350 0 : }
351 :
352 0 : CfgExport::~CfgExport()
353 : {
354 0 : pOutputStream.close();
355 0 : }
356 :
357 :
358 0 : void CfgExport::WorkOnResourceEnd()
359 : {
360 0 : if ( bLocalize ) {
361 0 : if ( !pStackData->sText["en-US"].isEmpty() )
362 : {
363 0 : OString sXComment = pStackData->sText[OString("x-comment")];
364 0 : OString sLocalId = pStackData->sIdentifier;
365 0 : OString sGroupId;
366 0 : if ( aStack.size() == 1 ) {
367 0 : sGroupId = sLocalId;
368 0 : sLocalId = "";
369 : }
370 : else {
371 0 : sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
372 : }
373 :
374 :
375 0 : OString sText = pStackData->sText[ "en-US" ];
376 0 : sText = helper::UnQuotHTML( sText );
377 :
378 : common::writePoEntry(
379 : "Cfgex", pOutputStream, sPath, pStackData->sResTyp,
380 0 : sGroupId, sLocalId, sXComment, sText);
381 : }
382 : }
383 0 : }
384 :
385 0 : void CfgExport::WorkOnText(
386 : OString &rText,
387 : const OString &rIsoLang
388 : )
389 : {
390 0 : if( !rIsoLang.isEmpty() ) rText = helper::UnQuotHTML( rText );
391 0 : }
392 :
393 :
394 :
395 : // class CfgMerge
396 :
397 :
398 0 : CfgMerge::CfgMerge(
399 : const OString &rMergeSource, const OString &rOutputFile,
400 : const OString &rFilename, const OString &rLanguage )
401 : : pMergeDataFile( NULL ),
402 : pResData( NULL ),
403 : sFilename( rFilename ),
404 0 : bEnglish( false )
405 : {
406 : pOutputStream.open(
407 0 : rOutputFile.getStr(), std::ios_base::out | std::ios_base::trunc);
408 0 : if (!pOutputStream.is_open())
409 : {
410 0 : std::cerr << "ERROR: Unable to open output file: " << rOutputFile << "\n";
411 0 : std::exit(EXIT_FAILURE);
412 : }
413 :
414 0 : if (!rMergeSource.isEmpty())
415 : {
416 : pMergeDataFile = new MergeDataFile(
417 0 : rMergeSource, global::inputPathname, true );
418 0 : if (rLanguage.equalsIgnoreAsciiCase("ALL") )
419 : {
420 0 : aLanguages = pMergeDataFile->GetLanguages();
421 : }
422 0 : else aLanguages.push_back(rLanguage);
423 : }
424 : else
425 0 : aLanguages.push_back(rLanguage);
426 0 : }
427 :
428 0 : CfgMerge::~CfgMerge()
429 : {
430 0 : pOutputStream.close();
431 0 : delete pMergeDataFile;
432 0 : delete pResData;
433 0 : }
434 :
435 0 : void CfgMerge::WorkOnText(OString &, const OString& rLangIndex)
436 : {
437 :
438 0 : if ( pMergeDataFile && bLocalize ) {
439 0 : if ( !pResData ) {
440 0 : OString sLocalId = pStackData->sIdentifier;
441 0 : OString sGroupId;
442 0 : if ( aStack.size() == 1 ) {
443 0 : sGroupId = sLocalId;
444 0 : sLocalId.clear();
445 : }
446 : else {
447 0 : sGroupId = aStack.GetAccessPath( aStack.size() - 2 );
448 : }
449 :
450 0 : pResData = new ResData( sGroupId, sFilename );
451 0 : pResData->sId = sLocalId;
452 0 : pResData->sResTyp = pStackData->sResTyp;
453 : }
454 :
455 0 : if (rLangIndex.equalsIgnoreAsciiCase("en-US"))
456 0 : bEnglish = true;
457 : }
458 0 : }
459 :
460 0 : void CfgMerge::Output(const OString& rOutput)
461 : {
462 0 : pOutputStream << rOutput.getStr();
463 0 : }
464 :
465 0 : void CfgMerge::WorkOnResourceEnd()
466 : {
467 :
468 0 : if ( pMergeDataFile && pResData && bLocalize && bEnglish ) {
469 0 : MergeEntrys *pEntrys = pMergeDataFile->GetMergeEntrysCaseSensitive( pResData );
470 0 : if ( pEntrys ) {
471 0 : OString sCur;
472 :
473 0 : for( size_t i = 0; i < aLanguages.size(); ++i ){
474 0 : sCur = aLanguages[ i ];
475 :
476 0 : OString sContent;
477 0 : pEntrys->GetText( sContent, STRING_TYP_TEXT, sCur , true );
478 0 : if (
479 0 : ( !sCur.equalsIgnoreAsciiCase("en-US") ) && !sContent.isEmpty())
480 : {
481 :
482 0 : OString sText = helper::QuotHTML( sContent);
483 :
484 0 : OString sAdditionalLine( "\t" );
485 :
486 0 : OString sTextTag = pStackData->sTextTag;
487 0 : OString sTemp = sTextTag.copy( sTextTag.indexOf( "xml:lang=" ));
488 :
489 0 : sal_Int32 n = 0;
490 0 : OString sSearch = sTemp.getToken(0, '"', n);
491 0 : sSearch += "\"";
492 0 : sSearch += sTemp.getToken(0, '"', n);
493 0 : sSearch += "\"";
494 :
495 0 : OString sReplace = sTemp.getToken(0, '"');
496 0 : sReplace += "\"";
497 0 : sReplace += sCur;
498 0 : sReplace += "\"";
499 :
500 0 : sTextTag = sTextTag.replaceFirst(sSearch, sReplace);
501 :
502 0 : sAdditionalLine += sTextTag;
503 0 : sAdditionalLine += sText;
504 0 : sAdditionalLine += pStackData->sEndTextTag;
505 :
506 0 : sAdditionalLine += "\n";
507 0 : sAdditionalLine += sLastWhitespace;
508 :
509 0 : Output( sAdditionalLine );
510 : }
511 0 : }
512 : }
513 : }
514 0 : delete pResData;
515 0 : pResData = NULL;
516 0 : bEnglish = false;
517 0 : }
518 :
519 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|