diff options
| author | Gerd Moellmann | 2000-06-23 04:39:18 +0000 |
|---|---|---|
| committer | Gerd Moellmann | 2000-06-23 04:39:18 +0000 |
| commit | 26b4dc849fc474ff9f1c6cc0a9f33f3c2cf086ba (patch) | |
| tree | 862f408c0c6eedefc366004596424e5647b5d2fd | |
| parent | 0908a1e3192261cfc94abc95ae48f2d54380d576 (diff) | |
| download | emacs-26b4dc849fc474ff9f1c6cc0a9f33f3c2cf086ba.tar.gz emacs-26b4dc849fc474ff9f1c6cc0a9f33f3c2cf086ba.zip | |
*** empty log message ***
| -rw-r--r-- | lisp/ChangeLog | 8 | ||||
| -rw-r--r-- | lisp/eshell/esh-mode.el | 1006 | ||||
| -rw-r--r-- | lisp/eshell/esh-util.el | 676 |
3 files changed, 1690 insertions, 0 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 655ddd13c53..61460d51a29 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,11 @@ | |||
| 1 | 2000-06-23 Gerd Moellmann <gerd@gnu.org> | ||
| 2 | |||
| 3 | * subdirs.el: Add eshell subdirectory. | ||
| 4 | |||
| 5 | * eshell: New subdirectory containing the Eshell package. | ||
| 6 | |||
| 7 | * pcomplete.el: New file. | ||
| 8 | |||
| 1 | 2000-06-22 Eli Zaretskii <eliz@is.elta.co.il> | 9 | 2000-06-22 Eli Zaretskii <eliz@is.elta.co.il> |
| 2 | 10 | ||
| 3 | * files.el (make-backup-file-name-1): On DOS/Windows, run the | 11 | * files.el (make-backup-file-name-1): On DOS/Windows, run the |
diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el new file mode 100644 index 00000000000..6df4602f9f6 --- /dev/null +++ b/lisp/eshell/esh-mode.el | |||
| @@ -0,0 +1,1006 @@ | |||
| 1 | ;;; esh-mode --- user interface | ||
| 2 | |||
| 3 | ;; Copyright (C) 1999, 2000 Free Sofware Foundation | ||
| 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 'esh-mode) | ||
| 23 | |||
| 24 | (eval-when-compile (require 'esh-maint)) | ||
| 25 | |||
| 26 | (defgroup eshell-mode nil | ||
| 27 | "This module contains code for handling input from the user." | ||
| 28 | :tag "User interface" | ||
| 29 | :group 'eshell) | ||
| 30 | |||
| 31 | ;;; Commentary: | ||
| 32 | |||
| 33 | ;; Basically, Eshell is used just like shell mode (<M-x shell>). The | ||
| 34 | ;; keystrokes for navigating the buffer, and accessing the command | ||
| 35 | ;; history, are identical. Unlike shell mode, however, Eshell mode's | ||
| 36 | ;; governing process is Emacs itself. With shell mode, an inferior | ||
| 37 | ;; shell process is executed that communicates with Emacs via comint | ||
| 38 | ;; -- a mode for handling sub-process interaction. Eshell mode, on | ||
| 39 | ;; the other hand, is a truly native Emacs shell. No subprocess are | ||
| 40 | ;; invoked except the ones requested by the user at the prompt. | ||
| 41 | ;; | ||
| 42 | ;; After entering a command, use <RET> to invoke it ([Command | ||
| 43 | ;; invocation]) . If there is a command on disk, it will be executed | ||
| 44 | ;; as in a normal shell. If there is no command by that name on disk, | ||
| 45 | ;; but a Lisp function with that name is defined, the Lisp function | ||
| 46 | ;; will be called, using the arguments passed on the command line. | ||
| 47 | ;; | ||
| 48 | ;; Some of the other features of the command interaction mode are: | ||
| 49 | ;; | ||
| 50 | ;; @ <M-RET> can be used to accumulate further commands while a | ||
| 51 | ;; command is currently running. Since all input is passed to the | ||
| 52 | ;; subprocess being executed, there is no automatic input queueing | ||
| 53 | ;; as there is with other shells. | ||
| 54 | ;; | ||
| 55 | ;; @ <C-c C-t> can be used to truncate the buffer if it grows too | ||
| 56 | ;; large. | ||
| 57 | ;; | ||
| 58 | ;; @ <C-c C-r> will move point to the beginning of the output of the | ||
| 59 | ;; last command. With a prefix argument, it will narrow to view | ||
| 60 | ;; only that output. | ||
| 61 | ;; | ||
| 62 | ;; @ <C-c C-o> will delete the output from the last command. | ||
| 63 | ;; | ||
| 64 | ;; @ <C-c C-f> will move forward a complete shell argument. | ||
| 65 | ;; | ||
| 66 | ;; @ <C-c C-b> will move backward a complete shell argument. | ||
| 67 | |||
| 68 | (require 'esh-module) | ||
| 69 | (require 'esh-cmd) | ||
| 70 | (require 'esh-io) | ||
| 71 | (require 'esh-var) | ||
| 72 | |||
| 73 | ;;; User Variables: | ||
| 74 | |||
| 75 | (defcustom eshell-mode-unload-hook nil | ||
| 76 | "*A hook that gets run when `eshell-mode' is unloaded." | ||
| 77 | :type 'hook | ||
| 78 | :group 'eshell-mode) | ||
| 79 | |||
| 80 | (defcustom eshell-mode-hook nil | ||
| 81 | "*A hook that gets run when `eshell-mode' is entered." | ||
| 82 | :type 'hook | ||
| 83 | :group 'eshell-mode) | ||
| 84 | |||
| 85 | (defcustom eshell-first-time-mode-hook nil | ||
| 86 | "*A hook that gets run the first time `eshell-mode' is entered. | ||
| 87 | That is to say, the first time during an Emacs session." | ||
| 88 | :type 'hook | ||
| 89 | :group 'eshell-mode) | ||
| 90 | |||
| 91 | (defcustom eshell-exit-hook '(eshell-query-kill-processes) | ||
| 92 | "*A hook that is run whenever `eshell' is exited. | ||
| 93 | This hook is only run if exiting actually kills the buffer." | ||
| 94 | :type 'hook | ||
| 95 | :group 'eshell-mode) | ||
| 96 | |||
| 97 | (defcustom eshell-kill-on-exit t | ||
| 98 | "*If non-nil, kill the Eshell buffer on the `exit' command. | ||
| 99 | Otherwise, the buffer will simply be buried." | ||
| 100 | :type 'boolean | ||
| 101 | :group 'eshell-mode) | ||
| 102 | |||
| 103 | (defcustom eshell-input-filter-functions nil | ||
| 104 | "*Functions to call before input is processed. | ||
| 105 | The input is contained in the region from `eshell-last-input-start' to | ||
| 106 | `eshell-last-input-end'." | ||
| 107 | :type 'hook | ||
| 108 | :group 'eshell-mode) | ||
| 109 | |||
| 110 | (defcustom eshell-expand-input-functions nil | ||
| 111 | "*Functions to call before input is parsed. | ||
| 112 | Each function is passed two arguments, which bounds the region of the | ||
| 113 | current input text." | ||
| 114 | :type 'hook | ||
| 115 | :group 'eshell-mode) | ||
| 116 | |||
| 117 | (defcustom eshell-scroll-to-bottom-on-input nil | ||
| 118 | "*Controls whether input to interpreter causes window to scroll. | ||
| 119 | If nil, then do not scroll. If t or `all', scroll all windows showing | ||
| 120 | buffer. If `this', scroll only the selected window. | ||
| 121 | |||
| 122 | See `eshell-preinput-scroll-to-bottom'." | ||
| 123 | :type '(radio (const :tag "Do not scroll Eshell windows" nil) | ||
| 124 | (const :tag "Scroll all windows showing the buffer" all) | ||
| 125 | (const :tag "Scroll only the selected window" this)) | ||
| 126 | :group 'eshell-mode) | ||
| 127 | |||
| 128 | (defcustom eshell-scroll-to-bottom-on-output nil | ||
| 129 | "*Controls whether interpreter output causes window to scroll. | ||
| 130 | If nil, then do not scroll. If t or `all', scroll all windows showing | ||
| 131 | buffer. If `this', scroll only the selected window. If `others', | ||
| 132 | scroll only those that are not the selected window. | ||
| 133 | |||
| 134 | See variable `eshell-scroll-show-maximum-output' and function | ||
| 135 | `eshell-postoutput-scroll-to-bottom'." | ||
| 136 | :type '(radio (const :tag "Do not scroll Eshell windows" nil) | ||
| 137 | (const :tag "Scroll all windows showing the buffer" all) | ||
| 138 | (const :tag "Scroll only the selected window" this) | ||
| 139 | (const :tag "Scroll all windows other than selected" this)) | ||
| 140 | :group 'eshell-mode) | ||
| 141 | |||
| 142 | (defcustom eshell-scroll-show-maximum-output t | ||
| 143 | "*Controls how interpreter output causes window to scroll. | ||
| 144 | If non-nil, then show the maximum output when the window is scrolled. | ||
| 145 | |||
| 146 | See variable `eshell-scroll-to-bottom-on-output' and function | ||
| 147 | `eshell-postoutput-scroll-to-bottom'." | ||
| 148 | :type 'boolean | ||
| 149 | :group 'eshell-mode) | ||
| 150 | |||
| 151 | (defcustom eshell-buffer-maximum-lines 1024 | ||
| 152 | "*The maximum size in lines for eshell buffers. | ||
| 153 | Eshell buffers are truncated from the top to be no greater than this | ||
| 154 | number, if the function `eshell-truncate-buffer' is on | ||
| 155 | `eshell-output-filter-functions'." | ||
| 156 | :type 'integer | ||
| 157 | :group 'eshell-mode) | ||
| 158 | |||
| 159 | (defcustom eshell-output-filter-functions | ||
| 160 | '(eshell-handle-control-codes | ||
| 161 | eshell-watch-for-password-prompt) | ||
| 162 | "*Functions to call before output is displayed. | ||
| 163 | These functions are only called for output that is displayed | ||
| 164 | interactively, and not for output which is redirected." | ||
| 165 | :type 'hook | ||
| 166 | :group 'eshell-mode) | ||
| 167 | |||
| 168 | (defcustom eshell-preoutput-filter-functions nil | ||
| 169 | "*Functions to call before output is inserted into the buffer. | ||
| 170 | These functions get one argument, a string containing the text to be | ||
| 171 | inserted. They return the string as it should be inserted." | ||
| 172 | :type 'hook | ||
| 173 | :group 'eshell-mode) | ||
| 174 | |||
| 175 | (defcustom eshell-password-prompt-regexp | ||
| 176 | "\\(\\([Oo]ld \\|[Nn]ew \\|Kerberos \\|CVS \\|'s \\|login \\|^\\)\ | ||
| 177 | [Pp]assword\\|pass phrase\\|\\(Enter\\|Repeat\\) passphrase\\)\ | ||
| 178 | \\( for [^@ \t\n]+@[^@ \t\n]+\\)?:\\s *\\'" | ||
| 179 | "*Regexp matching prompts for passwords in the inferior process. | ||
| 180 | This is used by `eshell-watch-for-password-prompt'." | ||
| 181 | :type 'regexp | ||
| 182 | :group 'eshell-mode) | ||
| 183 | |||
| 184 | (defcustom eshell-skip-prompt-function nil | ||
| 185 | "*A function called from beginning of line to skip the prompt." | ||
| 186 | :type 'function | ||
| 187 | :group 'eshell-mode) | ||
| 188 | |||
| 189 | (defcustom eshell-status-in-modeline t | ||
| 190 | "*If non-nil, let the user know a command is running in the modeline." | ||
| 191 | :type 'boolean | ||
| 192 | :group 'eshell-mode) | ||
| 193 | |||
| 194 | (defvar eshell-non-interactive-p nil | ||
| 195 | "A variable which is non-nil when Eshell is not running interactively. | ||
| 196 | Modules should use this variable so that they don't clutter non-interactive | ||
| 197 | sessions, such as when using `eshell-command'.") | ||
| 198 | |||
| 199 | (defvar eshell-first-time-p t | ||
| 200 | "A variable which is non-nil the first time Eshell is loaded.") | ||
| 201 | |||
| 202 | ;; Internal Variables: | ||
| 203 | |||
| 204 | ;; these are only set to `nil' initially for the sake of the | ||
| 205 | ;; byte-compiler, when compiling other files which `require' this one | ||
| 206 | (defvar eshell-mode nil) | ||
| 207 | (defvar eshell-mode-map nil) | ||
| 208 | (defvar eshell-command-running-string "--") | ||
| 209 | (defvar eshell-command-map nil) | ||
| 210 | (defvar eshell-command-prefix nil) | ||
| 211 | (defvar eshell-last-input-start nil) | ||
| 212 | (defvar eshell-last-input-end nil) | ||
| 213 | (defvar eshell-last-output-start nil) | ||
| 214 | (defvar eshell-last-output-block-begin nil) | ||
| 215 | (defvar eshell-last-output-end nil) | ||
| 216 | |||
| 217 | (defvar eshell-currently-handling-window nil) | ||
| 218 | (defvar eshell-mode-syntax-table nil) | ||
| 219 | (defvar eshell-mode-abbrev-table nil) | ||
| 220 | |||
| 221 | (define-abbrev-table 'eshell-mode-abbrev-table ()) | ||
| 222 | |||
| 223 | (eval-when-compile | ||
| 224 | (unless (eshell-under-xemacs-p) | ||
| 225 | (defalias 'characterp 'ignore) | ||
| 226 | (defalias 'char-int 'ignore))) | ||
| 227 | |||
| 228 | (if (not eshell-mode-syntax-table) | ||
| 229 | (let ((i 0)) | ||
| 230 | (setq eshell-mode-syntax-table (make-syntax-table)) | ||
| 231 | (while (< i ?0) | ||
| 232 | (modify-syntax-entry i "_ " eshell-mode-syntax-table) | ||
| 233 | (setq i (1+ i))) | ||
| 234 | (setq i (1+ ?9)) | ||
| 235 | (while (< i ?A) | ||
| 236 | (modify-syntax-entry i "_ " eshell-mode-syntax-table) | ||
| 237 | (setq i (1+ i))) | ||
| 238 | (setq i (1+ ?Z)) | ||
| 239 | (while (< i ?a) | ||
| 240 | (modify-syntax-entry i "_ " eshell-mode-syntax-table) | ||
| 241 | (setq i (1+ i))) | ||
| 242 | (setq i (1+ ?z)) | ||
| 243 | (while (< i 128) | ||
| 244 | (modify-syntax-entry i "_ " eshell-mode-syntax-table) | ||
| 245 | (setq i (1+ i))) | ||
| 246 | (modify-syntax-entry ? " " eshell-mode-syntax-table) | ||
| 247 | (modify-syntax-entry ?\t " " eshell-mode-syntax-table) | ||
| 248 | (modify-syntax-entry ?\f " " eshell-mode-syntax-table) | ||
| 249 | (modify-syntax-entry ?\n "> " eshell-mode-syntax-table) | ||
| 250 | ;; Give CR the same syntax as newline, for selective-display. | ||
| 251 | (modify-syntax-entry ?\^m "> " eshell-mode-syntax-table) | ||
| 252 | ;;; (modify-syntax-entry ?\; "< " eshell-mode-syntax-table) | ||
| 253 | (modify-syntax-entry ?` "' " eshell-mode-syntax-table) | ||
| 254 | (modify-syntax-entry ?' "' " eshell-mode-syntax-table) | ||
| 255 | (modify-syntax-entry ?, "' " eshell-mode-syntax-table) | ||
| 256 | ;; Used to be singlequote; changed for flonums. | ||
| 257 | (modify-syntax-entry ?. "_ " eshell-mode-syntax-table) | ||
| 258 | (modify-syntax-entry ?- "_ " eshell-mode-syntax-table) | ||
| 259 | (modify-syntax-entry ?| ". " eshell-mode-syntax-table) | ||
| 260 | (modify-syntax-entry ?# "' " eshell-mode-syntax-table) | ||
| 261 | (modify-syntax-entry ?\" "\" " eshell-mode-syntax-table) | ||
| 262 | (modify-syntax-entry ?\\ "/ " eshell-mode-syntax-table) | ||
| 263 | (modify-syntax-entry ?\( "() " eshell-mode-syntax-table) | ||
| 264 | (modify-syntax-entry ?\) ")( " eshell-mode-syntax-table) | ||
| 265 | (modify-syntax-entry ?\{ "(} " eshell-mode-syntax-table) | ||
| 266 | (modify-syntax-entry ?\} "){ " eshell-mode-syntax-table) | ||
| 267 | (modify-syntax-entry ?\[ "(] " eshell-mode-syntax-table) | ||
| 268 | (modify-syntax-entry ?\] ")[ " eshell-mode-syntax-table) | ||
| 269 | ;; All non-word multibyte characters should be `symbol'. | ||
| 270 | (if (eshell-under-xemacs-p) | ||
| 271 | (map-char-table | ||
| 272 | (function | ||
| 273 | (lambda (key val) | ||
| 274 | (and (characterp key) | ||
| 275 | (>= (char-int key) 256) | ||
| 276 | (/= (char-syntax key) ?w) | ||
| 277 | (modify-syntax-entry key "_ " | ||
| 278 | eshell-mode-syntax-table)))) | ||
| 279 | (standard-syntax-table)) | ||
| 280 | (map-char-table | ||
| 281 | (function | ||
| 282 | (lambda (key val) | ||
| 283 | (and (>= key 256) | ||
| 284 | (/= (char-syntax key) ?w) | ||
| 285 | (modify-syntax-entry key "_ " | ||
| 286 | eshell-mode-syntax-table)))) | ||
| 287 | (standard-syntax-table))))) | ||
| 288 | |||
| 289 | ;;; User Functions: | ||
| 290 | |||
| 291 | ;;;###autoload | ||
| 292 | (defun eshell-mode () | ||
| 293 | "Emacs shell interactive mode. | ||
| 294 | |||
| 295 | \\{eshell-mode-map}" | ||
| 296 | (kill-all-local-variables) | ||
| 297 | |||
| 298 | (setq major-mode 'eshell-mode) | ||
| 299 | (setq mode-name "EShell") | ||
| 300 | (set (make-local-variable 'eshell-mode) t) | ||
| 301 | |||
| 302 | (make-local-variable 'eshell-mode-map) | ||
| 303 | (setq eshell-mode-map (make-sparse-keymap)) | ||
| 304 | (use-local-map eshell-mode-map) | ||
| 305 | |||
| 306 | (when eshell-status-in-modeline | ||
| 307 | (make-local-variable 'eshell-command-running-string) | ||
| 308 | (let ((fmt (copy-list mode-line-format))) | ||
| 309 | (make-local-variable 'mode-line-format) | ||
| 310 | (setq mode-line-format fmt)) | ||
| 311 | (let ((modeline (memq 'mode-line-modified mode-line-format))) | ||
| 312 | (if modeline | ||
| 313 | (setcar modeline 'eshell-command-running-string)))) | ||
| 314 | |||
| 315 | (define-key eshell-mode-map [return] 'eshell-send-input) | ||
| 316 | (define-key eshell-mode-map [(control ?m)] 'eshell-send-input) | ||
| 317 | (define-key eshell-mode-map [(control ?j)] 'eshell-send-input) | ||
| 318 | (define-key eshell-mode-map [(meta return)] 'eshell-queue-input) | ||
| 319 | (define-key eshell-mode-map [(meta control ?m)] 'eshell-queue-input) | ||
| 320 | (define-key eshell-mode-map [(meta control ?l)] 'eshell-show-output) | ||
| 321 | |||
| 322 | (set (make-local-variable 'eshell-command-prefix) | ||
| 323 | (make-symbol "eshell-command-prefix")) | ||
| 324 | (fset eshell-command-prefix (make-sparse-keymap)) | ||
| 325 | (set (make-local-variable 'eshell-command-map) | ||
| 326 | (symbol-function eshell-command-prefix)) | ||
| 327 | (define-key eshell-mode-map [(control ?c)] eshell-command-prefix) | ||
| 328 | |||
| 329 | (define-key eshell-command-map [(meta ?o)] 'eshell-mark-output) | ||
| 330 | |||
| 331 | (define-key eshell-command-map [(control ?a)] 'eshell-bol) | ||
| 332 | (define-key eshell-command-map [(control ?b)] 'eshell-backward-argument) | ||
| 333 | (define-key eshell-command-map [(control ?e)] 'eshell-show-maximum-output) | ||
| 334 | (define-key eshell-command-map [(control ?f)] 'eshell-forward-argument) | ||
| 335 | (define-key eshell-command-map [return] 'eshell-copy-old-input) | ||
| 336 | (define-key eshell-command-map [(control ?m)] 'eshell-copy-old-input) | ||
| 337 | (define-key eshell-command-map [(control ?o)] 'eshell-kill-output) | ||
| 338 | (define-key eshell-command-map [(control ?r)] 'eshell-show-output) | ||
| 339 | (define-key eshell-command-map [(control ?t)] 'eshell-truncate-buffer) | ||
| 340 | (define-key eshell-command-map [(control ?u)] 'eshell-kill-input) | ||
| 341 | (define-key eshell-command-map [(control ?w)] 'backward-kill-word) | ||
| 342 | |||
| 343 | (setq local-abbrev-table eshell-mode-abbrev-table) | ||
| 344 | (set-syntax-table eshell-mode-syntax-table) | ||
| 345 | |||
| 346 | (set (make-local-variable 'dired-directory) default-directory) | ||
| 347 | (set (make-local-variable 'list-buffers-directory) | ||
| 348 | (expand-file-name default-directory)) | ||
| 349 | |||
| 350 | ;; always set the tab width to 8 in Eshell buffers, since external | ||
| 351 | ;; commands which do their own formatting almost always expect this | ||
| 352 | (set (make-local-variable 'tab-width) 8) | ||
| 353 | |||
| 354 | ;; always display everything from a return value | ||
| 355 | (if (boundp 'print-length) | ||
| 356 | (set (make-local-variable 'print-length) nil)) | ||
| 357 | (if (boundp 'print-level) | ||
| 358 | (set (make-local-variable 'print-level) nil)) | ||
| 359 | |||
| 360 | ;; set require-final-newline to nil; otherwise, all redirected | ||
| 361 | ;; output will end with a newline, whether or not the source | ||
| 362 | ;; indicated it! | ||
| 363 | (set (make-local-variable 'require-final-newline) nil) | ||
| 364 | |||
| 365 | (set (make-local-variable 'max-lisp-eval-depth) | ||
| 366 | (max 3000 max-lisp-eval-depth)) | ||
| 367 | (set (make-local-variable 'max-specpdl-size) | ||
| 368 | (max 6000 max-lisp-eval-depth)) | ||
| 369 | |||
| 370 | (set (make-local-variable 'eshell-last-input-start) (point-marker)) | ||
| 371 | (set (make-local-variable 'eshell-last-input-end) (point-marker)) | ||
| 372 | (set (make-local-variable 'eshell-last-output-start) (point-marker)) | ||
| 373 | (set (make-local-variable 'eshell-last-output-end) (point-marker)) | ||
| 374 | (set (make-local-variable 'eshell-last-output-block-begin) (point)) | ||
| 375 | |||
| 376 | (let ((modules-list (copy-list eshell-modules-list))) | ||
| 377 | (make-local-variable 'eshell-modules-list) | ||
| 378 | (setq eshell-modules-list modules-list)) | ||
| 379 | |||
| 380 | ;; load extension modules into memory. This will cause any global | ||
| 381 | ;; variables they define to be visible, since some of the core | ||
| 382 | ;; modules sometimes take advantage of their functionality if used. | ||
| 383 | (eshell-for module eshell-modules-list | ||
| 384 | (let ((module-fullname (symbol-name module)) | ||
| 385 | module-shortname) | ||
| 386 | (if (string-match "^eshell-\\(.*\\)" module-fullname) | ||
| 387 | (setq module-shortname | ||
| 388 | (concat "em-" (match-string 1 module-fullname)))) | ||
| 389 | (unless module-shortname | ||
| 390 | (error "Invalid Eshell module name: %s" module-fullname)) | ||
| 391 | (unless (featurep (intern module-shortname)) | ||
| 392 | (load module-shortname)))) | ||
| 393 | |||
| 394 | (unless (file-exists-p eshell-directory-name) | ||
| 395 | (eshell-make-private-directory eshell-directory-name t)) | ||
| 396 | |||
| 397 | ;; load core Eshell modules for this session | ||
| 398 | (eshell-for module (eshell-subgroups 'eshell) | ||
| 399 | (run-hooks (intern-soft (concat (symbol-name module) | ||
| 400 | "-load-hook")))) | ||
| 401 | |||
| 402 | ;; load extension modules for this session | ||
| 403 | (eshell-for module eshell-modules-list | ||
| 404 | (let ((load-hook (intern-soft (concat (symbol-name module) | ||
| 405 | "-load-hook")))) | ||
| 406 | (if (and load-hook (boundp load-hook)) | ||
| 407 | (run-hooks load-hook)))) | ||
| 408 | |||
| 409 | (when eshell-scroll-to-bottom-on-input | ||
| 410 | (make-local-hook 'pre-command-hook) | ||
| 411 | (add-hook 'pre-command-hook 'eshell-preinput-scroll-to-bottom t t)) | ||
| 412 | |||
| 413 | (when eshell-scroll-show-maximum-output | ||
| 414 | (set (make-local-variable 'scroll-conservatively) 1000)) | ||
| 415 | |||
| 416 | (when eshell-status-in-modeline | ||
| 417 | (make-local-hook 'eshell-pre-command-hook) | ||
| 418 | (add-hook 'eshell-pre-command-hook 'eshell-command-started nil t) | ||
| 419 | (make-local-hook 'eshell-post-command-hook) | ||
| 420 | (add-hook 'eshell-post-command-hook 'eshell-command-finished nil t)) | ||
| 421 | |||
| 422 | (make-local-hook 'kill-buffer-hook) | ||
| 423 | (add-hook 'kill-buffer-hook | ||
| 424 | (function | ||
| 425 | (lambda () | ||
| 426 | (run-hooks 'eshell-exit-hook))) t t) | ||
| 427 | |||
| 428 | (if eshell-first-time-p | ||
| 429 | (run-hooks 'eshell-first-time-mode-hook)) | ||
| 430 | (run-hooks 'eshell-mode-hook) | ||
| 431 | (run-hooks 'eshell-post-command-hook)) | ||
| 432 | |||
| 433 | (put 'eshell-mode 'mode-class 'special) | ||
| 434 | |||
| 435 | (eshell-deftest mode major-mode | ||
| 436 | "Major mode is correct" | ||
| 437 | (eq major-mode 'eshell-mode)) | ||
| 438 | |||
| 439 | (eshell-deftest mode eshell-mode-variable | ||
| 440 | "`eshell-mode' is true" | ||
| 441 | (eq eshell-mode t)) | ||
| 442 | |||
| 443 | (eshell-deftest var window-height | ||
| 444 | "LINES equals window height" | ||
| 445 | (eshell-command-result-p "= $LINES (window-height)" "t\n")) | ||
| 446 | |||
| 447 | (defun eshell-command-started () | ||
| 448 | "Indicate in the modeline that a command has started." | ||
| 449 | (setq eshell-command-running-string "**") | ||
| 450 | (force-mode-line-update)) | ||
| 451 | |||
| 452 | (defun eshell-command-finished () | ||
| 453 | "Indicate in the modeline that a command has finished." | ||
| 454 | (setq eshell-command-running-string "--") | ||
| 455 | (force-mode-line-update)) | ||
| 456 | |||
| 457 | (eshell-deftest mode command-running-p | ||
| 458 | "Modeline shows no command running" | ||
| 459 | (or (eshell-under-xemacs-p) | ||
| 460 | (not eshell-status-in-modeline) | ||
| 461 | (and (memq 'eshell-command-running-string mode-line-format) | ||
| 462 | (equal eshell-command-running-string "--")))) | ||
| 463 | |||
| 464 | ;;; Internal Functions: | ||
| 465 | |||
| 466 | (defun eshell-move-argument (limit func property arg) | ||
| 467 | "Move forward ARG arguments." | ||
| 468 | (catch 'eshell-incomplete | ||
| 469 | (eshell-parse-arguments (save-excursion (eshell-bol) (point)) | ||
| 470 | (line-end-position))) | ||
| 471 | (let ((pos | ||
| 472 | (save-excursion | ||
| 473 | (funcall func 1) | ||
| 474 | (while (and (> arg 0) | ||
| 475 | (not (= (point) limit))) | ||
| 476 | (if (get-text-property (point) property) | ||
| 477 | (setq arg (1- arg))) | ||
| 478 | (if (> arg 0) | ||
| 479 | (funcall func 1))) | ||
| 480 | (point)))) | ||
| 481 | (goto-char pos) | ||
| 482 | (if (and (eq func 'forward-char) | ||
| 483 | (= (1+ pos) limit)) | ||
| 484 | (forward-char 1)))) | ||
| 485 | |||
| 486 | (eshell-deftest arg forward-arg | ||
| 487 | "Move across command arguments" | ||
| 488 | (eshell-insert-command "echo $(+ 1 (- 4 3)) \"alpha beta\" file" 'ignore) | ||
| 489 | (let ((here (point)) begin valid) | ||
| 490 | (eshell-bol) | ||
| 491 | (setq begin (point)) | ||
| 492 | (eshell-forward-argument 4) | ||
| 493 | (setq valid (= here (point))) | ||
| 494 | (eshell-backward-argument 4) | ||
| 495 | (prog1 | ||
| 496 | (and valid (= begin (point))) | ||
| 497 | (eshell-bol) | ||
| 498 | (delete-region (point) (point-max))))) | ||
| 499 | |||
| 500 | (defun eshell-forward-argument (&optional arg) | ||
| 501 | "Move forward ARG arguments." | ||
| 502 | (interactive "p") | ||
| 503 | (eshell-move-argument (point-max) 'forward-char 'arg-end arg)) | ||
| 504 | |||
| 505 | (defun eshell-backward-argument (&optional arg) | ||
| 506 | "Move backward ARG arguments." | ||
| 507 | (interactive "p") | ||
| 508 | (eshell-move-argument (point-min) 'backward-char 'arg-begin arg)) | ||
| 509 | |||
| 510 | (defun eshell-bol () | ||
| 511 | "Goes to the beginning of line, then skips past the prompt, if any." | ||
| 512 | (interactive) | ||
| 513 | (beginning-of-line) | ||
| 514 | (and eshell-skip-prompt-function | ||
| 515 | (funcall eshell-skip-prompt-function))) | ||
| 516 | |||
| 517 | (defsubst eshell-push-command-mark () | ||
| 518 | "Push a mark at the end of the last input text." | ||
| 519 | (push-mark (1- eshell-last-input-end) t)) | ||
| 520 | |||
| 521 | (custom-add-option 'eshell-pre-command-hook 'eshell-push-command-mark) | ||
| 522 | |||
| 523 | (defsubst eshell-goto-input-start () | ||
| 524 | "Goto the start of the last command input. | ||
| 525 | Putting this function on `eshell-pre-command-hook' will mimic Plan 9's | ||
| 526 | 9term behavior." | ||
| 527 | (goto-char eshell-last-input-start)) | ||
| 528 | |||
| 529 | (custom-add-option 'eshell-pre-command-hook 'eshell-push-command-mark) | ||
| 530 | |||
| 531 | (defsubst eshell-interactive-print (string) | ||
| 532 | "Print STRING to the eshell display buffer." | ||
| 533 | (eshell-output-filter nil string)) | ||
| 534 | |||
| 535 | (defsubst eshell-begin-on-new-line () | ||
| 536 | "This function outputs a newline if not at beginning of line." | ||
| 537 | (save-excursion | ||
| 538 | (goto-char eshell-last-output-end) | ||
| 539 | (or (bolp) | ||
| 540 | (eshell-interactive-print "\n")))) | ||
| 541 | |||
| 542 | (defsubst eshell-reset (&optional no-hooks) | ||
| 543 | "Output a prompt on a new line, aborting any current input. | ||
| 544 | If NO-HOOKS is non-nil, then `eshell-post-command-hook' won't be run." | ||
| 545 | (goto-char (point-max)) | ||
| 546 | (setq eshell-last-input-start (point-marker) | ||
| 547 | eshell-last-input-end (point-marker) | ||
| 548 | eshell-last-output-start (point-marker) | ||
| 549 | eshell-last-output-block-begin (point) | ||
| 550 | eshell-last-output-end (point-marker)) | ||
| 551 | (eshell-begin-on-new-line) | ||
| 552 | (unless no-hooks | ||
| 553 | (run-hooks 'eshell-post-command-hook) | ||
| 554 | (goto-char (point-max)))) | ||
| 555 | |||
| 556 | (defun eshell-parse-command-input (beg end &optional args) | ||
| 557 | "Parse the command input from BEG to END. | ||
| 558 | The difference is that `eshell-parse-command' expects a complete | ||
| 559 | command string (and will error if it doesn't get one), whereas this | ||
| 560 | function will inform the caller whether more input is required. | ||
| 561 | |||
| 562 | If nil is returned, more input is necessary (probably because a | ||
| 563 | multi-line input string wasn't terminated properly). Otherwise, it | ||
| 564 | will return the parsed command." | ||
| 565 | (let (command) | ||
| 566 | (unless (catch 'eshell-incomplete | ||
| 567 | (ignore | ||
| 568 | (setq command | ||
| 569 | (eshell-parse-command (cons beg end) args t)))) | ||
| 570 | command))) | ||
| 571 | |||
| 572 | (defun eshell-update-markers (pmark) | ||
| 573 | "Update the input and output markers relative to point and PMARK." | ||
| 574 | (set-marker eshell-last-input-start pmark) | ||
| 575 | (set-marker eshell-last-input-end (point)) | ||
| 576 | (set-marker eshell-last-output-end (point))) | ||
| 577 | |||
| 578 | (defun eshell-queue-input (&optional use-region) | ||
| 579 | "Queue the current input text for execution by Eshell. | ||
| 580 | Particularly, don't send the text to the current process, even if it's | ||
| 581 | waiting for input." | ||
| 582 | (interactive "P") | ||
| 583 | (eshell-send-input use-region t)) | ||
| 584 | |||
| 585 | (eshell-deftest mode queue-input | ||
| 586 | "Queue command input" | ||
| 587 | (eshell-insert-command "sleep 2") | ||
| 588 | (eshell-insert-command "echo alpha" 'eshell-queue-input) | ||
| 589 | (let ((count 10)) | ||
| 590 | (while (and eshell-current-command | ||
| 591 | (> count 0)) | ||
| 592 | (sit-for 1 0) | ||
| 593 | (setq count (1- count)))) | ||
| 594 | (eshell-match-result "alpha\n")) | ||
| 595 | |||
| 596 | (defun eshell-send-input (&optional use-region queue-p no-newline) | ||
| 597 | "Send the input received to Eshell for parsing and processing.. | ||
| 598 | After `eshell-last-output-end', sends all text from that marker to | ||
| 599 | point as input. Before that marker, calls `eshell-get-old-input' to | ||
| 600 | retrieve old input, copies it to the end of the buffer, and sends it. | ||
| 601 | |||
| 602 | If USE-REGION is non-nil, the current region (between point and mark) | ||
| 603 | will be used as input. | ||
| 604 | |||
| 605 | If QUEUE-P is non-nil, input will be queued until the next prompt, | ||
| 606 | rather than sent to the currently active process. If no process, the | ||
| 607 | input is processed immediately. | ||
| 608 | |||
| 609 | If NO-NEWLINE is non-nil, the input is sent without an implied final | ||
| 610 | newline." | ||
| 611 | (interactive "P") | ||
| 612 | ;; Note that the input string does not include its terminal newline. | ||
| 613 | (let ((proc-running-p (and (eshell-interactive-process) | ||
| 614 | (not queue-p))) | ||
| 615 | (inhibit-point-motion-hooks t) | ||
| 616 | after-change-functions) | ||
| 617 | (unless (and proc-running-p | ||
| 618 | (not (eq (process-status | ||
| 619 | (eshell-interactive-process)) 'run))) | ||
| 620 | (if (or proc-running-p | ||
| 621 | (>= (point) eshell-last-output-end)) | ||
| 622 | (goto-char (point-max)) | ||
| 623 | (let ((copy (eshell-get-old-input use-region))) | ||
| 624 | (goto-char eshell-last-output-end) | ||
| 625 | (insert-and-inherit copy))) | ||
| 626 | (unless no-newline | ||
| 627 | (insert-before-markers-and-inherit ?\n)) | ||
| 628 | (if proc-running-p | ||
| 629 | (progn | ||
| 630 | (eshell-update-markers eshell-last-output-end) | ||
| 631 | (if (= eshell-last-input-start eshell-last-input-end) | ||
| 632 | (unless no-newline | ||
| 633 | (process-send-string (eshell-interactive-process) "\n")) | ||
| 634 | (process-send-region (eshell-interactive-process) | ||
| 635 | eshell-last-input-start | ||
| 636 | eshell-last-input-end))) | ||
| 637 | (if (= eshell-last-output-end (point)) | ||
| 638 | (run-hooks 'eshell-post-command-hook) | ||
| 639 | (let (input) | ||
| 640 | (eshell-condition-case err | ||
| 641 | (progn | ||
| 642 | (setq input (buffer-substring-no-properties | ||
| 643 | eshell-last-output-end (1- (point)))) | ||
| 644 | (run-hook-with-args 'eshell-expand-input-functions | ||
| 645 | eshell-last-output-end (1- (point))) | ||
| 646 | (let ((cmd (eshell-parse-command-input | ||
| 647 | eshell-last-output-end (1- (point))))) | ||
| 648 | (when cmd | ||
| 649 | (eshell-update-markers eshell-last-output-end) | ||
| 650 | (setq input (buffer-substring-no-properties | ||
| 651 | eshell-last-input-start | ||
| 652 | (1- eshell-last-input-end))) | ||
| 653 | (run-hooks 'eshell-input-filter-functions) | ||
| 654 | (and (catch 'eshell-terminal | ||
| 655 | (ignore | ||
| 656 | (eshell-eval-command cmd input))) | ||
| 657 | (eshell-life-is-too-much))))) | ||
| 658 | (quit | ||
| 659 | (eshell-reset t) | ||
| 660 | (run-hooks 'eshell-post-command-hook) | ||
| 661 | (signal 'quit nil)) | ||
| 662 | (error | ||
| 663 | (eshell-reset t) | ||
| 664 | (eshell-interactive-print | ||
| 665 | (concat (error-message-string err) "\n")) | ||
| 666 | (run-hooks 'eshell-post-command-hook) | ||
| 667 | (insert-and-inherit input))))))))) | ||
| 668 | |||
| 669 | (eshell-deftest proc send-to-subprocess | ||
| 670 | "Send input to a subprocess" | ||
| 671 | ;; jww (1999-12-06): what about when bc is unavailable? | ||
| 672 | (if (not (eshell-search-path "bc")) | ||
| 673 | t | ||
| 674 | (eshell-insert-command "bc") | ||
| 675 | (eshell-insert-command "1 + 2") | ||
| 676 | (sit-for 1 0) | ||
| 677 | (forward-line -1) | ||
| 678 | (prog1 | ||
| 679 | (looking-at "3\n") | ||
| 680 | (eshell-insert-command "quit") | ||
| 681 | (sit-for 1 0)))) | ||
| 682 | |||
| 683 | (defsubst eshell-kill-new () | ||
| 684 | "Add the last input text to the kill ring." | ||
| 685 | (kill-ring-save eshell-last-input-start eshell-last-input-end)) | ||
| 686 | |||
| 687 | (custom-add-option 'eshell-input-filter-functions 'eshell-kill-new) | ||
| 688 | |||
| 689 | (defun eshell-output-filter (process string) | ||
| 690 | "Send the output from PROCESS (STRING) to the interactive display. | ||
| 691 | This is done after all necessary filtering has been done." | ||
| 692 | (let ((oprocbuf (if process (process-buffer process) | ||
| 693 | (current-buffer))) | ||
| 694 | (inhibit-point-motion-hooks t) | ||
| 695 | after-change-functions) | ||
| 696 | (let ((functions eshell-preoutput-filter-functions)) | ||
| 697 | (while (and functions string) | ||
| 698 | (setq string (funcall (car functions) string)) | ||
| 699 | (setq functions (cdr functions)))) | ||
| 700 | (if (and string oprocbuf (buffer-name oprocbuf)) | ||
| 701 | (let ((obuf (current-buffer)) | ||
| 702 | opoint obeg oend) | ||
| 703 | (set-buffer oprocbuf) | ||
| 704 | (setq opoint (point)) | ||
| 705 | (setq obeg (point-min)) | ||
| 706 | (setq oend (point-max)) | ||
| 707 | (let ((buffer-read-only nil) | ||
| 708 | (nchars (length string)) | ||
| 709 | (ostart nil)) | ||
| 710 | (widen) | ||
| 711 | (goto-char eshell-last-output-end) | ||
| 712 | (setq ostart (point)) | ||
| 713 | (if (<= (point) opoint) | ||
| 714 | (setq opoint (+ opoint nchars))) | ||
| 715 | (if (< (point) obeg) | ||
| 716 | (setq obeg (+ obeg nchars))) | ||
| 717 | (if (<= (point) oend) | ||
| 718 | (setq oend (+ oend nchars))) | ||
| 719 | (insert-before-markers string) | ||
| 720 | (if (= (window-start (selected-window)) (point)) | ||
| 721 | (set-window-start (selected-window) | ||
| 722 | (- (point) nchars))) | ||
| 723 | (if (= (point) eshell-last-input-end) | ||
| 724 | (set-marker eshell-last-input-end | ||
| 725 | (- eshell-last-input-end nchars))) | ||
| 726 | (set-marker eshell-last-output-start ostart) | ||
| 727 | (set-marker eshell-last-output-end (point)) | ||
| 728 | (force-mode-line-update)) | ||
| 729 | (narrow-to-region obeg oend) | ||
| 730 | (goto-char opoint) | ||
| 731 | (eshell-run-output-filters) | ||
| 732 | (set-buffer obuf))))) | ||
| 733 | |||
| 734 | (defun eshell-run-output-filters () | ||
| 735 | "Run the `eshell-output-filter-functions' on the current output." | ||
| 736 | (save-current-buffer | ||
| 737 | (run-hooks 'eshell-output-filter-functions)) | ||
| 738 | (setq eshell-last-output-block-begin | ||
| 739 | (marker-position eshell-last-output-end))) | ||
| 740 | |||
| 741 | ;;; jww (1999-10-23): this needs testing | ||
| 742 | (defun eshell-preinput-scroll-to-bottom () | ||
| 743 | "Go to the end of buffer in all windows showing it. | ||
| 744 | Movement occurs if point in the selected window is not after the | ||
| 745 | process mark, and `this-command' is an insertion command. Insertion | ||
| 746 | commands recognised are `self-insert-command', `yank', and | ||
| 747 | `hilit-yank'. Depends on the value of | ||
| 748 | `eshell-scroll-to-bottom-on-input'. | ||
| 749 | |||
| 750 | This function should be a pre-command hook." | ||
| 751 | (if (memq this-command '(self-insert-command yank hilit-yank)) | ||
| 752 | (let* ((selected (selected-window)) | ||
| 753 | (current (current-buffer)) | ||
| 754 | (scroll eshell-scroll-to-bottom-on-input)) | ||
| 755 | (if (< (point) eshell-last-output-end) | ||
| 756 | (if (eq scroll 'this) | ||
| 757 | (goto-char (point-max)) | ||
| 758 | (walk-windows | ||
| 759 | (function | ||
| 760 | (lambda (window) | ||
| 761 | (when (and (eq (window-buffer window) current) | ||
| 762 | (or (eq scroll t) (eq scroll 'all))) | ||
| 763 | (select-window window) | ||
| 764 | (goto-char (point-max)) | ||
| 765 | (select-window selected)))) | ||
| 766 | nil t)))))) | ||
| 767 | |||
| 768 | ;;; jww (1999-10-23): this needs testing | ||
| 769 | (defun eshell-postoutput-scroll-to-bottom () | ||
| 770 | "Go to the end of buffer in all windows showing it. | ||
| 771 | Does not scroll if the current line is the last line in the buffer. | ||
| 772 | Depends on the value of `eshell-scroll-to-bottom-on-output' and | ||
| 773 | `eshell-scroll-show-maximum-output'. | ||
| 774 | |||
| 775 | This function should be in the list `eshell-output-filter-functions'." | ||
| 776 | (let* ((selected (selected-window)) | ||
| 777 | (current (current-buffer)) | ||
| 778 | (scroll eshell-scroll-to-bottom-on-output)) | ||
| 779 | (unwind-protect | ||
| 780 | (walk-windows | ||
| 781 | (function | ||
| 782 | (lambda (window) | ||
| 783 | (if (eq (window-buffer window) current) | ||
| 784 | (progn | ||
| 785 | (select-window window) | ||
| 786 | (if (and (< (point) eshell-last-output-end) | ||
| 787 | (or (eq scroll t) (eq scroll 'all) | ||
| 788 | ;; Maybe user wants point to jump to end. | ||
| 789 | (and (eq scroll 'this) | ||
| 790 | (eq selected window)) | ||
| 791 | (and (eq scroll 'others) | ||
| 792 | (not (eq selected window))) | ||
| 793 | ;; If point was at the end, keep it at end. | ||
| 794 | (>= (point) eshell-last-output-start))) | ||
| 795 | (goto-char eshell-last-output-end)) | ||
| 796 | ;; Optionally scroll so that the text | ||
| 797 | ;; ends at the bottom of the window. | ||
| 798 | (if (and eshell-scroll-show-maximum-output | ||
| 799 | (>= (point) eshell-last-output-end)) | ||
| 800 | (save-excursion | ||
| 801 | (goto-char (point-max)) | ||
| 802 | (recenter -1))) | ||
| 803 | (select-window selected))))) | ||
| 804 | nil t) | ||
| 805 | (set-buffer current)))) | ||
| 806 | |||
| 807 | (custom-add-option 'eshell-output-filter-functions | ||
| 808 | 'eshell-postoutput-scroll-to-bottom) | ||
| 809 | |||
| 810 | (defun eshell-beginning-of-input () | ||
| 811 | "Return the location of the start of the previous input." | ||
| 812 | eshell-last-input-start) | ||
| 813 | |||
| 814 | (defun eshell-beginning-of-output () | ||
| 815 | "Return the location of the end of the previous output block." | ||
| 816 | eshell-last-input-end) | ||
| 817 | |||
| 818 | (defun eshell-end-of-output () | ||
| 819 | "Return the location of the end of the previous output block." | ||
| 820 | (if (eshell-using-module 'eshell-prompt) | ||
| 821 | eshell-last-output-start | ||
| 822 | eshell-last-output-end)) | ||
| 823 | |||
| 824 | (defun eshell-kill-output () | ||
| 825 | "Kill all output from interpreter since last input. | ||
| 826 | Does not delete the prompt." | ||
| 827 | (interactive) | ||
| 828 | (save-excursion | ||
| 829 | (goto-char (eshell-beginning-of-output)) | ||
| 830 | (insert "*** output flushed ***\n") | ||
| 831 | (delete-region (point) (eshell-end-of-output)))) | ||
| 832 | |||
| 833 | (eshell-deftest io flush-output | ||
| 834 | "Flush previous output" | ||
| 835 | (eshell-insert-command "echo alpha") | ||
| 836 | (eshell-kill-output) | ||
| 837 | (and (eshell-match-result (regexp-quote "*** output flushed ***\n")) | ||
| 838 | (forward-line) | ||
| 839 | (= (point) eshell-last-output-start))) | ||
| 840 | |||
| 841 | (defun eshell-show-output (&optional arg) | ||
| 842 | "Display start of this batch of interpreter output at top of window. | ||
| 843 | Sets mark to the value of point when this command is run. | ||
| 844 | With a prefix argument, narrows region to last command output." | ||
| 845 | (interactive "P") | ||
| 846 | (goto-char (eshell-beginning-of-output)) | ||
| 847 | (set-window-start (selected-window) | ||
| 848 | (save-excursion | ||
| 849 | (goto-char (eshell-beginning-of-input)) | ||
| 850 | (line-beginning-position))) | ||
| 851 | (if arg | ||
| 852 | (narrow-to-region (eshell-beginning-of-output) | ||
| 853 | (eshell-end-of-output))) | ||
| 854 | (eshell-end-of-output)) | ||
| 855 | |||
| 856 | (defun eshell-mark-output (&optional arg) | ||
| 857 | "Display start of this batch of interpreter output at top of window. | ||
| 858 | Sets mark to the value of point when this command is run. | ||
| 859 | With a prefix argument, narrows region to last command output." | ||
| 860 | (interactive "P") | ||
| 861 | (push-mark (eshell-show-output arg))) | ||
| 862 | |||
| 863 | (defun eshell-kill-input () | ||
| 864 | "Kill all text from last stuff output by interpreter to point." | ||
| 865 | (interactive) | ||
| 866 | (if (> (point) eshell-last-output-end) | ||
| 867 | (kill-region eshell-last-output-end (point)) | ||
| 868 | (let ((here (point))) | ||
| 869 | (eshell-bol) | ||
| 870 | (kill-region (point) here)))) | ||
| 871 | |||
| 872 | (defun eshell-show-maximum-output () | ||
| 873 | "Put the end of the buffer at the bottom of the window." | ||
| 874 | (interactive) | ||
| 875 | (if (interactive-p) | ||
| 876 | (widen)) | ||
| 877 | (goto-char (point-max)) | ||
| 878 | (recenter -1)) | ||
| 879 | |||
| 880 | (defun eshell-get-old-input (&optional use-current-region) | ||
| 881 | "Return the command input on the current line." | ||
| 882 | (if use-current-region | ||
| 883 | (buffer-substring (min (point) (mark)) | ||
| 884 | (max (point) (mark))) | ||
| 885 | (save-excursion | ||
| 886 | (beginning-of-line) | ||
| 887 | (and eshell-skip-prompt-function | ||
| 888 | (funcall eshell-skip-prompt-function)) | ||
| 889 | (let ((beg (point))) | ||
| 890 | (end-of-line) | ||
| 891 | (buffer-substring beg (point)))))) | ||
| 892 | |||
| 893 | (defun eshell-copy-old-input () | ||
| 894 | "Insert after prompt old input at point as new input to be edited." | ||
| 895 | (interactive) | ||
| 896 | (let ((input (eshell-get-old-input))) | ||
| 897 | (goto-char eshell-last-output-end) | ||
| 898 | (insert-and-inherit input))) | ||
| 899 | |||
| 900 | (eshell-deftest mode run-old-command | ||
| 901 | "Re-run an old command" | ||
| 902 | (eshell-insert-command "echo alpha") | ||
| 903 | (goto-char eshell-last-input-start) | ||
| 904 | (string= (eshell-get-old-input) "echo alpha")) | ||
| 905 | |||
| 906 | (defun eshell/exit () | ||
| 907 | "Leave or kill the Eshell buffer, depending on `eshell-kill-on-exit'." | ||
| 908 | (throw 'eshell-terminal t)) | ||
| 909 | |||
| 910 | (defun eshell-life-is-too-much () | ||
| 911 | "Kill the current buffer (or bury it). Good-bye Eshell." | ||
| 912 | (interactive) | ||
| 913 | (if (not eshell-kill-on-exit) | ||
| 914 | (bury-buffer) | ||
| 915 | (kill-buffer (current-buffer)))) | ||
| 916 | |||
| 917 | (defun eshell-truncate-buffer () | ||
| 918 | "Truncate the buffer to `eshell-buffer-maximum-lines'. | ||
| 919 | This function could be on `eshell-output-filter-functions' or bound to | ||
| 920 | a key." | ||
| 921 | (interactive) | ||
| 922 | (save-excursion | ||
| 923 | (goto-char eshell-last-output-end) | ||
| 924 | (let ((lines (count-lines 1 (point))) | ||
| 925 | (inhibit-read-only t)) | ||
| 926 | (forward-line (- eshell-buffer-maximum-lines)) | ||
| 927 | (beginning-of-line) | ||
| 928 | (let ((pos (point))) | ||
| 929 | (if (bobp) | ||
| 930 | (if (interactive-p) | ||
| 931 | (error "Buffer too short to truncate")) | ||
| 932 | (delete-region (point-min) (point)) | ||
| 933 | (if (interactive-p) | ||
| 934 | (message "Truncated buffer from %d to %d lines (%.1fk freed)" | ||
| 935 | lines eshell-buffer-maximum-lines | ||
| 936 | (/ pos 1024.0)))))))) | ||
| 937 | |||
| 938 | (custom-add-option 'eshell-output-filter-functions | ||
| 939 | 'eshell-truncate-buffer) | ||
| 940 | |||
| 941 | (defun send-invisible (str) | ||
| 942 | "Read a string without echoing. | ||
| 943 | Then send it to the process running in the current buffer." | ||
| 944 | (interactive "P") ; Defeat snooping via C-x ESC ESC | ||
| 945 | (let ((str (read-passwd | ||
| 946 | (format "Password: " | ||
| 947 | (process-name (eshell-interactive-process)))))) | ||
| 948 | (if (stringp str) | ||
| 949 | (process-send-string (eshell-interactive-process) | ||
| 950 | (concat str "\n")) | ||
| 951 | (message "Warning: text will be echoed")))) | ||
| 952 | |||
| 953 | (defun eshell-watch-for-password-prompt () | ||
| 954 | "Prompt in the minibuffer for password and send without echoing. | ||
| 955 | This function uses `send-invisible' to read and send a password to the | ||
| 956 | buffer's process if STRING contains a password prompt defined by | ||
| 957 | `eshell-password-prompt-regexp'. | ||
| 958 | |||
| 959 | This function could be in the list `eshell-output-filter-functions'." | ||
| 960 | (when (eshell-interactive-process) | ||
| 961 | (save-excursion | ||
| 962 | (goto-char eshell-last-output-block-begin) | ||
| 963 | (beginning-of-line) | ||
| 964 | (if (re-search-forward eshell-password-prompt-regexp | ||
| 965 | eshell-last-output-end t) | ||
| 966 | (send-invisible nil))))) | ||
| 967 | |||
| 968 | (custom-add-option 'eshell-output-filter-functions | ||
| 969 | 'eshell-watch-for-password-prompt) | ||
| 970 | |||
| 971 | (defun eshell-handle-control-codes () | ||
| 972 | "Act properly when certain control codes are seen." | ||
| 973 | (save-excursion | ||
| 974 | (let ((orig (point))) | ||
| 975 | (goto-char eshell-last-output-block-begin) | ||
| 976 | (unless (eolp) | ||
| 977 | (beginning-of-line)) | ||
| 978 | (while (< (point) eshell-last-output-end) | ||
| 979 | (let ((char (char-after))) | ||
| 980 | (cond | ||
| 981 | ((eq char ?\r) | ||
| 982 | (if (< (1+ (point)) eshell-last-output-end) | ||
| 983 | (if (memq (char-after (1+ (point))) | ||
| 984 | '(?\n ?\r)) | ||
| 985 | (delete-char 1) | ||
| 986 | (let ((end (1+ (point)))) | ||
| 987 | (beginning-of-line) | ||
| 988 | (delete-region (point) end))) | ||
| 989 | (add-text-properties (point) (1+ (point)) | ||
| 990 | '(invisible t)) | ||
| 991 | (forward-char))) | ||
| 992 | ((eq char ?\a) | ||
| 993 | (delete-char 1) | ||
| 994 | (beep)) | ||
| 995 | ((eq char ?\C-h) | ||
| 996 | (delete-backward-char 1) | ||
| 997 | (delete-char 1)) | ||
| 998 | (t | ||
| 999 | (forward-char)))))))) | ||
| 1000 | |||
| 1001 | (custom-add-option 'eshell-output-filter-functions | ||
| 1002 | 'eshell-handle-control-codes) | ||
| 1003 | |||
| 1004 | ;;; Code: | ||
| 1005 | |||
| 1006 | ;;; esh-mode.el ends here | ||
diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el new file mode 100644 index 00000000000..eb69e5e0f98 --- /dev/null +++ b/lisp/eshell/esh-util.el | |||
| @@ -0,0 +1,676 @@ | |||
| 1 | ;;; esh-util --- general utilities | ||
| 2 | |||
| 3 | ;; Copyright (C) 1999, 2000 Free Sofware Foundation | ||
| 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 'esh-util) | ||
| 23 | |||
| 24 | (eval-when-compile (require 'esh-maint)) | ||
| 25 | |||
| 26 | (defgroup eshell-util nil | ||
| 27 | "This is general utility code, meant for use by Eshell itself." | ||
| 28 | :tag "General utilities" | ||
| 29 | :group 'eshell) | ||
| 30 | |||
| 31 | ;;; Commentary: | ||
| 32 | |||
| 33 | (require 'pp) | ||
| 34 | |||
| 35 | ;;; User Variables: | ||
| 36 | |||
| 37 | (defcustom eshell-group-file "/etc/group" | ||
| 38 | "*If non-nil, the name of the group file on your system." | ||
| 39 | :type '(choice (const :tag "No group file" nil) file) | ||
| 40 | :group 'eshell-util) | ||
| 41 | |||
| 42 | (defcustom eshell-passwd-file "/etc/passwd" | ||
| 43 | "*If non-nil, the name of the passwd file on your system." | ||
| 44 | :type '(choice (const :tag "No passwd file" nil) file) | ||
| 45 | :group 'eshell-util) | ||
| 46 | |||
| 47 | (defcustom eshell-hosts-file "/etc/hosts" | ||
| 48 | "*The name of the /etc/hosts file." | ||
| 49 | :type '(choice (const :tag "No hosts file" nil) file) | ||
| 50 | :group 'eshell-util) | ||
| 51 | |||
| 52 | (defcustom eshell-handle-errors t | ||
| 53 | "*If non-nil, Eshell will handle errors itself. | ||
| 54 | Setting this to nil is offered as an aid to debugging only." | ||
| 55 | :type 'boolean | ||
| 56 | :group 'eshell-util) | ||
| 57 | |||
| 58 | (defcustom eshell-private-file-modes 384 ; umask 177 | ||
| 59 | "*The file-modes value to use for creating \"private\" files." | ||
| 60 | :type 'integer | ||
| 61 | :group 'eshell-util) | ||
| 62 | |||
| 63 | (defcustom eshell-private-directory-modes 448 ; umask 077 | ||
| 64 | "*The file-modes value to use for creating \"private\" directories." | ||
| 65 | :type 'integer | ||
| 66 | :group 'eshell-util) | ||
| 67 | |||
| 68 | (defcustom eshell-tar-regexp | ||
| 69 | "\\.t\\(ar\\(\\.\\(gz\\|bz2\\|Z\\)\\)?\\|gz\\|a[zZ]\\|z2\\)\\'" | ||
| 70 | "*Regular expression used to match tar file names." | ||
| 71 | :type 'regexp | ||
| 72 | :group 'eshell-util) | ||
| 73 | |||
| 74 | (defcustom eshell-convert-numeric-arguments t | ||
| 75 | "*If non-nil, converting arguments of numeric form to Lisp numbers. | ||
| 76 | Numeric form is tested using the regular expression | ||
| 77 | `eshell-number-regexp'." | ||
| 78 | :type 'boolean | ||
| 79 | :group 'eshell-util) | ||
| 80 | |||
| 81 | (defcustom eshell-number-regexp "\\(0\\|-?[1-9][0-9]*\\(\\.[0-9]+\\)?\\)" | ||
| 82 | "*Regular expression used to match numeric arguments. | ||
| 83 | If `eshell-convert-numeric-arguments' is non-nil, and an argument | ||
| 84 | matches this regexp, it will be converted to a Lisp number, using the | ||
| 85 | function `string-to-number'." | ||
| 86 | :type 'regexp | ||
| 87 | :group 'eshell-util) | ||
| 88 | |||
| 89 | ;;; Internal Variables: | ||
| 90 | |||
| 91 | (defvar eshell-group-names nil | ||
| 92 | "A cache to hold the names of groups.") | ||
| 93 | |||
| 94 | (defvar eshell-group-timestamp nil | ||
| 95 | "A timestamp of when the group file was read.") | ||
| 96 | |||
| 97 | (defvar eshell-user-names nil | ||
| 98 | "A cache to hold the names of users.") | ||
| 99 | |||
| 100 | (defvar eshell-user-timestamp nil | ||
| 101 | "A timestamp of when the user file was read.") | ||
| 102 | |||
| 103 | (defvar eshell-host-names nil | ||
| 104 | "A cache the names of frequently accessed hosts.") | ||
| 105 | |||
| 106 | (defvar eshell-host-timestamp nil | ||
| 107 | "A timestamp of when the hosts file was read.") | ||
| 108 | |||
| 109 | ;;; Functions: | ||
| 110 | |||
| 111 | (defsubst eshell-under-xemacs-p () | ||
| 112 | "Return non-nil if we are running under XEmacs." | ||
| 113 | (boundp 'xemacs-logo)) | ||
| 114 | |||
| 115 | (defsubst eshell-under-windows-p () | ||
| 116 | "Return non-nil if we are running under MS-DOS/Windows." | ||
| 117 | (memq system-type '(ms-dos windows-nt))) | ||
| 118 | |||
| 119 | (defmacro eshell-condition-case (tag form &rest handlers) | ||
| 120 | "Like `condition-case', but only if `eshell-pass-through-errors' is nil." | ||
| 121 | (if eshell-handle-errors | ||
| 122 | `(condition-case ,tag | ||
| 123 | ,form | ||
| 124 | ,@handlers) | ||
| 125 | form)) | ||
| 126 | |||
| 127 | (put 'eshell-condition-case 'lisp-indent-function 2) | ||
| 128 | |||
| 129 | (defmacro eshell-deftest (module name label &rest forms) | ||
| 130 | (if (and (fboundp 'cl-compiling-file) (cl-compiling-file)) | ||
| 131 | nil | ||
| 132 | (let ((fsym (intern (concat "eshell-test--" (symbol-name name))))) | ||
| 133 | `(eval-when-compile | ||
| 134 | (ignore | ||
| 135 | (defun ,fsym () ,label | ||
| 136 | (eshell-run-test (quote ,module) (quote ,fsym) ,label | ||
| 137 | (quote (progn ,@forms))))))))) | ||
| 138 | |||
| 139 | (put 'eshell-deftest 'lisp-indent-function 2) | ||
| 140 | |||
| 141 | (defun eshell-find-delimiter | ||
| 142 | (open close &optional bound reverse-p backslash-p) | ||
| 143 | "From point, find the CLOSE delimiter corresponding to OPEN. | ||
| 144 | The matching is bounded by BOUND. | ||
| 145 | If REVERSE-P is non-nil, process the region backwards. | ||
| 146 | If BACKSLASH-P is non-nil, and OPEN and CLOSE are the same character, | ||
| 147 | then quoting is done by a backslash, rather than a doubled delimiter." | ||
| 148 | (save-excursion | ||
| 149 | (let ((depth 1) | ||
| 150 | (bound (or bound (point-max)))) | ||
| 151 | (if (if reverse-p | ||
| 152 | (eq (char-before) close) | ||
| 153 | (eq (char-after) open)) | ||
| 154 | (forward-char (if reverse-p -1 1))) | ||
| 155 | (while (and (> depth 0) | ||
| 156 | (funcall (if reverse-p '> '<) (point) bound)) | ||
| 157 | (let ((c (if reverse-p (char-before) (char-after))) nc) | ||
| 158 | (cond ((and (not reverse-p) | ||
| 159 | (or (not (eq open close)) | ||
| 160 | backslash-p) | ||
| 161 | (eq c ?\\) | ||
| 162 | (setq nc (char-after (1+ (point)))) | ||
| 163 | (or (eq nc open) (eq nc close))) | ||
| 164 | (forward-char 1)) | ||
| 165 | ((and reverse-p | ||
| 166 | (or (not (eq open close)) | ||
| 167 | backslash-p) | ||
| 168 | (or (eq c open) (eq c close)) | ||
| 169 | (eq (char-before (1- (point))) | ||
| 170 | ?\\)) | ||
| 171 | (forward-char -1)) | ||
| 172 | ((eq open close) | ||
| 173 | (if (eq c open) | ||
| 174 | (if (and (not backslash-p) | ||
| 175 | (eq (if reverse-p | ||
| 176 | (char-before (1- (point))) | ||
| 177 | (char-after (1+ (point)))) open)) | ||
| 178 | (forward-char (if reverse-p -1 1)) | ||
| 179 | (setq depth (1- depth))))) | ||
| 180 | ((= c open) | ||
| 181 | (setq depth (+ depth (if reverse-p -1 1)))) | ||
| 182 | ((= c close) | ||
| 183 | (setq depth (+ depth (if reverse-p 1 -1)))))) | ||
| 184 | (forward-char (if reverse-p -1 1))) | ||
| 185 | (if (= depth 0) | ||
| 186 | (if reverse-p (point) (1- (point))))))) | ||
| 187 | |||
| 188 | (defun eshell-convert (string) | ||
| 189 | "Convert STRING into a more native looking Lisp object." | ||
| 190 | (if (not (stringp string)) | ||
| 191 | string | ||
| 192 | (let ((len (length string))) | ||
| 193 | (if (= len 0) | ||
| 194 | string | ||
| 195 | (if (eq (aref string (1- len)) ?\n) | ||
| 196 | (setq string (substring string 0 (1- len)))) | ||
| 197 | (if (string-match "\n" string) | ||
| 198 | (split-string string "\n") | ||
| 199 | (if (and eshell-convert-numeric-arguments | ||
| 200 | (string-match | ||
| 201 | (concat "\\`\\s-*" eshell-number-regexp | ||
| 202 | "\\s-*\\'") string)) | ||
| 203 | (string-to-number string) | ||
| 204 | string)))))) | ||
| 205 | |||
| 206 | (defun eshell-sublist (l &optional n m) | ||
| 207 | "Return from LIST the N to M elements. | ||
| 208 | If N or M is nil, it means the end of the list." | ||
| 209 | (let* ((a (copy-list l)) | ||
| 210 | result) | ||
| 211 | (if (and m (consp (nthcdr m a))) | ||
| 212 | (setcdr (nthcdr m a) nil)) | ||
| 213 | (if n | ||
| 214 | (setq a (nthcdr n a)) | ||
| 215 | (setq n (1- (length a)) | ||
| 216 | a (last a))) | ||
| 217 | a)) | ||
| 218 | |||
| 219 | (defun eshell-split-path (path) | ||
| 220 | "Split a path into multiple subparts." | ||
| 221 | (let ((len (length path)) | ||
| 222 | (i 0) (li 0) | ||
| 223 | parts) | ||
| 224 | (if (and (eshell-under-windows-p) | ||
| 225 | (> len 2) | ||
| 226 | (eq (aref path 0) directory-sep-char) | ||
| 227 | (eq (aref path 1) directory-sep-char)) | ||
| 228 | (setq i 2)) | ||
| 229 | (while (< i len) | ||
| 230 | (if (and (eq (aref path i) directory-sep-char) | ||
| 231 | (not (get-text-property i 'escaped path))) | ||
| 232 | (setq parts (cons (if (= li i) | ||
| 233 | (char-to-string directory-sep-char) | ||
| 234 | (substring path li (1+ i))) parts) | ||
| 235 | li (1+ i))) | ||
| 236 | (setq i (1+ i))) | ||
| 237 | (if (< li i) | ||
| 238 | (setq parts (cons (substring path li i) parts))) | ||
| 239 | (if (and (eshell-under-windows-p) | ||
| 240 | (string-match "\\`[A-Za-z]:\\'" (car (last parts)))) | ||
| 241 | (setcar (last parts) | ||
| 242 | (concat (car (last parts)) | ||
| 243 | (char-to-string directory-sep-char)))) | ||
| 244 | (nreverse parts))) | ||
| 245 | |||
| 246 | (defun eshell-to-flat-string (value) | ||
| 247 | "Make value a string. If separated by newlines change them to spaces." | ||
| 248 | (let ((text (eshell-stringify value))) | ||
| 249 | (if (string-match "\n+\\'" text) | ||
| 250 | (setq text (replace-match "" t t text))) | ||
| 251 | (while (string-match "\n+" text) | ||
| 252 | (setq text (replace-match " " t t text))) | ||
| 253 | text)) | ||
| 254 | |||
| 255 | (defmacro eshell-for (for-var for-list &rest forms) | ||
| 256 | "Iterate through a list" | ||
| 257 | `(let ((list-iter ,for-list)) | ||
| 258 | (while list-iter | ||
| 259 | (let ((,for-var (car list-iter))) | ||
| 260 | ,@forms) | ||
| 261 | (setq list-iter (cdr list-iter))))) | ||
| 262 | |||
| 263 | (put 'eshell-for 'lisp-indent-function 2) | ||
| 264 | |||
| 265 | (defun eshell-flatten-list (args) | ||
| 266 | "Flatten any lists within ARGS, so that there are no sublists." | ||
| 267 | (let ((new-list (list t))) | ||
| 268 | (eshell-for a args | ||
| 269 | (if (and (listp a) | ||
| 270 | (listp (cdr a))) | ||
| 271 | (nconc new-list (eshell-flatten-list a)) | ||
| 272 | (nconc new-list (list a)))) | ||
| 273 | (cdr new-list))) | ||
| 274 | |||
| 275 | (defun eshell-uniqify-list (l) | ||
| 276 | "Remove occurring multiples in L. You probably want to sort first." | ||
| 277 | (let ((m l)) | ||
| 278 | (while m | ||
| 279 | (while (and (cdr m) | ||
| 280 | (string= (car m) | ||
| 281 | (cadr m))) | ||
| 282 | (setcdr m (cddr m))) | ||
| 283 | (setq m (cdr m)))) | ||
| 284 | l) | ||
| 285 | |||
| 286 | (defun eshell-stringify (object) | ||
| 287 | "Convert OBJECT into a string value." | ||
| 288 | (cond | ||
| 289 | ((stringp object) object) | ||
| 290 | ((and (listp object) | ||
| 291 | (not (eq object nil))) | ||
| 292 | (let ((string (pp-to-string object))) | ||
| 293 | (substring string 0 (1- (length string))))) | ||
| 294 | ((numberp object) | ||
| 295 | (number-to-string object)) | ||
| 296 | (t | ||
| 297 | (pp-to-string object)))) | ||
| 298 | |||
| 299 | (defsubst eshell-stringify-list (args) | ||
| 300 | "Convert each element of ARGS into a string value." | ||
| 301 | (mapcar 'eshell-stringify args)) | ||
| 302 | |||
| 303 | (defsubst eshell-flatten-and-stringify (&rest args) | ||
| 304 | "Flatten and stringify all of the ARGS into a single string." | ||
| 305 | (mapconcat 'eshell-stringify (eshell-flatten-list args) " ")) | ||
| 306 | |||
| 307 | ;; the next two are from GNUS, and really should be made part of Emacs | ||
| 308 | ;; some day | ||
| 309 | (defsubst eshell-time-less-p (t1 t2) | ||
| 310 | "Say whether time T1 is less than time T2." | ||
| 311 | (or (< (car t1) (car t2)) | ||
| 312 | (and (= (car t1) (car t2)) | ||
| 313 | (< (nth 1 t1) (nth 1 t2))))) | ||
| 314 | |||
| 315 | (defsubst eshell-time-to-seconds (time) | ||
| 316 | "Convert TIME to a floating point number." | ||
| 317 | (+ (* (car time) 65536.0) | ||
| 318 | (cadr time) | ||
| 319 | (/ (or (car (cdr (cdr time))) 0) 1000000.0))) | ||
| 320 | |||
| 321 | (defsubst eshell-directory-files (regexp &optional directory) | ||
| 322 | "Return a list of files in the given DIRECTORY matching REGEXP." | ||
| 323 | (directory-files (or directory default-directory) | ||
| 324 | directory regexp)) | ||
| 325 | |||
| 326 | (defun eshell-regexp-arg (prompt) | ||
| 327 | "Return list of regexp and prefix arg using PROMPT." | ||
| 328 | (let* (;; Don't clobber this. | ||
| 329 | (last-command last-command) | ||
| 330 | (regexp (read-from-minibuffer prompt nil nil nil | ||
| 331 | 'minibuffer-history-search-history))) | ||
| 332 | (list (if (string-equal regexp "") | ||
| 333 | (setcar minibuffer-history-search-history | ||
| 334 | (nth 1 minibuffer-history-search-history)) | ||
| 335 | regexp) | ||
| 336 | (prefix-numeric-value current-prefix-arg)))) | ||
| 337 | |||
| 338 | (defun eshell-printable-size (filesize &optional human-readable | ||
| 339 | block-size use-colors) | ||
| 340 | "Return a printable FILESIZE." | ||
| 341 | (let ((size (float (or filesize 0)))) | ||
| 342 | (if human-readable | ||
| 343 | (if (< size human-readable) | ||
| 344 | (if (= (round size) 0) | ||
| 345 | "0" | ||
| 346 | (if block-size | ||
| 347 | "1.0k" | ||
| 348 | (format "%.0f" size))) | ||
| 349 | (setq size (/ size human-readable)) | ||
| 350 | (if (< size human-readable) | ||
| 351 | (if (<= size 9.94) | ||
| 352 | (format "%.1fk" size) | ||
| 353 | (format "%.0fk" size)) | ||
| 354 | (setq size (/ size human-readable)) | ||
| 355 | (if (< size human-readable) | ||
| 356 | (let ((str (if (<= size 9.94) | ||
| 357 | (format "%.1fM" size) | ||
| 358 | (format "%.0fM" size)))) | ||
| 359 | (if use-colors | ||
| 360 | (put-text-property 0 (length str) | ||
| 361 | 'face 'bold str)) | ||
| 362 | str) | ||
| 363 | (setq size (/ size human-readable)) | ||
| 364 | (if (< size human-readable) | ||
| 365 | (let ((str (if (<= size 9.94) | ||
| 366 | (format "%.1fG" size) | ||
| 367 | (format "%.0fG" size)))) | ||
| 368 | (if use-colors | ||
| 369 | (put-text-property 0 (length str) | ||
| 370 | 'face 'bold-italic str)) | ||
| 371 | str))))) | ||
| 372 | (if block-size | ||
| 373 | (setq size (/ size block-size))) | ||
| 374 | (format "%.0f" size)))) | ||
| 375 | |||
| 376 | (defun eshell-winnow-list (entries exclude &optional predicates) | ||
| 377 | "Pare down the ENTRIES list using the EXCLUDE regexp, and PREDICATES. | ||
| 378 | The original list is not affected. If the result is only one element | ||
| 379 | long, it will be returned itself, rather than returning a one-element | ||
| 380 | list." | ||
| 381 | (let ((flist (list t)) | ||
| 382 | valid p listified) | ||
| 383 | (unless (listp entries) | ||
| 384 | (setq entries (list entries) | ||
| 385 | listified t)) | ||
| 386 | (eshell-for entry entries | ||
| 387 | (unless (and exclude (string-match exclude entry)) | ||
| 388 | (setq p predicates valid (null p)) | ||
| 389 | (while p | ||
| 390 | (if (funcall (car p) entry) | ||
| 391 | (setq valid t) | ||
| 392 | (setq p nil valid nil)) | ||
| 393 | (setq p (cdr p))) | ||
| 394 | (when valid | ||
| 395 | (nconc flist (list entry))))) | ||
| 396 | (if listified | ||
| 397 | (cadr flist) | ||
| 398 | (cdr flist)))) | ||
| 399 | |||
| 400 | (defsubst eshell-redisplay () | ||
| 401 | "Allow Emacs to redisplay buffers." | ||
| 402 | ;; for some strange reason, Emacs 21 is prone to trigger an | ||
| 403 | ;; "args out of range" error in `sit-for', if this function | ||
| 404 | ;; runs while point is in the minibuffer and the users attempt | ||
| 405 | ;; to use completion. Don't ask me. | ||
| 406 | (ignore-errors (sit-for 0 0))) | ||
| 407 | |||
| 408 | (defun eshell-read-passwd-file (file) | ||
| 409 | "Return an alist correlating gids to group names in FILE." | ||
| 410 | (let (names) | ||
| 411 | (when (file-readable-p file) | ||
| 412 | (with-temp-buffer | ||
| 413 | (insert-file-contents file) | ||
| 414 | (goto-char (point-min)) | ||
| 415 | (while (not (eobp)) | ||
| 416 | (let* ((fields | ||
| 417 | (split-string (buffer-substring | ||
| 418 | (point) (progn (end-of-line) | ||
| 419 | (point))) ":"))) | ||
| 420 | (if (and fields (nth 0 fields) (nth 2 fields)) | ||
| 421 | (setq names (cons (cons (string-to-int (nth 2 fields)) | ||
| 422 | (nth 0 fields)) | ||
| 423 | names)))) | ||
| 424 | (forward-line)))) | ||
| 425 | names)) | ||
| 426 | |||
| 427 | (defun eshell-read-passwd (file result-var timestamp-var) | ||
| 428 | "Read the contents of /etc/passwd for user names." | ||
| 429 | (if (or (not (symbol-value result-var)) | ||
| 430 | (not (symbol-value timestamp-var)) | ||
| 431 | (eshell-time-less-p | ||
| 432 | (symbol-value timestamp-var) | ||
| 433 | (nth 5 (file-attributes file)))) | ||
| 434 | (progn | ||
| 435 | (set result-var (eshell-read-passwd-file file)) | ||
| 436 | (set timestamp-var (current-time)))) | ||
| 437 | (symbol-value result-var)) | ||
| 438 | |||
| 439 | (defun eshell-read-group-names () | ||
| 440 | "Read the contents of /etc/group for group names." | ||
| 441 | (if eshell-group-file | ||
| 442 | (eshell-read-passwd eshell-group-file 'eshell-group-names | ||
| 443 | 'eshell-group-timestamp))) | ||
| 444 | |||
| 445 | (defsubst eshell-group-id (name) | ||
| 446 | "Return the user id for user NAME." | ||
| 447 | (car (rassoc name (eshell-read-group-names)))) | ||
| 448 | |||
| 449 | (defsubst eshell-group-name (gid) | ||
| 450 | "Return the group name for the given GID." | ||
| 451 | (cdr (assoc gid (eshell-read-group-names)))) | ||
| 452 | |||
| 453 | (defun eshell-read-user-names () | ||
| 454 | "Read the contents of /etc/passwd for user names." | ||
| 455 | (if eshell-passwd-file | ||
| 456 | (eshell-read-passwd eshell-passwd-file 'eshell-user-names | ||
| 457 | 'eshell-user-timestamp))) | ||
| 458 | |||
| 459 | (defsubst eshell-user-id (name) | ||
| 460 | "Return the user id for user NAME." | ||
| 461 | (car (rassoc name (eshell-read-user-names)))) | ||
| 462 | |||
| 463 | (defalias 'eshell-user-name 'user-login-name) | ||
| 464 | |||
| 465 | (defun eshell-read-hosts-file (filename) | ||
| 466 | "Read in the hosts from the /etc/hosts file." | ||
| 467 | (let (hosts) | ||
| 468 | (with-temp-buffer | ||
| 469 | (insert-file-contents eshell-hosts-file) | ||
| 470 | (goto-char (point-min)) | ||
| 471 | (while (re-search-forward | ||
| 472 | "^\\(\\S-+\\)\\s-+\\(\\S-+\\)\\(\\s-*\\(\\S-+\\)\\)?" nil t) | ||
| 473 | (if (match-string 1) | ||
| 474 | (add-to-list 'hosts (match-string 1))) | ||
| 475 | (if (match-string 2) | ||
| 476 | (add-to-list 'hosts (match-string 2))) | ||
| 477 | (if (match-string 4) | ||
| 478 | (add-to-list 'hosts (match-string 4))))) | ||
| 479 | (sort hosts 'string-lessp))) | ||
| 480 | |||
| 481 | (defun eshell-read-hosts (file result-var timestamp-var) | ||
| 482 | "Read the contents of /etc/passwd for user names." | ||
| 483 | (if (or (not (symbol-value result-var)) | ||
| 484 | (not (symbol-value timestamp-var)) | ||
| 485 | (eshell-time-less-p | ||
| 486 | (symbol-value timestamp-var) | ||
| 487 | (nth 5 (file-attributes file)))) | ||
| 488 | (progn | ||
| 489 | (set result-var (eshell-read-hosts-file file)) | ||
| 490 | (set timestamp-var (current-time)))) | ||
| 491 | (symbol-value result-var)) | ||
| 492 | |||
| 493 | (defun eshell-read-host-names () | ||
| 494 | "Read the contents of /etc/hosts for host names." | ||
| 495 | (if eshell-hosts-file | ||
| 496 | (eshell-read-hosts eshell-hosts-file 'eshell-host-names | ||
| 497 | 'eshell-host-timestamp))) | ||
| 498 | |||
| 499 | (unless (fboundp 'line-end-position) | ||
| 500 | (defsubst line-end-position (&optional N) | ||
| 501 | (save-excursion (end-of-line N) (point)))) | ||
| 502 | |||
| 503 | (unless (fboundp 'line-beginning-position) | ||
| 504 | (defsubst line-beginning-position (&optional N) | ||
| 505 | (save-excursion (beginning-of-line N) (point)))) | ||
| 506 | |||
| 507 | (unless (fboundp 'subst-char-in-string) | ||
| 508 | (defun subst-char-in-string (fromchar tochar string &optional inplace) | ||
| 509 | "Replace FROMCHAR with TOCHAR in STRING each time it occurs. | ||
| 510 | Unless optional argument INPLACE is non-nil, return a new string." | ||
| 511 | (let ((i (length string)) | ||
| 512 | (newstr (if inplace string (copy-sequence string)))) | ||
| 513 | (while (> i 0) | ||
| 514 | (setq i (1- i)) | ||
| 515 | (if (eq (aref newstr i) fromchar) | ||
| 516 | (aset newstr i tochar))) | ||
| 517 | newstr))) | ||
| 518 | |||
| 519 | (defsubst eshell-copy-environment () | ||
| 520 | "Return an unrelated copy of `process-environment'." | ||
| 521 | (mapcar 'concat process-environment)) | ||
| 522 | |||
| 523 | (defun eshell-subgroups (groupsym) | ||
| 524 | "Return all of the subgroups of GROUPSYM." | ||
| 525 | (let ((subgroups (get groupsym 'custom-group)) | ||
| 526 | (subg (list t))) | ||
| 527 | (while subgroups | ||
| 528 | (if (eq (cadr (car subgroups)) 'custom-group) | ||
| 529 | (nconc subg (list (caar subgroups)))) | ||
| 530 | (setq subgroups (cdr subgroups))) | ||
| 531 | (cdr subg))) | ||
| 532 | |||
| 533 | (defmacro eshell-with-file-modes (modes &rest forms) | ||
| 534 | "Evaluate, with file-modes set to MODES, the given FORMS." | ||
| 535 | `(let ((modes (default-file-modes))) | ||
| 536 | (set-default-file-modes ,modes) | ||
| 537 | (unwind-protect | ||
| 538 | (progn ,@forms) | ||
| 539 | (set-default-file-modes modes)))) | ||
| 540 | |||
| 541 | (defmacro eshell-with-private-file-modes (&rest forms) | ||
| 542 | "Evaluate FORMS with private file modes set." | ||
| 543 | `(eshell-with-file-modes ,eshell-private-file-modes ,@forms)) | ||
| 544 | |||
| 545 | (defsubst eshell-make-private-directory (dir &optional parents) | ||
| 546 | "Make DIR with file-modes set to `eshell-private-directory-modes'." | ||
| 547 | (eshell-with-file-modes eshell-private-directory-modes | ||
| 548 | (make-directory dir parents))) | ||
| 549 | |||
| 550 | (defsubst eshell-substring (string sublen) | ||
| 551 | "Return the beginning of STRING, up to SUBLEN bytes." | ||
| 552 | (if string | ||
| 553 | (if (> (length string) sublen) | ||
| 554 | (substring string 0 sublen) | ||
| 555 | string))) | ||
| 556 | |||
| 557 | (unless (fboundp 'directory-files-and-attributes) | ||
| 558 | (defun directory-files-and-attributes (dir &optional full match nosort) | ||
| 559 | (documentation 'directory-files) | ||
| 560 | (let* ((dir (expand-file-name dir)) | ||
| 561 | (default-directory dir)) | ||
| 562 | (mapcar | ||
| 563 | (function | ||
| 564 | (lambda (file) | ||
| 565 | (cons file (file-attributes file)))) | ||
| 566 | (directory-files dir full match nosort))))) | ||
| 567 | |||
| 568 | (defun eshell-directory-files-and-attributes (dir &optional full match nosort) | ||
| 569 | "Make sure to use the handler for `directory-file-and-attributes'." | ||
| 570 | (let ((dfh (find-file-name-handler dir 'directory-files))) | ||
| 571 | (if (not dfh) | ||
| 572 | (directory-files-and-attributes dir full match nosort) | ||
| 573 | (let* ((files (funcall dfh 'directory-files dir full match nosort)) | ||
| 574 | (fah (find-file-name-handler dir 'file-attributes)) | ||
| 575 | (default-directory (expand-file-name dir))) | ||
| 576 | (mapcar | ||
| 577 | (function | ||
| 578 | (lambda (file) | ||
| 579 | (cons file (funcall fah 'file-attributes file)))) | ||
| 580 | files))))) | ||
| 581 | |||
| 582 | (defun eshell-copy-list (list) | ||
| 583 | "Return a copy of a list, which may be a dotted list. | ||
| 584 | The elements of the list are not copied, just the list structure itself." | ||
| 585 | (if (consp list) | ||
| 586 | (let ((res nil)) | ||
| 587 | (while (consp list) (push (pop list) res)) | ||
| 588 | (prog1 (nreverse res) (setcdr res list))) | ||
| 589 | (car list))) | ||
| 590 | |||
| 591 | (defun eshell-copy-tree (tree &optional vecp) | ||
| 592 | "Make a copy of TREE. | ||
| 593 | If TREE is a cons cell, this recursively copies both its car and its cdr. | ||
| 594 | Contrast to copy-sequence, which copies only along the cdrs. With second | ||
| 595 | argument VECP, this copies vectors as well as conses." | ||
| 596 | (if (consp tree) | ||
| 597 | (let ((p (setq tree (eshell-copy-list tree)))) | ||
| 598 | (while (consp p) | ||
| 599 | (if (or (consp (car p)) (and vecp (vectorp (car p)))) | ||
| 600 | (setcar p (eshell-copy-tree (car p) vecp))) | ||
| 601 | (or (listp (cdr p)) (setcdr p (eshell-copy-tree (cdr p) vecp))) | ||
| 602 | (cl-pop p))) | ||
| 603 | (if (and vecp (vectorp tree)) | ||
| 604 | (let ((i (length (setq tree (copy-sequence tree))))) | ||
| 605 | (while (>= (setq i (1- i)) 0) | ||
| 606 | (aset tree i (eshell-copy-tree (aref tree i) vecp)))))) | ||
| 607 | tree) | ||
| 608 | |||
| 609 | ; (defun eshell-copy-file | ||
| 610 | ; (file newname &optional ok-if-already-exists keep-date) | ||
| 611 | ; "Copy FILE to NEWNAME. See docs for `copy-file'." | ||
| 612 | ; (let (copied) | ||
| 613 | ; (if (string-match "\\`\\([^:]+\\):\\(.*\\)" file) | ||
| 614 | ; (let ((front (match-string 1 file)) | ||
| 615 | ; (back (match-string 2 file)) | ||
| 616 | ; buffer) | ||
| 617 | ; (if (and front (string-match eshell-tar-regexp front) | ||
| 618 | ; (setq buffer (find-file-noselect front))) | ||
| 619 | ; (with-current-buffer buffer | ||
| 620 | ; (goto-char (point-min)) | ||
| 621 | ; (if (re-search-forward (concat " " (regexp-quote back) | ||
| 622 | ; "$") nil t) | ||
| 623 | ; (progn | ||
| 624 | ; (tar-copy (if (file-directory-p newname) | ||
| 625 | ; (expand-file-name | ||
| 626 | ; (file-name-nondirectory back) newname) | ||
| 627 | ; newname)) | ||
| 628 | ; (setq copied t)) | ||
| 629 | ; (error "%s not found in tar file %s" back front)))))) | ||
| 630 | ; (unless copied | ||
| 631 | ; (copy-file file newname ok-if-already-exists keep-date)))) | ||
| 632 | |||
| 633 | ; (defun eshell-file-attributes (filename) | ||
| 634 | ; "Return a list of attributes of file FILENAME. | ||
| 635 | ; See the documentation for `file-attributes'." | ||
| 636 | ; (let (result) | ||
| 637 | ; (when (string-match "\\`\\([^:]+\\):\\(.*\\)\\'" filename) | ||
| 638 | ; (let ((front (match-string 1 filename)) | ||
| 639 | ; (back (match-string 2 filename)) | ||
| 640 | ; buffer) | ||
| 641 | ; (when (and front (string-match eshell-tar-regexp front) | ||
| 642 | ; (setq buffer (find-file-noselect front))) | ||
| 643 | ; (with-current-buffer buffer | ||
| 644 | ; (goto-char (point-min)) | ||
| 645 | ; (when (re-search-forward (concat " " (regexp-quote back) | ||
| 646 | ; "\\s-*$") nil t) | ||
| 647 | ; (let* ((descrip (tar-current-descriptor)) | ||
| 648 | ; (tokens (tar-desc-tokens descrip))) | ||
| 649 | ; (setq result | ||
| 650 | ; (list | ||
| 651 | ; (cond | ||
| 652 | ; ((eq (tar-header-link-type tokens) 5) | ||
| 653 | ; t) | ||
| 654 | ; ((eq (tar-header-link-type tokens) t) | ||
| 655 | ; (tar-header-link-name tokens))) | ||
| 656 | ; 1 | ||
| 657 | ; (tar-header-uid tokens) | ||
| 658 | ; (tar-header-gid tokens) | ||
| 659 | ; (tar-header-date tokens) | ||
| 660 | ; (tar-header-date tokens) | ||
| 661 | ; (tar-header-date tokens) | ||
| 662 | ; (tar-header-size tokens) | ||
| 663 | ; (concat | ||
| 664 | ; (cond | ||
| 665 | ; ((eq (tar-header-link-type tokens) 5) "d") | ||
| 666 | ; ((eq (tar-header-link-type tokens) t) "l") | ||
| 667 | ; (t "-")) | ||
| 668 | ; (tar-grind-file-mode (tar-header-mode tokens) | ||
| 669 | ; (make-string 9 ? ) 0)) | ||
| 670 | ; nil nil nil)))))))) | ||
| 671 | ; (or result | ||
| 672 | ; (file-attributes filename)))) | ||
| 673 | |||
| 674 | ;;; Code: | ||
| 675 | |||
| 676 | ;;; esh-util.el ends here | ||