(toplevel): Provide `descr-text'.
[bpt/emacs.git] / lisp / files.el
index 849c642..34fdefd 100644 (file)
@@ -203,7 +203,7 @@ If non-nil, this directory is used instead of `temporary-file-directory'
 by programs that create small temporary files.  This is for systems that
 have fast storage with limited space, such as a RAM disk."
   :group 'files
-  :type 'directory)
+  :type '(choice (const nil) directory))
 
 ;; The system null device. (Should reference NULL_DEVICE from C.)
 (defvar null-device "/dev/null" "The system null device.")
@@ -296,21 +296,31 @@ Normally auto-save files are written under other names."
   `(("\\`/[^/]*:\\(.+/\\)*\\(.*\\)"
      ;; Don't put "\\2" inside expand-file-name, since it will be
      ;; transformed to "/2" on DOS/Windows.
-     ,(concat temporary-file-directory "\\2")))
+     ,(concat temporary-file-directory "\\2") t))
   "*Transforms to apply to buffer file name before making auto-save file name.
-Each transform is a list (REGEXP REPLACEMENT):
+Each transform is a list (REGEXP REPLACEMENT UNIQUIFY):
 REGEXP is a regular expression to match against the file name.
 If it matches, `replace-match' is used to replace the
 matching part with REPLACEMENT.
+If the optional element UNIQUIFY is non-nil, the auto-save file name is
+constructed by taking the directory part of the replaced file-name,
+concatenated with the buffer file name with all directory separators
+changed to `!' to prevent clashes.  This will not work
+correctly if your filesystem truncates the resulting name.
+
 All the transforms in the list are tried, in the order they are listed.
 When one transform applies, its result is final;
 no further transforms are tried.
 
 The default value is set up to put the auto-save file into the
 temporary directory (see the variable `temporary-file-directory') for
-editing a remote file."
+editing a remote file.
+
+On MS-DOS filesystems without long names this variable is always
+ignored."
   :group 'auto-save
-  :type '(repeat (list (string :tag "Regexp") (string :tag "Replacement")))
+  :type '(repeat (list (string :tag "Regexp") (string :tag "Replacement")
+                                          (boolean :tag "Uniquify")))
   :version "21.1")
 
 (defcustom save-abbrevs t
@@ -459,6 +469,25 @@ However, on some systems, the function is redefined with a definition
 that really does change some file names to canonicalize certain
 patterns and to guarantee valid names."
   filename)
+
+(defun read-directory-name (prompt &optional dir default-dirname mustmatch initial)
+  "Read directory name, prompting with PROMPT and completing in directory DIR.
+Value is not expanded---you must call `expand-file-name' yourself.
+Default name to DEFAULT-DIRNAME if user enters a null string.
+ (If DEFAULT-DIRNAME is omitted, the current buffer's directory is used,
+  except that if INITIAL is specified, that combined with DIR is used.)
+Fourth arg MUSTMATCH non-nil means require existing directory's name.
+ Non-nil and non-t means also require confirmation after completion.
+Fifth arg INITIAL specifies text to start with.
+DIR defaults to current buffer's directory default."
+  (unless dir
+    (setq dir default-directory))
+  (unless default-dirname
+    (setq default-dirname
+         (if initial (concat dir initial) default-directory)))
+  (read-file-name prompt dir default-dirname mustmatch initial
+                 'file-directory-p))
+
 \f
 (defun pwd ()
   "Show the current default directory."
@@ -507,7 +536,7 @@ Not actually set up until the first time you use it.")
 If your environment includes a `CDPATH' variable, try each one of that
 colon-separated list of directories when resolving a relative directory name."
   (interactive
-   (list (read-file-name "Change default directory: "
+   (list (read-directory-name "Change default directory: "
                         default-directory default-directory
                         (and (member cd-path '(nil ("./")))
                              (null (getenv "CDPATH"))))))
@@ -536,13 +565,35 @@ colon-separated list of directories when resolving a relative directory name."
                       (read-file-name "Load file: "))))
   (load (expand-file-name file) nil nil t))
 
-(defun load-completion (string predicate action)
+(defun locate-file (filename path &optional suffixes predicate)
+  "Search for FILENAME through PATH.
+If SUFFIXES is non-nil, it should be a list of suffixes to append to
+file name when searching.  If SUFFIXES is nil, it is equivalent to '(\"\").
+If non-nil, PREDICATE is used instead of `file-readable-p'.
+PREDICATE can also be an integer to pass to the `access' system call,
+in which case file-name handlers are ignored.  This usage is deprecated.
+
+For compatibility, PREDICATE can also be one of the symbols
+`executable', `readable', `writable', or `exists', or a list of
+one or more of those symbols."
+  (if (and predicate (symbolp predicate) (not (functionp predicate)))
+      (setq predicate (list predicate)))
+  (when (and (consp predicate) (not (functionp predicate)))
+    (setq predicate
+         (logior (if (memq 'executable predicate) 1 0)
+                 (if (memq 'writable predicate) 2 0)
+                 (if (memq 'readable predicate) 4 0))))
+  (locate-file-internal filename path suffixes predicate))
+
+(defun locate-file-completion (string path-and-suffixes action)
+  "Do completion for file names passed to `locate-file'.
+PATH-AND-SUFFIXES is a pair of lists (DIRECTORIES . SUFFIXES)."
   (if (file-name-absolute-p string)
-      (read-file-name-internal string predicate action)
+      (read-file-name-internal string nil action)
     (let ((names nil)
-         (suffix (concat (regexp-opt load-suffixes t) "\\'"))
+         (suffix (concat (regexp-opt (cdr path-and-suffixes) t) "\\'"))
          (string-dir (file-name-directory string)))
-      (dolist (dir load-path)
+      (dolist (dir (car path-and-suffixes))
        (if string-dir (setq dir (expand-file-name string-dir dir)))
        (when (file-directory-p dir)
          (dolist (file (file-name-all-completions
@@ -552,13 +603,16 @@ colon-separated list of directories when resolving a relative directory name."
              (setq file (substring file 0 (match-beginning 0)))
              (push (if string-dir (concat string-dir file) file) names)))))
       (if action
-         (all-completions string (mapcar 'list names) predicate)
-       (try-completion string (mapcar 'list names) predicate)))))
+         (all-completions string (mapcar 'list names))
+       (try-completion string (mapcar 'list names))))))
 
 (defun load-library (library)
   "Load the library named LIBRARY.
 This is an interface to the function `load'."
-  (interactive (list (completing-read "Load library: " 'load-completion)))
+  (interactive
+   (list (completing-read "Load library: "
+                         'locate-file-completion
+                         (cons load-path load-suffixes))))
   (load library))
 
 (defun file-local-copy (file)
@@ -734,14 +788,38 @@ documentation for additional customization information."
     (pop-to-buffer buffer t norecord)
     (raise-frame (window-frame (selected-window)))))
 
+(defvar find-file-default nil
+  "Used within `find-file-read-args'.")
+
+(defun find-file-read-args (prompt)
+  (list (let ((find-file-default
+              (and buffer-file-name
+                   (abbreviate-file-name buffer-file-name)))
+             (munge-default-fun
+              (lambda ()
+                (setq minibuffer-default find-file-default)
+                ;; Clear out this hook so it does not interfere
+                ;; with any recursive minibuffer usage.
+                (pop minibuffer-setup-hook)))
+             (minibuffer-setup-hook
+              minibuffer-setup-hook))
+         (add-hook 'minibuffer-setup-hook munge-default-fun)
+         (read-file-name prompt nil default-directory))
+       current-prefix-arg))
+
 (defun find-file (filename &optional wildcards)
   "Edit file FILENAME.
 Switch to a buffer visiting file FILENAME,
 creating one if none already exists.
+Interactively, the default if you just type RET is the current directory,
+but the visited file name is available through the minibuffer history:
+type M-n to pull it into the minibuffer.
+
 Interactively, or if WILDCARDS is non-nil in a call from Lisp,
 expand wildcards (if any) and visit multiple files.  Wildcard expansion
 can be suppressed by setting `find-file-wildcards'."
-  (interactive "FFind file: \np")
+  (interactive
+   (find-file-read-args "Find file: "))
   (let ((value (find-file-noselect filename nil nil wildcards)))
     (if (listp value)
        (mapcar 'switch-to-buffer (nreverse value))
@@ -751,9 +829,14 @@ can be suppressed by setting `find-file-wildcards'."
   "Edit file FILENAME, in another window.
 May create a new window, or reuse an existing one.
 See the function `display-buffer'.
+
+Interactively, the default if you just type RET is the current directory,
+but the visited file name is available through the minibuffer history:
+type M-n to pull it into the minibuffer.
+
 Interactively, or if WILDCARDS is non-nil in a call from Lisp,
 expand wildcards (if any) and visit multiple files."
-  (interactive "FFind file in other window: \np")
+  (interactive (find-file-read-args "FFind file in other window: "))
   (let ((value (find-file-noselect filename nil nil wildcards)))
     (if (listp value)
        (progn
@@ -766,9 +849,14 @@ expand wildcards (if any) and visit multiple files."
   "Edit file FILENAME, in another frame.
 May create a new frame, or reuse an existing one.
 See the function `display-buffer'.
+
+Interactively, the default if you just type RET is the current directory,
+but the visited file name is available through the minibuffer history:
+type M-n to pull it into the minibuffer.
+
 Interactively, or if WILDCARDS is non-nil in a call from Lisp,
 expand wildcards (if any) and visit multiple files."
-  (interactive "FFind file in other frame: \np")
+  (interactive (find-file-read-args "FFind file in other frame: "))
   (let ((value (find-file-noselect filename nil nil wildcards)))
     (if (listp value)
        (progn
@@ -779,9 +867,9 @@ expand wildcards (if any) and visit multiple files."
 
 (defun find-file-read-only (filename &optional wildcards)
   "Edit file FILENAME but don't allow changes.
-Like `find-file' but marks buffer as read-only.
+Like \\[find-file] but marks buffer as read-only.
 Use \\[toggle-read-only] to permit editing."
-  (interactive "fFind file read-only: \np")
+  (interactive (find-file-read-args "fFind file read-only: "))
   (find-file filename wildcards)
   (toggle-read-only 1)
   (current-buffer))
@@ -790,7 +878,7 @@ Use \\[toggle-read-only] to permit editing."
   "Edit file FILENAME in another window but don't allow changes.
 Like \\[find-file-other-window] but marks buffer as read-only.
 Use \\[toggle-read-only] to permit editing."
-  (interactive "fFind file read-only other window: \np")
+  (interactive (find-file-read-args "fFind file read-only other window: "))
   (find-file-other-window filename wildcards)
   (toggle-read-only 1)
   (current-buffer))
@@ -799,7 +887,7 @@ Use \\[toggle-read-only] to permit editing."
   "Edit file FILENAME in another frame but don't allow changes.
 Like \\[find-file-other-frame] but marks buffer as read-only.
 Use \\[toggle-read-only] to permit editing."
-  (interactive "fFind file read-only other frame: \np")
+  (interactive (find-file-read-args "fFind file read-only other frame: "))
   (find-file-other-frame filename wildcards)
   (toggle-read-only 1)
   (current-buffer))
@@ -1091,7 +1179,7 @@ that are visiting the various files."
                  (unless (or (eq read-only buffer-file-read-only)
                              (eq read-only buffer-read-only))
                    (when (or nowarn
-                             (let ((question 
+                             (let ((question
                                     (format "File %s is %s on disk.  Change buffer mode? "
                                             buffer-file-name
                                             (if read-only "read-only" "writable"))))
@@ -1322,7 +1410,7 @@ unless NOMODES is non-nil."
                  "Use M-x make-directory RET RET to create the directory"
                "Use C-u M-x make-directory RET RET to create directory and its parents")))))
       (when msg
-       (message msg)
+       (message "%s" msg)
        (or not-serious (sit-for 1 nil t))))
     (when (and auto-save-default (not noauto))
       (auto-save-mode t)))
@@ -1372,7 +1460,8 @@ in that case, this function acts as if `enable-local-variables' were t."
   (mapc
    (lambda (elt)
      (cons (purecopy (car elt)) (cdr elt)))
-   '(("\\.te?xt\\'" . text-mode)
+   '(("\\.in\\'" nil t)
+     ("\\.te?xt\\'" . text-mode)
      ("\\.c\\'" . c-mode)
      ("\\.h\\'" . c-mode)
      ("\\.tex\\'" . tex-mode)
@@ -1387,6 +1476,7 @@ in that case, this function acts as if `enable-local-variables' were t."
      ("\\.p\\'" . pascal-mode)
      ("\\.pas\\'" . pascal-mode)
      ("\\.ad[abs]\\'" . ada-mode)
+     ("\\.ad[bs].dg\\'" . ada-mode)
      ("\\.\\([pP]\\([Llm]\\|erl\\)\\|al\\)\\'" . perl-mode)
      ("\\.s?html?\\'" . html-mode)
      ("\\.cc\\'" . c++-mode)
@@ -1402,7 +1492,7 @@ in that case, this function acts as if `enable-local-variables' were t."
      ("\\.m\\'" . objc-mode)
      ("\\.java\\'" . java-mode)
      ("\\.mk\\'" . makefile-mode)
-     ("\\(M\\|m\\|GNUm\\)akefile\\(\\.in\\)?\\'" . makefile-mode)
+     ("\\(M\\|m\\|GNUm\\)akefile\\'" . makefile-mode)
      ("\\.am\\'" . makefile-mode)      ;For Automake.
      ;; Less common extensions come here
      ;; so more common ones above are found faster.
@@ -1452,6 +1542,7 @@ in that case, this function acts as if `enable-local-variables' were t."
      ("\\.sim\\'" . simula-mode)
      ("\\.mss\\'" . scribe-mode)
      ("\\.f90\\'" . f90-mode)
+     ("\\.f95\\'" . f90-mode)
      ("\\.indent\\.pro\\'" . fundamental-mode) ; to avoid idlwave-mode
      ("\\.pro\\'" . idlwave-mode)
      ("\\.lsp\\'" . lisp-mode)
@@ -1940,7 +2031,7 @@ is specified, returning t if it is specified."
 (put 'mode-line-buffer-identification 'risky-local-variable t)
 (put 'mode-line-modes 'risky-local-variable t)
 (put 'mode-line-position 'risky-local-variable t)
-(put 'display-time-string-forms 'risky-local-variable t)
+(put 'display-time-string 'risky-local-variable t)
 
 ;; This one is safe because the user gets to check it before it is used.
 (put 'compile-command 'safe-local-variable t)
@@ -2944,7 +3035,8 @@ saying what text to write."
   ;; `make-backup-file-name' will get us the right directory for
   ;; ordinary or numeric backups.  It might create a directory for
   ;; backups as a side-effect, according to `backup-directory-alist'.
-  (let* ((filename (make-backup-file-name filename))
+  (let* ((filename (file-name-sans-versions
+                   (make-backup-file-name filename)))
         (file (file-name-nondirectory filename))
         (dir  (file-name-directory    filename))
         (comp (file-name-all-completions file dir))
@@ -3130,8 +3222,12 @@ non-nil, it is called instead of rereading visited file contents."
                          (if auto-save-p 'emacs-mule-unix
                            coding-system-for-read)))
                     ;; Note that this preserves point in an intelligent way.
-                    (insert-file-contents file-name (not auto-save-p)
-                                          nil nil t))))
+                    (if preserve-modes
+                        (let ((buffer-file-formats buffer-file-formats))
+                          (insert-file-contents file-name (not auto-save-p)
+                                                nil nil t))
+                      (insert-file-contents file-name (not auto-save-p)
+                                            nil nil t)))))
               ;; Recompute the truename in case changes in symlinks
               ;; have changed the truename.
               (setq buffer-file-truename
@@ -3354,16 +3450,24 @@ See also `auto-save-file-name-p'."
   (if buffer-file-name
       (let ((list auto-save-file-name-transforms)
            (filename buffer-file-name)
-           result)
+           result uniq)
        ;; Apply user-specified translations
        ;; to the file name.
        (while (and list (not result))
          (if (string-match (car (car list)) filename)
              (setq result (replace-match (cadr (car list)) t nil
-                                         filename)))
+                                         filename)
+                   uniq (car (cddr (car list)))))
          (setq list (cdr list)))
-       (if result (setq filename result))
-
+       (if result
+           (if uniq
+               (setq filename (concat
+                               (file-name-directory result)
+                               (subst-char-in-string
+                                directory-sep-char ?!
+                                (replace-regexp-in-string "!" "!!"
+                                                          filename))))
+             (setq filename result)))
        (setq result
              (if (and (eq system-type 'ms-dos)
                       (not (msdos-long-file-names)))
@@ -3396,7 +3500,8 @@ See also `auto-save-file-name-p'."
     ;; mode tends to create a good number of these.)
 
     (let ((buffer-name (buffer-name))
-         (limit 0))
+         (limit 0)
+         filename)
       ;; Eliminate all slashes and backslashes by
       ;; replacing them with sequences that start with %.
       ;; Quote % also, to keep distinct names distinct.
@@ -3409,26 +3514,34 @@ See also `auto-save-file-name-p'."
          (setq buffer-name (replace-match replacement t t buffer-name))
          (setq limit (1+ (match-end 0)))))
       ;; Generate the file name.
-      (make-temp-file
-       (let ((fname
-             (expand-file-name
-              (format "#%s#" buffer-name)
-              ;; Try a few alternative directories, to get one we can
-              ;; write it.
-              (cond
-               ((file-writable-p default-directory) default-directory)
-               ((file-writable-p "/var/tmp/") "/var/tmp/")
-               ("~/")))))
-        (if (and (memq system-type '(ms-dos windows-nt))
-                 ;; Don't modify remote (ange-ftp) filenames
-                 (not (string-match "^/\\w+@[-A-Za-z0-9._]+:" fname)))
-            ;; The call to convert-standard-filename is in case
-            ;; buffer-name includes characters not allowed by the
-            ;; DOS/Windows filesystems.  make-temp-file writes to the
-            ;; file it creates, so we must fix the file name _before_
-            ;; make-temp-file is called.
-            (convert-standard-filename fname)
-          fname))))))
+      (setq file-name
+           (make-temp-file
+            (let ((fname
+                   (expand-file-name
+                    (format "#%s#" buffer-name)
+                    ;; Try a few alternative directories, to get one we can
+                    ;; write it.
+                    (cond
+                     ((file-writable-p default-directory) default-directory)
+                     ((file-writable-p "/var/tmp/") "/var/tmp/")
+                     ("~/")))))
+              (if (and (memq system-type '(ms-dos windows-nt))
+                       ;; Don't modify remote (ange-ftp) filenames
+                       (not (string-match "^/\\w+@[-A-Za-z0-9._]+:" fname)))
+                  ;; The call to convert-standard-filename is in case
+                  ;; buffer-name includes characters not allowed by the
+                  ;; DOS/Windows filesystems.  make-temp-file writes to the
+                  ;; file it creates, so we must fix the file name _before_
+                  ;; make-temp-file is called.
+                  (convert-standard-filename fname)
+                fname))
+            nil "#"))
+      ;; make-temp-file creates the file,
+      ;; but we don't want it to exist until we do an auto-save.
+      (condition-case ()
+         (delete-file file-name)
+       (file-error nil))
+      file-name)))
 
 (defun auto-save-file-name-p (filename)
   "Return non-nil if FILENAME can be yielded by `make-auto-save-file-name'.
@@ -3917,8 +4030,8 @@ With prefix arg, silently save all file-visiting buffers, then kill."
                          ;; `identity' means just return the first arg
                          ;; as stripped of its quoting.
                          (substitute-in-file-name . identity)
-                         (file-name-completion 1)
-                         (file-name-all-completions 1)
+                         (file-name-completion 1)
+                         (file-name-all-completions 1)
                          (rename-file 0 1)
                          (copy-file 0 1)
                          (make-symbolic-link 0 1)
@@ -3941,12 +4054,7 @@ With prefix arg, silently save all file-visiting buffers, then kill."
        (setq file-arg-indices (cdr file-arg-indices))))
     (if (eq file-arg-indices 'identity)
        (car arguments)
-      (let ((value (apply operation arguments)))
-       (cond ((memq operation '(file-name-completion))
-              (and value (concat "/:" value)))
-             ((memq operation '(file-name-all-completions))
-              (mapcar (lambda (name) (concat "/:" name)) value))
-             (t value))))))
+      (apply operation arguments))))
 \f
 (define-key ctl-x-map "\C-f" 'find-file)
 (define-key ctl-x-map "\C-r" 'find-file-read-only)