Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include "sal/config.h"
30 : :
31 : : #include <algorithm>
32 : : #include <cassert>
33 : : #include <cstddef>
34 : : #include <fstream>
35 : : #include <string>
36 : :
37 : : #include <stdio.h>
38 : :
39 : : #include <rtl/strbuf.hxx>
40 : : #include "sal/main.h"
41 : : #include "helper.hxx"
42 : : #include "tagtest.hxx"
43 : : #include "gsicheck.hxx"
44 : :
45 : : namespace {
46 : :
47 : : sal_Int32 const MAX_GID_LID_LEN = 250;
48 : :
49 : 0 : rtl::OString copyUpTo(
50 : : rtl::OString const & text, sal_Int32 start, sal_Int32 maximumLength)
51 : : {
52 : : assert(start >= 0 && start <= text.getLength());
53 : 0 : return text.copy(start, std::min(text.getLength() - start, maximumLength));
54 : : }
55 : :
56 : 0 : rtl::OString addSuffix(
57 : : rtl::OString const & pathname, rtl::OString const & suffix)
58 : : {
59 : 0 : sal_Int32 n = pathname.lastIndexOf('.');
60 : 0 : if (n == -1) {
61 : : fprintf(
62 : : stderr,
63 : : ("Error: pathname \"%s\" does not contain dot to add suffix in"
64 : : " front of\n"),
65 : 0 : pathname.getStr());
66 : 0 : exit(EXIT_FAILURE);
67 : : }
68 : 0 : return pathname.replaceAt(n, 0, suffix);
69 : : }
70 : :
71 : : }
72 : :
73 : : /*****************************************************************************/
74 : 0 : void PrintMessage( rtl::OString const & aType, rtl::OString const & aMsg, rtl::OString const & aPrefix,
75 : : rtl::OString const & aContext, sal_Bool bPrintContext, std::size_t nLine, rtl::OString aUniqueId = rtl::OString() )
76 : : /*****************************************************************************/
77 : : {
78 : 0 : fprintf( stdout, "%s %s, Line %u", aType.getStr(), aPrefix.getStr(), static_cast<unsigned>( nLine ) );
79 : 0 : if ( !aUniqueId.isEmpty() )
80 : 0 : fprintf( stdout, ", UniqueID %s", aUniqueId.getStr() );
81 : 0 : fprintf( stdout, ": %s", aMsg.getStr() );
82 : :
83 : 0 : if ( bPrintContext )
84 : 0 : fprintf( stdout, " \"%s\"", aContext.getStr() );
85 : 0 : fprintf( stdout, "\n" );
86 : 0 : }
87 : :
88 : : /*****************************************************************************/
89 : 0 : void PrintError( rtl::OString const & aMsg, rtl::OString const & aPrefix,
90 : : rtl::OString const & aContext, sal_Bool bPrintContext, std::size_t nLine, rtl::OString const & aUniqueId = rtl::OString() )
91 : : /*****************************************************************************/
92 : : {
93 : 0 : PrintMessage( "Error:", aMsg, aPrefix, aContext, bPrintContext, nLine, aUniqueId );
94 : 0 : }
95 : :
96 : 0 : bool LanguageOK( rtl::OString const & aLang )
97 : : {
98 : 0 : sal_Int32 n = 0;
99 : 0 : rtl::OString t0(aLang.getToken(0, '-', n));
100 : 0 : if (n == -1) {
101 : 0 : return !t0.isEmpty()
102 : 0 : && (helper::isAllAsciiDigits(t0)
103 : 0 : || helper::isAllAsciiLowerCase(t0));
104 : : }
105 : 0 : rtl::OString t1(aLang.getToken(0, '-', n));
106 : : return n == -1
107 : 0 : && !t0.isEmpty() && helper::isAllAsciiLowerCase(t0)
108 : 0 : && !t1.isEmpty() && helper::isAllAsciiUpperCase(t1)
109 : 0 : && !t0.equalsIgnoreAsciiCase(t1);
110 : : }
111 : :
112 : 0 : class LazyStream: public std::ofstream
113 : : {
114 : :
115 : : private:
116 : : rtl::OString aFileName;
117 : : bool bOpened;
118 : :
119 : : public:
120 : 0 : LazyStream()
121 : : : aFileName()
122 : 0 : , bOpened(false)
123 : 0 : {};
124 : :
125 : 0 : void SetFileName( const rtl::OString& rFileName )
126 : : {
127 : 0 : aFileName = rFileName;
128 : 0 : };
129 : :
130 : : void LazyOpen();
131 : : };
132 : :
133 : 0 : void LazyStream::LazyOpen()
134 : : {
135 : 0 : if ( !bOpened )
136 : : {
137 : 0 : open(aFileName.getStr(), std::ios_base::out | std::ios_base::trunc);
138 : 0 : if (!is_open())
139 : : {
140 : : fprintf( stderr, "\nERROR: Could not open Output-File %s!\n\n",
141 : 0 : aFileName.getStr() );
142 : 0 : exit ( 4 );
143 : : }
144 : 0 : bOpened = true;
145 : : }
146 : 0 : }
147 : :
148 : :
149 : : //
150 : : // class GSILine
151 : : //
152 : :
153 : : /*****************************************************************************/
154 : 0 : GSILine::GSILine( const rtl::OString &rLine, std::size_t nLine )
155 : : /*****************************************************************************/
156 : : : nLineNumber( nLine )
157 : : , bOK( sal_True )
158 : : , bFixed ( sal_False )
159 : 0 : , data_( rLine )
160 : : {
161 : 0 : if (rLine.isEmpty()) {
162 : 0 : NotOK();
163 : : return;
164 : : }
165 : :
166 : 0 : aFormat = FORMAT_SDF;
167 : 0 : sal_Int32 n = 0;
168 : 0 : aUniqId = rLine.getToken(0, '\t', n); // token 0
169 : 0 : aUniqId += "/";
170 : 0 : aUniqId += rLine.getToken(0, '\t', n); // token 1
171 : 0 : aUniqId += "/";
172 : 0 : aUniqId += rLine.getToken(1, '\t', n); // token 3
173 : 0 : aUniqId += "/";
174 : 0 : rtl::OString gid(rLine.getToken(0, '\t', n)); // token 4
175 : 0 : aUniqId += gid;
176 : 0 : aUniqId += "/";
177 : 0 : rtl::OString lid(rLine.getToken(0, '\t', n)); // token 5
178 : 0 : aUniqId += lid;
179 : 0 : aUniqId += "/";
180 : 0 : aUniqId += rLine.getToken(0, '\t', n); // token 6
181 : 0 : aUniqId += "/";
182 : 0 : aUniqId += rLine.getToken(0, '\t', n); // token 7
183 : 0 : rtl::OString length(rLine.getToken(0, '\t', n)); // token 8
184 : 0 : aLineType = rtl::OString();
185 : 0 : aLangId = rLine.getToken(0, '\t', n); // token 9
186 : 0 : aText = rLine.getToken(0, '\t', n); // token 10
187 : 0 : aQuickHelpText = rLine.getToken(1, '\t', n); // token 12
188 : 0 : aTitle = rLine.getToken(0, '\t', n); // token 13
189 : 0 : if (n == -1) {
190 : 0 : NotOK();
191 : : return;
192 : : }
193 : 0 : rLine.getToken(0, '\t', n); // token 14
194 : 0 : if (n != -1) {
195 : 0 : NotOK();
196 : : return;
197 : : }
198 : :
199 : : // do some more format checks here
200 : 0 : if (!helper::isAllAsciiDigits(length)) {
201 : : PrintError(
202 : : "The length field does not contain a number!", "Line format",
203 : 0 : length, true, GetLineNumber(), GetUniqId());
204 : 0 : NotOK();
205 : : }
206 : 0 : if (!LanguageOK(aLangId)) {
207 : : PrintError(
208 : : "The Language is invalid!", "Line format", aLangId, true,
209 : 0 : GetLineNumber(), GetUniqId());
210 : 0 : NotOK();
211 : : }
212 : : // Limit GID and LID to MAX_GID_LID_LEN chars each for database conformity,
213 : : // see #137575#:
214 : 0 : if (gid.getLength() > MAX_GID_LID_LEN || lid.getLength() > MAX_GID_LID_LEN)
215 : : {
216 : : PrintError(
217 : : (rtl::OString(
218 : : RTL_CONSTASCII_STRINGPARAM("GID and LID may only be "))
219 : : + rtl::OString::valueOf(MAX_GID_LID_LEN)
220 : : + rtl::OString(RTL_CONSTASCII_STRINGPARAM(" chars long each"))),
221 : 0 : "Line format", aLangId, true, GetLineNumber(), GetUniqId());
222 : 0 : NotOK();
223 : 0 : }
224 : : }
225 : :
226 : : /*****************************************************************************/
227 : 0 : void GSILine::NotOK()
228 : : /*****************************************************************************/
229 : : {
230 : 0 : bOK = sal_False;
231 : 0 : }
232 : :
233 : : /*****************************************************************************/
234 : 0 : void GSILine::ReassembleLine()
235 : : /*****************************************************************************/
236 : : {
237 : 0 : if (GetLineFormat() != FORMAT_SDF) {
238 : : PrintError(
239 : : "Cannot reassemble line of unknown type (internal Error).",
240 : : "Line format", rtl::OString(), false, GetLineNumber(),
241 : 0 : GetUniqId());
242 : 0 : return;
243 : : }
244 : 0 : rtl::OStringBuffer b;
245 : 0 : sal_Int32 n = 0;
246 : 0 : for (sal_Int32 i = 0; i != 10; ++i) {
247 : 0 : b.append(data_.getToken(0, '\t', n)); // token 0--9
248 : 0 : b.append('\t');
249 : : }
250 : 0 : b.append(aText);
251 : 0 : b.append('\t');
252 : 0 : b.append(data_.getToken(1, '\t', n));
253 : : // token 11; should be empty but there are some places in sc not
254 : : // reflected to sources
255 : 0 : b.append('\t');
256 : 0 : b.append(aQuickHelpText);
257 : 0 : b.append('\t');
258 : 0 : b.append(aTitle);
259 : 0 : b.append('\t');
260 : 0 : b.append(data_.getToken(2, '\t', n)); // token 14
261 : 0 : data_ = b.makeStringAndClear();
262 : : }
263 : :
264 : : //
265 : : // class GSIBlock
266 : : //
267 : : /*****************************************************************************/
268 : 0 : GSIBlock::GSIBlock( sal_Bool PbPrintContext, sal_Bool bSource, sal_Bool bTrans, sal_Bool bRef, sal_Bool bAllowSusp )
269 : : /*****************************************************************************/
270 : : : pSourceLine( NULL )
271 : : , pReferenceLine( NULL )
272 : : , bPrintContext( PbPrintContext )
273 : : , bCheckSourceLang( bSource )
274 : : , bCheckTranslationLang( bTrans )
275 : : , bReference( bRef )
276 : : , bAllowSuspicious( bAllowSusp )
277 : 0 : , bHasBlockError( sal_False )
278 : : {
279 : 0 : }
280 : :
281 : : /*****************************************************************************/
282 : 0 : GSIBlock::~GSIBlock()
283 : : /*****************************************************************************/
284 : : {
285 : 0 : delete pSourceLine;
286 : 0 : delete pReferenceLine;
287 : :
288 : 0 : for ( size_t i = 0, n = maList.size(); i < n; ++i )
289 : 0 : delete maList[ i ];
290 : 0 : maList.clear();
291 : 0 : }
292 : :
293 : 0 : void GSIBlock::InsertLine( GSILine* pLine, const rtl::OString &rSourceLang)
294 : : {
295 : 0 : if ( pLine->GetLanguageId() == rSourceLang )
296 : : {
297 : 0 : if ( pSourceLine )
298 : : {
299 : 0 : PrintError( "Source Language entry double. Treating as Translation.", "File format", "", pLine->GetLineNumber(), pLine->GetUniqId() );
300 : 0 : bHasBlockError = sal_True;
301 : 0 : pSourceLine->NotOK();
302 : 0 : pLine->NotOK();
303 : : }
304 : : else
305 : : {
306 : 0 : pSourceLine = pLine;
307 : 0 : return;
308 : : }
309 : : }
310 : :
311 : 0 : if (!rSourceLang.isEmpty()) // only check blockstructure if source lang is given
312 : : {
313 : 0 : for ( size_t nPos = 0, n = maList.size(); nPos < n; ++nPos )
314 : : {
315 : 0 : if ( maList[ nPos ]->GetLanguageId() == pLine->GetLanguageId() )
316 : : {
317 : 0 : PrintError( "Translation Language entry double. Checking both.", "File format", "", pLine->GetLineNumber(), pLine->GetUniqId() );
318 : 0 : bHasBlockError = sal_True;
319 : 0 : maList[ nPos ]->NotOK();
320 : 0 : pLine->NotOK();
321 : : }
322 : 0 : nPos++;
323 : : }
324 : : }
325 : 0 : maList.push_back( pLine );
326 : : }
327 : :
328 : : /*****************************************************************************/
329 : 0 : void GSIBlock::SetReferenceLine( GSILine* pLine )
330 : : /*****************************************************************************/
331 : : {
332 : 0 : pReferenceLine = pLine;
333 : 0 : }
334 : :
335 : : /*****************************************************************************/
336 : 0 : void GSIBlock::PrintMessage( rtl::OString const & aType, rtl::OString const & aMsg, rtl::OString const & aPrefix,
337 : : rtl::OString const & aContext, std::size_t nLine, rtl::OString const & aUniqueId )
338 : : /*****************************************************************************/
339 : : {
340 : 0 : ::PrintMessage( aType, aMsg, aPrefix, aContext, bPrintContext, nLine, aUniqueId );
341 : 0 : }
342 : :
343 : : /*****************************************************************************/
344 : 0 : void GSIBlock::PrintError( rtl::OString const & aMsg, rtl::OString const & aPrefix,
345 : : rtl::OString const & aContext, std::size_t nLine, rtl::OString const & aUniqueId )
346 : : /*****************************************************************************/
347 : : {
348 : 0 : PrintMessage( "Error:", aMsg, aPrefix, aContext, nLine, aUniqueId );
349 : 0 : }
350 : :
351 : : /*****************************************************************************/
352 : 0 : void GSIBlock::PrintList( ParserMessageList *pList, rtl::OString const & aPrefix,
353 : : GSILine *pLine )
354 : : /*****************************************************************************/
355 : : {
356 : 0 : for ( size_t i = 0 ; i < pList->size() ; i++ )
357 : : {
358 : 0 : ParserMessage *pMsg = (*pList)[ i ];
359 : 0 : rtl::OString aContext;
360 : 0 : if ( bPrintContext )
361 : : {
362 : 0 : if ( pMsg->GetTagBegin() == -1 )
363 : 0 : aContext = pLine->GetText().copy( 0, 300 );
364 : : else
365 : 0 : aContext = helper::abbreviate( pLine->data_, pMsg->GetTagBegin()-150, 300 );
366 : 0 : aContext = aContext.trim();
367 : : }
368 : :
369 : 0 : PrintMessage( pMsg->Prefix(), pMsg->GetErrorText(), aPrefix, aContext, pLine->GetLineNumber(), pLine->GetUniqId() );
370 : 0 : }
371 : 0 : }
372 : :
373 : : /*****************************************************************************/
374 : 0 : sal_Bool GSIBlock::IsUTF8( const rtl::OString &aTestee, sal_Bool bFixTags, sal_Int32 &nErrorPos, rtl::OString &aErrorMsg, sal_Bool &bHasBeenFixed, rtl::OString &aFixed ) const
375 : : /*****************************************************************************/
376 : : {
377 : : rtl::OUString aUTF8Tester(
378 : 0 : rtl::OStringToOUString(aTestee, RTL_TEXTENCODING_UTF8));
379 : : rtl::OString aTestee2(
380 : 0 : rtl::OUStringToOString(aUTF8Tester, RTL_TEXTENCODING_UTF8));
381 : 0 : sal_Int32 i = 0;
382 : 0 : while (i != std::min(aTestee.getLength(), aTestee2.getLength())
383 : 0 : && aTestee[i] == aTestee2[i])
384 : : {
385 : 0 : ++i;
386 : : }
387 : 0 : if (i != aTestee.getLength() || i != aTestee2.getLength())
388 : : {
389 : 0 : aUTF8Tester = rtl::OUString(aTestee.getStr(), i, RTL_TEXTENCODING_UTF8);
390 : 0 : nErrorPos = aUTF8Tester.getLength();
391 : 0 : aErrorMsg = "UTF8 Encoding seems to be broken";
392 : 0 : return sal_False;
393 : : }
394 : :
395 : : nErrorPos = helper::indexOfAnyAsciiL(
396 : : aUTF8Tester,
397 : : RTL_CONSTASCII_STRINGPARAM(
398 : : "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11\x12"
399 : 0 : "\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"));
400 : 0 : if (nErrorPos != -1)
401 : : {
402 : 0 : aErrorMsg = "String contains illegal character";
403 : 0 : return sal_False;
404 : : }
405 : :
406 : 0 : if ( bFixTags )
407 : : {
408 : 0 : bHasBeenFixed = sal_False;
409 : 0 : aFixed = rtl::OString();
410 : : }
411 : :
412 : 0 : return sal_True;
413 : : }
414 : :
415 : : /*****************************************************************************/
416 : 0 : sal_Bool GSIBlock::TestUTF8( GSILine* pTestee, sal_Bool bFixTags )
417 : : /*****************************************************************************/
418 : : {
419 : 0 : sal_Int32 nErrorPos = 0;
420 : 0 : rtl::OString aErrorMsg;
421 : 0 : sal_Bool bError = sal_False;
422 : 0 : rtl::OString aFixed;
423 : 0 : sal_Bool bHasBeenFixed = sal_False;
424 : 0 : if ( !IsUTF8( pTestee->GetText(), bFixTags, nErrorPos, aErrorMsg, bHasBeenFixed, aFixed ) )
425 : : {
426 : 0 : rtl::OString aContext(copyUpTo(pTestee->GetText(), nErrorPos, 20));
427 : 0 : PrintError(rtl::OStringBuffer(aErrorMsg).append(RTL_CONSTASCII_STRINGPARAM(" in Text at Position "))
428 : 0 : .append(nErrorPos).getStr(),
429 : 0 : "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId());
430 : 0 : bError = sal_True;
431 : 0 : if ( bHasBeenFixed )
432 : : {
433 : 0 : pTestee->SetText( aFixed );
434 : 0 : pTestee->SetFixed();
435 : 0 : }
436 : : }
437 : 0 : if ( !IsUTF8( pTestee->GetQuickHelpText(), bFixTags, nErrorPos, aErrorMsg, bHasBeenFixed, aFixed ) )
438 : : {
439 : : rtl::OString aContext(
440 : 0 : copyUpTo(pTestee->GetQuickHelpText(), nErrorPos, 20));
441 : 0 : PrintError(rtl::OStringBuffer(aErrorMsg).append(RTL_CONSTASCII_STRINGPARAM(" in QuickHelpText at Position "))
442 : 0 : .append(nErrorPos).getStr(),
443 : 0 : "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId());
444 : 0 : bError = sal_True;
445 : 0 : if ( bHasBeenFixed )
446 : : {
447 : 0 : pTestee->SetQuickHelpText( aFixed );
448 : 0 : pTestee->SetFixed();
449 : 0 : }
450 : : }
451 : 0 : if ( !IsUTF8( pTestee->GetTitle(), bFixTags, nErrorPos, aErrorMsg, bHasBeenFixed, aFixed ) )
452 : : {
453 : 0 : rtl::OString aContext( pTestee->GetTitle().copy( nErrorPos, 20 ) );
454 : 0 : PrintError(rtl::OStringBuffer(aErrorMsg).append(RTL_CONSTASCII_STRINGPARAM(" in Title at Position "))
455 : 0 : .append(nErrorPos).getStr(),
456 : 0 : "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId());
457 : 0 : bError = sal_True;
458 : 0 : if ( bHasBeenFixed )
459 : : {
460 : 0 : pTestee->SetTitle( aFixed );
461 : 0 : pTestee->SetFixed();
462 : 0 : }
463 : : }
464 : 0 : if ( bError )
465 : 0 : pTestee->NotOK();
466 : 0 : return !bError;
467 : : }
468 : :
469 : :
470 : : /*****************************************************************************/
471 : 0 : sal_Bool GSIBlock::HasSuspiciousChars( GSILine* pTestee, GSILine* pSource )
472 : : /*****************************************************************************/
473 : : {
474 : 0 : sal_Int32 nPos = 0;
475 : 0 : if ( !bAllowSuspicious && ( nPos = pTestee->GetText().indexOf("??")) != -1 )
476 : 0 : if ( pSource->GetText().indexOf("??") == -1 )
477 : : {
478 : : rtl::OUString aUTF8Tester(
479 : : rtl::OStringToOUString(
480 : 0 : pTestee->GetText().copy(0, nPos), RTL_TEXTENCODING_UTF8));
481 : 0 : sal_Int32 nErrorPos = aUTF8Tester.getLength();
482 : 0 : rtl::OString aContext( helper::abbreviate( pTestee->GetText(), nPos, 20 ) );
483 : : PrintError(rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM("Found double questionmark in translation only. Looks like an encoding problem at Position "))
484 : 0 : .append(nErrorPos).getStr(),
485 : 0 : "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId());
486 : 0 : pTestee->NotOK();
487 : 0 : return sal_True;
488 : : }
489 : :
490 : 0 : return sal_False;
491 : : }
492 : :
493 : :
494 : : /*****************************************************************************/
495 : 0 : sal_Bool GSIBlock::CheckSyntax( std::size_t nLine, sal_Bool bRequireSourceLine, sal_Bool bFixTags )
496 : : /*****************************************************************************/
497 : : {
498 : 0 : static LingTest aTester;
499 : 0 : sal_Bool bHasError = sal_False;
500 : :
501 : 0 : if ( !pSourceLine )
502 : : {
503 : 0 : if ( bRequireSourceLine )
504 : : {
505 : 0 : PrintError( "No source language entry defined!", "File format", "", nLine );
506 : 0 : bHasBlockError = sal_True;
507 : : }
508 : : }
509 : : else
510 : : {
511 : 0 : aTester.CheckReference( pSourceLine );
512 : 0 : if ( pSourceLine->HasMessages() )
513 : : {
514 : 0 : PrintList( pSourceLine->GetMessageList(), "ReferenceString", pSourceLine );
515 : 0 : pSourceLine->NotOK();
516 : 0 : bHasError = sal_True;
517 : : }
518 : : }
519 : 0 : if ( bReference )
520 : : {
521 : 0 : if ( !pReferenceLine )
522 : : {
523 : : GSILine *pSource;
524 : 0 : if ( pSourceLine )
525 : 0 : pSource = pSourceLine;
526 : : else
527 : 0 : pSource = maList.empty() ? NULL : maList[ 0 ]; // get some other line
528 : 0 : if ( pSource )
529 : 0 : PrintError( "No reference line found. Entry is new in source file", "File format", "", pSource->GetLineNumber(), pSource->GetUniqId() );
530 : : else
531 : 0 : PrintError( "No reference line found. Entry is new in source file", "File format", "", nLine );
532 : 0 : bHasBlockError = sal_True;
533 : : }
534 : : else
535 : : {
536 : 0 : if ( pSourceLine && pSourceLine->data_ != pReferenceLine->data_ )
537 : : {
538 : 0 : sal_Int32 nPos = pSourceLine->data_.indexOf( pReferenceLine->data_ );
539 : 0 : rtl::OStringBuffer aContext( pReferenceLine->data_.copy( nPos - 5, 15) );
540 : 0 : aContext.append( "\" --> \"" ).append( pSourceLine->data_.copy( nPos - 5, 15) );
541 : 0 : PrintError( "Source Language Entry has changed.", "File format", aContext.makeStringAndClear(), pSourceLine->GetLineNumber(), pSourceLine->GetUniqId() );
542 : 0 : pSourceLine->NotOK();
543 : 0 : bHasError = sal_True;
544 : : }
545 : : }
546 : : }
547 : :
548 : 0 : if ( pSourceLine )
549 : 0 : bHasError |= !TestUTF8( pSourceLine, bFixTags );
550 : :
551 : 0 : for ( size_t i = 0, n = maList.size(); i < n; ++i )
552 : : {
553 : 0 : GSILine* pItem = maList[ i ];
554 : 0 : aTester.CheckTestee( pItem, pSourceLine != NULL, bFixTags );
555 : 0 : if ( pItem->HasMessages() || aTester.HasCompareWarnings() )
556 : : {
557 : 0 : if ( pItem->HasMessages() || aTester.GetCompareWarnings().HasErrors() )
558 : 0 : pItem->NotOK();
559 : 0 : bHasError = sal_True;
560 : 0 : PrintList( pItem->GetMessageList(), "Translation", pItem );
561 : 0 : PrintList( &(aTester.GetCompareWarnings()), "Translation Tag Mismatch", pItem );
562 : : }
563 : 0 : bHasError |= !TestUTF8( pItem, bFixTags );
564 : 0 : if ( pSourceLine )
565 : 0 : bHasError |= HasSuspiciousChars( pItem, pSourceLine );
566 : : }
567 : :
568 : 0 : return bHasError || bHasBlockError;
569 : : }
570 : :
571 : 0 : void GSIBlock::WriteError( LazyStream &aErrOut, sal_Bool bRequireSourceLine )
572 : : {
573 : 0 : if ( pSourceLine && pSourceLine->IsOK() && bCheckSourceLang && !bHasBlockError )
574 : 0 : return;
575 : :
576 : 0 : sal_Bool bHasError = sal_False;
577 : 0 : sal_Bool bCopyAll = ( !pSourceLine && bRequireSourceLine ) || ( pSourceLine && !pSourceLine->IsOK() && !bCheckTranslationLang ) || bHasBlockError;
578 : 0 : for ( size_t i = 0, n = maList.size(); i < n; ++i )
579 : : {
580 : 0 : GSILine* pItem = maList[ i ];
581 : 0 : if ( !pItem->IsOK() || bCopyAll )
582 : : {
583 : 0 : bHasError = sal_True;
584 : 0 : aErrOut.LazyOpen();
585 : 0 : aErrOut << pItem->data_.getStr() << '\n';
586 : : }
587 : : }
588 : :
589 : 0 : if ( pSourceLine && ( bHasError || !pSourceLine->IsOK() ) && !( !bHasError && bCheckTranslationLang ) )
590 : : {
591 : 0 : aErrOut.LazyOpen();
592 : 0 : aErrOut << pSourceLine->data_.getStr() << '\n';
593 : : }
594 : : }
595 : :
596 : 0 : void GSIBlock::WriteCorrect( LazyStream &aOkOut, sal_Bool bRequireSourceLine )
597 : : {
598 : 0 : if ( ( !pSourceLine && bRequireSourceLine ) || ( pSourceLine && !pSourceLine->IsOK() && !bCheckTranslationLang ) )
599 : 0 : return;
600 : :
601 : 0 : sal_Bool bHasOK = sal_False;
602 : 0 : for ( size_t i = 0, n = maList.size(); i < n; ++i )
603 : : {
604 : 0 : GSILine* pItem = maList[ i ];
605 : 0 : if ( ( pItem->IsOK() || bCheckSourceLang ) && !bHasBlockError )
606 : : {
607 : 0 : bHasOK = sal_True;
608 : 0 : aOkOut.LazyOpen();
609 : 0 : aOkOut << pItem->data_.getStr() << '\n';
610 : : }
611 : : }
612 : :
613 : 0 : if ( ( pSourceLine && pSourceLine->IsOK() && ( !maList.empty() || !bCheckTranslationLang ) ) || ( bHasOK && bCheckTranslationLang ) )
614 : : {
615 : 0 : aOkOut.LazyOpen();
616 : 0 : aOkOut << pSourceLine->data_.getStr() << '\n';
617 : : }
618 : : }
619 : :
620 : 0 : void GSIBlock::WriteFixed( LazyStream &aFixOut )
621 : : {
622 : 0 : if ( pSourceLine && !pSourceLine->IsFixed() && bCheckSourceLang )
623 : 0 : return;
624 : :
625 : 0 : sal_Bool bHasFixes = sal_False;
626 : 0 : for ( size_t i = 0, n = maList.size(); i < n; ++i )
627 : : {
628 : 0 : GSILine* pItem = maList[ i ];
629 : 0 : if ( pItem->IsFixed() )
630 : : {
631 : 0 : bHasFixes = sal_True;
632 : 0 : aFixOut.LazyOpen();
633 : 0 : aFixOut << pItem->data_.getStr() << '\n';
634 : : }
635 : : }
636 : :
637 : 0 : if ( pSourceLine && ( bHasFixes || pSourceLine->IsFixed() ) )
638 : : {
639 : 0 : aFixOut.LazyOpen();
640 : 0 : aFixOut << pSourceLine->data_.getStr() << '\n';
641 : : }
642 : : }
643 : :
644 : : /*****************************************************************************/
645 : 0 : void Help()
646 : : /*****************************************************************************/
647 : : {
648 : 0 : fprintf( stdout, "\n" );
649 : 0 : fprintf( stdout, "gsicheck checks the syntax of tags in SDF-Files\n" );
650 : 0 : fprintf( stdout, " checks for inconsistencies and malicious UTF8 encoding\n" );
651 : 0 : fprintf( stdout, " checks tags in Online Help\n" );
652 : : fprintf( stdout, " relax GID/LID length to %s\n",
653 : 0 : rtl::OString::valueOf(static_cast<sal_Int32>(MAX_GID_LID_LEN)).getStr() );
654 : 0 : fprintf( stdout, "\n" );
655 : 0 : fprintf( stdout, "Syntax: gsicheck [ -c ] [-f] [ -we ] [ -wef ErrorFilename ] [ -wc ]\n" );
656 : 0 : fprintf( stdout, " [ -wcf CorrectFilename ] [ -s | -t ] [ -l LanguageID ]\n" );
657 : 0 : fprintf( stdout, " [ -r ReferenceFile ] filename\n" );
658 : 0 : fprintf( stdout, "\n" );
659 : 0 : fprintf( stdout, "-c Add context to error message (Print the line containing the error)\n" );
660 : 0 : fprintf( stdout, "-f try to fix errors. See also -wf -wff \n" );
661 : 0 : fprintf( stdout, "-wf Write File containing all fixed parts\n" );
662 : 0 : fprintf( stdout, "-wff Same as above but give own filename\n" );
663 : 0 : fprintf( stdout, "-we Write File containing all errors\n" );
664 : 0 : fprintf( stdout, "-wef Same as above but give own filename\n" );
665 : 0 : fprintf( stdout, "-wc Write File containing all correct parts\n" );
666 : 0 : fprintf( stdout, "-wcf Same as above but give own filename\n" );
667 : 0 : fprintf( stdout, "-s Check only source language. Should be used before handing out to vendor.\n" );
668 : 0 : fprintf( stdout, "-t Check only Translation language(s). Should be used before merging.\n" );
669 : 0 : fprintf( stdout, "-e disable encoding checks. E.g.: double questionmark \'??\' which may be the\n" );
670 : 0 : fprintf( stdout, " result of false conversions\n" );
671 : 0 : fprintf( stdout, "-l ISO language code of the source language.\n" );
672 : 0 : fprintf( stdout, " Default is en-US. Use \"\" (empty string) or 'none'\n" );
673 : 0 : fprintf( stdout, " to disable source language dependent checks\n" );
674 : 0 : fprintf( stdout, "-r Reference filename to check that source language entries\n" );
675 : 0 : fprintf( stdout, " have not been changed\n" );
676 : 0 : fprintf( stdout, "\n" );
677 : 0 : }
678 : :
679 : 0 : SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) {
680 : 0 : sal_Bool bError = sal_False;
681 : 0 : sal_Bool bPrintContext = sal_False;
682 : 0 : sal_Bool bCheckSourceLang = sal_False;
683 : 0 : sal_Bool bCheckTranslationLang = sal_False;
684 : 0 : sal_Bool bWriteError = sal_False;
685 : 0 : sal_Bool bWriteCorrect = sal_False;
686 : 0 : sal_Bool bWriteFixed = sal_False;
687 : 0 : sal_Bool bFixTags = sal_False;
688 : 0 : sal_Bool bAllowSuspicious = sal_False;
689 : 0 : rtl::OString aErrorFilename;
690 : 0 : rtl::OString aCorrectFilename;
691 : 0 : rtl::OString aFixedFilename;
692 : 0 : sal_Bool bFileHasError = sal_False;
693 : 0 : rtl::OString aSourceLang( "en-US" ); // English is default
694 : 0 : rtl::OString aFilename;
695 : 0 : rtl::OString aReferenceFilename;
696 : 0 : sal_Bool bReferenceFile = sal_False;
697 : 0 : for ( int i = 1 ; i < argc ; i++ )
698 : : {
699 : 0 : if ( *argv[ i ] == '-' )
700 : : {
701 : 0 : switch (*(argv[ i ]+1))
702 : : {
703 : 0 : case 'c':bPrintContext = sal_True;
704 : 0 : break;
705 : : case 'w':
706 : : {
707 : 0 : if ( (*(argv[ i ]+2)) == 'e' )
708 : : {
709 : 0 : if ( (*(argv[ i ]+3)) == 'f' )
710 : 0 : if ( (i+1) < argc )
711 : : {
712 : 0 : aErrorFilename = argv[i + 1];
713 : 0 : bWriteError = sal_True;
714 : 0 : i++;
715 : : }
716 : : else
717 : : {
718 : 0 : fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
719 : 0 : bError = sal_True;
720 : : }
721 : : else
722 : 0 : bWriteError = sal_True;
723 : : }
724 : 0 : else if ( (*(argv[ i ]+2)) == 'c' )
725 : 0 : if ( (*(argv[ i ]+3)) == 'f' )
726 : 0 : if ( (i+1) < argc )
727 : : {
728 : 0 : aCorrectFilename = argv[i + 1];
729 : 0 : bWriteCorrect = sal_True;
730 : 0 : i++;
731 : : }
732 : : else
733 : : {
734 : 0 : fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
735 : 0 : bError = sal_True;
736 : : }
737 : : else
738 : 0 : bWriteCorrect = sal_True;
739 : 0 : else if ( (*(argv[ i ]+2)) == 'f' )
740 : 0 : if ( (*(argv[ i ]+3)) == 'f' )
741 : 0 : if ( (i+1) < argc )
742 : : {
743 : 0 : aFixedFilename = argv[i + 1];
744 : 0 : bWriteFixed = sal_True;
745 : 0 : bFixTags = sal_True;
746 : 0 : i++;
747 : : }
748 : : else
749 : : {
750 : 0 : fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
751 : 0 : bError = sal_True;
752 : : }
753 : : else
754 : : {
755 : 0 : bWriteFixed = sal_True;
756 : 0 : bFixTags = sal_True;
757 : : }
758 : : else
759 : : {
760 : 0 : fprintf( stderr, "\nERROR: Unknown Switch %s!\n\n", argv[ i ] );
761 : 0 : bError = sal_True;
762 : : }
763 : : }
764 : 0 : break;
765 : 0 : case 's':bCheckSourceLang = sal_True;
766 : 0 : break;
767 : 0 : case 't':bCheckTranslationLang = sal_True;
768 : 0 : break;
769 : : case 'l':
770 : : {
771 : 0 : if ( (i+1) < argc )
772 : : {
773 : 0 : aSourceLang = argv[ i+1 ];
774 : 0 : if ( aSourceLang.equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("none")) )
775 : 0 : aSourceLang = rtl::OString();
776 : 0 : i++;
777 : : }
778 : : else
779 : : {
780 : 0 : fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
781 : 0 : bError = sal_True;
782 : : }
783 : : }
784 : 0 : break;
785 : : case 'r':
786 : : {
787 : 0 : if ( (i+1) < argc )
788 : : {
789 : 0 : aReferenceFilename = argv[ i+1 ];
790 : 0 : bReferenceFile = sal_True;
791 : 0 : i++;
792 : : }
793 : : else
794 : : {
795 : 0 : fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
796 : 0 : bError = sal_True;
797 : : }
798 : : }
799 : 0 : break;
800 : : case 'f':
801 : : {
802 : 0 : bFixTags = sal_True;
803 : : }
804 : 0 : break;
805 : : case 'e':
806 : : {
807 : 0 : bAllowSuspicious = sal_True;
808 : : }
809 : 0 : break;
810 : : default:
811 : 0 : fprintf( stderr, "\nERROR: Unknown Switch %s!\n\n", argv[ i ] );
812 : 0 : bError = sal_True;
813 : : }
814 : : }
815 : : else
816 : : {
817 : 0 : if (aFilename.isEmpty())
818 : 0 : aFilename = argv[i];
819 : : else
820 : : {
821 : 0 : fprintf( stderr, "\nERROR: Only one filename may be specified!\n\n");
822 : 0 : bError = sal_True;
823 : : }
824 : : }
825 : : }
826 : :
827 : :
828 : 0 : if (aFilename.isEmpty() || bError)
829 : : {
830 : 0 : Help();
831 : 0 : exit ( 0 );
832 : : }
833 : :
834 : 0 : if ( !aSourceLang.isEmpty() && !LanguageOK( aSourceLang ) )
835 : : {
836 : 0 : fprintf( stderr, "\nERROR: The Language '%s' is invalid!\n\n", aSourceLang.getStr() );
837 : 0 : Help();
838 : 0 : exit ( 1 );
839 : : }
840 : :
841 : 0 : if ( bCheckSourceLang && bCheckTranslationLang )
842 : : {
843 : 0 : fprintf( stderr, "\nERROR: The Options -s and -t are mutually exclusive.\nUse only one of them.\n\n" );
844 : 0 : Help();
845 : 0 : exit ( 1 );
846 : : }
847 : :
848 : :
849 : :
850 : 0 : std::ifstream aGSI(aFilename.getStr());
851 : 0 : if (!aGSI.is_open()) {
852 : 0 : fprintf( stderr, "\nERROR: Could not open GSI-File %s!\n\n", aFilename.getStr() );
853 : 0 : exit ( 3 );
854 : : }
855 : :
856 : 0 : std::ifstream aReferenceGSI;
857 : 0 : if ( bReferenceFile )
858 : : {
859 : 0 : aReferenceGSI.open(aReferenceFilename.getStr());
860 : 0 : if (!aReferenceGSI.is_open()) {
861 : 0 : fprintf( stderr, "\nERROR: Could not open Input-File %s!\n\n", aFilename.getStr() );
862 : 0 : exit ( 3 );
863 : : }
864 : : }
865 : :
866 : 0 : LazyStream aOkOut;
867 : 0 : if ( bWriteCorrect )
868 : : {
869 : 0 : if (aCorrectFilename.isEmpty())
870 : : {
871 : : aCorrectFilename = addSuffix(
872 : 0 : aFilename, rtl::OString(RTL_CONSTASCII_STRINGPARAM("_ok")));
873 : : }
874 : 0 : aOkOut.SetFileName(aCorrectFilename);
875 : : }
876 : :
877 : 0 : LazyStream aErrOut;
878 : 0 : if ( bWriteError )
879 : : {
880 : 0 : if (aErrorFilename.isEmpty())
881 : : {
882 : : aErrorFilename = addSuffix(
883 : 0 : aFilename, rtl::OString(RTL_CONSTASCII_STRINGPARAM("_err")));
884 : : }
885 : 0 : aErrOut.SetFileName(aErrorFilename);
886 : : }
887 : :
888 : 0 : LazyStream aFixOut;
889 : 0 : if ( bWriteFixed )
890 : : {
891 : 0 : if (aFixedFilename.isEmpty())
892 : : {
893 : : aFixedFilename = addSuffix(
894 : 0 : aFilename, rtl::OString(RTL_CONSTASCII_STRINGPARAM("_fix")));
895 : : }
896 : 0 : aFixOut.SetFileName(aFixedFilename);
897 : : }
898 : :
899 : :
900 : 0 : GSILine* pReferenceLine = NULL;
901 : 0 : std::size_t nReferenceLine = 0;
902 : :
903 : 0 : GSILine* pGSILine = NULL;
904 : 0 : rtl::OString aOldId("No Valid ID"); // just set to something which can never be an ID
905 : 0 : GSIBlock *pBlock = NULL;
906 : 0 : std::size_t nLine = 0;
907 : :
908 : 0 : while (!aGSI.eof())
909 : : {
910 : 0 : std::string s;
911 : 0 : std::getline(aGSI, s);
912 : 0 : nLine++;
913 : 0 : pGSILine = new GSILine(rtl::OString(s.data(), s.length()), nLine );
914 : 0 : sal_Bool bDelete = sal_True;
915 : :
916 : :
917 : 0 : if ( !pGSILine->data_.isEmpty() )
918 : : {
919 : 0 : if ( FORMAT_UNKNOWN == pGSILine->GetLineFormat() )
920 : : {
921 : 0 : PrintError( "Format of line is unknown. Ignoring!", "Line format", pGSILine->data_.copy( 0,40 ), bPrintContext, pGSILine->GetLineNumber() );
922 : 0 : pGSILine->NotOK();
923 : 0 : if ( bWriteError )
924 : : {
925 : 0 : bFileHasError = sal_True;
926 : 0 : aErrOut.LazyOpen();
927 : 0 : aErrOut << pGSILine->data_.getStr();
928 : : }
929 : : }
930 : 0 : else if ( pGSILine->GetLineType().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("res-comment")) )
931 : : { // ignore comment lines, but write them to Correct Items File
932 : 0 : if ( bWriteCorrect )
933 : : {
934 : 0 : aOkOut.LazyOpen();
935 : 0 : aOkOut << pGSILine->data_.getStr() << '\n';
936 : : }
937 : : }
938 : : else
939 : : {
940 : 0 : rtl::OString aId = pGSILine->GetUniqId();
941 : 0 : if ( aId != aOldId )
942 : : {
943 : 0 : if ( pBlock )
944 : : {
945 : 0 : bFileHasError |= pBlock->CheckSyntax( nLine, !aSourceLang.isEmpty(), bFixTags );
946 : :
947 : 0 : if ( bWriteError )
948 : 0 : pBlock->WriteError( aErrOut, !aSourceLang.isEmpty() );
949 : 0 : if ( bWriteCorrect )
950 : 0 : pBlock->WriteCorrect( aOkOut, !aSourceLang.isEmpty() );
951 : 0 : if ( bWriteFixed )
952 : 0 : pBlock->WriteFixed( aFixOut );
953 : :
954 : 0 : delete pBlock;
955 : : }
956 : 0 : pBlock = new GSIBlock( bPrintContext, bCheckSourceLang, bCheckTranslationLang, bReferenceFile, bAllowSuspicious );
957 : :
958 : 0 : aOldId = aId;
959 : :
960 : :
961 : : // find corresponding line in reference file
962 : 0 : if ( bReferenceFile )
963 : : {
964 : 0 : sal_Bool bContinueSearching = sal_True;
965 : 0 : while ( ( !aReferenceGSI.eof() || pReferenceLine ) && bContinueSearching )
966 : : {
967 : 0 : if ( !pReferenceLine )
968 : : {
969 : 0 : std::string s2;
970 : 0 : std::getline(aReferenceGSI, s2);
971 : 0 : nReferenceLine++;
972 : : pReferenceLine = new GSILine(
973 : 0 : rtl::OString(s2.data(), s2.length()),
974 : 0 : nReferenceLine);
975 : : }
976 : 0 : if ( pReferenceLine->GetLineFormat() != FORMAT_UNKNOWN )
977 : : {
978 : 0 : if ( pReferenceLine->GetUniqId() == aId && pReferenceLine->GetLanguageId() == aSourceLang )
979 : : {
980 : 0 : pBlock->SetReferenceLine( pReferenceLine );
981 : 0 : pReferenceLine = NULL;
982 : : }
983 : 0 : else if ( pReferenceLine->GetUniqId() > aId )
984 : : {
985 : 0 : bContinueSearching = sal_False;
986 : : }
987 : : else
988 : : {
989 : 0 : if ( pReferenceLine->GetUniqId() < aId && pReferenceLine->GetLanguageId() == aSourceLang )
990 : 0 : PrintError( "No Entry in source file found. Entry has been removed from source file", "File format", "", bPrintContext, pGSILine->GetLineNumber(), pReferenceLine->GetUniqId() );
991 : 0 : delete pReferenceLine;
992 : 0 : pReferenceLine = NULL;
993 : : }
994 : : }
995 : : else
996 : : {
997 : 0 : delete pReferenceLine;
998 : 0 : pReferenceLine = NULL;
999 : : }
1000 : :
1001 : : }
1002 : : }
1003 : :
1004 : : }
1005 : :
1006 : 0 : pBlock->InsertLine( pGSILine, aSourceLang );
1007 : 0 : bDelete = sal_False;
1008 : : }
1009 : : }
1010 : 0 : if ( bDelete )
1011 : 0 : delete pGSILine;
1012 : :
1013 : 0 : }
1014 : 0 : if ( pBlock )
1015 : : {
1016 : 0 : bFileHasError |= pBlock->CheckSyntax( nLine, !aSourceLang.isEmpty(), bFixTags );
1017 : :
1018 : 0 : if ( bWriteError )
1019 : 0 : pBlock->WriteError( aErrOut, !aSourceLang.isEmpty() );
1020 : 0 : if ( bWriteCorrect )
1021 : 0 : pBlock->WriteCorrect( aOkOut, !aSourceLang.isEmpty() );
1022 : 0 : if ( bWriteFixed )
1023 : 0 : pBlock->WriteFixed( aFixOut );
1024 : :
1025 : 0 : delete pBlock;
1026 : : }
1027 : 0 : aGSI.close();
1028 : :
1029 : 0 : if ( bWriteError )
1030 : 0 : aErrOut.close();
1031 : 0 : if ( bWriteCorrect )
1032 : 0 : aOkOut.close();
1033 : 0 : if ( bWriteFixed )
1034 : 0 : aFixOut.close();
1035 : :
1036 : 0 : if ( bFileHasError )
1037 : 0 : return 55;
1038 : : else
1039 : 0 : return 0;
1040 : : }
1041 : :
1042 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|