;;; files.el --- file input and output commands for Emacs
-;; Copyright (C) 1985,86,87,92,93,94,95,96,97,98,99,2000,01,02,03,2004
-;;; Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1987, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+;; 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
;; Maintainer: FSF
:group 'backup)
(defcustom require-final-newline nil
- "*Value of t says silently ensure a file ends in a newline when it is saved.
-Non-nil but not t says ask user whether to add a newline when there isn't one.
-nil means don't add newlines."
- :type '(choice (const :tag "Off" nil)
- (const :tag "Add" t)
+ "*Whether to add a newline automatically at the end of the file.
+
+A value of t means do this only when the file is about to be saved.
+A value of `visit' means do this right after the file is visited.
+A value of `visit-save' means do it at both of those times.
+Any other non-nil value means ask user whether to add a newline, when saving.
+nil means don't add newlines.
+
+Certain major modes set this locally to the value obtained
+from `mode-require-final-newline'."
+ :type '(choice (const :tag "When visiting" visit)
+ (const :tag "When saving" t)
+ (const :tag "When visiting or saving" visit-save)
+ (const :tag "Never" nil)
(other :tag "Ask" ask))
:group 'editing-basics)
+(defcustom mode-require-final-newline t
+ "*Whether to add a newline at the end of the file, in certain major modes.
+Those modes set `require-final-newline' to this value when you enable them.
+They do so because they are used for files that are supposed
+to end in newlines, and the question is how to arrange that.
+
+A value of t means do this only when the file is about to be saved.
+A value of `visit' means do this right after the file is visited.
+A value of `visit-save' means do it at both of those times.
+Any other non-nil value means ask user whether to add a newline, when saving."
+ :type '(choice (const :tag "When visiting" visit)
+ (const :tag "When saving" t)
+ (const :tag "When visiting or saving" visit-save)
+ (other :tag "Ask" ask))
+ :group 'editing-basics
+ :version "22.1")
+
(defcustom auto-save-default t
"*Non-nil says by default do auto-saving of every file-visiting buffer."
:type 'boolean
The functions are called in the order given until one of them returns non-nil.")
(defvaralias 'find-file-not-found-hooks 'find-file-not-found-functions)
(make-obsolete-variable
- 'find-file-not-found-hooks 'find-file-not-found-functions "21.4")
+ 'find-file-not-found-hooks 'find-file-not-found-functions "22.1")
;;;It is not useful to make this a local variable.
;;;(put 'find-file-hooks 'permanent-local t)
:group 'find-file
:type 'hook
:options '(auto-insert)
- :version "21.4")
+ :version "22.1")
(defvaralias 'find-file-hooks 'find-file-hook)
-(make-obsolete-variable 'find-file-hooks 'find-file-hook "21.4")
+(make-obsolete-variable 'find-file-hooks 'find-file-hook "22.1")
(defvar write-file-functions nil
"List of functions to be called before writing out a buffer to a file.
updates before the buffer is saved, use `before-save-hook' .")
(put 'write-file-functions 'permanent-local t)
(defvaralias 'write-file-hooks 'write-file-functions)
-(make-obsolete-variable 'write-file-hooks 'write-file-functions "21.4")
+(make-obsolete-variable 'write-file-hooks 'write-file-functions "22.1")
(defvar local-write-file-hooks nil)
(make-variable-buffer-local 'local-write-file-hooks)
(put 'local-write-file-hooks 'permanent-local t)
-(make-obsolete-variable 'local-write-file-hooks 'write-file-functions "21.4")
+(make-obsolete-variable 'local-write-file-hooks 'write-file-functions "22.1")
(defvar write-contents-functions nil
"List of functions to be called before writing out a buffer to a file.
use `before-save-hook'.")
(make-variable-buffer-local 'write-contents-functions)
(defvaralias 'write-contents-hooks 'write-contents-functions)
-(make-obsolete-variable 'write-contents-hooks 'write-contents-functions "21.4")
+(make-obsolete-variable 'write-contents-hooks 'write-contents-functions "22.1")
(defcustom enable-local-variables t
"*Control use of local variables in files you visit.
To visit a file without any kind of conversion and without
automatically choosing a major mode, use \\[find-file-literally]."
- (interactive
- (find-file-read-args "Find file: " nil))
+ (interactive (find-file-read-args "Find file: " nil))
(let ((value (find-file-noselect filename nil nil wildcards)))
(if (listp value)
(mapcar 'switch-to-buffer (nreverse value))
(if (listp value)
(progn
(setq value (nreverse value))
- (switch-to-buffer-other-window (car value))
- (mapcar 'switch-to-buffer (cdr value)))
+ (cons (switch-to-buffer-other-window (car value))
+ (mapcar 'switch-to-buffer (cdr value))))
(switch-to-buffer-other-window value))))
(defun find-file-other-frame (filename &optional wildcards)
(if (listp value)
(progn
(setq value (nreverse value))
- (switch-to-buffer-other-frame (car value))
- (mapcar 'switch-to-buffer (cdr value)))
+ (cons (switch-to-buffer-other-frame (car value))
+ (mapcar 'switch-to-buffer (cdr value))))
(switch-to-buffer-other-frame value))))
(defun find-file-existing (filename &optional wildcards)
"Edit file FILENAME but don't allow changes.
Like \\[find-file] but marks buffer as read-only.
Use \\[toggle-read-only] to permit editing."
- (interactive (find-file-read-args "Find file read-only: " t))
- (unless (file-exists-p filename) (error "%s does not exist" filename))
- (find-file filename wildcards)
- (toggle-read-only 1)
- (current-buffer))
+ (interactive (find-file-read-args "Find file read-only: " nil))
+ (unless (or (and wildcards find-file-wildcards
+ (not (string-match "\\`/:" filename))
+ (string-match "[[*?]" filename))
+ (file-exists-p filename))
+ (error "%s does not exist" filename))
+ (let ((value (find-file filename wildcards)))
+ (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
+ (if (listp value) value (list value)))
+ value))
(defun find-file-read-only-other-window (filename &optional wildcards)
"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 (find-file-read-args "Find file read-only other window: " t))
- (unless (file-exists-p filename) (error "%s does not exist" filename))
- (find-file-other-window filename wildcards)
- (toggle-read-only 1)
- (current-buffer))
+ (interactive (find-file-read-args "Find file read-only other window: " nil))
+ (unless (or (and wildcards find-file-wildcards
+ (not (string-match "\\`/:" filename))
+ (string-match "[[*?]" filename))
+ (file-exists-p filename))
+ (error "%s does not exist" filename))
+ (let ((value (find-file-other-window filename wildcards)))
+ (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
+ (if (listp value) value (list value)))
+ value))
(defun find-file-read-only-other-frame (filename &optional wildcards)
"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 (find-file-read-args "Find file read-only other frame: " t))
- (unless (file-exists-p filename) (error "%s does not exist" filename))
- (find-file-other-frame filename wildcards)
- (toggle-read-only 1)
- (current-buffer))
-
-(defun find-alternate-file-other-window (filename)
+ (interactive (find-file-read-args "Find file read-only other frame: " nil))
+ (unless (or (and wildcards find-file-wildcards
+ (not (string-match "\\`/:" filename))
+ (string-match "[[*?]" filename))
+ (file-exists-p filename))
+ (error "%s does not exist" filename))
+ (let ((value (find-file-other-frame filename wildcards)))
+ (mapc (lambda (b) (with-current-buffer b (toggle-read-only 1)))
+ (if (listp value) value (list value)))
+ value))
+
+(defun find-alternate-file-other-window (filename &optional wildcards)
"Find file FILENAME as a replacement for the file in the next window.
-This command does not select that window."
+This command does not select that window.
+
+Interactively, or if WILDCARDS is non-nil in a call from Lisp,
+expand wildcards (if any) and replace the file with multiple files."
(interactive
(save-selected-window
(other-window 1)
(setq file-name (file-name-nondirectory file)
file-dir (file-name-directory file)))
(list (read-file-name
- "Find alternate file: " file-dir nil nil file-name)))))
+ "Find alternate file: " file-dir nil nil file-name)
+ t))))
(if (one-window-p)
- (find-file-other-window filename)
+ (find-file-other-window filename wildcards)
(save-selected-window
(other-window 1)
- (find-alternate-file filename))))
+ (find-alternate-file filename wildcards))))
-(defun find-alternate-file (filename)
+(defun find-alternate-file (filename &optional wildcards)
"Find file FILENAME, select its buffer, kill previous buffer.
If the current buffer now contains an empty file that you just visited
-\(presumably by mistake), use this command to visit the file you really want."
+\(presumably by mistake), use this command to visit the file you really want.
+
+Interactively, or if WILDCARDS is non-nil in a call from Lisp,
+expand wildcards (if any) and replace the file with multiple files."
(interactive
(let ((file buffer-file-name)
(file-name nil)
(setq file-name (file-name-nondirectory file)
file-dir (file-name-directory file)))
(list (read-file-name
- "Find alternate file: " file-dir nil nil file-name))))
+ "Find alternate file: " file-dir nil nil file-name)
+ t)))
(unless (run-hook-with-args-until-failure 'kill-buffer-query-functions)
(error "Aborted"))
(when (and (buffer-modified-p) (buffer-file-name))
(setq buffer-file-truename nil)
;; Likewise for dired buffers.
(setq dired-directory nil)
- (find-file filename))
+ (find-file filename wildcards))
(when (eq obuf (current-buffer))
;; This executes if find-file gets an error
;; and does not really find anything.
"Return the buffer visiting file FILENAME (a string).
This is like `get-file-buffer', except that it checks for any buffer
visiting the same file, possibly under a different name.
-If PREDICATE is non-nil, only a buffer satisfying it can be returned.
+If PREDICATE is non-nil, only buffers satisfying it are eligible,
+and others are ignored.
If there is no such live buffer, return nil."
(let ((predicate (or predicate #'identity))
(truename (abbreviate-file-name (file-truename filename))))
When nil, never request confirmation."
:group 'files
:group 'find-file
- :version "21.4"
+ :version "22.1"
:type '(choice integer (const :tag "Never request confirmation" nil)))
(defun find-file-noselect (filename &optional nowarn rawfile wildcards)
Optional second arg RAWFILE non-nil means the file is read literally.
Optional third arg WILDCARDS non-nil means do wildcard processing
and visit all the matching files. When wildcards are actually
-used and expanded, the value is a list of buffers
-that are visiting the various files."
+used and expanded, return a list of buffers that are visiting
+the various files."
(setq filename
(abbreviate-file-name
(expand-file-name filename)))
(when (and view-read-only view-mode)
(view-mode-disable))
(normal-mode t)
+ ;; If requested, add a newline at the end of the file.
+ (and (memq require-final-newline '(visit visit-save))
+ (> (point-max) (point-min))
+ (/= (char-after (1- (point-max))) ?\n)
+ (not (and (eq selective-display t)
+ (= (char-after (1- (point-max))) ?\r)))
+ (save-excursion
+ (goto-char (point-max))
+ (insert "\n")))
(when (and buffer-read-only
view-read-only
(not (eq (get major-mode 'mode-class) 'special)))
If the element has the form (REGEXP FUNCTION NON-NIL), then after
calling FUNCTION (if it's not nil), we delete the suffix that matched
-REGEXP and search the list again for another match.")
+REGEXP and search the list again for another match.
+
+If the file name matches `inhibit-first-line-modes-regexps',
+then `auto-mode-alist' is not processed.
+See also `interpreter-mode-alist', which detects executable script modes
+based on the interpreters they specify to run,
+and `magic-mode-alist', which determines modes based on file contents.")
(defvar interpreter-mode-alist
;; Note: The entries for the modes defined in cc-mode.el (awk-mode
("guile" . scheme-mode)
("clisp" . lisp-mode)))
"Alist mapping interpreter names to major modes.
-This alist applies to files whose first line starts with `#!'.
+This is used for files whose first lines match `auto-mode-interpreter-regexp'.
Each element looks like (INTERPRETER . MODE).
The car of each element is compared with
the name of the interpreter specified in the first line.
-If it matches, mode MODE is selected.")
+If it matches, mode MODE is selected.
+
+See also `auto-mode-alist'.")
(defvar inhibit-first-line-modes-regexps '("\\.tar\\'" "\\.tgz\\'")
"List of regexps; if one matches a file name, don't look for `-*-'.")
(concat "\\(?:<\\?xml\\s +[^>]*>\\)?\\s *<"
comment-re "*"
"\\(?:!DOCTYPE\\s +[^>]*>\\s *<\\s *" comment-re "*\\)?"
- "[Hh][Tt][Mm][Ll]")) . html-mode)
+ "[Hh][Tt][Mm][Ll]"))
+ . html-mode)
;; These two must come after html, because they are more general:
("<\\?xml " . xml-mode)
(,(let* ((incomment-re "\\(?:[^-]\\|-[^-]\\)")
(comment-re (concat "\\(?:!--" incomment-re "*-->\\s *<\\)")))
- (concat "\\s *<" comment-re "*!DOCTYPE ")) . sgml-mode)
+ (concat "\\s *<" comment-re "*!DOCTYPE "))
+ . sgml-mode)
("%![^V]" . ps-mode)
("# xmcd " . conf-unix-mode))
"Alist of buffer beginnings vs. corresponding major mode functions.
(save-excursion
(goto-char (point-max))
(search-backward "\n\^L" (max (- (point-max) 3000) (point-min)) 'move)
- (if (let ((case-fold-search t))
- (and (search-forward "Local Variables:" nil t)
- (or (eq enable-local-variables t)
- mode-only
- (and enable-local-variables
- (save-window-excursion
- (switch-to-buffer (current-buffer))
- (save-excursion
- (beginning-of-line)
- (set-window-start (selected-window) (point)))
- (y-or-n-p (format "Set local variables as specified at end of %s? "
- (if buffer-file-name
- (file-name-nondirectory
- buffer-file-name)
- (concat "buffer "
- (buffer-name))))))))))
- (let (prefix prefixlen suffix beg
- (enable-local-eval enable-local-eval))
- ;; The prefix is what comes before "local variables:" in its line.
- ;; The suffix is what comes after "local variables:" in its line.
- (skip-chars-forward " \t")
- (or (eolp)
- (setq suffix (buffer-substring (point)
- (progn (end-of-line) (point)))))
- (goto-char (match-beginning 0))
- (or (bolp)
- (setq prefix
- (buffer-substring (point)
- (progn (beginning-of-line) (point)))))
-
- (if prefix (setq prefixlen (length prefix)
- prefix (regexp-quote prefix)))
- (if suffix (setq suffix (concat (regexp-quote suffix) "$")))
- (forward-line 1)
- (let ((startpos (point))
- endpos
- (thisbuf (current-buffer)))
- (save-excursion
- (if (not (re-search-forward
- (concat (or prefix "")
- "[ \t]*End:[ \t]*"
- (or suffix ""))
- nil t))
- (error "Local variables list is not properly terminated"))
- (beginning-of-line)
- (setq endpos (point)))
-
- (with-temp-buffer
- (insert-buffer-substring thisbuf startpos endpos)
- (goto-char (point-min))
- (subst-char-in-region (point) (point-max)
- ?\^m ?\n)
- (while (not (eobp))
- ;; Discard the prefix, if any.
- (if prefix
- (if (looking-at prefix)
- (delete-region (point) (match-end 0))
- (error "Local variables entry is missing the prefix")))
- (end-of-line)
- ;; Discard the suffix, if any.
- (if suffix
- (if (looking-back suffix)
- (delete-region (match-beginning 0) (point))
- (error "Local variables entry is missing the suffix")))
- (forward-line 1))
- (goto-char (point-min))
-
- (while (not (eobp))
- ;; Find the variable name; strip whitespace.
- (skip-chars-forward " \t")
- (setq beg (point))
- (skip-chars-forward "^:\n")
- (if (eolp) (error "Missing colon in local variables entry"))
- (skip-chars-backward " \t")
- (let* ((str (buffer-substring beg (point)))
- (var (read str))
- val)
- ;; Read the variable value.
- (skip-chars-forward "^:")
- (forward-char 1)
- (setq val (read (current-buffer)))
- (if mode-only
- (if (eq var 'mode)
- (setq mode-specified t))
- ;; Set the variable. "Variables" mode and eval are funny.
- (with-current-buffer thisbuf
- (hack-one-local-variable var val))))
- (forward-line 1)))))))
+ (when (let ((case-fold-search t))
+ (and (search-forward "Local Variables:" nil t)
+ (or (eq enable-local-variables t)
+ mode-only
+ (and enable-local-variables
+ (save-window-excursion
+ (switch-to-buffer (current-buffer))
+ (save-excursion
+ (beginning-of-line)
+ (set-window-start (selected-window) (point)))
+ (y-or-n-p (format "Set local variables as specified at end of %s? "
+ (if buffer-file-name
+ (file-name-nondirectory
+ buffer-file-name)
+ (concat "buffer "
+ (buffer-name))))))))))
+ (skip-chars-forward " \t")
+ (let ((enable-local-eval enable-local-eval)
+ ;; suffix is what comes after "local variables:" in its line.
+ (suffix
+ (concat
+ (regexp-quote (buffer-substring (point) (line-end-position)))
+ "$"))
+ ;; prefix is what comes before "local variables:" in its line.
+ (prefix
+ (concat "^" (regexp-quote
+ (buffer-substring (line-beginning-position)
+ (match-beginning 0)))))
+ beg)
+
+ (forward-line 1)
+ (let ((startpos (point))
+ endpos
+ (thisbuf (current-buffer)))
+ (save-excursion
+ (if (not (re-search-forward
+ (concat prefix "[ \t]*End:[ \t]*" suffix)
+ nil t))
+ (error "Local variables list is not properly terminated"))
+ (beginning-of-line)
+ (setq endpos (point)))
+
+ (with-temp-buffer
+ (insert-buffer-substring thisbuf startpos endpos)
+ (goto-char (point-min))
+ (subst-char-in-region (point) (point-max) ?\^m ?\n)
+ (while (not (eobp))
+ ;; Discard the prefix.
+ (if (looking-at prefix)
+ (delete-region (point) (match-end 0))
+ (error "Local variables entry is missing the prefix"))
+ (end-of-line)
+ ;; Discard the suffix.
+ (if (looking-back suffix)
+ (delete-region (match-beginning 0) (point))
+ (error "Local variables entry is missing the suffix"))
+ (forward-line 1))
+ (goto-char (point-min))
+
+ (while (not (eobp))
+ ;; Find the variable name; strip whitespace.
+ (skip-chars-forward " \t")
+ (setq beg (point))
+ (skip-chars-forward "^:\n")
+ (if (eolp) (error "Missing colon in local variables entry"))
+ (skip-chars-backward " \t")
+ (let* ((str (buffer-substring beg (point)))
+ (var (read str))
+ val)
+ ;; Read the variable value.
+ (skip-chars-forward "^:")
+ (forward-char 1)
+ (setq val (read (current-buffer)))
+ (if mode-only
+ (if (eq var 'mode)
+ (setq mode-specified t))
+ ;; Set the variable. "Variables" mode and eval are funny.
+ (with-current-buffer thisbuf
+ (hack-one-local-variable var val))))
+ (forward-line 1)))))))
(unless mode-only
(run-hooks 'hack-local-variables-hook))
mode-specified))
they appear in an `eval' local variable specification, without first
asking you for confirmation."
:group 'find-file
- :version "21.4"
+ :version "22.1"
:type '(repeat sexp))
(put 'c-set-style 'safe-local-eval-function t)
(defun normal-backup-enable-predicate (name)
"Default `backup-enable-predicate' function.
-Checks for files in `temporary-file-directory' or
-`small-temporary-file-directory'."
+Checks for files in `temporary-file-directory',
+`small-temporary-file-directory', and /tmp."
(not (or (let ((comp (compare-strings temporary-file-directory 0 nil
name 0 nil)))
;; Directory is under temporary-file-directory.
(and (not (eq comp t))
(< comp (- (length temporary-file-directory)))))
+ (let ((comp (compare-strings "/tmp" 0 nil
+ name 0 nil)))
+ ;; Directory is under /tmp.
+ (and (not (eq comp t))
+ (< comp (- (length "/tmp")))))
(if small-temporary-file-directory
(let ((comp (compare-strings small-temporary-file-directory
0 nil
(not (and (eq selective-display t)
(= (char-after (1- (point-max))) ?\r)))
(or (eq require-final-newline t)
+ (eq require-final-newline 'visit-save)
(and require-final-newline
(y-or-n-p
(format "Buffer %s does not end in newline. Add one? "
;; but inhibited if one of write-file-functions returns non-nil.
;; It returns a value (MODES . BACKUPNAME), like backup-buffer.
(defun basic-save-buffer-1 ()
- (if save-buffer-coding-system
- (let ((coding-system-for-write save-buffer-coding-system))
+ (prog1
+ (if save-buffer-coding-system
+ (let ((coding-system-for-write save-buffer-coding-system))
+ (basic-save-buffer-2))
(basic-save-buffer-2))
- (basic-save-buffer-2)))
+ (setq buffer-file-coding-system-explicit last-coding-system-used)))
;; This returns a value (MODES . BACKUPNAME), like backup-buffer.
(defun basic-save-buffer-2 ()
"ACTION-ALIST argument used in call to `map-y-or-n-p'.")
(put 'save-some-buffers-action-alist 'risky-local-variable t)
+(defvar buffer-save-without-query nil
+ "Non-nil means `save-some-buffers' should save this buffer without asking.")
+(make-variable-buffer-local 'buffer-save-without-query)
+
(defun save-some-buffers (&optional arg pred)
"Save some modified file-visiting buffers. Asks user about each one.
You can answer `y' to save, `n' not to save, `C-r' to look at the
change the additional actions you can take on files."
(interactive "P")
(save-window-excursion
- (let* ((queried nil)
- (files-done
+ (let* (queried some-automatic
+ files-done abbrevs-done)
+ (dolist (buffer (buffer-list))
+ ;; First save any buffers that we're supposed to save unconditionally.
+ ;; That way the following code won't ask about them.
+ (with-current-buffer buffer
+ (when (and buffer-save-without-query (buffer-modified-p))
+ (setq some-automatic t)
+ (save-buffer))))
+ ;; Ask about those buffers that merit it,
+ ;; and record the number thus saved.
+ (setq files-done
(map-y-or-n-p
(function
(lambda (buffer)
(buffer-list)
'("buffer" "buffers" "save")
save-some-buffers-action-alist))
- (abbrevs-done
- (and save-abbrevs abbrevs-changed
- (progn
- (if (or arg
- (eq save-abbrevs 'silently)
- (y-or-n-p (format "Save abbrevs in %s? "
- abbrev-file-name)))
- (write-abbrev-file nil))
- ;; Don't keep bothering user if he says no.
- (setq abbrevs-changed nil)
- t))))
+ ;; Maybe to save abbrevs, and record whether
+ ;; we either saved them or asked to.
+ (and save-abbrevs abbrevs-changed
+ (progn
+ (if (or arg
+ (eq save-abbrevs 'silently)
+ (y-or-n-p (format "Save abbrevs in %s? "
+ abbrev-file-name)))
+ (write-abbrev-file nil))
+ ;; Don't keep bothering user if he says no.
+ (setq abbrevs-changed nil)
+ (setq abbrevs-done t)))
(or queried (> files-done 0) abbrevs-done
- (message "(No files need saving)")))))
+ (message (if some-automatic
+ "(Some special files were saved without asking)"
+ "(No files need saving)"))))))
\f
(defun not-modified (&optional arg)
"Mark current buffer as unmodified, not needing to be saved.
(unlock-buffer)))
(widen)
(let ((coding-system-for-read
- ;; Auto-saved file shoule be read without
- ;; any code conversion.
- (if auto-save-p 'emacs-mule-unix
+ ;; Auto-saved file shoule be read by Emacs'
+ ;; internal coding.
+ (if auto-save-p 'auto-save-coding
(or coding-system-for-read
- buffer-file-coding-system))))
+ buffer-file-coding-system-explicit))))
;; This force after-insert-file-set-coding
;; (called from insert-file-contents) to set
;; buffer-file-coding-system to a proper value.
(buffer-substring (point) end)))))))))
+(defvar insert-directory-ls-version 'unknown)
+
;; insert-directory
;; - must insert _exactly_one_line_ describing FILE if WILDCARD and
;; FULL-DIRECTORY-P is nil.
(concat (file-name-as-directory file) ".")
file))))))))
+ ;; If we got "//DIRED//" in the output, it means we got a real
+ ;; directory listing, even if `ls' returned nonzero.
+ ;; So ignore any errors.
+ (when (if (stringp switches)
+ (string-match "--dired\\>" switches)
+ (member "--dired" switches))
+ (save-excursion
+ (forward-line -2)
+ (when (looking-at "//SUBDIRED//")
+ (forward-line -1))
+ (if (looking-at "//DIRED//")
+ (setq result 0))))
+
+ (when (and (not (eq 0 result))
+ (eq insert-directory-ls-version 'unknown))
+ ;; The first time ls returns an error,
+ ;; find the version numbers of ls,
+ ;; and set insert-directory-ls-version
+ ;; to > if it is more than 5.2.1, < if it is less, nil if it
+ ;; is equal or if the info cannot be obtained.
+ ;; (That can mean it isn't GNU ls.)
+ (let ((version-out
+ (with-temp-buffer
+ (call-process "ls" nil t nil "--version")
+ (buffer-string))))
+ (if (string-match "ls (.*utils) \\([0-9.]*\\)$" version-out)
+ (let* ((version (match-string 1 version-out))
+ (split (split-string version "[.]"))
+ (numbers (mapcar 'string-to-int split))
+ (min '(5 2 1))
+ comparison)
+ (while (and (not comparison) (or numbers min))
+ (cond ((null min)
+ (setq comparison '>))
+ ((null numbers)
+ (setq comparison '<))
+ ((> (car numbers) (car min))
+ (setq comparison '>))
+ ((< (car numbers) (car min))
+ (setq comparison '<))
+ (t
+ (setq numbers (cdr numbers)
+ min (cdr min)))))
+ (setq insert-directory-ls-version (or comparison '=)))
+ (setq insert-directory-ls-version nil))))
+
+ ;; For GNU ls versions 5.2.2 and up, ignore minor errors.
+ (when (and (eq 1 result) (eq insert-directory-ls-version '>))
+ (setq result 0))
+
;; If `insert-directory-program' failed, signal an error.
(unless (eq 0 result)
;; Delete the error message it may have output.
(when (if (stringp switches)
(string-match "--dired\\>" switches)
(member "--dired" switches))
+ ;; The following overshoots by one line for an empty
+ ;; directory listed with "--dired", but without "-a"
+ ;; switch, where the ls output contains a
+ ;; "//DIRED-OPTIONS//" line, but no "//DIRED//" line.
+ ;; We take care of that case later.
(forward-line -2)
(when (looking-at "//SUBDIRED//")
(delete-region (point) (progn (forward-line 1) (point)))
(forward-line -1))
(if (looking-at "//DIRED//")
- (let ((end (line-end-position)))
+ (let ((end (line-end-position))
+ (linebeg (point))
+ error-lines)
+ ;; Find all the lines that are error messages,
+ ;; and record the bounds of each one.
+ (goto-char beg)
+ (while (< (point) linebeg)
+ (or (eql (following-char) ?\s)
+ (push (list (point) (line-end-position)) error-lines))
+ (forward-line 1))
+ (setq error-lines (nreverse error-lines))
+ ;; Now read the numeric positions of file names.
+ (goto-char linebeg)
(forward-word 1)
(forward-char 3)
(while (< (point) end)
- (let ((start (+ beg (read (current-buffer))))
- (end (+ beg (read (current-buffer)))))
+ (let ((start (insert-directory-adj-pos
+ (+ beg (read (current-buffer)))
+ error-lines))
+ (end (insert-directory-adj-pos
+ (+ beg (read (current-buffer)))
+ error-lines)))
(if (memq (char-after end) '(?\n ?\ ))
;; End is followed by \n or by " -> ".
(put-text-property start end 'dired-filename t)
(end-of-line))))
(goto-char end)
(beginning-of-line)
- (delete-region (point) (progn (forward-line 2) (point))))
- (forward-line 1)
- (if (looking-at "//DIRED-OPTIONS//")
- (delete-region (point) (progn (forward-line 1) (point)))
- (forward-line 1))))
+ (delete-region (point) (progn (forward-line 1) (point))))
+ ;; Take care of the case where the ls output contains a
+ ;; "//DIRED-OPTIONS//"-line, but no "//DIRED//"-line
+ ;; and we went one line too far back (see above).
+ (forward-line 1))
+ (if (looking-at "//DIRED-OPTIONS//")
+ (delete-region (point) (progn (forward-line 1) (point)))))
;; Now decode what read if necessary.
(let ((coding (or coding-system-for-read
(end-of-line)
(insert " available " available)))))))))))
+(defun insert-directory-adj-pos (pos error-lines)
+ "Convert `ls --dired' file name position value POS to a buffer position.
+File name position values returned in ls --dired output
+count only stdout; they don't count the error messages sent to stderr.
+So this function converts to them to real buffer positions.
+ERROR-LINES is a list of buffer positions of error message lines,
+of the form (START END)."
+ (while (and error-lines (< (caar error-lines) pos))
+ (setq pos (+ pos (- (nth 1 (car error-lines)) (nth 0 (car error-lines)))))
+ (pop error-lines))
+ pos)
+
(defun insert-directory-safely (file switches
&optional wildcard full-directory-p)
"Insert directory listing for FILE, formatted according to SWITCHES.