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

2573 lines, 1308 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 _InitVarsFromEnv(mem, environ)
957
958 # MUTABLE GLOBAL that's SEPARATE from $PWD. Used by the 'pwd' builtin, but
959 # it can't be modified by users.
960 val = mem.GetValue('PWD')
961 # should be true since it's exported
962 assert val.tag() == value_e.Str, val
963 pwd = cast(value.Str, val).s
964 mem.SetPwd(pwd)
965
966
967def InitInteractive(mem):
968 # type: (Mem) -> None
969 """Initialization that's only done in the interactive/headless shell."""
970
971 # Same default PS1 as bash
972 if mem.GetValue('PS1').tag() == value_e.Undef:
973 SetGlobalString(mem, 'PS1', r'\s-\v\$ ')
974
975
976class ctx_FuncCall(object):
977 """For func calls."""
978
979 def __init__(self, mem, func):
980 # type: (Mem, value.Func) -> None
981
982 frame = NewDict() # type: Dict[str, Cell]
983 mem.var_stack.append(frame)
984
985 mem.PushCall(func.name, func.parsed.name)
986 self.mem = mem
987
988 def __enter__(self):
989 # type: () -> None
990 pass
991
992 def __exit__(self, type, value, traceback):
993 # type: (Any, Any, Any) -> None
994 self.mem.PopCall()
995 self.mem.var_stack.pop()
996
997
998class ctx_ProcCall(object):
999 """For proc calls, including shell functions."""
1000
1001 def __init__(self, mem, mutable_opts, proc, argv):
1002 # type: (Mem, MutableOpts, value.Proc, List[str]) -> None
1003
1004 # TODO:
1005 # - argv stack shouldn't be used for procs
1006 # - we can bind a real variable @A if we want
1007 # - procs should be in the var namespace
1008 #
1009 # should we separate procs and shell functions?
1010 # - dynamic scope is one difference
1011 # - '$@" shift etc. are another difference
1012
1013 frame = NewDict() # type: Dict[str, Cell]
1014
1015 assert argv is not None
1016 if proc.sh_compat:
1017 # shell function
1018 mem.argv_stack.append(_ArgFrame(argv))
1019 else:
1020 # procs
1021 # - open: is equivalent to ...ARGV
1022 # - closed: ARGV is empty list
1023 frame['ARGV'] = _MakeArgvCell(argv)
1024
1025 mem.var_stack.append(frame)
1026
1027 mem.PushCall(proc.name, proc.name_tok)
1028
1029 # Dynamic scope is only for shell functions
1030 mutable_opts.PushDynamicScope(proc.sh_compat)
1031
1032 # It may have been disabled with ctx_ErrExit for 'if echo $(false)', but
1033 # 'if p' should be allowed.
1034 self.mem = mem
1035 self.mutable_opts = mutable_opts
1036 self.sh_compat = proc.sh_compat
1037
1038 def __enter__(self):
1039 # type: () -> None
1040 pass
1041
1042 def __exit__(self, type, value, traceback):
1043 # type: (Any, Any, Any) -> None
1044 self.mutable_opts.PopDynamicScope()
1045 self.mem.PopCall()
1046 self.mem.var_stack.pop()
1047
1048 if self.sh_compat:
1049 self.mem.argv_stack.pop()
1050
1051
1052class ctx_Temp(object):
1053 """For FOO=bar myfunc, etc."""
1054
1055 def __init__(self, mem):
1056 # type: (Mem) -> None
1057 mem.PushTemp()
1058 self.mem = mem
1059
1060 def __enter__(self):
1061 # type: () -> None
1062 pass
1063
1064 def __exit__(self, type, value, traceback):
1065 # type: (Any, Any, Any) -> None
1066 self.mem.PopTemp()
1067
1068
1069class ctx_Registers(object):
1070 """For $PS1, $PS4, $PROMPT_COMMAND, traps, and headless EVAL.
1071
1072 This is tightly coupled to state.Mem, so it's not in builtin/pure_ysh.
1073 """
1074
1075 def __init__(self, mem):
1076 # type: (Mem) -> None
1077
1078 # Because some prompts rely on the status leaking. See issue #853.
1079 # PS1 also does.
1080 last = mem.last_status[-1]
1081 mem.last_status.append(last)
1082 mem.try_status.append(0)
1083 mem.try_error.append(value.Dict({}))
1084
1085 # TODO: We should also copy these values! Turn the whole thing into a
1086 # frame.
1087 mem.pipe_status.append([])
1088 mem.process_sub_status.append([])
1089
1090 mem.regex_match.append(regex_match.No)
1091
1092 self.mem = mem
1093
1094 def __enter__(self):
1095 # type: () -> None
1096 pass
1097
1098 def __exit__(self, type, value, traceback):
1099 # type: (Any, Any, Any) -> None
1100 self.mem.regex_match.pop()
1101
1102 self.mem.process_sub_status.pop()
1103 self.mem.pipe_status.pop()
1104
1105 self.mem.try_error.pop()
1106 self.mem.try_status.pop()
1107 self.mem.last_status.pop()
1108
1109
1110class ctx_ThisDir(object):
1111 """For $_this_dir."""
1112
1113 def __init__(self, mem, filename):
1114 # type: (Mem, Optional[str]) -> None
1115 self.do_pop = False
1116 if filename is not None: # script_name in main() may be -c, etc.
1117 d = os_path.dirname(os_path.abspath(filename))
1118 mem.this_dir.append(d)
1119 self.do_pop = True
1120
1121 self.mem = mem
1122
1123 def __enter__(self):
1124 # type: () -> None
1125 pass
1126
1127 def __exit__(self, type, value, traceback):
1128 # type: (Any, Any, Any) -> None
1129 if self.do_pop:
1130 self.mem.this_dir.pop()
1131
1132
1133def _MakeArgvCell(argv):
1134 # type: (List[str]) -> Cell
1135 items = [value.Str(a) for a in argv] # type: List[value_t]
1136 return Cell(False, False, False, value.List(items))
1137
1138
1139class ctx_Eval(object):
1140 """Push temporary variable frame and override $0, $1, $2, etc."""
1141
1142 def __init__(self, mem, dollar0, pos_args, vars):
1143 # type: (Mem, Optional[str], Optional[List[str]], Optional[Dict[str, value_t]]) -> None
1144 self.mem = mem
1145 self.dollar0 = dollar0
1146 self.pos_args = pos_args
1147 self.vars = vars
1148
1149 # $0 needs to have lexical scoping. So we store it with other locals.
1150 # As "0" cannot be parsed as an lvalue, we can safely store dollar0 there.
1151 if dollar0 is not None:
1152 assert mem.GetValue("0", scope_e.LocalOnly).tag() == value_e.Undef
1153 self.dollar0_lval = LeftName("0", loc.Missing)
1154 mem.SetLocalName(self.dollar0_lval, value.Str(dollar0))
1155
1156 if pos_args is not None:
1157 mem.argv_stack.append(_ArgFrame(pos_args))
1158
1159 if vars is not None:
1160 frame = {} # type: Dict[str, Cell]
1161 for name in vars:
1162 frame[name] = Cell(False, False, False, vars[name])
1163
1164 mem.var_stack.append(frame)
1165
1166 def __enter__(self):
1167 # type: () -> None
1168 pass
1169
1170 def __exit__(self, type, value_, traceback):
1171 # type: (Any, Any, Any) -> None
1172 if self.vars is not None:
1173 self.mem.var_stack.pop()
1174
1175 if self.pos_args is not None:
1176 self.mem.argv_stack.pop()
1177
1178 if self.dollar0 is not None:
1179 self.mem.SetLocalName(self.dollar0_lval, value.Undef)
1180
1181
1182class Mem(object):
1183 """For storing variables.
1184
1185 Callers:
1186 User code: assigning and evaluating variables, in command context or
1187 arithmetic context.
1188 Completion engine: for COMP_WORDS, etc.
1189 Builtins call it implicitly: read, cd for $PWD, $OLDPWD, etc.
1190
1191 Modules: cmd_eval, word_eval, expr_eval, completion
1192 """
1193
1194 def __init__(self, dollar0, argv, arena, debug_stack):
1195 # type: (str, List[str], alloc.Arena, List[debug_frame_t]) -> None
1196 """
1197 Args:
1198 arena: currently unused
1199 """
1200 # circular dep initialized out of line
1201 self.exec_opts = None # type: optview.Exec
1202 self.unsafe_arith = None # type: sh_expr_eval.UnsafeArith
1203
1204 self.dollar0 = dollar0
1205 # If you only use YSH procs and funcs, this will remain at length 1.
1206 self.argv_stack = [_ArgFrame(argv)]
1207
1208 frame = NewDict() # type: Dict[str, Cell]
1209
1210 frame['ARGV'] = _MakeArgvCell(argv)
1211
1212 self.var_stack = [frame]
1213
1214 # The debug_stack isn't strictly necessary for execution. We use it
1215 # for crash dumps and for 3 parallel arrays: BASH_SOURCE, FUNCNAME, and
1216 # BASH_LINENO.
1217 self.debug_stack = debug_stack
1218
1219 self.pwd = None # type: Optional[str]
1220 self.seconds_start = time_.time()
1221
1222 self.token_for_line = None # type: Optional[Token]
1223 self.loc_for_expr = loc.Missing # type: loc_t
1224
1225 self.last_arg = '' # $_ is initially empty, NOT unset
1226 self.line_num = value.Str('')
1227
1228 # Done ONCE on initialization
1229 self.root_pid = posix.getpid()
1230
1231 # TODO:
1232 # - These are REGISTERS mutated by user code.
1233 # - Call it self.reg_stack? with ctx_Registers
1234 # - push-registers builtin
1235 self.last_status = [0] # type: List[int] # a stack
1236 self.try_status = [0] # type: List[int] # a stack
1237 self.try_error = [value.Dict({})] # type: List[value.Dict] # a stack
1238 self.pipe_status = [[]] # type: List[List[int]] # stack
1239 self.process_sub_status = [[]] # type: List[List[int]] # stack
1240
1241 # A stack but NOT a register?
1242 self.this_dir = [] # type: List[str]
1243 self.regex_match = [regex_match.No] # type: List[regex_match_t]
1244
1245 self.last_bg_pid = -1 # Uninitialized value mutable public variable
1246
1247 self.running_debug_trap = False # set by ctx_DebugTrap()
1248 self.running_err_trap = False # set by ctx_ErrTrap
1249 self.is_main = True # we start out in main
1250
1251 # For the ctx builtin
1252 self.ctx_stack = [] # type: List[Dict[str, value_t]]
1253
1254 def __repr__(self):
1255 # type: () -> str
1256 parts = [] # type: List[str]
1257 parts.append('<Mem')
1258 for i, frame in enumerate(self.var_stack):
1259 parts.append(' -- %d --' % i)
1260 for n, v in frame.iteritems():
1261 parts.append(' %s %s' % (n, v))
1262 parts.append('>')
1263 return '\n'.join(parts) + '\n'
1264
1265 def SetPwd(self, pwd):
1266 # type: (str) -> None
1267 """Used by builtins."""
1268 self.pwd = pwd
1269
1270 def ParsingChangesAllowed(self):
1271 # type: () -> bool
1272 """For checking that syntax options are only used at the top level."""
1273
1274 # DISALLOW proc calls : they push argv_stack, var_stack, debug_stack
1275 # ALLOW source foo.sh arg1: pushes argv_stack, debug_stack
1276 # ALLOW FOO=bar : pushes var_stack
1277 return len(self.var_stack) == 1 or len(self.argv_stack) == 1
1278
1279 def Dump(self):
1280 # type: () -> Tuple[List[value_t], List[value_t], List[value_t]]
1281 """Copy state before unwinding the stack."""
1282 var_stack = [
1283 value.Dict(_DumpVarFrame(frame)) for frame in self.var_stack
1284 ] # type: List[value_t]
1285 argv_stack = [value.Dict(frame.Dump())
1286 for frame in self.argv_stack] # type: List[value_t]
1287
1288 debug_stack = [] # type: List[value_t]
1289
1290 # Reuse these immutable objects
1291 t_call = value.Str('Call')
1292 t_source = value.Str('Source')
1293 t_main = value.Str('Main')
1294
1295 for frame in reversed(self.debug_stack):
1296 UP_frame = frame
1297 with tagswitch(frame) as case:
1298 if case(debug_frame_e.Call):
1299 frame = cast(debug_frame.Call, UP_frame)
1300 d = {
1301 'type': t_call,
1302 'func_name': value.Str(frame.func_name)
1303 } # type: Dict[str, value_t]
1304
1305 _AddCallToken(d, frame.call_tok)
1306 # TODO: Add def_tok
1307
1308 elif case(debug_frame_e.Source):
1309 frame = cast(debug_frame.Source, UP_frame)
1310 d = {
1311 'type': t_source,
1312 'source_name': value.Str(frame.source_name)
1313 }
1314 _AddCallToken(d, frame.call_tok)
1315
1316 elif case(debug_frame_e.Main):
1317 frame = cast(debug_frame.Main, UP_frame)
1318 d = {'type': t_main, 'dollar0': value.Str(frame.dollar0)}
1319
1320 debug_stack.append(value.Dict(d))
1321 return var_stack, argv_stack, debug_stack
1322
1323 def SetLastArgument(self, s):
1324 # type: (str) -> None
1325 """For $_"""
1326 self.last_arg = s
1327
1328 def SetTokenForLine(self, tok):
1329 # type: (Token) -> None
1330 """Set a token to compute $LINENO
1331
1332 This means it should be set on SimpleCommand, ShAssignment, ((, [[,
1333 case, etc. -- anything that evaluates a word. Example: there was a bug
1334 with 'case $LINENO'
1335
1336 This token also used as a "least-specific" / fallback location for
1337 errors in ExecuteAndCatch().
1338
1339 Although most of that should be taken over by 'with ui.ctx_Location()`,
1340 for the errfmt.
1341 """
1342 if self.running_debug_trap or self.running_err_trap:
1343 return
1344
1345 #if tok.span_id == runtime.NO_SPID:
1346 # NOTE: This happened in the osh-runtime benchmark for yash.
1347 #log('Warning: span_id undefined in SetTokenForLine')
1348
1349 #import traceback
1350 #traceback.print_stack()
1351 #return
1352
1353 self.token_for_line = tok
1354
1355 def SetLocationForExpr(self, blame_loc):
1356 # type: (loc_t) -> None
1357 """
1358 A more specific fallback location, like the $[ in
1359
1360 echo $[len(42)]
1361 """
1362 self.loc_for_expr = blame_loc
1363
1364 def GetFallbackLocation(self):
1365 # type: () -> loc_t
1366
1367 if self.loc_for_expr != loc.Missing: # more specific
1368 return self.loc_for_expr
1369
1370 if self.token_for_line: # less specific
1371 return self.token_for_line
1372
1373 return loc.Missing
1374
1375 #
1376 # Status Variable Stack (for isolating $PS1 and $PS4)
1377 #
1378
1379 def LastStatus(self):
1380 # type: () -> int
1381 return self.last_status[-1]
1382
1383 def TryStatus(self):
1384 # type: () -> int
1385 return self.try_status[-1]
1386
1387 def TryError(self):
1388 # type: () -> value.Dict
1389 return self.try_error[-1]
1390
1391 def PipeStatus(self):
1392 # type: () -> List[int]
1393 return self.pipe_status[-1]
1394
1395 def SetLastStatus(self, x):
1396 # type: (int) -> None
1397 self.last_status[-1] = x
1398
1399 def SetTryStatus(self, x):
1400 # type: (int) -> None
1401 self.try_status[-1] = x
1402
1403 def SetTryError(self, x):
1404 # type: (value.Dict) -> None
1405 self.try_error[-1] = x
1406
1407 def SetPipeStatus(self, x):
1408 # type: (List[int]) -> None
1409 self.pipe_status[-1] = x
1410
1411 def SetSimplePipeStatus(self, status):
1412 # type: (int) -> None
1413
1414 # Optimization to avoid allocations
1415 top = self.pipe_status[-1]
1416 if len(top) == 1:
1417 top[0] = status
1418 else:
1419 self.pipe_status[-1] = [status]
1420
1421 def SetProcessSubStatus(self, x):
1422 # type: (List[int]) -> None
1423 self.process_sub_status[-1] = x
1424
1425 #
1426 # Call Stack
1427 #
1428
1429 def PushCall(self, func_name, def_tok):
1430 # type: (str, Token) -> None
1431 """Push argv, var, and debug stack frames.
1432
1433 Currently used for proc and func calls. TODO: New func evaluator may
1434 not use it.
1435
1436 Args:
1437 def_tok: Token where proc or func was defined, used to compute
1438 BASH_SOURCE.
1439 """
1440 # self.token_for_line can be None?
1441 self.debug_stack.append(
1442 debug_frame.Call(self.token_for_line, def_tok, func_name))
1443
1444 def PopCall(self):
1445 # type: () -> None
1446 """
1447 Args:
1448 should_pop_argv_stack: Pass False if PushCall was given None for argv
1449 True for proc, False for func
1450 """
1451 self.debug_stack.pop()
1452
1453 def ShouldRunDebugTrap(self):
1454 # type: () -> bool
1455
1456 # TODO: RunLastPart of pipeline can disable this
1457
1458 # Don't recursively run DEBUG trap
1459 if self.running_debug_trap:
1460 return False
1461
1462 # Don't run it inside functions
1463 if len(self.var_stack) > 1:
1464 return False
1465
1466 return True
1467
1468 def InsideFunction(self):
1469 # type: () -> bool
1470 """For the ERR trap"""
1471
1472 # Don't run it inside functions
1473 return len(self.var_stack) > 1
1474
1475 def PushSource(self, source_name, argv):
1476 # type: (str, List[str]) -> None
1477 """ For 'source foo.sh 1 2 3' """
1478 if len(argv):
1479 self.argv_stack.append(_ArgFrame(argv))
1480
1481 # self.token_for_line can be None?
1482 self.debug_stack.append(
1483 debug_frame.Source(self.token_for_line, source_name))
1484
1485 def PopSource(self, argv):
1486 # type: (List[str]) -> None
1487 self.debug_stack.pop()
1488
1489 if len(argv):
1490 self.argv_stack.pop()
1491
1492 def PushTemp(self):
1493 # type: () -> None
1494 """For the temporary scope in 'FOO=bar BAR=baz echo'.
1495
1496 Also for PS4 evaluation with more variables.
1497 """
1498 # We don't want the 'read' builtin to write to this frame!
1499 frame = NewDict() # type: Dict[str, Cell]
1500 self.var_stack.append(frame)
1501
1502 def PopTemp(self):
1503 # type: () -> None
1504 self.var_stack.pop()
1505
1506 def TopNamespace(self):
1507 # type: () -> Dict[str, Cell]
1508 """For eval_to_dict()."""
1509 return self.var_stack[-1]
1510
1511 #
1512 # Argv
1513 #
1514
1515 def Shift(self, n):
1516 # type: (int) -> int
1517 frame = self.argv_stack[-1]
1518 num_args = len(frame.argv)
1519
1520 if (frame.num_shifted + n) <= num_args:
1521 frame.num_shifted += n
1522 return 0 # success
1523 else:
1524 return 1 # silent error
1525
1526 def GetArg0(self):
1527 # type: () -> value.Str
1528 """Like GetArgNum(0) but with a more specific type."""
1529 return value.Str(self.dollar0)
1530
1531 def GetArgNum(self, arg_num):
1532 # type: (int) -> value_t
1533 if arg_num == 0:
1534 # $0 may be overriden, eg. by Str => replace()
1535 vars = self.var_stack[-1]
1536 if "0" in vars and vars["0"].val.tag() != value_e.Undef:
1537 return vars["0"].val
1538 return value.Str(self.dollar0)
1539
1540 return self.argv_stack[-1].GetArgNum(arg_num)
1541
1542 def GetArgv(self):
1543 # type: () -> List[str]
1544 """For $* and $@."""
1545 return self.argv_stack[-1].GetArgv()
1546
1547 def SetArgv(self, argv):
1548 # type: (List[str]) -> None
1549 """For set -- 1 2 3."""
1550 # from set -- 1 2 3
1551 self.argv_stack[-1].SetArgv(argv)
1552
1553 #
1554 # Special Vars
1555 #
1556
1557 def GetSpecialVar(self, op_id):
1558 # type: (int) -> value_t
1559 if op_id == Id.VSub_Bang: # $!
1560 n = self.last_bg_pid
1561 if n == -1:
1562 return value.Undef # could be an error
1563
1564 elif op_id == Id.VSub_QMark: # $?
1565 # External commands need WIFEXITED test. What about subshells?
1566 n = self.last_status[-1]
1567
1568 elif op_id == Id.VSub_Pound: # $#
1569 n = self.argv_stack[-1].GetNumArgs()
1570
1571 elif op_id == Id.VSub_Dollar: # $$
1572 n = self.root_pid
1573
1574 else:
1575 raise NotImplementedError(op_id)
1576
1577 return value.Str(str(n))
1578
1579 #
1580 # Named Vars
1581 #
1582
1583 def _ResolveNameOnly(self, name, which_scopes):
1584 # type: (str, scope_t) -> Tuple[Optional[Cell], Dict[str, Cell]]
1585 """Helper for getting and setting variable.
1586
1587 Returns:
1588 cell: The cell corresponding to looking up 'name' with the given mode, or
1589 None if it's not found.
1590 name_map: The name_map it should be set to or deleted from.
1591 """
1592 if which_scopes == scope_e.Dynamic:
1593 for i in xrange(len(self.var_stack) - 1, -1, -1):
1594 name_map = self.var_stack[i]
1595 if name in name_map:
1596 cell = name_map[name]
1597 return cell, name_map
1598 no_cell = None # type: Optional[Cell]
1599 return no_cell, self.var_stack[0] # set in global name_map
1600
1601 if which_scopes == scope_e.LocalOnly:
1602 name_map = self.var_stack[-1]
1603 return name_map.get(name), name_map
1604
1605 if which_scopes == scope_e.GlobalOnly:
1606 name_map = self.var_stack[0]
1607 return name_map.get(name), name_map
1608
1609 if which_scopes == scope_e.LocalOrGlobal:
1610 # Local
1611 name_map = self.var_stack[-1]
1612 cell = name_map.get(name)
1613 if cell:
1614 return cell, name_map
1615
1616 # Global
1617 name_map = self.var_stack[0]
1618 return name_map.get(name), name_map
1619
1620 raise AssertionError()
1621
1622 def _ResolveNameOrRef(
1623 self,
1624 name, # type: str
1625 which_scopes, # type: scope_t
1626 ref_trail=None, # type: Optional[List[str]]
1627 ):
1628 # type: (...) -> Tuple[Optional[Cell], Dict[str, Cell], str]
1629 """Look up a cell and namespace, but respect the nameref flag.
1630
1631 Resolving namerefs does RECURSIVE calls.
1632 """
1633 cell, name_map = self._ResolveNameOnly(name, which_scopes)
1634
1635 if cell is None or not cell.nameref:
1636 return cell, name_map, name # not a nameref
1637
1638 val = cell.val
1639 UP_val = val
1640 with tagswitch(val) as case:
1641 if case(value_e.Undef):
1642 # This is 'local -n undef_ref', which is kind of useless, because the
1643 # more common idiom is 'local -n ref=$1'. Note that you can mutate
1644 # references themselves with local -n ref=new.
1645 if self.exec_opts.strict_nameref():
1646 e_die('nameref %r is undefined' % name)
1647 else:
1648 return cell, name_map, name # fallback
1649
1650 elif case(value_e.Str):
1651 val = cast(value.Str, UP_val)
1652 new_name = val.s
1653
1654 else:
1655 # SetValue() protects the invariant that nameref is Undef or Str
1656 raise AssertionError(val.tag())
1657
1658 # TODO: Respect eval_unsafe_arith here (issue 881). See how it's done in
1659 # 'printf -v' with MakeArithParser
1660 if not match.IsValidVarName(new_name):
1661 # e.g. '#' or '1' or ''
1662 if self.exec_opts.strict_nameref():
1663 e_die('nameref %r contains invalid variable name %r' %
1664 (name, new_name))
1665 else:
1666 # Bash has this odd behavior of clearing the nameref bit when
1667 # ref=#invalid#. strict_nameref avoids it.
1668 cell.nameref = False
1669 return cell, name_map, name # fallback
1670
1671 # Check for circular namerefs.
1672 if ref_trail is None:
1673 ref_trail = [name]
1674 else:
1675 if new_name in ref_trail:
1676 e_die('Circular nameref %s' % ' -> '.join(ref_trail))
1677 ref_trail.append(new_name)
1678
1679 # 'declare -n' uses dynamic scope.
1680 cell, name_map, cell_name = self._ResolveNameOrRef(new_name,
1681 scope_e.Dynamic,
1682 ref_trail=ref_trail)
1683 return cell, name_map, cell_name
1684
1685 def IsBashAssoc(self, name):
1686 # type: (str) -> bool
1687 """Returns whether a name resolve to a cell with an associative array.
1688
1689 We need to know this to evaluate the index expression properly
1690 -- should it be coerced to an integer or not?
1691 """
1692 cell, _, _ = self._ResolveNameOrRef(name, self.ScopesForReading())
1693 # foo=([key]=value)
1694 return cell is not None and cell.val.tag() == value_e.BashAssoc
1695
1696 def SetPlace(self, place, val, blame_loc):
1697 # type: (value.Place, value_t, loc_t) -> None
1698
1699 yval = place.lval
1700 UP_yval = yval
1701 with tagswitch(yval) as case:
1702 if case(y_lvalue_e.Local):
1703 yval = cast(LeftName, UP_yval)
1704
1705 # Check that the frame is still alive
1706 found = False
1707 for i in xrange(len(self.var_stack) - 1, -1, -1):
1708 frame = self.var_stack[i]
1709 if frame is place.frame:
1710 found = True
1711 #log('FOUND %s', found)
1712 break
1713 if not found:
1714 e_die(
1715 "Can't assign to place that's no longer on the call stack.",
1716 blame_loc)
1717
1718 cell = frame.get(yval.name)
1719 if cell is None:
1720 cell = Cell(False, False, False, val)
1721 frame[yval.name] = cell
1722 else:
1723 cell.val = val
1724
1725 elif case(y_lvalue_e.Container):
1726 e_die('Container place not implemented', blame_loc)
1727
1728 else:
1729 raise AssertionError()
1730
1731 def SetLocalName(self, lval, val):
1732 # type: (LeftName, value_t) -> None
1733
1734 # Equivalent to
1735 # self._ResolveNameOnly(lval.name, scope_e.LocalOnly)
1736 name_map = self.var_stack[-1]
1737 cell = name_map.get(lval.name)
1738
1739 if cell:
1740 if cell.readonly:
1741 e_die("Can't assign to readonly value %r" % lval.name,
1742 lval.blame_loc)
1743 cell.val = val # Mutate value_t
1744 else:
1745 cell = Cell(False, False, False, val)
1746 name_map[lval.name] = cell
1747
1748 def SetNamed(self, lval, val, which_scopes, flags=0):
1749 # type: (LeftName, value_t, scope_t, int) -> None
1750
1751 if flags & SetNameref or flags & ClearNameref:
1752 # declare -n ref=x # refers to the ref itself
1753 cell, name_map = self._ResolveNameOnly(lval.name, which_scopes)
1754 cell_name = lval.name
1755 else:
1756 # ref=x # mutates THROUGH the reference
1757
1758 # Note on how to implement declare -n ref='a[42]'
1759 # 1. Call _ResolveNameOnly()
1760 # 2. If cell.nameref, call self.unsafe_arith.ParseVarRef() ->
1761 # BracedVarSub
1762 # 3. Turn BracedVarSub into an sh_lvalue, and call
1763 # self.unsafe_arith.SetValue() wrapper with ref_trail
1764 cell, name_map, cell_name = self._ResolveNameOrRef(
1765 lval.name, which_scopes)
1766
1767 if cell:
1768 # Clear before checking readonly bit.
1769 # NOTE: Could be cell.flags &= flag_clear_mask
1770 if flags & ClearExport:
1771 cell.exported = False
1772 if flags & ClearReadOnly:
1773 cell.readonly = False
1774 if flags & ClearNameref:
1775 cell.nameref = False
1776
1777 if val is not None: # e.g. declare -rx existing
1778 # Note: this DYNAMIC check means we can't have 'const' in a loop.
1779 # But that's true for 'readonly' too, and hoisting it makes more
1780 # sense anyway.
1781 if cell.readonly:
1782 e_die("Can't assign to readonly value %r" % lval.name,
1783 lval.blame_loc)
1784 cell.val = val # CHANGE VAL
1785
1786 # NOTE: Could be cell.flags |= flag_set_mask
1787 if flags & SetExport:
1788 cell.exported = True
1789 if flags & SetReadOnly:
1790 cell.readonly = True
1791 if flags & SetNameref:
1792 cell.nameref = True
1793
1794 else:
1795 if val is None: # declare -rx nonexistent
1796 # set -o nounset; local foo; echo $foo # It's still undefined!
1797 val = value.Undef # export foo, readonly foo
1798
1799 cell = Cell(bool(flags & SetExport), bool(flags & SetReadOnly),
1800 bool(flags & SetNameref), val)
1801 name_map[cell_name] = cell
1802
1803 # Maintain invariant that only strings and undefined cells can be
1804 # exported.
1805 assert cell.val is not None, cell
1806
1807 if cell.val.tag() not in (value_e.Undef, value_e.Str):
1808 if cell.exported:
1809 if self.exec_opts.strict_array():
1810 e_die("Only strings can be exported (strict_array)",
1811 lval.blame_loc)
1812 if cell.nameref:
1813 e_die("nameref must be a string", lval.blame_loc)
1814
1815 def SetValue(self, lval, val, which_scopes, flags=0):
1816 # type: (sh_lvalue_t, value_t, scope_t, int) -> None
1817 """
1818 Args:
1819 lval: sh_lvalue
1820 val: value, or None if only changing flags
1821 which_scopes:
1822 Local | Global | Dynamic - for builtins, PWD, etc.
1823 flags: packed pair (keyword_id, bit mask of set/clear flags)
1824
1825 Note: in bash, PWD=/ changes the directory. But not in dash.
1826 """
1827 # STRICTNESS / SANENESS:
1828 #
1829 # 1) Don't create arrays automatically, e.g. a[1000]=x
1830 # 2) Never change types? yeah I think that's a good idea, at least for oil
1831 # (not sh, for compatibility). set -o strict_types or something. That
1832 # means arrays have to be initialized with let arr = [], which is fine.
1833 # This helps with stuff like IFS. It starts off as a string, and assigning
1834 # it to a list is an error. I guess you will have to turn this no for
1835 # bash?
1836 #
1837 # TODO:
1838 # - COMPUTED vars can't be set
1839 # - What about PWD / OLDPWD / UID / EUID ? You can simply make them
1840 # readonly.
1841 # - Maybe PARSE $PS1 and $PS4 when they're set, to avoid the error on use?
1842 # - Other validity: $HOME could be checked for existence
1843
1844 UP_lval = lval
1845 with tagswitch(lval) as case:
1846 if case(sh_lvalue_e.Var):
1847 lval = cast(LeftName, UP_lval)
1848
1849 self.SetNamed(lval, val, which_scopes, flags=flags)
1850
1851 elif case(sh_lvalue_e.Indexed):
1852 lval = cast(sh_lvalue.Indexed, UP_lval)
1853
1854 # There is no syntax 'declare a[x]'
1855 assert val is not None, val
1856
1857 # TODO: relax this for Oil
1858 assert val.tag() == value_e.Str, val
1859 rval = cast(value.Str, val)
1860
1861 # Note: location could be a[x]=1 or (( a[ x ] = 1 ))
1862 left_loc = lval.blame_loc
1863
1864 # bash/mksh have annoying behavior of letting you do LHS assignment to
1865 # Undef, which then turns into an INDEXED array. (Undef means that set
1866 # -o nounset fails.)
1867 cell, name_map, _ = self._ResolveNameOrRef(
1868 lval.name, which_scopes)
1869 if not cell:
1870 self._BindNewArrayWithEntry(name_map, lval, rval, flags)
1871 return
1872
1873 if cell.readonly:
1874 e_die("Can't assign to readonly array", left_loc)
1875
1876 UP_cell_val = cell.val
1877 # undef[0]=y is allowed
1878 with tagswitch(UP_cell_val) as case2:
1879 if case2(value_e.Undef):
1880 self._BindNewArrayWithEntry(name_map, lval, rval,
1881 flags)
1882 return
1883
1884 elif case2(value_e.Str):
1885 # s=x
1886 # s[1]=y # invalid
1887 e_die("Can't assign to items in a string", left_loc)
1888
1889 elif case2(value_e.BashArray):
1890 cell_val = cast(value.BashArray, UP_cell_val)
1891 strs = cell_val.strs
1892
1893 n = len(strs)
1894 index = lval.index
1895 if index < 0: # a[-1]++ computes this twice; could we avoid it?
1896 index += n
1897
1898 if 0 <= index and index < n:
1899 strs[index] = rval.s
1900 else:
1901 # Fill it in with None. It could look like this:
1902 # ['1', 2, 3, None, None, '4', None]
1903 # Then ${#a[@]} counts the entries that are not None.
1904 #
1905 # TODO: strict_array for Oil arrays won't auto-fill.
1906 n = index - len(strs) + 1
1907 for i in xrange(n):
1908 strs.append(None)
1909 strs[lval.index] = rval.s
1910 return
1911
1912 # This could be an object, eggex object, etc. It won't be
1913 # BashAssoc shouldn because we query IsBashAssoc before evaluating
1914 # sh_lhs. Could conslidate with s[i] case above
1915 e_die(
1916 "Value of type %s can't be indexed" % ui.ValType(cell.val),
1917 left_loc)
1918
1919 elif case(sh_lvalue_e.Keyed):
1920 lval = cast(sh_lvalue.Keyed, UP_lval)
1921
1922 # There is no syntax 'declare A["x"]'
1923 assert val is not None, val
1924 assert val.tag() == value_e.Str, val
1925 rval = cast(value.Str, val)
1926
1927 left_loc = lval.blame_loc
1928
1929 cell, name_map, _ = self._ResolveNameOrRef(
1930 lval.name, which_scopes)
1931 if cell.readonly:
1932 e_die("Can't assign to readonly associative array",
1933 left_loc)
1934
1935 # We already looked it up before making the sh_lvalue
1936 assert cell.val.tag() == value_e.BashAssoc, cell
1937 cell_val2 = cast(value.BashAssoc, cell.val)
1938
1939 cell_val2.d[lval.key] = rval.s
1940
1941 else:
1942 raise AssertionError(lval.tag())
1943
1944 def _BindNewArrayWithEntry(self, name_map, lval, val, flags):
1945 # type: (Dict[str, Cell], sh_lvalue.Indexed, value.Str, int) -> None
1946 """Fill 'name_map' with a new indexed array entry."""
1947 no_str = None # type: Optional[str]
1948 items = [no_str] * lval.index
1949 items.append(val.s)
1950 new_value = value.BashArray(items)
1951
1952 # arrays can't be exported; can't have BashAssoc flag
1953 readonly = bool(flags & SetReadOnly)
1954 name_map[lval.name] = Cell(False, readonly, False, new_value)
1955
1956 def InternalSetGlobal(self, name, new_val):
1957 # type: (str, value_t) -> None
1958 """For setting read-only globals internally.
1959
1960 Args:
1961 name: string (not Lhs)
1962 new_val: value
1963
1964 The variable must already exist.
1965
1966 Use case: SHELLOPTS.
1967 """
1968 cell = self.var_stack[0][name]
1969 cell.val = new_val
1970
1971 def GetValue(self, name, which_scopes=scope_e.Shopt):
1972 # type: (str, scope_t) -> value_t
1973 """Used by the WordEvaluator, ArithEvaluator, ExprEvaluator, etc."""
1974 assert isinstance(name, str), name
1975
1976 if which_scopes == scope_e.Shopt:
1977 which_scopes = self.ScopesForReading()
1978
1979 with str_switch(name) as case:
1980 # "Registers"
1981 if case('_status'):
1982 return num.ToBig(self.TryStatus())
1983
1984 elif case('_error'):
1985 return self.TryError()
1986
1987 elif case('_this_dir'):
1988 if len(self.this_dir) == 0:
1989 # e.g. osh -c '' doesn't have it set
1990 # Should we give a custom error here?
1991 # If you're at the interactive shell, 'source mymodule.oil' will still
1992 # work because 'source' sets it.
1993 return value.Undef
1994 else:
1995 return value.Str(self.this_dir[-1]) # top of stack
1996
1997 elif case('PIPESTATUS'):
1998 strs2 = [str(i)
1999 for i in self.pipe_status[-1]] # type: List[str]
2000 return value.BashArray(strs2)
2001
2002 elif case('_pipeline_status'):
2003 items = [num.ToBig(i)
2004 for i in self.pipe_status[-1]] # type: List[value_t]
2005 return value.List(items)
2006
2007 elif case('_process_sub_status'): # YSH naming convention
2008 items = [num.ToBig(i) for i in self.process_sub_status[-1]]
2009 return value.List(items)
2010
2011 elif case('BASH_REMATCH'):
2012 top_match = self.regex_match[-1]
2013 with tagswitch(top_match) as case2:
2014 if case2(regex_match_e.No):
2015 groups = [] # type: List[str]
2016 elif case2(regex_match_e.Yes):
2017 m = cast(RegexMatch, top_match)
2018 groups = util.RegexGroupStrings(m.s, m.indices)
2019 return value.BashArray(groups)
2020
2021 # Do lookup of system globals before looking at user variables. Note: we
2022 # could optimize this at compile-time like $?. That would break
2023 # ${!varref}, but it's already broken for $?.
2024
2025 elif case('FUNCNAME'):
2026 # bash wants it in reverse order. This is a little inefficient but we're
2027 # not depending on deque().
2028 strs = [] # type: List[str]
2029 for frame in reversed(self.debug_stack):
2030 UP_frame = frame
2031 with tagswitch(frame) as case2:
2032 if case2(debug_frame_e.Call):
2033 frame = cast(debug_frame.Call, UP_frame)
2034 strs.append(frame.func_name)
2035
2036 elif case2(debug_frame_e.Source):
2037 # bash doesn't tell you the filename sourced
2038 strs.append('source')
2039
2040 elif case2(debug_frame_e.Main):
2041 strs.append('main') # also bash behavior
2042
2043 return value.BashArray(strs) # TODO: Reuse this object too?
2044
2045 # $BASH_SOURCE and $BASH_LINENO have OFF BY ONE design bugs:
2046 #
2047 # ${BASH_LINENO[$i]} is the line number in the source file
2048 # (${BASH_SOURCE[$i+1]}) where ${FUNCNAME[$i]} was called (or
2049 # ${BASH_LINENO[$i-1]} if referenced within another shell function).
2050 #
2051 # https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
2052
2053 elif case('BASH_SOURCE'):
2054 strs = []
2055 for frame in reversed(self.debug_stack):
2056 UP_frame = frame
2057 with tagswitch(frame) as case2:
2058 if case2(debug_frame_e.Call):
2059 frame = cast(debug_frame.Call, UP_frame)
2060
2061 # Weird bash behavior
2062 assert frame.def_tok.line is not None
2063 source_str = ui.GetLineSourceString(
2064 frame.def_tok.line)
2065 strs.append(source_str)
2066
2067 elif case2(debug_frame_e.Source):
2068 frame = cast(debug_frame.Source, UP_frame)
2069 # Is this right?
2070 strs.append(frame.source_name)
2071
2072 elif case2(debug_frame_e.Main):
2073 frame = cast(debug_frame.Main, UP_frame)
2074 strs.append(frame.dollar0)
2075
2076 return value.BashArray(strs) # TODO: Reuse this object too?
2077
2078 elif case('BASH_LINENO'):
2079 strs = []
2080 for frame in reversed(self.debug_stack):
2081 UP_frame = frame
2082 with tagswitch(frame) as case2:
2083 if case2(debug_frame_e.Call):
2084 frame = cast(debug_frame.Call, UP_frame)
2085 strs.append(_LineNumber(frame.call_tok))
2086
2087 elif case2(debug_frame_e.Source):
2088 frame = cast(debug_frame.Source, UP_frame)
2089 strs.append(_LineNumber(frame.call_tok))
2090
2091 elif case2(debug_frame_e.Main):
2092 # Bash does this to line up with 'main'
2093 strs.append('0')
2094
2095 return value.BashArray(strs) # TODO: Reuse this object too?
2096
2097 elif case('LINENO'):
2098 assert self.token_for_line is not None
2099 # Reuse object with mutation
2100 # TODO: maybe use interned GetLineNumStr?
2101 self.line_num.s = str(self.token_for_line.line.line_num)
2102 return self.line_num
2103
2104 elif case('BASHPID'): # TODO: YSH io->getpid()
2105 return value.Str(str(posix.getpid()))
2106
2107 elif case('_'):
2108 return value.Str(self.last_arg)
2109
2110 elif case('SECONDS'):
2111 f = time_.time() - self.seconds_start
2112 ok, big_int = mops.FromFloat(f)
2113 assert ok, f # should never be NAN or INFINITY
2114 return value.Int(big_int)
2115
2116 else:
2117 # In the case 'declare -n ref='a[42]', the result won't be a cell. Idea to
2118 # fix this:
2119 # 1. Call self.unsafe_arith.ParseVarRef() -> BracedVarSub
2120 # 2. Call self.unsafe_arith.GetNameref(bvs_part), and get a value_t
2121 # We still need a ref_trail to detect cycles.
2122 cell, _, _ = self._ResolveNameOrRef(name, which_scopes)
2123 if cell:
2124 return cell.val
2125
2126 return value.Undef
2127
2128 def GetCell(self, name, which_scopes=scope_e.Shopt):
2129 # type: (str, scope_t) -> Cell
2130 """Get both the value and flags.
2131
2132 Usages:
2133 - the 'pp' builtin.
2134 - declare -p
2135 - ${x@a}
2136 - to test of 'TZ' is exported in printf? Why?
2137 """
2138 if which_scopes == scope_e.Shopt:
2139 which_scopes = self.ScopesForReading()
2140
2141 cell, _ = self._ResolveNameOnly(name, which_scopes)
2142 return cell
2143
2144 def Unset(self, lval, which_scopes):
2145 # type: (sh_lvalue_t, scope_t) -> bool
2146 """
2147 Returns:
2148 Whether the cell was found.
2149 """
2150 # TODO: Refactor sh_lvalue type to avoid this
2151 UP_lval = lval
2152
2153 with tagswitch(lval) as case:
2154 if case(sh_lvalue_e.Var): # unset x
2155 lval = cast(LeftName, UP_lval)
2156 var_name = lval.name
2157 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2158 lval = cast(sh_lvalue.Indexed, UP_lval)
2159 var_name = lval.name
2160 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2161 lval = cast(sh_lvalue.Keyed, UP_lval)
2162 var_name = lval.name
2163 else:
2164 raise AssertionError()
2165
2166 if which_scopes == scope_e.Shopt:
2167 which_scopes = self.ScopesForWriting()
2168
2169 cell, name_map, cell_name = self._ResolveNameOrRef(
2170 var_name, which_scopes)
2171 if not cell:
2172 return False # 'unset' builtin falls back on functions
2173 if cell.readonly:
2174 raise error.Runtime("Can't unset readonly variable %r" % var_name)
2175
2176 with tagswitch(lval) as case:
2177 if case(sh_lvalue_e.Var): # unset x
2178 # Make variables in higher scopes visible.
2179 # example: test/spec.sh builtin-vars -r 24 (ble.sh)
2180 mylib.dict_erase(name_map, cell_name)
2181
2182 # alternative that some shells use:
2183 # name_map[cell_name].val = value.Undef
2184 # cell.exported = False
2185
2186 # This should never happen because we do recursive lookups of namerefs.
2187 assert not cell.nameref, cell
2188
2189 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2190 lval = cast(sh_lvalue.Indexed, UP_lval)
2191 # Note: Setting an entry to None and shifting entries are pretty
2192 # much the same in shell.
2193
2194 val = cell.val
2195 UP_val = val
2196 if val.tag() != value_e.BashArray:
2197 raise error.Runtime("%r isn't an array" % var_name)
2198
2199 val = cast(value.BashArray, UP_val)
2200 strs = val.strs
2201
2202 n = len(strs)
2203 last_index = n - 1
2204 index = lval.index
2205 if index < 0:
2206 index += n
2207
2208 if index == last_index:
2209 # Special case: The array SHORTENS if you unset from the end. You
2210 # can tell with a+=(3 4)
2211 strs.pop()
2212 elif 0 <= index and index < last_index:
2213 strs[index] = None
2214 else:
2215 # If it's not found, it's not an error. In other words, 'unset'
2216 # ensures that a value doesn't exist, regardless of whether it
2217 # existed. It's idempotent.
2218 # (Ousterhout specifically argues that the strict behavior was a
2219 # mistake for Tcl!)
2220 pass
2221
2222 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2223 lval = cast(sh_lvalue.Keyed, UP_lval)
2224
2225 val = cell.val
2226 UP_val = val
2227
2228 # note: never happens because of mem.IsBashAssoc test for sh_lvalue.Keyed
2229 #if val.tag() != value_e.BashAssoc:
2230 # raise error.Runtime("%r isn't an associative array" % lval.name)
2231
2232 val = cast(value.BashAssoc, UP_val)
2233 mylib.dict_erase(val.d, lval.key)
2234
2235 else:
2236 raise AssertionError(lval)
2237
2238 return True
2239
2240 def ScopesForReading(self):
2241 # type: () -> scope_t
2242 """Read scope."""
2243 return (scope_e.Dynamic
2244 if self.exec_opts.dynamic_scope() else scope_e.LocalOrGlobal)
2245
2246 def ScopesForWriting(self):
2247 # type: () -> scope_t
2248 """Write scope."""
2249 return (scope_e.Dynamic
2250 if self.exec_opts.dynamic_scope() else scope_e.LocalOnly)
2251
2252 def ClearFlag(self, name, flag):
2253 # type: (str, int) -> bool
2254 """Used for export -n.
2255
2256 We don't use SetValue() because even if rval is None, it will make an
2257 Undef value in a scope.
2258 """
2259 cell, name_map = self._ResolveNameOnly(name, self.ScopesForReading())
2260 if cell:
2261 if flag & ClearExport:
2262 cell.exported = False
2263 if flag & ClearNameref:
2264 cell.nameref = False
2265 return True
2266 else:
2267 return False
2268
2269 def GetExported(self):
2270 # type: () -> Dict[str, str]
2271 """Get all the variables that are marked exported."""
2272 # TODO: This is run on every SimpleCommand. Should we have a dirty flag?
2273 # We have to notice these things:
2274 # - If an exported variable is changed.
2275 # - If the set of exported variables changes.
2276
2277 exported = {} # type: Dict[str, str]
2278 # Search from globals up. Names higher on the stack will overwrite names
2279 # lower on the stack.
2280 for scope in self.var_stack:
2281 for name, cell in iteritems(scope):
2282 # TODO: Disallow exporting at assignment time. If an exported Str is
2283 # changed to BashArray, also clear its 'exported' flag.
2284 if cell.exported and cell.val.tag() == value_e.Str:
2285 val = cast(value.Str, cell.val)
2286 exported[name] = val.s
2287 return exported
2288
2289 def VarNames(self):
2290 # type: () -> List[str]
2291 """For internal OSH completion and compgen -A variable.
2292
2293 NOTE: We could also add $? $$ etc.?
2294 """
2295 ret = [] # type: List[str]
2296 # Look up the stack, yielding all variables. Bash seems to do this.
2297 for scope in self.var_stack:
2298 for name in scope:
2299 ret.append(name)
2300 return ret
2301
2302 def VarNamesStartingWith(self, prefix):
2303 # type: (str) -> List[str]
2304 """For ${!prefix@}"""
2305 # Look up the stack, yielding all variables. Bash seems to do this.
2306 names = [] # type: List[str]
2307 for scope in self.var_stack:
2308 for name in scope:
2309 if name.startswith(prefix):
2310 names.append(name)
2311 return names
2312
2313 def GetAllVars(self):
2314 # type: () -> Dict[str, str]
2315 """Get all variables and their values, for 'set' builtin."""
2316 result = {} # type: Dict[str, str]
2317 for scope in self.var_stack:
2318 for name, cell in iteritems(scope):
2319 # TODO: Show other types?
2320 val = cell.val
2321 if val.tag() == value_e.Str:
2322 str_val = cast(value.Str, val)
2323 result[name] = str_val.s
2324 return result
2325
2326 def GetAllCells(self, which_scopes):
2327 # type: (scope_t) -> Dict[str, Cell]
2328 """Get all variables and their values, for 'set' builtin."""
2329 result = {} # type: Dict[str, Cell]
2330
2331 if which_scopes == scope_e.Dynamic:
2332 scopes = self.var_stack
2333 elif which_scopes == scope_e.LocalOnly:
2334 scopes = self.var_stack[-1:]
2335 elif which_scopes == scope_e.GlobalOnly:
2336 scopes = self.var_stack[0:1]
2337 elif which_scopes == scope_e.LocalOrGlobal:
2338 scopes = [self.var_stack[0]]
2339 if len(self.var_stack) > 1:
2340 scopes.append(self.var_stack[-1])
2341 else:
2342 raise AssertionError()
2343
2344 for scope in scopes:
2345 for name, cell in iteritems(scope):
2346 result[name] = cell
2347 return result
2348
2349 def IsGlobalScope(self):
2350 # type: () -> bool
2351 return len(self.var_stack) == 1
2352
2353 def SetRegexMatch(self, match):
2354 # type: (regex_match_t) -> None
2355 self.regex_match[-1] = match
2356
2357 def GetRegexMatch(self):
2358 # type: () -> regex_match_t
2359 return self.regex_match[-1]
2360
2361 def PushContextStack(self, context):
2362 # type: (Dict[str, value_t]) -> None
2363 self.ctx_stack.append(context)
2364
2365 def GetContext(self):
2366 # type: () -> Optional[Dict[str, value_t]]
2367 if len(self.ctx_stack):
2368 return self.ctx_stack[-1]
2369 return None
2370
2371 def PopContextStack(self):
2372 # type: () -> Dict[str, value_t]
2373 assert self.ctx_stack, "Empty context stack"
2374 return self.ctx_stack.pop()
2375
2376
2377class Procs:
2378
2379 def __init__(self, mem):
2380 # type: (Mem) -> None
2381 self.mem = mem
2382 self.sh_funcs = {} # type: Dict[str, value.Proc]
2383
2384 def SetProc(self, name, proc):
2385 # type: (str, value.Proc) -> None
2386 self.mem.var_stack[0][name] = Cell(False, False, False, proc)
2387
2388 def SetShFunc(self, name, proc):
2389 # type: (str, value.Proc) -> None
2390 self.sh_funcs[name] = proc
2391
2392 def Get(self, name):
2393 # type: (str) -> value.Proc
2394 """Try to find a proc/sh-func by `name`, or return None if not found.
2395
2396 First, we search for a proc, and then a sh-func. This means that procs
2397 can shadow the definition of sh-funcs.
2398 """
2399 maybe_proc = self.mem.GetValue(name)
2400 if maybe_proc.tag() == value_e.Proc:
2401 return cast(value.Proc, maybe_proc)
2402
2403 if name in self.sh_funcs:
2404 return self.sh_funcs[name]
2405
2406 return None
2407
2408 def Del(self, to_del):
2409 # type: (str) -> None
2410 """Undefine a sh-func with name `to_del`, if it exists."""
2411 mylib.dict_erase(self.sh_funcs, to_del)
2412
2413 def GetNames(self):
2414 # type: () -> List[str]
2415 """Returns a *sorted* list of all proc names"""
2416 names = list(self.sh_funcs.keys())
2417
2418 vars = self.mem.var_stack[0]
2419 for name in vars:
2420 cell = vars[name]
2421 if cell.val.tag() == value_e.Proc:
2422 names.append(name)
2423
2424 return sorted(names)
2425
2426
2427#
2428# Wrappers to Set Variables
2429#
2430
2431
2432def OshLanguageSetValue(mem, lval, val, flags=0):
2433 # type: (Mem, sh_lvalue_t, value_t, int) -> None
2434 """Like 'setvar' (scope_e.LocalOnly), unless dynamic scope is on.
2435
2436 That is, it respects shopt --unset dynamic_scope.
2437
2438 Used for assignment builtins, (( a = b )), {fd}>out, ${x=}, etc.
2439 """
2440 which_scopes = mem.ScopesForWriting()
2441 mem.SetValue(lval, val, which_scopes, flags=flags)
2442
2443
2444def BuiltinSetValue(mem, lval, val):
2445 # type: (Mem, sh_lvalue_t, value_t) -> None
2446 """Equivalent of x=$y
2447
2448 Called by BuiltinSetString and BuiltinSetArray Used directly by
2449 printf -v because it can mutate an array
2450 """
2451 mem.SetValue(lval, val, mem.ScopesForWriting())
2452
2453
2454def BuiltinSetString(mem, name, s):
2455 # type: (Mem, str, str) -> None
2456 """Set a string by looking up the stack.
2457
2458 Used for 'read', 'getopts', completion builtins, etc.
2459 """
2460 assert isinstance(s, str)
2461 BuiltinSetValue(mem, location.LName(name), value.Str(s))
2462
2463
2464def BuiltinSetArray(mem, name, a):
2465 # type: (Mem, str, List[str]) -> None
2466 """Set an array by looking up the stack.
2467
2468 Used by compadjust, read -a, etc.
2469 """
2470 assert isinstance(a, list)
2471 BuiltinSetValue(mem, location.LName(name), value.BashArray(a))
2472
2473
2474def SetGlobalString(mem, name, s):
2475 # type: (Mem, str, str) -> None
2476 """Helper for completion, etc."""
2477 assert isinstance(s, str)
2478 val = value.Str(s)
2479 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2480
2481
2482def SetGlobalArray(mem, name, a):
2483 # type: (Mem, str, List[str]) -> None
2484 """Used by completion, shell initialization, etc."""
2485 assert isinstance(a, list)
2486 mem.SetNamed(location.LName(name), value.BashArray(a), scope_e.GlobalOnly)
2487
2488
2489def _SetGlobalValue(mem, name, val):
2490 # type: (Mem, str, value_t) -> None
2491 """Helper for completion, etc."""
2492 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2493
2494
2495def ExportGlobalString(mem, name, s):
2496 # type: (Mem, str, str) -> None
2497 """Helper for completion, $PWD, $OLDPWD, etc."""
2498 assert isinstance(s, str)
2499 val = value.Str(s)
2500 mem.SetNamed(location.LName(name),
2501 val,
2502 scope_e.GlobalOnly,
2503 flags=SetExport)
2504
2505
2506#
2507# Wrappers to Get Variables
2508#
2509
2510
2511def DynamicGetVar(mem, name, which_scopes):
2512 # type: (Mem, str, scope_t) -> value_t
2513 """
2514 For getVar() and shvarGet()
2515 """
2516 val = mem.GetValue(name, which_scopes=which_scopes)
2517
2518 # Undef is not a user-visible value!
2519 # There's no way to distinguish null from undefined.
2520 if val.tag() == value_e.Undef:
2521 return value.Null
2522
2523 return val
2524
2525
2526def GetString(mem, name):
2527 # type: (Mem, str) -> str
2528 """Wrapper around GetValue(). Check that HOME, PWD, OLDPWD, etc. are
2529 strings. bash doesn't have these errors because ${array} is ${array[0]}.
2530
2531 TODO: We could also check this when you're storing variables?
2532 """
2533 val = mem.GetValue(name)
2534 UP_val = val
2535 with tagswitch(val) as case:
2536 if case(value_e.Undef):
2537 raise error.Runtime("$%s isn't defined" % name)
2538 elif case(value_e.Str):
2539 return cast(value.Str, UP_val).s
2540 else:
2541 # User would have to 'unset HOME' to get rid of exported flag
2542 raise error.Runtime("$%s should be a string" % name)
2543
2544
2545def MaybeString(mem, name):
2546 # type: (Mem, str) -> Optional[str]
2547 """Like GetString(), but doesn't throw an exception."""
2548 try:
2549 return GetString(mem, name)
2550 except error.Runtime:
2551 return None
2552
2553
2554def GetInteger(mem, name):
2555 # type: (Mem, str) -> int
2556 """For OPTIND variable used in getopts builtin.
2557
2558 TODO: it could be value.Int() ?
2559 """
2560 val = mem.GetValue(name)
2561 if val.tag() != value_e.Str:
2562 raise error.Runtime('$%s should be a string, got %s' %
2563 (name, ui.ValType(val)))
2564 s = cast(value.Str, val).s
2565 try:
2566 i = int(s)
2567 except ValueError:
2568 raise error.Runtime("$%s doesn't look like an integer, got %r" %
2569 (name, s))
2570 return i
2571
2572
2573# vim: sw=4