changeset 1395:f6f838c4a26e

(buffer-file-number): New variable. (find-file-noselect): Record the file's filenum and devnum. Notify if any buffer has the same values. (basic-save-buffer): Save new filenum and devnum. For file-precious-flag, pass real name as VISIT arg of write-region. (set-visited-file-name): Likewise. Clear buffer-file-{number,truename} if now visiting no file.
author Richard M. Stallman <rms@gnu.org>
date Mon, 12 Oct 1992 04:45:53 +0000
parents 3f3934ca2df6
children 17365cdb1c10
files lisp/files.el
diffstat 1 files changed, 224 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/files.el	Sun Oct 11 20:41:13 1992 +0000
+++ b/lisp/files.el	Mon Oct 12 04:45:53 1992 +0000
@@ -92,6 +92,30 @@
 Automatically local in all buffers.")
 (make-variable-buffer-local 'buffer-offer-save)
 
+(defconst find-file-existing-other-name nil
+  "*Non-nil means find a file under alternative names, in existing buffers.
+This means if any existing buffer is visiting the file you want
+under another name, you get the existing buffer instead of a new buffer.")
+
+(defconst find-file-visit-truename nil
+  "*Non-nil means visit a file under its truename.
+The truename of a file is found by chasing all links
+both at the file level and at the levels of the containing directories.")
+
+(defvar buffer-file-truename nil
+  "The truename of the file visited in the current buffer.
+This variable is automatically local in all buffers, when non-nil.")
+(make-variable-buffer-local 'buffer-file-truename)
+(put 'buffer-file-truename 'permanent-local t)
+
+(defvar buffer-file-number nil
+  "The device number and file number of the file visited in the current buffer.
+The value is a list of the form (FILENUM DEVNUM).
+This pair of numbers uniquely identifies the file.
+If the buffer is visiting a new file, the value is nil.")
+(make-variable-buffer-local 'buffer-file-number)
+(put 'buffer-file-number 'permanent-local t)
+
 (defconst file-precious-flag nil
   "*Non-nil means protect against I/O errors while saving files.
 Some modes set this non-nil in particular buffers.")
@@ -238,6 +262,27 @@
     (if handler
 	(funcall handler 'file-local-copy file)
       nil)))
+
+(defun file-truename (filename)
+  "Return the truename of FILENAME, which should be absolute.
+The truename of a file name is found by chasing symbolic links
+both at the level of the file and at the level of the directories
+containing it, until no links are left at any level."
+  (let ((dir (file-name-directory filename))
+	target)
+    ;; Get the truename of the directory.
+    (or (string= dir "/")
+	(setq dir (file-name-as-directory (file-truename (directory-file-name dir)))))
+    ;; Put it back on the file name.
+    (setq filename (concat (file-name-nondirectory filename) dir))
+    ;; Is the file name the name of a link?
+    (setq target (file-symlink-p filename))
+    (if target
+	;; Yes => chase that link, then start all over
+	;; since the link may point to a directory name that uses links.
+	(file-truename (expand-file-name target dir))
+      ;; No, we are done!
+      filename)))
 
 (defun switch-to-buffer-other-window (buffer)
   "Select buffer BUFFER in another window."
@@ -379,8 +424,46 @@
       (if find-file-run-dired
 	  (dired-noselect filename)
 	(error "%s is a directory." filename))
-    (let ((buf (get-file-buffer filename))
-	  error)
+    (let* ((buf (get-file-buffer filename))
+	   (truename (abbreviate-file-name (file-truename filename)))
+	   (number (nthcdr 10 (file-attributes truename)))
+	   ;; Find any buffer for a file which has same truename.
+	   (same-truename
+	    (or buf ; Shortcut
+		(let (found
+		      (list (buffer-list)))
+		  (while (and (not found) list)
+		    (save-excursion
+		      (set-buffer (car list))
+		      (if (string= buffer-file-truename truename)
+			(setq found (car list))))
+		    (setq list (cdr list)))
+		  found)))
+	   (same-number
+	    (or buf ; Shortcut
+		(and number
+		     (let (found
+			   (list (buffer-list)))
+		       (while (and (not found) list)
+			 (save-excursion
+			   (set-buffer (car list))
+			   (if (equal buffer-file-number number)
+			     (setq found (car list))))
+			 (setq list (cdr list)))
+		       found))))
+	   error)
+      ;; Let user know if there is a buffer with the same truename.
+      (if (and (not buf) same-truename (not nowarn))
+	  (message "%s and %s are the same file (%s)"
+		   filename (buffer-file-name same-truename)
+		   truename)
+	(if (and (not buf) same-number (not nowarn))
+	  (message "%s and %s are the same file"
+		   filename (buffer-file-name same-number))))
+
+      ;; Optionally also find that buffer.
+      (if (or find-file-existing-other-name find-file-visit-truename)
+	  (setq buf (or same-truename same-number)))
       (if buf
 	  (or nowarn
 	      (verify-visited-file-modtime buf)
@@ -396,12 +479,13 @@
 		       (set-buffer buf)
 		       (revert-buffer t t)))))
 	(save-excursion
-	  (let* ((link-name (car (file-attributes filename)))
-		 (linked-buf (and (stringp link-name)
-				  (get-file-buffer link-name))))
-	    (if (bufferp linked-buf)
-		(message "Symbolic link to file in buffer %s"
-			 (buffer-name linked-buf))))
+;;; The truename stuff makes this obsolete.
+;;;	  (let* ((link-name (car (file-attributes filename)))
+;;;		 (linked-buf (and (stringp link-name)
+;;;				  (get-file-buffer link-name))))
+;;;	    (if (bufferp linked-buf)
+;;;		(message "Symbolic link to file in buffer %s"
+;;;			 (buffer-name linked-buf))))
 	  (setq buf (create-file-buffer filename))
 	  (set-buffer buf)
 	  (erase-buffer)
@@ -414,6 +498,10 @@
 	       (while (and hooks
 			   (not (funcall (car hooks))))
 		 (setq hooks (cdr hooks))))))
+	  ;; Find the file's truename, and maybe use that as visited name.
+	  (setq buffer-file-truename (abbreviate-file-name truename))
+	  (setq buffer-file-number number) 
+	  (if find-file-visit-truename (setq filename buffer-file-truename))
 	  ;; Set buffer's default directory to that of the file.
 	  (setq default-directory (file-name-directory filename))
 	  ;; Turn off backup files for certain file names.  Since
@@ -562,7 +650,7 @@
 not check for the \"mode:\" local variable in the Local Variables
 section of the file; for that, use `hack-local-variables'.
 
-If enable-local-variables is nil, this function will not check for a
+If `enable-local-variables' is nil, this function does not check for a
 -*- mode tag."
   ;; Look for -*-MODENAME-*- or -*- ... mode: MODENAME; ... -*-
   (let (beg end mode)
@@ -609,8 +697,72 @@
 	      (setq alist (cdr alist)))))))
     (if mode (funcall mode))))
 
+(defun hack-local-variables-prop-line ()
+  ;; Set local variables specified in the -*- line.
+  ;; Returns t if mode was set.
+  (save-excursion
+    (goto-char (point-min))
+    (skip-chars-forward " \t\n\r")
+    (let ((result '())
+	  (end (save-excursion (end-of-line) (point)))
+	  mode-p)
+      ;; Parse the -*- line into the `result' alist.
+      (cond ((not (search-forward "-*-" end t))
+	     ;; doesn't have one.
+	     nil)
+	    ((looking-at "[ \t]*\\([^ \t\n\r:;]+\\)\\([ \t]*-\\*-\\)")
+	     ;; Simple form: "-*- MODENAME -*-".
+	     (setq result
+	       (list (cons 'mode
+			   (intern (buffer-substring
+				    (match-beginning 1)
+				    (match-end 1)))))))
+	    (t
+	     ;; Hairy form: '-*-' [ <variable> ':' <value> ';' ]* '-*-'
+	     ;; (last ";" is optional).
+	     (save-excursion
+	       (if (search-forward "-*-" end t)
+		   (setq end (- (point) 3))
+		 (error "-*- not terminated before end of line")))
+	     (while (< (point) end)
+	       (or (looking-at "[ \t]*\\([^ \t\n:]+\\)[ \t]*:[ \t]*")
+		   (error "malformed -*- line"))
+	       (goto-char (match-end 0))
+	       (let ((key (intern (downcase (buffer-substring
+					     (match-beginning 1)
+					     (match-end 1)))))
+		     (val (save-restriction
+			    (narrow-to-region (point) end)
+			    (read (current-buffer)))))
+		 (setq result (cons (cons key val) result))
+		 (skip-chars-forward " \t;")))
+	     (setq result (nreverse result))))
+
+      ;; Mode is magic.
+      (let (mode)
+	(while (setq mode (assq 'mode result))
+	  (setq mode-p t result (delq mode result))
+	  (funcall (intern (concat (downcase (symbol-name (cdr mode)))
+				   "-mode")))))
+      
+      (if (and result
+	       (or (eq enable-local-variables t)
+		   (and enable-local-variables
+			(save-window-excursion
+			  (switch-to-buffer (current-buffer))
+			  (y-or-n-p (format "Set local variables as specified in -*- line of %s? "
+					    (file-name-nondirectory buffer-file-name)))))))
+	  (while result
+	    (let ((key (car (car result)))
+		  (val (cdr (car result))))
+	      ;; 'mode has already been removed from this list.
+	      (hack-one-local-variable key val))
+	    (setq result (cdr result))))
+      mode-p)))
+
 (defun hack-local-variables ()
   "Parse and put into effect this buffer's local variables spec."
+  (hack-local-variables-prop-line)
   ;; Look for "Local variables:" line in last page.
   (save-excursion
     (goto-char (point-max))
@@ -674,27 +826,39 @@
 		(or (if suffix (looking-at suffix) (eolp))
 		    (error "Local variables entry is terminated incorrectly"))
 		;; Set the variable.  "Variables" mode and eval are funny.
-		(cond ((eq var 'mode)
-		       (funcall (intern (concat (downcase (symbol-name val))
-						"-mode"))))
-		      ((eq var 'enable-local-eval)
-		       nil)
-		      ((eq var 'eval)
-		       (if (and (not (string= (user-login-name) "root"))
-				(or (eq enable-local-eval t)
-				    (and enable-local-eval
-					 (save-window-excursion
-					   (switch-to-buffer (current-buffer))
-					   (save-excursion
-					     (beginning-of-line)
-					     (set-window-start (selected-window) (point)))
-					   (setq enable-local-eval
-						 (y-or-n-p (format "Process `eval' local variable in file %s? "
-								   (file-name-nondirectory buffer-file-name))))))))
-			   (save-excursion (eval val))
-			 (message "Ignoring `eval:' in file's local variables")))
-		      (t (make-local-variable var)
-			 (set var val))))))))))
+		(hack-one-local-variable var val))))))))
+
+(defconst ignored-local-variables
+  '(enable-local-eval)
+  "Variables to be ignored in a file's local variable spec.")
+
+;; "Set" one variable in a local variables spec.
+;; A few variable names are treated specially.
+(defun hack-one-local-variable (var val)
+  (cond ((eq var 'mode)
+	 (funcall (intern (concat (downcase (symbol-name val))
+				  "-mode"))))
+	((memq var ignored-local-variables)
+	 nil)
+	;; "Setting" eval means either eval it or do nothing.
+	((eq var 'eval)
+	 (if (and (not (string= (user-login-name) "root"))
+		  (or (eq enable-local-eval t)
+		      (and enable-local-eval
+			   (save-window-excursion
+			     (switch-to-buffer (current-buffer))
+			     (save-excursion
+			       (beginning-of-line)
+			       (set-window-start (selected-window) (point)))
+			     (setq enable-local-eval
+				   (y-or-n-p (format "Process `eval' local variable in file %s? "
+						     (file-name-nondirectory buffer-file-name))))))))
+	     (save-excursion (eval val))
+	   (message "Ignoring `eval:' in file's local variables")))
+	;; Ordinary variable, really set it.
+	(t (make-local-variable var)
+	   (set var val))))
+
 
 (defun set-visited-file-name (filename)
   "Change name of file visited in current buffer to FILENAME.
@@ -724,6 +888,14 @@
 	(rename-buffer new-name t)))
   (setq buffer-backed-up nil)
   (clear-visited-file-modtime)
+  (if filename
+      (progn
+	(setq buffer-file-truename
+	      (abbreviate-file-name (file-truename buffer-file-name)))
+	(if find-file-visit-truename
+	    (setq buffer-file-name buffer-file-truename))
+	(setq buffer-file-number (nth 10 (file-attributes buffer-file-name))))
+    (setq buffer-file-truename nil buffer-file-number nil))
   ;; write-file-hooks is normally used for things like ftp-find-file
   ;; that visit things that are not local files as if they were files.
   ;; Changing to visit an ordinary local file instead should flush the hook.
@@ -1067,40 +1239,28 @@
 		   (or buffer-backed-up
 		       (setq setmodes (backup-buffer)))
 		   (if file-precious-flag
-		       ;; If file is precious, rename it away before
-		       ;; overwriting it.
-		       (let ((rename t)
-			     realname tempname temp)
-			 ;; Chase symlinks; rename the ultimate actual file.
-			 (setq realname buffer-file-name)
-			 (while (setq temp (file-symlink-p realname))
-			   (setq realname temp))
-			 (setq tempname (concat realname "#"))
-			 (condition-case ()
-			     (progn (rename-file realname tempname t)
-				    (setq setmodes (file-modes tempname)))
-			   (file-error (setq rename nil tempname nil)))
-			 (if (file-directory-p realname)
-			     (error "%s is a directory" realname))
+		       ;; If file is precious, write temp name, then rename it.
+		       (let ((dir (file-name-directory buffer-file-name))
+			     (realname buffer-file-name)
+			     tempname temp nogood i succeed)
+			 (setq i 0)
+			 (setq nogood t)
+			 ;; Find the temporary name to write under.
+			 (while nogood
+			   (setq tempname (format "%s#tmp#%d" dir i))
+			   (setq nogood (file-exists-p tempname))
+			   (setq i (1+ i)))
 			 (unwind-protect
 			     (progn (clear-visited-file-modtime)
 				    (write-region (point-min) (point-max)
-						  realname nil t)
-				    (setq rename nil))
-			   ;; If rename is still t, writing failed.
-			   ;; So rename the old file back to original name,
-			   (if rename
-			       (progn
-				 (rename-file tempname realname t)
-				 (clear-visited-file-modtime))
-			     ;; Otherwise we don't need the original file,
-			     ;; so flush it, if we still have it.
-			     ;; If rename failed due to name length restriction
-			     ;; then TEMPNAME is now nil.
-			     (if tempname
-				 (condition-case ()
-				     (delete-file tempname)
-				   (error nil))))))
+						  tempname nil realname)
+				    (setq succeed t))
+			   ;; If writing the temp file fails,
+			   ;; delete the temp file.
+			   (or succeed (delete-file tempname)))
+			 ;; We succeeded in writing the temp file,
+			 ;; so rename it.
+			 (rename-file tempname buffer-file-name t))
 		     ;; If file not writable, see if we can make it writable
 		     ;; temporarily while we write it.
 		     ;; But no need to do so if we have just backed it up
@@ -1111,9 +1271,10 @@
 			    (set-file-modes buffer-file-name 511)))
 		     (write-region (point-min) (point-max)
 				   buffer-file-name nil t)))))
+	  (setq buffer-file-number (nth 10 (file-attributes buffer-file-name)))
 	  (if setmodes
 	      (condition-case ()
-		   (set-file-modes buffer-file-name setmodes)
+		  (set-file-modes buffer-file-name setmodes)
 		(error nil))))
 	;; If the auto-save file was recent before this command,
 	;; delete it now.
@@ -1355,7 +1516,7 @@
 
 (defun auto-save-mode (arg)
   "Toggle auto-saving of contents of current buffer.
-With ARG, turn auto-saving on if positive, else off."
+With prefix argument ARG, turn auto-saving on if positive, else off."
   (interactive "P")
   (setq buffer-auto-save-file-name
         (and (if (null arg)