blob: d60d5974a969a9e7187e5d46e34ef95a58045950 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_l10ntools.hxx"
#include <stdio.h>
#include <tools/fsys.hxx>
#include <tools/stream.hxx>
#include <tools/list.hxx>
// local includes
#include "tagtest.hxx"
#include "gsicheck.hxx"
#define MAX_GID_LID_LEN 250
/*****************************************************************************/
void PrintMessage( ByteString aType, ByteString aMsg, ByteString aPrefix,
ByteString aContext, sal_Bool bPrintContext, sal_uLong nLine, ByteString aUniqueId = ByteString() )
/*****************************************************************************/
{
fprintf( stdout, "%s %s, Line %lu", aType.GetBuffer(), aPrefix.GetBuffer(), nLine );
if ( aUniqueId.Len() )
fprintf( stdout, ", UniqueID %s", aUniqueId.GetBuffer() );
fprintf( stdout, ": %s", aMsg.GetBuffer() );
if ( bPrintContext )
fprintf( stdout, " \"%s\"", aContext.GetBuffer() );
fprintf( stdout, "\n" );
}
/*****************************************************************************/
void PrintError( ByteString aMsg, ByteString aPrefix,
ByteString aContext, sal_Bool bPrintContext, sal_uLong nLine, ByteString aUniqueId = ByteString() )
/*****************************************************************************/
{
PrintMessage( "Error:", aMsg, aPrefix, aContext, bPrintContext, nLine, aUniqueId );
}
sal_Bool LanguageOK( ByteString aLang )
{
if ( !aLang.Len() )
return sal_False;
if ( aLang.IsNumericAscii() )
return sal_True;
if ( aLang.GetTokenCount( '-' ) == 1 )
return aLang.IsAlphaAscii() && aLang.IsLowerAscii();
else if ( aLang.GetTokenCount( '-' ) == 2 )
{
ByteString aTok0( aLang.GetToken( 0, '-' ) );
ByteString aTok1( aLang.GetToken( 1, '-' ) );
return aTok0.Len() && aTok0.IsAlphaAscii() && aTok0.IsLowerAscii()
&& aTok1.Len() && aTok1.IsAlphaAscii() && aTok1.IsUpperAscii()
&& !aTok1.EqualsIgnoreCaseAscii( aTok0 );
}
return sal_False;
}
//
// class LazySvFileStream
//
class LazySvFileStream : public SvFileStream
{
private:
String aFileName;
sal_Bool bOpened;
StreamMode eOpenMode;
public:
LazySvFileStream()
: aFileName()
, bOpened( sal_False )
, eOpenMode( 0 )
{};
void SetOpenParams( const String& rFileName, StreamMode eOpenModeP )
{
aFileName = rFileName;
eOpenMode = eOpenModeP;
};
void LazyOpen();
};
void LazySvFileStream::LazyOpen()
{
if ( !bOpened )
{
Open( aFileName, eOpenMode );
if ( !IsOpen())
{
fprintf( stderr, "\nERROR: Could not open Output-File %s!\n\n", ByteString( aFileName, RTL_TEXTENCODING_ASCII_US ).GetBuffer() );
exit ( 4 );
}
bOpened = sal_True;
}
}
//
// class GSILine
//
/*****************************************************************************/
GSILine::GSILine( const ByteString &rLine, sal_uLong nLine )
/*****************************************************************************/
: ByteString( rLine )
, nLineNumber( nLine )
, bOK( sal_True )
, bFixed ( sal_False )
{
if ( rLine.GetTokenCount( '\t' ) == 15 )
{
aFormat = FORMAT_SDF;
aUniqId = rLine.GetToken( 0, '\t' );
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' ) );
aLineType = "";
aLangId = rLine.GetToken( 9, '\t' );
aText = rLine.GetToken( 10, '\t' );
aQuickHelpText = rLine.GetToken( 12, '\t' );
aTitle = rLine.GetToken( 13, '\t' );
// do some more format checks here
if ( !rLine.GetToken( 8, '\t' ).IsNumericAscii() )
{
PrintError( "The length field does not contain a number!", "Line format", rLine.GetToken( 8, '\t' ), sal_True, GetLineNumber(), GetUniqId() );
NotOK();
}
if ( !LanguageOK( aLangId ) )
{
PrintError( "The Language is invalid!", "Line format", aLangId, sal_True, GetLineNumber(), GetUniqId() );
NotOK();
}
// limit GID and LID to MAX_GID_LID_LEN chars each for database conformity, see #137575#
if ( rLine.GetToken( 4, '\t' ).Len() > MAX_GID_LID_LEN || rLine.GetToken( 5, '\t' ).Len() > MAX_GID_LID_LEN )
{
PrintError( ByteString("GID and LID may only be ").Append( ByteString::CreateFromInt32(MAX_GID_LID_LEN) ).Append( " chars long each!" ), "Line format", aLangId, sal_True, GetLineNumber(), GetUniqId() );
NotOK();
}
}
else // allow tabs in gsi files
{
aFormat = FORMAT_GSI;
ByteString sTmp( rLine );
sal_uInt16 nPos = sTmp.Search( "($$)" );
sal_uInt16 nStart = 0;
if ( nPos != STRING_NOTFOUND )
{
aUniqId = sTmp.Copy( nStart, nPos - nStart );
nStart = nPos + 4; // + length of the delemiter
nPos = sTmp.Search( "($$)", nStart );
}
if ( nPos != STRING_NOTFOUND )
{
aLineType = sTmp.Copy( nStart, nPos - nStart );
nStart = nPos + 4; // + length of the delemiter
nPos = sTmp.Search( "($$)", nStart );
aUniqId.Append( "/" );
aUniqId.Append( aLineType );
}
if ( nPos != STRING_NOTFOUND )
{
aLangId = sTmp.Copy( nStart, nPos - nStart );
nStart = nPos + 4; // + length of the delemiter
nPos = sTmp.Search( "($$)", nStart );
}
if ( nPos != STRING_NOTFOUND )
{
// ByteString aStatus = sTmp.Copy( nStart, nPos - nStart ); // ext int ...
nStart = nPos + 4; // + length of the delemiter
}
if ( nPos != STRING_NOTFOUND )
aText = sTmp.Copy( nStart );
else
aFormat = FORMAT_UNKNOWN;
}
if ( FORMAT_UNKNOWN == GetLineFormat() )
NotOK();
}
/*****************************************************************************/
void GSILine::NotOK()
/*****************************************************************************/
{
bOK = sal_False;
}
/*****************************************************************************/
void GSILine::ReassembleLine()
/*****************************************************************************/
{
ByteString aReassemble;
if ( GetLineFormat() == FORMAT_SDF )
{
sal_uInt16 i;
for ( i = 0 ; i < 10 ; i++ )
{
aReassemble.Append( GetToken( i, '\t' ) );
aReassemble.Append( "\t" );
}
aReassemble.Append( aText );
aReassemble.Append( "\t" );
aReassemble.Append( GetToken( 11, '\t' ) ); // should be empty but there are some places in sc. Not reflected to sources!!
aReassemble.Append( "\t" );
aReassemble.Append( aQuickHelpText );
aReassemble.Append( "\t" );
aReassemble.Append( aTitle );
for ( i = 14 ; i < 15 ; i++ )
{
aReassemble.Append( "\t" );
aReassemble.Append( GetToken( i, '\t' ) );
}
*(ByteString*)this = aReassemble;
}
else if ( GetLineFormat() == FORMAT_GSI )
{
sal_uInt16 nPos = Search( "($$)" );
sal_uInt16 nStart = 0;
if ( nPos != STRING_NOTFOUND )
{
nStart = nPos + 4; // + length of the delemiter
nPos = Search( "($$)", nStart );
}
if ( nPos != STRING_NOTFOUND )
{
nStart = nPos + 4; // + length of the delemiter
nPos = Search( "($$)", nStart );
}
if ( nPos != STRING_NOTFOUND )
{
nStart = nPos + 4; // + length of the delemiter
nPos = Search( "($$)", nStart );
}
if ( nPos != STRING_NOTFOUND )
{
nStart = nPos + 4; // + length of the delemiter
}
if ( nPos != STRING_NOTFOUND )
{
aReassemble = Copy( 0, nStart );
aReassemble += aText;
*(ByteString*)this = aReassemble;
}
else
PrintError( "Cannot reassemble GSI line (internal Error).", "Line format", "", sal_False, GetLineNumber(), GetUniqId() );
}
else
PrintError( "Cannot reassemble line of unknown type (internal Error).", "Line format", "", sal_False, GetLineNumber(), GetUniqId() );
}
//
// class GSIBlock
//
/*****************************************************************************/
GSIBlock::GSIBlock( sal_Bool PbPrintContext, sal_Bool bSource, sal_Bool bTrans, sal_Bool bRef, sal_Bool bAllowKID, sal_Bool bAllowSusp )
/*****************************************************************************/
: pSourceLine( NULL )
, pReferenceLine( NULL )
, bPrintContext( PbPrintContext )
, bCheckSourceLang( bSource )
, bCheckTranslationLang( bTrans )
, bReference( bRef )
, bAllowKeyIDs( bAllowKID )
, bAllowSuspicious( bAllowSusp )
, bHasBlockError( sal_False )
{
}
/*****************************************************************************/
GSIBlock::~GSIBlock()
/*****************************************************************************/
{
delete pSourceLine;
delete pReferenceLine;
for ( sal_uLong i = 0; i < Count(); i++ )
delete ( GetObject( i ));
}
/*****************************************************************************/
void GSIBlock::InsertLine( GSILine* pLine, ByteString aSourceLang)
/*****************************************************************************/
{
if ( pLine->GetLanguageId().Equals( aSourceLang ) )
{
if ( pSourceLine )
{
PrintError( "Source Language entry double. Treating as Translation.", "File format", "", pLine->GetLineNumber(), pLine->GetUniqId() );
bHasBlockError = sal_True;
pSourceLine->NotOK();
pLine->NotOK();
}
else
{
pSourceLine = pLine;
return;
}
}
sal_uLong nPos = 0;
if ( aSourceLang.Len() ) // only check blockstructure if source lang is given
{
while ( nPos < Count() )
{
if ( GetObject( nPos )->GetLanguageId().Equals( pLine->GetLanguageId() ) )
{
PrintError( "Translation Language entry double. Checking both.", "File format", "", pLine->GetLineNumber(), pLine->GetUniqId() );
bHasBlockError = sal_True;
GetObject( nPos )->NotOK();
pLine->NotOK();
}
nPos++;
}
}
Insert( pLine, LIST_APPEND );
}
/*****************************************************************************/
void GSIBlock::SetReferenceLine( GSILine* pLine )
/*****************************************************************************/
{
pReferenceLine = pLine;
}
/*****************************************************************************/
void GSIBlock::PrintMessage( ByteString aType, ByteString aMsg, ByteString aPrefix,
ByteString aContext, sal_uLong nLine, ByteString aUniqueId )
/*****************************************************************************/
{
::PrintMessage( aType, aMsg, aPrefix, aContext, bPrintContext, nLine, aUniqueId );
}
/*****************************************************************************/
void GSIBlock::PrintError( ByteString aMsg, ByteString aPrefix,
ByteString aContext, sal_uLong nLine, ByteString aUniqueId )
/*****************************************************************************/
{
PrintMessage( "Error:", aMsg, aPrefix, aContext, nLine, aUniqueId );
}
/*****************************************************************************/
void GSIBlock::PrintList( ParserMessageList *pList, ByteString aPrefix,
GSILine *pLine )
/*****************************************************************************/
{
sal_uLong i;
for ( i = 0 ; i < pList->Count() ; i++ )
{
ParserMessage *pMsg = pList->GetObject( i );
ByteString aContext;
if ( bPrintContext )
{
if ( pMsg->GetTagBegin() == STRING_NOTFOUND )
aContext = pLine->GetText().Copy( 0, 300 );
else
aContext = pLine->Copy( pMsg->GetTagBegin()-150, 300 );
aContext.EraseTrailingChars(' ');
aContext.EraseLeadingChars(' ');
}
PrintMessage( pMsg->Prefix(), pMsg->GetErrorText(), aPrefix, aContext, pLine->GetLineNumber(), pLine->GetUniqId() );
}
}
/*****************************************************************************/
sal_Bool GSIBlock::IsUTF8( const ByteString &aTestee, sal_Bool bFixTags, sal_uInt16 &nErrorPos, ByteString &aErrorMsg, sal_Bool &bHasBeenFixed, ByteString &aFixed ) const
/*****************************************************************************/
{
String aUTF8Tester( aTestee, RTL_TEXTENCODING_UTF8 );
if ( STRING_MATCH != (nErrorPos = ByteString( aUTF8Tester, RTL_TEXTENCODING_UTF8 ).Match( aTestee )) )
{
aUTF8Tester = String( aTestee.GetBuffer(), nErrorPos, RTL_TEXTENCODING_UTF8 );
nErrorPos = aUTF8Tester.Len();
aErrorMsg = ByteString( "UTF8 Encoding seems to be broken" );
return sal_False;
}
nErrorPos = aUTF8Tester.SearchChar( String::CreateFromAscii( "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f" ).GetBuffer() );
if ( nErrorPos != STRING_NOTFOUND )
{
aErrorMsg = ByteString( "String contains illegal character" );
return sal_False;
}
if ( bFixTags )
{
bHasBeenFixed = sal_False;
aFixed.Erase();
}
if ( !bAllowKeyIDs )
{
sal_Bool bIsKeyID = sal_False;
sal_Bool bNewId = sal_False;
ByteString aID( aTestee );
sal_uInt16 nAfterID = 0;
if ( aTestee.Equals( "{&", 0, 2 ) )
{ // check for strings from instset_native like "{&Tahoma8}335795.Installation Wiza ..."
sal_uInt16 nTagEnd = aTestee.Search( '}' );
if ( nTagEnd != STRING_NOTFOUND )
{
if ( bFixTags )
aFixed = aTestee.Copy( 0, nTagEnd+1 );
nErrorPos = nTagEnd+1;
aID = aTestee.Copy( nTagEnd+1 );
nAfterID = nTagEnd+1;
}
}
ByteString aDelimiter( (String)String( sal_Unicode(0x2016) ), RTL_TEXTENCODING_UTF8 );
if ( aID.Equals( aDelimiter, 6, aDelimiter.Len() ) )
{ // New KeyId 6 Letters, digits and spechial chars followed by delimiter
bNewId = sal_True;
nErrorPos = 1;
aID = aID.Copy( 0, 6 );
nAfterID += 6;
nAfterID = nAfterID + aDelimiter.Len();
}
else if ( ( aID.GetChar(6) == '*' ) && aID.Equals( aDelimiter, 7, aDelimiter.Len() ) )
{ // New KeyId 6 Letters, digits and spechial chars followed by '*delimiter' to indicate translation in progress
bNewId = sal_True;
nErrorPos = 1;
aID = aID.Copy( 0, 6 );
nAfterID += 7;
nAfterID = nAfterID + aDelimiter.Len();
}
else if ( aID.GetTokenCount( '.' ) > 1 )
{ // test for old KeyIDs 5 to 6 digits followed by a dot '44373.'
bNewId = sal_False;
nErrorPos = 1;
aID = aID.GetToken( 0, '.' );
nAfterID = nAfterID + aID.Len();
}
else
{
aID.Erase();
}
if ( bNewId )
{
if ( aID.Len() == 6 )
{
bIsKeyID = sal_True;
ByteString aDigits("0123456789abcdefghijklmnopqrstuvwxyz+-<=>");
for ( sal_uInt16 i=0 ; i < aID.Len() ;i++ )
{
if ( aDigits.Search( aID.GetChar(i) ) == STRING_NOTFOUND )
bIsKeyID = sal_False;
}
}
}
else
{
if ( aID.Len() > 0 && aID.GetChar(aID.Len()-1) == '*' )
aID.Erase( aID.Len()-1 );
if ( aID.IsNumericAscii() && aID.Len() >= 5 )
bIsKeyID = sal_True;
}
if ( bIsKeyID )
{
aErrorMsg = ByteString( "String contains KeyID" );
if ( bFixTags )
{
aFixed += aTestee.Copy( nAfterID );
bHasBeenFixed = sal_True;
aErrorMsg = ByteString( "FIXED String containing KeyID" );
}
else
aErrorMsg = ByteString( "String contains KeyID" );
return sal_False;
}
}
return sal_True;
}
/*****************************************************************************/
sal_Bool GSIBlock::TestUTF8( GSILine* pTestee, sal_Bool bFixTags )
/*****************************************************************************/
{
sal_uInt16 nErrorPos = 0;
ByteString aErrorMsg;
sal_Bool bError = sal_False;
ByteString aFixed;
sal_Bool bHasBeenFixed = sal_False;
if ( !IsUTF8( pTestee->GetText(), bFixTags, nErrorPos, aErrorMsg, bHasBeenFixed, aFixed ) )
{
ByteString aContext( pTestee->GetText().Copy( nErrorPos, 20 ) );
PrintError( aErrorMsg.Append(" in Text at Position " ).Append( ByteString::CreateFromInt32( nErrorPos ) ), "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId() );
bError = sal_True;
if ( bHasBeenFixed )
{
pTestee->SetText( aFixed );
pTestee->SetFixed();
}
}
if ( !IsUTF8( pTestee->GetQuickHelpText(), bFixTags, nErrorPos, aErrorMsg, bHasBeenFixed, aFixed ) )
{
ByteString aContext( pTestee->GetQuickHelpText().Copy( nErrorPos, 20 ) );
PrintError( aErrorMsg.Append(" in QuickHelpText at Position " ).Append( ByteString::CreateFromInt32( nErrorPos ) ), "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId() );
bError = sal_True;
if ( bHasBeenFixed )
{
pTestee->SetQuickHelpText( aFixed );
pTestee->SetFixed();
}
}
if ( !IsUTF8( pTestee->GetTitle(), bFixTags, nErrorPos, aErrorMsg, bHasBeenFixed, aFixed ) )
{
ByteString aContext( pTestee->GetTitle().Copy( nErrorPos, 20 ) );
PrintError( aErrorMsg.Append(" in Title at Position " ).Append( ByteString::CreateFromInt32( nErrorPos ) ), "Text format", aContext, pTestee->GetLineNumber(), pTestee->GetUniqId() );
bError = sal_True;
if ( bHasBeenFixed )
{
pTestee->SetTitle( aFixed );
pTestee->SetFixed();
}
}
if ( bError )
pTestee->NotOK();
return !bError;
}
/*****************************************************************************/
sal_Bool GSIBlock::HasSuspiciousChars( GSILine* pTestee, GSILine* pSource )
/*****************************************************************************/
{
sal_uInt16 nPos = 0;
if ( !bAllowSuspicious && ( nPos = pTestee->GetText().Search("??")) != STRING_NOTFOUND )
if ( pSource->GetText().Search("??") == STRING_NOTFOUND )
{
String aUTF8Tester = String( pTestee->GetText(), 0, nPos, RTL_TEXTENCODING_UTF8 );
sal_uInt16 nErrorPos = aUTF8Tester.Len();
ByteString aContext( pTestee->GetText().Copy( nPos, 20 ) );
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() );
pTestee->NotOK();
return sal_True;
}
return sal_False;
}
/*****************************************************************************/
sal_Bool GSIBlock::CheckSyntax( sal_uLong nLine, sal_Bool bRequireSourceLine, sal_Bool bFixTags )
/*****************************************************************************/
{
static LingTest aTester;
sal_Bool bHasError = sal_False;
if ( !pSourceLine )
{
if ( bRequireSourceLine )
{
PrintError( "No source language entry defined!", "File format", "", nLine );
bHasBlockError = sal_True;
}
}
else
{
aTester.CheckReference( pSourceLine );
if ( pSourceLine->HasMessages() )
{
PrintList( pSourceLine->GetMessageList(), "ReferenceString", pSourceLine );
pSourceLine->NotOK();
bHasError = sal_True;
}
}
if ( bReference )
{
if ( !pReferenceLine )
{
GSILine *pSource;
if ( pSourceLine )
pSource = pSourceLine;
else
pSource = GetObject( 0 ); // get some other line
if ( pSource )
PrintError( "No reference line found. Entry is new in source file", "File format", "", pSource->GetLineNumber(), pSource->GetUniqId() );
else
PrintError( "No reference line found. Entry is new in source file", "File format", "", nLine );
bHasBlockError = sal_True;
}
else
{
if ( pSourceLine && !pSourceLine->Equals( *pReferenceLine ) )
{
xub_StrLen nPos = pSourceLine->Match( *pReferenceLine );
ByteString aContext( pReferenceLine->Copy( nPos - 5, 15) );
aContext.Append( "\" --> \"" ).Append( pSourceLine->Copy( nPos - 5, 15) );
PrintError( "Source Language Entry has changed.", "File format", aContext, pSourceLine->GetLineNumber(), pSourceLine->GetUniqId() );
pSourceLine->NotOK();
bHasError = sal_True;
}
}
}
if ( pSourceLine )
bHasError |= !TestUTF8( pSourceLine, bFixTags );
sal_uLong i;
for ( i = 0; i < Count(); i++ )
{
aTester.CheckTestee( GetObject( i ), pSourceLine != NULL, bFixTags );
if ( GetObject( i )->HasMessages() || aTester.HasCompareWarnings() )
{
if ( GetObject( i )->HasMessages() || aTester.GetCompareWarnings().HasErrors() )
GetObject( i )->NotOK();
bHasError = sal_True;
PrintList( GetObject( i )->GetMessageList(), "Translation", GetObject( i ) );
PrintList( &(aTester.GetCompareWarnings()), "Translation Tag Missmatch", GetObject( i ) );
}
bHasError |= !TestUTF8( GetObject( i ), bFixTags );
if ( pSourceLine )
bHasError |= HasSuspiciousChars( GetObject( i ), pSourceLine );
}
return bHasError || bHasBlockError;
}
void GSIBlock::WriteError( LazySvFileStream &aErrOut, sal_Bool bRequireSourceLine )
{
if ( pSourceLine && pSourceLine->IsOK() && bCheckSourceLang && !bHasBlockError )
return;
sal_Bool bHasError = sal_False;
sal_Bool bCopyAll = ( !pSourceLine && bRequireSourceLine ) || ( pSourceLine && !pSourceLine->IsOK() && !bCheckTranslationLang ) || bHasBlockError;
sal_uLong i;
for ( i = 0; i < Count(); i++ )
{
if ( !GetObject( i )->IsOK() || bCopyAll )
{
bHasError = sal_True;
aErrOut.LazyOpen();
aErrOut.WriteLine( *GetObject( i ) );
}
}
if ( pSourceLine && ( bHasError || !pSourceLine->IsOK() ) && !( !bHasError && bCheckTranslationLang ) )
{
aErrOut.LazyOpen();
aErrOut.WriteLine( *pSourceLine );
}
}
void GSIBlock::WriteCorrect( LazySvFileStream &aOkOut, sal_Bool bRequireSourceLine )
{
if ( ( !pSourceLine && bRequireSourceLine ) || ( pSourceLine && !pSourceLine->IsOK() && !bCheckTranslationLang ) )
return;
sal_Bool bHasOK = sal_False;
sal_uLong i;
for ( i = 0; i < Count(); i++ )
{
if ( ( GetObject( i )->IsOK() || bCheckSourceLang ) && !bHasBlockError )
{
bHasOK = sal_True;
aOkOut.LazyOpen();
aOkOut.WriteLine( *GetObject( i ) );
}
}
if ( ( pSourceLine && pSourceLine->IsOK() && ( Count() || !bCheckTranslationLang ) ) || ( bHasOK && bCheckTranslationLang ) )
{
aOkOut.LazyOpen();
aOkOut.WriteLine( *pSourceLine );
}
}
void GSIBlock::WriteFixed( LazySvFileStream &aFixOut, sal_Bool /*bRequireSourceLine*/ )
{
if ( pSourceLine && !pSourceLine->IsFixed() && bCheckSourceLang )
return;
sal_Bool bHasFixes = sal_False;
sal_uLong i;
for ( i = 0; i < Count(); i++ )
{
if ( GetObject( i )->IsFixed() )
{
bHasFixes = sal_True;
aFixOut.LazyOpen();
aFixOut.WriteLine( *GetObject( i ) );
}
}
if ( pSourceLine && ( bHasFixes || pSourceLine->IsFixed() ) )
{
aFixOut.LazyOpen();
aFixOut.WriteLine( *pSourceLine );
}
}
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
void Help()
/*****************************************************************************/
{
fprintf( stdout, "\n" );
fprintf( stdout, "gsicheck Version 1.9.0 (c)1999 - 2006 by SUN Microsystems\n" );
fprintf( stdout, "=========================================================\n" );
fprintf( stdout, "\n" );
fprintf( stdout, "gsicheck checks the syntax of tags in GSI-Files and SDF-Files\n" );
fprintf( stdout, " checks for inconsistencies and malicious UTF8 encoding\n" );
fprintf( stdout, " checks tags in Online Help\n" );
fprintf( stdout, " checks for *new* KeyIDs and relax GID/LID length to %s\n", ByteString::CreateFromInt32(MAX_GID_LID_LEN).GetBuffer() );
fprintf( stdout, "\n" );
fprintf( stdout, "Syntax: gsicheck [ -c ] [-f] [ -we ] [ -wef ErrorFilename ] [ -wc ]\n" );
fprintf( stdout, " [ -wcf CorrectFilename ] [ -s | -t ] [ -l LanguageID ]\n" );
fprintf( stdout, " [ -r ReferenceFile ] filename\n" );
fprintf( stdout, "\n" );
fprintf( stdout, "-c Add context to error message (Print the line containing the error)\n" );
fprintf( stdout, "-f try to fix errors. See also -wf -wff \n" );
fprintf( stdout, "-wf Write File containing all fixed parts\n" );
fprintf( stdout, "-wff Same as above but give own filename\n" );
fprintf( stdout, "-we Write File containing all errors\n" );
fprintf( stdout, "-wef Same as above but give own filename\n" );
fprintf( stdout, "-wc Write File containing all correct parts\n" );
fprintf( stdout, "-wcf Same as above but give own filename\n" );
fprintf( stdout, "-s Check only source language. Should be used before handing out to vendor.\n" );
fprintf( stdout, "-t Check only Translation language(s). Should be used before merging.\n" );
fprintf( stdout, "-k Allow KeyIDs to be present in strings\n" );
fprintf( stdout, "-e disable encoding checks. E.g.: double questionmark \'??\' which may be the\n" );
fprintf( stdout, " result of false conversions\n" );
fprintf( stdout, "-l ISO Languagecode or numerical 2 digits Identifier of the source language.\n" );
fprintf( stdout, " Default is en-US. Use \"\" (empty string) or 'none'\n" );
fprintf( stdout, " to disable source language dependent checks\n" );
fprintf( stdout, "-r Reference filename to check that source language entries\n" );
fprintf( stdout, " have not been changed\n" );
fprintf( stdout, "\n" );
}
/*****************************************************************************/
#if defined(UNX) || defined(OS2)
int main( int argc, char *argv[] )
#else
int _cdecl main( int argc, char *argv[] )
#endif
/*****************************************************************************/
{
sal_Bool bError = sal_False;
sal_Bool bPrintContext = sal_False;
sal_Bool bCheckSourceLang = sal_False;
sal_Bool bCheckTranslationLang = sal_False;
sal_Bool bWriteError = sal_False;
sal_Bool bWriteCorrect = sal_False;
sal_Bool bWriteFixed = sal_False;
sal_Bool bFixTags = sal_False;
sal_Bool bAllowKID = sal_False;
sal_Bool bAllowSuspicious = sal_False;
String aErrorFilename;
String aCorrectFilename;
String aFixedFilename;
sal_Bool bFileHasError = sal_False;
ByteString aSourceLang( "en-US" ); // English is default
ByteString aFilename;
ByteString aReferenceFilename;
sal_Bool bReferenceFile = sal_False;
for ( sal_uInt16 i = 1 ; i < argc ; i++ )
{
if ( *argv[ i ] == '-' )
{
switch (*(argv[ i ]+1))
{
case 'c':bPrintContext = sal_True;
break;
case 'w':
{
if ( (*(argv[ i ]+2)) == 'e' )
{
if ( (*(argv[ i ]+3)) == 'f' )
if ( (i+1) < argc )
{
aErrorFilename = String( argv[ i+1 ], RTL_TEXTENCODING_ASCII_US );
bWriteError = sal_True;
i++;
}
else
{
fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
bError = sal_True;
}
else
bWriteError = sal_True;
}
else if ( (*(argv[ i ]+2)) == 'c' )
if ( (*(argv[ i ]+3)) == 'f' )
if ( (i+1) < argc )
{
aCorrectFilename = String( argv[ i+1 ], RTL_TEXTENCODING_ASCII_US );
bWriteCorrect = sal_True;
i++;
}
else
{
fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
bError = sal_True;
}
else
bWriteCorrect = sal_True;
else if ( (*(argv[ i ]+2)) == 'f' )
if ( (*(argv[ i ]+3)) == 'f' )
if ( (i+1) < argc )
{
aFixedFilename = String( argv[ i+1 ], RTL_TEXTENCODING_ASCII_US );
bWriteFixed = sal_True;
bFixTags = sal_True;
i++;
}
else
{
fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
bError = sal_True;
}
else
{
bWriteFixed = sal_True;
bFixTags = sal_True;
}
else
{
fprintf( stderr, "\nERROR: Unknown Switch %s!\n\n", argv[ i ] );
bError = sal_True;
}
}
break;
case 's':bCheckSourceLang = sal_True;
break;
case 't':bCheckTranslationLang = sal_True;
break;
case 'l':
{
if ( (i+1) < argc )
{
aSourceLang = ByteString( argv[ i+1 ] );
if ( aSourceLang.EqualsIgnoreCaseAscii( "none" ) )
aSourceLang.Erase();
i++;
}
else
{
fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
bError = sal_True;
}
}
break;
case 'r':
{
if ( (i+1) < argc )
{
aReferenceFilename = argv[ i+1 ];
bReferenceFile = sal_True;
i++;
}
else
{
fprintf( stderr, "\nERROR: Switch %s requires parameter!\n\n", argv[ i ] );
bError = sal_True;
}
}
break;
case 'f':
{
bFixTags = sal_True;
}
break;
case 'k':
{
bAllowKID = sal_True;
}
break;
case 'e':
{
bAllowSuspicious = sal_True;
}
break;
default:
fprintf( stderr, "\nERROR: Unknown Switch %s!\n\n", argv[ i ] );
bError = sal_True;
}
}
else
{
if ( !aFilename.Len())
aFilename = ByteString( argv[ i ] );
else
{
fprintf( stderr, "\nERROR: Only one filename may be specified!\n\n");
bError = sal_True;
}
}
}
if ( !aFilename.Len() || bError )
{
Help();
exit ( 0 );
}
if ( aSourceLang.Len() && !LanguageOK( aSourceLang ) )
{
fprintf( stderr, "\nERROR: The Language '%s' is invalid!\n\n", aSourceLang.GetBuffer() );
Help();
exit ( 1 );
}
if ( bCheckSourceLang && bCheckTranslationLang )
{
fprintf( stderr, "\nERROR: The Options -s and -t are mutually exclusive.\nUse only one of them.\n\n" );
Help();
exit ( 1 );
}
DirEntry aSource = DirEntry( String( aFilename, RTL_TEXTENCODING_ASCII_US ));
if ( !aSource.Exists()) {
fprintf( stderr, "\nERROR: GSI-File %s not found!\n\n", aFilename.GetBuffer() );
exit ( 2 );
}
SvFileStream aGSI( String( aFilename, RTL_TEXTENCODING_ASCII_US ), STREAM_STD_READ );
if ( !aGSI.IsOpen()) {
fprintf( stderr, "\nERROR: Could not open GSI-File %s!\n\n", aFilename.GetBuffer() );
exit ( 3 );
}
SvFileStream aReferenceGSI;
if ( bReferenceFile )
{
DirEntry aReferenceSource = DirEntry( String( aReferenceFilename, RTL_TEXTENCODING_ASCII_US ));
if ( !aReferenceSource.Exists()) {
fprintf( stderr, "\nERROR: GSI-File %s not found!\n\n", aFilename.GetBuffer() );
exit ( 2 );
}
aReferenceGSI.Open( String( aReferenceFilename, RTL_TEXTENCODING_ASCII_US ), STREAM_STD_READ );
if ( !aReferenceGSI.IsOpen()) {
fprintf( stderr, "\nERROR: Could not open Input-File %s!\n\n", aFilename.GetBuffer() );
exit ( 3 );
}
}
LazySvFileStream aOkOut;
String aBaseName = aSource.GetBase();
if ( bWriteCorrect )
{
if ( !aCorrectFilename.Len() )
{
String sTmpBase( aBaseName );
sTmpBase += String( "_ok", RTL_TEXTENCODING_ASCII_US );
aSource.SetBase( sTmpBase );
aCorrectFilename = aSource.GetFull();
}
aOkOut.SetOpenParams( aCorrectFilename , STREAM_STD_WRITE | STREAM_TRUNC );
}
LazySvFileStream aErrOut;
if ( bWriteError )
{
if ( !aErrorFilename.Len() )
{
String sTmpBase( aBaseName );
sTmpBase += String( "_err", RTL_TEXTENCODING_ASCII_US );
aSource.SetBase( sTmpBase );
aErrorFilename = aSource.GetFull();
}
aErrOut.SetOpenParams( aErrorFilename , STREAM_STD_WRITE | STREAM_TRUNC );
}
LazySvFileStream aFixOut;
if ( bWriteFixed )
{
if ( !aFixedFilename.Len() )
{
String sTmpBase( aBaseName );
sTmpBase += String( "_fix", RTL_TEXTENCODING_ASCII_US );
aSource.SetBase( sTmpBase );
aFixedFilename = aSource.GetFull();
}
aFixOut.SetOpenParams( aFixedFilename , STREAM_STD_WRITE | STREAM_TRUNC );
}
ByteString sReferenceLine;
GSILine* pReferenceLine = NULL;
ByteString aOldReferenceId("No Valid ID"); // just set to something which can never be an ID
sal_uLong nReferenceLine = 0;
ByteString sGSILine;
GSILine* pGSILine = NULL;
ByteString aOldId("No Valid ID"); // just set to something which can never be an ID
GSIBlock *pBlock = NULL;
sal_uLong nLine = 0;
while ( !aGSI.IsEof() )
{
aGSI.ReadLine( sGSILine );
nLine++;
pGSILine = new GSILine( sGSILine, nLine );
sal_Bool bDelete = sal_True;
if ( pGSILine->Len() )
{
if ( FORMAT_UNKNOWN == pGSILine->GetLineFormat() )
{
PrintError( "Format of line is unknown. Ignoring!", "Line format", pGSILine->Copy( 0,40 ), bPrintContext, pGSILine->GetLineNumber() );
pGSILine->NotOK();
if ( bWriteError )
{
bFileHasError = sal_True;
aErrOut.LazyOpen();
aErrOut.WriteLine( *pGSILine );
}
}
else if ( pGSILine->GetLineType().EqualsIgnoreCaseAscii("res-comment") )
{ // ignore comment lines, but write them to Correct Items File
if ( bWriteCorrect )
{
aOkOut.LazyOpen();
aOkOut.WriteLine( *pGSILine );
}
}
else
{
ByteString aId = pGSILine->GetUniqId();
if ( aId != aOldId )
{
if ( pBlock )
{
bFileHasError |= pBlock->CheckSyntax( nLine, aSourceLang.Len() != 0, bFixTags );
if ( bWriteError )
pBlock->WriteError( aErrOut, aSourceLang.Len() != 0 );
if ( bWriteCorrect )
pBlock->WriteCorrect( aOkOut, aSourceLang.Len() != 0 );
if ( bWriteFixed )
pBlock->WriteFixed( aFixOut, aSourceLang.Len() != 0 );
delete pBlock;
}
pBlock = new GSIBlock( bPrintContext, bCheckSourceLang, bCheckTranslationLang, bReferenceFile, bAllowKID, bAllowSuspicious );
aOldId = aId;
// find corresponding line in reference file
if ( bReferenceFile )
{
sal_Bool bContinueSearching = sal_True;
while ( ( !aReferenceGSI.IsEof() || pReferenceLine ) && bContinueSearching )
{
if ( !pReferenceLine )
{
aReferenceGSI.ReadLine( sReferenceLine );
nReferenceLine++;
pReferenceLine = new GSILine( sReferenceLine, nReferenceLine );
}
if ( pReferenceLine->GetLineFormat() != FORMAT_UNKNOWN )
{
if ( pReferenceLine->GetUniqId() == aId && pReferenceLine->GetLanguageId().Equals( aSourceLang ) )
{
pBlock->SetReferenceLine( pReferenceLine );
pReferenceLine = NULL;
}
else if ( pReferenceLine->GetUniqId() > aId )
{
// if ( pGSILine->GetLanguageId() == aSourceLang )
// PrintError( "No reference line found. Entry is new in source file", "File format", "", bPrintContext, pGSILine->GetLineNumber(), aId );
bContinueSearching = sal_False;
}
else
{
if ( pReferenceLine->GetUniqId() < aId && pReferenceLine->GetLanguageId().Equals( aSourceLang ) )
PrintError( "No Entry in source file found. Entry has been removed from source file", "File format", "", bPrintContext, pGSILine->GetLineNumber(), pReferenceLine->GetUniqId() );
delete pReferenceLine;
pReferenceLine = NULL;
}
}
else
{
delete pReferenceLine;
pReferenceLine = NULL;
}
}
}
}
pBlock->InsertLine( pGSILine, aSourceLang );
bDelete = sal_False;
}
}
if ( bDelete )
delete pGSILine;
}
if ( pBlock )
{
bFileHasError |= pBlock->CheckSyntax( nLine, aSourceLang.Len() != 0, bFixTags );
if ( bWriteError )
pBlock->WriteError( aErrOut, aSourceLang.Len() != 0 );
if ( bWriteCorrect )
pBlock->WriteCorrect( aOkOut, aSourceLang.Len() != 0 );
if ( bWriteFixed )
pBlock->WriteFixed( aFixOut, aSourceLang.Len() != 0 );
delete pBlock;
}
aGSI.Close();
if ( bWriteError )
aErrOut.Close();
if ( bWriteCorrect )
aOkOut.Close();
if ( bWriteFixed )
aFixOut.Close();
if ( bFileHasError )
return 55;
else
return 0;
}