/**************************************************************************
* * tdefile_diff . cpp
* * - - - - - - - - - - - - - - - - - - -
* * begin : Sun Jan 20 23 : 25 : 44 2002
* * copyright : ( C ) 2002 - 2003 by Otto Bruggeman
* * email : otto . bruggeman @ home . nl
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/***************************************************************************
* *
* * 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 .
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* * Patch by Volker Augustin for empty diff files . Februari 8 , 2002
* *
* * Patched to work with CVS from after February 26 , 2002 Otto
* *
* * Patched to work with CVS from after March 24 , 2002 Otto
* *
* * Added support for Perforce diffs , April 26 , 2003 Otto Bruggeman
* *
* * Added support for Subversion diffs , September 11 , 2003 Otto Bruggeman
*/
# include <tqcstring.h>
# include <tqdatetime.h>
# include <tqdict.h>
# include <tqfile.h>
# include <tqregexp.h>
# include <tqvalidator.h>
# include <kdebug.h>
# include <kgenericfactory.h>
# include <tdelocale.h>
# include <kprocess.h>
# include <kurl.h>
# include "tdefile_diff.h"
K_EXPORT_COMPONENT_FACTORY ( tdefile_diff , KGenericFactory < KDiffPlugin > ( " tdefile_diff " ) )
KDiffPlugin : : KDiffPlugin ( TQObject * parent , const char * name ,
const TQStringList & preferredItems )
: KFilePlugin ( parent , name , preferredItems )
{
kdDebug ( 7034 ) < < " diff plugin " < < endl ;
KFileMimeTypeInfo * info = addMimeTypeInfo ( " text/x-diff " ) ;
KFileMimeTypeInfo : : GroupInfo * group ;
group = addGroupInfo ( info , " General " , i18n ( " General " ) ) ;
addItemInfo ( group , " Files " , i18n ( " Files " ) , TQVariant : : UInt ) ;
addItemInfo ( group , " First " , i18n ( " First File " ) , TQVariant : : String ) ;
addItemInfo ( group , " Format " , i18n ( " Format " ) , TQVariant : : String ) ;
addItemInfo ( group , " DiffProgram " , i18n ( " Diff Program " ) , TQVariant : : String ) ;
addItemInfo ( group , " Hunks " , i18n ( " Hunks " ) , TQVariant : : UInt ) ;
group = addGroupInfo ( info , " Statistics " , i18n ( " Statistics " ) ) ;
addItemInfo ( group , " Insert " , i18n ( " Insertions " ) , TQVariant : : UInt ) ;
addItemInfo ( group , " Modify " , i18n ( " Changes " ) , TQVariant : : UInt ) ;
addItemInfo ( group , " Delete " , i18n ( " Deletions " ) , TQVariant : : UInt ) ;
}
bool KDiffPlugin : : readInfo ( KFileMetaInfo & info , uint what )
{
// This is a hack to avoid using the what stuff, since it is not yet implemented
what = 0 ;
// Used to determine if false or true should be returned
bool dataSet = false ;
KFileMetaInfoGroup group ;
TQFile file ( info . path ( ) ) ;
TQStringList lines ;
if ( file . open ( IO_ReadOnly ) )
{
TQTextStream stream ( & file ) ;
while ( ! stream . eof ( ) )
{
lines . append ( stream . readLine ( ) ) ;
}
file . close ( ) ;
}
TQString format ;
TQString program ;
enum KDiffPlugin : : Format diffFormat ;
enum KDiffPlugin : : DiffProgram diffProgram ;
diffFormat = determineDiffFormat ( lines ) ;
format = determineI18nedFormat ( diffFormat ) ;
diffProgram = determineDiffProgram ( lines ) ;
program = determineI18nedProgram ( diffProgram ) ;
int numberOfAdditions = 0 ;
int numberOfDeletions = 0 ;
int numberOfChanges = 0 ;
int numberOfHunks = 0 ;
int numberOfFiles = 0 ;
if ( what ! = KFileMetaInfo : : Fastest )
{
determineDiffInfo ( lines , diffFormat , & numberOfFiles , & numberOfHunks , & numberOfAdditions , & numberOfChanges , & numberOfDeletions ) ;
}
TQString filename ;
TQRegExp firstFile ( " ^Index: (.*) " ) ;
TQStringList : : ConstIterator it = lines . begin ( ) ;
it = lines . begin ( ) ;
while ( it ! = lines . end ( ) )
{
if ( firstFile . exactMatch ( ( * it ) ) )
{
filename = firstFile . cap ( 1 ) ;
// only interested in the first filename
break ;
}
+ + it ;
}
kdDebug ( 7034 ) < < " Diff Format : " < < format < < endl ; // i18n-ed but that is not a problem unless i get i18n-ed debug output ah well, we'll figure something out when then happens
if ( what ! = KFileMetaInfo : : Fastest )
{
// These dont get calculated in fastest mode...
kdDebug ( 7034 ) < < " Number of additions : " < < numberOfAdditions < < endl ;
kdDebug ( 7034 ) < < " Number of deletions : " < < numberOfDeletions < < endl ;
kdDebug ( 7034 ) < < " Number of changes : " < < numberOfChanges < < endl ;
kdDebug ( 7034 ) < < " Number of hunks : " < < numberOfHunks < < endl ;
}
group = appendGroup ( info , " General " ) ;
if ( numberOfFiles ! = 0 & & what ! = KFileMetaInfo : : Fastest )
{
appendItem ( group , " Files " , numberOfFiles ) ;
dataSet = true ;
}
if ( ! filename . isEmpty ( ) )
{
appendItem ( group , " First " , filename ) ;
dataSet = true ;
}
if ( ! format . isEmpty ( ) )
{
appendItem ( group , " Format " , format ) ;
dataSet = true ;
}
if ( ! program . isEmpty ( ) )
{
appendItem ( group , " DiffProgram " , program ) ;
dataSet = true ;
}
if ( numberOfHunks ! = 0 & & what ! = KFileMetaInfo : : Fastest )
{
appendItem ( group , " Hunks " , numberOfHunks ) ;
dataSet = true ;
}
group = appendGroup ( info , " Statistics " ) ;
if ( numberOfAdditions ! = 0 & & what ! = KFileMetaInfo : : Fastest )
{
appendItem ( group , " Insert " , numberOfAdditions ) ;
dataSet = true ;
}
if ( numberOfChanges ! = 0 & & what ! = KFileMetaInfo : : Fastest )
{
appendItem ( group , " Modify " , numberOfChanges ) ;
dataSet = true ;
}
if ( numberOfDeletions ! = 0 & & what ! = KFileMetaInfo : : Fastest )
{
appendItem ( group , " Delete " , numberOfDeletions ) ;
dataSet = true ;
}
return dataSet ;
}
enum KDiffPlugin : : Format KDiffPlugin : : determineDiffFormat ( const TQStringList lines ) const
{
TQString line ;
if ( lines . count ( ) = = 0 )
{
return KDiffPlugin : : Empty ;
}
TQStringList : : ConstIterator it = lines . begin ( ) ;
while ( it ! = lines . end ( ) )
{
line = ( * it ) ;
if ( line . find ( TQRegExp ( " ^[0-9]+[0-9,]*[acd][0-9]+[0-9,]*$ " ) , 0 ) = = 0 )
{
return KDiffPlugin : : Normal ;
}
else if ( line . find ( TQRegExp ( " ^--- " ) , 0 ) = = 0 )
{
// unified has first a '^--- ' line, then a '^+++ ' line
return KDiffPlugin : : Unified ;
}
else if ( line . find ( TQRegExp ( " ^ \\ * \\ * \\ * [^ \\ t]+ \\ t " ) , 0 ) = = 0 )
{
// context has first a '^*** ' line, then a '^--- ' line
return KDiffPlugin : : Context ;
}
else if ( line . find ( TQRegExp ( " ^[acd][0-9]+ [0-9]+ " ) , 0 ) = = 0 )
{
return KDiffPlugin : : RCS ;
}
else if ( line . find ( TQRegExp ( " ^[0-9]+[0-9,]*[acd] " ) , 0 ) = = 0 )
{
return KDiffPlugin : : Ed ;
}
+ + it ;
}
return KDiffPlugin : : Unknown ;
}
enum KDiffPlugin : : DiffProgram KDiffPlugin : : determineDiffProgram ( const TQStringList lines ) const
{
if ( lines . count ( ) = = 0 )
{
return KDiffPlugin : : Undeterminable ;
}
TQStringList : : ConstIterator it = lines . begin ( ) ;
// very crude, might need some more refining
TQRegExp diffRE ( " ^diff .* " ) ;
TQRegExp p4sRE ( " ^==== " ) ;
bool indexFound = false ;
while ( it ! = lines . end ( ) )
{
if ( ( * it ) . startsWith ( " Index: " ) )
indexFound = true ;
else if ( ( * it ) . startsWith ( " retrieving revision " ) )
return KDiffPlugin : : CVSDiff ;
else if ( diffRE . exactMatch ( * it ) )
return KDiffPlugin : : Diff ;
else if ( p4sRE . exactMatch ( * it ) )
return KDiffPlugin : : Perforce ;
+ + it ;
}
if ( indexFound ) // but no "retrieving revision" found like only cvs diff adds.
return KDiffPlugin : : SubVersion ;
return KDiffPlugin : : Undeterminable ;
}
const TQString KDiffPlugin : : determineI18nedFormat ( enum KDiffPlugin : : Format diffFormat ) const
{
TQString format ;
switch ( diffFormat )
{
case KDiffPlugin : : Context :
format = i18n ( " Context " ) ;
break ;
case KDiffPlugin : : Ed :
format = i18n ( " Ed " ) ;
break ;
case KDiffPlugin : : Normal :
format = i18n ( " Normal " ) ;
break ;
case KDiffPlugin : : RCS :
format = i18n ( " RCS " ) ;
break ;
case KDiffPlugin : : Unified :
format = i18n ( " Unified " ) ;
break ;
case KDiffPlugin : : Empty :
format = i18n ( " Not Available (file empty) " ) ;
break ;
case KDiffPlugin : : Unknown :
format = i18n ( " Unknown " ) ;
break ;
case KDiffPlugin : : SideBySide :
format = i18n ( " Side by Side " ) ;
}
return format ;
}
const TQString KDiffPlugin : : determineI18nedProgram ( enum KDiffPlugin : : DiffProgram diffProgram ) const
{
TQString program ;
switch ( diffProgram )
{
case KDiffPlugin : : CVSDiff :
program = i18n ( " CVSDiff " ) ;
break ;
case KDiffPlugin : : Diff :
program = i18n ( " Diff " ) ;
break ;
case KDiffPlugin : : Diff3 :
program = i18n ( " Diff3 " ) ;
break ;
case KDiffPlugin : : Perforce :
program = i18n ( " Perforce " ) ;
break ;
case KDiffPlugin : : SubVersion :
program = i18n ( " SubVersion " ) ;
break ;
case KDiffPlugin : : Undeterminable :
program = i18n ( " Unknown " ) ;
break ;
}
return program ;
}
void KDiffPlugin : : determineDiffInfo ( const TQStringList lines ,
enum KDiffPlugin : : Format diffFormat ,
int * numberOfFiles ,
int * numberOfHunks ,
int * numberOfAdditions ,
int * numberOfChanges ,
int * numberOfDeletions )
{
TQString line ;
TQRegExp edAdd ( " ([0-9]+) ( | , ( [ 0 - 9 ] + ) ) a " ) ;
TQRegExp edDel ( " ([0-9]+) ( | , ( [ 0 - 9 ] + ) ) d " ) ;
TQRegExp edMod ( " ([0-9]+) ( | , ( [ 0 - 9 ] + ) ) c " ) ;
TQRegExp normalAdd ( " [0-9]+a([0-9]+) ( | , ( [ 0 - 9 ] + ) ) " ) ;
TQRegExp normalDel ( " ([0-9]+) ( | , ( [ 0 - 9 ] + ) ) d ( | [ 0 - 9 ] + ) " ) ;
TQRegExp normalMod ( " ([0-9]+) ( | , ( [ 0 - 9 ] + ) ) c ( [ 0 - 9 ] + ) ( | , ( [ 0 - 9 ] + ) ) " ) ;
TQRegExp rcsAdd ( " a[0-9]+ ([0-9]+) " ) ;
TQRegExp rcsDel ( " d[0-9]+ ([0-9]+) " ) ;
TQStringList : : ConstIterator it = lines . begin ( ) ;
switch ( diffFormat )
{
case KDiffPlugin : : Context :
while ( it ! = lines . end ( ) )
{
if ( ( * it ) . startsWith ( " *************** " ) )
{
( * numberOfHunks ) + + ;
// kdDebug(7034) << "Context Hunk : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " *** " ) )
{
( * numberOfFiles ) + + ;
// kdDebug(7034) << "Context File : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " --- " ) ) { } // ignore
else if ( ( * it ) . startsWith ( " + " ) )
{
( * numberOfAdditions ) + + ;
// kdDebug(7034) << "Context Insertion : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " - " ) )
{
( * numberOfDeletions ) + + ;
// kdDebug(7034) << "Context Deletion : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " ! " ) )
{
( * numberOfChanges ) + + ;
// kdDebug(7034) << "Context Modified : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " " ) )
{
// kdDebug(7034) << "Context Context : " << (*it) << endl;
}
else
{
// kdDebug(7034) << "Context Unknown : " << (*it) << endl;
}
+ + it ;
}
( * numberOfChanges ) / = 2 ; // changes are in both parts of the hunks
( * numberOfFiles ) - = ( * numberOfHunks ) ; // it counts old parts of a hunk as files :(
break ;
case KDiffPlugin : : Ed :
while ( it ! = lines . end ( ) )
{
if ( ( * it ) . startsWith ( " diff " ) )
{
( * numberOfFiles ) + + ;
// kdDebug(7034) << "Ed File : " << (*it) << endl;
}
else if ( edAdd . exactMatch ( ( * it ) ) )
{
// kdDebug(7034) << "Ed Insertion : " << (*it) << endl;
( * numberOfHunks ) + + ;
+ + it ;
while ( it ! = lines . end ( ) & & ! ( * it ) . startsWith ( " . " ) )
{
( * numberOfAdditions ) + + ;
// kdDebug(7034) << "Ed Insertion : " << (*it) << endl;
+ + it ;
}
}
else if ( edDel . exactMatch ( ( * it ) ) )
{
// kdDebug(7034) << "Ed Deletion : " << (*it) << endl;
( * numberOfHunks ) + + ;
( * numberOfDeletions ) + = ( edDel . cap ( 3 ) . isEmpty ( ) ? 1 : edDel . cap ( 3 ) . toInt ( ) - edDel . cap ( 1 ) . toInt ( ) + 1 ) ;
// kdDebug(7034) << "Ed noOfLines : " << (edDel.cap(3).isEmpty() ? 1 : edDel.cap(3).toInt() - edDel.cap(1).toInt() + 1) << endl;
}
else if ( edMod . exactMatch ( ( * it ) ) )
{
// kdDebug(7034) << "Ed Modification : " << (*it) << endl;
if ( edMod . cap ( 3 ) . isEmpty ( ) )
( * numberOfDeletions ) + + ;
else
( * numberOfDeletions ) + = edMod . cap ( 3 ) . toInt ( ) - edMod . cap ( 1 ) . toInt ( ) + 1 ;
( * numberOfHunks ) + + ;
+ + it ;
while ( it ! = lines . end ( ) & & ! ( * it ) . startsWith ( " . " ) )
{
( * numberOfAdditions ) + + ;
// kdDebug(7034) << "Ed Modification : " << (*it) << endl;
+ + it ;
}
}
else
{
// kdDebug(7034) << "Ed Unknown : " << (*it) << endl;
}
+ + it ;
}
break ;
case KDiffPlugin : : Normal :
while ( it ! = lines . end ( ) )
{
if ( ( * it ) . startsWith ( " diff " ) )
{
( * numberOfFiles ) + + ;
// kdDebug(7034) << "Normal File : " << (*it) << endl;
}
else if ( normalAdd . exactMatch ( * it ) )
{
// kdDebug(7034) << "Normal Insertion : " << (*it) << endl;
( * numberOfHunks ) + + ;
if ( normalAdd . cap ( 3 ) . isEmpty ( ) )
{
( * numberOfAdditions ) + + ;
// kdDebug(7034) << "Normal Addition : " << 1 << endl;
}
else
{
( * numberOfAdditions ) + = normalAdd . cap ( 3 ) . toInt ( ) - normalAdd . cap ( 1 ) . toInt ( ) + 1 ;
// kdDebug(7034) << "Normal Addition : " << normalAdd.cap(3).toInt() - normalAdd.cap(1).toInt() + 1 << endl;
}
}
else if ( normalDel . exactMatch ( * it ) )
{
// kdDebug(7034) << "Normal Deletion : " << (*it) << endl;
( * numberOfHunks ) + + ;
if ( normalDel . cap ( 3 ) . isEmpty ( ) )
{
( * numberOfDeletions ) + + ;
// kdDebug(7034) << "Normal Deletion : " << 1 << endl;
}
else
{
( * numberOfDeletions ) + = normalDel . cap ( 3 ) . toInt ( ) - normalDel . cap ( 1 ) . toInt ( ) + 1 ;
// kdDebug(7034) << "Normal Deletion : " << normalDel.cap(3).toInt() - normalDel.cap(1).toInt() + 1 << endl;
}
}
else if ( normalMod . exactMatch ( * it ) )
{
// kdDebug(7034) << "Normal Modification : " << (*it) << endl;
( * numberOfHunks ) + + ;
if ( normalMod . cap ( 3 ) . isEmpty ( ) )
{
( * numberOfDeletions ) + + ;
// kdDebug(7034) << "Normal Deletion : " << 1 << endl;
}
else
{
( * numberOfDeletions ) + = normalMod . cap ( 3 ) . toInt ( ) - normalMod . cap ( 1 ) . toInt ( ) + 1 ;
// kdDebug(7034) << "Normal Deletion : " << normalMod.cap(3).toInt() - normalMod.cap(1).toInt() + 1 << endl;
}
if ( normalMod . cap ( 6 ) . isEmpty ( ) )
{
( * numberOfAdditions ) + + ;
// kdDebug(7034) << "Normal Addition : " << 1 << endl;
}
else
{
( * numberOfAdditions ) + = normalMod . cap ( 6 ) . toInt ( ) - normalMod . cap ( 4 ) . toInt ( ) + 1 ;
// kdDebug(7034) << "Normal Addition : " << normalMod.cap(6).toInt() - normalMod.cap(4).toInt() + 1 << endl;
}
}
else if ( ( * it ) . startsWith ( " > " ) )
{
// numberOfAdditions++;
// kdDebug(7034) << "Normal Insertion : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " < " ) )
{
// numberOfDeletions++;
// kdDebug(7034) << "Normal Deletion : " << (*it) << endl;
}
else
{
// kdDebug(7034) << "Normal Unknown : " << (*it) << endl;
}
+ + it ;
}
break ;
case KDiffPlugin : : RCS :
while ( it ! = lines . end ( ) )
{
if ( ( * it ) . startsWith ( " diff " ) ) // works for cvs diff, have to test for normal diff
{
// kdDebug(7034) << "RCS File : " << (*it) << endl;
( * numberOfFiles ) + + ;
}
else if ( rcsAdd . exactMatch ( * it ) )
{
// kdDebug(7034) << "RCS Insertion : " << (*it) << endl;
( * numberOfHunks ) + + ;
( * numberOfAdditions ) + = rcsAdd . cap ( 1 ) . toInt ( ) ;
// kdDebug(7034) << "RCS noOfLines : " << rcsAdd.cap(1).toInt() << endl;
}
else if ( rcsDel . exactMatch ( * it ) )
{
// kdDebug(7034) << "RCS Deletion : " << (*it) << endl;
( * numberOfHunks ) + + ;
( * numberOfDeletions ) + = rcsDel . cap ( 1 ) . toInt ( ) ;
// kdDebug(7034) << "RCS noOfLines : " << rcsDel.cap(1).toInt() << endl;
}
else
{
// kdDebug(7034) << "RCS Unknown : " << (*it) << endl;
}
+ + it ;
}
break ;
case KDiffPlugin : : Unified :
while ( it ! = lines . end ( ) )
{
if ( ( * it ) . startsWith ( " @@ " ) )
{
( * numberOfHunks ) + + ;
// kdDebug(7034) << "Unified Hunk : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " --- " ) )
{
( * numberOfFiles ) + + ;
// kdDebug(7034) << "Unified File : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " +++ " ) ) { } // ignore (dont count as insertion)
else if ( ( * it ) . startsWith ( " + " ) )
{
( * numberOfAdditions ) + + ;
// kdDebug(7034) << "Unified Insertion : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " - " ) )
{
( * numberOfDeletions ) + + ;
// kdDebug(7034) << "Unified Deletion : " << (*it) << endl;
}
else if ( ( * it ) . startsWith ( " " ) )
{
// kdDebug(7034) << "Unified Context : " << (*it) << endl;
}
else
{
// kdDebug(7034) << "Unified Unknown : " << (*it) << endl;
}
+ + it ;
}
break ;
case KDiffPlugin : : Empty :
case KDiffPlugin : : Unknown :
case KDiffPlugin : : SideBySide :
break ;
}
}
# include "tdefile_diff.moc"