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

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