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

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