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

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