Mercurial > hg > easyhg-kdiff3
diff kdiff3/src/diff.cpp @ 58:8af4bb9d9a5a
Version 0.9.83
author | joachim99 |
---|---|
date | Sun, 07 Mar 2004 09:59:09 +0000 |
parents | 32d5cbf9db71 |
children | efe33e938730 |
line wrap: on
line diff
--- a/kdiff3/src/diff.cpp Sat Jan 31 14:25:47 2004 +0000 +++ b/kdiff3/src/diff.cpp Sun Mar 07 09:59:09 2004 +0000 @@ -2,7 +2,7 @@ diff.cpp - description ------------------- begin : Mon Mar 18 2002 - copyright : (C) 2002 by Joachim Eibl + copyright : (C) 2002-2004 by Joachim Eibl email : joachim.eibl@gmx.de ***************************************************************************/ @@ -20,6 +20,7 @@ #include "diff.h" #include "fileaccess.h" +#include "optiondialog.h" #include <kmessagebox.h> #include <klocale.h> @@ -32,7 +33,7 @@ //using namespace std; -int LineData::width() +int LineData::width() const { int w=0; int j=0; @@ -110,64 +111,424 @@ } -// class needed during preprocess phase -class LineDataRef + + +static bool isLineOrBufEnd( const char* p, int i, int size ) { - const LineData* m_pLd; -public: - LineDataRef(const LineData* pLd){ m_pLd = pLd; } + return + i>=size // End of file + || p[i]=='\n' // Normal end of line - bool operator<(const LineDataRef& ldr2) const - { - const LineData* pLd1 = m_pLd; - const LineData* pLd2 = ldr2.m_pLd; - const char* p1 = pLd1->pFirstNonWhiteChar; - const char* p2 = pLd2->pFirstNonWhiteChar; + // 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' + ; +} - int size1=pLd1->size; - int size2=pLd2->size; - int i1=min2(pLd1->pFirstNonWhiteChar - pLd1->pLine,size1); - int i2=min2(pLd2->pFirstNonWhiteChar - pLd2->pLine,size2); - for(;;) - { - while( i1<size1 && isWhite( p1[i1] ) ) ++i1; - while( i2<size2 && isWhite( p2[i2] ) ) ++i2; - if ( i1==size1 || i2==size2 ) - { - if ( i1==size1 && i2==size2 ) return false; // Equal - if ( i1==size1 ) return true; // String 1 is shorter than string 2 - if ( i2==size2 ) return false; // String 1 is longer than string 2 - } - if ( p1[i1]==p2[i2] ) { ++i1; ++i2; continue; } - return p1[i1]<p2[i2]; - } - } -}; +/* 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. +- Run other preprocessing steps: Uppercase, ignore comments, + 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() +{ + m_pOptionDialog = 0; + reset(); +} + +SourceData::~SourceData() +{ + reset(); +} void SourceData::reset() { + m_normalData.reset(); + m_lmppData.reset(); + if ( !m_tempInputFileName.isEmpty() ) + { + FileAccess::removeFile( m_tempInputFileName ); + m_tempInputFileName = ""; + } +} + +void SourceData::setFilename( const QString& filename ) +{ + if (filename.isEmpty()) + { + reset(); + } + else + { + FileAccess fa( filename ); + setFileAccess( fa ); + } +} + +bool SourceData::isEmpty() +{ + return getFilename().isEmpty(); +} + +bool SourceData::hasData() +{ + return m_normalData.m_pBuf != 0; +} + +void SourceData::setOptionDialog( OptionDialog* pOptionDialog ) +{ + m_pOptionDialog = pOptionDialog; +} + +QString SourceData::getFilename() +{ + return m_fileAccess.absFilePath(); +} + +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 = ""; + } +} + +void SourceData::setData( const QString& data ) +{ + // Create a temp file for preprocessing: + if ( m_tempInputFileName.isEmpty() ) + { + m_tempInputFileName = FileAccess::tempFileName(); + } + + FileAccess f( m_tempInputFileName ); + bool bSuccess = f.writeFile( encodeString(data, m_pOptionDialog), data.length() ); + if ( !bSuccess ) + { + KMessageBox::error( m_pOptionDialog, i18n("Writing clipboard data to temp file failed.") ); + return; + } + + m_aliasName = i18n("From Clipboard"); + m_fileAccess = FileAccess(""); // Effect: m_fileAccess.isValid() is false +} + +const LineData* SourceData::getLineDataForDiff() const +{ + return m_lmppData.m_pBuf==0 ? &m_normalData.m_v[0] : &m_lmppData.m_v[0]; +} + +const LineData* SourceData::getLineDataForDisplay() const +{ + return &m_normalData.m_v[0]; +} + +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 +{ + return getSizeBytes() == other.getSizeBytes() && memcmp( getBuf(), other.getBuf(), getSizeBytes() )==0; +} + +void SourceData::FileData::reset() +{ delete (char*)m_pBuf; m_pBuf = 0; m_v.clear(); m_size = 0; m_vSize = 0; - m_bIsText = false; - m_bPreserve = false; - m_fileAccess = FileAccess(""); + m_bIsText = true; } +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. + 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(); + char* pBuf; + m_size = src.m_size; + m_pBuf = pBuf = new char[m_size+100]; + memcpy( pBuf, src.m_pBuf, m_size ); +} + +void SourceData::readAndPreprocess() +{ + QString fileNameIn1; + QString fileNameOut1; + QString fileNameIn2; + QString fileNameOut2; + + bool bTempFileFromClipboard = !m_fileAccess.isValid(); + + // Detect the input for the preprocessing operations + if ( !bTempFileFromClipboard ) + { + if ( m_fileAccess.isLocal() ) + { + fileNameIn1 = m_fileAccess.absFilePath(); + } + else // File is not local: create a temporary local copy: + { + if ( m_tempInputFileName.isEmpty() ) { m_tempInputFileName = FileAccess::tempFileName(); } + + m_fileAccess.copyFile(m_tempInputFileName); + fileNameIn1 = m_tempInputFileName; + } + } + else // The input was set via setData(), probably from clipboard. + { + fileNameIn1 = m_tempInputFileName; + } + + m_normalData.reset(); + m_lmppData.reset(); + + FileAccess faIn(fileNameIn1); + int fileInSize = faIn.size(); + + if ( fileInSize > 0 ) + { + +#ifdef _WIN32 + QString catCmd = "type"; + fileNameIn1.replace( '/', "\\" ); +#else + QString catCmd = "cat"; +#endif + + // Run the first preprocessor + if ( m_pOptionDialog->m_PreProcessorCmd.isEmpty() ) + { + // No preprocessing: Read the file directly: + m_normalData.readFile( fileNameIn1 ); + } + else + { + QString ppCmd = m_pOptionDialog->m_PreProcessorCmd; + fileNameOut1 = FileAccess::tempFileName(); + QString cmd = catCmd + " \"" + fileNameIn1 + "\" | " + ppCmd + " >\"" + fileNameOut1+"\""; + ::system( encodeString(cmd, m_pOptionDialog) ); + 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." + ).arg(cmd) ); + m_pOptionDialog->m_PreProcessorCmd = ""; + m_normalData.readFile( fileNameIn1 ); + } + } + + // Internal Preprocessing: Uppercase-conversion + bool bInternalPreprocessing = false; + if ( m_pOptionDialog->m_bUpCase ) + { + int i; + char* pBuf = const_cast<char*>(m_normalData.m_pBuf); + for(i=0; i<m_normalData.m_size; ++i) + { + pBuf[i] = toupper(pBuf[i]); + } + + bInternalPreprocessing = true; + } + + // LineMatching Preprocessor + if ( ! m_pOptionDialog->m_LineMatchingPreProcessorCmd.isEmpty() ) + { + if ( bInternalPreprocessing ) + { + // write data to file after internal preprocessing before running the external LMPP-cmd. + if ( !fileNameOut1.isEmpty() ) + { + FileAccess::removeFile( fileNameOut1 ); + fileNameOut1=""; + } + + fileNameIn2 = FileAccess::tempFileName(); + bool bSuccess = m_normalData.writeFile( fileNameIn2 ); + if ( !bSuccess ) + { + KMessageBox::error(m_pOptionDialog, i18n("Error writing temporary file: %1").arg(fileNameIn2) ); + } + } + else + { + fileNameIn2 = fileNameOut1.isEmpty() ? fileNameIn1 : fileNameOut1; + } + + QString ppCmd = m_pOptionDialog->m_LineMatchingPreProcessorCmd; + fileNameOut2 = FileAccess::tempFileName(); + QString cmd = catCmd + " \"" + fileNameIn2 + "\" | " + ppCmd + " >\"" + fileNameOut2 + "\""; + ::system( encodeString(cmd, m_pOptionDialog) ); + 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." + ).arg(cmd) ); + m_pOptionDialog->m_LineMatchingPreProcessorCmd = ""; + m_lmppData.readFile( fileNameIn2 ); + } + FileAccess::removeFile( fileNameOut2 ); + + if ( bInternalPreprocessing && !fileNameIn2.isEmpty() ) + { + FileAccess::removeFile( fileNameIn2 ); + fileNameIn2=""; + } + } + else if ( m_pOptionDialog->m_bIgnoreComments ) + { + // 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(); + } + } + + m_normalData.preprocess( m_pOptionDialog->m_bPreserveCarriageReturn ); + m_lmppData.preprocess( false ); + + 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. + m_lmppData.m_v[i].pLine = m_lmppData.m_pBuf+m_lmppData.m_size; + } + + m_lmppData.m_vSize = m_normalData.m_vSize; + } + + // 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; + } + } + + // Remove unneeded temporary files. (A temp file from clipboard must not be deleted.) + if ( !bTempFileFromClipboard && !m_tempInputFileName.isEmpty() ) + { + FileAccess::removeFile( m_tempInputFileName ); + m_tempInputFileName = ""; + } + + if ( !fileNameOut1.isEmpty() ) + { + FileAccess::removeFile( fileNameOut1 ); + fileNameOut1=""; + } +} + + /** Prepare the linedata vector for every input line.*/ -void SourceData::preprocess( bool bPreserveCR ) +void SourceData::FileData::preprocess( bool bPreserveCR ) { const char* p = m_pBuf; m_bIsText = true; int lines = 1; - int i; for( i=0; i<m_size; ++i ) { - if (p[i]=='\n') + if ( isLineOrBufEnd(p,i,m_size) ) { ++lines; } @@ -184,7 +545,7 @@ int whiteLength = 0; for( i=0; i<=m_size; ++i ) { - if ( i==m_size || p[i]=='\n' ) // The last line does not end with a linefeed. + if ( isLineOrBufEnd( p, i, m_size ) ) { m_v[lineIdx].pLine = &p[ i-lineLength ]; while ( !bPreserveCR && lineLength>0 && m_v[lineIdx].pLine[lineLength-1]=='\r' ) @@ -237,7 +598,7 @@ { bWhite = false; ++i; - for( ; i<size && p[i]!='\'' && p[i]!='\n'; ++i) + for( ; !isLineOrBufEnd(p,i,size) && p[i]!='\''; ++i) ; if (p[i]=='\'') ++i; } @@ -247,7 +608,7 @@ { bWhite = false; ++i; - for( ; i<size && !(p[i]=='"' && p[i-1]!='\\') && p[i]!='\n'; ++i) + for( ; !isLineOrBufEnd(p,i,size) && !(p[i]=='"' && p[i-1]!='\\'); ++i) ; if (p[i]=='"') ++i; } @@ -258,7 +619,7 @@ int commentStart = i; bCommentInLine = true; i+=2; - for( ; i<size && p[i]!='\n'; ++i) + for( ; !isLineOrBufEnd(p,i,size); ++i) ; if ( !bWhite ) { @@ -273,7 +634,7 @@ int commentStart = i; bCommentInLine = true; i+=2; - for( ; i<size && p[i]!='\n'; ++i) + for( ; !isLineOrBufEnd(p,i,size); ++i) { if ( i+1<size && p[i]=='*' && p[i+1]=='/') // end of the comment { @@ -293,7 +654,7 @@ } - if (p[i]=='\n' || i>=size ) + if ( isLineOrBufEnd(p,i,size) ) { return; } @@ -307,7 +668,7 @@ // 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. -void SourceData::removeComments( LineData* pLD ) +void SourceData::FileData::removeComments() { int line=0; char* p = (char*)m_pBuf; @@ -324,7 +685,7 @@ int commentStart = i; bCommentInLine = true; - for( ; i<size && p[i]!='\n'; ++i) + for( ; !isLineOrBufEnd(p,i,size); ++i) { if ( i+1<size && p[i]=='*' && p[i+1]=='/') // end of the comment { @@ -346,8 +707,8 @@ } // end of line - assert( i>=size || p[i]=='\n'); - pLD[line].bContainsPureComment = bCommentInLine && bWhite; + assert( isLineOrBufEnd(p,i,size)); + m_v[line].bContainsPureComment = bCommentInLine && bWhite; /* std::cout << line << " : " << ( bCommentInLine ? "c" : " " ) << ( bWhite ? "w " : " ") << @@ -357,171 +718,8 @@ } } -// read and preprocess file for line matching input data -void SourceData::readLMPPFile( SourceData* pOrigSource, const QString& ppCmd, bool bUpCase, bool bRemoveComments ) -{ - if ( ( ppCmd.isEmpty() && !bRemoveComments ) || pOrigSource->m_bPreserve ) - { - reset(); - } - else - { - setFilename( pOrigSource->m_fileAccess.absFilePath() ); - readPPFile( false, ppCmd, bUpCase ); - if ( m_vSize < pOrigSource->m_vSize ) - { - m_v.resize( pOrigSource->m_vSize ); - m_vSize = pOrigSource->m_vSize; - } - } - if ( bRemoveComments && m_vSize==pOrigSource->m_vSize ) - removeComments( &pOrigSource->m_v[0] ); -} -void SourceData::readPPFile( bool bPreserveCR, const QString& ppCmd, bool bUpCase ) -{ - if ( !m_bPreserve ) - { - if ( ! ppCmd.isEmpty() && !m_fileName.isEmpty() && FileAccess::exists( m_fileName ) ) - { - QString fileNameOut = FileAccess::tempFileName(); -#ifdef _WIN32 - QString cmd = QString("type ") + m_fileName + " | " + ppCmd + " >" + fileNameOut; -#else - QString cmd = QString("cat ") + m_fileName + " | " + ppCmd + " >" + fileNameOut; -#endif - ::system( cmd.ascii() ); - readFile( fileNameOut, true, bUpCase ); - FileAccess::removeFile( fileNameOut ); - } - else - { - readFile( m_fileAccess.absFilePath(), true, bUpCase ); - } - } - preprocess( bPreserveCR ); -} - -void SourceData::readFile( const QString& filename, bool bFollowLinks, bool bUpCase ) -{ - delete (char*)m_pBuf; - m_size = 0; - m_pBuf = 0; - char* pBuf = 0; - if ( filename.isEmpty() ) { return; } - - if ( !bFollowLinks ) - { - FileAccess fi( filename ); - if ( fi.isSymLink() ) - { - QString s = fi.readLink(); - m_size = s.length(); - m_pBuf = pBuf = new char[m_size+100]; - memcpy( pBuf, s.ascii(), m_size ); - return; - } - } - - - FileAccess fa( filename ); - m_size = fa.sizeForReading(); - m_pBuf = pBuf = new char[m_size+100]; - bool bSuccess = fa.readFile( pBuf, m_size ); - if ( !bSuccess ) - { - delete pBuf; - m_pBuf = 0; - m_size = 0; - return; - } - - - if ( bUpCase ) - { - int i; - for(i=0; i<m_size; ++i) - { - pBuf[i] = toupper(pBuf[i]); - } - } - -} - -void SourceData::setData( const QString& data, bool bUpCase ) -{ - delete (char*)m_pBuf; - m_size = data.length(); - m_pBuf = 0; - - char* pBuf = 0; - m_pBuf = pBuf = new char[m_size+100]; - - memcpy( pBuf, data.ascii(), m_size ); - if ( bUpCase ) - { - int i; - for(i=0; i<m_size; ++i) - { - pBuf[i] = toupper(pBuf[i]); - } - } - m_bPreserve = true; - m_fileName=""; - m_aliasName = i18n("From Clipboard"); - m_fileAccess = FileAccess(""); -} - -void SourceData::setFilename( const QString& filename ) -{ - FileAccess fa( filename ); - setFileAccess( fa ); -} - -QString SourceData::getFilename() -{ - return m_fileName; -} - -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(); - m_bPreserve = false; - m_fileName = m_fileAccess.absFilePath(); -} - -void prepareOccurances( LineData* p, int size ) -{ - // Special analysis: Find out how often this line occurs - // Only problem: A simple search will cost O(N^2). - // To avoid this we will use a map. Then the cost will only be - // O(N*log N). (A hash table would be even better.) - - std::map<LineDataRef,int> occurancesMap; - int i; - for( i=0; i<size; ++i ) - { - ++occurancesMap[ LineDataRef( &p[i] ) ]; - } - - for( i=0; i<size; ++i ) - { - p[i].occurances = occurancesMap[ LineDataRef( &p[i] ) ]; - } -} - // First step void calcDiff3LineListUsingAB( const DiffList* pDiffListAB, @@ -580,13 +778,13 @@ ++lineB; } - d3ll.push_back( d3l ); + d3ll.push_back( d3l ); } } // Second step -void calcDiff3LineListUsingAC( +void calcDiff3LineListUsingAC( const DiffList* pDiffListAC, Diff3LineList& d3ll ) @@ -617,13 +815,13 @@ if( d.nofEquals>0 ) { // Find the corresponding lineA - while( (*i3).lineA!=lineA ) + while( (*i3).lineA!=lineA ) ++i3; (*i3).lineC = lineC; (*i3).bAEqC = true; (*i3).bBEqC = (*i3).bAEqB; - + --d.nofEquals; ++lineA; ++lineC; @@ -655,7 +853,7 @@ } // Third step -void calcDiff3LineListUsingBC( +void calcDiff3LineListUsingBC( const DiffList* pDiffListBC, Diff3LineList& d3ll ) @@ -728,7 +926,7 @@ while( i3 != i3b && i3!=d3ll.end() ) { - if ( (*i3).lineB != -1 ) + if ( (*i3).lineB != -1 ) ++nofDisturbingLines; ++i3; } @@ -740,7 +938,7 @@ while( i3 != i3b ) { - if ( (*i3).lineB != -1 ) + if ( (*i3).lineB != -1 ) { Diff3Line d3l; d3l.lineB = (*i3).lineB; @@ -778,7 +976,7 @@ int nofDisturbingLines = 0; while( i3 != i3c && i3!=d3ll.end() ) { - if ( (*i3).lineC != -1 ) + if ( (*i3).lineC != -1 ) ++nofDisturbingLines; ++i3; } @@ -789,7 +987,7 @@ i3 = i3b; while( i3 != i3c ) { - if ( (*i3).lineC != -1 ) + if ( (*i3).lineC != -1 ) { Diff3Line d3l; d3l.lineC = (*i3).lineC; @@ -816,7 +1014,7 @@ } } } - + --d.nofEquals; ++lineB; ++lineC; @@ -861,8 +1059,8 @@ int li=0; for( ; it!=d3ll.end(); ++it, ++li ) { - printf( "%4d %4d %4d %4d A%c=B A%c=C B%c=C\n", - li, (*it).lineA, (*it).lineB, (*it).lineC, + printf( "%4d %4d %4d %4d A%c=B A%c=C B%c=C\n", + li, (*it).lineA, (*it).lineB, (*it).lineC, (*it).bAEqB ? '=' : '!', (*it).bAEqC ? '=' : '!', (*it).bBEqC ? '=' : '!' ); } printf("\n");*/ @@ -873,8 +1071,8 @@ #endif // Fourth step -void calcDiff3LineListTrim( - Diff3LineList& d3ll, LineData* pldA, LineData* pldB, LineData* pldC +void calcDiff3LineListTrim( + Diff3LineList& d3ll, const LineData* pldA, const LineData* pldB, const LineData* pldC ) { const Diff3Line d3l_empty; @@ -974,7 +1172,7 @@ (*i).lineA = (*i3).lineA; (*i).lineB = (*i3).lineB; (*i).bAEqB = true; - + (*i3).lineA = -1; (*i3).lineB = -1; (*i3).bAEqB = false; @@ -993,7 +1191,7 @@ (*i).lineA = (*i3).lineA; (*i).lineC = (*i3).lineC; (*i).bAEqC = true; - + (*i3).lineA = -1; (*i3).lineC = -1; (*i3).bAEqC = false; @@ -1012,7 +1210,7 @@ (*i).lineB = (*i3).lineB; (*i).lineC = (*i3).lineC; (*i).bBEqC = true; - + (*i3).lineB = -1; (*i3).lineC = -1; (*i3).bBEqC = false; @@ -1052,8 +1250,8 @@ int li=0; for( ; it!=d3ll.end(); ++it, ++li ) { - printf( "%4d %4d %4d %4d A%c=B A%c=C B%c=C\n", - li, (*it).lineA, (*it).lineB, (*it).lineC, + printf( "%4d %4d %4d %4d A%c=B A%c=C B%c=C\n", + li, (*it).lineA, (*it).lineB, (*it).lineC, (*it).bAEqB ? '=' : '!', (*it).bAEqC ? '=' : '!', (*it).bBEqC ? '=' : '!' ); } @@ -1061,7 +1259,7 @@ } void calcWhiteDiff3Lines( - Diff3LineList& d3ll, LineData* pldA, LineData* pldB, LineData* pldC + Diff3LineList& d3ll, const LineData* pldA, const LineData* pldB, const LineData* pldC ) { Diff3LineList::iterator i3 = d3ll.begin(); @@ -1294,8 +1492,8 @@ void fineDiff( Diff3LineList& diff3LineList, int selector, - LineData* v1, - LineData* v2, + const LineData* v1, + const LineData* v2, bool& bTextsTotalEqual ) { @@ -1325,9 +1523,21 @@ // Optimize the diff list. DiffList::iterator dli; + bool bUsefulFineDiff = false; for( dli = pDiffList->begin(); dli!=pDiffList->end(); ++dli) { - if( dli->nofEquals < 4 && (dli->diff1>0 || dli->diff2>0) ) + if( dli->nofEquals >= 4 ) + { + bUsefulFineDiff = true; + break; + } + } + + for( dli = pDiffList->begin(); dli!=pDiffList->end(); ++dli) + { + if( dli->nofEquals < 4 && (dli->diff1>0 || dli->diff2>0) + && !( bUsefulFineDiff && dli==pDiffList->begin() ) + ) { dli->diff1 += dli->nofEquals; dli->diff2 += dli->nofEquals; @@ -1365,7 +1575,7 @@ { d3lv[j] = &(*i); } - assert( j==(int)d3lv.size() ); + assert( j==(int)d3lv.size() ); }