(universal-argument-other-key): Call reset-this-command-lengths.
[bpt/emacs.git] / lisp / vc-hooks.el
CommitLineData
aae56ea7 1;;; vc-hooks.el --- resident support for version-control
594722a8 2
38179d42 3;; Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
594722a8
ER
4
5;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
f2ee4191
RS
6;; Modified by:
7;; Per Cederqvist <ceder@lysator.liu.se>
8;; Andre Spiegel <spiegel@berlin.informatik.uni-stuttgart.de>
594722a8
ER
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
24;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25
26;;; Commentary:
27
f2ee4191
RS
28;; This is the always-loaded portion of VC.
29;; It takes care VC-related activities that are done when you visit a file,
30;; so that vc.el itself is loaded only when you use a VC command.
594722a8
ER
31;; See the commentary of vc.el.
32
33;;; Code:
34
e1c0c2d1
KH
35;; Customization Variables (the rest is in vc.el)
36
37(defvar vc-default-back-end nil
38 "*Back-end actually used by this interface; may be SCCS or RCS.
39The value is only computed when needed to avoid an expensive search.")
40
41(defvar vc-path
42 (if (file-directory-p "/usr/sccs")
43 '("/usr/sccs")
44 nil)
45 "*List of extra directories to search for version control commands.")
46
594722a8
ER
47(defvar vc-master-templates
48 '(("%sRCS/%s,v" . RCS) ("%s%s,v" . RCS) ("%sRCS/%s" . RCS)
174edc13
RS
49 ("%sSCCS/s.%s" . SCCS) ("%ss.%s". SCCS)
50 vc-find-cvs-master)
594722a8
ER
51 "*Where to look for version-control master files.
52The first pair corresponding to a given back end is used as a template
53when creating new masters.")
54
55(defvar vc-make-backup-files nil
5032bd23 56 "*If non-nil, backups of registered files are made as with other files.
9228cfac 57If nil (the default), files covered by version control don't get backups.")
594722a8 58
624c0e9d
RS
59(defvar vc-display-status t
60 "*If non-nil, display revision number and lock status in modeline.
198d5c00
RS
61Otherwise, not displayed.")
62
e1c0c2d1
KH
63(defvar vc-consult-headers t
64 "*Identify work files by searching for version headers.")
65
66(defvar vc-mistrust-permissions nil
67 "*Don't assume that permissions and ownership track version-control status.")
68
69(defvar vc-keep-workfiles t
70 "*If non-nil, don't delete working files after registering changes.
71If the back-end is CVS, workfiles are always kept, regardless of the
72value of this flag.")
73
594722a8 74;; Tell Emacs about this new kind of minor mode
7bc2b98b
ER
75(if (not (assoc 'vc-mode minor-mode-alist))
76 (setq minor-mode-alist (cons '(vc-mode vc-mode)
594722a8
ER
77 minor-mode-alist)))
78
7bc2b98b 79(make-variable-buffer-local 'vc-mode)
c43e436c 80(put 'vc-mode 'permanent-local t)
594722a8
ER
81
82;; We need a notion of per-file properties because the version
f2ee4191
RS
83;; control state of a file is expensive to derive --- we compute
84;; them when the file is initially found, keep them up to date
85;; during any subsequent VC operations, and forget them when
86;; the buffer is killed.
594722a8 87
80169ab5
ER
88(defmacro vc-error-occurred (&rest body)
89 (list 'condition-case nil (cons 'progn (append body '(nil))) '(error t)))
90
594722a8
ER
91(defvar vc-file-prop-obarray [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
92 "Obarray for per-file properties.")
93
f2ee4191
RS
94(defvar vc-buffer-backend t)
95(make-variable-buffer-local 'vc-buffer-backend)
96
594722a8
ER
97(defun vc-file-setprop (file property value)
98 ;; set per-file property
99 (put (intern file vc-file-prop-obarray) property value))
100
101(defun vc-file-getprop (file property)
102 ;; get per-file property
103 (get (intern file vc-file-prop-obarray) property))
104
e1c0c2d1
KH
105(defun vc-file-clearprops (file)
106 ;; clear all properties of a given file
107 (setplist (intern file vc-file-prop-obarray) nil))
f2ee4191 108
02d383eb
RS
109;;; Functions that determine property values, by examining the
110;;; working file, the master file, or log program output
e1c0c2d1
KH
111
112(defun vc-match-substring (bn)
113 (buffer-substring (match-beginning bn) (match-end bn)))
114
115(defun vc-lock-file (file)
116 ;; Generate lock file name corresponding to FILE
117 (let ((master (vc-name file)))
118 (and
119 master
120 (string-match "\\(.*/\\)s\\.\\(.*\\)" master)
121 (concat
122 (substring master (match-beginning 1) (match-end 1))
123 "p."
124 (substring master (match-beginning 2) (match-end 2))))))
125
126(defun vc-parse-buffer (patterns &optional file properties)
127 ;; Use PATTERNS to parse information out of the current buffer.
128 ;; Each element of PATTERNS is a list of 2 to 3 elements. The first element
129 ;; is the pattern to be matched, and the second (an integer) is the
130 ;; number of the subexpression that should be returned. If there's
131 ;; a third element (also the number of a subexpression), that
132 ;; subexpression is assumed to be a date field and we want the most
133 ;; recent entry matching the template.
134 ;; If FILE and PROPERTIES are given, the latter must be a list of
135 ;; properties of the same length as PATTERNS; each property is assigned
136 ;; the corresponding value.
137 (mapcar (function (lambda (p)
138 (goto-char (point-min))
139 (cond
140 ((eq (length p) 2) ;; search for first entry
141 (let ((value nil))
142 (if (re-search-forward (car p) nil t)
143 (setq value (vc-match-substring (elt p 1))))
144 (if file
145 (progn (vc-file-setprop file (car properties) value)
146 (setq properties (cdr properties))))
147 value))
148 ((eq (length p) 3) ;; search for latest entry
149 (let ((latest-date "") (latest-val))
150 (while (re-search-forward (car p) nil t)
151 (let ((date (vc-match-substring (elt p 2))))
152 (if (string< latest-date date)
153 (progn
154 (setq latest-date date)
155 (setq latest-val
156 (vc-match-substring (elt p 1)))))))
157 (if file
158 (progn (vc-file-setprop file (car properties) latest-val)
159 (setq properties (cdr properties))))
160 latest-val)))))
161 patterns)
162 )
163
02d383eb
RS
164(defun vc-insert-file (file &optional limit blocksize)
165 ;; Insert the contents of FILE into the current buffer.
166 ;; Optional argument LIMIT is a regexp. If present,
167 ;; the file is inserted in chunks of size BLOCKSIZE
168 ;; (default 8 kByte), until the first occurence of
169 ;; LIMIT is found. The function returns nil if FILE
170 ;; doesn't exist.
171 (cond ((file-exists-p file)
172 (cond (limit
173 (if (not blocksize) (setq blocksize 8192))
174 (let (found s)
175 (while (not found)
176 (setq s (buffer-size))
177 (goto-char (1+ s))
178 (setq found
179 (or (zerop (car (cdr
180 (insert-file-contents file nil s
181 (+ s blocksize)))))
182 (progn (beginning-of-line)
183 (re-search-forward limit nil t)))))))
184 (t (insert-file-contents file)))
185 (set-buffer-modified-p nil)
186 (auto-save-mode nil)
187 t)
188 (t nil)))
189
190(defun vc-parse-locks (file locks)
191 ;; Parse RCS or SCCS locks.
192 ;; The result is a list of the form ((VERSION USER) (VERSION USER) ...),
193 ;; which is returned and stored into the property `vc-master-locks'.
194 (if (not locks)
195 (vc-file-setprop file 'vc-master-locks 'none)
196 (let ((found t) (index 0) master-locks version user)
197 (cond ((eq (vc-backend file) 'SCCS)
198 (while (string-match "^\\([0-9.]+\\) [0-9.]+ \\([^ ]+\\) .*\n?"
199 locks index)
200 (setq version (substring locks
201 (match-beginning 1) (match-end 1)))
202 (setq user (substring locks
203 (match-beginning 2) (match-end 2)))
204 (setq master-locks (append master-locks
205 (list (cons version user))))
206 (setq index (match-end 0))))
207 ((eq (vc-backend file) 'RCS)
208 (while (string-match "[ \t\n]*\\([^:]+\\):\\([0-9.]+\\)"
209 locks index)
210 (setq version (substring locks
211 (match-beginning 2) (match-end 2)))
212 (setq user (substring locks
213 (match-beginning 1) (match-end 1)))
214 (setq master-locks (append master-locks
215 (list (cons version user))))
216 (setq index (match-end 0)))))
217 (vc-file-setprop file 'vc-master-locks (or master-locks 'none)))))
218
219(defun vc-fetch-master-properties (file)
220 ;; Fetch those properties of FILE that are stored in the master file.
1efcbf46
RS
221 ;; For an RCS file, we don't get vc-latest-version vc-your-latest-version
222 ;; here because that is slow.
223 ;; That gets done if/when the functions vc-latest-version
224 ;; and vc-your-latest-version get called.
02d383eb
RS
225 (save-excursion
226 (cond
227 ((eq (vc-backend file) 'SCCS)
228 (set-buffer (get-buffer-create "*vc-info*"))
229 (if (vc-insert-file (vc-lock-file file))
230 (progn (vc-parse-locks file (buffer-string))
231 (erase-buffer))
232 (vc-file-setprop file 'vc-master-locks 'none))
233 (vc-insert-file (vc-name file) "^\001e")
234 (vc-parse-buffer
235 (list '("^\001d D \\([^ ]+\\)" 1)
236 (list (concat "^\001d D \\([^ ]+\\) .* "
237 (regexp-quote (user-login-name)) " ") 1))
238 file
239 '(vc-latest-version vc-your-latest-version)))
240
241 ((eq (vc-backend file) 'RCS)
242 (set-buffer (get-buffer-create "*vc-info*"))
1efcbf46 243 (vc-insert-file (vc-name file) "^locks")
02d383eb
RS
244 (vc-parse-buffer
245 (list '("^head[ \t\n]+\\([^;]+\\);" 1)
246 '("^branch[ \t\n]+\\([^;]+\\);" 1)
1efcbf46 247 '("^locks\\([^;]+\\);" 1))
02d383eb
RS
248 file
249 '(vc-head-version
250 vc-default-branch
1efcbf46 251 vc-master-locks))
02d383eb
RS
252 ;; determine vc-top-version: it is either the head version,
253 ;; or the tip of the default branch
254 (let ((default-branch (vc-file-getprop file 'vc-default-branch)))
255 (cond
256 ;; no default branch
257 ((or (not default-branch) (string= "" default-branch))
258 (vc-file-setprop file 'vc-top-version
259 (vc-file-getprop file 'vc-head-version)))
260 ;; default branch is actually a revision
261 ((string-match "^[0-9]+\\.[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*$"
262 default-branch)
263 (vc-file-setprop file 'vc-top-version default-branch))
264 ;; else, search for the tip of the default branch
1efcbf46
RS
265 (t (erase-buffer)
266 (vc-insert-file (vc-name file) "^desc")
267 (vc-parse-buffer (list (list
02d383eb
RS
268 (concat "^\\("
269 (regexp-quote default-branch)
270 "\\.[0-9]+\\)\ndate[ \t]+\\([0-9.]+\\);") 1 2))
271 file '(vc-top-version)))))
272 ;; translate the locks
273 (vc-parse-locks file (vc-file-getprop file 'vc-master-locks)))
274
275 ((eq (vc-backend file) 'CVS)
276 ;; don't switch to the *vc-info* buffer before running the
277 ;; command, because that would change its default directory
278 (save-excursion (set-buffer (get-buffer-create "*vc-info*"))
279 (erase-buffer))
280 (let ((exec-path (append vc-path exec-path))
281 ;; Add vc-path to PATH for the execution of this command.
282 (process-environment
283 (cons (concat "PATH=" (getenv "PATH")
04094290
RS
284 path-separator
285 (mapconcat 'identity vc-path path-separator))
02d383eb
RS
286 process-environment)))
287 (apply 'call-process "cvs" nil "*vc-info*" nil
288 (list "status" (file-name-nondirectory file))))
289 (set-buffer (get-buffer "*vc-info*"))
290 (set-buffer-modified-p nil)
291 (auto-save-mode nil)
292 (vc-parse-buffer
293 ;; CVS 1.3 says "RCS Version:", other releases "RCS Revision:",
294 ;; and CVS 1.4a1 says "Repository revision:".
295 '(("\\(RCS Version\\|RCS Revision\\|Repository revision\\):[\t ]+\\([0-9.]+\\)" 2)
1efcbf46 296 ("^File: [^ \t]+[ \t]+Status: \\(.*\\)" 1))
02d383eb
RS
297 file
298 '(vc-latest-version vc-cvs-status))
299 ;; Translate those status values that are needed into symbols.
300 ;; Any other value is converted to nil.
301 (let ((status (vc-file-getprop file 'vc-cvs-status)))
302 (cond ((string-match "Up-to-date" status)
303 (vc-file-setprop file 'vc-cvs-status 'up-to-date)
304 (vc-file-setprop file 'vc-checkout-time
305 (nth 5 (file-attributes file))))
306 ((string-match "Locally Modified" status)
307 (vc-file-setprop file 'vc-cvs-status 'locally-modified))
308 ((string-match "Needs Merge" status)
309 (vc-file-setprop file 'vc-cvs-status 'needs-merge))
310 (t (vc-file-setprop file 'vc-cvs-status nil))))))
311 (kill-buffer (current-buffer))))
e1c0c2d1
KH
312
313;;; Functions that determine property values, by examining the
314;;; working file, the master file, or log program output
315
316(defun vc-consult-rcs-headers (file)
317 ;; Search for RCS headers in FILE, and set properties
318 ;; accordingly. This function can be disabled by setting
319 ;; vc-consult-headers to nil.
320 ;; Returns: nil if no headers were found
321 ;; (or if the feature is disabled,
322 ;; or if there is currently no buffer
323 ;; visiting FILE)
324 ;; 'rev if a workfile revision was found
325 ;; 'rev-and-lock if revision and lock info was found
326 (cond
327 ((or (not vc-consult-headers)
02d383eb 328 (not (get-file-buffer file))) nil)
e1c0c2d1
KH
329 ((save-excursion
330 (set-buffer (get-file-buffer file))
331 (goto-char (point-min))
332 (cond
333 ;; search for $Id or $Header
334 ;; -------------------------
1efcbf46
RS
335 ((or (and (search-forward "$Id: " nil t)
336 (looking-at "[^ ]+ \\([0-9.]+\\) "))
337 (and (progn (goto-char (point-min))
fceee007 338 (search-forward "$Header: " nil t))
1efcbf46 339 (looking-at "[^ ]+ \\([0-9.]+\\) ")))
04094290 340 (goto-char (match-end 0))
e1c0c2d1 341 ;; if found, store the revision number ...
1efcbf46
RS
342 (let ((rev (buffer-substring (match-beginning 1)
343 (match-end 1))))
e1c0c2d1
KH
344 ;; ... and check for the locking state
345 (if (re-search-forward
346 (concat "\\=[0-9]+/[0-9]+/[0-9]+ " ; date
347 "[0-9]+:[0-9]+:[0-9]+ " ; time
348 "[^ ]+ [^ ]+ ") ; author & state
349 nil t)
350 (cond
351 ;; unlocked revision
352 ((looking-at "\\$")
353 (vc-file-setprop file 'vc-workfile-version rev)
02d383eb 354 (vc-file-setprop file 'vc-locking-user 'none)
e1c0c2d1
KH
355 'rev-and-lock)
356 ;; revision is locked by some user
357 ((looking-at "\\([^ ]+\\) \\$")
358 (vc-file-setprop file 'vc-workfile-version rev)
359 (vc-file-setprop file 'vc-locking-user
360 (buffer-substring (match-beginning 1)
361 (match-end 1)))
e1c0c2d1
KH
362 'rev-and-lock)
363 ;; everything else: false
364 (nil))
365 ;; unexpected information in
366 ;; keyword string --> quit
367 nil)))
368 ;; search for $Revision
369 ;; --------------------
370 ((re-search-forward (concat "\\$"
371 "Revision: \\([0-9.]+\\) \\$")
372 nil t)
373 ;; if found, store the revision number ...
374 (let ((rev (buffer-substring (match-beginning 1)
375 (match-end 1))))
376 ;; and see if there's any lock information
377 (goto-char (point-min))
378 (if (re-search-forward (concat "\\$" "Locker:") nil t)
379 (cond ((looking-at " \\([^ ]+\\) \\$")
380 (vc-file-setprop file 'vc-workfile-version rev)
381 (vc-file-setprop file 'vc-locking-user
382 (buffer-substring (match-beginning 1)
383 (match-end 1)))
e1c0c2d1
KH
384 'rev-and-lock)
385 ((looking-at " *\\$")
386 (vc-file-setprop file 'vc-workfile-version rev)
02d383eb 387 (vc-file-setprop file 'vc-locking-user 'none)
e1c0c2d1
KH
388 'rev-and-lock)
389 (t
390 (vc-file-setprop file 'vc-workfile-version rev)
02d383eb 391 (vc-file-setprop file 'vc-locking-user 'none)
e1c0c2d1
KH
392 'rev-and-lock))
393 (vc-file-setprop file 'vc-workfile-version rev)
394 'rev)))
395 ;; else: nothing found
396 ;; -------------------
397 (t nil))))))
398
02d383eb
RS
399;;; Access functions to file properties
400;;; (Properties should be _set_ using vc-file-setprop, but
401;;; _retrieved_ only through these functions, which decide
402;;; if the property is already known or not. A property should
403;;; only be retrieved by vc-file-getprop if there is no
404;;; access function.)
405
406;;; properties indicating the backend
407;;; being used for FILE
e1c0c2d1
KH
408
409(defun vc-backend-subdirectory-name (&optional file)
410 ;; Where the master and lock files for the current directory are kept
411 (symbol-name
412 (or
413 (and file (vc-backend file))
414 vc-default-back-end
415 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS)))))
416
02d383eb
RS
417(defun vc-name (file)
418 "Return the master name of a file, nil if it is not registered."
419 (or (vc-file-getprop file 'vc-name)
420 (let ((name-and-type (vc-registered file)))
421 (if name-and-type
422 (progn
423 (vc-file-setprop file 'vc-backend (cdr name-and-type))
424 (vc-file-setprop file 'vc-name (car name-and-type)))))))
e1c0c2d1 425
02d383eb
RS
426(defun vc-backend (file)
427 "Return the version-control type of a file, nil if it is not registered."
428 (and file
429 (or (vc-file-getprop file 'vc-backend)
430 (let ((name-and-type (vc-registered file)))
431 (if name-and-type
432 (progn
433 (vc-file-setprop file 'vc-name (car name-and-type))
434 (vc-file-setprop file 'vc-backend (cdr name-and-type))))))))
e1c0c2d1 435
02d383eb 436;;; properties indicating the locking state
e1c0c2d1
KH
437
438(defun vc-cvs-status (file)
439 ;; Return the cvs status of FILE
440 ;; (Status field in output of "cvs status")
441 (cond ((vc-file-getprop file 'vc-cvs-status))
02d383eb 442 (t (vc-fetch-master-properties file)
e1c0c2d1
KH
443 (vc-file-getprop file 'vc-cvs-status))))
444
02d383eb
RS
445(defun vc-master-locks (file)
446 ;; Return the lock entries in the master of FILE.
447 ;; Return 'none if there are no such entries, and a list
448 ;; of the form ((VERSION USER) (VERSION USER) ...) otherwise.
449 (cond ((vc-file-getprop file 'vc-master-locks))
450 (t (vc-fetch-master-properties file)
451 (vc-file-getprop file 'vc-master-locks))))
452
453(defun vc-master-locking-user (file)
454 ;; Return the master file's idea of who is locking
455 ;; the current workfile version of FILE.
456 ;; Return 'none if it is not locked.
457 (let ((master-locks (vc-master-locks file)) lock)
458 (if (eq master-locks 'none) 'none
459 ;; search for a lock on the current workfile version
460 (setq lock (assoc (vc-workfile-version file) master-locks))
461 (cond (lock (cdr lock))
462 ('none)))))
463
e1c0c2d1 464(defun vc-locking-user (file)
02d383eb
RS
465 ;; Return the name of the person currently holding a lock on FILE.
466 ;; Return nil if there is no such person.
467 ;; Under CVS, a file is considered locked if it has been modified since
468 ;; it was checked out. Under CVS, this will sometimes return the uid of
469 ;; the owner of the file (as a number) instead of a string.
470 ;; The property is cached. It is only looked up if it is currently nil.
471 ;; Note that, for a file that is not locked, the actual property value
472 ;; is 'none, to distinguish it from an unknown locking state. That value
473 ;; is converted to nil by this function, and returned to the caller.
474 (let ((locking-user (vc-file-getprop file 'vc-locking-user)))
475 (if locking-user
476 ;; if we already know the property, return it
477 (if (eq locking-user 'none) nil locking-user)
478
479 ;; otherwise, infer the property...
480 (cond
481 ;; in the CVS case, check the status
482 ((eq (vc-backend file) 'CVS)
483 (if (eq (vc-cvs-status file) 'up-to-date)
484 (vc-file-setprop file 'vc-locking-user 'none)
485 ;; The expression below should return the username of the owner
486 ;; of the file. It doesn't. It returns the username if it is
487 ;; you, or otherwise the UID of the owner of the file. The
488 ;; return value from this function is only used by
489 ;; vc-dired-reformat-line, and it does the proper thing if a UID
490 ;; is returned.
491 ;;
492 ;; The *proper* way to fix this would be to implement a built-in
493 ;; function in Emacs, say, (username UID), that returns the
494 ;; username of a given UID.
495 ;;
496 ;; The result of this hack is that vc-directory will print the
497 ;; name of the owner of the file for any files that are
498 ;; modified.
499 (let ((uid (nth 2 (file-attributes file))))
500 (if (= uid (user-uid))
501 (vc-file-setprop file 'vc-locking-user (user-login-name))
502 (vc-file-setprop file 'vc-locking-user uid)))))
503
504 ;; RCS case: attempt a header search. If this feature is
505 ;; disabled, vc-consult-rcs-headers always returns nil.
506 ((and (eq (vc-backend file) 'RCS)
507 (eq (vc-consult-rcs-headers file) 'rev-and-lock)))
508
509 ;; if the file permissions are not trusted,
510 ;; use the information from the master file
511 ((or (not vc-keep-workfiles)
512 (eq vc-mistrust-permissions 't)
513 (and vc-mistrust-permissions
514 (funcall vc-mistrust-permissions
515 (vc-backend-subdirectory-name file))))
516 (vc-file-setprop file 'vc-locking-user (vc-master-locking-user file)))
517
518 ;; Otherwise: Use the file permissions. (But if it turns out that the
519 ;; file is not owned by the user, use the master file.)
520 ;; This implementation assumes that any file which is under version
521 ;; control and has -rw-r--r-- is locked by its owner. This is true
522 ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--.
523 ;; We have to be careful not to exclude files with execute bits on;
524 ;; scripts can be under version control too. Also, we must ignore the
525 ;; group-read and other-read bits, since paranoid users turn them off.
526 ;; This hack wins because calls to the somewhat expensive
527 ;; `vc-fetch-master-properties' function only have to be made if
528 ;; (a) the file is locked by someone other than the current user,
529 ;; or (b) some untoward manipulation behind vc's back has changed
530 ;; the owner or the `group' or `other' write bits.
531 (t
532 (let ((attributes (file-attributes file)))
533 (cond ((string-match ".r-..-..-." (nth 8 attributes))
534 (vc-file-setprop file 'vc-locking-user 'none))
535 ((and (= (nth 2 attributes) (user-uid))
536 (string-match ".rw..-..-." (nth 8 attributes)))
537 (vc-file-setprop file 'vc-locking-user (user-login-name)))
538 (t
539 (vc-file-setprop file 'vc-locking-user
540 (vc-master-locking-user file))))
541 )))
542 ;; recursively call the function again,
543 ;; to convert a possible 'none value
544 (vc-locking-user file))))
545
546;;; properties to store current and recent version numbers
e1c0c2d1
KH
547
548(defun vc-latest-version (file)
549 ;; Return version level of the latest version of FILE
02d383eb 550 (cond ((vc-file-getprop file 'vc-latest-version))
1efcbf46 551 (t (vc-fetch-properties file)
02d383eb 552 (vc-file-getprop file 'vc-latest-version))))
e1c0c2d1
KH
553
554(defun vc-your-latest-version (file)
555 ;; Return version level of the latest version of FILE checked in by you
02d383eb 556 (cond ((vc-file-getprop file 'vc-your-latest-version))
1efcbf46 557 (t (vc-fetch-properties file)
02d383eb 558 (vc-file-getprop file 'vc-your-latest-version))))
e1c0c2d1 559
02d383eb 560(defun vc-top-version (file)
e1c0c2d1
KH
561 ;; Return version level of the highest revision on the default branch
562 ;; If there is no default branch, return the highest version number
563 ;; on the trunk.
564 ;; This property is defined for RCS only.
02d383eb
RS
565 (cond ((vc-file-getprop file 'vc-top-version))
566 (t (vc-fetch-master-properties file)
567 (vc-file-getprop file 'vc-top-version))))
e1c0c2d1 568
1efcbf46
RS
569(defun vc-fetch-properties (file)
570 ;; Fetch vc-latest-version and vc-your-latest-version
571 ;; if that wasn't already done.
04094290
RS
572 (cond
573 ((eq (vc-backend file) 'RCS)
574 (set-buffer (get-buffer-create "*vc-info*"))
575 (vc-insert-file (vc-name file) "^desc")
576 (vc-parse-buffer
577 (list '("^\\([0-9]+\\.[0-9.]+\\)\ndate[ \t]+\\([0-9.]+\\);" 1 2)
578 (list (concat "^\\([0-9]+\\.[0-9.]+\\)\n"
579 "date[ \t]+\\([0-9.]+\\);[ \t]+"
580 "author[ \t]+"
581 (regexp-quote (user-login-name)) ";") 1 2))
582 file
583 '(vc-latest-version vc-your-latest-version)))
584 (t (vc-fetch-master-properties file))
1efcbf46
RS
585 ))
586
e1c0c2d1
KH
587(defun vc-workfile-version (file)
588 ;; Return version level of the current workfile FILE
589 ;; This is attempted by first looking at the RCS keywords.
590 ;; If there are no keywords in the working file,
02d383eb 591 ;; vc-top-version is taken.
e1c0c2d1
KH
592 ;; Note that this property is cached, that is, it is only
593 ;; looked up if it is nil.
594 ;; For SCCS, this property is equivalent to vc-latest-version.
595 (cond ((vc-file-getprop file 'vc-workfile-version))
596 ((eq (vc-backend file) 'SCCS) (vc-latest-version file))
597 ((eq (vc-backend file) 'RCS)
598 (if (vc-consult-rcs-headers file)
599 (vc-file-getprop file 'vc-workfile-version)
02d383eb 600 (let ((rev (cond ((vc-top-version file))
e1c0c2d1
KH
601 ((vc-latest-version file)))))
602 (vc-file-setprop file 'vc-workfile-version rev)
603 rev)))
604 ((eq (vc-backend file) 'CVS)
605 (if (vc-consult-rcs-headers file) ;; CVS
606 (vc-file-getprop file 'vc-workfile-version)
607 (vc-find-cvs-master (file-name-directory file)
608 (file-name-nondirectory file))
609 (vc-file-getprop file 'vc-workfile-version)))))
f2ee4191 610
594722a8
ER
611;;; actual version-control code starts here
612
613(defun vc-registered (file)
18c8a18e
PE
614 (let (handler handlers)
615 (if (boundp 'file-name-handler-alist)
b993101e 616 (setq handler (find-file-name-handler file 'vc-registered)))
18c8a18e
PE
617 (if handler
618 (funcall handler 'vc-registered file)
619 ;; Search for a master corresponding to the given file
620 (let ((dirname (or (file-name-directory file) ""))
621 (basename (file-name-nondirectory file)))
622 (catch 'found
623 (mapcar
624 (function (lambda (s)
174edc13
RS
625 (if (atom s)
626 (funcall s dirname basename)
627 (let ((trial (format (car s) dirname basename)))
628 (if (and (file-exists-p trial)
629 ;; Make sure the file we found with name
630 ;; TRIAL is not the source file itself.
631 ;; That can happen with RCS-style names
632 ;; if the file name is truncated
633 ;; (e.g. to 14 chars). See if either
634 ;; directory or attributes differ.
635 (or (not (string= dirname
636 (file-name-directory trial)))
637 (not (equal
638 (file-attributes file)
639 (file-attributes trial)))))
640 (throw 'found (cons trial (cdr s))))))))
18c8a18e
PE
641 vc-master-templates)
642 nil)))))
594722a8 643
174edc13
RS
644(defun vc-find-cvs-master (dirname basename)
645 ;; Check if DIRNAME/BASENAME is handled by CVS.
646 ;; If it is, do a (throw 'found (cons MASTER 'CVS)).
647 ;; Note: If the file is ``cvs add''ed but not yet ``cvs commit''ed
648 ;; the MASTER will not actually exist yet. The other parts of VC
f2ee4191 649 ;; checks for this condition. This function returns nil if
174edc13
RS
650 ;; DIRNAME/BASENAME is not handled by CVS.
651 (if (and (file-directory-p (concat dirname "CVS/"))
04094290
RS
652 (file-readable-p (concat dirname "CVS/Entries"))
653 (file-readable-p (concat dirname "CVS/Repository")))
f2ee4191 654 (let ((bufs nil) (fold case-fold-search))
174edc13
RS
655 (unwind-protect
656 (save-excursion
657 (setq bufs (list
658 (find-file-noselect (concat dirname "CVS/Entries"))))
659 (set-buffer (car bufs))
660 (goto-char (point-min))
f2ee4191
RS
661 ;; make sure the file name is searched
662 ;; case-sensitively
663 (setq case-fold-search nil)
174edc13
RS
664 (cond
665 ((re-search-forward
666 (concat "^/" (regexp-quote basename) "/\\([^/]*\\)/")
667 nil t)
f2ee4191 668 (setq case-fold-search fold) ;; restore the old value
174edc13
RS
669 ;; We found it. Store away version number, now
670 ;; that we are anyhow so close to finding it.
671 (vc-file-setprop (concat dirname basename)
f2ee4191 672 'vc-workfile-version
174edc13
RS
673 (buffer-substring (match-beginning 1)
674 (match-end 1)))
675 (setq bufs (cons (find-file-noselect
676 (concat dirname "CVS/Repository"))
677 bufs))
678 (set-buffer (car bufs))
679 (let ((master
680 (concat (file-name-as-directory
681 (buffer-substring (point-min)
682 (1- (point-max))))
683 basename
684 ",v")))
f2ee4191
RS
685 (throw 'found (cons master 'CVS))))
686 (t (setq case-fold-search fold) ;; restore the old value
687 nil)))
174edc13
RS
688 (mapcar (function kill-buffer) bufs)))))
689
f2ee4191
RS
690(defun vc-buffer-backend ()
691 "Return the version-control type of the visited file, or nil if none."
692 (if (eq vc-buffer-backend t)
e1c0c2d1 693 (setq vc-buffer-backend (vc-backend (buffer-file-name)))
f2ee4191
RS
694 vc-buffer-backend))
695
c844616c 696(defun vc-toggle-read-only (&optional verbose)
c43e436c
RS
697 "Change read-only status of current buffer, perhaps via version control.
698If the buffer is visiting a file registered with version control,
699then check the file in or out. Otherwise, just change the read-only flag
f2ee4191 700of the buffer. With prefix argument, ask for version number."
c844616c 701 (interactive "P")
e1c0c2d1 702 (if (vc-backend (buffer-file-name))
c844616c 703 (vc-next-action verbose)
594722a8 704 (toggle-read-only)))
c43e436c 705(define-key global-map "\C-x\C-q" 'vc-toggle-read-only)
594722a8
ER
706
707(defun vc-mode-line (file &optional label)
7bc2b98b 708 "Set `vc-mode' to display type of version control for FILE.
594722a8 709The value is set in the current buffer, which should be the buffer
624c0e9d
RS
710visiting FILE. Second optional arg LABEL is put in place of version
711control system name."
18c8a18e 712 (interactive (list buffer-file-name nil))
e1c0c2d1
KH
713 (let ((vc-type (vc-backend file))
714 (vc-status-string (and vc-display-status (vc-status file))))
f2ee4191 715 (setq vc-mode
e1c0c2d1
KH
716 (concat " " (or label (symbol-name vc-type)) vc-status-string))
717 ;; Make the buffer read-only if the file is not locked
718 ;; (or unchanged, in the CVS case).
719 ;; Determine this by looking at the mode string,
720 ;; so that no further external status query is necessary
721 (if vc-status-string
722 (if (eq (elt vc-status-string 0) ?-)
723 (setq buffer-read-only t))
724 (if (not (vc-locking-user file))
725 (setq buffer-read-only t)))
f2ee4191
RS
726 ;; Even root shouldn't modify a registered file without
727 ;; locking it first.
728 (and vc-type
729 (not buffer-read-only)
730 (zerop (user-uid))
731 (require 'vc)
732 (not (equal (user-login-name) (vc-locking-user file)))
733 (setq buffer-read-only t))
734 (and (null vc-type)
735 (file-symlink-p file)
e1c0c2d1 736 (let ((link-type (vc-backend (file-symlink-p file))))
f2ee4191
RS
737 (if link-type
738 (message
739 "Warning: symbolic link to %s-controlled source file"
740 link-type))))
741 (force-mode-line-update)
742 ;;(set-buffer-modified-p (buffer-modified-p)) ;;use this if Emacs 18
743 vc-type))
594722a8 744
e1c0c2d1 745(defun vc-status (file)
45c92c0c 746 ;; Return string for placement in modeline by `vc-mode-line'.
e1c0c2d1 747 ;; Format:
624c0e9d 748 ;;
e1c0c2d1
KH
749 ;; "-REV" if the revision is not locked
750 ;; ":REV" if the revision is locked by the user
751 ;; ":LOCKER:REV" if the revision is locked by somebody else
752 ;; " @@" for a CVS file that is added, but not yet committed
174edc13 753 ;;
e1c0c2d1
KH
754 ;; In the CVS case, a "locked" working file is a
755 ;; working file that is modified with respect to the master.
756 ;; The file is "locked" from the moment when the user makes
757 ;; the buffer writable.
758 ;;
759 ;; This function assumes that the file is registered.
760
761 (let ((locker (vc-locking-user file))
762 (rev (vc-workfile-version file)))
763 (cond ((string= "0" rev)
764 " @@")
38179d42
RM
765 ((not locker)
766 (concat "-" rev))
767 ((if (stringp locker)
768 (string= locker (user-login-name))
769 (= locker (user-uid)))
e1c0c2d1
KH
770 (concat ":" rev))
771 (t
772 (concat ":" locker ":" rev)))))
f2ee4191 773
594722a8
ER
774;;; install a call to the above as a find-file hook
775(defun vc-find-file-hook ()
18c8a18e
PE
776 ;; Recompute whether file is version controlled,
777 ;; if user has killed the buffer and revisited.
f2ee4191
RS
778 (cond
779 (buffer-file-name
780 (vc-file-clearprops buffer-file-name)
781 (cond
e1c0c2d1 782 ((vc-backend buffer-file-name)
f2ee4191
RS
783 (vc-mode-line buffer-file-name)
784 (cond ((not vc-make-backup-files)
785 ;; Use this variable, not make-backup-files,
786 ;; because this is for things that depend on the file name.
787 (make-local-variable 'backup-inhibited)
788 (setq backup-inhibited t))))))))
594722a8 789
6379911c 790(add-hook 'find-file-hooks 'vc-find-file-hook)
594722a8
ER
791
792;;; more hooks, this time for file-not-found
793(defun vc-file-not-found-hook ()
794 "When file is not found, try to check it out from RCS or SCCS.
795Returns t if checkout was successful, nil otherwise."
e1c0c2d1 796 (if (vc-backend buffer-file-name)
624c0e9d 797 (save-excursion
594722a8 798 (require 'vc)
02d383eb 799 (setq default-directory (file-name-directory (buffer-file-name)))
594722a8
ER
800 (not (vc-error-occurred (vc-checkout buffer-file-name))))))
801
6379911c 802(add-hook 'find-file-not-found-hooks 'vc-file-not-found-hook)
594722a8 803
f2ee4191
RS
804;; Discard info about a file when we kill its buffer.
805(defun vc-kill-buffer-hook ()
806 (if (stringp (buffer-file-name))
807 (progn
808 (vc-file-clearprops (buffer-file-name))
809 (kill-local-variable 'vc-buffer-backend))))
810
811;;;(add-hook 'kill-buffer-hook 'vc-kill-buffer-hook)
812
594722a8 813;;; Now arrange for bindings and autoloading of the main package.
7bc2b98b
ER
814;;; Bindings for this have to go in the global map, as we'll often
815;;; want to call them from random buffers.
594722a8
ER
816
817(setq vc-prefix-map (lookup-key global-map "\C-xv"))
818(if (not (keymapp vc-prefix-map))
819 (progn
820 (setq vc-prefix-map (make-sparse-keymap))
821 (define-key global-map "\C-xv" vc-prefix-map)
822 (define-key vc-prefix-map "a" 'vc-update-change-log)
823 (define-key vc-prefix-map "c" 'vc-cancel-version)
18c8a18e 824 (define-key vc-prefix-map "d" 'vc-directory)
594722a8
ER
825 (define-key vc-prefix-map "h" 'vc-insert-headers)
826 (define-key vc-prefix-map "i" 'vc-register)
827 (define-key vc-prefix-map "l" 'vc-print-log)
828 (define-key vc-prefix-map "r" 'vc-retrieve-snapshot)
829 (define-key vc-prefix-map "s" 'vc-create-snapshot)
830 (define-key vc-prefix-map "u" 'vc-revert-buffer)
831 (define-key vc-prefix-map "v" 'vc-next-action)
18c8a18e 832 (define-key vc-prefix-map "=" 'vc-diff)
624c0e9d
RS
833 (define-key vc-prefix-map "~" 'vc-version-other-window)))
834
b662fbb8
RM
835(if (not (boundp 'vc-menu-map))
836 ;; Don't do the menu bindings if menu-bar.el wasn't loaded to defvar
837 ;; vc-menu-map.
838 ()
839 ;;(define-key vc-menu-map [show-files]
840 ;; '("Show Files under VC" . (vc-directory t)))
841 (define-key vc-menu-map [vc-directory] '("Show Locked Files" . vc-directory))
842 (define-key vc-menu-map [separator1] '("----"))
843 (define-key vc-menu-map [vc-rename-file] '("Rename File" . vc-rename-file))
844 (define-key vc-menu-map [vc-version-other-window]
845 '("Show Other Version" . vc-version-other-window))
846 (define-key vc-menu-map [vc-diff] '("Compare with Last Version" . vc-diff))
847 (define-key vc-menu-map [vc-update-change-log]
848 '("Update ChangeLog" . vc-update-change-log))
849 (define-key vc-menu-map [vc-print-log] '("Show History" . vc-print-log))
850 (define-key vc-menu-map [separator2] '("----"))
851 (define-key vc-menu-map [undo] '("Undo Last Check-In" . vc-cancel-version))
852 (define-key vc-menu-map [vc-revert-buffer]
853 '("Revert to Last Version" . vc-revert-buffer))
854 (define-key vc-menu-map [vc-insert-header]
855 '("Insert Header" . vc-insert-headers))
856 (define-key vc-menu-map [vc-menu-check-in] '("Check In" . vc-next-action))
857 (define-key vc-menu-map [vc-check-out] '("Check Out" . vc-toggle-read-only))
858 (define-key vc-menu-map [vc-register] '("Register" . vc-register))
859 (put 'vc-rename-file 'menu-enable 'vc-mode)
860 (put 'vc-version-other-window 'menu-enable 'vc-mode)
861 (put 'vc-diff 'menu-enable 'vc-mode)
862 (put 'vc-update-change-log 'menu-enable
f2ee4191 863 '(eq (vc-buffer-backend) 'RCS))
b662fbb8
RM
864 (put 'vc-print-log 'menu-enable 'vc-mode)
865 (put 'vc-cancel-version 'menu-enable 'vc-mode)
866 (put 'vc-revert-buffer 'menu-enable 'vc-mode)
867 (put 'vc-insert-headers 'menu-enable 'vc-mode)
868 (put 'vc-next-action 'menu-enable '(and vc-mode (not buffer-read-only)))
869 (put 'vc-toggle-read-only 'menu-enable '(and vc-mode buffer-read-only))
76a8bf4c 870 (put 'vc-register 'menu-enable '(and buffer-file-name (not vc-mode)))
b662fbb8 871 )
594722a8 872
594722a8
ER
873(provide 'vc-hooks)
874
875;;; vc-hooks.el ends here