]> git.lyx.org Git - lyx.git/blob - src/VCBackend.cpp
31a8eb32e513a92f1e32fce4ca676ea233bc9812
[lyx.git] / src / VCBackend.cpp
1 /**
2  * \file VCBackend.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Pavel Sanda
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "VCBackend.h"
15 #include "Buffer.h"
16 #include "LyX.h"
17 #include "FuncRequest.h"
18
19 #include "frontends/alert.h"
20 #include "frontends/Application.h"
21
22 #include "support/convert.h"
23 #include "support/debug.h"
24 #include "support/filetools.h"
25 #include "support/gettext.h"
26 #include "support/lstrings.h"
27 #include "support/Path.h"
28 #include "support/Systemcall.h"
29 #include "support/regex.h"
30
31 #include <fstream>
32
33 using namespace std;
34 using namespace lyx::support;
35
36
37
38 namespace lyx {
39
40
41 int VCS::doVCCommandCall(string const & cmd, FileName const & path)
42 {
43         LYXERR(Debug::LYXVC, "doVCCommandCall: " << cmd);
44         Systemcall one;
45         support::PathChanger p(path);
46         return one.startscript(Systemcall::Wait, cmd, string(), false);
47 }
48
49
50 int VCS::doVCCommand(string const & cmd, FileName const & path, bool reportError)
51 {
52         if (owner_)
53                 owner_->setBusy(true);
54
55         int const ret = doVCCommandCall(cmd, path);
56
57         if (owner_)
58                 owner_->setBusy(false);
59         if (ret && reportError)
60                 frontend::Alert::error(_("Revision control error."),
61                         bformat(_("Some problem occured while running the command:\n"
62                                   "'%1$s'."),
63                         from_utf8(cmd)));
64         return ret;
65 }
66
67
68 bool VCS::makeRCSRevision(string const &version, string &revis) const
69 {
70         string rev = revis;
71         
72         if (isStrInt(rev)) {
73                 int back = convert<int>(rev);
74                 // if positive use as the last number in the whole revision string
75                 if (back > 0) {
76                         string base;
77                         rsplit(version, base , '.' );
78                         rev = base + "." + rev;
79                 }
80                 if (back == 0)
81                         rev = version;
82                 // we care about the last number from revision string
83                 // in case of backward indexing
84                 if (back < 0) {
85                         string cur, base;
86                         cur = rsplit(version, base , '.' );
87                         if (!isStrInt(cur))
88                                 return false;
89                         int want = convert<int>(cur) + back;
90                         if (want <= 0)
91                                 return false;
92                         
93                         rev = base + "." + convert<string>(want);
94                 }
95         }
96
97         revis = rev;
98         return true;
99 }
100
101
102 bool VCS::checkparentdirs(FileName const & file, std::string const & pathname)
103 {
104         FileName dirname = file.onlyPath();
105         FileName tocheck = FileName(addName(dirname.absFileName(),pathname));
106         LYXERR(Debug::LYXVC, "check file: " << tocheck.absFileName());
107         bool result = tocheck.exists();
108         while ( !result && !dirname.empty() ) {
109                 //this construct because of #8295
110                 dirname = FileName(dirname.absFileName()).parentPath();
111                 LYXERR(Debug::LYXVC, "check directory: " << dirname.absFileName());
112                 tocheck = FileName(addName(dirname.absFileName(),pathname));
113                 result = tocheck.exists();
114         }
115         return result;
116 }
117
118         
119 /////////////////////////////////////////////////////////////////////
120 //
121 // RCS
122 //
123 /////////////////////////////////////////////////////////////////////
124
125 RCS::RCS(FileName const & m, Buffer * b) : VCS(b)
126 {
127         master_ = m;
128         scanMaster();
129 }
130
131
132 FileName const RCS::findFile(FileName const & file)
133 {
134         // Check if *,v exists.
135         FileName tmp(file.absFileName() + ",v");
136         LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under rcs: " << tmp);
137         if (tmp.isReadableFile()) {
138                 LYXERR(Debug::LYXVC, "Yes, " << file << " is under rcs.");
139                 return tmp;
140         }
141
142         // Check if RCS/*,v exists.
143         tmp = FileName(addName(addPath(onlyPath(file.absFileName()), "RCS"), file.absFileName()) + ",v");
144         LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under rcs: " << tmp);
145         if (tmp.isReadableFile()) {
146                 LYXERR(Debug::LYXVC, "Yes, " << file << " is under rcs.");
147                 return tmp;
148         }
149
150         return FileName();
151 }
152
153
154 void RCS::retrieve(FileName const & file)
155 {
156         LYXERR(Debug::LYXVC, "LyXVC::RCS: retrieve.\n\t" << file);
157         doVCCommandCall("co -q -r " + quoteName(file.toFilesystemEncoding()),
158                          FileName());
159 }
160
161
162 void RCS::scanMaster()
163 {
164         if (master_.empty())
165                 return;
166
167         LYXERR(Debug::LYXVC, "LyXVC::RCS: scanMaster: " << master_);
168
169         ifstream ifs(master_.toFilesystemEncoding().c_str());
170
171         string token;
172         bool read_enough = false;
173
174         while (!read_enough && ifs >> token) {
175                 LYXERR(Debug::LYXVC, "LyXVC::scanMaster: current lex text: `"
176                         << token << '\'');
177
178                 if (token.empty())
179                         continue;
180                 else if (token == "head") {
181                         // get version here
182                         string tmv;
183                         ifs >> tmv;
184                         tmv = rtrim(tmv, ";");
185                         version_ = tmv;
186                         LYXERR(Debug::LYXVC, "LyXVC: version found to be " << tmv);
187                 } else if (contains(token, "access")
188                            || contains(token, "symbols")
189                            || contains(token, "strict")) {
190                         // nothing
191                 } else if (contains(token, "locks")) {
192                         // get locker here
193                         if (contains(token, ';')) {
194                                 locker_ = "Unlocked";
195                                 vcstatus = UNLOCKED;
196                                 continue;
197                         }
198                         string tmpt;
199                         string s1;
200                         string s2;
201                         do {
202                                 ifs >> tmpt;
203                                 s1 = rtrim(tmpt, ";");
204                                 // tmp is now in the format <user>:<version>
205                                 s1 = split(s1, s2, ':');
206                                 // s2 is user, and s1 is version
207                                 if (s1 == version_) {
208                                         locker_ = s2;
209                                         vcstatus = LOCKED;
210                                         break;
211                                 }
212                         } while (!contains(tmpt, ';'));
213
214                 } else if (token == "comment") {
215                         // we don't need to read any further than this.
216                         read_enough = true;
217                 } else {
218                         // unexpected
219                         LYXERR(Debug::LYXVC, "LyXVC::scanMaster(): unexpected token");
220                 }
221         }
222 }
223
224
225 void RCS::registrer(string const & msg)
226 {
227         string cmd = "ci -q -u -i -t-\"";
228         cmd += msg;
229         cmd += "\" ";
230         cmd += quoteName(onlyFileName(owner_->absFileName()));
231         doVCCommand(cmd, FileName(owner_->filePath()));
232 }
233
234
235 string RCS::checkIn(string const & msg)
236 {
237         int ret = doVCCommand("ci -q -u -m\"" + msg + "\" "
238                     + quoteName(onlyFileName(owner_->absFileName())),
239                     FileName(owner_->filePath()));
240         return ret ? string() : "RCS: Proceeded";
241 }
242
243
244 bool RCS::checkInEnabled()
245 {
246         return owner_ && !owner_->isReadonly();
247 }
248
249
250 bool RCS::isCheckInWithConfirmation()
251 {
252         // FIXME one day common getDiff for all backends
253         // docstring diff;
254         // if (getDiff(file, diff) && diff.empty())
255         //      return false;
256
257         FileName tmpf = FileName::tempName("lyxvcout");
258         if (tmpf.empty()) {
259                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
260                 return true;
261         }
262
263         doVCCommandCall("rcsdiff " + quoteName(owner_->absFileName())
264                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
265                 FileName(owner_->filePath()));
266
267         docstring diff = tmpf.fileContents("UTF-8");
268         tmpf.erase();
269
270         if (diff.empty())
271                 return false;
272
273         return true;
274 }
275
276
277 string RCS::checkOut()
278 {
279         owner_->markClean();
280         int ret = doVCCommand("co -q -l " + quoteName(onlyFileName(owner_->absFileName())),
281                     FileName(owner_->filePath()));
282         return ret ? string() : "RCS: Proceeded";
283 }
284
285
286 bool RCS::checkOutEnabled()
287 {
288         return owner_ && owner_->isReadonly();
289 }
290
291
292 string RCS::repoUpdate()
293 {
294         lyxerr << "Sorry, not implemented." << endl;
295         return string();
296 }
297
298
299 bool RCS::repoUpdateEnabled()
300 {
301         return false;
302 }
303
304
305 string RCS::lockingToggle()
306 {
307         //FIXME this might be actually possible, study rcs -U, rcs -L.
308         //State should be easy to get inside scanMaster.
309         //It would fix #4370 and make rcs/svn usage even more closer.
310         lyxerr << "Sorry, not implemented." << endl;
311         return string();
312 }
313
314
315 bool RCS::lockingToggleEnabled()
316 {
317         return false;
318 }
319
320
321 bool RCS::revert()
322 {
323         if (doVCCommand("co -f -u" + version_ + " "
324                     + quoteName(onlyFileName(owner_->absFileName())),
325                     FileName(owner_->filePath())))
326                 return false;
327         // We ignore changes and just reload!
328         owner_->markClean();
329         return true;
330 }
331
332
333 bool RCS::isRevertWithConfirmation()
334 {
335         //FIXME owner && diff ?
336         return true;
337 }
338
339
340 void RCS::undoLast()
341 {
342         LYXERR(Debug::LYXVC, "LyXVC: undoLast");
343         doVCCommand("rcs -o" + version_ + " "
344                     + quoteName(onlyFileName(owner_->absFileName())),
345                     FileName(owner_->filePath()));
346 }
347
348
349 bool RCS::undoLastEnabled()
350 {
351         return true;
352 }
353
354
355 void RCS::getLog(FileName const & tmpf)
356 {
357         doVCCommand("rlog " + quoteName(onlyFileName(owner_->absFileName()))
358                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
359                     FileName(owner_->filePath()));
360 }
361
362
363 bool RCS::toggleReadOnlyEnabled()
364 {
365         // This got broken somewhere along lfuns dispatch reorganization.
366         // reloadBuffer would be needed after this, but thats problematic
367         // since we are inside Buffer::dispatch.
368         // return true;
369         return false;
370 }
371
372
373 string RCS::revisionInfo(LyXVC::RevisionInfo const info)
374 {
375         if (info == LyXVC::File)
376                 return version_;
377         // fill the rest of the attributes for a single file
378         if (rev_date_cache_.empty())
379                 if (!getRevisionInfo())
380                         return string();
381
382         switch (info) {
383                 case LyXVC::Author:
384                         return rev_author_cache_;
385                 case LyXVC::Date:
386                         return rev_date_cache_;
387                 case LyXVC::Time:
388                         return rev_time_cache_;
389                 default: ;
390         }
391
392         return string();
393 }
394
395
396 bool RCS::getRevisionInfo()
397 {
398         FileName tmpf = FileName::tempName("lyxvcout");
399         if (tmpf.empty()) {
400                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
401                 return false;
402         }
403         doVCCommand("rlog -r " + quoteName(onlyFileName(owner_->absFileName()))
404                 + " > " + quoteName(tmpf.toFilesystemEncoding()),
405                 FileName(owner_->filePath()));
406
407         if (tmpf.empty())
408                 return false;
409
410         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
411         string line;
412
413         // we reached to the entry, i.e. after initial log message
414         bool entry=false;
415         // line with critical info, e.g:
416         //"date: 2011/07/02 11:02:54;  author: sanda;  state: Exp;  lines: +17 -2"
417         string result;
418
419         while (ifs) {
420                 getline(ifs, line);
421                 LYXERR(Debug::LYXVC, line);
422                 if (entry && prefixIs(line, "date:")) {
423                         result = line;
424                         break;
425                 }
426                 if (prefixIs(line, "revision"))
427                         entry = true;
428         }
429         if (result.empty())
430                 return false;
431
432         rev_date_cache_ = token(result, ' ', 1);
433         rev_time_cache_ = rtrim(token(result, ' ', 2), ";");
434         rev_author_cache_ = trim(token(token(result, ';', 1), ':', 1));
435
436         return !rev_author_cache_.empty();
437 }
438
439 bool RCS::prepareFileRevision(string const &revis, string & f)
440 {
441         string rev = revis;
442         if (!VCS::makeRCSRevision(version_, rev))
443                 return false;
444
445         FileName tmpf = FileName::tempName("lyxvcrev_" + rev + "_");
446         if (tmpf.empty()) {
447                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
448                 return false;
449         }
450
451         doVCCommand("co -p" + rev + " "
452                       + quoteName(onlyFileName(owner_->absFileName()))
453                       + " > " + quoteName(tmpf.toFilesystemEncoding()),
454                 FileName(owner_->filePath()));
455         if (tmpf.isFileEmpty())
456                 return false;
457
458         f = tmpf.absFileName();
459         return true;
460 }
461
462
463 bool RCS::prepareFileRevisionEnabled()
464 {
465         return true;
466 }
467
468
469 /////////////////////////////////////////////////////////////////////
470 //
471 // CVS
472 //
473 /////////////////////////////////////////////////////////////////////
474
475 CVS::CVS(FileName const & m, Buffer * b) : VCS(b)
476 {
477         master_ = m;
478         have_rev_info_ = false;
479         scanMaster();
480 }
481
482
483 FileName const CVS::findFile(FileName const & file)
484 {
485         // First we look for the CVS/Entries in the same dir
486         // where we have file.
487         FileName const entries(onlyPath(file.absFileName()) + "/CVS/Entries");
488         string const tmpf = '/' + onlyFileName(file.absFileName()) + '/';
489         LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under cvs in `" << entries
490                              << "' for `" << tmpf << '\'');
491         if (entries.isReadableFile()) {
492                 // Ok we are at least in a CVS dir. Parse the CVS/Entries
493                 // and see if we can find this file. We do a fast and
494                 // dirty parse here.
495                 ifstream ifs(entries.toFilesystemEncoding().c_str());
496                 string line;
497                 while (getline(ifs, line)) {
498                         LYXERR(Debug::LYXVC, "\tEntries: " << line);
499                         if (contains(line, tmpf))
500                                 return entries;
501                 }
502         }
503         return FileName();
504 }
505
506
507 void CVS::scanMaster()
508 {
509         LYXERR(Debug::LYXVC, "LyXVC::CVS: scanMaster. \n     Checking: " << master_);
510         // Ok now we do the real scan...
511         ifstream ifs(master_.toFilesystemEncoding().c_str());
512         string name = onlyFileName(owner_->absFileName());
513         string tmpf = '/' + name + '/';
514         LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\'');
515         string line;
516         static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)");
517         while (getline(ifs, line)) {
518                 LYXERR(Debug::LYXVC, "\t  line: " << line);
519                 if (contains(line, tmpf)) {
520                         // Ok extract the fields.
521                         smatch sm;
522
523                         regex_match(line, sm, reg);
524
525                         //sm[0]; // whole matched string
526                         //sm[1]; // filename
527                         version_ = sm.str(2);
528                         string const file_date = sm.str(3);
529
530                         //sm[4]; // options
531                         //sm[5]; // tag or tagdate
532                         FileName file(owner_->absFileName());
533                         if (file.isReadableFile()) {
534                                 time_t mod = file.lastModified();
535                                 string mod_date = rtrim(asctime(gmtime(&mod)), "\n");
536                                 LYXERR(Debug::LYXVC, "Date in Entries: `" << file_date
537                                         << "'\nModification date of file: `" << mod_date << '\'');
538                                 if (file.isReadOnly()) {
539                                         // readonly checkout is unlocked
540                                         vcstatus = UNLOCKED;
541                                 } else {
542                                         FileName bdir(addPath(master_.onlyPath().absFileName(),"Base"));
543                                         FileName base(addName(bdir.absFileName(),name));
544                                         // if base version is existent "cvs edit" was used to lock
545                                         vcstatus = base.isReadableFile() ? LOCKED : NOLOCKING;
546                                 }
547                         } else {
548                                 vcstatus = NOLOCKING;
549                         }
550                         break;
551                 }
552         }
553 }
554
555
556 string const CVS::getTarget(OperationMode opmode) const
557 {
558         switch(opmode) {
559         case Directory:
560                 // in client server mode CVS does not like full path operand for directory operation
561                 // since LyX switches to the repo dir "." is good enough as target
562                 return ".";
563         case File:
564                 return quoteName(onlyFileName(owner_->absFileName()));
565         }
566         return string();
567 }
568
569
570 docstring CVS::toString(CvsStatus status) const
571 {
572         switch (status) {
573         case UpToDate:
574                 return _("Up-to-date");
575         case LocallyModified:
576                 return _("Locally Modified");
577         case LocallyAdded:
578                 return _("Locally Added");
579         case NeedsMerge:
580                 return _("Needs Merge");
581         case NeedsCheckout:
582                 return _("Needs Checkout");
583         case NoCvsFile:
584                 return _("No CVS file");
585         case StatusError:
586                 return _("Cannot retrieve CVS status");
587         }
588         return 0;
589 }
590
591
592 int CVS::doVCCommandWithOutput(string const & cmd, FileName const & path,
593         FileName const & output, bool reportError)
594 {
595         string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding());
596         return doVCCommand(cmd + redirection, path, reportError);
597 }
598
599
600 int CVS::doVCCommandCallWithOutput(std::string const & cmd,
601         support::FileName const & path,
602         support::FileName const & output)
603 {
604         string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding());
605         return doVCCommandCall(cmd + redirection, path);
606 }
607
608
609 CVS::CvsStatus CVS::getStatus()
610 {
611         FileName tmpf = FileName::tempName("lyxvcout");
612         if (tmpf.empty()) {
613                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
614                 return StatusError;
615         }
616
617         if (doVCCommandCallWithOutput("cvs status " + getTarget(File),
618                 FileName(owner_->filePath()), tmpf)) {
619                 tmpf.removeFile();
620                 return StatusError;
621         }
622
623         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
624         CvsStatus status = NoCvsFile;
625
626         while (ifs) {
627                 string line;
628                 getline(ifs, line);
629                 LYXERR(Debug::LYXVC, line << "\n");
630                 if (prefixIs(line, "File:")) {
631                         if (contains(line, "Up-to-date"))
632                                 status = UpToDate;
633                         else if (contains(line, "Locally Modified"))
634                                 status = LocallyModified;
635                         else if (contains(line, "Locally Added"))
636                                 status = LocallyAdded;
637                         else if (contains(line, "Needs Merge"))
638                                 status = NeedsMerge;
639                         else if (contains(line, "Needs Checkout"))
640                                 status = NeedsCheckout;
641                 }
642         }
643         tmpf.removeFile();
644         return status;
645 }
646
647 void CVS::getRevisionInfo()
648 {
649         if (have_rev_info_)
650                 return;
651         have_rev_info_ = true;
652         FileName tmpf = FileName::tempName("lyxvcout");
653         if (tmpf.empty()) {
654                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
655                 return;
656         }
657         
658         int rc = doVCCommandCallWithOutput("cvs log -r" + version_ 
659                 + " " + getTarget(File),
660                 FileName(owner_->filePath()), tmpf);
661         if (rc) {
662                 tmpf.removeFile();
663                 LYXERR(Debug::LYXVC, "cvs log failed with exit code " << rc);
664                 return;
665         }
666         
667         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
668         static regex const reg("date: (.*) (.*) (.*);  author: (.*);  state: (.*);(.*)");
669
670         while (ifs) {
671                 string line;
672                 getline(ifs, line);
673                 LYXERR(Debug::LYXVC, line << "\n");
674                 if (prefixIs(line, "date:")) {
675                         smatch sm;
676                         regex_match(line, sm, reg);
677                         //sm[0]; // whole matched string
678                         rev_date_cache_ = sm[1];
679                         rev_time_cache_ = sm[2];
680                         //sm[3]; // GMT offset
681                         rev_author_cache_ = sm[4];
682                         break;
683                 }
684         }
685         tmpf.removeFile();
686         if (rev_author_cache_.empty())
687                 LYXERR(Debug::LYXVC,
688                    "Could not retrieve revision info for " << version_ <<
689                    " of " << getTarget(File));
690 }
691
692
693 void CVS::registrer(string const & msg)
694 {
695         doVCCommand("cvs -q add -m \"" + msg + "\" "
696                 + getTarget(File),
697                 FileName(owner_->filePath()));
698 }
699
700
701 void CVS::getDiff(OperationMode opmode, FileName const & tmpf)
702 {
703         doVCCommandWithOutput("cvs diff " + getTarget(opmode),
704                 FileName(owner_->filePath()), tmpf, false);
705 }
706
707
708 int CVS::edit()
709 {
710         vcstatus = LOCKED;
711         return doVCCommand("cvs -q edit " + getTarget(File),
712                 FileName(owner_->filePath()));
713 }
714
715
716 int CVS::unedit()
717 {
718         vcstatus = UNLOCKED;
719         return doVCCommand("cvs -q unedit " + getTarget(File),
720                 FileName(owner_->filePath()));
721 }
722
723
724 int CVS::update(OperationMode opmode, FileName const & tmpf)
725 {
726         return doVCCommandWithOutput("cvs -q update "
727                 + getTarget(opmode),
728                 FileName(owner_->filePath()), tmpf, false);
729 }
730
731
732 string CVS::scanLogFile(FileName const & f, string & status)
733 {
734         ifstream ifs(f.toFilesystemEncoding().c_str());
735
736         while (ifs) {
737                 string line;
738                 getline(ifs, line);
739                 LYXERR(Debug::LYXVC, line << "\n");
740                 if (!line.empty())
741                         status += line + "; ";
742                 if (prefixIs(line, "C ")) {
743                         ifs.close();
744                         return line;
745                 }
746         }
747         ifs.close();
748         return string();
749 }
750         
751         
752 string CVS::checkIn(string const & msg)
753 {
754         CvsStatus status = getStatus();
755         switch (status) {
756         case UpToDate:
757                 if (vcstatus != NOLOCKING)
758                         unedit();
759                 return "CVS: Proceeded";
760         case LocallyModified:
761         case LocallyAdded: {
762                 int rc = doVCCommand("cvs -q commit -m \"" + msg + "\" "
763                         + getTarget(File),
764                     FileName(owner_->filePath()));
765                 return rc ? string() : "CVS: Proceeded";
766         }
767         case NeedsMerge:
768         case NeedsCheckout:
769                 frontend::Alert::error(_("Revision control error."),
770                         _("The repository version is newer then the current check out.\n"
771                           "You have to update from repository first or revert your changes.")) ;
772                 break;
773         default:
774                 frontend::Alert::error(_("Revision control error."),
775                         bformat(_("Bad status when checking in changes.\n"
776                                           "\n'%1$s'\n\n"),
777                                 toString(status)));
778                 break;
779         }
780         return string();
781 }
782
783
784 bool CVS::isLocked() const
785 {
786         FileName fn(owner_->absFileName());
787         fn.refresh();
788         return !fn.isReadOnly();
789 }
790
791
792 bool CVS::checkInEnabled()
793 {
794         if (vcstatus != NOLOCKING)
795                 return isLocked();
796         else
797                 return true;
798 }
799
800
801 bool CVS::isCheckInWithConfirmation()
802 {
803         CvsStatus status = getStatus();
804         return status == LocallyModified || status == LocallyAdded;
805 }
806
807
808 string CVS::checkOut()
809 {
810         if (vcstatus != NOLOCKING && edit())
811                 return string();
812         FileName tmpf = FileName::tempName("lyxvcout");
813         if (tmpf.empty()) {
814                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
815                 return string();
816         }
817         
818         int rc = update(File, tmpf);
819         string log;
820         string const res = scanLogFile(tmpf, log);
821         if (!res.empty()) {
822                 frontend::Alert::error(_("Revision control error."),
823                         bformat(_("Error when updating from repository.\n"
824                                 "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
825                                 "After pressing OK, LyX will try to reopen the resolved document."),
826                                 from_local8bit(res)));
827                 rc = 0;
828         }
829         
830         tmpf.erase();
831         return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log;
832 }
833
834
835 bool CVS::checkOutEnabled()
836 {
837         if (vcstatus != NOLOCKING)
838                 return !isLocked();
839         else
840                 return true;
841 }
842
843
844 string CVS::repoUpdate()
845 {
846         FileName tmpf = FileName::tempName("lyxvcout");
847         if (tmpf.empty()) {
848                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
849                 return string();
850         }
851         
852         getDiff(Directory, tmpf);
853         docstring res = tmpf.fileContents("UTF-8");
854         if (!res.empty()) {
855                 LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
856                 docstring const file = from_utf8(owner_->filePath());
857                 docstring text = bformat(_("There were detected changes "
858                                 "in the working directory:\n%1$s\n\n"
859                                 "Possible file conflicts must be then resolved manually "
860                                 "or you will need to revert back to the repository version."), file);
861                 int ret = frontend::Alert::prompt(_("Changes detected"),
862                                 text, 0, 1, _("&Continue"), _("&Abort"), _("View &Log ..."));
863                 if (ret == 2 ) {
864                         dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
865                         ret = frontend::Alert::prompt(_("Changes detected"),
866                                 text, 0, 1, _("&Continue"), _("&Abort"));
867                         hideDialogs("file", 0);
868                 }
869                 if (ret == 1 ) {
870                         tmpf.removeFile();
871                         return string();
872                 }
873         }
874
875         int rc = update(Directory, tmpf);
876         res += "Update log:\n" + tmpf.fileContents("UTF-8");
877         LYXERR(Debug::LYXVC, res);
878
879         string log;
880         string sres = scanLogFile(tmpf, log);
881         if (!sres.empty()) {
882                 docstring const file = owner_->fileName().displayName(20);
883                 frontend::Alert::error(_("Revision control error."),
884                         bformat(_("Error when updating document %1$s from repository.\n"
885                                           "You have to manually resolve the conflicts NOW!\n'%2$s'.\n\n"
886                                           "After pressing OK, LyX will try to reopen the resolved document."),
887                                 file, from_local8bit(sres)));
888                 rc = 0;
889         }
890         
891         tmpf.removeFile();
892
893         return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log;
894 }
895
896
897 bool CVS::repoUpdateEnabled()
898 {
899         return true;
900 }
901
902
903 string CVS::lockingToggle()
904 {
905         lyxerr << "Sorry, not implemented." << endl;
906         return string();
907 }
908
909
910 bool CVS::lockingToggleEnabled()
911 {
912         return false;
913 }
914
915
916 bool CVS::isRevertWithConfirmation()
917 {
918         CvsStatus status = getStatus();
919         return !owner_->isClean() || status == LocallyModified || status == NeedsMerge;
920 }
921
922
923 bool CVS::revert()
924 {
925         // Reverts to the version in CVS repository and
926         // gets the updated version from the repository.
927         CvsStatus status = getStatus();
928         switch (status) {
929         case UpToDate:
930                 if (vcstatus != NOLOCKING)
931                         return 0 == unedit();
932                 break;
933         case NeedsMerge:
934         case NeedsCheckout:
935         case LocallyModified: {
936                 FileName f(owner_->absFileName());
937                 f.removeFile();
938                 update(File, FileName());
939                 owner_->markClean();
940                 break;
941         }
942         case LocallyAdded: {
943                 docstring const file = owner_->fileName().displayName(20);
944                 frontend::Alert::error(_("Revision control error."),
945                         bformat(_("The document %1$s is not in repository.\n"
946                                   "You have to check in the first revision before you can revert."),
947                                 file)) ;
948                 return false;
949         }
950         default: {
951                 docstring const file = owner_->fileName().displayName(20);
952                 frontend::Alert::error(_("Revision control error."),
953                         bformat(_("Cannot revert document %1$s to repository version.\n"
954                                   "The status '%2$s' is unexpected."),
955                                 file, toString(status)));
956                 return false;
957                 }
958         }
959         return true;
960 }
961
962
963 void CVS::undoLast()
964 {
965         // merge the current with the previous version
966         // in a reverse patch kind of way, so that the
967         // result is to revert the last changes.
968         lyxerr << "Sorry, not implemented." << endl;
969 }
970
971
972 bool CVS::undoLastEnabled()
973 {
974         return false;
975 }
976
977
978 void CVS::getLog(FileName const & tmpf)
979 {
980         doVCCommandWithOutput("cvs log " + getTarget(File),
981                 FileName(owner_->filePath()),
982                 tmpf);
983 }
984
985
986 bool CVS::toggleReadOnlyEnabled()
987 {
988         return false;
989 }
990
991
992 string CVS::revisionInfo(LyXVC::RevisionInfo const info)
993 {
994         if (!version_.empty()) {
995                 getRevisionInfo();
996                 switch (info) {
997                 case LyXVC::File:
998                         return version_;
999                 case LyXVC::Author:
1000                         return rev_author_cache_;
1001                 case LyXVC::Date:
1002                         return rev_date_cache_;
1003                 case LyXVC::Time:
1004                         return rev_time_cache_;
1005                 default: ;
1006                 }
1007         }
1008         return string();
1009 }
1010
1011
1012 bool CVS::prepareFileRevision(string const & revis, string & f)
1013 {
1014         string rev = revis;
1015         if (!VCS::makeRCSRevision(version_, rev))
1016                 return false;
1017
1018         FileName tmpf = FileName::tempName("lyxvcrev_" + rev + "_");
1019         if (tmpf.empty()) {
1020                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1021                 return false;
1022         }
1023
1024         doVCCommandWithOutput("cvs update -p -r" + rev + " "
1025                 + getTarget(File),
1026                 FileName(owner_->filePath()), tmpf);
1027         if (tmpf.isFileEmpty())
1028                 return false;
1029
1030         f = tmpf.absFileName();
1031         return true;
1032 }
1033
1034
1035 bool CVS::prepareFileRevisionEnabled()
1036 {
1037         return true;
1038 }
1039
1040
1041 /////////////////////////////////////////////////////////////////////
1042 //
1043 // SVN
1044 //
1045 /////////////////////////////////////////////////////////////////////
1046
1047 SVN::SVN(FileName const & m, Buffer * b) : VCS(b)
1048 {
1049         master_ = m;
1050         locked_mode_ = 0;
1051         scanMaster();
1052 }
1053
1054
1055 FileName const SVN::findFile(FileName const & file)
1056 {
1057         // First we check the existence of repository meta data.
1058         if (!VCS::checkparentdirs(file, ".svn")) {
1059                 LYXERR(Debug::LYXVC, "Cannot find SVN meta data for " << file);
1060                 return FileName();
1061         }
1062
1063         // Now we check the status of the file.
1064         FileName tmpf = FileName::tempName("lyxvcout");
1065         if (tmpf.empty()) {
1066                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1067                 return FileName();
1068         }
1069
1070         string const fname = onlyFileName(file.absFileName());
1071         LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under svn control for `" << fname << '\'');
1072         bool found = 0 == doVCCommandCall("svn info " + quoteName(fname)
1073                                                 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1074                                                 file.onlyPath());
1075         LYXERR(Debug::LYXVC, "SVN control: " << (found ? "enabled" : "disabled"));
1076         return found ? file : FileName();
1077 }
1078
1079
1080 void SVN::scanMaster()
1081 {
1082         // vcstatus code is somewhat superflous, until we want
1083         // to implement read-only toggle for svn.
1084         vcstatus = NOLOCKING;
1085         if (checkLockMode()) {
1086                 if (isLocked()) {
1087                         vcstatus = LOCKED;
1088                 } else {
1089                         vcstatus = UNLOCKED;
1090                 }
1091         }
1092 }
1093
1094
1095 bool SVN::checkLockMode()
1096 {
1097         FileName tmpf = FileName::tempName("lyxvcout");
1098         if (tmpf.empty()){
1099                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1100                 return false;
1101         }
1102
1103         LYXERR(Debug::LYXVC, "Detecting locking mode...");
1104         if (doVCCommandCall("svn proplist " + quoteName(onlyFileName(owner_->absFileName()))
1105                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1106                     FileName(owner_->filePath())))
1107                 return false;
1108
1109         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1110         string line;
1111         bool ret = false;
1112
1113         while (ifs && !ret) {
1114                 getline(ifs, line);
1115                 LYXERR(Debug::LYXVC, line);
1116                 if (contains(line, "svn:needs-lock"))
1117                         ret = true;
1118         }
1119         LYXERR(Debug::LYXVC, "Locking enabled: " << ret);
1120         ifs.close();
1121         locked_mode_ = ret;
1122         return ret;
1123
1124 }
1125
1126
1127 bool SVN::isLocked() const
1128 {
1129         FileName file(owner_->absFileName());
1130         file.refresh();
1131         return !file.isReadOnly();
1132 }
1133
1134
1135 void SVN::registrer(string const & /*msg*/)
1136 {
1137         doVCCommand("svn add -q " + quoteName(onlyFileName(owner_->absFileName())),
1138                     FileName(owner_->filePath()));
1139 }
1140
1141
1142 string SVN::checkIn(string const & msg)
1143 {
1144         FileName tmpf = FileName::tempName("lyxvcout");
1145         if (tmpf.empty()){
1146                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1147                 return N_("Error: Could not generate logfile.");
1148         }
1149
1150         doVCCommand("svn commit -m \"" + msg + "\" "
1151                     + quoteName(onlyFileName(owner_->absFileName()))
1152                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1153                     FileName(owner_->filePath()));
1154
1155         string log;
1156         string res = scanLogFile(tmpf, log);
1157         if (!res.empty())
1158                 frontend::Alert::error(_("Revision control error."),
1159                                 _("Error when committing to repository.\n"
1160                                 "You have to manually resolve the problem.\n"
1161                                 "LyX will reopen the document after you press OK."));
1162         else
1163                 fileLock(false, tmpf, log);
1164
1165         tmpf.erase();
1166         return log.empty() ? string() : "SVN: " + log;
1167 }
1168
1169
1170 bool SVN::checkInEnabled()
1171 {
1172         if (locked_mode_)
1173                 return isLocked();
1174         else
1175                 return true;
1176 }
1177
1178
1179 bool SVN::isCheckInWithConfirmation()
1180 {
1181         // FIXME one day common getDiff and perhaps OpMode for all backends
1182
1183         FileName tmpf = FileName::tempName("lyxvcout");
1184         if (tmpf.empty()) {
1185                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1186                 return true;
1187         }
1188
1189         doVCCommandCall("svn diff " + quoteName(owner_->absFileName())
1190                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1191                 FileName(owner_->filePath()));
1192
1193         docstring diff = tmpf.fileContents("UTF-8");
1194         tmpf.erase();
1195
1196         if (diff.empty())
1197                 return false;
1198
1199         return true;
1200 }
1201
1202
1203 // FIXME Correctly return code should be checked instead of this.
1204 // This would need another solution than just plain startscript.
1205 // Hint from Andre': QProcess::readAllStandardError()...
1206 string SVN::scanLogFile(FileName const & f, string & status)
1207 {
1208         ifstream ifs(f.toFilesystemEncoding().c_str());
1209         string line;
1210
1211         while (ifs) {
1212                 getline(ifs, line);
1213                 LYXERR(Debug::LYXVC, line << "\n");
1214                 if (!line.empty()) 
1215                         status += line + "; ";
1216                 if (prefixIs(line, "C ") || prefixIs(line, "CU ")
1217                                          || contains(line, "Commit failed")) {
1218                         ifs.close();
1219                         return line;
1220                 }
1221                 if (contains(line, "svn:needs-lock")) {
1222                         ifs.close();
1223                         return line;
1224                 }
1225         }
1226         ifs.close();
1227         return string();
1228 }
1229
1230
1231 void SVN::fileLock(bool lock, FileName const & tmpf, string &status)
1232 {
1233         if (!locked_mode_ || (isLocked() == lock))
1234                 return;
1235
1236         string const arg = lock ? "lock " : "unlock ";
1237         doVCCommand("svn "+ arg + quoteName(onlyFileName(owner_->absFileName()))
1238                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1239                     FileName(owner_->filePath()));
1240
1241         // Lock error messages go unfortunately on stderr and are unreachible this way.
1242         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1243         string line;
1244         while (ifs) {
1245                 getline(ifs, line);
1246                 if (!line.empty()) status += line + "; ";
1247         }
1248         ifs.close();
1249
1250         if (!isLocked() && lock)
1251                 frontend::Alert::error(_("Revision control error."),
1252                         _("Error while acquiring write lock.\n"
1253                         "Another user is most probably editing\n"
1254                         "the current document now!\n"
1255                         "Also check the access to the repository."));
1256         if (isLocked() && !lock)
1257                 frontend::Alert::error(_("Revision control error."),
1258                         _("Error while releasing write lock.\n"
1259                         "Check the access to the repository."));
1260 }
1261
1262
1263 string SVN::checkOut()
1264 {
1265         FileName tmpf = FileName::tempName("lyxvcout");
1266         if (tmpf.empty()) {
1267                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1268                 return N_("Error: Could not generate logfile.");
1269         }
1270
1271         doVCCommand("svn update --non-interactive " + quoteName(onlyFileName(owner_->absFileName()))
1272                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1273                     FileName(owner_->filePath()));
1274
1275         string log;
1276         string const res = scanLogFile(tmpf, log);
1277         if (!res.empty())
1278                 frontend::Alert::error(_("Revision control error."),
1279                         bformat(_("Error when updating from repository.\n"
1280                                 "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
1281                                 "After pressing OK, LyX will try to reopen the resolved document."),
1282                         from_local8bit(res)));
1283
1284         fileLock(true, tmpf, log);
1285
1286         tmpf.erase();
1287         return log.empty() ? string() : "SVN: " + log;
1288 }
1289
1290
1291 bool SVN::checkOutEnabled()
1292 {
1293         if (locked_mode_)
1294                 return !isLocked();
1295         else
1296                 return true;
1297 }
1298
1299
1300 string SVN::repoUpdate()
1301 {
1302         FileName tmpf = FileName::tempName("lyxvcout");
1303         if (tmpf.empty()) {
1304                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1305                 return N_("Error: Could not generate logfile.");
1306         }
1307
1308         doVCCommand("svn diff " + quoteName(owner_->filePath())
1309                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1310                 FileName(owner_->filePath()));
1311         docstring res = tmpf.fileContents("UTF-8");
1312         if (!res.empty()) {
1313                 LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
1314                 docstring const file = from_utf8(owner_->filePath());
1315                 docstring text = bformat(_("There were detected changes "
1316                                 "in the working directory:\n%1$s\n\n"
1317                                 "In case of file conflict version of the local directory files "
1318                                 "will be preferred."
1319                                 "\n\nContinue?"), file);
1320                 int ret = frontend::Alert::prompt(_("Changes detected"),
1321                                 text, 0, 1, _("&Yes"), _("&No"), _("View &Log ..."));
1322                 if (ret == 2 ) {
1323                         dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
1324                         ret = frontend::Alert::prompt(_("Changes detected"),
1325                                 text, 0, 1, _("&Yes"), _("&No"));
1326                         hideDialogs("file", 0);
1327                 }
1328                 if (ret == 1 ) {
1329                         tmpf.erase();
1330                         return string();
1331                 }
1332         }
1333
1334         // Reverting looks too harsh, see bug #6255.
1335         // doVCCommand("svn revert -R " + quoteName(owner_->filePath())
1336         // + " > " + quoteName(tmpf.toFilesystemEncoding()),
1337         // FileName(owner_->filePath()));
1338         // res = "Revert log:\n" + tmpf.fileContents("UTF-8");
1339         doVCCommand("svn update --accept mine-full " + quoteName(owner_->filePath())
1340                 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1341                 FileName(owner_->filePath()));
1342         res += "Update log:\n" + tmpf.fileContents("UTF-8");
1343
1344         LYXERR(Debug::LYXVC, res);
1345         tmpf.erase();
1346         return to_utf8(res);
1347 }
1348
1349
1350 bool SVN::repoUpdateEnabled()
1351 {
1352         return true;
1353 }
1354
1355
1356 string SVN::lockingToggle()
1357 {
1358         FileName tmpf = FileName::tempName("lyxvcout");
1359         if (tmpf.empty()) {
1360                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1361                 return N_("Error: Could not generate logfile.");
1362         }
1363
1364         int ret = doVCCommand("svn proplist " + quoteName(onlyFileName(owner_->absFileName()))
1365                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1366                     FileName(owner_->filePath()));
1367         if (ret)
1368                 return string();
1369
1370         string log;
1371         string res = scanLogFile(tmpf, log);
1372         bool locking = contains(res, "svn:needs-lock");
1373         if (!locking)
1374                 ret = doVCCommand("svn propset svn:needs-lock ON "
1375                     + quoteName(onlyFileName(owner_->absFileName()))
1376                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1377                     FileName(owner_->filePath()));
1378         else
1379                 ret = doVCCommand("svn propdel svn:needs-lock "
1380                     + quoteName(onlyFileName(owner_->absFileName()))
1381                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1382                     FileName(owner_->filePath()));
1383         if (ret)
1384                 return string();
1385
1386         tmpf.erase();
1387         frontend::Alert::warning(_("VCN File Locking"),
1388                 (locking ? _("Locking property unset.") : _("Locking property set.")) + "\n"
1389                 + _("Do not forget to commit the locking property into the repository."),
1390                 true);
1391
1392         return string("SVN: ") +  N_("Locking property set.");
1393 }
1394
1395
1396 bool SVN::lockingToggleEnabled()
1397 {
1398         return true;
1399 }
1400
1401
1402 bool SVN::revert()
1403 {
1404         // Reverts to the version in SVN repository and
1405         // gets the updated version from the repository.
1406         string const fil = quoteName(onlyFileName(owner_->absFileName()));
1407
1408         if (doVCCommand("svn revert -q " + fil,
1409                     FileName(owner_->filePath())))
1410                 return false;
1411         owner_->markClean();
1412         return true;
1413 }
1414
1415
1416 bool SVN::isRevertWithConfirmation()
1417 {
1418         //FIXME owner && diff
1419         return true;
1420 }
1421
1422
1423 void SVN::undoLast()
1424 {
1425         // merge the current with the previous version
1426         // in a reverse patch kind of way, so that the
1427         // result is to revert the last changes.
1428         lyxerr << "Sorry, not implemented." << endl;
1429 }
1430
1431
1432 bool SVN::undoLastEnabled()
1433 {
1434         return false;
1435 }
1436
1437
1438 string SVN::revisionInfo(LyXVC::RevisionInfo const info)
1439 {
1440         if (info == LyXVC::Tree) {
1441                         if (rev_tree_cache_.empty())
1442                                 if (!getTreeRevisionInfo())
1443                                         rev_tree_cache_ = "?";
1444                         if (rev_tree_cache_ == "?")
1445                                 return string();
1446
1447                         return rev_tree_cache_;
1448         }
1449
1450         // fill the rest of the attributes for a single file
1451         if (rev_file_cache_.empty())
1452                 if (!getFileRevisionInfo())
1453                         rev_file_cache_ = "?";
1454
1455         switch (info) {
1456                 case LyXVC::File:
1457                         if (rev_file_cache_ == "?")
1458                                 return string();
1459                         return rev_file_cache_;
1460                 case LyXVC::Author:
1461                         return rev_author_cache_;
1462                 case LyXVC::Date:
1463                         return rev_date_cache_;
1464                 case LyXVC::Time:
1465                         return rev_time_cache_;
1466                 default: ;
1467
1468         }
1469
1470         return string();
1471 }
1472
1473
1474 bool SVN::getFileRevisionInfo()
1475 {
1476         FileName tmpf = FileName::tempName("lyxvcout");
1477         if (tmpf.empty()) {
1478                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1479                 return false;
1480         }
1481
1482         doVCCommand("svn info --xml " + quoteName(onlyFileName(owner_->absFileName()))
1483                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1484                     FileName(owner_->filePath()));
1485
1486         if (tmpf.empty())
1487                 return false;
1488
1489         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1490         string line;
1491         // commit log part
1492         bool c = false;
1493         string rev;
1494
1495         while (ifs) {
1496                 getline(ifs, line);
1497                 LYXERR(Debug::LYXVC, line);
1498                 if (prefixIs(line, "<commit"))
1499                         c = true;
1500                 if (c && prefixIs(line, "   revision=\"") && suffixIs(line, "\">")) {
1501                         string l1 = subst(line, "revision=\"", "");
1502                         string l2 = trim(subst(l1, "\">", ""));
1503                         if (isStrInt(l2))
1504                                 rev_file_cache_ = rev = l2;
1505                 }
1506                 if (c && prefixIs(line, "<author>") && suffixIs(line, "</author>")) {
1507                         string l1 = subst(line, "<author>", "");
1508                         string l2 = subst(l1, "</author>", "");
1509                         rev_author_cache_ = l2;
1510                 }
1511                 if (c && prefixIs(line, "<date>") && suffixIs(line, "</date>")) {
1512                         string l1 = subst(line, "<date>", "");
1513                         string l2 = subst(l1, "</date>", "");
1514                         l2 = split(l2, l1, 'T');
1515                         rev_date_cache_ = l1;
1516                         l2 = split(l2, l1, '.');
1517                         rev_time_cache_ = l1;
1518                 }
1519         }
1520
1521         ifs.close();
1522         tmpf.erase();
1523         return !rev.empty();
1524 }
1525
1526
1527 bool SVN::getTreeRevisionInfo()
1528 {
1529         FileName tmpf = FileName::tempName("lyxvcout");
1530         if (tmpf.empty()) {
1531                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1532                 return false;
1533         }
1534
1535         doVCCommand("svnversion -n . > " + quoteName(tmpf.toFilesystemEncoding()),
1536                     FileName(owner_->filePath()));
1537
1538         if (tmpf.empty())
1539                 return false;
1540
1541         // only first line in case something bad happens.
1542         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1543         string line;
1544         getline(ifs, line);
1545         ifs.close();
1546         tmpf.erase();
1547
1548         rev_tree_cache_ = line;
1549         return !line.empty();
1550 }
1551
1552
1553 void SVN::getLog(FileName const & tmpf)
1554 {
1555         doVCCommand("svn log " + quoteName(onlyFileName(owner_->absFileName()))
1556                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1557                     FileName(owner_->filePath()));
1558 }
1559
1560
1561 bool SVN::prepareFileRevision(string const & revis, string & f)
1562 {
1563         if (!isStrInt(revis))
1564                 return false;
1565
1566         int rev = convert<int>(revis);
1567         if (rev <= 0)
1568                 if (!getFileRevisionInfo())
1569                         return false;
1570         if (rev == 0)
1571                 rev = convert<int>(rev_file_cache_);
1572         // go back for minus rev
1573         else if (rev < 0) {
1574                 rev = rev + convert<int>(rev_file_cache_);
1575                 if (rev < 1)
1576                         return false;
1577         }
1578
1579         string revname = convert<string>(rev);
1580         FileName tmpf = FileName::tempName("lyxvcrev_" + revname + "_");
1581         if (tmpf.empty()) {
1582                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1583                 return false;
1584         }
1585
1586         doVCCommand("svn cat -r " + revname + " "
1587                       + quoteName(onlyFileName(owner_->absFileName()))
1588                       + " > " + quoteName(tmpf.toFilesystemEncoding()),
1589                 FileName(owner_->filePath()));
1590         if (tmpf.isFileEmpty())
1591                 return false;
1592
1593         f = tmpf.absFileName();
1594         return true;
1595 }
1596
1597
1598 bool SVN::prepareFileRevisionEnabled()
1599 {
1600         return true;
1601 }
1602
1603
1604
1605 bool SVN::toggleReadOnlyEnabled()
1606 {
1607         return false;
1608 }
1609
1610
1611 } // namespace lyx