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

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