]> git.lyx.org Git - lyx.git/blobdiff - src/VCBackend.cpp
Pure HTML output for math macros.
[lyx.git] / src / VCBackend.cpp
index c8afe18d3d5f6d2a1442a5c429f7f356da2f659f..5bbc1746b417ae0dd2720dbb4c575a4d8076e637 100644 (file)
@@ -4,6 +4,7 @@
  * Licence details can be found in the file COPYING.
  *
  * \author Lars Gullik Bjønnes
+ * \author Pavel Sanda
  *
  * Full author contact details are available in file CREDITS.
  */
 
 #include "VCBackend.h"
 #include "Buffer.h"
+#include "LyX.h"
+#include "FuncRequest.h"
 
 #include "frontends/alert.h"
+#include "frontends/Application.h"
 
+#include "support/convert.h"
 #include "support/debug.h"
 #include "support/filetools.h"
 #include "support/gettext.h"
@@ -41,7 +46,7 @@ int VCS::doVCCommandCall(string const & cmd, FileName const & path)
        LYXERR(Debug::LYXVC, "doVCCommandCall: " << cmd);
        Systemcall one;
        support::PathChanger p(path);
-       return one.startscript(Systemcall::Wait, cmd);
+       return one.startscript(Systemcall::Wait, cmd, false);
 }
 
 
@@ -209,14 +214,14 @@ bool RCS::checkOutEnabled()
 }
 
 
-string RCS::repoSynchro()
+string RCS::repoUpdate()
 {
        lyxerr << "Sorry, not implemented." << endl;
        return string();
 }
 
 
-bool RCS::repoSynchroEnabled()
+bool RCS::repoUpdateEnabled()
 {
        return false;
 }
@@ -237,7 +242,7 @@ bool RCS::lockingToggleEnabled()
 
 void RCS::revert()
 {
-       doVCCommand("co -f -u" + version() + " "
+       doVCCommand("co -f -u" + version_ + " "
                    + quoteName(onlyFilename(owner_->absFileName())),
                    FileName(owner_->filePath()));
        // We ignore changes and just reload!
@@ -248,7 +253,7 @@ void RCS::revert()
 void RCS::undoLast()
 {
        LYXERR(Debug::LYXVC, "LyXVC: undoLast");
-       doVCCommand("rcs -o" + version() + " "
+       doVCCommand("rcs -o" + version_ + " "
                    + quoteName(onlyFilename(owner_->absFileName())),
                    FileName(owner_->filePath()));
 }
@@ -269,6 +274,66 @@ void RCS::getLog(FileName const & tmpf)
 
 
 bool RCS::toggleReadOnlyEnabled()
+{
+       // This got broken somewhere along lfuns dispatch reorganization.
+       // reloadBuffer would be needed after this, but thats problematic
+       // since we are inside Buffer::dispatch.
+       // return true;
+       return false;
+}
+
+string RCS::revisionInfo(LyXVC::RevisionInfo const info)
+{
+       if (info == LyXVC::File)
+               return version_;
+       return string();
+}
+
+
+bool RCS::prepareFileRevision(string const &revis, string & f)
+{
+       string rev = revis;
+
+       if (isStrInt(rev)) {
+               int back = convert<int>(rev);
+               if (back > 0)
+                       return false;
+               if (back == 0)
+                       rev = version_;
+               // we care about the last number from revision string
+               // in case of backward indexing
+               if (back < 0) {
+                       string cur, base;
+                       cur = rsplit(version_, base , '.' );
+                       if (!isStrInt(cur))
+                               return false;
+                       int want = convert<int>(cur) + back;
+                       if (want <= 0)
+                               return false;
+
+                       rev = base + "." + convert<string>(want);
+               }
+       }
+
+       FileName tmpf = FileName::tempName("lyxvcrev");
+       if (tmpf.empty()) {
+               LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
+               return N_("Error: Could not generate logfile.");
+       }
+
+       doVCCommand("co -p" + rev + " "
+                     + quoteName(onlyFilename(owner_->absFileName()))
+                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
+               FileName(owner_->filePath()));
+       if (tmpf.isFileEmpty())
+               return false;
+
+       f = tmpf.absFilename();
+       return true;
+}
+
+
+bool RCS::prepareFileRevisionEnabled()
 {
        return true;
 }
@@ -347,7 +412,7 @@ void CVS::scanMaster()
                                locker_ = "Unlocked";
                                vcstatus = UNLOCKED;
                        } else {
-                               // Here we should also to some more checking
+                               // Here we should also do some more checking
                                // to see if there are conflicts or not.
                                locker_ = "Locked";
                                vcstatus = LOCKED;
@@ -397,14 +462,14 @@ bool CVS::checkOutEnabled()
 }
 
 
-string CVS::repoSynchro()
+string CVS::repoUpdate()
 {
        lyxerr << "Sorry, not implemented." << endl;
        return string();
 }
 
 
-bool CVS::repoSynchroEnabled()
+bool CVS::repoUpdateEnabled()
 {
        return false;
 }
@@ -468,6 +533,27 @@ bool CVS::toggleReadOnlyEnabled()
        return false;
 }
 
+
+string CVS::revisionInfo(LyXVC::RevisionInfo const info)
+{
+       if (info == LyXVC::File)
+               return version_;
+       return string();
+}
+
+
+bool CVS::prepareFileRevision(string const &, string &)
+{
+       return false;
+}
+
+
+bool CVS::prepareFileRevisionEnabled()
+{
+       return false;
+}
+
+
 /////////////////////////////////////////////////////////////////////
 //
 // SVN
@@ -512,15 +598,14 @@ FileName const SVN::findFile(FileName const & file)
 
 void SVN::scanMaster()
 {
-       locker_.clear();
+       // vcstatus code is somewhat superflous, until we want
+       // to implement read-only toggle for svn.
        vcstatus = NOLOCKING;
        if (checkLockMode()) {
                if (isLocked()) {
-                       locker_ = "Locked";
                        vcstatus = LOCKED;
                } else {
-                       locker_ = "Unlocked";
-                       vcstatus = LOCKED;
+                       vcstatus = UNLOCKED;
                }
        }
 }
@@ -560,9 +645,8 @@ bool SVN::checkLockMode()
 
 bool SVN::isLocked() const
 {
-       //refresh file info
-       FileName file(file_.absFilename());
-       return !file.isReadOnly();
+       file_.refresh();
+       return !file_.isReadOnly();
 }
 
 
@@ -592,12 +676,12 @@ string SVN::checkIn(string const & msg)
                frontend::Alert::error(_("Revision control error."),
                                _("Error when committing to repository.\n"
                                "You have to manually resolve the problem.\n"
-                               "After pressing OK, LyX will reopen the document."));
+                               "LyX will reopen the document after you press OK."));
        else
                fileLock(false, tmpf, log);
 
        tmpf.erase();
-       return "SVN: " + log;
+       return log.empty() ? string() : "SVN: " + log;
 }
 
 
@@ -620,9 +704,11 @@ string SVN::scanLogFile(FileName const & f, string & status)
 
        while (ifs) {
                getline(ifs, line);
-               lyxerr << line << "\n";
-               if (!line.empty()) status += line + "; ";
-               if (prefixIs(line, "C ") || contains(line, "Commit failed")) {
+               LYXERR(Debug::LYXVC, line << "\n");
+               if (!line.empty()) 
+                       status += line + "; ";
+               if (prefixIs(line, "C ") || prefixIs(line, "CU ")
+                                        || contains(line, "Commit failed")) {
                        ifs.close();
                        return line;
                }
@@ -641,11 +727,12 @@ void SVN::fileLock(bool lock, FileName const & tmpf, string &status)
        if (!locked_mode_ || (isLocked() == lock))
                return;
 
-       string arg = lock ? "lock " : "unlock ";
+       string const arg = lock ? "lock " : "unlock ";
        doVCCommand("svn "+ arg + quoteName(onlyFilename(owner_->absFileName()))
                    + " > " + quoteName(tmpf.toFilesystemEncoding()),
                    FileName(owner_->filePath()));
 
+       // Lock error messages go unfortunately on stderr and are unreachible this way.
        ifstream ifs(tmpf.toFilesystemEncoding().c_str());
        string line;
        while (ifs) {
@@ -656,13 +743,13 @@ void SVN::fileLock(bool lock, FileName const & tmpf, string &status)
 
        if (!isLocked() && lock)
                frontend::Alert::error(_("Revision control error."),
-                       _("Error when acquiring write lock.\n"
-                       "Most probably another user is editing\n"
+                       _("Error while acquiring write lock.\n"
+                       "Another user is most probably editing\n"
                        "the current document now!\n"
                        "Also check the access to the repository."));
        if (isLocked() && !lock)
                frontend::Alert::error(_("Revision control error."),
-                       _("Error when releasing write lock.\n"
+                       _("Error while releasing write lock.\n"
                        "Check the access to the repository."));
 }
 
@@ -675,23 +762,23 @@ string SVN::checkOut()
                return N_("Error: Could not generate logfile.");
        }
 
-       doVCCommand("svn update " + quoteName(onlyFilename(owner_->absFileName()))
+       doVCCommand("svn update --non-interactive " + quoteName(onlyFilename(owner_->absFileName()))
                    + " > " + quoteName(tmpf.toFilesystemEncoding()),
                    FileName(owner_->filePath()));
 
        string log;
-       string res = scanLogFile(tmpf, log);
+       string const res = scanLogFile(tmpf, log);
        if (!res.empty())
                frontend::Alert::error(_("Revision control error."),
                        bformat(_("Error when updating from repository.\n"
                                "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
-                               "After pressing OK, LyX will try to reopen resolved document."),
+                               "After pressing OK, LyX will try to reopen the resolved document."),
                        from_local8bit(res)));
 
        fileLock(true, tmpf, log);
 
        tmpf.erase();
-       return "SVN: " + log;
+       return log.empty() ? string() : "SVN: " + log;
 }
 
 
@@ -704,7 +791,7 @@ bool SVN::checkOutEnabled()
 }
 
 
-string SVN::repoSynchro()
+string SVN::repoUpdate()
 {
        FileName tmpf = FileName::tempName("lyxvcout");
        if (tmpf.empty()) {
@@ -713,31 +800,38 @@ string SVN::repoSynchro()
        }
 
        doVCCommand("svn diff " + quoteName(owner_->filePath())
-       + " > " + quoteName(tmpf.toFilesystemEncoding()),
-       FileName(owner_->filePath()));
+                   + " > " + quoteName(tmpf.toFilesystemEncoding()),
+               FileName(owner_->filePath()));
        docstring res = tmpf.fileContents("UTF-8");
        if (!res.empty()) {
                LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
                docstring const file = from_utf8(owner_->filePath());
                docstring text = bformat(_("There were detected changes "
-                               "in the working directory.\n"
-                               "Synchronizing with repository will discard "
-                               "any uncommitted changes in the directory:\n%1$s"
+                               "in the working directory:\n%1$s\n\n"
+                               "In case of file conflict version of the local directory files "
+                               "will be preferred."
                                "\n\nContinue?"), file);
-               int const ret = frontend::Alert::prompt(_("Changes detected"),
+               int ret = frontend::Alert::prompt(_("Changes detected"),
+                               text, 0, 1, _("&Yes"), _("&No"), _("View &Log ..."));
+               if (ret == 2 ) {
+                       dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFilename()));
+                       ret = frontend::Alert::prompt(_("Changes detected"),
                                text, 0, 1, _("&Yes"), _("&No"));
-               if (ret) {
+                       hideDialogs("file", 0);
+               }
+               if (ret == 1 ) {
                        tmpf.erase();
                        return string();
                }
        }
 
-       doVCCommand("svn revert -R " + quoteName(owner_->filePath())
-       + " > " + quoteName(tmpf.toFilesystemEncoding()),
-       FileName(owner_->filePath()));
-       res = "Revert log:\n" + tmpf.fileContents("UTF-8");
-       doVCCommand("svn update " + quoteName(owner_->filePath())
-       + " > " + quoteName(tmpf.toFilesystemEncoding()),
+       // Reverting looks too harsh, see bug #6255.
+       // doVCCommand("svn revert -R " + quoteName(owner_->filePath())
+       // + " > " + quoteName(tmpf.toFilesystemEncoding()),
+       // FileName(owner_->filePath()));
+       // res = "Revert log:\n" + tmpf.fileContents("UTF-8");
+       doVCCommand("svn update --accept mine-full " + quoteName(owner_->filePath())
+               + " > " + quoteName(tmpf.toFilesystemEncoding()),
        FileName(owner_->filePath()));
        res += "Update log:\n" + tmpf.fileContents("UTF-8");
 
@@ -747,7 +841,7 @@ string SVN::repoSynchro()
 }
 
 
-bool SVN::repoSynchroEnabled()
+bool SVN::repoUpdateEnabled()
 {
        return true;
 }
@@ -826,6 +920,121 @@ bool SVN::undoLastEnabled()
 }
 
 
+string SVN::revisionInfo(LyXVC::RevisionInfo const info)
+{
+       if (info == LyXVC::Tree) {
+                       if (rev_tree_cache_.empty())
+                               if (!getTreeRevisionInfo())
+                                       rev_tree_cache_ = "?";
+                       if (rev_tree_cache_ == "?")
+                               return string();
+
+                       return rev_tree_cache_;
+       }
+
+       // fill the rest of the attributes for a single file
+       if (rev_file_cache_.empty())
+               if (!getFileRevisionInfo())
+                       rev_file_cache_ = "?";
+
+       switch (info) {
+               case LyXVC::File:
+                       if (rev_file_cache_ == "?")
+                               return string();
+                       return rev_file_cache_;
+               case LyXVC::Author:
+                       return rev_author_cache_;
+               case LyXVC::Date:
+                       return rev_date_cache_;
+               case LyXVC::Time:
+                       return rev_time_cache_;
+               default: ;
+
+       }
+
+       return string();
+}
+
+
+bool SVN::getFileRevisionInfo()
+{
+       FileName tmpf = FileName::tempName("lyxvcout");
+       if (tmpf.empty()) {
+               LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
+               return N_("Error: Could not generate logfile.");
+       }
+
+       doVCCommand("svn info --xml " + quoteName(onlyFilename(owner_->absFileName()))
+                   + " > " + quoteName(tmpf.toFilesystemEncoding()),
+                   FileName(owner_->filePath()));
+
+       if (tmpf.empty())
+               return false;
+
+       ifstream ifs(tmpf.toFilesystemEncoding().c_str());
+       string line;
+       // commit log part
+       bool c = false;
+       string rev;
+
+       while (ifs) {
+               getline(ifs, line);
+               LYXERR(Debug::LYXVC, line);
+               if (prefixIs(line, "<commit"))
+                       c = true;
+               if (c && prefixIs(line, "   revision=\"") && suffixIs(line, "\">")) {
+                       string l1 = subst(line, "revision=\"", "");
+                       string l2 = trim(subst(l1, "\">", ""));
+                       if (isStrInt(l2))
+                               rev_file_cache_ = rev = l2;
+               }
+               if (c && prefixIs(line, "<author>") && suffixIs(line, "</author>")) {
+                       string l1 = subst(line, "<author>", "");
+                       string l2 = subst(l1, "</author>", "");
+                       rev_author_cache_ = l2;
+               }
+               if (c && prefixIs(line, "<date>") && suffixIs(line, "</date>")) {
+                       string l1 = subst(line, "<date>", "");
+                       string l2 = subst(l1, "</date>", "");
+                       l2 = split(l2, l1, 'T');
+                       rev_date_cache_ = l1;
+                       l2 = split(l2, l1, '.');
+                       rev_time_cache_ = l1;
+               }
+       }
+
+       ifs.close();
+       tmpf.erase();
+       return !rev.empty();
+}
+
+
+bool SVN::getTreeRevisionInfo()
+{
+       FileName tmpf = FileName::tempName("lyxvcout");
+       if (tmpf.empty()) {
+               LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
+               return N_("Error: Could not generate logfile.");
+       }
+
+       doVCCommand("svnversion -n . > " + quoteName(tmpf.toFilesystemEncoding()),
+                   FileName(owner_->filePath()));
+
+       if (tmpf.empty())
+               return false;
+
+       // only first line in case something bad happens.
+       ifstream ifs(tmpf.toFilesystemEncoding().c_str());
+       string line;
+       getline(ifs, line);
+       ifs.close();
+       tmpf.erase();
+
+       rev_tree_cache_ = line;
+       return !line.empty();
+}
+
+
 void SVN::getLog(FileName const & tmpf)
 {
        doVCCommand("svn log " + quoteName(onlyFilename(owner_->absFileName()))
@@ -834,6 +1043,49 @@ void SVN::getLog(FileName const & tmpf)
 }
 
 
+bool SVN::prepareFileRevision(string const & revis, string & f)
+{
+       if (!isStrInt(revis))
+               return false;
+
+       int rev = convert<int>(revis);
+       if (rev <= 0)
+               if (!getFileRevisionInfo())
+                       return false;
+       if (rev == 0)
+               rev = convert<int>(rev_file_cache_);
+       // go back for minus rev
+       else if (rev < 0) {
+               rev = rev + convert<int>(rev_file_cache_);
+               if (rev < 1)
+                       return false;
+       }
+
+       FileName tmpf = FileName::tempName("lyxvcrev");
+       if (tmpf.empty()) {
+               LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
+               return N_("Error: Could not generate logfile.");
+       }
+
+       doVCCommand("svn cat -r " + convert<string>(rev) + " "
+                     + quoteName(onlyFilename(owner_->absFileName()))
+                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
+               FileName(owner_->filePath()));
+       if (tmpf.isFileEmpty())
+               return false;
+
+       f = tmpf.absFilename();
+       return true;
+}
+
+
+bool SVN::prepareFileRevisionEnabled()
+{
+       return true;
+}
+
+
+
 bool SVN::toggleReadOnlyEnabled()
 {
        return false;