em-unix.el 38 KB

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