Correctly handle reaching the end of the interval tree. (Bug#15344)
[bpt/emacs.git] / lisp / eshell / esh-var.el
CommitLineData
ae5e4c48 1;;; esh-var.el --- handling of variables -*- lexical-binding:t -*-
affbf647 2
ba318903 3;; Copyright (C) 1999-2014 Free Software Foundation, Inc.
affbf647 4
7de5b421
GM
5;; Author: John Wiegley <johnw@gnu.org>
6
affbf647
GM
7;; This file is part of GNU Emacs.
8
4ee57b2a 9;; GNU Emacs is free software: you can redistribute it and/or modify
affbf647 10;; it under the terms of the GNU General Public License as published by
4ee57b2a
GM
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
affbf647
GM
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
4ee57b2a 20;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
affbf647 21
affbf647
GM
22;;; Commentary:
23
24;; These are the possible variable interpolation syntaxes. Also keep
25;; in mind that if an argument looks like a number, it will be
26;; converted to a number. This is not significant when invoking
27;; external commands, but it's important when calling Lisp functions.
28;;
29;; $VARIABLE
30;;
31;; Interval the value of an environment variable, or a Lisp variable
32;;
33;; $ALSO-VAR
34;;
0138efd4 35;; "-" is a valid part of a variable name.
affbf647
GM
36;;
37;; $<MYVAR>-TOO
38;;
39;; Only "MYVAR" is part of the variable name in this case.
40;;
41;; $#VARIABLE
42;;
43;; Returns the length of the value of VARIABLE. This could also be
44;; done using the `length' Lisp function.
45;;
46;; $(lisp)
47;;
48;; Returns result of lisp evaluation. Note: Used alone like this, it
49;; is identical to just saying (lisp); but with the variable expansion
50;; form, the result may be interpolated a larger string, such as
51;; '$(lisp)/other'.
52;;
53;; ${command}
54;;
55;; Returns the value of an eshell subcommand. See the note above
56;; regarding Lisp evaluations.
57;;
58;; $ANYVAR[10]
59;;
60;; Return the 10th element of ANYVAR. If ANYVAR's value is a string,
61;; it will be split in order to make it a list. The splitting will
62;; occur at whitespace.
63;;
64;; $ANYVAR[: 10]
65;;
66;; As above, except that splitting occurs at the colon now.
67;;
68;; $ANYVAR[: 10 20]
69;;
70;; As above, but instead of returning just a string, it now returns a
71;; list of two strings. If the result is being interpolated into a
72;; larger string, this list will be flattened into one big string,
73;; with each element separated by a space.
74;;
75;; $ANYVAR["\\\\" 10]
76;;
77;; Separate on backslash characters. Actually, the first argument --
78;; if it doesn't have the form of a number, or a plain variable name
79;; -- can be any regular expression. So to split on numbers, use
80;; '$ANYVAR["[0-9]+" 10 20]'.
81;;
82;; $ANYVAR[hello]
83;;
84;; Calls `assoc' on ANYVAR with 'hello', expecting it to be an alist.
85;;
86;; $#ANYVAR[hello]
87;;
88;; Returns the length of the cdr of the element of ANYVAR who car is
89;; equal to "hello".
90;;
91;; There are also a few special variables defined by Eshell. '$$' is
92;; the value of the last command (t or nil, in the case of an external
93;; command). This makes it possible to chain results:
94;;
95;; /tmp $ echo /var/spool/mail/johnw
96;; /var/spool/mail/johnw
97;; /tmp $ dirname $$
98;; /var/spool/mail/
99;; /tmp $ cd $$
100;; /var/spool/mail $
101;;
102;; '$_' refers to the last argument of the last command. And $?
103;; contains the exit code of the last command (0 or 1 for Lisp
104;; functions, based on successful completion).
105
8c7309fe
GM
106;;; Code:
107
11740ce5
GM
108(provide 'esh-var)
109
f87b1284
GM
110(require 'esh-util)
111(require 'esh-cmd)
a464a6c7 112(require 'esh-opt)
f87b1284
GM
113
114(require 'pcomplete)
affbf647
GM
115(require 'env)
116(require 'ring)
117
11740ce5
GM
118(defgroup eshell-var nil
119 "Variable interpolation is introduced whenever the '$' character
120appears unquoted in any argument (except when that argument is
121surrounded by single quotes). It may be used to interpolate a
122variable value, a subcommand, or even the result of a Lisp form."
123 :tag "Variable handling"
124 :group 'eshell)
125
affbf647
GM
126;;; User Variables:
127
d783d303 128(defcustom eshell-var-load-hook nil
ec60da52 129 "A list of functions to call when loading `eshell-var'."
d783d303 130 :version "24.1" ; removed eshell-var-initialize
affbf647
GM
131 :type 'hook
132 :group 'eshell-var)
133
134(defcustom eshell-prefer-lisp-variables nil
ec60da52 135 "If non-nil, prefer Lisp variables to environment variables."
affbf647
GM
136 :type 'boolean
137 :group 'eshell-var)
138
139(defcustom eshell-complete-export-definition t
ec60da52 140 "If non-nil, completing names for `export' shows current definition."
affbf647
GM
141 :type 'boolean
142 :group 'eshell-var)
143
6995786d 144(defcustom eshell-modify-global-environment nil
ec60da52 145 "If non-nil, using `export' changes Emacs's global environment."
6995786d
JW
146 :type 'boolean
147 :group 'eshell-var)
148
affbf647 149(defcustom eshell-variable-name-regexp "[A-Za-z0-9_-]+"
ec60da52 150 "A regexp identifying what constitutes a variable name reference.
affbf647
GM
151Note that this only applies for '$NAME'. If the syntax '$<NAME>' is
152used, then NAME can contain any character, including angle brackets,
153if they are quoted with a backslash."
154 :type 'regexp
155 :group 'eshell-var)
156
157(defcustom eshell-variable-aliases-list
158 '(;; for eshell.el
159 ("COLUMNS" (lambda (indices) (window-width)) t)
160 ("LINES" (lambda (indices) (window-height)) t)
161
162 ;; for eshell-cmd.el
163 ("_" (lambda (indices)
164 (if (not indices)
165 (car (last eshell-last-arguments))
166 (eshell-apply-indices eshell-last-arguments
167 indices))))
168 ("?" eshell-last-command-status)
169 ("$" eshell-last-command-result)
170 ("0" eshell-command-name)
171 ("1" (lambda (indices) (nth 0 eshell-command-arguments)))
172 ("2" (lambda (indices) (nth 1 eshell-command-arguments)))
173 ("3" (lambda (indices) (nth 2 eshell-command-arguments)))
174 ("4" (lambda (indices) (nth 3 eshell-command-arguments)))
175 ("5" (lambda (indices) (nth 4 eshell-command-arguments)))
176 ("6" (lambda (indices) (nth 5 eshell-command-arguments)))
177 ("7" (lambda (indices) (nth 6 eshell-command-arguments)))
178 ("8" (lambda (indices) (nth 7 eshell-command-arguments)))
179 ("9" (lambda (indices) (nth 8 eshell-command-arguments)))
180 ("*" (lambda (indices)
181 (if (not indices)
182 eshell-command-arguments
183 (eshell-apply-indices eshell-command-arguments
184 indices)))))
ec60da52 185 "This list provides aliasing for variable references.
affbf647 186It is very similar in concept to what `eshell-user-aliases-list' does
0d8863b3
GM
187for commands. Each member of this defines the name of a command,
188and the Lisp value to return for that variable if it is accessed
189via the syntax '$NAME'.
affbf647
GM
190
191If the value is a function, that function will be called with two
192arguments: the list of the indices that was used in the reference, and
193whether the user is requesting the length of the ultimate element.
194For example, a reference of '$NAME[10][20]' would result in the
195function for alias `NAME' being called (assuming it were aliased to a
196function), and the arguments passed to this function would be the list
197'(10 20)', and nil."
198 :type '(repeat (list string sexp
199 (choice (const :tag "Copy to environment" t)
200 (const :tag "Use only in Eshell" nil))))
201 :group 'eshell-var)
202
203(put 'eshell-variable-aliases-list 'risky-local-variable t)
204
205;;; Functions:
206
207(defun eshell-var-initialize ()
208 "Initialize the variable handle code."
209 ;; Break the association with our parent's environment. Otherwise,
210 ;; changing a variable will affect all of Emacs.
6995786d
JW
211 (unless eshell-modify-global-environment
212 (set (make-local-variable 'process-environment)
213 (eshell-copy-environment)))
affbf647
GM
214
215 (define-key eshell-command-map [(meta ?v)] 'eshell-insert-envvar)
216
217 (set (make-local-variable 'eshell-special-chars-inside-quoting)
218 (append eshell-special-chars-inside-quoting '(?$)))
219 (set (make-local-variable 'eshell-special-chars-outside-quoting)
220 (append eshell-special-chars-outside-quoting '(?$)))
221
affbf647
GM
222 (add-hook 'eshell-parse-argument-hook 'eshell-interpolate-variable t t)
223
affbf647
GM
224 (add-hook 'eshell-prepare-command-hook
225 'eshell-handle-local-variables nil t)
226
227 (when (eshell-using-module 'eshell-cmpl)
affbf647
GM
228 (add-hook 'pcomplete-try-first-hook
229 'eshell-complete-variable-reference nil t)
230 (add-hook 'pcomplete-try-first-hook
231 'eshell-complete-variable-assignment nil t)))
232
233(defun eshell-handle-local-variables ()
234 "Allow for the syntax 'VAR=val <command> <args>'."
235 ;; strip off any null commands, which can only happen if a variable
236 ;; evaluates to nil, such as "$var x", where `var' is nil. The
237 ;; command name in that case becomes `x', for compatibility with
238 ;; most regular shells (the difference is that they do an
239 ;; interpolation pass before the argument parsing pass, but Eshell
240 ;; does both at the same time).
241 (while (and (not eshell-last-command-name)
242 eshell-last-arguments)
243 (setq eshell-last-command-name (car eshell-last-arguments)
244 eshell-last-arguments (cdr eshell-last-arguments)))
245 (let ((setvar "\\`\\([A-Za-z_][A-Za-z0-9_]*\\)=\\(.*\\)\\'")
246 (command (eshell-stringify eshell-last-command-name))
247 (args eshell-last-arguments))
248 ;; local variable settings (such as 'CFLAGS=-O2 make') are handled
249 ;; by making the whole command into a subcommand, and calling
250 ;; setenv immediately before the command is invoked. This means
251 ;; that 'BLAH=x cd blah' won't work exactly as expected, but that
252 ;; is by no means a typical use of local environment variables.
253 (if (and command (string-match setvar command))
254 (throw
255 'eshell-replace-command
256 (list
257 'eshell-as-subcommand
258 (append
259 (list 'progn)
260 (let ((l (list t)))
261 (while (string-match setvar command)
262 (nconc
263 l (list
264 (list 'setenv (match-string 1 command)
265 (match-string 2 command)
266 (= (length (match-string 2 command)) 0))))
267 (setq command (eshell-stringify (car args))
268 args (cdr args)))
269 (cdr l))
270 (list (list 'eshell-named-command
271 command (list 'quote args)))))))))
272
273(defun eshell-interpolate-variable ()
274 "Parse a variable interpolation.
275This function is explicit for adding to `eshell-parse-argument-hook'."
276 (when (and (eq (char-after) ?$)
ca7aae91 277 (/= (1+ (point)) (point-max)))
affbf647
GM
278 (forward-char)
279 (list 'eshell-escape-arg
280 (eshell-parse-variable))))
281
282(defun eshell/define (var-alias definition)
012d3cb5 283 "Define a VAR-ALIAS using DEFINITION."
affbf647
GM
284 (if (not definition)
285 (setq eshell-variable-aliases-list
286 (delq (assoc var-alias eshell-variable-aliases-list)
287 eshell-variable-aliases-list))
288 (let ((def (assoc var-alias eshell-variable-aliases-list))
289 (alias-def
290 (list var-alias
291 (list 'quote (if (= (length definition) 1)
292 (car definition)
293 definition)))))
294 (if def
295 (setq eshell-variable-aliases-list
296 (delq (assoc var-alias eshell-variable-aliases-list)
297 eshell-variable-aliases-list)))
298 (setq eshell-variable-aliases-list
299 (cons alias-def
300 eshell-variable-aliases-list))))
301 nil)
302
303(defun eshell/export (&rest sets)
a4fd4556 304 "This alias allows the `export' command to act as bash users expect."
affbf647 305 (while sets
ca7aae91
JW
306 (if (and (stringp (car sets))
307 (string-match "^\\([^=]+\\)=\\(.*\\)" (car sets)))
affbf647
GM
308 (setenv (match-string 1 (car sets))
309 (match-string 2 (car sets))))
310 (setq sets (cdr sets))))
311
79cf8e80
JW
312(defun pcomplete/eshell-mode/export ()
313 "Completion function for Eshell's `export'."
314 (while (pcomplete-here
315 (if eshell-complete-export-definition
316 process-environment
317 (eshell-envvar-names)))))
318
ca7aae91
JW
319(defun eshell/unset (&rest args)
320 "Unset an environment variable."
321 (while args
322 (if (stringp (car args))
323 (setenv (car args) nil t))
324 (setq args (cdr args))))
325
79cf8e80
JW
326(defun pcomplete/eshell-mode/unset ()
327 "Completion function for Eshell's `unset'."
328 (while (pcomplete-here (eshell-envvar-names))))
affbf647
GM
329
330(defun eshell/setq (&rest args)
331 "Allow command-ish use of `setq'."
332 (let (last-value)
333 (while args
334 (let ((sym (intern (car args)))
335 (val (cadr args)))
336 (setq last-value (set sym val)
337 args (cddr args))))
338 last-value))
339
340(defun pcomplete/eshell-mode/setq ()
341 "Completion function for Eshell's `setq'."
342 (while (and (pcomplete-here (all-completions pcomplete-stub
343 obarray 'boundp))
344 (pcomplete-here))))
345
346(defun eshell/env (&rest args)
f6b1b0a8 347 "Implementation of `env' in Lisp."
affbf647
GM
348 (eshell-init-print-buffer)
349 (eshell-eval-using-options
350 "env" args
351 '((?h "help" nil nil "show this usage screen")
352 :external "env"
353 :usage "<no arguments>")
a9eeff78 354 (dolist (setting (sort (eshell-environment-variables) 'string-lessp))
affbf647
GM
355 (eshell-buffered-print setting "\n"))
356 (eshell-flush)))
357
358(defun eshell-insert-envvar (envvar-name)
359 "Insert ENVVAR-NAME into the current buffer at point."
360 (interactive
361 (list (read-envvar-name "Name of environment variable: " t)))
362 (insert-and-inherit "$" envvar-name))
363
364(defun eshell-envvar-names (&optional environment)
365 "Return a list of currently visible environment variable names."
366 (mapcar (function
367 (lambda (x)
368 (substring x 0 (string-match "=" x))))
369 (or environment process-environment)))
370
371(defun eshell-environment-variables ()
372 "Return a `process-environment', fully updated.
373This involves setting any variable aliases which affect the
374environment, as specified in `eshell-variable-aliases-list'."
375 (let ((process-environment (eshell-copy-environment)))
a9eeff78 376 (dolist (var-alias eshell-variable-aliases-list)
affbf647
GM
377 (if (nth 2 var-alias)
378 (setenv (car var-alias)
379 (eshell-stringify
380 (or (eshell-get-variable (car var-alias)) "")))))
381 process-environment))
382
383(defun eshell-parse-variable ()
384 "Parse the next variable reference at point.
385The variable name could refer to either an environment variable, or a
386Lisp variable. The priority order depends on the setting of
387`eshell-prefer-lisp-variables'.
388
389Its purpose is to call `eshell-parse-variable-ref', and then to
390process any indices that come after the variable reference."
391 (let* ((get-len (when (eq (char-after) ?#)
392 (forward-char) t))
393 value indices)
394 (setq value (eshell-parse-variable-ref)
395 indices (and (not (eobp))
396 (eq (char-after) ?\[)
397 (eshell-parse-indices))
170266d0 398 value `(let ((indices ',indices)) ,value))
affbf647 399 (if get-len
170266d0 400 `(length ,value)
affbf647
GM
401 value)))
402
403(defun eshell-parse-variable-ref ()
404 "Eval a variable reference.
405Returns a Lisp form which, if evaluated, will return the value of the
406variable.
407
408Possible options are:
409
410 NAME an environment or Lisp variable value
411 <LONG-NAME> disambiguates the length of the name
412 {COMMAND} result of command is variable's value
413 (LISP-FORM) result of Lisp form is variable's value"
170266d0
SM
414 (cond
415 ((eq (char-after) ?{)
416 (let ((end (eshell-find-delimiter ?\{ ?\})))
417 (if (not end)
418 (throw 'eshell-incomplete ?\{)
419 (prog1
420 (list 'eshell-convert
421 (list 'eshell-command-to-value
422 (list 'eshell-as-subcommand
423 (eshell-parse-command
424 (cons (1+ (point)) end)))))
425 (goto-char (1+ end))))))
426 ((memq (char-after) '(?\' ?\"))
427 (let ((name (if (eq (char-after) ?\')
428 (eshell-parse-literal-quote)
429 (eshell-parse-double-quote))))
430 (if name
affbf647 431 (list 'eshell-get-variable (eval name) 'indices))))
170266d0
SM
432 ((eq (char-after) ?\<)
433 (let ((end (eshell-find-delimiter ?\< ?\>)))
434 (if (not end)
435 (throw 'eshell-incomplete ?\<)
436 (let* ((temp (make-temp-file temporary-file-directory))
437 (cmd (concat (buffer-substring (1+ (point)) end)
438 " > " temp)))
439 (prog1
440 (list
441 'let (list (list 'eshell-current-handles
442 (list 'eshell-create-handles temp
443 (list 'quote 'overwrite))))
444 (list
445 'progn
446 (list 'eshell-as-subcommand
447 (eshell-parse-command cmd))
448 (list 'ignore
449 (list 'nconc 'eshell-this-command-hook
450 (list 'list
451 (list 'function
452 (list 'lambda nil
453 (list 'delete-file temp))))))
454 (list 'quote temp)))
455 (goto-char (1+ end)))))))
456 ((eq (char-after) ?\()
457 (condition-case nil
458 (list 'eshell-command-to-value
459 (list 'eshell-lisp-command
460 (list 'quote (read (current-buffer)))))
461 (end-of-file
462 (throw 'eshell-incomplete ?\())))
463 ((assoc (char-to-string (char-after))
464 eshell-variable-aliases-list)
465 (forward-char)
466 (list 'eshell-get-variable
467 (char-to-string (char-before)) 'indices))
468 ((looking-at eshell-variable-name-regexp)
469 (prog1
470 (list 'eshell-get-variable (match-string 0) 'indices)
471 (goto-char (match-end 0))))
472 (t
473 (error "Invalid variable reference"))))
474
475(defvar eshell-glob-function)
affbf647 476
affbf647
GM
477(defun eshell-parse-indices ()
478 "Parse and return a list of list of indices."
479 (let (indices)
480 (while (eq (char-after) ?\[)
481 (let ((end (eshell-find-delimiter ?\[ ?\])))
482 (if (not end)
483 (throw 'eshell-incomplete ?\[)
484 (forward-char)
485 (let (eshell-glob-function)
486 (setq indices (cons (eshell-parse-arguments (point) end)
487 indices)))
488 (goto-char (1+ end)))))
489 (nreverse indices)))
490
491(defun eshell-get-variable (name &optional indices)
492 "Get the value for the variable NAME."
493 (let* ((alias (assoc name eshell-variable-aliases-list))
494 (var (if alias
495 (cadr alias)
496 name)))
497 (if (and alias (functionp var))
498 (funcall var indices)
499 (eshell-apply-indices
500 (cond
501 ((stringp var)
502 (let ((sym (intern-soft var)))
503 (if (and sym (boundp sym)
504 (or eshell-prefer-lisp-variables
1e53bb4b 505 (memq sym eshell--local-vars) ; bug#15372
affbf647
GM
506 (not (getenv var))))
507 (symbol-value sym)
508 (getenv var))))
509 ((symbolp var)
510 (symbol-value var))
511 (t
512 (error "Unknown variable `%s'" (eshell-stringify var))))
513 indices))))
514
515(defun eshell-apply-indices (value indices)
516 "Apply to VALUE all of the given INDICES, returning the sub-result.
517The format of INDICES is:
518
519 ((INT-OR-NAME-OR-OTHER INT-OR-NAME INT-OR-NAME ...)
520 ...)
521
522Each member of INDICES represents a level of nesting. If the first
523member of a sublist is not an integer or name, and the value it's
524reference is a string, that will be used as the regexp with which is
525to divide the string into sub-parts. The default is whitespace.
526Otherwise, each INT-OR-NAME refers to an element of the list value.
527Integers imply a direct index, and names, an associate lookup using
528`assoc'.
529
530For example, to retrieve the second element of a user's record in
531'/etc/passwd', the variable reference would look like:
532
533 ${egrep johnw /etc/passwd}[: 2]"
534 (while indices
535 (let ((refs (car indices)))
536 (when (stringp value)
537 (let (separator)
538 (if (not (or (not (stringp (caar indices)))
539 (string-match
540 (concat "^" eshell-variable-name-regexp "$")
541 (caar indices))))
542 (setq separator (caar indices)
543 refs (cdr refs)))
544 (setq value
545 (mapcar 'eshell-convert
546 (split-string value separator)))))
547 (cond
548 ((< (length refs) 0)
5b423d48 549 (error "Invalid array variable index: %s"
affbf647
GM
550 (eshell-stringify refs)))
551 ((= (length refs) 1)
552 (setq value (eshell-index-value value (car refs))))
553 (t
554 (let ((new-value (list t)))
555 (while refs
556 (nconc new-value
557 (list (eshell-index-value value
558 (car refs))))
559 (setq refs (cdr refs)))
560 (setq value (cdr new-value))))))
561 (setq indices (cdr indices)))
562 value)
563
564(defun eshell-index-value (value index)
565 "Reference VALUE using the given INDEX."
566 (if (stringp index)
567 (cdr (assoc index value))
568 (cond
569 ((ring-p value)
570 (if (> index (ring-length value))
571 (error "Index exceeds length of ring")
572 (ring-ref value index)))
573 ((listp value)
574 (if (> index (length value))
575 (error "Index exceeds length of list")
576 (nth index value)))
577 ((vectorp value)
578 (if (> index (length value))
579 (error "Index exceeds length of vector")
580 (aref value index)))
581 (t
582 (error "Invalid data type for indexing")))))
583
584;;;_* Variable name completion
585
586(defun eshell-complete-variable-reference ()
587 "If there is a variable reference, complete it."
588 (let ((arg (pcomplete-actual-arg)) index)
589 (when (setq index
590 (string-match
591 (concat "\\$\\(" eshell-variable-name-regexp
592 "\\)?\\'") arg))
593 (setq pcomplete-stub (substring arg (1+ index)))
594 (throw 'pcomplete-completions (eshell-variables-list)))))
595
596(defun eshell-variables-list ()
597 "Generate list of applicable variables."
598 (let ((argname pcomplete-stub)
599 completions)
a9eeff78 600 (dolist (alias eshell-variable-aliases-list)
affbf647
GM
601 (if (string-match (concat "^" argname) (car alias))
602 (setq completions (cons (car alias) completions))))
603 (sort
604 (append
605 (mapcar
606 (function
607 (lambda (varname)
608 (let ((value (eshell-get-variable varname)))
609 (if (and value
610 (stringp value)
611 (file-directory-p value))
6b0e3e4d 612 (concat varname "/")
affbf647
GM
613 varname))))
614 (eshell-envvar-names (eshell-environment-variables)))
615 (all-completions argname obarray 'boundp)
616 completions)
617 'string-lessp)))
618
619(defun eshell-complete-variable-assignment ()
620 "If there is a variable assignment, allow completion of entries."
621 (let ((arg (pcomplete-actual-arg)) pos)
622 (when (string-match (concat "\\`" eshell-variable-name-regexp "=") arg)
623 (setq pos (match-end 0))
624 (if (string-match "\\(:\\)[^:]*\\'" arg)
625 (setq pos (match-end 1)))
626 (setq pcomplete-stub (substring arg pos))
627 (throw 'pcomplete-completions (pcomplete-entries)))))
628
affbf647 629;;; esh-var.el ends here