Dired: Duplicate Files (the operation, not how to get rid of 'em) - emacs

Sometimes I want to create a duplicate of a number of files (say, config files), which initially should have the same content as the initial files. Therefore I'd like to be able mark some files in dired and "duplicate" them, this duplication procedure could work similar like the duplication procedure utilised by most file managers, when pasting to the original directory: The file names of the duplicated get "(Copy)" appended (just before the file extension).
I can't seem to find a built-in dired function that does this, maybe someone can help/has already created a function like this?
Help is much appreciated!

There is one function that does what you want: dired-do-copy-regexp
Example of use:
mark the files
M-x dired-do-copy-regexp
\(.*\)\.\(.*\)
\1 (copy).\2
For a file named foo.txt you will be creating another named foo (copy).txt
Note that my first regexp has two groups, and the second regexp references them. You can do much more complicated things, if needed.

Maybe you will want to rename the functions (I didn't come up with better names), maybe some more elaborate formatting, if you wish...
(defcustom dired-keep-marker-version ?V
"Controls marking of versioned files.
If t, versioned files are marked if and as the corresponding original files were.
If a character, copied files are unconditionally marked with that character."
:type '(choice (const :tag "Keep" t)
(character :tag "Mark"))
:group 'dired-mark)
(defun dired-version-file (from to ok-flag)
(dired-handle-overwrite to)
(dired-copy-file-recursive from to ok-flag dired-copy-preserve-time t
dired-recursive-copies))
(defun dired-do-version (&optional arg)
"Search for numeric pattern in file name and create a version of that file
with that number incremented by one, or, in case such file already exists,
will search for a file with the similar name, incrementing the counter each
time by one.
Additionally, if called with prefix argument, will prompt for number format.
The formatting is the same as is used with `format' function."
(interactive "P")
(let ((fn-list (dired-get-marked-files nil nil)))
(dired-create-files
(function dired-version-file) "Version" fn-list
(function
(lambda (from)
(let (new-name (i 0) (fmt (if arg (read-string "Version format: " "%d") "%d")))
(while (or (null new-name) (file-exists-p new-name))
(setq new-name
(if (string-match "^\\([^0-9]*\\)\\([0-9]+\\)\\(.*\\)$" from)
(concat (match-string 1 from)
(format fmt
(+ (string-to-number (match-string 2 from)) (1+ i)))
(match-string 3 from))
(concat from (format (concat "." fmt) i)))
i (1+ i))) new-name)))
dired-keep-marker-version)))
(define-key dired-mode-map (kbd "c") 'dired-do-version)
Also, I've originally used v to bind this function because I don't use dired-view, but you would need to bind that inside direds hook. c just was the first undefined key, so I used it.

In the Dired mode, put cursor on the file you want to duplicate or mark that file, then press "C". You will be prompted for new name.
You can use this feature to copy files between Dired buffers as well. To make it possible put into your init file:
(setq dired-dwim-target t)

Related

Emacs: fix 'changed on disk'/'Reread from disk' when file has not changed [duplicate]

How to disable Emacs from checking the buffer file was changed outside the editor?
Emacs is really trying to help you here. Read the info page on Protection against Simultaneous Editing.
But, if you still want to avoid that message/prompt, you can redefine the function that is doing the prompting:
(defun ask-user-about-supersession-threat (fn)
"blatantly ignore files that changed on disk"
)
(defun ask-user-about-lock (file opponent)
"always grab lock"
t)
The second function there is for when two people are using Emacs to edit the same file, and would provide a similar prompt (but not the one you seemed to refer to in the question).
I'd advise against overriding the two routines, but it's there if you want.
On the off chance global-auto-revert-mode is on, you could disable that. Add this to your .emacs:
(global-auto-revert-mode -1)
You can tell if the mode is on by looking at the variable of the same name:
C-h v global-auto-revert-mode RET
If the value is t, then the mode is on, otherwise it is off.
I have the following in my .emacs. It makes Emacs only ask about really changed files. If a file remains the same bytewise, just its timestamp is updated, as often happens when you switch branches in VCS, this "change" is ignored by Emacs.
;; Ignore modification-time-only changes in files, i.e. ones that
;; don't really change the contents. This happens often with
;; switching between different VC buffers.
(defun update-buffer-modtime-if-byte-identical ()
(let* ((size (buffer-size))
(byte-size (position-bytes size))
(filename buffer-file-name))
(when (and byte-size (<= size 1000000))
(let* ((attributes (file-attributes filename))
(file-size (nth 7 attributes)))
(when (and file-size
(= file-size byte-size)
(string= (buffer-substring-no-properties 1 (1+ size))
(with-temp-buffer
(insert-file-contents filename)
(buffer-string))))
(set-visited-file-modtime (nth 5 attributes))
t)))))
(defun verify-visited-file-modtime--ignore-byte-identical (original &optional buffer)
(or (funcall original buffer)
(with-current-buffer buffer
(update-buffer-modtime-if-byte-identical))))
(advice-add 'verify-visited-file-modtime :around #'verify-visited-file-modtime--ignore-byte-identical)
(defun ask-user-about-supersession-threat--ignore-byte-identical (original &rest arguments)
(unless (update-buffer-modtime-if-byte-identical)
(apply original arguments)))
(advice-add 'ask-user-about-supersession-threat :around #'ask-user-about-supersession-threat--ignore-byte-identical)
In my case I wanted:
(setq revert-without-query '(".*"))
Documentation for revert-without-query:
Specify which files should be reverted without query.
The value is a list of regular expressions.
If the file name matches one of these regular expressions,
then ‘revert-buffer’ reverts the file without querying
if the file has changed on disk and you have not edited the buffer.
I had annoyance with this because every time I switched branches in git, emacs thought all my files had changed.
Revbuffs helps you cope with the symptoms of this. It allows you to cause all your buffers to be reloaded.
You can also try (global-auto-revert-mode) which will automatically revert your files to what's on disk.

Allow dired-do-copy and dired-do-rename to create new dir on the fly

Does anyone have an emacs lisp hack that would allow the creation of a new directory on the fly during dired-do-copy or dired-do-rename? I understand that it can be created prior to running one of these two commands. Extra points for some type of "Are you sure..." prompt.
It look like a case of applying an advice. The question being: what to
advice. Looking at the dired code, it seem that the correct target is
dired-mark-read-file-name that is used to read the destination
file-name. This will work:
(defadvice dired-mark-read-file-name (after rv:dired-create-dir-when-needed (prompt dir op-symbol arg files &optional default) activate)
(when (member op-symbol '(copy move))
(let ((directory-name (if (< 1 (length files))
ad-return-value
(file-name-directory ad-return-value))))
(when (and (not (file-directory-p directory-name))
(y-or-n-p (format "directory %s doesn't exist, create it?" directory-name)))
(make-directory directory-name t)))))
Note that maybe the first when (when (member op-symbol '(copy move))) could be removed for this to apply to more case of file creation in dired. But I'm not sure of when dired-mark-read-file-name is called, So I let this test there to reduce potential unwanted side-effect

Emacs org-mode: textual reference to a file:line

I am using org-mode in Emacs to document my development activities. One of the tasks which I must continuously do by hand is to describe areas of code. Emacs has a very nice Bookmark List: create a bookmark with CTRL-x r m, list them with CTRL-x r l. This is very useful, but is not quite what I need.
Org-mode has the concept of link, and the command org-store-link will record a link to the current position in any file, which can be pasted to the org-file. The problem with this is two-fold:
It is stored as an org-link, and the linked position is not directly visible (just the description).
It is stored in the format file/search, which is not what I want.
I need to have the bookmark in textual form, so that I can copy paste it into org-mode, end edit it if needed, with a simple format like this:
absolute-file-path:line
And this must be obtained from the current point position. The workflow would be as simple as:
Go to the position which I want to record
Call a function: position-to-kill-ring (I would bind this to a keyboard shortcut)
Go to the org-mode buffer.
Yank the position.
Edit if needed (sometimes I need to change absolute paths by relative paths, since my code is in a different location in different machines)
Unfortunately my lisp is non-existent, so I do not know how to do this. Is there a simple solution to my problem?
(defun position-to-kill-ring ()
"Copy to the kill ring a string in the format \"file-name:line-number\"
for the current buffer's file name, and the line number at point."
(interactive)
(kill-new
(format "%s:%d" (buffer-file-name) (save-restriction
(widen) (line-number-at-pos)))))
You want to use the org-create-file-search-functions and org-execute-file-search-functions hooks.
For example, if you need the search you describe for text-mode files, use this:
(add-hook 'org-create-file-search-functions
'(lambda ()
(when (eq major-mode 'text-mode)
(number-to-string (line-number-at-pos)))))
(add-hook 'org-execute-file-search-functions
'(lambda (search-string)
(when (eq major-mode 'text-mode)
(goto-line (string-to-number search-string)))))
Then M-x org-store-link RET will do the right thing (store a line number as the search string) and C-c C-o (i.e. M-x org-open-at-point RET) will open the file and go to this line number.
You can of course check for other modes and/or conditions.
An elisp beginner myself I though of it as a good exercise et voila:
Edit: Rewrote it using the format methode, but I still think not storing it to the kill-ring is less intrusive in my workflow (don't know about you). Also I have added the capability to add column position.
(defvar current-file-reference "" "Global variable to store the current file reference")
(defun store-file-line-and-col ()
"Stores the current file, line and column point is at in a string in format \"file-name:line-number-column-number\". Insert the string using \"insert-file-reference\"."
(interactive)
(setq current-file-reference (format "%s:%d:%d" (buffer-file-name) (line-number-at-pos) (current-column))))
(defun store-file-and-line ()
"Stores the current file and line oint is at in a string in format \"file-name:line-number\". Insert the string using \"insert-file-reference\"."
(interactive)
(setq current-file-reference (format "%s:%d" (buffer-file-name) (line-number-at-pos))))
(defun insert-file-reference ()
"Inserts the value stored for current-file-reference at point."
(interactive)
(if (string= "" current-file-reference)
(message "No current file/line/column set!")
(insert current-file-reference)))
Not tested extensively but working for me. Just hit store-file-and-line or store-file-line-and-col to store current location and insert-file-reference to insert the stored value at point.
BTW, if you want something better than FILE:LINE, you can try to use add-log-current-defun (in add-log.el) which should return the name of the current function.
;; Insert a org link to the function in the next window
(defun insert-org-link-to-func ()
(interactive)
(insert (with-current-buffer (window-buffer (next-window))
(org-make-link-string
(concat "file:" (buffer-file-name)
"::" (number-to-string (line-number-at-pos)))
(which-function)
))))
This func generates link with the function name as the description.
Open two windows, one is the org file and the other is src code.
Then M-x insert-org-link-to-func RET

Gnus: How to save *all* attachments at once?

I would like to save all attachments to an email at once. I therefore set gnus-summary-save-parts-default-mime to ".* /.*". However, when using "X m", I not only get all attachments, but also a file named "nnimap+my.name#googlemail.com/INBOX.2393.1" (referring to the account I'm reading emails from) which contains the signature of the email I received. How can I exclude files of this "type" from being saved on "X m"? In other words: How can I specify the correct regexp for gnus-summary-save-parts-default-mime to prevent this file from being saved, too?
This defadvice will do what you want for the moment by excluding any parts that do not have filenames (in this case that is true of the article itself):
(defadvice gnus-summary-save-parts-1 (around gnus-summary-save-parts-exclude-self activate)
(let ((handle (ad-get-arg 2)))
(unless (and (not (stringp (car handle)))
(not (mm-handle-filename handle)))
ad-do-it)))
I am using Gnus v5.13; if you're also using the same or similar version, let me know if this modified version of gnus-summary-save-parts-1 works for you; you will want to set gnus-summary-save-parts-exclude-article to t. If it works for you, I will submit a patch for it to the Gnus projects.
Note, either use the above defadvice OR use the code below, but do not use both together. The defadvice is an easy quick fix that you can use for the moment. The code below I will submit as a patch to the Gnus project and I only included this here for you to test to see if it works on your system if you are also using Gnus v5.13. If they accept this patch and make it part of a future release then you will not need the defadvice above; instead you'll just be able to customize the gnus-summary-save-parts-exclude-article variable.
(require 'gnus)
(require 'gnus-sum)
(defcustom gnus-summary-save-parts-exclude-article nil
"If non-nil don't save article along with attachments."
:group 'gnus-article-mime
:type 'boolean)
(defun gnus-summary-save-parts-1 (type dir handle reverse)
(if (stringp (car handle))
(mapcar (lambda (h) (gnus-summary-save-parts-1 type dir h reverse))
(cdr handle))
(when (if reverse
(not (string-match type (mm-handle-media-type handle)))
(string-match type (mm-handle-media-type handle)))
(let* ((name (or
(mm-handle-filename handle)
(unless gnus-summary-save-parts-exclude-article
(format "%s.%d.%d" gnus-newsgroup-name
(cdr gnus-article-current)
gnus-summary-save-parts-counter))))
(file (when name
(expand-file-name
(gnus-map-function
mm-file-name-rewrite-functions
(file-name-nondirectory
name))
dir))))
(when file
(incf gnus-summary-save-parts-counter)
(unless (file-exists-p file)
(mm-save-part-to-file handle file)))))))

What is the canonical way to list numbered backup files emacs has created?

I know how to configure emacs to keep numbered backups. I don't know the most canonical way to find those numbered backups.
The emacs function "find-backup-file-name" seems like it is the closest. Its documentation states:
This function computes the file name for a new backup file for filename. It may also propose certain existing backup files for deletion. find-backup-file-name returns a list whose CAR is the name for the new backup file and whose CDR is a list of backup files whose deletion is proposed.
However, this is not what I am looking for. I'm looking for a list of ALL previously created backup files. Here's the code (paraphrased) I have written to accomplish this:
(defvar backup-directory "~/emacs.d/backups/")
(defun get-backup-pattern (file-name)
(concat "*" (replace-regexp-in-string "\/" "\\!" file-name t t) ".~*"))
(butlast
(split-string
(shell-command-to-string
(concat "find "
backup-directory
" -name \""
(get-backup-pattern (buffer-file-name))
"\""))
"\n"))
This method works fine. However, shelling out to "find" seems a like a hack to me; Especially since this method is platform specific.
Is there a built-in method I should use or at least something more idiomatic?
Personally, I don't save backup files in a central folder so I can't provide working code, but if you want to search the contents of a directory, use directory-files.
So here is the solution I've decided on. I went away from using the *nix find command and am using directory-files as suggested.
(defun get-filter-pattern (file-name)
(concat (replace-regexp-in-string "\/" "!" file-name t t)
".~[0-9]*~*$"))
(defun filter (condp lst)
(delq nil
(mapcar (lambda (x) (and (funcall condp x) x)) lst)))
(defun filter-files (backup-directory buffer-file-name)
(mapcar (lambda (backup-name) (concat backup-directory backup-name))
(filter (lambda (backup-name)
(string-match (get-filter-pattern buffer-file-name) backup-name))
(directory-files backup-directory))))
Perhaps this isn't quite as optimized as using find. However, it should be platform independent (ie can use on Windows).