]> git.lyx.org Git - lyx.git/blob - src/VCBackend.cpp
installer: uninstall fixes
[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)
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, FileName const & f)
476 {
477         master_ = m;
478         file_ = f;
479         have_rev_info_ = false;
480         scanMaster();
481 }
482
483
484 FileName const CVS::findFile(FileName const & file)
485 {
486         // First we look for the CVS/Entries in the same dir
487         // where we have file.
488         FileName const entries(onlyPath(file.absFileName()) + "/CVS/Entries");
489         string const tmpf = '/' + onlyFileName(file.absFileName()) + '/';
490         LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under cvs in `" << entries
491                              << "' for `" << tmpf << '\'');
492         if (entries.isReadableFile()) {
493                 // Ok we are at least in a CVS dir. Parse the CVS/Entries
494                 // and see if we can find this file. We do a fast and
495                 // dirty parse here.
496                 ifstream ifs(entries.toFilesystemEncoding().c_str());
497                 string line;
498                 while (getline(ifs, line)) {
499                         LYXERR(Debug::LYXVC, "\tEntries: " << line);
500                         if (contains(line, tmpf))
501                                 return entries;
502                 }
503         }
504         return FileName();
505 }
506
507
508 void CVS::scanMaster()
509 {
510         LYXERR(Debug::LYXVC, "LyXVC::CVS: scanMaster. \n     Checking: " << master_);
511         // Ok now we do the real scan...
512         ifstream ifs(master_.toFilesystemEncoding().c_str());
513         string name = onlyFileName(file_.absFileName());
514         string tmpf = '/' + name + '/';
515         LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\'');
516         string line;
517         static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)");
518         while (getline(ifs, line)) {
519                 LYXERR(Debug::LYXVC, "\t  line: " << line);
520                 if (contains(line, tmpf)) {
521                         // Ok extract the fields.
522                         smatch sm;
523
524                         regex_match(line, sm, reg);
525
526                         //sm[0]; // whole matched string
527                         //sm[1]; // filename
528                         version_ = sm.str(2);
529                         string const file_date = sm.str(3);
530
531                         //sm[4]; // options
532                         //sm[5]; // tag or tagdate
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, FileName const & f)
1048 {
1049         owner_ = 0;
1050         master_ = m;
1051         file_ = f;
1052         locked_mode_ = 0;
1053         scanMaster();
1054 }
1055
1056
1057 FileName const SVN::findFile(FileName const & file)
1058 {
1059         // First we check the existence of repository meta data.
1060         if (!VCS::checkparentdirs(file, ".svn")) {
1061                 LYXERR(Debug::LYXVC, "Cannot find SVN meta data for " << file);
1062                 return FileName();
1063         }
1064
1065         // Now we check the status of the file.
1066         FileName tmpf = FileName::tempName("lyxvcout");
1067         if (tmpf.empty()) {
1068                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1069                 return FileName();
1070         }
1071
1072         string const fname = onlyFileName(file.absFileName());
1073         LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under svn control for `" << fname << '\'');
1074         bool found = 0 == doVCCommandCall("svn info " + quoteName(fname)
1075                                                 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1076                                                 file.onlyPath());
1077         LYXERR(Debug::LYXVC, "SVN control: " << (found ? "enabled" : "disabled"));
1078         return found ? file : FileName();
1079 }
1080
1081
1082 void SVN::scanMaster()
1083 {
1084         // vcstatus code is somewhat superflous, until we want
1085         // to implement read-only toggle for svn.
1086         vcstatus = NOLOCKING;
1087         if (checkLockMode()) {
1088                 if (isLocked()) {
1089                         vcstatus = LOCKED;
1090                 } else {
1091                         vcstatus = UNLOCKED;
1092                 }
1093         }
1094 }
1095
1096
1097 bool SVN::checkLockMode()
1098 {
1099         FileName tmpf = FileName::tempName("lyxvcout");
1100         if (tmpf.empty()){
1101                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1102                 return false;
1103         }
1104
1105         LYXERR(Debug::LYXVC, "Detecting locking mode...");
1106         if (doVCCommandCall("svn proplist " + quoteName(file_.onlyFileName())
1107                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1108                     file_.onlyPath()))
1109                 return false;
1110
1111         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1112         string line;
1113         bool ret = false;
1114
1115         while (ifs && !ret) {
1116                 getline(ifs, line);
1117                 LYXERR(Debug::LYXVC, line);
1118                 if (contains(line, "svn:needs-lock"))
1119                         ret = true;
1120         }
1121         LYXERR(Debug::LYXVC, "Locking enabled: " << ret);
1122         ifs.close();
1123         locked_mode_ = ret;
1124         return ret;
1125
1126 }
1127
1128
1129 bool SVN::isLocked() const
1130 {
1131         file_.refresh();
1132         return !file_.isReadOnly();
1133 }
1134
1135
1136 void SVN::registrer(string const & /*msg*/)
1137 {
1138         doVCCommand("svn add -q " + quoteName(onlyFileName(owner_->absFileName())),
1139                     FileName(owner_->filePath()));
1140 }
1141
1142
1143 string SVN::checkIn(string const & msg)
1144 {
1145         FileName tmpf = FileName::tempName("lyxvcout");
1146         if (tmpf.empty()){
1147                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1148                 return N_("Error: Could not generate logfile.");
1149         }
1150
1151         doVCCommand("svn commit -m \"" + msg + "\" "
1152                     + quoteName(onlyFileName(owner_->absFileName()))
1153                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1154                     FileName(owner_->filePath()));
1155
1156         string log;
1157         string res = scanLogFile(tmpf, log);
1158         if (!res.empty())
1159                 frontend::Alert::error(_("Revision control error."),
1160                                 _("Error when committing to repository.\n"
1161                                 "You have to manually resolve the problem.\n"
1162                                 "LyX will reopen the document after you press OK."));
1163         else
1164                 fileLock(false, tmpf, log);
1165
1166         tmpf.erase();
1167         return log.empty() ? string() : "SVN: " + log;
1168 }
1169
1170
1171 bool SVN::checkInEnabled()
1172 {
1173         if (locked_mode_)
1174                 return isLocked();
1175         else
1176                 return true;
1177 }
1178
1179
1180 bool SVN::isCheckInWithConfirmation()
1181 {
1182         // FIXME one day common getDiff and perhaps OpMode for all backends
1183
1184         FileName tmpf = FileName::tempName("lyxvcout");
1185         if (tmpf.empty()) {
1186                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1187                 return true;
1188         }
1189
1190         doVCCommandCall("svn diff " + quoteName(owner_->absFileName())
1191                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1192                 FileName(owner_->filePath()));
1193
1194         docstring diff = tmpf.fileContents("UTF-8");
1195         tmpf.erase();
1196
1197         if (diff.empty())
1198                 return false;
1199
1200         return true;
1201 }
1202
1203
1204 // FIXME Correctly return code should be checked instead of this.
1205 // This would need another solution than just plain startscript.
1206 // Hint from Andre': QProcess::readAllStandardError()...
1207 string SVN::scanLogFile(FileName const & f, string & status)
1208 {
1209         ifstream ifs(f.toFilesystemEncoding().c_str());
1210         string line;
1211
1212         while (ifs) {
1213                 getline(ifs, line);
1214                 LYXERR(Debug::LYXVC, line << "\n");
1215                 if (!line.empty()) 
1216                         status += line + "; ";
1217                 if (prefixIs(line, "C ") || prefixIs(line, "CU ")
1218                                          || contains(line, "Commit failed")) {
1219                         ifs.close();
1220                         return line;
1221                 }
1222                 if (contains(line, "svn:needs-lock")) {
1223                         ifs.close();
1224                         return line;
1225                 }
1226         }
1227         ifs.close();
1228         return string();
1229 }
1230
1231
1232 void SVN::fileLock(bool lock, FileName const & tmpf, string &status)
1233 {
1234         if (!locked_mode_ || (isLocked() == lock))
1235                 return;
1236
1237         string const arg = lock ? "lock " : "unlock ";
1238         doVCCommand("svn "+ arg + quoteName(onlyFileName(owner_->absFileName()))
1239                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1240                     FileName(owner_->filePath()));
1241
1242         // Lock error messages go unfortunately on stderr and are unreachible this way.
1243         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1244         string line;
1245         while (ifs) {
1246                 getline(ifs, line);
1247                 if (!line.empty()) status += line + "; ";
1248         }
1249         ifs.close();
1250
1251         if (!isLocked() && lock)
1252                 frontend::Alert::error(_("Revision control error."),
1253                         _("Error while acquiring write lock.\n"
1254                         "Another user is most probably editing\n"
1255                         "the current document now!\n"
1256                         "Also check the access to the repository."));
1257         if (isLocked() && !lock)
1258                 frontend::Alert::error(_("Revision control error."),
1259                         _("Error while releasing write lock.\n"
1260                         "Check the access to the repository."));
1261 }
1262
1263
1264 string SVN::checkOut()
1265 {
1266         FileName tmpf = FileName::tempName("lyxvcout");
1267         if (tmpf.empty()) {
1268                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1269                 return N_("Error: Could not generate logfile.");
1270         }
1271
1272         doVCCommand("svn update --non-interactive " + quoteName(onlyFileName(owner_->absFileName()))
1273                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1274                     FileName(owner_->filePath()));
1275
1276         string log;
1277         string const res = scanLogFile(tmpf, log);
1278         if (!res.empty())
1279                 frontend::Alert::error(_("Revision control error."),
1280                         bformat(_("Error when updating from repository.\n"
1281                                 "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
1282                                 "After pressing OK, LyX will try to reopen the resolved document."),
1283                         from_local8bit(res)));
1284
1285         fileLock(true, tmpf, log);
1286
1287         tmpf.erase();
1288         return log.empty() ? string() : "SVN: " + log;
1289 }
1290
1291
1292 bool SVN::checkOutEnabled()
1293 {
1294         if (locked_mode_)
1295                 return !isLocked();
1296         else
1297                 return true;
1298 }
1299
1300
1301 string SVN::repoUpdate()
1302 {
1303         FileName tmpf = FileName::tempName("lyxvcout");
1304         if (tmpf.empty()) {
1305                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1306                 return N_("Error: Could not generate logfile.");
1307         }
1308
1309         doVCCommand("svn diff " + quoteName(owner_->filePath())
1310                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1311                 FileName(owner_->filePath()));
1312         docstring res = tmpf.fileContents("UTF-8");
1313         if (!res.empty()) {
1314                 LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
1315                 docstring const file = from_utf8(owner_->filePath());
1316                 docstring text = bformat(_("There were detected changes "
1317                                 "in the working directory:\n%1$s\n\n"
1318                                 "In case of file conflict version of the local directory files "
1319                                 "will be preferred."
1320                                 "\n\nContinue?"), file);
1321                 int ret = frontend::Alert::prompt(_("Changes detected"),
1322                                 text, 0, 1, _("&Yes"), _("&No"), _("View &Log ..."));
1323                 if (ret == 2 ) {
1324                         dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
1325                         ret = frontend::Alert::prompt(_("Changes detected"),
1326                                 text, 0, 1, _("&Yes"), _("&No"));
1327                         hideDialogs("file", 0);
1328                 }
1329                 if (ret == 1 ) {
1330                         tmpf.erase();
1331                         return string();
1332                 }
1333         }
1334
1335         // Reverting looks too harsh, see bug #6255.
1336         // doVCCommand("svn revert -R " + quoteName(owner_->filePath())
1337         // + " > " + quoteName(tmpf.toFilesystemEncoding()),
1338         // FileName(owner_->filePath()));
1339         // res = "Revert log:\n" + tmpf.fileContents("UTF-8");
1340         doVCCommand("svn update --accept mine-full " + quoteName(owner_->filePath())
1341                 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1342                 FileName(owner_->filePath()));
1343         res += "Update log:\n" + tmpf.fileContents("UTF-8");
1344
1345         LYXERR(Debug::LYXVC, res);
1346         tmpf.erase();
1347         return to_utf8(res);
1348 }
1349
1350
1351 bool SVN::repoUpdateEnabled()
1352 {
1353         return true;
1354 }
1355
1356
1357 string SVN::lockingToggle()
1358 {
1359         FileName tmpf = FileName::tempName("lyxvcout");
1360         if (tmpf.empty()) {
1361                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1362                 return N_("Error: Could not generate logfile.");
1363         }
1364
1365         int ret = doVCCommand("svn proplist " + quoteName(onlyFileName(owner_->absFileName()))
1366                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1367                     FileName(owner_->filePath()));
1368         if (ret)
1369                 return string();
1370
1371         string log;
1372         string res = scanLogFile(tmpf, log);
1373         bool locking = contains(res, "svn:needs-lock");
1374         if (!locking)
1375                 ret = doVCCommand("svn propset svn:needs-lock ON "
1376                     + quoteName(onlyFileName(owner_->absFileName()))
1377                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1378                     FileName(owner_->filePath()));
1379         else
1380                 ret = doVCCommand("svn propdel svn:needs-lock "
1381                     + quoteName(onlyFileName(owner_->absFileName()))
1382                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1383                     FileName(owner_->filePath()));
1384         if (ret)
1385                 return string();
1386
1387         tmpf.erase();
1388         frontend::Alert::warning(_("VCN File Locking"),
1389                 (locking ? _("Locking property unset.") : _("Locking property set.")) + "\n"
1390                 + _("Do not forget to commit the locking property into the repository."),
1391                 true);
1392
1393         return string("SVN: ") +  N_("Locking property set.");
1394 }
1395
1396
1397 bool SVN::lockingToggleEnabled()
1398 {
1399         return true;
1400 }
1401
1402
1403 bool SVN::revert()
1404 {
1405         // Reverts to the version in SVN repository and
1406         // gets the updated version from the repository.
1407         string const fil = quoteName(onlyFileName(owner_->absFileName()));
1408
1409         if (doVCCommand("svn revert -q " + fil,
1410                     FileName(owner_->filePath())))
1411                 return false;
1412         owner_->markClean();
1413         return true;
1414 }
1415
1416
1417 bool SVN::isRevertWithConfirmation()
1418 {
1419         //FIXME owner && diff
1420         return true;
1421 }
1422
1423
1424 void SVN::undoLast()
1425 {
1426         // merge the current with the previous version
1427         // in a reverse patch kind of way, so that the
1428         // result is to revert the last changes.
1429         lyxerr << "Sorry, not implemented." << endl;
1430 }
1431
1432
1433 bool SVN::undoLastEnabled()
1434 {
1435         return false;
1436 }
1437
1438
1439 string SVN::revisionInfo(LyXVC::RevisionInfo const info)
1440 {
1441         if (info == LyXVC::Tree) {
1442                         if (rev_tree_cache_.empty())
1443                                 if (!getTreeRevisionInfo())
1444                                         rev_tree_cache_ = "?";
1445                         if (rev_tree_cache_ == "?")
1446                                 return string();
1447
1448                         return rev_tree_cache_;
1449         }
1450
1451         // fill the rest of the attributes for a single file
1452         if (rev_file_cache_.empty())
1453                 if (!getFileRevisionInfo())
1454                         rev_file_cache_ = "?";
1455
1456         switch (info) {
1457                 case LyXVC::File:
1458                         if (rev_file_cache_ == "?")
1459                                 return string();
1460                         return rev_file_cache_;
1461                 case LyXVC::Author:
1462                         return rev_author_cache_;
1463                 case LyXVC::Date:
1464                         return rev_date_cache_;
1465                 case LyXVC::Time:
1466                         return rev_time_cache_;
1467                 default: ;
1468
1469         }
1470
1471         return string();
1472 }
1473
1474
1475 bool SVN::getFileRevisionInfo()
1476 {
1477         FileName tmpf = FileName::tempName("lyxvcout");
1478         if (tmpf.empty()) {
1479                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1480                 return false;
1481         }
1482
1483         doVCCommand("svn info --xml " + quoteName(onlyFileName(owner_->absFileName()))
1484                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1485                     FileName(owner_->filePath()));
1486
1487         if (tmpf.empty())
1488                 return false;
1489
1490         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1491         string line;
1492         // commit log part
1493         bool c = false;
1494         string rev;
1495
1496         while (ifs) {
1497                 getline(ifs, line);
1498                 LYXERR(Debug::LYXVC, line);
1499                 if (prefixIs(line, "<commit"))
1500                         c = true;
1501                 if (c && prefixIs(line, "   revision=\"") && suffixIs(line, "\">")) {
1502                         string l1 = subst(line, "revision=\"", "");
1503                         string l2 = trim(subst(l1, "\">", ""));
1504                         if (isStrInt(l2))
1505                                 rev_file_cache_ = rev = l2;
1506                 }
1507                 if (c && prefixIs(line, "<author>") && suffixIs(line, "</author>")) {
1508                         string l1 = subst(line, "<author>", "");
1509                         string l2 = subst(l1, "</author>", "");
1510                         rev_author_cache_ = l2;
1511                 }
1512                 if (c && prefixIs(line, "<date>") && suffixIs(line, "</date>")) {
1513                         string l1 = subst(line, "<date>", "");
1514                         string l2 = subst(l1, "</date>", "");
1515                         l2 = split(l2, l1, 'T');
1516                         rev_date_cache_ = l1;
1517                         l2 = split(l2, l1, '.');
1518                         rev_time_cache_ = l1;
1519                 }
1520         }
1521
1522         ifs.close();
1523         tmpf.erase();
1524         return !rev.empty();
1525 }
1526
1527
1528 bool SVN::getTreeRevisionInfo()
1529 {
1530         FileName tmpf = FileName::tempName("lyxvcout");
1531         if (tmpf.empty()) {
1532                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1533                 return false;
1534         }
1535
1536         doVCCommand("svnversion -n . > " + quoteName(tmpf.toFilesystemEncoding()),
1537                     FileName(owner_->filePath()));
1538
1539         if (tmpf.empty())
1540                 return false;
1541
1542         // only first line in case something bad happens.
1543         ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1544         string line;
1545         getline(ifs, line);
1546         ifs.close();
1547         tmpf.erase();
1548
1549         rev_tree_cache_ = line;
1550         return !line.empty();
1551 }
1552
1553
1554 void SVN::getLog(FileName const & tmpf)
1555 {
1556         doVCCommand("svn log " + quoteName(onlyFileName(owner_->absFileName()))
1557                     + " > " + quoteName(tmpf.toFilesystemEncoding()),
1558                     FileName(owner_->filePath()));
1559 }
1560
1561
1562 bool SVN::prepareFileRevision(string const & revis, string & f)
1563 {
1564         if (!isStrInt(revis))
1565                 return false;
1566
1567         int rev = convert<int>(revis);
1568         if (rev <= 0)
1569                 if (!getFileRevisionInfo())
1570                         return false;
1571         if (rev == 0)
1572                 rev = convert<int>(rev_file_cache_);
1573         // go back for minus rev
1574         else if (rev < 0) {
1575                 rev = rev + convert<int>(rev_file_cache_);
1576                 if (rev < 1)
1577                         return false;
1578         }
1579
1580         string revname = convert<string>(rev);
1581         FileName tmpf = FileName::tempName("lyxvcrev_" + revname + "_");
1582         if (tmpf.empty()) {
1583                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1584                 return false;
1585         }
1586
1587         doVCCommand("svn cat -r " + revname + " "
1588                       + quoteName(onlyFileName(owner_->absFileName()))
1589                       + " > " + quoteName(tmpf.toFilesystemEncoding()),
1590                 FileName(owner_->filePath()));
1591         if (tmpf.isFileEmpty())
1592                 return false;
1593
1594         f = tmpf.absFileName();
1595         return true;
1596 }
1597
1598
1599 bool SVN::prepareFileRevisionEnabled()
1600 {
1601         return true;
1602 }
1603
1604
1605
1606 bool SVN::toggleReadOnlyEnabled()
1607 {
1608         return false;
1609 }
1610
1611
1612 } // namespace lyx