1

I got a problem for an advice-add inside Dired's copy function (dired-do-copy).
My object is it to filter all marked files/directories in Dired, which should be copied to an other location within Dired. The filter is simply finding all files including a recursive search in marked directories which are greater in files size than a given treasure/limit value. Only the files, which are smaller in file size than this value should be copied by Dired in the normal way. The other greater one's I would like to copy by a separate function (not mentioned here) in an asynchronous process.

My problem is that the ordinary list, where all files are mentioned with its absolute path can not be given to the dired-do-copy function. Every time I copy within Dired with activated following advice-add

I get this error:

apply: Wrong number of arguments: (0 . 1), 2

Please see my advice-add function:

 (defun filter-files-*-marked-greater-than-*LIMIT-VALUE-COPYING-TRASH* (old-function  &rest arguments)

   ;; for testing and debugging
   (message "Here are all given arguments %S" arguments )

   (let* (
         (list-of-marked-files-first (dired-get-marked-files))
         (list-of-marked-files-second list-of-marked-files-first)
         (list-of-all-files-within-all-marked-directories nil)
         list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*
         list-of-all-marked-files-greater-than*LIMIT-VALUE-COPYING-TRASH*
         number-of-marked-files
         singleFile
         singleFileOrDirectory
         fileSize
         fileSizeKB
         fileSizeMB
         numberOfFilesToCopy
         numberOfFilesToCopy-greater-than-*LIMIT-VALUE-COPYING-TRASH*
         newArgsList
         )

     ;; filter all directories from marked list
     (while list-of-marked-files-first
       (setq singleFileOrDirectory (pop list-of-marked-files-first))
       ;; if a directory then recursivly collect all files inside this directory
       ;; and subdirectories and add them to
       ;; list-of-all-files-within-all-marked-directories
       ;; directory check follows --> first condition is also true if it is
       ;; a symlink (see file-attributes)
       (if (and (nth 0 (file-attributes singleFileOrDirectory)) (booleanp (nth 0 (file-attributes singleFileOrDirectory))))
          (progn
            (setq list-of-all-files-within-all-marked-directories (append list-of-all-files-within-all-marked-directories (directory-files-recursively singleFileOrDirectory ".*" t)))
            ;; delete the directory from the list-of-marked-files-second
            (delete singleFileOrDirectory list-of-marked-files-second))))
     ;; put these to lists together, where both have only files listed inside
     (setq list-of-marked-files-second (append list-of-marked-files-second list-of-all-files-within-all-marked-directories))
     ;; determine elements in list-of-marked-files-second, where only files and
     ;; no directories reside inside
     (setq number-of-marked-files (length list-of-marked-files-second))
     ;; true as long any marked files exist AND not more than one file was found,
     ;; which is larger than *LIMIT-VALUE-COPYING-TRASH*
     (while  list-of-marked-files-second
       (setq singleFile (pop list-of-marked-files-second))
       ;; check for directory is obsolete, because there shouldn't be any
       ;; directories inside
       (if (not (nth 0 (file-attributes singleFile)))
          (progn
            (setq fileSize (nth 7 (file-attributes singleFile)))
            (setq fileSizeKB (/ fileSize 1024))
            (setq fileSizeMB (/ fileSizeKB 1024))

            (if (> fileSizeMB *LIMIT-VALUE-COPYING-TRASH*)
            (progn
              (add-to-list 'list-of-all-marked-files-greater-than*LIMIT-VALUE-COPYING-TRASH* singleFile)
              ;; remove file greater than *LIMIT-VALUE-COPYING-TRASH* from
              ;; given arguments in ordwer to copy it with rsync
              ;; (delete singleFile arguments)

              )
              (add-to-list 'list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH* singleFile)
              )
            )
        )
       )

     ;; for testing and debugging
     (setq  numberOfFilesToCopy-greater-than-*LIMIT-VALUE-COPYING-TRASH* (length list-of-all-marked-files-greater-than*LIMIT-VALUE-COPYING-TRASH*))
     ;; for testing and debugging
     (setq numberOfFilesToCopy (length list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*))
     ;; for testing and debugging
     (setq numberOfArgsElements (length arguments))

     ;; for testing and debugging
     (if (sequencep arguments)
        (message (propertize (concat "arguments is sequencep" ".") 'face '(:foreground "cyan"))))

     ;; for testing and debugging
     (if (sequencep list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*)
        (message (propertize (concat "list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH* is sequencep" ".") 'face '(:foreground "cyan"))))



     ;; (while list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*
     ;;   (setq runner (pop list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*))
     ;;   (message (propertize runner 'face '(:foreground "cyan"))))

     ;; for testing and debugging
     (message "Here are all arguments end the end of advice-add function %S" arguments)

     ;; for testing and debugging
     (print arguments)


     ;; for testing and debugging
     (add-to-list 'newArgsList list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*)

     ;; testing: when directory is given for copying
     ;; for testing and debugging
     (message (propertize (concat (number-to-string numberOfFilesToCopy) " Number of arguments given to dired-do-copy ") 'face '(:foreground "cyan")))
     (message (propertize (concat (number-to-string numberOfFilesToCopy-greater-than-*LIMIT-VALUE-COPYING-TRASH*) " Number of found files greater than 10MB.") 'face '(:foreground "cyan")))
     (message (propertize (concat (number-to-string numberOfArgsElements) " Number of elements in Args.") 'face '(:foreground "cyan")))

     ;; (setq arguments list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*)

     ;; !!!!!!!replacing
     ;; (apply old-function newArgsList)
     ;; (apply old-function )

     ;; with
     ;; (apply dired-do-copy newArgsList)
     ;; (apply dired-do-copy 'list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*)


     ;; !!!!!!!with following line do not work
     (apply old-function list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*)
     ;; !!!!!!!

     )
   )

And here my final advice-add declaration.

(advice-add 'dired-do-copy :around #'filter-files-*-marked-greater-than-*LIMIT-VALUE-COPYING-TRASH*)
1
  • 1
    Please consider reformatting your code a bit, so the lines are not so long, requiring scrolling.)
    – Drew
    Jan 17, 2019 at 16:45

2 Answers 2

1

Your error is that you use apply but the last arg you pass to it is not a list of arguments but a single argument which is a list of files.

IOW, if you want to pass that list of files as the first arg to the old-function, you should use (funcall old-function list-of-files-smaller-than-*LIMIT-VALUE-COPYING-TRASH*) (btw, I wonder why stopped at less than 60 chars for that variable name. Memory is plentiful nowadays so you can use even much longer variable names).

But in any case, dired-do-copy does not accept a list of files as argument. So you likely need to place your advice elsewhere, e.g. on dired-copy-file.

One more thing: please don't use add-to-list on local variables (it doesn't work in lexically scoped code), use push (if you know the element isn't already in the list) or cl-pushnew (from the cl-lib package bundled with Emacs) instead.

0

@Stefan has answered your question about advice, but this might help you accomplish your aim in another way.


If you use Dired+ then you can use command diredp-mark-sexp-recursive, bound to M-+ M-( and M-+ * ( by default, to mark all files in the current directory, and all files in its marked subdirectories, and the same thing recursively, when those files satisfy a condition that you specify interactively. This is a recursively acting version of standard Dired command dired-mark-sexp (which acts only in the current Dired listing, but which could contain inserted subdirs).

(All commands that act recursively on marked subdirs (or all subdirs) are on prefix key M-+.)

You are prompted for a Lisp sexp that specifies the condition to satisfy. In this case you would just enter this:

(> size *LIMIT-VALUE-COPYING-TRASH*)

or more probably this, if you don't want to also mark directories:

(and (not (file-directory-p name)) (> size *LIMIT-VALUE-COPYING-TRASH*))

That's not hard to do, but where name and size come from - what are they? These are predefined variables for use with such commands. Their values are taken from each file tested, in turn. (See the doc string, below.)

The same command is menu item If... of submenu Here and Below of menu-bar menu Marks.

C-h f diredp-mark-sexp-recursive says:

diredp-mark-sexp-recursive is an interactive Lisp function in [`dired+.el’.

(diredp-mark-sexp-recursive PREDICATE &optional ARG DETAILS)

Mark files here and below for which PREDICATE returns non-nil.

Like diredp-mark-sexp’, but act recursively on subdirs.

  • A non-negative prefix arg means to unmark those files instead.

  • A non-positive prefix arg means to ignore subdir markings and act instead on ALL subdirs. That is, mark all in this directory and all descendant directories.

PREDICATE is a lisp sexp that can refer to the following symbols as variables:

  • mode [string] file permission bits, e.g. "-rw-r--r--"
  • nlink` [integer] number of links to file
  • size [integer] file size in bytes
  • uid [string] owner
  • gid [string] group (If the gid is not displayed by ls, this will still be set (to the same as uid))
  • time [string] the time that ls displays, e.g. "Feb 12 14:17"
  • name [string] the name of the file
  • sym [string] if file is a symbolic link, the linked-to name, else ""
  • inode [integer] the inode of the file (only for ls -i output)
  • blks [integer] the size of the file for ls -s output (ususally in blocks or, with -k, in Kbytes)

Examples:

  • Mark zero-length files: (equal 0 size)
  • Mark files last modified on Feb 2: (string-match "Feb 2" time)
  • Mark uncompiled Emacs Lisp files (.el’ file without a .elc file): First, Dired just the source files: dired *.el. Then, use M-x diredp-mark-sexp-recursive with this sexp: (not (file-exists-p (concat name "c")))

There’s an ambiguity when a single integer not followed by a unit prefix precedes the file mode: It is then parsed as inode number and not as block size (this always works for GNU coreutils ls).

Another limitation is that the uid field is needed for the function to work correctly. In particular, the field is not present for some values of ls-lisp-emulation.

This function operates only on the Dired buffer content. It does not refer at all to the underlying file system. Contrast this with find-dired, which might be preferable for the task at hand.

When called from Lisp, DETAILS is passed to diredp-get-subdirs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.