Change spelling of the Free Software Foundation.
[bpt/emacs.git] / lisp / eshell / em-unix.el
CommitLineData
affbf647
GM
1;;; em-unix --- UNIX command aliases
2
faadfb0a 3;; Copyright (C) 1999, 2000 Free Software Foundation
affbf647
GM
4
5;; This file is part of GNU Emacs.
6
7;; GNU Emacs is free software; you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation; either version 2, or (at your option)
10;; any later version.
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with GNU Emacs; see the file COPYING. If not, write to the
19;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20;; Boston, MA 02111-1307, USA.
21
22(provide 'em-unix)
23
24(eval-when-compile (require 'esh-maint))
25
26(defgroup eshell-unix nil
27 "This module defines many of the more common UNIX utilities as
28aliases implemented in Lisp. These include mv, ln, cp, rm, etc. If
29the user passes arguments which are too complex, or are unrecognized
30by the Lisp variant, the external version will be called (if
31available). The only reason not to use them would be because they are
32usually much slower. But in several cases their tight integration
33with Eshell makes them more versatile than their traditional cousins
34\(such as being able to use `kill' to kill Eshell background processes
35by name)."
36 :tag "UNIX commands in Lisp"
37 :group 'eshell-module)
38
39;;; Commentary:
40
41;; This file contains implementations of several UNIX command in Emacs
42;; Lisp, for several reasons:
43;;
44;; 1) it makes them available on all platforms where the Lisp
45;; functions used are available
46;;
47;; 2) it makes their functionality accessible and modified by the
48;; Lisp programmer.
49;;
50;; 3) it allows Eshell to refrain from having to invoke external
51;; processes for common operations.
52
53(defcustom eshell-unix-load-hook '(eshell-unix-initialize)
54 "*A list of functions to run when `eshell-unix' is loaded."
55 :type 'hook
56 :group 'eshell-unix)
57
58(defcustom eshell-plain-grep-behavior nil
59 "*If non-nil, standalone \"grep\" commands will behave normally.
60Standalone in this context means not redirected, and not on the
61receiving side of a command pipeline."
62 :type 'boolean
63 :group 'eshell-unix)
64
65(defcustom eshell-no-grep-available (not (eshell-search-path "grep"))
66 "*If non-nil, no grep is available on the current machine."
67 :type 'boolean
68 :group 'eshell-unix)
69
70(defcustom eshell-plain-diff-behavior nil
71 "*If non-nil, standalone \"diff\" commands will behave normally.
72Standalone in this context means not redirected, and not on the
73receiving side of a command pipeline."
74 :type 'boolean
75 :group 'eshell-unix)
76
77(defcustom eshell-plain-locate-behavior nil
78 "*If non-nil, standalone \"locate\" commands will behave normally.
79Standalone in this context means not redirected, and not on the
80receiving side of a command pipeline."
81 :type 'boolean
82 :group 'eshell-unix)
83
84(defcustom eshell-rm-removes-directories nil
85 "*If non-nil, `rm' will remove directory entries.
86Otherwise, `rmdir' is required."
87 :type 'boolean
88 :group 'eshell-unix)
89
90(defcustom eshell-rm-interactive-query (= (user-uid) 0)
91 "*If non-nil, `rm' will query before removing anything."
92 :type 'boolean
93 :group 'eshell-unix)
94
95(defcustom eshell-mv-interactive-query (= (user-uid) 0)
96 "*If non-nil, `mv' will query before overwriting anything."
97 :type 'boolean
98 :group 'eshell-unix)
99
100(defcustom eshell-mv-overwrite-files t
101 "*If non-nil, `mv' will overwrite files without warning."
102 :type 'boolean
103 :group 'eshell-unix)
104
105(defcustom eshell-cp-interactive-query (= (user-uid) 0)
106 "*If non-nil, `cp' will query before overwriting anything."
107 :type 'boolean
108 :group 'eshell-unix)
109
110(defcustom eshell-cp-overwrite-files t
111 "*If non-nil, `cp' will overwrite files without warning."
112 :type 'boolean
113 :group 'eshell-unix)
114
115(defcustom eshell-ln-interactive-query (= (user-uid) 0)
116 "*If non-nil, `ln' will query before overwriting anything."
117 :type 'boolean
118 :group 'eshell-unix)
119
120(defcustom eshell-ln-overwrite-files t
121 "*If non-nil, `ln' will overwrite files without warning."
122 :type 'boolean
123 :group 'eshell-unix)
124
125(require 'esh-opt)
126
127;;; Functions:
128
129(defun eshell-unix-initialize ()
130 "Initialize the UNIX support/emulation code."
131 (make-local-hook 'eshell-post-command-hook)
132 (when (eshell-using-module 'eshell-cmpl)
133 (make-local-hook 'pcomplete-try-first-hook)
134 (add-hook 'pcomplete-try-first-hook
135 'eshell-complete-host-reference nil t)))
136
137(defalias 'eshell/date 'current-time-string)
138(defalias 'eshell/basename 'file-name-nondirectory)
139(defalias 'eshell/dirname 'file-name-directory)
140
141(eval-when-compile
142 (defvar interactive)
143 (defvar preview)
144 (defvar recursive)
145 (defvar verbose))
146
147(defun eshell/man (&rest args)
148 "Invoke man, flattening the arguments appropriately."
149 (funcall 'man (apply 'eshell-flatten-and-stringify args)))
150
151(defun eshell-remove-entries (path files &optional top-level)
152 (while files
153 (if (string-match "\\`\\.\\.?\\'"
154 (file-name-nondirectory (car files)))
155 (if top-level
156 (eshell-error "rm: cannot remove `.' or `..'\n"))
157 (if (and (file-directory-p (car files))
158 (not (file-symlink-p (car files))))
159 (let ((dir (file-name-as-directory (car files))))
160 (eshell-remove-entries dir
161 (mapcar
162 (function
163 (lambda (file)
164 (concat dir file)))
165 (directory-files dir)))
166 (if verbose
167 (eshell-printn (format "rm: removing directory `%s'"
168 (car files))))
169 (unless
170 (or preview
171 (and interactive
172 (not (y-or-n-p
173 (format "rm: remove directory `%s'? "
174 (car files))))))
175 (eshell-funcalln 'delete-directory (car files))))
176 (if verbose
177 (eshell-printn (format "rm: removing file `%s'"
178 (car files))))
179 (unless (or preview
180 (and interactive
181 (not (y-or-n-p
182 (format "rm: remove `%s'? "
183 (car files))))))
184 (eshell-funcalln 'delete-file (car files)))))
185 (setq files (cdr files))))
186
187(defun eshell/rm (&rest args)
188 "Implementation of rm in Lisp.
189This is implemented to call either `delete-file', `kill-buffer',
190`kill-process', or `unintern', depending on the nature of the
191argument."
192 (setq args (eshell-flatten-list args))
193 (eshell-eval-using-options
194 "rm" args
195 '((?h "help" nil nil "show this usage screen")
196 (?f "force" nil force-removal "force removal")
197 (?i "interactive" nil interactive "prompt before any removal")
198 (?n "preview" nil preview "don't change anything on disk")
199 (?r "recursive" nil recursive
200 "remove the contents of directories recursively")
201 (?R nil nil recursive "(same)")
202 (?v "verbose" nil verbose "explain what is being done")
203 :preserve-args
204 :external "rm"
205 :show-usage
206 :usage "[OPTION]... FILE...
207Remove (unlink) the FILE(s).")
208 (unless interactive
209 (setq interactive eshell-rm-interactive-query))
210 (if (and force-removal interactive)
211 (setq interactive nil))
212 (while args
213 (let ((entry (if (stringp (car args))
214 (directory-file-name (car args))
215 (if (numberp (car args))
216 (number-to-string (car args))
217 (car args)))))
218 (cond
219 ((bufferp entry)
220 (if verbose
221 (eshell-printn (format "rm: removing buffer `%s'" entry)))
222 (unless (or preview
223 (and interactive
224 (not (y-or-n-p (format "rm: delete buffer `%s'? "
225 entry)))))
226 (eshell-funcalln 'kill-buffer entry)))
227 ((processp entry)
228 (if verbose
229 (eshell-printn (format "rm: killing process `%s'" entry)))
230 (unless (or preview
231 (and interactive
232 (not (y-or-n-p (format "rm: kill process `%s'? "
233 entry)))))
234 (eshell-funcalln 'kill-process entry)))
235 ((symbolp entry)
236 (if verbose
237 (eshell-printn (format "rm: uninterning symbol `%s'" entry)))
238 (unless
239 (or preview
240 (and interactive
241 (not (y-or-n-p (format "rm: unintern symbol `%s'? "
242 entry)))))
243 (eshell-funcalln 'unintern entry)))
244 ((stringp entry)
245 (if (and (file-directory-p entry)
246 (not (file-symlink-p entry)))
247 (if (or recursive
248 eshell-rm-removes-directories)
249 (if (or preview
250 (not interactive)
251 (y-or-n-p
252 (format "rm: descend into directory `%s'? "
253 entry)))
254 (eshell-remove-entries nil (list entry) t))
255 (eshell-error (format "rm: %s: is a directory\n" entry)))
256 (eshell-remove-entries nil (list entry) t)))))
257 (setq args (cdr args)))
258 nil))
259
260(defun eshell/mkdir (&rest args)
261 "Implementation of mkdir in Lisp."
262 (eshell-eval-using-options
263 "mkdir" args
264 '((?h "help" nil nil "show this usage screen")
265 :external "mkdir"
266 :show-usage
267 :usage "[OPTION] DIRECTORY...
268Create the DIRECTORY(ies), if they do not already exist.")
269 (while args
270 (eshell-funcalln 'make-directory (car args))
271 (setq args (cdr args)))
272 nil))
273
274(defun eshell/rmdir (&rest args)
275 "Implementation of rmdir in Lisp."
276 (eshell-eval-using-options
277 "rmdir" args
278 '((?h "help" nil nil "show this usage screen")
279 :external "rmdir"
280 :show-usage
281 :usage "[OPTION] DIRECTORY...
282Remove the DIRECTORY(ies), if they are empty.")
283 (while args
284 (eshell-funcalln 'delete-directory (car args))
285 (setq args (cdr args)))
286 nil))
287
288(eval-when-compile
289 (defvar no-dereference)
290 (defvar preview)
291 (defvar verbose))
292
293(defvar eshell-warn-dot-directories t)
294
295(defun eshell-shuffle-files (command action files target func deep &rest args)
296 "Shuffle around some filesystem entries, using FUNC to do the work."
297 (if (null target)
298 (error "%s: missing destination file" command))
299 (let ((attr-target (file-attributes target))
300 (is-dir (or (file-directory-p target)
301 (and preview (not eshell-warn-dot-directories))))
302 attr)
303 (if (and (not preview) (not is-dir)
304 (> (length files) 1))
305 (error "%s: when %s multiple files, last argument must be a directory"
306 command action))
307 (while files
308 (setcar files (directory-file-name (car files)))
309 (cond
310 ((string-match "\\`\\.\\.?\\'"
311 (file-name-nondirectory (car files)))
312 (if eshell-warn-dot-directories
313 (eshell-error (format "%s: %s: omitting directory\n"
314 command (car files)))))
315 ((and attr-target
316 (not (eshell-under-windows-p))
317 (setq attr (file-attributes (car files)))
318 (= (nth 10 attr-target) (nth 10 attr))
319 (= (nth 11 attr-target) (nth 11 attr)))
320 (eshell-error (format "%s: `%s' and `%s' are the same file\n"
321 command (car files) target)))
322 (t
323 (let ((source (car files))
324 (target (if is-dir
325 (expand-file-name
326 (file-name-nondirectory (car files)) target)
327 target))
328 link)
329 (if (and (file-directory-p source)
330 (or (not no-dereference)
331 (not (file-symlink-p source)))
332 (not (memq func '(make-symbolic-link
333 add-name-to-file))))
334 (if (and (eq func 'copy-file)
335 (not recursive))
336 (eshell-error (format "%s: %s: omitting directory\n"
337 command (car files)))
338 (let (eshell-warn-dot-directories)
339 (if (and (not deep)
340 (eq func 'rename-file)
341 (= (nth 11 (file-attributes
342 (file-name-directory
343 (expand-file-name source))))
344 (nth 11 (file-attributes
345 (file-name-directory
346 (expand-file-name target))))))
347 (apply 'eshell-funcalln func source target args)
348 (unless (file-directory-p target)
349 (if verbose
350 (eshell-printn
351 (format "%s: making directory %s"
352 command target)))
353 (unless preview
354 (eshell-funcalln 'make-directory target)))
355 (eshell-shuffle-files command action
356 (mapcar
357 (function
358 (lambda (file)
359 (concat source "/" file)))
360 (directory-files source))
361 target func t args)
362 (when (eq func 'rename-file)
363 (if verbose
364 (eshell-printn
365 (format "%s: deleting directory %s"
366 command source)))
367 (unless preview
368 (eshell-funcalln 'delete-directory source))))))
369 (if verbose
370 (eshell-printn (format "%s: %s -> %s" command
371 source target)))
372 (unless preview
373 (if (and no-dereference
374 (setq link (file-symlink-p source)))
375 (progn
376 (apply 'eshell-funcalln 'make-symbolic-link
377 link target args)
378 (if (eq func 'rename-file)
379 (if (and (file-directory-p source)
380 (not (file-symlink-p source)))
381 (eshell-funcalln 'delete-directory source)
382 (eshell-funcalln 'delete-file source))))
383 (apply 'eshell-funcalln func source target args)))))))
384 (setq files (cdr files)))))
385
386(defun eshell-shorthand-tar-command (command args)
387 "Rewrite `cp -v dir a.tar.gz' to `tar cvzf a.tar.gz dir'."
388 (let* ((archive (car (last args)))
389 (tar-args
390 (cond ((string-match "z2" archive) "If")
391 ((string-match "gz" archive) "zf")
392 ((string-match "\\(az\\|Z\\)" archive) "Zf")
393 (t "f"))))
394 (if (file-exists-p archive)
395 (setq tar-args (concat "u" tar-args))
396 (setq tar-args (concat "c" tar-args)))
397 (if verbose
398 (setq tar-args (concat "v" tar-args)))
399 (if (equal command "mv")
400 (setq tar-args (concat "--remove-files -" tar-args)))
401 ;; truncate the archive name from the arguments
402 (setcdr (last args 2) nil)
403 (throw 'eshell-replace-command
404 (eshell-parse-command
405 (format "tar %s %s" tar-args archive) args))))
406
407;; this is to avoid duplicating code...
408(defmacro eshell-mvcp-template
409 (command action func query-var force-var &optional preserve)
410 `(if (and (string-match eshell-tar-regexp (car (last args)))
411 (or (> (length args) 2)
412 (and (file-directory-p (car args))
413 (or (not no-dereference)
414 (not (file-symlink-p (car args)))))))
415 (eshell-shorthand-tar-command ,command args)
416 (let (target)
417 (if (> (length args) 1)
418 (progn
419 (setq target (car (last args)))
420 (setcdr (last args 2) nil))
421 (setq args nil))
422 (eshell-shuffle-files
423 ,command ,action args target ,func nil
424 ,@(append
425 `((if (and (or interactive
426 ,query-var)
427 (not force))
428 1 (or force ,force-var)))
429 (if preserve
430 (list preserve)))))
431 nil))
432
433(defun eshell/mv (&rest args)
434 "Implementation of mv in Lisp."
435 (eshell-eval-using-options
436 "mv" args
437 '((?f "force" nil force
438 "remove existing destinations, never prompt")
439 (?i "interactive" nil interactive
440 "request confirmation if target already exists")
441 (?n "preview" nil preview
442 "don't change anything on disk")
443 (?v "verbose" nil verbose
444 "explain what is being done")
445 (nil "help" nil nil "show this usage screen")
446 :external "mv"
447 :show-usage
448 :usage "[OPTION]... SOURCE DEST
449 or: mv [OPTION]... SOURCE... DIRECTORY
450Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
451\[OPTION] DIRECTORY...")
452 (let ((no-dereference t))
453 (eshell-mvcp-template "mv" "moving" 'rename-file
454 eshell-mv-interactive-query
455 eshell-mv-overwrite-files))))
456
457(defun eshell/cp (&rest args)
458 "Implementation of cp in Lisp."
459 (eshell-eval-using-options
460 "cp" args
461 '((?a "archive" nil archive
462 "same as -dpR")
463 (?d "no-dereference" nil no-dereference
464 "preserve links")
465 (?f "force" nil force
466 "remove existing destinations, never prompt")
467 (?i "interactive" nil interactive
468 "request confirmation if target already exists")
469 (?n "preview" nil preview
470 "don't change anything on disk")
471 (?p "preserve" nil preserve
472 "preserve file attributes if possible")
473 (?R "recursive" nil recursive
474 "copy directories recursively")
475 (?v "verbose" nil verbose
476 "explain what is being done")
477 (nil "help" nil nil "show this usage screen")
478 :external "cp"
479 :show-usage
480 :usage "[OPTION]... SOURCE DEST
481 or: cp [OPTION]... SOURCE... DIRECTORY
482Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.")
483 (if archive
484 (setq preserve t no-dereference t recursive t))
485 (eshell-mvcp-template "cp" "copying" 'copy-file
486 eshell-cp-interactive-query
487 eshell-cp-overwrite-files preserve)))
488
489(defun eshell/ln (&rest args)
490 "Implementation of ln in Lisp."
491 (eshell-eval-using-options
492 "ln" args
493 '((?h "help" nil nil "show this usage screen")
494 (?s "symbolic" nil symbolic
495 "make symbolic links instead of hard links")
496 (?i "interactive" nil interactive "request confirmation if target already exists")
497 (?f "force" nil force "remove existing destinations, never prompt")
498 (?n "preview" nil preview
499 "don't change anything on disk")
500 (?v "verbose" nil verbose "explain what is being done")
501 :external "ln"
502 :show-usage
503 :usage "[OPTION]... TARGET [LINK_NAME]
504 or: ln [OPTION]... TARGET... DIRECTORY
505Create a link to the specified TARGET with optional LINK_NAME. If there is
506more than one TARGET, the last argument must be a directory; create links
507in DIRECTORY to each TARGET. Create hard links by default, symbolic links
508with '--symbolic'. When creating hard links, each TARGET must exist.")
509 (let (target no-dereference)
510 (if (> (length args) 1)
511 (progn
512 (setq target (car (last args)))
513 (setcdr (last args 2) nil))
514 (setq args nil))
515 (eshell-shuffle-files "ln" "linking" args target
516 (if symbolic
517 'make-symbolic-link
518 'add-name-to-file) nil
519 (if (and (or interactive
520 eshell-ln-interactive-query)
521 (not force))
522 1 (or force eshell-ln-overwrite-files))))
523 nil))
524
525(defun eshell/cat (&rest args)
526 "Implementation of cat in Lisp."
527 (if eshell-in-pipeline-p
528 (throw 'eshell-replace-command
529 (eshell-parse-command "*cat" args))
530 (eshell-init-print-buffer)
531 (eshell-eval-using-options
532 "cat" args
533 '((?h "help" nil nil "show this usage screen")
534 :external "cat"
535 :show-usage
536 :usage "[OPTION] FILE...
537Concatenate FILE(s), or standard input, to standard output.")
538 (eshell-for file args
539 (if (string= file "-")
540 (throw 'eshell-external
541 (eshell-external-command "cat" args))))
542 (let ((curbuf (current-buffer)))
543 (eshell-for file args
544 (with-temp-buffer
545 (insert-file-contents file)
546 (goto-char (point-min))
547 (while (not (eobp))
548 (let ((str (buffer-substring
549 (point) (min (1+ (line-end-position))
550 (point-max)))))
551 (with-current-buffer curbuf
552 (eshell-buffered-print str)))
553 (forward-line)))))
554 (eshell-flush)
555 ;; if the file does not end in a newline, do not emit one
556 (setq eshell-ensure-newline-p nil))))
557
558;; special front-end functions for compilation-mode buffers
559
560(defun eshell/make (&rest args)
561 "Use `compile' to do background makes."
562 (if (and eshell-current-subjob-p
563 (eshell-interactive-output-p))
564 (let ((compilation-process-setup-function
565 (list 'lambda nil
566 (list 'setq 'process-environment
567 (list 'quote (eshell-copy-environment))))))
568 (compile (concat "make " (eshell-flatten-and-stringify args))))
569 (throw 'eshell-replace-command
570 (eshell-parse-command "*make" args))))
571
572(defun eshell-occur-mode-goto-occurrence ()
573 "Go to the occurrence the current line describes."
574 (interactive)
575 (let ((pos (occur-mode-find-occurrence)))
576 (pop-to-buffer (marker-buffer pos))
577 (goto-char (marker-position pos))))
578
579(defun eshell-occur-mode-mouse-goto (event)
580 "In Occur mode, go to the occurrence whose line you click on."
581 (interactive "e")
582 (let (buffer pos)
583 (save-excursion
584 (set-buffer (window-buffer (posn-window (event-end event))))
585 (save-excursion
586 (goto-char (posn-point (event-end event)))
587 (setq pos (occur-mode-find-occurrence))
588 (setq buffer occur-buffer)))
589 (pop-to-buffer (marker-buffer pos))
590 (goto-char (marker-position pos))))
591
592(defun eshell-poor-mans-grep (args)
593 "A poor version of grep that opens every file and uses `occur'.
594This eats up memory, since it leaves the buffers open (to speed future
595searches), and it's very slow. But, if your system has no grep
596available..."
597 (save-selected-window
598 (let ((default-dir default-directory))
599 (with-current-buffer (get-buffer-create "*grep*")
600 (let ((inhibit-read-only t)
601 (default-directory default-dir))
602 (erase-buffer)
603 (occur-mode)
604 (let ((files (eshell-flatten-list (cdr args)))
605 (inhibit-redisplay t)
606 string)
607 (when (car args)
608 (if (get-buffer "*Occur*")
609 (kill-buffer (get-buffer "*Occur*")))
610 (setq string nil)
611 (while files
612 (with-current-buffer (find-file-noselect (car files))
613 (save-excursion
614 (ignore-errors
615 (occur (car args))))
616 (if (get-buffer "*Occur*")
617 (with-current-buffer (get-buffer "*Occur*")
618 (setq string (buffer-string))
619 (kill-buffer (current-buffer)))))
620 (if string (insert string))
621 (setq string nil
622 files (cdr files)))))
623 (setq occur-buffer (current-buffer))
624 (local-set-key [mouse-2] 'eshell-occur-mode-mouse-goto)
625 (local-set-key [(control ?c) (control ?c)]
626 'eshell-occur-mode-goto-occurrence)
627 (local-set-key [(control ?m)]
628 'eshell-occur-mode-goto-occurrence)
629 (local-set-key [return] 'eshell-occur-mode-goto-occurrence)
630 (pop-to-buffer (current-buffer) t)
631 (goto-char (point-min))
632 (resize-temp-buffer-window))))))
633
634(defun eshell-grep (command args &optional maybe-use-occur)
635 "Generic service function for the various grep aliases.
636It calls Emacs' grep utility if the command is not redirecting output,
637and if it's not part of a command pipeline. Otherwise, it calls the
638external command."
639 (if (and maybe-use-occur eshell-no-grep-available)
640 (eshell-poor-mans-grep args)
641 (if (or eshell-plain-grep-behavior
642 (not (and (eshell-interactive-output-p)
643 (not eshell-in-pipeline-p)
644 (not eshell-in-subcommand-p))))
645 (throw 'eshell-replace-command
646 (eshell-parse-command (concat "*" command) args))
647 (let* ((compilation-process-setup-function
648 (list 'lambda nil
649 (list 'setq 'process-environment
650 (list 'quote (eshell-copy-environment)))))
651 (args (mapconcat 'identity
652 (mapcar 'shell-quote-argument
653 (eshell-flatten-list args))
654 " "))
655 (cmd (progn
656 (set-text-properties 0 (length args)
657 '(invisible t) args)
658 (format "%s -n %s" command args)))
659 compilation-scroll-output)
660 (grep cmd)))))
661
662(defun eshell/grep (&rest args)
663 "Use Emacs grep facility instead of calling external grep."
664 (eshell-grep "grep" args t))
665
666(defun eshell/egrep (&rest args)
667 "Use Emacs grep facility instead of calling external egrep."
668 (eshell-grep "egrep" args t))
669
670(defun eshell/fgrep (&rest args)
671 "Use Emacs grep facility instead of calling external fgrep."
672 (eshell-grep "fgrep" args t))
673
674(defun eshell/agrep (&rest args)
675 "Use Emacs grep facility instead of calling external agrep."
676 (eshell-grep "agrep" args))
677
678(defun eshell/glimpse (&rest args)
679 "Use Emacs grep facility instead of calling external glimpse."
680 (let (null-device)
681 (eshell-grep "glimpse" (append '("-z" "-y") args))))
682
683;; completions rules for some common UNIX commands
684
685(defsubst eshell-complete-hostname ()
686 "Complete a command that wants a hostname for an argument."
687 (pcomplete-here (eshell-read-host-names)))
688
689(defun eshell-complete-host-reference ()
690 "If there is a host reference, complete it."
691 (let ((arg (pcomplete-actual-arg))
692 index)
693 (when (setq index (string-match "@[a-z.]*\\'" arg))
694 (setq pcomplete-stub (substring arg (1+ index))
695 pcomplete-last-completion-raw t)
696 (throw 'pcomplete-completions (eshell-read-host-names)))))
697
698(defalias 'pcomplete/ftp 'eshell-complete-hostname)
699(defalias 'pcomplete/ncftp 'eshell-complete-hostname)
700(defalias 'pcomplete/ping 'eshell-complete-hostname)
701(defalias 'pcomplete/rlogin 'eshell-complete-hostname)
702
703(defun pcomplete/telnet ()
704 (require 'pcmpl-unix)
705 (pcomplete-opt "xl(pcmpl-unix-user-names)")
706 (eshell-complete-hostname))
707
708(defun pcomplete/rsh ()
709 "Complete `rsh', which, after the user and hostname, is like xargs."
710 (require 'pcmpl-unix)
711 (pcomplete-opt "l(pcmpl-unix-user-names)")
712 (eshell-complete-hostname)
713 (pcomplete-here (funcall pcomplete-command-completion-function))
714 (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
715 pcomplete-default-completion-function)))
716
717(defalias 'pcomplete/ssh 'pcomplete/rsh)
718
719(eval-when-compile
720 (defvar block-size)
721 (defvar by-bytes)
722 (defvar dereference-links)
723 (defvar grand-total)
724 (defvar human-readable)
725 (defvar max-depth)
726 (defvar only-one-filesystem)
727 (defvar show-all))
728
729(defsubst eshell-du-size-string (size)
730 (let* ((str (eshell-printable-size size human-readable block-size t))
731 (len (length str)))
732 (concat str (if (< len 8)
733 (make-string (- 8 len) ? )))))
734
735(defun eshell-du-sum-directory (path depth)
736 "Summarize PATH, and its member directories."
737 (let ((entries (eshell-directory-files-and-attributes path))
738 (size 0.0))
739 (while entries
740 (unless (string-match "\\`\\.\\.?\\'" (caar entries))
741 (let* ((entry (concat path (char-to-string directory-sep-char)
742 (caar entries)))
743 (symlink (and (stringp (cadr (car entries)))
744 (cadr (car entries)))))
745 (unless (or (and symlink (not dereference-links))
746 (and only-one-filesystem
747 (not (= only-one-filesystem
748 (nth 12 (car entries))))))
749 (if symlink
750 (setq entry symlink))
751 (setq size
752 (+ size
753 (if (eq t (cadr (car entries)))
754 (eshell-du-sum-directory entry (1+ depth))
755 (let ((file-size (nth 8 (car entries))))
756 (prog1
757 file-size
758 (if show-all
759 (eshell-print
760 (concat (eshell-du-size-string file-size)
761 entry "\n")))))))))))
762 (setq entries (cdr entries)))
763 (if (or (not max-depth)
764 (= depth max-depth)
765 (= depth 0))
766 (eshell-print (concat (eshell-du-size-string size)
767 (directory-file-name path) "\n")))
768 size))
769
770(defun eshell/du (&rest args)
771 "Implementation of \"du\" in Lisp, passing RAGS."
772 (if (eshell-search-path "du")
773 (throw 'eshell-replace-command
774 (eshell-parse-command "*du" args))
775 (eshell-eval-using-options
776 "du" args
777 '((?a "all" nil show-all
778 "write counts for all files, not just directories")
779 (nil "block-size" t block-size
780 "use SIZE-byte blocks (i.e., --block-size SIZE)")
781 (?b "bytes" nil by-bytes
782 "print size in bytes")
783 (?c "total" nil grand-total
784 "produce a grand total")
785 (?d "max-depth" t max-depth
786 "display data only this many levels of data")
787 (?h "human-readable" 1024 human-readable
788 "print sizes in human readable format")
789 (?H "is" 1000 human-readable
790 "likewise, but use powers of 1000 not 1024")
791 (?k "kilobytes" 1024 block-size
792 "like --block-size 1024")
793 (?L "dereference" nil dereference-links
794 "dereference all symbolic links")
795 (?m "megabytes" 1048576 block-size
796 "like --block-size 1048576")
797 (?s "summarize" 0 max-depth
798 "display only a total for each argument")
799 (?x "one-file-system" nil only-one-filesystem
800 "skip directories on different filesystems")
801 (nil "help" nil nil
802 "show this usage screen")
803 :external "du"
804 :usage "[OPTION]... FILE...
805Summarize disk usage of each FILE, recursively for directories.")
806 (unless by-bytes
807 (setq block-size (or block-size 1024)))
808 (if (and max-depth (stringp max-depth))
809 (setq max-depth (string-to-int max-depth)))
810 ;; filesystem support means nothing under Windows
811 (if (eshell-under-windows-p)
812 (setq only-one-filesystem nil))
813 (unless args
814 (setq args '(".")))
815 (let ((size 0.0))
816 (while args
817 (if only-one-filesystem
818 (setq only-one-filesystem
819 (nth 11 (file-attributes
820 (file-name-as-directory (car args))))))
821 (setq size (+ size (eshell-du-sum-directory
822 (directory-file-name (car args)) 0)))
823 (setq args (cdr args)))
824 (if grand-total
825 (eshell-print (concat (eshell-du-size-string size)
826 "total\n")))))))
827
828(defvar eshell-time-start nil)
829
830(defun eshell-show-elapsed-time ()
831 (let ((elapsed (format "%.3f secs\n"
832 (- (eshell-time-to-seconds (current-time))
833 eshell-time-start))))
834 (set-text-properties 0 (length elapsed) '(face bold) elapsed)
835 (eshell-interactive-print elapsed))
836 (remove-hook 'eshell-post-command-hook 'eshell-show-elapsed-time t))
837
838(defun eshell/time (&rest args)
839 "Implementation of \"time\" in Lisp."
840 (let ((time-args (copy-alist args))
841 (continue t)
842 last-arg)
843 (while (and continue args)
844 (if (not (string-match "^-" (car args)))
845 (progn
846 (if last-arg
847 (setcdr last-arg nil)
848 (setq args '("")))
849 (setq continue nil))
850 (setq last-arg args
851 args (cdr args))))
852 (eshell-eval-using-options
853 "time" args
854 '((?h "help" nil nil "show this usage screen")
855 :external "time"
856 :show-usage
857 :usage "COMMAND...
858Show wall-clock time elapsed during execution of COMMAND.")
859 (setq eshell-time-start (eshell-time-to-seconds (current-time)))
860 (add-hook 'eshell-post-command-hook 'eshell-show-elapsed-time nil t)
861 ;; after setting
862 (throw 'eshell-replace-command
863 (eshell-parse-command (car time-args) (cdr time-args))))))
864
865(defalias 'eshell/whoami 'user-login-name)
866
867(defvar eshell-diff-window-config nil)
868
869(defun eshell-diff-quit ()
870 "Restore the window configuration previous to diff'ing."
871 (interactive)
872 (if eshell-diff-window-config
873 (set-window-configuration eshell-diff-window-config)))
874
875(defun eshell/diff (&rest args)
876 "Alias \"diff\" to call Emacs `diff' function."
877 (if (or eshell-plain-diff-behavior
878 (not (and (eshell-interactive-output-p)
879 (not eshell-in-pipeline-p)
880 (not eshell-in-subcommand-p))))
881 (throw 'eshell-replace-command
882 (eshell-parse-command "*diff" args))
883 (setq args (eshell-flatten-list args))
884 (if (< (length args) 2)
885 (error "diff: missing operand"))
886 (let ((old (car (last args 2)))
887 (new (car (last args)))
888 (config (current-window-configuration)))
889 (if (= (length args) 2)
890 (setq args nil)
891 (setcdr (last args 3) nil))
892 (with-current-buffer
893 (diff old new (eshell-flatten-and-stringify args))
894 (when (fboundp 'diff-mode)
895 (diff-mode)
896 (set (make-local-variable 'eshell-diff-window-config) config)
897 (local-set-key [?q] 'eshell-diff-quit)
898 (if (fboundp 'turn-on-font-lock-if-enabled)
899 (turn-on-font-lock-if-enabled))))
900 (other-window 1)
901 (goto-char (point-min))
902 nil)))
903
904(defun eshell/locate (&rest args)
905 "Alias \"locate\" to call Emacs `locate' function."
906 (if (or eshell-plain-locate-behavior
907 (not (and (eshell-interactive-output-p)
908 (not eshell-in-pipeline-p)
909 (not eshell-in-subcommand-p)))
910 (and (stringp (car args))
911 (string-match "^-" (car args))))
912 (throw 'eshell-replace-command
913 (eshell-parse-command "*locate" args))
914 (save-selected-window
915 (let ((locate-history-list (list (car args))))
916 (locate-with-filter (car args) (cadr args))))))
917
918(defun eshell/occur (&rest args)
919 "Alias \"occur\" to call Emacs `occur' function."
920 (let ((inhibit-read-only t))
921 (if args
922 (error "usage: occur: (REGEXP)")
923 (occur (car args)))))
924
925;;; Code:
926
927;;; em-unix.el ends here