]> git.lyx.org Git - lyx.git/blob - src/VCBackend.cpp
We are completely user unfriendly.
[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  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "VCBackend.h"
14 #include "Buffer.h"
15
16 #include "frontends/alert.h"
17
18 #include "support/debug.h"
19 #include "support/filetools.h"
20 #include "support/gettext.h"
21 #include "support/lstrings.h"
22 #include "support/Path.h"
23 #include "support/Systemcall.h"
24
25 #include <boost/regex.hpp>
26
27 #include <fstream>
28
29 using namespace std;
30 using namespace lyx::support;
31
32 using boost::regex;
33 using boost::regex_match;
34 using boost::smatch;
35
36 namespace lyx {
37
38
39 int VCS::doVCCommand(string const & cmd, FileName const & path)
40 {
41         LYXERR(Debug::LYXVC, "doVCCommand: " << cmd);
42         Systemcall one;
43         support::PathChanger p(path);
44         int const ret = one.startscript(Systemcall::Wait, cmd);
45         if (ret)
46                 frontend::Alert::error(_("Revision control error."),
47                         _("Please check you have installed revision control program.") + "\n"
48                         + from_ascii(cmd));
49         return ret;
50 }
51
52
53 /////////////////////////////////////////////////////////////////////
54 //
55 // RCS
56 //
57 /////////////////////////////////////////////////////////////////////
58
59 RCS::RCS(FileName const & m)
60 {
61         master_ = m;
62         scanMaster();
63 }
64
65
66 FileName const RCS::findFile(FileName const & file)
67 {
68         // Check if *,v exists.
69         FileName tmp(file.absFilename() + ",v");
70         LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under rcs: " << tmp);
71         if (tmp.isReadableFile()) {
72                 LYXERR(Debug::LYXVC, "Yes " << file << " is under rcs.");
73                 return tmp;
74         }
75
76         // Check if RCS/*,v exists.
77         tmp = FileName(addName(addPath(onlyPath(file.absFilename()), "RCS"), file.absFilename()) + ",v");
78         LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under rcs: " << tmp);
79         if (tmp.isReadableFile()) {
80                 LYXERR(Debug::LYXVC, "Yes " << file << " it is under rcs.");
81                 return tmp;
82         }
83
84         return FileName();
85 }
86
87
88 void RCS::retrieve(FileName const & file)
89 {
90         LYXERR(Debug::LYXVC, "LyXVC::RCS: retrieve.\n\t" << file);
91         VCS::doVCCommand("co -q -r " + quoteName(file.toFilesystemEncoding()),
92                          FileName());
93 }
94
95
96 void RCS::scanMaster()
97 {
98         LYXERR(Debug::LYXVC, "LyXVC::RCS: scanMaster.");
99
100         ifstream ifs(master_.toFilesystemEncoding().c_str());
101
102         string token;
103         bool read_enough = false;
104
105         while (!read_enough && ifs >> token) {
106                 LYXERR(Debug::LYXVC, "LyXVC::scanMaster: current lex text: `"
107                         << token << '\'');
108
109                 if (token.empty())
110                         continue;
111                 else if (token == "head") {
112                         // get version here
113                         string tmv;
114                         ifs >> tmv;
115                         tmv = rtrim(tmv, ";");
116                         version_ = tmv;
117                         LYXERR(Debug::LYXVC, "LyXVC: version found to be " << tmv);
118                 } else if (contains(token, "access")
119                            || contains(token, "symbols")
120                            || contains(token, "strict")) {
121                         // nothing
122                 } else if (contains(token, "locks")) {
123                         // get locker here
124                         if (contains(token, ';')) {
125                                 locker_ = "Unlocked";
126                                 vcstatus = UNLOCKED;
127                                 continue;
128                         }
129                         string tmpt;
130                         string s1;
131                         string s2;
132                         do {
133                                 ifs >> tmpt;
134                                 s1 = rtrim(tmpt, ";");
135                                 // tmp is now in the format <user>:<version>
136                                 s1 = split(s1, s2, ':');
137                                 // s2 is user, and s1 is version
138                                 if (s1 == version_) {
139                                         locker_ = s2;
140                                         vcstatus = LOCKED;
141                                         break;
142                                 }
143                         } while (!contains(tmpt, ';'));
144
145                 } else if (token == "comment") {
146                         // we don't need to read any further than this.
147                         read_enough = true;
148                 } else {
149                         // unexpected
150                         LYXERR(Debug::LYXVC, "LyXVC::scanMaster(): unexpected token");
151                 }
152         }
153 }
154
155
156 void RCS::registrer(string const & msg)
157 {
158         string cmd = "ci -q -u -i -t-\"";
159         cmd += msg;
160         cmd += "\" ";
161         cmd += quoteName(onlyFilename(owner_->absFileName()));
162         doVCCommand(cmd, FileName(owner_->filePath()));
163 }
164
165
166 void RCS::checkIn(string const & msg)
167 {
168         doVCCommand("ci -q -u -m\"" + msg + "\" "
169                     + quoteName(onlyFilename(owner_->absFileName())),
170                     FileName(owner_->filePath()));
171 }
172
173 bool RCS::checkInEnabled()
174 {
175         return owner_ && !owner_->isReadonly();
176 }
177
178 void RCS::checkOut()
179 {
180         owner_->markClean();
181         doVCCommand("co -q -l " + quoteName(onlyFilename(owner_->absFileName())),
182                     FileName(owner_->filePath()));
183 }
184
185
186 bool RCS::checkOutEnabled()
187 {
188         return owner_ && owner_->isReadonly();
189 }
190
191
192 void RCS::revert()
193 {
194         doVCCommand("co -f -u" + version() + " "
195                     + quoteName(onlyFilename(owner_->absFileName())),
196                     FileName(owner_->filePath()));
197         // We ignore changes and just reload!
198         owner_->markClean();
199 }
200
201
202 void RCS::undoLast()
203 {
204         LYXERR(Debug::LYXVC, "LyXVC: undoLast");
205         doVCCommand("rcs -o" + version() + " "
206                     + quoteName(onlyFilename(owner_->absFileName())),
207                     FileName(owner_->filePath()));
208 }
209
210
211 bool RCS::undoLastEnabled()
212 {
213         return true;
214 }
215
216
217 void RCS::getLog(FileName const & tmpf)
218 {
219         doVCCommand("rlog " + quoteName(onlyFilename(owner_->absFileName()))
220                     + " > " + tmpf.toFilesystemEncoding(),
221                     FileName(owner_->filePath()));
222 }
223
224
225 /////////////////////////////////////////////////////////////////////
226 //
227 // CVS
228 //
229 /////////////////////////////////////////////////////////////////////
230
231 CVS::CVS(FileName const & m, FileName const & f)
232 {
233         master_ = m;
234         file_ = f;
235         scanMaster();
236 }
237
238
239 FileName const CVS::findFile(FileName const & file)
240 {
241         // First we look for the CVS/Entries in the same dir
242         // where we have file.
243         FileName const entries(onlyPath(file.absFilename()) + "/CVS/Entries");
244         string const tmpf = '/' + onlyFilename(file.absFilename()) + '/';
245         LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under cvs in `" << entries
246                              << "' for `" << tmpf << '\'');
247         if (entries.isReadableFile()) {
248                 // Ok we are at least in a CVS dir. Parse the CVS/Entries
249                 // and see if we can find this file. We do a fast and
250                 // dirty parse here.
251                 ifstream ifs(entries.toFilesystemEncoding().c_str());
252                 string line;
253                 while (getline(ifs, line)) {
254                         LYXERR(Debug::LYXVC, "\tEntries: " << line);
255                         if (contains(line, tmpf))
256                                 return entries;
257                 }
258         }
259         return FileName();
260 }
261
262
263 void CVS::scanMaster()
264 {
265         LYXERR(Debug::LYXVC, "LyXVC::CVS: scanMaster. \n     Checking: " << master_);
266         // Ok now we do the real scan...
267         ifstream ifs(master_.toFilesystemEncoding().c_str());
268         string tmpf = '/' + onlyFilename(file_.absFilename()) + '/';
269         LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\'');
270         string line;
271         static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)");
272         while (getline(ifs, line)) {
273                 LYXERR(Debug::LYXVC, "\t  line: " << line);
274                 if (contains(line, tmpf)) {
275                         // Ok extract the fields.
276                         smatch sm;
277
278                         regex_match(line, sm, reg);
279
280                         //sm[0]; // whole matched string
281                         //sm[1]; // filename
282                         version_ = sm.str(2);
283                         string const file_date = sm.str(3);
284
285                         //sm[4]; // options
286                         //sm[5]; // tag or tagdate
287                         // FIXME: must double check file is stattable/existing
288                         time_t mod = file_.lastModified();
289                         string mod_date = rtrim(asctime(gmtime(&mod)), "\n");
290                         LYXERR(Debug::LYXVC, "Date in Entries: `" << file_date
291                                 << "'\nModification date of file: `" << mod_date << '\'');
292                         //FIXME this whole locking bussiness is not working under cvs and the machinery
293                         // conforms to the ci usage, not cvs.
294                         if (file_date == mod_date) {
295                                 locker_ = "Unlocked";
296                                 vcstatus = UNLOCKED;
297                         } else {
298                                 // Here we should also to some more checking
299                                 // to see if there are conflicts or not.
300                                 locker_ = "Locked";
301                                 vcstatus = LOCKED;
302                         }
303                         break;
304                 }
305         }
306 }
307
308
309 void CVS::registrer(string const & msg)
310 {
311         doVCCommand("cvs -q add -m \"" + msg + "\" "
312                     + quoteName(onlyFilename(owner_->absFileName())),
313                     FileName(owner_->filePath()));
314 }
315
316
317 void CVS::checkIn(string const & msg)
318 {
319         doVCCommand("cvs -q commit -m \"" + msg + "\" "
320                     + quoteName(onlyFilename(owner_->absFileName())),
321                     FileName(owner_->filePath()));
322 }
323
324
325 bool CVS::checkInEnabled()
326 {
327         return true;
328 }
329
330
331 void CVS::checkOut()
332 {
333         // cvs update or perhaps for cvs this should be a noop
334         lyxerr << "Sorry not implemented." << endl;
335 }
336
337
338 bool CVS::checkOutEnabled()
339 {
340         return false;
341 }
342
343
344 void CVS::revert()
345 {
346         // This is sensitive operation, so at lest some check before
347         if (doVCCommand("cvs --help", FileName(owner_->filePath())))
348                 return;
349         // Reverts to the version in CVS repository and
350         // gets the updated version from the repository.
351         string const fil = quoteName(onlyFilename(owner_->absFileName()));
352
353         doVCCommand("rm -f " + fil + "; cvs update " + fil,
354                     FileName(owner_->filePath()));
355         owner_->markClean();
356 }
357
358
359 void CVS::undoLast()
360 {
361         // merge the current with the previous version
362         // in a reverse patch kind of way, so that the
363         // result is to revert the last changes.
364         lyxerr << "Sorry not implemented." << endl;
365 }
366
367
368 bool CVS::undoLastEnabled()
369 {
370         return false;
371 }
372
373
374 void CVS::getLog(FileName const & tmpf)
375 {
376         doVCCommand("cvs log " + quoteName(onlyFilename(owner_->absFileName()))
377                     + " > " + tmpf.toFilesystemEncoding(),
378                     FileName(owner_->filePath()));
379 }
380
381
382 } // namespace lyx