def Open(self, path, mode='r'): # type: (str, str) -> mylib.LineReader """Opens a path for read, but moves it out of the reserved 3-9 fd range. Returns: A Python file object. The caller is responsible for Close(). Raises: OSError if the path can't be found. """ if mode == 'r': fd_mode = posix.O_RDONLY elif mode == 'w': fd_mode = posix.O_CREAT | posix.O_RDWR else: raise AssertionError(mode) fd = posix.open(path, fd_mode, 0o666) # may raise OSError new_fd = self._GetFreeDescriptor() posix.dup2(fd, new_fd) posix.close(fd) try: f = posix.fdopen(new_fd, mode) # Might raise IOError except IOError as e: raise OSError(*e.args) # Consistently raise OSError return f
def Open(self, path, mode='r'): # type: (str, str) -> mylib.LineReader """Opens a path for read, but moves it out of the reserved 3-9 fd range. Returns: A Python file object. The caller is responsible for Close(). Raises: OSError if the path can't be found. """ if mode == 'r': fd_mode = posix.O_RDONLY elif mode == 'w': fd_mode = posix.O_CREAT | posix.O_RDWR else: raise AssertionError(mode) fd = posix.open(path, fd_mode, 0o666) # may raise OSError # Immediately move it to a new location new_fd = fcntl.fcntl(fd, fcntl.F_DUPFD, _SHELL_MIN_FD) posix.close(fd) # Return a Python file handle try: f = posix.fdopen(new_fd, mode) # Might raise IOError except IOError as e: raise OSError(*e.args) # Consistently raise OSError return f
def _Open(self, path, c_mode, fd_mode): # type: (str, str, int) -> mylib.LineReader fd = posix.open(path, fd_mode, 0o666) # may raise OSError # Immediately move it to a new location new_fd = fcntl_.fcntl(fd, fcntl_.F_DUPFD, _SHELL_MIN_FD) # type: int posix.close(fd) # Return a Python file handle # Might raise IOError. Python-induced wart: we have to handle BOTH IOError # and OSError at higher levels. f = posix.fdopen(new_fd, c_mode) return f
def _Open(self, path, c_mode, fd_mode): # type: (str, str, int) -> mylib.LineReader fd = posix.open(path, fd_mode, 0o666) # may raise OSError # Immediately move it to a new location new_fd = fcntl.fcntl(fd, fcntl.F_DUPFD, _SHELL_MIN_FD) posix.close(fd) # Return a Python file handle try: f = posix.fdopen(new_fd, c_mode) # Might raise IOError except IOError as e: raise OSError(*e.args) # Consistently raise OSError return f
def _ApplyRedirect(self, r, waiter): # type: (redirect, Waiter) -> None arg = r.arg UP_arg = arg with tagswitch(arg) as case: if case(redirect_arg_e.Path): arg = cast(redirect_arg__Path, UP_arg) if r.op_id in (Id.Redir_Great, Id.Redir_AndGreat): # > &> # NOTE: This is different than >| because it respects noclobber, but # that option is almost never used. See test/wild.sh. mode = posix.O_CREAT | posix.O_WRONLY | posix.O_TRUNC elif r.op_id == Id.Redir_Clobber: # >| mode = posix.O_CREAT | posix.O_WRONLY | posix.O_TRUNC elif r.op_id in (Id.Redir_DGreat, Id.Redir_AndDGreat): # >> &>> mode = posix.O_CREAT | posix.O_WRONLY | posix.O_APPEND elif r.op_id == Id.Redir_Less: # < mode = posix.O_RDONLY elif r.op_id == Id.Redir_LessGreat: # <> mode = posix.O_CREAT | posix.O_RDWR else: raise NotImplementedError(r.op_id) # NOTE: 0666 is affected by umask, all shells use it. try: open_fd = posix.open(arg.filename, mode, 0o666) except OSError as e: self.errfmt.Print_("Can't open %r: %s" % (arg.filename, pyutil.strerror(e)), span_id=r.op_spid) raise # redirect failed new_fd = self._PushDup(open_fd, r.loc) if new_fd != NO_FD: posix.close(open_fd) # Now handle &> and &>> and their variants. These pairs are the same: # # stdout_stderr.py &> out-err.txt # stdout_stderr.py > out-err.txt 2>&1 # # stdout_stderr.py 3&> out-err.txt # stdout_stderr.py 3> out-err.txt 2>&3 # # Ditto for {fd}> and {fd}&> if r.op_id in (Id.Redir_AndGreat, Id.Redir_AndDGreat): self._PushDup(new_fd, redir_loc.Fd(2)) elif case(redirect_arg_e.CopyFd): # e.g. echo hi 1>&2 arg = cast(redirect_arg__CopyFd, UP_arg) if r.op_id == Id.Redir_GreatAnd: # 1>&2 self._PushDup(arg.target_fd, r.loc) elif r.op_id == Id.Redir_LessAnd: # 0<&5 # The only difference between >& and <& is the default file # descriptor argument. self._PushDup(arg.target_fd, r.loc) else: raise NotImplementedError() elif case(redirect_arg_e.MoveFd): # e.g. echo hi 5>&6- arg = cast(redirect_arg__MoveFd, UP_arg) new_fd = self._PushDup(arg.target_fd, r.loc) if new_fd != NO_FD: posix.close(arg.target_fd) UP_loc = r.loc if r.loc.tag_() == redir_loc_e.Fd: fd = cast(redir_loc__Fd, UP_loc).fd else: fd = NO_FD self.cur_frame.saved.append(_RedirFrame(new_fd, fd, False)) elif case(redirect_arg_e.CloseFd): # e.g. echo hi 5>&- self._PushCloseFd(r.loc) elif case(redirect_arg_e.HereDoc): arg = cast(redirect_arg__HereDoc, UP_arg) # NOTE: Do these descriptors have to be moved out of the range 0-9? read_fd, write_fd = posix.pipe() self._PushDup(read_fd, r.loc) # stdin is now the pipe # We can't close like we do in the filename case above? The writer can # get a "broken pipe". self._PushClose(read_fd) thunk = _HereDocWriterThunk(write_fd, arg.body) # TODO: Use PIPE_SIZE to save a process in the case of small here docs, # which are the common case. (dash does this.) start_process = True #start_process = False if start_process: here_proc = Process(thunk, self.job_state) # NOTE: we could close the read pipe here, but it doesn't really # matter because we control the code. _ = here_proc.Start() #log('Started %s as %d', here_proc, pid) self._PushWait(here_proc, waiter) # Now that we've started the child, close it in the parent. posix.close(write_fd) else: posix.write(write_fd, arg.body) posix.close(write_fd)
def _ApplyRedirect(self, r, waiter): # type: (redirect_t, Waiter) -> bool ok = True UP_r = r with tagswitch(r) as case: if case(redirect_e.Path): r = cast(redirect__Path, UP_r) if r.op_id in (Id.Redir_Great, Id.Redir_AndGreat): # > &> # NOTE: This is different than >| because it respects noclobber, but # that option is almost never used. See test/wild.sh. mode = posix.O_CREAT | posix.O_WRONLY | posix.O_TRUNC elif r.op_id == Id.Redir_Clobber: # >| mode = posix.O_CREAT | posix.O_WRONLY | posix.O_TRUNC elif r.op_id in (Id.Redir_DGreat, Id.Redir_AndDGreat): # >> &>> mode = posix.O_CREAT | posix.O_WRONLY | posix.O_APPEND elif r.op_id == Id.Redir_Less: # < mode = posix.O_RDONLY else: raise NotImplementedError(r.op_id) # NOTE: 0666 is affected by umask, all shells use it. try: target_fd = posix.open(r.filename, mode, 0o666) except OSError as e: self.errfmt.Print( "Can't open %r: %s", r.filename, posix.strerror(e.errno), span_id=r.op_spid) return False # Apply redirect if not self._PushDup(target_fd, r.fd): ok = False # Now handle the extra redirects for aliases &> and &>>. # # We can rewrite # stdout_stderr.py &> out-err.txt # as # stdout_stderr.py > out-err.txt 2>&1 # # And rewrite # stdout_stderr.py 3&> out-err.txt # as # stdout_stderr.py 3> out-err.txt 2>&3 if ok: if r.op_id == Id.Redir_AndGreat: if not self._PushDup(r.fd, 2): ok = False elif r.op_id == Id.Redir_AndDGreat: if not self._PushDup(r.fd, 2): ok = False posix.close(target_fd) # We already made a copy of it. # I don't think we need to close(0) because it will be restored from its # saved position (10), which closes it. #self._PushClose(r.fd) elif case(redirect_e.FileDesc): # e.g. echo hi 1>&2 r = cast(redirect__FileDesc, UP_r) if r.op_id == Id.Redir_GreatAnd: # 1>&2 if not self._PushDup(r.target_fd, r.fd): ok = False elif r.op_id == Id.Redir_LessAnd: # 0<&5 # The only difference between >& and <& is the default file # descriptor argument. if not self._PushDup(r.target_fd, r.fd): ok = False else: raise NotImplementedError() elif case(redirect_e.HereDoc): r = cast(redirect__HereDoc, UP_r) # NOTE: Do these descriptors have to be moved out of the range 0-9? read_fd, write_fd = posix.pipe() if not self._PushDup(read_fd, r.fd): # stdin is now the pipe ok = False # We can't close like we do in the filename case above? The writer can # get a "broken pipe". self._PushClose(read_fd) thunk = _HereDocWriterThunk(write_fd, r.body) # TODO: Use PIPE_SIZE to save a process in the case of small here docs, # which are the common case. (dash does this.) start_process = True #start_process = False if start_process: here_proc = Process(thunk, self.job_state) # NOTE: we could close the read pipe here, but it doesn't really # matter because we control the code. _ = here_proc.Start() #log('Started %s as %d', here_proc, pid) self._PushWait(here_proc, waiter) # Now that we've started the child, close it in the parent. posix.close(write_fd) else: posix.write(write_fd, r.body) posix.close(write_fd) return ok