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

2473 lines, 1260 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 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 return {
761 # Easier to serialize value.BashArray than value.List
762 'argv': value.BashArray(self.argv),
763 'num_shifted': num.ToBig(self.num_shifted),
764 }
765
766 def GetArgNum(self, arg_num):
767 # type: (int) -> value_t
768 index = self.num_shifted + arg_num - 1
769 if index >= len(self.argv):
770 return value.Undef
771
772 return value.Str(self.argv[index])
773
774 def GetArgv(self):
775 # type: () -> List[str]
776 return self.argv[self.num_shifted:]
777
778 def GetNumArgs(self):
779 # type: () -> int
780 return len(self.argv) - self.num_shifted
781
782 def SetArgv(self, argv):
783 # type: (List[str]) -> None
784 self.argv = argv
785 self.num_shifted = 0
786
787
788def _DumpVarFrame(frame):
789 # type: (Dict[str, Cell]) -> Dict[str, value_t]
790 """Dump the stack frame as reasonably compact and readable JSON."""
791
792 vars_json = {} # type: Dict[str, value_t]
793 for name, cell in iteritems(frame):
794 cell_json = {} # type: Dict[str, value_t]
795
796 buf = mylib.BufWriter()
797 if cell.exported:
798 buf.write('x')
799 if cell.readonly:
800 buf.write('r')
801 flags = buf.getvalue()
802 if len(flags):
803 cell_json['flags'] = value.Str(flags)
804
805 # TODO:
806 # - Use packle for crash dumps! Then we can represent object cycles
807 # - Right now the JSON serializer will probably crash
808 # - although BashArray and BashAssoc may need 'type' tags
809 # - they don't round trip correctly
810 # - maybe add value.Tombstone here or something?
811 # - value.{Func,Eggex,...} may have value.Tombstone and
812 # vm.ValueIdString()?
813
814 with tagswitch(cell.val) as case:
815 if case(value_e.Undef):
816 cell_json['type'] = value.Str('Undef')
817
818 elif case(value_e.Str):
819 cell_json['type'] = value.Str('Str')
820 cell_json['value'] = cell.val
821
822 elif case(value_e.BashArray):
823 cell_json['type'] = value.Str('BashArray')
824 cell_json['value'] = cell.val
825
826 elif case(value_e.BashAssoc):
827 cell_json['type'] = value.Str('BashAssoc')
828 cell_json['value'] = cell.val
829
830 else:
831 # TODO: should we show the object ID here?
832 pass
833
834 vars_json[name] = value.Dict(cell_json)
835
836 return vars_json
837
838
839def _GetWorkingDir():
840 # type: () -> str
841 """Fallback for pwd and $PWD when there's no 'cd' and no inherited $PWD."""
842 try:
843 return posix.getcwd()
844 except (IOError, OSError) as e:
845 e_die("Can't determine working directory: %s" % pyutil.strerror(e))
846
847
848def _LineNumber(tok):
849 # type: (Optional[Token]) -> str
850 """ For $BASH_LINENO """
851 if tok is None:
852 return '-1'
853 return str(tok.line.line_num)
854
855
856def _AddCallToken(d, token):
857 # type: (Dict[str, value_t], Optional[Token]) -> None
858 if token is None:
859 return
860 d['call_source'] = value.Str(ui.GetLineSourceString(token.line))
861 d['call_line_num'] = num.ToBig(token.line.line_num)
862 d['call_line'] = value.Str(token.line.content)
863
864
865def _InitDefaults(mem):
866 # type: (Mem) -> None
867
868 # Default value; user may unset it.
869 # $ echo -n "$IFS" | python -c 'import sys;print repr(sys.stdin.read())'
870 # ' \t\n'
871 SetGlobalString(mem, 'IFS', split.DEFAULT_IFS)
872
873 # NOTE: Should we put these in a name_map for Oil?
874 SetGlobalString(mem, 'UID', str(posix.getuid()))
875 SetGlobalString(mem, 'EUID', str(posix.geteuid()))
876 SetGlobalString(mem, 'PPID', str(posix.getppid()))
877
878 SetGlobalString(mem, 'HOSTNAME', libc.gethostname())
879
880 # In bash, this looks like 'linux-gnu', 'linux-musl', etc. Scripts test
881 # for 'darwin' and 'freebsd' too. They generally don't like at 'gnu' or
882 # 'musl'. We don't have that info, so just make it 'linux'.
883 SetGlobalString(mem, 'OSTYPE', pyos.OsType())
884
885 # For getopts builtin
886 SetGlobalString(mem, 'OPTIND', '1')
887
888 # When xtrace_rich is off, this is just like '+ ', the shell default
889 SetGlobalString(mem, 'PS4', '${SHX_indent}${SHX_punct}${SHX_pid_str} ')
890
891 # bash-completion uses this. Value copied from bash. It doesn't integrate
892 # with 'readline' yet.
893 SetGlobalString(mem, 'COMP_WORDBREAKS', _READLINE_DELIMS)
894
895 # TODO on $HOME: bash sets it if it's a login shell and not in POSIX mode!
896 # if (login_shell == 1 && posixly_correct == 0)
897 # set_home_var ();
898
899
900def _InitVarsFromEnv(mem, environ):
901 # type: (Mem, Dict[str, str]) -> None
902
903 # This is the way dash and bash work -- at startup, they turn everything in
904 # 'environ' variable into shell variables. Bash has an export_env
905 # variable. Dash has a loop through environ in init.c
906 for n, v in iteritems(environ):
907 mem.SetNamed(location.LName(n),
908 value.Str(v),
909 scope_e.GlobalOnly,
910 flags=SetExport)
911
912 # If it's not in the environment, initialize it. This makes it easier to
913 # update later in MutableOpts.
914
915 # TODO: IFS, etc. should follow this pattern. Maybe need a SysCall
916 # interface? self.syscall.getcwd() etc.
917
918 val = mem.GetValue('SHELLOPTS')
919 if val.tag() == value_e.Undef:
920 SetGlobalString(mem, 'SHELLOPTS', '')
921 # Now make it readonly
922 mem.SetNamed(location.LName('SHELLOPTS'),
923 None,
924 scope_e.GlobalOnly,
925 flags=SetReadOnly)
926
927 # Usually we inherit PWD from the parent shell. When it's not set, we may
928 # compute it.
929 val = mem.GetValue('PWD')
930 if val.tag() == value_e.Undef:
931 SetGlobalString(mem, 'PWD', _GetWorkingDir())
932 # Now mark it exported, no matter what. This is one of few variables
933 # EXPORTED. bash and dash both do it. (e.g. env -i -- dash -c env)
934 mem.SetNamed(location.LName('PWD'),
935 None,
936 scope_e.GlobalOnly,
937 flags=SetExport)
938
939 val = mem.GetValue('PATH')
940 if val.tag() == value_e.Undef:
941 # Setting PATH to these two dirs match what zsh and mksh do. bash and dash
942 # add {,/usr/,/usr/local}/{bin,sbin}
943 SetGlobalString(mem, 'PATH', '/bin:/usr/bin')
944
945
946def InitMem(mem, environ, version_str):
947 # type: (Mem, Dict[str, str], str) -> None
948 """Initialize memory with shell defaults.
949
950 Other interpreters could have different builtin variables.
951 """
952 # TODO: REMOVE this legacy. ble.sh checks it!
953 SetGlobalString(mem, 'OIL_VERSION', version_str)
954
955 SetGlobalString(mem, 'OILS_VERSION', version_str)
956 _InitDefaults(mem)
957 _InitVarsFromEnv(mem, environ)
958
959 # MUTABLE GLOBAL that's SEPARATE from $PWD. Used by the 'pwd' builtin, but
960 # it can't be modified by users.
961 val = mem.GetValue('PWD')
962 # should be true since it's exported
963 assert val.tag() == value_e.Str, val
964 pwd = cast(value.Str, val).s
965 mem.SetPwd(pwd)
966
967
968def InitInteractive(mem):
969 # type: (Mem) -> None
970 """Initialization that's only done in the interactive/headless shell."""
971
972 # Same default PS1 as bash
973 if mem.GetValue('PS1').tag() == value_e.Undef:
974 SetGlobalString(mem, 'PS1', r'\s-\v\$ ')
975
976
977class ctx_FuncCall(object):
978 """For func calls."""
979
980 def __init__(self, mem, func):
981 # type: (Mem, value.Func) -> None
982
983 frame = NewDict() # type: Dict[str, Cell]
984 mem.var_stack.append(frame)
985
986 mem.PushCall(func.name, func.parsed.name)
987 self.mem = mem
988
989 def __enter__(self):
990 # type: () -> None
991 pass
992
993 def __exit__(self, type, value, traceback):
994 # type: (Any, Any, Any) -> None
995 self.mem.PopCall()
996 self.mem.var_stack.pop()
997
998
999class ctx_ProcCall(object):
1000 """For proc calls, including shell functions."""
1001
1002 def __init__(self, mem, mutable_opts, proc, argv):
1003 # type: (Mem, MutableOpts, value.Proc, List[str]) -> None
1004
1005 # TODO:
1006 # - argv stack shouldn't be used for procs
1007 # - we can bind a real variable @A if we want
1008 # - procs should be in the var namespace
1009 #
1010 # should we separate procs and shell functions?
1011 # - dynamic scope is one difference
1012 # - '$@" shift etc. are another difference
1013
1014 frame = NewDict() # type: Dict[str, Cell]
1015
1016 assert argv is not None
1017 if proc.sh_compat:
1018 # shell function
1019 mem.argv_stack.append(_ArgFrame(argv))
1020 else:
1021 # procs
1022 # - open: is equivalent to ...ARGV
1023 # - closed: ARGV is empty list
1024 frame['ARGV'] = _MakeArgvCell(argv)
1025
1026 mem.var_stack.append(frame)
1027
1028 mem.PushCall(proc.name, proc.name_tok)
1029
1030 # Dynamic scope is only for shell functions
1031 mutable_opts.PushDynamicScope(proc.sh_compat)
1032
1033 # It may have been disabled with ctx_ErrExit for 'if echo $(false)', but
1034 # 'if p' should be allowed.
1035 self.mem = mem
1036 self.mutable_opts = mutable_opts
1037 self.sh_compat = proc.sh_compat
1038
1039 def __enter__(self):
1040 # type: () -> None
1041 pass
1042
1043 def __exit__(self, type, value, traceback):
1044 # type: (Any, Any, Any) -> None
1045 self.mutable_opts.PopDynamicScope()
1046 self.mem.PopCall()
1047 self.mem.var_stack.pop()
1048
1049 if self.sh_compat:
1050 self.mem.argv_stack.pop()
1051
1052
1053class ctx_Temp(object):
1054 """For FOO=bar myfunc, etc."""
1055
1056 def __init__(self, mem):
1057 # type: (Mem) -> None
1058 mem.PushTemp()
1059 self.mem = mem
1060
1061 def __enter__(self):
1062 # type: () -> None
1063 pass
1064
1065 def __exit__(self, type, value, traceback):
1066 # type: (Any, Any, Any) -> None
1067 self.mem.PopTemp()
1068
1069
1070class ctx_Registers(object):
1071 """For $PS1, $PS4, $PROMPT_COMMAND, traps, and headless EVAL.
1072
1073 This is tightly coupled to state.Mem, so it's not in builtin/pure_ysh.
1074 """
1075
1076 def __init__(self, mem):
1077 # type: (Mem) -> None
1078
1079 # Because some prompts rely on the status leaking. See issue #853.
1080 # PS1 also does.
1081 last = mem.last_status[-1]
1082 mem.last_status.append(last)
1083 mem.try_status.append(0)
1084 mem.try_error.append(value.Dict({}))
1085
1086 # TODO: We should also copy these values! Turn the whole thing into a
1087 # frame.
1088 mem.pipe_status.append([])
1089 mem.process_sub_status.append([])
1090
1091 mem.regex_match.append(regex_match.No)
1092
1093 self.mem = mem
1094
1095 def __enter__(self):
1096 # type: () -> None
1097 pass
1098
1099 def __exit__(self, type, value, traceback):
1100 # type: (Any, Any, Any) -> None
1101 self.mem.regex_match.pop()
1102
1103 self.mem.process_sub_status.pop()
1104 self.mem.pipe_status.pop()
1105
1106 self.mem.try_error.pop()
1107 self.mem.try_status.pop()
1108 self.mem.last_status.pop()
1109
1110
1111class ctx_ThisDir(object):
1112 """For $_this_dir."""
1113
1114 def __init__(self, mem, filename):
1115 # type: (Mem, Optional[str]) -> None
1116 self.do_pop = False
1117 if filename is not None: # script_name in main() may be -c, etc.
1118 d = os_path.dirname(os_path.abspath(filename))
1119 mem.this_dir.append(d)
1120 self.do_pop = True
1121
1122 self.mem = mem
1123
1124 def __enter__(self):
1125 # type: () -> None
1126 pass
1127
1128 def __exit__(self, type, value, traceback):
1129 # type: (Any, Any, Any) -> None
1130 if self.do_pop:
1131 self.mem.this_dir.pop()
1132
1133
1134def _MakeArgvCell(argv):
1135 # type: (List[str]) -> Cell
1136 items = [value.Str(a) for a in argv] # type: List[value_t]
1137 return Cell(False, False, False, value.List(items))
1138
1139
1140class Mem(object):
1141 """For storing variables.
1142
1143 Callers:
1144 User code: assigning and evaluating variables, in command context or
1145 arithmetic context.
1146 Completion engine: for COMP_WORDS, etc.
1147 Builtins call it implicitly: read, cd for $PWD, $OLDPWD, etc.
1148
1149 Modules: cmd_eval, word_eval, expr_eval, completion
1150 """
1151
1152 def __init__(self, dollar0, argv, arena, debug_stack):
1153 # type: (str, List[str], alloc.Arena, List[debug_frame_t]) -> None
1154 """
1155 Args:
1156 arena: currently unused
1157 """
1158 # circular dep initialized out of line
1159 self.exec_opts = None # type: optview.Exec
1160 self.unsafe_arith = None # type: sh_expr_eval.UnsafeArith
1161
1162 self.dollar0 = dollar0
1163 # If you only use YSH procs and funcs, this will remain at length 1.
1164 self.argv_stack = [_ArgFrame(argv)]
1165
1166 frame = NewDict() # type: Dict[str, Cell]
1167
1168 frame['ARGV'] = _MakeArgvCell(argv)
1169
1170 self.var_stack = [frame]
1171
1172 # The debug_stack isn't strictly necessary for execution. We use it
1173 # for crash dumps and for 3 parallel arrays: BASH_SOURCE, FUNCNAME, and
1174 # BASH_LINENO.
1175 self.debug_stack = debug_stack
1176
1177 self.pwd = None # type: Optional[str]
1178 self.seconds_start = time_.time()
1179
1180 self.token_for_line = None # type: Optional[Token]
1181 self.loc_for_expr = loc.Missing # type: loc_t
1182
1183 self.last_arg = '' # $_ is initially empty, NOT unset
1184 self.line_num = value.Str('')
1185
1186 # Done ONCE on initialization
1187 self.root_pid = posix.getpid()
1188
1189 # TODO:
1190 # - These are REGISTERS mutated by user code.
1191 # - Call it self.reg_stack? with ctx_Registers
1192 # - push-registers builtin
1193 self.last_status = [0] # type: List[int] # a stack
1194 self.try_status = [0] # type: List[int] # a stack
1195 self.try_error = [value.Dict({})] # type: List[value.Dict] # a stack
1196 self.pipe_status = [[]] # type: List[List[int]] # stack
1197 self.process_sub_status = [[]] # type: List[List[int]] # stack
1198
1199 # A stack but NOT a register?
1200 self.this_dir = [] # type: List[str]
1201 self.regex_match = [regex_match.No] # type: List[regex_match_t]
1202
1203 self.last_bg_pid = -1 # Uninitialized value mutable public variable
1204
1205 self.running_debug_trap = False # set by ctx_DebugTrap()
1206 self.running_err_trap = False # set by ctx_ErrTrap
1207 self.is_main = True # we start out in main
1208
1209 # For the ctx builtin
1210 self.ctx_stack = [] # type: List[Dict[str, value_t]]
1211
1212 def __repr__(self):
1213 # type: () -> str
1214 parts = [] # type: List[str]
1215 parts.append('<Mem')
1216 for i, frame in enumerate(self.var_stack):
1217 parts.append(' -- %d --' % i)
1218 for n, v in frame.iteritems():
1219 parts.append(' %s %s' % (n, v))
1220 parts.append('>')
1221 return '\n'.join(parts) + '\n'
1222
1223 def SetPwd(self, pwd):
1224 # type: (str) -> None
1225 """Used by builtins."""
1226 self.pwd = pwd
1227
1228 def ParsingChangesAllowed(self):
1229 # type: () -> bool
1230 """For checking that syntax options are only used at the top level."""
1231
1232 # DISALLOW proc calls : they push argv_stack, var_stack, debug_stack
1233 # ALLOW source foo.sh arg1: pushes argv_stack, debug_stack
1234 # ALLOW FOO=bar : pushes var_stack
1235 return len(self.var_stack) == 1 or len(self.argv_stack) == 1
1236
1237 def Dump(self):
1238 # type: () -> Tuple[List[value_t], List[value_t], List[value_t]]
1239 """Copy state before unwinding the stack."""
1240 var_stack = [
1241 value.Dict(_DumpVarFrame(frame)) for frame in self.var_stack
1242 ] # type: List[value_t]
1243 argv_stack = [value.Dict(frame.Dump())
1244 for frame in self.argv_stack] # type: List[value_t]
1245
1246 debug_stack = [] # type: List[value_t]
1247
1248 # Reuse these immutable objects
1249 t_call = value.Str('Call')
1250 t_source = value.Str('Source')
1251 t_main = value.Str('Main')
1252
1253 for frame in reversed(self.debug_stack):
1254 UP_frame = frame
1255 with tagswitch(frame) as case:
1256 if case(debug_frame_e.Call):
1257 frame = cast(debug_frame.Call, UP_frame)
1258 d = {
1259 'type': t_call,
1260 'func_name': value.Str(frame.func_name)
1261 } # type: Dict[str, value_t]
1262
1263 _AddCallToken(d, frame.call_tok)
1264 # TODO: Add def_tok
1265
1266 elif case(debug_frame_e.Source):
1267 frame = cast(debug_frame.Source, UP_frame)
1268 d = {
1269 'type': t_source,
1270 'source_name': value.Str(frame.source_name)
1271 }
1272 _AddCallToken(d, frame.call_tok)
1273
1274 elif case(debug_frame_e.Main):
1275 frame = cast(debug_frame.Main, UP_frame)
1276 d = {'type': t_main, 'dollar0': value.Str(frame.dollar0)}
1277
1278 debug_stack.append(value.Dict(d))
1279 return var_stack, argv_stack, debug_stack
1280
1281 def SetLastArgument(self, s):
1282 # type: (str) -> None
1283 """For $_"""
1284 self.last_arg = s
1285
1286 def SetTokenForLine(self, tok):
1287 # type: (Token) -> None
1288 """Set a token to compute $LINENO
1289
1290 This means it should be set on SimpleCommand, ShAssignment, ((, [[,
1291 case, etc. -- anything that evaluates a word. Example: there was a bug
1292 with 'case $LINENO'
1293
1294 This token also used as a "least-specific" / fallback location for
1295 errors in ExecuteAndCatch().
1296
1297 Although most of that should be taken over by 'with ui.ctx_Location()`,
1298 for the errfmt.
1299 """
1300 if self.running_debug_trap or self.running_err_trap:
1301 return
1302
1303 #if tok.span_id == runtime.NO_SPID:
1304 # NOTE: This happened in the osh-runtime benchmark for yash.
1305 #log('Warning: span_id undefined in SetTokenForLine')
1306
1307 #import traceback
1308 #traceback.print_stack()
1309 #return
1310
1311 self.token_for_line = tok
1312
1313 def SetLocationForExpr(self, blame_loc):
1314 # type: (loc_t) -> None
1315 """
1316 A more specific fallback location, like the $[ in
1317
1318 echo $[len(42)]
1319 """
1320 self.loc_for_expr = blame_loc
1321
1322 def GetFallbackLocation(self):
1323 # type: () -> loc_t
1324
1325 if self.loc_for_expr != loc.Missing: # more specific
1326 return self.loc_for_expr
1327
1328 if self.token_for_line: # less specific
1329 return self.token_for_line
1330
1331 return loc.Missing
1332
1333 #
1334 # Status Variable Stack (for isolating $PS1 and $PS4)
1335 #
1336
1337 def LastStatus(self):
1338 # type: () -> int
1339 return self.last_status[-1]
1340
1341 def TryStatus(self):
1342 # type: () -> int
1343 return self.try_status[-1]
1344
1345 def TryError(self):
1346 # type: () -> value.Dict
1347 return self.try_error[-1]
1348
1349 def PipeStatus(self):
1350 # type: () -> List[int]
1351 return self.pipe_status[-1]
1352
1353 def SetLastStatus(self, x):
1354 # type: (int) -> None
1355 self.last_status[-1] = x
1356
1357 def SetTryStatus(self, x):
1358 # type: (int) -> None
1359 self.try_status[-1] = x
1360
1361 def SetTryError(self, x):
1362 # type: (value.Dict) -> None
1363 self.try_error[-1] = x
1364
1365 def SetPipeStatus(self, x):
1366 # type: (List[int]) -> None
1367 self.pipe_status[-1] = x
1368
1369 def SetSimplePipeStatus(self, status):
1370 # type: (int) -> None
1371
1372 # Optimization to avoid allocations
1373 top = self.pipe_status[-1]
1374 if len(top) == 1:
1375 top[0] = status
1376 else:
1377 self.pipe_status[-1] = [status]
1378
1379 def SetProcessSubStatus(self, x):
1380 # type: (List[int]) -> None
1381 self.process_sub_status[-1] = x
1382
1383 #
1384 # Call Stack
1385 #
1386
1387 def PushCall(self, func_name, def_tok):
1388 # type: (str, Token) -> None
1389 """Push argv, var, and debug stack frames.
1390
1391 Currently used for proc and func calls. TODO: New func evaluator may
1392 not use it.
1393
1394 Args:
1395 def_tok: Token where proc or func was defined, used to compute
1396 BASH_SOURCE.
1397 """
1398 # self.token_for_line can be None?
1399 self.debug_stack.append(
1400 debug_frame.Call(self.token_for_line, def_tok, func_name))
1401
1402 def PopCall(self):
1403 # type: () -> None
1404 """
1405 Args:
1406 should_pop_argv_stack: Pass False if PushCall was given None for argv
1407 True for proc, False for func
1408 """
1409 self.debug_stack.pop()
1410
1411 def ShouldRunDebugTrap(self):
1412 # type: () -> bool
1413
1414 # TODO: RunLastPart of pipeline can disable this
1415
1416 # Don't recursively run DEBUG trap
1417 if self.running_debug_trap:
1418 return False
1419
1420 # Don't run it inside functions
1421 if len(self.var_stack) > 1:
1422 return False
1423
1424 return True
1425
1426 def InsideFunction(self):
1427 # type: () -> bool
1428 """For the ERR trap"""
1429
1430 # Don't run it inside functions
1431 return len(self.var_stack) > 1
1432
1433 def PushSource(self, source_name, argv):
1434 # type: (str, List[str]) -> None
1435 """ For 'source foo.sh 1 2 3' """
1436 if len(argv):
1437 self.argv_stack.append(_ArgFrame(argv))
1438
1439 # self.token_for_line can be None?
1440 self.debug_stack.append(
1441 debug_frame.Source(self.token_for_line, source_name))
1442
1443 def PopSource(self, argv):
1444 # type: (List[str]) -> None
1445 self.debug_stack.pop()
1446
1447 if len(argv):
1448 self.argv_stack.pop()
1449
1450 def PushTemp(self):
1451 # type: () -> None
1452 """For the temporary scope in 'FOO=bar BAR=baz echo'.
1453
1454 Also for PS4 evaluation with more variables.
1455 """
1456 # We don't want the 'read' builtin to write to this frame!
1457 frame = NewDict() # type: Dict[str, Cell]
1458 self.var_stack.append(frame)
1459
1460 def PopTemp(self):
1461 # type: () -> None
1462 self.var_stack.pop()
1463
1464 def TopNamespace(self):
1465 # type: () -> Dict[str, Cell]
1466 """For eval_to_dict()."""
1467 return self.var_stack[-1]
1468
1469 #
1470 # Argv
1471 #
1472
1473 def Shift(self, n):
1474 # type: (int) -> int
1475 frame = self.argv_stack[-1]
1476 num_args = len(frame.argv)
1477
1478 if (frame.num_shifted + n) <= num_args:
1479 frame.num_shifted += n
1480 return 0 # success
1481 else:
1482 return 1 # silent error
1483
1484 def GetArg0(self):
1485 # type: () -> value.Str
1486 """Like GetArgNum(0) but with a more specific type."""
1487 return value.Str(self.dollar0)
1488
1489 def GetArgNum(self, arg_num):
1490 # type: (int) -> value_t
1491 if arg_num == 0:
1492 # $0 may be overriden, eg. by Str => replace()
1493 vars = self.var_stack[-1]
1494 if "0" in vars and vars["0"].val.tag() != value_e.Undef:
1495 return vars["0"].val
1496 return value.Str(self.dollar0)
1497
1498 return self.argv_stack[-1].GetArgNum(arg_num)
1499
1500 def GetArgv(self):
1501 # type: () -> List[str]
1502 """For $* and $@."""
1503 return self.argv_stack[-1].GetArgv()
1504
1505 def SetArgv(self, argv):
1506 # type: (List[str]) -> None
1507 """For set -- 1 2 3."""
1508 # from set -- 1 2 3
1509 self.argv_stack[-1].SetArgv(argv)
1510
1511 #
1512 # Special Vars
1513 #
1514
1515 def GetSpecialVar(self, op_id):
1516 # type: (int) -> value_t
1517 if op_id == Id.VSub_Bang: # $!
1518 n = self.last_bg_pid
1519 if n == -1:
1520 return value.Undef # could be an error
1521
1522 elif op_id == Id.VSub_QMark: # $?
1523 # External commands need WIFEXITED test. What about subshells?
1524 n = self.last_status[-1]
1525
1526 elif op_id == Id.VSub_Pound: # $#
1527 n = self.argv_stack[-1].GetNumArgs()
1528
1529 elif op_id == Id.VSub_Dollar: # $$
1530 n = self.root_pid
1531
1532 else:
1533 raise NotImplementedError(op_id)
1534
1535 return value.Str(str(n))
1536
1537 #
1538 # Named Vars
1539 #
1540
1541 def _ResolveNameOnly(self, name, which_scopes):
1542 # type: (str, scope_t) -> Tuple[Optional[Cell], Dict[str, Cell]]
1543 """Helper for getting and setting variable.
1544
1545 Returns:
1546 cell: The cell corresponding to looking up 'name' with the given mode, or
1547 None if it's not found.
1548 name_map: The name_map it should be set to or deleted from.
1549 """
1550 if which_scopes == scope_e.Dynamic:
1551 for i in xrange(len(self.var_stack) - 1, -1, -1):
1552 name_map = self.var_stack[i]
1553 if name in name_map:
1554 cell = name_map[name]
1555 return cell, name_map
1556 no_cell = None # type: Optional[Cell]
1557 return no_cell, self.var_stack[0] # set in global name_map
1558
1559 if which_scopes == scope_e.LocalOnly:
1560 name_map = self.var_stack[-1]
1561 return name_map.get(name), name_map
1562
1563 if which_scopes == scope_e.GlobalOnly:
1564 name_map = self.var_stack[0]
1565 return name_map.get(name), name_map
1566
1567 if which_scopes == scope_e.LocalOrGlobal:
1568 # Local
1569 name_map = self.var_stack[-1]
1570 cell = name_map.get(name)
1571 if cell:
1572 return cell, name_map
1573
1574 # Global
1575 name_map = self.var_stack[0]
1576 return name_map.get(name), name_map
1577
1578 raise AssertionError()
1579
1580 def _ResolveNameOrRef(
1581 self,
1582 name, # type: str
1583 which_scopes, # type: scope_t
1584 ref_trail=None, # type: Optional[List[str]]
1585 ):
1586 # type: (...) -> Tuple[Optional[Cell], Dict[str, Cell], str]
1587 """Look up a cell and namespace, but respect the nameref flag.
1588
1589 Resolving namerefs does RECURSIVE calls.
1590 """
1591 cell, name_map = self._ResolveNameOnly(name, which_scopes)
1592
1593 if cell is None or not cell.nameref:
1594 return cell, name_map, name # not a nameref
1595
1596 val = cell.val
1597 UP_val = val
1598 with tagswitch(val) as case:
1599 if case(value_e.Undef):
1600 # This is 'local -n undef_ref', which is kind of useless, because the
1601 # more common idiom is 'local -n ref=$1'. Note that you can mutate
1602 # references themselves with local -n ref=new.
1603 if self.exec_opts.strict_nameref():
1604 e_die('nameref %r is undefined' % name)
1605 else:
1606 return cell, name_map, name # fallback
1607
1608 elif case(value_e.Str):
1609 val = cast(value.Str, UP_val)
1610 new_name = val.s
1611
1612 else:
1613 # SetValue() protects the invariant that nameref is Undef or Str
1614 raise AssertionError(val.tag())
1615
1616 # TODO: Respect eval_unsafe_arith here (issue 881). See how it's done in
1617 # 'printf -v' with MakeArithParser
1618 if not match.IsValidVarName(new_name):
1619 # e.g. '#' or '1' or ''
1620 if self.exec_opts.strict_nameref():
1621 e_die('nameref %r contains invalid variable name %r' %
1622 (name, new_name))
1623 else:
1624 # Bash has this odd behavior of clearing the nameref bit when
1625 # ref=#invalid#. strict_nameref avoids it.
1626 cell.nameref = False
1627 return cell, name_map, name # fallback
1628
1629 # Check for circular namerefs.
1630 if ref_trail is None:
1631 ref_trail = [name]
1632 else:
1633 if new_name in ref_trail:
1634 e_die('Circular nameref %s' % ' -> '.join(ref_trail))
1635 ref_trail.append(new_name)
1636
1637 # 'declare -n' uses dynamic scope.
1638 cell, name_map, cell_name = self._ResolveNameOrRef(new_name,
1639 scope_e.Dynamic,
1640 ref_trail=ref_trail)
1641 return cell, name_map, cell_name
1642
1643 def IsBashAssoc(self, name):
1644 # type: (str) -> bool
1645 """Returns whether a name resolve to a cell with an associative array.
1646
1647 We need to know this to evaluate the index expression properly
1648 -- should it be coerced to an integer or not?
1649 """
1650 cell, _, _ = self._ResolveNameOrRef(name, self.ScopesForReading())
1651 # foo=([key]=value)
1652 return cell is not None and cell.val.tag() == value_e.BashAssoc
1653
1654 def SetPlace(self, place, val, blame_loc):
1655 # type: (value.Place, value_t, loc_t) -> None
1656
1657 yval = place.lval
1658 UP_yval = yval
1659 with tagswitch(yval) as case:
1660 if case(y_lvalue_e.Local):
1661 yval = cast(LeftName, UP_yval)
1662
1663 # Check that the frame is still alive
1664 found = False
1665 for i in xrange(len(self.var_stack) - 1, -1, -1):
1666 frame = self.var_stack[i]
1667 if frame is place.frame:
1668 found = True
1669 #log('FOUND %s', found)
1670 break
1671 if not found:
1672 e_die(
1673 "Can't assign to place that's no longer on the call stack.",
1674 blame_loc)
1675
1676 cell = frame.get(yval.name)
1677 if cell is None:
1678 cell = Cell(False, False, False, val)
1679 frame[yval.name] = cell
1680 else:
1681 cell.val = val
1682
1683 elif case(y_lvalue_e.Container):
1684 e_die('Container place not implemented', blame_loc)
1685
1686 else:
1687 raise AssertionError()
1688
1689 def SetLocalName(self, lval, val):
1690 # type: (LeftName, value_t) -> None
1691
1692 # Equivalent to
1693 # self._ResolveNameOnly(lval.name, scope_e.LocalOnly)
1694 name_map = self.var_stack[-1]
1695 cell = name_map.get(lval.name)
1696
1697 if cell:
1698 if cell.readonly:
1699 e_die("Can't assign to readonly value %r" % lval.name,
1700 lval.blame_loc)
1701 cell.val = val # Mutate value_t
1702 else:
1703 cell = Cell(False, False, False, val)
1704 name_map[lval.name] = cell
1705
1706 def SetNamed(self, lval, val, which_scopes, flags=0):
1707 # type: (LeftName, value_t, scope_t, int) -> None
1708
1709 if flags & SetNameref or flags & ClearNameref:
1710 # declare -n ref=x # refers to the ref itself
1711 cell, name_map = self._ResolveNameOnly(lval.name, which_scopes)
1712 cell_name = lval.name
1713 else:
1714 # ref=x # mutates THROUGH the reference
1715
1716 # Note on how to implement declare -n ref='a[42]'
1717 # 1. Call _ResolveNameOnly()
1718 # 2. If cell.nameref, call self.unsafe_arith.ParseVarRef() ->
1719 # BracedVarSub
1720 # 3. Turn BracedVarSub into an sh_lvalue, and call
1721 # self.unsafe_arith.SetValue() wrapper with ref_trail
1722 cell, name_map, cell_name = self._ResolveNameOrRef(
1723 lval.name, which_scopes)
1724
1725 if cell:
1726 # Clear before checking readonly bit.
1727 # NOTE: Could be cell.flags &= flag_clear_mask
1728 if flags & ClearExport:
1729 cell.exported = False
1730 if flags & ClearReadOnly:
1731 cell.readonly = False
1732 if flags & ClearNameref:
1733 cell.nameref = False
1734
1735 if val is not None: # e.g. declare -rx existing
1736 # Note: this DYNAMIC check means we can't have 'const' in a loop.
1737 # But that's true for 'readonly' too, and hoisting it makes more
1738 # sense anyway.
1739 if cell.readonly:
1740 e_die("Can't assign to readonly value %r" % lval.name,
1741 lval.blame_loc)
1742 cell.val = val # CHANGE VAL
1743
1744 # NOTE: Could be cell.flags |= flag_set_mask
1745 if flags & SetExport:
1746 cell.exported = True
1747 if flags & SetReadOnly:
1748 cell.readonly = True
1749 if flags & SetNameref:
1750 cell.nameref = True
1751
1752 else:
1753 if val is None: # declare -rx nonexistent
1754 # set -o nounset; local foo; echo $foo # It's still undefined!
1755 val = value.Undef # export foo, readonly foo
1756
1757 cell = Cell(bool(flags & SetExport), bool(flags & SetReadOnly),
1758 bool(flags & SetNameref), val)
1759 name_map[cell_name] = cell
1760
1761 # Maintain invariant that only strings and undefined cells can be
1762 # exported.
1763 assert cell.val is not None, cell
1764
1765 if cell.val.tag() not in (value_e.Undef, value_e.Str):
1766 if cell.exported:
1767 if self.exec_opts.strict_array():
1768 e_die("Only strings can be exported (strict_array)",
1769 lval.blame_loc)
1770 if cell.nameref:
1771 e_die("nameref must be a string", lval.blame_loc)
1772
1773 def SetValue(self, lval, val, which_scopes, flags=0):
1774 # type: (sh_lvalue_t, value_t, scope_t, int) -> None
1775 """
1776 Args:
1777 lval: sh_lvalue
1778 val: value, or None if only changing flags
1779 which_scopes:
1780 Local | Global | Dynamic - for builtins, PWD, etc.
1781 flags: packed pair (keyword_id, bit mask of set/clear flags)
1782
1783 Note: in bash, PWD=/ changes the directory. But not in dash.
1784 """
1785 # STRICTNESS / SANENESS:
1786 #
1787 # 1) Don't create arrays automatically, e.g. a[1000]=x
1788 # 2) Never change types? yeah I think that's a good idea, at least for oil
1789 # (not sh, for compatibility). set -o strict_types or something. That
1790 # means arrays have to be initialized with let arr = [], which is fine.
1791 # This helps with stuff like IFS. It starts off as a string, and assigning
1792 # it to a list is an error. I guess you will have to turn this no for
1793 # bash?
1794 #
1795 # TODO:
1796 # - COMPUTED vars can't be set
1797 # - What about PWD / OLDPWD / UID / EUID ? You can simply make them
1798 # readonly.
1799 # - Maybe PARSE $PS1 and $PS4 when they're set, to avoid the error on use?
1800 # - Other validity: $HOME could be checked for existence
1801
1802 UP_lval = lval
1803 with tagswitch(lval) as case:
1804 if case(sh_lvalue_e.Var):
1805 lval = cast(LeftName, UP_lval)
1806
1807 self.SetNamed(lval, val, which_scopes, flags=flags)
1808
1809 elif case(sh_lvalue_e.Indexed):
1810 lval = cast(sh_lvalue.Indexed, UP_lval)
1811
1812 # There is no syntax 'declare a[x]'
1813 assert val is not None, val
1814
1815 # TODO: relax this for Oil
1816 assert val.tag() == value_e.Str, val
1817 rval = cast(value.Str, val)
1818
1819 # Note: location could be a[x]=1 or (( a[ x ] = 1 ))
1820 left_loc = lval.blame_loc
1821
1822 # bash/mksh have annoying behavior of letting you do LHS assignment to
1823 # Undef, which then turns into an INDEXED array. (Undef means that set
1824 # -o nounset fails.)
1825 cell, name_map, _ = self._ResolveNameOrRef(
1826 lval.name, which_scopes)
1827 if not cell:
1828 self._BindNewArrayWithEntry(name_map, lval, rval, flags)
1829 return
1830
1831 if cell.readonly:
1832 e_die("Can't assign to readonly array", left_loc)
1833
1834 UP_cell_val = cell.val
1835 # undef[0]=y is allowed
1836 with tagswitch(UP_cell_val) as case2:
1837 if case2(value_e.Undef):
1838 self._BindNewArrayWithEntry(name_map, lval, rval,
1839 flags)
1840 return
1841
1842 elif case2(value_e.Str):
1843 # s=x
1844 # s[1]=y # invalid
1845 e_die("Can't assign to items in a string", left_loc)
1846
1847 elif case2(value_e.BashArray):
1848 cell_val = cast(value.BashArray, UP_cell_val)
1849 strs = cell_val.strs
1850
1851 n = len(strs)
1852 index = lval.index
1853 if index < 0: # a[-1]++ computes this twice; could we avoid it?
1854 index += n
1855
1856 if 0 <= index and index < n:
1857 strs[index] = rval.s
1858 else:
1859 # Fill it in with None. It could look like this:
1860 # ['1', 2, 3, None, None, '4', None]
1861 # Then ${#a[@]} counts the entries that are not None.
1862 #
1863 # TODO: strict_array for Oil arrays won't auto-fill.
1864 n = index - len(strs) + 1
1865 for i in xrange(n):
1866 strs.append(None)
1867 strs[lval.index] = rval.s
1868 return
1869
1870 # This could be an object, eggex object, etc. It won't be
1871 # BashAssoc shouldn because we query IsBashAssoc before evaluating
1872 # sh_lhs. Could conslidate with s[i] case above
1873 e_die(
1874 "Value of type %s can't be indexed" % ui.ValType(cell.val),
1875 left_loc)
1876
1877 elif case(sh_lvalue_e.Keyed):
1878 lval = cast(sh_lvalue.Keyed, UP_lval)
1879
1880 # There is no syntax 'declare A["x"]'
1881 assert val is not None, val
1882 assert val.tag() == value_e.Str, val
1883 rval = cast(value.Str, val)
1884
1885 left_loc = lval.blame_loc
1886
1887 cell, name_map, _ = self._ResolveNameOrRef(
1888 lval.name, which_scopes)
1889 if cell.readonly:
1890 e_die("Can't assign to readonly associative array",
1891 left_loc)
1892
1893 # We already looked it up before making the sh_lvalue
1894 assert cell.val.tag() == value_e.BashAssoc, cell
1895 cell_val2 = cast(value.BashAssoc, cell.val)
1896
1897 cell_val2.d[lval.key] = rval.s
1898
1899 else:
1900 raise AssertionError(lval.tag())
1901
1902 def _BindNewArrayWithEntry(self, name_map, lval, val, flags):
1903 # type: (Dict[str, Cell], sh_lvalue.Indexed, value.Str, int) -> None
1904 """Fill 'name_map' with a new indexed array entry."""
1905 no_str = None # type: Optional[str]
1906 items = [no_str] * lval.index
1907 items.append(val.s)
1908 new_value = value.BashArray(items)
1909
1910 # arrays can't be exported; can't have BashAssoc flag
1911 readonly = bool(flags & SetReadOnly)
1912 name_map[lval.name] = Cell(False, readonly, False, new_value)
1913
1914 def InternalSetGlobal(self, name, new_val):
1915 # type: (str, value_t) -> None
1916 """For setting read-only globals internally.
1917
1918 Args:
1919 name: string (not Lhs)
1920 new_val: value
1921
1922 The variable must already exist.
1923
1924 Use case: SHELLOPTS.
1925 """
1926 cell = self.var_stack[0][name]
1927 cell.val = new_val
1928
1929 def GetValue(self, name, which_scopes=scope_e.Shopt):
1930 # type: (str, scope_t) -> value_t
1931 """Used by the WordEvaluator, ArithEvaluator, ExprEvaluator, etc."""
1932 assert isinstance(name, str), name
1933
1934 if which_scopes == scope_e.Shopt:
1935 which_scopes = self.ScopesForReading()
1936
1937 with str_switch(name) as case:
1938 # "Registers"
1939 if case('_status'):
1940 return num.ToBig(self.TryStatus())
1941
1942 elif case('_error'):
1943 return self.TryError()
1944
1945 elif case('_this_dir'):
1946 if len(self.this_dir) == 0:
1947 # e.g. osh -c '' doesn't have it set
1948 # Should we give a custom error here?
1949 # If you're at the interactive shell, 'source mymodule.oil' will still
1950 # work because 'source' sets it.
1951 return value.Undef
1952 else:
1953 return value.Str(self.this_dir[-1]) # top of stack
1954
1955 elif case('PIPESTATUS'):
1956 strs2 = [str(i)
1957 for i in self.pipe_status[-1]] # type: List[str]
1958 return value.BashArray(strs2)
1959
1960 elif case('_pipeline_status'):
1961 items = [num.ToBig(i)
1962 for i in self.pipe_status[-1]] # type: List[value_t]
1963 return value.List(items)
1964
1965 elif case('_process_sub_status'): # YSH naming convention
1966 items = [num.ToBig(i) for i in self.process_sub_status[-1]]
1967 return value.List(items)
1968
1969 elif case('BASH_REMATCH'):
1970 top_match = self.regex_match[-1]
1971 with tagswitch(top_match) as case2:
1972 if case2(regex_match_e.No):
1973 groups = [] # type: List[str]
1974 elif case2(regex_match_e.Yes):
1975 m = cast(RegexMatch, top_match)
1976 groups = util.RegexGroupStrings(m.s, m.indices)
1977 return value.BashArray(groups)
1978
1979 # Do lookup of system globals before looking at user variables. Note: we
1980 # could optimize this at compile-time like $?. That would break
1981 # ${!varref}, but it's already broken for $?.
1982
1983 elif case('FUNCNAME'):
1984 # bash wants it in reverse order. This is a little inefficient but we're
1985 # not depending on deque().
1986 strs = [] # type: List[str]
1987 for frame in reversed(self.debug_stack):
1988 UP_frame = frame
1989 with tagswitch(frame) as case2:
1990 if case2(debug_frame_e.Call):
1991 frame = cast(debug_frame.Call, UP_frame)
1992 strs.append(frame.func_name)
1993
1994 elif case2(debug_frame_e.Source):
1995 # bash doesn't tell you the filename sourced
1996 strs.append('source')
1997
1998 elif case2(debug_frame_e.Main):
1999 strs.append('main') # also bash behavior
2000
2001 return value.BashArray(strs) # TODO: Reuse this object too?
2002
2003 # $BASH_SOURCE and $BASH_LINENO have OFF BY ONE design bugs:
2004 #
2005 # ${BASH_LINENO[$i]} is the line number in the source file
2006 # (${BASH_SOURCE[$i+1]}) where ${FUNCNAME[$i]} was called (or
2007 # ${BASH_LINENO[$i-1]} if referenced within another shell function).
2008 #
2009 # https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
2010
2011 elif case('BASH_SOURCE'):
2012 strs = []
2013 for frame in reversed(self.debug_stack):
2014 UP_frame = frame
2015 with tagswitch(frame) as case2:
2016 if case2(debug_frame_e.Call):
2017 frame = cast(debug_frame.Call, UP_frame)
2018
2019 # Weird bash behavior
2020 assert frame.def_tok.line is not None
2021 source_str = ui.GetLineSourceString(
2022 frame.def_tok.line)
2023 strs.append(source_str)
2024
2025 elif case2(debug_frame_e.Source):
2026 frame = cast(debug_frame.Source, UP_frame)
2027 # Is this right?
2028 strs.append(frame.source_name)
2029
2030 elif case2(debug_frame_e.Main):
2031 frame = cast(debug_frame.Main, UP_frame)
2032 strs.append(frame.dollar0)
2033
2034 return value.BashArray(strs) # TODO: Reuse this object too?
2035
2036 elif case('BASH_LINENO'):
2037 strs = []
2038 for frame in reversed(self.debug_stack):
2039 UP_frame = frame
2040 with tagswitch(frame) as case2:
2041 if case2(debug_frame_e.Call):
2042 frame = cast(debug_frame.Call, UP_frame)
2043 strs.append(_LineNumber(frame.call_tok))
2044
2045 elif case2(debug_frame_e.Source):
2046 frame = cast(debug_frame.Source, UP_frame)
2047 strs.append(_LineNumber(frame.call_tok))
2048
2049 elif case2(debug_frame_e.Main):
2050 # Bash does this to line up with 'main'
2051 strs.append('0')
2052
2053 return value.BashArray(strs) # TODO: Reuse this object too?
2054
2055 elif case('LINENO'):
2056 assert self.token_for_line is not None
2057 # Reuse object with mutation
2058 # TODO: maybe use interned GetLineNumStr?
2059 self.line_num.s = str(self.token_for_line.line.line_num)
2060 return self.line_num
2061
2062 elif case('BASHPID'): # TODO: YSH io->getpid()
2063 return value.Str(str(posix.getpid()))
2064
2065 elif case('_'):
2066 return value.Str(self.last_arg)
2067
2068 elif case('SECONDS'):
2069 return value.Int(
2070 mops.FromFloat(time_.time() - self.seconds_start))
2071
2072 else:
2073 # In the case 'declare -n ref='a[42]', the result won't be a cell. Idea to
2074 # fix this:
2075 # 1. Call self.unsafe_arith.ParseVarRef() -> BracedVarSub
2076 # 2. Call self.unsafe_arith.GetNameref(bvs_part), and get a value_t
2077 # We still need a ref_trail to detect cycles.
2078 cell, _, _ = self._ResolveNameOrRef(name, which_scopes)
2079 if cell:
2080 return cell.val
2081
2082 return value.Undef
2083
2084 def GetCell(self, name, which_scopes=scope_e.Shopt):
2085 # type: (str, scope_t) -> Cell
2086 """Get both the value and flags.
2087
2088 Usages:
2089 - the 'pp' builtin.
2090 - declare -p
2091 - ${x@a}
2092 - to test of 'TZ' is exported in printf? Why?
2093 """
2094 if which_scopes == scope_e.Shopt:
2095 which_scopes = self.ScopesForReading()
2096
2097 cell, _ = self._ResolveNameOnly(name, which_scopes)
2098 return cell
2099
2100 def Unset(self, lval, which_scopes):
2101 # type: (sh_lvalue_t, scope_t) -> bool
2102 """
2103 Returns:
2104 Whether the cell was found.
2105 """
2106 # TODO: Refactor sh_lvalue type to avoid this
2107 UP_lval = lval
2108
2109 with tagswitch(lval) as case:
2110 if case(sh_lvalue_e.Var): # unset x
2111 lval = cast(LeftName, UP_lval)
2112 var_name = lval.name
2113 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2114 lval = cast(sh_lvalue.Indexed, UP_lval)
2115 var_name = lval.name
2116 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2117 lval = cast(sh_lvalue.Keyed, UP_lval)
2118 var_name = lval.name
2119 else:
2120 raise AssertionError()
2121
2122 if which_scopes == scope_e.Shopt:
2123 which_scopes = self.ScopesForWriting()
2124
2125 cell, name_map, cell_name = self._ResolveNameOrRef(
2126 var_name, which_scopes)
2127 if not cell:
2128 return False # 'unset' builtin falls back on functions
2129 if cell.readonly:
2130 raise error.Runtime("Can't unset readonly variable %r" % var_name)
2131
2132 with tagswitch(lval) as case:
2133 if case(sh_lvalue_e.Var): # unset x
2134 # Make variables in higher scopes visible.
2135 # example: test/spec.sh builtin-vars -r 24 (ble.sh)
2136 mylib.dict_erase(name_map, cell_name)
2137
2138 # alternative that some shells use:
2139 # name_map[cell_name].val = value.Undef
2140 # cell.exported = False
2141
2142 # This should never happen because we do recursive lookups of namerefs.
2143 assert not cell.nameref, cell
2144
2145 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2146 lval = cast(sh_lvalue.Indexed, UP_lval)
2147 # Note: Setting an entry to None and shifting entries are pretty
2148 # much the same in shell.
2149
2150 val = cell.val
2151 UP_val = val
2152 if val.tag() != value_e.BashArray:
2153 raise error.Runtime("%r isn't an array" % var_name)
2154
2155 val = cast(value.BashArray, UP_val)
2156 strs = val.strs
2157
2158 n = len(strs)
2159 last_index = n - 1
2160 index = lval.index
2161 if index < 0:
2162 index += n
2163
2164 if index == last_index:
2165 # Special case: The array SHORTENS if you unset from the end. You
2166 # can tell with a+=(3 4)
2167 strs.pop()
2168 elif 0 <= index and index < last_index:
2169 strs[index] = None
2170 else:
2171 # If it's not found, it's not an error. In other words, 'unset'
2172 # ensures that a value doesn't exist, regardless of whether it
2173 # existed. It's idempotent.
2174 # (Ousterhout specifically argues that the strict behavior was a
2175 # mistake for Tcl!)
2176 pass
2177
2178 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2179 lval = cast(sh_lvalue.Keyed, UP_lval)
2180
2181 val = cell.val
2182 UP_val = val
2183
2184 # note: never happens because of mem.IsBashAssoc test for sh_lvalue.Keyed
2185 #if val.tag() != value_e.BashAssoc:
2186 # raise error.Runtime("%r isn't an associative array" % lval.name)
2187
2188 val = cast(value.BashAssoc, UP_val)
2189 mylib.dict_erase(val.d, lval.key)
2190
2191 else:
2192 raise AssertionError(lval)
2193
2194 return True
2195
2196 def ScopesForReading(self):
2197 # type: () -> scope_t
2198 """Read scope."""
2199 return (scope_e.Dynamic
2200 if self.exec_opts.dynamic_scope() else scope_e.LocalOrGlobal)
2201
2202 def ScopesForWriting(self):
2203 # type: () -> scope_t
2204 """Write scope."""
2205 return (scope_e.Dynamic
2206 if self.exec_opts.dynamic_scope() else scope_e.LocalOnly)
2207
2208 def ClearFlag(self, name, flag):
2209 # type: (str, int) -> bool
2210 """Used for export -n.
2211
2212 We don't use SetValue() because even if rval is None, it will make an
2213 Undef value in a scope.
2214 """
2215 cell, name_map = self._ResolveNameOnly(name, self.ScopesForReading())
2216 if cell:
2217 if flag & ClearExport:
2218 cell.exported = False
2219 if flag & ClearNameref:
2220 cell.nameref = False
2221 return True
2222 else:
2223 return False
2224
2225 def GetExported(self):
2226 # type: () -> Dict[str, str]
2227 """Get all the variables that are marked exported."""
2228 # TODO: This is run on every SimpleCommand. Should we have a dirty flag?
2229 # We have to notice these things:
2230 # - If an exported variable is changed.
2231 # - If the set of exported variables changes.
2232
2233 exported = {} # type: Dict[str, str]
2234 # Search from globals up. Names higher on the stack will overwrite names
2235 # lower on the stack.
2236 for scope in self.var_stack:
2237 for name, cell in iteritems(scope):
2238 # TODO: Disallow exporting at assignment time. If an exported Str is
2239 # changed to BashArray, also clear its 'exported' flag.
2240 if cell.exported and cell.val.tag() == value_e.Str:
2241 val = cast(value.Str, cell.val)
2242 exported[name] = val.s
2243 return exported
2244
2245 def VarNames(self):
2246 # type: () -> List[str]
2247 """For internal OSH completion and compgen -A variable.
2248
2249 NOTE: We could also add $? $$ etc.?
2250 """
2251 ret = [] # type: List[str]
2252 # Look up the stack, yielding all variables. Bash seems to do this.
2253 for scope in self.var_stack:
2254 for name in scope:
2255 ret.append(name)
2256 return ret
2257
2258 def VarNamesStartingWith(self, prefix):
2259 # type: (str) -> List[str]
2260 """For ${!prefix@}"""
2261 # Look up the stack, yielding all variables. Bash seems to do this.
2262 names = [] # type: List[str]
2263 for scope in self.var_stack:
2264 for name in scope:
2265 if name.startswith(prefix):
2266 names.append(name)
2267 return names
2268
2269 def GetAllVars(self):
2270 # type: () -> Dict[str, str]
2271 """Get all variables and their values, for 'set' builtin."""
2272 result = {} # type: Dict[str, str]
2273 for scope in self.var_stack:
2274 for name, cell in iteritems(scope):
2275 # TODO: Show other types?
2276 val = cell.val
2277 if val.tag() == value_e.Str:
2278 str_val = cast(value.Str, val)
2279 result[name] = str_val.s
2280 return result
2281
2282 def GetAllCells(self, which_scopes):
2283 # type: (scope_t) -> Dict[str, Cell]
2284 """Get all variables and their values, for 'set' builtin."""
2285 result = {} # type: Dict[str, Cell]
2286
2287 if which_scopes == scope_e.Dynamic:
2288 scopes = self.var_stack
2289 elif which_scopes == scope_e.LocalOnly:
2290 scopes = self.var_stack[-1:]
2291 elif which_scopes == scope_e.GlobalOnly:
2292 scopes = self.var_stack[0:1]
2293 elif which_scopes == scope_e.LocalOrGlobal:
2294 scopes = [self.var_stack[0]]
2295 if len(self.var_stack) > 1:
2296 scopes.append(self.var_stack[-1])
2297 else:
2298 raise AssertionError()
2299
2300 for scope in scopes:
2301 for name, cell in iteritems(scope):
2302 result[name] = cell
2303 return result
2304
2305 def IsGlobalScope(self):
2306 # type: () -> bool
2307 return len(self.var_stack) == 1
2308
2309 def SetRegexMatch(self, match):
2310 # type: (regex_match_t) -> None
2311 self.regex_match[-1] = match
2312
2313 def GetRegexMatch(self):
2314 # type: () -> regex_match_t
2315 return self.regex_match[-1]
2316
2317 def PushContextStack(self, context):
2318 # type: (Dict[str, value_t]) -> None
2319 self.ctx_stack.append(context)
2320
2321 def GetContext(self):
2322 # type: () -> Optional[Dict[str, value_t]]
2323 if len(self.ctx_stack):
2324 return self.ctx_stack[-1]
2325 return None
2326
2327 def PopContextStack(self):
2328 # type: () -> Dict[str, value_t]
2329 assert self.ctx_stack, "Empty context stack"
2330 return self.ctx_stack.pop()
2331
2332
2333#
2334# Wrappers to Set Variables
2335#
2336
2337
2338def OshLanguageSetValue(mem, lval, val, flags=0):
2339 # type: (Mem, sh_lvalue_t, value_t, int) -> None
2340 """Like 'setvar' (scope_e.LocalOnly), unless dynamic scope is on.
2341
2342 That is, it respects shopt --unset dynamic_scope.
2343
2344 Used for assignment builtins, (( a = b )), {fd}>out, ${x=}, etc.
2345 """
2346 which_scopes = mem.ScopesForWriting()
2347 mem.SetValue(lval, val, which_scopes, flags=flags)
2348
2349
2350def BuiltinSetValue(mem, lval, val):
2351 # type: (Mem, sh_lvalue_t, value_t) -> None
2352 """Equivalent of x=$y
2353
2354 Called by BuiltinSetString and BuiltinSetArray Used directly by
2355 printf -v because it can mutate an array
2356 """
2357 mem.SetValue(lval, val, mem.ScopesForWriting())
2358
2359
2360def BuiltinSetString(mem, name, s):
2361 # type: (Mem, str, str) -> None
2362 """Set a string by looking up the stack.
2363
2364 Used for 'read', 'getopts', completion builtins, etc.
2365 """
2366 assert isinstance(s, str)
2367 BuiltinSetValue(mem, location.LName(name), value.Str(s))
2368
2369
2370def BuiltinSetArray(mem, name, a):
2371 # type: (Mem, str, List[str]) -> None
2372 """Set an array by looking up the stack.
2373
2374 Used by compadjust, read -a, etc.
2375 """
2376 assert isinstance(a, list)
2377 BuiltinSetValue(mem, location.LName(name), value.BashArray(a))
2378
2379
2380def SetGlobalString(mem, name, s):
2381 # type: (Mem, str, str) -> None
2382 """Helper for completion, etc."""
2383 assert isinstance(s, str)
2384 val = value.Str(s)
2385 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2386
2387
2388def SetGlobalArray(mem, name, a):
2389 # type: (Mem, str, List[str]) -> None
2390 """Used by completion, shell initialization, etc."""
2391 assert isinstance(a, list)
2392 mem.SetNamed(location.LName(name), value.BashArray(a), scope_e.GlobalOnly)
2393
2394
2395def ExportGlobalString(mem, name, s):
2396 # type: (Mem, str, str) -> None
2397 """Helper for completion, $PWD, $OLDPWD, etc."""
2398 assert isinstance(s, str)
2399 val = value.Str(s)
2400 mem.SetNamed(location.LName(name),
2401 val,
2402 scope_e.GlobalOnly,
2403 flags=SetExport)
2404
2405
2406#
2407# Wrappers to Get Variables
2408#
2409
2410
2411def DynamicGetVar(mem, name, which_scopes):
2412 # type: (Mem, str, scope_t) -> value_t
2413 """
2414 For getVar() and shvarGet()
2415 """
2416 val = mem.GetValue(name, which_scopes=which_scopes)
2417
2418 # Undef is not a user-visible value!
2419 # There's no way to distinguish null from undefined.
2420 if val.tag() == value_e.Undef:
2421 return value.Null
2422
2423 return val
2424
2425
2426def GetString(mem, name):
2427 # type: (Mem, str) -> str
2428 """Wrapper around GetValue(). Check that HOME, PWD, OLDPWD, etc. are
2429 strings. bash doesn't have these errors because ${array} is ${array[0]}.
2430
2431 TODO: We could also check this when you're storing variables?
2432 """
2433 val = mem.GetValue(name)
2434 UP_val = val
2435 with tagswitch(val) as case:
2436 if case(value_e.Undef):
2437 raise error.Runtime("$%s isn't defined" % name)
2438 elif case(value_e.Str):
2439 return cast(value.Str, UP_val).s
2440 else:
2441 # User would have to 'unset HOME' to get rid of exported flag
2442 raise error.Runtime("$%s should be a string" % name)
2443
2444
2445def MaybeString(mem, name):
2446 # type: (Mem, str) -> Optional[str]
2447 """Like GetString(), but doesn't throw an exception."""
2448 try:
2449 return GetString(mem, name)
2450 except error.Runtime:
2451 return None
2452
2453
2454def GetInteger(mem, name):
2455 # type: (Mem, str) -> int
2456 """For OPTIND variable used in getopts builtin.
2457
2458 TODO: it could be value.Int() ?
2459 """
2460 val = mem.GetValue(name)
2461 if val.tag() != value_e.Str:
2462 raise error.Runtime('$%s should be a string, got %s' %
2463 (name, ui.ValType(val)))
2464 s = cast(value.Str, val).s
2465 try:
2466 i = int(s)
2467 except ValueError:
2468 raise error.Runtime("$%s doesn't look like an integer, got %r" %
2469 (name, s))
2470 return i
2471
2472
2473# vim: sw=4