changeset 33560:da206bbad86b

Functions reordered.
author André Spiegel <spiegel@gnu.org>
date Thu, 16 Nov 2000 18:14:41 +0000
parents c6a08bfab7fd
children 044ca47ee3d1
files lisp/vc-cvs.el lisp/vc-rcs.el lisp/vc-sccs.el
diffstat 3 files changed, 1092 insertions(+), 996 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/vc-cvs.el	Thu Nov 16 17:09:04 2000 +0000
+++ b/lisp/vc-cvs.el	Thu Nov 16 18:14:41 2000 +0000
@@ -5,7 +5,7 @@
 ;; Author:      FSF (see vc.el for full credits)
 ;; Maintainer:  Andre Spiegel <spiegel@gnu.org>
 
-;; $Id: vc-cvs.el,v 1.10 2000/11/16 15:29:40 spiegel Exp $
+;; $Id: vc-cvs.el,v 1.11 2000/11/16 16:42:10 spiegel Exp $
 
 ;; This file is part of GNU Emacs.
 
@@ -28,6 +28,10 @@
 
 ;;; Code:
 
+;;; 
+;;; Customization options
+;;;
+
 (defcustom vc-cvs-register-switches nil
   "*Extra switches for registering a file into CVS.
 A string or list of strings passed to the checkin program by
@@ -67,6 +71,22 @@
   :version "21.1"
   :group 'vc)
 
+
+;;;
+;;; Internal variables
+;;;
+
+(defvar vc-cvs-local-month-numbers
+  '(("Jan" . 1) ("Feb" .  2) ("Mar" .  3) ("Apr" .  4)
+    ("May" . 5) ("Jun" .  6) ("Jul" .  7) ("Aug" .  8)
+    ("Sep" . 9) ("Oct" . 10) ("Nov" . 11) ("Dec" . 12))
+  "Local association list of month numbers.")
+
+
+;;;
+;;; State-querying functions 
+;;;
+
 ;;;###autoload (defun vc-cvs-registered (f)
 ;;;###autoload   (when (file-readable-p (expand-file-name
 ;;;###autoload 			  "CVS/Entries" (file-name-directory f)))
@@ -92,97 +112,6 @@
 	   (t nil)))
       nil)))
 
-(defun vc-cvs-stay-local-p (file)
-  "Return non-nil if VC should stay local when handling FILE."
-  (if vc-cvs-stay-local
-      (let* ((dirname (if (file-directory-p file)
-			  (directory-file-name file)
-			(file-name-directory file)))
-	     (prop
-	      (or (vc-file-getprop dirname 'vc-cvs-stay-local-p)
-		  (let ((rootname (expand-file-name "CVS/Root" dirname)))
-		    (vc-file-setprop
-		     dirname 'vc-cvs-stay-local-p
-		     (when (file-readable-p rootname)
-		       (with-temp-buffer
-			 (vc-insert-file rootname)
-			 (goto-char (point-min))
-			 (if (looking-at "\\([^:]*\\):")
-			     (if (not (stringp vc-cvs-stay-local))
-				 'yes
-			       (let ((hostname (match-string 1)))
-				 (if (string-match vc-cvs-stay-local hostname)
-				     'yes
-				   'no)))
-			   'no))))))))
-	(if (eq prop 'yes) t nil))))
-           
-(defun vc-cvs-workfile-version (file)
-  "CVS-specific version of `vc-workfile-version'."
-  ;; There is no need to consult RCS headers under CVS, because we
-  ;; get the workfile version for free when we recognize that a file
-  ;; is registered in CVS.
-  (vc-cvs-registered file)
-  (vc-file-getprop file 'vc-workfile-version))
-
-(defun vc-cvs-checkout-model (file)
-  "CVS-specific version of `vc-checkout-model'."
-  (if (or (getenv "CVSREAD")
-          ;; If the file is not writable (despite CVSREAD being
-          ;; undefined), this is probably because the file is being
-          ;; "watched" by other developers.
-          ;; (If vc-mistrust-permissions was t, we actually shouldn't
-          ;; trust this, but there is no other way to learn this from CVS
-          ;; at the moment (version 1.9).)
-          (string-match "r-..-..-." (nth 8 (file-attributes file))))
-      'announce
-    'implicit))
-
-;; VC Dired functions
-
-(defun vc-cvs-dired-state-info (file)
-  "CVS-specific version of `vc-dired-state-info'."
-  (let* ((cvs-state (vc-state file))
-	 (state (cond ((eq cvs-state 'edited)    "modified")
-		      ((eq cvs-state 'needs-patch)      "patch")
-		      ((eq cvs-state 'needs-merge)         "merge")
-		      ;; FIXME: those two states cannot occur right now
-		      ((eq cvs-state 'unlocked-changes) "conflict")
-		      ((eq cvs-state 'locally-added)       "added")
-		      )))
-    (if state (concat "(" state ")"))))
-
-(defun vc-cvs-parse-status (&optional full)
-  "Parse output of \"cvs status\" command in the current buffer.
-Set file properties accordingly.  Unless FULL is t, parse only
-essential information."
-  (let (file status)
-    (goto-char (point-min))
-    (if (re-search-forward "^File: " nil t)
-        (cond
-         ((looking-at "no file") nil)
-         ((re-search-forward "\\=\\([^ \t]+\\)" nil t)
-	  (setq file (expand-file-name (match-string 1)))
-          (vc-file-setprop file 'vc-backend 'CVS)
-          (if (not (re-search-forward "\\=[ \t]+Status: \\(.*\\)" nil t))
-              (setq status "Unknown")
-            (setq status (match-string 1)))
-          (if (and full
-                   (re-search-forward
-		    "\\(RCS Version\\|RCS Revision\\|Repository revision\\):\
-\[\t ]+\\([0-9.]+\\)"
-                    nil t))
-              (vc-file-setprop file 'vc-latest-version (match-string 2)))
-          (cond
-           ((string-match "Up-to-date" status)
-            (vc-file-setprop file 'vc-checkout-time
-                             (nth 5 (file-attributes file)))
-            'up-to-date)
-           ((string-match "Locally Modified"    status) 'edited)
-	   ((string-match "Needs Merge"         status) 'needs-merge)
-	   ((string-match "Needs \\(Checkout\\|Patch\\)" status) 'needs-patch)
-	   (t 'edited)))))))
-
 (defun vc-cvs-state (file)
   "CVS-specific version of `vc-state'."
   (if (vc-cvs-stay-local-p file)
@@ -207,6 +136,50 @@
         'up-to-date
       'edited)))
 
+(defun vc-cvs-dir-state (dir)
+  "Find the CVS state of all files in DIR."
+  (if (vc-cvs-stay-local-p dir)
+      (vc-cvs-dir-state-heuristic dir)
+    (let ((default-directory dir))
+      ;; Don't specify DIR in this command, the default-directory is
+      ;; enough.  Otherwise it might fail with remote repositories.
+      (with-temp-buffer
+	(vc-do-command t 0 "cvs" nil "status" "-l")
+	(goto-char (point-min))
+	(while (re-search-forward "^=+\n\\([^=\n].*\n\\|\n\\)+" nil t)
+	  (narrow-to-region (match-beginning 0) (match-end 0))
+	  (vc-cvs-parse-status)
+	  (goto-char (point-max))
+	  (widen))))))
+
+(defun vc-cvs-workfile-version (file)
+  "CVS-specific version of `vc-workfile-version'."
+  ;; There is no need to consult RCS headers under CVS, because we
+  ;; get the workfile version for free when we recognize that a file
+  ;; is registered in CVS.
+  (vc-cvs-registered file)
+  (vc-file-getprop file 'vc-workfile-version))
+
+(defun vc-cvs-latest-on-branch-p (file)
+  "Return t iff current workfile version of FILE is the latest on its branch."
+  ;; Since this is only used as a sanity check for vc-cancel-version,
+  ;; and that is not supported under CVS at all, we can safely return t here.
+  ;; TODO: Think of getting rid of this altogether.
+  t)
+
+(defun vc-cvs-checkout-model (file)
+  "CVS-specific version of `vc-checkout-model'."
+  (if (or (getenv "CVSREAD")
+          ;; If the file is not writable (despite CVSREAD being
+          ;; undefined), this is probably because the file is being
+          ;; "watched" by other developers.
+          ;; (If vc-mistrust-permissions was t, we actually shouldn't
+          ;; trust this, but there is no other way to learn this from CVS
+          ;; at the moment (version 1.9).)
+          (string-match "r-..-..-." (nth 8 (file-attributes file))))
+      'announce
+    'implicit))
+
 (defun vc-cvs-mode-line-string (file)
   "Return string for placement into the modeline for FILE.
 Compared to the default implementation, this function handles the
@@ -227,288 +200,54 @@
            ;; for 'needs-patch and 'needs-merge.
            (concat "CVS:" rev)))))
 
-(defun vc-cvs-dir-state (dir)
-  "Find the CVS state of all files in DIR."
-  (if (vc-cvs-stay-local-p dir)
-      (vc-cvs-dir-state-heuristic dir)
-    (let ((default-directory dir))
-      ;; Don't specify DIR in this command, the default-directory is
-      ;; enough.  Otherwise it might fail with remote repositories.
-      (with-temp-buffer
-	(vc-do-command t 0 "cvs" nil "status" "-l")
-	(goto-char (point-min))
-	(while (re-search-forward "^=+\n\\([^=\n].*\n\\|\n\\)+" nil t)
-	  (narrow-to-region (match-beginning 0) (match-end 0))
-	  (vc-cvs-parse-status)
-	  (goto-char (point-max))
-	  (widen))))))
-
-(defun vc-cvs-dir-state-heuristic (dir)
-  "Find the CVS state of all files in DIR, using only local information."
-  (with-temp-buffer
-    (vc-insert-file (expand-file-name "CVS/Entries" dir))
-    (goto-char (point-min))
-    (while (not (eobp))
-      (when (looking-at "/\\([^/]*\\)/")
-	(let ((file (expand-file-name (match-string 1) dir)))
-	  (unless (vc-file-getprop file 'vc-state)
-	    (vc-cvs-parse-entry file t))))
-      (forward-line 1))))
+(defun vc-cvs-dired-state-info (file)
+  "CVS-specific version of `vc-dired-state-info'."
+  (let* ((cvs-state (vc-state file))
+	 (state (cond ((eq cvs-state 'edited)    "modified")
+		      ((eq cvs-state 'needs-patch)      "patch")
+		      ((eq cvs-state 'needs-merge)         "merge")
+		      ;; FIXME: those two states cannot occur right now
+		      ((eq cvs-state 'unlocked-changes) "conflict")
+		      ((eq cvs-state 'locally-added)       "added")
+		      )))
+    (if state (concat "(" state ")"))))
 
-(defun vc-cvs-parse-entry (file &optional set-state)
-  "Parse a line from CVS/Entries.
-Compare modification time to that of the FILE, set file properties
-accordingly.  However, `vc-state' is set only if optional arg SET-STATE
-is non-nil."
-  (cond
-   ;; entry for a "locally added" file (not yet committed)
-   ((looking-at "/[^/]+/0/")
-    (vc-file-setprop file 'vc-checkout-time 0)
-    (vc-file-setprop file 'vc-workfile-version "0")
-    (if set-state (vc-file-setprop file 'vc-state 'edited)))
-   ;; normal entry
-   ((looking-at
-     (concat "/[^/]+"
-	     ;; revision
-	     "/\\([^/]*\\)"
-	     ;; timestamp
-	     "/[A-Z][a-z][a-z]"       ;; week day (irrelevant)
-	     " \\([A-Z][a-z][a-z]\\)" ;; month name
-	     " *\\([0-9]*\\)"         ;; day of month
-	     " \\([0-9]*\\):\\([0-9]*\\):\\([0-9]*\\)"  ;; hms
-	     " \\([0-9]*\\)"          ;; year
-	     ;; optional conflict field
-	     "\\(+[^/]*\\)?/"))
-    (vc-file-setprop file 'vc-workfile-version (match-string 1))
-    ;; compare checkout time and modification time
-    (let ((second (string-to-number (match-string 6)))
-	  (minute (string-to-number (match-string 5)))
-	  (hour (string-to-number (match-string 4)))
-	  (day (string-to-number (match-string 3)))
-	  (year (string-to-number (match-string 7)))
-	  (month (/ (string-match
-		     (match-string 2)
-		     "xxxJanFebMarAprMayJunJulAugSepOctNovDec")
-		    3))
-	  (mtime (nth 5 (file-attributes file))))
-      (cond ((equal mtime
-		    (encode-time second minute hour day month year 0))
-	     (vc-file-setprop file 'vc-checkout-time mtime)
-	     (if set-state (vc-file-setprop file 'vc-state 'up-to-date)))
-	    (t
-	     (vc-file-setprop file 'vc-checkout-time 0)
-	     (if set-state (vc-file-setprop file 'vc-state 'edited))))))
-   ;; entry with arbitrary text as timestamp
-   ;; (this means we should consider it modified)
-   ((looking-at
-     (concat "/[^/]+"
-	     ;; revision
-	     "/\\([^/]*\\)"
-	     ;; timestamp (arbitrary text)
-	     "/[^/]*"
-	     ;; optional conflict field
-	     "\\(+[^/]*\\)?/"))
-    (vc-file-setprop file 'vc-workfile-version (match-string 1))
-    (vc-file-setprop file 'vc-checkout-time 0)
-    (if set-state (vc-file-setprop file 'vc-state 'edited)))))
 
-(defun vc-cvs-print-log (file)
-  "Get change log associated with FILE."
-  (vc-do-command t (if (vc-cvs-stay-local-p file) 'async 0)
-                 "cvs" file "log"))
+;;;
+;;; State-changing functions
+;;;
 
-(defun vc-cvs-show-log-entry (version)
-  (when (re-search-forward
-	 ;; also match some context, for safety
-	 (concat "----\nrevision " version
-		 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
-    ;; set the display window so that
-    ;; the whole log entry is displayed
-    (let (start end lines)
-      (beginning-of-line) (forward-line -1) (setq start (point))
-      (if (not (re-search-forward "^----*\nrevision" nil t))
-	  (setq end (point-max))
-	(beginning-of-line) (forward-line -1) (setq end (point)))
-      (setq lines (count-lines start end))
-      (cond
-       ;; if the global information and this log entry fit
-       ;; into the window, display from the beginning
-       ((< (count-lines (point-min) end) (window-height))
-	(goto-char (point-min))
-	(recenter 0)
-	(goto-char start))
-       ;; if the whole entry fits into the window,
-       ;; display it centered
-       ((< (1+ lines) (window-height))
-	(goto-char start)
-	(recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
-       ;; otherwise (the entry is too large for the window),
-       ;; display from the start
-       (t
-	(goto-char start)
-	(recenter 0))))))
-
-(defun vc-cvs-create-snapshot (dir name branchp)
-  "Assign to DIR's current version a given NAME.
-If BRANCHP is non-nil, the name is created as a branch (and the current
-workspace is immediately moved to that new branch)."
-  (vc-do-command nil 0 "cvs" dir "tag" "-c" (if branchp "-b") name)
-  (when branchp (vc-do-command nil 0 "cvs" dir "update" "-r" name)))
+(defun vc-cvs-register (file &optional rev comment)
+  "Register FILE into the CVS version-control system.
+COMMENT can be used to provide an initial description of FILE.
 
-(defun vc-cvs-retrieve-snapshot (dir name update)
-  "Retrieve a snapshot at and below DIR.
-NAME is the name of the snapshot; if it is empty, do a `cvs update'.
-If UPDATE is non-nil, then update (resynch) any affected buffers."
-  (with-current-buffer (get-buffer-create "*vc*")
-    (let ((default-directory dir))
-      (erase-buffer)
-      (if (or (not name) (string= name ""))
-	  (vc-do-command t 0 "cvs" nil "update")
-	(vc-do-command t 0 "cvs" nil "update" "-r" name))
-      (when update
-	(goto-char (point-min))
-	(while (not (eobp))
-	  (if (looking-at "\\([CMUP]\\) \\(.*\\)")
-	      (let* ((file (expand-file-name (match-string 2) dir))
-		     (state (match-string 1))
-		     (buffer (find-buffer-visiting file)))
-		(when buffer
-		  (cond
-		   ((or (string= state "U")
-			(string= state "P"))
-		    (vc-file-setprop file 'vc-state 'up-to-date)
-		    (vc-file-setprop file 'vc-workfile-version nil)
-		    (vc-file-setprop file 'vc-checkout-time
-				     (nth 5 (file-attributes file))))
-		   ((or (string= state "M")
-			(string= state "C"))
-		    (vc-file-setprop file 'vc-state 'edited)
-		    (vc-file-setprop file 'vc-workfile-version nil)
-		    (vc-file-setprop file 'vc-checkout-time 0)))
-		  (vc-resynch-buffer file t t))))
-	  (forward-line 1))))))
-
-(defun vc-cvs-merge (file first-version &optional second-version)
-  "Merge changes into current working copy of FILE.
-The changes are between FIRST-VERSION and SECOND-VERSION."
-  (vc-do-command nil 0 "cvs" file
-                 "update" "-kk"
-                 (concat "-j" first-version)
-                 (concat "-j" second-version))
-  (vc-file-setprop file 'vc-state 'edited)
-  (save-excursion
-    (set-buffer (get-buffer "*vc*"))
-    (goto-char (point-min))
-    (if (re-search-forward "conflicts during merge" nil t)
-        1				; signal error
-      0)))				; signal success
+`vc-register-switches' and `vc-cvs-register-switches' are passed to
+the CVS command (in that order)."
+    (let ((switches (list
+		     (if (stringp vc-register-switches)
+			 (list vc-register-switches)
+		       vc-register-switches)
+		     (if (stringp vc-cvs-register-switches)
+			 (list vc-cvs-register-switches)
+		       vc-cvs-register-switches))))
+    
+      (apply 'vc-do-command nil 0 "cvs" file
+	     "add"
+	     (and comment (string-match "[^\t\n ]" comment)
+		  (concat "-m" comment))
+	     switches)))
 
-(defun vc-cvs-merge-news (file)
-  "Merge in any new changes made to FILE."
-  (message "Merging changes into %s..." file)
-  (save-excursion
-    ;; (vc-file-setprop file 'vc-workfile-version nil)
-    (vc-file-setprop file 'vc-checkout-time 0)
-    (vc-do-command nil 0 "cvs" file "update")
-    ;; Analyze the merge result reported by CVS, and set
-    ;; file properties accordingly.
-    (set-buffer (get-buffer "*vc*"))
-    (goto-char (point-min))
-    ;; get new workfile version
-    (if (re-search-forward (concat "^Merging differences between "
-				   "[01234567890.]* and "
-				   "\\([01234567890.]*\\) into")
-			   nil t)
-	(vc-file-setprop file 'vc-workfile-version (match-string 1))
-      (vc-file-setprop file 'vc-workfile-version nil))
-    ;; get file status
-    (prog1
-        (if (eq (buffer-size) 0)
-            0 ;; there were no news; indicate success
-          (if (re-search-forward
-               (concat "^\\([CMUP] \\)?"
-                       (regexp-quote (file-name-nondirectory file))
-                       "\\( already contains the differences between \\)?")
-               nil t)
-              (cond
-               ;; Merge successful, we are in sync with repository now
-               ((or (match-string 2)
-                    (string= (match-string 1) "U ")
-                    (string= (match-string 1) "P "))
-                (vc-file-setprop file 'vc-state 'up-to-date)
-                (vc-file-setprop file 'vc-checkout-time
-                                 (nth 5 (file-attributes file)))
-                0);; indicate success to the caller
-               ;; Merge successful, but our own changes are still in the file
-               ((string= (match-string 1) "M ")
-                (vc-file-setprop file 'vc-state 'edited)
-                0);; indicate success to the caller
-               ;; Conflicts detected!
-               (t
-                (vc-file-setprop file 'vc-state 'edited)
-                1);; signal the error to the caller
-               )
-            (pop-to-buffer "*vc*")
-            (error "Couldn't analyze cvs update result")))
-      (message "Merging changes into %s...done" file))))
+(defun vc-cvs-responsible-p (file)
+  "Return non-nil if CVS thinks it is responsible for FILE."
+  (file-directory-p (expand-file-name "CVS"
+				      (if (file-directory-p file)
+					  file
+					(file-name-directory file)))))
 
-(defun vc-cvs-check-headers ()
-  "Check if the current file has any headers in it."
-  (save-excursion
-    (goto-char (point-min))
-    (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
-\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
-
-(defun vc-cvs-steal (file &optional rev)
-  "Steal the lock on the current workfile for FILE and revision REV.
-Inappropriate for CVS"
-  (error "You cannot steal a CVS lock; there are no CVS locks to steal"))
-
-;; vc-check `not reached' for CVS.
-
-(defun vc-cvs-revert (file)
-  "Revert FILE to the version it was based on."
-  ;; Check out via standard output (caused by the final argument
-  ;; FILE below), so that no sticky tag is set.
-  (vc-cvs-checkout file nil (vc-workfile-version file) file)
-  ;; If "cvs edit" was used to make the file writable,
-  ;; call "cvs unedit" now to undo that.
-  (if (and (not (eq (vc-cvs-checkout-model file) 'implicit))
-           vc-cvs-use-edit)
-      (vc-do-command nil 0 "cvs" file "unedit")))
-
-(defun vc-cvs-diff (file &optional oldvers newvers)
-  "Get a difference report using CVS between two versions of FILE."
-  (let (options status
-        (diff-switches-list (if (listp diff-switches)
-                                diff-switches
-                              (list diff-switches))))
-    (if (string= (vc-workfile-version file) "0")
-	;; This file is added but not yet committed; there is no master file.
-	(if (or oldvers newvers)
-	    (error "No revisions of %s exist" file)
-	  ;; we regard this as "changed".
-	  ;; diff it against /dev/null.
-          (apply 'vc-do-command t
-                 1 "diff" file
-                 (append diff-switches-list '("/dev/null"))))
-      (setq status
-            (apply 'vc-do-command t
-                   (if (vc-cvs-stay-local-p file) 'async 1)
-                   "cvs" file "diff"
-                   (and oldvers (concat "-r" oldvers))
-                   (and newvers (concat "-r" newvers))
-                   diff-switches-list))
-      (if (vc-cvs-stay-local-p file) 
-          1 ;; async diff, pessimistic assumption 
-        status))))
-
-(defun vc-cvs-latest-on-branch-p (file)
-  "Return t iff current workfile version of FILE is the latest on its branch."
-  ;; Since this is only used as a sanity check for vc-cancel-version,
-  ;; and that is not supported under CVS at all, we can safely return t here.
-  ;; TODO: Think of getting rid of this altogether.
-  t)
+(defun vc-cvs-could-register (file)
+  "Return non-nil if FILE could be registered in CVS.
+This is only possible if CVS is responsible for FILE's directory."
+  (vc-cvs-responsible-p file))
 
 (defun vc-cvs-checkin (file rev comment)
   "CVS-specific version of `vc-backend-checkin'."
@@ -553,42 +292,6 @@
     ;; if this was an explicit check-in, remove the sticky tag
     (if rev (vc-do-command t 0 "cvs" file "update" "-A"))))
 
-(defun vc-cvs-responsible-p (file)
-  "Return non-nil if CVS thinks it is responsible for FILE."
-  (file-directory-p (expand-file-name "CVS"
-				      (if (file-directory-p file)
-					  file
-					(file-name-directory file)))))
-
-(defun vc-cvs-could-register (file)
-  "Return non-nil if FILE could be registered in CVS.
-This is only possible if CVS is responsible for FILE's directory."
-  (vc-cvs-responsible-p file))
-
-(defun vc-cvs-make-version-backups-p (file)
-  "Return non-nil if version backups should be made for FILE."
-  (vc-cvs-stay-local-p file))
-
-(defun vc-cvs-register (file &optional rev comment)
-  "Register FILE into the CVS version-control system.
-COMMENT can be used to provide an initial description of FILE.
-
-`vc-register-switches' and `vc-cvs-register-switches' are passed to
-the CVS command (in that order)."
-    (let ((switches (list
-		     (if (stringp vc-register-switches)
-			 (list vc-register-switches)
-		       vc-register-switches)
-		     (if (stringp vc-cvs-register-switches)
-			 (list vc-cvs-register-switches)
-		       vc-cvs-register-switches))))
-    
-      (apply 'vc-do-command nil 0 "cvs" file
-	     "add"
-	     (and comment (string-match "[^\t\n ]" comment)
-		  (concat "-m" comment))
-	     switches)))
-
 (defun vc-cvs-checkout (file &optional writable rev workfile)
   "Retrieve a revision of FILE into a WORKFILE.
 WRITABLE non-nil means that the file should be writable.
@@ -670,18 +373,154 @@
 	(vc-mode-line file)
 	(message "Checking out %s...done" filename)))))
 
+(defun vc-cvs-revert (file)
+  "Revert FILE to the version it was based on."
+  ;; Check out via standard output (caused by the final argument
+  ;; FILE below), so that no sticky tag is set.
+  (vc-cvs-checkout file nil (vc-workfile-version file) file)
+  ;; If "cvs edit" was used to make the file writable,
+  ;; call "cvs unedit" now to undo that.
+  (if (and (not (eq (vc-cvs-checkout-model file) 'implicit))
+           vc-cvs-use-edit)
+      (vc-do-command nil 0 "cvs" file "unedit")))
+
+(defun vc-cvs-merge (file first-version &optional second-version)
+  "Merge changes into current working copy of FILE.
+The changes are between FIRST-VERSION and SECOND-VERSION."
+  (vc-do-command nil 0 "cvs" file
+                 "update" "-kk"
+                 (concat "-j" first-version)
+                 (concat "-j" second-version))
+  (vc-file-setprop file 'vc-state 'edited)
+  (save-excursion
+    (set-buffer (get-buffer "*vc*"))
+    (goto-char (point-min))
+    (if (re-search-forward "conflicts during merge" nil t)
+        1				; signal error
+      0)))				; signal success
+
+(defun vc-cvs-merge-news (file)
+  "Merge in any new changes made to FILE."
+  (message "Merging changes into %s..." file)
+  (save-excursion
+    ;; (vc-file-setprop file 'vc-workfile-version nil)
+    (vc-file-setprop file 'vc-checkout-time 0)
+    (vc-do-command nil 0 "cvs" file "update")
+    ;; Analyze the merge result reported by CVS, and set
+    ;; file properties accordingly.
+    (set-buffer (get-buffer "*vc*"))
+    (goto-char (point-min))
+    ;; get new workfile version
+    (if (re-search-forward (concat "^Merging differences between "
+				   "[01234567890.]* and "
+				   "\\([01234567890.]*\\) into")
+			   nil t)
+	(vc-file-setprop file 'vc-workfile-version (match-string 1))
+      (vc-file-setprop file 'vc-workfile-version nil))
+    ;; get file status
+    (prog1
+        (if (eq (buffer-size) 0)
+            0 ;; there were no news; indicate success
+          (if (re-search-forward
+               (concat "^\\([CMUP] \\)?"
+                       (regexp-quote (file-name-nondirectory file))
+                       "\\( already contains the differences between \\)?")
+               nil t)
+              (cond
+               ;; Merge successful, we are in sync with repository now
+               ((or (match-string 2)
+                    (string= (match-string 1) "U ")
+                    (string= (match-string 1) "P "))
+                (vc-file-setprop file 'vc-state 'up-to-date)
+                (vc-file-setprop file 'vc-checkout-time
+                                 (nth 5 (file-attributes file)))
+                0);; indicate success to the caller
+               ;; Merge successful, but our own changes are still in the file
+               ((string= (match-string 1) "M ")
+                (vc-file-setprop file 'vc-state 'edited)
+                0);; indicate success to the caller
+               ;; Conflicts detected!
+               (t
+                (vc-file-setprop file 'vc-state 'edited)
+                1);; signal the error to the caller
+               )
+            (pop-to-buffer "*vc*")
+            (error "Couldn't analyze cvs update result")))
+      (message "Merging changes into %s...done" file))))
+
+
+;;;
+;;; History functions
+;;;
+
+(defun vc-cvs-print-log (file)
+  "Get change log associated with FILE."
+  (vc-do-command t (if (vc-cvs-stay-local-p file) 'async 0)
+                 "cvs" file "log"))
+
+(defun vc-cvs-show-log-entry (version)
+  (when (re-search-forward
+	 ;; also match some context, for safety
+	 (concat "----\nrevision " version
+		 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
+    ;; set the display window so that
+    ;; the whole log entry is displayed
+    (let (start end lines)
+      (beginning-of-line) (forward-line -1) (setq start (point))
+      (if (not (re-search-forward "^----*\nrevision" nil t))
+	  (setq end (point-max))
+	(beginning-of-line) (forward-line -1) (setq end (point)))
+      (setq lines (count-lines start end))
+      (cond
+       ;; if the global information and this log entry fit
+       ;; into the window, display from the beginning
+       ((< (count-lines (point-min) end) (window-height))
+	(goto-char (point-min))
+	(recenter 0)
+	(goto-char start))
+       ;; if the whole entry fits into the window,
+       ;; display it centered
+       ((< (1+ lines) (window-height))
+	(goto-char start)
+	(recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
+       ;; otherwise (the entry is too large for the window),
+       ;; display from the start
+       (t
+	(goto-char start)
+	(recenter 0))))))
+
+(defun vc-cvs-diff (file &optional oldvers newvers)
+  "Get a difference report using CVS between two versions of FILE."
+  (let (options status
+        (diff-switches-list (if (listp diff-switches)
+                                diff-switches
+                              (list diff-switches))))
+    (if (string= (vc-workfile-version file) "0")
+	;; This file is added but not yet committed; there is no master file.
+	(if (or oldvers newvers)
+	    (error "No revisions of %s exist" file)
+	  ;; we regard this as "changed".
+	  ;; diff it against /dev/null.
+          (apply 'vc-do-command t
+                 1 "diff" file
+                 (append diff-switches-list '("/dev/null"))))
+      (setq status
+            (apply 'vc-do-command t
+                   (if (vc-cvs-stay-local-p file) 'async 1)
+                   "cvs" file "diff"
+                   (and oldvers (concat "-r" oldvers))
+                   (and newvers (concat "-r" newvers))
+                   diff-switches-list))
+      (if (vc-cvs-stay-local-p file) 
+          1 ;; async diff, pessimistic assumption 
+        status))))
+
 (defun vc-cvs-annotate-command (file buffer &optional version)
   "Execute \"cvs annotate\" on FILE, inserting the contents in BUFFER.
 Optional arg VERSION is a version to annotate from."
   (vc-do-command buffer 0 "cvs" file "annotate" (if version
                                                     (concat "-r" version))))
 
-(defvar vc-cvs-local-month-numbers
-  '(("Jan" . 1) ("Feb" .  2) ("Mar" .  3) ("Apr" .  4)
-    ("May" . 5) ("Jun" .  6) ("Jul" .  7) ("Aug" .  8)
-    ("Sep" . 9) ("Oct" . 10) ("Nov" . 11) ("Dec" . 12))
-  "Local association list of month numbers.")
-
 (defun vc-cvs-annotate-difference (point)
   "Return the difference between the time of the line and the current time.
 Return values are as defined for `current-time'."
@@ -709,6 +548,197 @@
 	  (beginning-of-line nil)
 	  (vc-cvs-annotate-difference (point))))))
 
+
+;;;
+;;; Snapshot system
+;;;
+
+(defun vc-cvs-create-snapshot (dir name branchp)
+  "Assign to DIR's current version a given NAME.
+If BRANCHP is non-nil, the name is created as a branch (and the current
+workspace is immediately moved to that new branch)."
+  (vc-do-command nil 0 "cvs" dir "tag" "-c" (if branchp "-b") name)
+  (when branchp (vc-do-command nil 0 "cvs" dir "update" "-r" name)))
+
+(defun vc-cvs-retrieve-snapshot (dir name update)
+  "Retrieve a snapshot at and below DIR.
+NAME is the name of the snapshot; if it is empty, do a `cvs update'.
+If UPDATE is non-nil, then update (resynch) any affected buffers."
+  (with-current-buffer (get-buffer-create "*vc*")
+    (let ((default-directory dir))
+      (erase-buffer)
+      (if (or (not name) (string= name ""))
+	  (vc-do-command t 0 "cvs" nil "update")
+	(vc-do-command t 0 "cvs" nil "update" "-r" name))
+      (when update
+	(goto-char (point-min))
+	(while (not (eobp))
+	  (if (looking-at "\\([CMUP]\\) \\(.*\\)")
+	      (let* ((file (expand-file-name (match-string 2) dir))
+		     (state (match-string 1))
+		     (buffer (find-buffer-visiting file)))
+		(when buffer
+		  (cond
+		   ((or (string= state "U")
+			(string= state "P"))
+		    (vc-file-setprop file 'vc-state 'up-to-date)
+		    (vc-file-setprop file 'vc-workfile-version nil)
+		    (vc-file-setprop file 'vc-checkout-time
+				     (nth 5 (file-attributes file))))
+		   ((or (string= state "M")
+			(string= state "C"))
+		    (vc-file-setprop file 'vc-state 'edited)
+		    (vc-file-setprop file 'vc-workfile-version nil)
+		    (vc-file-setprop file 'vc-checkout-time 0)))
+		  (vc-resynch-buffer file t t))))
+	  (forward-line 1))))))
+
+
+;;;
+;;; Miscellaneous
+;;;
+
+(defun vc-cvs-make-version-backups-p (file)
+  "Return non-nil if version backups should be made for FILE."
+  (vc-cvs-stay-local-p file))
+
+(defun vc-cvs-check-headers ()
+  "Check if the current file has any headers in it."
+  (save-excursion
+    (goto-char (point-min))
+    (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
+\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
+
+
+;;;
+;;; Internal functions
+;;;
+
+(defun vc-cvs-stay-local-p (file)
+  "Return non-nil if VC should stay local when handling FILE."
+  (if vc-cvs-stay-local
+      (let* ((dirname (if (file-directory-p file)
+			  (directory-file-name file)
+			(file-name-directory file)))
+	     (prop
+	      (or (vc-file-getprop dirname 'vc-cvs-stay-local-p)
+		  (let ((rootname (expand-file-name "CVS/Root" dirname)))
+		    (vc-file-setprop
+		     dirname 'vc-cvs-stay-local-p
+		     (when (file-readable-p rootname)
+		       (with-temp-buffer
+			 (vc-insert-file rootname)
+			 (goto-char (point-min))
+			 (if (looking-at "\\([^:]*\\):")
+			     (if (not (stringp vc-cvs-stay-local))
+				 'yes
+			       (let ((hostname (match-string 1)))
+				 (if (string-match vc-cvs-stay-local hostname)
+				     'yes
+				   'no)))
+			   'no))))))))
+	(if (eq prop 'yes) t nil))))
+
+(defun vc-cvs-parse-status (&optional full)
+  "Parse output of \"cvs status\" command in the current buffer.
+Set file properties accordingly.  Unless FULL is t, parse only
+essential information."
+  (let (file status)
+    (goto-char (point-min))
+    (if (re-search-forward "^File: " nil t)
+        (cond
+         ((looking-at "no file") nil)
+         ((re-search-forward "\\=\\([^ \t]+\\)" nil t)
+	  (setq file (expand-file-name (match-string 1)))
+          (vc-file-setprop file 'vc-backend 'CVS)
+          (if (not (re-search-forward "\\=[ \t]+Status: \\(.*\\)" nil t))
+              (setq status "Unknown")
+            (setq status (match-string 1)))
+          (if (and full
+                   (re-search-forward
+		    "\\(RCS Version\\|RCS Revision\\|Repository revision\\):\
+\[\t ]+\\([0-9.]+\\)"
+                    nil t))
+              (vc-file-setprop file 'vc-latest-version (match-string 2)))
+          (cond
+           ((string-match "Up-to-date" status)
+            (vc-file-setprop file 'vc-checkout-time
+                             (nth 5 (file-attributes file)))
+            'up-to-date)
+           ((string-match "Locally Modified"    status) 'edited)
+	   ((string-match "Needs Merge"         status) 'needs-merge)
+	   ((string-match "Needs \\(Checkout\\|Patch\\)" status) 'needs-patch)
+	   (t 'edited)))))))
+
+(defun vc-cvs-dir-state-heuristic (dir)
+  "Find the CVS state of all files in DIR, using only local information."
+  (with-temp-buffer
+    (vc-insert-file (expand-file-name "CVS/Entries" dir))
+    (goto-char (point-min))
+    (while (not (eobp))
+      (when (looking-at "/\\([^/]*\\)/")
+	(let ((file (expand-file-name (match-string 1) dir)))
+	  (unless (vc-file-getprop file 'vc-state)
+	    (vc-cvs-parse-entry file t))))
+      (forward-line 1))))
+
+(defun vc-cvs-parse-entry (file &optional set-state)
+  "Parse a line from CVS/Entries.
+Compare modification time to that of the FILE, set file properties
+accordingly.  However, `vc-state' is set only if optional arg SET-STATE
+is non-nil."
+  (cond
+   ;; entry for a "locally added" file (not yet committed)
+   ((looking-at "/[^/]+/0/")
+    (vc-file-setprop file 'vc-checkout-time 0)
+    (vc-file-setprop file 'vc-workfile-version "0")
+    (if set-state (vc-file-setprop file 'vc-state 'edited)))
+   ;; normal entry
+   ((looking-at
+     (concat "/[^/]+"
+	     ;; revision
+	     "/\\([^/]*\\)"
+	     ;; timestamp
+	     "/[A-Z][a-z][a-z]"       ;; week day (irrelevant)
+	     " \\([A-Z][a-z][a-z]\\)" ;; month name
+	     " *\\([0-9]*\\)"         ;; day of month
+	     " \\([0-9]*\\):\\([0-9]*\\):\\([0-9]*\\)"  ;; hms
+	     " \\([0-9]*\\)"          ;; year
+	     ;; optional conflict field
+	     "\\(+[^/]*\\)?/"))
+    (vc-file-setprop file 'vc-workfile-version (match-string 1))
+    ;; compare checkout time and modification time
+    (let ((second (string-to-number (match-string 6)))
+	  (minute (string-to-number (match-string 5)))
+	  (hour (string-to-number (match-string 4)))
+	  (day (string-to-number (match-string 3)))
+	  (year (string-to-number (match-string 7)))
+	  (month (/ (string-match
+		     (match-string 2)
+		     "xxxJanFebMarAprMayJunJulAugSepOctNovDec")
+		    3))
+	  (mtime (nth 5 (file-attributes file))))
+      (cond ((equal mtime
+		    (encode-time second minute hour day month year 0))
+	     (vc-file-setprop file 'vc-checkout-time mtime)
+	     (if set-state (vc-file-setprop file 'vc-state 'up-to-date)))
+	    (t
+	     (vc-file-setprop file 'vc-checkout-time 0)
+	     (if set-state (vc-file-setprop file 'vc-state 'edited))))))
+   ;; entry with arbitrary text as timestamp
+   ;; (this means we should consider it modified)
+   ((looking-at
+     (concat "/[^/]+"
+	     ;; revision
+	     "/\\([^/]*\\)"
+	     ;; timestamp (arbitrary text)
+	     "/[^/]*"
+	     ;; optional conflict field
+	     "\\(+[^/]*\\)?/"))
+    (vc-file-setprop file 'vc-workfile-version (match-string 1))
+    (vc-file-setprop file 'vc-checkout-time 0)
+    (if set-state (vc-file-setprop file 'vc-state 'edited)))))
+           
 (provide 'vc-cvs)
 
 ;;; vc-cvs.el ends here
--- a/lisp/vc-rcs.el	Thu Nov 16 17:09:04 2000 +0000
+++ b/lisp/vc-rcs.el	Thu Nov 16 18:14:41 2000 +0000
@@ -5,7 +5,7 @@
 ;; Author:     FSF (see vc.el for full credits)
 ;; Maintainer: Andre Spiegel <spiegel@gnu.org>
 
-;; $Id: vc-rcs.el,v 1.10 2000/10/03 11:33:59 spiegel Exp $
+;; $Id: vc-rcs.el,v 1.11 2000/10/03 12:08:40 spiegel Exp $
 
 ;; This file is part of GNU Emacs.
 
@@ -28,6 +28,10 @@
 
 ;;; Code:
 
+;;;
+;;; Customization options
+;;;
+
 (eval-when-compile
   (require 'cl))
 
@@ -99,6 +103,11 @@
   :version "21.1"
   :group 'vc)
 
+
+;;;
+;;; State-querying functions
+;;;
+
 ;;;###autoload
 (progn (defun vc-rcs-registered (f) (vc-default-registered 'RCS f)))
 
@@ -164,6 +173,446 @@
                    (vc-rcs-state file))))
         (vc-rcs-state file)))))
 
+(defun vc-rcs-workfile-version (file)
+  "RCS-specific version of `vc-workfile-version'."
+  (or (and vc-consult-headers
+           (vc-rcs-consult-headers file)
+           (vc-file-getprop file 'vc-workfile-version))
+      (progn
+        (vc-rcs-fetch-master-state file)
+        (vc-file-getprop file 'vc-workfile-version))))
+
+(defun vc-rcs-latest-on-branch-p (file &optional version)
+  "Return non-nil if workfile version of FILE is the latest on its branch.
+When VERSION is given, perform check for that version."
+  (unless version (setq version (vc-workfile-version file)))
+  (with-temp-buffer
+    (string= version
+	     (if (vc-rcs-trunk-p version)
+		 (progn
+		   ;; Compare VERSION to the head version number.
+		   (vc-insert-file (vc-name file) "^[0-9]")
+		   (vc-parse-buffer "^head[ \t\n]+\\([^;]+\\);" 1))
+	       ;; If we are not on the trunk, we need to examine the
+	       ;; whole current branch.
+	       (vc-insert-file (vc-name file) "^desc")
+	       (vc-rcs-find-most-recent-rev (vc-rcs-branch-part version))))))
+
+(defun vc-rcs-checkout-model (file)
+  "RCS-specific version of `vc-checkout-model'."
+  (vc-rcs-consult-headers file)
+  (or (vc-file-getprop file 'vc-checkout-model)
+      (progn (vc-rcs-fetch-master-state file)
+	     (vc-file-getprop file 'vc-checkout-model))))
+
+(defun vc-rcs-workfile-unchanged-p (file)
+  "RCS-specific implementation of vc-workfile-unchanged-p."
+  ;; Try to use rcsdiff --brief.  If rcsdiff does not understand that,
+  ;; do a double take and remember the fact for the future
+  (let* ((version (concat "-r" (vc-workfile-version file)))
+         (status (if (eq vc-rcsdiff-knows-brief 'no)
+                     (vc-do-command nil 1 "rcsdiff" file version)
+                   (vc-do-command nil 2 "rcsdiff" file "--brief" version))))
+    (if (eq status 2)
+        (if (not vc-rcsdiff-knows-brief)
+            (setq vc-rcsdiff-knows-brief 'no
+                  status (vc-do-command nil 1 "rcsdiff" file version))
+          (error "rcsdiff failed"))
+      (if (not vc-rcsdiff-knows-brief) (setq vc-rcsdiff-knows-brief 'yes)))
+    ;; The workfile is unchanged if rcsdiff found no differences.
+    (zerop status)))
+
+
+;;;
+;;; State-changing functions
+;;;
+
+(defun vc-rcs-register (file &optional rev comment)
+  "Register FILE into the RCS version-control system.
+REV is the optional revision number for the file.  COMMENT can be used
+to provide an initial description of FILE.
+
+`vc-register-switches' and `vc-rcs-register-switches' are passed to
+the RCS command (in that order).
+
+Automatically retrieve a read-only version of the file with keywords
+expanded if `vc-keep-workfiles' is non-nil, otherwise, delete the workfile."
+    (let ((subdir (expand-file-name "RCS" (file-name-directory file)))
+	  (switches (list
+		     (if (stringp vc-register-switches)
+			 (list vc-register-switches)
+		       vc-register-switches)
+		     (if (stringp vc-rcs-register-switches)
+		     (list vc-rcs-register-switches)
+		     vc-rcs-register-switches))))
+      
+      (and (not (file-exists-p subdir))
+	   (not (directory-files (file-name-directory file)
+				 nil ".*,v$" t))
+	   (yes-or-no-p "Create RCS subdirectory? ")
+	   (make-directory subdir))
+      (apply 'vc-do-command nil 0 "ci" file
+	     ;; if available, use the secure registering option
+	     (and (vc-rcs-release-p "5.6.4") "-i")
+	     (concat (if vc-keep-workfiles "-u" "-r") rev)
+	     (and comment (concat "-t-" comment))
+	     switches)
+      ;; parse output to find master file name and workfile version
+      (with-current-buffer "*vc*"
+        (goto-char (point-min))
+        (let ((name (if (looking-at (concat "^\\(.*\\)  <--  "
+                                            (file-name-nondirectory file)))
+                        (match-string 1))))
+          (if (not name)
+              ;; if we couldn't find the master name,
+              ;; run vc-rcs-registered to get it
+              ;; (will be stored into the vc-name property)
+              (vc-rcs-registered file)
+            (vc-file-setprop file 'vc-name
+                             (if (file-name-absolute-p name)
+                                 name
+                               (expand-file-name
+                                name
+                                (file-name-directory file))))))
+        (vc-file-setprop file 'vc-workfile-version
+                         (if (re-search-forward
+                              "^initial revision: \\([0-9.]+\\).*\n"
+                              nil t)
+                             (match-string 1))))))
+
+(defun vc-rcs-responsible-p (file)
+  "Return non-nil if RCS thinks it would be responsible for registering FILE."
+  ;; TODO: check for all the patterns in vc-rcs-master-templates
+  (file-directory-p (expand-file-name "RCS" (file-name-directory file))))
+
+(defun vc-rcs-receive-file (file rev)
+  "Implementation of receive-file for RCS."
+  (let ((checkout-model (vc-checkout-model file)))
+    (vc-rcs-register file rev "")
+    (when (eq checkout-model 'implicit)
+      (vc-rcs-set-non-strict-locking file))
+    (vc-rcs-set-default-branch file (concat rev ".1"))))
+
+(defun vc-rcs-unregister (file)
+  "Unregister FILE from RCS.
+If this leaves the RCS subdirectory empty, ask the user
+whether to remove it."
+  (let* ((master (vc-name file))
+	 (dir (file-name-directory master))
+	 (backup-info (find-backup-file-name master)))
+    (if (not backup-info)
+	(delete-file master)
+      (rename-file master (car backup-info) 'ok-if-already-exists)
+      (dolist (f (cdr backup-info)) (ignore-errors (delete-file f))))
+    (and (string= (file-name-nondirectory (directory-file-name dir)) "RCS")
+	 ;; check whether RCS dir is empty, i.e. it does not
+	 ;; contain any files except "." and ".."
+	 (not (directory-files dir nil 
+			       "^\\([^.]\\|\\.[^.]\\|\\.\\.[^.]\\).*"))
+	 (yes-or-no-p (format "Directory %s is empty; remove it? " dir))
+	 (delete-directory dir))))
+
+(defun vc-rcs-checkin (file rev comment)
+  "RCS-specific version of `vc-backend-checkin'."
+  (let ((switches (if (stringp vc-checkin-switches)
+		      (list vc-checkin-switches)
+		    vc-checkin-switches)))
+    (let ((old-version (vc-workfile-version file)) new-version
+	  (default-branch (vc-file-getprop file 'vc-rcs-default-branch)))
+      ;; Force branch creation if an appropriate 
+      ;; default branch has been set.
+      (and (not rev)
+	   default-branch
+	   (string-match (concat "^" (regexp-quote old-version) "\\.")
+			 default-branch)
+	   (setq rev default-branch)
+	   (setq switches (cons "-f" switches)))
+      (apply 'vc-do-command nil 0 "ci" (vc-name file)
+	     ;; if available, use the secure check-in option
+	     (and (vc-rcs-release-p "5.6.4") "-j")
+	     (concat (if vc-keep-workfiles "-u" "-r") rev)
+	     (concat "-m" comment)
+	     switches)
+      (vc-file-setprop file 'vc-workfile-version nil)
+
+      ;; determine the new workfile version
+      (set-buffer "*vc*")
+      (goto-char (point-min))
+      (when (or (re-search-forward
+		 "new revision: \\([0-9.]+\\);" nil t)
+		(re-search-forward
+		 "reverting to previous revision \\([0-9.]+\\)" nil t))
+	(setq new-version (match-string 1))
+	(vc-file-setprop file 'vc-workfile-version new-version))
+
+      ;; if we got to a different branch, adjust the default
+      ;; branch accordingly
+      (cond
+       ((and old-version new-version
+	     (not (string= (vc-rcs-branch-part old-version)
+			   (vc-rcs-branch-part new-version))))
+	(vc-rcs-set-default-branch file 
+				   (if (vc-rcs-trunk-p new-version) nil
+				     (vc-rcs-branch-part new-version)))
+	;; If this is an old RCS release, we might have
+	;; to remove a remaining lock.
+	(if (not (vc-rcs-release-p "5.6.2"))
+	    ;; exit status of 1 is also accepted.
+	    ;; It means that the lock was removed before.
+	    (vc-do-command nil 1 "rcs" (vc-name file)
+			   (concat "-u" old-version))))))))
+
+(defun vc-rcs-checkout (file &optional writable rev workfile)
+  "Retrieve a copy of a saved version of FILE into a workfile."
+  (let ((filename (or workfile file))
+	(file-buffer (get-file-buffer file))
+	switches)
+    (message "Checking out %s..." filename)
+    (save-excursion
+      ;; Change buffers to get local value of vc-checkout-switches.
+      (if file-buffer (set-buffer file-buffer))
+      (setq switches (if (stringp vc-checkout-switches)
+			 (list vc-checkout-switches)
+		       vc-checkout-switches))
+      ;; Save this buffer's default-directory
+      ;; and use save-excursion to make sure it is restored
+      ;; in the same buffer it was saved in.
+      (let ((default-directory default-directory))
+	(save-excursion
+	  ;; Adjust the default-directory so that the check-out creates
+	  ;; the file in the right place.
+	  (setq default-directory (file-name-directory filename))
+	  (if workfile  ;; RCS
+	      ;; RCS can't check out into arbitrary file names directly.
+	      ;; Use `co -p' and make stdout point to the correct file.
+	      (let ((vc-modes (logior (file-modes (vc-name file))
+				      (if writable 128 0)))
+		    (failed t))
+		(unwind-protect
+		    (progn
+                      (let ((coding-system-for-read 'no-conversion)
+                            (coding-system-for-write 'no-conversion))
+                        (with-temp-file filename
+                          (apply 'vc-do-command
+                                 (current-buffer) 0 "co" (vc-name file)
+                                 "-q" ;; suppress diagnostic output
+                                 (if writable "-l")
+                                 (concat "-p" rev)
+                                 switches)))
+                      (set-file-modes filename
+				      (logior (file-modes (vc-name file))
+					      (if writable 128 0)))
+		      (setq failed nil))
+		  (and failed (file-exists-p filename)
+		       (delete-file filename))))
+	    (let (new-version)
+	      ;; if we should go to the head of the trunk,
+	      ;; clear the default branch first
+	      (and rev (string= rev "")
+		   (vc-rcs-set-default-branch file nil))
+	      ;; now do the checkout
+	      (apply 'vc-do-command
+		     nil 0 "co" (vc-name file)
+		     ;; If locking is not strict, force to overwrite
+		     ;; the writable workfile.
+		     (if (eq (vc-checkout-model file) 'implicit) "-f")
+		     (if writable "-l")
+		     (if rev (concat "-r" rev)
+		       ;; if no explicit revision was specified,
+		       ;; check out that of the working file
+		       (let ((workrev (vc-workfile-version file)))
+			 (if workrev (concat "-r" workrev)
+			   nil)))
+		     switches)
+	      ;; determine the new workfile version
+	      (with-current-buffer "*vc*"
+		(setq new-version
+		      (vc-parse-buffer "^revision \\([0-9.]+\\).*\n" 1)))
+	      (vc-file-setprop file 'vc-workfile-version new-version)
+	      ;; if necessary, adjust the default branch
+	      (and rev (not (string= rev ""))
+		   (vc-rcs-set-default-branch 
+		    file
+		    (if (vc-rcs-latest-on-branch-p file new-version)
+			(if (vc-rcs-trunk-p new-version) nil
+			  (vc-rcs-branch-part new-version))
+		      new-version))))))
+	(message "Checking out %s...done" filename)))))
+
+(defun vc-rcs-revert (file)
+  "Revert FILE to the version it was based on."
+  (vc-do-command nil 0 "co" (vc-name file) "-f"
+		 (concat "-u" (vc-workfile-version file))))
+
+(defun vc-rcs-cancel-version (file writable)
+  "Undo the most recent checkin of FILE.
+WRITABLE non-nil means previous version should be locked."
+  (let* ((target (vc-workfile-version file))
+	 (previous (if (vc-trunk-p target) "" (vc-branch-part target)))
+	 (config (current-window-configuration))
+	 (done nil))
+    (vc-do-command nil 0 "rcs" (vc-name file) (concat "-o" target))
+    ;; Check out the most recent remaining version.  If it fails, because
+    ;; the whole branch got deleted, do a double-take and check out the
+    ;; version where the branch started.
+    (while (not done)
+      (condition-case err
+	  (progn
+	    (vc-do-command nil 0 "co" (vc-name file) "-f"
+			   (concat (if writable "-l" "-u") previous))
+	    (setq done t))
+	(error (set-buffer "*vc*")
+	       (goto-char (point-min))
+	       (if (search-forward "no side branches present for" nil t)
+		   (progn (setq previous (vc-branch-part previous))
+			  (vc-rcs-set-default-branch file previous)
+			  ;; vc-do-command popped up a window with
+			  ;; the error message.  Get rid of it, by
+			  ;; restoring the old window configuration.
+			  (set-window-configuration config))
+		 ;; No, it was some other error: re-signal it.
+		 (signal (car err) (cdr err))))))))
+
+(defun vc-rcs-merge (file first-version &optional second-version)
+  "Merge changes into current working copy of FILE.
+The changes are between FIRST-VERSION and SECOND-VERSION."
+  (vc-do-command nil 1 "rcsmerge" (vc-name file)
+		 "-kk"			; ignore keyword conflicts
+		 (concat "-r" first-version)
+		 (if second-version (concat "-r" second-version))))
+
+(defun vc-rcs-steal-lock (file &optional rev)
+  "Steal the lock on the current workfile for FILE and revision REV.
+Needs RCS 5.6.2 or later for -M."
+  (vc-do-command nil 0 "rcs" (vc-name file) "-M"
+		 (concat "-u" rev) (concat "-l" rev)))
+
+
+
+;;;
+;;; History functions
+;;;
+
+(defun vc-rcs-print-log (file)
+  "Get change log associated with FILE."
+  (vc-do-command t 0 "rlog" (vc-name file)))
+
+(defun vc-rcs-show-log-entry (version)
+  (when (re-search-forward
+	 ;; also match some context, for safety
+	 (concat "----\nrevision " version
+		 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
+    ;; set the display window so that
+    ;; the whole log entry is displayed
+    (let (start end lines)
+      (beginning-of-line) (forward-line -1) (setq start (point))
+      (if (not (re-search-forward "^----*\nrevision" nil t))
+	  (setq end (point-max))
+	(beginning-of-line) (forward-line -1) (setq end (point)))
+      (setq lines (count-lines start end))
+      (cond
+       ;; if the global information and this log entry fit
+       ;; into the window, display from the beginning
+       ((< (count-lines (point-min) end) (window-height))
+	(goto-char (point-min))
+	(recenter 0)
+	(goto-char start))
+       ;; if the whole entry fits into the window,
+       ;; display it centered
+       ((< (1+ lines) (window-height))
+	(goto-char start)
+	(recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
+       ;; otherwise (the entry is too large for the window),
+       ;; display from the start
+       (t
+	(goto-char start)
+	(recenter 0))))))
+
+(defun vc-rcs-diff (file &optional oldvers newvers)
+  "Get a difference report using RCS between two versions of FILE."
+  (if (not oldvers) (setq oldvers (vc-workfile-version file)))
+  ;; If we know that --brief is not supported, don't try it.
+  (let* ((diff-switches-list (if (listp diff-switches)
+				 diff-switches
+			       (list diff-switches)))
+	 (options (append (list "-q"
+				(concat "-r" oldvers)
+				(and newvers (concat "-r" newvers)))
+			  diff-switches-list)))
+    (apply 'vc-do-command t 1 "rcsdiff" file options)))
+
+
+;;;
+;;; Snapshot system
+;;;
+
+(defun vc-rcs-assign-name (file name)
+  "Assign to FILE's latest version a given NAME."
+  (vc-do-command nil 0 "rcs" (vc-name file) (concat "-n" name ":")))
+
+
+;;;
+;;; Miscellaneous
+;;;
+
+(defun vc-rcs-check-headers ()
+  "Check if the current file has any headers in it."
+  (save-excursion
+    (goto-char (point-min))
+         (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
+\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
+
+(defun vc-rcs-clear-headers ()
+  "Implementation of vc-clear-headers for RCS."
+  (let ((case-fold-search nil))
+    (goto-char (point-min))
+    (while (re-search-forward
+            (concat "\\$\\(Author\\|Date\\|Header\\|Id\\|Locker\\|Name\\|"
+                    "RCSfile\\|Revision\\|Source\\|State\\): [^$\n]+\\$")
+            nil t)
+      (replace-match "$\\1$"))))
+
+(defun vc-rcs-rename-file (old new)
+  ;; Just move the master file (using vc-rcs-master-templates).
+  (vc-rename-master (vc-name old) new vc-rcs-master-templates))
+
+
+;;;
+;;; Internal functions
+;;;
+
+(defun vc-rcs-trunk-p (rev)
+  "Return t if REV is an RCS revision on the trunk."
+  (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
+
+(defun vc-rcs-branch-part (rev)
+  "Return the branch part of an RCS revision number REV"
+  (substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
+
+(defun vc-rcs-branch-p (rev)
+  "Return t if REV is an RCS branch revision"
+  (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
+
+(defun vc-rcs-minor-part (rev)
+  "Return the minor version number of an RCS revision number REV."
+  (string-match "[0-9]+\\'" rev)
+  (substring rev (match-beginning 0) (match-end 0)))
+
+(defun vc-rcs-previous-version (rev)
+  "Guess the previous RCS version number"
+  (let ((branch (vc-rcs-branch-part rev))
+        (minor-num (string-to-number (vc-rcs-minor-part rev))))
+    (if (> minor-num 1)
+        ;; version does probably not start a branch or release
+        (concat branch "." (number-to-string (1- minor-num)))
+      (if (vc-rcs-trunk-p rev)
+          ;; we are at the beginning of the trunk --
+          ;; don't know anything to return here
+          ""
+        ;; we are at the beginning of a branch --
+        ;; return version of starting point
+        (vc-rcs-branch-part branch)))))
+
 (defun vc-rcs-workfile-is-newer (file)
   "Return non-nil if FILE is newer than its RCS master.
 This likely means that FILE has been changed with respect
@@ -174,24 +623,6 @@
 	(and (= (nth 0 file-time) (nth 0 master-time))
 	     (> (nth 1 file-time) (nth 1 master-time))))))
 
-(defun vc-rcs-workfile-version (file)
-  "RCS-specific version of `vc-workfile-version'."
-  (or (and vc-consult-headers
-           (vc-rcs-consult-headers file)
-           (vc-file-getprop file 'vc-workfile-version))
-      (progn
-        (vc-rcs-fetch-master-state file)
-        (vc-file-getprop file 'vc-workfile-version))))
-
-(defun vc-rcs-checkout-model (file)
-  "RCS-specific version of `vc-checkout-model'."
-  (vc-rcs-consult-headers file)
-  (or (vc-file-getprop file 'vc-checkout-model)
-      (progn (vc-rcs-fetch-master-state file)
-	     (vc-file-getprop file 'vc-checkout-model))))
-
-;;; internal code
-
 (defun vc-rcs-find-most-recent-rev (branch)
   "Find most recent revision on BRANCH."
   (goto-char (point-min))
@@ -373,179 +804,6 @@
 	    (vc-file-setprop file 'vc-checkout-model 'implicit)))
      status))))
 
-(defun vc-rcs-workfile-unchanged-p (file)
-  "RCS-specific implementation of vc-workfile-unchanged-p."
-  ;; Try to use rcsdiff --brief.  If rcsdiff does not understand that,
-  ;; do a double take and remember the fact for the future
-  (let* ((version (concat "-r" (vc-workfile-version file)))
-         (status (if (eq vc-rcsdiff-knows-brief 'no)
-                     (vc-do-command nil 1 "rcsdiff" file version)
-                   (vc-do-command nil 2 "rcsdiff" file "--brief" version))))
-    (if (eq status 2)
-        (if (not vc-rcsdiff-knows-brief)
-            (setq vc-rcsdiff-knows-brief 'no
-                  status (vc-do-command nil 1 "rcsdiff" file version))
-          (error "rcsdiff failed"))
-      (if (not vc-rcsdiff-knows-brief) (setq vc-rcsdiff-knows-brief 'yes)))
-    ;; The workfile is unchanged if rcsdiff found no differences.
-    (zerop status)))
-
-(defun vc-rcs-trunk-p (rev)
-  "Return t if REV is an RCS revision on the trunk."
-  (not (eq nil (string-match "\\`[0-9]+\\.[0-9]+\\'" rev))))
-
-(defun vc-rcs-branch-part (rev)
-  "Return the branch part of an RCS revision number REV"
-  (substring rev 0 (string-match "\\.[0-9]+\\'" rev)))
-
-(defun vc-rcs-latest-on-branch-p (file &optional version)
-  "Return non-nil if workfile version of FILE is the latest on its branch.
-When VERSION is given, perform check for that version."
-  (unless version (setq version (vc-workfile-version file)))
-  (with-temp-buffer
-    (string= version
-	     (if (vc-rcs-trunk-p version)
-		 (progn
-		   ;; Compare VERSION to the head version number.
-		   (vc-insert-file (vc-name file) "^[0-9]")
-		   (vc-parse-buffer "^head[ \t\n]+\\([^;]+\\);" 1))
-	       ;; If we are not on the trunk, we need to examine the
-	       ;; whole current branch.
-	       (vc-insert-file (vc-name file) "^desc")
-	       (vc-rcs-find-most-recent-rev (vc-rcs-branch-part version))))))
-
-(defun vc-rcs-branch-p (rev)
-  "Return t if REV is an RCS branch revision"
-  (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
-
-(defun vc-rcs-minor-part (rev)
-  "Return the minor version number of an RCS revision number REV."
-  (string-match "[0-9]+\\'" rev)
-  (substring rev (match-beginning 0) (match-end 0)))
-
-(defun vc-rcs-previous-version (rev)
-  "Guess the previous RCS version number"
-  (let ((branch (vc-rcs-branch-part rev))
-        (minor-num (string-to-number (vc-rcs-minor-part rev))))
-    (if (> minor-num 1)
-        ;; version does probably not start a branch or release
-        (concat branch "." (number-to-string (1- minor-num)))
-      (if (vc-rcs-trunk-p rev)
-          ;; we are at the beginning of the trunk --
-          ;; don't know anything to return here
-          ""
-        ;; we are at the beginning of a branch --
-        ;; return version of starting point
-        (vc-rcs-branch-part branch)))))
-
-(defun vc-rcs-print-log (file)
-  "Get change log associated with FILE."
-  (vc-do-command t 0 "rlog" (vc-name file)))
-
-(defun vc-rcs-show-log-entry (version)
-  (when (re-search-forward
-	 ;; also match some context, for safety
-	 (concat "----\nrevision " version
-		 "\\(\tlocked by:.*\n\\|\n\\)date: ") nil t)
-    ;; set the display window so that
-    ;; the whole log entry is displayed
-    (let (start end lines)
-      (beginning-of-line) (forward-line -1) (setq start (point))
-      (if (not (re-search-forward "^----*\nrevision" nil t))
-	  (setq end (point-max))
-	(beginning-of-line) (forward-line -1) (setq end (point)))
-      (setq lines (count-lines start end))
-      (cond
-       ;; if the global information and this log entry fit
-       ;; into the window, display from the beginning
-       ((< (count-lines (point-min) end) (window-height))
-	(goto-char (point-min))
-	(recenter 0)
-	(goto-char start))
-       ;; if the whole entry fits into the window,
-       ;; display it centered
-       ((< (1+ lines) (window-height))
-	(goto-char start)
-	(recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
-       ;; otherwise (the entry is too large for the window),
-       ;; display from the start
-       (t
-	(goto-char start)
-	(recenter 0))))))
-
-(defun vc-rcs-assign-name (file name)
-  "Assign to FILE's latest version a given NAME."
-  (vc-do-command nil 0 "rcs" (vc-name file) (concat "-n" name ":")))
-
-(defun vc-rcs-merge (file first-version &optional second-version)
-  "Merge changes into current working copy of FILE.
-The changes are between FIRST-VERSION and SECOND-VERSION."
-  (vc-do-command nil 1 "rcsmerge" (vc-name file)
-		 "-kk"			; ignore keyword conflicts
-		 (concat "-r" first-version)
-		 (if second-version (concat "-r" second-version))))
-
-(defun vc-rcs-check-headers ()
-  "Check if the current file has any headers in it."
-  (save-excursion
-    (goto-char (point-min))
-         (re-search-forward "\\$[A-Za-z\300-\326\330-\366\370-\377]+\
-\\(: [\t -#%-\176\240-\377]*\\)?\\$" nil t)))
-
-(defun vc-rcs-clear-headers ()
-  "Implementation of vc-clear-headers for RCS."
-  (let ((case-fold-search nil))
-    (goto-char (point-min))
-    (while (re-search-forward
-            (concat "\\$\\(Author\\|Date\\|Header\\|Id\\|Locker\\|Name\\|"
-                    "RCSfile\\|Revision\\|Source\\|State\\): [^$\n]+\\$")
-            nil t)
-      (replace-match "$\\1$"))))
-
-(defun vc-rcs-steal-lock (file &optional rev)
-  "Steal the lock on the current workfile for FILE and revision REV.
-Needs RCS 5.6.2 or later for -M."
-  (vc-do-command nil 0 "rcs" (vc-name file) "-M"
-		 (concat "-u" rev) (concat "-l" rev)))
-
-(defun vc-rcs-cancel-version (file writable)
-  "Undo the most recent checkin of FILE.
-WRITABLE non-nil means previous version should be locked."
-  (let* ((target (vc-workfile-version file))
-	 (previous (if (vc-trunk-p target) "" (vc-branch-part target)))
-	 (config (current-window-configuration))
-	 (done nil))
-    (vc-do-command nil 0 "rcs" (vc-name file) (concat "-o" target))
-    ;; Check out the most recent remaining version.  If it fails, because
-    ;; the whole branch got deleted, do a double-take and check out the
-    ;; version where the branch started.
-    (while (not done)
-      (condition-case err
-	  (progn
-	    (vc-do-command nil 0 "co" (vc-name file) "-f"
-			   (concat (if writable "-l" "-u") previous))
-	    (setq done t))
-	(error (set-buffer "*vc*")
-	       (goto-char (point-min))
-	       (if (search-forward "no side branches present for" nil t)
-		   (progn (setq previous (vc-branch-part previous))
-			  (vc-rcs-set-default-branch file previous)
-			  ;; vc-do-command popped up a window with
-			  ;; the error message.  Get rid of it, by
-			  ;; restoring the old window configuration.
-			  (set-window-configuration config))
-		 ;; No, it was some other error: re-signal it.
-		 (signal (car err) (cdr err))))))))
-
-(defun vc-rcs-revert (file)
-  "Revert FILE to the version it was based on."
-  (vc-do-command nil 0 "co" (vc-name file) "-f"
-		 (concat "-u" (vc-workfile-version file))))
-
-(defun vc-rcs-rename-file (old new)
-  ;; Just move the master file (using vc-rcs-master-templates).
-  (vc-rename-master (vc-name old) new vc-rcs-master-templates))
-
 (defun vc-release-greater-or-equal (r1 r2)
   "Compare release numbers, represented as strings.
 Release components are assumed cardinal numbers, not decimal fractions
@@ -581,55 +839,6 @@
 	     (not (eq installation 'unknown)))
 	(vc-release-greater-or-equal installation release))))
 
-(defun vc-rcs-checkin (file rev comment)
-  "RCS-specific version of `vc-backend-checkin'."
-  (let ((switches (if (stringp vc-checkin-switches)
-		      (list vc-checkin-switches)
-		    vc-checkin-switches)))
-    (let ((old-version (vc-workfile-version file)) new-version
-	  (default-branch (vc-file-getprop file 'vc-rcs-default-branch)))
-      ;; Force branch creation if an appropriate 
-      ;; default branch has been set.
-      (and (not rev)
-	   default-branch
-	   (string-match (concat "^" (regexp-quote old-version) "\\.")
-			 default-branch)
-	   (setq rev default-branch)
-	   (setq switches (cons "-f" switches)))
-      (apply 'vc-do-command nil 0 "ci" (vc-name file)
-	     ;; if available, use the secure check-in option
-	     (and (vc-rcs-release-p "5.6.4") "-j")
-	     (concat (if vc-keep-workfiles "-u" "-r") rev)
-	     (concat "-m" comment)
-	     switches)
-      (vc-file-setprop file 'vc-workfile-version nil)
-
-      ;; determine the new workfile version
-      (set-buffer "*vc*")
-      (goto-char (point-min))
-      (when (or (re-search-forward
-		 "new revision: \\([0-9.]+\\);" nil t)
-		(re-search-forward
-		 "reverting to previous revision \\([0-9.]+\\)" nil t))
-	(setq new-version (match-string 1))
-	(vc-file-setprop file 'vc-workfile-version new-version))
-
-      ;; if we got to a different branch, adjust the default
-      ;; branch accordingly
-      (cond
-       ((and old-version new-version
-	     (not (string= (vc-rcs-branch-part old-version)
-			   (vc-rcs-branch-part new-version))))
-	(vc-rcs-set-default-branch file 
-				   (if (vc-rcs-trunk-p new-version) nil
-				     (vc-rcs-branch-part new-version)))
-	;; If this is an old RCS release, we might have
-	;; to remove a remaining lock.
-	(if (not (vc-rcs-release-p "5.6.2"))
-	    ;; exit status of 1 is also accepted.
-	    ;; It means that the lock was removed before.
-	    (vc-do-command nil 1 "rcs" (vc-name file)
-			   (concat "-u" old-version))))))))
 
 (defun vc-rcs-system-release ()
   "Return the RCS release installed on this system, as a string.
@@ -645,104 +854,6 @@
 		       (vc-parse-buffer "^RCS version \\([0-9.]+ *.*\\)" 1)))
 		'unknown))))
 
-(defun vc-rcs-diff (file &optional oldvers newvers)
-  "Get a difference report using RCS between two versions of FILE."
-  (if (not oldvers) (setq oldvers (vc-workfile-version file)))
-  ;; If we know that --brief is not supported, don't try it.
-  (let* ((diff-switches-list (if (listp diff-switches)
-				 diff-switches
-			       (list diff-switches)))
-	 (options (append (list "-q"
-				(concat "-r" oldvers)
-				(and newvers (concat "-r" newvers)))
-			  diff-switches-list)))
-    (apply 'vc-do-command t 1 "rcsdiff" file options)))
-
-(defun vc-rcs-responsible-p (file)
-  "Return non-nil if RCS thinks it would be responsible for registering FILE."
-  ;; TODO: check for all the patterns in vc-rcs-master-templates
-  (file-directory-p (expand-file-name "RCS" (file-name-directory file))))
-
-(defun vc-rcs-register (file &optional rev comment)
-  "Register FILE into the RCS version-control system.
-REV is the optional revision number for the file.  COMMENT can be used
-to provide an initial description of FILE.
-
-`vc-register-switches' and `vc-rcs-register-switches' are passed to
-the RCS command (in that order).
-
-Automatically retrieve a read-only version of the file with keywords
-expanded if `vc-keep-workfiles' is non-nil, otherwise, delete the workfile."
-    (let ((subdir (expand-file-name "RCS" (file-name-directory file)))
-	  (switches (list
-		     (if (stringp vc-register-switches)
-			 (list vc-register-switches)
-		       vc-register-switches)
-		     (if (stringp vc-rcs-register-switches)
-		     (list vc-rcs-register-switches)
-		     vc-rcs-register-switches))))
-      
-      (and (not (file-exists-p subdir))
-	   (not (directory-files (file-name-directory file)
-				 nil ".*,v$" t))
-	   (yes-or-no-p "Create RCS subdirectory? ")
-	   (make-directory subdir))
-      (apply 'vc-do-command nil 0 "ci" file
-	     ;; if available, use the secure registering option
-	     (and (vc-rcs-release-p "5.6.4") "-i")
-	     (concat (if vc-keep-workfiles "-u" "-r") rev)
-	     (and comment (concat "-t-" comment))
-	     switches)
-      ;; parse output to find master file name and workfile version
-      (with-current-buffer "*vc*"
-        (goto-char (point-min))
-        (let ((name (if (looking-at (concat "^\\(.*\\)  <--  "
-                                            (file-name-nondirectory file)))
-                        (match-string 1))))
-          (if (not name)
-              ;; if we couldn't find the master name,
-              ;; run vc-rcs-registered to get it
-              ;; (will be stored into the vc-name property)
-              (vc-rcs-registered file)
-            (vc-file-setprop file 'vc-name
-                             (if (file-name-absolute-p name)
-                                 name
-                               (expand-file-name
-                                name
-                                (file-name-directory file))))))
-        (vc-file-setprop file 'vc-workfile-version
-                         (if (re-search-forward
-                              "^initial revision: \\([0-9.]+\\).*\n"
-                              nil t)
-                             (match-string 1))))))
-
-(defun vc-rcs-unregister (file)
-  "Unregister FILE from RCS.
-If this leaves the RCS subdirectory empty, ask the user
-whether to remove it."
-  (let* ((master (vc-name file))
-	 (dir (file-name-directory master))
-	 (backup-info (find-backup-file-name master)))
-    (if (not backup-info)
-	(delete-file master)
-      (rename-file master (car backup-info) 'ok-if-already-exists)
-      (dolist (f (cdr backup-info)) (ignore-errors (delete-file f))))
-    (and (string= (file-name-nondirectory (directory-file-name dir)) "RCS")
-	 ;; check whether RCS dir is empty, i.e. it does not
-	 ;; contain any files except "." and ".."
-	 (not (directory-files dir nil 
-			       "^\\([^.]\\|\\.[^.]\\|\\.\\.[^.]\\).*"))
-	 (yes-or-no-p (format "Directory %s is empty; remove it? " dir))
-	 (delete-directory dir))))
-
-(defun vc-rcs-receive-file (file rev)
-  "Implementation of receive-file for RCS."
-  (let ((checkout-model (vc-checkout-model file)))
-    (vc-rcs-register file rev "")
-    (when (eq checkout-model 'implicit)
-      (vc-rcs-set-non-strict-locking file))
-    (vc-rcs-set-default-branch file (concat rev ".1"))))
-
 (defun vc-rcs-set-non-strict-locking (file)
   (vc-do-command nil 0 "rcs" file "-U")
   (vc-file-setprop file 'vc-checkout-model 'implicit)
@@ -752,83 +863,6 @@
   (vc-do-command nil 0 "rcs" (vc-name file) (concat "-b" branch))
   (vc-file-setprop file 'vc-rcs-default-branch branch))
 
-(defun vc-rcs-checkout (file &optional writable rev workfile)
-  "Retrieve a copy of a saved version of FILE into a workfile."
-  (let ((filename (or workfile file))
-	(file-buffer (get-file-buffer file))
-	switches)
-    (message "Checking out %s..." filename)
-    (save-excursion
-      ;; Change buffers to get local value of vc-checkout-switches.
-      (if file-buffer (set-buffer file-buffer))
-      (setq switches (if (stringp vc-checkout-switches)
-			 (list vc-checkout-switches)
-		       vc-checkout-switches))
-      ;; Save this buffer's default-directory
-      ;; and use save-excursion to make sure it is restored
-      ;; in the same buffer it was saved in.
-      (let ((default-directory default-directory))
-	(save-excursion
-	  ;; Adjust the default-directory so that the check-out creates
-	  ;; the file in the right place.
-	  (setq default-directory (file-name-directory filename))
-	  (if workfile  ;; RCS
-	      ;; RCS can't check out into arbitrary file names directly.
-	      ;; Use `co -p' and make stdout point to the correct file.
-	      (let ((vc-modes (logior (file-modes (vc-name file))
-				      (if writable 128 0)))
-		    (failed t))
-		(unwind-protect
-		    (progn
-                      (let ((coding-system-for-read 'no-conversion)
-                            (coding-system-for-write 'no-conversion))
-                        (with-temp-file filename
-                          (apply 'vc-do-command
-                                 (current-buffer) 0 "co" (vc-name file)
-                                 "-q" ;; suppress diagnostic output
-                                 (if writable "-l")
-                                 (concat "-p" rev)
-                                 switches)))
-                      (set-file-modes filename
-				      (logior (file-modes (vc-name file))
-					      (if writable 128 0)))
-		      (setq failed nil))
-		  (and failed (file-exists-p filename)
-		       (delete-file filename))))
-	    (let (new-version)
-	      ;; if we should go to the head of the trunk,
-	      ;; clear the default branch first
-	      (and rev (string= rev "")
-		   (vc-rcs-set-default-branch file nil))
-	      ;; now do the checkout
-	      (apply 'vc-do-command
-		     nil 0 "co" (vc-name file)
-		     ;; If locking is not strict, force to overwrite
-		     ;; the writable workfile.
-		     (if (eq (vc-checkout-model file) 'implicit) "-f")
-		     (if writable "-l")
-		     (if rev (concat "-r" rev)
-		       ;; if no explicit revision was specified,
-		       ;; check out that of the working file
-		       (let ((workrev (vc-workfile-version file)))
-			 (if workrev (concat "-r" workrev)
-			   nil)))
-		     switches)
-	      ;; determine the new workfile version
-	      (with-current-buffer "*vc*"
-		(setq new-version
-		      (vc-parse-buffer "^revision \\([0-9.]+\\).*\n" 1)))
-	      (vc-file-setprop file 'vc-workfile-version new-version)
-	      ;; if necessary, adjust the default branch
-	      (and rev (not (string= rev ""))
-		   (vc-rcs-set-default-branch 
-		    file
-		    (if (vc-rcs-latest-on-branch-p file new-version)
-			(if (vc-rcs-trunk-p new-version) nil
-			  (vc-rcs-branch-part new-version))
-		      new-version))))))
-	(message "Checking out %s...done" filename)))))
-
 (provide 'vc-rcs)
 
 ;;; vc-rcs.el ends here
--- a/lisp/vc-sccs.el	Thu Nov 16 17:09:04 2000 +0000
+++ b/lisp/vc-sccs.el	Thu Nov 16 18:14:41 2000 +0000
@@ -5,7 +5,7 @@
 ;; Author:     FSF (see vc.el for full credits)
 ;; Maintainer: Andre Spiegel <spiegel@gnu.org>
 
-;; $Id: vc-sccs.el,v 1.3 2000/09/07 20:06:55 fx Exp $
+;; $Id: vc-sccs.el,v 1.4 2000/09/09 00:48:40 monnier Exp $
 
 ;; This file is part of GNU Emacs.
 
@@ -28,6 +28,10 @@
 
 ;;; Code:
 
+;;; 
+;;; Customization options
+;;;
+
 (defcustom vc-sccs-register-switches nil
   "*Extra switches for registering a file in SCCS.
 A string or list of strings passed to the checkin program by
@@ -58,8 +62,18 @@
   :version "21.1"
   :group 'vc)
 
+
+;;;
+;;; Internal variables
+;;;
+
 (defconst vc-sccs-name-assoc-file "VC-names")
 
+
+;;;
+;;; State-querying functions
+;;;
+
 ;;;###autoload
 (progn (defun vc-sccs-registered (f) (vc-default-registered 'SCCS f)))
 
@@ -108,6 +122,12 @@
     (vc-insert-file (vc-name file) "^\001e")
     (vc-parse-buffer "^\001d D \\([^ ]+\\)" 1)))
 
+(defun vc-sccs-latest-on-branch-p (file)
+  "Return t iff the current workfile version of FILE is latest on its branch."
+  ;; Always return t; we do not support previous versions in the workfile
+  ;; under SCCS.
+  t)
+
 (defun vc-sccs-checkout-model (file)
   "SCCS-specific version of `vc-checkout-model'."
   'locking)
@@ -118,174 +138,10 @@
          (list "--brief" "-q"
                (concat "-r" (vc-workfile-version file)))))
 
-;; internal code
-
-;; This function is wrapped with `progn' so that the autoload cookie
-;; copies the whole function itself into loaddefs.el rather than just placing
-;; a (autoload 'vc-sccs-search-project-dir "vc-sccs") which would not
-;; help us avoid loading vc-sccs.
-;;;###autoload
-(progn (defun vc-sccs-search-project-dir (dirname basename)
-  "Return the name of a master file in the SCCS project directory.
-Does not check whether the file exists but returns nil if it does not
-find any project directory."
-  (let ((project-dir (getenv "PROJECTDIR")) dirs dir)
-    (when project-dir
-      (if (file-name-absolute-p project-dir)
-	  (setq dirs '("SCCS" ""))
-	(setq dirs '("src/SCCS" "src" "source/SCCS" "source"))
-	(setq project-dir (expand-file-name (concat "~" project-dir))))
-      (while (and (not dir) dirs)
-	(setq dir (expand-file-name (car dirs) project-dir))
-	(unless (file-directory-p dir)
-	  (setq dir nil)
-	  (setq dirs (cdr dirs))))
-      (and dir (expand-file-name (concat "s." basename) dir))))))
-
-(defun vc-sccs-lock-file (file)
-  "Generate lock file name corresponding to FILE."
-  (let ((master (vc-name file)))
-    (and
-     master
-     (string-match "\\(.*/\\)\\(s\\.\\)\\(.*\\)" master)
-     (replace-match "p." t t master 2))))
-
-(defun vc-sccs-parse-locks ()
-  "Parse SCCS locks in current buffer.
-The result is a list of the form ((VERSION . USER) (VERSION . USER) ...)."
-  (let (master-locks)
-    (goto-char (point-min))
-    (while (re-search-forward "^\\([0-9.]+\\) [0-9.]+ \\([^ ]+\\) .*\n?"
-			      nil t)
-      (setq master-locks
-	    (cons (cons (match-string 1) (match-string 2)) master-locks)))
-    ;; FIXME: is it really necessary to reverse ?
-    (nreverse master-locks)))
 
-(defun vc-sccs-print-log (file)
-  "Get change log associated with FILE."
-  (vc-do-command t 0 "prs" (vc-name file)))
-
-(defun vc-sccs-assign-name (file name)
-  "Assign to FILE's latest version a given NAME."
-  (vc-sccs-add-triple name file (vc-workfile-version file)))
-
-;; Named-configuration support
-
-(defun vc-sccs-add-triple (name file rev)
-  (with-current-buffer
-      (find-file-noselect
-       (expand-file-name vc-sccs-name-assoc-file
-			 (file-name-directory (vc-name file))))
-    (goto-char (point-max))
-    (insert name "\t:\t" file "\t" rev "\n")
-    (basic-save-buffer)
-    (kill-buffer (current-buffer))))
-
-(defun vc-sccs-rename-file (old new)
-  ;; Move the master file (using vc-rcs-master-templates).
-  (vc-rename-master (vc-name old) new vc-sccs-master-templates)
-  ;; Update the snapshot file.
-  (with-current-buffer
-      (find-file-noselect
-       (expand-file-name vc-sccs-name-assoc-file
-			 (file-name-directory (vc-name old))))
-    (goto-char (point-min))
-    ;; (replace-regexp (concat ":" (regexp-quote old) "$") (concat ":" new))
-    (while (re-search-forward (concat ":" (regexp-quote old) "$") nil t)
-      (replace-match (concat ":" new) nil nil))
-    (basic-save-buffer)
-    (kill-buffer (current-buffer))))
-
-(defun vc-sccs-lookup-triple (file name)
-  "Return the numeric version corresponding to a named snapshot of FILE.
-If NAME is nil or a version number string it's just passed through."
-  (if (or (null name)
-	  (let ((firstchar (aref name 0)))
-	    (and (>= firstchar ?0) (<= firstchar ?9))))
-      name
-    (with-temp-buffer
-      (vc-insert-file
-       (expand-file-name vc-sccs-name-assoc-file
-			 (file-name-directory (vc-name file))))
-      (vc-parse-buffer (concat name "\t:\t" file "\t\\(.+\\)") 1))))
-
-(defun vc-sccs-merge (file first-version &optional second-version)
-  (error "Merging not implemented for SCCS"))
-
-(defun vc-sccs-check-headers ()
-  "Check if the current file has any headers in it."
-  (save-excursion
-    (goto-char (point-min))
-    (re-search-forward  "%[A-Z]%" nil t)))
-
-(defun vc-sccs-steal-lock (file &optional rev)
-  "Steal the lock on the current workfile for FILE and revision REV."
-  (vc-do-command nil 0 "unget" (vc-name file) "-n" (if rev (concat "-r" rev)))
-  (vc-do-command nil 0 "get" (vc-name file) "-g" (if rev (concat "-r" rev))))
-
-(defun vc-sccs-cancel-version (file writable)
-  "Undo the most recent checkin of FILE.
-WRITABLE non-nil means previous version should be locked."
-  (vc-do-command nil 0 "rmdel"
-		 (vc-name file)
-		 (concat "-r" (vc-workfile-version file)))
-  (vc-do-command nil 0 "get"
-		 (vc-name file)
-		 (if writable "-e")))
-
-(defun vc-sccs-revert (file)
-  "Revert FILE to the version it was based on."
-  (vc-do-command nil 0 "unget" (vc-name file))
-  (vc-do-command nil 0 "get" (vc-name file))
-  ;; Checking out explicit versions is not supported under SCCS, yet.
-  ;; We always "revert" to the latest version; therefore
-  ;; vc-workfile-version is cleared here so that it gets recomputed.
-  (vc-file-setprop file 'vc-workfile-version nil))
-
-(defun vc-sccs-checkin (file rev comment)
-  "SCCS-specific version of `vc-backend-checkin'."
-  (let ((switches (if (stringp vc-checkin-switches)
-		      (list vc-checkin-switches)
-		    vc-checkin-switches)))
-    (apply 'vc-do-command nil 0 "delta" (vc-name file)
-	   (if rev (concat "-r" rev))
-	   (concat "-y" comment)
-	   switches)
-    (if vc-keep-workfiles
-	(vc-do-command nil 0 "get" (vc-name file)))))
-
-(defun vc-sccs-latest-on-branch-p (file)
-  "Return t iff the current workfile version of FILE is latest on its branch."
-  ;; Always return t; we do not support previous versions in the workfile
-  ;; under SCCS.
-  t)
-
-(defun vc-sccs-logentry-check ()
-  "Check that the log entry in the current buffer is acceptable for SCCS."
-  (when (>= (buffer-size) 512)
-    (goto-char 512)
-    (error "Log must be less than 512 characters; point is now at pos 512")))
-
-(defun vc-sccs-diff (file &optional oldvers newvers)
-  "Get a difference report using SCCS between two versions of FILE."
-  (setq oldvers (vc-sccs-lookup-triple file oldvers))
-  (setq newvers (vc-sccs-lookup-triple file newvers))
-  (let* ((diff-switches-list (if (listp diff-switches)
-                                diff-switches
-                              (list diff-switches)))
-	 (options (append (list "-q"
-				(and oldvers (concat "-r" oldvers))
-				(and newvers (concat "-r" newvers)))
-			  diff-switches-list)))
-    (apply 'vc-do-command t 1 "vcdiff" (vc-name file) options)))
-
-(defun vc-sccs-responsible-p (file)
-  "Return non-nil if SCCS thinks it would be responsible for registering FILE."
-  ;; TODO: check for all the patterns in vc-sccs-master-templates
-  (or (file-directory-p (expand-file-name "SCCS" (file-name-directory file)))
-      (stringp (vc-sccs-search-project-dir (or (file-name-directory file) "")
-					   (file-name-nondirectory file)))))
+;;;
+;;; State-changing functions
+;;;
 
 (defun vc-sccs-register (file &optional rev comment)
   "Register FILE into the SCCS version-control system.
@@ -321,6 +177,25 @@
       (if vc-keep-workfiles
 	  (vc-do-command nil 0 "get" (vc-name file)))))
 
+(defun vc-sccs-responsible-p (file)
+  "Return non-nil if SCCS thinks it would be responsible for registering FILE."
+  ;; TODO: check for all the patterns in vc-sccs-master-templates
+  (or (file-directory-p (expand-file-name "SCCS" (file-name-directory file)))
+      (stringp (vc-sccs-search-project-dir (or (file-name-directory file) "")
+					   (file-name-nondirectory file)))))
+
+(defun vc-sccs-checkin (file rev comment)
+  "SCCS-specific version of `vc-backend-checkin'."
+  (let ((switches (if (stringp vc-checkin-switches)
+		      (list vc-checkin-switches)
+		    vc-checkin-switches)))
+    (apply 'vc-do-command nil 0 "delta" (vc-name file)
+	   (if rev (concat "-r" rev))
+	   (concat "-y" comment)
+	   switches)
+    (if vc-keep-workfiles
+	(vc-do-command nil 0 "get" (vc-name file)))))
+
 (defun vc-sccs-checkout (file &optional writable rev workfile)
   "Retrieve a copy of a saved version of SCCS controlled FILE into a WORKFILE.
 WRITABLE non-nil means that the file should be writable.  REV is the
@@ -379,9 +254,166 @@
 		   switches)))))
     (message "Checking out %s...done" filename)))
 
-(defun vc-sccs-update-changelog (files)
-  (error "Sorry, generating ChangeLog entries is not implemented for SCCS"))
+(defun vc-sccs-revert (file)
+  "Revert FILE to the version it was based on."
+  (vc-do-command nil 0 "unget" (vc-name file))
+  (vc-do-command nil 0 "get" (vc-name file))
+  ;; Checking out explicit versions is not supported under SCCS, yet.
+  ;; We always "revert" to the latest version; therefore
+  ;; vc-workfile-version is cleared here so that it gets recomputed.
+  (vc-file-setprop file 'vc-workfile-version nil))
+
+(defun vc-sccs-cancel-version (file writable)
+  "Undo the most recent checkin of FILE.
+WRITABLE non-nil means previous version should be locked."
+  (vc-do-command nil 0 "rmdel"
+		 (vc-name file)
+		 (concat "-r" (vc-workfile-version file)))
+  (vc-do-command nil 0 "get"
+		 (vc-name file)
+		 (if writable "-e")))
+
+(defun vc-sccs-steal-lock (file &optional rev)
+  "Steal the lock on the current workfile for FILE and revision REV."
+  (vc-do-command nil 0 "unget" (vc-name file) "-n" (if rev (concat "-r" rev)))
+  (vc-do-command nil 0 "get" (vc-name file) "-g" (if rev (concat "-r" rev))))
+
+
+;;;
+;;; History functions
+;;;
+
+(defun vc-sccs-print-log (file)
+  "Get change log associated with FILE."
+  (vc-do-command t 0 "prs" (vc-name file)))
+
+(defun vc-sccs-logentry-check ()
+  "Check that the log entry in the current buffer is acceptable for SCCS."
+  (when (>= (buffer-size) 512)
+    (goto-char 512)
+    (error "Log must be less than 512 characters; point is now at pos 512")))
+
+(defun vc-sccs-diff (file &optional oldvers newvers)
+  "Get a difference report using SCCS between two versions of FILE."
+  (setq oldvers (vc-sccs-lookup-triple file oldvers))
+  (setq newvers (vc-sccs-lookup-triple file newvers))
+  (let* ((diff-switches-list (if (listp diff-switches)
+                                diff-switches
+                              (list diff-switches)))
+	 (options (append (list "-q"
+				(and oldvers (concat "-r" oldvers))
+				(and newvers (concat "-r" newvers)))
+			  diff-switches-list)))
+    (apply 'vc-do-command t 1 "vcdiff" (vc-name file) options)))
+
+
+;;;
+;;; Snapshot system
+;;;
+
+(defun vc-sccs-assign-name (file name)
+  "Assign to FILE's latest version a given NAME."
+  (vc-sccs-add-triple name file (vc-workfile-version file)))
+
+
+;;;
+;;; Miscellaneous
+;;;
+
+(defun vc-sccs-check-headers ()
+  "Check if the current file has any headers in it."
+  (save-excursion
+    (goto-char (point-min))
+    (re-search-forward  "%[A-Z]%" nil t)))
+
+(defun vc-sccs-rename-file (old new)
+  ;; Move the master file (using vc-rcs-master-templates).
+  (vc-rename-master (vc-name old) new vc-sccs-master-templates)
+  ;; Update the snapshot file.
+  (with-current-buffer
+      (find-file-noselect
+       (expand-file-name vc-sccs-name-assoc-file
+			 (file-name-directory (vc-name old))))
+    (goto-char (point-min))
+    ;; (replace-regexp (concat ":" (regexp-quote old) "$") (concat ":" new))
+    (while (re-search-forward (concat ":" (regexp-quote old) "$") nil t)
+      (replace-match (concat ":" new) nil nil))
+    (basic-save-buffer)
+    (kill-buffer (current-buffer))))
+
+
+;;;
+;;; Internal functions
+;;;
+
+;; This function is wrapped with `progn' so that the autoload cookie
+;; copies the whole function itself into loaddefs.el rather than just placing
+;; a (autoload 'vc-sccs-search-project-dir "vc-sccs") which would not
+;; help us avoid loading vc-sccs.
+;;;###autoload
+(progn (defun vc-sccs-search-project-dir (dirname basename)
+  "Return the name of a master file in the SCCS project directory.
+Does not check whether the file exists but returns nil if it does not
+find any project directory."
+  (let ((project-dir (getenv "PROJECTDIR")) dirs dir)
+    (when project-dir
+      (if (file-name-absolute-p project-dir)
+	  (setq dirs '("SCCS" ""))
+	(setq dirs '("src/SCCS" "src" "source/SCCS" "source"))
+	(setq project-dir (expand-file-name (concat "~" project-dir))))
+      (while (and (not dir) dirs)
+	(setq dir (expand-file-name (car dirs) project-dir))
+	(unless (file-directory-p dir)
+	  (setq dir nil)
+	  (setq dirs (cdr dirs))))
+      (and dir (expand-file-name (concat "s." basename) dir))))))
+
+(defun vc-sccs-lock-file (file)
+  "Generate lock file name corresponding to FILE."
+  (let ((master (vc-name file)))
+    (and
+     master
+     (string-match "\\(.*/\\)\\(s\\.\\)\\(.*\\)" master)
+     (replace-match "p." t t master 2))))
+
+(defun vc-sccs-parse-locks ()
+  "Parse SCCS locks in current buffer.
+The result is a list of the form ((VERSION . USER) (VERSION . USER) ...)."
+  (let (master-locks)
+    (goto-char (point-min))
+    (while (re-search-forward "^\\([0-9.]+\\) [0-9.]+ \\([^ ]+\\) .*\n?"
+			      nil t)
+      (setq master-locks
+	    (cons (cons (match-string 1) (match-string 2)) master-locks)))
+    ;; FIXME: is it really necessary to reverse ?
+    (nreverse master-locks)))
+
+(defun vc-sccs-add-triple (name file rev)
+  (with-current-buffer
+      (find-file-noselect
+       (expand-file-name vc-sccs-name-assoc-file
+			 (file-name-directory (vc-name file))))
+    (goto-char (point-max))
+    (insert name "\t:\t" file "\t" rev "\n")
+    (basic-save-buffer)
+    (kill-buffer (current-buffer))))
+
+(defun vc-sccs-lookup-triple (file name)
+  "Return the numeric version corresponding to a named snapshot of FILE.
+If NAME is nil or a version number string it's just passed through."
+  (if (or (null name)
+	  (let ((firstchar (aref name 0)))
+	    (and (>= firstchar ?0) (<= firstchar ?9))))
+      name
+    (with-temp-buffer
+      (vc-insert-file
+       (expand-file-name vc-sccs-name-assoc-file
+			 (file-name-directory (vc-name file))))
+      (vc-parse-buffer (concat name "\t:\t" file "\t\\(.+\\)") 1))))
 
 (provide 'vc-sccs)
 
 ;;; vc-sccs.el ends here
+
+
+