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