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

2512 lines, 1274 significant
1# Copyright 2016 Andy Chu. All rights reserved.
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7"""
8state.py - Interpreter state
9"""
10from __future__ import print_function
11import time as time_ # avoid name conflict
12
13from _devbuild.gen.id_kind_asdl import Id
14from _devbuild.gen.option_asdl import option_i
15from _devbuild.gen.runtime_asdl import (scope_e, scope_t, Cell)
16from _devbuild.gen.syntax_asdl import (loc, loc_t, Token, debug_frame,
17 debug_frame_e, debug_frame_t)
18from _devbuild.gen.types_asdl import opt_group_i
19from _devbuild.gen.value_asdl import (value, value_e, value_t, sh_lvalue,
20 sh_lvalue_e, sh_lvalue_t, LeftName,
21 y_lvalue_e, regex_match, regex_match_e,
22 regex_match_t, RegexMatch)
23from core import error
24from core.error import e_usage, e_die
25from core import num
26from core import pyos
27from core import pyutil
28from core import optview
29from core import ui
30from core import util
31from frontend import consts
32from frontend import location
33from frontend import match
34from mycpp import mops
35from mycpp import mylib
36from mycpp.mylib import (log, print_stderr, str_switch, tagswitch, iteritems,
37 NewDict)
38from osh import split
39from pylib import os_path
40from pylib import path_stat
41
42import libc
43import posix_ as posix
44from posix_ import X_OK # translated directly to C macro
45
46from typing import Tuple, List, Dict, Optional, Any, cast, TYPE_CHECKING
47
48if TYPE_CHECKING:
49 from _devbuild.gen.option_asdl import option_t
50 from core import alloc
51 from osh import sh_expr_eval
52
53_ = log
54
55# This was derived from bash --norc -c 'argv "$COMP_WORDBREAKS".
56# Python overwrites this to something Python-specific in Modules/readline.c, so
57# we have to set it back!
58# Used in both core/competion.py and osh/state.py
59_READLINE_DELIMS = ' \t\n"\'><=;|&(:'
60
61# flags for mem.SetValue()
62SetReadOnly = 1 << 0
63ClearReadOnly = 1 << 1
64SetExport = 1 << 2
65ClearExport = 1 << 3
66SetNameref = 1 << 4
67ClearNameref = 1 << 5
68
69
70def LookupExecutable(name, path_dirs, exec_required=True):
71 # type: (str, List[str], bool) -> Optional[str]
72 """
73 Returns either
74 - the name if it's a relative path that exists
75 - the executable name resolved against path_dirs
76 - None if not found
77 """
78 if len(name) == 0: # special case for "$(true)"
79 return None
80
81 if '/' in name:
82 return name if path_stat.exists(name) else None
83
84 for path_dir in path_dirs:
85 full_path = os_path.join(path_dir, name)
86 if exec_required:
87 found = posix.access(full_path, X_OK)
88 else:
89 found = path_stat.exists(full_path)
90
91 if found:
92 return full_path
93
94 return None
95
96
97class SearchPath(object):
98 """For looking up files in $PATH."""
99
100 def __init__(self, mem):
101 # type: (Mem) -> None
102 self.mem = mem
103 self.cache = {} # type: Dict[str, str]
104
105 def _GetPath(self):
106 # type: () -> List[str]
107
108 # TODO: Could cache this to avoid split() allocating all the time.
109 val = self.mem.GetValue('PATH')
110 UP_val = val
111 if val.tag() == value_e.Str:
112 val = cast(value.Str, UP_val)
113 return val.s.split(':')
114 else:
115 return [] # treat as empty path
116
117 def LookupOne(self, name, exec_required=True):
118 # type: (str, bool) -> Optional[str]
119 """
120 Returns the path itself (if relative path), the resolved path, or None.
121 """
122 return LookupExecutable(name,
123 self._GetPath(),
124 exec_required=exec_required)
125
126 def LookupReflect(self, name, do_all):
127 # type: (str, bool) -> List[str]
128 """
129 Like LookupOne(), with an option for 'type -a' to return all paths.
130 """
131 if len(name) == 0: # special case for "$(true)"
132 return []
133
134 if '/' in name:
135 if path_stat.exists(name):
136 return [name]
137 else:
138 return []
139
140 results = [] # type: List[str]
141 for path_dir in self._GetPath():
142 full_path = os_path.join(path_dir, name)
143 if path_stat.exists(full_path):
144 results.append(full_path)
145 if not do_all:
146 return results
147
148 return results
149
150 def CachedLookup(self, name):
151 # type: (str) -> Optional[str]
152 #log('name %r', name)
153 if name in self.cache:
154 return self.cache[name]
155
156 full_path = self.LookupOne(name)
157 if full_path is not None:
158 self.cache[name] = full_path
159 return full_path
160
161 def MaybeRemoveEntry(self, name):
162 # type: (str) -> None
163 """When the file system changes."""
164 mylib.dict_erase(self.cache, name)
165
166 def ClearCache(self):
167 # type: () -> None
168 """For hash -r."""
169 self.cache.clear()
170
171 def CachedCommands(self):
172 # type: () -> List[str]
173 return self.cache.values()
174
175
176class ctx_Source(object):
177 """For source builtin."""
178
179 def __init__(self, mem, source_name, argv):
180 # type: (Mem, str, List[str]) -> None
181 mem.PushSource(source_name, argv)
182 self.mem = mem
183 self.argv = argv
184
185 # Whenever we're sourcing, the 'is-main' builtin will return 1 (false)
186 self.to_restore = self.mem.is_main
187 self.mem.is_main = False
188
189 def __enter__(self):
190 # type: () -> None
191 pass
192
193 def __exit__(self, type, value, traceback):
194 # type: (Any, Any, Any) -> None
195 self.mem.PopSource(self.argv)
196
197 self.mem.is_main = self.to_restore
198
199
200class ctx_DebugTrap(object):
201 """For trap DEBUG."""
202
203 def __init__(self, mem):
204 # type: (Mem) -> None
205 mem.running_debug_trap = True
206 self.mem = mem
207
208 def __enter__(self):
209 # type: () -> None
210 pass
211
212 def __exit__(self, type, value, traceback):
213 # type: (Any, Any, Any) -> None
214 self.mem.running_debug_trap = False
215
216
217class ctx_ErrTrap(object):
218 """For trap ERR."""
219
220 def __init__(self, mem):
221 # type: (Mem) -> None
222 mem.running_err_trap = True
223 self.mem = mem
224
225 def __enter__(self):
226 # type: () -> None
227 pass
228
229 def __exit__(self, type, value, traceback):
230 # type: (Any, Any, Any) -> None
231 self.mem.running_err_trap = False
232
233
234class ctx_Option(object):
235 """Shopt --unset errexit { false }"""
236
237 def __init__(self, mutable_opts, opt_nums, b):
238 # type: (MutableOpts, List[int], bool) -> None
239 for opt_num in opt_nums:
240 mutable_opts.Push(opt_num, b)
241 if opt_num == option_i.errexit:
242 # it wasn't disabled
243 mutable_opts.errexit_disabled_tok.append(None)
244
245 self.mutable_opts = mutable_opts
246 self.opt_nums = opt_nums
247
248 def __enter__(self):
249 # type: () -> None
250 pass
251
252 def __exit__(self, type, value, traceback):
253 # type: (Any, Any, Any) -> None
254 for opt_num in self.opt_nums: # don't bother to do it in reverse order
255 if opt_num == option_i.errexit:
256 self.mutable_opts.errexit_disabled_tok.pop()
257 self.mutable_opts.Pop(opt_num)
258
259
260class ctx_AssignBuiltin(object):
261 """Local x=$(false) is disallowed."""
262
263 def __init__(self, mutable_opts):
264 # type: (MutableOpts) -> None
265 self.strict = False
266 if mutable_opts.Get(option_i.strict_errexit):
267 mutable_opts.Push(option_i._allow_command_sub, False)
268 mutable_opts.Push(option_i._allow_process_sub, False)
269 self.strict = True
270
271 self.mutable_opts = mutable_opts
272
273 def __enter__(self):
274 # type: () -> None
275 pass
276
277 def __exit__(self, type, value, traceback):
278 # type: (Any, Any, Any) -> None
279 if self.strict:
280 self.mutable_opts.Pop(option_i._allow_command_sub)
281 self.mutable_opts.Pop(option_i._allow_process_sub)
282
283
284class ctx_YshExpr(object):
285 """Command sub must fail in 'mystring' ++ $(false)"""
286
287 def __init__(self, mutable_opts):
288 # type: (MutableOpts) -> None
289 mutable_opts.Push(option_i.command_sub_errexit, True)
290 self.mutable_opts = mutable_opts
291
292 def __enter__(self):
293 # type: () -> None
294 pass
295
296 def __exit__(self, type, value, traceback):
297 # type: (Any, Any, Any) -> None
298 self.mutable_opts.Pop(option_i.command_sub_errexit)
299
300
301class ctx_ErrExit(object):
302 """Manages the errexit setting.
303
304 - The user can change it with builtin 'set' at any point in the code.
305 - These constructs implicitly disable 'errexit':
306 - if / while / until conditions
307 - ! (part of pipeline)
308 - && ||
309 """
310
311 def __init__(self, mutable_opts, b, disabled_tok):
312 # type: (MutableOpts, bool, Optional[Token]) -> None
313
314 # If we're disabling it, we need a span ID. If not, then we should NOT
315 # have one.
316 assert b == (disabled_tok is None)
317
318 mutable_opts.Push(option_i.errexit, b)
319 mutable_opts.errexit_disabled_tok.append(disabled_tok)
320
321 self.strict = False
322 if mutable_opts.Get(option_i.strict_errexit):
323 mutable_opts.Push(option_i._allow_command_sub, False)
324 mutable_opts.Push(option_i._allow_process_sub, False)
325 self.strict = True
326
327 self.mutable_opts = mutable_opts
328
329 def __enter__(self):
330 # type: () -> None
331 pass
332
333 def __exit__(self, type, value, traceback):
334 # type: (Any, Any, Any) -> None
335 self.mutable_opts.errexit_disabled_tok.pop()
336 self.mutable_opts.Pop(option_i.errexit)
337
338 if self.strict:
339 self.mutable_opts.Pop(option_i._allow_command_sub)
340 self.mutable_opts.Pop(option_i._allow_process_sub)
341
342
343class OptHook(object):
344 """Interface for option hooks."""
345
346 def __init__(self):
347 # type: () -> None
348 """Empty constructor for mycpp."""
349 pass
350
351 def OnChange(self, opt0_array, opt_name, b):
352 # type: (List[bool], str, bool) -> bool
353 """This method is called whenever an option is changed.
354
355 Returns success or failure.
356 """
357 return True
358
359
360def InitOpts():
361 # type: () -> List[bool]
362
363 opt0_array = [False] * option_i.ARRAY_SIZE
364 for opt_num in consts.DEFAULT_TRUE:
365 opt0_array[opt_num] = True
366 return opt0_array
367
368
369def MakeOpts(mem, opt_hook):
370 # type: (Mem, OptHook) -> Tuple[optview.Parse, optview.Exec, MutableOpts]
371
372 # Unusual representation: opt0_array + opt_stacks. For two features:
373 #
374 # - POSIX errexit disable semantics
375 # - Oil's shopt --set nullglob { ... }
376 #
377 # We could do it with a single List of stacks. But because shopt --set
378 # random_option { ... } is very uncommon, we optimize and store the ZERO
379 # element of the stack in a flat array opt0_array (default False), and then
380 # the rest in opt_stacks, where the value could be None. By allowing the
381 # None value, we save ~50 or so list objects in the common case.
382
383 opt0_array = InitOpts()
384 # Overrides, including errexit
385 no_stack = None # type: List[bool] # for mycpp
386 opt_stacks = [no_stack] * option_i.ARRAY_SIZE # type: List[List[bool]]
387
388 parse_opts = optview.Parse(opt0_array, opt_stacks)
389 exec_opts = optview.Exec(opt0_array, opt_stacks)
390 mutable_opts = MutableOpts(mem, opt0_array, opt_stacks, opt_hook)
391
392 return parse_opts, exec_opts, mutable_opts
393
394
395def _SetGroup(opt0_array, opt_nums, b):
396 # type: (List[bool], List[int], bool) -> None
397 for opt_num in opt_nums:
398 b2 = not b if opt_num in consts.DEFAULT_TRUE else b
399 opt0_array[opt_num] = b2
400
401
402def MakeOilOpts():
403 # type: () -> optview.Parse
404 opt0_array = InitOpts()
405 _SetGroup(opt0_array, consts.YSH_ALL, True)
406
407 no_stack = None # type: List[bool]
408 opt_stacks = [no_stack] * option_i.ARRAY_SIZE # type: List[List[bool]]
409
410 parse_opts = optview.Parse(opt0_array, opt_stacks)
411 return parse_opts
412
413
414def _AnyOptionNum(opt_name):
415 # type: (str) -> option_t
416 opt_num = consts.OptionNum(opt_name)
417 if opt_num == 0:
418 e_usage('got invalid option %r' % opt_name, loc.Missing)
419
420 # Note: we relaxed this for Oil so we can do 'shopt --unset errexit' consistently
421 #if opt_num not in consts.SHOPT_OPTION_NUMS:
422 # e_usage("doesn't own option %r (try 'set')" % opt_name)
423
424 return opt_num
425
426
427def _SetOptionNum(opt_name):
428 # type: (str) -> option_t
429 opt_num = consts.OptionNum(opt_name)
430 if opt_num == 0:
431 e_usage('got invalid option %r' % opt_name, loc.Missing)
432
433 if opt_num not in consts.SET_OPTION_NUMS:
434 e_usage("invalid option %r (try shopt)" % opt_name, loc.Missing)
435
436 return opt_num
437
438
439class MutableOpts(object):
440
441 def __init__(self, mem, opt0_array, opt_stacks, opt_hook):
442 # type: (Mem, List[bool], List[List[bool]], OptHook) -> None
443 self.mem = mem
444 self.opt0_array = opt0_array
445 self.opt_stacks = opt_stacks
446 self.errexit_disabled_tok = [] # type: List[Token]
447
448 # Used for 'set -o vi/emacs'
449 self.opt_hook = opt_hook
450
451 def Init(self):
452 # type: () -> None
453
454 # This comes after all the 'set' options.
455 UP_shellopts = self.mem.GetValue('SHELLOPTS')
456 # Always true in Oil, see Init above
457 if UP_shellopts.tag() == value_e.Str:
458 shellopts = cast(value.Str, UP_shellopts)
459 self._InitOptionsFromEnv(shellopts.s)
460
461 def _InitOptionsFromEnv(self, shellopts):
462 # type: (str) -> None
463 # e.g. errexit:nounset:pipefail
464 lookup = shellopts.split(':')
465 for opt_num in consts.SET_OPTION_NUMS:
466 name = consts.OptionName(opt_num)
467 if name in lookup:
468 self._SetOldOption(name, True)
469
470 def Push(self, opt_num, b):
471 # type: (int, bool) -> None
472 overlay = self.opt_stacks[opt_num]
473 if overlay is None or len(overlay) == 0:
474 self.opt_stacks[opt_num] = [b] # Allocate a new list
475 else:
476 overlay.append(b)
477
478 def Pop(self, opt_num):
479 # type: (int) -> bool
480 overlay = self.opt_stacks[opt_num]
481 assert overlay is not None
482 return overlay.pop()
483
484 def PushDynamicScope(self, b):
485 # type: (bool) -> None
486 """B: False if it's a proc, and True if it's a shell function."""
487 # If it's already disabled, keep it disabled
488 if not self.Get(option_i.dynamic_scope):
489 b = False
490 self.Push(option_i.dynamic_scope, b)
491
492 def PopDynamicScope(self):
493 # type: () -> None
494 self.Pop(option_i.dynamic_scope)
495
496 def Get(self, opt_num):
497 # type: (int) -> bool
498 # Like _Getter in core/optview.py
499 overlay = self.opt_stacks[opt_num]
500 if overlay is None or len(overlay) == 0:
501 return self.opt0_array[opt_num]
502 else:
503 return overlay[-1] # the top value
504
505 def _Set(self, opt_num, b):
506 # type: (int, bool) -> None
507 """Used to disable errexit.
508
509 For bash compatibility in command sub.
510 """
511
512 # Like _Getter in core/optview.py
513 overlay = self.opt_stacks[opt_num]
514 if overlay is None or len(overlay) == 0:
515 self.opt0_array[opt_num] = b
516 else:
517 overlay[-1] = b # The top value
518
519 def set_interactive(self):
520 # type: () -> None
521 self._Set(option_i.interactive, True)
522
523 def set_redefine_proc_func(self):
524 # type: () -> None
525 """For interactive shells."""
526 self._Set(option_i.redefine_proc_func, True)
527
528 def set_redefine_module(self):
529 # type: () -> None
530 """For interactive shells."""
531 self._Set(option_i.redefine_module, True)
532
533 def set_emacs(self):
534 # type: () -> None
535 self._Set(option_i.emacs, True)
536
537 def set_xtrace(self, b):
538 # type: (bool) -> None
539 self._Set(option_i.xtrace, b)
540
541 def _SetArrayByNum(self, opt_num, b):
542 # type: (int, bool) -> None
543 if (opt_num in consts.PARSE_OPTION_NUMS and
544 not self.mem.ParsingChangesAllowed()):
545 e_die('Syntax options must be set at the top level '
546 '(outside any function)')
547
548 self._Set(opt_num, b)
549
550 def SetDeferredErrExit(self, b):
551 # type: (bool) -> None
552 """Set the errexit flag, possibly deferring it.
553
554 Implements the unusual POSIX "defer" behavior. Callers: set -o
555 errexit, shopt -s oil:all, oil:upgrade
556 """
557 #log('Set %s', b)
558
559 # Defer it until we pop by setting the BOTTOM OF THE STACK.
560 self.opt0_array[option_i.errexit] = b
561
562 def DisableErrExit(self):
563 # type: () -> None
564 """Called by core/process.py to implement bash quirks."""
565 self._Set(option_i.errexit, False)
566
567 def ErrExitDisabledToken(self):
568 # type: () -> Optional[Token]
569 """If errexit is disabled by POSIX rules, return Token for construct.
570
571 e.g. the Token for 'if' or '&&' etc.
572 """
573 # Bug fix: The errexit disabling inherently follows a STACK DISCIPLINE.
574 # But we run trap handlers in the MAIN LOOP, which break this. So just
575 # declare that it's never disabled in a trap.
576 if self.Get(option_i._running_trap):
577 return None
578
579 if len(self.errexit_disabled_tok) == 0:
580 return None
581
582 return self.errexit_disabled_tok[-1]
583
584 def ErrExitIsDisabled(self):
585 # type: () -> bool
586 """
587 Similar to ErrExitDisabledToken, for ERR trap
588 """
589 if len(self.errexit_disabled_tok) == 0:
590 return False
591
592 return self.errexit_disabled_tok[-1] is not None
593
594 def _SetOldOption(self, opt_name, b):
595 # type: (str, bool) -> None
596 """Private version for synchronizing from SHELLOPTS."""
597 assert '_' not in opt_name
598 assert opt_name in consts.SET_OPTION_NAMES
599
600 opt_num = consts.OptionNum(opt_name)
601 assert opt_num != 0, opt_name
602
603 if opt_num == option_i.errexit:
604 self.SetDeferredErrExit(b)
605 else:
606 if opt_num == option_i.verbose and b:
607 print_stderr('Warning: set -o verbose not implemented')
608 self._SetArrayByNum(opt_num, b)
609
610 # note: may FAIL before we get here.
611
612 success = self.opt_hook.OnChange(self.opt0_array, opt_name, b)
613
614 def SetOldOption(self, opt_name, b):
615 # type: (str, bool) -> None
616 """For set -o, set +o, or shopt -s/-u -o."""
617 unused = _SetOptionNum(opt_name) # validate it
618 self._SetOldOption(opt_name, b)
619
620 UP_val = self.mem.GetValue('SHELLOPTS')
621 assert UP_val.tag() == value_e.Str, UP_val
622 val = cast(value.Str, UP_val)
623 shellopts = val.s
624
625 # Now check if SHELLOPTS needs to be updated. It may be exported.
626 #
627 # NOTE: It might be better to skip rewriting SEHLLOPTS in the common case
628 # where it is not used. We could do it lazily upon GET.
629
630 # Also, it would be slightly more efficient to update SHELLOPTS if
631 # settings were batched, Examples:
632 # - set -eu
633 # - shopt -s foo bar
634 if b:
635 if opt_name not in shellopts:
636 new_val = value.Str('%s:%s' % (shellopts, opt_name))
637 self.mem.InternalSetGlobal('SHELLOPTS', new_val)
638 else:
639 if opt_name in shellopts:
640 names = [n for n in shellopts.split(':') if n != opt_name]
641 new_val = value.Str(':'.join(names))
642 self.mem.InternalSetGlobal('SHELLOPTS', new_val)
643
644 def SetAnyOption(self, opt_name, b):
645 # type: (str, bool) -> None
646 """For shopt -s/-u and sh -O/+O."""
647
648 # shopt -s all:oil turns on all Oil options, which includes all strict #
649 # options
650 opt_group = consts.OptionGroupNum(opt_name)
651 if opt_group == opt_group_i.YshUpgrade:
652 _SetGroup(self.opt0_array, consts.YSH_UPGRADE, b)
653 self.SetDeferredErrExit(b) # Special case
654 return
655
656 if opt_group == opt_group_i.YshAll:
657 _SetGroup(self.opt0_array, consts.YSH_ALL, b)
658 self.SetDeferredErrExit(b) # Special case
659 return
660
661 if opt_group == opt_group_i.StrictAll:
662 _SetGroup(self.opt0_array, consts.STRICT_ALL, b)
663 return
664
665 opt_num = _AnyOptionNum(opt_name)
666
667 if opt_num == option_i.errexit:
668 self.SetDeferredErrExit(b)
669 return
670
671 self._SetArrayByNum(opt_num, b)
672
673 def ShowOptions(self, opt_names):
674 # type: (List[str]) -> None
675 """For 'set -o' and 'shopt -p -o'."""
676 # TODO: Maybe sort them differently?
677
678 if len(opt_names) == 0: # if none, supplied, show all
679 opt_names = [consts.OptionName(i) for i in consts.SET_OPTION_NUMS]
680
681 for opt_name in opt_names:
682 opt_num = _SetOptionNum(opt_name)
683 b = self.Get(opt_num)
684 print('set %so %s' % ('-' if b else '+', opt_name))
685
686 def ShowShoptOptions(self, opt_names):
687 # type: (List[str]) -> None
688 """For 'shopt -p'."""
689
690 # Respect option groups.
691 opt_nums = [] # type: List[int]
692 for opt_name in opt_names:
693 opt_group = consts.OptionGroupNum(opt_name)
694 if opt_group == opt_group_i.YshUpgrade:
695 opt_nums.extend(consts.YSH_UPGRADE)
696 elif opt_group == opt_group_i.YshAll:
697 opt_nums.extend(consts.YSH_ALL)
698 elif opt_group == opt_group_i.StrictAll:
699 opt_nums.extend(consts.STRICT_ALL)
700 else:
701 index = consts.OptionNum(opt_name)
702 # Minor incompatibility with bash: we validate everything before
703 # printing.
704 if index == 0:
705 e_usage('got invalid option %r' % opt_name, loc.Missing)
706 opt_nums.append(index)
707
708 if len(opt_names) == 0:
709 # If none supplied, show all>
710 # TODO: Should this show 'set' options too?
711 opt_nums.extend(consts.VISIBLE_SHOPT_NUMS)
712
713 for opt_num in opt_nums:
714 b = self.Get(opt_num)
715 print('shopt -%s %s' %
716 ('s' if b else 'u', consts.OptionName(opt_num)))
717
718
719class _ArgFrame(object):
720 """Stack frame for arguments array."""
721
722 def __init__(self, argv):
723 # type: (List[str]) -> None
724 self.argv = argv
725 self.num_shifted = 0
726
727 def __repr__(self):
728 # type: () -> str
729 return '<_ArgFrame %s %d at %x>' % (self.argv, self.num_shifted,
730 id(self))
731
732 def Dump(self):
733 # type: () -> Dict[str, value_t]
734 items = [value.Str(s) for s in self.argv] # type: List[value_t]
735 argv = value.List(items)
736 return {
737 'argv': argv,
738 'num_shifted': num.ToBig(self.num_shifted),
739 }
740
741 def GetArgNum(self, arg_num):
742 # type: (int) -> value_t
743 index = self.num_shifted + arg_num - 1
744 if index >= len(self.argv):
745 return value.Undef
746
747 return value.Str(self.argv[index])
748
749 def GetArgv(self):
750 # type: () -> List[str]
751 return self.argv[self.num_shifted:]
752
753 def GetNumArgs(self):
754 # type: () -> int
755 return len(self.argv) - self.num_shifted
756
757 def SetArgv(self, argv):
758 # type: (List[str]) -> None
759 self.argv = argv
760 self.num_shifted = 0
761
762
763def _DumpVarFrame(frame):
764 # type: (Dict[str, Cell]) -> Dict[str, value_t]
765 """Dump the stack frame as reasonably compact and readable JSON."""
766
767 vars_json = {} # type: Dict[str, value_t]
768 for name, cell in iteritems(frame):
769 cell_json = {} # type: Dict[str, value_t]
770
771 buf = mylib.BufWriter()
772 if cell.exported:
773 buf.write('x')
774 if cell.readonly:
775 buf.write('r')
776 flags = buf.getvalue()
777 if len(flags):
778 cell_json['flags'] = value.Str(flags)
779
780 # TODO:
781 # - Use packle for crash dumps! Then we can represent object cycles
782 # - Right now the JSON serializer will probably crash
783 # - although BashArray and BashAssoc may need 'type' tags
784 # - they don't round trip correctly
785 # - maybe add value.Tombstone here or something?
786 # - value.{Func,Eggex,...} may have value.Tombstone and
787 # vm.ValueIdString()?
788
789 with tagswitch(cell.val) as case:
790 if case(value_e.Undef):
791 cell_json['val'] = value.Null
792
793 elif case(value_e.Str, value_e.BashArray, value_e.BashAssoc):
794 cell_json['val'] = cell.val
795
796 else:
797 # TODO: should we show the object ID here?
798 pass
799
800 vars_json[name] = value.Dict(cell_json)
801
802 return vars_json
803
804
805def _GetWorkingDir():
806 # type: () -> str
807 """Fallback for pwd and $PWD when there's no 'cd' and no inherited $PWD."""
808 try:
809 return posix.getcwd()
810 except (IOError, OSError) as e:
811 e_die("Can't determine working directory: %s" % pyutil.strerror(e))
812
813
814def _LineNumber(tok):
815 # type: (Optional[Token]) -> str
816 """ For $BASH_LINENO """
817 if tok is None:
818 return '-1'
819 return str(tok.line.line_num)
820
821
822def _AddCallToken(d, token):
823 # type: (Dict[str, value_t], Optional[Token]) -> None
824 if token is None:
825 return
826 d['call_source'] = value.Str(ui.GetLineSourceString(token.line))
827 d['call_line_num'] = num.ToBig(token.line.line_num)
828 d['call_line'] = value.Str(token.line.content)
829
830
831def _InitDefaults(mem):
832 # type: (Mem) -> None
833
834 # Default value; user may unset it.
835 # $ echo -n "$IFS" | python -c 'import sys;print repr(sys.stdin.read())'
836 # ' \t\n'
837 SetGlobalString(mem, 'IFS', split.DEFAULT_IFS)
838
839 # NOTE: Should we put these in a name_map for Oil?
840 SetGlobalString(mem, 'UID', str(posix.getuid()))
841 SetGlobalString(mem, 'EUID', str(posix.geteuid()))
842 SetGlobalString(mem, 'PPID', str(posix.getppid()))
843
844 SetGlobalString(mem, 'HOSTNAME', libc.gethostname())
845
846 # In bash, this looks like 'linux-gnu', 'linux-musl', etc. Scripts test
847 # for 'darwin' and 'freebsd' too. They generally don't like at 'gnu' or
848 # 'musl'. We don't have that info, so just make it 'linux'.
849 SetGlobalString(mem, 'OSTYPE', pyos.OsType())
850
851 # For getopts builtin
852 SetGlobalString(mem, 'OPTIND', '1')
853
854 # When xtrace_rich is off, this is just like '+ ', the shell default
855 SetGlobalString(mem, 'PS4', '${SHX_indent}${SHX_punct}${SHX_pid_str} ')
856
857 # bash-completion uses this. Value copied from bash. It doesn't integrate
858 # with 'readline' yet.
859 SetGlobalString(mem, 'COMP_WORDBREAKS', _READLINE_DELIMS)
860
861 # TODO on $HOME: bash sets it if it's a login shell and not in POSIX mode!
862 # if (login_shell == 1 && posixly_correct == 0)
863 # set_home_var ();
864
865
866def _InitVarsFromEnv(mem, environ):
867 # type: (Mem, Dict[str, str]) -> None
868
869 # This is the way dash and bash work -- at startup, they turn everything in
870 # 'environ' variable into shell variables. Bash has an export_env
871 # variable. Dash has a loop through environ in init.c
872 for n, v in iteritems(environ):
873 mem.SetNamed(location.LName(n),
874 value.Str(v),
875 scope_e.GlobalOnly,
876 flags=SetExport)
877
878 # If it's not in the environment, initialize it. This makes it easier to
879 # update later in MutableOpts.
880
881 # TODO: IFS, etc. should follow this pattern. Maybe need a SysCall
882 # interface? self.syscall.getcwd() etc.
883
884 val = mem.GetValue('SHELLOPTS')
885 if val.tag() == value_e.Undef:
886 SetGlobalString(mem, 'SHELLOPTS', '')
887 # Now make it readonly
888 mem.SetNamed(location.LName('SHELLOPTS'),
889 None,
890 scope_e.GlobalOnly,
891 flags=SetReadOnly)
892
893 # Usually we inherit PWD from the parent shell. When it's not set, we may
894 # compute it.
895 val = mem.GetValue('PWD')
896 if val.tag() == value_e.Undef:
897 SetGlobalString(mem, 'PWD', _GetWorkingDir())
898 # Now mark it exported, no matter what. This is one of few variables
899 # EXPORTED. bash and dash both do it. (e.g. env -i -- dash -c env)
900 mem.SetNamed(location.LName('PWD'),
901 None,
902 scope_e.GlobalOnly,
903 flags=SetExport)
904
905 val = mem.GetValue('PATH')
906 if val.tag() == value_e.Undef:
907 # Setting PATH to these two dirs match what zsh and mksh do. bash and dash
908 # add {,/usr/,/usr/local}/{bin,sbin}
909 SetGlobalString(mem, 'PATH', '/bin:/usr/bin')
910
911
912def InitMem(mem, environ, version_str):
913 # type: (Mem, Dict[str, str], str) -> None
914 """Initialize memory with shell defaults.
915
916 Other interpreters could have different builtin variables.
917 """
918 # TODO: REMOVE this legacy. ble.sh checks it!
919 SetGlobalString(mem, 'OIL_VERSION', version_str)
920
921 SetGlobalString(mem, 'OILS_VERSION', version_str)
922
923 # The source builtin understands '///' to mean "relative to embedded stdlib"
924 SetGlobalString(mem, 'LIB_OSH', '///osh')
925 SetGlobalString(mem, 'LIB_YSH', '///ysh')
926
927 # - C spells it NAN
928 # - JavaScript spells it NaN
929 # - Python 2 has float('nan'), while Python 3 has math.nan.
930 #
931 # - libc prints the strings 'nan' and 'inf'
932 # - Python 3 prints the strings 'nan' and 'inf'
933 # - JavaScript prints 'NaN' and 'Infinity', which is more stylized
934 _SetGlobalValue(mem, 'NAN', value.Float(pyutil.nan()))
935 _SetGlobalValue(mem, 'INFINITY', value.Float(pyutil.infinity()))
936
937 _InitDefaults(mem)
938 _InitVarsFromEnv(mem, environ)
939
940 # MUTABLE GLOBAL that's SEPARATE from $PWD. Used by the 'pwd' builtin, but
941 # it can't be modified by users.
942 val = mem.GetValue('PWD')
943 # should be true since it's exported
944 assert val.tag() == value_e.Str, val
945 pwd = cast(value.Str, val).s
946 mem.SetPwd(pwd)
947
948
949def InitInteractive(mem):
950 # type: (Mem) -> None
951 """Initialization that's only done in the interactive/headless shell."""
952
953 # Same default PS1 as bash
954 if mem.GetValue('PS1').tag() == value_e.Undef:
955 SetGlobalString(mem, 'PS1', r'\s-\v\$ ')
956
957
958class ctx_FuncCall(object):
959 """For func calls."""
960
961 def __init__(self, mem, func):
962 # type: (Mem, value.Func) -> None
963
964 frame = NewDict() # type: Dict[str, Cell]
965 mem.var_stack.append(frame)
966
967 mem.PushCall(func.name, func.parsed.name)
968 self.mem = mem
969
970 def __enter__(self):
971 # type: () -> None
972 pass
973
974 def __exit__(self, type, value, traceback):
975 # type: (Any, Any, Any) -> None
976 self.mem.PopCall()
977 self.mem.var_stack.pop()
978
979
980class ctx_ProcCall(object):
981 """For proc calls, including shell functions."""
982
983 def __init__(self, mem, mutable_opts, proc, argv):
984 # type: (Mem, MutableOpts, value.Proc, List[str]) -> None
985
986 # TODO:
987 # - argv stack shouldn't be used for procs
988 # - we can bind a real variable @A if we want
989 # - procs should be in the var namespace
990 #
991 # should we separate procs and shell functions?
992 # - dynamic scope is one difference
993 # - '$@" shift etc. are another difference
994
995 frame = NewDict() # type: Dict[str, Cell]
996
997 assert argv is not None
998 if proc.sh_compat:
999 # shell function
1000 mem.argv_stack.append(_ArgFrame(argv))
1001 else:
1002 # procs
1003 # - open: is equivalent to ...ARGV
1004 # - closed: ARGV is empty list
1005 frame['ARGV'] = _MakeArgvCell(argv)
1006
1007 mem.var_stack.append(frame)
1008
1009 mem.PushCall(proc.name, proc.name_tok)
1010
1011 # Dynamic scope is only for shell functions
1012 mutable_opts.PushDynamicScope(proc.sh_compat)
1013
1014 # It may have been disabled with ctx_ErrExit for 'if echo $(false)', but
1015 # 'if p' should be allowed.
1016 self.mem = mem
1017 self.mutable_opts = mutable_opts
1018 self.sh_compat = proc.sh_compat
1019
1020 def __enter__(self):
1021 # type: () -> None
1022 pass
1023
1024 def __exit__(self, type, value, traceback):
1025 # type: (Any, Any, Any) -> None
1026 self.mutable_opts.PopDynamicScope()
1027 self.mem.PopCall()
1028 self.mem.var_stack.pop()
1029
1030 if self.sh_compat:
1031 self.mem.argv_stack.pop()
1032
1033
1034class ctx_Temp(object):
1035 """For FOO=bar myfunc, etc."""
1036
1037 def __init__(self, mem):
1038 # type: (Mem) -> None
1039 mem.PushTemp()
1040 self.mem = mem
1041
1042 def __enter__(self):
1043 # type: () -> None
1044 pass
1045
1046 def __exit__(self, type, value, traceback):
1047 # type: (Any, Any, Any) -> None
1048 self.mem.PopTemp()
1049
1050
1051class ctx_Registers(object):
1052 """For $PS1, $PS4, $PROMPT_COMMAND, traps, and headless EVAL.
1053
1054 This is tightly coupled to state.Mem, so it's not in builtin/pure_ysh.
1055 """
1056
1057 def __init__(self, mem):
1058 # type: (Mem) -> None
1059
1060 # Because some prompts rely on the status leaking. See issue #853.
1061 # PS1 also does.
1062 last = mem.last_status[-1]
1063 mem.last_status.append(last)
1064 mem.try_status.append(0)
1065 mem.try_error.append(value.Dict({}))
1066
1067 # TODO: We should also copy these values! Turn the whole thing into a
1068 # frame.
1069 mem.pipe_status.append([])
1070 mem.process_sub_status.append([])
1071
1072 mem.regex_match.append(regex_match.No)
1073
1074 self.mem = mem
1075
1076 def __enter__(self):
1077 # type: () -> None
1078 pass
1079
1080 def __exit__(self, type, value, traceback):
1081 # type: (Any, Any, Any) -> None
1082 self.mem.regex_match.pop()
1083
1084 self.mem.process_sub_status.pop()
1085 self.mem.pipe_status.pop()
1086
1087 self.mem.try_error.pop()
1088 self.mem.try_status.pop()
1089 self.mem.last_status.pop()
1090
1091
1092class ctx_ThisDir(object):
1093 """For $_this_dir."""
1094
1095 def __init__(self, mem, filename):
1096 # type: (Mem, Optional[str]) -> None
1097 self.do_pop = False
1098 if filename is not None: # script_name in main() may be -c, etc.
1099 d = os_path.dirname(os_path.abspath(filename))
1100 mem.this_dir.append(d)
1101 self.do_pop = True
1102
1103 self.mem = mem
1104
1105 def __enter__(self):
1106 # type: () -> None
1107 pass
1108
1109 def __exit__(self, type, value, traceback):
1110 # type: (Any, Any, Any) -> None
1111 if self.do_pop:
1112 self.mem.this_dir.pop()
1113
1114
1115def _MakeArgvCell(argv):
1116 # type: (List[str]) -> Cell
1117 items = [value.Str(a) for a in argv] # type: List[value_t]
1118 return Cell(False, False, False, value.List(items))
1119
1120
1121class Mem(object):
1122 """For storing variables.
1123
1124 Callers:
1125 User code: assigning and evaluating variables, in command context or
1126 arithmetic context.
1127 Completion engine: for COMP_WORDS, etc.
1128 Builtins call it implicitly: read, cd for $PWD, $OLDPWD, etc.
1129
1130 Modules: cmd_eval, word_eval, expr_eval, completion
1131 """
1132
1133 def __init__(self, dollar0, argv, arena, debug_stack):
1134 # type: (str, List[str], alloc.Arena, List[debug_frame_t]) -> None
1135 """
1136 Args:
1137 arena: currently unused
1138 """
1139 # circular dep initialized out of line
1140 self.exec_opts = None # type: optview.Exec
1141 self.unsafe_arith = None # type: sh_expr_eval.UnsafeArith
1142
1143 self.dollar0 = dollar0
1144 # If you only use YSH procs and funcs, this will remain at length 1.
1145 self.argv_stack = [_ArgFrame(argv)]
1146
1147 frame = NewDict() # type: Dict[str, Cell]
1148
1149 frame['ARGV'] = _MakeArgvCell(argv)
1150
1151 self.var_stack = [frame]
1152
1153 # The debug_stack isn't strictly necessary for execution. We use it
1154 # for crash dumps and for 3 parallel arrays: BASH_SOURCE, FUNCNAME, and
1155 # BASH_LINENO.
1156 self.debug_stack = debug_stack
1157
1158 self.pwd = None # type: Optional[str]
1159 self.seconds_start = time_.time()
1160
1161 self.token_for_line = None # type: Optional[Token]
1162 self.loc_for_expr = loc.Missing # type: loc_t
1163
1164 self.last_arg = '' # $_ is initially empty, NOT unset
1165 self.line_num = value.Str('')
1166
1167 # Done ONCE on initialization
1168 self.root_pid = posix.getpid()
1169
1170 # TODO:
1171 # - These are REGISTERS mutated by user code.
1172 # - Call it self.reg_stack? with ctx_Registers
1173 # - push-registers builtin
1174 self.last_status = [0] # type: List[int] # a stack
1175 self.try_status = [0] # type: List[int] # a stack
1176 self.try_error = [value.Dict({})] # type: List[value.Dict] # a stack
1177 self.pipe_status = [[]] # type: List[List[int]] # stack
1178 self.process_sub_status = [[]] # type: List[List[int]] # stack
1179
1180 # A stack but NOT a register?
1181 self.this_dir = [] # type: List[str]
1182 self.regex_match = [regex_match.No] # type: List[regex_match_t]
1183
1184 self.last_bg_pid = -1 # Uninitialized value mutable public variable
1185
1186 self.running_debug_trap = False # set by ctx_DebugTrap()
1187 self.running_err_trap = False # set by ctx_ErrTrap
1188 self.is_main = True # we start out in main
1189
1190 # For the ctx builtin
1191 self.ctx_stack = [] # type: List[Dict[str, value_t]]
1192
1193 def __repr__(self):
1194 # type: () -> str
1195 parts = [] # type: List[str]
1196 parts.append('<Mem')
1197 for i, frame in enumerate(self.var_stack):
1198 parts.append(' -- %d --' % i)
1199 for n, v in frame.iteritems():
1200 parts.append(' %s %s' % (n, v))
1201 parts.append('>')
1202 return '\n'.join(parts) + '\n'
1203
1204 def SetPwd(self, pwd):
1205 # type: (str) -> None
1206 """Used by builtins."""
1207 self.pwd = pwd
1208
1209 def ParsingChangesAllowed(self):
1210 # type: () -> bool
1211 """For checking that syntax options are only used at the top level."""
1212
1213 # DISALLOW proc calls : they push argv_stack, var_stack, debug_stack
1214 # ALLOW source foo.sh arg1: pushes argv_stack, debug_stack
1215 # ALLOW FOO=bar : pushes var_stack
1216 return len(self.var_stack) == 1 or len(self.argv_stack) == 1
1217
1218 def Dump(self):
1219 # type: () -> Tuple[List[value_t], List[value_t], List[value_t]]
1220 """Copy state before unwinding the stack."""
1221 var_stack = [
1222 value.Dict(_DumpVarFrame(frame)) for frame in self.var_stack
1223 ] # type: List[value_t]
1224 argv_stack = [value.Dict(frame.Dump())
1225 for frame in self.argv_stack] # type: List[value_t]
1226
1227 debug_stack = [] # type: List[value_t]
1228
1229 # Reuse these immutable objects
1230 t_call = value.Str('Call')
1231 t_source = value.Str('Source')
1232 t_main = value.Str('Main')
1233
1234 for frame in reversed(self.debug_stack):
1235 UP_frame = frame
1236 with tagswitch(frame) as case:
1237 if case(debug_frame_e.Call):
1238 frame = cast(debug_frame.Call, UP_frame)
1239 d = {
1240 'type': t_call,
1241 'func_name': value.Str(frame.func_name)
1242 } # type: Dict[str, value_t]
1243
1244 _AddCallToken(d, frame.call_tok)
1245 # TODO: Add def_tok
1246
1247 elif case(debug_frame_e.Source):
1248 frame = cast(debug_frame.Source, UP_frame)
1249 d = {
1250 'type': t_source,
1251 'source_name': value.Str(frame.source_name)
1252 }
1253 _AddCallToken(d, frame.call_tok)
1254
1255 elif case(debug_frame_e.Main):
1256 frame = cast(debug_frame.Main, UP_frame)
1257 d = {'type': t_main, 'dollar0': value.Str(frame.dollar0)}
1258
1259 debug_stack.append(value.Dict(d))
1260 return var_stack, argv_stack, debug_stack
1261
1262 def SetLastArgument(self, s):
1263 # type: (str) -> None
1264 """For $_"""
1265 self.last_arg = s
1266
1267 def SetTokenForLine(self, tok):
1268 # type: (Token) -> None
1269 """Set a token to compute $LINENO
1270
1271 This means it should be set on SimpleCommand, ShAssignment, ((, [[,
1272 case, etc. -- anything that evaluates a word. Example: there was a bug
1273 with 'case $LINENO'
1274
1275 This token also used as a "least-specific" / fallback location for
1276 errors in ExecuteAndCatch().
1277
1278 Although most of that should be taken over by 'with ui.ctx_Location()`,
1279 for the errfmt.
1280 """
1281 if self.running_debug_trap or self.running_err_trap:
1282 return
1283
1284 #if tok.span_id == runtime.NO_SPID:
1285 # NOTE: This happened in the osh-runtime benchmark for yash.
1286 #log('Warning: span_id undefined in SetTokenForLine')
1287
1288 #import traceback
1289 #traceback.print_stack()
1290 #return
1291
1292 self.token_for_line = tok
1293
1294 def SetLocationForExpr(self, blame_loc):
1295 # type: (loc_t) -> None
1296 """
1297 A more specific fallback location, like the $[ in
1298
1299 echo $[len(42)]
1300 """
1301 self.loc_for_expr = blame_loc
1302
1303 def GetFallbackLocation(self):
1304 # type: () -> loc_t
1305
1306 if self.loc_for_expr != loc.Missing: # more specific
1307 return self.loc_for_expr
1308
1309 if self.token_for_line: # less specific
1310 return self.token_for_line
1311
1312 return loc.Missing
1313
1314 #
1315 # Status Variable Stack (for isolating $PS1 and $PS4)
1316 #
1317
1318 def LastStatus(self):
1319 # type: () -> int
1320 return self.last_status[-1]
1321
1322 def TryStatus(self):
1323 # type: () -> int
1324 return self.try_status[-1]
1325
1326 def TryError(self):
1327 # type: () -> value.Dict
1328 return self.try_error[-1]
1329
1330 def PipeStatus(self):
1331 # type: () -> List[int]
1332 return self.pipe_status[-1]
1333
1334 def SetLastStatus(self, x):
1335 # type: (int) -> None
1336 self.last_status[-1] = x
1337
1338 def SetTryStatus(self, x):
1339 # type: (int) -> None
1340 self.try_status[-1] = x
1341
1342 def SetTryError(self, x):
1343 # type: (value.Dict) -> None
1344 self.try_error[-1] = x
1345
1346 def SetPipeStatus(self, x):
1347 # type: (List[int]) -> None
1348 self.pipe_status[-1] = x
1349
1350 def SetSimplePipeStatus(self, status):
1351 # type: (int) -> None
1352
1353 # Optimization to avoid allocations
1354 top = self.pipe_status[-1]
1355 if len(top) == 1:
1356 top[0] = status
1357 else:
1358 self.pipe_status[-1] = [status]
1359
1360 def SetProcessSubStatus(self, x):
1361 # type: (List[int]) -> None
1362 self.process_sub_status[-1] = x
1363
1364 #
1365 # Call Stack
1366 #
1367
1368 def PushCall(self, func_name, def_tok):
1369 # type: (str, Token) -> None
1370 """Push argv, var, and debug stack frames.
1371
1372 Currently used for proc and func calls. TODO: New func evaluator may
1373 not use it.
1374
1375 Args:
1376 def_tok: Token where proc or func was defined, used to compute
1377 BASH_SOURCE.
1378 """
1379 # self.token_for_line can be None?
1380 self.debug_stack.append(
1381 debug_frame.Call(self.token_for_line, def_tok, func_name))
1382
1383 def PopCall(self):
1384 # type: () -> None
1385 """
1386 Args:
1387 should_pop_argv_stack: Pass False if PushCall was given None for argv
1388 True for proc, False for func
1389 """
1390 self.debug_stack.pop()
1391
1392 def ShouldRunDebugTrap(self):
1393 # type: () -> bool
1394
1395 # TODO: RunLastPart of pipeline can disable this
1396
1397 # Don't recursively run DEBUG trap
1398 if self.running_debug_trap:
1399 return False
1400
1401 # Don't run it inside functions
1402 if len(self.var_stack) > 1:
1403 return False
1404
1405 return True
1406
1407 def InsideFunction(self):
1408 # type: () -> bool
1409 """For the ERR trap"""
1410
1411 # Don't run it inside functions
1412 return len(self.var_stack) > 1
1413
1414 def PushSource(self, source_name, argv):
1415 # type: (str, List[str]) -> None
1416 """ For 'source foo.sh 1 2 3' """
1417 if len(argv):
1418 self.argv_stack.append(_ArgFrame(argv))
1419
1420 # self.token_for_line can be None?
1421 self.debug_stack.append(
1422 debug_frame.Source(self.token_for_line, source_name))
1423
1424 def PopSource(self, argv):
1425 # type: (List[str]) -> None
1426 self.debug_stack.pop()
1427
1428 if len(argv):
1429 self.argv_stack.pop()
1430
1431 def PushTemp(self):
1432 # type: () -> None
1433 """For the temporary scope in 'FOO=bar BAR=baz echo'.
1434
1435 Also for PS4 evaluation with more variables.
1436 """
1437 # We don't want the 'read' builtin to write to this frame!
1438 frame = NewDict() # type: Dict[str, Cell]
1439 self.var_stack.append(frame)
1440
1441 def PopTemp(self):
1442 # type: () -> None
1443 self.var_stack.pop()
1444
1445 def TopNamespace(self):
1446 # type: () -> Dict[str, Cell]
1447 """For eval_to_dict()."""
1448 return self.var_stack[-1]
1449
1450 #
1451 # Argv
1452 #
1453
1454 def Shift(self, n):
1455 # type: (int) -> int
1456 frame = self.argv_stack[-1]
1457 num_args = len(frame.argv)
1458
1459 if (frame.num_shifted + n) <= num_args:
1460 frame.num_shifted += n
1461 return 0 # success
1462 else:
1463 return 1 # silent error
1464
1465 def GetArg0(self):
1466 # type: () -> value.Str
1467 """Like GetArgNum(0) but with a more specific type."""
1468 return value.Str(self.dollar0)
1469
1470 def GetArgNum(self, arg_num):
1471 # type: (int) -> value_t
1472 if arg_num == 0:
1473 # $0 may be overriden, eg. by Str => replace()
1474 vars = self.var_stack[-1]
1475 if "0" in vars and vars["0"].val.tag() != value_e.Undef:
1476 return vars["0"].val
1477 return value.Str(self.dollar0)
1478
1479 return self.argv_stack[-1].GetArgNum(arg_num)
1480
1481 def GetArgv(self):
1482 # type: () -> List[str]
1483 """For $* and $@."""
1484 return self.argv_stack[-1].GetArgv()
1485
1486 def SetArgv(self, argv):
1487 # type: (List[str]) -> None
1488 """For set -- 1 2 3."""
1489 # from set -- 1 2 3
1490 self.argv_stack[-1].SetArgv(argv)
1491
1492 #
1493 # Special Vars
1494 #
1495
1496 def GetSpecialVar(self, op_id):
1497 # type: (int) -> value_t
1498 if op_id == Id.VSub_Bang: # $!
1499 n = self.last_bg_pid
1500 if n == -1:
1501 return value.Undef # could be an error
1502
1503 elif op_id == Id.VSub_QMark: # $?
1504 # External commands need WIFEXITED test. What about subshells?
1505 n = self.last_status[-1]
1506
1507 elif op_id == Id.VSub_Pound: # $#
1508 n = self.argv_stack[-1].GetNumArgs()
1509
1510 elif op_id == Id.VSub_Dollar: # $$
1511 n = self.root_pid
1512
1513 else:
1514 raise NotImplementedError(op_id)
1515
1516 return value.Str(str(n))
1517
1518 #
1519 # Named Vars
1520 #
1521
1522 def _ResolveNameOnly(self, name, which_scopes):
1523 # type: (str, scope_t) -> Tuple[Optional[Cell], Dict[str, Cell]]
1524 """Helper for getting and setting variable.
1525
1526 Returns:
1527 cell: The cell corresponding to looking up 'name' with the given mode, or
1528 None if it's not found.
1529 name_map: The name_map it should be set to or deleted from.
1530 """
1531 if which_scopes == scope_e.Dynamic:
1532 for i in xrange(len(self.var_stack) - 1, -1, -1):
1533 name_map = self.var_stack[i]
1534 if name in name_map:
1535 cell = name_map[name]
1536 return cell, name_map
1537 no_cell = None # type: Optional[Cell]
1538 return no_cell, self.var_stack[0] # set in global name_map
1539
1540 if which_scopes == scope_e.LocalOnly:
1541 name_map = self.var_stack[-1]
1542 return name_map.get(name), name_map
1543
1544 if which_scopes == scope_e.GlobalOnly:
1545 name_map = self.var_stack[0]
1546 return name_map.get(name), name_map
1547
1548 if which_scopes == scope_e.LocalOrGlobal:
1549 # Local
1550 name_map = self.var_stack[-1]
1551 cell = name_map.get(name)
1552 if cell:
1553 return cell, name_map
1554
1555 # Global
1556 name_map = self.var_stack[0]
1557 return name_map.get(name), name_map
1558
1559 raise AssertionError()
1560
1561 def _ResolveNameOrRef(
1562 self,
1563 name, # type: str
1564 which_scopes, # type: scope_t
1565 ref_trail=None, # type: Optional[List[str]]
1566 ):
1567 # type: (...) -> Tuple[Optional[Cell], Dict[str, Cell], str]
1568 """Look up a cell and namespace, but respect the nameref flag.
1569
1570 Resolving namerefs does RECURSIVE calls.
1571 """
1572 cell, name_map = self._ResolveNameOnly(name, which_scopes)
1573
1574 if cell is None or not cell.nameref:
1575 return cell, name_map, name # not a nameref
1576
1577 val = cell.val
1578 UP_val = val
1579 with tagswitch(val) as case:
1580 if case(value_e.Undef):
1581 # This is 'local -n undef_ref', which is kind of useless, because the
1582 # more common idiom is 'local -n ref=$1'. Note that you can mutate
1583 # references themselves with local -n ref=new.
1584 if self.exec_opts.strict_nameref():
1585 e_die('nameref %r is undefined' % name)
1586 else:
1587 return cell, name_map, name # fallback
1588
1589 elif case(value_e.Str):
1590 val = cast(value.Str, UP_val)
1591 new_name = val.s
1592
1593 else:
1594 # SetValue() protects the invariant that nameref is Undef or Str
1595 raise AssertionError(val.tag())
1596
1597 # TODO: Respect eval_unsafe_arith here (issue 881). See how it's done in
1598 # 'printf -v' with MakeArithParser
1599 if not match.IsValidVarName(new_name):
1600 # e.g. '#' or '1' or ''
1601 if self.exec_opts.strict_nameref():
1602 e_die('nameref %r contains invalid variable name %r' %
1603 (name, new_name))
1604 else:
1605 # Bash has this odd behavior of clearing the nameref bit when
1606 # ref=#invalid#. strict_nameref avoids it.
1607 cell.nameref = False
1608 return cell, name_map, name # fallback
1609
1610 # Check for circular namerefs.
1611 if ref_trail is None:
1612 ref_trail = [name]
1613 else:
1614 if new_name in ref_trail:
1615 e_die('Circular nameref %s' % ' -> '.join(ref_trail))
1616 ref_trail.append(new_name)
1617
1618 # 'declare -n' uses dynamic scope.
1619 cell, name_map, cell_name = self._ResolveNameOrRef(new_name,
1620 scope_e.Dynamic,
1621 ref_trail=ref_trail)
1622 return cell, name_map, cell_name
1623
1624 def IsBashAssoc(self, name):
1625 # type: (str) -> bool
1626 """Returns whether a name resolve to a cell with an associative array.
1627
1628 We need to know this to evaluate the index expression properly
1629 -- should it be coerced to an integer or not?
1630 """
1631 cell, _, _ = self._ResolveNameOrRef(name, self.ScopesForReading())
1632 # foo=([key]=value)
1633 return cell is not None and cell.val.tag() == value_e.BashAssoc
1634
1635 def SetPlace(self, place, val, blame_loc):
1636 # type: (value.Place, value_t, loc_t) -> None
1637
1638 yval = place.lval
1639 UP_yval = yval
1640 with tagswitch(yval) as case:
1641 if case(y_lvalue_e.Local):
1642 yval = cast(LeftName, UP_yval)
1643
1644 # Check that the frame is still alive
1645 found = False
1646 for i in xrange(len(self.var_stack) - 1, -1, -1):
1647 frame = self.var_stack[i]
1648 if frame is place.frame:
1649 found = True
1650 #log('FOUND %s', found)
1651 break
1652 if not found:
1653 e_die(
1654 "Can't assign to place that's no longer on the call stack.",
1655 blame_loc)
1656
1657 cell = frame.get(yval.name)
1658 if cell is None:
1659 cell = Cell(False, False, False, val)
1660 frame[yval.name] = cell
1661 else:
1662 cell.val = val
1663
1664 elif case(y_lvalue_e.Container):
1665 e_die('Container place not implemented', blame_loc)
1666
1667 else:
1668 raise AssertionError()
1669
1670 def SetLocalName(self, lval, val):
1671 # type: (LeftName, value_t) -> None
1672
1673 # Equivalent to
1674 # self._ResolveNameOnly(lval.name, scope_e.LocalOnly)
1675 name_map = self.var_stack[-1]
1676 cell = name_map.get(lval.name)
1677
1678 if cell:
1679 if cell.readonly:
1680 e_die("Can't assign to readonly value %r" % lval.name,
1681 lval.blame_loc)
1682 cell.val = val # Mutate value_t
1683 else:
1684 cell = Cell(False, False, False, val)
1685 name_map[lval.name] = cell
1686
1687 def SetNamed(self, lval, val, which_scopes, flags=0):
1688 # type: (LeftName, value_t, scope_t, int) -> None
1689
1690 if flags & SetNameref or flags & ClearNameref:
1691 # declare -n ref=x # refers to the ref itself
1692 cell, name_map = self._ResolveNameOnly(lval.name, which_scopes)
1693 cell_name = lval.name
1694 else:
1695 # ref=x # mutates THROUGH the reference
1696
1697 # Note on how to implement declare -n ref='a[42]'
1698 # 1. Call _ResolveNameOnly()
1699 # 2. If cell.nameref, call self.unsafe_arith.ParseVarRef() ->
1700 # BracedVarSub
1701 # 3. Turn BracedVarSub into an sh_lvalue, and call
1702 # self.unsafe_arith.SetValue() wrapper with ref_trail
1703 cell, name_map, cell_name = self._ResolveNameOrRef(
1704 lval.name, which_scopes)
1705
1706 if cell:
1707 # Clear before checking readonly bit.
1708 # NOTE: Could be cell.flags &= flag_clear_mask
1709 if flags & ClearExport:
1710 cell.exported = False
1711 if flags & ClearReadOnly:
1712 cell.readonly = False
1713 if flags & ClearNameref:
1714 cell.nameref = False
1715
1716 if val is not None: # e.g. declare -rx existing
1717 # Note: this DYNAMIC check means we can't have 'const' in a loop.
1718 # But that's true for 'readonly' too, and hoisting it makes more
1719 # sense anyway.
1720 if cell.readonly:
1721 e_die("Can't assign to readonly value %r" % lval.name,
1722 lval.blame_loc)
1723 cell.val = val # CHANGE VAL
1724
1725 # NOTE: Could be cell.flags |= flag_set_mask
1726 if flags & SetExport:
1727 cell.exported = True
1728 if flags & SetReadOnly:
1729 cell.readonly = True
1730 if flags & SetNameref:
1731 cell.nameref = True
1732
1733 else:
1734 if val is None: # declare -rx nonexistent
1735 # set -o nounset; local foo; echo $foo # It's still undefined!
1736 val = value.Undef # export foo, readonly foo
1737
1738 cell = Cell(bool(flags & SetExport), bool(flags & SetReadOnly),
1739 bool(flags & SetNameref), val)
1740 name_map[cell_name] = cell
1741
1742 # Maintain invariant that only strings and undefined cells can be
1743 # exported.
1744 assert cell.val is not None, cell
1745
1746 if cell.val.tag() not in (value_e.Undef, value_e.Str):
1747 if cell.exported:
1748 if self.exec_opts.strict_array():
1749 e_die("Only strings can be exported (strict_array)",
1750 lval.blame_loc)
1751 if cell.nameref:
1752 e_die("nameref must be a string", lval.blame_loc)
1753
1754 def SetValue(self, lval, val, which_scopes, flags=0):
1755 # type: (sh_lvalue_t, value_t, scope_t, int) -> None
1756 """
1757 Args:
1758 lval: sh_lvalue
1759 val: value, or None if only changing flags
1760 which_scopes:
1761 Local | Global | Dynamic - for builtins, PWD, etc.
1762 flags: packed pair (keyword_id, bit mask of set/clear flags)
1763
1764 Note: in bash, PWD=/ changes the directory. But not in dash.
1765 """
1766 # STRICTNESS / SANENESS:
1767 #
1768 # 1) Don't create arrays automatically, e.g. a[1000]=x
1769 # 2) Never change types? yeah I think that's a good idea, at least for oil
1770 # (not sh, for compatibility). set -o strict_types or something. That
1771 # means arrays have to be initialized with let arr = [], which is fine.
1772 # This helps with stuff like IFS. It starts off as a string, and assigning
1773 # it to a list is an error. I guess you will have to turn this no for
1774 # bash?
1775 #
1776 # TODO:
1777 # - COMPUTED vars can't be set
1778 # - What about PWD / OLDPWD / UID / EUID ? You can simply make them
1779 # readonly.
1780 # - Maybe PARSE $PS1 and $PS4 when they're set, to avoid the error on use?
1781 # - Other validity: $HOME could be checked for existence
1782
1783 UP_lval = lval
1784 with tagswitch(lval) as case:
1785 if case(sh_lvalue_e.Var):
1786 lval = cast(LeftName, UP_lval)
1787
1788 self.SetNamed(lval, val, which_scopes, flags=flags)
1789
1790 elif case(sh_lvalue_e.Indexed):
1791 lval = cast(sh_lvalue.Indexed, UP_lval)
1792
1793 # There is no syntax 'declare a[x]'
1794 assert val is not None, val
1795
1796 # TODO: relax this for Oil
1797 assert val.tag() == value_e.Str, val
1798 rval = cast(value.Str, val)
1799
1800 # Note: location could be a[x]=1 or (( a[ x ] = 1 ))
1801 left_loc = lval.blame_loc
1802
1803 # bash/mksh have annoying behavior of letting you do LHS assignment to
1804 # Undef, which then turns into an INDEXED array. (Undef means that set
1805 # -o nounset fails.)
1806 cell, name_map, _ = self._ResolveNameOrRef(
1807 lval.name, which_scopes)
1808 if not cell:
1809 self._BindNewArrayWithEntry(name_map, lval, rval, flags)
1810 return
1811
1812 if cell.readonly:
1813 e_die("Can't assign to readonly array", left_loc)
1814
1815 UP_cell_val = cell.val
1816 # undef[0]=y is allowed
1817 with tagswitch(UP_cell_val) as case2:
1818 if case2(value_e.Undef):
1819 self._BindNewArrayWithEntry(name_map, lval, rval,
1820 flags)
1821 return
1822
1823 elif case2(value_e.Str):
1824 # s=x
1825 # s[1]=y # invalid
1826 e_die("Can't assign to items in a string", left_loc)
1827
1828 elif case2(value_e.BashArray):
1829 cell_val = cast(value.BashArray, UP_cell_val)
1830 strs = cell_val.strs
1831
1832 n = len(strs)
1833 index = lval.index
1834 if index < 0: # a[-1]++ computes this twice; could we avoid it?
1835 index += n
1836
1837 if 0 <= index and index < n:
1838 strs[index] = rval.s
1839 else:
1840 # Fill it in with None. It could look like this:
1841 # ['1', 2, 3, None, None, '4', None]
1842 # Then ${#a[@]} counts the entries that are not None.
1843 #
1844 # TODO: strict_array for Oil arrays won't auto-fill.
1845 n = index - len(strs) + 1
1846 for i in xrange(n):
1847 strs.append(None)
1848 strs[lval.index] = rval.s
1849 return
1850
1851 # This could be an object, eggex object, etc. It won't be
1852 # BashAssoc shouldn because we query IsBashAssoc before evaluating
1853 # sh_lhs. Could conslidate with s[i] case above
1854 e_die(
1855 "Value of type %s can't be indexed" % ui.ValType(cell.val),
1856 left_loc)
1857
1858 elif case(sh_lvalue_e.Keyed):
1859 lval = cast(sh_lvalue.Keyed, UP_lval)
1860
1861 # There is no syntax 'declare A["x"]'
1862 assert val is not None, val
1863 assert val.tag() == value_e.Str, val
1864 rval = cast(value.Str, val)
1865
1866 left_loc = lval.blame_loc
1867
1868 cell, name_map, _ = self._ResolveNameOrRef(
1869 lval.name, which_scopes)
1870 if cell.readonly:
1871 e_die("Can't assign to readonly associative array",
1872 left_loc)
1873
1874 # We already looked it up before making the sh_lvalue
1875 assert cell.val.tag() == value_e.BashAssoc, cell
1876 cell_val2 = cast(value.BashAssoc, cell.val)
1877
1878 cell_val2.d[lval.key] = rval.s
1879
1880 else:
1881 raise AssertionError(lval.tag())
1882
1883 def _BindNewArrayWithEntry(self, name_map, lval, val, flags):
1884 # type: (Dict[str, Cell], sh_lvalue.Indexed, value.Str, int) -> None
1885 """Fill 'name_map' with a new indexed array entry."""
1886 no_str = None # type: Optional[str]
1887 items = [no_str] * lval.index
1888 items.append(val.s)
1889 new_value = value.BashArray(items)
1890
1891 # arrays can't be exported; can't have BashAssoc flag
1892 readonly = bool(flags & SetReadOnly)
1893 name_map[lval.name] = Cell(False, readonly, False, new_value)
1894
1895 def InternalSetGlobal(self, name, new_val):
1896 # type: (str, value_t) -> None
1897 """For setting read-only globals internally.
1898
1899 Args:
1900 name: string (not Lhs)
1901 new_val: value
1902
1903 The variable must already exist.
1904
1905 Use case: SHELLOPTS.
1906 """
1907 cell = self.var_stack[0][name]
1908 cell.val = new_val
1909
1910 def GetValue(self, name, which_scopes=scope_e.Shopt):
1911 # type: (str, scope_t) -> value_t
1912 """Used by the WordEvaluator, ArithEvaluator, ExprEvaluator, etc."""
1913 assert isinstance(name, str), name
1914
1915 if which_scopes == scope_e.Shopt:
1916 which_scopes = self.ScopesForReading()
1917
1918 with str_switch(name) as case:
1919 # "Registers"
1920 if case('_status'):
1921 return num.ToBig(self.TryStatus())
1922
1923 elif case('_error'):
1924 return self.TryError()
1925
1926 elif case('_this_dir'):
1927 if len(self.this_dir) == 0:
1928 # e.g. osh -c '' doesn't have it set
1929 # Should we give a custom error here?
1930 # If you're at the interactive shell, 'source mymodule.oil' will still
1931 # work because 'source' sets it.
1932 return value.Undef
1933 else:
1934 return value.Str(self.this_dir[-1]) # top of stack
1935
1936 elif case('PIPESTATUS'):
1937 strs2 = [str(i)
1938 for i in self.pipe_status[-1]] # type: List[str]
1939 return value.BashArray(strs2)
1940
1941 elif case('_pipeline_status'):
1942 items = [num.ToBig(i)
1943 for i in self.pipe_status[-1]] # type: List[value_t]
1944 return value.List(items)
1945
1946 elif case('_process_sub_status'): # YSH naming convention
1947 items = [num.ToBig(i) for i in self.process_sub_status[-1]]
1948 return value.List(items)
1949
1950 elif case('BASH_REMATCH'):
1951 top_match = self.regex_match[-1]
1952 with tagswitch(top_match) as case2:
1953 if case2(regex_match_e.No):
1954 groups = [] # type: List[str]
1955 elif case2(regex_match_e.Yes):
1956 m = cast(RegexMatch, top_match)
1957 groups = util.RegexGroupStrings(m.s, m.indices)
1958 return value.BashArray(groups)
1959
1960 # Do lookup of system globals before looking at user variables. Note: we
1961 # could optimize this at compile-time like $?. That would break
1962 # ${!varref}, but it's already broken for $?.
1963
1964 elif case('FUNCNAME'):
1965 # bash wants it in reverse order. This is a little inefficient but we're
1966 # not depending on deque().
1967 strs = [] # type: List[str]
1968 for frame in reversed(self.debug_stack):
1969 UP_frame = frame
1970 with tagswitch(frame) as case2:
1971 if case2(debug_frame_e.Call):
1972 frame = cast(debug_frame.Call, UP_frame)
1973 strs.append(frame.func_name)
1974
1975 elif case2(debug_frame_e.Source):
1976 # bash doesn't tell you the filename sourced
1977 strs.append('source')
1978
1979 elif case2(debug_frame_e.Main):
1980 strs.append('main') # also bash behavior
1981
1982 return value.BashArray(strs) # TODO: Reuse this object too?
1983
1984 # $BASH_SOURCE and $BASH_LINENO have OFF BY ONE design bugs:
1985 #
1986 # ${BASH_LINENO[$i]} is the line number in the source file
1987 # (${BASH_SOURCE[$i+1]}) where ${FUNCNAME[$i]} was called (or
1988 # ${BASH_LINENO[$i-1]} if referenced within another shell function).
1989 #
1990 # https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
1991
1992 elif case('BASH_SOURCE'):
1993 strs = []
1994 for frame in reversed(self.debug_stack):
1995 UP_frame = frame
1996 with tagswitch(frame) as case2:
1997 if case2(debug_frame_e.Call):
1998 frame = cast(debug_frame.Call, UP_frame)
1999
2000 # Weird bash behavior
2001 assert frame.def_tok.line is not None
2002 source_str = ui.GetLineSourceString(
2003 frame.def_tok.line)
2004 strs.append(source_str)
2005
2006 elif case2(debug_frame_e.Source):
2007 frame = cast(debug_frame.Source, UP_frame)
2008 # Is this right?
2009 strs.append(frame.source_name)
2010
2011 elif case2(debug_frame_e.Main):
2012 frame = cast(debug_frame.Main, UP_frame)
2013 strs.append(frame.dollar0)
2014
2015 return value.BashArray(strs) # TODO: Reuse this object too?
2016
2017 elif case('BASH_LINENO'):
2018 strs = []
2019 for frame in reversed(self.debug_stack):
2020 UP_frame = frame
2021 with tagswitch(frame) as case2:
2022 if case2(debug_frame_e.Call):
2023 frame = cast(debug_frame.Call, UP_frame)
2024 strs.append(_LineNumber(frame.call_tok))
2025
2026 elif case2(debug_frame_e.Source):
2027 frame = cast(debug_frame.Source, UP_frame)
2028 strs.append(_LineNumber(frame.call_tok))
2029
2030 elif case2(debug_frame_e.Main):
2031 # Bash does this to line up with 'main'
2032 strs.append('0')
2033
2034 return value.BashArray(strs) # TODO: Reuse this object too?
2035
2036 elif case('LINENO'):
2037 assert self.token_for_line is not None
2038 # Reuse object with mutation
2039 # TODO: maybe use interned GetLineNumStr?
2040 self.line_num.s = str(self.token_for_line.line.line_num)
2041 return self.line_num
2042
2043 elif case('BASHPID'): # TODO: YSH io->getpid()
2044 return value.Str(str(posix.getpid()))
2045
2046 elif case('_'):
2047 return value.Str(self.last_arg)
2048
2049 elif case('SECONDS'):
2050 return value.Int(
2051 mops.FromFloat(time_.time() - self.seconds_start))
2052
2053 else:
2054 # In the case 'declare -n ref='a[42]', the result won't be a cell. Idea to
2055 # fix this:
2056 # 1. Call self.unsafe_arith.ParseVarRef() -> BracedVarSub
2057 # 2. Call self.unsafe_arith.GetNameref(bvs_part), and get a value_t
2058 # We still need a ref_trail to detect cycles.
2059 cell, _, _ = self._ResolveNameOrRef(name, which_scopes)
2060 if cell:
2061 return cell.val
2062
2063 return value.Undef
2064
2065 def GetCell(self, name, which_scopes=scope_e.Shopt):
2066 # type: (str, scope_t) -> Cell
2067 """Get both the value and flags.
2068
2069 Usages:
2070 - the 'pp' builtin.
2071 - declare -p
2072 - ${x@a}
2073 - to test of 'TZ' is exported in printf? Why?
2074 """
2075 if which_scopes == scope_e.Shopt:
2076 which_scopes = self.ScopesForReading()
2077
2078 cell, _ = self._ResolveNameOnly(name, which_scopes)
2079 return cell
2080
2081 def Unset(self, lval, which_scopes):
2082 # type: (sh_lvalue_t, scope_t) -> bool
2083 """
2084 Returns:
2085 Whether the cell was found.
2086 """
2087 # TODO: Refactor sh_lvalue type to avoid this
2088 UP_lval = lval
2089
2090 with tagswitch(lval) as case:
2091 if case(sh_lvalue_e.Var): # unset x
2092 lval = cast(LeftName, UP_lval)
2093 var_name = lval.name
2094 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2095 lval = cast(sh_lvalue.Indexed, UP_lval)
2096 var_name = lval.name
2097 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2098 lval = cast(sh_lvalue.Keyed, UP_lval)
2099 var_name = lval.name
2100 else:
2101 raise AssertionError()
2102
2103 if which_scopes == scope_e.Shopt:
2104 which_scopes = self.ScopesForWriting()
2105
2106 cell, name_map, cell_name = self._ResolveNameOrRef(
2107 var_name, which_scopes)
2108 if not cell:
2109 return False # 'unset' builtin falls back on functions
2110 if cell.readonly:
2111 raise error.Runtime("Can't unset readonly variable %r" % var_name)
2112
2113 with tagswitch(lval) as case:
2114 if case(sh_lvalue_e.Var): # unset x
2115 # Make variables in higher scopes visible.
2116 # example: test/spec.sh builtin-vars -r 24 (ble.sh)
2117 mylib.dict_erase(name_map, cell_name)
2118
2119 # alternative that some shells use:
2120 # name_map[cell_name].val = value.Undef
2121 # cell.exported = False
2122
2123 # This should never happen because we do recursive lookups of namerefs.
2124 assert not cell.nameref, cell
2125
2126 elif case(sh_lvalue_e.Indexed): # unset 'a[1]'
2127 lval = cast(sh_lvalue.Indexed, UP_lval)
2128 # Note: Setting an entry to None and shifting entries are pretty
2129 # much the same in shell.
2130
2131 val = cell.val
2132 UP_val = val
2133 if val.tag() != value_e.BashArray:
2134 raise error.Runtime("%r isn't an array" % var_name)
2135
2136 val = cast(value.BashArray, UP_val)
2137 strs = val.strs
2138
2139 n = len(strs)
2140 last_index = n - 1
2141 index = lval.index
2142 if index < 0:
2143 index += n
2144
2145 if index == last_index:
2146 # Special case: The array SHORTENS if you unset from the end. You
2147 # can tell with a+=(3 4)
2148 strs.pop()
2149 elif 0 <= index and index < last_index:
2150 strs[index] = None
2151 else:
2152 # If it's not found, it's not an error. In other words, 'unset'
2153 # ensures that a value doesn't exist, regardless of whether it
2154 # existed. It's idempotent.
2155 # (Ousterhout specifically argues that the strict behavior was a
2156 # mistake for Tcl!)
2157 pass
2158
2159 elif case(sh_lvalue_e.Keyed): # unset 'A["K"]'
2160 lval = cast(sh_lvalue.Keyed, UP_lval)
2161
2162 val = cell.val
2163 UP_val = val
2164
2165 # note: never happens because of mem.IsBashAssoc test for sh_lvalue.Keyed
2166 #if val.tag() != value_e.BashAssoc:
2167 # raise error.Runtime("%r isn't an associative array" % lval.name)
2168
2169 val = cast(value.BashAssoc, UP_val)
2170 mylib.dict_erase(val.d, lval.key)
2171
2172 else:
2173 raise AssertionError(lval)
2174
2175 return True
2176
2177 def ScopesForReading(self):
2178 # type: () -> scope_t
2179 """Read scope."""
2180 return (scope_e.Dynamic
2181 if self.exec_opts.dynamic_scope() else scope_e.LocalOrGlobal)
2182
2183 def ScopesForWriting(self):
2184 # type: () -> scope_t
2185 """Write scope."""
2186 return (scope_e.Dynamic
2187 if self.exec_opts.dynamic_scope() else scope_e.LocalOnly)
2188
2189 def ClearFlag(self, name, flag):
2190 # type: (str, int) -> bool
2191 """Used for export -n.
2192
2193 We don't use SetValue() because even if rval is None, it will make an
2194 Undef value in a scope.
2195 """
2196 cell, name_map = self._ResolveNameOnly(name, self.ScopesForReading())
2197 if cell:
2198 if flag & ClearExport:
2199 cell.exported = False
2200 if flag & ClearNameref:
2201 cell.nameref = False
2202 return True
2203 else:
2204 return False
2205
2206 def GetExported(self):
2207 # type: () -> Dict[str, str]
2208 """Get all the variables that are marked exported."""
2209 # TODO: This is run on every SimpleCommand. Should we have a dirty flag?
2210 # We have to notice these things:
2211 # - If an exported variable is changed.
2212 # - If the set of exported variables changes.
2213
2214 exported = {} # type: Dict[str, str]
2215 # Search from globals up. Names higher on the stack will overwrite names
2216 # lower on the stack.
2217 for scope in self.var_stack:
2218 for name, cell in iteritems(scope):
2219 # TODO: Disallow exporting at assignment time. If an exported Str is
2220 # changed to BashArray, also clear its 'exported' flag.
2221 if cell.exported and cell.val.tag() == value_e.Str:
2222 val = cast(value.Str, cell.val)
2223 exported[name] = val.s
2224 return exported
2225
2226 def VarNames(self):
2227 # type: () -> List[str]
2228 """For internal OSH completion and compgen -A variable.
2229
2230 NOTE: We could also add $? $$ etc.?
2231 """
2232 ret = [] # type: List[str]
2233 # Look up the stack, yielding all variables. Bash seems to do this.
2234 for scope in self.var_stack:
2235 for name in scope:
2236 ret.append(name)
2237 return ret
2238
2239 def VarNamesStartingWith(self, prefix):
2240 # type: (str) -> List[str]
2241 """For ${!prefix@}"""
2242 # Look up the stack, yielding all variables. Bash seems to do this.
2243 names = [] # type: List[str]
2244 for scope in self.var_stack:
2245 for name in scope:
2246 if name.startswith(prefix):
2247 names.append(name)
2248 return names
2249
2250 def GetAllVars(self):
2251 # type: () -> Dict[str, str]
2252 """Get all variables and their values, for 'set' builtin."""
2253 result = {} # type: Dict[str, str]
2254 for scope in self.var_stack:
2255 for name, cell in iteritems(scope):
2256 # TODO: Show other types?
2257 val = cell.val
2258 if val.tag() == value_e.Str:
2259 str_val = cast(value.Str, val)
2260 result[name] = str_val.s
2261 return result
2262
2263 def GetAllCells(self, which_scopes):
2264 # type: (scope_t) -> Dict[str, Cell]
2265 """Get all variables and their values, for 'set' builtin."""
2266 result = {} # type: Dict[str, Cell]
2267
2268 if which_scopes == scope_e.Dynamic:
2269 scopes = self.var_stack
2270 elif which_scopes == scope_e.LocalOnly:
2271 scopes = self.var_stack[-1:]
2272 elif which_scopes == scope_e.GlobalOnly:
2273 scopes = self.var_stack[0:1]
2274 elif which_scopes == scope_e.LocalOrGlobal:
2275 scopes = [self.var_stack[0]]
2276 if len(self.var_stack) > 1:
2277 scopes.append(self.var_stack[-1])
2278 else:
2279 raise AssertionError()
2280
2281 for scope in scopes:
2282 for name, cell in iteritems(scope):
2283 result[name] = cell
2284 return result
2285
2286 def IsGlobalScope(self):
2287 # type: () -> bool
2288 return len(self.var_stack) == 1
2289
2290 def SetRegexMatch(self, match):
2291 # type: (regex_match_t) -> None
2292 self.regex_match[-1] = match
2293
2294 def GetRegexMatch(self):
2295 # type: () -> regex_match_t
2296 return self.regex_match[-1]
2297
2298 def PushContextStack(self, context):
2299 # type: (Dict[str, value_t]) -> None
2300 self.ctx_stack.append(context)
2301
2302 def GetContext(self):
2303 # type: () -> Optional[Dict[str, value_t]]
2304 if len(self.ctx_stack):
2305 return self.ctx_stack[-1]
2306 return None
2307
2308 def PopContextStack(self):
2309 # type: () -> Dict[str, value_t]
2310 assert self.ctx_stack, "Empty context stack"
2311 return self.ctx_stack.pop()
2312
2313
2314class Procs:
2315
2316 def __init__(self, mem):
2317 # type: (Mem) -> None
2318 self.mem = mem
2319 self.sh_funcs = {} # type: Dict[str, value.Proc]
2320
2321 def SetProc(self, name, proc):
2322 # type: (str, value.Proc) -> None
2323 self.mem.var_stack[0][name] = Cell(False, False, False, proc)
2324
2325 def SetShFunc(self, name, proc):
2326 # type: (str, value.Proc) -> None
2327 self.sh_funcs[name] = proc
2328
2329 def Get(self, name):
2330 # type: (str) -> value.Proc
2331 """Try to find a proc/sh-func by `name`, or return None if not found.
2332
2333 First, we search for a proc, and then a sh-func. This means that procs
2334 can shadow the definition of sh-funcs.
2335 """
2336 vars = self.mem.var_stack[0]
2337 if name in vars:
2338 maybe_proc = vars[name]
2339 if maybe_proc.val.tag() == value_e.Proc:
2340 return cast(value.Proc, maybe_proc.val)
2341
2342 if name in self.sh_funcs:
2343 return self.sh_funcs[name]
2344
2345 return None
2346
2347 def Del(self, to_del):
2348 # type: (str) -> None
2349 """Undefine a sh-func with name `to_del`, if it exists."""
2350 mylib.dict_erase(self.sh_funcs, to_del)
2351
2352 def GetNames(self):
2353 # type: () -> List[str]
2354 """Returns a *sorted* list of all proc names"""
2355 names = list(self.sh_funcs.keys())
2356
2357 vars = self.mem.var_stack[0]
2358 for name in vars:
2359 cell = vars[name]
2360 if cell.val.tag() == value_e.Proc:
2361 names.append(name)
2362
2363 return sorted(names)
2364
2365
2366#
2367# Wrappers to Set Variables
2368#
2369
2370
2371def OshLanguageSetValue(mem, lval, val, flags=0):
2372 # type: (Mem, sh_lvalue_t, value_t, int) -> None
2373 """Like 'setvar' (scope_e.LocalOnly), unless dynamic scope is on.
2374
2375 That is, it respects shopt --unset dynamic_scope.
2376
2377 Used for assignment builtins, (( a = b )), {fd}>out, ${x=}, etc.
2378 """
2379 which_scopes = mem.ScopesForWriting()
2380 mem.SetValue(lval, val, which_scopes, flags=flags)
2381
2382
2383def BuiltinSetValue(mem, lval, val):
2384 # type: (Mem, sh_lvalue_t, value_t) -> None
2385 """Equivalent of x=$y
2386
2387 Called by BuiltinSetString and BuiltinSetArray Used directly by
2388 printf -v because it can mutate an array
2389 """
2390 mem.SetValue(lval, val, mem.ScopesForWriting())
2391
2392
2393def BuiltinSetString(mem, name, s):
2394 # type: (Mem, str, str) -> None
2395 """Set a string by looking up the stack.
2396
2397 Used for 'read', 'getopts', completion builtins, etc.
2398 """
2399 assert isinstance(s, str)
2400 BuiltinSetValue(mem, location.LName(name), value.Str(s))
2401
2402
2403def BuiltinSetArray(mem, name, a):
2404 # type: (Mem, str, List[str]) -> None
2405 """Set an array by looking up the stack.
2406
2407 Used by compadjust, read -a, etc.
2408 """
2409 assert isinstance(a, list)
2410 BuiltinSetValue(mem, location.LName(name), value.BashArray(a))
2411
2412
2413def SetGlobalString(mem, name, s):
2414 # type: (Mem, str, str) -> None
2415 """Helper for completion, etc."""
2416 assert isinstance(s, str)
2417 val = value.Str(s)
2418 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2419
2420
2421def SetGlobalArray(mem, name, a):
2422 # type: (Mem, str, List[str]) -> None
2423 """Used by completion, shell initialization, etc."""
2424 assert isinstance(a, list)
2425 mem.SetNamed(location.LName(name), value.BashArray(a), scope_e.GlobalOnly)
2426
2427
2428def _SetGlobalValue(mem, name, val):
2429 # type: (Mem, str, value_t) -> None
2430 """Helper for completion, etc."""
2431 mem.SetNamed(location.LName(name), val, scope_e.GlobalOnly)
2432
2433
2434def ExportGlobalString(mem, name, s):
2435 # type: (Mem, str, str) -> None
2436 """Helper for completion, $PWD, $OLDPWD, etc."""
2437 assert isinstance(s, str)
2438 val = value.Str(s)
2439 mem.SetNamed(location.LName(name),
2440 val,
2441 scope_e.GlobalOnly,
2442 flags=SetExport)
2443
2444
2445#
2446# Wrappers to Get Variables
2447#
2448
2449
2450def DynamicGetVar(mem, name, which_scopes):
2451 # type: (Mem, str, scope_t) -> value_t
2452 """
2453 For getVar() and shvarGet()
2454 """
2455 val = mem.GetValue(name, which_scopes=which_scopes)
2456
2457 # Undef is not a user-visible value!
2458 # There's no way to distinguish null from undefined.
2459 if val.tag() == value_e.Undef:
2460 return value.Null
2461
2462 return val
2463
2464
2465def GetString(mem, name):
2466 # type: (Mem, str) -> str
2467 """Wrapper around GetValue(). Check that HOME, PWD, OLDPWD, etc. are
2468 strings. bash doesn't have these errors because ${array} is ${array[0]}.
2469
2470 TODO: We could also check this when you're storing variables?
2471 """
2472 val = mem.GetValue(name)
2473 UP_val = val
2474 with tagswitch(val) as case:
2475 if case(value_e.Undef):
2476 raise error.Runtime("$%s isn't defined" % name)
2477 elif case(value_e.Str):
2478 return cast(value.Str, UP_val).s
2479 else:
2480 # User would have to 'unset HOME' to get rid of exported flag
2481 raise error.Runtime("$%s should be a string" % name)
2482
2483
2484def MaybeString(mem, name):
2485 # type: (Mem, str) -> Optional[str]
2486 """Like GetString(), but doesn't throw an exception."""
2487 try:
2488 return GetString(mem, name)
2489 except error.Runtime:
2490 return None
2491
2492
2493def GetInteger(mem, name):
2494 # type: (Mem, str) -> int
2495 """For OPTIND variable used in getopts builtin.
2496
2497 TODO: it could be value.Int() ?
2498 """
2499 val = mem.GetValue(name)
2500 if val.tag() != value_e.Str:
2501 raise error.Runtime('$%s should be a string, got %s' %
2502 (name, ui.ValType(val)))
2503 s = cast(value.Str, val).s
2504 try:
2505 i = int(s)
2506 except ValueError:
2507 raise error.Runtime("$%s doesn't look like an integer, got %r" %
2508 (name, s))
2509 return i
2510
2511
2512# vim: sw=4