File: echoff.py 1 #!/usr/bin/python 2 3 # The MIT License (MIT) 4 # 5 # Copyright (c) 2026 pacman64 6 # 7 # Permission is hereby granted, free of charge, to any person obtaining a copy 8 # of this software and associated documentation files (the "Software"), to deal 9 # in the Software without restriction, including without limitation the rights 10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 # copies of the Software, and to permit persons to whom the Software is 12 # furnished to do so, subject to the following conditions: 13 # 14 # The above copyright notice and this permission notice shall be included in 15 # all copies or substantial portions of the Software. 16 # 17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 # SOFTWARE. 24 25 26 from curses import ( 27 cbreak, curs_set, endwin, initscr, noecho, resetty, savetty, set_escdelay, 28 ) 29 from itertools import islice 30 from os import dup2 31 from sys import argv, exit, stderr 32 33 34 info = ''' 35 echoff [words...] 36 37 Emit words using the command-line arguments given (like `echo` does) on an 38 alternate screen (off the regular screen). You can quit by pressing (almost) 39 any key on your keyboard, or even force-quit it via Control + C. 40 ''' 41 42 43 # leave default/original stdin alone, so this tool can be called with 44 # the likes of `xargs` (for example), and still wait for keyboard input 45 # dup2(0, 3) 46 with open('/dev/tty', 'rb') as inp: 47 dup2(inp.fileno(), 0) 48 49 screen = initscr() 50 savetty() 51 noecho() 52 cbreak() 53 screen.keypad(True) 54 curs_set(0) 55 set_escdelay(10) 56 57 err = None 58 res = 0 59 60 try: 61 try: 62 pos = 0 63 for s in islice(argv, 1, None): 64 if pos > 0: 65 screen.addstr(0, pos, ' ') 66 pos += 1 67 screen.addstr(0, pos, s) 68 pos += len(s) 69 except KeyboardInterrupt as e: 70 # only handle Control + C, just in case loop is very slow 71 raise e 72 except Exception: 73 # just quit the loop, ignoring any out-of-screen display errors 74 pass 75 76 screen.getkey() 77 except KeyboardInterrupt: 78 res = 1 79 except Exception as e: 80 err = e 81 res = 1 82 83 try: 84 resetty() 85 except: 86 pass 87 try: 88 endwin() 89 except: 90 pass 91 if err: 92 print(str(e), file=stderr) 93 exit(res)