OILS / core / state.py View on Github | oilshell.org

2580 lines, 1313 significant
1# Copyright 2016 Andy Chu. All rights reserved.
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7"""
8state.py - Interpreter state
9"""
10from __future__ import print_function
11import time as time_ # avoid name conflict
12
13from _devbuild.gen.id_kind_asdl import Id
14from _devbuild.gen.option_asdl import option_i
15from _devbuild.gen.runtime_asdl import (scope_e, scope_t, Cell)
16from _devbuild.gen.syntax_asdl import (loc, loc_t, Token, debug_frame,
17 debug_frame_e, debug_frame_t)
18from _devbuild.gen.types_asdl import opt_group_i
19from _devbuild.gen.value_asdl import (value, value_e, value_t, sh_lvalue,
20 sh_lvalue_e, sh_lvalue_t, LeftName,
21 y_lvalue_e, regex_match, regex_match_e,
22 regex_match_t, RegexMatch)
23from core import error
24from core.error import e_usage, e_die
25from core import num
26from core import pyos
27from core import pyutil
28from core import optview
29from display import ui
30from core import util
31from frontend import consts
32from frontend import location
33from frontend import match
34from mycpp import mops
35from mycpp import mylib
36from mycpp.mylib import (log, print_stderr, str_switch, tagswitch, iteritems,
37 NewDict)
38from osh import split
39from pylib import os_path
40from pylib import path_stat
41
42import libc
43import posix_ as posix
44from posix_ import X_OK # translated directly to C macro
45
46from typing import Tuple, List, Dict, Optional, Any, cast, TYPE_CHECKING
47
48if TYPE_CHECKING:
49 from _devbuild.gen.option_asdl import option_t
50 from core import alloc
51 from osh import sh_expr_eval
52
53_ = log
54
55# This was derived from bash --norc -c 'argv "$COMP_WORDBREAKS".
56# Python overwrites this to something Python-specific in Modules/readline.c, so
57# we have to set it back!
58# Used in both core/competion.py and osh/state.py
59_READLINE_DELIMS = ' \t\n"\'><=;|&(:'
60
61# flags for mem.SetValue()
62SetReadOnly = 1 << 0
63ClearReadOnly = 1 << 1
64SetExport = 1 << 2
65ClearExport = 1 << 3
66SetNameref = 1 << 4
67ClearNameref = 1 << 5
68
69
70def LookupExecutable(name, path_dirs, exec_required=True):
71 # type: (str, List[str], bool) -> Optional[str]
72 """
73 Returns either
74 - the name if it's a relative path that exists
75 - the executable name resolved against path_dirs
76 - None if not found
77 """
78 if len(name) == 0: # special case for "$(true)"
79 return None
80
81 if '/' in name:
82 return name if path_stat.exists(name) else None
83
84 for path_dir in path_dirs:
85 full_path = os_path.join(path_dir, name)
86 if exec_required:
87 found = posix.access(full_path, X_OK)
88 else:
89 found = path_stat.exists(full_path)
90
91 if found:
92 return full_path
93
94 return None
95
96
97class SearchPath(object):
98 """For looking up files in $PATH."""
99
100 def __init__(self, mem):
101 # type: (Mem) -> None
102 self.mem = mem
103 self.cache = {} # type: Dict[str, str]
104
105 def _GetPath(self):
106 # type: () -> List[str]
107
108 # TODO: Could cache this to avoid split() allocating all the time.
109 val = self.mem.GetValue('PATH')
110 UP_val = val
111 if val.tag() == value_e.Str:
112 val = cast(value.Str, UP_val)
113 return val.s.split(':')
114 else:
115 return [] # treat as empty path
116
117 def LookupOne(self, name, exec_required=True):
118 # type: (str, bool) -> Optional[str]
119 """
120 Returns the path itself (if relative path), the resolved path, or None.
121 """
122 return LookupExecutable(name,
123 self._GetPath(),
124 exec_required=exec_required)
125
126 def LookupReflect(self, name, do_all):
127 # type: (str, bool) -> List[str]
128 """
129 Like LookupOne(), with an option for 'type -a' to return all paths.
130 """
131 if len(name) == 0: # special case for "$(true)"
132 return []
133
134 if '/' in name:
135 if path_stat.exists(name):
136 return [name]
137 else:
138 return []
139
140 results = [] # type: List[str]
141 for path_dir in self._GetPath():
142 full_path = os_path.join(path_dir, name)
143 if path_stat.exists(full_path):
144 results.append(full_path)
145 if not do_all:
146 return results
147
148 return results
149
150 def CachedLookup(self, name):
151 # type: (str) -> Optional[str]
152 #log('name %r', name)
153 if name in self.cache:
154 return self.cache[name]
155
156 full_path = self.LookupOne(name)
157 if full_path is not None:
158 self.cache[name] = full_path
159 return full_path
160
161 def MaybeRemoveEntry(self, name):
162 # type: (str) -> None
163 """When the file system changes."""
164 mylib.dict_erase(self.cache, name)
165
166 def ClearCache(self):
167 # type: () -> None
168 """For hash -r."""
169 self.cache.clear()
170
171 def CachedCommands(self):
172 # type: () -> List[str]
173 return self.cache.values()
174
175
176class ctx_Source(object):
177 """For source builtin."""
178
179 def __init__(self, mem, source_name, argv):
180 # type: (Mem, str, List[str]) -> None
181 mem.PushSource(source_name, argv)
182 self.mem = mem
183 self.argv = argv
184
185 # Whenever we're sourcing, the 'is-main' builtin will return 1 (false)
186 self.to_restore = self.mem.is_main
187 self.mem.is_main = False
188
189 def __enter__(self):
190 # type: () -> None
191 pass
192
193 def __exit__(self, type, value, traceback):
194 # type: (Any, Any, Any) -> None
195 self.mem.PopSource(self.argv)
196
197 self.mem.is_main = self.to_restore
198
199
200class ctx_DebugTrap(object):
201 """For trap DEBUG."""
202
203 def __init__(self, mem):
204 # type: (Mem) -> None
205 mem.running_debug_trap = True
206 self.mem = mem
207
208 def __enter__(self):
209 # type: () -> None
210 pass
211
212 def __exit__(self, type, value, traceback):
213 # type: (Any, Any, Any) -> None
214 self.mem.running_debug_trap = False
215
216
217class ctx_ErrTrap(object):
218 """For trap ERR."""
219
220 def __init__(self, mem):
221 # type: (Mem) -> None
222 mem.running_err_trap = True
223 self.mem = mem
224
225 def __enter__(self):
226 # type: () -> None
227 pass
228
229 def __exit__(self, type, value, traceback):
230 # type: (Any, Any, Any) -> None
231 self.mem.running_err_trap = False
232
233
234class ctx_Option(object):
235 """Shopt --unset errexit { false }"""
236
237 def __init__(self, mutable_opts, opt_nums, b):
238 # type: (MutableOpts, List[int], bool) -> None
239 for opt_num in opt_nums:
240 mutable_opts.Push(opt_num, b)
241 if opt_num == option_i.errexit:
242 # it wasn't disabled
243 mutable_opts.errexit_disabled_tok.append(None)
244
245 self.mutable_opts = mutable_opts
246 self.opt_nums = opt_nums
247
248 def __enter__(self):
249 # type: () -> None
250 pass
251
252 def __exit__(self, type, value, traceback):
253 # type: (Any, Any, Any) -> None
254 for opt_num in self.opt_nums: # don't bother to do it in reverse order
255 if opt_num == option_i.errexit:
256 self.mutable_opts.errexit_disabled_tok.pop()
257 self.mutable_opts.Pop(opt_num)
258
259
260class ctx_AssignBuiltin(object):
261 """Local x=$(false) is disallowed."""
262
263 def __init__(self, mutable_opts):
264 # type: (MutableOpts) -> None
265 self.strict = False
266 if mutable_opts.Get(option_i.strict_errexit):
267 mutable_opts.Push(option_i._allow_command_sub, False)
268 mutable_opts.Push(option_i._allow_process_sub, False)
269 self.strict = True
270
271 self.mutable_opts = mutable_opts
272
273 def __enter__(self):
274 # type: () -> None
275 pass
276
277 def __exit__(self, type, value, traceback):
278 # type: (Any, Any, Any) -> None
279 if self.strict:
280 self.mutable_opts.Pop(option_i._allow_command_sub)
281 self.mutable_opts.Pop(option_i._allow_process_sub)
282
283
284class ctx_YshExpr(object):
285 """Command sub must fail in 'mystring' ++ $(false)"""
286
287 def __init__(self, mutable_opts):
288 # type: (MutableOpts) -> None
289
290 # Similar to $LIB_OSH/bash-strict.sh
291
292 # TODO: consider errexit:all group, or even ysh:all
293 # It would be nice if this were more efficient
294 mutable_opts.Push(option_i.command_sub_errexit, True)
295 mutable_opts.Push(option_i.errexit, True)
296 mutable_opts.Push(option_i.pipefail, True)
297 mutable_opts.Push(option_i.inherit_errexit, True)
298 mutable_opts.Push(option_i.strict_errexit, True)
299
300 # What about nounset? This has a similar pitfall -- it's not running
301 # like YSH.
302 # e.g. var x = $(echo $zz)
303
304 self.mutable_opts = mutable_opts
305
306 def __enter__(self):
307 # type: () -> None
308 pass
309
310 def __exit__(self, type, value, traceback):
311 # type: (Any, Any, Any) -> None
312 self.mutable_opts.Pop(option_i.command_sub_errexit)
313 self.mutable_opts.Pop(option_i.errexit)
314 self.mutable_opts.Pop(option_i.pipefail)
315 self.mutable_opts.Pop(option_i.inherit_errexit)
316 self.mutable_opts.Pop(option_i.strict_errexit)
317
318
319class ctx_ErrExit(object):
320 """Manages the errexit setting.
321
322 - The user can change it with builtin 'set' at any point in the code.
323 - These constructs implicitly disable 'errexit':
324 - if / while / until conditions
325 - ! (part of pipeline)
326 - && ||
327 """
328
329 def __init__(self, mutable_opts, b, disabled_tok):
330 # type: (MutableOpts, bool, Optional[Token]) -> None
331
332 # If we're disabling it, we need a span ID. If not, then we should NOT
333 # have one.
334 assert b == (disabled_tok is None)
335
336 mutable_opts.Push(option_i.errexit, b)
337 mutable_opts.errexit_disabled_tok.append(disabled_tok)
338
339 self.strict = False
340 if mutable_opts.Get(option_i.strict_errexit):
341 mutable_opts.Push(option_i._allow_command_sub, False)
342 mutable_opts.Push(option_i._allow_process_sub, False)
343 self.strict = True
344
345 self.mutable_opts = mutable_opts
346
347 def __enter__(self):
348 # type: () -> None
349 pass
350
351 def __exit__(self, type, value, traceback):
352 # type: (Any, Any, Any) -> None
353 self.mutable_opts.errexit_disabled_tok.pop()
354 self.mutable_opts.Pop(option_i.errexit)
355
356 if self.strict:
357 self.mutable_opts.Pop(option_i._allow_command_sub)
358 self.mutable_opts.Pop(option_i._allow_process_sub)
359
360
361class OptHook(object):
362 """Interface for option hooks."""
363
364 def __init__(self):
365 # type: () -> None
366 """Empty constructor for mycpp."""
367 pass
368
369 def OnChange(self, opt0_array, opt_name, b):
370 # type: (List[bool], str, bool) -> bool
371 """This method is called whenever an option is changed.
372
373 Returns success or failure.
374 """
375 return True
376
377
378def InitOpts():
379 # type: () -> List[bool]
380
381 opt0_array = [False] * option_i.ARRAY_SIZE
382 for opt_num in consts.DEFAULT_TRUE:
383 opt0_array[opt_num] = True
384 return opt0_array
385
386
387def MakeOpts(mem, opt_hook):
388 # type: (Mem, OptHook) -> Tuple[optview.Parse, optview.Exec, MutableOpts]
389
390 # Unusual representation: opt0_array + opt_stacks. For two features:
391 #
392 # - POSIX errexit disable semantics
393 # - Oil's shopt --set nullglob { ... }
394 #
395 # We could do it with a single List of stacks. But because shopt --set
396 # random_option { ... } is very uncommon, we optimize and store the ZERO
397 # element of the stack in a flat array opt0_array (default False), and then
398 # the rest in opt_stacks, where the value could be None. By allowing the
399 # None value, we save ~50 or so list objects in the common case.
400
401 opt0_array = InitOpts()
402 # Overrides, including errexit
403 no_stack = None # type: List[bool] # for mycpp
404 opt_stacks = [no_stack] * option_i.ARRAY_SIZE # type: List[List[bool]]
405
406 parse_opts = optview.Parse(opt0_array, opt_stacks)
407 exec_opts = optview.Exec(opt0_array, opt_stacks)
408 mutable_opts = MutableOpts(mem, opt0_array, opt_stacks, opt_hook)
409
410 return parse_opts, exec_opts, mutable_opts
411
412
413def _SetGroup(opt0_array, opt_nums, b):
414 # type: (List[bool], List[int], bool) -> None
415 for opt_num in opt_nums:
416 b2 = not b if opt_num in consts.DEFAULT_TRUE else b
417 opt0_array[opt_num] = b2
418
419
420def MakeOilOpts():
421 # type: () -> optview.Parse
422 opt0_array = InitOpts()
423 _SetGroup(opt0_array, consts.YSH_ALL, True)
424
425 no_stack = None # type: List[bool]
426 opt_stacks = [no_stack] * option_i.ARRAY_SIZE # type: List[List[bool]]
427
428 parse_opts = optview.Parse(opt0_array, opt_stacks)
429 return parse_opts
430
431
432def _AnyOptionNum(opt_name):
433 # type: (str) -> option_t
434 opt_num = consts.OptionNum(opt_name)
435 if opt_num == 0:
436 e_usage('got invalid option %r' % opt_name, loc.Missing)
437
438 # Note: we relaxed this for Oil so we can do 'shopt --unset errexit' consistently
439 #if opt_num not in consts.SHOPT_OPTION_NUMS:
440 # e_usage("doesn't own option %r (try 'set')" % opt_name)
441
442 return opt_num
443
444
445def _SetOptionNum(opt_name):
446 # type: (str) -> option_t
447 opt_num = consts.OptionNum(opt_name)
448 if opt_num == 0:
449 e_usage('got invalid option %r' % opt_name, loc.Missing)
450
451 if opt_num not in consts.SET_OPTION_NUMS:
452 e_usage("invalid option %r (try shopt)" % opt_name, loc.Missing)
453
454 return opt_num
455
456
457class MutableOpts(object):
458
459 def __init__(self, mem, opt0_array, opt_stacks, opt_hook):
460 # type: (Mem, List[bool], List[List[bool]], OptHook) -> None
461 self.mem = mem
462 self.opt0_array = opt0_array
463 self.opt_stacks = opt_stacks
464 self.errexit_disabled_tok = [] # type: List[Token]
465
466 # Used for 'set -o vi/emacs'
467 self.opt_hook = opt_hook
468
469 def Init(self):
470 # type: () -> None
471
472 # This comes after all the 'set' options.
473 UP_shellopts = self.mem.GetValue('SHELLOPTS')
474 # Always true in Oil, see Init above
475 if UP_shellopts.tag() == value_e.Str:
476 shellopts = cast(value.Str, UP_shellopts)
477 self._InitOptionsFromEnv(shellopts.s)
478
479 def _InitOptionsFromEnv(self, shellopts):
480 # type: (str) -> None
481 # e.g. errexit:nounset:pipefail
482 lookup = shellopts.split(':')
483 for opt_num in consts.SET_OPTION_NUMS:
484 name = consts.OptionName(opt_num)
485 if name in lookup:
486 self._SetOldOption(name, True)
487
488 def Push(self, opt_num, b):
489 # type: (int, bool) -> None
490 overlay = self.opt_stacks[opt_num]
491 if overlay is None or len(overlay) == 0:
492 self.opt_stacks[opt_num] = [b] # Allocate a new list
493 else:
494 overlay.append(b)
495
496 def Pop(self, opt_num):
497 # type: (int) -> bool
498 overlay = self.opt_stacks[opt_num]
499 assert overlay is not None
500 return overlay.pop()
501
502 def PushDynamicScope(self, b):
503 # type: (bool) -> None
504 """B: False if it's a proc, and True if it's a shell function."""
505 # If it's already disabled, keep it disabled
506 if not self.Get(option_i.dynamic_scope):
507 b = False
508 self.Push(option_i.dynamic_scope, b)
509
510 def PopDynamicScope(self):
511 # type: () -> None
512 self.Pop(option_i.dynamic_scope)
513
514 def Get(self, opt_num):
515 # type: (int) -> bool
516 # Like _Getter in core/optview.py
517 overlay = self.opt_stacks[opt_num]
518 if overlay is None or len(overlay) == 0:
519 return self.opt0_array[opt_num]
520 else:
521 return overlay[-1] # the top value
522
523 def _Set(self, opt_num, b):
524 # type: (int, bool) -> None
525 """Used to disable errexit.
526
527 For bash compatibility in command sub.
528 """
529
530 # Like _Getter in core/optview.py
531 overlay = self.opt_stacks[opt_num]
532 if overlay is None or len(overlay) == 0:
533 self.opt0_array[opt_num] = b
534 else:
535 overlay[-1] = b # The top value
536
537 def set_interactive(self):
538 # type: () -> None
539 self._Set(option_i.interactive, True)
540
541 def set_redefine_proc_func(self):
542 # type: () -> None
543 """For interactive shells."""
544 self._Set(option_i.redefine_proc_func, True)
545
546 def set_redefine_module(self):
547 # type: () -> None
548 """For interactive shells."""
549 self._Set(option_i.redefine_module, True)
550
551 def set_emacs(self):
552 # type: () -> None
553 self._Set(option_i.emacs, True)
554
555 def set_xtrace(self, b):
556 # type: (bool) -> None
557 self._Set(option_i.xtrace, b)
558
559 def _SetArrayByNum(self, opt_num, b):
560 # type: (int, bool) -> None
561 if (opt_num in consts.PARSE_OPTION_NUMS and
562 not self.mem.ParsingChangesAllowed()):
563 e_die('Syntax options must be set at the top level '
564 '(outside any function)')
565
566 self._Set(opt_num, b)
567
568 def SetDeferredErrExit(self, b):
569 # type: (bool) -> None
570 """Set the errexit flag, possibly deferring it.
571
572 Implements the unusual POSIX "defer" behavior. Callers: set -o
573 errexit, shopt -s oil:all, oil:upgrade
574 """
575 #log('Set %s', b)
576
577 # Defer it until we pop by setting the BOTTOM OF THE STACK.
578 self.opt0_array[option_i.errexit] = b
579
580 def DisableErrExit(self):
581 # type: () -> None
582 """Called by core/process.py to implement bash quirks."""
583 self._Set(option_i.errexit, False)
584
585 def ErrExitDisabledToken(self):
586 # type: () -> Optional[Token]
587 """If errexit is disabled by POSIX rules, return Token for construct.
588
589 e.g. the Token for 'if' or '&&' etc.
590 """
591 # Bug fix: The errexit disabling inherently follows a STACK DISCIPLINE.
592 # But we run trap handlers in the MAIN LOOP, which break this. So just
593 # declare that it's never disabled in a trap.
594 if self.Get(option_i._running_trap):
595 return None
596
597 if len(self.errexit_disabled_tok) == 0:
598 return None
599
600 return self.errexit_disabled_tok[-1]
601
602 def ErrExitIsDisabled(self):
603 # type: () -> bool
604 """
605 Similar to ErrExitDisabledToken, for ERR trap
606 """
607 if len(self.errexit_disabled_tok) == 0:
608 return False
609
610 return self.errexit_disabled_tok[-1] is not None
611
612 def _SetOldOption(self, opt_name, b):
613 # type: (str, bool) -> None
614 """Private version for synchronizing from SHELLOPTS."""
615 assert '_' not in opt_name
616 assert opt_name in consts.SET_OPTION_NAMES
617
618 opt_num = consts.OptionNum(opt_name)
619 assert opt_num != 0, opt_name
620
621 if opt_num == option_i.errexit:
622 self.SetDeferredErrExit(b)
623 else:
624 if opt_num == option_i.verbose and b:
625 print_stderr('Warning: set -o verbose not implemented')
626 self._SetArrayByNum(opt_num, b)
627
628 # note: may FAIL before we get here.
629
630 success = self.opt_hook.OnChange(self.opt0_array, opt_name, b)
631
632 def SetOldOption(self, opt_name, b):
633 # type: (str, bool) -> None
634 """For set -o, set +o, or shopt -s/-u -o."""
635 unused = _SetOptionNum(opt_name) # validate it
636 self._SetOldOption(opt_name, b)
637
638 UP_val = self.mem.GetValue('SHELLOPTS')
639 assert UP_val.tag() == value_e.Str, UP_val
640 val = cast(value.Str, UP_val)
641 shellopts = val.s
642
643 # Now check if SHELLOPTS needs to be updated. It may be exported.
644 #
645 # NOTE: It might be better to skip rewriting SEHLLOPTS in the common case
646 # where it is not used. We could do it lazily upon GET.
647
648 # Also, it would be slightly more efficient to update SHELLOPTS if
649 # settings were batched, Examples:
650 # - set -eu
651 # - shopt -s foo bar
652 if b:
653 if opt_name not in shellopts:
654 new_val = value.Str('%s:%s' % (shellopts, opt_name))
655 self.mem.InternalSetGlobal('SHELLOPTS', new_val)
656 else:
657 if opt_name in shellopts:
658 names = [n for n in shellopts.split(':') if n != opt_name]
659 new_val = value.Str(':'.join(names))
660 self.mem.InternalSetGlobal('SHELLOPTS', new_val)
661
662 def SetAnyOption(self, opt_name, b):
663 # type: (str, bool) -> None
664 """For shopt -s/-u and sh -O/+O."""
665
666 # shopt -s all:oil turns on all Oil options, which includes all strict #
667 # options
668 opt_group = consts.OptionGroupNum(opt_name)
669 if opt_group == opt_group_i.YshUpgrade:
670 _SetGroup(self.opt0_array, consts.YSH_UPGRADE, b)
671 self.SetDeferredErrExit(b) # Special case
672 return
673
674 if opt_group == opt_group_i.YshAll:
675 _SetGroup(self.opt0_array, consts.YSH_ALL, b)
676 self.SetDeferredErrExit(b) # Special case
677 return
678
679 if opt_group == opt_group_i.StrictAll:
680 _SetGroup(self.opt0_array, consts.STRICT_ALL, b)
681 return
682
683 opt_num = _AnyOptionNum(opt_name)
684
685 if opt_num == option_i.errexit:
686 self.SetDeferredErrExit(b)
687 return
688
689 self._SetArrayByNum(opt_num, b)
690
691 def ShowOptions(self, opt_names):
692 # type: (List[str]) -> None
693 """For 'set -o' and 'shopt -p -o'."""
694 # TODO: Maybe sort them differently?
695
696 if len(opt_names) == 0: # if none, supplied, show all
697 opt_names = [consts.OptionName(i) for i in consts.SET_OPTION_NUMS]
698
699 for opt_name in opt_names:
700 opt_num = _SetOptionNum(opt_name)
701 b = self.Get(opt_num)
702 print('set %so %s' % ('-' if b else '+', opt_name))
703
704 def ShowShoptOptions(self, opt_names):
705 # type: (List[str]) -> None
706 """For 'shopt -p'."""
707
708 # Respect option groups.
709 opt_nums = [] # type: List[int]
710 for opt_name in opt_names:
711 opt_group = consts.OptionGroupNum(opt_name)
712 if opt_group == opt_group_i.YshUpgrade:
713 opt_nums.extend(consts.YSH_UPGRADE)
714 elif opt_group == opt_group_i.YshAll:
715 opt_nums.extend(consts.YSH_ALL)
716 elif opt_group == opt_group_i.StrictAll:
717 opt_nums.extend(consts.STRICT_ALL)
718 else:
719 index = consts.OptionNum(opt_name)
720 # Minor incompatibility with bash: we validate everything before
721 # printing.
722 if index == 0:
723 e_usage('got invalid option %r' % opt_name, loc.Missing)
724 opt_nums.append(index)
725
726 if len(opt_names) == 0:
727 # If none supplied, show all>
728 # TODO: Should this show 'set' options too?
729 opt_nums.extend(consts.VISIBLE_SHOPT_NUMS)
730
731 for opt_num in opt_nums:
732 b = self.Get(opt_num)
733 print('shopt -%s %s' %
734 ('s' if b else 'u', consts.OptionName(opt_num)))
735
736
737class _ArgFrame(object):
738 """Stack frame for arguments array."""
739
740 def __init__(self, argv):
741 # type: (List[str]) -> None
742 self.argv = argv
743 self.num_shifted = 0
744
745 def __repr__(self):
746 # type: () -> str
747 return '<_ArgFrame %s %d at %x>' % (self.argv, self.num_shifted,
748 id(self))
749
750 def Dump(self):
751 # type: () -> Dict[str, value_t]
752 items = [value.Str(s) for s in self.argv] # type: List[value_t]
753 argv = value.List(items)
754 return {
755 'argv': argv,
756 'num_shifted': num.ToBig(self.num_shifted),
757 }
758
759 def GetArgNum(self, arg_num):
760 # type: (int) -> value_t
761 index = self.num_shifted + arg_num - 1
762 if index >= len(self.argv):
763 return value.Undef
764
765 return value.Str(self.argv[index])
766
767 def GetArgv(self):
768 # type: () -> List[str]
769 return self.argv[self.num_shifted:]
770
771 def GetNumArgs(self):
772 # type: () -> int
773 return len(self.argv) - self.num_shifted
774
775 def SetArgv(self, argv):
776 # type: (List[str]) -> None
777 self.argv = argv
778 self.num_shifted = 0
779
780
781def _DumpVarFrame(frame):
782 # type: (Dict[str, Cell]) -> Dict[str, value_t]
783 """Dump the stack frame as reasonably compact and readable JSON."""
784
785 vars_json = {} # type: Dict[str, value_t]
786 for name, cell in iteritems(frame):
787 cell_json = {} # type: Dict[str, value_t]
788
789 buf = mylib.BufWriter()
790 if cell.exported:
791 buf.write('x')
792 if cell.readonly:
793 buf.write('r')
794 flags = buf.getvalue()
795 if len(flags):
796 cell_json['flags'] = value.Str(flags)
797
798 # TODO:
799 # - Use packle for crash dumps! Then we can represent object cycles
800 # - Right now the JSON serializer will probably crash
801 # - although BashArray and BashAssoc may need 'type' tags
802 # - they don't round trip correctly
803 # - maybe add value.Tombstone here or something?
804 # - value.{Func,Eggex,...} may have value.Tombstone and
805 # vm.ValueIdString()?
806
807 with tagswitch(cell.val) as case:
808 if case(value_e.Undef):
809 cell_json['val'] = value.Null
810
811 elif case(value_e.Str, value_e.BashArray, value_e.BashAssoc):
812 cell_json['val'] = cell.val
813
814 else:
815 # TODO: should we show the object ID here?
816 pass
817
818 vars_json[name] = value.Dict(cell_json)
819
820 return vars_json
821
822
823def GetWorkingDir():
824 # type: () -> str
825 """Fallback for pwd and $PWD when there's no 'cd' and no inherited $PWD."""
826 try:
827 return posix.getcwd()
828 except (IOError, OSError) as e:
829 e_die("Can't determine working directory: %s" % pyutil.strerror(e))
830
831
832def _LineNumber(tok):
833 # type: (Optional[Token]) -> str
834 """ For $BASH_LINENO """
835 if tok is None:
836 return '-1'
837 return str(tok.line.line_num)
838
839
840def _AddCallToken(d, token):
841 # type: (Dict[str, value_t], Optional[Token]) -> None
842 if token is None:
843 return
844 d['call_source'] = value.Str(ui.GetLineSourceString(token.line))
845 d['call_line_num'] = num.ToBig(token.line.line_num)
846 d['call_line'] = value.Str(token.line.content)
847
848
849def _InitDefaults(mem):
850 # type: (Mem) -> None
851
852 # Default value; user may unset it.
853 # $ echo -n "$IFS" | python -c 'import sys;print repr(sys.stdin.read())'
854 # ' \t\n'
855 SetGlobalString(mem, 'IFS', split.DEFAULT_IFS)
856
857 # NOTE: Should we put these in a name_map for Oil?
858 SetGlobalString(mem, 'UID', str(posix.getuid()))
859 SetGlobalString(mem, 'EUID', str(posix.geteuid()))
860 SetGlobalString(mem, 'PPID', str(posix.getppid()))
861
862 SetGlobalString(mem, 'HOSTNAME', libc.gethostname())
863
864 # In bash, this looks like 'linux-gnu', 'linux-musl', etc. Scripts test
865 # for 'darwin' and 'freebsd' too. They generally don't like at 'gnu' or
866 # 'musl'. We don't have that info, so just make it 'linux'.
867 SetGlobalString(mem, 'OSTYPE', pyos.OsType())
868
869 # For getopts builtin
870 SetGlobalString(mem, 'OPTIND', '1')
871
872 # When xtrace_rich is off, this is just like '+ ', the shell default
873 SetGlobalString(mem, 'PS4', '${SHX_indent}${SHX_punct}${SHX_pid_str} ')
874
875 # bash-completion uses this. Value copied from bash. It doesn't integrate
876 # with 'readline' yet.
877 SetGlobalString(mem, 'COMP_WORDBREAKS', _READLINE_DELIMS)
878
879 # TODO on $HOME: bash sets it if it's a login shell and not in POSIX mode!
880 # if (login_shell == 1 && posixly_correct == 0)
881 # set_home_var ();
882
883
884def InitVarsFromEnv(mem, environ):
885 # type: (Mem, Dict[str, str]) -> None
886
887 # This is the way dash and bash work -- at startup, they turn everything in
888 # 'environ' variable into shell variables. Bash has an export_env
889 # variable. Dash has a loop through environ in init.c
890 for n, v in iteritems(environ):
891 mem.SetNamed(location.LName(n),
892 value.Str(v),
893 scope_e.GlobalOnly,
894 flags=SetExport)
895
896 # If it's not in the environment, initialize it. This makes it easier to
897 # update later in MutableOpts.
898
899 # TODO: IFS, etc. should follow this pattern. Maybe need a SysCall
900 # interface? self.syscall.getcwd() etc.
901
902 val = mem.GetValue('SHELLOPTS')
903 if val.tag() == value_e.Undef:
904 SetGlobalString(mem, 'SHELLOPTS', '')
905 # Now make it readonly
906 mem.SetNamed(location.LName('SHELLOPTS'),
907 None,
908 scope_e.GlobalOnly,
909 flags=SetReadOnly)
910
911 # Usually we inherit PWD from the parent shell. When it's not set, we may
912 # compute it.
913 val = mem.GetValue('PWD')
914 if val.tag() == value_e.Undef:
915 SetGlobalString(mem, 'PWD', GetWorkingDir())
916 # Now mark it exported, no matter what. This is one of few variables
917 # EXPORTED. bash and dash both do it. (e.g. env -i -- dash -c env)
918 mem.SetNamed(location.LName('PWD'),
919 None,
920 scope_e.GlobalOnly,
921 flags=SetExport)
922
923 val = mem.GetValue('PATH')
924 if val.tag() == value_e.Undef:
925 # Setting PATH to these two dirs match what zsh and mksh do. bash and dash
926 # add {,/usr/,/usr/local}/{bin,sbin}
927 SetGlobalString(mem, 'PATH', '/bin:/usr/bin')
928
929
930def InitMem(mem, environ, version_str):
931 # type: (Mem, Dict[str, str], str) -> None
932 """Initialize memory with shell defaults.
933
934 Other interpreters could have different builtin variables.
935 """
936 # TODO: REMOVE this legacy. ble.sh checks it!
937 SetGlobalString(mem, 'OIL_VERSION', version_str)
938
939 SetGlobalString(mem, 'OILS_VERSION', version_str)
940
941 # The source builtin understands '///' to mean "relative to embedded stdlib"
942 SetGlobalString(mem, 'LIB_OSH', '///osh')
943 SetGlobalString(mem, 'LIB_YSH', '///ysh')
944
945 # - C spells it NAN
946 # - JavaScript spells it NaN
947 # - Python 2 has float('nan'), while Python 3 has math.nan.
948 #
949 # - libc prints the strings 'nan' and 'inf'
950 # - Python 3 prints the strings 'nan' and 'inf'
951 # - JavaScript prints 'NaN' and 'Infinity', which is more stylized
952 _SetGlobalValue(mem, 'NAN', value.Float(pyutil.nan()))
953 _SetGlobalValue(mem, 'INFINITY', value.Float(pyutil.infinity()))
954
955 _InitDefaults(mem)
956
957
958def InitInteractive(mem):
959 # type: (Mem) -> None
960 """Initialization that's only done in the interactive/headless shell."""
961
962 # Same default PS1 as bash
963 if mem.GetValue('PS1').tag() == value_e.Undef:
964 SetGlobalString(mem, 'PS1', r'\s-\v\$ ')
965
966
967class ctx_FuncCall(object):
968 """For func calls."""
969
970 def __init__(self, mem, func):
971 # type: (Mem, value.Func) -> None
972
973 frame = NewDict() # type: Dict[str, Cell]
974 mem.var_stack.append(frame)
975
976 mem.PushCall(func.name, func.parsed.name)
977 self.mem = mem
978
979 def __enter__(self):
980 # type: () -> None
981 pass
982
983 def __exit__(self, type, value, traceback):
984 # type: (Any, Any, Any) -> None
985 self.mem.PopCall()
986 self.mem.var_stack.pop()
987
988
989class ctx_ProcCall(object):
990 """For proc calls, including shell functions."""
991
992 def __init__(self, mem, mutable_opts, proc, argv):
993 # type: (Mem, MutableOpts, value.Proc, List[str]) -> None
994
995 # TODO:
996 # - argv stack shouldn't be used for procs
997 # - we can bind a real variable @A if we want
998 # - procs should be in the var namespace
999 #
1000 # should we separate procs and shell functions?
1001 # - dynamic scope is one difference
1002 # - '$@" shift etc. are another difference
1003
1004 frame = NewDict() # type: Dict[str, Cell]
1005
1006 assert argv is not None
1007 if proc.sh_compat:
1008 # shell function
1009 mem.argv_stack.append(_ArgFrame(argv))
1010 else:
1011 # procs
1012 # - open: is equivalent to ...ARGV
1013 # - closed: ARGV is empty list
1014 frame['ARGV'] = _MakeArgvCell(argv)
1015
1016 mem.var_stack.append(frame)
1017
1018 mem.PushCall(proc.name, proc.name_tok)
1019
1020 # Dynamic scope is only for shell functions
1021 mutable_opts.PushDynamicScope(proc.sh_compat)
1022
1023 # It may have been disabled with ctx_ErrExit for 'if echo $(false)', but
1024 # 'if p' should be allowed.
1025 self.mem = mem
1026 self.mutable_opts = mutable_opts
1027 self.sh_compat = proc.sh_compat
1028
1029 def __enter__(self):
1030 # type: () -> None
1031 pass
1032
1033 def __exit__(self, type, value, traceback):
1034 # type: (Any, Any, Any) -> None
1035 self.mutable_opts.PopDynamicScope()
1036 self.mem.PopCall()
1037 self.mem.var_stack.pop()
1038
1039 if self.sh_compat:
1040 self.mem.argv_stack.pop()
1041
1042
1043class ctx_Temp(object):
1044 """For FOO=bar myfunc, etc."""
1045
1046 def __init__(self, mem):
1047 # type: (Mem) -> None
1048 mem.PushTemp()
1049 self.mem = mem
1050
1051 def __enter__(self):
1052 # type: () -> None
1053 pass
1054
1055 def __exit__(self, type, value, traceback):
1056 # type: (Any, Any, Any) -> None
1057 self.mem.PopTemp()
1058
1059
1060class ctx_Registers(object):
1061 """For $PS1, $PS4, $PROMPT_COMMAND, traps, and headless EVAL.
1062
1063 This is tightly coupled to state.Mem, so it's not in builtin/pure_ysh.
1064 """
1065
1066 def __init__(self, mem):
1067 # type: (Mem) -> None
1068
1069 # Because some prompts rely on the status leaking. See issue #853.
1070 # PS1 also does.
1071 last = mem.last_status[-1]
1072 mem.last_status.append(last)
1073 mem.try_status.append(0)
1074 mem.try_error.append(value.Dict({}))
1075
1076 # TODO: We should also copy these values! Turn the whole thing into a
1077 # frame.
1078 mem.pipe_status.append([])
1079 mem.process_sub_status.append([])
1080
1081 mem.regex_match.append(regex_match.No)
1082
1083 self.mem = mem
1084
1085 def __enter__(self):
1086 # type: () -> None
1087 pass
1088
1089 def __exit__(self, type, value, traceback):
1090 # type: (Any, Any, Any) -> None
1091 self.mem.regex_match.pop()
1092
1093 self.mem.process_sub_status.pop()
1094 self.mem.pipe_status.pop()
1095
1096 self.mem.try_error.pop()
1097 self.mem.try_status.pop()
1098 self.mem.last_status.pop()
1099
1100
1101class ctx_ThisDir(object):
1102 """For $_this_dir."""
1103
1104 def __init__(self, mem, filename):
1105 # type: (Mem, Optional[str]) -> None
1106 self.do_pop = False
1107 if filename is not None: # script_name in main() may be -c, etc.
1108 d = os_path.dirname(os_path.abspath(filename))
1109 mem.this_dir.append(d)
1110 self.do_pop = True
1111
1112 self.mem = mem
1113
1114 def __enter__(self):
1115 # type: () -> None
1116 pass
1117
1118 def __exit__(self, type, value, traceback):
1119 # type: (Any, Any, Any) -> None
1120 if self.do_pop:
1121 self.mem.this_dir.pop()
1122
1123
1124def _MakeArgvCell(argv):
1125 # type: (List[str]) -> Cell
1126 items = [value.Str(a) for a in argv] # type: List[value_t]
1127 return Cell(False, False, False, value.List(items))
1128
1129
1130class ctx_Eval(object):
1131 """Push temporary set of variables, $0, $1, $2, etc."""
1132
1133 def __init__(self, mem, dollar0, pos_args, vars):
1134 # type: (Mem, Optional[str], Optional[List[str]], Optional[Dict[str, value_t]]) -> None
1135 self.mem = mem
1136 self.dollar0 = dollar0
1137 self.pos_args = pos_args
1138 self.vars = vars
1139
1140 # $0 needs to have lexical scoping. So we store it with other locals.
1141 # As "0" cannot be parsed as an lvalue, we can safely store dollar0 there.
1142 if dollar0 is not None:
1143 assert mem.GetValue("0", scope_e.LocalOnly).tag() == value_e.Undef
1144 self.dollar0_lval = LeftName("0", loc.Missing)
1145 mem.SetLocalName(self.dollar0_lval, value.Str(dollar0))
1146
1147 if pos_args is not None:
1148 mem.argv_stack.append(_ArgFrame(pos_args))
1149
1150 if vars is not None:
1151 self.restore = [] # type: List[Tuple[LeftName, value_t]]
1152 self._Push(vars)
1153
1154 def __enter__(self):
1155 # type: () -> None
1156 pass
1157
1158 def __exit__(self, type, value_, traceback):
1159 # type: (Any, Any, Any) -> None
1160 if self.vars is not None:
1161 self._Pop()
1162
1163 if self.pos_args is not None:
1164 self.mem.argv_stack.pop()
1165
1166 if self.dollar0 is not None:
1167 self.mem.SetLocalName(self.dollar0_lval, value.Undef)
1168
1169 # Note: _Push and _Pop are separate methods because the C++ translation
1170 # doesn't like when they are inline in __init__ and __exit__.
1171 def _Push(self, vars):
1172 # type: (Dict[str, value_t]) -> None
1173 for name in vars:
1174 lval = location.LName(name)
1175 # LocalOnly because we are only overwriting the current scope
1176 old_val = self.mem.GetValue(name, scope_e.LocalOnly)
1177 self.restore.append((lval, old_val))
1178 self.mem.SetNamed(lval, vars[name], scope_e.LocalOnly)
1179
1180 def _Pop(self):
1181 # type: () -> None
1182 for lval, old_val in self.restore:
1183 if old_val.tag() == value_e.Undef:
1184 self.mem.Unset(lval, scope_e.LocalOnly)
1185 else:
1186 self.mem.SetNamed(lval, old_val, scope_e.LocalOnly)
1187
1188
1189class Mem(object):
1190 """For storing variables.
1191
1192 Callers:
1193 User code: assigning and evaluating variables, in command context or
1194 arithmetic context.
1195 Completion engine: for COMP_WORDS, etc.
1196 Builtins call it implicitly: read, cd for $PWD, $OLDPWD, etc.
1197
1198 Modules: cmd_eval, word_eval, expr_eval, completion
1199 """
1200
1201 def __init__(self, dollar0, argv, arena, debug_stack):
1202 # type: (str, List[str], alloc.Arena, List[debug_frame_t]) -> None
1203 """
1204 Args:
1205 arena: currently unused
1206 """
1207 # circular dep initialized out of line
1208 self.exec_opts = None # type: optview.Exec
1209 self.unsafe_arith = None # type: sh_expr_eval.UnsafeArith
1210
1211 self.dollar0 = dollar0
1212 # If you only use YSH procs and funcs, this will remain at length 1.
1213 self.argv_stack = [_ArgFrame(argv)]
1214
1215 frame = NewDict() # type: Dict[str, Cell]
1216
1217 frame['ARGV'] = _MakeArgvCell(argv)
1218
1219 self.var_stack = [frame]
1220
1221 # The debug_stack isn't strictly necessary for execution. We use it
1222 # for crash dumps and for 3 parallel arrays: BASH_SOURCE, FUNCNAME, and
1223 # BASH_LINENO.
1224 self.debug_stack = debug_stack
1225
1226 self.pwd = None # type: Optional[str]
1227 self.seconds_start = time_.time()
1228
1229 self.token_for_line = None # type: Optional[Token]
1230 self.loc_for_expr = loc.Missing # type: loc_t
1231
1232 self.last_arg = '' # $_ is initially empty, NOT unset
1233 self.line_num = value.Str('')
1234
1235 # Done ONCE on initialization
1236 self.root_pid = posix.getpid()
1237
1238 # TODO:
1239 # - These are REGISTERS mutated by user code.
1240 # - Call it self.reg_stack? with ctx_Registers
1241 # - push-registers builtin
1242 self.last_status = [0] # type: List[int] # a stack
1243 self.try_status = [0] # type: List[int] # a stack
1244 self.try_error = [value.Dict({})] # type: List[value.Dict] # a stack
1245 self.pipe_status = [[]] # type: List[List[int]] # stack
1246 self.process_sub_status = [[]] # type: List[List[int]] # stack
1247
1248 # A stack but NOT a register?
1249 self.this_dir = [] # type: List[str]
1250 self.regex_match = [regex_match.No] # type: List[regex_match_t]
1251
1252 self.last_bg_pid = -1 # Uninitialized value mutable public variable
1253
1254 self.running_debug_trap = False # set by ctx_DebugTrap()
1255 self.running_err_trap = False # set by ctx_ErrTrap
1256 self.is_main = True # we start out in main
1257
1258 # For the ctx builtin
1259 self.ctx_stack = [] # type: List[Dict[str, value_t]]
1260
1261 def __repr__(self):
1262 # type: () -> str
1263 parts = [] # type: List[str]
1264 parts.append('<Mem')
1265 for i, frame in enumerate(self.var_stack):
1266 parts.append(' -- %d --' % i)
1267 for n, v in frame.iteritems():
1268 parts.append(' %s %s' % (n, v))
1269 parts.append('>')
1270 return '\n'.join(parts) + '\n'
1271
1272 def SetPwd(self, pwd):
1273 # type: (str) -> None
1274 """Used by builtins."""
1275 self.pwd = pwd
1276
1277 def ParsingChangesAllowed(self):
1278 # type: () -> bool
1279 """For checking that syntax options are only used at the top level."""
1280
1281 # DISALLOW proc calls : they push argv_stack, var_stack, debug_stack
1282 # ALLOW source foo.sh arg1: pushes argv_stack, debug_stack
1283 # ALLOW FOO=bar : pushes var_stack
1284 return len(self.var_stack) == 1 or len(self.argv_stack) == 1
1285
1286 def Dump(self):
1287 # type: () -> Tuple[List[value_t], List[value_t], List[value_t]]
1288 """Copy state before unwinding the stack."""
1289 var_stack = [
1290 value.Dict(_DumpVarFrame(frame)) for frame in self.var_stack
1291 ] # type: List[value_t]
1292 argv_stack = [value.Dict(frame.Dump())
1293 for frame in self.argv_stack] # type: List[value_t]
1294
1295 debug_stack = [] # type: List[value_t]
1296
1297 # Reuse these immutable objects
1298 t_call = value.Str('Call')
1299 t_source = value.Str('Source')
1300 t_main = value.Str('Main')
1301
1302 for frame in reversed(self.debug_stack):
1303 UP_frame = frame
1304 with tagswitch(frame) as case:
1305 if case(debug_frame_e.Call):
1306 frame = cast(debug_frame.Call, UP_frame)
1307 d = {
1308 'type': t_call,
1309 'func_name': value.Str(frame.func_name)
1310 } # type: Dict[str, value_t]
1311
1312 _AddCallToken(d, frame.call_tok)
1313 # TODO: Add def_tok
1314
1315 elif case(debug_frame_e.Source):
1316 frame = cast(debug_frame.Source, UP_frame)
1317 d = {
1318 'type': t_source,
1319 'source_name': value.Str(frame.source_name)
1320 }
1321 _AddCallToken(d, frame.call_tok)
1322
1323 elif case(debug_frame_e.Main):
1324 frame = cast(debug_frame.Main, UP_frame)
1325 d = {'type': t_main, 'dollar0': value.Str(frame.dollar0)}
1326
1327 debug_stack.append(value.Dict(d))
1328 return var_stack, argv_stack, debug_stack
1329
1330 def SetLastArgument(self, s):
1331 # type: (str) -> None
1332 """For $_"""
1333 self.last_arg = s
1334
1335 def SetTokenForLine(self, tok):
1336 # type: (Token) -> None
1337 """Set a token to compute $LINENO
1338
1339 This means it should be set on SimpleCommand, ShAssignment, ((, [[,
1340 case, etc. -- anything that evaluates a word. Example: there was a bug
1341 with 'case $LINENO'
1342
1343 This token also used as a "least-specific" / fallback location for
1344 errors in ExecuteAndCatch().
1345
1346 Although most of that should be taken over by 'with ui.ctx_Location()`,
1347 for the errfmt.
1348 """
1349 if self.running_debug_trap or self.running_err_trap:
1350 return
1351
1352 #if tok.span_id == runtime.NO_SPID:
1353 # NOTE: This happened in the osh-runtime benchmark for yash.
1354 #log('Warning: span_id undefined in SetTokenForLine')
1355
1356 #import traceback
1357 #traceback.print_stack()
1358 #return
1359
1360 self.token_for_line = tok
1361
1362 def SetLocationForExpr(self, blame_loc):
1363 # type: (loc_t) -> None
1364 """
1365 A more specific fallback location, like the $[ in
1366
1367 echo $[len(42)]
1368 """
1369 self.loc_for_expr = blame_loc
1370
1371 def GetFallbackLocation(self):
1372 # type: () -> loc_t
1373
1374 if self.loc_for_expr != loc.Missing: # more specific
1375 return self.loc_for_expr
1376
1377 if self.token_for_line: # less specific
1378 return self.token_for_line
1379
1380 return loc.Missing
1381
1382 #
1383 # Status Variable Stack (for isolating $PS1 and $PS4)
1384 #
1385
1386 def LastStatus(self):
1387 # type: () -> int
1388 return self.last_status[-1]
1389
1390 def TryStatus(self):
1391 # type: () -> int
1392 return self.try_status[-1]
1393
1394 def TryError(self):
1395 # type: () -> value.Dict
1396 return self.try_error[-1]
1397
1398 def PipeStatus(self):
1399 # type: () -> List[int]
1400 return self.pipe_status[-1]
1401
1402 def SetLastStatus(self, x):
1403 # type: (int) -> None
1404 self.last_status[-1] = x
1405
1406 def SetTryStatus(self, x):
1407 # type: (int) -> None
1408 self.try_status[-1] = x
1409
1410 def SetTryError(self, x):
1411 # type: (value.Dict) -> None
1412 self.try_error[-1] = x
1413
1414 def SetPipeStatus(self, x):
1415 # type: (List[int]) -> None
1416 self.pipe_status[-1] = x
1417
1418 def SetSimplePipeStatus(self, status):
1419 # type: (int) -> None
1420
1421 # Optimization to avoid allocations
1422 top = self.pipe_status[-1]
1423 if len(top) == 1:
1424 top[0] = status
1425 else:
1426 self.pipe_status[-1] = [status]
1427
1428 def SetProcessSubStatus(self, x):
1429 # type: (List[int]) -> None
1430 self.process_sub_status[-1] = x
1431
1432 #
1433 # Call Stack
1434 #
1435
1436 def PushCall(self, func_name, def_tok):
1437 # type: (str, Token) -> None
1438 """Push argv, var, and debug stack frames.
1439
1440 Currently used for proc and func calls. TODO: New func evaluator may
1441 not use it.
1442
1443 Args:
1444 def_tok: Token where proc or func was defined, used to compute
1445 BASH_SOURCE.
1446 """
1447 # self.token_for_line can be None?
1448 self.debug_stack.append(
1449 debug_frame.Call(self.token_for_line, def_tok, func_name))
1450
1451 def PopCall(self):
1452 # type: () -> None
1453 """
1454 Args:
1455 should_pop_argv_stack: Pass False if PushCall was given None for argv
1456 True for proc, False for func
1457 """
1458 self.debug_stack.pop()
1459
1460 def ShouldRunDebugTrap(self):
1461 # type: () -> bool
1462
1463 # TODO: RunLastPart of pipeline can disable this
1464
1465 # Don't recursively run DEBUG trap
1466 if self.running_debug_trap:
1467 return False
1468
1469 # Don't run it inside functions
1470 if len(self.var_stack) > 1:
1471 return False
1472
1473 return True
1474
1475 def InsideFunction(self):
1476 # type: () -> bool
1477 """For the ERR trap"""
1478
1479 # Don't run it inside functions
1480 return len(self.var_stack) > 1
1481
1482 def PushSource(self, source_name, argv):
1483 # type: (str, List[str]) -> None
1484 """ For 'source foo.sh 1 2 3' """
1485 if len(argv):
1486 self.argv_stack.append(_ArgFrame(argv))
1487
1488 # self.token_for_line can be None?
1489 self.debug_stack.append(
1490 debug_frame.Source(self.token_for_line, source_name))
1491
1492 def PopSource(self, argv):
1493 # type: (List[str]) -> None
1494 self.debug_stack.pop()
1495
1496 if len(argv):
1497 self.argv_stack.pop()
1498
1499 def PushTemp(self):
1500 # type: () -> None
1501 """For the temporary scope in 'FOO=bar BAR=baz echo'.
1502
1503 Also for PS4 evaluation with more variables.
1504 """
1505 # We don't want the 'read' builtin to write to this frame!
1506 frame = NewDict() # type: Dict[str, Cell]
1507 self.var_stack.append(frame)
1508
1509 def PopTemp(self):
1510 # type: () -> None
1511 self.var_stack.pop()
1512
1513 def TopNamespace(self):
1514 # type: () -> Dict[str, Cell]
1515 """For eval_to_dict()."""
1516 return self.var_stack[-1]
1517
1518 #
1519 # Argv
1520 #
1521
1522 def Shift(self, n):
1523 # type: (int) -> int
1524 frame = self.argv_stack[-1]
1525 num_args = len(frame.argv)
1526
1527 if (frame.num_shifted + n) <= num_args:
1528 frame.num_shifted += n
1529 return 0 # success
1530 else:
1531 return 1 # silent error
1532
1533 def GetArg0(self):
1534 # type: () -> value.Str
1535 """Like GetArgNum(0) but with a more specific type."""
1536 return value.Str(self.dollar0)
1537
1538 def GetArgNum(self, arg_num):
1539 # type: (int) -> value_t
1540 if arg_num == 0:
1541 # $0 may be overriden, eg. by Str => replace()
1542 vars = self.var_stack[-1]
1543 if "0" in vars and vars["0"].val.tag() != value_e.Undef:
1544 return vars["0"].val
1545 return value.Str(self.dollar0)
1546
1547 return self.argv_stack[-1].GetArgNum(arg_num)
1548
1549 def GetArgv(self):
1550 # type: () -> List[str]
1551 """For $* and $@."""
1552 return self.argv_stack[-1].GetArgv()
1553
1554 def SetArgv(self, argv):
1555 # type: (List[str]) -> None
1556 """For set -- 1 2 3."""
1557 # from set -- 1 2 3
1558 self.argv_stack[-1].SetArgv(argv)
1559
1560 #
1561 # Special Vars
1562 #
1563
1564 def GetSpecialVar(self, op_id):
1565 # type: (int) -> value_t
1566 if op_id == Id.VSub_Bang: # $!
1567 n = self.last_bg_pid
1568 if n == -1:
1569 return value.Undef # could be an error
1570
1571 elif op_id == Id.VSub_QMark: # $?
1572 # External commands need WIFEXITED test. What about subshells?
1573 n = self.last_status[-1]
1574
1575 elif op_id == Id.VSub_Pound: # $#
1576 n = self.argv_stack[-1].GetNumArgs()
1577
1578 elif op_id == Id.VSub_Dollar: # $$
1579 n = self.root_pid
1580
1581 else:
1582 raise NotImplementedError(op_id)
1583
1584 return value.Str(str(n))
1585
1586 #
1587 # Named Vars
1588 #
1589
1590 def _ResolveNameOnly(self, name, which_scopes):
1591 # type: (str, scope_t) -> Tuple[Optional[Cell], Dict[str, Cell]]
1592 """Helper for getting and setting variable.
1593
1594 Returns:
1595 cell: The cell corresponding to looking up 'name' with the given mode, or
1596 None if it's not found.
1597 name_map: The name_map it should be set to or deleted from.
1598 """
1599 if which_scopes == scope_e.Dynamic:
1600 for i in xrange(len(self.var_stack) - 1, -1, -1):
1601 name_map = self.var_stack[i]
1602 if name in name_map:
1603 cell = name_map[name]
1604 return cell, name_map
1605 no_cell = None # type: Optional[Cell]
1606 return no_cell, self.var_stack[0] # set in global name_map
1607
1608 if which_scopes == scope_e.LocalOnly:
1609 name_map = self.var_stack[-1]
1610 return name_map.get(name), name_map
1611
1612 if which_scopes == scope_e.GlobalOnly:
1613 name_map = self.var_stack[0]
1614 return name_map.get(name), name_map
1615
1616 if which_scopes == scope_e.LocalOrGlobal:
1617 # Local
1618 name_map = self.var_stack[-1]
1619 cell = name_map.get(name)
1620 if cell:
1621 return cell, name_map
1622
1623 # Global
1624 name_map = self.var_stack[0]
1625 return name_map.get(name), name_map
1626
1627 raise AssertionError()
1628
1629 def _ResolveNameOrRef(
1630 self,
1631 name, # type: str
1632 which_scopes, # type: scope_t
1633 ref_trail=None, # type: Optional[List[str]]
1634 ):
1635 # type: (...) -> Tuple[Optional[Cell], Dict[str, Cell], str]
1636 """Look up a cell and namespace, but respect the nameref flag.
1637
1638 Resolving namerefs does RECURSIVE calls.
1639 """
1640 cell, name_map = self._ResolveNameOnly(name, which_scopes)
1641
1642 if cell is None or not cell.nameref:
1643 return cell, name_map, name # not a nameref
1644
1645 val = cell.val
1646 UP_val = val
1647 with tagswitch(val) as case:
1648 if case(value_e.Undef):
1649 # This is 'local -n undef_ref', which is kind of useless, because the
1650 # more common idiom is 'local -n ref=$1'. Note that you can mutate
1651 # references themselves with local -n ref=new.
1652 if self.exec_opts.strict_nameref():
1653 e_die('nameref %r is undefined' % name)
1654 else:
1655 return cell, name_map, name # fallback
1656
1657 elif case(value_e.Str):
1658 val = cast(value.Str, UP_val)
1659 new_name = val.s
1660
1661 else:
1662 # SetValue() protects the invariant that nameref is Undef or Str
1663 raise AssertionError(val.tag())
1664
1665 # TODO: Respect eval_unsafe_arith here (issue 881). See how it's done in
1666 # 'printf -v' with MakeArithParser
1667 if not match.IsValidVarName(new_name):
1668 # e.g. '#' or '1' or ''
1669 if self.exec_opts.strict_nameref():
1670 e_die('nameref %r contains invalid variable name %r' %
1671 (name, new_name))
1672 else:
1673 # Bash has this odd behavior of clearing the nameref bit when
1674 # ref=#invalid#. strict_nameref avoids it.
1675 cell.nameref = False
1676 return cell, name_map, name # fallback
1677
1678 # Check for circular namerefs.
1679 if ref_trail is None:
1680 ref_trail = [name]
1681 else:
1682 if new_name in ref_trail:
1683 e_die('Circular nameref %s' % ' -> '.join(ref_trail))
1684 ref_trail.append(new_name)
1685
1686 # 'declare -n' uses dynamic scope.
1687 cell, name_map, cell_name = self._ResolveNameOrRef(new_name,
1688 scope_e.Dynamic,
1689 ref_trail=ref_trail)
1690 return cell, name_map, cell_name
1691
1692 def IsBashAssoc(self, name):
1693 # type: (str) -> bool
1694 """Returns whether a name resolve to a cell with an associative array.
1695
1696 We need to know this to evaluate the index expression properly
1697 -- should it be coerced to an integer or not?
1698 """
1699 cell, _, _ = self._ResolveNameOrRef(name, self.ScopesForReading())
1700 # foo=([key]=value)
1701 return cell is not None and cell.val.tag() == value_e.BashAssoc
1702
1703 def SetPlace(self, place, val, blame_loc):
1704 # type: (value.Place, value_t, loc_t) -> None
1705
1706 yval = place.lval
1707 UP_yval = yval
1708 with tagswitch(yval) as case:
1709 if case(y_lvalue_e.Local):
1710 yval = cast(LeftName, UP_yval)
1711
1712 # Check that the frame is still alive
1713 found = False
1714 for i in xrange(len(self.var_stack) - 1, -1, -1):
1715 frame = self.var_stack[i]
1716 if frame is place.frame:
1717 found = True
1718 #log('FOUND %s', found)
1719 break
1720 if not found:
1721 e_die(
1722 "Can't assign to place that's no longer on the call stack.",
1723 blame_loc)
1724
1725 cell = frame.get(yval.name)
1726 if cell is None:
1727 cell = Cell(False, False, False, val)
1728 frame[yval.name] = cell
1729 else:
1730 cell.val = val
1731
1732 elif case(y_lvalue_e.Container):
1733 e_die('Container place not implemented', blame_loc)
1734
1735 else:
1736 raise AssertionError()
1737
1738 def SetLocalName(self, lval, val):
1739 # type: (LeftName, value_t) -> None
1740
1741 # Equivalent to
1742 # self._ResolveNameOnly(lval.name, scope_e.LocalOnly)
1743 name_map = self.var_stack[-1]
1744 cell = name_map.get(lval.name)
1745
1746 if cell:
1747 if cell.readonly:
1748 e_die("Can't assign to readonly value %r" % lval.name,
1749 lval.blame_loc)
1750 cell.val = val # Mutate value_t
1751 else:
1752 cell = Cell(False, False, False, val)
1753 name_map[lval.name] = cell
1754
1755 def SetNamed(self, lval, val, which_scopes, flags=0):
1756 # type: (LeftName, value_t, scope_t, int) -> None
1757
1758 if flags & SetNameref or flags & ClearNameref:
1759 # declare -n ref=x # refers to the ref itself
1760 cell, name_map = self._ResolveNameOnly(lval.name, which_scopes)
1761 cell_name = lval.name
1762 else:
1763 # ref=x # mutates THROUGH the reference
1764
1765 # Note on how to implement declare -n ref='a[42]'
1766 # 1. Call _ResolveNameOnly()
1767 # 2. If cell.nameref, call self.unsafe_arith.ParseVarRef() ->
1768 # BracedVarSub
1769 # 3. Turn BracedVarSub into an sh_lvalue, and call
1770 # self.unsafe_arith.SetValue() wrapper with ref_trail
1771 cell, name_map, cell_name = self._ResolveNameOrRef(
1772 lval.name, which_scopes)
1773
1774 if cell:
1775 # Clear before checking readonly bit.
1776 # NOTE: Could be cell.flags &= flag_clear_mask
1777 if flags & ClearExport:
1778 cell.exported = False
1779 if flags & ClearReadOnly:
1780 cell.readonly = False
1781 if flags & ClearNameref:
1782 cell.nameref = False
1783
1784 if val is not None: # e.g. declare -rx existing
1785 # Note: this DYNAMIC check means we can't have 'const' in a loop.
1786 # But that's true for 'readonly' too, and hoisting it makes more
1787 # sense anyway.
1788 if cell.readonly:
1789 e_die("Can't assign to readonly value %r" % lval.name,
1790 lval.blame_loc)
1791 cell.val = val # CHANGE VAL
1792
1793 # NOTE: Could be cell.flags |= flag_set_mask
1794 if flags & SetExport:
1795 cell.exported = True
1796 if flags & SetReadOnly:
1797 cell.readonly = True
1798 if flags & SetNameref:
1799 cell.nameref = True
1800
1801 else:
1802 if val is None: # declare -rx nonexistent
1803 # set -o nounset; local foo; echo $foo # It's still undefined!
1804 val = value.Undef # export foo, readonly foo
1805
1806 cell = Cell(bool(flags & SetExport), bool(flags & SetReadOnly),
1807 bool(flags & SetNameref), val)
1808 name_map[cell_name] = cell
1809
1810 # Maintain invariant that only strings and undefined cells can be
1811 # exported.
1812 assert cell.val is not None, cell
1813
1814 if cell.val.tag() not in (value_e.Undef, value_e.Str):
1815 if cell.exported:
1816 if self.exec_opts.strict_array():
1817 e_die("Only strings can be exported (strict_array)",
1818 lval.blame_loc)
1819 if cell.nameref:
1820 e_die("nameref must be a string", lval.blame_loc)
1821
1822 def SetValue(self, lval, val, which_scopes, flags=0):
1823 # type: (sh_lvalue_t, value_t, scope_t, int) -> None
1824 """
1825 Args:
1826 lval: sh_lvalue
1827 val: value, or None if only changing flags
1828 which_scopes:
1829 Local | Global | Dynamic - for builtins, PWD, etc.
1830 flags: packed pair (keyword_id, bit mask of set/clear flags)
1831
1832 Note: in bash, PWD=/ changes the directory. But not in dash.
1833 """
1834 # STRICTNESS / SANENESS:
1835 #
1836 # 1) Don't create arrays automatically, e.g. a[1000]=x
1837 # 2) Never change types? yeah I think that's a good idea, at least for oil
1838 # (not sh, for compatibility). set -o strict_types or something. That
1839 # means arrays have to be initialized with let arr = [], which is fine.
1840 # This helps with stuff like IFS. It starts off as a string, and assigning
1841 # it to a list is an error. I guess you will have to turn this no for
1842 # bash?
1843 #
1844 # TODO:
1845 # - COMPUTED vars can't be set
1846 # - What about PWD / OLDPWD / UID / EUID ? You can simply make them
1847 # readonly.
1848 # - Maybe PARSE $PS1 and $PS4 when they're set, to avoid the error on use?
1849 # - Other validity: $HOME could be checked for existence
1850
1851 UP_lval = lval
1852 with tagswitch(lval) as case:
1853 if case(sh_lvalue_e.Var):
1854 lval = cast(LeftName, UP_lval)
1855
1856 self.SetNamed(lval, val, which_scopes, flags=flags)
1857
1858 elif case(sh_lvalue_e.Indexed):
1859 lval = cast(sh_lvalue.Indexed, UP_lval)
1860
1861 # There is no syntax 'declare a[x]'
1862 assert val is not None, val
1863
1864 # TODO: relax this for Oil
1865 assert val.tag() == value_e.Str, val
1866 rval = cast(value.Str, val)
1867
1868 # Note: location could be a[x]=1 or (( a[ x ] = 1 ))
1869 left_loc = lval.blame_loc
1870
1871 # bash/mksh have annoying behavior of letting you do LHS assignment to
1872 # Undef, which then turns into an INDEXED array. (Undef means that set
1873 # -o nounset fails.)
1874 cell, name_map, _ = self._ResolveNameOrRef(
1875 lval.name, which_scopes)
1876 if not cell:
1877 self._BindNewArrayWithEntry(name_map, lval, rval, flags)
1878 return
1879
1880 if cell.readonly:
1881 e_die("Can't assign to readonly array", left_loc)
1882
1883 UP_cell_val = cell.val
1884 # undef[0]=y is allowed
1885 with tagswitch(UP_cell_val) as case2:
1886 if case2(value_e.Undef):
1887 self._BindNewArrayWithEntry(name_map, lval, rval,
1888 flags)
1889 return
1890
1891 elif case2(value_e.Str):
1892 # s=x
1893 # s[1]=y # invalid
1894 e_die("Can't assign to items in a string", left_loc)
1895
1896 elif case2(value_e.BashArray):
1897 cell_val = cast(value.BashArray, UP_cell_val)
1898 strs = cell_val.strs
1899
1900 n = len(strs)
1901 index = lval.index
1902 if index < 0: # a[-1]++ computes this twice; could we avoid it?
1903 index += n
1904
1905 if 0 <= index and index < n:
1906 strs[index] = rval.s
1907 else:
1908 # Fill it in with None. It could look like this:
1909 # ['1', 2, 3, None, None, '4', None]
1910 # Then ${#a[@]} counts the entries that are not None.
1911 #
1912 # TODO: strict_array for Oil arrays won't auto-fill.
1913 n = index - len(strs) + 1
1914 for i in xrange(n):
1915 strs.append(None)
1916 strs[lval.index] = rval.s
1917 return
1918
1919 # This could be an object, eggex object, etc. It won't be
1920 # BashAssoc shouldn because we query IsBashAssoc before evaluating
1921 # sh_lhs. Could conslidate with s[i] case above
1922 e_die(
1923 "Value of type %s can't be indexed" % ui.ValType(cell.val),
1924 left_loc)
1925
1926 elif case(sh_lvalue_e.Keyed):
1927 lval = cast(sh_lvalue.Keyed, UP_lval)
1928
1929 # There is no syntax 'declare A["x"]'
1930 assert val is not None, val
1931 assert val.tag() == value_e.Str, val
1932 rval = cast(value.Str, val)
1933
1934 left_loc = lval.blame_loc
1935
1936 cell, name_map, _ = self._ResolveNameOrRef(
1937 lval.name, which_scopes)
1938 if cell.readonly:
1939 e_die("Can't assign to readonly associative array",
1940 left_loc)
1941
1942 # We already looked it up before making the sh_lvalue
1943 assert cell.val.tag() == value_e.BashAssoc, cell
1944 cell_val2 = cast(value.BashAssoc, cell.val)
1945
1946 cell_val2.d[lval.key] = rval.s
1947
1948 else:
1949 raise AssertionError(lval.tag())
1950
1951 def _BindNewArrayWithEntry(self, name_map, lval, val, flags):
1952 # type: (Dict[str, Cell], sh_lvalue.Indexed, value.Str, int) -> None
1953 """Fill 'name_map' with a new indexed array entry."""
1954 no_str = None # type: Optional[str]
1955 items = [no_str] * lval.index
1956 items.append(val.s)
1957 new_value = value.BashArray(items)
1958
1959 # arrays can't be exported; can't have BashAssoc flag
1960 readonly = bool(flags & SetReadOnly)
1961 name_map[lval.name] = Cell(False, readonly, False, new_value)
1962
1963 def InternalSetGlobal(self, name, new_val):
1964 # type: (str, value_t) -> None
1965 """For setting read-only globals internally.
1966
1967 Args:
1968 name: string (not Lhs)
1969 new_val: value
1970
1971 The variable must already exist.
1972
1973 Use case: SHELLOPTS.
1974 """
1975 cell = self.var_stack[0][name]
1976 cell.val = new_val
1977
1978 def GetValue(self, name, which_scopes=scope_e.Shopt):
1979 # type: (str, scope_t) -> value_t
1980 """Used by the WordEvaluator, ArithEvaluator, ExprEvaluator, etc."""
1981 assert isinstance(name, str), name
1982
1983 if which_scopes == scope_e.Shopt:
1984 which_scopes = self.ScopesForReading()
1985
1986 with str_switch(name) as case:
1987 # "Registers"
1988 if case('_status'):
1989 return num.ToBig(self.TryStatus())
1990
1991 elif case('_error'):
1992 return self.TryError()
1993
1994 elif case('_this_dir'):
1995 if len(self.this_dir) == 0:
1996 # e.g. osh -c '' doesn't have it set
1997 # Should we give a custom error here?
1998 # If you're at the interactive shell, 'source mymodule.oil' will still
1999 # work because 'source' sets it.
2000 return value.Undef
2001 else:
2002 return value.Str(self.this_dir[-1]) # top of stack
2003
2004 elif case('PIPESTATUS'):
2005 strs2 = [str(i)
2006 for i in self.pipe_status[-1]] # type: List[str]
2007 return value.BashArray(strs2)
2008
2009 elif case('_pipeline_status'):
2010 items = [num.ToBig(i)
2011 for i in self.pipe_status[-1]] # type: List[value_t]
2012 return value.List(items)
2013
2014 elif case('_process_sub_status'): # YSH naming convention
2015 items = [num.ToBig(i) for i in self.process_sub_status[-1]]
2016 return value.List(items)
2017
2018 elif case('BASH_REMATCH'):
2019 top_match = self.regex_match[-1]
2020 with tagswitch(top_match) as case2:
2021 if case2(regex_match_e.No):
2022 groups = [] # type: List[str]
2023 elif case2(regex_match_e.Yes):
2024 m = cast(RegexMatch, top_match)
2025 groups = util.RegexGroupStrings(m.s, m.indices)
2026 return value.BashArray(groups)
2027
2028 # Do lookup of system globals before looking at user variables. Note: we
2029 # could optimize this at compile-time like $?. That would break
2030 # ${!varref}, but it's already broken for $?.
2031
2032 elif case('FUNCNAME'):
2033 # bash wants it in reverse order. This is a little inefficient but we're
2034 # not depending on deque().
2035 strs = [] # type: List[str]
2036 for frame in reversed(self.debug_stack):
2037 UP_frame = frame
2038 with tagswitch(frame) as case2:
2039 if case2(debug_frame_e.Call):
2040 frame = cast(debug_frame.Call, UP_frame)
2041 strs.append(frame.func_name)
2042
2043 elif case2(debug_frame_e.Source):
2044 # bash doesn't tell you the filename sourced
2045 strs.append('source')
2046
2047 elif case2(debug_frame_e.Main):
2048 strs.append('main') # also bash behavior
2049
2050 return value.BashArray(strs) # TODO: Reuse this object too?
2051
2052 # $BASH_SOURCE and $BASH_LINENO have OFF BY ONE design bugs:
2053 #
2054 # ${BASH_LINENO[$i]} is the line number in the source file
2055 # (${BASH_SOURCE[$i+1]}) where ${FUNCNAME[$i]} was called (or
2056 # ${BASH_LINENO[$i-1]} if referenced within another shell function).
2057 #
2058 # https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
2059
2060 elif case('BASH_SOURCE'):
2061 strs = []
2062 for frame in reversed(self.debug_stack):
2063 UP_frame = frame
2064 with tagswitch(frame) as case2:
2065 if case2(debug_frame_e.Call):
2066 frame = cast(debug_frame.Call, UP_frame)
2067
2068 # Weird bash behavior
2069 assert frame.def_tok.line is not None
2070 source_str = ui.GetLineSourceString(
2071 frame.def_tok.line)
2072 strs.append(source_str)
2073
2074 elif case2(debug_frame_e.Source):
2075 frame = cast(debug_frame.Source, UP_frame)
2076 # Is this right?
2077 strs.append(frame.source_name)
2078
2079 elif case2(debug_frame_e.Main):
2080 frame = cast(debug_frame.Main, UP_frame)
2081 strs.append(frame.dollar0)
2082
2083 return value.BashArray(strs) # TODO: Reuse this object too?
2084
2085 elif case('BASH_LINENO'):
2086 strs = []
2087 for frame in reversed(self.debug_stack):
2088 UP_frame = frame
2089 with tagswitch(frame) as case2:
2090 if case2(debug_frame_e.Call):
2091 frame = cast(debug_frame.Call, UP_frame)
2092 strs.append(_LineNumber(frame.call_tok))
2093
2094 elif case2(debug_frame_e.Source):
2095 frame = cast(debug_frame.Source, UP_frame)
2096 strs.append(_LineNumber(frame.call_tok))
2097
2098 elif case2(debug_frame_e.Main):
2099 # Bash does this to line up with 'main'
2100 strs.append('0')
2101
2102 return value.BashArray(strs) # TODO: Reuse this object too?
2103
2104 elif case('LINENO'):
2105 assert self.token_for_line is not None
2106 # Reuse object with mutation
2107 # TODO: maybe use interned GetLineNumStr?
2108 self.line_num.s = str(self.token_for_line.line.line_num)
2109 return self.line_num
2110
2111 elif case('BASHPID'): # TODO: YSH io->getpid()
2112 return value.Str(str(posix.getpid()))
2113
2114 elif case('_'):
2115 return value.Str(self.last_arg)
2116
2117 elif case('SECONDS'):
2118 f = time_.time() - self.seconds_start
2119 ok, big_int = mops.FromFloat(f)
2120 assert ok, f # should never be NAN or INFINITY
2121 return value.Int(big_int)
2122
2123 else:
2124 # In the case 'declare -n ref='a[42]', the result won't be a cell. Idea to
2125 # fix this:
2126 # 1. Call self.unsafe_arith.ParseVarRef() -> BracedVarSub
2127 # 2. Call self.unsafe_arith.GetNameref(bvs_part), and get a value_t
2128 # We still need a ref_trail to detect cycles.
2129 cell, _, _ = self._ResolveNameOrRef(name, which_scopes)
2130 if cell:
2131 return cell.val
2132
2133 return value.Undef
2134
2135 def GetCell(self, name, which_scopes=scope_e.Shopt):
2136 # type: (str, scope_t) -> Cell
2137 """Get both the value and flags.
2138
2139 Usages:
2140 - the 'pp' builtin.
2141 - declare -p
2142 - ${x@a}
2143 - to test of 'TZ' is exported in printf? Why?
2144 """
2145 if which_scopes == scope_e.Shopt:
2146 which_scopes = self.ScopesForReading()
2147
2148 cell, _ = self._ResolveNameOnly(name, which_scopes)
2149 return cell
2150
2151 def Unset(self, lval, which_scopes):
2152 # type: (sh_lvalue_t, scope_t) -> bool
2153 """
2154 Returns:
2155 Whether the cell was found.
2156 """
2157 # TODO: Refactor sh_lvalue type to avoid this
2158 UP_lval = lval
2159
2160 with tagswitch(lval) as case:
2161 if case(sh_lvalue_e.Var): # unset x
2162 lval = cast(LeftName, UP_lval)
2163 var_name = lval.name
2164 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2165 lval = cast(sh_lvalue.Indexed, UP_lval)
2166 var_name = lval.name
2167 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2168 lval = cast(sh_lvalue.Keyed, UP_lval)
2169 var_name = lval.name
2170 else:
2171 raise AssertionError()
2172
2173 if which_scopes == scope_e.Shopt:
2174 which_scopes = self.ScopesForWriting()
2175
2176 cell, name_map, cell_name = self._ResolveNameOrRef(
2177 var_name, which_scopes)
2178 if not cell:
2179 return False # 'unset' builtin falls back on functions
2180 if cell.readonly:
2181 raise error.Runtime("Can't unset readonly variable %r" % var_name)
2182
2183 with tagswitch(lval) as case:
2184 if case(sh_lvalue_e.Var): # unset x
2185 # Make variables in higher scopes visible.
2186 # example: test/spec.sh builtin-vars -r 24 (ble.sh)
2187 mylib.dict_erase(name_map, cell_name)
2188
2189 # alternative that some shells use:
2190 # name_map[cell_name].val = value.Undef
2191 # cell.exported = False
2192
2193 # This should never happen because we do recursive lookups of namerefs.
2194 assert not cell.nameref, cell
2195
2196 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2197 lval = cast(sh_lvalue.Indexed, UP_lval)
2198 # Note: Setting an entry to None and shifting entries are pretty
2199 # much the same in shell.
2200
2201 val = cell.val
2202 UP_val = val
2203 if val.tag() != value_e.BashArray:
2204 raise error.Runtime("%r isn't an array" % var_name)
2205
2206 val = cast(value.BashArray, UP_val)
2207 strs = val.strs
2208
2209 n = len(strs)
2210 last_index = n - 1
2211 index = lval.index
2212 if index < 0:
2213 index += n
2214
2215 if index == last_index:
2216 # Special case: The array SHORTENS if you unset from the end. You
2217 # can tell with a+=(3 4)
2218 strs.pop()
2219 elif 0 <= index and index < last_index:
2220 strs[index] = None
2221 else:
2222 # If it's not found, it's not an error. In other words, 'unset'
2223 # ensures that a value doesn't exist, regardless of whether it
2224 # existed. It's idempotent.
2225 # (Ousterhout specifically argues that the strict behavior was a
2226 # mistake for Tcl!)
2227 pass
2228
2229 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2230 lval = cast(sh_lvalue.Keyed, UP_lval)
2231
2232 val = cell.val
2233 UP_val = val
2234
2235 # note: never happens because of mem.IsBashAssoc test for sh_lvalue.Keyed
2236 #if val.tag() != value_e.BashAssoc:
2237 # raise error.Runtime("%r isn't an associative array" % lval.name)
2238
2239 val = cast(value.BashAssoc, UP_val)
2240 mylib.dict_erase(val.d, lval.key)
2241
2242 else:
2243 raise AssertionError(lval)
2244
2245 return True
2246
2247 def ScopesForReading(self):
2248 # type: () -> scope_t
2249 """Read scope."""
2250 return (scope_e.Dynamic
2251 if self.exec_opts.dynamic_scope() else scope_e.LocalOrGlobal)
2252
2253 def ScopesForWriting(self):
2254 # type: () -> scope_t
2255 """Write scope."""
2256 return (scope_e.Dynamic
2257 if self.exec_opts.dynamic_scope() else scope_e.LocalOnly)
2258
2259 def ClearFlag(self, name, flag):
2260 # type: (str, int) -> bool
2261 """Used for export -n.
2262
2263 We don't use SetValue() because even if rval is None, it will make an
2264 Undef value in a scope.
2265 """
2266 cell, name_map = self._ResolveNameOnly(name, self.ScopesForReading())
2267 if cell:
2268 if flag & ClearExport:
2269 cell.exported = False
2270 if flag & ClearNameref:
2271 cell.nameref = False
2272 return True
2273 else:
2274 return False
2275
2276 def GetExported(self):
2277 # type: () -> Dict[str, str]
2278 """Get all the variables that are marked exported."""
2279 # TODO: This is run on every SimpleCommand. Should we have a dirty flag?
2280 # We have to notice these things:
2281 # - If an exported variable is changed.
2282 # - If the set of exported variables changes.
2283
2284 exported = {} # type: Dict[str, str]
2285 # Search from globals up. Names higher on the stack will overwrite names
2286 # lower on the stack.
2287 for scope in self.var_stack:
2288 for name, cell in iteritems(scope):
2289 # TODO: Disallow exporting at assignment time. If an exported Str is
2290 # changed to BashArray, also clear its 'exported' flag.
2291 if cell.exported and cell.val.tag() == value_e.Str:
2292 val = cast(value.Str, cell.val)
2293 exported[name] = val.s
2294 return exported
2295
2296 def VarNames(self):
2297 # type: () -> List[str]
2298 """For internal OSH completion and compgen -A variable.
2299
2300 NOTE: We could also add $? $$ etc.?
2301 """
2302 ret = [] # type: List[str]
2303 # Look up the stack, yielding all variables. Bash seems to do this.
2304 for scope in self.var_stack:
2305 for name in scope:
2306 ret.append(name)
2307 return ret
2308
2309 def VarNamesStartingWith(self, prefix):
2310 # type: (str) -> List[str]
2311 """For ${!prefix@}"""
2312 # Look up the stack, yielding all variables. Bash seems to do this.
2313 names = [] # type: List[str]
2314 for scope in self.var_stack:
2315 for name in scope:
2316 if name.startswith(prefix):
2317 names.append(name)
2318 return names
2319
2320 def GetAllVars(self):
2321 # type: () -> Dict[str, str]
2322 """Get all variables and their values, for 'set' builtin."""
2323 result = {} # type: Dict[str, str]
2324 for scope in self.var_stack:
2325 for name, cell in iteritems(scope):
2326 # TODO: Show other types?
2327 val = cell.val
2328 if val.tag() == value_e.Str:
2329 str_val = cast(value.Str, val)
2330 result[name] = str_val.s
2331 return result
2332
2333 def GetAllCells(self, which_scopes):
2334 # type: (scope_t) -> Dict[str, Cell]
2335 """Get all variables and their values, for 'set' builtin."""
2336 result = {} # type: Dict[str, Cell]
2337
2338 if which_scopes == scope_e.Dynamic:
2339 scopes = self.var_stack
2340 elif which_scopes == scope_e.LocalOnly:
2341 scopes = self.var_stack[-1:]
2342 elif which_scopes == scope_e.GlobalOnly:
2343 scopes = self.var_stack[0:1]
2344 elif which_scopes == scope_e.LocalOrGlobal:
2345 scopes = [self.var_stack[0]]
2346 if len(self.var_stack) > 1:
2347 scopes.append(self.var_stack[-1])
2348 else:
2349 raise AssertionError()
2350
2351 for scope in scopes:
2352 for name, cell in iteritems(scope):
2353 result[name] = cell
2354 return result
2355
2356 def IsGlobalScope(self):
2357 # type: () -> bool
2358 return len(self.var_stack) == 1
2359
2360 def SetRegexMatch(self, match):
2361 # type: (regex_match_t) -> None
2362 self.regex_match[-1] = match
2363
2364 def GetRegexMatch(self):
2365 # type: () -> regex_match_t
2366 return self.regex_match[-1]
2367
2368 def PushContextStack(self, context):
2369 # type: (Dict[str, value_t]) -> None
2370 self.ctx_stack.append(context)
2371
2372 def GetContext(self):
2373 # type: () -> Optional[Dict[str, value_t]]
2374 if len(self.ctx_stack):
2375 return self.ctx_stack[-1]
2376 return None
2377
2378 def PopContextStack(self):
2379 # type: () -> Dict[str, value_t]
2380 assert self.ctx_stack, "Empty context stack"
2381 return self.ctx_stack.pop()
2382
2383
2384class Procs:
2385
2386 def __init__(self, mem):
2387 # type: (Mem) -> None
2388 self.mem = mem
2389 self.sh_funcs = {} # type: Dict[str, value.Proc]
2390
2391 def SetProc(self, name, proc):
2392 # type: (str, value.Proc) -> None
2393 self.mem.var_stack[0][name] = Cell(False, False, False, proc)
2394
2395 def SetShFunc(self, name, proc):
2396 # type: (str, value.Proc) -> None
2397 self.sh_funcs[name] = proc
2398
2399 def Get(self, name):
2400 # type: (str) -> value.Proc
2401 """Try to find a proc/sh-func by `name`, or return None if not found.
2402
2403 First, we search for a proc, and then a sh-func. This means that procs
2404 can shadow the definition of sh-funcs.
2405 """
2406 maybe_proc = self.mem.GetValue(name)
2407 if maybe_proc.tag() == value_e.Proc:
2408 return cast(value.Proc, maybe_proc)
2409
2410 if name in self.sh_funcs:
2411 return self.sh_funcs[name]
2412
2413 return None
2414
2415 def Del(self, to_del):
2416 # type: (str) -> None
2417 """Undefine a sh-func with name `to_del`, if it exists."""
2418 mylib.dict_erase(self.sh_funcs, to_del)
2419
2420 def GetNames(self):
2421 # type: () -> List[str]
2422 """Returns a *sorted* list of all proc names"""
2423 names = list(self.sh_funcs.keys())
2424
2425 vars = self.mem.var_stack[0]
2426 for name in vars:
2427 cell = vars[name]
2428 if cell.val.tag() == value_e.Proc:
2429 names.append(name)
2430
2431 return sorted(names)
2432
2433
2434#
2435# Wrappers to Set Variables
2436#
2437
2438
2439def OshLanguageSetValue(mem, lval, val, flags=0):
2440 # type: (Mem, sh_lvalue_t, value_t, int) -> None
2441 """Like 'setvar' (scope_e.LocalOnly), unless dynamic scope is on.
2442
2443 That is, it respects shopt --unset dynamic_scope.
2444
2445 Used for assignment builtins, (( a = b )), {fd}>out, ${x=}, etc.
2446 """
2447 which_scopes = mem.ScopesForWriting()
2448 mem.SetValue(lval, val, which_scopes, flags=flags)
2449
2450
2451def BuiltinSetValue(mem, lval, val):
2452 # type: (Mem, sh_lvalue_t, value_t) -> None
2453 """Equivalent of x=$y
2454
2455 Called by BuiltinSetString and BuiltinSetArray Used directly by
2456 printf -v because it can mutate an array
2457 """
2458 mem.SetValue(lval, val, mem.ScopesForWriting())
2459
2460
2461def BuiltinSetString(mem, name, s):
2462 # type: (Mem, str, str) -> None
2463 """Set a string by looking up the stack.
2464
2465 Used for 'read', 'getopts', completion builtins, etc.
2466 """
2467 assert isinstance(s, str)
2468 BuiltinSetValue(mem, location.LName(name), value.Str(s))
2469
2470
2471def BuiltinSetArray(mem, name, a):
2472 # type: (Mem, str, List[str]) -> None
2473 """Set an array by looking up the stack.
2474
2475 Used by compadjust, read -a, etc.
2476 """
2477 assert isinstance(a, list)
2478 BuiltinSetValue(mem, location.LName(name), value.BashArray(a))
2479
2480
2481def SetGlobalString(mem, name, s):
2482 # type: (Mem, str, str) -> None
2483 """Helper for completion, etc."""
2484 assert isinstance(s, str)
2485 val = value.Str(s)
2486 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2487
2488
2489def SetGlobalArray(mem, name, a):
2490 # type: (Mem, str, List[str]) -> None
2491 """Used by completion, shell initialization, etc."""
2492 assert isinstance(a, list)
2493 mem.SetNamed(location.LName(name), value.BashArray(a), scope_e.GlobalOnly)
2494
2495
2496def _SetGlobalValue(mem, name, val):
2497 # type: (Mem, str, value_t) -> None
2498 """Helper for completion, etc."""
2499 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2500
2501
2502def ExportGlobalString(mem, name, s):
2503 # type: (Mem, str, str) -> None
2504 """Helper for completion, $PWD, $OLDPWD, etc."""
2505 assert isinstance(s, str)
2506 val = value.Str(s)
2507 mem.SetNamed(location.LName(name),
2508 val,
2509 scope_e.GlobalOnly,
2510 flags=SetExport)
2511
2512
2513#
2514# Wrappers to Get Variables
2515#
2516
2517
2518def DynamicGetVar(mem, name, which_scopes):
2519 # type: (Mem, str, scope_t) -> value_t
2520 """
2521 For getVar() and shvarGet()
2522 """
2523 val = mem.GetValue(name, which_scopes=which_scopes)
2524
2525 # Undef is not a user-visible value!
2526 # There's no way to distinguish null from undefined.
2527 if val.tag() == value_e.Undef:
2528 return value.Null
2529
2530 return val
2531
2532
2533def GetString(mem, name):
2534 # type: (Mem, str) -> str
2535 """Wrapper around GetValue(). Check that HOME, PWD, OLDPWD, etc. are
2536 strings. bash doesn't have these errors because ${array} is ${array[0]}.
2537
2538 TODO: We could also check this when you're storing variables?
2539 """
2540 val = mem.GetValue(name)
2541 UP_val = val
2542 with tagswitch(val) as case:
2543 if case(value_e.Undef):
2544 raise error.Runtime("$%s isn't defined" % name)
2545 elif case(value_e.Str):
2546 return cast(value.Str, UP_val).s
2547 else:
2548 # User would have to 'unset HOME' to get rid of exported flag
2549 raise error.Runtime("$%s should be a string" % name)
2550
2551
2552def MaybeString(mem, name):
2553 # type: (Mem, str) -> Optional[str]
2554 """Like GetString(), but doesn't throw an exception."""
2555 try:
2556 return GetString(mem, name)
2557 except error.Runtime:
2558 return None
2559
2560
2561def GetInteger(mem, name):
2562 # type: (Mem, str) -> int
2563 """For OPTIND variable used in getopts builtin.
2564
2565 TODO: it could be value.Int() ?
2566 """
2567 val = mem.GetValue(name)
2568 if val.tag() != value_e.Str:
2569 raise error.Runtime('$%s should be a string, got %s' %
2570 (name, ui.ValType(val)))
2571 s = cast(value.Str, val).s
2572 try:
2573 i = int(s)
2574 except ValueError:
2575 raise error.Runtime("$%s doesn't look like an integer, got %r" %
2576 (name, s))
2577 return i
2578
2579
2580# vim: sw=4