(gud-make-debug-menu): Cope if no local map yet.
[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))))))
3be2a362
RS
311 (if (get-buffer "*vc-info*")
312 (kill-buffer (get-buffer "*vc-info*")))))
e1c0c2d1
KH
313
314;;; Functions that determine property values, by examining the
315;;; working file, the master file, or log program output
316
317(defun vc-consult-rcs-headers (file)
318 ;; Search for RCS headers in FILE, and set properties
319 ;; accordingly. This function can be disabled by setting
320 ;; vc-consult-headers to nil.
321 ;; Returns: nil if no headers were found
322 ;; (or if the feature is disabled,
323 ;; or if there is currently no buffer
324 ;; visiting FILE)
325 ;; 'rev if a workfile revision was found
326 ;; 'rev-and-lock if revision and lock info was found
327 (cond
328 ((or (not vc-consult-headers)
02d383eb 329 (not (get-file-buffer file))) nil)
e1c0c2d1
KH
330 ((save-excursion
331 (set-buffer (get-file-buffer file))
332 (goto-char (point-min))
333 (cond
334 ;; search for $Id or $Header
335 ;; -------------------------
1efcbf46
RS
336 ((or (and (search-forward "$Id: " nil t)
337 (looking-at "[^ ]+ \\([0-9.]+\\) "))
338 (and (progn (goto-char (point-min))
fceee007 339 (search-forward "$Header: " nil t))
1efcbf46 340 (looking-at "[^ ]+ \\([0-9.]+\\) ")))
04094290 341 (goto-char (match-end 0))
e1c0c2d1 342 ;; if found, store the revision number ...
1efcbf46
RS
343 (let ((rev (buffer-substring (match-beginning 1)
344 (match-end 1))))
e1c0c2d1
KH
345 ;; ... and check for the locking state
346 (if (re-search-forward
347 (concat "\\=[0-9]+/[0-9]+/[0-9]+ " ; date
348 "[0-9]+:[0-9]+:[0-9]+ " ; time
349 "[^ ]+ [^ ]+ ") ; author & state
350 nil t)
351 (cond
352 ;; unlocked revision
353 ((looking-at "\\$")
354 (vc-file-setprop file 'vc-workfile-version rev)
02d383eb 355 (vc-file-setprop file 'vc-locking-user 'none)
e1c0c2d1
KH
356 'rev-and-lock)
357 ;; revision is locked by some user
358 ((looking-at "\\([^ ]+\\) \\$")
359 (vc-file-setprop file 'vc-workfile-version rev)
360 (vc-file-setprop file 'vc-locking-user
361 (buffer-substring (match-beginning 1)
362 (match-end 1)))
e1c0c2d1
KH
363 'rev-and-lock)
364 ;; everything else: false
365 (nil))
366 ;; unexpected information in
367 ;; keyword string --> quit
368 nil)))
369 ;; search for $Revision
370 ;; --------------------
371 ((re-search-forward (concat "\\$"
372 "Revision: \\([0-9.]+\\) \\$")
373 nil t)
374 ;; if found, store the revision number ...
375 (let ((rev (buffer-substring (match-beginning 1)
376 (match-end 1))))
377 ;; and see if there's any lock information
378 (goto-char (point-min))
379 (if (re-search-forward (concat "\\$" "Locker:") nil t)
380 (cond ((looking-at " \\([^ ]+\\) \\$")
381 (vc-file-setprop file 'vc-workfile-version rev)
382 (vc-file-setprop file 'vc-locking-user
383 (buffer-substring (match-beginning 1)
384 (match-end 1)))
e1c0c2d1
KH
385 'rev-and-lock)
386 ((looking-at " *\\$")
387 (vc-file-setprop file 'vc-workfile-version rev)
02d383eb 388 (vc-file-setprop file 'vc-locking-user 'none)
e1c0c2d1
KH
389 'rev-and-lock)
390 (t
391 (vc-file-setprop file 'vc-workfile-version rev)
02d383eb 392 (vc-file-setprop file 'vc-locking-user 'none)
e1c0c2d1
KH
393 'rev-and-lock))
394 (vc-file-setprop file 'vc-workfile-version rev)
395 'rev)))
396 ;; else: nothing found
397 ;; -------------------
398 (t nil))))))
399
02d383eb
RS
400;;; Access functions to file properties
401;;; (Properties should be _set_ using vc-file-setprop, but
402;;; _retrieved_ only through these functions, which decide
403;;; if the property is already known or not. A property should
404;;; only be retrieved by vc-file-getprop if there is no
405;;; access function.)
406
407;;; properties indicating the backend
408;;; being used for FILE
e1c0c2d1
KH
409
410(defun vc-backend-subdirectory-name (&optional file)
411 ;; Where the master and lock files for the current directory are kept
412 (symbol-name
413 (or
414 (and file (vc-backend file))
415 vc-default-back-end
416 (setq vc-default-back-end (if (vc-find-binary "rcs") 'RCS 'SCCS)))))
417
02d383eb
RS
418(defun vc-name (file)
419 "Return the master name of a file, nil if it is not registered."
420 (or (vc-file-getprop file 'vc-name)
421 (let ((name-and-type (vc-registered file)))
422 (if name-and-type
423 (progn
424 (vc-file-setprop file 'vc-backend (cdr name-and-type))
425 (vc-file-setprop file 'vc-name (car name-and-type)))))))
e1c0c2d1 426
02d383eb
RS
427(defun vc-backend (file)
428 "Return the version-control type of a file, nil if it is not registered."
429 (and file
430 (or (vc-file-getprop file 'vc-backend)
431 (let ((name-and-type (vc-registered file)))
432 (if name-and-type
433 (progn
434 (vc-file-setprop file 'vc-name (car name-and-type))
435 (vc-file-setprop file 'vc-backend (cdr name-and-type))))))))
e1c0c2d1 436
02d383eb 437;;; properties indicating the locking state
e1c0c2d1
KH
438
439(defun vc-cvs-status (file)
440 ;; Return the cvs status of FILE
441 ;; (Status field in output of "cvs status")
442 (cond ((vc-file-getprop file 'vc-cvs-status))
02d383eb 443 (t (vc-fetch-master-properties file)
e1c0c2d1
KH
444 (vc-file-getprop file 'vc-cvs-status))))
445
02d383eb
RS
446(defun vc-master-locks (file)
447 ;; Return the lock entries in the master of FILE.
448 ;; Return 'none if there are no such entries, and a list
449 ;; of the form ((VERSION USER) (VERSION USER) ...) otherwise.
450 (cond ((vc-file-getprop file 'vc-master-locks))
451 (t (vc-fetch-master-properties file)
452 (vc-file-getprop file 'vc-master-locks))))
453
454(defun vc-master-locking-user (file)
455 ;; Return the master file's idea of who is locking
456 ;; the current workfile version of FILE.
457 ;; Return 'none if it is not locked.
458 (let ((master-locks (vc-master-locks file)) lock)
459 (if (eq master-locks 'none) 'none
460 ;; search for a lock on the current workfile version
461 (setq lock (assoc (vc-workfile-version file) master-locks))
462 (cond (lock (cdr lock))
463 ('none)))))
464
e1c0c2d1 465(defun vc-locking-user (file)
02d383eb
RS
466 ;; Return the name of the person currently holding a lock on FILE.
467 ;; Return nil if there is no such person.
468 ;; Under CVS, a file is considered locked if it has been modified since
469 ;; it was checked out. Under CVS, this will sometimes return the uid of
470 ;; the owner of the file (as a number) instead of a string.
471 ;; The property is cached. It is only looked up if it is currently nil.
472 ;; Note that, for a file that is not locked, the actual property value
473 ;; is 'none, to distinguish it from an unknown locking state. That value
474 ;; is converted to nil by this function, and returned to the caller.
475 (let ((locking-user (vc-file-getprop file 'vc-locking-user)))
476 (if locking-user
477 ;; if we already know the property, return it
478 (if (eq locking-user 'none) nil locking-user)
479
480 ;; otherwise, infer the property...
481 (cond
482 ;; in the CVS case, check the status
483 ((eq (vc-backend file) 'CVS)
484 (if (eq (vc-cvs-status file) 'up-to-date)
485 (vc-file-setprop file 'vc-locking-user 'none)
486 ;; The expression below should return the username of the owner
487 ;; of the file. It doesn't. It returns the username if it is
488 ;; you, or otherwise the UID of the owner of the file. The
489 ;; return value from this function is only used by
490 ;; vc-dired-reformat-line, and it does the proper thing if a UID
491 ;; is returned.
492 ;;
493 ;; The *proper* way to fix this would be to implement a built-in
494 ;; function in Emacs, say, (username UID), that returns the
495 ;; username of a given UID.
496 ;;
497 ;; The result of this hack is that vc-directory will print the
498 ;; name of the owner of the file for any files that are
499 ;; modified.
500 (let ((uid (nth 2 (file-attributes file))))
501 (if (= uid (user-uid))
502 (vc-file-setprop file 'vc-locking-user (user-login-name))
503 (vc-file-setprop file 'vc-locking-user uid)))))
504
505 ;; RCS case: attempt a header search. If this feature is
506 ;; disabled, vc-consult-rcs-headers always returns nil.
507 ((and (eq (vc-backend file) 'RCS)
508 (eq (vc-consult-rcs-headers file) 'rev-and-lock)))
509
510 ;; if the file permissions are not trusted,
511 ;; use the information from the master file
512 ((or (not vc-keep-workfiles)
513 (eq vc-mistrust-permissions 't)
514 (and vc-mistrust-permissions
515 (funcall vc-mistrust-permissions
516 (vc-backend-subdirectory-name file))))
517 (vc-file-setprop file 'vc-locking-user (vc-master-locking-user file)))
518
519 ;; Otherwise: Use the file permissions. (But if it turns out that the
520 ;; file is not owned by the user, use the master file.)
521 ;; This implementation assumes that any file which is under version
522 ;; control and has -rw-r--r-- is locked by its owner. This is true
523 ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--.
524 ;; We have to be careful not to exclude files with execute bits on;
525 ;; scripts can be under version control too. Also, we must ignore the
526 ;; group-read and other-read bits, since paranoid users turn them off.
527 ;; This hack wins because calls to the somewhat expensive
528 ;; `vc-fetch-master-properties' function only have to be made if
529 ;; (a) the file is locked by someone other than the current user,
530 ;; or (b) some untoward manipulation behind vc's back has changed
531 ;; the owner or the `group' or `other' write bits.
532 (t
533 (let ((attributes (file-attributes file)))
534 (cond ((string-match ".r-..-..-." (nth 8 attributes))
535 (vc-file-setprop file 'vc-locking-user 'none))
536 ((and (= (nth 2 attributes) (user-uid))
537 (string-match ".rw..-..-." (nth 8 attributes)))
538 (vc-file-setprop file 'vc-locking-user (user-login-name)))
539 (t
540 (vc-file-setprop file 'vc-locking-user
541 (vc-master-locking-user file))))
542 )))
543 ;; recursively call the function again,
544 ;; to convert a possible 'none value
545 (vc-locking-user file))))
546
547;;; properties to store current and recent version numbers
e1c0c2d1
KH
548
549(defun vc-latest-version (file)
550 ;; Return version level of the latest version of FILE
02d383eb 551 (cond ((vc-file-getprop file 'vc-latest-version))
1efcbf46 552 (t (vc-fetch-properties file)
02d383eb 553 (vc-file-getprop file 'vc-latest-version))))
e1c0c2d1
KH
554
555(defun vc-your-latest-version (file)
556 ;; Return version level of the latest version of FILE checked in by you
02d383eb 557 (cond ((vc-file-getprop file 'vc-your-latest-version))
1efcbf46 558 (t (vc-fetch-properties file)
02d383eb 559 (vc-file-getprop file 'vc-your-latest-version))))
e1c0c2d1 560
02d383eb 561(defun vc-top-version (file)
e1c0c2d1
KH
562 ;; Return version level of the highest revision on the default branch
563 ;; If there is no default branch, return the highest version number
564 ;; on the trunk.
565 ;; This property is defined for RCS only.
02d383eb
RS
566 (cond ((vc-file-getprop file 'vc-top-version))
567 (t (vc-fetch-master-properties file)
568 (vc-file-getprop file 'vc-top-version))))
e1c0c2d1 569
1efcbf46
RS
570(defun vc-fetch-properties (file)
571 ;; Fetch vc-latest-version and vc-your-latest-version
572 ;; if that wasn't already done.
04094290
RS
573 (cond
574 ((eq (vc-backend file) 'RCS)
ee526b55
RS
575 (save-excursion
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))))
04094290 586 (t (vc-fetch-master-properties file))
1efcbf46
RS
587 ))
588
e1c0c2d1
KH
589(defun vc-workfile-version (file)
590 ;; Return version level of the current workfile FILE
591 ;; This is attempted by first looking at the RCS keywords.
592 ;; If there are no keywords in the working file,
02d383eb 593 ;; vc-top-version is taken.
e1c0c2d1
KH
594 ;; Note that this property is cached, that is, it is only
595 ;; looked up if it is nil.
596 ;; For SCCS, this property is equivalent to vc-latest-version.
597 (cond ((vc-file-getprop file 'vc-workfile-version))
598 ((eq (vc-backend file) 'SCCS) (vc-latest-version file))
599 ((eq (vc-backend file) 'RCS)
600 (if (vc-consult-rcs-headers file)
601 (vc-file-getprop file 'vc-workfile-version)
02d383eb 602 (let ((rev (cond ((vc-top-version file))
e1c0c2d1
KH
603 ((vc-latest-version file)))))
604 (vc-file-setprop file 'vc-workfile-version rev)
605 rev)))
606 ((eq (vc-backend file) 'CVS)
607 (if (vc-consult-rcs-headers file) ;; CVS
608 (vc-file-getprop file 'vc-workfile-version)
609 (vc-find-cvs-master (file-name-directory file)
610 (file-name-nondirectory file))
611 (vc-file-getprop file 'vc-workfile-version)))))
f2ee4191 612
594722a8
ER
613;;; actual version-control code starts here
614
615(defun vc-registered (file)
18c8a18e
PE
616 (let (handler handlers)
617 (if (boundp 'file-name-handler-alist)
b993101e 618 (setq handler (find-file-name-handler file 'vc-registered)))
18c8a18e
PE
619 (if handler
620 (funcall handler 'vc-registered file)
621 ;; Search for a master corresponding to the given file
622 (let ((dirname (or (file-name-directory file) ""))
623 (basename (file-name-nondirectory file)))
624 (catch 'found
625 (mapcar
626 (function (lambda (s)
174edc13
RS
627 (if (atom s)
628 (funcall s dirname basename)
629 (let ((trial (format (car s) dirname basename)))
630 (if (and (file-exists-p trial)
631 ;; Make sure the file we found with name
632 ;; TRIAL is not the source file itself.
633 ;; That can happen with RCS-style names
634 ;; if the file name is truncated
635 ;; (e.g. to 14 chars). See if either
636 ;; directory or attributes differ.
637 (or (not (string= dirname
638 (file-name-directory trial)))
639 (not (equal
640 (file-attributes file)
641 (file-attributes trial)))))
642 (throw 'found (cons trial (cdr s))))))))
18c8a18e
PE
643 vc-master-templates)
644 nil)))))
594722a8 645
174edc13
RS
646(defun vc-find-cvs-master (dirname basename)
647 ;; Check if DIRNAME/BASENAME is handled by CVS.
648 ;; If it is, do a (throw 'found (cons MASTER 'CVS)).
649 ;; Note: If the file is ``cvs add''ed but not yet ``cvs commit''ed
650 ;; the MASTER will not actually exist yet. The other parts of VC
f2ee4191 651 ;; checks for this condition. This function returns nil if
174edc13
RS
652 ;; DIRNAME/BASENAME is not handled by CVS.
653 (if (and (file-directory-p (concat dirname "CVS/"))
04094290
RS
654 (file-readable-p (concat dirname "CVS/Entries"))
655 (file-readable-p (concat dirname "CVS/Repository")))
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