1 ;;; em-unix.el --- UNIX command aliases
3 ;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
4 ;; 2008, 2009, 2010 Free Software Foundation, Inc.
6 ;; Author: John Wiegley <johnw@gnu.org>
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
25 ;; This file contains implementations of several UNIX command in Emacs
26 ;; Lisp, for several reasons:
28 ;; 1) it makes them available on all platforms where the Lisp
29 ;; functions used are available
31 ;; 2) it makes their functionality accessible and modified by the
34 ;; 3) it allows Eshell to refrain from having to invoke external
35 ;; processes for common operations.
44 (eshell-defgroup eshell-unix nil
45 "This module defines many of the more common UNIX utilities as
46 aliases implemented in Lisp. These include mv, ln, cp, rm, etc. If
47 the user passes arguments which are too complex, or are unrecognized
48 by the Lisp variant, the external version will be called (if
49 available). The only reason not to use them would be because they are
50 usually much slower. But in several cases their tight integration
51 with Eshell makes them more versatile than their traditional cousins
52 \(such as being able to use `kill' to kill Eshell background processes
54 :tag
"UNIX commands in Lisp"
55 :group
'eshell-module
)
57 (defcustom eshell-unix-load-hook
'(eshell-unix-initialize)
58 "*A list of functions to run when `eshell-unix' is loaded."
62 (defcustom eshell-plain-grep-behavior nil
63 "*If non-nil, standalone \"grep\" commands will behave normally.
64 Standalone in this context means not redirected, and not on the
65 receiving side of a command pipeline."
69 (defcustom eshell-no-grep-available
(not (eshell-search-path "grep"))
70 "*If non-nil, no grep is available on the current machine."
74 (defcustom eshell-plain-diff-behavior nil
75 "*If non-nil, standalone \"diff\" commands will behave normally.
76 Standalone in this context means not redirected, and not on the
77 receiving side of a command pipeline."
81 (defcustom eshell-plain-locate-behavior
(featurep 'xemacs
)
82 "*If non-nil, standalone \"locate\" commands will behave normally.
83 Standalone in this context means not redirected, and not on the
84 receiving side of a command pipeline."
88 (defcustom eshell-rm-removes-directories nil
89 "*If non-nil, `rm' will remove directory entries.
90 Otherwise, `rmdir' is required."
94 (defcustom eshell-rm-interactive-query
(= (user-uid) 0)
95 "*If non-nil, `rm' will query before removing anything."
99 (defcustom eshell-mv-interactive-query
(= (user-uid) 0)
100 "*If non-nil, `mv' will query before overwriting anything."
104 (defcustom eshell-mv-overwrite-files t
105 "*If non-nil, `mv' will overwrite files without warning."
109 (defcustom eshell-cp-interactive-query
(= (user-uid) 0)
110 "*If non-nil, `cp' will query before overwriting anything."
114 (defcustom eshell-cp-overwrite-files t
115 "*If non-nil, `cp' will overwrite files without warning."
119 (defcustom eshell-ln-interactive-query
(= (user-uid) 0)
120 "*If non-nil, `ln' will query before overwriting anything."
124 (defcustom eshell-ln-overwrite-files nil
125 "*If non-nil, `ln' will overwrite files without warning."
129 (defcustom eshell-default-target-is-dot nil
130 "*If non-nil, the default destination for cp, mv or ln is `.'."
134 (defcustom eshell-du-prefer-over-ange nil
135 "*Use Eshell's du in ange-ftp remote directories.
136 Otherwise, Emacs will attempt to use rsh to invoke du on the remote machine."
142 (defun eshell-unix-initialize ()
143 "Initialize the UNIX support/emulation code."
144 (when (eshell-using-module 'eshell-cmpl
)
145 (add-hook 'pcomplete-try-first-hook
146 'eshell-complete-host-reference nil t
))
147 (make-local-variable 'eshell-complex-commands
)
148 (setq eshell-complex-commands
149 (append '("grep" "egrep" "fgrep" "agrep" "glimpse" "locate"
150 "cat" "time" "cp" "mv" "make" "du" "diff" "su" "sudo")
151 eshell-complex-commands
)))
153 (defalias 'eshell
/date
'current-time-string
)
154 (defalias 'eshell
/basename
'file-name-nondirectory
)
155 (defalias 'eshell
/dirname
'file-name-directory
)
162 (defun eshell/man
(&rest args
)
163 "Invoke man, flattening the arguments appropriately."
164 (funcall 'man
(apply 'eshell-flatten-and-stringify args
)))
166 (put 'eshell
/man
'eshell-no-numeric-conversions t
)
168 (defun eshell/info
(&rest args
)
169 "Run the info command in-frame with the same behavior as command-line `info', ie:
170 'info' => goes to top info window
171 'info arg1' => IF arg1 is a file, then visits arg1
172 'info arg1' => OTHERWISE goes to top info window and then menu item arg1
173 'info arg1 arg2' => does action for arg1 (either visit-file or menu-item) and then menu item arg2
175 (eval-and-compile (require 'info
))
177 ((not (stringp (car args
)))
179 ((file-exists-p (expand-file-name (car args
)))
180 (expand-file-name (car args
)))
181 ((file-exists-p (concat (expand-file-name (car args
)) ".info"))
182 (concat (expand-file-name (car args
)) ".info")))))
184 ;; If the first arg is a file, then go to that file's Top node
185 ;; Otherwise, go to the global directory
188 (setq args
(cdr args
))
189 (Info-find-node file
"Top"))
192 ;; Treat all remaining args as menu references
194 (Info-menu (car args
))
195 (setq args
(cdr args
)))))
197 (defun eshell-remove-entries (path files
&optional top-level
)
198 "From PATH, remove all of the given FILES, perhaps interactively."
200 (if (string-match "\\`\\.\\.?\\'"
201 (file-name-nondirectory (car files
)))
203 (eshell-error "rm: cannot remove `.' or `..'\n"))
204 (if (and (file-directory-p (car files
))
205 (not (file-symlink-p (car files
))))
206 (let ((dir (file-name-as-directory (car files
))))
207 (eshell-remove-entries dir
212 (directory-files dir
)))
214 (eshell-printn (format "rm: removing directory `%s'"
220 (format "rm: remove directory `%s'? "
222 (eshell-funcalln 'delete-directory
(car files
))))
224 (eshell-printn (format "rm: removing file `%s'"
229 (format "rm: remove `%s'? "
231 (eshell-funcalln 'delete-file
(car files
)))))
232 (setq files
(cdr files
))))
234 (defun eshell/rm
(&rest args
)
235 "Implementation of rm in Lisp.
236 This is implemented to call either `delete-file', `kill-buffer',
237 `kill-process', or `unintern', depending on the nature of the
239 (setq args
(eshell-flatten-list args
))
240 (eshell-eval-using-options
242 '((?h
"help" nil nil
"show this usage screen")
243 (?f
"force" nil force-removal
"force removal")
244 (?i
"interactive" nil interactive
"prompt before any removal")
245 (?n
"preview" nil preview
"don't change anything on disk")
246 (?r
"recursive" nil recursive
247 "remove the contents of directories recursively")
248 (?R nil nil recursive
"(same)")
249 (?v
"verbose" nil verbose
"explain what is being done")
253 :usage
"[OPTION]... FILE...
254 Remove (unlink) the FILE(s).")
256 (setq interactive eshell-rm-interactive-query
))
257 (if (and force-removal interactive
)
258 (setq interactive nil
))
260 (let ((entry (if (stringp (car args
))
261 (directory-file-name (car args
))
262 (if (numberp (car args
))
263 (number-to-string (car args
))
268 (eshell-printn (format "rm: removing buffer `%s'" entry
)))
271 (not (y-or-n-p (format "rm: delete buffer `%s'? "
273 (eshell-funcalln 'kill-buffer entry
)))
274 ((eshell-processp entry
)
276 (eshell-printn (format "rm: killing process `%s'" entry
)))
279 (not (y-or-n-p (format "rm: kill process `%s'? "
281 (eshell-funcalln 'kill-process entry
)))
284 (eshell-printn (format "rm: uninterning symbol `%s'" entry
)))
288 (not (y-or-n-p (format "rm: unintern symbol `%s'? "
290 (eshell-funcalln 'unintern entry
)))
292 (if (and (file-directory-p entry
)
293 (not (file-symlink-p entry
)))
295 eshell-rm-removes-directories
)
299 (format "rm: descend into directory `%s'? "
301 (eshell-remove-entries nil
(list entry
) t
))
302 (eshell-error (format "rm: %s: is a directory\n" entry
)))
303 (eshell-remove-entries nil
(list entry
) t
)))))
304 (setq args
(cdr args
)))
307 (put 'eshell
/rm
'eshell-no-numeric-conversions t
)
309 (defun eshell/mkdir
(&rest args
)
310 "Implementation of mkdir in Lisp."
311 (eshell-eval-using-options
313 '((?h
"help" nil nil
"show this usage screen")
316 :usage
"[OPTION] DIRECTORY...
317 Create the DIRECTORY(ies), if they do not already exist.")
319 (eshell-funcalln 'make-directory
(car args
))
320 (setq args
(cdr args
)))
323 (put 'eshell
/mkdir
'eshell-no-numeric-conversions t
)
325 (defun eshell/rmdir
(&rest args
)
326 "Implementation of rmdir in Lisp."
327 (eshell-eval-using-options
329 '((?h
"help" nil nil
"show this usage screen")
332 :usage
"[OPTION] DIRECTORY...
333 Remove the DIRECTORY(ies), if they are empty.")
335 (eshell-funcalln 'delete-directory
(car args
))
336 (setq args
(cdr args
)))
339 (put 'eshell
/rmdir
'eshell-no-numeric-conversions t
)
341 (defvar no-dereference
)
345 (defvar eshell-warn-dot-directories t
)
347 (defun eshell-shuffle-files (command action files target func deep
&rest args
)
348 "Shuffle around some filesystem entries, using FUNC to do the work."
349 (let ((attr-target (eshell-file-attributes target
))
350 (is-dir (or (file-directory-p target
)
351 (and preview
(not eshell-warn-dot-directories
))))
353 (if (and (not preview
) (not is-dir
)
354 (> (length files
) 1))
355 (error "%s: when %s multiple files, last argument must be a directory"
358 (setcar files
(directory-file-name (car files
)))
360 ((string-match "\\`\\.\\.?\\'"
361 (file-name-nondirectory (car files
)))
362 (if eshell-warn-dot-directories
363 (eshell-error (format "%s: %s: omitting directory\n"
364 command
(car files
)))))
366 (or (not (eshell-under-windows-p))
367 (eq system-type
'ms-dos
))
368 (setq attr
(eshell-file-attributes (car files
)))
369 (nth 10 attr-target
) (nth 10 attr
)
370 ;; Use equal, not -, since the inode and the device could
372 (equal (nth 10 attr-target
) (nth 10 attr
))
373 (nth 11 attr-target
) (nth 11 attr
)
374 (equal (nth 11 attr-target
) (nth 11 attr
)))
375 (eshell-error (format "%s: `%s' and `%s' are the same file\n"
376 command
(car files
) target
)))
378 (let ((source (car files
))
381 (file-name-nondirectory (car files
)) target
)
384 (if (and (file-directory-p source
)
385 (or (not no-dereference
)
386 (not (file-symlink-p source
)))
387 (not (memq func
'(make-symbolic-link
389 (if (and (eq func
'copy-file
)
391 (eshell-error (format "%s: %s: omitting directory\n"
392 command
(car files
)))
393 (let (eshell-warn-dot-directories)
395 (eq func
'rename-file
)
396 ;; Use equal, since the device might be a
398 (equal (nth 11 (eshell-file-attributes
401 (expand-file-name source
)))))
402 (nth 11 (eshell-file-attributes
405 (expand-file-name target
)))))))
406 (apply 'eshell-funcalln func source target args
)
407 (unless (file-directory-p target
)
410 (format "%s: making directory %s"
413 (eshell-funcalln 'make-directory target
)))
414 (apply 'eshell-shuffle-files
419 (concat source
"/" file
)))
420 (directory-files source
))
422 (when (eq func
'rename-file
)
425 (format "%s: deleting directory %s"
428 (eshell-funcalln 'delete-directory source
))))))
430 (eshell-printn (format "%s: %s -> %s" command
433 (if (and no-dereference
434 (setq link
(file-symlink-p source
)))
436 (apply 'eshell-funcalln
'make-symbolic-link
438 (if (eq func
'rename-file
)
439 (if (and (file-directory-p source
)
440 (not (file-symlink-p source
)))
441 (eshell-funcalln 'delete-directory source
)
442 (eshell-funcalln 'delete-file source
))))
443 (apply 'eshell-funcalln func source target args
)))))))
444 (setq files
(cdr files
)))))
446 (defun eshell-shorthand-tar-command (command args
)
447 "Rewrite `cp -v dir a.tar.gz' to `tar cvzf a.tar.gz dir'."
448 (let* ((archive (car (last args
)))
450 (cond ((string-match "z2" archive
) "If")
451 ((string-match "gz" archive
) "zf")
452 ((string-match "\\(az\\|Z\\)" archive
) "Zf")
454 (if (file-exists-p archive
)
455 (setq tar-args
(concat "u" tar-args
))
456 (setq tar-args
(concat "c" tar-args
)))
458 (setq tar-args
(concat "v" tar-args
)))
459 (if (equal command
"mv")
460 (setq tar-args
(concat "--remove-files -" tar-args
)))
461 ;; truncate the archive name from the arguments
462 (setcdr (last args
2) nil
)
463 (throw 'eshell-replace-command
464 (eshell-parse-command
465 (format "tar %s %s" tar-args archive
) args
))))
467 ;; this is to avoid duplicating code...
468 (defmacro eshell-mvcpln-template
(command action func query-var
469 force-var
&optional preserve
)
470 `(let ((len (length args
)))
472 (and (= len
1) (null eshell-default-target-is-dot
)))
473 (error "%s: missing destination file or directory" ,command
))
476 (setq args
(eshell-stringify-list (eshell-flatten-list args
)))
477 (if (and ,(not (equal command
"ln"))
478 (string-match eshell-tar-regexp
(car (last args
)))
479 (or (> (length args
) 2)
480 (and (file-directory-p (car args
))
481 (or (not no-dereference
)
482 (not (file-symlink-p (car args
)))))))
483 (eshell-shorthand-tar-command ,command args
)
484 (let ((target (car (last args
)))
486 (setcdr (last args
2) nil
)
487 (eshell-shuffle-files
488 ,command
,action args target
,func nil
490 `((if (and (or interactive
493 1 (or force
,force-var
)))
498 (defun eshell/mv
(&rest args
)
499 "Implementation of mv in Lisp."
500 (eshell-eval-using-options
502 '((?f
"force" nil force
503 "remove existing destinations, never prompt")
504 (?i
"interactive" nil interactive
505 "request confirmation if target already exists")
506 (?n
"preview" nil preview
507 "don't change anything on disk")
508 (?v
"verbose" nil verbose
509 "explain what is being done")
510 (nil "help" nil nil
"show this usage screen")
514 :usage
"[OPTION]... SOURCE DEST
515 or: mv [OPTION]... SOURCE... DIRECTORY
516 Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
517 \[OPTION] DIRECTORY...")
518 (let ((no-dereference t
))
519 (eshell-mvcpln-template "mv" "moving" 'rename-file
520 eshell-mv-interactive-query
521 eshell-mv-overwrite-files
))))
523 (put 'eshell
/mv
'eshell-no-numeric-conversions t
)
525 (defun eshell/cp
(&rest args
)
526 "Implementation of cp in Lisp."
527 (eshell-eval-using-options
529 '((?a
"archive" nil archive
531 (?d
"no-dereference" nil no-dereference
533 (?f
"force" nil force
534 "remove existing destinations, never prompt")
535 (?i
"interactive" nil interactive
536 "request confirmation if target already exists")
537 (?n
"preview" nil preview
538 "don't change anything on disk")
539 (?p
"preserve" nil preserve
540 "preserve file attributes if possible")
541 (?R
"recursive" nil recursive
542 "copy directories recursively")
543 (?v
"verbose" nil verbose
544 "explain what is being done")
545 (nil "help" nil nil
"show this usage screen")
549 :usage
"[OPTION]... SOURCE DEST
550 or: cp [OPTION]... SOURCE... DIRECTORY
551 Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.")
553 (setq preserve t no-dereference t recursive t
))
554 (eshell-mvcpln-template "cp" "copying" 'copy-file
555 eshell-cp-interactive-query
556 eshell-cp-overwrite-files preserve
)))
558 (put 'eshell
/cp
'eshell-no-numeric-conversions t
)
560 (defun eshell/ln
(&rest args
)
561 "Implementation of ln in Lisp."
562 (eshell-eval-using-options
564 '((?h
"help" nil nil
"show this usage screen")
565 (?s
"symbolic" nil symbolic
566 "make symbolic links instead of hard links")
567 (?i
"interactive" nil interactive
568 "request confirmation if target already exists")
569 (?f
"force" nil force
"remove existing destinations, never prompt")
570 (?n
"preview" nil preview
571 "don't change anything on disk")
572 (?v
"verbose" nil verbose
"explain what is being done")
576 :usage
"[OPTION]... TARGET [LINK_NAME]
577 or: ln [OPTION]... TARGET... DIRECTORY
578 Create a link to the specified TARGET with optional LINK_NAME. If there is
579 more than one TARGET, the last argument must be a directory; create links
580 in DIRECTORY to each TARGET. Create hard links by default, symbolic links
581 with '--symbolic'. When creating hard links, each TARGET must exist.")
582 (let ((no-dereference t
))
583 (eshell-mvcpln-template "ln" "linking"
587 eshell-ln-interactive-query
588 eshell-ln-overwrite-files
))))
590 (put 'eshell
/ln
'eshell-no-numeric-conversions t
)
592 (defun eshell/cat
(&rest args
)
593 "Implementation of cat in Lisp.
594 If in a pipeline, or the file is not a regular file, directory or
595 symlink, then revert to the system's definition of cat."
596 (setq args
(eshell-stringify-list (eshell-flatten-list args
)))
597 (if (or eshell-in-pipeline-p
600 (unless (or (and (stringp arg
)
602 (eq (aref arg
0) ?-
))
603 (let ((attrs (eshell-file-attributes arg
)))
604 (and attrs
(memq (aref (nth 8 attrs
) 0)
606 (throw 'special t
)))))
607 (let ((ext-cat (eshell-search-path "cat")))
609 (throw 'eshell-replace-command
610 (eshell-parse-command ext-cat args
))
611 (if eshell-in-pipeline-p
612 (error "Eshell's `cat' does not work in pipelines")
613 (error "Eshell's `cat' cannot display one of the files given"))))
614 (eshell-init-print-buffer)
615 (eshell-eval-using-options
617 '((?h
"help" nil nil
"show this usage screen")
620 :usage
"[OPTION] FILE...
621 Concatenate FILE(s), or standard input, to standard output.")
622 (eshell-for file args
623 (if (string= file
"-")
624 (throw 'eshell-external
625 (eshell-external-command "cat" args
))))
626 (let ((curbuf (current-buffer)))
627 (eshell-for file args
629 (insert-file-contents file
)
630 (goto-char (point-min))
632 (let ((str (buffer-substring
633 (point) (min (1+ (line-end-position))
635 (with-current-buffer curbuf
636 (eshell-buffered-print str
)))
639 ;; if the file does not end in a newline, do not emit one
640 (setq eshell-ensure-newline-p nil
))))
642 (put 'eshell
/cat
'eshell-no-numeric-conversions t
)
644 ;; special front-end functions for compilation-mode buffers
646 (defun eshell/make
(&rest args
)
647 "Use `compile' to do background makes."
648 (if (and eshell-current-subjob-p
649 (eshell-interactive-output-p))
650 (let ((compilation-process-setup-function
652 (list 'setq
'process-environment
653 (list 'quote
(eshell-copy-environment))))))
654 (compile (concat "make " (eshell-flatten-and-stringify args
))))
655 (throw 'eshell-replace-command
656 (eshell-parse-command "*make" (eshell-stringify-list
657 (eshell-flatten-list args
))))))
659 (put 'eshell
/make
'eshell-no-numeric-conversions t
)
661 (defun eshell-occur-mode-goto-occurrence ()
662 "Go to the occurrence the current line describes."
664 (let ((pos (occur-mode-find-occurrence)))
665 (pop-to-buffer (marker-buffer pos
))
666 (goto-char (marker-position pos
))))
668 (defun eshell-occur-mode-mouse-goto (event)
669 "In Occur mode, go to the occurrence whose line you click on."
672 (with-current-buffer (window-buffer (posn-window (event-end event
)))
674 (goto-char (posn-point (event-end event
)))
675 (setq pos
(occur-mode-find-occurrence))))
676 (pop-to-buffer (marker-buffer pos
))
677 (goto-char (marker-position pos
))))
679 (defun eshell-poor-mans-grep (args)
680 "A poor version of grep that opens every file and uses `occur'.
681 This eats up memory, since it leaves the buffers open (to speed future
682 searches), and it's very slow. But, if your system has no grep
684 (save-selected-window
685 (let ((default-dir default-directory
))
686 (with-current-buffer (get-buffer-create "*grep*")
687 (let ((inhibit-read-only t
)
688 (default-directory default-dir
))
691 (let ((files (eshell-stringify-list
692 (eshell-flatten-list (cdr args
))))
693 (inhibit-redisplay t
)
696 (if (get-buffer "*Occur*")
697 (kill-buffer (get-buffer "*Occur*")))
700 (with-current-buffer (find-file-noselect (car files
))
704 (if (get-buffer "*Occur*")
705 (with-current-buffer (get-buffer "*Occur*")
706 (setq string
(buffer-string))
707 (kill-buffer (current-buffer)))))
708 (if string
(insert string
))
710 files
(cdr files
)))))
711 (local-set-key [mouse-2
] 'eshell-occur-mode-mouse-goto
)
712 (local-set-key [(control ?c
) (control ?c
)]
713 'eshell-occur-mode-goto-occurrence
)
714 (local-set-key [(control ?m
)]
715 'eshell-occur-mode-goto-occurrence
)
716 (local-set-key [return] 'eshell-occur-mode-goto-occurrence)
717 (pop-to-buffer (current-buffer) t)
718 (goto-char (point-min))
719 (resize-temp-buffer-window))))))
721 (defun eshell-grep (command args &optional maybe-use-occur)
722 "Generic service function for the various grep aliases.
723 It calls Emacs' grep utility if the command is not redirecting output,
724 and if it's not part of a command pipeline. Otherwise, it calls the
726 (if (and maybe-use-occur eshell-no-grep-available)
727 (eshell-poor-mans-grep args)
728 (if (or eshell-plain-grep-behavior
729 (not (and (eshell-interactive-output-p)
730 (not eshell-in-pipeline-p)
731 (not eshell-in-subcommand-p))))
732 (throw 'eshell-replace-command
733 (eshell-parse-command (concat "*" command)
734 (eshell-stringify-list
735 (eshell-flatten-list args))))
736 (let* ((args (mapconcat 'identity
737 (mapcar 'shell-quote-argument
738 (eshell-stringify-list
739 (eshell-flatten-list args)))
742 (set-text-properties 0 (length args)
744 (format "%s -n %s" command args)))
745 compilation-scroll-output)
748 (defun eshell/grep (&rest args)
749 "Use Emacs grep facility instead of calling external grep."
750 (eshell-grep "grep" args t))
752 (defun eshell/egrep (&rest args)
753 "Use Emacs grep facility instead of calling external egrep."
754 (eshell-grep "egrep" args t))
756 (defun eshell/fgrep (&rest args)
757 "Use Emacs grep facility instead of calling external fgrep."
758 (eshell-grep "fgrep" args t))
760 (defun eshell/agrep (&rest args)
761 "Use Emacs grep facility instead of calling external agrep."
762 (eshell-grep "agrep" args))
764 (defun eshell/glimpse (&rest args)
765 "Use Emacs grep facility instead of calling external glimpse."
767 (eshell-grep "glimpse" (append '("-z" "-y") args))))
769 ;; completions rules for some common UNIX commands
771 (defsubst eshell-complete-hostname ()
772 "Complete a command that wants a hostname for an argument."
773 (pcomplete-here (eshell-read-host-names)))
775 (defun eshell-complete-host-reference ()
776 "If there is a host reference, complete it."
777 (let ((arg (pcomplete-actual-arg))
779 (when (setq index (string-match "@[a-z.]*\\'" arg))
780 (setq pcomplete-stub (substring arg (1+ index))
781 pcomplete-last-completion-raw t)
782 (throw 'pcomplete-completions (eshell-read-host-names)))))
784 (defalias 'pcomplete/ftp 'eshell-complete-hostname)
785 (defalias 'pcomplete/ncftp 'eshell-complete-hostname)
786 (defalias 'pcomplete/ping 'eshell-complete-hostname)
787 (defalias 'pcomplete/rlogin 'eshell-complete-hostname)
789 (defun pcomplete/telnet ()
790 (require 'pcmpl-unix)
791 (pcomplete-opt "xl(pcmpl-unix-user-names)")
792 (eshell-complete-hostname))
794 (defun pcomplete/rsh ()
795 "Complete `rsh', which, after the user and hostname, is like xargs."
796 (require 'pcmpl-unix)
797 (pcomplete-opt "l(pcmpl-unix-user-names)")
798 (eshell-complete-hostname)
799 (pcomplete-here (funcall pcomplete-command-completion-function))
800 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
801 pcomplete-default-completion-function)))
803 (defalias 'pcomplete/ssh 'pcomplete/rsh)
807 (defvar dereference-links)
809 (defvar human-readable)
811 (defvar only-one-filesystem)
814 (defsubst eshell-du-size-string (size)
815 (let* ((str (eshell-printable-size size human-readable block-size t))
817 (concat str (if (< len 8)
818 (make-string (- 8 len) ? )))))
820 (defun eshell-du-sum-directory (path depth)
821 "Summarize PATH, and its member directories."
822 (let ((entries (eshell-directory-files-and-attributes path))
825 (unless (string-match "\\`\\.\\.?\\'" (caar entries))
826 (let* ((entry (concat path "/"
828 (symlink (and (stringp (cadr (car entries)))
829 (cadr (car entries)))))
830 (unless (or (and symlink (not dereference-links))
831 (and only-one-filesystem
832 (/= only-one-filesystem
833 (nth 12 (car entries)))))
835 (setq entry symlink))
838 (if (eq t (cadr (car entries)))
839 (eshell-du-sum-directory entry (1+ depth))
840 (let ((file-size (nth 8 (car entries))))
845 (concat (eshell-du-size-string file-size)
846 entry "\n")))))))))))
847 (setq entries (cdr entries)))
848 (if (or (not max-depth)
851 (eshell-print (concat (eshell-du-size-string size)
852 (directory-file-name path) "\n")))
855 (defun eshell/du (&rest args)
856 "Implementation of \"du\" in Lisp, passing ARGS."
858 (eshell-stringify-list (eshell-flatten-list args))
860 (let ((ext-du (eshell-search-path "du")))
862 (not (catch 'have-ange-path
865 (file-remote-p (expand-file-name arg) 'method) "ftp")
866 (throw 'have-ange-path t))))))
867 (throw 'eshell-replace-command
868 (eshell-parse-command ext-du args))
869 (eshell-eval-using-options
871 '((?a "all" nil show-all
872 "write counts for all files, not just directories")
873 (nil "block-size" t block-size
874 "use SIZE-byte blocks (i.e., --block-size SIZE)")
875 (?b "bytes" nil by-bytes
876 "print size in bytes")
877 (?c "total" nil grand-total
878 "produce a grand total")
879 (?d "max-depth" t max-depth
880 "display data only this many levels of data")
881 (?h "human-readable" 1024 human-readable
882 "print sizes in human readable format")
883 (?H "is" 1000 human-readable
884 "likewise, but use powers of 1000 not 1024")
885 (?k "kilobytes" 1024 block-size
886 "like --block-size 1024")
887 (?L "dereference" nil dereference-links
888 "dereference all symbolic links")
889 (?m "megabytes" 1048576 block-size
890 "like --block-size 1048576")
891 (?s "summarize" 0 max-depth
892 "display only a total for each argument")
893 (?x "one-file-system" nil only-one-filesystem
894 "skip directories on different filesystems")
896 "show this usage screen")
898 :usage "[OPTION]... FILE...
899 Summarize disk usage of each FILE, recursively for directories.")
901 (setq block-size (or block-size 1024)))
902 (if (and max-depth (stringp max-depth))
903 (setq max-depth (string-to-number max-depth)))
904 ;; filesystem support means nothing under Windows
905 (if (eshell-under-windows-p)
906 (setq only-one-filesystem nil))
907 (let ((size 0.0) ange-cache)
909 (if only-one-filesystem
910 (setq only-one-filesystem
911 (nth 11 (eshell-file-attributes
912 (file-name-as-directory (car args))))))
913 (setq size (+ size (eshell-du-sum-directory
914 (directory-file-name (car args)) 0)))
915 (setq args (cdr args)))
917 (eshell-print (concat (eshell-du-size-string size)
920 (defvar eshell-time-start nil)
922 (defun eshell-show-elapsed-time ()
923 (let ((elapsed (format "%.3f secs\n"
924 (- (eshell-time-to-seconds (current-time))
925 eshell-time-start))))
926 (set-text-properties 0 (length elapsed) '(face bold) elapsed)
927 (eshell-interactive-print elapsed))
928 (remove-hook 'eshell-post-command-hook 'eshell-show-elapsed-time t))
930 (defun eshell/time (&rest args)
931 "Implementation of \"time\" in Lisp."
932 (let ((time-args (copy-alist args))
935 (while (and continue args)
936 (if (not (string-match "^-" (car args)))
939 (setcdr last-arg nil)
944 (eshell-eval-using-options
946 '((?h "help" nil nil "show this usage screen")
950 Show wall-clock time elapsed during execution of COMMAND.")
951 (setq eshell-time-start (eshell-time-to-seconds (current-time)))
952 (add-hook 'eshell-post-command-hook 'eshell-show-elapsed-time nil t)
954 (throw 'eshell-replace-command
955 (eshell-parse-command (car time-args)
956 ;;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2007-08/msg00205.html
957 (eshell-stringify-list
958 (eshell-flatten-list (cdr time-args))))))))
960 (defun eshell/whoami (&rest args)
961 "Make \"whoami\" Tramp aware."
962 (or (file-remote-p default-directory 'user) (user-login-name)))
964 (defvar eshell-diff-window-config nil)
966 (defun eshell-diff-quit ()
967 "Restore the window configuration previous to diff'ing."
969 (if eshell-diff-window-config
970 (set-window-configuration eshell-diff-window-config)))
972 (defun nil-blank-string (string)
973 "Return STRING, or nil if STRING contains only non-blank characters."
975 ((string-match "[^[:blank:]]" string) string)
978 (defun eshell/diff (&rest args)
979 "Alias \"diff\" to call Emacs `diff' function."
980 (let ((orig-args (eshell-stringify-list (eshell-flatten-list args))))
981 (if (or eshell-plain-diff-behavior
982 (not (and (eshell-interactive-output-p)
983 (not eshell-in-pipeline-p)
984 (not eshell-in-subcommand-p))))
985 (throw 'eshell-replace-command
986 (eshell-parse-command "*diff" orig-args))
987 (setq args (copy-sequence orig-args))
988 (if (< (length args) 2)
989 (throw 'eshell-replace-command
990 (eshell-parse-command "*diff" orig-args)))
991 (let ((old (car (last args 2)))
992 (new (car (last args)))
993 (config (current-window-configuration)))
994 (if (= (length args) 2)
996 (setcdr (last args 3) nil))
1000 (nil-blank-string (eshell-flatten-and-stringify args)))
1002 (throw 'eshell-replace-command
1003 (eshell-parse-command "*diff" orig-args))))
1004 (when (fboundp 'diff-mode)
1005 (make-local-variable 'compilation-finish-functions)
1007 'compilation-finish-functions
1009 (with-current-buffer buff
1011 (set (make-local-variable 'eshell-diff-window-config)
1013 (local-set-key [?q] 'eshell-diff-quit)
1014 (if (fboundp 'turn-on-font-lock-if-enabled)
1015 (turn-on-font-lock-if-enabled))
1016 (goto-char (point-min))))))
1017 (pop-to-buffer (current-buffer))))))
1020 (put 'eshell/diff 'eshell-no-numeric-conversions t)
1022 (defun eshell/locate (&rest args)
1023 "Alias \"locate\" to call Emacs `locate' function."
1024 (if (or eshell-plain-locate-behavior
1025 (not (and (eshell-interactive-output-p)
1026 (not eshell-in-pipeline-p)
1027 (not eshell-in-subcommand-p)))
1028 (and (stringp (car args))
1029 (string-match "^-" (car args))))
1030 (throw 'eshell-replace-command
1031 (eshell-parse-command "*locate" (eshell-stringify-list
1032 (eshell-flatten-list args))))
1033 (save-selected-window
1034 (let ((locate-history-list (list (car args))))
1035 (locate-with-filter (car args) (cadr args))))))
1037 (put 'eshell/locate 'eshell-no-numeric-conversions t)
1039 (defun eshell/occur (&rest args)
1040 "Alias \"occur\" to call Emacs `occur' function."
1041 (let ((inhibit-read-only t))
1042 (if (> (length args) 2)
1043 (error "usage: occur: (REGEXP &optional NLINES)")
1044 (apply 'occur args))))
1046 (put 'eshell/occur 'eshell-no-numeric-conversions t)
1048 ;; Pacify the byte-compiler.
1049 (defvar tramp-default-proxies-alist)
1051 (defun eshell/su (&rest args)
1052 "Alias \"su\" to call Tramp."
1054 (setq args (eshell-stringify-list (eshell-flatten-list args)))
1055 (let ((orig-args (copy-tree args)))
1056 (eshell-eval-using-options
1058 '((?h "help" nil nil "show this usage screen")
1059 (?l "login" nil login "provide a login environment")
1060 (? nil nil login "provide a login environment")
1061 :usage "[- | -l | --login] [USER]
1062 Become another USER during a login session.")
1063 (throw 'eshell-replace-command
1065 (host (or (file-remote-p default-directory 'host)
1067 (dir (or (file-remote-p default-directory 'localname)
1068 (expand-file-name default-directory))))
1069 (eshell-for arg args
1070 (if (string-equal arg "-") (setq login t) (setq user arg)))
1071 ;; `eshell-eval-using-options' does not handle "-".
1072 (if (member "-" orig-args) (setq login t))
1073 (if login (setq dir "~/"))
1074 (if (and (file-remote-p default-directory)
1077 "su" (file-remote-p default-directory 'method)))
1079 user (file-remote-p default-directory 'user)))))
1081 'tramp-default-proxies-alist
1082 (list host user (file-remote-p default-directory))))
1083 (eshell-parse-command
1084 "cd" (list (format "/su:%s@%s:%s" user host dir))))))))
1086 (put 'eshell/su 'eshell-no-numeric-conversions t)
1088 (defun eshell/sudo (&rest args)
1089 "Alias \"sudo\" to call Tramp."
1091 (setq args (eshell-stringify-list (eshell-flatten-list args)))
1092 (let ((orig-args (copy-tree args)))
1093 (eshell-eval-using-options
1095 '((?h "help" nil nil "show this usage screen")
1096 (?u "user" t user "execute a command as another USER")
1098 :usage "[(-u | --user) USER] COMMAND
1099 Execute a COMMAND as the superuser or another USER.")
1100 (throw 'eshell-external
1101 (let ((user (or user "root"))
1102 (host (or (file-remote-p default-directory 'host)
1104 (dir (or (file-remote-p default-directory 'localname)
1105 (expand-file-name default-directory))))
1106 ;; `eshell-eval-using-options' reads options of COMMAND.
1107 (while (and (stringp (car orig-args))
1108 (member (car orig-args) '("-u" "--user")))
1109 (setq orig-args (cddr orig-args)))
1110 (if (and (file-remote-p default-directory)
1113 "sudo" (file-remote-p default-directory 'method)))
1115 user (file-remote-p default-directory 'user)))))
1117 'tramp-default-proxies-alist
1118 (list host user (file-remote-p default-directory))))
1119 (let ((default-directory (format "/sudo:%s@%s:%s" user host dir)))
1120 (eshell-named-command (car orig-args) (cdr orig-args))))))))
1122 (put 'eshell/sudo 'eshell-no-numeric-conversions t)
1127 ;; generated-autoload-file: "esh-groups.el"
1130 ;; arch-tag: 2462edd2-a76a-4cf2-897d-92e9a82ac1c9
1131 ;;; em-unix.el ends here