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

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