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

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