PageRenderTime 64ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/libreoffice-3.6.0.2/l10ntools/source/gsicheck.cxx

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