comparison 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
comparison
equal deleted inserted replaced
57:023fbd76c1e3 58:8af4bb9d9a5a
1 /*************************************************************************** 1 /***************************************************************************
2 diff.cpp - description 2 diff.cpp - description
3 ------------------- 3 -------------------
4 begin : Mon Mar 18 2002 4 begin : Mon Mar 18 2002
5 copyright : (C) 2002 by Joachim Eibl 5 copyright : (C) 2002-2004 by Joachim Eibl
6 email : joachim.eibl@gmx.de 6 email : joachim.eibl@gmx.de
7 ***************************************************************************/ 7 ***************************************************************************/
8 8
9 /*************************************************************************** 9 /***************************************************************************
10 * * 10 * *
18 #include <stdio.h> 18 #include <stdio.h>
19 #include <iostream> 19 #include <iostream>
20 20
21 #include "diff.h" 21 #include "diff.h"
22 #include "fileaccess.h" 22 #include "fileaccess.h"
23 #include "optiondialog.h"
23 24
24 #include <kmessagebox.h> 25 #include <kmessagebox.h>
25 #include <klocale.h> 26 #include <klocale.h>
26 #include <qfileinfo.h> 27 #include <qfileinfo.h>
27 #include <qdir.h> 28 #include <qdir.h>
30 #include <assert.h> 31 #include <assert.h>
31 #include <ctype.h> 32 #include <ctype.h>
32 //using namespace std; 33 //using namespace std;
33 34
34 35
35 int LineData::width() 36 int LineData::width() const
36 { 37 {
37 int w=0; 38 int w=0;
38 int j=0; 39 int j=0;
39 for( int i=0; i<size; ++i ) 40 for( int i=0; i<size; ++i )
40 { 41 {
108 return false; 109 return false;
109 } 110 }
110 } 111 }
111 112
112 113
113 // class needed during preprocess phase 114
114 class LineDataRef 115
115 { 116 static bool isLineOrBufEnd( const char* p, int i, int size )
116 const LineData* m_pLd; 117 {
117 public: 118 return
118 LineDataRef(const LineData* pLd){ m_pLd = pLd; } 119 i>=size // End of file
119 120 || p[i]=='\n' // Normal end of line
120 bool operator<(const LineDataRef& ldr2) const 121
121 { 122 // No support for Mac-end of line yet, because incompatible with GNU-diff-routines.
122 const LineData* pLd1 = m_pLd; 123 // || ( p[i]=='\r' && (i>=size-1 || p[i+1]!='\n')
123 const LineData* pLd2 = ldr2.m_pLd; 124 // && (i==0 || p[i-1]!='\n') ) // Special case: '\r' without '\n'
124 const char* p1 = pLd1->pFirstNonWhiteChar; 125 ;
125 const char* p2 = pLd2->pFirstNonWhiteChar; 126 }
126 127
127 int size1=pLd1->size; 128
128 int size2=pLd2->size; 129 /* Features of class SourceData:
129 130 - Read a file (from the given URL) or accept data via a string.
130 int i1=min2(pLd1->pFirstNonWhiteChar - pLd1->pLine,size1); 131 - Allocate and free buffers as necessary.
131 int i2=min2(pLd2->pFirstNonWhiteChar - pLd2->pLine,size2); 132 - Run a preprocessor, when specified.
132 for(;;) 133 - Run the line-matching preprocessor, when specified.
133 { 134 - Run other preprocessing steps: Uppercase, ignore comments,
134 while( i1<size1 && isWhite( p1[i1] ) ) ++i1; 135 remove carriage return, ignore numbers.
135 while( i2<size2 && isWhite( p2[i2] ) ) ++i2; 136
136 if ( i1==size1 || i2==size2 ) 137 Order of operation:
137 { 138 1. If data was given via a string then save it to a temp file. (see setData())
138 if ( i1==size1 && i2==size2 ) return false; // Equal 139 2. If the specified file is nonlocal (URL) copy it to a temp file.
139 if ( i1==size1 ) return true; // String 1 is shorter than string 2 140 3. If a preprocessor was specified, run the input file through it.
140 if ( i2==size2 ) return false; // String 1 is longer than string 2 141 4. Read the output of the preprocessor.
141 } 142 5. If Uppercase was specified: Turn the read data to uppercase.
142 if ( p1[i1]==p2[i2] ) { ++i1; ++i2; continue; } 143 6. Write the result to a temp file.
143 return p1[i1]<p2[i2]; 144 7. If a line-matching preprocessor was specified, run the temp file through it.
144 } 145 8. Read the output of the line-matching preprocessor.
145 } 146 9. If ignore numbers was specified, strip the LMPP-output of all numbers.
146 }; 147 10. If ignore comments was specified, strip the LMPP-output of comments.
148
149 Optimizations: Skip unneeded steps.
150 */
151
152 SourceData::SourceData()
153 {
154 m_pOptionDialog = 0;
155 reset();
156 }
157
158 SourceData::~SourceData()
159 {
160 reset();
161 }
147 162
148 void SourceData::reset() 163 void SourceData::reset()
164 {
165 m_normalData.reset();
166 m_lmppData.reset();
167 if ( !m_tempInputFileName.isEmpty() )
168 {
169 FileAccess::removeFile( m_tempInputFileName );
170 m_tempInputFileName = "";
171 }
172 }
173
174 void SourceData::setFilename( const QString& filename )
175 {
176 if (filename.isEmpty())
177 {
178 reset();
179 }
180 else
181 {
182 FileAccess fa( filename );
183 setFileAccess( fa );
184 }
185 }
186
187 bool SourceData::isEmpty()
188 {
189 return getFilename().isEmpty();
190 }
191
192 bool SourceData::hasData()
193 {
194 return m_normalData.m_pBuf != 0;
195 }
196
197 void SourceData::setOptionDialog( OptionDialog* pOptionDialog )
198 {
199 m_pOptionDialog = pOptionDialog;
200 }
201
202 QString SourceData::getFilename()
203 {
204 return m_fileAccess.absFilePath();
205 }
206
207 QString SourceData::getAliasName()
208 {
209 return m_aliasName.isEmpty() ? m_fileAccess.prettyAbsPath() : m_aliasName;
210 }
211
212 void SourceData::setAliasName( const QString& name )
213 {
214 m_aliasName = name;
215 }
216
217 void SourceData::setFileAccess( const FileAccess& fileAccess )
218 {
219 m_fileAccess = fileAccess;
220 m_aliasName = QString();
221 if ( !m_tempInputFileName.isEmpty() )
222 {
223 FileAccess::removeFile( m_tempInputFileName );
224 m_tempInputFileName = "";
225 }
226 }
227
228 void SourceData::setData( const QString& data )
229 {
230 // Create a temp file for preprocessing:
231 if ( m_tempInputFileName.isEmpty() )
232 {
233 m_tempInputFileName = FileAccess::tempFileName();
234 }
235
236 FileAccess f( m_tempInputFileName );
237 bool bSuccess = f.writeFile( encodeString(data, m_pOptionDialog), data.length() );
238 if ( !bSuccess )
239 {
240 KMessageBox::error( m_pOptionDialog, i18n("Writing clipboard data to temp file failed.") );
241 return;
242 }
243
244 m_aliasName = i18n("From Clipboard");
245 m_fileAccess = FileAccess(""); // Effect: m_fileAccess.isValid() is false
246 }
247
248 const LineData* SourceData::getLineDataForDiff() const
249 {
250 return m_lmppData.m_pBuf==0 ? &m_normalData.m_v[0] : &m_lmppData.m_v[0];
251 }
252
253 const LineData* SourceData::getLineDataForDisplay() const
254 {
255 return &m_normalData.m_v[0];
256 }
257
258 int SourceData::getSizeLines() const
259 {
260 return m_normalData.m_vSize;
261 }
262
263 int SourceData::getSizeBytes() const
264 {
265 return m_normalData.m_size;
266 }
267
268 const char* SourceData::getBuf() const
269 {
270 return m_normalData.m_pBuf;
271 }
272
273 bool SourceData::isText()
274 {
275 return m_normalData.m_bIsText;
276 }
277
278 bool SourceData::isFromBuffer()
279 {
280 return !m_fileAccess.isValid();
281 }
282
283
284 bool SourceData::isBinaryEqualWith( const SourceData& other ) const
285 {
286 return getSizeBytes() == other.getSizeBytes() && memcmp( getBuf(), other.getBuf(), getSizeBytes() )==0;
287 }
288
289 void SourceData::FileData::reset()
149 { 290 {
150 delete (char*)m_pBuf; 291 delete (char*)m_pBuf;
151 m_pBuf = 0; 292 m_pBuf = 0;
152 m_v.clear(); 293 m_v.clear();
153 m_size = 0; 294 m_size = 0;
154 m_vSize = 0; 295 m_vSize = 0;
155 m_bIsText = false; 296 m_bIsText = true;
156 m_bPreserve = false; 297 }
157 m_fileAccess = FileAccess(""); 298
158 } 299 bool SourceData::FileData::readFile( const QString& filename )
300 {
301 reset();
302 if ( filename.isEmpty() ) { return true; }
303
304 FileAccess fa( filename );
305 m_size = fa.sizeForReading();
306 char* pBuf;
307 m_pBuf = pBuf = new char[m_size+100]; // Alloc 100 byte extra: Savety hack, not nice but does no harm.
308 bool bSuccess = fa.readFile( pBuf, m_size );
309 if ( !bSuccess )
310 {
311 delete pBuf;
312 m_pBuf = 0;
313 m_size = 0;
314 }
315 return bSuccess;
316 }
317
318 bool SourceData::saveNormalDataAs( const QString& fileName )
319 {
320 return m_normalData.writeFile( fileName );
321 }
322
323 bool SourceData::FileData::writeFile( const QString& filename )
324 {
325 if ( filename.isEmpty() ) { return true; }
326
327 FileAccess fa( filename );
328 bool bSuccess = fa.writeFile(m_pBuf, m_size);
329 return bSuccess;
330 }
331
332 void SourceData::FileData::copyBufFrom( const FileData& src )
333 {
334 reset();
335 char* pBuf;
336 m_size = src.m_size;
337 m_pBuf = pBuf = new char[m_size+100];
338 memcpy( pBuf, src.m_pBuf, m_size );
339 }
340
341 void SourceData::readAndPreprocess()
342 {
343 QString fileNameIn1;
344 QString fileNameOut1;
345 QString fileNameIn2;
346 QString fileNameOut2;
347
348 bool bTempFileFromClipboard = !m_fileAccess.isValid();
349
350 // Detect the input for the preprocessing operations
351 if ( !bTempFileFromClipboard )
352 {
353 if ( m_fileAccess.isLocal() )
354 {
355 fileNameIn1 = m_fileAccess.absFilePath();
356 }
357 else // File is not local: create a temporary local copy:
358 {
359 if ( m_tempInputFileName.isEmpty() ) { m_tempInputFileName = FileAccess::tempFileName(); }
360
361 m_fileAccess.copyFile(m_tempInputFileName);
362 fileNameIn1 = m_tempInputFileName;
363 }
364 }
365 else // The input was set via setData(), probably from clipboard.
366 {
367 fileNameIn1 = m_tempInputFileName;
368 }
369
370 m_normalData.reset();
371 m_lmppData.reset();
372
373 FileAccess faIn(fileNameIn1);
374 int fileInSize = faIn.size();
375
376 if ( fileInSize > 0 )
377 {
378
379 #ifdef _WIN32
380 QString catCmd = "type";
381 fileNameIn1.replace( '/', "\\" );
382 #else
383 QString catCmd = "cat";
384 #endif
385
386 // Run the first preprocessor
387 if ( m_pOptionDialog->m_PreProcessorCmd.isEmpty() )
388 {
389 // No preprocessing: Read the file directly:
390 m_normalData.readFile( fileNameIn1 );
391 }
392 else
393 {
394 QString ppCmd = m_pOptionDialog->m_PreProcessorCmd;
395 fileNameOut1 = FileAccess::tempFileName();
396 QString cmd = catCmd + " \"" + fileNameIn1 + "\" | " + ppCmd + " >\"" + fileNameOut1+"\"";
397 ::system( encodeString(cmd, m_pOptionDialog) );
398 bool bSuccess = m_normalData.readFile( fileNameOut1 );
399 if ( fileInSize >0 && ( !bSuccess || m_normalData.m_size==0 ) )
400 {
401 KMessageBox::error(m_pOptionDialog,
402 i18n("Preprocessing possibly failed. Check this command:\n\n %1"
403 "\n\nThe preprocessing command will be disabled now."
404 ).arg(cmd) );
405 m_pOptionDialog->m_PreProcessorCmd = "";
406 m_normalData.readFile( fileNameIn1 );
407 }
408 }
409
410 // Internal Preprocessing: Uppercase-conversion
411 bool bInternalPreprocessing = false;
412 if ( m_pOptionDialog->m_bUpCase )
413 {
414 int i;
415 char* pBuf = const_cast<char*>(m_normalData.m_pBuf);
416 for(i=0; i<m_normalData.m_size; ++i)
417 {
418 pBuf[i] = toupper(pBuf[i]);
419 }
420
421 bInternalPreprocessing = true;
422 }
423
424 // LineMatching Preprocessor
425 if ( ! m_pOptionDialog->m_LineMatchingPreProcessorCmd.isEmpty() )
426 {
427 if ( bInternalPreprocessing )
428 {
429 // write data to file after internal preprocessing before running the external LMPP-cmd.
430 if ( !fileNameOut1.isEmpty() )
431 {
432 FileAccess::removeFile( fileNameOut1 );
433 fileNameOut1="";
434 }
435
436 fileNameIn2 = FileAccess::tempFileName();
437 bool bSuccess = m_normalData.writeFile( fileNameIn2 );
438 if ( !bSuccess )
439 {
440 KMessageBox::error(m_pOptionDialog, i18n("Error writing temporary file: %1").arg(fileNameIn2) );
441 }
442 }
443 else
444 {
445 fileNameIn2 = fileNameOut1.isEmpty() ? fileNameIn1 : fileNameOut1;
446 }
447
448 QString ppCmd = m_pOptionDialog->m_LineMatchingPreProcessorCmd;
449 fileNameOut2 = FileAccess::tempFileName();
450 QString cmd = catCmd + " \"" + fileNameIn2 + "\" | " + ppCmd + " >\"" + fileNameOut2 + "\"";
451 ::system( encodeString(cmd, m_pOptionDialog) );
452 bool bSuccess = m_lmppData.readFile( fileNameOut2 );
453 if ( FileAccess(fileNameIn2).size()>0 && ( !bSuccess || m_lmppData.m_size==0 ) )
454 {
455 KMessageBox::error(m_pOptionDialog,
456 i18n("The line-matching-preprocessing possibly failed. Check this command:\n\n %1"
457 "\n\nThe line-matching-preprocessing command will be disabled now."
458 ).arg(cmd) );
459 m_pOptionDialog->m_LineMatchingPreProcessorCmd = "";
460 m_lmppData.readFile( fileNameIn2 );
461 }
462 FileAccess::removeFile( fileNameOut2 );
463
464 if ( bInternalPreprocessing && !fileNameIn2.isEmpty() )
465 {
466 FileAccess::removeFile( fileNameIn2 );
467 fileNameIn2="";
468 }
469 }
470 else if ( m_pOptionDialog->m_bIgnoreComments )
471 {
472 // We need a copy of the normal data.
473 m_lmppData.copyBufFrom( m_normalData );
474 }
475 else
476 { // We don't need any lmpp data at all.
477 m_lmppData.reset();
478 }
479 }
480
481 m_normalData.preprocess( m_pOptionDialog->m_bPreserveCarriageReturn );
482 m_lmppData.preprocess( false );
483
484 if ( m_lmppData.m_vSize < m_normalData.m_vSize )
485 {
486 // This probably is the fault of the LMPP-Command, but not worth reporting.
487 m_lmppData.m_v.resize( m_normalData.m_vSize );
488 for(int i=m_lmppData.m_vSize; i<m_normalData.m_vSize; ++i )
489 { // Set all empty lines to point to the end of the buffer.
490 m_lmppData.m_v[i].pLine = m_lmppData.m_pBuf+m_lmppData.m_size;
491 }
492
493 m_lmppData.m_vSize = m_normalData.m_vSize;
494 }
495
496 // Ignore comments
497 if ( m_pOptionDialog->m_bIgnoreComments )
498 {
499 m_lmppData.removeComments();
500 int vSize = min2(m_normalData.m_vSize, m_lmppData.m_vSize);
501 for(int i=0; i<vSize; ++i )
502 {
503 m_normalData.m_v[i].bContainsPureComment = m_lmppData.m_v[i].bContainsPureComment;
504 }
505 }
506
507 // Remove unneeded temporary files. (A temp file from clipboard must not be deleted.)
508 if ( !bTempFileFromClipboard && !m_tempInputFileName.isEmpty() )
509 {
510 FileAccess::removeFile( m_tempInputFileName );
511 m_tempInputFileName = "";
512 }
513
514 if ( !fileNameOut1.isEmpty() )
515 {
516 FileAccess::removeFile( fileNameOut1 );
517 fileNameOut1="";
518 }
519 }
520
159 521
160 /** Prepare the linedata vector for every input line.*/ 522 /** Prepare the linedata vector for every input line.*/
161 void SourceData::preprocess( bool bPreserveCR ) 523 void SourceData::FileData::preprocess( bool bPreserveCR )
162 { 524 {
163 const char* p = m_pBuf; 525 const char* p = m_pBuf;
164 m_bIsText = true; 526 m_bIsText = true;
165 int lines = 1; 527 int lines = 1;
166
167 int i; 528 int i;
168 for( i=0; i<m_size; ++i ) 529 for( i=0; i<m_size; ++i )
169 { 530 {
170 if (p[i]=='\n') 531 if ( isLineOrBufEnd(p,i,m_size) )
171 { 532 {
172 ++lines; 533 ++lines;
173 } 534 }
174 if ( p[i]=='\0' ) 535 if ( p[i]=='\0' )
175 { 536 {
182 int lineLength=0; 543 int lineLength=0;
183 bool bNonWhiteFound = false; 544 bool bNonWhiteFound = false;
184 int whiteLength = 0; 545 int whiteLength = 0;
185 for( i=0; i<=m_size; ++i ) 546 for( i=0; i<=m_size; ++i )
186 { 547 {
187 if ( i==m_size || p[i]=='\n' ) // The last line does not end with a linefeed. 548 if ( isLineOrBufEnd( p, i, m_size ) )
188 { 549 {
189 m_v[lineIdx].pLine = &p[ i-lineLength ]; 550 m_v[lineIdx].pLine = &p[ i-lineLength ];
190 while ( !bPreserveCR && lineLength>0 && m_v[lineIdx].pLine[lineLength-1]=='\r' ) 551 while ( !bPreserveCR && lineLength>0 && m_v[lineIdx].pLine[lineLength-1]=='\r' )
191 { 552 {
192 --lineLength; 553 --lineLength;
235 // (if not in a string) 596 // (if not in a string)
236 if ( p[i]=='\'' ) 597 if ( p[i]=='\'' )
237 { 598 {
238 bWhite = false; 599 bWhite = false;
239 ++i; 600 ++i;
240 for( ; i<size && p[i]!='\'' && p[i]!='\n'; ++i) 601 for( ; !isLineOrBufEnd(p,i,size) && p[i]!='\''; ++i)
241 ; 602 ;
242 if (p[i]=='\'') ++i; 603 if (p[i]=='\'') ++i;
243 } 604 }
244 605
245 // Strings have priority over comments: e.g. "/* Not a comment, but a string. */" 606 // Strings have priority over comments: e.g. "/* Not a comment, but a string. */"
246 else if ( p[i]=='"' ) 607 else if ( p[i]=='"' )
247 { 608 {
248 bWhite = false; 609 bWhite = false;
249 ++i; 610 ++i;
250 for( ; i<size && !(p[i]=='"' && p[i-1]!='\\') && p[i]!='\n'; ++i) 611 for( ; !isLineOrBufEnd(p,i,size) && !(p[i]=='"' && p[i-1]!='\\'); ++i)
251 ; 612 ;
252 if (p[i]=='"') ++i; 613 if (p[i]=='"') ++i;
253 } 614 }
254 615
255 // C++-comment 616 // C++-comment
256 else if ( p[i]=='/' && i+1<size && p[i+1] =='/' ) 617 else if ( p[i]=='/' && i+1<size && p[i+1] =='/' )
257 { 618 {
258 int commentStart = i; 619 int commentStart = i;
259 bCommentInLine = true; 620 bCommentInLine = true;
260 i+=2; 621 i+=2;
261 for( ; i<size && p[i]!='\n'; ++i) 622 for( ; !isLineOrBufEnd(p,i,size); ++i)
262 ; 623 ;
263 if ( !bWhite ) 624 if ( !bWhite )
264 { 625 {
265 memset( &p[commentStart], ' ', i-commentStart ); 626 memset( &p[commentStart], ' ', i-commentStart );
266 } 627 }
271 else if ( p[i]=='/' && i+1<size && p[i+1] =='*' ) 632 else if ( p[i]=='/' && i+1<size && p[i+1] =='*' )
272 { 633 {
273 int commentStart = i; 634 int commentStart = i;
274 bCommentInLine = true; 635 bCommentInLine = true;
275 i+=2; 636 i+=2;
276 for( ; i<size && p[i]!='\n'; ++i) 637 for( ; !isLineOrBufEnd(p,i,size); ++i)
277 { 638 {
278 if ( i+1<size && p[i]=='*' && p[i+1]=='/') // end of the comment 639 if ( i+1<size && p[i]=='*' && p[i+1]=='/') // end of the comment
279 { 640 {
280 i+=2; 641 i+=2;
281 642
291 bStartsOpenComment = true; 652 bStartsOpenComment = true;
292 return; 653 return;
293 } 654 }
294 655
295 656
296 if (p[i]=='\n' || i>=size ) 657 if ( isLineOrBufEnd(p,i,size) )
297 { 658 {
298 return; 659 return;
299 } 660 }
300 else if ( !isspace(p[i]) ) 661 else if ( !isspace(p[i]) )
301 { 662 {
305 } 666 }
306 667
307 // Modifies the input data, and replaces C/C++ comments with whitespace 668 // Modifies the input data, and replaces C/C++ comments with whitespace
308 // when the line contains other data too. If the line contains only 669 // when the line contains other data too. If the line contains only
309 // a comment or white data, remember this in the flag bContainsPureComment. 670 // a comment or white data, remember this in the flag bContainsPureComment.
310 void SourceData::removeComments( LineData* pLD ) 671 void SourceData::FileData::removeComments()
311 { 672 {
312 int line=0; 673 int line=0;
313 char* p = (char*)m_pBuf; 674 char* p = (char*)m_pBuf;
314 bool bWithinComment=false; 675 bool bWithinComment=false;
315 int size = m_size; 676 int size = m_size;
322 if ( bWithinComment ) 683 if ( bWithinComment )
323 { 684 {
324 int commentStart = i; 685 int commentStart = i;
325 bCommentInLine = true; 686 bCommentInLine = true;
326 687
327 for( ; i<size && p[i]!='\n'; ++i) 688 for( ; !isLineOrBufEnd(p,i,size); ++i)
328 { 689 {
329 if ( i+1<size && p[i]=='*' && p[i+1]=='/') // end of the comment 690 if ( i+1<size && p[i]=='*' && p[i+1]=='/') // end of the comment
330 { 691 {
331 i+=2; 692 i+=2;
332 693
344 { 705 {
345 checkLineForComments( p, i, size, bWhite, bCommentInLine, bWithinComment ); 706 checkLineForComments( p, i, size, bWhite, bCommentInLine, bWithinComment );
346 } 707 }
347 708
348 // end of line 709 // end of line
349 assert( i>=size || p[i]=='\n'); 710 assert( isLineOrBufEnd(p,i,size));
350 pLD[line].bContainsPureComment = bCommentInLine && bWhite; 711 m_v[line].bContainsPureComment = bCommentInLine && bWhite;
351 /* std::cout << line << " : " << 712 /* std::cout << line << " : " <<
352 ( bCommentInLine ? "c" : " " ) << 713 ( bCommentInLine ? "c" : " " ) <<
353 ( bWhite ? "w " : " ") << 714 ( bWhite ? "w " : " ") <<
354 std::string(pLD[line].pLine, pLD[line].size) << std::endl;*/ 715 std::string(pLD[line].pLine, pLD[line].size) << std::endl;*/
355 716
356 ++line; 717 ++line;
357 } 718 }
358 } 719 }
359 720
360 // read and preprocess file for line matching input data 721
361 void SourceData::readLMPPFile( SourceData* pOrigSource, const QString& ppCmd, bool bUpCase, bool bRemoveComments )
362 {
363 if ( ( ppCmd.isEmpty() && !bRemoveComments ) || pOrigSource->m_bPreserve )
364 {
365 reset();
366 }
367 else
368 {
369 setFilename( pOrigSource->m_fileAccess.absFilePath() );
370 readPPFile( false, ppCmd, bUpCase );
371 if ( m_vSize < pOrigSource->m_vSize )
372 {
373 m_v.resize( pOrigSource->m_vSize );
374 m_vSize = pOrigSource->m_vSize;
375 }
376 }
377 if ( bRemoveComments && m_vSize==pOrigSource->m_vSize )
378 removeComments( &pOrigSource->m_v[0] );
379 }
380
381
382 void SourceData::readPPFile( bool bPreserveCR, const QString& ppCmd, bool bUpCase )
383 {
384 if ( !m_bPreserve )
385 {
386 if ( ! ppCmd.isEmpty() && !m_fileName.isEmpty() && FileAccess::exists( m_fileName ) )
387 {
388 QString fileNameOut = FileAccess::tempFileName();
389 #ifdef _WIN32
390 QString cmd = QString("type ") + m_fileName + " | " + ppCmd + " >" + fileNameOut;
391 #else
392 QString cmd = QString("cat ") + m_fileName + " | " + ppCmd + " >" + fileNameOut;
393 #endif
394 ::system( cmd.ascii() );
395 readFile( fileNameOut, true, bUpCase );
396 FileAccess::removeFile( fileNameOut );
397 }
398 else
399 {
400 readFile( m_fileAccess.absFilePath(), true, bUpCase );
401 }
402 }
403 preprocess( bPreserveCR );
404 }
405
406 void SourceData::readFile( const QString& filename, bool bFollowLinks, bool bUpCase )
407 {
408 delete (char*)m_pBuf;
409 m_size = 0;
410 m_pBuf = 0;
411 char* pBuf = 0;
412 if ( filename.isEmpty() ) { return; }
413
414 if ( !bFollowLinks )
415 {
416 FileAccess fi( filename );
417 if ( fi.isSymLink() )
418 {
419 QString s = fi.readLink();
420 m_size = s.length();
421 m_pBuf = pBuf = new char[m_size+100];
422 memcpy( pBuf, s.ascii(), m_size );
423 return;
424 }
425 }
426
427
428 FileAccess fa( filename );
429 m_size = fa.sizeForReading();
430 m_pBuf = pBuf = new char[m_size+100];
431 bool bSuccess = fa.readFile( pBuf, m_size );
432 if ( !bSuccess )
433 {
434 delete pBuf;
435 m_pBuf = 0;
436 m_size = 0;
437 return;
438 }
439
440
441 if ( bUpCase )
442 {
443 int i;
444 for(i=0; i<m_size; ++i)
445 {
446 pBuf[i] = toupper(pBuf[i]);
447 }
448 }
449
450 }
451
452 void SourceData::setData( const QString& data, bool bUpCase )
453 {
454 delete (char*)m_pBuf;
455 m_size = data.length();
456 m_pBuf = 0;
457
458 char* pBuf = 0;
459 m_pBuf = pBuf = new char[m_size+100];
460
461 memcpy( pBuf, data.ascii(), m_size );
462 if ( bUpCase )
463 {
464 int i;
465 for(i=0; i<m_size; ++i)
466 {
467 pBuf[i] = toupper(pBuf[i]);
468 }
469 }
470 m_bPreserve = true;
471 m_fileName="";
472 m_aliasName = i18n("From Clipboard");
473 m_fileAccess = FileAccess("");
474 }
475
476 void SourceData::setFilename( const QString& filename )
477 {
478 FileAccess fa( filename );
479 setFileAccess( fa );
480 }
481
482 QString SourceData::getFilename()
483 {
484 return m_fileName;
485 }
486
487 QString SourceData::getAliasName()
488 {
489 return m_aliasName.isEmpty() ? m_fileAccess.prettyAbsPath() : m_aliasName;
490 }
491
492 void SourceData::setAliasName( const QString& name )
493 {
494 m_aliasName = name;
495 }
496
497 void SourceData::setFileAccess( const FileAccess& fileAccess )
498 {
499 m_fileAccess = fileAccess;
500 m_aliasName = QString();
501 m_bPreserve = false;
502 m_fileName = m_fileAccess.absFilePath();
503 }
504
505 void prepareOccurances( LineData* p, int size )
506 {
507 // Special analysis: Find out how often this line occurs
508 // Only problem: A simple search will cost O(N^2).
509 // To avoid this we will use a map. Then the cost will only be
510 // O(N*log N). (A hash table would be even better.)
511
512 std::map<LineDataRef,int> occurancesMap;
513 int i;
514 for( i=0; i<size; ++i )
515 {
516 ++occurancesMap[ LineDataRef( &p[i] ) ];
517 }
518
519 for( i=0; i<size; ++i )
520 {
521 p[i].occurances = occurancesMap[ LineDataRef( &p[i] ) ];
522 }
523 }
524 722
525 // First step 723 // First step
526 void calcDiff3LineListUsingAB( 724 void calcDiff3LineListUsingAB(
527 const DiffList* pDiffListAB, 725 const DiffList* pDiffListAB,
528 Diff3LineList& d3ll 726 Diff3LineList& d3ll
578 d3l.lineB = lineB; 776 d3l.lineB = lineB;
579 --d.diff2; 777 --d.diff2;
580 ++lineB; 778 ++lineB;
581 } 779 }
582 780
583 d3ll.push_back( d3l ); 781 d3ll.push_back( d3l );
584 } 782 }
585 } 783 }
586 784
587 785
588 // Second step 786 // Second step
589 void calcDiff3LineListUsingAC( 787 void calcDiff3LineListUsingAC(
590 const DiffList* pDiffListAC, 788 const DiffList* pDiffListAC,
591 Diff3LineList& d3ll 789 Diff3LineList& d3ll
592 ) 790 )
593 { 791 {
594 //////////////// 792 ////////////////
615 813
616 Diff3Line d3l; 814 Diff3Line d3l;
617 if( d.nofEquals>0 ) 815 if( d.nofEquals>0 )
618 { 816 {
619 // Find the corresponding lineA 817 // Find the corresponding lineA
620 while( (*i3).lineA!=lineA ) 818 while( (*i3).lineA!=lineA )
621 819
622 ++i3; 820 ++i3;
623 (*i3).lineC = lineC; 821 (*i3).lineC = lineC;
624 (*i3).bAEqC = true; 822 (*i3).bAEqC = true;
625 (*i3).bBEqC = (*i3).bAEqB; 823 (*i3).bBEqC = (*i3).bAEqB;
626 824
627 --d.nofEquals; 825 --d.nofEquals;
628 ++lineA; 826 ++lineA;
629 ++lineC; 827 ++lineC;
630 ++i3; 828 ++i3;
631 } 829 }
653 } 851 }
654 } 852 }
655 } 853 }
656 854
657 // Third step 855 // Third step
658 void calcDiff3LineListUsingBC( 856 void calcDiff3LineListUsingBC(
659 const DiffList* pDiffListBC, 857 const DiffList* pDiffListBC,
660 Diff3LineList& d3ll 858 Diff3LineList& d3ll
661 ) 859 )
662 { 860 {
663 //////////////// 861 ////////////////
726 Diff3LineList::iterator i3 = i3c; 924 Diff3LineList::iterator i3 = i3c;
727 int nofDisturbingLines = 0; 925 int nofDisturbingLines = 0;
728 while( i3 != i3b && i3!=d3ll.end() ) 926 while( i3 != i3b && i3!=d3ll.end() )
729 927
730 { 928 {
731 if ( (*i3).lineB != -1 ) 929 if ( (*i3).lineB != -1 )
732 ++nofDisturbingLines; 930 ++nofDisturbingLines;
733 ++i3; 931 ++i3;
734 } 932 }
735 933
736 if ( nofDisturbingLines>0 && nofDisturbingLines < d.nofEquals ) 934 if ( nofDisturbingLines>0 && nofDisturbingLines < d.nofEquals )
738 // Move the disturbing lines up, out of sight. 936 // Move the disturbing lines up, out of sight.
739 i3 = i3c; 937 i3 = i3c;
740 938
741 while( i3 != i3b ) 939 while( i3 != i3b )
742 { 940 {
743 if ( (*i3).lineB != -1 ) 941 if ( (*i3).lineB != -1 )
744 { 942 {
745 Diff3Line d3l; 943 Diff3Line d3l;
746 d3l.lineB = (*i3).lineB; 944 d3l.lineB = (*i3).lineB;
747 (*i3).lineB = -1; 945 (*i3).lineB = -1;
748 946
776 { 974 {
777 Diff3LineList::iterator i3 = i3b; 975 Diff3LineList::iterator i3 = i3b;
778 int nofDisturbingLines = 0; 976 int nofDisturbingLines = 0;
779 while( i3 != i3c && i3!=d3ll.end() ) 977 while( i3 != i3c && i3!=d3ll.end() )
780 { 978 {
781 if ( (*i3).lineC != -1 ) 979 if ( (*i3).lineC != -1 )
782 ++nofDisturbingLines; 980 ++nofDisturbingLines;
783 ++i3; 981 ++i3;
784 } 982 }
785 983
786 if ( nofDisturbingLines>0 && nofDisturbingLines < d.nofEquals ) 984 if ( nofDisturbingLines>0 && nofDisturbingLines < d.nofEquals )
787 { 985 {
788 // Move the disturbing lines up, out of sight. 986 // Move the disturbing lines up, out of sight.
789 i3 = i3b; 987 i3 = i3b;
790 while( i3 != i3c ) 988 while( i3 != i3c )
791 { 989 {
792 if ( (*i3).lineC != -1 ) 990 if ( (*i3).lineC != -1 )
793 { 991 {
794 Diff3Line d3l; 992 Diff3Line d3l;
795 d3l.lineC = (*i3).lineC; 993 d3l.lineC = (*i3).lineC;
796 (*i3).lineC = -1; 994 (*i3).lineC = -1;
797 (*i3).bAEqC = false; 995 (*i3).bAEqC = false;
814 (*i3b).lineC = lineC; 1012 (*i3b).lineC = lineC;
815 (*i3b).bBEqC = true; 1013 (*i3b).bBEqC = true;
816 } 1014 }
817 } 1015 }
818 } 1016 }
819 1017
820 --d.nofEquals; 1018 --d.nofEquals;
821 ++lineB; 1019 ++lineB;
822 ++lineC; 1020 ++lineC;
823 ++i3b; 1021 ++i3b;
824 ++i3c; 1022 ++i3c;
859 /* 1057 /*
860 Diff3LineList::iterator it = d3ll.begin(); 1058 Diff3LineList::iterator it = d3ll.begin();
861 int li=0; 1059 int li=0;
862 for( ; it!=d3ll.end(); ++it, ++li ) 1060 for( ; it!=d3ll.end(); ++it, ++li )
863 { 1061 {
864 printf( "%4d %4d %4d %4d A%c=B A%c=C B%c=C\n", 1062 printf( "%4d %4d %4d %4d A%c=B A%c=C B%c=C\n",
865 li, (*it).lineA, (*it).lineB, (*it).lineC, 1063 li, (*it).lineA, (*it).lineB, (*it).lineC,
866 (*it).bAEqB ? '=' : '!', (*it).bAEqC ? '=' : '!', (*it).bBEqC ? '=' : '!' ); 1064 (*it).bAEqB ? '=' : '!', (*it).bAEqC ? '=' : '!', (*it).bBEqC ? '=' : '!' );
867 } 1065 }
868 printf("\n");*/ 1066 printf("\n");*/
869 } 1067 }
870 1068
871 #ifdef _WIN32 1069 #ifdef _WIN32
872 using ::equal; 1070 using ::equal;
873 #endif 1071 #endif
874 1072
875 // Fourth step 1073 // Fourth step
876 void calcDiff3LineListTrim( 1074 void calcDiff3LineListTrim(
877 Diff3LineList& d3ll, LineData* pldA, LineData* pldB, LineData* pldC 1075 Diff3LineList& d3ll, const LineData* pldA, const LineData* pldB, const LineData* pldC
878 ) 1076 )
879 { 1077 {
880 const Diff3Line d3l_empty; 1078 const Diff3Line d3l_empty;
881 d3ll.remove( d3l_empty ); 1079 d3ll.remove( d3l_empty );
882 1080
972 int l = lineA > lineB ? lineA : lineB; 1170 int l = lineA > lineB ? lineA : lineB;
973 1171
974 (*i).lineA = (*i3).lineA; 1172 (*i).lineA = (*i3).lineA;
975 (*i).lineB = (*i3).lineB; 1173 (*i).lineB = (*i3).lineB;
976 (*i).bAEqB = true; 1174 (*i).bAEqB = true;
977 1175
978 (*i3).lineA = -1; 1176 (*i3).lineA = -1;
979 (*i3).lineB = -1; 1177 (*i3).lineB = -1;
980 (*i3).bAEqB = false; 1178 (*i3).bAEqB = false;
981 i3A = i; 1179 i3A = i;
982 i3B = i; 1180 i3B = i;
991 Diff3LineList::iterator i = lineA > lineC ? i3A : i3C; 1189 Diff3LineList::iterator i = lineA > lineC ? i3A : i3C;
992 int l = lineA > lineC ? lineA : lineC; 1190 int l = lineA > lineC ? lineA : lineC;
993 (*i).lineA = (*i3).lineA; 1191 (*i).lineA = (*i3).lineA;
994 (*i).lineC = (*i3).lineC; 1192 (*i).lineC = (*i3).lineC;
995 (*i).bAEqC = true; 1193 (*i).bAEqC = true;
996 1194
997 (*i3).lineA = -1; 1195 (*i3).lineA = -1;
998 (*i3).lineC = -1; 1196 (*i3).lineC = -1;
999 (*i3).bAEqC = false; 1197 (*i3).bAEqC = false;
1000 i3A = i; 1198 i3A = i;
1001 i3C = i; 1199 i3C = i;
1010 Diff3LineList::iterator i = lineB > lineC ? i3B : i3C; 1208 Diff3LineList::iterator i = lineB > lineC ? i3B : i3C;
1011 int l = lineB > lineC ? lineB : lineC; 1209 int l = lineB > lineC ? lineB : lineC;
1012 (*i).lineB = (*i3).lineB; 1210 (*i).lineB = (*i3).lineB;
1013 (*i).lineC = (*i3).lineC; 1211 (*i).lineC = (*i3).lineC;
1014 (*i).bBEqC = true; 1212 (*i).bBEqC = true;
1015 1213
1016 (*i3).lineB = -1; 1214 (*i3).lineB = -1;
1017 (*i3).lineC = -1; 1215 (*i3).lineC = -1;
1018 (*i3).bBEqC = false; 1216 (*i3).bBEqC = false;
1019 i3B = i; 1217 i3B = i;
1020 i3C = i; 1218 i3C = i;
1050 1248
1051 Diff3LineList::iterator it = d3ll.begin(); 1249 Diff3LineList::iterator it = d3ll.begin();
1052 int li=0; 1250 int li=0;
1053 for( ; it!=d3ll.end(); ++it, ++li ) 1251 for( ; it!=d3ll.end(); ++it, ++li )
1054 { 1252 {
1055 printf( "%4d %4d %4d %4d A%c=B A%c=C B%c=C\n", 1253 printf( "%4d %4d %4d %4d A%c=B A%c=C B%c=C\n",
1056 li, (*it).lineA, (*it).lineB, (*it).lineC, 1254 li, (*it).lineA, (*it).lineB, (*it).lineC,
1057 (*it).bAEqB ? '=' : '!', (*it).bAEqC ? '=' : '!', (*it).bBEqC ? '=' : '!' ); 1255 (*it).bAEqB ? '=' : '!', (*it).bAEqC ? '=' : '!', (*it).bBEqC ? '=' : '!' );
1058 1256
1059 } 1257 }
1060 */ 1258 */
1061 } 1259 }
1062 1260
1063 void calcWhiteDiff3Lines( 1261 void calcWhiteDiff3Lines(
1064 Diff3LineList& d3ll, LineData* pldA, LineData* pldB, LineData* pldC 1262 Diff3LineList& d3ll, const LineData* pldA, const LineData* pldB, const LineData* pldC
1065 ) 1263 )
1066 { 1264 {
1067 Diff3LineList::iterator i3 = d3ll.begin(); 1265 Diff3LineList::iterator i3 = d3ll.begin();
1068 1266
1069 for( ; i3!=d3ll.end(); ++i3 ) 1267 for( ; i3!=d3ll.end(); ++i3 )
1292 } 1490 }
1293 1491
1294 void fineDiff( 1492 void fineDiff(
1295 Diff3LineList& diff3LineList, 1493 Diff3LineList& diff3LineList,
1296 int selector, 1494 int selector,
1297 LineData* v1, 1495 const LineData* v1,
1298 LineData* v2, 1496 const LineData* v2,
1299 bool& bTextsTotalEqual 1497 bool& bTextsTotalEqual
1300 ) 1498 )
1301 { 1499 {
1302 // Finetuning: Diff each line with deltas 1500 // Finetuning: Diff each line with deltas
1303 int maxSearchLength=500; 1501 int maxSearchLength=500;
1323 // std::cout << std::string( v1[k1].pLine, v1[k1].size ) << "\n"; 1521 // std::cout << std::string( v1[k1].pLine, v1[k1].size ) << "\n";
1324 calcDiff( v1[k1].pLine, v1[k1].size, v2[k2].pLine, v2[k2].size, *pDiffList, 2, maxSearchLength ); 1522 calcDiff( v1[k1].pLine, v1[k1].size, v2[k2].pLine, v2[k2].size, *pDiffList, 2, maxSearchLength );
1325 1523
1326 // Optimize the diff list. 1524 // Optimize the diff list.
1327 DiffList::iterator dli; 1525 DiffList::iterator dli;
1526 bool bUsefulFineDiff = false;
1328 for( dli = pDiffList->begin(); dli!=pDiffList->end(); ++dli) 1527 for( dli = pDiffList->begin(); dli!=pDiffList->end(); ++dli)
1329 { 1528 {
1330 if( dli->nofEquals < 4 && (dli->diff1>0 || dli->diff2>0) ) 1529 if( dli->nofEquals >= 4 )
1530 {
1531 bUsefulFineDiff = true;
1532 break;
1533 }
1534 }
1535
1536 for( dli = pDiffList->begin(); dli!=pDiffList->end(); ++dli)
1537 {
1538 if( dli->nofEquals < 4 && (dli->diff1>0 || dli->diff2>0)
1539 && !( bUsefulFineDiff && dli==pDiffList->begin() )
1540 )
1331 { 1541 {
1332 dli->diff1 += dli->nofEquals; 1542 dli->diff1 += dli->nofEquals;
1333 dli->diff2 += dli->nofEquals; 1543 dli->diff2 += dli->nofEquals;
1334 dli->nofEquals = 0; 1544 dli->nofEquals = 0;
1335 } 1545 }
1363 int j=0; 1573 int j=0;
1364 for( i= d3ll.begin(); i!= d3ll.end(); ++i, ++j) 1574 for( i= d3ll.begin(); i!= d3ll.end(); ++i, ++j)
1365 { 1575 {
1366 d3lv[j] = &(*i); 1576 d3lv[j] = &(*i);
1367 } 1577 }
1368 assert( j==(int)d3lv.size() ); 1578 assert( j==(int)d3lv.size() );
1369 } 1579 }
1370 1580
1371 1581
1372 #include "diff.moc" 1582 #include "diff.moc"