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

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