From 853968efbc49e55f27b0b3110a90bab2fc0a2abc Mon Sep 17 00:00:00 2001 From: Sam Colwell Date: Sat, 9 Feb 2019 16:41:27 -0500 Subject: [PATCH] Duping stdin fd before using fdopen --- py65/monitor.py | 32 ++++++++++++++++++++++---------- py65/utils/console.py | 23 ++++++++++++++--------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/py65/monitor.py b/py65/monitor.py index e3f424b..8027e44 100644 --- a/py65/monitor.py +++ b/py65/monitor.py @@ -54,17 +54,17 @@ class Monitor(cmd.Cmd): self.prompt = "." self._add_shortcuts() + # Save the current system input mode so it can be restored after + # after processing commands and before exiting. + console.save_mode(sys.stdin) + # Attempt to get a copy of stdin that is unbuffered on systems # that support it. This allows for immediate response to # typed input as well as pasted input. If unable to get an # unbuffered version of stdin, the original version is returned. - stdin = console.get_unbuffered_stdin(stdin) + self.unbuffered_stdin = console.get_unbuffered_stdin(stdin) - cmd.Cmd.__init__(self, stdin=stdin, stdout=stdout) - - # Save the current input mode so it can be restored after - # after processing commands and before exiting. - console.save_mode(self.stdin) + cmd.Cmd.__init__(self, stdin=self.unbuffered_stdin, stdout=stdout) # Check for any exceptions thrown during __init__ while # processing the arguments. @@ -93,9 +93,21 @@ class Monitor(cmd.Cmd): except: # Restore input mode on any exception and then rethrow the # exception. - console.restore_mode(self.stdin) + console.restore_mode() raise + def __del__(self): + try: + # Restore the input mode. + console.restore_mode() + # Close the unbuffered input file handle, if it exists. + if self.unbuffered_stdin != None: + if self.unbuffered_stdin != sys.stdin: + self.unbuffered_stdin.close() + except: + pass + + def _parse_args(self, argv): try: shortopts = 'hi:o:m:l:r:g:' @@ -160,7 +172,7 @@ class Monitor(cmd.Cmd): self._output_mpu_status() # Switch back to the previous input mode. - console.restore_mode(self.stdin) + console.restore_mode() return result @@ -511,7 +523,7 @@ class Monitor(cmd.Cmd): break # Switch back to the previous input mode. - console.restore_mode(self.stdin) + console.restore_mode() def help_radix(self): self._output("radix [H|D|O|B]") @@ -907,7 +919,7 @@ def main(args=None): c.cmdloop() except KeyboardInterrupt: c._output('') - console.restore_mode(c.stdin) + console.restore_mode() if __name__ == "__main__": main() diff --git a/py65/utils/console.py b/py65/utils/console.py index 21e6d47..de6f61d 100644 --- a/py65/utils/console.py +++ b/py65/utils/console.py @@ -45,6 +45,7 @@ else: from select import select oldattr = None + oldstdin = None def get_unbuffered_stdin(stdin): """ Attempt to get and return a copy of stdin that is @@ -55,21 +56,20 @@ else: if stdin != None: try: # Reopen stdin with no buffer. - return os.fdopen(stdin.fileno(), 'rb', 0) + return os.fdopen(os.dup(stdin.fileno()), 'rb', 0) except Exception as e: print(e) # Unable to reopen this file handle with no buffer. # Just use the original file handle. - print("Error opening unbuffered stdin - using buffered version") return stdin else: # If stdin is None, try using sys.stdin for input. try: # Reopen the system's stdin with no buffer. - return os.fdopen(sys.stdin.fileno(), 'rb', 0) - except Exception as e: - print(e) - print("Error opening default unbuffered stdin - using buffered version") + return os.fdopen(os.dup(sys.stdin.fileno()), 'rb', 0) + except: + # If unable to get an unbuffered stdin, just return + # None, which is what we started with if we got here. return None def save_mode(stdin): @@ -82,11 +82,13 @@ else: # For non-Windows systems, save the original input settings, # which will typically be blocking reads with echo. global oldattr + global oldstdin # When the input is not a pty/tty, this will fail. # In that case, it's ok to ignore the failure. try: # Save the current terminal setup. + oldstdin = stdin fd = stdin.fileno() oldattr = termios.tcgetattr(fd) except: @@ -120,19 +122,22 @@ else: # a tty. pass - def restore_mode(stdin): + def restore_mode(): """For operating systems that support it, restore the previous input mode. """ # Restore the previous input setup. global oldattr + global oldstdin try: - fd = stdin.fileno() + # Restore the original system stdin. + oldfd = oldstdin.fileno() # If there is a previous setting, restore it. if oldattr != None: - termios.tcsetattr(fd, termios.TCSANOW, oldattr) + # Restore it on the original system stdin. + termios.tcsetattr(oldfd, termios.TCSANOW, oldattr) except: # Quietly ignore termios errors, such as stdin not being a tty. pass