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

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