1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2005 - 2015 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 =begin edoc |
|
7 File implementing an asynchronous file like socket interface for the debugger. |
|
8 =end |
|
9 |
|
10 require 'socket' |
|
11 |
|
12 require 'DebugProtocol' |
|
13 |
|
14 def AsyncPendingWrite(file) |
|
15 =begin edoc |
|
16 Module function to check for data to be written. |
|
17 |
|
18 @param file The file object to be checked (file) |
|
19 @return Flag indicating if there is data wating (int) |
|
20 =end |
|
21 begin |
|
22 pending = file.pendingWrite |
|
23 rescue |
|
24 pending = 0 |
|
25 end |
|
26 return pending |
|
27 end |
|
28 |
|
29 class AsyncFile |
|
30 =begin edoc |
|
31 # Class wrapping a socket object with a file interface. |
|
32 =end |
|
33 @@maxtries = 10 |
|
34 @@maxbuffersize = 1024 * 1024 * 4 |
|
35 |
|
36 def initialize(sock, mode, name) |
|
37 =begin edoc |
|
38 Constructor |
|
39 |
|
40 @param sock the socket object being wrapped |
|
41 @param mode mode of this file (string) |
|
42 @param name name of this file (string) |
|
43 =end |
|
44 |
|
45 # Initialise the attributes. |
|
46 @closed = false |
|
47 @sock = sock |
|
48 @mode = mode |
|
49 @name = name |
|
50 @nWriteErrors = 0 |
|
51 |
|
52 @wpending = '' |
|
53 end |
|
54 |
|
55 def checkMode(mode) |
|
56 =begin edoc |
|
57 Private method to check the mode. |
|
58 |
|
59 This method checks, if an operation is permitted according to |
|
60 the mode of the file. If it is not, an IOError is raised. |
|
61 |
|
62 @param mode the mode to be checked (string) |
|
63 =end |
|
64 if mode != @mode |
|
65 raise IOError, '[Errno 9] Bad file descriptor' |
|
66 end |
|
67 end |
|
68 |
|
69 def nWrite(n) |
|
70 =begin edoc |
|
71 Private method to write a specific number of pending bytes. |
|
72 |
|
73 @param n the number of bytes to be written (int) |
|
74 =end |
|
75 if n > 0 |
|
76 begin |
|
77 buf = "%s%s" % [@wpending[0...n], EOT] |
|
78 sent = @sock.send(buf, 0) |
|
79 if sent > n |
|
80 sent -= EOT.length |
|
81 end |
|
82 @wpending = @wpending[sent..-1] |
|
83 @nWriteErrors = 0 |
|
84 rescue IOError |
|
85 @nWriteErrors += 1 |
|
86 if @nWriteErrors > self.maxtries |
|
87 raise |
|
88 # assume that an error that occurs 10 times wont go away |
|
89 end |
|
90 end |
|
91 end |
|
92 end |
|
93 |
|
94 def pendingWrite |
|
95 =begin edoc |
|
96 Public method that returns the number of bytes waiting to be written. |
|
97 |
|
98 @return the number of bytes to be written (int) |
|
99 =end |
|
100 ind = @wpending.rindex("\n") |
|
101 if ind |
|
102 return ind + 1 |
|
103 else |
|
104 return 0 |
|
105 end |
|
106 end |
|
107 |
|
108 def close |
|
109 =begin edoc |
|
110 Public method to close the file. |
|
111 =end |
|
112 if not @closed |
|
113 flush() |
|
114 begin |
|
115 @sock.close() |
|
116 rescue IOError |
|
117 end |
|
118 @closed = true |
|
119 end |
|
120 end |
|
121 |
|
122 def flush |
|
123 =begin edoc |
|
124 Public method to write all pending bytes. |
|
125 =end |
|
126 nWrite(@wpending.length) |
|
127 end |
|
128 |
|
129 def isatty |
|
130 =begin edoc |
|
131 Public method to indicate whether a tty interface is supported. |
|
132 |
|
133 @return always false |
|
134 =end |
|
135 return false |
|
136 end |
|
137 |
|
138 def fileno |
|
139 =begin edoc |
|
140 Public method returning the file number. |
|
141 |
|
142 @return file number (int) |
|
143 =end |
|
144 return @sock.fileno() |
|
145 end |
|
146 |
|
147 def getSock |
|
148 =begin edoc |
|
149 Public method to get the socket object. |
|
150 |
|
151 @return the socket object |
|
152 =end |
|
153 return @sock |
|
154 end |
|
155 |
|
156 def read(size = -1) |
|
157 =begin edoc |
|
158 Public method to read bytes from this file. |
|
159 |
|
160 @param size maximum number of bytes to be read (int) |
|
161 @return the bytes read (any) |
|
162 =end |
|
163 checkMode('r') |
|
164 |
|
165 if size < 0 |
|
166 size = 20000 |
|
167 end |
|
168 |
|
169 return @sock.recv(size) |
|
170 end |
|
171 |
|
172 def readline(size = -1) |
|
173 =begin edoc |
|
174 Public method to read a line from this file. |
|
175 |
|
176 <b>Note</b>: This method will not block and may return |
|
177 only a part of a line if that is all that is available. |
|
178 |
|
179 @param size maximum number of bytes to be read (int) |
|
180 @return one line of text up to size bytes (string) |
|
181 =end |
|
182 checkMode('r') |
|
183 |
|
184 if size < 0 |
|
185 size = 20000 |
|
186 end |
|
187 |
|
188 # The integration of the debugger client event loop and the connection |
|
189 # to the debugger relies on the two lines of the debugger command being |
|
190 # delivered as two separate events. Therefore we make sure we only |
|
191 # read a line at a time. |
|
192 line = @sock.recv(size, Socket::MSG_PEEK) |
|
193 |
|
194 eol = line.index("\n") |
|
195 |
|
196 if eol and eol >= 0 |
|
197 size = eol + 1 |
|
198 else |
|
199 size = line.length |
|
200 end |
|
201 |
|
202 # Now we know how big the line is, read it for real. |
|
203 return @sock.recv(size) |
|
204 end |
|
205 |
|
206 def readlines(sizehint = -1) |
|
207 =begin edoc |
|
208 Public method to read all lines from this file. |
|
209 |
|
210 @param sizehint hint of the numbers of bytes to be read (int) |
|
211 @return list of lines read (list of strings) |
|
212 =end |
|
213 lines = [] |
|
214 room = sizehint |
|
215 |
|
216 line = readline(room) |
|
217 linelen = line.length |
|
218 |
|
219 while linelen > 0 |
|
220 lines << line |
|
221 |
|
222 if sizehint >= 0 |
|
223 room = room - linelen |
|
224 |
|
225 if room <= 0 |
|
226 break |
|
227 end |
|
228 end |
|
229 |
|
230 line = readline(room) |
|
231 linelen = line.length |
|
232 end |
|
233 |
|
234 return lines |
|
235 end |
|
236 |
|
237 def gets() |
|
238 =begin edoc |
|
239 Public method to read a line from this file. |
|
240 =end |
|
241 readline() |
|
242 end |
|
243 |
|
244 def seek(offset, whence=IO::SEEK_SET) |
|
245 =begin edoc |
|
246 Public method to move the filepointer. |
|
247 |
|
248 @exception IOError This method is not supported and always raises an |
|
249 IOError. |
|
250 =end |
|
251 raise IOError, '[Errno 29] Illegal seek' |
|
252 end |
|
253 |
|
254 def tell |
|
255 =begin edoc |
|
256 Public method to get the filepointer position. |
|
257 |
|
258 @exception IOError This method is not supported and always raises an |
|
259 IOError. |
|
260 =end |
|
261 raise IOError, '[Errno 29] Illegal seek' |
|
262 end |
|
263 |
|
264 def <<(s) |
|
265 =begin edoc |
|
266 Synonym for write(s). |
|
267 |
|
268 @param s bytes to be written (string) |
|
269 =end |
|
270 write(s) |
|
271 end |
|
272 |
|
273 def write(s) |
|
274 =begin edoc |
|
275 Public method to write a string to the file. |
|
276 |
|
277 @param s bytes to be written (string) |
|
278 =end |
|
279 checkMode("w") |
|
280 tries = 0 |
|
281 s = s.to_s |
|
282 if @wpending.length == 0 |
|
283 @wpending = s.dup |
|
284 elsif @wpending.length + s.length > @@maxbuffersize |
|
285 # flush wpending if too big |
|
286 while @wpending.length > 0 |
|
287 # if we have a persistent error in sending the data, an |
|
288 # exception will be raised in nWrite |
|
289 flush |
|
290 tries += 1 |
|
291 if tries > @@maxtries |
|
292 raise IOError, "Too many attempts to send data" |
|
293 end |
|
294 end |
|
295 @wpending = s.dup |
|
296 else |
|
297 @wpending << s |
|
298 end |
|
299 nWrite(pendingWrite()) |
|
300 end |
|
301 |
|
302 def writelines(list) |
|
303 =begin edoc |
|
304 Public method to write a list of strings to the file. |
|
305 |
|
306 @param list the list to be written (list of string) |
|
307 =end |
|
308 list.each do |s| |
|
309 write(s) |
|
310 end |
|
311 end |
|
312 end |
|