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

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