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