def _InitVarsFromEnv(self, environ): # This is the way dash and bash work -- at startup, they turn everything in # 'environ' variable into shell variables. Bash has an export_env # variable. Dash has a loop through environ in init.c for n, v in environ.iteritems(): self.SetVar(sh_lhs_expr.Name(n), value.Str(v), (var_flags_e.Exported, ), scope_e.GlobalOnly) # If it's not in the environment, initialize it. This makes it easier to # update later in ExecOpts. # TODO: IFS, etc. should follow this pattern. Maybe need a SysCall # interface? self.syscall.getcwd() etc. v = self.GetVar('SHELLOPTS') if v.tag == value_e.Undef: SetGlobalString(self, 'SHELLOPTS', '') # Now make it readonly self.SetVar(sh_lhs_expr.Name('SHELLOPTS'), None, (var_flags_e.ReadOnly, ), scope_e.GlobalOnly) # Usually we inherit PWD from the parent shell. When it's not set, we may # compute it. v = self.GetVar('PWD') if v.tag == value_e.Undef: SetGlobalString(self, 'PWD', _GetWorkingDir()) # Now mark it exported, no matter what. This is one of few variables # EXPORTED. bash and dash both do it. (e.g. env -i -- dash -c env) self.SetVar(sh_lhs_expr.Name('PWD'), None, (var_flags_e.Exported, ), scope_e.GlobalOnly)
def __call__(self, cmd_val): arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() arg, _ = GETLINE_SPEC.Parse(arg_r) if arg.cstr: # TODO: implement it # returns error if it can't decode raise NotImplementedError() var_name, var_spid = arg_r.ReadRequired2('requires a variable name') if var_name.startswith(':'): # optional : sigil var_name = var_name[1:] next_arg, next_spid = arg_r.Peek2() if next_arg is not None: raise args.UsageError('got extra argument', span_id=next_spid) # TODO: use a more efficient function in C line = builtin.ReadLineFromStdin() if not line: # EOF return 1 if not arg.end: if line.endswith('\r\n'): line = line[:-2] elif line.endswith('\n'): line = line[:-1] self.mem.SetVar(sh_lhs_expr.Name(var_name), value.Str(line), (), scope_e.LocalOnly) return 0
def ToLValue(node): # type: (arith_expr_t) -> sh_lhs_expr_t """Determine if a node is a valid L-value by whitelisting tags. Valid: x = y a[1] = y Invalid: a[0][0] = y """ UP_node = node with tagswitch(node) as case: if case(arith_expr_e.VarRef): node = cast(arith_expr__VarRef, UP_node) # For consistency with osh/cmd_parse.py, append a span_id. # TODO: (( a[ x ] = 1 )) and a[x]=1 should use different LST nodes. n = sh_lhs_expr.Name(node.token.val) n.spids.append(node.token.span_id) return n elif case(arith_expr_e.Binary): node = cast(arith_expr__Binary, UP_node) if (node.op_id == Id.Arith_LBracket and node.left.tag_() == arith_expr_e.VarRef): left = cast(arith_expr__VarRef, node.left) return sh_lhs_expr.IndexedName(left.token.val, node.right) # But a[0][0] = 1 is NOT valid. return None
def SetStringDynamic(mem, name, s): """Set a string by looking up the stack. Used for getopts. """ assert isinstance(s, str) mem.SetVar(sh_lhs_expr.Name(name), value.Str(s), (), scope_e.Dynamic)
def SetArrayDynamic(mem, name, a): """Set an array by looking up the stack. Used for _init_completion. """ assert isinstance(a, list) mem.SetVar(sh_lhs_expr.Name(name), value.MaybeStrArray(a), (), scope_e.Dynamic)
def SetLocalString(mem, name, s): """Set a local string. Used for: 1) for loop iteration variables 2) temporary environments like FOO=bar BAR=$FOO cmd, 3) read builtin """ assert isinstance(s, str) mem.SetVar(sh_lhs_expr.Name(name), value.Str(s), (), scope_e.LocalOnly)
def ToLValue(node): # type: (arith_expr_t) -> sh_lhs_expr_t """Determine if a node is a valid L-value by whitelisting tags. Args: node: ExprNode (could be VarExprNode or BinaryExprNode) """ # foo = bar, foo[1] = bar if isinstance(node, arith_expr__VarRef): # For consistency with osh/cmd_parse.py, append a span_id. # TODO: (( a[ x ] = 1 )) and a[x]=1 should use different LST nodes. n = sh_lhs_expr.Name(node.token.val) n.spids.append(node.token.span_id) return n if isinstance(node, arith_expr__Binary): # For example, a[0][0] = 1 is NOT valid. if (node.op_id == Id.Arith_LBracket and isinstance(node.left, arith_expr__VarRef)): return sh_lhs_expr.IndexedName(node.left.token.val, node.right) return None
def Run(self, cmd_val): arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # skip 'json' action, action_spid = arg_r.Peek2() if action is None: raise error.Usage(_JSON_ACTION_ERROR) arg_r.Next() if action == 'write': arg, _ = JSON_WRITE_SPEC.Parse(arg_r) # GetVar() of each name and print it. for var_name in arg_r.Rest(): if var_name.startswith(':'): var_name = var_name[1:] val = self.mem.GetVar(var_name) with tagswitch(val) as case: if case(value_e.Undef): # TODO: blame the right span_id self.errfmt.Print("no variable named %r is defined", var_name) return 1 elif case(value_e.Str): obj = val.s elif case(value_e.MaybeStrArray): obj = val.strs elif case(value_e.AssocArray): obj = val.d elif case(value_e.Obj): obj = val.obj else: raise AssertionError(val) if arg.pretty: indent = arg.indent extra_newline = False else: # How yajl works: if indent is -1, then everything is on one line. indent = -1 extra_newline = True j = yajl.dump(obj, sys.stdout, indent=indent) if extra_newline: sys.stdout.write('\n') # TODO: Accept a block. They aren't hooked up yet. if cmd_val.block: # TODO: flatten value.{Str,Obj} into a flat dict? namespace = self.cmd_ev.EvalBlock(cmd_val.block) print(yajl.dump(namespace)) elif action == 'read': arg, _ = JSON_READ_SPEC.Parse(arg_r) # TODO: # Respect -validate=F var_name, name_spid = arg_r.ReadRequired2("expected variable name") if var_name.startswith(':'): var_name = var_name[1:] if not match.IsValidVarName(var_name): raise error.Usage('got invalid variable name %r' % var_name, span_id=name_spid) try: # Use a global _STDIN, because we get EBADF on a redirect if we use a # local. A Py_DECREF closes the file, which we don't want, because the # redirect is responsible for freeing it. # # https://github.com/oilshell/oil/issues/675 # # TODO: write a better binding like yajl.readfd() # # It should use streaming like here: # https://lloyd.github.io/yajl/ obj = yajl.load(_STDIN) except ValueError as e: self.errfmt.Print('json read: %s', e, span_id=action_spid) return 1 self.mem.SetVar(sh_lhs_expr.Name(var_name), value.Obj(obj), scope_e.LocalOnly) else: raise error.Usage(_JSON_ACTION_ERROR, span_id=action_spid) return 0
def __call__(self, cmd_val): arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids) arg_r.Next() # skip 'json' action, action_spid = arg_r.Peek2() if action is None: raise args.UsageError(_JSON_ACTION_ERROR) arg_r.Next() if action == 'write': arg, _ = JSON_WRITE_SPEC.Parse(arg_r) # GetVar() of each name and print it. for var_name in arg_r.Rest(): if var_name.startswith(':'): var_name = var_name[1:] val = self.mem.GetVar(var_name) with tagswitch(val) as case: if case(value_e.Undef): # TODO: blame the right span_id self.errfmt.Print("no variable named %r is defined", var_name) return 1 elif case(value_e.Str): obj = val.s elif case(value_e.MaybeStrArray): obj = val.strs elif case(value_e.AssocArray): obj = val.d elif case(value_e.Obj): obj = val.obj else: raise AssertionError(val) if arg.pretty: indent = arg.indent extra_newline = False else: # How yajl works: if indent is -1, then everything is on one line. indent = -1 extra_newline = True j = yajl.dump(obj, sys.stdout, indent=indent) if extra_newline: sys.stdout.write('\n') # TODO: Accept a block. They aren't hooked up yet. if cmd_val.block: # TODO: flatten value.{Str,Obj} into a flat dict? namespace = self.ex.EvalBlock(cmd_val.block) print(yajl.dump(namespace)) elif action == 'read': arg, _ = JSON_READ_SPEC.Parse(arg_r) # TODO: # Respect -validate=F var_name, name_spid = arg_r.ReadRequired2("expected variable name") if var_name.startswith(':'): var_name = var_name[1:] if not match.IsValidVarName(var_name): raise args.UsageError('got invalid variable name %r' % var_name, span_id=name_spid) # Have to use this over sys.stdin because of redirects # TODO: change binding to yajl.readfd() ? stdin = posix_.fdopen(0) try: obj = yajl.load(stdin) except ValueError as e: self.errfmt.Print('json read: %s', e, span_id=action_spid) return 1 self.mem.SetVar(sh_lhs_expr.Name(var_name), value.Obj(obj), (), scope_e.LocalOnly) else: raise args.UsageError(_JSON_ACTION_ERROR, span_id=action_spid) return 0
def SetGlobalFunc(mem, name, func): # type: (Mem, str, Union[Callable, ParameterizedArray, type]) -> None """Used by bin/oil.py to set split(), etc.""" assert callable(func), func mem.SetVar(sh_lhs_expr.Name(name), value.Obj(func), scope_e.GlobalOnly)
def ExportGlobalString(mem, name, s): """Helper for completion, $PWD, $OLDPWD, etc.""" assert isinstance(s, str) val = value.Str(s) mem.SetVar(sh_lhs_expr.Name(name), val, (var_flags_e.Exported, ), scope_e.GlobalOnly)
def SetLocalArray(mem, name, a): """Helper for completion.""" assert isinstance(a, list) mem.SetVar(sh_lhs_expr.Name(name), value.MaybeStrArray(a), (), scope_e.LocalOnly)
def SetGlobalString(mem, name, s): """Helper for completion, etc.""" assert isinstance(s, str) val = value.Str(s) mem.SetVar(sh_lhs_expr.Name(name), val, (), scope_e.GlobalOnly)
def SetGlobalFunc(mem, name, func): """Used by bin/oil.py to set split(), etc.""" assert callable(func), func mem.SetVar(sh_lhs_expr.Name(name), value.Obj(func), (), scope_e.GlobalOnly)