diff.cpp 55.4 KB
Newer Older
Joachim Eibl's avatar
Joachim Eibl committed
1 2 3 4
/***************************************************************************
                          diff.cpp  -  description
                             -------------------
    begin                : Mon Mar 18 2002
Joachim Eibl's avatar
Joachim Eibl committed
5
    copyright            : (C) 2002-2007 by Joachim Eibl
Joachim Eibl's avatar
Joachim Eibl committed
6
    email                : joachim.eibl at gmx.de
Joachim Eibl's avatar
Joachim Eibl committed
7 8 9 10 11 12 13 14 15 16 17 18
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <stdio.h>
Joachim Eibl's avatar
Joachim Eibl committed
19
#include <cstdlib>
Joachim Eibl's avatar
Joachim Eibl committed
20 21 22 23
#include <iostream>

#include "diff.h"
#include "fileaccess.h"
Joachim Eibl's avatar
Joachim Eibl committed
24
#include "optiondialog.h"
Joachim Eibl's avatar
Joachim Eibl committed
25 26 27

#include <kmessagebox.h>
#include <klocale.h>
Joachim Eibl's avatar
Joachim Eibl committed
28 29 30 31

#include <QFileInfo>
#include <QDir>
#include <QTextCodec>
Joachim Eibl's avatar
Joachim Eibl committed
32
#include <QTextStream>
Joachim Eibl's avatar
Joachim Eibl committed
33
#include <QProcess>
Joachim Eibl's avatar
Joachim Eibl committed
34 35 36 37 38 39 40

#include <map>
#include <assert.h>
#include <ctype.h>
//using namespace std;


Joachim Eibl's avatar
Joachim Eibl committed
41
int LineData::width(int tabSize) const
Joachim Eibl's avatar
Joachim Eibl committed
42 43 44 45 46 47 48
{
   int w=0;
   int j=0;
   for( int i=0; i<size; ++i )
   {
      if ( pLine[i]=='\t' )
      {
Joachim Eibl's avatar
Joachim Eibl committed
49
         for(j %= tabSize; j<tabSize; ++j)
Joachim Eibl's avatar
Joachim Eibl committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
            ++w;
         j=0;
      }
      else
      {
         ++w;
         ++j;
      }
   }
   return w;
}


// The bStrict flag is true during the test where a nonmatching area ends.
// Then the equal()-function requires that the match has more than 2 nonwhite characters.
// This is to avoid matches on trivial lines (e.g. with white space only).
// This choice is good for C/C++.
bool equal( const LineData& l1, const LineData& l2, bool bStrict )
{
   if ( l1.pLine==0 || l2.pLine==0) return false;

Joachim Eibl's avatar
Joachim Eibl committed
71
   if ( bStrict && g_bIgnoreTrivialMatches )//&& (l1.occurances>=5 || l2.occurances>=5) )
Joachim Eibl's avatar
Joachim Eibl committed
72 73 74
      return false;

   // Ignore white space diff
Joachim Eibl's avatar
Joachim Eibl committed
75 76
   const QChar* p1 = l1.pLine;
   const QChar* p1End = p1 + l1.size;
Joachim Eibl's avatar
Joachim Eibl committed
77

Joachim Eibl's avatar
Joachim Eibl committed
78 79
   const QChar* p2 = l2.pLine;
   const QChar* p2End = p2 + l2.size;
Joachim Eibl's avatar
Joachim Eibl committed
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

   if ( g_bIgnoreWhiteSpace )
   {
      int nonWhite = 0;
      for(;;)
      {
         while( isWhite( *p1 ) && p1!=p1End ) ++p1;
         while( isWhite( *p2 ) && p2!=p2End ) ++p2;

         if ( p1 == p1End  &&  p2 == p2End )
         {
            if ( bStrict && g_bIgnoreTrivialMatches )
            {  // Then equality is not enough
               return nonWhite>2;
            }
            else  // equality is enough
               return true;
         }
         else if ( p1 == p1End || p2 == p2End )
            return false;

         if( *p1 != *p2 )
            return false;
         ++p1;
         ++p2;
         ++nonWhite;
      }
   }

   else
   {
      if ( l1.size==l2.size && memcmp(p1, p2, l1.size)==0)
         return true;
      else
         return false;
   }
}


Joachim Eibl's avatar
Joachim Eibl committed
119
static bool isLineOrBufEnd( const QChar* p, int i, int size )
Joachim Eibl's avatar
Joachim Eibl committed
120
{
Joachim Eibl's avatar
Joachim Eibl committed
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
   return 
      i>=size        // End of file
      || p[i]=='\n'  // Normal end of line

      // No support for Mac-end of line yet, because incompatible with GNU-diff-routines.      
      // || ( p[i]=='\r' && (i>=size-1 || p[i+1]!='\n') 
      //                 && (i==0        || p[i-1]!='\n') )  // Special case: '\r' without '\n'
      ;
}


/* Features of class SourceData:
- Read a file (from the given URL) or accept data via a string.
- Allocate and free buffers as necessary.
- Run a preprocessor, when specified.
- Run the line-matching preprocessor, when specified.
Joachim Eibl's avatar
Joachim Eibl committed
137
- Run other preprocessing steps: Uppercase, ignore comments,
Joachim Eibl's avatar
Joachim Eibl committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
                                 remove carriage return, ignore numbers.

Order of operation:
 1. If data was given via a string then save it to a temp file. (see setData())
 2. If the specified file is nonlocal (URL) copy it to a temp file.
 3. If a preprocessor was specified, run the input file through it.
 4. Read the output of the preprocessor.
 5. If Uppercase was specified: Turn the read data to uppercase.
 6. Write the result to a temp file.
 7. If a line-matching preprocessor was specified, run the temp file through it.
 8. Read the output of the line-matching preprocessor.
 9. If ignore numbers was specified, strip the LMPP-output of all numbers.
10. If ignore comments was specified, strip the LMPP-output of comments.

Optimizations: Skip unneeded steps.
*/

SourceData::SourceData()
Joachim Eibl's avatar
Joachim Eibl committed
156
{
Joachim Eibl's avatar
Joachim Eibl committed
157 158 159
   m_pOptionDialog = 0;
   reset();
}
Joachim Eibl's avatar
Joachim Eibl committed
160

Joachim Eibl's avatar
Joachim Eibl committed
161 162 163 164 165 166 167
SourceData::~SourceData()
{
   reset();
}

void SourceData::reset()
{
Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
168
   m_pEncoding = 0;   
Joachim Eibl's avatar
0.9.86  
Joachim Eibl committed
169
   m_fileAccess = FileAccess();
Joachim Eibl's avatar
Joachim Eibl committed
170 171 172
   m_normalData.reset();
   m_lmppData.reset();
   if ( !m_tempInputFileName.isEmpty() )
Joachim Eibl's avatar
Joachim Eibl committed
173
   {
Joachim Eibl's avatar
Joachim Eibl committed
174 175 176 177
      FileAccess::removeFile( m_tempInputFileName );
      m_tempInputFileName = "";
   }
}
Joachim Eibl's avatar
Joachim Eibl committed
178

Joachim Eibl's avatar
Joachim Eibl committed
179 180 181 182 183 184 185 186 187 188 189 190
void SourceData::setFilename( const QString& filename )
{
   if (filename.isEmpty())
   {
      reset();
   }
   else
   {
      FileAccess fa( filename );
      setFileAccess( fa );
   }
}
191

Joachim Eibl's avatar
Joachim Eibl committed
192 193 194 195 196 197 198 199 200 201
bool SourceData::isEmpty() 
{ 
   return getFilename().isEmpty(); 
}

bool SourceData::hasData() 
{ 
   return m_normalData.m_pBuf != 0;
}

Joachim Eibl's avatar
0.9.86  
Joachim Eibl committed
202 203 204 205 206
bool SourceData::isValid()
{
   return isEmpty() || hasData();
}

Joachim Eibl's avatar
Joachim Eibl committed
207 208 209 210 211 212 213
void SourceData::setOptionDialog( OptionDialog* pOptionDialog )
{
   m_pOptionDialog = pOptionDialog;
}

QString SourceData::getFilename()
{
Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
214
   return m_fileAccess.absoluteFilePath();
Joachim Eibl's avatar
Joachim Eibl committed
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
}

QString SourceData::getAliasName()
{
   return m_aliasName.isEmpty() ? m_fileAccess.prettyAbsPath() : m_aliasName;
}

void SourceData::setAliasName( const QString& name )
{
   m_aliasName = name;
}

void SourceData::setFileAccess( const FileAccess& fileAccess )
{
   m_fileAccess = fileAccess;
   m_aliasName = QString();
   if ( !m_tempInputFileName.isEmpty() )
   {
      FileAccess::removeFile( m_tempInputFileName );
      m_tempInputFileName = "";
Joachim Eibl's avatar
Joachim Eibl committed
235
   }
Joachim Eibl's avatar
Joachim Eibl committed
236
}
Joachim Eibl's avatar
Joachim Eibl committed
237

Joachim Eibl's avatar
Joachim Eibl committed
238 239 240 241 242 243 244
void SourceData::setData( const QString& data )
{
   // Create a temp file for preprocessing:
   if ( m_tempInputFileName.isEmpty() )
   {
      m_tempInputFileName = FileAccess::tempFileName();
   }
Joachim Eibl's avatar
Joachim Eibl committed
245

Joachim Eibl's avatar
Joachim Eibl committed
246
   FileAccess f( m_tempInputFileName );
Joachim Eibl's avatar
Joachim Eibl committed
247 248
   QByteArray ba = QTextCodec::codecForName("UTF-8")->fromUnicode(data);
   bool bSuccess = f.writeFile( ba.constData(), ba.length() );
Joachim Eibl's avatar
Joachim Eibl committed
249 250 251 252 253
   if ( !bSuccess )
   {
      KMessageBox::error( m_pOptionDialog, i18n("Writing clipboard data to temp file failed.") );
      return;
   }
Joachim Eibl's avatar
Joachim Eibl committed
254

Joachim Eibl's avatar
Joachim Eibl committed
255 256 257 258
   m_aliasName = i18n("From Clipboard");
   m_fileAccess = FileAccess("");  // Effect: m_fileAccess.isValid() is false
}

Joachim Eibl's avatar
Joachim Eibl committed
259
const LineData* SourceData::getLineDataForDiff() const
Joachim Eibl's avatar
Joachim Eibl committed
260
{
Joachim Eibl's avatar
Joachim Eibl committed
261 262 263 264
   if ( m_lmppData.m_pBuf==0 )
      return m_normalData.m_v.size()>0 ? &m_normalData.m_v[0] : 0;
   else
      return m_lmppData.m_v.size()>0   ? &m_lmppData.m_v[0]   : 0;
Joachim Eibl's avatar
Joachim Eibl committed
265 266 267 268
}

const LineData* SourceData::getLineDataForDisplay() const
{
Joachim Eibl's avatar
Joachim Eibl committed
269
   return m_normalData.m_v.size()>0 ? &m_normalData.m_v[0] : 0;
Joachim Eibl's avatar
Joachim Eibl committed
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
}

int  SourceData::getSizeLines() const
{
   return m_normalData.m_vSize;
}

int SourceData::getSizeBytes() const
{
   return m_normalData.m_size;
}

const char* SourceData::getBuf() const
{
   return m_normalData.m_pBuf;
}

bool SourceData::isText()
{
   return m_normalData.m_bIsText;
}
 
bool SourceData::isFromBuffer()
{
   return !m_fileAccess.isValid();
}


bool SourceData::isBinaryEqualWith( const SourceData& other ) const
{
Joachim Eibl's avatar
Joachim Eibl committed
300 301
   return m_fileAccess.exists() && other.m_fileAccess.exists() &&
          getSizeBytes() == other.getSizeBytes() && 
Joachim Eibl's avatar
0.9.86  
Joachim Eibl committed
302
          ( getSizeBytes()==0 || memcmp( getBuf(), other.getBuf(), getSizeBytes() )==0 );
Joachim Eibl's avatar
Joachim Eibl committed
303 304 305
}

void SourceData::FileData::reset()
Joachim Eibl's avatar
Joachim Eibl committed
306
{
Joachim Eibl's avatar
Joachim Eibl committed
307
   delete[] (char*)m_pBuf;
Joachim Eibl's avatar
Joachim Eibl committed
308 309 310 311
   m_pBuf = 0;
   m_v.clear();
   m_size = 0;
   m_vSize = 0;
Joachim Eibl's avatar
Joachim Eibl committed
312
   m_bIsText = true;
Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
313
   m_eLineEndStyle = eLineEndStyleUndefined;
Joachim Eibl's avatar
Joachim Eibl committed
314 315
}

Joachim Eibl's avatar
Joachim Eibl committed
316 317 318 319 320 321 322 323 324
bool SourceData::FileData::readFile( const QString& filename )
{
   reset();
   if ( filename.isEmpty() )   { return true; }

   FileAccess fa( filename );
   m_size = fa.sizeForReading();
   char* pBuf;
   m_pBuf = pBuf = new char[m_size+100];  // Alloc 100 byte extra: Savety hack, not nice but does no harm.
Joachim Eibl's avatar
Joachim Eibl committed
325 326
                                // Some extra bytes at the end of the buffer are needed by
                                // the diff algorithm. See also GnuDiff::diff_2_files().
Joachim Eibl's avatar
Joachim Eibl committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
   bool bSuccess = fa.readFile( pBuf, m_size );
   if ( !bSuccess )
   {
      delete pBuf;
      m_pBuf = 0;
      m_size = 0;
   }
   return bSuccess;
}

bool SourceData::saveNormalDataAs( const QString& fileName )
{
   return m_normalData.writeFile( fileName );
}

bool SourceData::FileData::writeFile( const QString& filename )
{
   if ( filename.isEmpty() )   { return true; }

   FileAccess fa( filename );
   bool bSuccess = fa.writeFile(m_pBuf, m_size);
   return bSuccess;
}

void SourceData::FileData::copyBufFrom( const FileData& src )
{
   reset();
Joachim Eibl's avatar
Joachim Eibl committed
354
   char* pBuf;
Joachim Eibl's avatar
Joachim Eibl committed
355 356 357 358 359
   m_size = src.m_size;
   m_pBuf = pBuf = new char[m_size+100];
   memcpy( pBuf, src.m_pBuf, m_size );
}

Joachim Eibl's avatar
Joachim Eibl committed
360 361 362
// Convert the input file from input encoding to output encoding and write it to the output file.
static bool convertFileEncoding( const QString& fileNameIn, QTextCodec* pCodecIn,
                                 const QString& fileNameOut, QTextCodec* pCodecOut )
Joachim Eibl's avatar
Joachim Eibl committed
363
{
Joachim Eibl's avatar
Joachim Eibl committed
364
   QFile in( fileNameIn );
Joachim Eibl's avatar
Joachim Eibl committed
365
   if ( ! in.open(QIODevice::ReadOnly ) )
Joachim Eibl's avatar
Joachim Eibl committed
366 367 368
      return false;
   QTextStream inStream( &in );
   inStream.setCodec( pCodecIn );
Joachim Eibl's avatar
Joachim Eibl committed
369
   inStream.setAutoDetectUnicode( false );
Joachim Eibl's avatar
Joachim Eibl committed
370 371

   QFile out( fileNameOut );
Joachim Eibl's avatar
Joachim Eibl committed
372
   if ( ! out.open( QIODevice::WriteOnly ) )
Joachim Eibl's avatar
Joachim Eibl committed
373 374 375 376
      return false;
   QTextStream outStream( &out );
   outStream.setCodec( pCodecOut );

Joachim Eibl's avatar
Joachim Eibl committed
377
   QString data = inStream.readAll();
Joachim Eibl's avatar
Joachim Eibl committed
378 379 380 381 382
   outStream << data;

   return true;
}

Joachim Eibl's avatar
Joachim Eibl committed
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
static QTextCodec* detectEncoding( const char* buf, qint64 size, qint64& skipBytes )
{
   if (size>=2)
   {
      skipBytes = 2;
      if (buf[0]=='\xFF' && buf[1]=='\xFE' )
         return QTextCodec::codecForName( "UTF-16LE" );

      if (buf[0]=='\xFE' && buf[1]=='\xFF' )
         return QTextCodec::codecForName( "UTF-16BE" );
   }
   if (size>=3)
   {
      skipBytes = 3;
      if (buf[0]=='\xEF' && buf[1]=='\xBB' && buf[2]=='\xBF' )
         return QTextCodec::codecForName( "UTF-8-BOM" );
   }
   skipBytes = 0;
   return 0;
}

QTextCodec* SourceData::detectEncoding( const QString& fileName, QTextCodec* pFallbackCodec )
{
   QFile f(fileName);
   if ( f.open(QIODevice::ReadOnly) )
   {
      char buf[4];
      qint64 size = f.read( buf, sizeof(buf) );
      qint64 skipBytes = 0;
      QTextCodec* pCodec = ::detectEncoding( buf, size, skipBytes );
      if (pCodec)
         return pCodec;
   }
   return pFallbackCodec;
}

void SourceData::readAndPreprocess( QTextCodec* pEncoding, bool bAutoDetectUnicode )
Joachim Eibl's avatar
Joachim Eibl committed
420 421
{
   m_pEncoding = pEncoding;
Joachim Eibl's avatar
Joachim Eibl committed
422 423 424 425
   QString fileNameIn1;
   QString fileNameOut1;
   QString fileNameIn2;
   QString fileNameOut2;
Joachim Eibl's avatar
Joachim Eibl committed
426

Joachim Eibl's avatar
Joachim Eibl committed
427
   bool bTempFileFromClipboard = !m_fileAccess.isValid();
Joachim Eibl's avatar
Joachim Eibl committed
428

Joachim Eibl's avatar
Joachim Eibl committed
429 430 431 432 433
   // Detect the input for the preprocessing operations
   if ( !bTempFileFromClipboard )
   {
      if ( m_fileAccess.isLocal() )
      {
Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
434
         fileNameIn1 = m_fileAccess.absoluteFilePath();
Joachim Eibl's avatar
Joachim Eibl committed
435 436 437 438
      }
      else    // File is not local: create a temporary local copy:
      {
         if ( m_tempInputFileName.isEmpty() )  { m_tempInputFileName = FileAccess::tempFileName(); }
Joachim Eibl's avatar
Joachim Eibl committed
439

Joachim Eibl's avatar
Joachim Eibl committed
440 441 442
         m_fileAccess.copyFile(m_tempInputFileName);
         fileNameIn1 = m_tempInputFileName;
      }
Joachim Eibl's avatar
Joachim Eibl committed
443 444 445 446
      if ( bAutoDetectUnicode )
      {
         m_pEncoding = detectEncoding( fileNameIn1, pEncoding );
      }
Joachim Eibl's avatar
Joachim Eibl committed
447 448 449 450
   }
   else // The input was set via setData(), probably from clipboard.
   {
      fileNameIn1 = m_tempInputFileName;
Joachim Eibl's avatar
Joachim Eibl committed
451
      m_pEncoding = QTextCodec::codecForName("UTF-8");
Joachim Eibl's avatar
Joachim Eibl committed
452
   }
Joachim Eibl's avatar
Joachim Eibl committed
453 454
   QTextCodec* pEncoding1 = m_pEncoding;
   QTextCodec* pEncoding2 = m_pEncoding;
Joachim Eibl's avatar
Joachim Eibl committed
455

Joachim Eibl's avatar
Joachim Eibl committed
456 457
   m_normalData.reset();
   m_lmppData.reset();
Joachim Eibl's avatar
Joachim Eibl committed
458

Joachim Eibl's avatar
Joachim Eibl committed
459 460
   FileAccess faIn(fileNameIn1);
   int fileInSize = faIn.size();
Joachim Eibl's avatar
Joachim Eibl committed
461

Joachim Eibl's avatar
0.9.86  
Joachim Eibl committed
462
   if ( faIn.exists() ) // fileInSize > 0 )
Joachim Eibl's avatar
Joachim Eibl committed
463
   {
Joachim Eibl's avatar
Joachim Eibl committed
464

Joachim Eibl's avatar
Joachim Eibl committed
465
#if defined(_WIN32) || defined(Q_OS_OS2)
Joachim Eibl's avatar
Joachim Eibl committed
466 467 468 469 470
      QString catCmd = "type";
      fileNameIn1.replace( '/', "\\" );
#else
      QString catCmd = "cat";
#endif
Joachim Eibl's avatar
Joachim Eibl committed
471

Joachim Eibl's avatar
Joachim Eibl committed
472 473 474 475 476 477 478 479
      // Run the first preprocessor
      if ( m_pOptionDialog->m_PreProcessorCmd.isEmpty() )
      {
         // No preprocessing: Read the file directly:
         m_normalData.readFile( fileNameIn1 );
      }
      else
      {
Joachim Eibl's avatar
Joachim Eibl committed
480 481 482 483 484 485 486 487 488 489
         QString fileNameInPP = fileNameIn1;

         if ( pEncoding1 != m_pOptionDialog->m_pEncodingPP )
         {
            // Before running the preprocessor convert to the format that the preprocessor expects.
            fileNameInPP = FileAccess::tempFileName();
            pEncoding1 = m_pOptionDialog->m_pEncodingPP;
            convertFileEncoding( fileNameIn1, pEncoding, fileNameInPP, pEncoding1 );
         }

Joachim Eibl's avatar
Joachim Eibl committed
490 491
         QString ppCmd = m_pOptionDialog->m_PreProcessorCmd;
         fileNameOut1 = FileAccess::tempFileName();
Joachim Eibl's avatar
Joachim Eibl committed
492 493 494 495 496 497 498 499

         QProcess ppProcess;
         ppProcess.setStandardInputFile( fileNameInPP );
         ppProcess.setStandardOutputFile( fileNameOut1 );
         ppProcess.start( ppCmd );
         ppProcess.waitForFinished(-1);
         //QString cmd = catCmd + " \"" + fileNameInPP + "\" | " + ppCmd  + " >\"" + fileNameOut1+"\"";
         //::system( encodeString(cmd) );
Joachim Eibl's avatar
Joachim Eibl committed
500 501 502 503 504 505
         bool bSuccess = m_normalData.readFile( fileNameOut1 );
         if ( fileInSize >0 && ( !bSuccess || m_normalData.m_size==0 ) )
         {
            KMessageBox::error(m_pOptionDialog, 
               i18n("Preprocessing possibly failed. Check this command:\n\n  %1"
                  "\n\nThe preprocessing command will be disabled now."
Joachim Eibl's avatar
Joachim Eibl committed
506
               ).arg(ppCmd) );
Joachim Eibl's avatar
Joachim Eibl committed
507 508
            m_pOptionDialog->m_PreProcessorCmd = "";
            m_normalData.readFile( fileNameIn1 );
Joachim Eibl's avatar
Joachim Eibl committed
509 510 511 512
            pEncoding1 = m_pEncoding;
         }
         if (fileNameInPP != fileNameIn1)
         {
Joachim Eibl's avatar
Joachim Eibl committed
513
            FileAccess::removeTempFile( fileNameInPP );
Joachim Eibl's avatar
Joachim Eibl committed
514 515
         }
      }
Joachim Eibl's avatar
Joachim Eibl committed
516

Joachim Eibl's avatar
Joachim Eibl committed
517 518 519
      // LineMatching Preprocessor
      if ( ! m_pOptionDialog->m_LineMatchingPreProcessorCmd.isEmpty() )
      {
Joachim Eibl's avatar
0.9.86  
Joachim Eibl committed
520
         fileNameIn2 = fileNameOut1.isEmpty() ? fileNameIn1 : fileNameOut1;
Joachim Eibl's avatar
Joachim Eibl committed
521 522 523 524
         QString fileNameInPP = fileNameIn2;
         pEncoding2 = pEncoding1;
         if ( pEncoding2 != m_pOptionDialog->m_pEncodingPP )
         {
Joachim Eibl's avatar
Joachim Eibl committed
525
            // Before running the preprocessor convert to the format that the preprocessor expects.
Joachim Eibl's avatar
Joachim Eibl committed
526 527 528 529 530
            fileNameInPP = FileAccess::tempFileName();
            pEncoding2 = m_pOptionDialog->m_pEncodingPP;
            convertFileEncoding( fileNameIn2, pEncoding1, fileNameInPP, pEncoding2 );
         }

Joachim Eibl's avatar
Joachim Eibl committed
531 532
         QString ppCmd = m_pOptionDialog->m_LineMatchingPreProcessorCmd;
         fileNameOut2 = FileAccess::tempFileName();
Joachim Eibl's avatar
Joachim Eibl committed
533 534 535 536 537 538 539
         QProcess ppProcess;
         ppProcess.setStandardInputFile( fileNameInPP );
         ppProcess.setStandardOutputFile( fileNameOut2 );
         ppProcess.start( ppCmd );
         ppProcess.waitForFinished(-1);
         //QString cmd = catCmd + " \"" + fileNameInPP + "\" | " + ppCmd  + " >\"" + fileNameOut2 + "\"";
         //::system( encodeString(cmd) );
Joachim Eibl's avatar
Joachim Eibl committed
540 541 542 543 544 545
         bool bSuccess = m_lmppData.readFile( fileNameOut2 );
         if ( FileAccess(fileNameIn2).size()>0 && ( !bSuccess || m_lmppData.m_size==0 ) )
         {
            KMessageBox::error(m_pOptionDialog, 
               i18n("The line-matching-preprocessing possibly failed. Check this command:\n\n  %1"
                    "\n\nThe line-matching-preprocessing command will be disabled now."
Joachim Eibl's avatar
Joachim Eibl committed
546
                   ).arg(ppCmd) );
Joachim Eibl's avatar
Joachim Eibl committed
547 548 549
            m_pOptionDialog->m_LineMatchingPreProcessorCmd = "";
            m_lmppData.readFile( fileNameIn2 );
         }
Joachim Eibl's avatar
Joachim Eibl committed
550
         FileAccess::removeTempFile( fileNameOut2 );
Joachim Eibl's avatar
Joachim Eibl committed
551 552
         if (fileNameInPP != fileNameIn2)
         {
Joachim Eibl's avatar
Joachim Eibl committed
553
            FileAccess::removeTempFile( fileNameInPP );
Joachim Eibl's avatar
Joachim Eibl committed
554
         }
Joachim Eibl's avatar
Joachim Eibl committed
555
      }
Joachim Eibl's avatar
0.9.86  
Joachim Eibl committed
556
      else if ( m_pOptionDialog->m_bIgnoreComments || m_pOptionDialog->m_bIgnoreCase )
Joachim Eibl's avatar
Joachim Eibl committed
557 558 559 560 561 562 563 564
      {
         // We need a copy of the normal data.
         m_lmppData.copyBufFrom( m_normalData );
      }
      else
      {  // We don't need any lmpp data at all.
         m_lmppData.reset();
      }
Joachim Eibl's avatar
Joachim Eibl committed
565 566 567 568 569
   }

   m_normalData.preprocess( m_pOptionDialog->m_bPreserveCarriageReturn, pEncoding1 );
   m_lmppData.preprocess( false, pEncoding2 );

Joachim Eibl's avatar
Joachim Eibl committed
570 571 572 573 574 575
   if ( m_lmppData.m_vSize < m_normalData.m_vSize )
   {
      // This probably is the fault of the LMPP-Command, but not worth reporting.
      m_lmppData.m_v.resize( m_normalData.m_vSize );
      for(int i=m_lmppData.m_vSize; i<m_normalData.m_vSize; ++i )
      {  // Set all empty lines to point to the end of the buffer.
Joachim Eibl's avatar
Joachim Eibl committed
576
         m_lmppData.m_v[i].pLine = m_lmppData.m_unicodeBuf.unicode()+m_lmppData.m_unicodeBuf.length();
Joachim Eibl's avatar
Joachim Eibl committed
577
      }
Joachim Eibl's avatar
Joachim Eibl committed
578

Joachim Eibl's avatar
Joachim Eibl committed
579 580
      m_lmppData.m_vSize = m_normalData.m_vSize;
   }
Joachim Eibl's avatar
0.9.86  
Joachim Eibl committed
581 582 583 584 585

   // Internal Preprocessing: Uppercase-conversion   
   if ( m_pOptionDialog->m_bIgnoreCase )
   {
      int i;
Joachim Eibl's avatar
Joachim Eibl committed
586 587 588
      QChar* pBuf = const_cast<QChar*>(m_lmppData.m_unicodeBuf.unicode());
      int ucSize = m_lmppData.m_unicodeBuf.length();
      for(i=0; i<ucSize; ++i)
Joachim Eibl's avatar
0.9.86  
Joachim Eibl committed
589
      {
Joachim Eibl's avatar
Joachim Eibl committed
590
         pBuf[i] = pBuf[i].toUpper();
Joachim Eibl's avatar
0.9.86  
Joachim Eibl committed
591
      }
Joachim Eibl's avatar
Joachim Eibl committed
592 593
   }

Joachim Eibl's avatar
Joachim Eibl committed
594 595 596 597 598 599 600 601 602 603
   // Ignore comments
   if ( m_pOptionDialog->m_bIgnoreComments )
   {
      m_lmppData.removeComments();
      int vSize = min2(m_normalData.m_vSize, m_lmppData.m_vSize);
      for(int i=0; i<vSize; ++i )
      {
         m_normalData.m_v[i].bContainsPureComment = m_lmppData.m_v[i].bContainsPureComment;
      }
   }
Joachim Eibl's avatar
Joachim Eibl committed
604

Joachim Eibl's avatar
Joachim Eibl committed
605 606 607
   // Remove unneeded temporary files. (A temp file from clipboard must not be deleted.)
   if ( !bTempFileFromClipboard && !m_tempInputFileName.isEmpty() )
   {
Joachim Eibl's avatar
Joachim Eibl committed
608
      FileAccess::removeTempFile( m_tempInputFileName );
Joachim Eibl's avatar
Joachim Eibl committed
609 610
      m_tempInputFileName = "";
   }
Joachim Eibl's avatar
Joachim Eibl committed
611

Joachim Eibl's avatar
Joachim Eibl committed
612 613
   if ( !fileNameOut1.isEmpty() )
   {
Joachim Eibl's avatar
Joachim Eibl committed
614
      FileAccess::removeTempFile( fileNameOut1 );
Joachim Eibl's avatar
Joachim Eibl committed
615 616 617 618 619
      fileNameOut1="";
   }
}


Joachim Eibl's avatar
Joachim Eibl committed
620
/** Prepare the linedata vector for every input line.*/
Joachim Eibl's avatar
Joachim Eibl committed
621
void SourceData::FileData::preprocess( bool bPreserveCR, QTextCodec* pEncoding )
Joachim Eibl's avatar
Joachim Eibl committed
622
{
Joachim Eibl's avatar
Joachim Eibl committed
623 624
   //m_unicodeBuf = decodeString( m_pBuf, m_size, eEncoding );

Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
625 626 627 628 629 630 631
   qint64 i;
   // detect line end style
   m_eLineEndStyle = eLineEndStyleUndefined;
   for( i=0; i<m_size; ++i )
   {
      if ( m_pBuf[i]=='\n' )
      {
Joachim Eibl's avatar
Joachim Eibl committed
632 633
         if ( (i>0 && m_pBuf[i-1]=='\r') ||  // normal file
              (i>3 && m_pBuf[i-1]=='\0' && m_pBuf[i-2]=='\r' && m_pBuf[i-3]=='\0')) // 16-bit unicode
Joachim Eibl's avatar
0.9.93  
Joachim Eibl committed
634 635 636 637 638 639
            m_eLineEndStyle = eLineEndStyleDos;
         else
            m_eLineEndStyle = eLineEndStyleUnix;
         break;   // Only analyze first line
      }
   }
Joachim Eibl's avatar
Joachim Eibl committed
640 641 642 643 644 645
   qint64 skipBytes = 0;
   QTextCodec* pCodec = ::detectEncoding( m_pBuf, m_size, skipBytes );
   if ( pCodec != pEncoding )
      skipBytes=0;

   QByteArray ba = QByteArray::fromRawData( m_pBuf+skipBytes, m_size-skipBytes );
Joachim Eibl's avatar
Joachim Eibl committed
646
   QTextStream ts( ba, QIODevice::ReadOnly );
Joachim Eibl's avatar
Joachim Eibl committed
647
   ts.setCodec( pEncoding);
Joachim Eibl's avatar
Joachim Eibl committed
648 649 650
   ts.setAutoDetectUnicode( false );
   m_unicodeBuf = ts.readAll();
   ba.clear();
Joachim Eibl's avatar
Joachim Eibl committed
651 652 653 654

   int ucSize = m_unicodeBuf.length();
   const QChar* p = m_unicodeBuf.unicode();

Joachim Eibl's avatar
Joachim Eibl committed
655 656
   m_bIsText = true;
   int lines = 1;
Joachim Eibl's avatar
Joachim Eibl committed
657
   for( i=0; i<ucSize; ++i )
Joachim Eibl's avatar
Joachim Eibl committed
658
   {
Joachim Eibl's avatar
Joachim Eibl committed
659
      if ( isLineOrBufEnd(p,i,ucSize) )
Joachim Eibl's avatar
Joachim Eibl committed
660 661 662 663 664 665 666 667 668 669 670 671 672 673
      {
         ++lines;
      }
      if ( p[i]=='\0' )
      {
         m_bIsText = false;
      }
   }

   m_v.resize( lines+5 );
   int lineIdx=0;
   int lineLength=0;
   bool bNonWhiteFound = false;
   int whiteLength = 0;
Joachim Eibl's avatar
Joachim Eibl committed
674
   for( i=0; i<=ucSize; ++i )
Joachim Eibl's avatar
Joachim Eibl committed
675
   {
Joachim Eibl's avatar
Joachim Eibl committed
676
      if ( isLineOrBufEnd( p, i, ucSize ) )
Joachim Eibl's avatar
Joachim Eibl committed
677 678
      {
         m_v[lineIdx].pLine = &p[ i-lineLength ];
679 680 681 682
         while ( !bPreserveCR  &&  lineLength>0  &&  m_v[lineIdx].pLine[lineLength-1]=='\r'  )
         {
            --lineLength;
         }
683
         m_v[lineIdx].pFirstNonWhiteChar = m_v[lineIdx].pLine + min2(whiteLength,lineLength);
Joachim Eibl's avatar
Joachim Eibl committed
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
         m_v[lineIdx].size = lineLength;
         lineLength = 0;
         bNonWhiteFound = false;
         whiteLength = 0;
         ++lineIdx;
      }
      else
      {
         ++lineLength;

         if ( ! bNonWhiteFound && isWhite( p[i] ) )
            ++whiteLength;
         else
            bNonWhiteFound = true;
      }
   }
   assert( lineIdx == lines );

   m_vSize = lines;
}

Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
705 706 707 708 709 710 711

// Must not be entered, when within a comment.
// Returns either at a newline-character p[i]=='\n' or when i==size.
// A line that contains only comments is still "white".
// Comments in white lines must remain, while comments in
// non-white lines are overwritten with spaces.
static void checkLineForComments(
Joachim Eibl's avatar
Joachim Eibl committed
712
   QChar* p,   // pointer to start of buffer
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
713 714 715 716 717 718
   int& i,    // index of current position (in, out)
   int size,  // size of buffer
   bool& bWhite,          // false if this line contains nonwhite characters (in, out)
   bool& bCommentInLine,  // true if any comment is within this line (in, out)
   bool& bStartsOpenComment  // true if the line ends within an comment (out)
   )
Joachim Eibl's avatar
Joachim Eibl committed
719
{
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
720 721 722 723 724 725 726 727 728
   bStartsOpenComment = false;
   for(; i<size; ++i )
   {
      // A single apostroph ' has prio over a double apostroph " (e.g. '"')
      // (if not in a string)
      if ( p[i]=='\'' )
      {
         bWhite = false;
         ++i;
Joachim Eibl's avatar
Joachim Eibl committed
729
         for( ; !isLineOrBufEnd(p,i,size) && p[i]!='\''; ++i)
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
730 731 732 733 734 735 736 737 738
            ;
         if (p[i]=='\'') ++i;
      }

      // Strings have priority over comments: e.g. "/* Not a comment, but a string. */"
      else if ( p[i]=='"' )
      {
         bWhite = false;
         ++i;
Joachim Eibl's avatar
Joachim Eibl committed
739
         for( ; !isLineOrBufEnd(p,i,size) && !(p[i]=='"' && p[i-1]!='\\'); ++i)
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
740 741 742 743 744 745 746 747 748 749
            ;
         if (p[i]=='"') ++i;
      }

      // C++-comment
      else if ( p[i]=='/' && i+1<size && p[i+1] =='/' )
      {
         int commentStart = i;
         bCommentInLine = true;
         i+=2;
Joachim Eibl's avatar
Joachim Eibl committed
750
         for( ; !isLineOrBufEnd(p,i,size); ++i)
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
751 752 753 754 755 756 757 758 759 760 761 762 763 764
            ;
         if ( !bWhite )
         {
            memset( &p[commentStart], ' ', i-commentStart );
         }
         return;
      }

      // C-comment
      else if ( p[i]=='/' && i+1<size && p[i+1] =='*' )
      {
         int commentStart = i;
         bCommentInLine = true;
         i+=2;
Joachim Eibl's avatar
Joachim Eibl committed
765
         for( ; !isLineOrBufEnd(p,i,size); ++i)
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
         {
            if ( i+1<size && p[i]=='*' && p[i+1]=='/')  // end of the comment
            {
               i+=2;

               // More comments in the line?
               checkLineForComments( p, i, size, bWhite, bCommentInLine, bStartsOpenComment );
               if ( !bWhite )
               {
                  memset( &p[commentStart], ' ', i-commentStart );
               }
               return;
            }
         }
         bStartsOpenComment = true;
         return;
      }


Joachim Eibl's avatar
Joachim Eibl committed
785
      if ( isLineOrBufEnd(p,i,size) )
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
786 787 788
      {
         return;
      }
Joachim Eibl's avatar
Joachim Eibl committed
789
      else if ( !p[i].isSpace() )
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
790 791 792 793 794 795 796 797 798
      {
         bWhite = false;
      }
   }
}

// Modifies the input data, and replaces C/C++ comments with whitespace
// when the line contains other data too. If the line contains only
// a comment or white data, remember this in the flag bContainsPureComment.
Joachim Eibl's avatar
Joachim Eibl committed
799
void SourceData::FileData::removeComments()
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
800 801
{
   int line=0;
Joachim Eibl's avatar
Joachim Eibl committed
802
   QChar* p = const_cast<QChar*>(m_unicodeBuf.unicode());
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
803
   bool bWithinComment=false;
Joachim Eibl's avatar
Joachim Eibl committed
804
   int size = m_unicodeBuf.length();
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
805 806 807 808 809 810 811 812 813 814 815
   for(int i=0; i<size; ++i )
   {
//      std::cout << "2        " << std::string(&p[i], m_v[line].size) << std::endl;
      bool bWhite = true;
      bool bCommentInLine = false;

      if ( bWithinComment )
      {
         int commentStart = i;
         bCommentInLine = true;

Joachim Eibl's avatar
Joachim Eibl committed
816
         for( ; !isLineOrBufEnd(p,i,size); ++i)
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
         {
            if ( i+1<size && p[i]=='*' && p[i+1]=='/')  // end of the comment
            {
               i+=2;

               // More comments in the line?
               checkLineForComments( p, i, size, bWhite, bCommentInLine, bWithinComment );
               if ( !bWhite )
               {
                  memset( &p[commentStart], ' ', i-commentStart );
               }
               break;
            }
         }
      }
      else
      {
         checkLineForComments( p, i, size, bWhite, bCommentInLine, bWithinComment );
      }

      // end of line
Joachim Eibl's avatar
Joachim Eibl committed
838 839
      assert( isLineOrBufEnd(p,i,size));
      m_v[line].bContainsPureComment = bCommentInLine && bWhite;
Joachim Eibl's avatar
0.9.80  
Joachim Eibl committed
840 841 842 843 844 845 846 847 848
/*      std::cout << line << " : " <<
       ( bCommentInLine ?  "c" : " " ) <<
       ( bWhite ? "w " : "  ") <<
       std::string(pLD[line].pLine, pLD[line].size) << std::endl;*/

      ++line;
   }
}

Joachim Eibl's avatar
Joachim Eibl committed
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908


// First step
void calcDiff3LineListUsingAB(
   const DiffList* pDiffListAB,
   Diff3LineList& d3ll
   )
{
   // First make d3ll for AB (from pDiffListAB)

   DiffList::const_iterator i=pDiffListAB->begin();
   int lineA=0;
   int lineB=0;
   Diff d(0,0,0);

   for(;;)
   {
      if ( d.nofEquals==0 && d.diff1==0 && d.diff2==0 )
      {
         if ( i!=pDiffListAB->end() )
         {
            d=*i;
            ++i;
         }
         else
            break;
      }

      Diff3Line d3l;
      if( d.nofEquals>0 )
      {
         d3l.bAEqB = true;
         d3l.lineA = lineA;
         d3l.lineB = lineB;
         --d.nofEquals;
         ++lineA;
         ++lineB;
      }
      else if ( d.diff1>0 && d.diff2>0 )
      {
         d3l.lineA = lineA;
         d3l.lineB = lineB;
         --d.diff1;
         --d.diff2;
         ++lineA;
         ++lineB;
      }
      else if ( d.diff1>0 )
      {
         d3l.lineA = lineA;
         --d.diff1;
         ++lineA;
      }
      else if ( d.diff2>0 )
      {
         d3l.lineB = lineB;
         --d.diff2;
         ++lineB;
      }

Joachim Eibl's avatar
Joachim Eibl committed
909
      d3ll.push_back( d3l );
Joachim Eibl's avatar
Joachim Eibl committed
910 911 912 913 914
   }
}


// Second step
Joachim Eibl's avatar
Joachim Eibl committed
915
void calcDiff3LineListUsingAC(
Joachim Eibl's avatar
Joachim Eibl committed
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
   const DiffList* pDiffListAC,
   Diff3LineList& d3ll
   )
{
   ////////////////
   // Now insert data from C using pDiffListAC

   DiffList::const_iterator i=pDiffListAC->begin();
   Diff3LineList::iterator i3 = d3ll.begin();
   int lineA=0;
   int lineC=0;
   Diff d(0,0,0);

   for(;;)
   {
      if ( d.nofEquals==0 && d.diff1==0 && d.diff2==0 )
      {
         if ( i!=pDiffListAC->end() )
         {
            d=*i;
            ++i;
         }
         else
            break;
      }

      Diff3Line d3l;
      if( d.nofEquals>0 )
      {
         // Find the corresponding lineA
Joachim Eibl's avatar
Joachim Eibl committed
946
         while( (*i3).lineA!=lineA )
Joachim Eibl's avatar
Joachim Eibl committed
947
            ++i3;
Joachim Eibl's avatar
Joachim Eibl committed
948

Joachim Eibl's avatar
Joachim Eibl committed
949 950 951
         (*i3).lineC = lineC;
         (*i3).bAEqC = true;
         (*i3).bBEqC = (*i3).bAEqB;
Joachim Eibl's avatar
Joachim Eibl committed
952

Joachim Eibl's avatar
Joachim Eibl committed
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982
         --d.nofEquals;
         ++lineA;
         ++lineC;
         ++i3;
      }
      else if ( d.diff1>0 && d.diff2>0 )
      {
         d3l.lineC = lineC;
         d3ll.insert( i3, d3l );
         --d.diff1;
         --d.diff2;
         ++lineA;
         ++lineC;
      }
      else if ( d.diff1>0 )
      {
         --d.diff1;
         ++lineA;
      }
      else if ( d.diff2>0 )
      {
         d3l.lineC = lineC;
         d3ll.insert( i3, d3l );
         --d.diff2;
         ++lineC;
      }
   }
}

// Third step
Joachim Eibl's avatar
Joachim Eibl committed
983
void calcDiff3LineListUsingBC(
Joachim Eibl's avatar
Joachim Eibl committed
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
   const DiffList* pDiffListBC,
   Diff3LineList& d3ll
   )
{
   ////////////////
   // Now improve the position of data from C using pDiffListBC
   // If a line from C equals a line from A then it is in the
   // same Diff3Line already.
   // If a line from C equals a line from B but not A, this
   // information will be used here.

   DiffList::const_iterator i=pDiffListBC->begin();
   Diff3LineList::iterator i3b = d3ll.begin();
   Diff3LineList::iterator i3c = d3ll.begin();
   int lineB=0;
   int lineC=0;
   Diff d(0,0,0);

   for(;;)
   {
      if ( d.nofEquals==0 && d.diff1==0 && d.diff2==0 )
      {
         if ( i!=pDiffListBC->end() )
         {
            d=*i;
            ++i;
         }
         else
            break;
      }

      Diff3Line d3l;
      if( d.nofEquals>0 )
      {
         // Find the corresponding lineB and lineC
1019
         while( i3b!=d3ll.end() && (*i3b).lineB!=lineB )
Joachim Eibl's avatar
Joachim Eibl committed
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
            ++i3b;

         while( i3c!=d3ll.end() && (*i3c).lineC!=lineC )
            ++i3c;

         assert(i3b!=d3ll.end());
         assert(i3c!=d3ll.end());

         if ( i3b==i3c )
         {
            assert( (*i3b).lineC == lineC );
            (*i3b).bBEqC = true;
         }
Joachim Eibl's avatar
Joachim Eibl committed
1033
         else
Joachim Eibl's avatar
Joachim Eibl committed
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
         {
            // Is it possible to move this line up?
            // Test if no other B's are used between i3c and i3b

            // First test which is before: i3c or i3b ?
            Diff3LineList::iterator i3c1 = i3c;
            Diff3LineList::iterator i3b1 = i3b;
            while( i3c1!=i3b  &&  i3b1!=i3c )
            {
               assert(i3b1!=d3ll.end() || i3c1!=d3ll.end());
               if( i3c1!=d3ll.end() ) ++i3c1;
               if( i3b1!=d3ll.end() ) ++i3b1;
            }

            if( i3c1==i3b  &&  !(*i3b).bAEqB ) // i3c before i3b
            {
               Diff3LineList::iterator i3 = i3c;
               int nofDisturbingLines = 0;
               while( i3 != i3b && i3!=d3ll.end() )
               {
Joachim Eibl's avatar
Joachim Eibl committed
1054
                  if ( (*i3).lineB != -1 )
Joachim Eibl's avatar
Joachim Eibl committed
1055 1056 1057 1058
                     ++nofDisturbingLines;
                  ++i3;
               }

Joachim Eibl's avatar
Joachim Eibl committed
1059
               if ( nofDisturbingLines>0 )//&& nofDisturbingLines < d.nofEquals*d.nofEquals+4 )
Joachim Eibl's avatar
Joachim Eibl committed
1060 1061 1062 1063 1064
               {
                  // Move the disturbing lines up, out of sight.
                  i3 = i3c;
                  while( i3 != i3b )
                  {
Joachim Eibl's avatar
Joachim Eibl committed
1065
                     if ( (*i3).lineB != -1 )
Joachim Eibl's avatar
Joachim Eibl committed
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
                     {
                        Diff3Line d3l;
                        d3l.lineB = (*i3).lineB;
                        (*i3).lineB = -1;
                        (*i3).bAEqB = false;
                        (*i3).bBEqC = false;
                        d3ll.insert( i3c, d3l );
                     }
                     ++i3;
                  }
                  nofDisturbingLines=0;
               }

               if ( nofDisturbingLines == 0 )
               {
                  // Yes, the line from B can be moved.
                  (*i3b).lineB = -1;   // This might leave an empty line: removed later.
                  (*i3b).bAEqB = false;
                  (*i3b).bAEqC = false;
                  (*i3b).bBEqC = false;
                  (*i3c).lineB = lineB;
                  (*i3c).bBEqC = true;
               }
            }
Joachim Eibl's avatar
Joachim Eibl committed
1090
            else if( i3b1==i3c  &&  !(*i3c).bAEqC)
Joachim Eibl's avatar
Joachim Eibl committed
1091 1092 1093 1094 1095
            {
               Diff3LineList::iterator i3 = i3b;
               int nofDisturbingLines = 0;
               while( i3 != i3c && i3!=d3ll.end() )
               {
Joachim Eibl's avatar
Joachim Eibl committed
1096
                  if ( (*i3).lineC != -1 )
Joachim Eibl's avatar
Joachim Eibl committed
1097 1098 1099 1100
                     ++nofDisturbingLines;
                  ++i3;
               }

Joachim Eibl's avatar
Joachim Eibl committed
1101
               if ( nofDisturbingLines>0 )//&& nofDisturbingLines < d.nofEquals*d.nofEquals+4 )
Joachim Eibl's avatar
Joachim Eibl committed
1102
               {
Joachim Eibl's avatar
Joachim Eibl committed
1103
                  // Move the disturbing lines up.
Joachim Eibl's avatar
Joachim Eibl committed
1104 1105 1106
                  i3 = i3b;
                  while( i3 != i3c )
                  {
Joachim Eibl's avatar
Joachim Eibl committed
1107
                     if ( (*i3).lineC != -1 )
Joachim Eibl's avatar
Joachim Eibl committed
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
                     {
                        Diff3Line d3l;
                        d3l.lineC = (*i3).lineC;
                        (*i3).lineC = -1;
                        (*i3).bAEqC = false;
                        (*i3).bBEqC = false;
                        d3ll.insert( i3b, d3l );
                     }
                     ++i3;
                  }
                  nofDisturbingLines=0;
               }

               if ( nofDisturbingLines == 0 )
               {
                  // Yes, the line from C can be moved.
                  (*i3c).lineC = -1;   // This might leave an empty line: removed later.
                  (*i3c).bAEqC = false;
                  (*i3c).bBEqC = false;
                  (*i3b).lineC = lineC;
                  (*i3b).bBEqC = true;
               }
            }
         }
Joachim Eibl's avatar
Joachim Eibl committed
1132

Joachim Eibl's avatar
Joachim Eibl committed
1133 1134 1135 1136 1137 1138 1139 1140 1141
         --d.nofEquals;
         ++lineB;
         ++lineC;
         ++i3b;
         ++i3c;
      }
      else if ( d.diff1>0 )
      {
         Diff3LineList::iterator i3 = i3b;
1142
         while( (*i3).lineB!=lineB )
Joachim Eibl's avatar
Joachim Eibl committed
1143 1144 1145
            ++i3;
         if( i3 != i3b  &&  (*i3).bAEqB==false )
         {
Joachim Eibl's avatar
Joachim Eibl committed
1146
            // Take B from this line and move it up as far as possible
Joachim Eibl's avatar
Joachim Eibl committed
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175
            d3l.lineB = lineB;
            d3ll.insert( i3b, d3l );
            (*i3).lineB = -1;
         }
         else
         {
            i3b=i3;
         }
         --d.diff1;
         ++lineB;
         ++i3b;

         if( d.diff2>0 )
         {
            --d.diff2;
            ++lineC;
         }
      }
      else if ( d.diff2>0 )
      {
         --d.diff2;
         ++lineC;
      }
   }
/*
   Diff3LineList::iterator it = d3ll.begin();
   int li=0;
   for( ; it!=d3ll.end(); ++it, ++li )
   {
Joachim Eibl's avatar
Joachim Eibl committed
1176 1177
      printf( "%4d %4d %4d %4d  A%c=B A%c=C B%c=C\n",
         li, (*it).lineA, (*it).lineB, (*it).lineC,
Joachim Eibl's avatar
Joachim Eibl committed
1178 1179 1180 1181 1182 1183 1184 1185 1186
         (*it).bAEqB ? '=' : '!', (*it).bAEqC ? '=' : '!', (*it).bBEqC ? '=' : '!' );
   }
   printf("\n");*/
}

#ifdef _WIN32
using ::equal;
#endif

Joachim Eibl's avatar
Joachim Eibl committed
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
// Test if the move would pass a barrier. Return true if not.
static bool isValidMove( ManualDiffHelpList* pManualDiffHelpList, int line1, int line2, int winIdx1, int winIdx2 )
{
   if (line1>=0 && line2>=0)
   {
      ManualDiffHelpList::const_iterator i;
      for( i = pManualDiffHelpList->begin(); i!=pManualDiffHelpList->end(); ++i )
      {
         const ManualDiffHelpEntry& mdhe = *i;

         // Barrier
         int l1 = winIdx1 == 1 ? mdhe.lineA1 : winIdx1==2 ? mdhe.lineB1 : mdhe.lineC1 ;
         int l2 = winIdx2 == 1 ? mdhe.lineA1 : winIdx2==2 ? mdhe.lineB1 : mdhe.lineC1 ;

         if ( l1>=0 && l2>=0 )
         {
Joachim Eibl's avatar
Joachim Eibl committed
1203
            if ( (line1>=l1 && line2<l2) || (line1<l1 && line2>=l2) )
Joachim Eibl's avatar
Joachim Eibl committed
1204 1205 1206 1207 1208
               return false;
            l1 = winIdx1 == 1 ? mdhe.lineA2 : winIdx1==2 ? mdhe.lineB2 : mdhe.lineC2 ;
            l2 = winIdx2 == 1 ? mdhe.lineA2 : winIdx2==2 ? mdhe.lineB2 : mdhe.lineC2 ;
            ++l1;
            ++l2;
Joachim Eibl's avatar
Joachim Eibl committed
1209
            if ( (line1>=l1 && line2<l2) || (line1<l1 && line2>=l2) )
Joachim Eibl's avatar
Joachim Eibl committed
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340
               return false;
         }
      }
   }
   return true; // no barrier passed.
}

void correctManualDiffAlignment( Diff3LineList& d3ll, ManualDiffHelpList* pManualDiffHelpList )
{
   if ( pManualDiffHelpList->empty() )
      return;

   // If a line appears unaligned in comparison to the manual alignment, correct this.

   ManualDiffHelpList::iterator iMDHL;
   for( iMDHL =  pManualDiffHelpList->begin(); iMDHL != pManualDiffHelpList->end(); ++iMDHL )
   {
      Diff3LineList::iterator i3 = d3ll.begin();
      int winIdxPreferred = 0;
      int missingWinIdx = 0;
      int alignedSum = (iMDHL->lineA1<0?0:1) + (iMDHL->lineB1<0?0:1) + (iMDHL->lineC1<0?0:1);
      if (alignedSum==2)
      {
         // If only A & B are aligned then let C rather be aligned with A
         // If only A & C are aligned then let B rather be aligned with A
         // If only B & C are aligned then let A rather be aligned with B
         missingWinIdx = iMDHL->lineA1<0 ? 1 : (iMDHL->lineB1<0 ? 2 : 3 );
         winIdxPreferred = missingWinIdx == 1 ? 2 : 1; 
      }
      else if (alignedSum<=1)
      {
         return;
      }

      // At the first aligned line, move up the two other lines into new d3ls until the second input is aligned
      // Then move up the third input until all three lines are aligned.
      int wi=0;
      for( ; i3!=d3ll.end(); ++i3 )
      {
         for ( wi=1; wi<=3; ++wi )
         {
            if ( i3->getLineInFile(wi) >= 0 && iMDHL->firstLine(wi) == i3->getLineInFile(wi) )
               break;
         }
         if ( wi<=3 )
            break;
      }

      if (wi>=1 && wi <= 3)
      {
         // Found manual alignment for one source
         Diff3LineList::iterator iDest = i3;

         // Move lines up until the next firstLine is found. Omit wi from move and search.
         int wi2=0;
         for( ; i3!=d3ll.end(); ++i3 )
         {
            for ( wi2=1; wi2<=3; ++wi2 )
            {
               if ( wi!=wi2 && i3->getLineInFile(wi2) >= 0 && iMDHL->firstLine(wi2) == i3->getLineInFile(wi2) )
                  break;
            }
            if (wi2>3)
            {  // Not yet found
               // Move both others up
               Diff3Line d3l;
               // Move both up
               if (wi==1) // Move B and C up
               {
                  d3l.bBEqC = i3->bBEqC;
                  d3l.lineB = i3->lineB;
                  d3l.lineC = i3->lineC;
                  i3->lineB = -1;
                  i3->lineC = -1;
               }
               if (wi==2) // Move A and C up
               {
                  d3l.bAEqC = i3->bAEqC;
                  d3l.lineA = i3->lineA;
                  d3l.lineC = i3->lineC;
                  i3->lineA = -1;
                  i3->lineC = -1;
               }
               if (wi==3) // Move A and B up
               {
                  d3l.bAEqB = i3->bAEqB;
                  d3l.lineA = i3->lineA;
                  d3l.lineB = i3->lineB;
                  i3->lineA = -1;
                  i3->lineB = -1;
               }
               i3->bAEqB = false;
               i3->bAEqC = false;
               i3->bBEqC = false;
               d3ll.insert( iDest, d3l );
            }
            else 
            {
               // align the found line with the line we already have here
               if ( i3 != iDest )
               {
                  if (wi2==1)
                  {
                     iDest->lineA = i3->lineA;
                     i3->lineA = -1;
                     i3->bAEqB = false;
                     i3->bAEqC = false;                   
                  }
                  else if (wi2==2)
                  {
                     iDest->lineB = i3->lineB;
                     i3->lineB = -1;
                     i3->bAEqB = false;
                     i3->bBEqC = false;                   
                  }
                  else if (wi2==3)
                  {
                     iDest->lineC = i3->lineC;
                     i3->lineC = -1;
                     i3->bBEqC = false;
                     i3->bAEqC = false;                   
                  }
               }

               if ( missingWinIdx!=0 )
               {
                  for( ; i3!=d3ll.end(); ++i3 )
                  {
                     int wi3 = missingWinIdx;
                     if ( i3->getLineInFile(wi3) >= 0 )
                     {
Joachim Eibl's avatar
Joachim Eibl committed
1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352
                        // not found, move the line before iDest
                        Diff3Line d3l;
                        if ( wi3==1 )
                        {
                           if (i3->bAEqB)  // Stop moving lines up if one equal is found.
                              break;
                           d3l.lineA = i3->lineA;
                           i3->lineA = -1;
                           i3->bAEqB = false;
                           i3->bAEqC = false;
                        }
                        if ( wi3==2 )
Joachim Eibl's avatar
Joachim Eibl committed
1353
                        {
Joachim Eibl's avatar
Joachim Eibl committed
1354 1355 1356 1357 1358 1359
                           if (i3->bAEqB)
                              break;
                           d3l.lineB = i3->lineB;
                           i3->lineB = -1;
                           i3->bAEqB = false;
                           i3->bBEqC = false;
Joachim Eibl's avatar
Joachim Eibl committed
1360
                        }
Joachim Eibl's avatar
Joachim Eibl committed
1361
                        if ( wi3==3 )
Joachim Eibl's avatar
Joachim Eibl committed
1362
                        {
Joachim Eibl's avatar
Joachim Eibl committed
1363 1364 1365 1366 1367 1368
                           if (i3->bAEqC)
                              break;
                           d3l.lineC = i3->lineC;
                           i3->lineC = -1;
                           i3->bAEqC = false;
                           i3->bBEqC = false;
Joachim Eibl's avatar
Joachim Eibl committed
1369
                        }
Joachim Eibl's avatar
Joachim Eibl committed
1370
                        d3ll.insert( iDest, d3l );
Joachim Eibl's avatar
Joachim Eibl committed
1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
                     }
                  } // for(), searching for wi3
               }
               break;
            }
         } // for(), searching for wi2
      } // if, wi found
   } // for (iMDHL)
}

Joachim Eibl's avatar
Joachim Eibl committed
1381
// Fourth step
Joachim Eibl's avatar
Joachim Eibl committed
1382
void calcDiff3LineListTrim(
Joachim Eibl's avatar
Joachim Eibl committed
1383
   Diff3LineList& d3ll, const LineData* pldA, const LineData* pldB, const LineData* pldC, ManualDiffHelpList* pManualDiffHelpList
Joachim Eibl's avatar
Joachim Eibl committed
1384 1385 1386 1387 1388 1389 1390 1391 1392 1393
   )
{
   const Diff3Line d3l_empty;
   d3ll.remove( d3l_empty );

   Diff3LineList::iterator i3 = d3ll.begin();
   Diff3LineList::iterator i3A = d3ll.begin();
   Diff3LineList::iterator i3B = d3ll.begin();
   Diff3LineList::iterator i3C = d3ll.begin();

Joachim Eibl's avatar
Joachim Eibl committed
1394 1395
   int line=0;  // diff3line counters
   int lineA=0; // 
Joachim Eibl's avatar
Joachim Eibl committed
1396 1397 1398
   int lineB=0;
   int lineC=0;

Joachim Eibl's avatar
Joachim Eibl committed
1399
   ManualDiffHelpList::iterator iMDHL = pManualDiffHelpList->begin();
Joachim Eibl's avatar
Joachim Eibl committed
1400 1401 1402 1403 1404 1405
   // The iterator i3 and the variable line look ahead.
   // The iterators i3A, i3B, i3C and corresponding lineA, lineB and lineC stop at empty lines, if found.
   // If possible, then the te