Update ooo320-m1
[ooovba.git] / transex3 / source / gsicheck.cxx
blobcda02df9c6943dcbc5416539005cba9d9102a162
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: gsicheck.cxx,v $
10 * $Revision: 1.29 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_transex3.hxx"
33 #include <stdio.h>
34 #include <tools/fsys.hxx>
35 #include <tools/stream.hxx>
36 #include <tools/list.hxx>
38 // local includes
39 #include "tagtest.hxx"
40 #include "gsicheck.hxx"
42 #define MAX_GID_LID_LEN 250
44 /*****************************************************************************/
45 void PrintMessage( ByteString aType, ByteString aMsg, ByteString aPrefix,
46 ByteString aContext, BOOL bPrintContext, ULONG nLine, ByteString aUniqueId = ByteString() )
47 /*****************************************************************************/
49 fprintf( stdout, "%s %s, Line %lu", aType.GetBuffer(), aPrefix.GetBuffer(), nLine );
50 if ( aUniqueId.Len() )
51 fprintf( stdout, ", UniqueID %s", aUniqueId.GetBuffer() );
52 fprintf( stdout, ": %s", aMsg.GetBuffer() );
54 if ( bPrintContext )
55 fprintf( stdout, " \"%s\"", aContext.GetBuffer() );
56 fprintf( stdout, "\n" );
59 /*****************************************************************************/
60 void PrintError( ByteString aMsg, ByteString aPrefix,
61 ByteString aContext, BOOL bPrintContext, ULONG nLine, ByteString aUniqueId = ByteString() )
62 /*****************************************************************************/
64 PrintMessage( "Error:", aMsg, aPrefix, aContext, bPrintContext, nLine, aUniqueId );
67 BOOL LanguageOK( ByteString aLang )
69 if ( !aLang.Len() )
70 return FALSE;
72 if ( aLang.IsNumericAscii() )
73 return TRUE;
75 if ( aLang.GetTokenCount( '-' ) == 1 )
76 return aLang.IsAlphaAscii() && aLang.IsLowerAscii();
77 else if ( aLang.GetTokenCount( '-' ) == 2 )
79 ByteString aTok0( aLang.GetToken( 0, '-' ) );
80 ByteString aTok1( aLang.GetToken( 1, '-' ) );
81 return aTok0.Len() && aTok0.IsAlphaAscii() && aTok0.IsLowerAscii()
82 && aTok1.Len() && aTok1.IsAlphaAscii() && aTok1.IsUpperAscii()
83 && !aTok1.EqualsIgnoreCaseAscii( aTok0 );
86 return FALSE;
91 // class LazySvFileStream
95 class LazySvFileStream : public SvFileStream
98 private:
99 String aFileName;
100 BOOL bOpened;
101 StreamMode eOpenMode;
103 public:
104 LazySvFileStream()
105 : aFileName()
106 , bOpened( FALSE )
107 , eOpenMode( 0 )
110 void SetOpenParams( const String& rFileName, StreamMode eOpenModeP )
112 aFileName = rFileName;
113 eOpenMode = eOpenModeP;
116 void LazyOpen();
119 void LazySvFileStream::LazyOpen()
121 if ( !bOpened )
123 Open( aFileName, eOpenMode );
124 if ( !IsOpen())
126 fprintf( stderr, "\nERROR: Could not open Output-File %s!\n\n", ByteString( aFileName, RTL_TEXTENCODING_ASCII_US ).GetBuffer() );
127 exit ( 4 );
129 bOpened = TRUE;
135 // class GSILine
138 /*****************************************************************************/
139 GSILine::GSILine( const ByteString &rLine, ULONG nLine )
140 /*****************************************************************************/
141 : ByteString( rLine )
142 , nLineNumber( nLine )
143 , bOK( TRUE )
144 , bFixed ( FALSE )
146 if ( rLine.GetTokenCount( '\t' ) == 15 )
148 aFormat = FORMAT_SDF;
149 aUniqId = rLine.GetToken( 0, '\t' );
150 aUniqId.Append("/").Append( rLine.GetToken( 1, '\t' ) ).Append("/").Append( rLine.GetToken( 3, '\t' ) ).Append("/").Append( rLine.GetToken( 4, '\t' ) ).Append("/").Append( rLine.GetToken( 5, '\t' ) ).Append("/").Append( rLine.GetToken( 6, '\t' ) ).Append("/").Append( rLine.GetToken( 7, '\t' ) );
151 aLineType = "";
152 aLangId = rLine.GetToken( 9, '\t' );
153 aText = rLine.GetToken( 10, '\t' );
154 aQuickHelpText = rLine.GetToken( 12, '\t' );
155 aTitle = rLine.GetToken( 13, '\t' );
157 // do some more format checks here
158 if ( !rLine.GetToken( 8, '\t' ).IsNumericAscii() )
160 PrintError( "The length field does not contain a number!", "Line format", rLine.GetToken( 8, '\t' ), TRUE, GetLineNumber(), GetUniqId() );
161 NotOK();
163 if ( !LanguageOK( aLangId ) )
165 PrintError( "The Language is invalid!", "Line format", aLangId, TRUE, GetLineNumber(), GetUniqId() );
166 NotOK();
168 // limit GID and LID to MAX_GID_LID_LEN chars each for database conformity, see #137575#
169 if ( rLine.GetToken( 4, '\t' ).Len() > MAX_GID_LID_LEN || rLine.GetToken( 5, '\t' ).Len() > MAX_GID_LID_LEN )
171 PrintError( ByteString("GID and LID may only be ").Append( ByteString::CreateFromInt32(MAX_GID_LID_LEN) ).Append( " chars long each!" ), "Line format", aLangId, TRUE, GetLineNumber(), GetUniqId() );
172 NotOK();
175 else // allow tabs in gsi files
177 aFormat = FORMAT_GSI;
178 ByteString sTmp( rLine );
179 USHORT nPos = sTmp.Search( "($$)" );
180 USHORT nStart = 0;
181 if ( nPos != STRING_NOTFOUND )
183 aUniqId = sTmp.Copy( nStart, nPos - nStart );
184 nStart = nPos + 4; // + length of the delemiter
185 nPos = sTmp.Search( "($$)", nStart );
187 if ( nPos != STRING_NOTFOUND )
189 aLineType = sTmp.Copy( nStart, nPos - nStart );
190 nStart = nPos + 4; // + length of the delemiter
191 nPos = sTmp.Search( "($$)", nStart );
192 aUniqId.Append( "/" );
193 aUniqId.Append( aLineType );
195 if ( nPos != STRING_NOTFOUND )
197 aLangId = sTmp.Copy( nStart, nPos - nStart );
198 nStart = nPos + 4; // + length of the delemiter
199 nPos = sTmp.Search( "($$)", nStart );
201 if ( nPos != STRING_NOTFOUND )
203 // ByteString aStatus = sTmp.Copy( nStart, nPos - nStart ); // ext int ...
204 nStart = nPos + 4; // + length of the delemiter
206 if ( nPos != STRING_NOTFOUND )
207 aText = sTmp.Copy( nStart );
208 else
209 aFormat = FORMAT_UNKNOWN;
212 if ( FORMAT_UNKNOWN == GetLineFormat() )
213 NotOK();
216 /*****************************************************************************/
217 void GSILine::NotOK()
218 /*****************************************************************************/
220 bOK = FALSE;
223 /*****************************************************************************/
224 void GSILine::ReassembleLine()
225 /*****************************************************************************/
227 ByteString aReassemble;
228 if ( GetLineFormat() == FORMAT_SDF )
230 USHORT i;
231 for ( i = 0 ; i < 10 ; i++ )
233 aReassemble.Append( GetToken( i, '\t' ) );
234 aReassemble.Append( "\t" );
236 aReassemble.Append( aText );
237 aReassemble.Append( "\t" );
238 aReassemble.Append( GetToken( 11, '\t' ) ); // should be empty but there are some places in sc. Not reflected to sources!!
239 aReassemble.Append( "\t" );
240 aReassemble.Append( aQuickHelpText );
241 aReassemble.Append( "\t" );
242 aReassemble.Append( aTitle );
243 for ( i = 14 ; i < 15 ; i++ )
245 aReassemble.Append( "\t" );
246 aReassemble.Append( GetToken( i, '\t' ) );
248 *(ByteString*)this = aReassemble;
250 else if ( GetLineFormat() == FORMAT_GSI )
252 USHORT nPos = Search( "($$)" );
253 USHORT nStart = 0;
254 if ( nPos != STRING_NOTFOUND )
256 nStart = nPos + 4; // + length of the delemiter
257 nPos = Search( "($$)", nStart );
259 if ( nPos != STRING_NOTFOUND )
261 nStart = nPos + 4; // + length of the delemiter
262 nPos = Search( "($$)", nStart );
264 if ( nPos != STRING_NOTFOUND )
266 nStart = nPos + 4; // + length of the delemiter
267 nPos = Search( "($$)", nStart );
269 if ( nPos != STRING_NOTFOUND )
271 nStart = nPos + 4; // + length of the delemiter
273 if ( nPos != STRING_NOTFOUND )
275 aReassemble = Copy( 0, nStart );
276 aReassemble += aText;
277 *(ByteString*)this = aReassemble;
279 else
280 PrintError( "Cannot reassemble GSI line (internal Error).", "Line format", "", FALSE, GetLineNumber(), GetUniqId() );
282 else
283 PrintError( "Cannot reassemble line of unknown type (internal Error).", "Line format", "", FALSE, GetLineNumber(), GetUniqId() );
287 // class GSIBlock
289 /*****************************************************************************/
290 GSIBlock::GSIBlock( BOOL PbPrintContext, BOOL bSource, BOOL bTrans, BOOL bRef, BOOL bAllowKID, BOOL bAllowSusp )
291 /*****************************************************************************/
292 : pSourceLine( NULL )
293 , pReferenceLine( NULL )
294 , bPrintContext( PbPrintContext )
295 , bCheckSourceLang( bSource )
296 , bCheckTranslationLang( bTrans )
297 , bReference( bRef )
298 , bAllowKeyIDs( bAllowKID )
299 , bAllowSuspicious( bAllowSusp )
300 , bHasBlockError( FALSE )
304 /*****************************************************************************/
305 GSIBlock::~GSIBlock()
306 /*****************************************************************************/
308 delete pSourceLine;
309 delete pReferenceLine;
311 for ( ULONG i = 0; i < Count(); i++ )
312 delete ( GetObject( i ));
315 /*****************************************************************************/
316 void GSIBlock::InsertLine( GSILine* pLine, ByteString aSourceLang)
317 /*****************************************************************************/
319 if ( pLine->GetLanguageId().Equals( aSourceLang ) )
321 if ( pSourceLine )
323 PrintError( "Source Language entry double. Treating as Translation.", "File format", "", pLine->GetLineNumber(), pLine->GetUniqId() );
324 bHasBlockError = TRUE;
325 pSourceLine->NotOK();
326 pLine->NotOK();
328 else
330 pSourceLine = pLine;
331 return;
334 ULONG nPos = 0;
336 if ( aSourceLang.Len() ) // only check blockstructure if source lang is given
338 while ( nPos < Count() )
340 if ( GetObject( nPos )->GetLanguageId().Equals( pLine->GetLanguageId() ) )
342 PrintError( "Translation Language entry double. Checking both.", "File format", "", pLine->GetLineNumber(), pLine->GetUniqId() );
343 bHasBlockError = TRUE;
344 GetObject( nPos )->NotOK();
345 pLine->NotOK();
347 nPos++;
350 Insert( pLine, LIST_APPEND );
353 /*****************************************************************************/
354 void GSIBlock::SetReferenceLine( GSILine* pLine )
355 /*****************************************************************************/
357 pReferenceLine = pLine;
360 /*****************************************************************************/
361 void GSIBlock::PrintMessage( ByteString aType, ByteString aMsg, ByteString aPrefix,
362 ByteString aContext, ULONG nLine, ByteString aUniqueId )
363 /*****************************************************************************/
365 ::PrintMessage( aType, aMsg, aPrefix, aContext, bPrintContext, nLine, aUniqueId );
368 /*****************************************************************************/
369 void GSIBlock::PrintError( ByteString aMsg, ByteString aPrefix,
370 ByteString aContext, ULONG nLine, ByteString aUniqueId )
371 /*****************************************************************************/
373 PrintMessage( "Error:", aMsg, aPrefix, aContext, nLine, aUniqueId );
376 /*****************************************************************************/
377 void GSIBlock::PrintList( ParserMessageList *pList, ByteString aPrefix,
378 GSILine *pLine )
379 /*****************************************************************************/
381 ULONG i;
382 for ( i = 0 ; i < pList->Count() ; i++ )
384 ParserMessage *pMsg = pList->GetObject( i );
385 ByteString aContext;
386 if ( bPrintContext )
388 if ( pMsg->GetTagBegin() == STRING_NOTFOUND )
389 aContext = pLine->GetText().Copy( 0, 300 );
390 else
391 aContext = pLine->Copy( pMsg->GetTagBegin()-150, 300 );
392 aContext.EraseTrailingChars(' ');
393 aContext.EraseLeadingChars(' ');
396 PrintMessage( pMsg->Prefix(), pMsg->GetErrorText(), aPrefix, aContext, pLine->GetLineNumber(), pLine->GetUniqId() );
400 /*****************************************************************************/
401 BOOL GSIBlock::IsUTF8( const ByteString &aTestee, BOOL bFixTags, USHORT &nErrorPos, ByteString &aErrorMsg, BOOL &bHasBeenFixed, ByteString &aFixed ) const
402 /*****************************************************************************/
404 String aUTF8Tester( aTestee, RTL_TEXTENCODING_UTF8 );
405 if ( STRING_MATCH != (nErrorPos = ByteString( aUTF8Tester, RTL_TEXTENCODING_UTF8 ).Match( aTestee )) )
407 aUTF8Tester = String( aTestee.GetBuffer(), nErrorPos, RTL_TEXTENCODING_UTF8 );
408 nErrorPos = aUTF8Tester.Len();
409 aErrorMsg = ByteString( "UTF8 Encoding seems to be broken" );
410 return FALSE;
413 nErrorPos = aUTF8Tester.SearchChar( String::CreateFromAscii( "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f"
414 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f" ).GetBuffer() );
415 if ( nErrorPos != STRING_NOTFOUND )
417 aErrorMsg = ByteString( "String contains illegal character" );
418 return FALSE;
421 if ( bFixTags )
423 bHasBeenFixed = FALSE;
424 aFixed.Erase();
427 if ( !bAllowKeyIDs )
429 BOOL bIsKeyID = FALSE;
430 BOOL bNewId = FALSE;
431 ByteString aID( aTestee );
432 USHORT nAfterID = 0;
434 if ( aTestee.Equals( "{&", 0, 2 ) )
435 { // check for strings from instset_native like "{&Tahoma8}335795.Installation Wiza ..."
436 USHORT nTagEnd = aTestee.Search( '}' );
437 if ( nTagEnd != STRING_NOTFOUND )
439 if ( bFixTags )
440 aFixed = aTestee.Copy( 0, nTagEnd+1 );
441 nErrorPos = nTagEnd+1;
442 aID = aTestee.Copy( nTagEnd+1 );
443 nAfterID = nTagEnd+1;
447 ByteString aDelimiter( (String)String( sal_Unicode(0x2016) ), RTL_TEXTENCODING_UTF8 );
449 if ( aID.Equals( aDelimiter, 6, aDelimiter.Len() ) )
450 { // New KeyId 6 Letters, digits and spechial chars followed by delimiter
451 bNewId = TRUE;
452 nErrorPos = 1;
453 aID = aID.Copy( 0, 6 );
454 nAfterID += 6;
455 nAfterID = nAfterID + aDelimiter.Len();
457 else if ( ( aID.GetChar(6) == '*' ) && aID.Equals( aDelimiter, 7, aDelimiter.Len() ) )
458 { // New KeyId 6 Letters, digits and spechial chars followed by '*delimiter' to indicate translation in progress
459 bNewId = TRUE;
460 nErrorPos = 1;
461 aID = aID.Copy( 0, 6 );
462 nAfterID += 7;
463 nAfterID = nAfterID + aDelimiter.Len();
465 else if ( aID.GetTokenCount( '.' ) > 1 )
466 { // test for old KeyIDs 5 to 6 digits followed by a dot '44373.'
467 bNewId = FALSE;
468 nErrorPos = 1;
469 aID = aID.GetToken( 0, '.' );
470 nAfterID = nAfterID + aID.Len();
472 else
474 aID.Erase();
477 if ( bNewId )
479 if ( aID.Len() == 6 )
481 bIsKeyID = TRUE;
482 ByteString aDigits("0123456789abcdefghijklmnopqrstuvwxyz+-<=>");
483 for ( USHORT i=0 ; i < aID.Len() ;i++ )
485 if ( aDigits.Search( aID.GetChar(i) ) == STRING_NOTFOUND )
486 bIsKeyID = FALSE;
490 else
492 if ( aID.Len() > 0 && aID.GetChar(aID.Len()-1) == '*' )
493 aID.Erase( aID.Len()-1 );
495 if ( aID.IsNumericAscii() && aID.Len() >= 5 )
496 bIsKeyID = TRUE;
499 if ( bIsKeyID )
501 aErrorMsg = ByteString( "String contains KeyID" );
502 if ( bFixTags )
504 aFixed += aTestee.Copy( nAfterID );
505 bHasBeenFixed = TRUE;
506 aErrorMsg = ByteString( "FIXED String containing KeyID" );
508 else
509 aErrorMsg = ByteString( "String contains KeyID" );
510 return FALSE;
514 return TRUE;
517 /*****************************************************************************/
518 BOOL GSIBlock::TestUTF8( GSILine* pTestee, BOOL bFixTags )
519 /*****************************************************************************/
521 USHORT nErrorPos = 0;
522 ByteString aErrorMsg;
523 BOOL bError = FALSE;
524 ByteString aFixed;
525 BOOL bHasBeenFixed = FALSE;
526 if ( !IsUTF8( pTestee->GetText(), bFixTags, nErrorPos, aErrorMsg, bHasBeenFixed, aFixed ) )
528 ByteString aContext( pTestee->GetText().Copy( nErrorPos, 20 ) );
529 PrintError( aErrorMsg.Append(" in Text at Position " ).Append( ByteString::CreateFromInt32( nErrorPos ) ), "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId() );
530 bError = TRUE;
531 if ( bHasBeenFixed )
533 pTestee->SetText( aFixed );
534 pTestee->SetFixed();
537 if ( !IsUTF8( pTestee->GetQuickHelpText(), bFixTags, nErrorPos, aErrorMsg, bHasBeenFixed, aFixed ) )
539 ByteString aContext( pTestee->GetQuickHelpText().Copy( nErrorPos, 20 ) );
540 PrintError( aErrorMsg.Append(" in QuickHelpText at Position " ).Append( ByteString::CreateFromInt32( nErrorPos ) ), "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId() );
541 bError = TRUE;
542 if ( bHasBeenFixed )
544 pTestee->SetQuickHelpText( aFixed );
545 pTestee->SetFixed();
548 if ( !IsUTF8( pTestee->GetTitle(), bFixTags, nErrorPos, aErrorMsg, bHasBeenFixed, aFixed ) )
550 ByteString aContext( pTestee->GetTitle().Copy( nErrorPos, 20 ) );
551 PrintError( aErrorMsg.Append(" in Title at Position " ).Append( ByteString::CreateFromInt32( nErrorPos ) ), "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId() );
552 bError = TRUE;
553 if ( bHasBeenFixed )
555 pTestee->SetTitle( aFixed );
556 pTestee->SetFixed();
559 if ( bError )
560 pTestee->NotOK();
561 return !bError;
565 /*****************************************************************************/
566 BOOL GSIBlock::HasSuspiciousChars( GSILine* pTestee, GSILine* pSource )
567 /*****************************************************************************/
569 USHORT nPos = 0;
570 if ( !bAllowSuspicious && ( nPos = pTestee->GetText().Search("??")) != STRING_NOTFOUND )
571 if ( pSource->GetText().Search("??") == STRING_NOTFOUND )
573 String aUTF8Tester = String( pTestee->GetText(), 0, nPos, RTL_TEXTENCODING_UTF8 );
574 USHORT nErrorPos = aUTF8Tester.Len();
575 ByteString aContext( pTestee->GetText().Copy( nPos, 20 ) );
576 PrintError( ByteString("Found double questionmark in translation only. Looks like an encoding problem at Position " ).Append( ByteString::CreateFromInt32( nErrorPos ) ), "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId() );
577 pTestee->NotOK();
578 return TRUE;
581 return FALSE;
585 /*****************************************************************************/
586 BOOL GSIBlock::CheckSyntax( ULONG nLine, BOOL bRequireSourceLine, BOOL bFixTags )
587 /*****************************************************************************/
589 static LingTest aTester;
590 BOOL bHasError = FALSE;
592 if ( !pSourceLine )
594 if ( bRequireSourceLine )
596 PrintError( "No source language entry defined!", "File format", "", nLine );
597 bHasBlockError = TRUE;
600 else
602 aTester.CheckReference( pSourceLine );
603 if ( pSourceLine->HasMessages() )
605 PrintList( pSourceLine->GetMessageList(), "ReferenceString", pSourceLine );
606 pSourceLine->NotOK();
607 bHasError = TRUE;
610 if ( bReference )
612 if ( !pReferenceLine )
614 GSILine *pSource;
615 if ( pSourceLine )
616 pSource = pSourceLine;
617 else
618 pSource = GetObject( 0 ); // get some other line
619 if ( pSource )
620 PrintError( "No reference line found. Entry is new in source file", "File format", "", pSource->GetLineNumber(), pSource->GetUniqId() );
621 else
622 PrintError( "No reference line found. Entry is new in source file", "File format", "", nLine );
623 bHasBlockError = TRUE;
625 else
627 if ( pSourceLine && !pSourceLine->Equals( *pReferenceLine ) )
629 xub_StrLen nPos = pSourceLine->Match( *pReferenceLine );
630 ByteString aContext( pReferenceLine->Copy( nPos - 5, 15) );
631 aContext.Append( "\" --> \"" ).Append( pSourceLine->Copy( nPos - 5, 15) );
632 PrintError( "Source Language Entry has changed.", "File format", aContext, pSourceLine->GetLineNumber(), pSourceLine->GetUniqId() );
633 pSourceLine->NotOK();
634 bHasError = TRUE;
639 if ( pSourceLine )
640 bHasError |= !TestUTF8( pSourceLine, bFixTags );
642 ULONG i;
643 for ( i = 0; i < Count(); i++ )
645 aTester.CheckTestee( GetObject( i ), pSourceLine != NULL, bFixTags );
646 if ( GetObject( i )->HasMessages() || aTester.HasCompareWarnings() )
648 if ( GetObject( i )->HasMessages() || aTester.GetCompareWarnings().HasErrors() )
649 GetObject( i )->NotOK();
650 bHasError = TRUE;
651 PrintList( GetObject( i )->GetMessageList(), "Translation", GetObject( i ) );
652 PrintList( &(aTester.GetCompareWarnings()), "Translation Tag Missmatch", GetObject( i ) );
654 bHasError |= !TestUTF8( GetObject( i ), bFixTags );
655 if ( pSourceLine )
656 bHasError |= HasSuspiciousChars( GetObject( i ), pSourceLine );
659 return bHasError || bHasBlockError;
662 void GSIBlock::WriteError( LazySvFileStream &aErrOut, BOOL bRequireSourceLine )
664 if ( pSourceLine && pSourceLine->IsOK() && bCheckSourceLang && !bHasBlockError )
665 return;
667 BOOL bHasError = FALSE;
668 BOOL bCopyAll = ( !pSourceLine && bRequireSourceLine ) || ( pSourceLine && !pSourceLine->IsOK() && !bCheckTranslationLang ) || bHasBlockError;
669 ULONG i;
670 for ( i = 0; i < Count(); i++ )
672 if ( !GetObject( i )->IsOK() || bCopyAll )
674 bHasError = TRUE;
675 aErrOut.LazyOpen();
676 aErrOut.WriteLine( *GetObject( i ) );
680 if ( pSourceLine && ( bHasError || !pSourceLine->IsOK() ) && !( !bHasError && bCheckTranslationLang ) )
682 aErrOut.LazyOpen();
683 aErrOut.WriteLine( *pSourceLine );
687 void GSIBlock::WriteCorrect( LazySvFileStream &aOkOut, BOOL bRequireSourceLine )
689 if ( ( !pSourceLine && bRequireSourceLine ) || ( pSourceLine && !pSourceLine->IsOK() && !bCheckTranslationLang ) )
690 return;
692 BOOL bHasOK = FALSE;
693 ULONG i;
694 for ( i = 0; i < Count(); i++ )
696 if ( ( GetObject( i )->IsOK() || bCheckSourceLang ) && !bHasBlockError )
698 bHasOK = TRUE;
699 aOkOut.LazyOpen();
700 aOkOut.WriteLine( *GetObject( i ) );
704 if ( ( pSourceLine && pSourceLine->IsOK() && ( Count() || !bCheckTranslationLang ) ) || ( bHasOK && bCheckTranslationLang ) )
706 aOkOut.LazyOpen();
707 aOkOut.WriteLine( *pSourceLine );
711 void GSIBlock::WriteFixed( LazySvFileStream &aFixOut, BOOL /*bRequireSourceLine*/ )
713 if ( pSourceLine && !pSourceLine->IsFixed() && bCheckSourceLang )
714 return;
716 BOOL bHasFixes = FALSE;
717 ULONG i;
718 for ( i = 0; i < Count(); i++ )
720 if ( GetObject( i )->IsFixed() )
722 bHasFixes = TRUE;
723 aFixOut.LazyOpen();
724 aFixOut.WriteLine( *GetObject( i ) );
728 if ( pSourceLine && ( bHasFixes || pSourceLine->IsFixed() ) )
730 aFixOut.LazyOpen();
731 aFixOut.WriteLine( *pSourceLine );
736 /*****************************************************************************/
737 /*****************************************************************************/
738 /*****************************************************************************/
739 /*****************************************************************************/
740 /*****************************************************************************/
741 /*****************************************************************************/
742 /*****************************************************************************/
744 /*****************************************************************************/
745 void Help()
746 /*****************************************************************************/
748 fprintf( stdout, "\n" );
749 fprintf( stdout, "gsicheck Version 1.9.0 (c)1999 - 2006 by SUN Microsystems\n" );
750 fprintf( stdout, "=========================================================\n" );
751 fprintf( stdout, "\n" );
752 fprintf( stdout, "gsicheck checks the syntax of tags in GSI-Files and SDF-Files\n" );
753 fprintf( stdout, " checks for inconsistencies and malicious UTF8 encoding\n" );
754 fprintf( stdout, " checks tags in Online Help\n" );
755 fprintf( stdout, " checks for *new* KeyIDs and relax GID/LID length to %s\n", ByteString::CreateFromInt32(MAX_GID_LID_LEN).GetBuffer() );
756 fprintf( stdout, "\n" );
757 fprintf( stdout, "Syntax: gsicheck [ -c ] [-f] [ -we ] [ -wef ErrorFilename ] [ -wc ]\n" );
758 fprintf( stdout, " [ -wcf CorrectFilename ] [ -s | -t ] [ -l LanguageID ]\n" );
759 fprintf( stdout, " [ -r ReferenceFile ] filename\n" );
760 fprintf( stdout, "\n" );
761 fprintf( stdout, "-c Add context to error message (Print the line containing the error)\n" );
762 fprintf( stdout, "-f try to fix errors. See also -wf -wff \n" );
763 fprintf( stdout, "-wf Write File containing all fixed parts\n" );
764 fprintf( stdout, "-wff Same as above but give own filename\n" );
765 fprintf( stdout, "-we Write File containing all errors\n" );
766 fprintf( stdout, "-wef Same as above but give own filename\n" );
767 fprintf( stdout, "-wc Write File containing all correct parts\n" );
768 fprintf( stdout, "-wcf Same as above but give own filename\n" );
769 fprintf( stdout, "-s Check only source language. Should be used before handing out to vendor.\n" );
770 fprintf( stdout, "-t Check only Translation language(s). Should be used before merging.\n" );
771 fprintf( stdout, "-k Allow KeyIDs to be present in strings\n" );
772 fprintf( stdout, "-e disable encoding checks. E.g.: double questionmark \'??\' which may be the\n" );
773 fprintf( stdout, " result of false conversions\n" );
774 fprintf( stdout, "-l ISO Languagecode or numerical 2 digits Identifier of the source language.\n" );
775 fprintf( stdout, " Default is en-US. Use \"\" (empty string) or 'none'\n" );
776 fprintf( stdout, " to disable source language dependent checks\n" );
777 fprintf( stdout, "-r Reference filename to check that source language entries\n" );
778 fprintf( stdout, " have not been changed\n" );
779 fprintf( stdout, "\n" );
782 /*****************************************************************************/
783 #if defined(UNX) || defined(OS2)
784 int main( int argc, char *argv[] )
785 #else
786 int _cdecl main( int argc, char *argv[] )
787 #endif
788 /*****************************************************************************/
791 BOOL bError = FALSE;
792 BOOL bPrintContext = FALSE;
793 BOOL bCheckSourceLang = FALSE;
794 BOOL bCheckTranslationLang = FALSE;
795 BOOL bWriteError = FALSE;
796 BOOL bWriteCorrect = FALSE;
797 BOOL bWriteFixed = FALSE;
798 BOOL bFixTags = FALSE;
799 BOOL bAllowKID = FALSE;
800 BOOL bAllowSuspicious = FALSE;
801 String aErrorFilename;
802 String aCorrectFilename;
803 String aFixedFilename;
804 BOOL bFileHasError = FALSE;
805 ByteString aSourceLang( "en-US" ); // English is default
806 ByteString aFilename;
807 ByteString aReferenceFilename;
808 BOOL bReferenceFile = FALSE;
809 for ( USHORT i = 1 ; i < argc ; i++ )
811 if ( *argv[ i ] == '-' )
813 switch (*(argv[ i ]+1))
815 case 'c':bPrintContext = TRUE;
816 break;
817 case 'w':
819 if ( (*(argv[ i ]+2)) == 'e' )
821 if ( (*(argv[ i ]+3)) == 'f' )
822 if ( (i+1) < argc )
824 aErrorFilename = String( argv[ i+1 ], RTL_TEXTENCODING_ASCII_US );
825 bWriteError = TRUE;
826 i++;
828 else
830 fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
831 bError = TRUE;
833 else
834 bWriteError = TRUE;
836 else if ( (*(argv[ i ]+2)) == 'c' )
837 if ( (*(argv[ i ]+3)) == 'f' )
838 if ( (i+1) < argc )
840 aCorrectFilename = String( argv[ i+1 ], RTL_TEXTENCODING_ASCII_US );
841 bWriteCorrect = TRUE;
842 i++;
844 else
846 fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
847 bError = TRUE;
849 else
850 bWriteCorrect = TRUE;
851 else if ( (*(argv[ i ]+2)) == 'f' )
852 if ( (*(argv[ i ]+3)) == 'f' )
853 if ( (i+1) < argc )
855 aFixedFilename = String( argv[ i+1 ], RTL_TEXTENCODING_ASCII_US );
856 bWriteFixed = TRUE;
857 bFixTags = TRUE;
858 i++;
860 else
862 fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
863 bError = TRUE;
865 else
867 bWriteFixed = TRUE;
868 bFixTags = TRUE;
870 else
872 fprintf( stderr, "\nERROR: Unknown Switch %s!\n\n", argv[ i ] );
873 bError = TRUE;
876 break;
877 case 's':bCheckSourceLang = TRUE;
878 break;
879 case 't':bCheckTranslationLang = TRUE;
880 break;
881 case 'l':
883 if ( (i+1) < argc )
885 aSourceLang = ByteString( argv[ i+1 ] );
886 if ( aSourceLang.EqualsIgnoreCaseAscii( "none" ) )
887 aSourceLang.Erase();
888 i++;
890 else
892 fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
893 bError = TRUE;
896 break;
897 case 'r':
899 if ( (i+1) < argc )
901 aReferenceFilename = argv[ i+1 ];
902 bReferenceFile = TRUE;
903 i++;
905 else
907 fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
908 bError = TRUE;
911 break;
912 case 'f':
914 bFixTags = TRUE;
916 break;
917 case 'k':
919 bAllowKID = TRUE;
921 break;
922 case 'e':
924 bAllowSuspicious = TRUE;
926 break;
927 default:
928 fprintf( stderr, "\nERROR: Unknown Switch %s!\n\n", argv[ i ] );
929 bError = TRUE;
932 else
934 if ( !aFilename.Len())
935 aFilename = ByteString( argv[ i ] );
936 else
938 fprintf( stderr, "\nERROR: Only one filename may be specified!\n\n");
939 bError = TRUE;
945 if ( !aFilename.Len() || bError )
947 Help();
948 exit ( 0 );
951 if ( aSourceLang.Len() && !LanguageOK( aSourceLang ) )
953 fprintf( stderr, "\nERROR: The Language '%s' is invalid!\n\n", aSourceLang.GetBuffer() );
954 Help();
955 exit ( 1 );
958 if ( bCheckSourceLang && bCheckTranslationLang )
960 fprintf( stderr, "\nERROR: The Options -s and -t are mutually exclusive.\nUse only one of them.\n\n" );
961 Help();
962 exit ( 1 );
967 DirEntry aSource = DirEntry( String( aFilename, RTL_TEXTENCODING_ASCII_US ));
968 if ( !aSource.Exists()) {
969 fprintf( stderr, "\nERROR: GSI-File %s not found!\n\n", aFilename.GetBuffer() );
970 exit ( 2 );
973 SvFileStream aGSI( String( aFilename, RTL_TEXTENCODING_ASCII_US ), STREAM_STD_READ );
974 if ( !aGSI.IsOpen()) {
975 fprintf( stderr, "\nERROR: Could not open GSI-File %s!\n\n", aFilename.GetBuffer() );
976 exit ( 3 );
979 SvFileStream aReferenceGSI;
980 if ( bReferenceFile )
982 DirEntry aReferenceSource = DirEntry( String( aReferenceFilename, RTL_TEXTENCODING_ASCII_US ));
983 if ( !aReferenceSource.Exists()) {
984 fprintf( stderr, "\nERROR: GSI-File %s not found!\n\n", aFilename.GetBuffer() );
985 exit ( 2 );
988 aReferenceGSI.Open( String( aReferenceFilename, RTL_TEXTENCODING_ASCII_US ), STREAM_STD_READ );
989 if ( !aReferenceGSI.IsOpen()) {
990 fprintf( stderr, "\nERROR: Could not open Input-File %s!\n\n", aFilename.GetBuffer() );
991 exit ( 3 );
995 LazySvFileStream aOkOut;
996 String aBaseName = aSource.GetBase();
997 if ( bWriteCorrect )
999 if ( !aCorrectFilename.Len() )
1001 String sTmpBase( aBaseName );
1002 sTmpBase += String( "_ok", RTL_TEXTENCODING_ASCII_US );
1003 aSource.SetBase( sTmpBase );
1004 aCorrectFilename = aSource.GetFull();
1006 aOkOut.SetOpenParams( aCorrectFilename , STREAM_STD_WRITE | STREAM_TRUNC );
1009 LazySvFileStream aErrOut;
1010 if ( bWriteError )
1012 if ( !aErrorFilename.Len() )
1014 String sTmpBase( aBaseName );
1015 sTmpBase += String( "_err", RTL_TEXTENCODING_ASCII_US );
1016 aSource.SetBase( sTmpBase );
1017 aErrorFilename = aSource.GetFull();
1019 aErrOut.SetOpenParams( aErrorFilename , STREAM_STD_WRITE | STREAM_TRUNC );
1022 LazySvFileStream aFixOut;
1023 if ( bWriteFixed )
1025 if ( !aFixedFilename.Len() )
1027 String sTmpBase( aBaseName );
1028 sTmpBase += String( "_fix", RTL_TEXTENCODING_ASCII_US );
1029 aSource.SetBase( sTmpBase );
1030 aFixedFilename = aSource.GetFull();
1032 aFixOut.SetOpenParams( aFixedFilename , STREAM_STD_WRITE | STREAM_TRUNC );
1036 ByteString sReferenceLine;
1037 GSILine* pReferenceLine = NULL;
1038 ByteString aOldReferenceId("No Valid ID"); // just set to something which can never be an ID
1039 ULONG nReferenceLine = 0;
1041 ByteString sGSILine;
1042 GSILine* pGSILine = NULL;
1043 ByteString aOldId("No Valid ID"); // just set to something which can never be an ID
1044 GSIBlock *pBlock = NULL;
1045 ULONG nLine = 0;
1047 while ( !aGSI.IsEof() )
1049 aGSI.ReadLine( sGSILine );
1050 nLine++;
1051 pGSILine = new GSILine( sGSILine, nLine );
1052 BOOL bDelete = TRUE;
1055 if ( pGSILine->Len() )
1057 if ( FORMAT_UNKNOWN == pGSILine->GetLineFormat() )
1059 PrintError( "Format of line is unknown. Ignoring!", "Line format", pGSILine->Copy( 0,40 ), bPrintContext, pGSILine->GetLineNumber() );
1060 pGSILine->NotOK();
1061 if ( bWriteError )
1063 bFileHasError = TRUE;
1064 aErrOut.LazyOpen();
1065 aErrOut.WriteLine( *pGSILine );
1068 else if ( pGSILine->GetLineType().EqualsIgnoreCaseAscii("res-comment") )
1069 { // ignore comment lines, but write them to Correct Items File
1070 if ( bWriteCorrect )
1072 aOkOut.LazyOpen();
1073 aOkOut.WriteLine( *pGSILine );
1076 else
1078 ByteString aId = pGSILine->GetUniqId();
1079 if ( aId != aOldId )
1081 if ( pBlock )
1083 bFileHasError |= pBlock->CheckSyntax( nLine, aSourceLang.Len() != 0, bFixTags );
1085 if ( bWriteError )
1086 pBlock->WriteError( aErrOut, aSourceLang.Len() != 0 );
1087 if ( bWriteCorrect )
1088 pBlock->WriteCorrect( aOkOut, aSourceLang.Len() != 0 );
1089 if ( bWriteFixed )
1090 pBlock->WriteFixed( aFixOut, aSourceLang.Len() != 0 );
1092 delete pBlock;
1094 pBlock = new GSIBlock( bPrintContext, bCheckSourceLang, bCheckTranslationLang, bReferenceFile, bAllowKID, bAllowSuspicious );
1096 aOldId = aId;
1099 // find corresponding line in reference file
1100 if ( bReferenceFile )
1102 BOOL bContinueSearching = TRUE;
1103 while ( ( !aReferenceGSI.IsEof() || pReferenceLine ) && bContinueSearching )
1105 if ( !pReferenceLine )
1107 aReferenceGSI.ReadLine( sReferenceLine );
1108 nReferenceLine++;
1109 pReferenceLine = new GSILine( sReferenceLine, nReferenceLine );
1111 if ( pReferenceLine->GetLineFormat() != FORMAT_UNKNOWN )
1113 if ( pReferenceLine->GetUniqId() == aId && pReferenceLine->GetLanguageId().Equals( aSourceLang ) )
1115 pBlock->SetReferenceLine( pReferenceLine );
1116 pReferenceLine = NULL;
1118 else if ( pReferenceLine->GetUniqId() > aId )
1120 // if ( pGSILine->GetLanguageId() == aSourceLang )
1121 // PrintError( "No reference line found. Entry is new in source file", "File format", "", bPrintContext, pGSILine->GetLineNumber(), aId );
1122 bContinueSearching = FALSE;
1124 else
1126 if ( pReferenceLine->GetUniqId() < aId && pReferenceLine->GetLanguageId().Equals( aSourceLang ) )
1127 PrintError( "No Entry in source file found. Entry has been removed from source file", "File format", "", bPrintContext, pGSILine->GetLineNumber(), pReferenceLine->GetUniqId() );
1128 delete pReferenceLine;
1129 pReferenceLine = NULL;
1132 else
1134 delete pReferenceLine;
1135 pReferenceLine = NULL;
1143 pBlock->InsertLine( pGSILine, aSourceLang );
1144 bDelete = FALSE;
1147 if ( bDelete )
1148 delete pGSILine;
1151 if ( pBlock )
1153 bFileHasError |= pBlock->CheckSyntax( nLine, aSourceLang.Len() != 0, bFixTags );
1155 if ( bWriteError )
1156 pBlock->WriteError( aErrOut, aSourceLang.Len() != 0 );
1157 if ( bWriteCorrect )
1158 pBlock->WriteCorrect( aOkOut, aSourceLang.Len() != 0 );
1159 if ( bWriteFixed )
1160 pBlock->WriteFixed( aFixOut, aSourceLang.Len() != 0 );
1162 delete pBlock;
1164 aGSI.Close();
1166 if ( bWriteError )
1167 aErrOut.Close();
1168 if ( bWriteCorrect )
1169 aOkOut.Close();
1170 if ( bWriteFixed )
1171 aFixOut.Close();
1173 if ( bFileHasError )
1174 return 55;
1175 else
1176 return 0;