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

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