comparison lisp/vc-rcs.el @ 31383:860d7ac182e3

(vc-rcs-show-log-entry): New function. (vc-rcs-checkin, vc-rcs-checkout): Don't set all properties. (vc-rcs-register): If there is no RCS subdir, ask the user whether to create one. (vc-rcs-state-heuristic): Use file-ownership-preserved-p. (vc-rcs-checkout): Remove the error-handling for missing-rcs. (vc-rcs-state-heuristic): Don't use file-writable-p. (vc-rcs-print-log): Insert in the current buffer. (vc-rcs-diff): Insert in the current buffer and remove unused arg CMP. (vc-rcs-workfile-unchanged-p): Use vc-do-command instead of vc-simple-command. (vc-rcs-fetch-master-state): Removed check for unlocked-changes to avoid doing a diff when opening a file. (vc-rcs-state): Added check for unlocked-changes. (vc-rcs-header): Escape Id. (vc-rcs-workfile-unchanged-p): Remove optional arg VERSION. (vc-rcs-state): Call vc-workfile-unchanged-p, not the RCS-specific version. (vc-rcs-state-heuristic): Use file-writable-p instead of comparing userids. (vc-rcs-fetch-master-state): Handle the case where rcs is missing. Simplify the logic by eliminating unreachable code. (vc-rcs-diff): Only pass `2' to vc-do-command if necessary and just do a recursive call if we need to retry. (vc-rcs-checkout): Handle the case where rcs is missing by making the buffer read-write if requested and re-signalling the error. (vc-rcs-find-most-recent-rev): New function. The code derives from the old vc-parse-buffer but uses the revision number rather than the date (much easier to compare robustly). (vc-rcs-fetch-master-state): Use `with-temp-buffer'. Adapt to the new vc-parse-buffer (and vc-rcs-find-most-recent-rev). Find the locking-user more directly. Check strict locking and set checkout-model appropriately. (vc-rcs-parse-locks): Remove. (vc-rcs-latest-on-branch-p): Use with-temp-buffer and adapt to the new vc-parse-buffer (and vc-rcs-find-most-recent-rev). (vc-rcs-system-release): Use with-current-buffer and vc-parse-buffer. (vc-rcs-register, vc-rcs-checkout): Use with-current-buffer. Merge in code from vc-rcs-hooks.el. Don't require 'vc anymore. (vc-rcs-responsible-p): Use expand-file-name instead of concat and file-directory-p instead of file-exists-p. (vc-rcs-exists): Remove. (vc-rcs-header): New var. Update Copyright. (vc-rcs-rename-file): New function. (vc-rcs-diff): Remove unused `backend' variable. (vc-rcs-clear-headers): New function; code moved here from vc-clear-headers in vc.el. (tail): Provide vc-rcs and remove vc-rcs-logentry-check. (vc-rcs-register): Parse command output to find master file name and workfile version. (vc-rcs-checkout): Removed call to vc-file-clear-masterprops. Require vc and vc-rcs-hooks. (vc-rcs-trunk-p, vc-rcs-branch-part): Move to vc-rcs-hooks. (vc-rcs-backend-release-p): Remove (use vc-rcs-release-p). (vc-release-greater-or-equal-p): Move from vc. (vc-rcs-trunk-p, vc-rcs-branch-p, vc-rcs-branch-part, vc-rcs-minor-part, vc-rcs-previous-version): Remove duplicates. (vc-rcs-checkout): Add a missing `new-version' argument in the call to vc-rcs-latest-on-branch-p. Hopefully that was the right one. (vc-rcs-steal-lock): Renamed from `vc-rcs-steal'. Updated everything to use `vc-checkout-model'. (vc-rcs-backend-release-p): function added. other stuff updated to reference this function instead of the old `vc-backend-release-p'. (vc-rcs-logentry-check): Function added. (vc-rcs-checkin, vc-rcs-previous-version) (vc-rcs-checkout): Name space cleaned up. No more revision number crunching function names that are not prefixed with vc-rcs. (vc-rcs-checkout-model): Function added. References to `vc-checkout-model' replaced. (vc-rcs-admin): Added the query-only option as required by the vc.el file. (vc-rcs-exists): Function added. (vc-*-checkout): Use with-temp-file instead of /bin/sh. Merged from mainline (vc-rcs-latest-on-branch-p): Moved to vc-rcs-hooks.el. (vc-rcs-latest-on-branch-p, vc-rcs-trunk-p) (vc-rcs-branch-p, vc-rcs-branch-part, vc-rcs-minor-part) (vc-rcs-previous-version): Functions added. (vc-rcs-diff): Function added. (vc-rcs-checkout) Bug (typo) found and fixed. (vc-rcs-register-switches) Variable `vc-rcs-register-switches' added. Require vc when compiling. (vc-rcs-print-log, vc-rcs-assign-name, vc-rcs-merge) (vc-rcs-check-headers, vc-rcs-steal, vc-rcs-uncheck, vc-rcs-revert) (vc-rcs-checkin): New functions (code from vc.el). (vc-rcs-previous-version, vc-rcs-system-release, vc-rcs-checkout): Doc fix. (vc-rcs-release): Deleted. (Duplicated vc-rcs-system-release). (vc-rcs-trunk-p, vc-rcs-branch-p, vc-rcs-branch-part) (vc-rcs-minor-part, vc-rcs-previous-version, vc-rcs-release) (vc-rcs-release-p, vc-rcs-admin, vc-rcs-checkout): New functions from vc.el. (vc-rcs-system-release): Renamed from vc-rcs-backend-release.
author Gerd Moellmann <gerd@gnu.org>
date Mon, 04 Sep 2000 19:47:43 +0000
parents
children f2ab9420390f
comparison
equal deleted inserted replaced
31382:cde9770b21e0 31383:860d7ac182e3
1 ;;; vc-rcs.el --- support for RCS version-control
2
3 ;; Copyright (C) 1992,93,94,95,96,97,98,99,2000 Free Software Foundation, Inc.
4
5 ;; Author: FSF (see vc.el for full credits)
6 ;; Maintainer: Andre Spiegel <spiegel@gnu.org>
7
8 ;; $Id: vc-rcs.el,v 1.36 2000/08/12 18:51:30 spiegel Exp $
9
10 ;; This file is part of GNU Emacs.
11
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26
27 ;;; Commentary: see vc.el
28
29 ;;; Code:
30
31 (defcustom vc-rcs-release nil
32 "*The release number of your RCS installation, as a string.
33 If nil, VC itself computes this value when it is first needed."
34 :type '(choice (const :tag "Auto" nil)
35 (string :tag "Specified")
36 (const :tag "Unknown" unknown))
37 :group 'vc)
38
39 (defcustom vc-rcs-register-switches nil
40 "*A string or list of strings; extra switches for registering a file
41 in RCS. These are passed to the checkin program by
42 \\[vc-rcs-register]."
43 :type '(choice (const :tag "None" nil)
44 (string :tag "Argument String")
45 (repeat :tag "Argument List"
46 :value ("")
47 string))
48 :group 'vc)
49
50 (defcustom vc-rcs-checkin-switches nil
51 "*A string or list of strings specifying extra switches for RCS checkin.
52 These are passed to the checkin program by \\[vc-rcs-checkin]."
53 :type '(choice (const :tag "None" nil)
54 (string :tag "Argument String")
55 (repeat :tag "Argument List"
56 :value ("")
57 string))
58 :group 'vc)
59
60 (defcustom vc-rcs-checkout-switches nil
61 "*A string or list of strings specifying extra switches for RCS checkout.
62 These are passed to the checkout program by \\[vc-rcs-checkout]."
63 :type '(choice (const :tag "None" nil)
64 (string :tag "Argument String")
65 (repeat :tag "Argument List"
66 :value ("")
67 string))
68 :group 'vc)
69
70 (defcustom vc-rcs-header (or (cdr (assoc 'RCS vc-header-alist)) '("\$Id\$"))
71 "*Header keywords to be inserted by `vc-insert-headers'."
72 :type 'string
73 :group 'vc)
74
75 (defcustom vc-rcsdiff-knows-brief nil
76 "*Indicates whether rcsdiff understands the --brief option.
77 The value is either `yes', `no', or nil. If it is nil, VC tries
78 to use --brief and sets this variable to remember whether it worked."
79 :type '(choice (const :tag "Work out" nil) (const yes) (const no))
80 :group 'vc)
81
82 ;;;###autoload
83 (defcustom vc-rcs-master-templates
84 '("%sRCS/%s,v" "%s%s,v" "%sRCS/%s")
85 "*Where to look for RCS master files.
86 For a description of possible values, see `vc-check-master-templates'."
87 :type '(choice (const :tag "Use standard RCS file names"
88 '("%sRCS/%s,v" "%s%s,v" "%sRCS/%s"))
89 (repeat :tag "User-specified"
90 (choice string
91 function)))
92 :version "20.5"
93 :group 'vc)
94
95 ;;;###autoload
96 (progn (defun vc-rcs-registered (f) (vc-default-registered 'RCS f)))
97
98 (defun vc-rcs-state (file)
99 "Implementation of `vc-state' for RCS."
100 (or (boundp 'vc-rcs-headers-result)
101 (and vc-consult-headers
102 (vc-rcs-consult-headers file)))
103 (let ((state
104 ;; vc-workfile-version might not be known; in that case the
105 ;; property is nil. vc-rcs-fetch-master-state knows how to
106 ;; handle that.
107 (vc-rcs-fetch-master-state file
108 (vc-file-getprop file
109 'vc-workfile-version))))
110 (if (eq state 'up-to-date)
111 (if (vc-workfile-unchanged-p file)
112 'up-to-date
113 'unlocked-changes)
114 state)))
115
116 (defun vc-rcs-state-heuristic (file)
117 "State heuristic for RCS."
118 (let (vc-rcs-headers-result)
119 (if (and vc-consult-headers
120 (setq vc-rcs-headers-result
121 (vc-rcs-consult-headers file))
122 (eq vc-rcs-headers-result 'rev-and-lock))
123 (let ((state (vc-file-getprop file 'vc-state)))
124 ;; If the headers say that the file is not locked, the
125 ;; permissions can tell us whether locking is used for
126 ;; the file or not.
127 (if (and (eq state 'up-to-date)
128 (not (vc-mistrust-permissions file)))
129 (cond
130 ((string-match ".rw..-..-." (nth 8 (file-attributes file)))
131 (vc-file-setprop file 'vc-checkout-model 'implicit))
132 ((string-match ".r-..-..-." (nth 8 (file-attributes file)))
133 (vc-file-setprop file 'vc-checkout-model 'locking))))
134 state)
135 (if (not (vc-mistrust-permissions file))
136 (let* ((attributes (file-attributes file))
137 (owner-uid (nth 2 attributes))
138 (permissions (nth 8 attributes)))
139 (cond ((string-match ".r-..-..-." permissions)
140 (vc-file-setprop file 'vc-checkout-model 'locking)
141 'up-to-date)
142 ((string-match ".rw..-..-." permissions)
143 (if (file-ownership-preserved-p file)
144 'edited
145 (vc-user-login-name owner-uid)))
146 (t
147 ;; Strange permissions. Fall through to
148 ;; expensive state computation.
149 (vc-rcs-state file))))
150 (vc-rcs-state file)))))
151
152 (defun vc-rcs-workfile-version (file)
153 "RCS-specific version of `vc-workfile-version'."
154 (or (and vc-consult-headers
155 (vc-rcs-consult-headers file)
156 (vc-file-getprop file 'vc-workfile-version))
157 (progn
158 (vc-rcs-fetch-master-state file)
159 (vc-file-getprop file 'vc-workfile-version))))
160
161 (defun vc-rcs-checkout-model (file)
162 "RCS-specific version of `vc-checkout-model'."
163 (vc-rcs-consult-headers file)
164 (or (vc-file-getprop file 'vc-checkout-model)
165 (progn (vc-rcs-fetch-master-state file)
166 (vc-file-getprop file 'vc-checkout-model))))
167
168 ;;; internal code
169
170 (defun vc-rcs-find-most-recent-rev (branch)
171 "Find most recent revision on BRANCH."
172 (goto-char (point-min))
173 (let ((latest-rev -1) value)
174 (while (re-search-forward (concat "^\\(" (regexp-quote branch)
175 "\\.\\([0-9]+\\)\\)\ndate[ \t]+[0-9.]+;")
176 nil t)
177 (let ((rev (string-to-number (match-string 2))))
178 (when (< latest-rev rev)
179 (setq latest-rev rev)
180 (setq value (match-string 1)))))
181 value))
182
183 (defun vc-rcs-fetch-master-state (file &optional workfile-version)
184 "Compute the master file's idea of the state of FILE. If a
185 WORKFILE-VERSION is given, compute the state of that version,
186 otherwise determine the workfile version based on the master file.
187 This function sets the properties `vc-workfile-version' and
188 `vc-checkout-model' to their correct values, based on the master
189 file."
190 (with-temp-buffer
191 (vc-insert-file (vc-name file) "^[0-9]")
192 (let ((workfile-is-latest nil))
193 (unless workfile-version
194 (let ((default-branch (vc-parse-buffer "^branch[ \t\n]+\\([^;]*\\);" 1)))
195 ;; Workfile version not known yet. Determine that first. It
196 ;; is either the head of the trunk, the head of the default
197 ;; branch, or the "default branch" itself, if that is a full
198 ;; revision number.
199 (cond
200 ;; no default branch
201 ((or (not default-branch) (string= "" default-branch))
202 (setq workfile-version
203 (vc-parse-buffer "^head[ \t\n]+\\([^;]+\\);" 1))
204 (setq workfile-is-latest t))
205 ;; default branch is actually a revision
206 ((string-match "^[0-9]+\\.[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*$"
207 default-branch)
208 (setq workfile-version default-branch))
209 ;; else, search for the head of the default branch
210 (t (vc-insert-file (vc-name file) "^desc")
211 (setq workfile-version
212 (vc-rcs-find-most-recent-rev default-branch))
213 (setq workfile-is-latest t)))
214 (vc-file-setprop file 'vc-workfile-version workfile-version)))
215 ;; Check strict locking
216 (goto-char (point-min))
217 (vc-file-setprop file 'vc-checkout-model
218 (if (re-search-forward ";[ \t\n]*strict;" nil t)
219 'locking 'implicit))
220 ;; Compute state of workfile version
221 (goto-char (point-min))
222 (let ((locking-user
223 (vc-parse-buffer (concat "^locks[ \t\n]+[^;]*[ \t\n]+\\([^:]+\\):"
224 (regexp-quote workfile-version)
225 "[^0-9.]")
226 1)))
227 (cond
228 ;; not locked
229 ((not locking-user)
230 (if (or workfile-is-latest
231 (vc-rcs-latest-on-branch-p file workfile-version))
232 ;; workfile version is latest on branch
233 'up-to-date
234 ;; workfile version is not latest on branch
235 'needs-patch))
236 ;; locked by the calling user
237 ((and (stringp locking-user)
238 (string= locking-user (vc-user-login-name)))
239 (if (or (eq (vc-checkout-model file) 'locking)
240 workfile-is-latest
241 (vc-rcs-latest-on-branch-p file workfile-version))
242 'edited
243 ;; Locking is not used for the file, but the owner does
244 ;; have a lock, and there is a higher version on the current
245 ;; branch. Not sure if this can occur, and if it is right
246 ;; to use `needs-merge' in this case.
247 'needs-merge))
248 ;; locked by somebody else
249 ((stringp locking-user)
250 locking-user)
251 (t
252 (error "Error getting state of RCS file")))))))
253
254 (defun vc-rcs-consult-headers (file)
255 "Search for RCS headers in FILE, and set properties accordingly.
256
257 Returns: nil if no headers were found
258 'rev if a workfile revision was found
259 'rev-and-lock if revision and lock info was found"
260 (cond
261 ((not (get-file-buffer file)) nil)
262 ((let (status version locking-user)
263 (save-excursion
264 (set-buffer (get-file-buffer file))
265 (goto-char (point-min))
266 (cond
267 ;; search for $Id or $Header
268 ;; -------------------------
269 ;; The `\ 's below avoid an RCS 5.7 bug when checking in this file.
270 ((or (and (search-forward "$Id\ : " nil t)
271 (looking-at "[^ ]+ \\([0-9.]+\\) "))
272 (and (progn (goto-char (point-min))
273 (search-forward "$Header\ : " nil t))
274 (looking-at "[^ ]+ \\([0-9.]+\\) ")))
275 (goto-char (match-end 0))
276 ;; if found, store the revision number ...
277 (setq version (match-string-no-properties 1))
278 ;; ... and check for the locking state
279 (cond
280 ((looking-at
281 (concat "[0-9]+[/-][01][0-9][/-][0-3][0-9] " ; date
282 "[0-2][0-9]:[0-5][0-9]+:[0-6][0-9]+\\([+-][0-9:]+\\)? " ; time
283 "[^ ]+ [^ ]+ ")) ; author & state
284 (goto-char (match-end 0)) ; [0-6] in regexp handles leap seconds
285 (cond
286 ;; unlocked revision
287 ((looking-at "\\$")
288 (setq locking-user 'none)
289 (setq status 'rev-and-lock))
290 ;; revision is locked by some user
291 ((looking-at "\\([^ ]+\\) \\$")
292 (setq locking-user (match-string-no-properties 1))
293 (setq status 'rev-and-lock))
294 ;; everything else: false
295 (nil)))
296 ;; unexpected information in
297 ;; keyword string --> quit
298 (nil)))
299 ;; search for $Revision
300 ;; --------------------
301 ((re-search-forward (concat "\\$"
302 "Revision: \\([0-9.]+\\) \\$")
303 nil t)
304 ;; if found, store the revision number ...
305 (setq version (match-string-no-properties 1))
306 ;; and see if there's any lock information
307 (goto-char (point-min))
308 (if (re-search-forward (concat "\\$" "Locker:") nil t)
309 (cond ((looking-at " \\([^ ]+\\) \\$")
310 (setq locking-user (match-string-no-properties 1))
311 (setq status 'rev-and-lock))
312 ((looking-at " *\\$")
313 (setq locking-user 'none)
314 (setq status 'rev-and-lock))
315 (t
316 (setq locking-user 'none)
317 (setq status 'rev-and-lock)))
318 (setq status 'rev)))
319 ;; else: nothing found
320 ;; -------------------
321 (t nil)))
322 (if status (vc-file-setprop file 'vc-workfile-version version))
323 (and (eq status 'rev-and-lock)
324 (vc-file-setprop file 'vc-state
325 (cond
326 ((eq locking-user 'none) 'up-to-date)
327 ((string= locking-user (vc-user-login-name)) 'edited)
328 (t locking-user)))
329 ;; If the file has headers, we don't want to query the
330 ;; master file, because that would eliminate all the
331 ;; performance gain the headers brought us. We therefore
332 ;; use a heuristic now to find out whether locking is used
333 ;; for this file. If we trust the file permissions, and the
334 ;; file is not locked, then if the file is read-only we
335 ;; assume that locking is used for the file, otherwise
336 ;; locking is not used.
337 (not (vc-mistrust-permissions file))
338 (vc-up-to-date-p file)
339 (if (string-match ".r-..-..-." (nth 8 (file-attributes file)))
340 (vc-file-setprop file 'vc-checkout-model 'locking)
341 (vc-file-setprop file 'vc-checkout-model 'implicit)))
342 status))))
343
344 (defun vc-rcs-workfile-unchanged-p (file)
345 "RCS-specific implementation of vc-workfile-unchanged-p."
346 ;; Try to use rcsdiff --brief. If rcsdiff does not understand that,
347 ;; do a double take and remember the fact for the future
348 (let* ((version (concat "-r" (vc-workfile-version file)))
349 (status (if (eq vc-rcsdiff-knows-brief 'no)
350 (vc-do-command nil 1 "rcsdiff" file version)
351 (vc-do-command nil 2 "rcsdiff" file "--brief" version))))
352 (if (eq status 2)
353 (if (not vc-rcsdiff-knows-brief)
354 (setq vc-rcsdiff-knows-brief 'no
355 status (vc-do-command nil 1 "rcsdiff" file version))
356 (error "rcsdiff failed"))
357 (if (not vc-rcsdiff-knows-brief) (setq vc-rcsdiff-knows-brief 'yes)))
358 ;; The workfile is unchanged if rcsdiff found no differences.
359 (zerop status)))
360
361 (defun vc-rcs-trunk-p (rev)
362 "Return t if REV is an RCS revision on the trunk."
363 (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
364
365 (defun vc-rcs-branch-part (rev)
366 "Return the branch part of an RCS revision number REV"
367 (substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
368
369 (defun vc-rcs-latest-on-branch-p (file &optional version)
370 "Return non-nil if workfile version of FILE is the latest on its branch.
371 When VERSION is given, perform check for that version."
372 (unless version (setq version (vc-workfile-version file)))
373 (with-temp-buffer
374 (string= version
375 (if (vc-rcs-trunk-p version)
376 (progn
377 ;; Compare VERSION to the head version number.
378 (vc-insert-file (vc-name file) "^[0-9]")
379 (vc-parse-buffer "^head[ \t\n]+\\([^;]+\\);" 1))
380 ;; If we are not on the trunk, we need to examine the
381 ;; whole current branch.
382 (vc-insert-file (vc-name file) "^desc")
383 (vc-rcs-find-most-recent-rev (vc-rcs-branch-part version))))))
384
385 (defun vc-rcs-branch-p (rev)
386 "Return t if REV is an RCS branch revision"
387 (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
388
389 (defun vc-rcs-minor-part (rev)
390 "Return the minor version number of an RCS revision number REV."
391 (string-match "[0-9]+\\'" rev)
392 (substring rev (match-beginning 0) (match-end 0)))
393
394 (defun vc-rcs-previous-version (rev)
395 "Guess the previous RCS version number"
396 (let ((branch (vc-rcs-branch-part rev))
397 (minor-num (string-to-number (vc-rcs-minor-part rev))))
398 (if (> minor-num 1)
399 ;; version does probably not start a branch or release
400 (concat branch "." (number-to-string (1- minor-num)))
401 (if (vc-rcs-trunk-p rev)
402 ;; we are at the beginning of the trunk --
403 ;; don't know anything to return here
404 ""
405 ;; we are at the beginning of a branch --
406 ;; return version of starting point
407 (vc-rcs-branch-part branch)))))
408
409 (defun vc-rcs-print-log (file)
410 "Get change log associated with FILE."
411 (vc-do-command t 0 "rlog" (vc-name file)))
412
413 (defun vc-rcs-show-log-entry (version)
414 (when (re-search-forward
415 ;; also match some context, for safety
416 (concat "----\nrevision " version
417 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
418 ;; set the display window so that
419 ;; the whole log entry is displayed
420 (let (start end lines)
421 (beginning-of-line) (forward-line -1) (setq start (point))
422 (if (not (re-search-forward "^----*\nrevision" nil t))
423 (setq end (point-max))
424 (beginning-of-line) (forward-line -1) (setq end (point)))
425 (setq lines (count-lines start end))
426 (cond
427 ;; if the global information and this log entry fit
428 ;; into the window, display from the beginning
429 ((< (count-lines (point-min) end) (window-height))
430 (goto-char (point-min))
431 (recenter 0)
432 (goto-char start))
433 ;; if the whole entry fits into the window,
434 ;; display it centered
435 ((< (1+ lines) (window-height))
436 (goto-char start)
437 (recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
438 ;; otherwise (the entry is too large for the window),
439 ;; display from the start
440 (t
441 (goto-char start)
442 (recenter 0))))))
443
444 (defun vc-rcs-assign-name (file name)
445 "Assign to FILE's latest version a given NAME."
446 (vc-do-command nil 0 "rcs" (vc-name file) (concat "-n" name ":")))
447
448 (defun vc-rcs-merge (file first-version &optional second-version)
449 "Merge changes into current working copy of FILE.
450 The changes are between FIRST-VERSION and SECOND-VERSION."
451 (vc-do-command nil 1 "rcsmerge" (vc-name file)
452 "-kk" ; ignore keyword conflicts
453 (concat "-r" first-version)
454 (if second-version (concat "-r" second-version))))
455
456 (defun vc-rcs-check-headers ()
457 "Check if the current file has any headers in it."
458 (save-excursion
459 (goto-char (point-min))
460 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
461 \\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
462
463 (defun vc-rcs-clear-headers ()
464 "Implementation of vc-clear-headers for RCS."
465 (let ((case-fold-search nil))
466 (goto-char (point-min))
467 (while (re-search-forward
468 (concat "\\$\\(Author\\|Date\\|Header\\|Id\\|Locker\\|Name\\|"
469 "RCSfile\\|Revision\\|Source\\|State\\): [^$\n]+\\$")
470 nil t)
471 (replace-match "$\\1$"))))
472
473 (defun vc-rcs-steal-lock (file &optional rev)
474 "Steal the lock on the current workfile for FILE and revision REV.
475 Needs RCS 5.6.2 or later for -M."
476 (vc-do-command nil 0 "rcs" (vc-name file) "-M"
477 (concat "-u" rev) (concat "-l" rev)))
478
479 (defun vc-rcs-uncheck (file target)
480 "Undo the checkin of FILE's revision TARGET."
481 (vc-do-command nil 0 "rcs" (vc-name file) (concat "-o" target)))
482
483 (defun vc-rcs-revert (file)
484 "Revert FILE to the version it was based on."
485 (vc-do-command nil 0 "co" (vc-name file) "-f"
486 (concat "-u" (vc-workfile-version file))))
487
488 (defun vc-rcs-rename-file (old new)
489 ;; Just move the master file (using vc-rcs-master-templates).
490 (vc-rename-master (vc-name old) new vc-rcs-master-templates))
491
492 (defun vc-release-greater-or-equal (r1 r2)
493 "Compare release numbers, represented as strings. Release
494 components are assumed cardinal numbers, not decimal fractions \(5.10
495 is a higher release than 5.9\). Omitted fields are considered lower
496 \(5.6.7 is earlier than 5.6.7.1\). Comparison runs till the end of
497 the string is found, or a non-numeric component shows up \(5.6.7 is
498 earlier than \"5.6.7 beta\", which is probably not what you want in
499 some cases\). This code is suitable for existing RCS release numbers.
500 CVS releases are handled reasonably, too \(1.3 < 1.4* < 1.5\)."
501 (let (v1 v2 i1 i2)
502 (catch 'done
503 (or (and (string-match "^\\.?\\([0-9]+\\)" r1)
504 (setq i1 (match-end 0))
505 (setq v1 (string-to-number (match-string 1 r1)))
506 (or (and (string-match "^\\.?\\([0-9]+\\)" r2)
507 (setq i2 (match-end 0))
508 (setq v2 (string-to-number (match-string 1 r2)))
509 (if (> v1 v2) (throw 'done t)
510 (if (< v1 v2) (throw 'done nil)
511 (throw 'done
512 (vc-release-greater-or-equal
513 (substring r1 i1)
514 (substring r2 i2)))))))
515 (throw 'done t)))
516 (or (and (string-match "^\\.?\\([0-9]+\\)" r2)
517 (throw 'done nil))
518 (throw 'done t)))))
519
520 (defun vc-rcs-release-p (release)
521 "Return t if we have RELEASE or better"
522 (let ((installation (vc-rcs-system-release)))
523 (if (and installation
524 (not (eq installation 'unknown)))
525 (vc-release-greater-or-equal installation release))))
526
527 (defun vc-rcs-checkin (file rev comment)
528 "RCS-specific version of `vc-backend-checkin'."
529 ;; Adaptation for RCS branch support: if this is an explicit checkin,
530 ;; or if the checkin creates a new branch, set the master file branch
531 ;; accordingly.
532 (let ((switches (if (stringp vc-checkin-switches)
533 (list vc-checkin-switches)
534 vc-checkin-switches)))
535 (let ((old-version (vc-workfile-version file)) new-version)
536 (apply 'vc-do-command nil 0 "ci" (vc-name file)
537 ;; if available, use the secure check-in option
538 (and (vc-rcs-release-p "5.6.4") "-j")
539 (concat (if vc-keep-workfiles "-u" "-r") rev)
540 (concat "-m" comment)
541 switches)
542 (vc-file-setprop file 'vc-workfile-version nil)
543
544 ;; determine the new workfile version
545 (set-buffer "*vc*")
546 (goto-char (point-min))
547 (when (or (re-search-forward
548 "new revision: \\([0-9.]+\\);" nil t)
549 (re-search-forward
550 "reverting to previous revision \\([0-9.]+\\)" nil t))
551 (setq new-version (match-string 1))
552 (vc-file-setprop file 'vc-workfile-version new-version))
553
554 ;; if we got to a different branch, adjust the default
555 ;; branch accordingly
556 (cond
557 ((and old-version new-version
558 (not (string= (vc-rcs-branch-part old-version)
559 (vc-rcs-branch-part new-version))))
560 (vc-do-command nil 0 "rcs" (vc-name file)
561 (if (vc-rcs-trunk-p new-version) "-b"
562 (concat "-b" (vc-rcs-branch-part new-version))))
563 ;; If this is an old RCS release, we might have
564 ;; to remove a remaining lock.
565 (if (not (vc-rcs-release-p "5.6.2"))
566 ;; exit status of 1 is also accepted.
567 ;; It means that the lock was removed before.
568 (vc-do-command nil 1 "rcs" (vc-name file)
569 (concat "-u" old-version))))))))
570
571 (defun vc-rcs-system-release ()
572 "Return the RCS release installed on this system, as a string.
573 Return symbol UNKNOWN if the release cannot be deducted. The user can
574 override this using variable `vc-rcs-release'.
575
576 If the user has not set variable `vc-rcs-release' and it is nil,
577 variable `vc-rcs-release' is set to the returned value."
578 (or vc-rcs-release
579 (setq vc-rcs-release
580 (or (and (zerop (vc-do-command nil nil "rcs" nil "-V"))
581 (with-current-buffer (get-buffer "*vc*")
582 (vc-parse-buffer "^RCS version \\([0-9.]+ *.*\\)" 1)))
583 'unknown))))
584
585 (defun vc-rcs-diff (file &optional oldvers newvers)
586 "Get a difference report using RCS between two versions of FILE."
587 (if (not oldvers) (setq oldvers (vc-workfile-version file)))
588 ;; If we know that --brief is not supported, don't try it.
589 (let* ((diff-switches-list (if (listp diff-switches)
590 diff-switches
591 (list diff-switches)))
592 (options (append (list "-q"
593 (concat "-r" oldvers)
594 (and newvers (concat "-r" newvers)))
595 diff-switches-list)))
596 (apply 'vc-do-command t 1 "rcsdiff" file options)))
597
598 (defun vc-rcs-responsible-p (file)
599 "Return non-nil if RCS thinks it would be responsible for registering FILE."
600 ;; TODO: check for all the patterns in vc-rcs-master-templates
601 (file-directory-p (expand-file-name "RCS" (file-name-directory file))))
602
603 (defun vc-rcs-register (file &optional rev comment)
604 "Register FILE into the RCS version-control system.
605 REV is the optional revision number for the file. COMMENT can be used
606 to provide an initial description of FILE.
607
608 `vc-register-switches' and `vc-rcs-register-switches' are passed to
609 the RCS command (in that order).
610
611 Automatically retrieve a read-only version of the file with keywords
612 expanded if `vc-keep-workfiles' is non-nil, otherwise, delete the workfile."
613 (vc-file-clearprops file)
614 (let ((subdir (expand-file-name "RCS" (file-name-directory file)))
615 (switches (list
616 (if (stringp vc-register-switches)
617 (list vc-register-switches)
618 vc-register-switches)
619 (if (stringp vc-rcs-register-switches)
620 (list vc-rcs-register-switches)
621 vc-rcs-register-switches))))
622
623 (and (not (file-exists-p subdir))
624 (not (directory-files (file-name-directory file)
625 nil ".*,v$" t))
626 (yes-or-no-p "Create RCS subdirectory? ")
627 (make-directory subdir))
628 (apply 'vc-do-command nil 0 "ci" file
629 ;; if available, use the secure registering option
630 (and (vc-rcs-release-p "5.6.4") "-i")
631 (concat (if vc-keep-workfiles "-u" "-r") rev)
632 (and comment (concat "-t-" comment))
633 switches)
634 ;; parse output to find master file name and workfile version
635 (with-current-buffer "*vc*"
636 (goto-char (point-min))
637 (let ((name (if (looking-at (concat "^\\(.*\\) <-- "
638 (file-name-nondirectory file)))
639 (match-string 1))))
640 (if (not name)
641 ;; if we couldn't find the master name,
642 ;; run vc-rcs-registered to get it
643 ;; (will be stored into the vc-name property)
644 (vc-rcs-registered file)
645 (vc-file-setprop file 'vc-name
646 (if (file-name-absolute-p name)
647 name
648 (expand-file-name
649 name
650 (file-name-directory file))))))
651 (vc-file-setprop file 'vc-workfile-version
652 (if (re-search-forward
653 "^initial revision: \\([0-9.]+\\).*\n"
654 nil t)
655 (match-string 1))))))
656
657 (defun vc-rcs-checkout (file &optional writable rev workfile)
658 "Retrieve a copy of a saved version of FILE into a workfile."
659 (let ((filename (or workfile file))
660 (file-buffer (get-file-buffer file))
661 switches)
662 (message "Checking out %s..." filename)
663 (save-excursion
664 ;; Change buffers to get local value of vc-checkout-switches.
665 (if file-buffer (set-buffer file-buffer))
666 (setq switches (if (stringp vc-checkout-switches)
667 (list vc-checkout-switches)
668 vc-checkout-switches))
669 ;; Save this buffer's default-directory
670 ;; and use save-excursion to make sure it is restored
671 ;; in the same buffer it was saved in.
672 (let ((default-directory default-directory))
673 (save-excursion
674 ;; Adjust the default-directory so that the check-out creates
675 ;; the file in the right place.
676 (setq default-directory (file-name-directory filename))
677 (if workfile ;; RCS
678 ;; RCS can't check out into arbitrary file names directly.
679 ;; Use `co -p' and make stdout point to the correct file.
680 (let ((vc-modes (logior (file-modes (vc-name file))
681 (if writable 128 0)))
682 (failed t))
683 (unwind-protect
684 (progn
685 (let ((coding-system-for-read 'no-conversion)
686 (coding-system-for-write 'no-conversion))
687 (with-temp-file filename
688 (apply 'vc-do-command
689 (current-buffer) 0 "co" (vc-name file)
690 "-q" ;; suppress diagnostic output
691 (if writable "-l")
692 (concat "-p" rev)
693 switches)))
694 (set-file-modes filename
695 (logior (file-modes (vc-name file))
696 (if writable 128 0)))
697 (setq failed nil))
698 (and failed (file-exists-p filename)
699 (delete-file filename))))
700 (let (new-version)
701 ;; if we should go to the head of the trunk,
702 ;; clear the default branch first
703 (and rev (string= rev "")
704 (vc-do-command nil 0 "rcs" (vc-name file) "-b"))
705 ;; now do the checkout
706 (apply 'vc-do-command
707 nil 0 "co" (vc-name file)
708 ;; If locking is not strict, force to overwrite
709 ;; the writable workfile.
710 (if (eq (vc-checkout-model file) 'implicit) "-f")
711 (if writable "-l")
712 (if rev (concat "-r" rev)
713 ;; if no explicit revision was specified,
714 ;; check out that of the working file
715 (let ((workrev (vc-workfile-version file)))
716 (if workrev (concat "-r" workrev)
717 nil)))
718 switches)
719 ;; determine the new workfile version
720 (with-current-buffer "*vc*"
721 (setq new-version
722 (vc-parse-buffer "^revision \\([0-9.]+\\).*\n" 1)))
723 (vc-file-setprop file 'vc-workfile-version new-version)
724 ;; if necessary, adjust the default branch
725 (and rev (not (string= rev ""))
726 (vc-do-command
727 nil 0 "rcs" (vc-name file)
728 (concat "-b"
729 (if (vc-rcs-latest-on-branch-p file new-version)
730 (if (vc-rcs-trunk-p new-version) nil
731 (vc-rcs-branch-part new-version))
732 new-version)))))))
733 (message "Checking out %s...done" filename)))))
734
735 (provide 'vc-rcs)
736
737 ;;; vc-rcs.el ends here