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

2530 lines, 1282 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 return value.Int(
2069 mops.FromFloat(time_.time() - self.seconds_start))
2070
2071 else:
2072 # In the case 'declare -n ref='a[42]', the result won't be a cell. Idea to
2073 # fix this:
2074 # 1. Call self.unsafe_arith.ParseVarRef() -> BracedVarSub
2075 # 2. Call self.unsafe_arith.GetNameref(bvs_part), and get a value_t
2076 # We still need a ref_trail to detect cycles.
2077 cell, _, _ = self._ResolveNameOrRef(name, which_scopes)
2078 if cell:
2079 return cell.val
2080
2081 return value.Undef
2082
2083 def GetCell(self, name, which_scopes=scope_e.Shopt):
2084 # type: (str, scope_t) -> Cell
2085 """Get both the value and flags.
2086
2087 Usages:
2088 - the 'pp' builtin.
2089 - declare -p
2090 - ${x@a}
2091 - to test of 'TZ' is exported in printf? Why?
2092 """
2093 if which_scopes == scope_e.Shopt:
2094 which_scopes = self.ScopesForReading()
2095
2096 cell, _ = self._ResolveNameOnly(name, which_scopes)
2097 return cell
2098
2099 def Unset(self, lval, which_scopes):
2100 # type: (sh_lvalue_t, scope_t) -> bool
2101 """
2102 Returns:
2103 Whether the cell was found.
2104 """
2105 # TODO: Refactor sh_lvalue type to avoid this
2106 UP_lval = lval
2107
2108 with tagswitch(lval) as case:
2109 if case(sh_lvalue_e.Var): # unset x
2110 lval = cast(LeftName, UP_lval)
2111 var_name = lval.name
2112 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2113 lval = cast(sh_lvalue.Indexed, UP_lval)
2114 var_name = lval.name
2115 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2116 lval = cast(sh_lvalue.Keyed, UP_lval)
2117 var_name = lval.name
2118 else:
2119 raise AssertionError()
2120
2121 if which_scopes == scope_e.Shopt:
2122 which_scopes = self.ScopesForWriting()
2123
2124 cell, name_map, cell_name = self._ResolveNameOrRef(
2125 var_name, which_scopes)
2126 if not cell:
2127 return False # 'unset' builtin falls back on functions
2128 if cell.readonly:
2129 raise error.Runtime("Can't unset readonly variable %r" % var_name)
2130
2131 with tagswitch(lval) as case:
2132 if case(sh_lvalue_e.Var): # unset x
2133 # Make variables in higher scopes visible.
2134 # example: test/spec.sh builtin-vars -r 24 (ble.sh)
2135 mylib.dict_erase(name_map, cell_name)
2136
2137 # alternative that some shells use:
2138 # name_map[cell_name].val = value.Undef
2139 # cell.exported = False
2140
2141 # This should never happen because we do recursive lookups of namerefs.
2142 assert not cell.nameref, cell
2143
2144 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2145 lval = cast(sh_lvalue.Indexed, UP_lval)
2146 # Note: Setting an entry to None and shifting entries are pretty
2147 # much the same in shell.
2148
2149 val = cell.val
2150 UP_val = val
2151 if val.tag() != value_e.BashArray:
2152 raise error.Runtime("%r isn't an array" % var_name)
2153
2154 val = cast(value.BashArray, UP_val)
2155 strs = val.strs
2156
2157 n = len(strs)
2158 last_index = n - 1
2159 index = lval.index
2160 if index < 0:
2161 index += n
2162
2163 if index == last_index:
2164 # Special case: The array SHORTENS if you unset from the end. You
2165 # can tell with a+=(3 4)
2166 strs.pop()
2167 elif 0 <= index and index < last_index:
2168 strs[index] = None
2169 else:
2170 # If it's not found, it's not an error. In other words, 'unset'
2171 # ensures that a value doesn't exist, regardless of whether it
2172 # existed. It's idempotent.
2173 # (Ousterhout specifically argues that the strict behavior was a
2174 # mistake for Tcl!)
2175 pass
2176
2177 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2178 lval = cast(sh_lvalue.Keyed, UP_lval)
2179
2180 val = cell.val
2181 UP_val = val
2182
2183 # note: never happens because of mem.IsBashAssoc test for sh_lvalue.Keyed
2184 #if val.tag() != value_e.BashAssoc:
2185 # raise error.Runtime("%r isn't an associative array" % lval.name)
2186
2187 val = cast(value.BashAssoc, UP_val)
2188 mylib.dict_erase(val.d, lval.key)
2189
2190 else:
2191 raise AssertionError(lval)
2192
2193 return True
2194
2195 def ScopesForReading(self):
2196 # type: () -> scope_t
2197 """Read scope."""
2198 return (scope_e.Dynamic
2199 if self.exec_opts.dynamic_scope() else scope_e.LocalOrGlobal)
2200
2201 def ScopesForWriting(self):
2202 # type: () -> scope_t
2203 """Write scope."""
2204 return (scope_e.Dynamic
2205 if self.exec_opts.dynamic_scope() else scope_e.LocalOnly)
2206
2207 def ClearFlag(self, name, flag):
2208 # type: (str, int) -> bool
2209 """Used for export -n.
2210
2211 We don't use SetValue() because even if rval is None, it will make an
2212 Undef value in a scope.
2213 """
2214 cell, name_map = self._ResolveNameOnly(name, self.ScopesForReading())
2215 if cell:
2216 if flag & ClearExport:
2217 cell.exported = False
2218 if flag & ClearNameref:
2219 cell.nameref = False
2220 return True
2221 else:
2222 return False
2223
2224 def GetExported(self):
2225 # type: () -> Dict[str, str]
2226 """Get all the variables that are marked exported."""
2227 # TODO: This is run on every SimpleCommand. Should we have a dirty flag?
2228 # We have to notice these things:
2229 # - If an exported variable is changed.
2230 # - If the set of exported variables changes.
2231
2232 exported = {} # type: Dict[str, str]
2233 # Search from globals up. Names higher on the stack will overwrite names
2234 # lower on the stack.
2235 for scope in self.var_stack:
2236 for name, cell in iteritems(scope):
2237 # TODO: Disallow exporting at assignment time. If an exported Str is
2238 # changed to BashArray, also clear its 'exported' flag.
2239 if cell.exported and cell.val.tag() == value_e.Str:
2240 val = cast(value.Str, cell.val)
2241 exported[name] = val.s
2242 return exported
2243
2244 def VarNames(self):
2245 # type: () -> List[str]
2246 """For internal OSH completion and compgen -A variable.
2247
2248 NOTE: We could also add $? $$ etc.?
2249 """
2250 ret = [] # type: List[str]
2251 # Look up the stack, yielding all variables. Bash seems to do this.
2252 for scope in self.var_stack:
2253 for name in scope:
2254 ret.append(name)
2255 return ret
2256
2257 def VarNamesStartingWith(self, prefix):
2258 # type: (str) -> List[str]
2259 """For ${!prefix@}"""
2260 # Look up the stack, yielding all variables. Bash seems to do this.
2261 names = [] # type: List[str]
2262 for scope in self.var_stack:
2263 for name in scope:
2264 if name.startswith(prefix):
2265 names.append(name)
2266 return names
2267
2268 def GetAllVars(self):
2269 # type: () -> Dict[str, str]
2270 """Get all variables and their values, for 'set' builtin."""
2271 result = {} # type: Dict[str, str]
2272 for scope in self.var_stack:
2273 for name, cell in iteritems(scope):
2274 # TODO: Show other types?
2275 val = cell.val
2276 if val.tag() == value_e.Str:
2277 str_val = cast(value.Str, val)
2278 result[name] = str_val.s
2279 return result
2280
2281 def GetAllCells(self, which_scopes):
2282 # type: (scope_t) -> Dict[str, Cell]
2283 """Get all variables and their values, for 'set' builtin."""
2284 result = {} # type: Dict[str, Cell]
2285
2286 if which_scopes == scope_e.Dynamic:
2287 scopes = self.var_stack
2288 elif which_scopes == scope_e.LocalOnly:
2289 scopes = self.var_stack[-1:]
2290 elif which_scopes == scope_e.GlobalOnly:
2291 scopes = self.var_stack[0:1]
2292 elif which_scopes == scope_e.LocalOrGlobal:
2293 scopes = [self.var_stack[0]]
2294 if len(self.var_stack) > 1:
2295 scopes.append(self.var_stack[-1])
2296 else:
2297 raise AssertionError()
2298
2299 for scope in scopes:
2300 for name, cell in iteritems(scope):
2301 result[name] = cell
2302 return result
2303
2304 def IsGlobalScope(self):
2305 # type: () -> bool
2306 return len(self.var_stack) == 1
2307
2308 def SetRegexMatch(self, match):
2309 # type: (regex_match_t) -> None
2310 self.regex_match[-1] = match
2311
2312 def GetRegexMatch(self):
2313 # type: () -> regex_match_t
2314 return self.regex_match[-1]
2315
2316 def PushContextStack(self, context):
2317 # type: (Dict[str, value_t]) -> None
2318 self.ctx_stack.append(context)
2319
2320 def GetContext(self):
2321 # type: () -> Optional[Dict[str, value_t]]
2322 if len(self.ctx_stack):
2323 return self.ctx_stack[-1]
2324 return None
2325
2326 def PopContextStack(self):
2327 # type: () -> Dict[str, value_t]
2328 assert self.ctx_stack, "Empty context stack"
2329 return self.ctx_stack.pop()
2330
2331
2332class Procs:
2333
2334 def __init__(self, mem):
2335 # type: (Mem) -> None
2336 self.mem = mem
2337 self.sh_funcs = {} # type: Dict[str, value.Proc]
2338
2339 def SetProc(self, name, proc):
2340 # type: (str, value.Proc) -> None
2341 self.mem.var_stack[0][name] = Cell(False, False, False, proc)
2342
2343 def SetShFunc(self, name, proc):
2344 # type: (str, value.Proc) -> None
2345 self.sh_funcs[name] = proc
2346
2347 def Get(self, name):
2348 # type: (str) -> value.Proc
2349 """Try to find a proc/sh-func by `name`, or return None if not found.
2350
2351 First, we search for a proc, and then a sh-func. This means that procs
2352 can shadow the definition of sh-funcs.
2353 """
2354 vars = self.mem.var_stack[0]
2355 if name in vars:
2356 maybe_proc = vars[name]
2357 if maybe_proc.val.tag() == value_e.Proc:
2358 return cast(value.Proc, maybe_proc.val)
2359
2360 if name in self.sh_funcs:
2361 return self.sh_funcs[name]
2362
2363 return None
2364
2365 def Del(self, to_del):
2366 # type: (str) -> None
2367 """Undefine a sh-func with name `to_del`, if it exists."""
2368 mylib.dict_erase(self.sh_funcs, to_del)
2369
2370 def GetNames(self):
2371 # type: () -> List[str]
2372 """Returns a *sorted* list of all proc names"""
2373 names = list(self.sh_funcs.keys())
2374
2375 vars = self.mem.var_stack[0]
2376 for name in vars:
2377 cell = vars[name]
2378 if cell.val.tag() == value_e.Proc:
2379 names.append(name)
2380
2381 return sorted(names)
2382
2383
2384#
2385# Wrappers to Set Variables
2386#
2387
2388
2389def OshLanguageSetValue(mem, lval, val, flags=0):
2390 # type: (Mem, sh_lvalue_t, value_t, int) -> None
2391 """Like 'setvar' (scope_e.LocalOnly), unless dynamic scope is on.
2392
2393 That is, it respects shopt --unset dynamic_scope.
2394
2395 Used for assignment builtins, (( a = b )), {fd}>out, ${x=}, etc.
2396 """
2397 which_scopes = mem.ScopesForWriting()
2398 mem.SetValue(lval, val, which_scopes, flags=flags)
2399
2400
2401def BuiltinSetValue(mem, lval, val):
2402 # type: (Mem, sh_lvalue_t, value_t) -> None
2403 """Equivalent of x=$y
2404
2405 Called by BuiltinSetString and BuiltinSetArray Used directly by
2406 printf -v because it can mutate an array
2407 """
2408 mem.SetValue(lval, val, mem.ScopesForWriting())
2409
2410
2411def BuiltinSetString(mem, name, s):
2412 # type: (Mem, str, str) -> None
2413 """Set a string by looking up the stack.
2414
2415 Used for 'read', 'getopts', completion builtins, etc.
2416 """
2417 assert isinstance(s, str)
2418 BuiltinSetValue(mem, location.LName(name), value.Str(s))
2419
2420
2421def BuiltinSetArray(mem, name, a):
2422 # type: (Mem, str, List[str]) -> None
2423 """Set an array by looking up the stack.
2424
2425 Used by compadjust, read -a, etc.
2426 """
2427 assert isinstance(a, list)
2428 BuiltinSetValue(mem, location.LName(name), value.BashArray(a))
2429
2430
2431def SetGlobalString(mem, name, s):
2432 # type: (Mem, str, str) -> None
2433 """Helper for completion, etc."""
2434 assert isinstance(s, str)
2435 val = value.Str(s)
2436 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2437
2438
2439def SetGlobalArray(mem, name, a):
2440 # type: (Mem, str, List[str]) -> None
2441 """Used by completion, shell initialization, etc."""
2442 assert isinstance(a, list)
2443 mem.SetNamed(location.LName(name), value.BashArray(a), scope_e.GlobalOnly)
2444
2445
2446def _SetGlobalValue(mem, name, val):
2447 # type: (Mem, str, value_t) -> None
2448 """Helper for completion, etc."""
2449 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2450
2451
2452def ExportGlobalString(mem, name, s):
2453 # type: (Mem, str, str) -> None
2454 """Helper for completion, $PWD, $OLDPWD, etc."""
2455 assert isinstance(s, str)
2456 val = value.Str(s)
2457 mem.SetNamed(location.LName(name),
2458 val,
2459 scope_e.GlobalOnly,
2460 flags=SetExport)
2461
2462
2463#
2464# Wrappers to Get Variables
2465#
2466
2467
2468def DynamicGetVar(mem, name, which_scopes):
2469 # type: (Mem, str, scope_t) -> value_t
2470 """
2471 For getVar() and shvarGet()
2472 """
2473 val = mem.GetValue(name, which_scopes=which_scopes)
2474
2475 # Undef is not a user-visible value!
2476 # There's no way to distinguish null from undefined.
2477 if val.tag() == value_e.Undef:
2478 return value.Null
2479
2480 return val
2481
2482
2483def GetString(mem, name):
2484 # type: (Mem, str) -> str
2485 """Wrapper around GetValue(). Check that HOME, PWD, OLDPWD, etc. are
2486 strings. bash doesn't have these errors because ${array} is ${array[0]}.
2487
2488 TODO: We could also check this when you're storing variables?
2489 """
2490 val = mem.GetValue(name)
2491 UP_val = val
2492 with tagswitch(val) as case:
2493 if case(value_e.Undef):
2494 raise error.Runtime("$%s isn't defined" % name)
2495 elif case(value_e.Str):
2496 return cast(value.Str, UP_val).s
2497 else:
2498 # User would have to 'unset HOME' to get rid of exported flag
2499 raise error.Runtime("$%s should be a string" % name)
2500
2501
2502def MaybeString(mem, name):
2503 # type: (Mem, str) -> Optional[str]
2504 """Like GetString(), but doesn't throw an exception."""
2505 try:
2506 return GetString(mem, name)
2507 except error.Runtime:
2508 return None
2509
2510
2511def GetInteger(mem, name):
2512 # type: (Mem, str) -> int
2513 """For OPTIND variable used in getopts builtin.
2514
2515 TODO: it could be value.Int() ?
2516 """
2517 val = mem.GetValue(name)
2518 if val.tag() != value_e.Str:
2519 raise error.Runtime('$%s should be a string, got %s' %
2520 (name, ui.ValType(val)))
2521 s = cast(value.Str, val).s
2522 try:
2523 i = int(s)
2524 except ValueError:
2525 raise error.Runtime("$%s doesn't look like an integer, got %r" %
2526 (name, s))
2527 return i
2528
2529
2530# vim: sw=4