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

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