updated
[polysh.git] / gsh / dispatchers.py
blobb93a9f611b668f917a2410b5e1f939e04fe10ad0
1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # GNU Library General Public License for more details.
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 # See the COPYING file for license information.
17 # Copyright (c) 2006, 2007, 2008 Guillaume Chazarain <guichaz@gmail.com>
19 import asyncore
20 import fcntl
21 import struct
22 import termios
24 from gsh import remote_dispatcher
25 from gsh.terminal_size import terminal_size
27 def all_instances():
28 """Iterator over all the remote_dispatcher instances"""
29 for i in asyncore.socket_map.itervalues():
30 if isinstance(i, remote_dispatcher.remote_dispatcher):
31 yield i
33 def make_unique_name(name):
34 """Each shell must have a unique display name, so identical hostnames are
35 suffixed by #NUMBER"""
36 display_names = set([i.display_name for i in all_instances()])
37 candidate_name = name
38 i = 0
39 while candidate_name in display_names:
40 i += 1
41 candidate_name = '%s#%d' % (name, i)
42 return candidate_name
44 def count_awaited_processes():
45 """Return a tuple with the number of awaited processes and the total
46 number"""
47 awaited = 0
48 total = 0
49 for i in all_instances():
50 if i.enabled:
51 total += 1
52 if i.state is not remote_dispatcher.STATE_IDLE:
53 awaited += 1
54 return awaited, total
56 def all_terminated():
57 """For each remote shell determine if its terminated"""
58 instances_found = False
59 for i in all_instances():
60 instances_found = True
61 if i.active and i.state is not remote_dispatcher.STATE_TERMINATED:
62 return False
63 return instances_found
65 max_display_name_length = 0
66 def update_max_display_name_length(change):
67 """The max_display_name_length serves to compute the length of the
68 whitespace used to align the output of the remote shells. A positive change
69 argument indicates that a remote shells with such a name length was enabled
70 while a negative change argument indicates a disabled remote shell"""
71 global max_display_name_length
73 if change < 0:
74 if -change < max_display_name_length:
75 # The disabled shell didn't have the longest name
76 return
77 new_max = 0
78 for i in all_instances():
79 if i.enabled:
80 l = len(i.display_name)
81 if l >= -change:
82 # The disabled shell was not alone with the longest name
83 return
84 new_max = max(l, new_max)
85 else:
86 new_max = max(change, max_display_name_length)
88 if new_max != max_display_name_length:
89 max_display_name_length = new_max
90 update_terminal_size()
92 def update_terminal_size():
93 """Propagate the terminal size to the remote shells accounting for the
94 place taken by the longest name"""
95 w, h = terminal_size()
96 w = max(w - max_display_name_length - 2, min(w, 10))
97 # python bug http://python.org/sf/1112949 on amd64
98 # from ajaxterm.py
99 bug = struct.unpack('i', struct.pack('I', termios.TIOCSWINSZ))[0]
100 packed_size = struct.pack('HHHH', h, w, 0, 0)
101 term_size = w, h
102 for i in all_instances():
103 if i.enabled and i.term_size != term_size:
104 i.term_size = term_size
105 fcntl.ioctl(i.fd, bug, packed_size)
107 def format_info(info_list):
108 """Turn a 2-dimension list of strings into a 1-dimension list of strings
109 with correct spacing"""
110 info_list.sort(key=lambda i:int(i[1][3:]))
111 max_lengths = []
112 if info_list:
113 nr_columns = len(info_list[0])
114 else:
115 nr_columns = 0
116 for i in xrange(nr_columns):
117 max_lengths.append(max([len(str(info[i])) for info in info_list]))
118 for info_id in xrange(len(info_list)):
119 info = info_list[info_id]
120 for str_id in xrange(len(info)):
121 orig_str = str(info[str_id])
122 indent = max_lengths[str_id] - len(orig_str)
123 info[str_id] = orig_str + indent * ' '
124 info_list[info_id] = ' '.join(info)