Home | History | Annotate | Download | only in guido
      1 import os
      2 import sys
      3 import string
      4 from Tkinter import *
      5 from ScrolledText import ScrolledText
      6 from Dialog import Dialog
      7 import signal
      8 
      9 BUFSIZE = 512
     10 
     11 class ShellWindow(ScrolledText):
     12 
     13     def __init__(self, master=None, shell=None, **cnf):
     14         if not shell:
     15             try:
     16                 shell = os.environ['SHELL']
     17             except KeyError:
     18                 shell = '/bin/sh'
     19             shell = shell + ' -i'
     20         args = string.split(shell)
     21         shell = args[0]
     22 
     23         apply(ScrolledText.__init__, (self, master), cnf)
     24         self.pos = '1.0'
     25         self.bind('<Return>', self.inputhandler)
     26         self.bind('<Control-c>', self.sigint)
     27         self.bind('<Control-t>', self.sigterm)
     28         self.bind('<Control-k>', self.sigkill)
     29         self.bind('<Control-d>', self.sendeof)
     30 
     31         self.pid, self.fromchild, self.tochild = spawn(shell, args)
     32         self.tk.createfilehandler(self.fromchild, READABLE,
     33                                   self.outputhandler)
     34 
     35     def outputhandler(self, file, mask):
     36         data = os.read(file, BUFSIZE)
     37         if not data:
     38             self.tk.deletefilehandler(file)
     39             pid, sts = os.waitpid(self.pid, 0)
     40             print 'pid', pid, 'status', sts
     41             self.pid = None
     42             detail = sts>>8
     43             cause = sts & 0xff
     44             if cause == 0:
     45                 msg = "exit status %d" % detail
     46             else:
     47                 msg = "killed by signal %d" % (cause & 0x7f)
     48                 if cause & 0x80:
     49                     msg = msg + " -- core dumped"
     50             Dialog(self.master,
     51                    text=msg,
     52                    title="Exit status",
     53                    bitmap='warning',
     54                    default=0,
     55                    strings=('OK',))
     56             return
     57         self.insert(END, data)
     58         self.pos = self.index("end - 1 char")
     59         self.yview_pickplace(END)
     60 
     61     def inputhandler(self, *args):
     62         if not self.pid:
     63             self.no_process()
     64             return "break"
     65         self.insert(END, "\n")
     66         line = self.get(self.pos, "end - 1 char")
     67         self.pos = self.index(END)
     68         os.write(self.tochild, line)
     69         return "break"
     70 
     71     def sendeof(self, *args):
     72         if not self.pid:
     73             self.no_process()
     74             return "break"
     75         os.close(self.tochild)
     76         return "break"
     77 
     78     def sendsig(self, sig):
     79         if not self.pid:
     80             self.no_process()
     81             return "break"
     82         os.kill(self.pid, sig)
     83         return "break"
     84 
     85     def sigint(self, *args):
     86         return self.sendsig(signal.SIGINT)
     87 
     88     def sigquit(self, *args):
     89         return self.sendsig(signal.SIGQUIT)
     90 
     91     def sigterm(self, *args):
     92         return self.sendsig(signal.SIGTERM)
     93 
     94     def sigkill(self, *args):
     95         return self.sendsig(signal.SIGKILL)
     96 
     97     def no_process(self):
     98         Dialog(self.master,
     99                text="No active process",
    100                title="No process",
    101                bitmap='error',
    102                default=0,
    103                strings=('OK',))
    104 
    105 MAXFD = 100     # Max number of file descriptors (os.getdtablesize()???)
    106 
    107 def spawn(prog, args):
    108     p2cread, p2cwrite = os.pipe()
    109     c2pread, c2pwrite = os.pipe()
    110     pid = os.fork()
    111     if pid == 0:
    112         # Child
    113         for i in 0, 1, 2:
    114             try:
    115                 os.close(i)
    116             except os.error:
    117                 pass
    118         if os.dup(p2cread) <> 0:
    119             sys.stderr.write('popen2: bad read dup\n')
    120         if os.dup(c2pwrite) <> 1:
    121             sys.stderr.write('popen2: bad write dup\n')
    122         if os.dup(c2pwrite) <> 2:
    123             sys.stderr.write('popen2: bad write dup\n')
    124         os.closerange(3, MAXFD)
    125         try:
    126             os.execvp(prog, args)
    127         finally:
    128             sys.stderr.write('execvp failed\n')
    129             os._exit(1)
    130     os.close(p2cread)
    131     os.close(c2pwrite)
    132     return pid, c2pread, p2cwrite
    133 
    134 def test():
    135     shell = string.join(sys.argv[1:])
    136     root = Tk()
    137     root.minsize(1, 1)
    138     if shell:
    139         w = ShellWindow(root, shell=shell)
    140     else:
    141         w = ShellWindow(root)
    142     w.pack(expand=1, fill=BOTH)
    143     w.focus_set()
    144     w.tk.mainloop()
    145 
    146 if __name__ == '__main__':
    147     test()
    148