You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/kfile-plugins/diff/kfile_diff.cpp

611 lines
16 KiB

/**************************************************************************
** kfile_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 <klocale.h>
#include <kprocess.h>
#include <kurl.h>
#include "kfile_diff.h"
K_EXPORT_COMPONENT_FACTORY(kfile_diff, KGenericFactory<KDiffPlugin>("kfile_diff"))
KDiffPlugin::KDiffPlugin(TQObject *tqparent, const char *name,
const TQStringList &preferredItems)
: KFilePlugin(tqparent, 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.tqfind( TQRegExp( "^[0-9]+[0-9,]*[acd][0-9]+[0-9,]*$" ), 0 ) == 0 )
{
return KDiffPlugin::Normal;
}
else if ( line.tqfind( TQRegExp( "^--- " ), 0 ) == 0 )
{
// unified has first a '^--- ' line, then a '^+++ ' line
return KDiffPlugin::Unified;
}
else if ( line.tqfind( TQRegExp( "^\\*\\*\\* [^\\t]+\\t" ), 0 ) == 0 )
{
// context has first a '^*** ' line, then a '^--- ' line
return KDiffPlugin::Context;
}
else if ( line.tqfind( TQRegExp( "^[acd][0-9]+ [0-9]+" ), 0 ) == 0 )
{
return KDiffPlugin::RCS;
}
else if ( line.tqfind( 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 "kfile_diff.moc"
/* vim: set ts=4 sw=4 noet: */