]> git.lyx.org Git - lyx.git/blobdiff - src/VCBackend.cpp
Sanitize cursors after a buffer has been reloaded
[lyx.git] / src / VCBackend.cpp
index 3eeb3d6a3ef3ccccf1db19aecfdd76ede3b1e8d7..ca603c9e1f8ed89c1c3ea9cd35ed045815485c41 100644 (file)
@@ -13,7 +13,6 @@
 
 #include "VCBackend.h"
 #include "Buffer.h"
-#include "DispatchResult.h"
 #include "LyX.h"
 #include "FuncRequest.h"
 
 #include "support/lstrings.h"
 #include "support/PathChanger.h"
 #include "support/Systemcall.h"
-#include "support/regex.h"
 #include "support/TempFile.h"
 
 #include <fstream>
+#include <iomanip>
+#include <regex>
 #include <sstream>
 
 using namespace std;
@@ -58,11 +58,15 @@ int VCS::doVCCommand(string const & cmd, FileName const & path, bool reportError
 
        if (owner_)
                owner_->setBusy(false);
-       if (ret && reportError)
+       if (ret && reportError) {
+               docstring rcsmsg;
+               if (prefixIs(cmd, "ci "))
+                       rcsmsg = "\n" + _("Check whether the GNU RCS package is installed on your system.");
                frontend::Alert::error(_("Revision control error."),
                        bformat(_("Some problem occurred while running the command:\n"
-                                 "'%1$s'."),
+                                 "'%1$s'.") + rcsmsg,
                        from_utf8(cmd)));
+       }
        return ret;
 }
 
@@ -101,18 +105,18 @@ bool VCS::makeRCSRevision(string const &version, string &revis) const
 }
 
 
-bool VCS::checkparentdirs(FileName const & file, std::string const & vcsdir)
+FileName VCS::checkParentDirs(FileName const & start, std::string const & file)
 {
-       FileName dirname = file.onlyPath();
+       FileName dirname = start.onlyPath();
        do {
-               FileName tocheck = FileName(addName(dirname.absFileName(), vcsdir));
+               FileName tocheck = FileName(addPathName(dirname.absFileName(), file));
                LYXERR(Debug::LYXVC, "check file: " << tocheck.absFileName());
                if (tocheck.exists())
-                       return true;
-               //this construct because of #8295
+                       return tocheck;
+               // this construct because of #8295
                dirname = FileName(dirname.absFileName()).parentPath();
        } while (!dirname.empty());
-       return false;
+       return FileName();
 }
 
 
@@ -157,8 +161,8 @@ bool RCS::retrieve(FileName const & file)
 {
        LYXERR(Debug::LYXVC, "LyXVC::RCS: retrieve.\n\t" << file);
        // The caller ensures that file does not exist, so no need to check that.
-       return doVCCommandCall("co -q -r " + quoteName(file.toFilesystemEncoding()),
-                              FileName()) == 0;
+       int const ret = doVCCommandCall("co -q -r " + quoteName(file.toFilesystemEncoding()));
+       return ret == 0;
 }
 
 
@@ -197,7 +201,7 @@ void RCS::scanMaster()
                        // get locker here
                        if (contains(token, ';')) {
                                locker_ = "Unlocked";
-                               vcstatus = UNLOCKED;
+                               vcstatus_ = UNLOCKED;
                                continue;
                        }
                        string tmpt;
@@ -211,7 +215,7 @@ void RCS::scanMaster()
                                // s2 is user, and s1 is version
                                if (s1 == version_) {
                                        locker_ = s2;
-                                       vcstatus = LOCKED;
+                                       vcstatus_ = LOCKED;
                                        break;
                                }
                        } while (!contains(tmpt, ';'));
@@ -391,7 +395,7 @@ void RCS::undoLast()
 
 bool RCS::undoLastEnabled()
 {
-       return true;
+       return owner_->hasReadonlyFlag();
 }
 
 
@@ -429,7 +433,8 @@ string RCS::revisionInfo(LyXVC::RevisionInfo const info)
                        return rev_date_cache_;
                case LyXVC::Time:
                        return rev_time_cache_;
-               default: ;
+               default:
+                       break;
        }
 
        return string();
@@ -533,6 +538,8 @@ FileName const CVS::findFile(FileName const & file)
 {
        // First we look for the CVS/Entries in the same dir
        // where we have file.
+       // Note that it is not necessary to search parent directories, since
+       // there will be a CVS/Entries file in every subdirectory.
        FileName const entries(onlyPath(file.absFileName()) + "/CVS/Entries");
        string const tmpf = '/' + onlyFileName(file.absFileName()) + '/';
        LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under cvs in `" << entries
@@ -558,8 +565,8 @@ void CVS::scanMaster()
        LYXERR(Debug::LYXVC, "LyXVC::CVS: scanMaster. \n     Checking: " << master_);
        // Ok now we do the real scan...
        ifstream ifs(master_.toFilesystemEncoding().c_str());
-       string name = onlyFileName(owner_->absFileName());
-       string tmpf = '/' + name + '/';
+       string const name = onlyFileName(owner_->absFileName());
+       string const tmpf = '/' + name + '/';
        LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\'');
        string line;
        static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)");
@@ -568,10 +575,10 @@ void CVS::scanMaster()
                if (contains(line, tmpf)) {
                        // Ok extract the fields.
                        smatch sm;
-
-                       // false positive from coverity
-                       // coverity[CHECKED_RETURN]
-                       regex_match(line, sm, reg);
+                       if (!regex_match(line, sm, reg)) {
+                               LYXERR(Debug::LYXVC, "\t  Cannot parse line. Skipping.");
+                               continue;
+                       }
 
                        //sm[0]; // whole matched string
                        //sm[1]; // filename
@@ -582,21 +589,21 @@ void CVS::scanMaster()
                        //sm[5]; // tag or tagdate
                        FileName file(owner_->absFileName());
                        if (file.isReadableFile()) {
-                               time_t mod = file.lastModified();
-                               string mod_date = rtrim(asctime(gmtime(&mod)), "\n");
+                               time_t const mod = file.lastModified();
+                               string const mod_date = rtrim(asctime(gmtime(&mod)), "\n");
                                LYXERR(Debug::LYXVC, "Date in Entries: `" << file_date
                                        << "'\nModification date of file: `" << mod_date << '\'');
                                if (file.isReadOnly()) {
                                        // readonly checkout is unlocked
-                                       vcstatus = UNLOCKED;
+                                       vcstatus_ = UNLOCKED;
                                } else {
                                        FileName bdir(addPath(master_.onlyPath().absFileName(),"Base"));
                                        FileName base(addName(bdir.absFileName(),name));
                                        // if base version is existent "cvs edit" was used to lock
-                                       vcstatus = base.isReadableFile() ? LOCKED : NOLOCKING;
+                                       vcstatus_ = base.isReadableFile() ? LOCKED : NOLOCKING;
                                }
                        } else {
-                               vcstatus = NOLOCKING;
+                               vcstatus_ = NOLOCKING;
                        }
                        break;
                }
@@ -732,12 +739,14 @@ void CVS::getRevisionInfo()
                LYXERR(Debug::LYXVC, line << '\n');
                if (prefixIs(line, "date:")) {
                        smatch sm;
-                       regex_match(line, sm, reg);
-                       //sm[0]; // whole matched string
-                       rev_date_cache_ = sm[1];
-                       rev_time_cache_ = sm[2];
-                       //sm[3]; // GMT offset
-                       rev_author_cache_ = sm[4];
+                       if (regex_match(line, sm, reg)) {
+                         //sm[0]; // whole matched string
+                         rev_date_cache_ = sm[1];
+                         rev_time_cache_ = sm[2];
+                         //sm[3]; // GMT offset
+                         rev_author_cache_ = sm[4];
+                       } else
+                         LYXERR(Debug::LYXVC, "\tCannot parse line. Skipping."); 
                        break;
                }
        }
@@ -804,7 +813,7 @@ void CVS::getDiff(OperationMode opmode, FileName const & tmpf)
 
 int CVS::edit()
 {
-       vcstatus = LOCKED;
+       vcstatus_ = LOCKED;
        return doVCCommand("cvs -q edit " + getTarget(File),
                FileName(owner_->filePath()));
 }
@@ -812,7 +821,7 @@ int CVS::edit()
 
 int CVS::unedit()
 {
-       vcstatus = UNLOCKED;
+       vcstatus_ = UNLOCKED;
        return doVCCommand("cvs -q unedit " + getTarget(File),
                FileName(owner_->filePath()));
 }
@@ -851,7 +860,7 @@ LyXVC::CommandResult CVS::checkIn(string const & msg, string & log)
        CvsStatus status = getStatus();
        switch (status) {
        case UpToDate:
-               if (vcstatus != NOLOCKING)
+               if (vcstatus_ != NOLOCKING)
                        if (unedit())
                                return LyXVC::ErrorCommand;
                log = "CVS: Proceeded";
@@ -893,7 +902,7 @@ bool CVS::isLocked() const
 
 bool CVS::checkInEnabled()
 {
-       if (vcstatus != NOLOCKING)
+       if (vcstatus_ != NOLOCKING)
                return isLocked();
        else
                return true;
@@ -909,7 +918,7 @@ bool CVS::isCheckInWithConfirmation()
 
 string CVS::checkOut()
 {
-       if (vcstatus != NOLOCKING && edit())
+       if (vcstatus_ != NOLOCKING && edit())
                return string();
        TempFile tempfile("lyxvout");
        FileName tmpf = tempfile.name();
@@ -936,7 +945,7 @@ string CVS::checkOut()
 
 bool CVS::checkOutEnabled()
 {
-       if (vcstatus != NOLOCKING)
+       if (vcstatus_ != NOLOCKING)
                return !isLocked();
        else
                return true;
@@ -967,7 +976,7 @@ string CVS::repoUpdate()
                        dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
                        ret = frontend::Alert::prompt(_("Changes detected"),
                                text, 0, 1, _("&Continue"), _("&Abort"));
-                       hideDialogs("file", 0);
+                       hideDialogs("file", nullptr);
                }
                if (ret == 1)
                        return string();
@@ -1026,7 +1035,7 @@ bool CVS::revert()
        CvsStatus status = getStatus();
        switch (status) {
        case UpToDate:
-               if (vcstatus != NOLOCKING)
+               if (vcstatus_ != NOLOCKING)
                        return 0 == unedit();
                break;
        case NeedsMerge:
@@ -1101,7 +1110,8 @@ string CVS::revisionInfo(LyXVC::RevisionInfo const info)
                        return rev_date_cache_;
                case LyXVC::Time:
                        return rev_time_cache_;
-               default: ;
+               default:
+                       break;
                }
        }
        return string();
@@ -1146,39 +1156,30 @@ bool CVS::prepareFileRevisionEnabled()
 //
 /////////////////////////////////////////////////////////////////////
 
-SVN::SVN(FileName const & m, Buffer * b) : VCS(b)
+SVN::SVN(Buffer * b) : VCS(b)
 {
        // Here we know that the buffer file is either already in SVN or
        // about to be registered
-       master_ = m;
-       locked_mode_ = 0;
+       locked_mode_ = false;
        scanMaster();
 }
 
 
-FileName const SVN::findFile(FileName const & file)
+bool SVN::findFile(FileName const & file)
 {
        // First we check the existence of repository meta data.
-       if (!VCS::checkparentdirs(file, ".svn")) {
+       if (VCS::checkParentDirs(file, ".svn").empty()) {
                LYXERR(Debug::LYXVC, "Cannot find SVN meta data for " << file);
-               return FileName();
+               return false;
        }
 
        // Now we check the status of the file.
-       TempFile tempfile("lyxvcout");
-       FileName tmpf = tempfile.name();
-       if (tmpf.empty()) {
-               LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
-               return FileName();
-       }
-
        string const fname = onlyFileName(file.absFileName());
        LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under svn control for `" << fname << '\'');
-       bool found = 0 == doVCCommandCall("svn info " + quoteName(fname)
-                                               + " > " + quoteName(tmpf.toFilesystemEncoding()),
+       bool found = 0 == doVCCommandCall("svn info " + quoteName(fname),
                                                file.onlyPath());
        LYXERR(Debug::LYXVC, "SVN control: " << (found ? "enabled" : "disabled"));
-       return found ? file : FileName();
+       return found;
 }
 
 
@@ -1186,13 +1187,9 @@ void SVN::scanMaster()
 {
        // vcstatus code is somewhat superflous,
        // until we want to implement read-only toggle for svn.
-       vcstatus = NOLOCKING;
-       if (checkLockMode()) {
-               if (isLocked())
-                       vcstatus = LOCKED;
-               else
-                       vcstatus = UNLOCKED;
-       }
+       vcstatus_ = NOLOCKING;
+       if (checkLockMode())
+               vcstatus_ = isLocked() ? LOCKED : UNLOCKED;
 }
 
 
@@ -1248,7 +1245,7 @@ bool SVN::retrieve(FileName const & file)
 
 void SVN::registrer(string const & /*msg*/)
 {
-       doVCCommand("svn add -q " + quoteName(onlyFileName(owner_->absFileName())),
+       doVCCommand("svn add -q --parents " + quoteName(onlyFileName(owner_->absFileName())),
                    FileName(owner_->filePath()));
 }
 
@@ -1527,7 +1524,7 @@ string SVN::repoUpdate()
                        dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
                        ret = frontend::Alert::prompt(_("Changes detected"),
                                text, 0, 1, _("&Yes"), _("&No"));
-                       hideDialogs("file", 0);
+                       hideDialogs("file", nullptr);
                }
                if (ret == 1)
                        return string();
@@ -1665,8 +1662,8 @@ string SVN::revisionInfo(LyXVC::RevisionInfo const info)
                        return rev_date_cache_;
                case LyXVC::Time:
                        return rev_time_cache_;
-               default: ;
-
+               default:
+                       break;
        }
 
        return string();
@@ -1819,21 +1816,20 @@ bool SVN::toggleReadOnlyEnabled()
 //
 /////////////////////////////////////////////////////////////////////
 
-GIT::GIT(FileName const & m, Buffer * b) : VCS(b)
+GIT::GIT(Buffer * b) : VCS(b)
 {
        // Here we know that the buffer file is either already in GIT or
        // about to be registered
-       master_ = m;
        scanMaster();
 }
 
 
-FileName const GIT::findFile(FileName const & file)
+bool GIT::findFile(FileName const & file)
 {
        // First we check the existence of repository meta data.
-       if (!VCS::checkparentdirs(file, ".git")) {
+       if (VCS::checkParentDirs(file, ".git").empty()) {
                LYXERR(Debug::LYXVC, "Cannot find GIT meta data for " << file);
-               return FileName();
+               return false;
        }
 
        // Now we check the status of the file.
@@ -1841,7 +1837,7 @@ FileName const GIT::findFile(FileName const & file)
        FileName tmpf = tempfile.name();
        if (tmpf.empty()) {
                LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
-               return FileName();
+               return false;
        }
 
        string const fname = onlyFileName(file.absFileName());
@@ -1854,7 +1850,7 @@ FileName const GIT::findFile(FileName const & file)
        tmpf.refresh();
        bool found = !tmpf.isFileEmpty();
        LYXERR(Debug::LYXVC, "GIT control: " << (found ? "enabled" : "disabled"));
-       return found ? file : FileName();
+       return found;
 }
 
 
@@ -1862,7 +1858,7 @@ void GIT::scanMaster()
 {
        // vcstatus code is somewhat superflous,
        // until we want to implement read-only toggle for git.
-       vcstatus = NOLOCKING;
+       vcstatus_ = NOLOCKING;
 }
 
 
@@ -2120,22 +2116,28 @@ string GIT::revisionInfo(LyXVC::RevisionInfo const info)
 
        // fill the rest of the attributes for a single file
        if (rev_file_cache_.empty())
-               if (!getFileRevisionInfo())
+               if (!getFileRevisionInfo()) {
                        rev_file_cache_ = "?";
+                       rev_file_abbrev_cache_ = "?";
+    }
 
        switch (info) {
                case LyXVC::File:
                        if (rev_file_cache_ == "?")
                                return string();
                        return rev_file_cache_;
+               case LyXVC::FileAbbrev:
+                       if (rev_file_abbrev_cache_ == "?")
+                               return string();
+                       return rev_file_abbrev_cache_;
                case LyXVC::Author:
                        return rev_author_cache_;
                case LyXVC::Date:
                        return rev_date_cache_;
                case LyXVC::Time:
                        return rev_time_cache_;
-               default: ;
-
+               default:
+                       break;
        }
 
        return string();
@@ -2151,7 +2153,7 @@ bool GIT::getFileRevisionInfo()
                return false;
        }
 
-       doVCCommand("git log -n 1 --pretty=format:%H%n%an%n%ai " + quoteName(onlyFileName(owner_->absFileName()))
+       doVCCommand("git log -n 1 --pretty=format:%H%n%h%n%an%n%ai " + quoteName(onlyFileName(owner_->absFileName()))
                    + " > " + quoteName(tmpf.toFilesystemEncoding()),
                    FileName(owner_->filePath()));
 
@@ -2162,6 +2164,8 @@ bool GIT::getFileRevisionInfo()
 
        if (ifs)
                getline(ifs, rev_file_cache_);
+       if (ifs)
+               getline(ifs, rev_file_abbrev_cache_);
        if (ifs)
                getline(ifs, rev_author_cache_);
        if (ifs) {
@@ -2185,7 +2189,8 @@ bool GIT::getTreeRevisionInfo()
        }
 
        doVCCommand("git describe --abbrev --dirty --long > " + quoteName(tmpf.toFilesystemEncoding()),
-                   FileName(owner_->filePath()));
+                   FileName(owner_->filePath()),
+                   false); //git describe returns $?=128 when no tag found (but git repo still exists)
 
        if (tmpf.empty())
                return false;