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

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