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