def _ReplaceBackslashCodes(self, tokens): # type: (List[Tuple[Id_t, str]]) -> str ret = [] # type: List[str] non_printing = 0 for id_, value in tokens: # BadBacklash means they should have escaped with \\. TODO: Make it an error. # 'echo -e' has a similar issue. if id_ in (Id.PS_Literals, Id.PS_BadBackslash): ret.append(value) elif id_ == Id.PS_Octal3: i = int(value[1:], 8) ret.append(chr(i % 256)) elif id_ == Id.PS_LBrace: non_printing += 1 ret.append('\x01') elif id_ == Id.PS_RBrace: non_printing -= 1 if non_printing < 0: # e.g. \]\[ return PROMPT_ERROR ret.append('\x02') elif id_ == Id.PS_Subst: # \u \h \w etc. ch = value[1:] if ch == '$': # So the user can tell if they're root or not. r = self.cache.Get('$') elif ch == 'u': r = self.cache.Get('user') elif ch == 'h': hostname = self.cache.Get('hostname') # foo.com -> foo r, _ = mylib.split_once(hostname, '.') elif ch == 'H': r = self.cache.Get('hostname') elif ch == 'w': try: pwd = state.GetString(self.mem, 'PWD') home = state.MaybeString( self.mem, 'HOME') # doesn't have to exist # Shorten to ~/mydir r = ui.PrettyDir(pwd, home) except error.Runtime as e: r = '<Error: %s>' % e.UserErrorString() elif ch == 'W': val = self.mem.GetValue('PWD') if val.tag_() == value_e.Str: str_val = cast(value__Str, val) r = os_path.basename(str_val.s) else: r = '<Error: PWD is not a string> ' else: r = consts.LookupCharPrompt(ch) # TODO: Handle more codes # R(r'\\[adehHjlnrstT@AuvVwW!#$\\]', Id.PS_Subst), if r is None: r = r'<Error: \%s not implemented in $PS1> ' % ch # See comment above on bash hack for $. ret.append(r.replace('$', '\\$')) else: raise AssertionError('Invalid token %r' % id_) # mismatched brackets, see https://github.com/oilshell/oil/pull/256 if non_printing != 0: return PROMPT_ERROR return ''.join(ret)
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