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

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