em-unix.el 35 KB

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