1 ;;; em-unix.el --- UNIX command aliases
3 ;; Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation
5 ;; Author: John Wiegley <johnw@gnu.org>
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
26 (eval-when-compile (require 'esh-maint
))
29 (defgroup eshell-unix nil
30 "This module defines many of the more common UNIX utilities as
31 aliases implemented in Lisp. These include mv, ln, cp, rm, etc. If
32 the user passes arguments which are too complex, or are unrecognized
33 by the Lisp variant, the external version will be called (if
34 available). The only reason not to use them would be because they are
35 usually much slower. But in several cases their tight integration
36 with Eshell makes them more versatile than their traditional cousins
37 \(such as being able to use `kill' to kill Eshell background processes
39 :tag
"UNIX commands in Lisp"
40 :group
'eshell-module
)
44 ;; This file contains implementations of several UNIX command in Emacs
45 ;; Lisp, for several reasons:
47 ;; 1) it makes them available on all platforms where the Lisp
48 ;; functions used are available
50 ;; 2) it makes their functionality accessible and modified by the
53 ;; 3) it allows Eshell to refrain from having to invoke external
54 ;; processes for common operations.
56 (defcustom eshell-unix-load-hook
'(eshell-unix-initialize)
57 "*A list of functions to run when `eshell-unix' is loaded."
61 (defcustom eshell-plain-grep-behavior nil
62 "*If non-nil, standalone \"grep\" commands will behave normally.
63 Standalone in this context means not redirected, and not on the
64 receiving side of a command pipeline."
68 (defcustom eshell-no-grep-available
(not (eshell-search-path "grep"))
69 "*If non-nil, no grep is available on the current machine."
73 (defcustom eshell-plain-diff-behavior nil
74 "*If non-nil, standalone \"diff\" commands will behave normally.
75 Standalone in this context means not redirected, and not on the
76 receiving side of a command pipeline."
80 (defcustom eshell-plain-locate-behavior
(eshell-under-xemacs-p)
81 "*If non-nil, standalone \"locate\" commands will behave normally.
82 Standalone in this context means not redirected, and not on the
83 receiving side of a command pipeline."
87 (defcustom eshell-rm-removes-directories nil
88 "*If non-nil, `rm' will remove directory entries.
89 Otherwise, `rmdir' is required."
93 (defcustom eshell-rm-interactive-query
(= (user-uid) 0)
94 "*If non-nil, `rm' will query before removing anything."
98 (defcustom eshell-mv-interactive-query
(= (user-uid) 0)
99 "*If non-nil, `mv' will query before overwriting anything."
103 (defcustom eshell-mv-overwrite-files t
104 "*If non-nil, `mv' will overwrite files without warning."
108 (defcustom eshell-cp-interactive-query
(= (user-uid) 0)
109 "*If non-nil, `cp' will query before overwriting anything."
113 (defcustom eshell-cp-overwrite-files t
114 "*If non-nil, `cp' will overwrite files without warning."
118 (defcustom eshell-ln-interactive-query
(= (user-uid) 0)
119 "*If non-nil, `ln' will query before overwriting anything."
123 (defcustom eshell-ln-overwrite-files nil
124 "*If non-nil, `ln' will overwrite files without warning."
128 (defcustom eshell-default-target-is-dot nil
129 "*If non-nil, the default destination for cp, mv or ln is `.'."
133 (defcustom eshell-du-prefer-over-ange nil
134 "*Use Eshell's du in ange-ftp remote directories.
135 Otherwise, Emacs will attempt to use rsh to invoke du on the remote machine."
143 (defun eshell-unix-initialize ()
144 "Initialize the UNIX support/emulation code."
145 (when (eshell-using-module 'eshell-cmpl
)
146 (add-hook 'pcomplete-try-first-hook
147 'eshell-complete-host-reference nil t
))
148 (make-local-variable 'eshell-complex-commands
)
149 (setq eshell-complex-commands
150 (append '("grep" "egrep" "fgrep" "agrep" "glimpse" "locate"
151 "cat" "time" "cp" "mv" "make" "du" "diff")
152 eshell-complex-commands
)))
154 (defalias 'eshell
/date
'current-time-string
)
155 (defalias 'eshell
/basename
'file-name-nondirectory
)
156 (defalias 'eshell
/dirname
'file-name-directory
)
164 (defun eshell/man
(&rest args
)
165 "Invoke man, flattening the arguments appropriately."
166 (funcall 'man
(apply 'eshell-flatten-and-stringify args
)))
168 (put 'eshell
/man
'eshell-no-numeric-conversions t
)
170 (defun eshell-remove-entries (path files
&optional top-level
)
171 "From PATH, remove all of the given FILES, perhaps interactively."
173 (if (string-match "\\`\\.\\.?\\'"
174 (file-name-nondirectory (car files
)))
176 (eshell-error "rm: cannot remove `.' or `..'\n"))
177 (if (and (file-directory-p (car files
))
178 (not (file-symlink-p (car files
))))
179 (let ((dir (file-name-as-directory (car files
))))
180 (eshell-remove-entries dir
185 (directory-files dir
)))
187 (eshell-printn (format "rm: removing directory `%s'"
193 (format "rm: remove directory `%s'? "
195 (eshell-funcalln 'delete-directory
(car files
))))
197 (eshell-printn (format "rm: removing file `%s'"
202 (format "rm: remove `%s'? "
204 (eshell-funcalln 'delete-file
(car files
)))))
205 (setq files
(cdr files
))))
207 (defun eshell/rm
(&rest args
)
208 "Implementation of rm in Lisp.
209 This is implemented to call either `delete-file', `kill-buffer',
210 `kill-process', or `unintern', depending on the nature of the
212 (setq args
(eshell-flatten-list args
))
213 (eshell-eval-using-options
215 '((?h
"help" nil nil
"show this usage screen")
216 (?f
"force" nil force-removal
"force removal")
217 (?i
"interactive" nil interactive
"prompt before any removal")
218 (?n
"preview" nil preview
"don't change anything on disk")
219 (?r
"recursive" nil recursive
220 "remove the contents of directories recursively")
221 (?R nil nil recursive
"(same)")
222 (?v
"verbose" nil verbose
"explain what is being done")
226 :usage
"[OPTION]... FILE...
227 Remove (unlink) the FILE(s).")
229 (setq interactive eshell-rm-interactive-query
))
230 (if (and force-removal interactive
)
231 (setq interactive nil
))
233 (let ((entry (if (stringp (car args
))
234 (directory-file-name (car args
))
235 (if (numberp (car args
))
236 (number-to-string (car args
))
241 (eshell-printn (format "rm: removing buffer `%s'" entry
)))
244 (not (y-or-n-p (format "rm: delete buffer `%s'? "
246 (eshell-funcalln 'kill-buffer entry
)))
247 ((eshell-processp entry
)
249 (eshell-printn (format "rm: killing process `%s'" entry
)))
252 (not (y-or-n-p (format "rm: kill process `%s'? "
254 (eshell-funcalln 'kill-process entry
)))
257 (eshell-printn (format "rm: uninterning symbol `%s'" entry
)))
261 (not (y-or-n-p (format "rm: unintern symbol `%s'? "
263 (eshell-funcalln 'unintern entry
)))
265 (if (and (file-directory-p entry
)
266 (not (file-symlink-p entry
)))
268 eshell-rm-removes-directories
)
272 (format "rm: descend into directory `%s'? "
274 (eshell-remove-entries nil
(list entry
) t
))
275 (eshell-error (format "rm: %s: is a directory\n" entry
)))
276 (eshell-remove-entries nil
(list entry
) t
)))))
277 (setq args
(cdr args
)))
280 (put 'eshell
/rm
'eshell-no-numeric-conversions t
)
282 (defun eshell/mkdir
(&rest args
)
283 "Implementation of mkdir in Lisp."
284 (eshell-eval-using-options
286 '((?h
"help" nil nil
"show this usage screen")
289 :usage
"[OPTION] DIRECTORY...
290 Create the DIRECTORY(ies), if they do not already exist.")
292 (eshell-funcalln 'make-directory
(car args
))
293 (setq args
(cdr args
)))
296 (put 'eshell
/mkdir
'eshell-no-numeric-conversions t
)
298 (defun eshell/rmdir
(&rest args
)
299 "Implementation of rmdir in Lisp."
300 (eshell-eval-using-options
302 '((?h
"help" nil nil
"show this usage screen")
305 :usage
"[OPTION] DIRECTORY...
306 Remove the DIRECTORY(ies), if they are empty.")
308 (eshell-funcalln 'delete-directory
(car args
))
309 (setq args
(cdr args
)))
312 (put 'eshell
/rmdir
'eshell-no-numeric-conversions t
)
315 (defvar no-dereference
)
319 (defvar eshell-warn-dot-directories t
)
321 (defun eshell-shuffle-files (command action files target func deep
&rest args
)
322 "Shuffle around some filesystem entries, using FUNC to do the work."
323 (let ((attr-target (eshell-file-attributes target
))
324 (is-dir (or (file-directory-p target
)
325 (and preview
(not eshell-warn-dot-directories
))))
327 (if (and (not preview
) (not is-dir
)
328 (> (length files
) 1))
329 (error "%s: when %s multiple files, last argument must be a directory"
332 (setcar files
(directory-file-name (car files
)))
334 ((string-match "\\`\\.\\.?\\'"
335 (file-name-nondirectory (car files
)))
336 (if eshell-warn-dot-directories
337 (eshell-error (format "%s: %s: omitting directory\n"
338 command
(car files
)))))
340 (or (not (eshell-under-windows-p))
341 (eq system-type
'ms-dos
))
342 (setq attr
(eshell-file-attributes (car files
)))
343 (nth 10 attr-target
) (nth 10 attr
)
344 ;; Use equal, not -, since the inode and the device could
346 (equal (nth 10 attr-target
) (nth 10 attr
))
347 (nth 11 attr-target
) (nth 11 attr
)
348 (equal (nth 11 attr-target
) (nth 11 attr
)))
349 (eshell-error (format "%s: `%s' and `%s' are the same file\n"
350 command
(car files
) target
)))
352 (let ((source (car files
))
355 (file-name-nondirectory (car files
)) target
)
358 (if (and (file-directory-p source
)
359 (or (not no-dereference
)
360 (not (file-symlink-p source
)))
361 (not (memq func
'(make-symbolic-link
363 (if (and (eq func
'copy-file
)
365 (eshell-error (format "%s: %s: omitting directory\n"
366 command
(car files
)))
367 (let (eshell-warn-dot-directories)
369 (eq func
'rename-file
)
370 ;; Use equal, since the device might be a
372 (equal (nth 11 (eshell-file-attributes
375 (expand-file-name source
)))))
376 (nth 11 (eshell-file-attributes
379 (expand-file-name target
)))))))
380 (apply 'eshell-funcalln func source target args
)
381 (unless (file-directory-p target
)
384 (format "%s: making directory %s"
387 (eshell-funcalln 'make-directory target
)))
388 (apply 'eshell-shuffle-files
393 (concat source
"/" file
)))
394 (directory-files source
))
396 (when (eq func
'rename-file
)
399 (format "%s: deleting directory %s"
402 (eshell-funcalln 'delete-directory source
))))))
404 (eshell-printn (format "%s: %s -> %s" command
407 (if (and no-dereference
408 (setq link
(file-symlink-p source
)))
410 (apply 'eshell-funcalln
'make-symbolic-link
412 (if (eq func
'rename-file
)
413 (if (and (file-directory-p source
)
414 (not (file-symlink-p source
)))
415 (eshell-funcalln 'delete-directory source
)
416 (eshell-funcalln 'delete-file source
))))
417 (apply 'eshell-funcalln func source target args
)))))))
418 (setq files
(cdr files
)))))
420 (defun eshell-shorthand-tar-command (command args
)
421 "Rewrite `cp -v dir a.tar.gz' to `tar cvzf a.tar.gz dir'."
422 (let* ((archive (car (last args
)))
424 (cond ((string-match "z2" archive
) "If")
425 ((string-match "gz" archive
) "zf")
426 ((string-match "\\(az\\|Z\\)" archive
) "Zf")
428 (if (file-exists-p archive
)
429 (setq tar-args
(concat "u" tar-args
))
430 (setq tar-args
(concat "c" tar-args
)))
432 (setq tar-args
(concat "v" tar-args
)))
433 (if (equal command
"mv")
434 (setq tar-args
(concat "--remove-files -" tar-args
)))
435 ;; truncate the archive name from the arguments
436 (setcdr (last args
2) nil
)
437 (throw 'eshell-replace-command
438 (eshell-parse-command
439 (format "tar %s %s" tar-args archive
) args
))))
441 ;; this is to avoid duplicating code...
442 (defmacro eshell-mvcpln-template
(command action func query-var
443 force-var
&optional preserve
)
444 `(let ((len (length args
)))
446 (and (= len
1) (null eshell-default-target-is-dot
)))
447 (error "%s: missing destination file or directory" ,command
))
450 (setq args
(eshell-stringify-list (eshell-flatten-list args
)))
451 (if (and ,(not (equal command
"ln"))
452 (string-match eshell-tar-regexp
(car (last args
)))
453 (or (> (length args
) 2)
454 (and (file-directory-p (car args
))
455 (or (not no-dereference
)
456 (not (file-symlink-p (car args
)))))))
457 (eshell-shorthand-tar-command ,command args
)
458 (let ((target (car (last args
)))
460 (setcdr (last args
2) nil
)
461 (eshell-shuffle-files
462 ,command
,action args target
,func nil
464 `((if (and (or interactive
467 1 (or force
,force-var
)))
472 (defun eshell/mv
(&rest args
)
473 "Implementation of mv in Lisp."
474 (eshell-eval-using-options
476 '((?f
"force" nil force
477 "remove existing destinations, never prompt")
478 (?i
"interactive" nil interactive
479 "request confirmation if target already exists")
480 (?n
"preview" nil preview
481 "don't change anything on disk")
482 (?v
"verbose" nil verbose
483 "explain what is being done")
484 (nil "help" nil nil
"show this usage screen")
488 :usage
"[OPTION]... SOURCE DEST
489 or: mv [OPTION]... SOURCE... DIRECTORY
490 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
491 \[OPTION] DIRECTORY...")
492 (let ((no-dereference t
))
493 (eshell-mvcpln-template "mv" "moving" 'rename-file
494 eshell-mv-interactive-query
495 eshell-mv-overwrite-files
))))
497 (put 'eshell
/mv
'eshell-no-numeric-conversions t
)
499 (defun eshell/cp
(&rest args
)
500 "Implementation of cp in Lisp."
501 (eshell-eval-using-options
503 '((?a
"archive" nil archive
505 (?d
"no-dereference" nil no-dereference
507 (?f
"force" nil force
508 "remove existing destinations, never prompt")
509 (?i
"interactive" nil interactive
510 "request confirmation if target already exists")
511 (?n
"preview" nil preview
512 "don't change anything on disk")
513 (?p
"preserve" nil preserve
514 "preserve file attributes if possible")
515 (?R
"recursive" nil recursive
516 "copy directories recursively")
517 (?v
"verbose" nil verbose
518 "explain what is being done")
519 (nil "help" nil nil
"show this usage screen")
523 :usage
"[OPTION]... SOURCE DEST
524 or: cp [OPTION]... SOURCE... DIRECTORY
525 Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.")
527 (setq preserve t no-dereference t recursive t
))
528 (eshell-mvcpln-template "cp" "copying" 'copy-file
529 eshell-cp-interactive-query
530 eshell-cp-overwrite-files preserve
)))
532 (put 'eshell
/cp
'eshell-no-numeric-conversions t
)
534 (defun eshell/ln
(&rest args
)
535 "Implementation of ln in Lisp."
536 (eshell-eval-using-options
538 '((?h
"help" nil nil
"show this usage screen")
539 (?s
"symbolic" nil symbolic
540 "make symbolic links instead of hard links")
541 (?i
"interactive" nil interactive
542 "request confirmation if target already exists")
543 (?f
"force" nil force
"remove existing destinations, never prompt")
544 (?n
"preview" nil preview
545 "don't change anything on disk")
546 (?v
"verbose" nil verbose
"explain what is being done")
550 :usage
"[OPTION]... TARGET [LINK_NAME]
551 or: ln [OPTION]... TARGET... DIRECTORY
552 Create a link to the specified TARGET with optional LINK_NAME. If there is
553 more than one TARGET, the last argument must be a directory; create links
554 in DIRECTORY to each TARGET. Create hard links by default, symbolic links
555 with '--symbolic'. When creating hard links, each TARGET must exist.")
556 (let ((no-dereference t
))
557 (eshell-mvcpln-template "ln" "linking"
561 eshell-ln-interactive-query
562 eshell-ln-overwrite-files
))))
564 (put 'eshell
/ln
'eshell-no-numeric-conversions t
)
566 (defun eshell/cat
(&rest args
)
567 "Implementation of cat in Lisp.
568 If in a pipeline, or the file is not a regular file, directory or
569 symlink, then revert to the system's definition of cat."
570 (setq args
(eshell-stringify-list (eshell-flatten-list args
)))
571 (if (or eshell-in-pipeline-p
574 (unless (or (and (stringp arg
)
576 (eq (aref arg
0) ?-
))
577 (let ((attrs (eshell-file-attributes arg
)))
578 (and attrs
(memq (aref (nth 8 attrs
) 0)
580 (throw 'special t
)))))
581 (let ((ext-cat (eshell-search-path "cat")))
583 (throw 'eshell-replace-command
584 (eshell-parse-command ext-cat args
))
585 (if eshell-in-pipeline-p
586 (error "Eshell's `cat' does not work in pipelines")
587 (error "Eshell's `cat' cannot display one of the files given"))))
588 (eshell-init-print-buffer)
589 (eshell-eval-using-options
591 '((?h
"help" nil nil
"show this usage screen")
594 :usage
"[OPTION] FILE...
595 Concatenate FILE(s), or standard input, to standard output.")
596 (eshell-for file args
597 (if (string= file
"-")
598 (throw 'eshell-external
599 (eshell-external-command "cat" args
))))
600 (let ((curbuf (current-buffer)))
601 (eshell-for file args
603 (insert-file-contents file
)
604 (goto-char (point-min))
606 (let ((str (buffer-substring
607 (point) (min (1+ (line-end-position))
609 (with-current-buffer curbuf
610 (eshell-buffered-print str
)))
613 ;; if the file does not end in a newline, do not emit one
614 (setq eshell-ensure-newline-p nil
))))
616 (put 'eshell
/cat
'eshell-no-numeric-conversions t
)
618 ;; special front-end functions for compilation-mode buffers
620 (defun eshell/make
(&rest args
)
621 "Use `compile' to do background makes."
622 (if (and eshell-current-subjob-p
623 (eshell-interactive-output-p))
624 (let ((compilation-process-setup-function
626 (list 'setq
'process-environment
627 (list 'quote
(eshell-copy-environment))))))
628 (compile (concat "make " (eshell-flatten-and-stringify args
))))
629 (throw 'eshell-replace-command
630 (eshell-parse-command "*make" (eshell-stringify-list
631 (eshell-flatten-list args
))))))
633 (put 'eshell
/make
'eshell-no-numeric-conversions t
)
635 (defun eshell-occur-mode-goto-occurrence ()
636 "Go to the occurrence the current line describes."
638 (let ((pos (occur-mode-find-occurrence)))
639 (pop-to-buffer (marker-buffer pos
))
640 (goto-char (marker-position pos
))))
642 (defun eshell-occur-mode-mouse-goto (event)
643 "In Occur mode, go to the occurrence whose line you click on."
647 (set-buffer (window-buffer (posn-window (event-end event
))))
649 (goto-char (posn-point (event-end event
)))
650 (setq pos
(occur-mode-find-occurrence))))
651 (pop-to-buffer (marker-buffer pos
))
652 (goto-char (marker-position pos
))))
654 (defun eshell-poor-mans-grep (args)
655 "A poor version of grep that opens every file and uses `occur'.
656 This eats up memory, since it leaves the buffers open (to speed future
657 searches), and it's very slow. But, if your system has no grep
659 (save-selected-window
660 (let ((default-dir default-directory
))
661 (with-current-buffer (get-buffer-create "*grep*")
662 (let ((inhibit-read-only t
)
663 (default-directory default-dir
))
666 (let ((files (eshell-stringify-list
667 (eshell-flatten-list (cdr args
))))
668 (inhibit-redisplay t
)
671 (if (get-buffer "*Occur*")
672 (kill-buffer (get-buffer "*Occur*")))
675 (with-current-buffer (find-file-noselect (car files
))
679 (if (get-buffer "*Occur*")
680 (with-current-buffer (get-buffer "*Occur*")
681 (setq string
(buffer-string))
682 (kill-buffer (current-buffer)))))
683 (if string
(insert string
))
685 files
(cdr files
)))))
686 (local-set-key [mouse-2
] 'eshell-occur-mode-mouse-goto
)
687 (local-set-key [(control ?c
) (control ?c
)]
688 'eshell-occur-mode-goto-occurrence
)
689 (local-set-key [(control ?m
)]
690 'eshell-occur-mode-goto-occurrence
)
691 (local-set-key [return] 'eshell-occur-mode-goto-occurrence)
692 (pop-to-buffer (current-buffer) t)
693 (goto-char (point-min))
694 (resize-temp-buffer-window))))))
696 (defun eshell-grep (command args &optional maybe-use-occur)
697 "Generic service function for the various grep aliases.
698 It calls Emacs' grep utility if the command is not redirecting output,
699 and if it's not part of a command pipeline. Otherwise, it calls the
701 (if (and maybe-use-occur eshell-no-grep-available)
702 (eshell-poor-mans-grep args)
703 (if (or eshell-plain-grep-behavior
704 (not (and (eshell-interactive-output-p)
705 (not eshell-in-pipeline-p)
706 (not eshell-in-subcommand-p))))
707 (throw 'eshell-replace-command
708 (eshell-parse-command (concat "*" command)
709 (eshell-stringify-list
710 (eshell-flatten-list args))))
711 (let* ((args (mapconcat 'identity
712 (mapcar 'shell-quote-argument
713 (eshell-stringify-list
714 (eshell-flatten-list args)))
717 (set-text-properties 0 (length args)
719 (format "%s -n %s" command args)))
720 compilation-scroll-output)
723 (defun eshell/grep (&rest args)
724 "Use Emacs grep facility instead of calling external grep."
725 (eshell-grep "grep" args t))
727 (defun eshell/egrep (&rest args)
728 "Use Emacs grep facility instead of calling external egrep."
729 (eshell-grep "egrep" args t))
731 (defun eshell/fgrep (&rest args)
732 "Use Emacs grep facility instead of calling external fgrep."
733 (eshell-grep "fgrep" args t))
735 (defun eshell/agrep (&rest args)
736 "Use Emacs grep facility instead of calling external agrep."
737 (eshell-grep "agrep" args))
739 (defun eshell/glimpse (&rest args)
740 "Use Emacs grep facility instead of calling external glimpse."
742 (eshell-grep "glimpse" (append '("-z" "-y") args))))
744 ;; completions rules for some common UNIX commands
746 (defsubst eshell-complete-hostname ()
747 "Complete a command that wants a hostname for an argument."
748 (pcomplete-here (eshell-read-host-names)))
750 (defun eshell-complete-host-reference ()
751 "If there is a host reference, complete it."
752 (let ((arg (pcomplete-actual-arg))
754 (when (setq index (string-match "@[a-z.]*\\'" arg))
755 (setq pcomplete-stub (substring arg (1+ index))
756 pcomplete-last-completion-raw t)
757 (throw 'pcomplete-completions (eshell-read-host-names)))))
759 (defalias 'pcomplete/ftp 'eshell-complete-hostname)
760 (defalias 'pcomplete/ncftp 'eshell-complete-hostname)
761 (defalias 'pcomplete/ping 'eshell-complete-hostname)
762 (defalias 'pcomplete/rlogin 'eshell-complete-hostname)
764 (defun pcomplete/telnet ()
765 (require 'pcmpl-unix)
766 (pcomplete-opt "xl(pcmpl-unix-user-names)")
767 (eshell-complete-hostname))
769 (defun pcomplete/rsh ()
770 "Complete `rsh', which, after the user and hostname, is like xargs."
771 (require 'pcmpl-unix)
772 (pcomplete-opt "l(pcmpl-unix-user-names)")
773 (eshell-complete-hostname)
774 (pcomplete-here (funcall pcomplete-command-completion-function))
775 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
776 pcomplete-default-completion-function)))
778 (defalias 'pcomplete/ssh 'pcomplete/rsh)
783 (defvar dereference-links)
785 (defvar human-readable)
787 (defvar only-one-filesystem)
790 (defsubst eshell-du-size-string (size)
791 (let* ((str (eshell-printable-size size human-readable block-size t))
793 (concat str (if (< len 8)
794 (make-string (- 8 len) ? )))))
796 (defun eshell-du-sum-directory (path depth)
797 "Summarize PATH, and its member directories."
798 (let ((entries (eshell-directory-files-and-attributes path))
801 (unless (string-match "\\`\\.\\.?\\'" (caar entries))
802 (let* ((entry (concat path (char-to-string directory-sep-char)
804 (symlink (and (stringp (cadr (car entries)))
805 (cadr (car entries)))))
806 (unless (or (and symlink (not dereference-links))
807 (and only-one-filesystem
808 (/= only-one-filesystem
809 (nth 12 (car entries)))))
811 (setq entry symlink))
814 (if (eq t (cadr (car entries)))
815 (eshell-du-sum-directory entry (1+ depth))
816 (let ((file-size (nth 8 (car entries))))
821 (concat (eshell-du-size-string file-size)
822 entry "\n")))))))))))
823 (setq entries (cdr entries)))
824 (if (or (not max-depth)
827 (eshell-print (concat (eshell-du-size-string size)
828 (directory-file-name path) "\n")))
831 (defun eshell/du (&rest args)
832 "Implementation of \"du\" in Lisp, passing ARGS."
834 (eshell-stringify-list (eshell-flatten-list args))
836 (let ((ext-du (eshell-search-path "du")))
838 (not (catch 'have-ange-path
840 (if (eq (find-file-name-handler (expand-file-name arg)
842 'ange-ftp-hook-function)
843 (throw 'have-ange-path t))))))
844 (throw 'eshell-replace-command
845 (eshell-parse-command ext-du args))
846 (eshell-eval-using-options
848 '((?a "all" nil show-all
849 "write counts for all files, not just directories")
850 (nil "block-size" t block-size
851 "use SIZE-byte blocks (i.e., --block-size SIZE)")
852 (?b "bytes" nil by-bytes
853 "print size in bytes")
854 (?c "total" nil grand-total
855 "produce a grand total")
856 (?d "max-depth" t max-depth
857 "display data only this many levels of data")
858 (?h "human-readable" 1024 human-readable
859 "print sizes in human readable format")
860 (?H "is" 1000 human-readable
861 "likewise, but use powers of 1000 not 1024")
862 (?k "kilobytes" 1024 block-size
863 "like --block-size 1024")
864 (?L "dereference" nil dereference-links
865 "dereference all symbolic links")
866 (?m "megabytes" 1048576 block-size
867 "like --block-size 1048576")
868 (?s "summarize" 0 max-depth
869 "display only a total for each argument")
870 (?x "one-file-system" nil only-one-filesystem
871 "skip directories on different filesystems")
873 "show this usage screen")
875 :usage "[OPTION]... FILE...
876 Summarize disk usage of each FILE, recursively for directories.")
878 (setq block-size (or block-size 1024)))
879 (if (and max-depth (stringp max-depth))
880 (setq max-depth (string-to-int max-depth)))
881 ;; filesystem support means nothing under Windows
882 (if (eshell-under-windows-p)
883 (setq only-one-filesystem nil))
884 (let ((size 0.0) ange-cache)
886 (if only-one-filesystem
887 (setq only-one-filesystem
888 (nth 11 (eshell-file-attributes
889 (file-name-as-directory (car args))))))
890 (setq size (+ size (eshell-du-sum-directory
891 (directory-file-name (car args)) 0)))
892 (setq args (cdr args)))
894 (eshell-print (concat (eshell-du-size-string size)
897 (defvar eshell-time-start nil)
899 (defun eshell-show-elapsed-time ()
900 (let ((elapsed (format "%.3f secs\n"
901 (- (eshell-time-to-seconds (current-time))
902 eshell-time-start))))
903 (set-text-properties 0 (length elapsed) '(face bold) elapsed)
904 (eshell-interactive-print elapsed))
905 (remove-hook 'eshell-post-command-hook 'eshell-show-elapsed-time t))
907 (defun eshell/time (&rest args)
908 "Implementation of \"time\" in Lisp."
909 (let ((time-args (copy-alist args))
912 (while (and continue args)
913 (if (not (string-match "^-" (car args)))
916 (setcdr last-arg nil)
921 (eshell-eval-using-options
923 '((?h "help" nil nil "show this usage screen")
927 Show wall-clock time elapsed during execution of COMMAND.")
928 (setq eshell-time-start (eshell-time-to-seconds (current-time)))
929 (add-hook 'eshell-post-command-hook 'eshell-show-elapsed-time nil t)
931 (throw 'eshell-replace-command
932 (eshell-parse-command (car time-args) (cdr time-args))))))
934 (defalias 'eshell/whoami 'user-login-name)
936 (defvar eshell-diff-window-config nil)
938 (defun eshell-diff-quit ()
939 "Restore the window configuration previous to diff'ing."
941 (if eshell-diff-window-config
942 (set-window-configuration eshell-diff-window-config)))
944 (defun eshell/diff (&rest args)
945 "Alias \"diff\" to call Emacs `diff' function."
946 (let ((orig-args (eshell-stringify-list (eshell-flatten-list args))))
947 (if (or eshell-plain-diff-behavior
948 (not (and (eshell-interactive-output-p)
949 (not eshell-in-pipeline-p)
950 (not eshell-in-subcommand-p))))
951 (throw 'eshell-replace-command
952 (eshell-parse-command "*diff" orig-args))
953 (setq args (copy-sequence orig-args))
954 (if (< (length args) 2)
955 (throw 'eshell-replace-command
956 (eshell-parse-command "*diff" orig-args)))
957 (let ((old (car (last args 2)))
958 (new (car (last args)))
959 (config (current-window-configuration)))
960 (if (= (length args) 2)
962 (setcdr (last args 3) nil))
965 (diff old new (eshell-flatten-and-stringify args))
967 (throw 'eshell-replace-command
968 (eshell-parse-command "*diff" orig-args))))
969 (when (fboundp 'diff-mode)
970 (make-local-variable 'compilation-finish-functions)
972 'compilation-finish-functions
974 (with-current-buffer buff
976 (set (make-local-variable 'eshell-diff-window-config)
978 (local-set-key [?q] 'eshell-diff-quit)
979 (if (fboundp 'turn-on-font-lock-if-enabled)
980 (turn-on-font-lock-if-enabled))
981 (goto-char (point-min))))))
982 (pop-to-buffer (current-buffer))))))
985 (put 'eshell/diff 'eshell-no-numeric-conversions t)
987 (defun eshell/locate (&rest args)
988 "Alias \"locate\" to call Emacs `locate' function."
989 (if (or eshell-plain-locate-behavior
990 (not (and (eshell-interactive-output-p)
991 (not eshell-in-pipeline-p)
992 (not eshell-in-subcommand-p)))
993 (and (stringp (car args))
994 (string-match "^-" (car args))))
995 (throw 'eshell-replace-command
996 (eshell-parse-command "*locate" (eshell-stringify-list
997 (eshell-flatten-list args))))
998 (save-selected-window
999 (let ((locate-history-list (list (car args))))
1000 (locate-with-filter (car args) (cadr args))))))
1002 (put 'eshell/locate 'eshell-no-numeric-conversions t)
1004 (defun eshell/occur (&rest args)
1005 "Alias \"occur\" to call Emacs `occur' function."
1006 (let ((inhibit-read-only t))
1007 (if (> (length args) 2)
1008 (error "usage: occur: (REGEXP &optional NLINES)")
1009 (apply 'occur args))))
1011 (put 'eshell/occur 'eshell-no-numeric-conversions t)
1015 ;;; arch-tag: 2462edd2-a76a-4cf2-897d-92e9a82ac1c9
1016 ;;; em-unix.el ends here