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

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