(isearch-mode-map): Bind frame events to nil.
[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")
284 ":" (mapconcat 'identity vc-path ":"))
285 process-environment)))
286 (apply 'call-process "cvs" nil "*vc-info*" nil
287 (list "status" (file-name-nondirectory file))))
288 (set-buffer (get-buffer "*vc-info*"))
289 (set-buffer-modified-p nil)
290 (auto-save-mode nil)
291 (vc-parse-buffer
292 ;; CVS 1.3 says "RCS Version:", other releases "RCS Revision:",
293 ;; and CVS 1.4a1 says "Repository revision:".
294 '(("\\(RCS Version\\|RCS Revision\\|Repository revision\\):[\t ]+\\([0-9.]+\\)" 2)
1efcbf46 295 ("^File: [^ \t]+[ \t]+Status: \\(.*\\)" 1))
02d383eb
RS
296 file
297 '(vc-latest-version vc-cvs-status))
298 ;; Translate those status values that are needed into symbols.
299 ;; Any other value is converted to nil.
300 (let ((status (vc-file-getprop file 'vc-cvs-status)))
301 (cond ((string-match "Up-to-date" status)
302 (vc-file-setprop file 'vc-cvs-status 'up-to-date)
303 (vc-file-setprop file 'vc-checkout-time
304 (nth 5 (file-attributes file))))
305 ((string-match "Locally Modified" status)
306 (vc-file-setprop file 'vc-cvs-status 'locally-modified))
307 ((string-match "Needs Merge" status)
308 (vc-file-setprop file 'vc-cvs-status 'needs-merge))
309 (t (vc-file-setprop file 'vc-cvs-status nil))))))
310 (kill-buffer (current-buffer))))
e1c0c2d1
KH
311
312;;; Functions that determine property values, by examining the
313;;; working file, the master file, or log program output
314
315(defun vc-consult-rcs-headers (file)
316 ;; Search for RCS headers in FILE, and set properties
317 ;; accordingly. This function can be disabled by setting
318 ;; vc-consult-headers to nil.
319 ;; Returns: nil if no headers were found
320 ;; (or if the feature is disabled,
321 ;; or if there is currently no buffer
322 ;; visiting FILE)
323 ;; 'rev if a workfile revision was found
324 ;; 'rev-and-lock if revision and lock info was found
325 (cond
326 ((or (not vc-consult-headers)
02d383eb 327 (not (get-file-buffer file))) nil)
e1c0c2d1
KH
328 ((save-excursion
329 (set-buffer (get-file-buffer file))
330 (goto-char (point-min))
331 (cond
332 ;; search for $Id or $Header
333 ;; -------------------------
1efcbf46
RS
334 ((or (and (search-forward "$Id: " nil t)
335 (looking-at "[^ ]+ \\([0-9.]+\\) "))
336 (and (progn (goto-char (point-min))
fceee007 337 (search-forward "$Header: " nil t))
1efcbf46 338 (looking-at "[^ ]+ \\([0-9.]+\\) ")))
e1c0c2d1 339 ;; if found, store the revision number ...
1efcbf46
RS
340 (let ((rev (buffer-substring (match-beginning 1)
341 (match-end 1))))
e1c0c2d1
KH
342 ;; ... and check for the locking state
343 (if (re-search-forward
344 (concat "\\=[0-9]+/[0-9]+/[0-9]+ " ; date
345 "[0-9]+:[0-9]+:[0-9]+ " ; time
346 "[^ ]+ [^ ]+ ") ; author & state
347 nil t)
348 (cond
349 ;; unlocked revision
350 ((looking-at "\\$")
351 (vc-file-setprop file 'vc-workfile-version rev)
02d383eb 352 (vc-file-setprop file 'vc-locking-user 'none)
e1c0c2d1
KH
353 'rev-and-lock)
354 ;; revision is locked by some user
355 ((looking-at "\\([^ ]+\\) \\$")
356 (vc-file-setprop file 'vc-workfile-version rev)
357 (vc-file-setprop file 'vc-locking-user
358 (buffer-substring (match-beginning 1)
359 (match-end 1)))
e1c0c2d1
KH
360 'rev-and-lock)
361 ;; everything else: false
362 (nil))
363 ;; unexpected information in
364 ;; keyword string --> quit
365 nil)))
366 ;; search for $Revision
367 ;; --------------------
368 ((re-search-forward (concat "\\$"
369 "Revision: \\([0-9.]+\\) \\$")
370 nil t)
371 ;; if found, store the revision number ...
372 (let ((rev (buffer-substring (match-beginning 1)
373 (match-end 1))))
374 ;; and see if there's any lock information
375 (goto-char (point-min))
376 (if (re-search-forward (concat "\\$" "Locker:") nil t)
377 (cond ((looking-at " \\([^ ]+\\) \\$")
378 (vc-file-setprop file 'vc-workfile-version rev)
379 (vc-file-setprop file 'vc-locking-user
380 (buffer-substring (match-beginning 1)
381 (match-end 1)))
e1c0c2d1
KH
382 'rev-and-lock)
383 ((looking-at " *\\$")
384 (vc-file-setprop file 'vc-workfile-version rev)
02d383eb 385 (vc-file-setprop file 'vc-locking-user 'none)
e1c0c2d1
KH
386 'rev-and-lock)
387 (t
388 (vc-file-setprop file 'vc-workfile-version rev)
02d383eb 389 (vc-file-setprop file 'vc-locking-user 'none)
e1c0c2d1
KH
390 'rev-and-lock))
391 (vc-file-setprop file 'vc-workfile-version rev)
392 'rev)))
393 ;; else: nothing found
394 ;; -------------------
395 (t nil))))))
396
02d383eb
RS
397;;; Access functions to file properties
398;;; (Properties should be _set_ using vc-file-setprop, but
399;;; _retrieved_ only through these functions, which decide
400;;; if the property is already known or not. A property should
401;;; only be retrieved by vc-file-getprop if there is no
402;;; access function.)
403
404;;; properties indicating the backend
405;;; being used for FILE
e1c0c2d1
KH
406
407(defun vc-backend-subdirectory-name (&optional file)
408 ;; Where the master and lock files for the current directory are kept
409 (symbol-name
410 (or
411 (and file (vc-backend file))
412 vc-default-back-end
413 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS)))))
414
02d383eb
RS
415(defun vc-name (file)
416 "Return the master name of a file, nil if it is not registered."
417 (or (vc-file-getprop file 'vc-name)
418 (let ((name-and-type (vc-registered file)))
419 (if name-and-type
420 (progn
421 (vc-file-setprop file 'vc-backend (cdr name-and-type))
422 (vc-file-setprop file 'vc-name (car name-and-type)))))))
e1c0c2d1 423
02d383eb
RS
424(defun vc-backend (file)
425 "Return the version-control type of a file, nil if it is not registered."
426 (and file
427 (or (vc-file-getprop file 'vc-backend)
428 (let ((name-and-type (vc-registered file)))
429 (if name-and-type
430 (progn
431 (vc-file-setprop file 'vc-name (car name-and-type))
432 (vc-file-setprop file 'vc-backend (cdr name-and-type))))))))
e1c0c2d1 433
02d383eb 434;;; properties indicating the locking state
e1c0c2d1
KH
435
436(defun vc-cvs-status (file)
437 ;; Return the cvs status of FILE
438 ;; (Status field in output of "cvs status")
439 (cond ((vc-file-getprop file 'vc-cvs-status))
02d383eb 440 (t (vc-fetch-master-properties file)
e1c0c2d1
KH
441 (vc-file-getprop file 'vc-cvs-status))))
442
02d383eb
RS
443(defun vc-master-locks (file)
444 ;; Return the lock entries in the master of FILE.
445 ;; Return 'none if there are no such entries, and a list
446 ;; of the form ((VERSION USER) (VERSION USER) ...) otherwise.
447 (cond ((vc-file-getprop file 'vc-master-locks))
448 (t (vc-fetch-master-properties file)
449 (vc-file-getprop file 'vc-master-locks))))
450
451(defun vc-master-locking-user (file)
452 ;; Return the master file's idea of who is locking
453 ;; the current workfile version of FILE.
454 ;; Return 'none if it is not locked.
455 (let ((master-locks (vc-master-locks file)) lock)
456 (if (eq master-locks 'none) 'none
457 ;; search for a lock on the current workfile version
458 (setq lock (assoc (vc-workfile-version file) master-locks))
459 (cond (lock (cdr lock))
460 ('none)))))
461
e1c0c2d1 462(defun vc-locking-user (file)
02d383eb
RS
463 ;; Return the name of the person currently holding a lock on FILE.
464 ;; Return nil if there is no such person.
465 ;; Under CVS, a file is considered locked if it has been modified since
466 ;; it was checked out. Under CVS, this will sometimes return the uid of
467 ;; the owner of the file (as a number) instead of a string.
468 ;; The property is cached. It is only looked up if it is currently nil.
469 ;; Note that, for a file that is not locked, the actual property value
470 ;; is 'none, to distinguish it from an unknown locking state. That value
471 ;; is converted to nil by this function, and returned to the caller.
472 (let ((locking-user (vc-file-getprop file 'vc-locking-user)))
473 (if locking-user
474 ;; if we already know the property, return it
475 (if (eq locking-user 'none) nil locking-user)
476
477 ;; otherwise, infer the property...
478 (cond
479 ;; in the CVS case, check the status
480 ((eq (vc-backend file) 'CVS)
481 (if (eq (vc-cvs-status file) 'up-to-date)
482 (vc-file-setprop file 'vc-locking-user 'none)
483 ;; The expression below should return the username of the owner
484 ;; of the file. It doesn't. It returns the username if it is
485 ;; you, or otherwise the UID of the owner of the file. The
486 ;; return value from this function is only used by
487 ;; vc-dired-reformat-line, and it does the proper thing if a UID
488 ;; is returned.
489 ;;
490 ;; The *proper* way to fix this would be to implement a built-in
491 ;; function in Emacs, say, (username UID), that returns the
492 ;; username of a given UID.
493 ;;
494 ;; The result of this hack is that vc-directory will print the
495 ;; name of the owner of the file for any files that are
496 ;; modified.
497 (let ((uid (nth 2 (file-attributes file))))
498 (if (= uid (user-uid))
499 (vc-file-setprop file 'vc-locking-user (user-login-name))
500 (vc-file-setprop file 'vc-locking-user uid)))))
501
502 ;; RCS case: attempt a header search. If this feature is
503 ;; disabled, vc-consult-rcs-headers always returns nil.
504 ((and (eq (vc-backend file) 'RCS)
505 (eq (vc-consult-rcs-headers file) 'rev-and-lock)))
506
507 ;; if the file permissions are not trusted,
508 ;; use the information from the master file
509 ((or (not vc-keep-workfiles)
510 (eq vc-mistrust-permissions 't)
511 (and vc-mistrust-permissions
512 (funcall vc-mistrust-permissions
513 (vc-backend-subdirectory-name file))))
514 (vc-file-setprop file 'vc-locking-user (vc-master-locking-user file)))
515
516 ;; Otherwise: Use the file permissions. (But if it turns out that the
517 ;; file is not owned by the user, use the master file.)
518 ;; This implementation assumes that any file which is under version
519 ;; control and has -rw-r--r-- is locked by its owner. This is true
520 ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--.
521 ;; We have to be careful not to exclude files with execute bits on;
522 ;; scripts can be under version control too. Also, we must ignore the
523 ;; group-read and other-read bits, since paranoid users turn them off.
524 ;; This hack wins because calls to the somewhat expensive
525 ;; `vc-fetch-master-properties' function only have to be made if
526 ;; (a) the file is locked by someone other than the current user,
527 ;; or (b) some untoward manipulation behind vc's back has changed
528 ;; the owner or the `group' or `other' write bits.
529 (t
530 (let ((attributes (file-attributes file)))
531 (cond ((string-match ".r-..-..-." (nth 8 attributes))
532 (vc-file-setprop file 'vc-locking-user 'none))
533 ((and (= (nth 2 attributes) (user-uid))
534 (string-match ".rw..-..-." (nth 8 attributes)))
535 (vc-file-setprop file 'vc-locking-user (user-login-name)))
536 (t
537 (vc-file-setprop file 'vc-locking-user
538 (vc-master-locking-user file))))
539 )))
540 ;; recursively call the function again,
541 ;; to convert a possible 'none value
542 (vc-locking-user file))))
543
544;;; properties to store current and recent version numbers
e1c0c2d1
KH
545
546(defun vc-latest-version (file)
547 ;; Return version level of the latest version of FILE
02d383eb 548 (cond ((vc-file-getprop file 'vc-latest-version))
1efcbf46 549 (t (vc-fetch-properties file)
02d383eb 550 (vc-file-getprop file 'vc-latest-version))))
e1c0c2d1
KH
551
552(defun vc-your-latest-version (file)
553 ;; Return version level of the latest version of FILE checked in by you
02d383eb 554 (cond ((vc-file-getprop file 'vc-your-latest-version))
1efcbf46 555 (t (vc-fetch-properties file)
02d383eb 556 (vc-file-getprop file 'vc-your-latest-version))))
e1c0c2d1 557
02d383eb 558(defun vc-top-version (file)
e1c0c2d1
KH
559 ;; Return version level of the highest revision on the default branch
560 ;; If there is no default branch, return the highest version number
561 ;; on the trunk.
562 ;; This property is defined for RCS only.
02d383eb
RS
563 (cond ((vc-file-getprop file 'vc-top-version))
564 (t (vc-fetch-master-properties file)
565 (vc-file-getprop file 'vc-top-version))))
e1c0c2d1 566
1efcbf46
RS
567(defun vc-fetch-properties (file)
568 ;; Fetch vc-latest-version and vc-your-latest-version
569 ;; if that wasn't already done.
570 (vc-backend-dispatch
571 file
572 ;; SCCS
573 (vc-fetch-master-properties file)
574 ;; RCS
575 (progn
576 (set-buffer (get-buffer-create "*vc-info*"))
577 (vc-insert-file (vc-name file) "^desc")
578 (vc-parse-buffer
579 (list '("^\\([0-9]+\\.[0-9.]+\\)\ndate[ \t]+\\([0-9.]+\\);" 1 2)
580 (list (concat "^\\([0-9]+\\.[0-9.]+\\)\n"
581 "date[ \t]+\\([0-9.]+\\);[ \t]+"
582 "author[ \t]+"
583 (regexp-quote (user-login-name)) ";") 1 2))
584 file
585 '(vc-latest-version vc-your-latest-version)))
586 ;; CVS
587 (vc-fetch-master-properties file)
588 ))
589
e1c0c2d1
KH
590(defun vc-workfile-version (file)
591 ;; Return version level of the current workfile FILE
592 ;; This is attempted by first looking at the RCS keywords.
593 ;; If there are no keywords in the working file,
02d383eb 594 ;; vc-top-version is taken.
e1c0c2d1
KH
595 ;; Note that this property is cached, that is, it is only
596 ;; looked up if it is nil.
597 ;; For SCCS, this property is equivalent to vc-latest-version.
598 (cond ((vc-file-getprop file 'vc-workfile-version))
599 ((eq (vc-backend file) 'SCCS) (vc-latest-version file))
600 ((eq (vc-backend file) 'RCS)
601 (if (vc-consult-rcs-headers file)
602 (vc-file-getprop file 'vc-workfile-version)
02d383eb 603 (let ((rev (cond ((vc-top-version file))
e1c0c2d1
KH
604 ((vc-latest-version file)))))
605 (vc-file-setprop file 'vc-workfile-version rev)
606 rev)))
607 ((eq (vc-backend file) 'CVS)
608 (if (vc-consult-rcs-headers file) ;; CVS
609 (vc-file-getprop file 'vc-workfile-version)
610 (vc-find-cvs-master (file-name-directory file)
611 (file-name-nondirectory file))
612 (vc-file-getprop file 'vc-workfile-version)))))
f2ee4191 613
594722a8
ER
614;;; actual version-control code starts here
615
616(defun vc-registered (file)
18c8a18e
PE
617 (let (handler handlers)
618 (if (boundp 'file-name-handler-alist)
b993101e 619 (setq handler (find-file-name-handler file 'vc-registered)))
18c8a18e
PE
620 (if handler
621 (funcall handler 'vc-registered file)
622 ;; Search for a master corresponding to the given file
623 (let ((dirname (or (file-name-directory file) ""))
624 (basename (file-name-nondirectory file)))
625 (catch 'found
626 (mapcar
627 (function (lambda (s)
174edc13
RS
628 (if (atom s)
629 (funcall s dirname basename)
630 (let ((trial (format (car s) dirname basename)))
631 (if (and (file-exists-p trial)
632 ;; Make sure the file we found with name
633 ;; TRIAL is not the source file itself.
634 ;; That can happen with RCS-style names
635 ;; if the file name is truncated
636 ;; (e.g. to 14 chars). See if either
637 ;; directory or attributes differ.
638 (or (not (string= dirname
639 (file-name-directory trial)))
640 (not (equal
641 (file-attributes file)
642 (file-attributes trial)))))
643 (throw 'found (cons trial (cdr s))))))))
18c8a18e
PE
644 vc-master-templates)
645 nil)))))
594722a8 646
174edc13
RS
647(defun vc-find-cvs-master (dirname basename)
648 ;; Check if DIRNAME/BASENAME is handled by CVS.
649 ;; If it is, do a (throw 'found (cons MASTER 'CVS)).
650 ;; Note: If the file is ``cvs add''ed but not yet ``cvs commit''ed
651 ;; the MASTER will not actually exist yet. The other parts of VC
f2ee4191 652 ;; checks for this condition. This function returns nil if
174edc13
RS
653 ;; DIRNAME/BASENAME is not handled by CVS.
654 (if (and (file-directory-p (concat dirname "CVS/"))
655 (file-readable-p (concat dirname "CVS/Entries")))
f2ee4191 656 (let ((bufs nil) (fold case-fold-search))
174edc13
RS
657 (unwind-protect
658 (save-excursion
659 (setq bufs (list
660 (find-file-noselect (concat dirname "CVS/Entries"))))
661 (set-buffer (car bufs))
662 (goto-char (point-min))
f2ee4191
RS
663 ;; make sure the file name is searched
664 ;; case-sensitively
665 (setq case-fold-search nil)
174edc13
RS
666 (cond
667 ((re-search-forward
668 (concat "^/" (regexp-quote basename) "/\\([^/]*\\)/")
669 nil t)
f2ee4191 670 (setq case-fold-search fold) ;; restore the old value
174edc13
RS
671 ;; We found it. Store away version number, now
672 ;; that we are anyhow so close to finding it.
673 (vc-file-setprop (concat dirname basename)
f2ee4191 674 'vc-workfile-version
174edc13
RS
675 (buffer-substring (match-beginning 1)
676 (match-end 1)))
677 (setq bufs (cons (find-file-noselect
678 (concat dirname "CVS/Repository"))
679 bufs))
680 (set-buffer (car bufs))
681 (let ((master
682 (concat (file-name-as-directory
683 (buffer-substring (point-min)
684 (1- (point-max))))
685 basename
686 ",v")))
f2ee4191
RS
687 (throw 'found (cons master 'CVS))))
688 (t (setq case-fold-search fold) ;; restore the old value
689 nil)))
174edc13
RS
690 (mapcar (function kill-buffer) bufs)))))
691
f2ee4191
RS
692(defun vc-buffer-backend ()
693 "Return the version-control type of the visited file, or nil if none."
694 (if (eq vc-buffer-backend t)
e1c0c2d1 695 (setq vc-buffer-backend (vc-backend (buffer-file-name)))
f2ee4191
RS
696 vc-buffer-backend))
697
c844616c 698(defun vc-toggle-read-only (&optional verbose)
c43e436c
RS
699 "Change read-only status of current buffer, perhaps via version control.
700If the buffer is visiting a file registered with version control,
701then check the file in or out. Otherwise, just change the read-only flag
f2ee4191 702of the buffer. With prefix argument, ask for version number."
c844616c 703 (interactive "P")
e1c0c2d1 704 (if (vc-backend (buffer-file-name))
c844616c 705 (vc-next-action verbose)
594722a8 706 (toggle-read-only)))
c43e436c 707(define-key global-map "\C-x\C-q" 'vc-toggle-read-only)
594722a8
ER
708
709(defun vc-mode-line (file &optional label)
7bc2b98b 710 "Set `vc-mode' to display type of version control for FILE.
594722a8 711The value is set in the current buffer, which should be the buffer
624c0e9d
RS
712visiting FILE. Second optional arg LABEL is put in place of version
713control system name."
18c8a18e 714 (interactive (list buffer-file-name nil))
e1c0c2d1
KH
715 (let ((vc-type (vc-backend file))
716 (vc-status-string (and vc-display-status (vc-status file))))
f2ee4191 717 (setq vc-mode
e1c0c2d1
KH
718 (concat " " (or label (symbol-name vc-type)) vc-status-string))
719 ;; Make the buffer read-only if the file is not locked
720 ;; (or unchanged, in the CVS case).
721 ;; Determine this by looking at the mode string,
722 ;; so that no further external status query is necessary
723 (if vc-status-string
724 (if (eq (elt vc-status-string 0) ?-)
725 (setq buffer-read-only t))
726 (if (not (vc-locking-user file))
727 (setq buffer-read-only t)))
f2ee4191
RS
728 ;; Even root shouldn't modify a registered file without
729 ;; locking it first.
730 (and vc-type
731 (not buffer-read-only)
732 (zerop (user-uid))
733 (require 'vc)
734 (not (equal (user-login-name) (vc-locking-user file)))
735 (setq buffer-read-only t))
736 (and (null vc-type)
737 (file-symlink-p file)
e1c0c2d1 738 (let ((link-type (vc-backend (file-symlink-p file))))
f2ee4191
RS
739 (if link-type
740 (message
741 "Warning: symbolic link to %s-controlled source file"
742 link-type))))
743 (force-mode-line-update)
744 ;;(set-buffer-modified-p (buffer-modified-p)) ;;use this if Emacs 18
745 vc-type))
594722a8 746
e1c0c2d1 747(defun vc-status (file)
45c92c0c 748 ;; Return string for placement in modeline by `vc-mode-line'.
e1c0c2d1 749 ;; Format:
624c0e9d 750 ;;
e1c0c2d1
KH
751 ;; "-REV" if the revision is not locked
752 ;; ":REV" if the revision is locked by the user
753 ;; ":LOCKER:REV" if the revision is locked by somebody else
754 ;; " @@" for a CVS file that is added, but not yet committed
174edc13 755 ;;
e1c0c2d1
KH
756 ;; In the CVS case, a "locked" working file is a
757 ;; working file that is modified with respect to the master.
758 ;; The file is "locked" from the moment when the user makes
759 ;; the buffer writable.
760 ;;
761 ;; This function assumes that the file is registered.
762
763 (let ((locker (vc-locking-user file))
764 (rev (vc-workfile-version file)))
765 (cond ((string= "0" rev)
766 " @@")
38179d42
RM
767 ((not locker)
768 (concat "-" rev))
769 ((if (stringp locker)
770 (string= locker (user-login-name))
771 (= locker (user-uid)))
e1c0c2d1
KH
772 (concat ":" rev))
773 (t
774 (concat ":" locker ":" rev)))))
f2ee4191 775
594722a8
ER
776;;; install a call to the above as a find-file hook
777(defun vc-find-file-hook ()
18c8a18e
PE
778 ;; Recompute whether file is version controlled,
779 ;; if user has killed the buffer and revisited.
f2ee4191
RS
780 (cond
781 (buffer-file-name
782 (vc-file-clearprops buffer-file-name)
783 (cond
e1c0c2d1 784 ((vc-backend buffer-file-name)
f2ee4191
RS
785 (vc-mode-line buffer-file-name)
786 (cond ((not vc-make-backup-files)
787 ;; Use this variable, not make-backup-files,
788 ;; because this is for things that depend on the file name.
789 (make-local-variable 'backup-inhibited)
790 (setq backup-inhibited t))))))))
594722a8 791
6379911c 792(add-hook 'find-file-hooks 'vc-find-file-hook)
594722a8
ER
793
794;;; more hooks, this time for file-not-found
795(defun vc-file-not-found-hook ()
796 "When file is not found, try to check it out from RCS or SCCS.
797Returns t if checkout was successful, nil otherwise."
e1c0c2d1 798 (if (vc-backend buffer-file-name)
624c0e9d 799 (save-excursion
594722a8 800 (require 'vc)
02d383eb 801 (setq default-directory (file-name-directory (buffer-file-name)))
594722a8
ER
802 (not (vc-error-occurred (vc-checkout buffer-file-name))))))
803
6379911c 804(add-hook 'find-file-not-found-hooks 'vc-file-not-found-hook)
594722a8 805
f2ee4191
RS
806;; Discard info about a file when we kill its buffer.
807(defun vc-kill-buffer-hook ()
808 (if (stringp (buffer-file-name))
809 (progn
810 (vc-file-clearprops (buffer-file-name))
811 (kill-local-variable 'vc-buffer-backend))))
812
813;;;(add-hook 'kill-buffer-hook 'vc-kill-buffer-hook)
814
594722a8 815;;; Now arrange for bindings and autoloading of the main package.
7bc2b98b
ER
816;;; Bindings for this have to go in the global map, as we'll often
817;;; want to call them from random buffers.
594722a8
ER
818
819(setq vc-prefix-map (lookup-key global-map "\C-xv"))
820(if (not (keymapp vc-prefix-map))
821 (progn
822 (setq vc-prefix-map (make-sparse-keymap))
823 (define-key global-map "\C-xv" vc-prefix-map)
824 (define-key vc-prefix-map "a" 'vc-update-change-log)
825 (define-key vc-prefix-map "c" 'vc-cancel-version)
18c8a18e 826 (define-key vc-prefix-map "d" 'vc-directory)
594722a8
ER
827 (define-key vc-prefix-map "h" 'vc-insert-headers)
828 (define-key vc-prefix-map "i" 'vc-register)
829 (define-key vc-prefix-map "l" 'vc-print-log)
830 (define-key vc-prefix-map "r" 'vc-retrieve-snapshot)
831 (define-key vc-prefix-map "s" 'vc-create-snapshot)
832 (define-key vc-prefix-map "u" 'vc-revert-buffer)
833 (define-key vc-prefix-map "v" 'vc-next-action)
18c8a18e 834 (define-key vc-prefix-map "=" 'vc-diff)
624c0e9d
RS
835 (define-key vc-prefix-map "~" 'vc-version-other-window)))
836
b662fbb8
RM
837(if (not (boundp 'vc-menu-map))
838 ;; Don't do the menu bindings if menu-bar.el wasn't loaded to defvar
839 ;; vc-menu-map.
840 ()
841 ;;(define-key vc-menu-map [show-files]
842 ;; '("Show Files under VC" . (vc-directory t)))
843 (define-key vc-menu-map [vc-directory] '("Show Locked Files" . vc-directory))
844 (define-key vc-menu-map [separator1] '("----"))
845 (define-key vc-menu-map [vc-rename-file] '("Rename File" . vc-rename-file))
846 (define-key vc-menu-map [vc-version-other-window]
847 '("Show Other Version" . vc-version-other-window))
848 (define-key vc-menu-map [vc-diff] '("Compare with Last Version" . vc-diff))
849 (define-key vc-menu-map [vc-update-change-log]
850 '("Update ChangeLog" . vc-update-change-log))
851 (define-key vc-menu-map [vc-print-log] '("Show History" . vc-print-log))
852 (define-key vc-menu-map [separator2] '("----"))
853 (define-key vc-menu-map [undo] '("Undo Last Check-In" . vc-cancel-version))
854 (define-key vc-menu-map [vc-revert-buffer]
855 '("Revert to Last Version" . vc-revert-buffer))
856 (define-key vc-menu-map [vc-insert-header]
857 '("Insert Header" . vc-insert-headers))
858 (define-key vc-menu-map [vc-menu-check-in] '("Check In" . vc-next-action))
859 (define-key vc-menu-map [vc-check-out] '("Check Out" . vc-toggle-read-only))
860 (define-key vc-menu-map [vc-register] '("Register" . vc-register))
861 (put 'vc-rename-file 'menu-enable 'vc-mode)
862 (put 'vc-version-other-window 'menu-enable 'vc-mode)
863 (put 'vc-diff 'menu-enable 'vc-mode)
864 (put 'vc-update-change-log 'menu-enable
f2ee4191 865 '(eq (vc-buffer-backend) 'RCS))
b662fbb8
RM
866 (put 'vc-print-log 'menu-enable 'vc-mode)
867 (put 'vc-cancel-version 'menu-enable 'vc-mode)
868 (put 'vc-revert-buffer 'menu-enable 'vc-mode)
869 (put 'vc-insert-headers 'menu-enable 'vc-mode)
870 (put 'vc-next-action 'menu-enable '(and vc-mode (not buffer-read-only)))
871 (put 'vc-toggle-read-only 'menu-enable '(and vc-mode buffer-read-only))
76a8bf4c 872 (put 'vc-register 'menu-enable '(and buffer-file-name (not vc-mode)))
b662fbb8 873 )
594722a8 874
594722a8
ER
875(provide 'vc-hooks)
876
877;;; vc-hooks.el ends here