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

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