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