;;; dired-aux.el --- less commonly used parts of dired -*-byte-compile-dynamic: t;-*-
-;; Copyright (C) 1985, 1986, 1992, 1994, 1998, 2000, 2001
+;; Copyright (C) 1985, 1986, 1992, 1994, 1998, 2000, 2001, 2004
;; Free Software Foundation, Inc.
;; Author: Sebastian Kremer <sk@thp.uni-koeln.de>.
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))
(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,
(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"))
;; 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))
'((?\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.
))
(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)))
+ (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
;; 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
;;; 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)
+(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
(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)
+(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"))
(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)
+(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)
+(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)
(defun dired-insert-subdir-doinsert (dirname switches)
;; Insert ls output after point.
;; Return the boundary of the inserted text (as list of BEG and END).
- (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)
- (let ((pt (point)))
- (dired-insert-directory dirname dired-actual-switches nil nil t)
- (goto-char pt))))
- (message "Reading directory %s...done" dirname)
- (list begin (point))))
+ (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,
with the command \\[tags-loop-continue]."
(interactive
"sQuery replace in marked files (regexp): \nsQuery replace %s by: \nP")
+ (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)))
(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