;;; dired-aux.el --- less commonly used parts of dired -*-byte-compile-dynamic: t;-*-
-;; Copyright (C) 1985, 1986, 1992, 1994, 1998, 2000 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1992, 1994, 1998, 2000, 2001, 2004
+;; Free Software Foundation, Inc.
;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>.
;; Maintainer: FSF
+;; Keywords: files
;; This file is part of GNU Emacs.
;;;###begin dired-cmd.el
;; Diffing and compressing
+(defconst dired-star-subst-regexp "\\(^\\|[ \t]\\)\\*\\([ \t]\\|$\\)")
+(defconst dired-quark-subst-regexp "\\(^\\|[ \t]\\)\\?\\([ \t]\\|$\\)")
+
;;;###autoload
(defun dired-diff (file &optional switches)
"Compare file at point with file FILE using `diff'.
-FILE defaults to the file at the mark.
+FILE defaults to the file at the mark. (That's the mark set by
+\\[set-mark-command], not by Dired's \\[dired-mark] command.)
The prompted-for file is the first file given to `diff'.
With prefix arg, prompt for second argument SWITCHES,
which is options for `diff'."
nil))
(diff-backup (dired-get-filename) switches))
+(defun dired-compare-directories (dir2 predicate)
+ "Mark files with different file attributes in two dired buffers.
+Compare file attributes of files in the current directory
+with file attributes in directory DIR2 using PREDICATE on pairs of files
+with the same name. Mark files for which PREDICATE returns non-nil.
+Mark files with different names if PREDICATE is nil (or interactively
+when the user enters empty input at the predicate prompt).
+
+PREDICATE is a Lisp expression that can refer to the following variables:
+
+ size1, size2 - file size in bytes
+ mtime1, mtime2 - last modification time in seconds, as a float
+ fa1, fa2 - list of file attributes
+ returned by function `file-attributes'
+
+ where 1 refers to attribute of file in the current dired buffer
+ and 2 to attribute of file in second dired buffer.
+
+Examples of PREDICATE:
+
+ (> mtime1 mtime2) - mark newer files
+ (not (= size1 size2)) - mark files with different sizes
+ (not (string= (nth 8 fa1) (nth 8 fa2))) - mark files with different modes
+ (not (and (= (nth 2 fa1) (nth 2 fa2)) - mark files with different UID
+ (= (nth 3 fa1) (nth 3 fa2)))) and GID."
+ (interactive
+ (list (read-file-name (format "Compare %s with: "
+ (dired-current-directory))
+ (dired-dwim-target-directory))
+ (read-from-minibuffer "Mark if (lisp expr or RET): " nil nil t nil "nil")))
+ (let* ((dir1 (dired-current-directory))
+ (file-alist1 (dired-files-attributes dir1))
+ (file-alist2 (dired-files-attributes dir2))
+ (file-list1 (mapcar
+ 'cadr
+ (dired-file-set-difference
+ file-alist1 file-alist2
+ predicate)))
+ (file-list2 (mapcar
+ 'cadr
+ (dired-file-set-difference
+ file-alist2 file-alist1
+ predicate))))
+ (dired-fun-in-all-buffers
+ dir1 nil
+ (lambda ()
+ (dired-mark-if
+ (member (dired-get-filename nil t) file-list1) nil)))
+ (dired-fun-in-all-buffers
+ dir2 nil
+ (lambda ()
+ (dired-mark-if
+ (member (dired-get-filename nil t) file-list2) nil)))
+ (message "Marked in dir1: %s files, in dir2: %s files"
+ (length file-list1)
+ (length file-list2))))
+
+(defun dired-file-set-difference (list1 list2 predicate)
+ "Combine LIST1 and LIST2 using a set-difference operation.
+The result list contains all file items that appear in LIST1 but not LIST2.
+This is a non-destructive function; it makes a copy of the data if necessary
+to avoid corrupting the original LIST1 and LIST2.
+PREDICATE (see `dired-compare-directories') is an additional match
+condition. Two file items are considered to match if they are equal
+*and* PREDICATE evaluates to t."
+ (if (or (null list1) (null list2))
+ list1
+ (let (res)
+ (dolist (file1 list1)
+ (unless (let ((list list2))
+ (while (and list
+ (not (let* ((file2 (car list))
+ (fa1 (caddr file1))
+ (fa2 (caddr file2))
+ (size1 (nth 7 fa1))
+ (size2 (nth 7 fa2))
+ (mtime1 (float-time (nth 5 fa1)))
+ (mtime2 (float-time (nth 5 fa2))))
+ (and
+ (equal (car file1) (car file2))
+ (not (eval predicate))))))
+ (setq list (cdr list)))
+ list)
+ (setq res (cons file1 res))))
+ (nreverse res))))
+
+(defun dired-files-attributes (dir)
+ "Return a list of all file names and attributes from DIR.
+List has a form of (file-name full-file-name (attribute-list))"
+ (mapcar
+ (lambda (file-name)
+ (let ((full-file-name (expand-file-name file-name dir)))
+ (list file-name
+ full-file-name
+ (file-attributes full-file-name))))
+ (directory-files dir)))
+\f
(defun dired-do-chxxx (attribute-name program op-symbol arg)
- ;; Change file attributes (mode, group, owner) of marked files and
+ ;; Change file attributes (mode, group, owner, timestamp) of marked files and
;; refresh their file lines.
;; ATTRIBUTE-NAME is a string describing the attribute to the user.
;; PROGRAM is the program used to change the attribute.
(dired-bunch-files 10000
(function dired-check-process)
(append
- (list operation program new-attribute)
+ (list operation program)
+ (if (eq op-symbol 'touch)
+ '("-t") nil)
+ (list new-attribute)
(if (string-match "gnu" system-configuration)
'("--") nil))
files))
"Change the group of the marked (or next ARG) files."
(interactive "P")
(if (memq system-type '(ms-dos windows-nt))
- (error "chgrp not supported on this system."))
+ (error "chgrp not supported on this system"))
(dired-do-chxxx "Group" "chgrp" 'chgrp arg))
;;;###autoload
"Change the owner of the marked (or next ARG) files."
(interactive "P")
(if (memq system-type '(ms-dos windows-nt))
- (error "chown not supported on this system."))
+ (error "chown not supported on this system"))
(dired-do-chxxx "Owner" dired-chown-program 'chown arg))
+(defun dired-do-touch (&optional arg)
+ "Change the timestamp of the marked (or next ARG) files.
+This calls touch."
+ (interactive "P")
+ (dired-do-chxxx "Timestamp" dired-touch-program 'touch arg))
+
;; Process all the files in FILES in batches of a convenient size,
;; by means of (FUNCALL FUNCTION ARGS... SOME-FILES...).
;; Batches are chosen to need less than MAX chars for the file names,
;; allowing 3 extra characters of separator per file name.
(defun dired-bunch-files (max function args files)
(let (pending
+ past
(pending-length 0)
failures)
;; Accumulate files as long as they fit in MAX chars,
;; If we have at least 1 pending file
;; and this file won't fit in the length limit, process now.
(if (and pending (> (+ thislength pending-length) max))
- (setq failures
- (nconc (apply function (append args pending))
- failures)
+ (setq pending (nreverse pending)
+ ;; The elements of PENDING are now in forward order.
+ ;; Do the operation and record failures.
+ failures (nconc (apply function (append args pending))
+ failures)
+ ;; Transfer the elemens of PENDING onto PAST
+ ;; and clear it out. Now PAST contains the first N files
+ ;; specified (for some N), and FILES contains the rest.
+ past (nconc past pending)
pending nil
pending-length 0))
;; Do (setq pending (cons thisfile pending))
(setq pending files)
(setq pending-length (+ thislength pending-length))
(setq files rest)))
- (nconc (apply function (append args pending))
- failures)))
+ (setq pending (nreverse pending))
+ (prog1
+ (nconc (apply function (append args pending))
+ failures)
+ ;; Now the original list FILES has been put back as it was.
+ (nconc past pending))))
;;;###autoload
(defun dired-do-print (&optional arg)
nil op-symbol files
(function read-string)
(format prompt (dired-mark-prompt arg files)) initial))
-
+\f
;;; Cleaning a directory: flagging some backups for deletion.
(defvar dired-file-version-alist)
(progn (beginning-of-line)
(delete-char 1)
(insert dired-del-marker)))))
-
+\f
;;; Shell commands
(defun dired-read-shell-command (prompt arg files)
the next ARG files are used. Just \\[universal-argument] means the current file.
The prompt mentions the file(s) or the marker, as appropriate.
-If there is output, it goes to a separate buffer.
+If there is a `*' in COMMAND, surrounded by whitespace, this runs
+COMMAND just once with the entire file list substituted there.
+
+If there is no `*', but there is a `?' in COMMAND, surrounded by
+whitespace, this runs COMMAND on each file individually with the
+file name substituted for `?'.
+
+Otherwise, this runs COMMAND on each file individually with the
+file name added at the end of COMMAND (separated by a space).
-Normally the command is run on each file individually.
-However, if there is a `*' in the command then it is run
-just once with the entire file list substituted there.
+`*' and `?' when not surrounded by whitespace have no special
+significance for `dired-do-shell-command', and are passed through
+normally to the shell, but you must confirm first. To pass `*' by
+itself to the shell as a wildcard, type `*\"\"'.
-If there is no `*', but a `?' in the command then it is still run
-on each file individually but with the filename substituted there
-instead of att the end of the command.
+If COMMAND produces output, it goes to a separate buffer.
-No automatic redisplay of dired buffers is attempted, as there's no
-telling what files the command may have changed. Type
-\\[dired-do-redisplay] to redisplay the marked files.
+This feature does not try to redisplay Dired buffers afterward, as
+there's no telling what files COMMAND may have changed.
+Type \\[dired-do-redisplay] to redisplay the marked files.
-The shell command has the top level directory as working directory, so
-output files usually are created there instead of in a subdir.
+When COMMAND runs, its working directory is the top-level directory of
+the Dired buffer, so output files usually are created there instead of
+in a subdir.
In a noninteractive call (from Lisp code), you must specify
the list of file names explicitly with the FILE-LIST argument."
files)
current-prefix-arg
files)))
- (let* ((on-each (not (string-match "\\*" command))))
- (if on-each
- (dired-bunch-files
- (- 10000 (length command))
- (function (lambda (&rest files)
- (dired-run-shell-command
- (dired-shell-stuff-it command files t arg))))
- nil
- file-list)
- ;; execute the shell command
- (dired-run-shell-command
- (dired-shell-stuff-it command file-list nil arg)))))
+ (let* ((on-each (not (string-match dired-star-subst-regexp command)))
+ (subst (not (string-match dired-quark-subst-regexp command)))
+ (star (not (string-match "\\*" command)))
+ (qmark (not (string-match "\\?" command))))
+ ;; Get confirmation for wildcards that may have been meant
+ ;; to control substitution of a file name or the file name list.
+ (if (cond ((not (or on-each subst))
+ (error "You can not combine `*' and `?' substitution marks"))
+ ((and star (not on-each))
+ (y-or-n-p "Confirm--do you mean to use `*' as a wildcard? "))
+ ((and qmark (not subst))
+ (y-or-n-p "Confirm--do you mean to use `?' as a wildcard? "))
+ (t))
+ (if on-each
+ (dired-bunch-files
+ (- 10000 (length command))
+ (function (lambda (&rest files)
+ (dired-run-shell-command
+ (dired-shell-stuff-it command files t arg))))
+ nil
+ file-list)
+ ;; execute the shell command
+ (dired-run-shell-command
+ (dired-shell-stuff-it command file-list nil arg))))))
;; Might use {,} for bash or csh:
(defvar dired-mark-prefix ""
;; Might be redefined for smarter things and could then use RAW-ARG
;; (coming from interactive P and currently ignored) to decide what to do.
;; Smart would be a way to access basename or extension of file names.
-;; See dired-trns.el for an approach to this.
- ;; Bug: There is no way to quote a * or a ?
- ;; On the other hand, you can never accidentally get a * or a ? into
- ;; your cmd.
(let ((stuff-it
- (cond ((string-match "\\*" command)
- (lambda (x) (replace-regexp-in-string "\\*" x command)))
- ((string-match "\\?" command)
- (lambda (x) (replace-regexp-in-string "\\?" x command)))
- (t (lambda (x) (concat command " " x))))))
+ (if (or (string-match dired-star-subst-regexp command)
+ (string-match dired-quark-subst-regexp command))
+ (lambda (x)
+ (let ((retval command))
+ (while (string-match
+ "\\(^\\|[ \t]\\)\\([*?]\\)\\([ \t]\\|$\\)" retval)
+ (setq retval (replace-match x t t retval 2)))
+ retval))
+ (lambda (x) (concat command dired-mark-separator x)))))
(if on-each
(mapconcat stuff-it (mapcar 'shell-quote-argument file-list) ";")
- (let ((fns (mapconcat 'shell-quote-argument
- file-list dired-mark-separator)))
+ (let ((files (mapconcat 'shell-quote-argument
+ file-list dired-mark-separator)))
(if (> (length file-list) 1)
- (setq fns (concat dired-mark-prefix fns dired-mark-postfix)))
- (funcall stuff-it fns)))))
+ (setq files (concat dired-mark-prefix files dired-mark-postfix)))
+ (funcall stuff-it files)))))
;; This is an extra function so that it can be redefined by ange-ftp.
(defun dired-run-shell-command (command)
(shell-command command)))
;; Return nil for sake of nconc in dired-bunch-files.
nil)
-
+\f
;; In Emacs 19 this will return program's exit status.
;; This is a separate function so that ange-ftp can redefine it.
(defun dired-call-process (program discard &rest arguments)
(set-buffer err-buffer)
(erase-buffer)
(setq default-directory dir ; caller's default-directory
- err (/= 0
- (apply (function dired-call-process) program nil arguments)))
+ err (not (eq 0
+ (apply (function dired-call-process) program nil arguments))))
(if err
(progn
(dired-log (concat program " " (prin1-to-string arguments) "\n"))
(kill-buffer err-buffer)
(message "%s...done" msg)
nil))))
-
+\f
;; Commands that delete or redisplay part of the dired buffer.
(defun dired-kill-line (&optional arg)
(while (/= 0 arg)
(setq file (dired-get-filename nil t))
(if (not file)
- (error "Can only kill file lines.")
+ (error "Can only kill file lines")
(save-excursion (and file
(dired-goto-subdir file)
(dired-kill-subdir)))
count))))
;;;###end dired-cmd.el
-
+\f
;;; 30K
;;;###begin dired-cp.el
;; For .z, try gunzip. It might be an old gzip file,
;; or it might be from compact? pack? (which?) but gunzip handles both.
("\\.z\\'" "" "gunzip")
+ ("\\.dz\\'" "" "dictunzip")
+ ("\\.tbz\\'" ".tar" "bunzip2")
("\\.bz2\\'" "" "bunzip2")
;; This item controls naming for compression.
("\\.tar\\'" ".tgz" nil))
"compress" "-f" file))
;; Don't use NEWNAME with `compress'.
(concat file ".Z"))))))))
-
+\f
(defun dired-mark-confirm (op-symbol arg)
;; Request confirmation from the user that the operation described
;; by OP-SYMBOL is to be performed on the marked files.
'((?\y . y) (?\040 . y) ; `y' or SPC means accept once
(?n . n) (?\177 . n) ; `n' or DEL skips once
(?! . yes) ; `!' accepts rest
- (?q. no) (?\e . no) ; `q' or ESC skips rest
+ (?q . no) (?\e . no) ; `q' or ESC skips rest
;; None of these keys quit - use C-g for that.
))
(apply 'message qprompt qs-args)
(setq char (set qs-var (read-char))))
(memq (cdr elt) '(t y yes)))))))
-
+\f
;;;###autoload
(defun dired-do-compress (&optional arg)
"Compress or uncompress marked (or next ARG) files."
arg)
(dired-move-to-filename)
(message "Redisplaying...done")))
-
+\f
(defun dired-update-file-line (file)
;; Delete the current line, and insert an entry for FILE.
;; If FILE is nil, then just delete the current line.
(subst-char-in-region opoint (1+ opoint) ?\040 char))))
(dired-move-to-filename))
-(defun dired-fun-in-all-buffers (directory file fun &rest args)
- ;; In all buffers dired'ing DIRECTORY, run FUN with ARGS.
- ;; If the buffer has a wildcard pattern, check that it matches FILE.
- ;; (FILE does not include a directory component.)
- ;; FILE may be nil, in which case ignore it.
- ;; Return list of buffers where FUN succeeded (i.e., returned non-nil).
- (let ((buf-list (dired-buffers-for-dir (expand-file-name directory)
- file))
- (obuf (current-buffer))
- buf success-list)
- (while buf-list
- (setq buf (car buf-list)
- buf-list (cdr buf-list))
- (unwind-protect
- (progn
- (set-buffer buf)
- (if (apply fun args)
- (setq success-list (cons (buffer-name buf) success-list))))
- (set-buffer obuf)))
- success-list))
-
;;;###autoload
(defun dired-add-file (filename &optional marker-char)
(dired-fun-in-all-buffers
(if (eq (following-char) ?\r)
(dired-unhide-subdir))
;; We are already where we should be, except when
- ;; point is before the subdir line or its total line.
+ ;; point is before the subdir line or its total line.
(let ((p (dired-after-subdir-garbage cur-dir)))
(if (< (point) p)
(goto-char p))))
(let (buffer-read-only opoint)
(beginning-of-line)
(setq opoint (point))
- (dired-add-entry-do-indentation marker-char)
- ;; don't expand `.'. Show just the file name within directory.
+ ;; Don't expand `.'. Show just the file name within directory.
(let ((default-directory directory))
- (insert-directory filename
- (concat dired-actual-switches "d")))
+ (dired-insert-directory directory
+ (concat dired-actual-switches "d")
+ (list filename)))
+ (goto-char opoint)
+ ;; Put in desired marker char.
+ (when marker-char
+ (let ((dired-marker-char
+ (if (integerp marker-char) marker-char dired-marker-char)))
+ (dired-mark nil)))
;; Compensate for a bug in ange-ftp.
;; It inserts the file's absolute name, rather than
;; the relative one. That may be hard to fix since it
(goto-char opoint)
(let ((inserted-name (dired-get-filename 'verbatim)))
(if (file-name-directory inserted-name)
- (progn
+ (let (props)
(end-of-line)
- (delete-char (- (length inserted-name)))
- (insert filename)
+ (forward-char (- (length inserted-name)))
+ (setq props (text-properties-at (point)))
+ (delete-char (length inserted-name))
+ (let ((pt (point)))
+ (insert filename)
+ (set-text-properties pt (point) props))
(forward-char 1))
(forward-line 1)))
- ;; Give each line a text property recording info about it.
- (dired-insert-set-properties opoint (point))
(forward-line -1)
(if dired-after-readin-hook ;; the subdir-alist is not affected...
(save-excursion ;; ...so we can run it right now:
(goto-char opoint))
(not reason))) ; return t on success, nil else
-;; This is a separate function for the sake of nested dired format.
-(defun dired-add-entry-do-indentation (marker-char)
- ;; two spaces or a marker plus a space:
- (insert (if marker-char
- (if (integerp marker-char) marker-char dired-marker-char)
- ?\040)
- ?\040))
-
(defun dired-after-subdir-garbage (dir)
;; Return pos of first file line of DIR, skipping header and total
;; or wildcard lines.
;;;###autoload
(defun dired-relist-file (file)
+ "Create or update the line for FILE in all Dired buffers it would belong in."
(dired-fun-in-all-buffers (file-name-directory file)
(file-name-nondirectory file)
(function dired-relist-entry) file))
(save-excursion (forward-line 1) (point))))
(setq file (directory-file-name file))
(dired-add-entry file (if (eq ?\040 marker) nil marker)))))
-
+\f
;;; Copy, move/rename, making hard and symbolic links
(defcustom dired-recursive-copies nil
"*Decide whether recursive copies are allowed.
-Nil means no recursive copies.
+nil means no recursive copies.
`always' means copy recursively without asking.
`top' means ask for each directory at top level.
Anything else means ask for each directory."
(defvar dired-overwrite-confirmed)
(defun dired-handle-overwrite (to)
- ;; Save old version of a to be overwritten file TO.
+ ;; Save old version of file TO that is to be overwritten.
;; `dired-overwrite-confirmed' and `overwrite-backup-query' are fluid vars
;; from dired-create-files.
(let (backup)
(copy-file from to ok-flag dired-copy-preserve-time)))
;;;###autoload
-(defun dired-rename-file (from to ok-flag)
- (dired-handle-overwrite to)
- (rename-file from to ok-flag) ; error is caught in -create-files
+(defun dired-rename-file (file newname ok-if-already-exists)
+ (dired-handle-overwrite newname)
+ (rename-file file newname ok-if-already-exists) ; error is caught in -create-files
;; Silently rename the visited file of any buffer visiting this file.
- (and (get-file-buffer from)
- (with-current-buffer (get-file-buffer from)
- (set-visited-file-name to nil t)))
- (dired-remove-file from)
+ (and (get-file-buffer file)
+ (with-current-buffer (get-file-buffer file)
+ (set-visited-file-name newname nil t)))
+ (dired-remove-file file)
;; See if it's an inserted subdir, and rename that, too.
- (dired-rename-subdir from to))
+ (dired-rename-subdir file newname))
(defun dired-rename-subdir (from-dir to-dir)
(setq from-dir (file-name-as-directory from-dir)
(if (and buffer-file-name
(dired-in-this-tree buffer-file-name expanded-from-dir))
(let ((modflag (buffer-modified-p))
- (to-file (replace-regexp-in-string
+ (to-file (dired-replace-in-string
(concat "^" (regexp-quote from-dir))
to-dir
buffer-file-name)))
;; Update buffer-local dired-subdir-alist
(setcar elt
(dired-normalize-subdir
- (replace-regexp-in-string regexp newtext (car elt)))))))
-
+ (dired-replace-in-string regexp newtext (car elt)))))))
+\f
;; The basic function for half a dozen variations on cp/mv/ln/ln -s.
(defun dired-create-files (file-creator operation fn-list name-constructor
&optional marker-char)
(message "%s: %s file%s"
operation success-count (dired-plural-s success-count)))))
(dired-move-to-filename))
-
+\f
(defun dired-do-create-files (op-symbol file-creator operation arg
&optional marker-char op1
how-to)
"Create a new file for each marked file.
Prompts user for target, which is a directory in which to create
the new files. Target may be a plain file if only one marked
- file exists.
+ file exists. The way the default for the target directory is
+ computed depends on the value of `dired-dwim-target-directory'.
OP-SYMBOL is the symbol for the operation. Function `dired-mark-pop-up'
will determine whether pop-ups are appropriate for this OP-SYMBOL.
FILE-CREATOR and OPERATION as in `dired-create-files'.
;; will return t because the filesystem is
;; case-insensitive, and Emacs will try to move
;; foo -> foo/foo, which fails.
- (if (and (memq system-type '(ms-dos windows-nt))
+ (if (and (memq system-type '(ms-dos windows-nt cygwin))
(eq op-symbol 'move)
dired-one-file
(string= (downcase
(dired-current-directory)))))
(or other-dir this-dir))
this-dir)))
-
+\f
;;;###autoload
(defun dired-create-directory (directory)
"Create a directory called DIRECTORY."
;; symlinks.
(defvar dired-copy-how-to-fn nil
- "Nil or a function used by `dired-do-copy' to determine target.
+ "nil or a function used by `dired-do-copy' to determine target.
See HOW-TO argument for `dired-do-create-files'.")
;;;###autoload
When operating on just the current file, you specify the new name.
When operating on multiple or marked files, you specify a directory,
and new copies of these files are made in that directory
-with the same names that the files currently have."
+with the same names that the files currently have. The default
+suggested for the target directory depends on the value of
+`dired-dwim-target', which see."
(interactive "P")
(let ((dired-recursive-copies dired-recursive-copies))
(dired-do-create-files 'copy (function dired-copy-file)
When operating on just the current file, you specify the new name.
When operating on multiple or marked files, you specify a directory
and new symbolic links are made in that directory
-with the same names that the files currently have."
+with the same names that the files currently have. The default
+suggested for the target directory depends on the value of
+`dired-dwim-target', which see."
(interactive "P")
(dired-do-create-files 'symlink (function make-symbolic-link)
"Symlink" arg dired-keep-marker-symlink))
When operating on just the current file, you specify the new name.
When operating on multiple or marked files, you specify a directory
and new hard links are made in that directory
-with the same names that the files currently have."
+with the same names that the files currently have. The default
+suggested for the target directory depends on the value of
+`dired-dwim-target', which see."
(interactive "P")
- (dired-do-create-files 'hardlink (function add-name-to-file)
+ (dired-do-create-files 'hardlink (function dired-hardlink)
"Hardlink" arg dired-keep-marker-hardlink))
+(defun dired-hardlink (file newname &optional ok-if-already-exists)
+ (dired-handle-overwrite newname)
+ ;; error is caught in -create-files
+ (add-name-to-file file newname ok-if-already-exists)
+ ;; Update the link count
+ (dired-relist-file file))
+
;;;###autoload
(defun dired-do-rename (&optional arg)
"Rename current file or all marked (or next ARG) files.
When renaming just the current file, you specify the new name.
-When renaming multiple or marked files, you specify a directory."
+When renaming multiple or marked files, you specify a directory.
+This command also renames any buffers that are visiting the files.
+The default suggested for the target directory depends on the value
+of `dired-dwim-target', which see."
(interactive "P")
(dired-do-create-files 'move (function dired-rename-file)
"Move" arg dired-keep-marker-rename "Rename"))
;;;###end dired-cp.el
-
+\f
;;; 5K
;;;###begin dired-re.el
(defun dired-do-create-files-regexp
- (file-creator operation arg regexp newname &optional whole-path marker-char)
+ (file-creator operation arg regexp newname &optional whole-name marker-char)
;; Create a new file for each marked file using regexps.
;; FILE-CREATOR and OPERATION as in dired-create-files.
;; ARG as in dired-get-marked-files.
;; Matches each marked file against REGEXP and constructs the new
;; filename from NEWNAME (like in function replace-match).
- ;; Optional arg WHOLE-PATH means match/replace the whole file name
+ ;; Optional arg WHOLE-NAME means match/replace the whole file name
;; instead of only the non-directory part of the file.
;; Optional arg MARKER-CHAR as in dired-create-files.
(let* ((fn-list (dired-get-marked-files nil arg))
(downcase operation)))
(regexp-name-constructor
;; Function to construct new filename using REGEXP and NEWNAME:
- (if whole-path ; easy (but rare) case
+ (if whole-name ; easy (but rare) case
(function
(lambda (from)
(let ((to (dired-string-replace-match regexp from newname))
to)
(dired-log "%s: %s did not match regexp %s\n"
operation from regexp)))))
- ;; not whole-path, replace non-directory part only
+ ;; not whole-name, replace non-directory part only
(function
(lambda (from)
(let* ((new (dired-string-replace-match
(defun dired-mark-read-regexp (operation)
;; Prompt user about performing OPERATION.
- ;; Read and return list of: regexp newname arg whole-path.
- (let* ((whole-path
+ ;; Read and return list of: regexp newname arg whole-name.
+ (let* ((whole-name
(equal 0 (prefix-numeric-value current-prefix-arg)))
(arg
- (if whole-path nil current-prefix-arg))
+ (if whole-name nil current-prefix-arg))
(regexp
(dired-read-regexp
- (concat (if whole-path "Path " "") operation " from (regexp): ")))
+ (concat (if whole-name "Abs. " "") operation " from (regexp): ")))
(newname
(read-string
- (concat (if whole-path "Path " "") operation " " regexp " to: "))))
- (list regexp newname arg whole-path)))
+ (concat (if whole-name "Abs. " "") operation " " regexp " to: "))))
+ (list regexp newname arg whole-name)))
;;;###autoload
-(defun dired-do-rename-regexp (regexp newname &optional arg whole-path)
- "Rename marked files containing REGEXP to NEWNAME.
+(defun dired-do-rename-regexp (regexp newname &optional arg whole-name)
+ "Rename selected files whose names match REGEXP to NEWNAME.
+
+With non-zero prefix argument ARG, the command operates on the next ARG
+files. Otherwise, it operates on all the marked files, or the current
+file if none are marked.
+
As each match is found, the user must type a character saying
what to do with it. For directions, type \\[help-command] at that time.
NEWNAME may contain \\=\\<n> or \\& as in `query-replace-regexp'.
(interactive (dired-mark-read-regexp "Rename"))
(dired-do-create-files-regexp
(function dired-rename-file)
- "Rename" arg regexp newname whole-path dired-keep-marker-rename))
+ "Rename" arg regexp newname whole-name dired-keep-marker-rename))
;;;###autoload
-(defun dired-do-copy-regexp (regexp newname &optional arg whole-path)
- "Copy all marked files containing REGEXP to NEWNAME.
+(defun dired-do-copy-regexp (regexp newname &optional arg whole-name)
+ "Copy selected files whose names match REGEXP to NEWNAME.
See function `dired-do-rename-regexp' for more info."
(interactive (dired-mark-read-regexp "Copy"))
(let ((dired-recursive-copies nil)) ; No recursive copies.
(dired-do-create-files-regexp
(function dired-copy-file)
(if dired-copy-preserve-time "Copy [-p]" "Copy")
- arg regexp newname whole-path dired-keep-marker-copy)))
+ arg regexp newname whole-name dired-keep-marker-copy)))
;;;###autoload
-(defun dired-do-hardlink-regexp (regexp newname &optional arg whole-path)
- "Hardlink all marked files containing REGEXP to NEWNAME.
+(defun dired-do-hardlink-regexp (regexp newname &optional arg whole-name)
+ "Hardlink selected files whose names match REGEXP to NEWNAME.
See function `dired-do-rename-regexp' for more info."
(interactive (dired-mark-read-regexp "HardLink"))
(dired-do-create-files-regexp
(function add-name-to-file)
- "HardLink" arg regexp newname whole-path dired-keep-marker-hardlink))
+ "HardLink" arg regexp newname whole-name dired-keep-marker-hardlink))
;;;###autoload
-(defun dired-do-symlink-regexp (regexp newname &optional arg whole-path)
- "Symlink all marked files containing REGEXP to NEWNAME.
+(defun dired-do-symlink-regexp (regexp newname &optional arg whole-name)
+ "Symlink selected files whose names match REGEXP to NEWNAME.
See function `dired-do-rename-regexp' for more info."
(interactive (dired-mark-read-regexp "SymLink"))
(dired-do-create-files-regexp
(function make-symbolic-link)
- "SymLink" arg regexp newname whole-path dired-keep-marker-symlink))
+ "SymLink" arg regexp newname whole-name dired-keep-marker-symlink))
(defun dired-create-files-non-directory
(file-creator basename-constructor operation arg)
(dired-rename-non-directory (function downcase) "Rename downcase" arg))
;;;###end dired-re.el
-
+\f
;;; 13K
;;;###begin dired-ins.el
(dired-insert-subdir-newpos dirname)) ; else compute new position
(dired-insert-subdir-doupdate
dirname elt (dired-insert-subdir-doinsert dirname switches))
- (if switches-have-R (dired-build-subdir-alist))
+ (if switches-have-R (dired-build-subdir-alist switches))
(dired-initial-position dirname)
(save-excursion (dired-mark-remembered mark-alist))))
(delete-region begin-marker (point)))))
(defun dired-insert-subdir-doinsert (dirname switches)
- ;; Insert ls output after point and put point on the correct
- ;; position for the subdir alist.
+ ;; Insert ls output after point.
;; Return the boundary of the inserted text (as list of BEG and END).
- (let ((begin (point)) end)
- (message "Reading directory %s..." dirname)
- (let ((dired-actual-switches
- (or switches
- (replace-regexp-in-string "R" "" dired-actual-switches))))
- (if (equal dirname (car (car (reverse dired-subdir-alist))))
- ;; top level directory may contain wildcards:
- (dired-readin-insert dired-directory)
- (let ((opoint (point)))
- (insert-directory dirname dired-actual-switches nil t)
- (dired-insert-set-properties opoint (point)))))
- (message "Reading directory %s...done" dirname)
- (setq end (point-marker))
- (indent-rigidly begin end 2)
- ;; call dired-insert-headerline afterwards, as under VMS dired-ls
- ;; does insert the headerline itself and the insert function just
- ;; moves point.
- ;; Need a marker for END as this inserts text.
- (goto-char begin)
- (if (not (looking-at "^ /.*:$"))
- (dired-insert-headerline dirname))
- ;; point is now like in dired-build-subdir-alist
- (prog1
- (list begin (marker-position end))
- (set-marker end nil))))
+ (save-excursion
+ (let ((begin (point)))
+ (message "Reading directory %s..." dirname)
+ (let ((dired-actual-switches
+ (or switches
+ (dired-replace-in-string "R" "" dired-actual-switches))))
+ (if (equal dirname (car (car (last dired-subdir-alist))))
+ ;; If doing the top level directory of the buffer,
+ ;; redo it as specified in dired-directory.
+ (dired-readin-insert)
+ (dired-insert-directory dirname dired-actual-switches nil nil t)))
+ (message "Reading directory %s...done" dirname)
+ (list begin (point)))))
(defun dired-insert-subdir-doupdate (dirname elt beg-end)
;; Point is at the correct subdir alist position for ELT,
(setq result
(cons (substring str end) result)))
(nreverse result)))
-
+\f
;;; moving by subdirectories
;;;###autoload
;; at either \r or \n after this function succeeds.
(progn (skip-chars-forward "^\r\n")
(point)))))
-
+\f
;;;###autoload
(defun dired-mark-subdir-files ()
"Mark all files except `.' and `..' in current subdirectory.
dir (file-name-directory (directory-file-name dir))))
;;(setq dir (expand-file-name dir))
(or (dired-goto-subdir dir)
- (error "Cannot go up to %s - not in this tree." dir))))
+ (error "Cannot go up to %s - not in this tree" dir))))
;;;###autoload
(defun dired-tree-down ()
(if pos
(goto-char pos)
(error "At the bottom"))))
-
+\f
;;; hiding
(defun dired-unhide-subdir ()
;;;###end dired-ins.el
-
+\f
;; Functions for searching in tags style among marked files.
;;;###autoload
Stops when a match is found.
To continue searching for next match, use command \\[tags-loop-continue]."
(interactive "sSearch marked files (regexp): ")
- (tags-search regexp '(dired-get-marked-files)))
+ (tags-search regexp '(dired-get-marked-files nil nil 'dired-nondirectory-p)))
;;;###autoload
(defun dired-do-query-replace-regexp (from to &optional delimited)
"Do `query-replace-regexp' of FROM with TO, on all marked files.
Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
-If you exit (\\[keyboard-quit] or ESC), you can resume the query replace
+If you exit (\\[keyboard-quit], RET or q), you can resume the query replace
with the command \\[tags-loop-continue]."
(interactive
"sQuery replace in marked files (regexp): \nsQuery replace %s by: \nP")
- (tags-query-replace from to delimited '(dired-get-marked-files)))
-
+ (dolist (file (dired-get-marked-files nil nil 'dired-nondirectory-p))
+ (let ((buffer (get-file-buffer file)))
+ (if (and buffer (with-current-buffer buffer
+ buffer-read-only))
+ (error "File `%s' is visited read-only" file))))
+ (tags-query-replace from to delimited
+ '(dired-get-marked-files nil nil 'dired-nondirectory-p)))
+
+(defun dired-nondirectory-p (file)
+ (not (file-directory-p file)))
+\f
;;;###autoload
(defun dired-show-file-type (file &optional deref-symlinks)
"Print the type of FILE, according to the `file' command.
(interactive (list (dired-get-filename t) current-prefix-arg))
(with-temp-buffer
(if deref-symlinks
- (call-process "file" nil t t "-L" file)
- (call-process "file" nil t t file))
+ (call-process "file" nil t t "-L" "--" file)
+ (call-process "file" nil t t "--" file))
(when (bolp)
(backward-delete-char 1))
- (message (buffer-string))))
+ (message "%s" (buffer-string))))
(provide 'dired-aux)
+;;; arch-tag: 4b508de9-a153-423d-8d3f-a1bbd86f4f60
;;; dired-aux.el ends here