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