Duping stdin fd before using fdopen

This commit is contained in:
Sam Colwell 2019-02-09 16:41:27 -05:00
parent 99b7f66070
commit 853968efbc
2 changed files with 36 additions and 19 deletions

View File

@ -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()

View File

@ -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