aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorAndré Spiegel2000-11-16 18:14:41 +0000
committerAndré Spiegel2000-11-16 18:14:41 +0000
commit8f98485f77bb76a93ea5b2370088837a54f7d4a2 (patch)
tree288aade07c362724b289e68c3a8cfa4355c4c5cc /lisp
parent4104194e1c28a2d8156dfebd1400542caf6f4ad0 (diff)
downloademacs-8f98485f77bb76a93ea5b2370088837a54f7d4a2.tar.gz
emacs-8f98485f77bb76a93ea5b2370088837a54f7d4a2.zip
Functions reordered.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/vc-cvs.el850
-rw-r--r--lisp/vc-rcs.el852
-rw-r--r--lisp/vc-sccs.el372
3 files changed, 1085 insertions, 989 deletions
diff --git a/lisp/vc-cvs.el b/lisp/vc-cvs.el
index b78d9c0829f..d761b6c625f 100644
--- a/lisp/vc-cvs.el
+++ b/lisp/vc-cvs.el
@@ -5,7 +5,7 @@
5;; Author: FSF (see vc.el for full credits) 5;; Author: FSF (see vc.el for full credits)
6;; Maintainer: Andre Spiegel <spiegel@gnu.org> 6;; Maintainer: Andre Spiegel <spiegel@gnu.org>
7 7
8;; $Id: vc-cvs.el,v 1.10 2000/11/16 15:29:40 spiegel Exp $ 8;; $Id: vc-cvs.el,v 1.11 2000/11/16 16:42:10 spiegel Exp $
9 9
10;; This file is part of GNU Emacs. 10;; This file is part of GNU Emacs.
11 11
@@ -28,6 +28,10 @@
28 28
29;;; Code: 29;;; Code:
30 30
31;;;
32;;; Customization options
33;;;
34
31(defcustom vc-cvs-register-switches nil 35(defcustom vc-cvs-register-switches nil
32 "*Extra switches for registering a file into CVS. 36 "*Extra switches for registering a file into CVS.
33A string or list of strings passed to the checkin program by 37A string or list of strings passed to the checkin program by
@@ -67,6 +71,22 @@ then VC only stays local for hosts that match it."
67 :version "21.1" 71 :version "21.1"
68 :group 'vc) 72 :group 'vc)
69 73
74
75;;;
76;;; Internal variables
77;;;
78
79(defvar vc-cvs-local-month-numbers
80 '(("Jan" . 1) ("Feb" . 2) ("Mar" . 3) ("Apr" . 4)
81 ("May" . 5) ("Jun" . 6) ("Jul" . 7) ("Aug" . 8)
82 ("Sep" . 9) ("Oct" . 10) ("Nov" . 11) ("Dec" . 12))
83 "Local association list of month numbers.")
84
85
86;;;
87;;; State-querying functions
88;;;
89
70;;;###autoload (defun vc-cvs-registered (f) 90;;;###autoload (defun vc-cvs-registered (f)
71;;;###autoload (when (file-readable-p (expand-file-name 91;;;###autoload (when (file-readable-p (expand-file-name
72;;;###autoload "CVS/Entries" (file-name-directory f))) 92;;;###autoload "CVS/Entries" (file-name-directory f)))
@@ -92,97 +112,6 @@ then VC only stays local for hosts that match it."
92 (t nil))) 112 (t nil)))
93 nil))) 113 nil)))
94 114
95(defun vc-cvs-stay-local-p (file)
96 "Return non-nil if VC should stay local when handling FILE."
97 (if vc-cvs-stay-local
98 (let* ((dirname (if (file-directory-p file)
99 (directory-file-name file)
100 (file-name-directory file)))
101 (prop
102 (or (vc-file-getprop dirname 'vc-cvs-stay-local-p)
103 (let ((rootname (expand-file-name "CVS/Root" dirname)))
104 (vc-file-setprop
105 dirname 'vc-cvs-stay-local-p
106 (when (file-readable-p rootname)
107 (with-temp-buffer
108 (vc-insert-file rootname)
109 (goto-char (point-min))
110 (if (looking-at "\\([^:]*\\):")
111 (if (not (stringp vc-cvs-stay-local))
112 'yes
113 (let ((hostname (match-string 1)))
114 (if (string-match vc-cvs-stay-local hostname)
115 'yes
116 'no)))
117 'no))))))))
118 (if (eq prop 'yes) t nil))))
119
120(defun vc-cvs-workfile-version (file)
121 "CVS-specific version of `vc-workfile-version'."
122 ;; There is no need to consult RCS headers under CVS, because we
123 ;; get the workfile version for free when we recognize that a file
124 ;; is registered in CVS.
125 (vc-cvs-registered file)
126 (vc-file-getprop file 'vc-workfile-version))
127
128(defun vc-cvs-checkout-model (file)
129 "CVS-specific version of `vc-checkout-model'."
130 (if (or (getenv "CVSREAD")
131 ;; If the file is not writable (despite CVSREAD being
132 ;; undefined), this is probably because the file is being
133 ;; "watched" by other developers.
134 ;; (If vc-mistrust-permissions was t, we actually shouldn't
135 ;; trust this, but there is no other way to learn this from CVS
136 ;; at the moment (version 1.9).)
137 (string-match "r-..-..-." (nth 8 (file-attributes file))))
138 'announce
139 'implicit))
140
141;; VC Dired functions
142
143(defun vc-cvs-dired-state-info (file)
144 "CVS-specific version of `vc-dired-state-info'."
145 (let* ((cvs-state (vc-state file))
146 (state (cond ((eq cvs-state 'edited) "modified")
147 ((eq cvs-state 'needs-patch) "patch")
148 ((eq cvs-state 'needs-merge) "merge")
149 ;; FIXME: those two states cannot occur right now
150 ((eq cvs-state 'unlocked-changes) "conflict")
151 ((eq cvs-state 'locally-added) "added")
152 )))
153 (if state (concat "(" state ")"))))
154
155(defun vc-cvs-parse-status (&optional full)
156 "Parse output of \"cvs status\" command in the current buffer.
157Set file properties accordingly. Unless FULL is t, parse only
158essential information."
159 (let (file status)
160 (goto-char (point-min))
161 (if (re-search-forward "^File: " nil t)
162 (cond
163 ((looking-at "no file") nil)
164 ((re-search-forward "\\=\\([^ \t]+\\)" nil t)
165 (setq file (expand-file-name (match-string 1)))
166 (vc-file-setprop file 'vc-backend 'CVS)
167 (if (not (re-search-forward "\\=[ \t]+Status: \\(.*\\)" nil t))
168 (setq status "Unknown")
169 (setq status (match-string 1)))
170 (if (and full
171 (re-search-forward
172 "\\(RCS Version\\|RCS Revision\\|Repository revision\\):\
173\[\t ]+\\([0-9.]+\\)"
174 nil t))
175 (vc-file-setprop file 'vc-latest-version (match-string 2)))
176 (cond
177 ((string-match "Up-to-date" status)
178 (vc-file-setprop file 'vc-checkout-time
179 (nth 5 (file-attributes file)))
180 'up-to-date)
181 ((string-match "Locally Modified" status) 'edited)
182 ((string-match "Needs Merge" status) 'needs-merge)
183 ((string-match "Needs \\(Checkout\\|Patch\\)" status) 'needs-patch)
184 (t 'edited)))))))
185
186(defun vc-cvs-state (file) 115(defun vc-cvs-state (file)
187 "CVS-specific version of `vc-state'." 116 "CVS-specific version of `vc-state'."
188 (if (vc-cvs-stay-local-p file) 117 (if (vc-cvs-stay-local-p file)
@@ -207,6 +136,50 @@ essential information."
207 'up-to-date 136 'up-to-date
208 'edited))) 137 'edited)))
209 138
139(defun vc-cvs-dir-state (dir)
140 "Find the CVS state of all files in DIR."
141 (if (vc-cvs-stay-local-p dir)
142 (vc-cvs-dir-state-heuristic dir)
143 (let ((default-directory dir))
144 ;; Don't specify DIR in this command, the default-directory is
145 ;; enough. Otherwise it might fail with remote repositories.
146 (with-temp-buffer
147 (vc-do-command t 0 "cvs" nil "status" "-l")
148 (goto-char (point-min))
149 (while (re-search-forward "^=+\n\\([^=\n].*\n\\|\n\\)+" nil t)
150 (narrow-to-region (match-beginning 0) (match-end 0))
151 (vc-cvs-parse-status)
152 (goto-char (point-max))
153 (widen))))))
154
155(defun vc-cvs-workfile-version (file)
156 "CVS-specific version of `vc-workfile-version'."
157 ;; There is no need to consult RCS headers under CVS, because we
158 ;; get the workfile version for free when we recognize that a file
159 ;; is registered in CVS.
160 (vc-cvs-registered file)
161 (vc-file-getprop file 'vc-workfile-version))
162
163(defun vc-cvs-latest-on-branch-p (file)
164 "Return t iff current workfile version of FILE is the latest on its branch."
165 ;; Since this is only used as a sanity check for vc-cancel-version,
166 ;; and that is not supported under CVS at all, we can safely return t here.
167 ;; TODO: Think of getting rid of this altogether.
168 t)
169
170(defun vc-cvs-checkout-model (file)
171 "CVS-specific version of `vc-checkout-model'."
172 (if (or (getenv "CVSREAD")
173 ;; If the file is not writable (despite CVSREAD being
174 ;; undefined), this is probably because the file is being
175 ;; "watched" by other developers.
176 ;; (If vc-mistrust-permissions was t, we actually shouldn't
177 ;; trust this, but there is no other way to learn this from CVS
178 ;; at the moment (version 1.9).)
179 (string-match "r-..-..-." (nth 8 (file-attributes file))))
180 'announce
181 'implicit))
182
210(defun vc-cvs-mode-line-string (file) 183(defun vc-cvs-mode-line-string (file)
211 "Return string for placement into the modeline for FILE. 184 "Return string for placement into the modeline for FILE.
212Compared to the default implementation, this function handles the 185Compared to the default implementation, this function handles the
@@ -227,288 +200,54 @@ special case of a CVS file that is added but not yet comitted."
227 ;; for 'needs-patch and 'needs-merge. 200 ;; for 'needs-patch and 'needs-merge.
228 (concat "CVS:" rev))))) 201 (concat "CVS:" rev)))))
229 202
230(defun vc-cvs-dir-state (dir) 203(defun vc-cvs-dired-state-info (file)
231 "Find the CVS state of all files in DIR." 204 "CVS-specific version of `vc-dired-state-info'."
232 (if (vc-cvs-stay-local-p dir) 205 (let* ((cvs-state (vc-state file))
233 (vc-cvs-dir-state-heuristic dir) 206 (state (cond ((eq cvs-state 'edited) "modified")
234 (let ((default-directory dir)) 207 ((eq cvs-state 'needs-patch) "patch")
235 ;; Don't specify DIR in this command, the default-directory is 208 ((eq cvs-state 'needs-merge) "merge")
236 ;; enough. Otherwise it might fail with remote repositories. 209 ;; FIXME: those two states cannot occur right now
237 (with-temp-buffer 210 ((eq cvs-state 'unlocked-changes) "conflict")
238 (vc-do-command t 0 "cvs" nil "status" "-l") 211 ((eq cvs-state 'locally-added) "added")
239 (goto-char (point-min)) 212 )))
240 (while (re-search-forward "^=+\n\\([^=\n].*\n\\|\n\\)+" nil t) 213 (if state (concat "(" state ")"))))
241 (narrow-to-region (match-beginning 0) (match-end 0))
242 (vc-cvs-parse-status)
243 (goto-char (point-max))
244 (widen))))))
245
246(defun vc-cvs-dir-state-heuristic (dir)
247 "Find the CVS state of all files in DIR, using only local information."
248 (with-temp-buffer
249 (vc-insert-file (expand-file-name "CVS/Entries" dir))
250 (goto-char (point-min))
251 (while (not (eobp))
252 (when (looking-at "/\\([^/]*\\)/")
253 (let ((file (expand-file-name (match-string 1) dir)))
254 (unless (vc-file-getprop file 'vc-state)
255 (vc-cvs-parse-entry file t))))
256 (forward-line 1))))
257 214
258(defun vc-cvs-parse-entry (file &optional set-state)
259 "Parse a line from CVS/Entries.
260Compare modification time to that of the FILE, set file properties
261accordingly. However, `vc-state' is set only if optional arg SET-STATE
262is non-nil."
263 (cond
264 ;; entry for a "locally added" file (not yet committed)
265 ((looking-at "/[^/]+/0/")
266 (vc-file-setprop file 'vc-checkout-time 0)
267 (vc-file-setprop file 'vc-workfile-version "0")
268 (if set-state (vc-file-setprop file 'vc-state 'edited)))
269 ;; normal entry
270 ((looking-at
271 (concat "/[^/]+"
272 ;; revision
273 "/\\([^/]*\\)"
274 ;; timestamp
275 "/[A-Z][a-z][a-z]" ;; week day (irrelevant)
276 " \\([A-Z][a-z][a-z]\\)" ;; month name
277 " *\\([0-9]*\\)" ;; day of month
278 " \\([0-9]*\\):\\([0-9]*\\):\\([0-9]*\\)" ;; hms
279 " \\([0-9]*\\)" ;; year
280 ;; optional conflict field
281 "\\(+[^/]*\\)?/"))
282 (vc-file-setprop file 'vc-workfile-version (match-string 1))
283 ;; compare checkout time and modification time
284 (let ((second (string-to-number (match-string 6)))
285 (minute (string-to-number (match-string 5)))
286 (hour (string-to-number (match-string 4)))
287 (day (string-to-number (match-string 3)))
288 (year (string-to-number (match-string 7)))
289 (month (/ (string-match
290 (match-string 2)
291 "xxxJanFebMarAprMayJunJulAugSepOctNovDec")
292 3))
293 (mtime (nth 5 (file-attributes file))))
294 (cond ((equal mtime
295 (encode-time second minute hour day month year 0))
296 (vc-file-setprop file 'vc-checkout-time mtime)
297 (if set-state (vc-file-setprop file 'vc-state 'up-to-date)))
298 (t
299 (vc-file-setprop file 'vc-checkout-time 0)
300 (if set-state (vc-file-setprop file 'vc-state 'edited))))))
301 ;; entry with arbitrary text as timestamp
302 ;; (this means we should consider it modified)
303 ((looking-at
304 (concat "/[^/]+"
305 ;; revision
306 "/\\([^/]*\\)"
307 ;; timestamp (arbitrary text)
308 "/[^/]*"
309 ;; optional conflict field
310 "\\(+[^/]*\\)?/"))
311 (vc-file-setprop file 'vc-workfile-version (match-string 1))
312 (vc-file-setprop file 'vc-checkout-time 0)
313 (if set-state (vc-file-setprop file 'vc-state 'edited)))))
314 215
315(defun vc-cvs-print-log (file) 216;;;
316 "Get change log associated with FILE." 217;;; State-changing functions
317 (vc-do-command t (if (vc-cvs-stay-local-p file) 'async 0) 218;;;
318 "cvs" file "log"))
319
320(defun vc-cvs-show-log-entry (version)
321 (when (re-search-forward
322 ;; also match some context, for safety
323 (concat "----\nrevision " version
324 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
325 ;; set the display window so that
326 ;; the whole log entry is displayed
327 (let (start end lines)
328 (beginning-of-line) (forward-line -1) (setq start (point))
329 (if (not (re-search-forward "^----*\nrevision" nil t))
330 (setq end (point-max))
331 (beginning-of-line) (forward-line -1) (setq end (point)))
332 (setq lines (count-lines start end))
333 (cond
334 ;; if the global information and this log entry fit
335 ;; into the window, display from the beginning
336 ((< (count-lines (point-min) end) (window-height))
337 (goto-char (point-min))
338 (recenter 0)
339 (goto-char start))
340 ;; if the whole entry fits into the window,
341 ;; display it centered
342 ((< (1+ lines) (window-height))
343 (goto-char start)
344 (recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
345 ;; otherwise (the entry is too large for the window),
346 ;; display from the start
347 (t
348 (goto-char start)
349 (recenter 0))))))
350
351(defun vc-cvs-create-snapshot (dir name branchp)
352 "Assign to DIR's current version a given NAME.
353If BRANCHP is non-nil, the name is created as a branch (and the current
354workspace is immediately moved to that new branch)."
355 (vc-do-command nil 0 "cvs" dir "tag" "-c" (if branchp "-b") name)
356 (when branchp (vc-do-command nil 0 "cvs" dir "update" "-r" name)))
357
358(defun vc-cvs-retrieve-snapshot (dir name update)
359 "Retrieve a snapshot at and below DIR.
360NAME is the name of the snapshot; if it is empty, do a `cvs update'.
361If UPDATE is non-nil, then update (resynch) any affected buffers."
362 (with-current-buffer (get-buffer-create "*vc*")
363 (let ((default-directory dir))
364 (erase-buffer)
365 (if (or (not name) (string= name ""))
366 (vc-do-command t 0 "cvs" nil "update")
367 (vc-do-command t 0 "cvs" nil "update" "-r" name))
368 (when update
369 (goto-char (point-min))
370 (while (not (eobp))
371 (if (looking-at "\\([CMUP]\\) \\(.*\\)")
372 (let* ((file (expand-file-name (match-string 2) dir))
373 (state (match-string 1))
374 (buffer (find-buffer-visiting file)))
375 (when buffer
376 (cond
377 ((or (string= state "U")
378 (string= state "P"))
379 (vc-file-setprop file 'vc-state 'up-to-date)
380 (vc-file-setprop file 'vc-workfile-version nil)
381 (vc-file-setprop file 'vc-checkout-time
382 (nth 5 (file-attributes file))))
383 ((or (string= state "M")
384 (string= state "C"))
385 (vc-file-setprop file 'vc-state 'edited)
386 (vc-file-setprop file 'vc-workfile-version nil)
387 (vc-file-setprop file 'vc-checkout-time 0)))
388 (vc-resynch-buffer file t t))))
389 (forward-line 1))))))
390
391(defun vc-cvs-merge (file first-version &optional second-version)
392 "Merge changes into current working copy of FILE.
393The changes are between FIRST-VERSION and SECOND-VERSION."
394 (vc-do-command nil 0 "cvs" file
395 "update" "-kk"
396 (concat "-j" first-version)
397 (concat "-j" second-version))
398 (vc-file-setprop file 'vc-state 'edited)
399 (save-excursion
400 (set-buffer (get-buffer "*vc*"))
401 (goto-char (point-min))
402 (if (re-search-forward "conflicts during merge" nil t)
403 1 ; signal error
404 0))) ; signal success
405
406(defun vc-cvs-merge-news (file)
407 "Merge in any new changes made to FILE."
408 (message "Merging changes into %s..." file)
409 (save-excursion
410 ;; (vc-file-setprop file 'vc-workfile-version nil)
411 (vc-file-setprop file 'vc-checkout-time 0)
412 (vc-do-command nil 0 "cvs" file "update")
413 ;; Analyze the merge result reported by CVS, and set
414 ;; file properties accordingly.
415 (set-buffer (get-buffer "*vc*"))
416 (goto-char (point-min))
417 ;; get new workfile version
418 (if (re-search-forward (concat "^Merging differences between "
419 "[01234567890.]* and "
420 "\\([01234567890.]*\\) into")
421 nil t)
422 (vc-file-setprop file 'vc-workfile-version (match-string 1))
423 (vc-file-setprop file 'vc-workfile-version nil))
424 ;; get file status
425 (prog1
426 (if (eq (buffer-size) 0)
427 0 ;; there were no news; indicate success
428 (if (re-search-forward
429 (concat "^\\([CMUP] \\)?"
430 (regexp-quote (file-name-nondirectory file))
431 "\\( already contains the differences between \\)?")
432 nil t)
433 (cond
434 ;; Merge successful, we are in sync with repository now
435 ((or (match-string 2)
436 (string= (match-string 1) "U ")
437 (string= (match-string 1) "P "))
438 (vc-file-setprop file 'vc-state 'up-to-date)
439 (vc-file-setprop file 'vc-checkout-time
440 (nth 5 (file-attributes file)))
441 0);; indicate success to the caller
442 ;; Merge successful, but our own changes are still in the file
443 ((string= (match-string 1) "M ")
444 (vc-file-setprop file 'vc-state 'edited)
445 0);; indicate success to the caller
446 ;; Conflicts detected!
447 (t
448 (vc-file-setprop file 'vc-state 'edited)
449 1);; signal the error to the caller
450 )
451 (pop-to-buffer "*vc*")
452 (error "Couldn't analyze cvs update result")))
453 (message "Merging changes into %s...done" file))))
454 219
455(defun vc-cvs-check-headers () 220(defun vc-cvs-register (file &optional rev comment)
456 "Check if the current file has any headers in it." 221 "Register FILE into the CVS version-control system.
457 (save-excursion 222COMMENT can be used to provide an initial description of FILE.
458 (goto-char (point-min))
459 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
460\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
461
462(defun vc-cvs-steal (file &optional rev)
463 "Steal the lock on the current workfile for FILE and revision REV.
464Inappropriate for CVS"
465 (error "You cannot steal a CVS lock; there are no CVS locks to steal"))
466
467;; vc-check `not reached' for CVS.
468 223
469(defun vc-cvs-revert (file) 224`vc-register-switches' and `vc-cvs-register-switches' are passed to
470 "Revert FILE to the version it was based on." 225the CVS command (in that order)."
471 ;; Check out via standard output (caused by the final argument 226 (let ((switches (list
472 ;; FILE below), so that no sticky tag is set. 227 (if (stringp vc-register-switches)
473 (vc-cvs-checkout file nil (vc-workfile-version file) file) 228 (list vc-register-switches)
474 ;; If "cvs edit" was used to make the file writable, 229 vc-register-switches)
475 ;; call "cvs unedit" now to undo that. 230 (if (stringp vc-cvs-register-switches)
476 (if (and (not (eq (vc-cvs-checkout-model file) 'implicit)) 231 (list vc-cvs-register-switches)
477 vc-cvs-use-edit) 232 vc-cvs-register-switches))))
478 (vc-do-command nil 0 "cvs" file "unedit"))) 233
234 (apply 'vc-do-command nil 0 "cvs" file
235 "add"
236 (and comment (string-match "[^\t\n ]" comment)
237 (concat "-m" comment))
238 switches)))
479 239
480(defun vc-cvs-diff (file &optional oldvers newvers) 240(defun vc-cvs-responsible-p (file)
481 "Get a difference report using CVS between two versions of FILE." 241 "Return non-nil if CVS thinks it is responsible for FILE."
482 (let (options status 242 (file-directory-p (expand-file-name "CVS"
483 (diff-switches-list (if (listp diff-switches) 243 (if (file-directory-p file)
484 diff-switches 244 file
485 (list diff-switches)))) 245 (file-name-directory file)))))
486 (if (string= (vc-workfile-version file) "0")
487 ;; This file is added but not yet committed; there is no master file.
488 (if (or oldvers newvers)
489 (error "No revisions of %s exist" file)
490 ;; we regard this as "changed".
491 ;; diff it against /dev/null.
492 (apply 'vc-do-command t
493 1 "diff" file
494 (append diff-switches-list '("/dev/null"))))
495 (setq status
496 (apply 'vc-do-command t
497 (if (vc-cvs-stay-local-p file) 'async 1)
498 "cvs" file "diff"
499 (and oldvers (concat "-r" oldvers))
500 (and newvers (concat "-r" newvers))
501 diff-switches-list))
502 (if (vc-cvs-stay-local-p file)
503 1 ;; async diff, pessimistic assumption
504 status))))
505 246
506(defun vc-cvs-latest-on-branch-p (file) 247(defun vc-cvs-could-register (file)
507 "Return t iff current workfile version of FILE is the latest on its branch." 248 "Return non-nil if FILE could be registered in CVS.
508 ;; Since this is only used as a sanity check for vc-cancel-version, 249This is only possible if CVS is responsible for FILE's directory."
509 ;; and that is not supported under CVS at all, we can safely return t here. 250 (vc-cvs-responsible-p file))
510 ;; TODO: Think of getting rid of this altogether.
511 t)
512 251
513(defun vc-cvs-checkin (file rev comment) 252(defun vc-cvs-checkin (file rev comment)
514 "CVS-specific version of `vc-backend-checkin'." 253 "CVS-specific version of `vc-backend-checkin'."
@@ -553,42 +292,6 @@ Inappropriate for CVS"
553 ;; if this was an explicit check-in, remove the sticky tag 292 ;; if this was an explicit check-in, remove the sticky tag
554 (if rev (vc-do-command t 0 "cvs" file "update" "-A")))) 293 (if rev (vc-do-command t 0 "cvs" file "update" "-A"))))
555 294
556(defun vc-cvs-responsible-p (file)
557 "Return non-nil if CVS thinks it is responsible for FILE."
558 (file-directory-p (expand-file-name "CVS"
559 (if (file-directory-p file)
560 file
561 (file-name-directory file)))))
562
563(defun vc-cvs-could-register (file)
564 "Return non-nil if FILE could be registered in CVS.
565This is only possible if CVS is responsible for FILE's directory."
566 (vc-cvs-responsible-p file))
567
568(defun vc-cvs-make-version-backups-p (file)
569 "Return non-nil if version backups should be made for FILE."
570 (vc-cvs-stay-local-p file))
571
572(defun vc-cvs-register (file &optional rev comment)
573 "Register FILE into the CVS version-control system.
574COMMENT can be used to provide an initial description of FILE.
575
576`vc-register-switches' and `vc-cvs-register-switches' are passed to
577the CVS command (in that order)."
578 (let ((switches (list
579 (if (stringp vc-register-switches)
580 (list vc-register-switches)
581 vc-register-switches)
582 (if (stringp vc-cvs-register-switches)
583 (list vc-cvs-register-switches)
584 vc-cvs-register-switches))))
585
586 (apply 'vc-do-command nil 0 "cvs" file
587 "add"
588 (and comment (string-match "[^\t\n ]" comment)
589 (concat "-m" comment))
590 switches)))
591
592(defun vc-cvs-checkout (file &optional writable rev workfile) 295(defun vc-cvs-checkout (file &optional writable rev workfile)
593 "Retrieve a revision of FILE into a WORKFILE. 296 "Retrieve a revision of FILE into a WORKFILE.
594WRITABLE non-nil means that the file should be writable. 297WRITABLE non-nil means that the file should be writable.
@@ -670,18 +373,154 @@ REV is the revision to check out into WORKFILE."
670 (vc-mode-line file) 373 (vc-mode-line file)
671 (message "Checking out %s...done" filename))))) 374 (message "Checking out %s...done" filename)))))
672 375
376(defun vc-cvs-revert (file)
377 "Revert FILE to the version it was based on."
378 ;; Check out via standard output (caused by the final argument
379 ;; FILE below), so that no sticky tag is set.
380 (vc-cvs-checkout file nil (vc-workfile-version file) file)
381 ;; If "cvs edit" was used to make the file writable,
382 ;; call "cvs unedit" now to undo that.
383 (if (and (not (eq (vc-cvs-checkout-model file) 'implicit))
384 vc-cvs-use-edit)
385 (vc-do-command nil 0 "cvs" file "unedit")))
386
387(defun vc-cvs-merge (file first-version &optional second-version)
388 "Merge changes into current working copy of FILE.
389The changes are between FIRST-VERSION and SECOND-VERSION."
390 (vc-do-command nil 0 "cvs" file
391 "update" "-kk"
392 (concat "-j" first-version)
393 (concat "-j" second-version))
394 (vc-file-setprop file 'vc-state 'edited)
395 (save-excursion
396 (set-buffer (get-buffer "*vc*"))
397 (goto-char (point-min))
398 (if (re-search-forward "conflicts during merge" nil t)
399 1 ; signal error
400 0))) ; signal success
401
402(defun vc-cvs-merge-news (file)
403 "Merge in any new changes made to FILE."
404 (message "Merging changes into %s..." file)
405 (save-excursion
406 ;; (vc-file-setprop file 'vc-workfile-version nil)
407 (vc-file-setprop file 'vc-checkout-time 0)
408 (vc-do-command nil 0 "cvs" file "update")
409 ;; Analyze the merge result reported by CVS, and set
410 ;; file properties accordingly.
411 (set-buffer (get-buffer "*vc*"))
412 (goto-char (point-min))
413 ;; get new workfile version
414 (if (re-search-forward (concat "^Merging differences between "
415 "[01234567890.]* and "
416 "\\([01234567890.]*\\) into")
417 nil t)
418 (vc-file-setprop file 'vc-workfile-version (match-string 1))
419 (vc-file-setprop file 'vc-workfile-version nil))
420 ;; get file status
421 (prog1
422 (if (eq (buffer-size) 0)
423 0 ;; there were no news; indicate success
424 (if (re-search-forward
425 (concat "^\\([CMUP] \\)?"
426 (regexp-quote (file-name-nondirectory file))
427 "\\( already contains the differences between \\)?")
428 nil t)
429 (cond
430 ;; Merge successful, we are in sync with repository now
431 ((or (match-string 2)
432 (string= (match-string 1) "U ")
433 (string= (match-string 1) "P "))
434 (vc-file-setprop file 'vc-state 'up-to-date)
435 (vc-file-setprop file 'vc-checkout-time
436 (nth 5 (file-attributes file)))
437 0);; indicate success to the caller
438 ;; Merge successful, but our own changes are still in the file
439 ((string= (match-string 1) "M ")
440 (vc-file-setprop file 'vc-state 'edited)
441 0);; indicate success to the caller
442 ;; Conflicts detected!
443 (t
444 (vc-file-setprop file 'vc-state 'edited)
445 1);; signal the error to the caller
446 )
447 (pop-to-buffer "*vc*")
448 (error "Couldn't analyze cvs update result")))
449 (message "Merging changes into %s...done" file))))
450
451
452;;;
453;;; History functions
454;;;
455
456(defun vc-cvs-print-log (file)
457 "Get change log associated with FILE."
458 (vc-do-command t (if (vc-cvs-stay-local-p file) 'async 0)
459 "cvs" file "log"))
460
461(defun vc-cvs-show-log-entry (version)
462 (when (re-search-forward
463 ;; also match some context, for safety
464 (concat "----\nrevision " version
465 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
466 ;; set the display window so that
467 ;; the whole log entry is displayed
468 (let (start end lines)
469 (beginning-of-line) (forward-line -1) (setq start (point))
470 (if (not (re-search-forward "^----*\nrevision" nil t))
471 (setq end (point-max))
472 (beginning-of-line) (forward-line -1) (setq end (point)))
473 (setq lines (count-lines start end))
474 (cond
475 ;; if the global information and this log entry fit
476 ;; into the window, display from the beginning
477 ((< (count-lines (point-min) end) (window-height))
478 (goto-char (point-min))
479 (recenter 0)
480 (goto-char start))
481 ;; if the whole entry fits into the window,
482 ;; display it centered
483 ((< (1+ lines) (window-height))
484 (goto-char start)
485 (recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
486 ;; otherwise (the entry is too large for the window),
487 ;; display from the start
488 (t
489 (goto-char start)
490 (recenter 0))))))
491
492(defun vc-cvs-diff (file &optional oldvers newvers)
493 "Get a difference report using CVS between two versions of FILE."
494 (let (options status
495 (diff-switches-list (if (listp diff-switches)
496 diff-switches
497 (list diff-switches))))
498 (if (string= (vc-workfile-version file) "0")
499 ;; This file is added but not yet committed; there is no master file.
500 (if (or oldvers newvers)
501 (error "No revisions of %s exist" file)
502 ;; we regard this as "changed".
503 ;; diff it against /dev/null.
504 (apply 'vc-do-command t
505 1 "diff" file
506 (append diff-switches-list '("/dev/null"))))
507 (setq status
508 (apply 'vc-do-command t
509 (if (vc-cvs-stay-local-p file) 'async 1)
510 "cvs" file "diff"
511 (and oldvers (concat "-r" oldvers))
512 (and newvers (concat "-r" newvers))
513 diff-switches-list))
514 (if (vc-cvs-stay-local-p file)
515 1 ;; async diff, pessimistic assumption
516 status))))
517
673(defun vc-cvs-annotate-command (file buffer &optional version) 518(defun vc-cvs-annotate-command (file buffer &optional version)
674 "Execute \"cvs annotate\" on FILE, inserting the contents in BUFFER. 519 "Execute \"cvs annotate\" on FILE, inserting the contents in BUFFER.
675Optional arg VERSION is a version to annotate from." 520Optional arg VERSION is a version to annotate from."
676 (vc-do-command buffer 0 "cvs" file "annotate" (if version 521 (vc-do-command buffer 0 "cvs" file "annotate" (if version
677 (concat "-r" version)))) 522 (concat "-r" version))))
678 523
679(defvar vc-cvs-local-month-numbers
680 '(("Jan" . 1) ("Feb" . 2) ("Mar" . 3) ("Apr" . 4)
681 ("May" . 5) ("Jun" . 6) ("Jul" . 7) ("Aug" . 8)
682 ("Sep" . 9) ("Oct" . 10) ("Nov" . 11) ("Dec" . 12))
683 "Local association list of month numbers.")
684
685(defun vc-cvs-annotate-difference (point) 524(defun vc-cvs-annotate-difference (point)
686 "Return the difference between the time of the line and the current time. 525 "Return the difference between the time of the line and the current time.
687Return values are as defined for `current-time'." 526Return values are as defined for `current-time'."
@@ -709,6 +548,197 @@ Return values are as defined for `current-time'."
709 (beginning-of-line nil) 548 (beginning-of-line nil)
710 (vc-cvs-annotate-difference (point)))))) 549 (vc-cvs-annotate-difference (point))))))
711 550
551
552;;;
553;;; Snapshot system
554;;;
555
556(defun vc-cvs-create-snapshot (dir name branchp)
557 "Assign to DIR's current version a given NAME.
558If BRANCHP is non-nil, the name is created as a branch (and the current
559workspace is immediately moved to that new branch)."
560 (vc-do-command nil 0 "cvs" dir "tag" "-c" (if branchp "-b") name)
561 (when branchp (vc-do-command nil 0 "cvs" dir "update" "-r" name)))
562
563(defun vc-cvs-retrieve-snapshot (dir name update)
564 "Retrieve a snapshot at and below DIR.
565NAME is the name of the snapshot; if it is empty, do a `cvs update'.
566If UPDATE is non-nil, then update (resynch) any affected buffers."
567 (with-current-buffer (get-buffer-create "*vc*")
568 (let ((default-directory dir))
569 (erase-buffer)
570 (if (or (not name) (string= name ""))
571 (vc-do-command t 0 "cvs" nil "update")
572 (vc-do-command t 0 "cvs" nil "update" "-r" name))
573 (when update
574 (goto-char (point-min))
575 (while (not (eobp))
576 (if (looking-at "\\([CMUP]\\) \\(.*\\)")
577 (let* ((file (expand-file-name (match-string 2) dir))
578 (state (match-string 1))
579 (buffer (find-buffer-visiting file)))
580 (when buffer
581 (cond
582 ((or (string= state "U")
583 (string= state "P"))
584 (vc-file-setprop file 'vc-state 'up-to-date)
585 (vc-file-setprop file 'vc-workfile-version nil)
586 (vc-file-setprop file 'vc-checkout-time
587 (nth 5 (file-attributes file))))
588 ((or (string= state "M")
589 (string= state "C"))
590 (vc-file-setprop file 'vc-state 'edited)
591 (vc-file-setprop file 'vc-workfile-version nil)
592 (vc-file-setprop file 'vc-checkout-time 0)))
593 (vc-resynch-buffer file t t))))
594 (forward-line 1))))))
595
596
597;;;
598;;; Miscellaneous
599;;;
600
601(defun vc-cvs-make-version-backups-p (file)
602 "Return non-nil if version backups should be made for FILE."
603 (vc-cvs-stay-local-p file))
604
605(defun vc-cvs-check-headers ()
606 "Check if the current file has any headers in it."
607 (save-excursion
608 (goto-char (point-min))
609 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
610\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
611
612
613;;;
614;;; Internal functions
615;;;
616
617(defun vc-cvs-stay-local-p (file)
618 "Return non-nil if VC should stay local when handling FILE."
619 (if vc-cvs-stay-local
620 (let* ((dirname (if (file-directory-p file)
621 (directory-file-name file)
622 (file-name-directory file)))
623 (prop
624 (or (vc-file-getprop dirname 'vc-cvs-stay-local-p)
625 (let ((rootname (expand-file-name "CVS/Root" dirname)))
626 (vc-file-setprop
627 dirname 'vc-cvs-stay-local-p
628 (when (file-readable-p rootname)
629 (with-temp-buffer
630 (vc-insert-file rootname)
631 (goto-char (point-min))
632 (if (looking-at "\\([^:]*\\):")
633 (if (not (stringp vc-cvs-stay-local))
634 'yes
635 (let ((hostname (match-string 1)))
636 (if (string-match vc-cvs-stay-local hostname)
637 'yes
638 'no)))
639 'no))))))))
640 (if (eq prop 'yes) t nil))))
641
642(defun vc-cvs-parse-status (&optional full)
643 "Parse output of \"cvs status\" command in the current buffer.
644Set file properties accordingly. Unless FULL is t, parse only
645essential information."
646 (let (file status)
647 (goto-char (point-min))
648 (if (re-search-forward "^File: " nil t)
649 (cond
650 ((looking-at "no file") nil)
651 ((re-search-forward "\\=\\([^ \t]+\\)" nil t)
652 (setq file (expand-file-name (match-string 1)))
653 (vc-file-setprop file 'vc-backend 'CVS)
654 (if (not (re-search-forward "\\=[ \t]+Status: \\(.*\\)" nil t))
655 (setq status "Unknown")
656 (setq status (match-string 1)))
657 (if (and full
658 (re-search-forward
659 "\\(RCS Version\\|RCS Revision\\|Repository revision\\):\
660\[\t ]+\\([0-9.]+\\)"
661 nil t))
662 (vc-file-setprop file 'vc-latest-version (match-string 2)))
663 (cond
664 ((string-match "Up-to-date" status)
665 (vc-file-setprop file 'vc-checkout-time
666 (nth 5 (file-attributes file)))
667 'up-to-date)
668 ((string-match "Locally Modified" status) 'edited)
669 ((string-match "Needs Merge" status) 'needs-merge)
670 ((string-match "Needs \\(Checkout\\|Patch\\)" status) 'needs-patch)
671 (t 'edited)))))))
672
673(defun vc-cvs-dir-state-heuristic (dir)
674 "Find the CVS state of all files in DIR, using only local information."
675 (with-temp-buffer
676 (vc-insert-file (expand-file-name "CVS/Entries" dir))
677 (goto-char (point-min))
678 (while (not (eobp))
679 (when (looking-at "/\\([^/]*\\)/")
680 (let ((file (expand-file-name (match-string 1) dir)))
681 (unless (vc-file-getprop file 'vc-state)
682 (vc-cvs-parse-entry file t))))
683 (forward-line 1))))
684
685(defun vc-cvs-parse-entry (file &optional set-state)
686 "Parse a line from CVS/Entries.
687Compare modification time to that of the FILE, set file properties
688accordingly. However, `vc-state' is set only if optional arg SET-STATE
689is non-nil."
690 (cond
691 ;; entry for a "locally added" file (not yet committed)
692 ((looking-at "/[^/]+/0/")
693 (vc-file-setprop file 'vc-checkout-time 0)
694 (vc-file-setprop file 'vc-workfile-version "0")
695 (if set-state (vc-file-setprop file 'vc-state 'edited)))
696 ;; normal entry
697 ((looking-at
698 (concat "/[^/]+"
699 ;; revision
700 "/\\([^/]*\\)"
701 ;; timestamp
702 "/[A-Z][a-z][a-z]" ;; week day (irrelevant)
703 " \\([A-Z][a-z][a-z]\\)" ;; month name
704 " *\\([0-9]*\\)" ;; day of month
705 " \\([0-9]*\\):\\([0-9]*\\):\\([0-9]*\\)" ;; hms
706 " \\([0-9]*\\)" ;; year
707 ;; optional conflict field
708 "\\(+[^/]*\\)?/"))
709 (vc-file-setprop file 'vc-workfile-version (match-string 1))
710 ;; compare checkout time and modification time
711 (let ((second (string-to-number (match-string 6)))
712 (minute (string-to-number (match-string 5)))
713 (hour (string-to-number (match-string 4)))
714 (day (string-to-number (match-string 3)))
715 (year (string-to-number (match-string 7)))
716 (month (/ (string-match
717 (match-string 2)
718 "xxxJanFebMarAprMayJunJulAugSepOctNovDec")
719 3))
720 (mtime (nth 5 (file-attributes file))))
721 (cond ((equal mtime
722 (encode-time second minute hour day month year 0))
723 (vc-file-setprop file 'vc-checkout-time mtime)
724 (if set-state (vc-file-setprop file 'vc-state 'up-to-date)))
725 (t
726 (vc-file-setprop file 'vc-checkout-time 0)
727 (if set-state (vc-file-setprop file 'vc-state 'edited))))))
728 ;; entry with arbitrary text as timestamp
729 ;; (this means we should consider it modified)
730 ((looking-at
731 (concat "/[^/]+"
732 ;; revision
733 "/\\([^/]*\\)"
734 ;; timestamp (arbitrary text)
735 "/[^/]*"
736 ;; optional conflict field
737 "\\(+[^/]*\\)?/"))
738 (vc-file-setprop file 'vc-workfile-version (match-string 1))
739 (vc-file-setprop file 'vc-checkout-time 0)
740 (if set-state (vc-file-setprop file 'vc-state 'edited)))))
741
712(provide 'vc-cvs) 742(provide 'vc-cvs)
713 743
714;;; vc-cvs.el ends here 744;;; vc-cvs.el ends here
diff --git a/lisp/vc-rcs.el b/lisp/vc-rcs.el
index 920fc4c1360..35c09d6335f 100644
--- a/lisp/vc-rcs.el
+++ b/lisp/vc-rcs.el
@@ -5,7 +5,7 @@
5;; Author: FSF (see vc.el for full credits) 5;; Author: FSF (see vc.el for full credits)
6;; Maintainer: Andre Spiegel <spiegel@gnu.org> 6;; Maintainer: Andre Spiegel <spiegel@gnu.org>
7 7
8;; $Id: vc-rcs.el,v 1.10 2000/10/03 11:33:59 spiegel Exp $ 8;; $Id: vc-rcs.el,v 1.11 2000/10/03 12:08:40 spiegel Exp $
9 9
10;; This file is part of GNU Emacs. 10;; This file is part of GNU Emacs.
11 11
@@ -28,6 +28,10 @@
28 28
29;;; Code: 29;;; Code:
30 30
31;;;
32;;; Customization options
33;;;
34
31(eval-when-compile 35(eval-when-compile
32 (require 'cl)) 36 (require 'cl))
33 37
@@ -99,6 +103,11 @@ For a description of possible values, see `vc-check-master-templates'."
99 :version "21.1" 103 :version "21.1"
100 :group 'vc) 104 :group 'vc)
101 105
106
107;;;
108;;; State-querying functions
109;;;
110
102;;;###autoload 111;;;###autoload
103(progn (defun vc-rcs-registered (f) (vc-default-registered 'RCS f))) 112(progn (defun vc-rcs-registered (f) (vc-default-registered 'RCS f)))
104 113
@@ -164,16 +173,6 @@ For a description of possible values, see `vc-check-master-templates'."
164 (vc-rcs-state file)))) 173 (vc-rcs-state file))))
165 (vc-rcs-state file))))) 174 (vc-rcs-state file)))))
166 175
167(defun vc-rcs-workfile-is-newer (file)
168 "Return non-nil if FILE is newer than its RCS master.
169This likely means that FILE has been changed with respect
170to its master version."
171 (let ((file-time (nth 5 (file-attributes file)))
172 (master-time (nth 5 (file-attributes (vc-name file)))))
173 (or (> (nth 0 file-time) (nth 0 master-time))
174 (and (= (nth 0 file-time) (nth 0 master-time))
175 (> (nth 1 file-time) (nth 1 master-time))))))
176
177(defun vc-rcs-workfile-version (file) 176(defun vc-rcs-workfile-version (file)
178 "RCS-specific version of `vc-workfile-version'." 177 "RCS-specific version of `vc-workfile-version'."
179 (or (and vc-consult-headers 178 (or (and vc-consult-headers
@@ -183,6 +182,22 @@ to its master version."
183 (vc-rcs-fetch-master-state file) 182 (vc-rcs-fetch-master-state file)
184 (vc-file-getprop file 'vc-workfile-version)))) 183 (vc-file-getprop file 'vc-workfile-version))))
185 184
185(defun vc-rcs-latest-on-branch-p (file &optional version)
186 "Return non-nil if workfile version of FILE is the latest on its branch.
187When VERSION is given, perform check for that version."
188 (unless version (setq version (vc-workfile-version file)))
189 (with-temp-buffer
190 (string= version
191 (if (vc-rcs-trunk-p version)
192 (progn
193 ;; Compare VERSION to the head version number.
194 (vc-insert-file (vc-name file) "^[0-9]")
195 (vc-parse-buffer "^head[ \t\n]+\\([^;]+\\);" 1))
196 ;; If we are not on the trunk, we need to examine the
197 ;; whole current branch.
198 (vc-insert-file (vc-name file) "^desc")
199 (vc-rcs-find-most-recent-rev (vc-rcs-branch-part version))))))
200
186(defun vc-rcs-checkout-model (file) 201(defun vc-rcs-checkout-model (file)
187 "RCS-specific version of `vc-checkout-model'." 202 "RCS-specific version of `vc-checkout-model'."
188 (vc-rcs-consult-headers file) 203 (vc-rcs-consult-headers file)
@@ -190,7 +205,423 @@ to its master version."
190 (progn (vc-rcs-fetch-master-state file) 205 (progn (vc-rcs-fetch-master-state file)
191 (vc-file-getprop file 'vc-checkout-model)))) 206 (vc-file-getprop file 'vc-checkout-model))))
192 207
193;;; internal code 208(defun vc-rcs-workfile-unchanged-p (file)
209 "RCS-specific implementation of vc-workfile-unchanged-p."
210 ;; Try to use rcsdiff --brief. If rcsdiff does not understand that,
211 ;; do a double take and remember the fact for the future
212 (let* ((version (concat "-r" (vc-workfile-version file)))
213 (status (if (eq vc-rcsdiff-knows-brief 'no)
214 (vc-do-command nil 1 "rcsdiff" file version)
215 (vc-do-command nil 2 "rcsdiff" file "--brief" version))))
216 (if (eq status 2)
217 (if (not vc-rcsdiff-knows-brief)
218 (setq vc-rcsdiff-knows-brief 'no
219 status (vc-do-command nil 1 "rcsdiff" file version))
220 (error "rcsdiff failed"))
221 (if (not vc-rcsdiff-knows-brief) (setq vc-rcsdiff-knows-brief 'yes)))
222 ;; The workfile is unchanged if rcsdiff found no differences.
223 (zerop status)))
224
225
226;;;
227;;; State-changing functions
228;;;
229
230(defun vc-rcs-register (file &optional rev comment)
231 "Register FILE into the RCS version-control system.
232REV is the optional revision number for the file. COMMENT can be used
233to provide an initial description of FILE.
234
235`vc-register-switches' and `vc-rcs-register-switches' are passed to
236the RCS command (in that order).
237
238Automatically retrieve a read-only version of the file with keywords
239expanded if `vc-keep-workfiles' is non-nil, otherwise, delete the workfile."
240 (let ((subdir (expand-file-name "RCS" (file-name-directory file)))
241 (switches (list
242 (if (stringp vc-register-switches)
243 (list vc-register-switches)
244 vc-register-switches)
245 (if (stringp vc-rcs-register-switches)
246 (list vc-rcs-register-switches)
247 vc-rcs-register-switches))))
248
249 (and (not (file-exists-p subdir))
250 (not (directory-files (file-name-directory file)
251 nil ".*,v$" t))
252 (yes-or-no-p "Create RCS subdirectory? ")
253 (make-directory subdir))
254 (apply 'vc-do-command nil 0 "ci" file
255 ;; if available, use the secure registering option
256 (and (vc-rcs-release-p "5.6.4") "-i")
257 (concat (if vc-keep-workfiles "-u" "-r") rev)
258 (and comment (concat "-t-" comment))
259 switches)
260 ;; parse output to find master file name and workfile version
261 (with-current-buffer "*vc*"
262 (goto-char (point-min))
263 (let ((name (if (looking-at (concat "^\\(.*\\) <-- "
264 (file-name-nondirectory file)))
265 (match-string 1))))
266 (if (not name)
267 ;; if we couldn't find the master name,
268 ;; run vc-rcs-registered to get it
269 ;; (will be stored into the vc-name property)
270 (vc-rcs-registered file)
271 (vc-file-setprop file 'vc-name
272 (if (file-name-absolute-p name)
273 name
274 (expand-file-name
275 name
276 (file-name-directory file))))))
277 (vc-file-setprop file 'vc-workfile-version
278 (if (re-search-forward
279 "^initial revision: \\([0-9.]+\\).*\n"
280 nil t)
281 (match-string 1))))))
282
283(defun vc-rcs-responsible-p (file)
284 "Return non-nil if RCS thinks it would be responsible for registering FILE."
285 ;; TODO: check for all the patterns in vc-rcs-master-templates
286 (file-directory-p (expand-file-name "RCS" (file-name-directory file))))
287
288(defun vc-rcs-receive-file (file rev)
289 "Implementation of receive-file for RCS."
290 (let ((checkout-model (vc-checkout-model file)))
291 (vc-rcs-register file rev "")
292 (when (eq checkout-model 'implicit)
293 (vc-rcs-set-non-strict-locking file))
294 (vc-rcs-set-default-branch file (concat rev ".1"))))
295
296(defun vc-rcs-unregister (file)
297 "Unregister FILE from RCS.
298If this leaves the RCS subdirectory empty, ask the user
299whether to remove it."
300 (let* ((master (vc-name file))
301 (dir (file-name-directory master))
302 (backup-info (find-backup-file-name master)))
303 (if (not backup-info)
304 (delete-file master)
305 (rename-file master (car backup-info) 'ok-if-already-exists)
306 (dolist (f (cdr backup-info)) (ignore-errors (delete-file f))))
307 (and (string= (file-name-nondirectory (directory-file-name dir)) "RCS")
308 ;; check whether RCS dir is empty, i.e. it does not
309 ;; contain any files except "." and ".."
310 (not (directory-files dir nil
311 "^\\([^.]\\|\\.[^.]\\|\\.\\.[^.]\\).*"))
312 (yes-or-no-p (format "Directory %s is empty; remove it? " dir))
313 (delete-directory dir))))
314
315(defun vc-rcs-checkin (file rev comment)
316 "RCS-specific version of `vc-backend-checkin'."
317 (let ((switches (if (stringp vc-checkin-switches)
318 (list vc-checkin-switches)
319 vc-checkin-switches)))
320 (let ((old-version (vc-workfile-version file)) new-version
321 (default-branch (vc-file-getprop file 'vc-rcs-default-branch)))
322 ;; Force branch creation if an appropriate
323 ;; default branch has been set.
324 (and (not rev)
325 default-branch
326 (string-match (concat "^" (regexp-quote old-version) "\\.")
327 default-branch)
328 (setq rev default-branch)
329 (setq switches (cons "-f" switches)))
330 (apply 'vc-do-command nil 0 "ci" (vc-name file)
331 ;; if available, use the secure check-in option
332 (and (vc-rcs-release-p "5.6.4") "-j")
333 (concat (if vc-keep-workfiles "-u" "-r") rev)
334 (concat "-m" comment)
335 switches)
336 (vc-file-setprop file 'vc-workfile-version nil)
337
338 ;; determine the new workfile version
339 (set-buffer "*vc*")
340 (goto-char (point-min))
341 (when (or (re-search-forward
342 "new revision: \\([0-9.]+\\);" nil t)
343 (re-search-forward
344 "reverting to previous revision \\([0-9.]+\\)" nil t))
345 (setq new-version (match-string 1))
346 (vc-file-setprop file 'vc-workfile-version new-version))
347
348 ;; if we got to a different branch, adjust the default
349 ;; branch accordingly
350 (cond
351 ((and old-version new-version
352 (not (string= (vc-rcs-branch-part old-version)
353 (vc-rcs-branch-part new-version))))
354 (vc-rcs-set-default-branch file
355 (if (vc-rcs-trunk-p new-version) nil
356 (vc-rcs-branch-part new-version)))
357 ;; If this is an old RCS release, we might have
358 ;; to remove a remaining lock.
359 (if (not (vc-rcs-release-p "5.6.2"))
360 ;; exit status of 1 is also accepted.
361 ;; It means that the lock was removed before.
362 (vc-do-command nil 1 "rcs" (vc-name file)
363 (concat "-u" old-version))))))))
364
365(defun vc-rcs-checkout (file &optional writable rev workfile)
366 "Retrieve a copy of a saved version of FILE into a workfile."
367 (let ((filename (or workfile file))
368 (file-buffer (get-file-buffer file))
369 switches)
370 (message "Checking out %s..." filename)
371 (save-excursion
372 ;; Change buffers to get local value of vc-checkout-switches.
373 (if file-buffer (set-buffer file-buffer))
374 (setq switches (if (stringp vc-checkout-switches)
375 (list vc-checkout-switches)
376 vc-checkout-switches))
377 ;; Save this buffer's default-directory
378 ;; and use save-excursion to make sure it is restored
379 ;; in the same buffer it was saved in.
380 (let ((default-directory default-directory))
381 (save-excursion
382 ;; Adjust the default-directory so that the check-out creates
383 ;; the file in the right place.
384 (setq default-directory (file-name-directory filename))
385 (if workfile ;; RCS
386 ;; RCS can't check out into arbitrary file names directly.
387 ;; Use `co -p' and make stdout point to the correct file.
388 (let ((vc-modes (logior (file-modes (vc-name file))
389 (if writable 128 0)))
390 (failed t))
391 (unwind-protect
392 (progn
393 (let ((coding-system-for-read 'no-conversion)
394 (coding-system-for-write 'no-conversion))
395 (with-temp-file filename
396 (apply 'vc-do-command
397 (current-buffer) 0 "co" (vc-name file)
398 "-q" ;; suppress diagnostic output
399 (if writable "-l")
400 (concat "-p" rev)
401 switches)))
402 (set-file-modes filename
403 (logior (file-modes (vc-name file))
404 (if writable 128 0)))
405 (setq failed nil))
406 (and failed (file-exists-p filename)
407 (delete-file filename))))
408 (let (new-version)
409 ;; if we should go to the head of the trunk,
410 ;; clear the default branch first
411 (and rev (string= rev "")
412 (vc-rcs-set-default-branch file nil))
413 ;; now do the checkout
414 (apply 'vc-do-command
415 nil 0 "co" (vc-name file)
416 ;; If locking is not strict, force to overwrite
417 ;; the writable workfile.
418 (if (eq (vc-checkout-model file) 'implicit) "-f")
419 (if writable "-l")
420 (if rev (concat "-r" rev)
421 ;; if no explicit revision was specified,
422 ;; check out that of the working file
423 (let ((workrev (vc-workfile-version file)))
424 (if workrev (concat "-r" workrev)
425 nil)))
426 switches)
427 ;; determine the new workfile version
428 (with-current-buffer "*vc*"
429 (setq new-version
430 (vc-parse-buffer "^revision \\([0-9.]+\\).*\n" 1)))
431 (vc-file-setprop file 'vc-workfile-version new-version)
432 ;; if necessary, adjust the default branch
433 (and rev (not (string= rev ""))
434 (vc-rcs-set-default-branch
435 file
436 (if (vc-rcs-latest-on-branch-p file new-version)
437 (if (vc-rcs-trunk-p new-version) nil
438 (vc-rcs-branch-part new-version))
439 new-version))))))
440 (message "Checking out %s...done" filename)))))
441
442(defun vc-rcs-revert (file)
443 "Revert FILE to the version it was based on."
444 (vc-do-command nil 0 "co" (vc-name file) "-f"
445 (concat "-u" (vc-workfile-version file))))
446
447(defun vc-rcs-cancel-version (file writable)
448 "Undo the most recent checkin of FILE.
449WRITABLE non-nil means previous version should be locked."
450 (let* ((target (vc-workfile-version file))
451 (previous (if (vc-trunk-p target) "" (vc-branch-part target)))
452 (config (current-window-configuration))
453 (done nil))
454 (vc-do-command nil 0 "rcs" (vc-name file) (concat "-o" target))
455 ;; Check out the most recent remaining version. If it fails, because
456 ;; the whole branch got deleted, do a double-take and check out the
457 ;; version where the branch started.
458 (while (not done)
459 (condition-case err
460 (progn
461 (vc-do-command nil 0 "co" (vc-name file) "-f"
462 (concat (if writable "-l" "-u") previous))
463 (setq done t))
464 (error (set-buffer "*vc*")
465 (goto-char (point-min))
466 (if (search-forward "no side branches present for" nil t)
467 (progn (setq previous (vc-branch-part previous))
468 (vc-rcs-set-default-branch file previous)
469 ;; vc-do-command popped up a window with
470 ;; the error message. Get rid of it, by
471 ;; restoring the old window configuration.
472 (set-window-configuration config))
473 ;; No, it was some other error: re-signal it.
474 (signal (car err) (cdr err))))))))
475
476(defun vc-rcs-merge (file first-version &optional second-version)
477 "Merge changes into current working copy of FILE.
478The changes are between FIRST-VERSION and SECOND-VERSION."
479 (vc-do-command nil 1 "rcsmerge" (vc-name file)
480 "-kk" ; ignore keyword conflicts
481 (concat "-r" first-version)
482 (if second-version (concat "-r" second-version))))
483
484(defun vc-rcs-steal-lock (file &optional rev)
485 "Steal the lock on the current workfile for FILE and revision REV.
486Needs RCS 5.6.2 or later for -M."
487 (vc-do-command nil 0 "rcs" (vc-name file) "-M"
488 (concat "-u" rev) (concat "-l" rev)))
489
490
491
492;;;
493;;; History functions
494;;;
495
496(defun vc-rcs-print-log (file)
497 "Get change log associated with FILE."
498 (vc-do-command t 0 "rlog" (vc-name file)))
499
500(defun vc-rcs-show-log-entry (version)
501 (when (re-search-forward
502 ;; also match some context, for safety
503 (concat "----\nrevision " version
504 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
505 ;; set the display window so that
506 ;; the whole log entry is displayed
507 (let (start end lines)
508 (beginning-of-line) (forward-line -1) (setq start (point))
509 (if (not (re-search-forward "^----*\nrevision" nil t))
510 (setq end (point-max))
511 (beginning-of-line) (forward-line -1) (setq end (point)))
512 (setq lines (count-lines start end))
513 (cond
514 ;; if the global information and this log entry fit
515 ;; into the window, display from the beginning
516 ((< (count-lines (point-min) end) (window-height))
517 (goto-char (point-min))
518 (recenter 0)
519 (goto-char start))
520 ;; if the whole entry fits into the window,
521 ;; display it centered
522 ((< (1+ lines) (window-height))
523 (goto-char start)
524 (recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
525 ;; otherwise (the entry is too large for the window),
526 ;; display from the start
527 (t
528 (goto-char start)
529 (recenter 0))))))
530
531(defun vc-rcs-diff (file &optional oldvers newvers)
532 "Get a difference report using RCS between two versions of FILE."
533 (if (not oldvers) (setq oldvers (vc-workfile-version file)))
534 ;; If we know that --brief is not supported, don't try it.
535 (let* ((diff-switches-list (if (listp diff-switches)
536 diff-switches
537 (list diff-switches)))
538 (options (append (list "-q"
539 (concat "-r" oldvers)
540 (and newvers (concat "-r" newvers)))
541 diff-switches-list)))
542 (apply 'vc-do-command t 1 "rcsdiff" file options)))
543
544
545;;;
546;;; Snapshot system
547;;;
548
549(defun vc-rcs-assign-name (file name)
550 "Assign to FILE's latest version a given NAME."
551 (vc-do-command nil 0 "rcs" (vc-name file) (concat "-n" name ":")))
552
553
554;;;
555;;; Miscellaneous
556;;;
557
558(defun vc-rcs-check-headers ()
559 "Check if the current file has any headers in it."
560 (save-excursion
561 (goto-char (point-min))
562 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
563\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
564
565(defun vc-rcs-clear-headers ()
566 "Implementation of vc-clear-headers for RCS."
567 (let ((case-fold-search nil))
568 (goto-char (point-min))
569 (while (re-search-forward
570 (concat "\\$\\(Author\\|Date\\|Header\\|Id\\|Locker\\|Name\\|"
571 "RCSfile\\|Revision\\|Source\\|State\\): [^$\n]+\\$")
572 nil t)
573 (replace-match "$\\1$"))))
574
575(defun vc-rcs-rename-file (old new)
576 ;; Just move the master file (using vc-rcs-master-templates).
577 (vc-rename-master (vc-name old) new vc-rcs-master-templates))
578
579
580;;;
581;;; Internal functions
582;;;
583
584(defun vc-rcs-trunk-p (rev)
585 "Return t if REV is an RCS revision on the trunk."
586 (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
587
588(defun vc-rcs-branch-part (rev)
589 "Return the branch part of an RCS revision number REV"
590 (substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
591
592(defun vc-rcs-branch-p (rev)
593 "Return t if REV is an RCS branch revision"
594 (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
595
596(defun vc-rcs-minor-part (rev)
597 "Return the minor version number of an RCS revision number REV."
598 (string-match "[0-9]+\\'" rev)
599 (substring rev (match-beginning 0) (match-end 0)))
600
601(defun vc-rcs-previous-version (rev)
602 "Guess the previous RCS version number"
603 (let ((branch (vc-rcs-branch-part rev))
604 (minor-num (string-to-number (vc-rcs-minor-part rev))))
605 (if (> minor-num 1)
606 ;; version does probably not start a branch or release
607 (concat branch "." (number-to-string (1- minor-num)))
608 (if (vc-rcs-trunk-p rev)
609 ;; we are at the beginning of the trunk --
610 ;; don't know anything to return here
611 ""
612 ;; we are at the beginning of a branch --
613 ;; return version of starting point
614 (vc-rcs-branch-part branch)))))
615
616(defun vc-rcs-workfile-is-newer (file)
617 "Return non-nil if FILE is newer than its RCS master.
618This likely means that FILE has been changed with respect
619to its master version."
620 (let ((file-time (nth 5 (file-attributes file)))
621 (master-time (nth 5 (file-attributes (vc-name file)))))
622 (or (> (nth 0 file-time) (nth 0 master-time))
623 (and (= (nth 0 file-time) (nth 0 master-time))
624 (> (nth 1 file-time) (nth 1 master-time))))))
194 625
195(defun vc-rcs-find-most-recent-rev (branch) 626(defun vc-rcs-find-most-recent-rev (branch)
196 "Find most recent revision on BRANCH." 627 "Find most recent revision on BRANCH."
@@ -373,179 +804,6 @@ Returns: nil if no headers were found
373 (vc-file-setprop file 'vc-checkout-model 'implicit))) 804 (vc-file-setprop file 'vc-checkout-model 'implicit)))
374 status)))) 805 status))))
375 806
376(defun vc-rcs-workfile-unchanged-p (file)
377 "RCS-specific implementation of vc-workfile-unchanged-p."
378 ;; Try to use rcsdiff --brief. If rcsdiff does not understand that,
379 ;; do a double take and remember the fact for the future
380 (let* ((version (concat "-r" (vc-workfile-version file)))
381 (status (if (eq vc-rcsdiff-knows-brief 'no)
382 (vc-do-command nil 1 "rcsdiff" file version)
383 (vc-do-command nil 2 "rcsdiff" file "--brief" version))))
384 (if (eq status 2)
385 (if (not vc-rcsdiff-knows-brief)
386 (setq vc-rcsdiff-knows-brief 'no
387 status (vc-do-command nil 1 "rcsdiff" file version))
388 (error "rcsdiff failed"))
389 (if (not vc-rcsdiff-knows-brief) (setq vc-rcsdiff-knows-brief 'yes)))
390 ;; The workfile is unchanged if rcsdiff found no differences.
391 (zerop status)))
392
393(defun vc-rcs-trunk-p (rev)
394 "Return t if REV is an RCS revision on the trunk."
395 (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
396
397(defun vc-rcs-branch-part (rev)
398 "Return the branch part of an RCS revision number REV"
399 (substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
400
401(defun vc-rcs-latest-on-branch-p (file &optional version)
402 "Return non-nil if workfile version of FILE is the latest on its branch.
403When VERSION is given, perform check for that version."
404 (unless version (setq version (vc-workfile-version file)))
405 (with-temp-buffer
406 (string= version
407 (if (vc-rcs-trunk-p version)
408 (progn
409 ;; Compare VERSION to the head version number.
410 (vc-insert-file (vc-name file) "^[0-9]")
411 (vc-parse-buffer "^head[ \t\n]+\\([^;]+\\);" 1))
412 ;; If we are not on the trunk, we need to examine the
413 ;; whole current branch.
414 (vc-insert-file (vc-name file) "^desc")
415 (vc-rcs-find-most-recent-rev (vc-rcs-branch-part version))))))
416
417(defun vc-rcs-branch-p (rev)
418 "Return t if REV is an RCS branch revision"
419 (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
420
421(defun vc-rcs-minor-part (rev)
422 "Return the minor version number of an RCS revision number REV."
423 (string-match "[0-9]+\\'" rev)
424 (substring rev (match-beginning 0) (match-end 0)))
425
426(defun vc-rcs-previous-version (rev)
427 "Guess the previous RCS version number"
428 (let ((branch (vc-rcs-branch-part rev))
429 (minor-num (string-to-number (vc-rcs-minor-part rev))))
430 (if (> minor-num 1)
431 ;; version does probably not start a branch or release
432 (concat branch "." (number-to-string (1- minor-num)))
433 (if (vc-rcs-trunk-p rev)
434 ;; we are at the beginning of the trunk --
435 ;; don't know anything to return here
436 ""
437 ;; we are at the beginning of a branch --
438 ;; return version of starting point
439 (vc-rcs-branch-part branch)))))
440
441(defun vc-rcs-print-log (file)
442 "Get change log associated with FILE."
443 (vc-do-command t 0 "rlog" (vc-name file)))
444
445(defun vc-rcs-show-log-entry (version)
446 (when (re-search-forward
447 ;; also match some context, for safety
448 (concat "----\nrevision " version
449 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
450 ;; set the display window so that
451 ;; the whole log entry is displayed
452 (let (start end lines)
453 (beginning-of-line) (forward-line -1) (setq start (point))
454 (if (not (re-search-forward "^----*\nrevision" nil t))
455 (setq end (point-max))
456 (beginning-of-line) (forward-line -1) (setq end (point)))
457 (setq lines (count-lines start end))
458 (cond
459 ;; if the global information and this log entry fit
460 ;; into the window, display from the beginning
461 ((< (count-lines (point-min) end) (window-height))
462 (goto-char (point-min))
463 (recenter 0)
464 (goto-char start))
465 ;; if the whole entry fits into the window,
466 ;; display it centered
467 ((< (1+ lines) (window-height))
468 (goto-char start)
469 (recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
470 ;; otherwise (the entry is too large for the window),
471 ;; display from the start
472 (t
473 (goto-char start)
474 (recenter 0))))))
475
476(defun vc-rcs-assign-name (file name)
477 "Assign to FILE's latest version a given NAME."
478 (vc-do-command nil 0 "rcs" (vc-name file) (concat "-n" name ":")))
479
480(defun vc-rcs-merge (file first-version &optional second-version)
481 "Merge changes into current working copy of FILE.
482The changes are between FIRST-VERSION and SECOND-VERSION."
483 (vc-do-command nil 1 "rcsmerge" (vc-name file)
484 "-kk" ; ignore keyword conflicts
485 (concat "-r" first-version)
486 (if second-version (concat "-r" second-version))))
487
488(defun vc-rcs-check-headers ()
489 "Check if the current file has any headers in it."
490 (save-excursion
491 (goto-char (point-min))
492 (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
493\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
494
495(defun vc-rcs-clear-headers ()
496 "Implementation of vc-clear-headers for RCS."
497 (let ((case-fold-search nil))
498 (goto-char (point-min))
499 (while (re-search-forward
500 (concat "\\$\\(Author\\|Date\\|Header\\|Id\\|Locker\\|Name\\|"
501 "RCSfile\\|Revision\\|Source\\|State\\): [^$\n]+\\$")
502 nil t)
503 (replace-match "$\\1$"))))
504
505(defun vc-rcs-steal-lock (file &optional rev)
506 "Steal the lock on the current workfile for FILE and revision REV.
507Needs RCS 5.6.2 or later for -M."
508 (vc-do-command nil 0 "rcs" (vc-name file) "-M"
509 (concat "-u" rev) (concat "-l" rev)))
510
511(defun vc-rcs-cancel-version (file writable)
512 "Undo the most recent checkin of FILE.
513WRITABLE non-nil means previous version should be locked."
514 (let* ((target (vc-workfile-version file))
515 (previous (if (vc-trunk-p target) "" (vc-branch-part target)))
516 (config (current-window-configuration))
517 (done nil))
518 (vc-do-command nil 0 "rcs" (vc-name file) (concat "-o" target))
519 ;; Check out the most recent remaining version. If it fails, because
520 ;; the whole branch got deleted, do a double-take and check out the
521 ;; version where the branch started.
522 (while (not done)
523 (condition-case err
524 (progn
525 (vc-do-command nil 0 "co" (vc-name file) "-f"
526 (concat (if writable "-l" "-u") previous))
527 (setq done t))
528 (error (set-buffer "*vc*")
529 (goto-char (point-min))
530 (if (search-forward "no side branches present for" nil t)
531 (progn (setq previous (vc-branch-part previous))
532 (vc-rcs-set-default-branch file previous)
533 ;; vc-do-command popped up a window with
534 ;; the error message. Get rid of it, by
535 ;; restoring the old window configuration.
536 (set-window-configuration config))
537 ;; No, it was some other error: re-signal it.
538 (signal (car err) (cdr err))))))))
539
540(defun vc-rcs-revert (file)
541 "Revert FILE to the version it was based on."
542 (vc-do-command nil 0 "co" (vc-name file) "-f"
543 (concat "-u" (vc-workfile-version file))))
544
545(defun vc-rcs-rename-file (old new)
546 ;; Just move the master file (using vc-rcs-master-templates).
547 (vc-rename-master (vc-name old) new vc-rcs-master-templates))
548
549(defun vc-release-greater-or-equal (r1 r2) 807(defun vc-release-greater-or-equal (r1 r2)
550 "Compare release numbers, represented as strings. 808 "Compare release numbers, represented as strings.
551Release components are assumed cardinal numbers, not decimal fractions 809Release components are assumed cardinal numbers, not decimal fractions
@@ -581,55 +839,6 @@ CVS releases are handled reasonably, too \(1.3 < 1.4* < 1.5\)."
581 (not (eq installation 'unknown))) 839 (not (eq installation 'unknown)))
582 (vc-release-greater-or-equal installation release)))) 840 (vc-release-greater-or-equal installation release))))
583 841
584(defun vc-rcs-checkin (file rev comment)
585 "RCS-specific version of `vc-backend-checkin'."
586 (let ((switches (if (stringp vc-checkin-switches)
587 (list vc-checkin-switches)
588 vc-checkin-switches)))
589 (let ((old-version (vc-workfile-version file)) new-version
590 (default-branch (vc-file-getprop file 'vc-rcs-default-branch)))
591 ;; Force branch creation if an appropriate
592 ;; default branch has been set.
593 (and (not rev)
594 default-branch
595 (string-match (concat "^" (regexp-quote old-version) "\\.")
596 default-branch)
597 (setq rev default-branch)
598 (setq switches (cons "-f" switches)))
599 (apply 'vc-do-command nil 0 "ci" (vc-name file)
600 ;; if available, use the secure check-in option
601 (and (vc-rcs-release-p "5.6.4") "-j")
602 (concat (if vc-keep-workfiles "-u" "-r") rev)
603 (concat "-m" comment)
604 switches)
605 (vc-file-setprop file 'vc-workfile-version nil)
606
607 ;; determine the new workfile version
608 (set-buffer "*vc*")
609 (goto-char (point-min))
610 (when (or (re-search-forward
611 "new revision: \\([0-9.]+\\);" nil t)
612 (re-search-forward
613 "reverting to previous revision \\([0-9.]+\\)" nil t))
614 (setq new-version (match-string 1))
615 (vc-file-setprop file 'vc-workfile-version new-version))
616
617 ;; if we got to a different branch, adjust the default
618 ;; branch accordingly
619 (cond
620 ((and old-version new-version
621 (not (string= (vc-rcs-branch-part old-version)
622 (vc-rcs-branch-part new-version))))
623 (vc-rcs-set-default-branch file
624 (if (vc-rcs-trunk-p new-version) nil
625 (vc-rcs-branch-part new-version)))
626 ;; If this is an old RCS release, we might have
627 ;; to remove a remaining lock.
628 (if (not (vc-rcs-release-p "5.6.2"))
629 ;; exit status of 1 is also accepted.
630 ;; It means that the lock was removed before.
631 (vc-do-command nil 1 "rcs" (vc-name file)
632 (concat "-u" old-version))))))))
633 842
634(defun vc-rcs-system-release () 843(defun vc-rcs-system-release ()
635 "Return the RCS release installed on this system, as a string. 844 "Return the RCS release installed on this system, as a string.
@@ -645,104 +854,6 @@ variable `vc-rcs-release' is set to the returned value."
645 (vc-parse-buffer "^RCS version \\([0-9.]+ *.*\\)" 1))) 854 (vc-parse-buffer "^RCS version \\([0-9.]+ *.*\\)" 1)))
646 'unknown)))) 855 'unknown))))
647 856
648(defun vc-rcs-diff (file &optional oldvers newvers)
649 "Get a difference report using RCS between two versions of FILE."
650 (if (not oldvers) (setq oldvers (vc-workfile-version file)))
651 ;; If we know that --brief is not supported, don't try it.
652 (let* ((diff-switches-list (if (listp diff-switches)
653 diff-switches
654 (list diff-switches)))
655 (options (append (list "-q"
656 (concat "-r" oldvers)
657 (and newvers (concat "-r" newvers)))
658 diff-switches-list)))
659 (apply 'vc-do-command t 1 "rcsdiff" file options)))
660
661(defun vc-rcs-responsible-p (file)
662 "Return non-nil if RCS thinks it would be responsible for registering FILE."
663 ;; TODO: check for all the patterns in vc-rcs-master-templates
664 (file-directory-p (expand-file-name "RCS" (file-name-directory file))))
665
666(defun vc-rcs-register (file &optional rev comment)
667 "Register FILE into the RCS version-control system.
668REV is the optional revision number for the file. COMMENT can be used
669to provide an initial description of FILE.
670
671`vc-register-switches' and `vc-rcs-register-switches' are passed to
672the RCS command (in that order).
673
674Automatically retrieve a read-only version of the file with keywords
675expanded if `vc-keep-workfiles' is non-nil, otherwise, delete the workfile."
676 (let ((subdir (expand-file-name "RCS" (file-name-directory file)))
677 (switches (list
678 (if (stringp vc-register-switches)
679 (list vc-register-switches)
680 vc-register-switches)
681 (if (stringp vc-rcs-register-switches)
682 (list vc-rcs-register-switches)
683 vc-rcs-register-switches))))
684
685 (and (not (file-exists-p subdir))
686 (not (directory-files (file-name-directory file)
687 nil ".*,v$" t))
688 (yes-or-no-p "Create RCS subdirectory? ")
689 (make-directory subdir))
690 (apply 'vc-do-command nil 0 "ci" file
691 ;; if available, use the secure registering option
692 (and (vc-rcs-release-p "5.6.4") "-i")
693 (concat (if vc-keep-workfiles "-u" "-r") rev)
694 (and comment (concat "-t-" comment))
695 switches)
696 ;; parse output to find master file name and workfile version
697 (with-current-buffer "*vc*"
698 (goto-char (point-min))
699 (let ((name (if (looking-at (concat "^\\(.*\\) <-- "
700 (file-name-nondirectory file)))
701 (match-string 1))))
702 (if (not name)
703 ;; if we couldn't find the master name,
704 ;; run vc-rcs-registered to get it
705 ;; (will be stored into the vc-name property)
706 (vc-rcs-registered file)
707 (vc-file-setprop file 'vc-name
708 (if (file-name-absolute-p name)
709 name
710 (expand-file-name
711 name
712 (file-name-directory file))))))
713 (vc-file-setprop file 'vc-workfile-version
714 (if (re-search-forward
715 "^initial revision: \\([0-9.]+\\).*\n"
716 nil t)
717 (match-string 1))))))
718
719(defun vc-rcs-unregister (file)
720 "Unregister FILE from RCS.
721If this leaves the RCS subdirectory empty, ask the user
722whether to remove it."
723 (let* ((master (vc-name file))
724 (dir (file-name-directory master))
725 (backup-info (find-backup-file-name master)))
726 (if (not backup-info)
727 (delete-file master)
728 (rename-file master (car backup-info) 'ok-if-already-exists)
729 (dolist (f (cdr backup-info)) (ignore-errors (delete-file f))))
730 (and (string= (file-name-nondirectory (directory-file-name dir)) "RCS")
731 ;; check whether RCS dir is empty, i.e. it does not
732 ;; contain any files except "." and ".."
733 (not (directory-files dir nil
734 "^\\([^.]\\|\\.[^.]\\|\\.\\.[^.]\\).*"))
735 (yes-or-no-p (format "Directory %s is empty; remove it? " dir))
736 (delete-directory dir))))
737
738(defun vc-rcs-receive-file (file rev)
739 "Implementation of receive-file for RCS."
740 (let ((checkout-model (vc-checkout-model file)))
741 (vc-rcs-register file rev "")
742 (when (eq checkout-model 'implicit)
743 (vc-rcs-set-non-strict-locking file))
744 (vc-rcs-set-default-branch file (concat rev ".1"))))
745
746(defun vc-rcs-set-non-strict-locking (file) 857(defun vc-rcs-set-non-strict-locking (file)
747 (vc-do-command nil 0 "rcs" file "-U") 858 (vc-do-command nil 0 "rcs" file "-U")
748 (vc-file-setprop file 'vc-checkout-model 'implicit) 859 (vc-file-setprop file 'vc-checkout-model 'implicit)
@@ -752,83 +863,6 @@ whether to remove it."
752 (vc-do-command nil 0 "rcs" (vc-name file) (concat "-b" branch)) 863 (vc-do-command nil 0 "rcs" (vc-name file) (concat "-b" branch))
753 (vc-file-setprop file 'vc-rcs-default-branch branch)) 864 (vc-file-setprop file 'vc-rcs-default-branch branch))
754 865
755(defun vc-rcs-checkout (file &optional writable rev workfile)
756 "Retrieve a copy of a saved version of FILE into a workfile."
757 (let ((filename (or workfile file))
758 (file-buffer (get-file-buffer file))
759 switches)
760 (message "Checking out %s..." filename)
761 (save-excursion
762 ;; Change buffers to get local value of vc-checkout-switches.
763 (if file-buffer (set-buffer file-buffer))
764 (setq switches (if (stringp vc-checkout-switches)
765 (list vc-checkout-switches)
766 vc-checkout-switches))
767 ;; Save this buffer's default-directory
768 ;; and use save-excursion to make sure it is restored
769 ;; in the same buffer it was saved in.
770 (let ((default-directory default-directory))
771 (save-excursion
772 ;; Adjust the default-directory so that the check-out creates
773 ;; the file in the right place.
774 (setq default-directory (file-name-directory filename))
775 (if workfile ;; RCS
776 ;; RCS can't check out into arbitrary file names directly.
777 ;; Use `co -p' and make stdout point to the correct file.
778 (let ((vc-modes (logior (file-modes (vc-name file))
779 (if writable 128 0)))
780 (failed t))
781 (unwind-protect
782 (progn
783 (let ((coding-system-for-read 'no-conversion)
784 (coding-system-for-write 'no-conversion))
785 (with-temp-file filename
786 (apply 'vc-do-command
787 (current-buffer) 0 "co" (vc-name file)
788 "-q" ;; suppress diagnostic output
789 (if writable "-l")
790 (concat "-p" rev)
791 switches)))
792 (set-file-modes filename
793 (logior (file-modes (vc-name file))
794 (if writable 128 0)))
795 (setq failed nil))
796 (and failed (file-exists-p filename)
797 (delete-file filename))))
798 (let (new-version)
799 ;; if we should go to the head of the trunk,
800 ;; clear the default branch first
801 (and rev (string= rev "")
802 (vc-rcs-set-default-branch file nil))
803 ;; now do the checkout
804 (apply 'vc-do-command
805 nil 0 "co" (vc-name file)
806 ;; If locking is not strict, force to overwrite
807 ;; the writable workfile.
808 (if (eq (vc-checkout-model file) 'implicit) "-f")
809 (if writable "-l")
810 (if rev (concat "-r" rev)
811 ;; if no explicit revision was specified,
812 ;; check out that of the working file
813 (let ((workrev (vc-workfile-version file)))
814 (if workrev (concat "-r" workrev)
815 nil)))
816 switches)
817 ;; determine the new workfile version
818 (with-current-buffer "*vc*"
819 (setq new-version
820 (vc-parse-buffer "^revision \\([0-9.]+\\).*\n" 1)))
821 (vc-file-setprop file 'vc-workfile-version new-version)
822 ;; if necessary, adjust the default branch
823 (and rev (not (string= rev ""))
824 (vc-rcs-set-default-branch
825 file
826 (if (vc-rcs-latest-on-branch-p file new-version)
827 (if (vc-rcs-trunk-p new-version) nil
828 (vc-rcs-branch-part new-version))
829 new-version))))))
830 (message "Checking out %s...done" filename)))))
831
832(provide 'vc-rcs) 866(provide 'vc-rcs)
833 867
834;;; vc-rcs.el ends here 868;;; vc-rcs.el ends here
diff --git a/lisp/vc-sccs.el b/lisp/vc-sccs.el
index db618915e90..bc02d199124 100644
--- a/lisp/vc-sccs.el
+++ b/lisp/vc-sccs.el
@@ -5,7 +5,7 @@
5;; Author: FSF (see vc.el for full credits) 5;; Author: FSF (see vc.el for full credits)
6;; Maintainer: Andre Spiegel <spiegel@gnu.org> 6;; Maintainer: Andre Spiegel <spiegel@gnu.org>
7 7
8;; $Id: vc-sccs.el,v 1.3 2000/09/07 20:06:55 fx Exp $ 8;; $Id: vc-sccs.el,v 1.4 2000/09/09 00:48:40 monnier Exp $
9 9
10;; This file is part of GNU Emacs. 10;; This file is part of GNU Emacs.
11 11
@@ -28,6 +28,10 @@
28 28
29;;; Code: 29;;; Code:
30 30
31;;;
32;;; Customization options
33;;;
34
31(defcustom vc-sccs-register-switches nil 35(defcustom vc-sccs-register-switches nil
32 "*Extra switches for registering a file in SCCS. 36 "*Extra switches for registering a file in SCCS.
33A string or list of strings passed to the checkin program by 37A string or list of strings passed to the checkin program by
@@ -58,8 +62,18 @@ For a description of possible values, see `vc-check-master-templates'."
58 :version "21.1" 62 :version "21.1"
59 :group 'vc) 63 :group 'vc)
60 64
65
66;;;
67;;; Internal variables
68;;;
69
61(defconst vc-sccs-name-assoc-file "VC-names") 70(defconst vc-sccs-name-assoc-file "VC-names")
62 71
72
73;;;
74;;; State-querying functions
75;;;
76
63;;;###autoload 77;;;###autoload
64(progn (defun vc-sccs-registered (f) (vc-default-registered 'SCCS f))) 78(progn (defun vc-sccs-registered (f) (vc-default-registered 'SCCS f)))
65 79
@@ -108,6 +122,12 @@ For a description of possible values, see `vc-check-master-templates'."
108 (vc-insert-file (vc-name file) "^\001e") 122 (vc-insert-file (vc-name file) "^\001e")
109 (vc-parse-buffer "^\001d D \\([^ ]+\\)" 1))) 123 (vc-parse-buffer "^\001d D \\([^ ]+\\)" 1)))
110 124
125(defun vc-sccs-latest-on-branch-p (file)
126 "Return t iff the current workfile version of FILE is latest on its branch."
127 ;; Always return t; we do not support previous versions in the workfile
128 ;; under SCCS.
129 t)
130
111(defun vc-sccs-checkout-model (file) 131(defun vc-sccs-checkout-model (file)
112 "SCCS-specific version of `vc-checkout-model'." 132 "SCCS-specific version of `vc-checkout-model'."
113 'locking) 133 'locking)
@@ -118,174 +138,10 @@ For a description of possible values, see `vc-check-master-templates'."
118 (list "--brief" "-q" 138 (list "--brief" "-q"
119 (concat "-r" (vc-workfile-version file))))) 139 (concat "-r" (vc-workfile-version file)))))
120 140
121;; internal code
122
123;; This function is wrapped with `progn' so that the autoload cookie
124;; copies the whole function itself into loaddefs.el rather than just placing
125;; a (autoload 'vc-sccs-search-project-dir "vc-sccs") which would not
126;; help us avoid loading vc-sccs.
127;;;###autoload
128(progn (defun vc-sccs-search-project-dir (dirname basename)
129 "Return the name of a master file in the SCCS project directory.
130Does not check whether the file exists but returns nil if it does not
131find any project directory."
132 (let ((project-dir (getenv "PROJECTDIR")) dirs dir)
133 (when project-dir
134 (if (file-name-absolute-p project-dir)
135 (setq dirs '("SCCS" ""))
136 (setq dirs '("src/SCCS" "src" "source/SCCS" "source"))
137 (setq project-dir (expand-file-name (concat "~" project-dir))))
138 (while (and (not dir) dirs)
139 (setq dir (expand-file-name (car dirs) project-dir))
140 (unless (file-directory-p dir)
141 (setq dir nil)
142 (setq dirs (cdr dirs))))
143 (and dir (expand-file-name (concat "s." basename) dir))))))
144
145(defun vc-sccs-lock-file (file)
146 "Generate lock file name corresponding to FILE."
147 (let ((master (vc-name file)))
148 (and
149 master
150 (string-match "\\(.*/\\)\\(s\\.\\)\\(.*\\)" master)
151 (replace-match "p." t t master 2))))
152
153(defun vc-sccs-parse-locks ()
154 "Parse SCCS locks in current buffer.
155The result is a list of the form ((VERSION . USER) (VERSION . USER) ...)."
156 (let (master-locks)
157 (goto-char (point-min))
158 (while (re-search-forward "^\\([0-9.]+\\) [0-9.]+ \\([^ ]+\\) .*\n?"
159 nil t)
160 (setq master-locks
161 (cons (cons (match-string 1) (match-string 2)) master-locks)))
162 ;; FIXME: is it really necessary to reverse ?
163 (nreverse master-locks)))
164 141
165(defun vc-sccs-print-log (file) 142;;;
166 "Get change log associated with FILE." 143;;; State-changing functions
167 (vc-do-command t 0 "prs" (vc-name file))) 144;;;
168
169(defun vc-sccs-assign-name (file name)
170 "Assign to FILE's latest version a given NAME."
171 (vc-sccs-add-triple name file (vc-workfile-version file)))
172
173;; Named-configuration support
174
175(defun vc-sccs-add-triple (name file rev)
176 (with-current-buffer
177 (find-file-noselect
178 (expand-file-name vc-sccs-name-assoc-file
179 (file-name-directory (vc-name file))))
180 (goto-char (point-max))
181 (insert name "\t:\t" file "\t" rev "\n")
182 (basic-save-buffer)
183 (kill-buffer (current-buffer))))
184
185(defun vc-sccs-rename-file (old new)
186 ;; Move the master file (using vc-rcs-master-templates).
187 (vc-rename-master (vc-name old) new vc-sccs-master-templates)
188 ;; Update the snapshot file.
189 (with-current-buffer
190 (find-file-noselect
191 (expand-file-name vc-sccs-name-assoc-file
192 (file-name-directory (vc-name old))))
193 (goto-char (point-min))
194 ;; (replace-regexp (concat ":" (regexp-quote old) "$") (concat ":" new))
195 (while (re-search-forward (concat ":" (regexp-quote old) "$") nil t)
196 (replace-match (concat ":" new) nil nil))
197 (basic-save-buffer)
198 (kill-buffer (current-buffer))))
199
200(defun vc-sccs-lookup-triple (file name)
201 "Return the numeric version corresponding to a named snapshot of FILE.
202If NAME is nil or a version number string it's just passed through."
203 (if (or (null name)
204 (let ((firstchar (aref name 0)))
205 (and (>= firstchar ?0) (<= firstchar ?9))))
206 name
207 (with-temp-buffer
208 (vc-insert-file
209 (expand-file-name vc-sccs-name-assoc-file
210 (file-name-directory (vc-name file))))
211 (vc-parse-buffer (concat name "\t:\t" file "\t\\(.+\\)") 1))))
212
213(defun vc-sccs-merge (file first-version &optional second-version)
214 (error "Merging not implemented for SCCS"))
215
216(defun vc-sccs-check-headers ()
217 "Check if the current file has any headers in it."
218 (save-excursion
219 (goto-char (point-min))
220 (re-search-forward "%[A-Z]%" nil t)))
221
222(defun vc-sccs-steal-lock (file &optional rev)
223 "Steal the lock on the current workfile for FILE and revision REV."
224 (vc-do-command nil 0 "unget" (vc-name file) "-n" (if rev (concat "-r" rev)))
225 (vc-do-command nil 0 "get" (vc-name file) "-g" (if rev (concat "-r" rev))))
226
227(defun vc-sccs-cancel-version (file writable)
228 "Undo the most recent checkin of FILE.
229WRITABLE non-nil means previous version should be locked."
230 (vc-do-command nil 0 "rmdel"
231 (vc-name file)
232 (concat "-r" (vc-workfile-version file)))
233 (vc-do-command nil 0 "get"
234 (vc-name file)
235 (if writable "-e")))
236
237(defun vc-sccs-revert (file)
238 "Revert FILE to the version it was based on."
239 (vc-do-command nil 0 "unget" (vc-name file))
240 (vc-do-command nil 0 "get" (vc-name file))
241 ;; Checking out explicit versions is not supported under SCCS, yet.
242 ;; We always "revert" to the latest version; therefore
243 ;; vc-workfile-version is cleared here so that it gets recomputed.
244 (vc-file-setprop file 'vc-workfile-version nil))
245
246(defun vc-sccs-checkin (file rev comment)
247 "SCCS-specific version of `vc-backend-checkin'."
248 (let ((switches (if (stringp vc-checkin-switches)
249 (list vc-checkin-switches)
250 vc-checkin-switches)))
251 (apply 'vc-do-command nil 0 "delta" (vc-name file)
252 (if rev (concat "-r" rev))
253 (concat "-y" comment)
254 switches)
255 (if vc-keep-workfiles
256 (vc-do-command nil 0 "get" (vc-name file)))))
257
258(defun vc-sccs-latest-on-branch-p (file)
259 "Return t iff the current workfile version of FILE is latest on its branch."
260 ;; Always return t; we do not support previous versions in the workfile
261 ;; under SCCS.
262 t)
263
264(defun vc-sccs-logentry-check ()
265 "Check that the log entry in the current buffer is acceptable for SCCS."
266 (when (>= (buffer-size) 512)
267 (goto-char 512)
268 (error "Log must be less than 512 characters; point is now at pos 512")))
269
270(defun vc-sccs-diff (file &optional oldvers newvers)
271 "Get a difference report using SCCS between two versions of FILE."
272 (setq oldvers (vc-sccs-lookup-triple file oldvers))
273 (setq newvers (vc-sccs-lookup-triple file newvers))
274 (let* ((diff-switches-list (if (listp diff-switches)
275 diff-switches
276 (list diff-switches)))
277 (options (append (list "-q"
278 (and oldvers (concat "-r" oldvers))
279 (and newvers (concat "-r" newvers)))
280 diff-switches-list)))
281 (apply 'vc-do-command t 1 "vcdiff" (vc-name file) options)))
282
283(defun vc-sccs-responsible-p (file)
284 "Return non-nil if SCCS thinks it would be responsible for registering FILE."
285 ;; TODO: check for all the patterns in vc-sccs-master-templates
286 (or (file-directory-p (expand-file-name "SCCS" (file-name-directory file)))
287 (stringp (vc-sccs-search-project-dir (or (file-name-directory file) "")
288 (file-name-nondirectory file)))))
289 145
290(defun vc-sccs-register (file &optional rev comment) 146(defun vc-sccs-register (file &optional rev comment)
291 "Register FILE into the SCCS version-control system. 147 "Register FILE into the SCCS version-control system.
@@ -321,6 +177,25 @@ expanded if `vc-keep-workfiles' is non-nil, otherwise, delete the workfile."
321 (if vc-keep-workfiles 177 (if vc-keep-workfiles
322 (vc-do-command nil 0 "get" (vc-name file))))) 178 (vc-do-command nil 0 "get" (vc-name file)))))
323 179
180(defun vc-sccs-responsible-p (file)
181 "Return non-nil if SCCS thinks it would be responsible for registering FILE."
182 ;; TODO: check for all the patterns in vc-sccs-master-templates
183 (or (file-directory-p (expand-file-name "SCCS" (file-name-directory file)))
184 (stringp (vc-sccs-search-project-dir (or (file-name-directory file) "")
185 (file-name-nondirectory file)))))
186
187(defun vc-sccs-checkin (file rev comment)
188 "SCCS-specific version of `vc-backend-checkin'."
189 (let ((switches (if (stringp vc-checkin-switches)
190 (list vc-checkin-switches)
191 vc-checkin-switches)))
192 (apply 'vc-do-command nil 0 "delta" (vc-name file)
193 (if rev (concat "-r" rev))
194 (concat "-y" comment)
195 switches)
196 (if vc-keep-workfiles
197 (vc-do-command nil 0 "get" (vc-name file)))))
198
324(defun vc-sccs-checkout (file &optional writable rev workfile) 199(defun vc-sccs-checkout (file &optional writable rev workfile)
325 "Retrieve a copy of a saved version of SCCS controlled FILE into a WORKFILE. 200 "Retrieve a copy of a saved version of SCCS controlled FILE into a WORKFILE.
326WRITABLE non-nil means that the file should be writable. REV is the 201WRITABLE non-nil means that the file should be writable. REV is the
@@ -379,9 +254,166 @@ revision to check out into WORKFILE."
379 switches))))) 254 switches)))))
380 (message "Checking out %s...done" filename))) 255 (message "Checking out %s...done" filename)))
381 256
382(defun vc-sccs-update-changelog (files) 257(defun vc-sccs-revert (file)
383 (error "Sorry, generating ChangeLog entries is not implemented for SCCS")) 258 "Revert FILE to the version it was based on."
259 (vc-do-command nil 0 "unget" (vc-name file))
260 (vc-do-command nil 0 "get" (vc-name file))
261 ;; Checking out explicit versions is not supported under SCCS, yet.
262 ;; We always "revert" to the latest version; therefore
263 ;; vc-workfile-version is cleared here so that it gets recomputed.
264 (vc-file-setprop file 'vc-workfile-version nil))
265
266(defun vc-sccs-cancel-version (file writable)
267 "Undo the most recent checkin of FILE.
268WRITABLE non-nil means previous version should be locked."
269 (vc-do-command nil 0 "rmdel"
270 (vc-name file)
271 (concat "-r" (vc-workfile-version file)))
272 (vc-do-command nil 0 "get"
273 (vc-name file)
274 (if writable "-e")))
275
276(defun vc-sccs-steal-lock (file &optional rev)
277 "Steal the lock on the current workfile for FILE and revision REV."
278 (vc-do-command nil 0 "unget" (vc-name file) "-n" (if rev (concat "-r" rev)))
279 (vc-do-command nil 0 "get" (vc-name file) "-g" (if rev (concat "-r" rev))))
280
281
282;;;
283;;; History functions
284;;;
285
286(defun vc-sccs-print-log (file)
287 "Get change log associated with FILE."
288 (vc-do-command t 0 "prs" (vc-name file)))
289
290(defun vc-sccs-logentry-check ()
291 "Check that the log entry in the current buffer is acceptable for SCCS."
292 (when (>= (buffer-size) 512)
293 (goto-char 512)
294 (error "Log must be less than 512 characters; point is now at pos 512")))
295
296(defun vc-sccs-diff (file &optional oldvers newvers)
297 "Get a difference report using SCCS between two versions of FILE."
298 (setq oldvers (vc-sccs-lookup-triple file oldvers))
299 (setq newvers (vc-sccs-lookup-triple file newvers))
300 (let* ((diff-switches-list (if (listp diff-switches)
301 diff-switches
302 (list diff-switches)))
303 (options (append (list "-q"
304 (and oldvers (concat "-r" oldvers))
305 (and newvers (concat "-r" newvers)))
306 diff-switches-list)))
307 (apply 'vc-do-command t 1 "vcdiff" (vc-name file) options)))
308
309
310;;;
311;;; Snapshot system
312;;;
313
314(defun vc-sccs-assign-name (file name)
315 "Assign to FILE's latest version a given NAME."
316 (vc-sccs-add-triple name file (vc-workfile-version file)))
317
318
319;;;
320;;; Miscellaneous
321;;;
322
323(defun vc-sccs-check-headers ()
324 "Check if the current file has any headers in it."
325 (save-excursion
326 (goto-char (point-min))
327 (re-search-forward "%[A-Z]%" nil t)))
328
329(defun vc-sccs-rename-file (old new)
330 ;; Move the master file (using vc-rcs-master-templates).
331 (vc-rename-master (vc-name old) new vc-sccs-master-templates)
332 ;; Update the snapshot file.
333 (with-current-buffer
334 (find-file-noselect
335 (expand-file-name vc-sccs-name-assoc-file
336 (file-name-directory (vc-name old))))
337 (goto-char (point-min))
338 ;; (replace-regexp (concat ":" (regexp-quote old) "$") (concat ":" new))
339 (while (re-search-forward (concat ":" (regexp-quote old) "$") nil t)
340 (replace-match (concat ":" new) nil nil))
341 (basic-save-buffer)
342 (kill-buffer (current-buffer))))
343
344
345;;;
346;;; Internal functions
347;;;
348
349;; This function is wrapped with `progn' so that the autoload cookie
350;; copies the whole function itself into loaddefs.el rather than just placing
351;; a (autoload 'vc-sccs-search-project-dir "vc-sccs") which would not
352;; help us avoid loading vc-sccs.
353;;;###autoload
354(progn (defun vc-sccs-search-project-dir (dirname basename)
355 "Return the name of a master file in the SCCS project directory.
356Does not check whether the file exists but returns nil if it does not
357find any project directory."
358 (let ((project-dir (getenv "PROJECTDIR")) dirs dir)
359 (when project-dir
360 (if (file-name-absolute-p project-dir)
361 (setq dirs '("SCCS" ""))
362 (setq dirs '("src/SCCS" "src" "source/SCCS" "source"))
363 (setq project-dir (expand-file-name (concat "~" project-dir))))
364 (while (and (not dir) dirs)
365 (setq dir (expand-file-name (car dirs) project-dir))
366 (unless (file-directory-p dir)
367 (setq dir nil)
368 (setq dirs (cdr dirs))))
369 (and dir (expand-file-name (concat "s." basename) dir))))))
370
371(defun vc-sccs-lock-file (file)
372 "Generate lock file name corresponding to FILE."
373 (let ((master (vc-name file)))
374 (and
375 master
376 (string-match "\\(.*/\\)\\(s\\.\\)\\(.*\\)" master)
377 (replace-match "p." t t master 2))))
378
379(defun vc-sccs-parse-locks ()
380 "Parse SCCS locks in current buffer.
381The result is a list of the form ((VERSION . USER) (VERSION . USER) ...)."
382 (let (master-locks)
383 (goto-char (point-min))
384 (while (re-search-forward "^\\([0-9.]+\\) [0-9.]+ \\([^ ]+\\) .*\n?"
385 nil t)
386 (setq master-locks
387 (cons (cons (match-string 1) (match-string 2)) master-locks)))
388 ;; FIXME: is it really necessary to reverse ?
389 (nreverse master-locks)))
390
391(defun vc-sccs-add-triple (name file rev)
392 (with-current-buffer
393 (find-file-noselect
394 (expand-file-name vc-sccs-name-assoc-file
395 (file-name-directory (vc-name file))))
396 (goto-char (point-max))
397 (insert name "\t:\t" file "\t" rev "\n")
398 (basic-save-buffer)
399 (kill-buffer (current-buffer))))
400
401(defun vc-sccs-lookup-triple (file name)
402 "Return the numeric version corresponding to a named snapshot of FILE.
403If NAME is nil or a version number string it's just passed through."
404 (if (or (null name)
405 (let ((firstchar (aref name 0)))
406 (and (>= firstchar ?0) (<= firstchar ?9))))
407 name
408 (with-temp-buffer
409 (vc-insert-file
410 (expand-file-name vc-sccs-name-assoc-file
411 (file-name-directory (vc-name file))))
412 (vc-parse-buffer (concat name "\t:\t" file "\t\\(.+\\)") 1))))
384 413
385(provide 'vc-sccs) 414(provide 'vc-sccs)
386 415
387;;; vc-sccs.el ends here 416;;; vc-sccs.el ends here
417
418
419