コード例 #1
0
ファイル: Cache.cpp プロジェクト: pampersrocker/G-CVSNT
// Read a string
std::string TortoiseRegistryCache::ReadString(const std::string& key, const std::string& name, 
                                              const std::string& defaultValue, bool *exists)
{
   std::string myKey = key;
   MakeLowerCase(myKey);
   FindAndReplace<std::string>(myKey, "\\", "/");

   std::string regPath = EnsureTrailingDelimiter(m_StoragePath) 
      + EnsureTrailingDelimiter(myKey) + name;
   return TortoiseRegistry::ReadString(regPath, defaultValue, exists);
}
コード例 #2
0
ファイル: Cache.cpp プロジェクト: pampersrocker/G-CVSNT
// Read a vector
void TortoiseRegistryCache::ReadVector(const std::string& key, const std::string& prefix, 
                                       std::vector<std::string>& values)
{
   std::string myKey = key;
   MakeLowerCase(myKey);
   FindAndReplace<std::string>(myKey, "\\", "/");

   std::string regPath = EnsureTrailingDelimiter(m_StoragePath) 
      + EnsureTrailingDelimiter(myKey) + prefix;
   TortoiseRegistry::ReadVector(regPath, values);
}
コード例 #3
0
ファイル: Cache.cpp プロジェクト: pampersrocker/G-CVSNT
// Write a string
void TortoiseRegistryCache::WriteString(const std::string& key, const std::string& name, 
                                        const std::string& value)
{
   // Update LRU list
   MarkAsUsed(key);

   std::string myKey = key;
   MakeLowerCase(myKey);
   FindAndReplace<std::string>(myKey, "\\", "/");

   // Write values
   std::string regPath = EnsureTrailingDelimiter(m_StoragePath) 
      + EnsureTrailingDelimiter(myKey) + name;
   TortoiseRegistry::WriteString(regPath, value);
}
コード例 #4
0
ファイル: CvsIgnore.cpp プロジェクト: chuan11/cfprogramas
// Do update ignored list
void DoUpdateIgnoredList()
{
   TDEBUG_ENTER("DoUpdateIgnoredList");
   defIgnoredList.clear();
   defIgnoreListTimeStamp = GetTickCount();

   // Default ignored list from cvsnt/src/ignore.c
   static const char* ign_default =
      ". .. RCSLOG tags TAGS RCS SCCS .make.state "
      ".nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj "
      "*.so *.Z *~ *.old *.elc *.ln *.bak *.orig *.rej _$* *$";

   ign_add(ign_default, defIgnoredList);

   // .cvsignore from homedir
   std::string homeDir;
   bool GotHomeDir = GetHomeDirectory(homeDir);

   if (GotHomeDir)
   {
      // Read .cvsignore file from HOME directory.
      homeDir = EnsureTrailingDelimiter(homeDir);
      homeDir += ".cvsignore";
      ReadIgnoredFile(homeDir, defIgnoredList);
   }
}
コード例 #5
0
std::string CVSStatus::CVSRootForPath(std::string path)   
{   
    TDEBUG_ENTER("CVSStatus::CVSRootForPath");   
    TDEBUG_TRACE("path: " << path);     
    
    // We must be a directory with a CVS dir     
    path = GetDirectoryPart(path);   
    path = EnsureTrailingDelimiter(path);     
    if (!CVSDirectoryHere(path))     
    {     
        TDEBUG_TRACE("No CVS directory found in " << path);     
        return "";    
    }     
    
    std::string root;    
    std::string rootFile = path + "CVS/Root";    
    std::ifstream in(rootFile.c_str(), std::ios::in);     
    if (!in.good())   
    {     
        TDEBUG_TRACE("Error opening " << rootFile);    
        return "";    
    }     
    std::getline(in, root);    
    
    return root;   
}   
コード例 #6
0
ファイル: Cache.cpp プロジェクト: pampersrocker/G-CVSNT
// Update LRU list
void TortoiseRegistryCache::MarkAsUsed(const std::string& key)
{
   std::string myKey = key;
   MakeLowerCase(myKey);
   FindAndReplace<std::string>(myKey, "\\", "/");

   std::string regPath = EnsureTrailingDelimiter(m_StoragePath) + "LRU";
   std::vector<std::string> keys;
   TortoiseRegistry::ReadVector(regPath, keys);

   std::vector<std::string>::iterator it = keys.begin();
   while (it != keys.end())
   {
      if (*it == myKey)
      {
         break;
      }
      it++;
   }

   if (it != keys.end())
   {
      keys.erase(it);
   }
   
   keys.insert(keys.begin(), myKey);

   TortoiseRegistry::WriteVector(regPath, keys);
}
コード例 #7
0
ファイル: FileTree.cpp プロジェクト: pampersrocker/G-CVSNT
// Get path
std::string FileTree::Node::GetPathFrom(const Node* node) const
{
   if (m_Parent == node || m_Parent == 0)
   {
      return GetName();
   }
   else
   {
      return EnsureTrailingDelimiter(m_Parent->GetPathFrom(node)) + GetName();
   }
}
コード例 #8
0
ファイル: FileTree.cpp プロジェクト: pampersrocker/G-CVSNT
// Get path
std::string FileTree::Node::GetPath() const
{
   if (m_Parent)
   {
      return EnsureTrailingDelimiter(m_Parent->GetPath()) + m_Parent->GetName();
   }
   else
   {
      return "";
   }
}
コード例 #9
0
ファイル: CvsIgnore.cpp プロジェクト: chuan11/cfprogramas
void BuildIgnoredList(std::vector<std::string> & ignlist, const std::string& path,
                      DWORD *timeStamp, FileChangeParams *fcp)
{
   TDEBUG_ENTER("BuildIgnoredList");
   bool doUpdate = false;

   std::string ignPath(path);
   ignPath = EnsureTrailingDelimiter(ignPath);
   ignPath += ".cvsignore";
   FileChangeParams myFcp;
   if (fcp)
      myFcp = GetFileChangeParams(ignPath);

   // Update default ignored list
   BuildDefaultIgnoredList();
   if (timeStamp)
   {
      if ((*timeStamp != defIgnoreListTimeStamp) || (*timeStamp == 0))
         doUpdate = true;
   }
   else
   {
      doUpdate = true;
   }

   // check directory ignored file
   if (fcp)
   {
      if ((fcp->IsNull()) || (*fcp != myFcp))
      {
         doUpdate = true;
      }
   }
   else
   {
      doUpdate = true;
   }

   // Do we have to update
   if (doUpdate)
   {
      ignlist.clear();
      ignlist = defIgnoredList;

      // TODO: read $CVSROOT/CVSROOT/cvsignore
      
      // Read .cvsignore from current directory
      ReadIgnoredFile(ignPath, ignlist);
      if (fcp)
         *fcp = myFcp;
      if (timeStamp)
         *timeStamp = defIgnoreListTimeStamp;
   }
}
コード例 #10
0
std::string CVSStatus::CVSRepositoryForPath(std::string path)   
{   
    TDEBUG_ENTER("CVSStatus::CVSRepositoryForPath");   
    TDEBUG_TRACE("  path: '" << path << "')");   
    
    // We must be a directory with a CVS dir     
    path = GetDirectoryPart(path);   
    path = EnsureTrailingDelimiter(path);     
    if (!CVSDirectoryHere(path))     
        return "";    
    
    std::string rootFile = path + "CVS/Repository";    
    std::ifstream in(rootFile.c_str(), std::ios::in);     
    if (!in.good())   
        return "";    
    std::string repository;    
    std::getline(in, repository);    
    
    // Apparently, the path in CVS/Repository may be either absolute or relative.    
    // So if it is absolute, we hack it to be relative.   
    TDEBUG_TRACE("  Repository is '" << repository << "')");    
    if (repository[0] == '/' || (repository.length() > 2 && repository[1] == ':'))   
    {     
        CVSRoot cvsroot(CVSRootForPath(path));   
        std::string root = cvsroot.GetDirectory();     
        TDEBUG_TRACE("  Root is '" << root << "')");   
        unsigned int i = 0;    
        // Find out how many leading chars match    
        while (i < root.length() && i < repository.length())    
        {    
            if (root[i] == repository[i]   
                || (root[i] == '\\' && repository[i] == '/')    
                || (root[i] == '/' && repository[i] == '\\'))   
            {   
                i++;     
                continue;   
            }   
            else   
            {   
                break;   
            }   
        }    
        // Also kill final slash     
        if (repository[i] == '/' || repository[i] == '\\')   
            ++i;   
        repository = repository.substr(i);    
    }     
    return repository;   
}   
コード例 #11
0
ファイル: CvsIgnore.cpp プロジェクト: chuan11/cfprogramas
// Build default ignored list
void BuildDefaultIgnoredList()
{
   TDEBUG_ENTER("BuildDefaultIgnoredList");
   CSHelper csHelper(myCriticalSection, true);
   std::string userCvsIgnoreFile;
   GetHomeDirectory(userCvsIgnoreFile);
   userCvsIgnoreFile = EnsureTrailingDelimiter(userCvsIgnoreFile) + ".cvsignore";
   FileChangeParams myFcp = GetFileChangeParams(userCvsIgnoreFile);

   // Update every dwUpdateIgnoredListInterval seconds
   if (GetTickCount() > defIgnoreListTimeStamp + 1000 * dwUpdateIgnoredListInterval)
   {
      DoUpdateIgnoredList();
      fcpCvsignore = myFcp;
   }
   // Update if .cvsignore in home dir has changed
   else if (fcpCvsignore != myFcp)
   {
      DoUpdateIgnoredList();
      fcpCvsignore = myFcp;
   }
}
コード例 #12
0
ファイル: Cache.cpp プロジェクト: pampersrocker/G-CVSNT
// Shrink cache
void TortoiseRegistryCache::Shrink()
{
   std::string regPath = EnsureTrailingDelimiter(m_StoragePath) + "LRU";
   std::vector<std::string> lruKeys, regKeys;
   TortoiseRegistry::ReadVector(regPath, lruKeys);

   if (lruKeys.size() <= m_MaxSize)
      return;

   // Remove keys from LRU list
   while (lruKeys.size() > m_MaxSize)
   {
      lruKeys.erase(lruKeys.end() - 1);
   }

   // Store keys in map for faster access
   std::vector<std::string>::iterator it = lruKeys.begin();
   std::map<std::string, bool> lruMap;
   while (it != lruKeys.end())
   {
      lruMap.insert(std::pair<std::string, bool>(*it, true));
      it++;
   }


   // Remove invalid keys
   TortoiseRegistry::ReadKeys(m_StoragePath, regKeys);
   std::string myKey;
   while (it != regKeys.end())
   {
      myKey = *it;
      MakeLowerCase(myKey);
      if (lruMap.find(myKey) == lruMap.end())
      {
         TortoiseRegistry::EraseKey(myKey);
      }
      it++;
   }
}
コード例 #13
0
ファイル: CvsEntries.cpp プロジェクト: maximilianomora/7hs
EntnodeData *Entries_SetVisited(const char* path, EntnodeMap& entries, const char* name,
                                const struct stat& finfo, bool isDir, bool isReadOnly,
                                bool isMissing, const std::vector<std::string>* ignlist)
{
   TDEBUG_ENTER("Entries_SetVisited");
   bool isCvs = false;
   std::string lookupName;
   if (isDir)
   {
      TDEBUG_TRACE("Is dir");
      EntnodeDir *adata = new EntnodeDir(path, name);
      ENTNODE anode(adata);
      adata->UnRef();

      lookupName = anode.Data()->GetName();
      EntnodeMap::iterator it = entries.find(lookupName);
      isCvs = it != entries.end();
      if (!isCvs)
         entries[lookupName] = anode;
   }
   else
   {
      TDEBUG_TRACE("Is no dir");
      EntnodeFile *adata = new EntnodeFile(path, name);
      ENTNODE anode(adata);
      adata->UnRef();

      lookupName = anode.Data()->GetName();
      EntnodeMap::iterator it = entries.find(lookupName);
      isCvs = it != entries.end();
      if (!isCvs)
         entries[lookupName] = anode;
   }

   const ENTNODE & theNode = entries[lookupName];
   EntnodeData *data = ((ENTNODE *)&theNode)->Data();
   data->SetVisited(true);
   if (!isCvs)
   {
      data->SetUnknown(true);
      if (ignlist && MatchIgnoredList(name, *ignlist) || 
         finfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
         data->SetIgnored(true);

      // the dir may have some cvs informations in it, despite the fact
      // that it is not referenced by the parent directory, so try
      // to figure it.

      if (!data->IsIgnored())
      {
         std::string cvsFile = path;
         cvsFile = EnsureTrailingDelimiter(cvsFile);
         cvsFile += name;
         cvsFile = EnsureTrailingDelimiter(cvsFile);
         cvsFile += "CVS";
         struct stat sb;
         TDEBUG_TRACE("Before stat");
         if (stat(cvsFile.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode))
         {
            data->SetUnknown(false);
         }
         TDEBUG_TRACE("After stat");
      }
   }

   data->SetReadOnly(isReadOnly);
   data->SetMissing(isMissing);

   if (isDir)
   {
      if (data->IsIgnored())
         data->SetDesc(_("Ignored Folder"));
      else if (data->IsUnknown())
         data->SetDesc(_("Non-CVS Folder"));
      else
         data->SetDesc(_("Folder"));
   }
   else if (!isMissing)
   {
      const char* ts = data->GetTS();

     TDEBUG_TRACE("Timestamp: " << (ts == 0 ? "NULL" : ts));
      // Revision "0" means "added"
      if (ts == 0)
      {
         data->SetUnmodified(true);
      }
      else if (strcmp(data->GetVN(), "0") == 0)
      {
         data->SetAdded(true);
         // Added files are always modified
         data->SetUnmodified(false);
      }
      else
      {
         data->SetUnmodified(unmodified(finfo, ts));
      }

      const char* ts_conflict = data->GetConflict();
      if (ts_conflict == 0)
         data->SetNeedsMerge(false);
      else
         data->SetNeedsMerge(unmodified(finfo,ts_conflict));

      data->SetLocked((finfo.st_mode & S_IWRITE) == 0);
      
      const char* info = 0;
      if (data->IsIgnored())
      {
         data->SetDesc(_("Ignored"));
      }
      else if (data->IsUnknown())
      {
         data->SetDesc(_("Non-CVS File"));
      }
      else if (data->NeedsMerge())
      {
         data->SetDesc(_("Conflict"));
      }
      else if ((info = data->GetOption()) != 0 && strcmp(info, "-kb") == 0)
      {
         data->SetDesc(data->IsUnmodified() ? _("Binary") : _("Mod. Binary"));
      }
      else
      {
         data->SetDesc(data->IsUnmodified() ? _("File") : _("Mod. File"));
      }
   }

   return data;
}
コード例 #14
0
ファイル: CvsEntries.cpp プロジェクト: maximilianomora/7hs
/* Read the entries file into a list, hashing on the file name.

   UPDATE_DIR is the name of the current directory, for use in error
   messages, or NULL if not known (that is, noone has gotten around
   to updating the caller to pass in the information).  */
bool Entries_Open(EntnodeMap& entries,
                  const char* fullpath,
                  FileChangeParams* fcp)
{
   TDEBUG_ENTER("Entries_Open");
   std::string cvsdir(fullpath);
   cvsdir = EnsureTrailingDelimiter(cvsdir);
   cvsdir += "CVS";
   cvsdir = EnsureTrailingDelimiter(cvsdir);

   if (fcp)
   {
      FileChangeParams myFcp = GetFileChangeParams(cvsdir + "Entries");
      if ((!(fcp->IsNull())) && (myFcp == *fcp))
      {
         return true;
      }
      *fcp = myFcp;
   }

   entries.clear();
   unsigned long sizeextra = 0;
   FILE* fpinx = fopen((cvsdir + "Entries.Extra").c_str(), "r");
   if (fpinx)
   {
      FileChangeParams myFcp = GetFileChangeParams(cvsdir + "Entries.Extra");
      sizeextra = myFcp.dwFileSizeLow;
   }
   FILE* fpin = fopen((cvsdir + "Entries").c_str(), "r");
   if (!fpin)
   {
      if (fpinx)
         fclose (fpinx);
      return false;
   }

   // Read contents of CVS/Rename into a set for easy lookup
   std::ifstream cvsRename((cvsdir + "Rename").c_str());
   std::set<std::string> renameEntries;
   while (cvsRename.good())
   {
      std::string file;
      // Each entry is
      //    newname
      //    (blank)
      //    newname
      //    oldname
      // We are only interested in the new name.
      std::getline(cvsRename, file);
      file = ExtractLastPart(file);
      renameEntries.insert(file);
      for (int i = 0; i < 3; ++i)
         std::getline(cvsRename, file);
   }
   EntnodeData* ent;
   char* extrabuf = 0;
   size_t lenreadx;
   if (fpinx && (sizeextra > 0))
   {
      extrabuf = (char*) malloc((sizeextra*2)+10);
      fseek(fpinx, 0, SEEK_SET);
      lenreadx = fread(extrabuf, sizeof(char), (sizeextra*2)+9, fpinx);
      *(extrabuf+lenreadx) = '\0';
      if (!feof(fpinx))
      {
         // could not read the whole file for some reason...
         free(extrabuf);
         extrabuf = 0;
      }
   }

   while ((ent = fgetentent(fpin, extrabuf, fullpath, 0, sizeextra)) != 0)
   {
      ENTNODE newnode(ent);
      ent->UnRef();

      std::string name = newnode.Data()->GetName();
      EntnodeMap::iterator it = entries.find(name);
      if (it != entries.end())
      {
         _ASSERT(false);
         TDEBUG_TRACE("Warning : duplicated entry in the 'CVS/Entries' file in folder " << fullpath);
      }
      std::set<std::string>::iterator renameIter = renameEntries.find(name);
      ent->SetRenamed(renameIter != renameEntries.end());
      entries[name] = newnode;
   }

   fclose (fpin);
   if (fpinx)
      fclose (fpinx);
   if (extrabuf)
      free (extrabuf);

   fpin = fopen((cvsdir + "Entries.log").c_str(), "r");
   if (fpin)
   {
      char cmd;

      while ((ent = fgetentent(fpin, extrabuf, fullpath, &cmd, sizeextra)) != 0)
      {
         ENTNODE newnode(ent);
         ent->UnRef();

         std::string name = newnode.Data()->GetName();

         switch (cmd)
         {
         case 'A':
            entries[name] = newnode;
            break;
         case 'R':
            entries.erase(std::string(name));
            break;
         default:
            /* Ignore unrecognized commands.  */
            TDEBUG_TRACE("Warning: Unrecognized command '" << cmd << "'");
            break;
         }
      }
      fclose (fpin);
   }
   return true;
}
コード例 #15
0
ファイル: FileTree.cpp プロジェクト: pampersrocker/G-CVSNT
// Get full name
std::string FileTree::Node::GetFullName() const
{
   return EnsureTrailingDelimiter(this->GetPath()) + GetName();
}
コード例 #16
0
// View a revision
bool DoView(DirectoryGroups& dirGroups, std::string rev)
{
   std::string dir = UniqueTemporaryDir();
   std::string viewFile = dir;
   bool ok = true;
   CVSAction glue(0);
   std::string cvsroot;
   bool unixSandbox = IsUnixSandbox(StripLastPart(dirGroups.mySingleAbsolute));

   if (!rev.empty())
   {
      viewFile += "\\" + MakeRevFilename(ExtractLastPart(dirGroups.mySingleAbsolute), rev);
      DeleteFileA(viewFile.c_str());
   }

   glue.SetProgressFinishedCaption(Printf(_("Viewing %s"), 
                                          wxText(dirGroups.mySingleAbsolute).c_str()));
   glue.SetProgressCaption(Printf(_("Viewing %s"), wxText(dirGroups.mySingleAbsolute).c_str()));
   cvsroot = CVSStatus::CVSRootForPath(dirGroups.mySingleDirectory);

   // Viewing revision
   if (!rev.empty())
   {
      glue.SetCVSRoot(CVSRoot(cvsroot));
      MakeArgs args; 
      std::string sTempDir = UniqueTemporaryDir();
      glue.SetCloseIfOK(true);
      args.add_option("checkout");
      if (unixSandbox)
      {
          CVSServerFeatures sf;
          sf.Initialize(&glue);
          sf.AddUnixLineEndingsFlag(args);
      }
      if (rev != "HEAD")
      {
         args.add_option("-r");
         args.add_option(rev);
      }
      args.add_option("-d");
      args.add_option("temp");
      std::string s = EnsureTrailingUnixDelimiter(CVSStatus::CVSRepositoryForPath(dirGroups.mySingleAbsolute))
         + ExtractLastPart(dirGroups.mySingleAbsolute);
      args.add_arg(s);
      
      ok = glue.Command(sTempDir, args);
      if (ok)
      {
         // Copy file to destination
         std::string file = EnsureTrailingDelimiter(sTempDir) + "temp\\" +
            ExtractLastPart(dirGroups.mySingleAbsolute);
         SetFileReadOnly(file.c_str(), false);
         CopyFileA(file.c_str(), viewFile.c_str(), false);
         SetFileReadOnly(viewFile.c_str(), true);
      }

      // Erase temporary directory
      DeleteDirectoryRec(sTempDir);

      if (!ok)
         goto Cleanup;
   }
     
   if (!FileExists(viewFile.c_str()))
   {
      DoMessageDialog(0, wxString(_("This file is new and has never been committed to the server or is an empty file on the server."))
                      + wxString(wxT("\n\n")) 
                      + wxString(wxText(dirGroups.mySingleAbsolute)));
      return true;
   }
   
   // Perform view, waiting for it to finish (so we can delete file)
   glue.LockProgressDialog(true);
   if (rev.empty())
      LaunchFile(dirGroups.mySingleAbsolute, true);
   else
      LaunchFile(viewFile, true);
   glue.LockProgressDialog(false);
    
Cleanup:
   // Clean up
   if (!rev.empty())
   {
      SetFileReadOnly(viewFile.c_str(), false);
      DeleteFileA(viewFile.c_str());
   }
   DeleteDirectoryRec(dir);

   return ok;
}
コード例 #17
0
bool DoDiff(DirectoryGroups& dirGroups,
            std::string rev1,
            std::string rev2,
            bool forceQuery)
{
   std::string dir = UniqueTemporaryDir();
   AutoDirectoryDeleter dirDeleter(dir);
   std::string diffFile = dir;
   std::string diffFile2 = diffFile;
   bool ok = true;
   CVSAction glue(0);

   if (rev1.empty())
      rev1 = CVSStatus::GetRevisionNumber(dirGroups.mySingleAbsolute);

   // Should we do a text diff only?
   std::string externalApp = GetExternalApplication("Diff",
                                                    dirGroups,
                                                    forceQuery);
   forceQuery = false;
   if (externalApp.empty())
   {
      // Perform a textual diff
      glue.SetProgressFinishedCaption(Printf(_("Finished diff in %s"), 
                                             wxText(dirGroups.mySingleDirectory).c_str()));
      glue.SetProgressCaption(Printf(_("Diffing in %s"), wxText(dirGroups.mySingleDirectory).c_str()));
      MakeArgs args;
      args.add_option("diff");
      args.add_option("-u");
      args.add_option("-r");
      args.add_option(rev1);
      if (!rev2.empty())
      {
         args.add_option("-r");
         args.add_option(rev2);
      }
      args.add_arg(dirGroups.mySingleRelative);

      return glue.Command(dirGroups.mySingleDirectory, args);
   }
   
   bool unixSandbox = IsUnixSandbox(StripLastPart(dirGroups.mySingleAbsolute));

   diffFile += "\\" + MakeRevFilename(ExtractLastPart(dirGroups.mySingleAbsolute), rev1);
   DeleteFileA(diffFile.c_str());
   AutoFileDeleter diffFileDeleter(diffFile);

   AutoFileDeleter diffFile2Deleter;
   if (!rev2.empty())
   {
      diffFile2 += "\\" + MakeRevFilename(ExtractLastPart(dirGroups.mySingleAbsolute), rev2);
      DeleteFileA(diffFile2.c_str());
      diffFile2Deleter.Attach(diffFile2);
   }

   glue.SetProgressFinishedCaption(Printf(_("Finished diff in %s"), 
                                          wxText(dirGroups.mySingleDirectory).c_str()));
   glue.SetProgressCaption(Printf(_("Diffing in %s"), wxText(dirGroups.mySingleDirectory).c_str()));
   std::string cvsroot = CVSStatus::CVSRootForPath(dirGroups.mySingleDirectory);

   CVSServerFeatures sf;
   if (unixSandbox)
       sf.Initialize(&glue);

   // Diffing two revisions
   if (!rev1.empty())
   {
       glue.SetCVSRoot(CVSRoot(cvsroot));
       MakeArgs args;
       std::string tempDir = UniqueTemporaryDir();
       glue.SetCloseIfOK(true);
       args.add_global_option("-f");
       args.add_option("checkout");
       if (rev1 != "HEAD")
       {
           args.add_option("-r");
           args.add_option(rev1);
       }
       args.add_option("-d");
       args.add_option("temp");
       if (unixSandbox)
           sf.AddUnixLineEndingsFlag(args);
       std::string s = EnsureTrailingUnixDelimiter(CVSStatus::CVSRepositoryForPath(dirGroups.mySingleAbsolute))
           + ExtractLastPart(dirGroups.mySingleAbsolute);
       args.add_arg(s);
      
       ok = glue.Command(tempDir, args);
       if (ok)
       {
           // Copy file to destination
           std::string file = EnsureTrailingDelimiter(tempDir) + "temp\\" + ExtractLastPart(dirGroups.mySingleAbsolute);
           SetFileReadOnly(file.c_str(), false);
           CopyFileA(file.c_str(), diffFile.c_str(), false);
           SetFileReadOnly(diffFile.c_str(), true);
       }

       // Erase temporary directory
       DeleteDirectoryRec(tempDir);
     
       if (!ok)
           return false;
       glue.CloseConsoleOutput();
   }
     
   if (!rev2.empty())
   {
       glue.SetCVSRoot(CVSRoot(cvsroot));
       MakeArgs args; 

       std::string tempDir = UniqueTemporaryDir();
       glue.SetCloseIfOK(true);
       args.add_global_option("-f");
       args.add_option("checkout");
       args.add_option("-r");
       args.add_option(rev2);
       args.add_option("-d");
       args.add_option("temp");
       if (unixSandbox)
           sf.AddUnixLineEndingsFlag(args);
       std::string s = EnsureTrailingUnixDelimiter(CVSStatus::CVSRepositoryForPath(dirGroups.mySingleAbsolute))
           + ExtractLastPart(dirGroups.mySingleAbsolute);
       args.add_arg(s);
      
       ok = glue.Command(tempDir, args);

       if (ok)
       {
           // Copy file to destination
           std::string file = EnsureTrailingDelimiter(tempDir) + "temp\\" 
               + ExtractLastPart(dirGroups.mySingleAbsolute);
           SetFileReadOnly(file.c_str(), false);
           CopyFileA(file.c_str(), diffFile2.c_str(), false);
           SetFileReadOnly(diffFile2.c_str(), true);
       }
     
       // Erase temporary directory
       DeleteDirectoryRec(tempDir);

       if (!ok)
           return false;
       glue.CloseConsoleOutput();
   }
    
   if (!FileExists(diffFile.c_str()))
   {
      DoMessageDialog(0, wxString(_("This file is new and has never been committed to the server or is an empty file on the server."))
                      + wxString(wxT("\n\n"))
                      + wxString(wxText(dirGroups.mySingleAbsolute)));
      return true;
   }
    
   do
   {
      externalApp = GetExternalApplication("Diff",
                                           dirGroups,
                                           forceQuery);
      forceQuery = false;
      if (externalApp.empty())
      {
          return false;
      }
      // Perform diff, waiting for it to finish (so we can delete file)
      glue.LockProgressDialog(true);
      if (rev2.empty())
          forceQuery = !RunExternalDiff(diffFile, dirGroups.mySingleAbsolute, dirGroups);
      else
          forceQuery = !RunExternalDiff(diffFile, diffFile2, dirGroups);
      glue.LockProgressDialog(false);
   }
   while (forceQuery);

   return true;
}