def Run(self, cmd_val): # type: (cmd_value__Argv) -> int attrs, arg_r = flag_spec.ParseCmdVal('cd', cmd_val) arg = arg_types.cd(attrs.attrs) dest_dir, arg_spid = arg_r.Peek2() if dest_dir is None: val = self.mem.GetVar('HOME') try: dest_dir = state.GetString(self.mem, 'HOME') except error.Runtime as e: self.errfmt.Print_(e.UserErrorString()) return 1 if dest_dir == '-': try: dest_dir = state.GetString(self.mem, 'OLDPWD') print(dest_dir) # Shells print the directory except error.Runtime as e: self.errfmt.Print_(e.UserErrorString()) return 1 try: pwd = state.GetString(self.mem, 'PWD') except error.Runtime as e: self.errfmt.Print_(e.UserErrorString()) return 1 # Calculate new directory, chdir() to it, then set PWD to it. NOTE: We can't # call posix.getcwd() because it can raise OSError if the directory was # removed (ENOENT.) abspath = os_path.join(pwd, dest_dir) # make it absolute, for cd .. if arg.P: # -P means resolve symbolic links, then process '..' real_dest_dir = libc.realpath(abspath) else: # -L means process '..' first. This just does string manipulation. (But # realpath afterward isn't correct?) real_dest_dir = os_path.normpath(abspath) try: posix.chdir(real_dest_dir) except OSError as e: self.errfmt.Print_("cd %r: %s" % (real_dest_dir, pyutil.strerror_OS(e)), span_id=arg_spid) return 1 state.ExportGlobalString(self.mem, 'PWD', real_dest_dir) # WEIRD: We need a copy that is NOT PWD, because the user could mutate PWD. # Other shells use global variables. self.mem.SetPwd(real_dest_dir) if cmd_val.block: self.dir_stack.Push(real_dest_dir) try: unused = self.cmd_ev.EvalBlock(cmd_val.block) finally: # TODO: Change this to a context manager. # note: it might be more consistent to use an exception here. if not _PopDirStack(self.mem, self.dir_stack, self.errfmt): return 1 else: # No block state.ExportGlobalString(self.mem, 'OLDPWD', pwd) self.dir_stack.Reset() # for pushd/popd/dirs return 0
def __call__(self, cmd_val): arg, i = CD_SPEC.ParseCmdVal(cmd_val) try: dest_dir = cmd_val.argv[i] except IndexError: val = self.mem.GetVar('HOME') if val.tag == value_e.Undef: self.errfmt.Print("$HOME isn't defined") return 1 elif val.tag == value_e.Str: dest_dir = val.s elif val.tag == value_e.MaybeStrArray: # User would have to unset $HOME to get rid of exported flag self.errfmt.Print("$HOME shouldn't be an array") return 1 if dest_dir == '-': old = self.mem.GetVar('OLDPWD', scope_e.GlobalOnly) if old.tag == value_e.Undef: self.errfmt.Print('$OLDPWD not set') return 1 elif old.tag == value_e.Str: dest_dir = old.s print(dest_dir) # Shells print the directory elif old.tag == value_e.MaybeStrArray: # TODO: Prevent the user from setting OLDPWD to array (or maybe they # can't even set it at all.) raise AssertionError('Invalid $OLDPWD') pwd = self.mem.GetVar('PWD') assert pwd.tag == value_e.Str, pwd # TODO: Need a general scheme to avoid # Calculate new directory, chdir() to it, then set PWD to it. NOTE: We can't # call posix.getcwd() because it can raise OSError if the directory was # removed (ENOENT.) abspath = os_path.join(pwd.s, dest_dir) # make it absolute, for cd .. if arg.P: # -P means resolve symbolic links, then process '..' real_dest_dir = libc.realpath(abspath) else: # -L means process '..' first. This just does string manipulation. (But # realpath afterward isn't correct?) real_dest_dir = os_path.normpath(abspath) try: posix.chdir(real_dest_dir) except OSError as e: self.errfmt.Print("cd %r: %s", real_dest_dir, posix.strerror(e.errno), span_id=cmd_val.arg_spids[i]) return 1 state.ExportGlobalString(self.mem, 'PWD', real_dest_dir) # WEIRD: We need a copy that is NOT PWD, because the user could mutate PWD. # Other shells use global variables. self.mem.SetPwd(real_dest_dir) if cmd_val.block: self.dir_stack.Push(real_dest_dir) try: unused = self.ex.EvalBlock(cmd_val.block) finally: # TODO: Change this to a context manager. _PopDirStack(self.mem, self.dir_stack, self.errfmt) else: # No block state.ExportGlobalString(self.mem, 'OLDPWD', pwd.s) self.dir_stack.Reset() # for pushd/popd/dirs return 0