summaryrefslogtreecommitdiffstats
path: root/vcs/cvsservice/cvsfileinfoprovider.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vcs/cvsservice/cvsfileinfoprovider.cpp')
-rw-r--r--vcs/cvsservice/cvsfileinfoprovider.cpp314
1 files changed, 314 insertions, 0 deletions
diff --git a/vcs/cvsservice/cvsfileinfoprovider.cpp b/vcs/cvsservice/cvsfileinfoprovider.cpp
new file mode 100644
index 00000000..1ac5bd76
--- /dev/null
+++ b/vcs/cvsservice/cvsfileinfoprovider.cpp
@@ -0,0 +1,314 @@
+/***************************************************************************
+ * Copyright (C) 2003 by Mario Scalas *
+ * *
+ * 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 <qregexp.h>
+#include <qtimer.h>
+#include <kurl.h>
+#include <kdebug.h>
+
+#include <urlutil.h>
+#include <kdevproject.h>
+
+#include <dcopref.h>
+#include <cvsjob_stub.h>
+#include <cvsservice_stub.h>
+
+#include "cvspart.h"
+#include "cvsdir.h"
+#include "cvsentry.h"
+#include "cvsfileinfoprovider.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// class CVSFileInfoProvider
+///////////////////////////////////////////////////////////////////////////////
+
+CVSFileInfoProvider::CVSFileInfoProvider( CvsServicePart *parent, CvsService_stub *cvsService )
+ : KDevVCSFileInfoProvider( parent, "cvsfileinfoprovider" ),
+ m_requestStatusJob( 0 ), m_cvsService( cvsService ), m_cachedDirEntries( 0 )
+{
+ connect( this, SIGNAL(needStatusUpdate(const CVSDir&)), this, SLOT(updateStatusFor(const CVSDir&)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+CVSFileInfoProvider::~CVSFileInfoProvider()
+{
+ if (m_requestStatusJob && m_requestStatusJob->isRunning())
+ m_requestStatusJob->cancel();
+ delete m_requestStatusJob;
+ delete m_cachedDirEntries;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const VCSFileInfoMap *CVSFileInfoProvider::status( const QString &dirPath )
+{
+ // Same dir: we can do with cache ...
+ if (dirPath != m_previousDirPath)
+ {
+ // ... different dir: flush old cache and cache new dir
+ delete m_cachedDirEntries;
+ CVSDir cvsdir( projectDirectory() + QDir::separator() + dirPath );
+ m_previousDirPath = dirPath;
+ m_cachedDirEntries = cvsdir.cacheableDirStatus();
+ }
+ return m_cachedDirEntries;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool CVSFileInfoProvider::requestStatus( const QString &dirPath, void *callerData, bool recursive, bool checkRepos )
+{
+ m_savedCallerData = callerData;
+ if (m_requestStatusJob)
+ {
+ delete m_requestStatusJob;
+ m_requestStatusJob = 0;
+ }
+ // Flush old cache
+ if (m_cachedDirEntries)
+ {
+ delete m_cachedDirEntries;
+ m_cachedDirEntries = 0;
+ m_previousDirPath = dirPath;
+ }
+
+
+ if (!checkRepos) {
+ kdDebug(9006) << "No repo check reqested; Just read CVS/Entries from: " << dirPath << endl;
+ QDir qd(projectDirectory()+QDir::separator()+dirPath);
+ CVSDir cdir(qd);
+ if (cdir.isValid())
+ {
+ emit needStatusUpdate(cdir);
+ return true;
+ }
+ kdDebug(9006) << dirPath << " is not a valid cvs directory" << endl;
+ return false;
+ }
+
+ // Fix a possible bug in cvs client:
+ // When "cvs status" get's called nonrecursiv for a directory, it will
+ // not print anything if the path ends with a slash. So we need to ensure
+ // this here.
+ QString newPath = dirPath;
+ if (newPath.endsWith("/"))
+ newPath.truncate( newPath.length()-1 );
+
+
+ // path, recursive, tagInfo: hmmm ... we may use tagInfo for collecting file tags ...
+ DCOPRef job = m_cvsService->status( newPath, recursive, false );
+ m_requestStatusJob = new CvsJob_stub( job.app(), job.obj() );
+
+ kdDebug(9006) << "Running command : " << m_requestStatusJob->cvsCommand() << endl;
+ connectDCOPSignal( job.app(), job.obj(), "jobExited(bool, int)", "slotJobExited(bool, int)", true );
+ connectDCOPSignal( job.app(), job.obj(), "receivedStdout(QString)", "slotReceivedOutput(QString)", true );
+ return m_requestStatusJob->execute();
+ /*
+ kdDebug(9006) << k_funcinfo << "Attempting to parse " << dirPath << " using CVS/Entries" << endl;
+ QDir qd(dirPath);
+ CVSDir cdir(qd);
+ if (cdir.isValid())
+ {
+ emit needStatusUpdate(cdir);
+ return true;
+ }*/
+}
+
+void CVSFileInfoProvider::propagateUpdate()
+{
+ emit statusReady( *m_cachedDirEntries, m_savedCallerData );
+}
+
+void CVSFileInfoProvider::updateStatusFor(const CVSDir& dir)
+{
+ m_cachedDirEntries = dir.cacheableDirStatus();
+ printOutFileInfoMap( *m_cachedDirEntries );
+
+ /* FileTree will call requestStatus() everytime the user expands a directory
+ * Unfortunatly requestStatus() will be called before the
+ * VCSFileTreeViewItem of the directory will be filled with the files
+ * it contains. Meaning, m_savedCallerData contains no childs at that
+ * time. When a dcop call is made to run "cvs status" this is no problem.
+ * The dcop call takes quit long, and so FileTree has enough time the fill
+ * in the childs before we report the status back.
+ * As far as the reading of the CVS/Entries file is very fast,
+ * it will happen that we emit statusReady() here before the directory
+ * item conains any childs. Therefor we need to give FileTree some time
+ * to update the directory item before we give the status infos.
+ */
+ QTimer::singleShot( 1000, this, SLOT(propagateUpdate()) );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CVSFileInfoProvider::slotJobExited( bool normalExit, int /*exitStatus*/ )
+{
+ kdDebug(9006) << "CVSFileInfoProvider::slotJobExited(bool,int)" << endl;
+ if (!normalExit)
+ return;
+
+// m_cachedDirEntries = parse( m_requestStatusJob->output() );
+ m_cachedDirEntries = parse( m_statusLines );
+ // Remove me when not debugging
+ printOutFileInfoMap( *m_cachedDirEntries );
+
+ emit statusReady( *m_cachedDirEntries, m_savedCallerData );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CVSFileInfoProvider::slotReceivedOutput( QString someOutput )
+{
+ QStringList strings = m_bufferedReader.process( someOutput );
+ if (strings.count() > 0)
+ {
+ m_statusLines += strings;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CVSFileInfoProvider::slotReceivedErrors( QString /*someErrors*/ )
+{
+ /* Nothing to do */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+QString CVSFileInfoProvider::projectDirectory() const
+{
+ return owner()->project()->projectDirectory();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+VCSFileInfoMap *CVSFileInfoProvider::parse( QStringList stringStream )
+{
+ QRegExp rx_recordStart( "^=+$" );
+ QRegExp rx_fileName( "^File: (\\.|\\-|\\w)+" );
+ QRegExp rx_fileStatus( "Status: (\\.|-|\\s|\\w)+" );
+ QRegExp rx_fileWorkRev( "\\bWorking revision:" );
+ QRegExp rx_fileRepoRev( "\\bRepository revision:" );
+ //QRegExp rx_stickyTag( "\\s+(Sticky Tag:\\W+(w+|\\(none\\)))" );
+ //QRegExp rx_stickyDate( "" ); // @todo but are they useful?? :-/
+ //QRegExp rx_stickyOptions( "" ); //@todo
+
+ QString fileName,
+ fileStatus,
+ workingRevision,
+ repositoryRevision,
+ stickyTag,
+ stickyDate,
+ stickyOptions;
+
+ VCSFileInfoMap *vcsStates = new VCSFileInfoMap;
+
+ int state = 0;
+ const int lastAcceptableState = 4;
+ // This is where the dirty parsing is done: from a string stream representing the
+ // 'cvs log' output we build a map with more useful strunctured data ;-)
+ for (QStringList::const_iterator it=stringStream.begin(); it != stringStream.end(); ++it)
+ {
+ QString s = (*it).stripWhiteSpace();
+ kdDebug(9006) << ">> Parsing: " << s << endl;
+
+ if (rx_recordStart.exactMatch( s ))
+ state = 1;
+ else if (state == 1 && rx_fileName.search( s ) >= 0 && rx_fileStatus.search( s ) >= 0) // FileName
+ {
+ fileName = rx_fileName.cap().replace( "File:", "" ).stripWhiteSpace();
+ fileStatus = rx_fileStatus.cap().replace( "Status:", "" ).stripWhiteSpace();
+ ++state; // Next state
+ kdDebug(9006) << ">> " << fileName << ", " << fileStatus << endl;
+ }
+ else if (state == 2 && rx_fileWorkRev.search( s ) >= 0)
+ {
+ workingRevision = s.replace( "Working revision:", "" ).stripWhiteSpace();
+
+ QRegExp rx_revision( "\\b(((\\d)+\\.?)*|New file!)" );
+ if (rx_revision.search( workingRevision ) >= 0)
+ {
+ workingRevision = rx_revision.cap();
+ kdDebug(9006) << ">> WorkRev: " << workingRevision << endl;
+ ++state;
+ }
+ }
+ else if (state == 3 && rx_fileRepoRev.search( s ) >= 0)
+ {
+ repositoryRevision = s.replace( "Repository revision:", "" ).stripWhiteSpace();
+
+ QRegExp rx_revision( "\\b(((\\d)+\\.?)*|No revision control file)" );
+ if (rx_revision.search( s ) >= 0)
+ {
+ repositoryRevision = rx_revision.cap();
+ kdDebug(9006) << ">> RepoRev: " << repositoryRevision << endl;
+ ++state;
+ }
+ }
+/*
+ else if (state == 4 && rx_stickyTag.search( s ) >= 0)
+ {
+ stickyTag = rx_stickyTag.cap();
+ ++state;
+ }
+*/
+ else if (state >= lastAcceptableState) // OK, parsed all useful info?
+ {
+ // Package stuff, put into map and get ready for a new record
+ VCSFileInfo vcsInfo( fileName, workingRevision, repositoryRevision,
+ String2EnumState( fileStatus ) );
+ kdDebug(9006) << "== Inserting: " << vcsInfo.toString() << endl;
+ vcsStates->insert( fileName, vcsInfo );
+ }
+ }
+ return vcsStates;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+VCSFileInfo::FileState CVSFileInfoProvider::String2EnumState( QString stateAsString )
+{
+ // @todo add more status as "Conflict" and "Sticky" (but I dunno how CVS writes it so I'm going
+ // to await until I have a conflict or somebody else fix it ;-)
+ // @todo use QRegExp for better matching since it seems strings have changed between CVS releases :-(
+ // @todo a new state for 'Needs patch'
+ if (stateAsString == "Up-to-date")
+ return VCSFileInfo::Uptodate;
+ else if (stateAsString == "Locally Modified")
+ return VCSFileInfo::Modified;
+ else if (stateAsString == "Locally Added")
+ return VCSFileInfo::Added;
+ else if (stateAsString == "Unresolved Conflict")
+ return VCSFileInfo::Conflict;
+ else if (stateAsString == "Needs Patch")
+ return VCSFileInfo::NeedsPatch;
+ else if (stateAsString == "Needs Checkout")
+ return VCSFileInfo::NeedsCheckout;
+ else
+ return VCSFileInfo::Unknown; /// \FIXME exhaust all the previous cases first ;-)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CVSFileInfoProvider::printOutFileInfoMap( const VCSFileInfoMap &map )
+{
+ kdDebug(9006) << "Files parsed:" << endl;
+ for (VCSFileInfoMap::const_iterator it = map.begin(); it != map.end(); ++it)
+ {
+ const VCSFileInfo &vcsInfo = *it;
+ kdDebug(9006) << vcsInfo.toString() << endl;
+ }
+}
+
+#include "cvsfileinfoprovider.moc"
+// kate: space-indent on; indent-width 4; replace-tabs on;