| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
(provide 'esh-cmd) |
|---|
| 26 |
|
|---|
| 27 |
(eval-when-compile (require 'esh-maint)) |
|---|
| 28 |
|
|---|
| 29 |
(defgroup eshell-cmd nil |
|---|
| 30 |
"Executing an Eshell command is as simple as typing it in and |
|---|
| 31 |
pressing <RET>. There are several different kinds of commands, |
|---|
| 32 |
however." |
|---|
| 33 |
:tag "Command invocation" |
|---|
| 34 |
|
|---|
| 35 |
:group 'eshell) |
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 |
|
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
|
|---|
| 46 |
|
|---|
| 47 |
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 |
|
|---|
| 53 |
|
|---|
| 54 |
|
|---|
| 55 |
|
|---|
| 56 |
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 |
(defcustom eshell-prefer-lisp-functions nil |
|---|
| 68 |
"*If non-nil, prefer Lisp functions to external commands." |
|---|
| 69 |
:type 'boolean |
|---|
| 70 |
:group 'eshell-cmd) |
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
|
|---|
| 77 |
|
|---|
| 78 |
|
|---|
| 79 |
|
|---|
| 80 |
|
|---|
| 81 |
|
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 |
|
|---|
| 93 |
|
|---|
| 94 |
|
|---|
| 95 |
|
|---|
| 96 |
|
|---|
| 97 |
|
|---|
| 98 |
|
|---|
| 99 |
|
|---|
| 100 |
|
|---|
| 101 |
|
|---|
| 102 |
|
|---|
| 103 |
|
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 |
|
|---|
| 109 |
|
|---|
| 110 |
|
|---|
| 111 |
|
|---|
| 112 |
|
|---|
| 113 |
|
|---|
| 114 |
|
|---|
| 115 |
(defcustom eshell-lisp-regexp "\\([(`]\\|#'\\)" |
|---|
| 116 |
"*A regexp which, if matched at beginning of an argument, means Lisp. |
|---|
| 117 |
Such arguments will be passed to `read', and then evaluated." |
|---|
| 118 |
:type 'regexp |
|---|
| 119 |
:group 'eshell-cmd) |
|---|
| 120 |
|
|---|
| 121 |
|
|---|
| 122 |
|
|---|
| 123 |
|
|---|
| 124 |
|
|---|
| 125 |
|
|---|
| 126 |
(defcustom eshell-pre-command-hook nil |
|---|
| 127 |
"*A hook run before each interactive command is invoked." |
|---|
| 128 |
:type 'hook |
|---|
| 129 |
:group 'eshell-cmd) |
|---|
| 130 |
|
|---|
| 131 |
(defcustom eshell-post-command-hook nil |
|---|
| 132 |
"*A hook run after each interactive command is invoked." |
|---|
| 133 |
:type 'hook |
|---|
| 134 |
:group 'eshell-cmd) |
|---|
| 135 |
|
|---|
| 136 |
(defcustom eshell-prepare-command-hook nil |
|---|
| 137 |
"*A set of functions called to prepare a named command. |
|---|
| 138 |
The command name and its argument are in `eshell-last-command-name' |
|---|
| 139 |
and `eshell-last-arguments'. The functions on this hook can change |
|---|
| 140 |
the value of these symbols if necessary. |
|---|
| 141 |
|
|---|
| 142 |
To prevent a command from executing at all, set |
|---|
| 143 |
`eshell-last-command-name' to nil." |
|---|
| 144 |
:type 'hook |
|---|
| 145 |
:group 'eshell-cmd) |
|---|
| 146 |
|
|---|
| 147 |
(defcustom eshell-named-command-hook nil |
|---|
| 148 |
"*A set of functions called before a named command is invoked. |
|---|
| 149 |
Each function will be passed the command name and arguments that were |
|---|
| 150 |
passed to `eshell-named-command'. |
|---|
| 151 |
|
|---|
| 152 |
If any of the functions returns a non-nil value, the named command |
|---|
| 153 |
will not be invoked, and that value will be returned from |
|---|
| 154 |
`eshell-named-command'. |
|---|
| 155 |
|
|---|
| 156 |
In order to substitute an alternate command form for execution, the |
|---|
| 157 |
hook function should throw it using the tag `eshell-replace-command'. |
|---|
| 158 |
For example: |
|---|
| 159 |
|
|---|
| 160 |
(add-hook 'eshell-named-command-hook 'subst-with-cd) |
|---|
| 161 |
(defun subst-with-cd (command args) |
|---|
| 162 |
(throw 'eshell-replace-command |
|---|
| 163 |
(eshell-parse-command \"cd\" args))) |
|---|
| 164 |
|
|---|
| 165 |
Although useless, the above code will cause any non-glob, non-Lisp |
|---|
| 166 |
command (i.e., 'ls' as opposed to '*ls' or '(ls)') to be replaced by a |
|---|
| 167 |
call to `cd' using the arguments that were passed to the function." |
|---|
| 168 |
:type 'hook |
|---|
| 169 |
:group 'eshell-cmd) |
|---|
| 170 |
|
|---|
| 171 |
(defcustom eshell-pre-rewrite-command-hook |
|---|
| 172 |
'(eshell-no-command-conversion |
|---|
| 173 |
eshell-subcommand-arg-values) |
|---|
| 174 |
"*A hook run before command rewriting begins. |
|---|
| 175 |
The terms of the command to be rewritten is passed as arguments, and |
|---|
| 176 |
may be modified in place. Any return value is ignored." |
|---|
| 177 |
:type 'hook |
|---|
| 178 |
:group 'eshell-cmd) |
|---|
| 179 |
|
|---|
| 180 |
(defcustom eshell-rewrite-command-hook |
|---|
| 181 |
'(eshell-rewrite-for-command |
|---|
| 182 |
eshell-rewrite-while-command |
|---|
| 183 |
eshell-rewrite-if-command |
|---|
| 184 |
eshell-rewrite-sexp-command |
|---|
| 185 |
eshell-rewrite-initial-subcommand |
|---|
| 186 |
eshell-rewrite-named-command) |
|---|
| 187 |
"*A set of functions used to rewrite the command argument. |
|---|
| 188 |
Once parsing of a command line is completed, the next step is to |
|---|
| 189 |
rewrite the initial argument into something runnable. |
|---|
| 190 |
|
|---|
| 191 |
A module may wish to associate special behavior with certain argument |
|---|
| 192 |
syntaxes at the beginning of a command line. They are welcome to do |
|---|
| 193 |
so by adding a function to this hook. The first function to return a |
|---|
| 194 |
substitute command form is the one used. Each function is passed the |
|---|
| 195 |
command's full argument list, which is a list of sexps (typically |
|---|
| 196 |
forms or strings)." |
|---|
| 197 |
:type 'hook |
|---|
| 198 |
:group 'eshell-cmd) |
|---|
| 199 |
|
|---|
| 200 |
(defcustom eshell-post-rewrite-command-hook nil |
|---|
| 201 |
"*A hook run after command rewriting is finished. |
|---|
| 202 |
Each function is passed the symbol containing the rewritten command, |
|---|
| 203 |
which may be modified directly. Any return value is ignored." |
|---|
| 204 |
:type 'hook |
|---|
| 205 |
:group 'eshell-cmd) |
|---|
| 206 |
|
|---|
| 207 |
(defcustom eshell-complex-commands '("ls") |
|---|
| 208 |
"*A list of commands names or functions, that determine complexity. |
|---|
| 209 |
That is, if a command is defined by a function named eshell/NAME, |
|---|
| 210 |
and NAME is part of this list, it is invoked as a complex command. |
|---|
| 211 |
Complex commands are always correct, but run much slower. If a |
|---|
| 212 |
command works fine without being part of this list, then it doesn't |
|---|
| 213 |
need to be. |
|---|
| 214 |
|
|---|
| 215 |
If an entry is a function, it will be called with the name, and should |
|---|
| 216 |
return non-nil if the command is complex." |
|---|
| 217 |
:type '(repeat :tag "Commands" |
|---|
| 218 |
(choice (string :tag "Name") |
|---|
| 219 |
(function :tag "Predicate"))) |
|---|
| 220 |
:group 'eshell-cmd) |
|---|
| 221 |
|
|---|
| 222 |
|
|---|
| 223 |
|
|---|
| 224 |
(require 'esh-util) |
|---|
| 225 |
(unless (eshell-under-xemacs-p) |
|---|
| 226 |
(require 'eldoc)) |
|---|
| 227 |
(require 'esh-arg) |
|---|
| 228 |
(require 'esh-proc) |
|---|
| 229 |
(require 'esh-ext) |
|---|
| 230 |
|
|---|
| 231 |
|
|---|
| 232 |
|
|---|
| 233 |
(defcustom eshell-cmd-load-hook '(eshell-cmd-initialize) |
|---|
| 234 |
"*A hook that gets run when `eshell-cmd' is loaded." |
|---|
| 235 |
:type 'hook |
|---|
| 236 |
:group 'eshell-cmd) |
|---|
| 237 |
|
|---|
| 238 |
(defcustom eshell-debug-command nil |
|---|
| 239 |
"*If non-nil, enable debugging code. SSLLOOWW. |
|---|
| 240 |
This option is only useful for reporting bugs. If you enable it, you |
|---|
| 241 |
will have to visit the file 'eshell-cmd.el' and run the command |
|---|
| 242 |
\\[eval-buffer]." |
|---|
| 243 |
:type 'boolean |
|---|
| 244 |
:group 'eshell-cmd) |
|---|
| 245 |
|
|---|
| 246 |
(defcustom eshell-deferrable-commands |
|---|
| 247 |
'(eshell-named-command |
|---|
| 248 |
eshell-lisp-command |
|---|
| 249 |
eshell-process-identity) |
|---|
| 250 |
"*A list of functions which might return an ansychronous process. |
|---|
| 251 |
If they return a process object, execution of the calling Eshell |
|---|
| 252 |
command will wait for completion (in the background) before finishing |
|---|
| 253 |
the command." |
|---|
| 254 |
:type '(repeat function) |
|---|
| 255 |
:group 'eshell-cmd) |
|---|
| 256 |
|
|---|
| 257 |
(defcustom eshell-subcommand-bindings |
|---|
| 258 |
'((eshell-in-subcommand-p t) |
|---|
| 259 |
(default-directory default-directory) |
|---|
| 260 |
(process-environment (eshell-copy-environment))) |
|---|
| 261 |
"*A list of `let' bindings for subcommand environments." |
|---|
| 262 |
:type 'sexp |
|---|
| 263 |
:group 'eshell-cmd) |
|---|
| 264 |
|
|---|
| 265 |
(put 'risky-local-variable 'eshell-subcommand-bindings t) |
|---|
| 266 |
|
|---|
| 267 |
(defvar eshell-ensure-newline-p nil |
|---|
| 268 |
"If non-nil, ensure that a newline is emitted after a Lisp form. |
|---|
| 269 |
This can be changed by Lisp forms that are evaluated from the Eshell |
|---|
| 270 |
command line.") |
|---|
| 271 |
|
|---|
| 272 |
|
|---|
| 273 |
|
|---|
| 274 |
(defvar eshell-current-command nil) |
|---|
| 275 |
(defvar eshell-command-name nil) |
|---|
| 276 |
(defvar eshell-command-arguments nil) |
|---|
| 277 |
(defvar eshell-in-pipeline-p nil) |
|---|
| 278 |
(defvar eshell-in-subcommand-p nil) |
|---|
| 279 |
(defvar eshell-last-arguments nil) |
|---|
| 280 |
(defvar eshell-last-command-name nil) |
|---|
| 281 |
(defvar eshell-last-async-proc nil |
|---|
| 282 |
"When this foreground process completes, resume command evaluation.") |
|---|
| 283 |
|
|---|
| 284 |
|
|---|
| 285 |
|
|---|
| 286 |
(defsubst eshell-interactive-process () |
|---|
| 287 |
"Return currently running command process, if non-Lisp." |
|---|
| 288 |
eshell-last-async-proc) |
|---|
| 289 |
|
|---|
| 290 |
(defun eshell-cmd-initialize () |
|---|
| 291 |
"Initialize the Eshell command processing module." |
|---|
| 292 |
(set (make-local-variable 'eshell-current-command) nil) |
|---|
| 293 |
(set (make-local-variable 'eshell-command-name) nil) |
|---|
| 294 |
(set (make-local-variable 'eshell-command-arguments) nil) |
|---|
| 295 |
(set (make-local-variable 'eshell-last-arguments) nil) |
|---|
| 296 |
(set (make-local-variable 'eshell-last-command-name) nil) |
|---|
| 297 |
(set (make-local-variable 'eshell-last-async-proc) nil) |
|---|
| 298 |
|
|---|
| 299 |
(add-hook 'eshell-kill-hook 'eshell-resume-command nil t) |
|---|
| 300 |
|
|---|
| 301 |
|
|---|
| 302 |
|
|---|
| 303 |
|
|---|
| 304 |
|
|---|
| 305 |
(add-hook 'eshell-post-command-hook |
|---|
| 306 |
(function |
|---|
| 307 |
(lambda () |
|---|
| 308 |
(setq eshell-current-command nil |
|---|
| 309 |
eshell-last-async-proc nil))) nil t) |
|---|
| 310 |
|
|---|
| 311 |
(add-hook 'eshell-parse-argument-hook |
|---|
| 312 |
'eshell-parse-subcommand-argument nil t) |
|---|
| 313 |
(add-hook 'eshell-parse-argument-hook |
|---|
| 314 |
'eshell-parse-lisp-argument nil t) |
|---|
| 315 |
|
|---|
| 316 |
(when (eshell-using-module 'eshell-cmpl) |
|---|
| 317 |
(add-hook 'pcomplete-try-first-hook |
|---|
| 318 |
'eshell-complete-lisp-symbols nil t))) |
|---|
| 319 |
|
|---|
| 320 |
(eshell-deftest var last-result-var |
|---|
| 321 |
"\"last result\" variable" |
|---|
| 322 |
(eshell-command-result-p "+ 1 2; + $$ 2" "3\n5\n")) |
|---|
| 323 |
|
|---|
| 324 |
(eshell-deftest var last-result-var2 |
|---|
| 325 |
"\"last result\" variable" |
|---|
| 326 |
(eshell-command-result-p "+ 1 2; + $$ $$" "3\n6\n")) |
|---|
| 327 |
|
|---|
| 328 |
(eshell-deftest var last-arg-var |
|---|
| 329 |
"\"last arg\" variable" |
|---|
| 330 |
(eshell-command-result-p "+ 1 2; + $_ 4" "3\n6\n")) |
|---|
| 331 |
|
|---|
| 332 |
(defun eshell-complete-lisp-symbols () |
|---|
| 333 |
"If there is a user reference, complete it." |
|---|
| 334 |
(let ((arg (pcomplete-actual-arg))) |
|---|
| 335 |
(when (string-match (concat "\\`" eshell-lisp-regexp) arg) |
|---|
| 336 |
(setq pcomplete-stub (substring arg (match-end 0)) |
|---|
| 337 |
pcomplete-last-completion-raw t) |
|---|
| 338 |
(throw 'pcomplete-completions |
|---|
| 339 |
(all-completions pcomplete-stub obarray 'boundp))))) |
|---|
| 340 |
|
|---|
| 341 |
|
|---|
| 342 |
|
|---|
| 343 |
(defun eshell-parse-command (command &optional args top-level) |
|---|
| 344 |
"Parse the COMMAND, adding ARGS if given. |
|---|
| 345 |
COMMAND can either be a string, or a cons cell demarcating a buffer |
|---|
| 346 |
region. TOP-LEVEL, if non-nil, means that the outermost command (the |
|---|
| 347 |
user's input command) is being parsed, and that pre and post command |
|---|
| 348 |
hooks should be run before and after the command." |
|---|
| 349 |
(let* (sep-terms |
|---|
| 350 |
(terms |
|---|
| 351 |
(append |
|---|
| 352 |
(if (consp command) |
|---|
| 353 |
(eshell-parse-arguments (car command) (cdr command)) |
|---|
| 354 |
(let ((here (point)) |
|---|
| 355 |
(inhibit-point-motion-hooks t) |
|---|
| 356 |
after-change-functions) |
|---|
| 357 |
(insert command) |
|---|
| 358 |
(prog1 |
|---|
| 359 |
(eshell-parse-arguments here (point)) |
|---|
| 360 |
(delete-region here (point))))) |
|---|
| 361 |
args)) |
|---|
| 362 |
(commands |
|---|
| 363 |
(mapcar |
|---|
| 364 |
(function |
|---|
| 365 |
(lambda (cmd) |
|---|
| 366 |
(if (or (not (car sep-terms)) |
|---|
| 367 |
(string= (car sep-terms) ";")) |
|---|
| 368 |
(setq cmd |
|---|
| 369 |
(eshell-parse-pipeline cmd (not (car sep-terms)))) |
|---|
| 370 |
(setq cmd |
|---|
| 371 |
(list 'eshell-do-subjob |
|---|
| 372 |
(list 'list (eshell-parse-pipeline cmd))))) |
|---|
| 373 |
(setq sep-terms (cdr sep-terms)) |
|---|
| 374 |
(if eshell-in-pipeline-p |
|---|
| 375 |
cmd |
|---|
| 376 |
(list 'eshell-trap-errors cmd)))) |
|---|
| 377 |
(eshell-separate-commands terms "[&;]" nil 'sep-terms)))) |
|---|
| 378 |
(let ((cmd commands)) |
|---|
| 379 |
(while cmd |
|---|
| 380 |
(if (cdr cmd) |
|---|
| 381 |
(setcar cmd (list 'eshell-commands (car cmd)))) |
|---|
| 382 |
(setq cmd (cdr cmd)))) |
|---|
| 383 |
(setq commands |
|---|
| 384 |
(append (list 'progn) |
|---|
| 385 |
(if top-level |
|---|
| 386 |
(list '(run-hooks 'eshell-pre-command-hook))) |
|---|
| 387 |
(if (not top-level) |
|---|
| 388 |
commands |
|---|
| 389 |
(list |
|---|
| 390 |
(list 'catch (quote 'top-level) |
|---|
| 391 |
(append (list 'progn) commands)) |
|---|
| 392 |
'(run-hooks 'eshell-post-command-hook))))) |
|---|
| 393 |
(if top-level |
|---|
| 394 |
(list 'eshell-commands commands) |
|---|
| 395 |
commands))) |
|---|
| 396 |
|
|---|
| 397 |
(defun eshell-debug-show-parsed-args (terms) |
|---|
| 398 |
"Display parsed arguments in the debug buffer." |
|---|
| 399 |
(ignore |
|---|
| 400 |
(if eshell-debug-command |
|---|
| 401 |
(eshell-debug-command "parsed arguments" terms)))) |
|---|
| 402 |
|
|---|
| 403 |
(defun eshell-no-command-conversion (terms) |
|---|
| 404 |
"Don't convert the command argument." |
|---|
| 405 |
(ignore |
|---|
| 406 |
(if (and (listp (car terms)) |
|---|
| 407 |
(eq (caar terms) 'eshell-convert)) |
|---|
| 408 |
(setcar terms (cadr (car terms)))))) |
|---|
| 409 |
|
|---|
| 410 |
(defun eshell-subcommand-arg-values (terms) |
|---|
| 411 |
"Convert subcommand arguments {x} to ${x}, in order to take their values." |
|---|
| 412 |
(setq terms (cdr terms)) |
|---|
| 413 |
(while terms |
|---|
| 414 |
(if (and (listp (car terms)) |
|---|
| 415 |
(eq (caar terms) 'eshell-as-subcommand)) |
|---|
| 416 |
(setcar terms (list 'eshell-convert |
|---|
| 417 |
(list 'eshell-command-to-value |
|---|
| 418 |
(car terms))))) |
|---|
| 419 |
(setq terms (cdr terms)))) |
|---|
| 420 |
|
|---|
| 421 |
(defun eshell-rewrite-sexp-command (terms) |
|---|
| 422 |
"Rewrite a sexp in initial position, such as '(+ 1 2)'." |
|---|
| 423 |
|
|---|
| 424 |
(if (and (listp (car terms)) |
|---|
| 425 |
(eq (caar terms) 'eshell-command-to-value)) |
|---|
| 426 |
(car (cdar terms)))) |
|---|
| 427 |
|
|---|
| 428 |
(eshell-deftest cmd lisp-command |
|---|
| 429 |
"Evaluate Lisp command" |
|---|
| 430 |
(eshell-command-result-p "(+ 1 2)" "3")) |
|---|
| 431 |
|
|---|
| 432 |
(eshell-deftest cmd lisp-command-args |
|---|
| 433 |
"Evaluate Lisp command (ignore args)" |
|---|
| 434 |
(eshell-command-result-p "(+ 1 2) 3" "3")) |
|---|
| 435 |
|
|---|
| 436 |
(defun eshell-rewrite-initial-subcommand (terms) |
|---|
| 437 |
"Rewrite a subcommand in initial position, such as '{+ 1 2}'." |
|---|
| 438 |
(if (and (listp (car terms)) |
|---|
| 439 |
(eq (caar terms) 'eshell-as-subcommand)) |
|---|
| 440 |
(car terms))) |
|---|
| 441 |
|
|---|
| 442 |
(eshell-deftest cmd subcommand |
|---|
| 443 |
"Run subcommand" |
|---|
| 444 |
(eshell-command-result-p "{+ 1 2}" "3\n")) |
|---|
| 445 |
|
|---|
| 446 |
(eshell-deftest cmd subcommand-args |
|---|
| 447 |
"Run subcommand (ignore args)" |
|---|
| 448 |
(eshell-command-result-p "{+ 1 2} 3" "3\n")) |
|---|
| 449 |
|
|---|
| 450 |
(eshell-deftest cmd subcommand-lisp |
|---|
| 451 |
"Run subcommand + Lisp form" |
|---|
| 452 |
(eshell-command-result-p "{(+ 1 2)}" "3\n")) |
|---|
| 453 |
|
|---|
| 454 |
(defun eshell-rewrite-named-command (terms) |
|---|
| 455 |
"If no other rewriting rule transforms TERMS, assume a named command." |
|---|
| 456 |
(let ((sym (if eshell-in-pipeline-p |
|---|
| 457 |
'eshell-named-command* |
|---|
| 458 |
'eshell-named-command)) |
|---|
| 459 |
(cmd (car terms)) |
|---|
| 460 |
(args (cdr terms))) |
|---|
| 461 |
(if args |
|---|
| 462 |
(list sym cmd (append (list 'list) (cdr terms))) |
|---|
| 463 |
(list sym cmd)))) |
|---|
| 464 |
|
|---|
| 465 |
(eshell-deftest cmd named-command |
|---|
| 466 |
"Execute named command" |
|---|
| 467 |
(eshell-command-result-p "+ 1 2" "3\n")) |
|---|
| 468 |
|
|---|
| 469 |
(eval-when-compile |
|---|
| 470 |
(defvar eshell-command-body) |
|---|
| 471 |
(defvar eshell-test-body)) |
|---|
| 472 |
|
|---|
| 473 |
(defsubst eshell-invokify-arg (arg &optional share-output silent) |
|---|
| 474 |
"Change ARG so it can be invoked from a structured command. |
|---|
| 475 |
|
|---|
| 476 |
SHARE-OUTPUT, if non-nil, means this invocation should share the |
|---|
| 477 |
current output stream, which is separately redirectable. SILENT |
|---|
| 478 |
means the user and/or any redirections shouldn't see any output |
|---|
| 479 |
from this command. If both SHARE-OUTPUT and SILENT are non-nil, |
|---|
| 480 |
the second is ignored." |
|---|
| 481 |
|
|---|
| 482 |
|
|---|
| 483 |
|
|---|
| 484 |
|
|---|
| 485 |
|
|---|
| 486 |
|
|---|
| 487 |
|
|---|
| 488 |
(if (and (listp arg) |
|---|
| 489 |
(eq (car arg) 'eshell-convert) |
|---|
| 490 |
(eq (car (cadr arg)) 'eshell-command-to-value)) |
|---|
| 491 |
(if share-output |
|---|
| 492 |
(cadr (cadr arg)) |
|---|
| 493 |
(list 'eshell-commands (cadr (cadr arg)) |
|---|
| 494 |
silent)) |
|---|
| 495 |
arg)) |
|---|
| 496 |
|
|---|
| 497 |
(defun eshell-rewrite-for-command (terms) |
|---|
| 498 |
"Rewrite a `for' command into its equivalent Eshell command form. |
|---|
| 499 |
Because the implementation of `for' relies upon conditional evaluation |
|---|
| 500 |
of its argument (i.e., use of a Lisp special form), it must be |
|---|
| 501 |
implemented via rewriting, rather than as a function." |
|---|
| 502 |
(if (and (stringp (car terms)) |
|---|
| 503 |
(string= (car terms) "for") |
|---|
| 504 |
(stringp (nth 2 terms)) |
|---|
| 505 |
(string= (nth 2 terms) "in")) |
|---|
| 506 |
(let ((body (car (last terms)))) |
|---|
| 507 |
(setcdr (last terms 2) nil) |
|---|
| 508 |
(list |
|---|
| 509 |
'let (list (list 'for-items |
|---|
| 510 |
(append |
|---|
| 511 |
(list 'append) |
|---|
| 512 |
(mapcar |
|---|
| 513 |
(function |
|---|
| 514 |
(lambda (elem) |
|---|
| 515 |
(if (listp elem) |
|---|
| 516 |
elem |
|---|
| 517 |
(list 'list elem)))) |
|---|
| 518 |
(cdr (cddr terms))))) |
|---|
| 519 |
(list 'eshell-command-body |
|---|
| 520 |
(list 'quote (list nil))) |
|---|
| 521 |
(list 'eshell-test-body |
|---|
| 522 |
(list 'quote (list nil)))) |
|---|
| 523 |
(list |
|---|
| 524 |
'progn |
|---|
| 525 |
(list |
|---|
| 526 |
'while (list 'car (list 'symbol-value |
|---|
| 527 |
(list 'quote 'for-items))) |
|---|
| 528 |
(list |
|---|
| 529 |
'progn |
|---|
| 530 |
(list 'let |
|---|
| 531 |
(list (list (intern (cadr terms)) |
|---|
| 532 |
(list 'car |
|---|
| 533 |
(list 'symbol-value |
|---|
| 534 |
(list 'quote 'for-items))))) |
|---|
| 535 |
(list 'eshell-protect |
|---|
| 536 |
(eshell-invokify-arg body t))) |
|---|
| 537 |
(list 'setcar 'for-items |
|---|
| 538 |
(list 'cadr |
|---|
| 539 |
(list 'symbol-value |
|---|
| 540 |
(list 'quote 'for-items)))) |
|---|
| 541 |
(list 'setcdr 'for-items |
|---|
| 542 |
(list 'cddr |
|---|
| 543 |
(list 'symbol-value |
|---|
| 544 |
(list 'quote 'for-items)))))) |
|---|
| 545 |
(list 'eshell-close-handles |
|---|
| 546 |
'eshell-last-command-status |
|---|
| 547 |
(list 'list (quote 'quote) |
|---|
| 548 |
'eshell-last-command-result))))))) |
|---|
| 549 |
|
|---|
| 550 |
(defun eshell-structure-basic-command (func names keyword test body |
|---|
| 551 |
&optional else vocal-test) |
|---|
| 552 |
"With TERMS, KEYWORD, and two NAMES, structure a basic command. |
|---|
| 553 |
The first of NAMES should be the positive form, and the second the |
|---|
| 554 |
negative. It's not likely that users should ever need to call this |
|---|
| 555 |
function. |
|---|
| 556 |
|
|---|
| 557 |
If VOCAL-TEST is non-nil, it means output from the test should be |
|---|
| 558 |
shown, as well as output from the body." |
|---|
| 559 |
|
|---|
| 560 |
|
|---|
| 561 |
|
|---|
| 562 |
(unless (eq (car test) 'eshell-convert) |
|---|
| 563 |
(setq test |
|---|
| 564 |
(list 'progn test |
|---|
| 565 |
(list 'eshell-exit-success-p)))) |
|---|
| 566 |
|
|---|
| 567 |
|
|---|
| 568 |
|
|---|
| 569 |
|
|---|
| 570 |
|
|---|
| 571 |
|
|---|
| 572 |
(if (or (eq names nil) |
|---|
| 573 |
(and (listp names) |
|---|
| 574 |
(string= keyword (cadr names)))) |
|---|
| 575 |
(setq test (list 'not test))) |
|---|
| 576 |
|
|---|
| 577 |
|
|---|
| 578 |
|
|---|
| 579 |
(list |
|---|
| 580 |
'let (list (list 'eshell-command-body |
|---|
| 581 |
(list 'quote (list nil))) |
|---|
| 582 |
(list 'eshell-test-body |
|---|
| 583 |
(list 'quote (list nil)))) |
|---|
| 584 |
(list func test body else) |
|---|
| 585 |
(list 'eshell-close-handles |
|---|
| 586 |
'eshell-last-command-status |
|---|
| 587 |
(list 'list (quote 'quote) |
|---|
| 588 |
'eshell-last-command-result)))) |
|---|
| 589 |
|
|---|
| 590 |
(defun eshell-rewrite-while-command (terms) |
|---|
| 591 |
"Rewrite a `while' command into its equivalent Eshell command form. |
|---|
| 592 |
Because the implementation of `while' relies upon conditional |
|---|
| 593 |
evaluation of its argument (i.e., use of a Lisp special form), it |
|---|
| 594 |
must be implemented via rewriting, rather than as a function." |
|---|
| 595 |
(if (and (stringp (car terms)) |
|---|
| 596 |
(member (car terms) '("while" "until"))) |
|---|
| 597 |
(eshell-structure-basic-command |
|---|
| 598 |
'while '("while" "until") (car terms) |
|---|
| 599 |
(eshell-invokify-arg (cadr terms) nil t) |
|---|
| 600 |
(list 'eshell-protect |
|---|
| 601 |
(eshell-invokify-arg (car (last terms)) t))))) |
|---|
| 602 |
|
|---|
| 603 |
(defun eshell-rewrite-if-command (terms) |
|---|
| 604 |
"Rewrite an `if' command into its equivalent Eshell command form. |
|---|
| 605 |
Because the implementation of `if' relies upon conditional |
|---|
| 606 |
evaluation of its argument (i.e., use of a Lisp special form), it |
|---|
| 607 |
must be implemented via rewriting, rather than as a function." |
|---|
| 608 |
(if (and (stringp (car terms)) |
|---|
| 609 |
(member (car terms) '("if" "unless"))) |
|---|
| 610 |
(eshell-structure-basic-command |
|---|
| 611 |
'if '("if" "unless") (car terms) |
|---|
| 612 |
(eshell-invokify-arg (cadr terms) nil t) |
|---|
| 613 |
(list 'eshell-protect |
|---|
| 614 |
(eshell-invokify-arg |
|---|
| 615 |
(if (= (length terms) 4) |
|---|
| 616 |
(car (last terms 2)) |
|---|
| 617 |
(car (last terms))) t)) |
|---|
| 618 |
(if (= (length terms) 4) |
|---|
| 619 |
(list 'eshell-protect |
|---|
| 620 |
(eshell-invokify-arg |
|---|
| 621 |
(car (last terms)))) t)))) |
|---|
| 622 |
|
|---|
| 623 |
(defun eshell-exit-success-p () |
|---|
| 624 |
"Return non-nil if the last command was \"successful\". |
|---|
| 625 |
For a bit of Lisp code, this means a return value of non-nil. |
|---|
| 626 |
For an external command, it means an exit code of 0." |
|---|
| 627 |
(if (save-match-data |
|---|
| 628 |
(string-match "#<\\(Lisp object\\|function .*\\)>" |
|---|
| 629 |
eshell-last-command-name)) |
|---|
| 630 |
eshell-last-command-result |
|---|
| 631 |
(= eshell-last-command-status 0))) |
|---|
| 632 |
|
|---|
| 633 |
(defun eshell-parse-pipeline (terms &optional final-p) |
|---|
| 634 |
"Parse a pipeline from TERMS, return the appropriate Lisp forms." |
|---|
| 635 |
(let* (sep-terms |
|---|
| 636 |
(bigpieces (eshell-separate-commands terms "\\(&&\\|||\\)" |
|---|
| 637 |
nil 'sep-terms)) |
|---|
| 638 |
(bp bigpieces) |
|---|
| 639 |
(results (list t)) |
|---|
| 640 |
final) |
|---|
| 641 |
(while bp |
|---|
| 642 |
(let ((subterms (car bp))) |
|---|
| 643 |
(let* ((pieces (eshell-separate-commands subterms "|")) |
|---|
| 644 |
(p pieces)) |
|---|
| 645 |
(while p |
|---|
| 646 |
(let ((cmd (car p))) |
|---|
| 647 |
(run-hook-with-args 'eshell-pre-rewrite-command-hook cmd) |
|---|
| 648 |
(setq cmd (run-hook-with-args-until-success |
|---|
| 649 |
'eshell-rewrite-command-hook cmd)) |
|---|
| 650 |
(run-hook-with-args 'eshell-post-rewrite-command-hook 'cmd) |
|---|
| 651 |
(setcar p cmd)) |
|---|
| 652 |
(setq p (cdr p))) |
|---|
| 653 |
(nconc results |
|---|
| 654 |
(list |
|---|
| 655 |
(if (<= (length pieces) 1) |
|---|
| 656 |
(car pieces) |
|---|
| 657 |
(assert (not eshell-in-pipeline-p)) |
|---|
| 658 |
(list 'eshell-execute-pipeline |
|---|
| 659 |
(list 'quote pieces)))))) |
|---|
| 660 |
(setq bp (cdr bp)))) |
|---|
| 661 |
|
|---|
| 662 |
|
|---|
| 663 |
(setq results (cdr results) |
|---|
| 664 |
results (nreverse results) |
|---|
| 665 |
final (car results) |
|---|
| 666 |
results (cdr results) |
|---|
| 667 |
sep-terms (nreverse sep-terms)) |
|---|
| 668 |
(while results |
|---|
| 669 |
(assert (car sep-terms)) |
|---|
| 670 |
(setq final (eshell-structure-basic-command |
|---|
| 671 |
'if (string= (car sep-terms) "&&") "if" |
|---|
| 672 |
(list 'eshell-protect (car results)) |
|---|
| 673 |
(list 'eshell-protect final) |
|---|
| 674 |
nil t) |
|---|
| 675 |
results (cdr results) |
|---|
| 676 |
sep-terms (cdr sep-terms))) |
|---|
| 677 |
final)) |
|---|
| 678 |
|
|---|
| 679 |
(defun eshell-parse-subcommand-argument () |
|---|
| 680 |
"Parse a subcommand argument of the form '{command}'." |
|---|
| 681 |
(if (and (not eshell-current-argument) |
|---|
| 682 |
(not eshell-current-quoted) |
|---|
| 683 |
(eq (char-after) ?\{) |
|---|
| 684 |
(or (= (point-max) (1+ (point))) |
|---|
| 685 |
(not (eq (char-after (1+ (point))) ?\})))) |
|---|
| 686 |
(let ((end (eshell-find-delimiter ?\{ ?\}))) |
|---|
| 687 |
(if (not end) |
|---|
| 688 |
(throw 'eshell-incomplete ?\{) |
|---|
| 689 |
(when (eshell-arg-delimiter (1+ end)) |
|---|
| 690 |
(prog1 |
|---|
| 691 |
(list 'eshell-as-subcommand |
|---|
| 692 |
(eshell-parse-command (cons (1+ (point)) end))) |
|---|
| 693 |
(goto-char (1+ end)))))))) |
|---|
| 694 |
|
|---|
| 695 |
(defun eshell-parse-lisp-argument () |
|---|
| 696 |
"Parse a Lisp expression which is specified as an argument." |
|---|
| 697 |
(if (and (not eshell-current-argument) |
|---|
| 698 |
(not eshell-current-quoted) |
|---|
| 699 |
(looking-at eshell-lisp-regexp)) |
|---|
| 700 |
(let* ((here (point)) |
|---|
| 701 |
(obj |
|---|
| 702 |
(condition-case err |
|---|
| 703 |
(read (current-buffer)) |
|---|
| 704 |
(end-of-file |
|---|
| 705 |
(throw 'eshell-incomplete ?\())))) |
|---|
| 706 |
(if (eshell-arg-delimiter) |
|---|
| 707 |
(list 'eshell-command-to-value |
|---|
| 708 |
(list 'eshell-lisp-command (list 'quote obj))) |
|---|
| 709 |
(ignore (goto-char here)))))) |
|---|
| 710 |
|
|---|
| 711 |
(defun eshell-separate-commands (terms separator &optional |
|---|
| 712 |
reversed last-terms-sym) |
|---|
| 713 |
"Separate TERMS using SEPARATOR. |
|---|
| 714 |
If REVERSED is non-nil, the list of separated term groups will be |
|---|
| 715 |
returned in reverse order. If LAST-TERMS-SYM is a symbol, its value |
|---|
| 716 |
will be set to a list of all the separator operators found (or '(list |
|---|
| 717 |
nil)' if none)." |
|---|
| 718 |
(let ((sub-terms (list t)) |
|---|
| 719 |
(eshell-sep-terms (list t)) |
|---|
| 720 |
subchains) |
|---|
| 721 |
(while terms |
|---|
| 722 |
(if (and (consp (car terms)) |
|---|
| 723 |
(eq (caar terms) 'eshell-operator) |
|---|
| 724 |
(string-match (concat "^" separator "$") |
|---|
| 725 |
(nth 1 (car terms)))) |
|---|
| 726 |
(progn |
|---|
| 727 |
(nconc eshell-sep-terms (list (nth 1 (car terms)))) |
|---|
| 728 |
(setq subchains (cons (cdr sub-terms) subchains) |
|---|
| 729 |
sub-terms (list t))) |
|---|
| 730 |
(nconc sub-terms (list (car terms)))) |
|---|
| 731 |
(setq terms (cdr terms))) |
|---|
| 732 |
(if (> (length sub-terms) 1) |
|---|
| 733 |
(setq subchains (cons (cdr sub-terms) subchains))) |
|---|
| 734 |
(if reversed |
|---|
| 735 |
(progn |
|---|
| 736 |
(if last-terms-sym |
|---|
| 737 |
(set last-terms-sym (reverse (cdr eshell-sep-terms)))) |
|---|
| 738 |
subchains) |
|---|
| 739 |
(if last-terms-sym |
|---|
| 740 |
(set last-terms-sym (cdr eshell-sep-terms))) |
|---|
| 741 |
(nreverse subchains)))) |
|---|
| 742 |
|
|---|
| 743 |
|
|---|
| 744 |
|
|---|
| 745 |
|
|---|
| 746 |
|
|---|
| 747 |
|
|---|
| 748 |
|
|---|
| 749 |
|
|---|
| 750 |
|
|---|
| 751 |
|
|---|
| 752 |
|
|---|
| 753 |
|
|---|
| 754 |
|
|---|
| 755 |
|
|---|
| 756 |
|
|---|
| 757 |
|
|---|
| 758 |
|
|---|
| 759 |
|
|---|
| 760 |
|
|---|
| 761 |
(defmacro eshell-do-subjob (object) |
|---|
| 762 |
"Evaluate a command OBJECT as a subjob. |
|---|
| 763 |
We indicate that the process was run in the background by returning it |
|---|
| 764 |
ensconced in a list." |
|---|
| 765 |
`(let ((eshell-current-subjob-p t)) |
|---|
| 766 |
,object)) |
|---|
| 767 |
|
|---|
| 768 |
(defmacro eshell-commands (object &optional silent) |
|---|
| 769 |
"Place a valid set of handles, and context, around command OBJECT." |
|---|
| 770 |
`(let ((eshell-current-handles |
|---|
| 771 |
(eshell-create-handles ,(not silent) 'append)) |
|---|
| 772 |
eshell-current-subjob-p) |
|---|
| 773 |
,object)) |
|---|
| 774 |
|
|---|
| 775 |
(defmacro eshell-trap-errors (object) |
|---|
| 776 |
"Trap any errors that occur, so they are not entirely fatal. |
|---|
| 777 |
Also, the variable `eshell-this-command-hook' is available for the |
|---|
| 778 |
duration of OBJECT's evaluation. Note that functions should be added |
|---|
| 779 |
to this hook using `nconc', and *not* `add-hook'. |
|---|
| 780 |
|
|---|
| 781 |
Someday, when Scheme will become the dominant Emacs language, all of |
|---|
| 782 |
this grossness will be made to disappear by using `call/cc'..." |
|---|
| 783 |
`(let ((eshell-this-command-hook (list 'ignore))) |
|---|
| 784 |
(eshell-condition-case err |
|---|
| 785 |
(prog1 |
|---|
| 786 |
,object |
|---|
| 787 |
(run-hooks 'eshell-this-command-hook)) |
|---|
| 788 |
(error |
|---|
| 789 |
(run-hooks 'eshell-this-command-hook) |
|---|
| 790 |
(eshell-errorn (error-message-string err)) |
|---|
| 791 |
(eshell-close-handles 1))))) |
|---|
| 792 |
|
|---|
| 793 |
(defmacro eshell-copy-handles (object) |
|---|
| 794 |
"Duplicate current I/O handles, so OBJECT works with its own copy." |
|---|
| 795 |
`(let ((eshell-current-handles |
|---|
| 796 |
(eshell-create-handles |
|---|
| 797 |
(car (aref eshell-current-handles |
|---|
| 798 |
eshell-output-handle)) nil |
|---|
| 799 |
(car (aref eshell-current-handles |
|---|
| 800 |
eshell-error-handle)) nil))) |
|---|
| 801 |
,object)) |
|---|
| 802 |
|
|---|
| 803 |
(defmacro eshell-protect (object) |
|---|
| 804 |
"Protect I/O handles, so they aren't get closed after eval'ing OBJECT." |
|---|
| 805 |
`(progn |
|---|
| 806 |
(eshell-protect-handles eshell-current-handles) |
|---|
| 807 |
,object)) |
|---|
| 808 |
|
|---|
| 809 |
(defmacro eshell-do-pipelines (pipeline) |
|---|
| 810 |
"Execute the commands in PIPELINE, connecting each to one another." |
|---|
| 811 |
(when (setq pipeline (cadr pipeline)) |
|---|
| 812 |
`(eshell-copy-handles |
|---|
| 813 |
(progn |
|---|
| 814 |
,(when (cdr pipeline) |
|---|
| 815 |
`(let (nextproc) |
|---|
| 816 |
(progn |
|---|
| 817 |
(set 'nextproc |
|---|
| 818 |
(eshell-do-pipelines (quote ,(cdr pipeline)))) |
|---|
| 819 |
(eshell-set-output-handle ,eshell-output-handle |
|---|
| 820 |
'append nextproc) |
|---|
| 821 |
(eshell-set-output-handle ,eshell-error-handle |
|---|
| 822 |
'append nextproc) |
|---|
| 823 |
(set 'tailproc (or tailproc nextproc))))) |
|---|
| 824 |
,(let ((head (car pipeline))) |
|---|
| 825 |
(if (memq (car head) '(let progn)) |
|---|
| 826 |
(setq head (car (last head)))) |
|---|
| 827 |
(when (memq (car head) eshell-deferrable-commands) |
|---|
| 828 |
(ignore |
|---|
| 829 |
(setcar head |
|---|
| 830 |
(intern-soft |
|---|
| 831 |
(concat (symbol-name (car head)) "*")))))) |
|---|
| 832 |
,(car pipeline))))) |
|---|
| 833 |
|
|---|
| 834 |
(defmacro eshell-do-pipelines-synchronously (pipeline) |
|---|
| 835 |
"Execute the commands in PIPELINE in sequence synchronously. |
|---|
| 836 |
Output of each command is passed as input to the next one in the pipeline. |
|---|
| 837 |
This is used on systems where `start-process' is not supported." |
|---|
| 838 |
(when (setq pipeline (cadr pipeline)) |
|---|
| 839 |
`(let (result) |
|---|
| 840 |
(progn |
|---|
| 841 |
,(when (cdr pipeline) |
|---|
| 842 |
`(let (output-marker) |
|---|
| 843 |
(progn |
|---|
| 844 |
(set 'output-marker ,(point-marker)) |
|---|
| 845 |
(eshell-set-output-handle ,eshell-output-handle |
|---|
| 846 |
'append output-marker) |
|---|
| 847 |
(eshell-set-output-handle ,eshell-error-handle |
|---|
| 848 |
'append output-marker)))) |
|---|
| 849 |
|
|---|