--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DebugClients/Ruby/AsyncFile.rb Mon Dec 28 16:03:33 2009 +0000 @@ -0,0 +1,309 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2005 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> +# + +=begin edoc +File implementing an asynchronous file like socket interface for the debugger. +=end + +if RUBY_VERSION < "1.9" + $KCODE = 'UTF8' + require 'jcode' +end + +require 'socket' + +require 'DebugProtocol' + +def AsyncPendingWrite(file) +=begin edoc +Module function to check for data to be written. + +@param file The file object to be checked (file) +@return Flag indicating if there is data wating (int) +=end + begin + pending = file.pendingWrite + rescue + pending = 0 + end + return pending +end + +class AsyncFile +=begin edoc +# Class wrapping a socket object with a file interface. +=end + @@maxtries = 10 + @@maxbuffersize = 1024 * 1024 * 4 + + def initialize(sock, mode, name) +=begin edoc +Constructor + +@param sock the socket object being wrapped +@param mode mode of this file (string) +@param name name of this file (string) +=end + + # Initialise the attributes. + @closed = false + @sock = sock + @mode = mode + @name = name + @nWriteErrors = 0 + + @wpending = '' + end + + def checkMode(mode) +=begin edoc +Private method to check the mode. + +This method checks, if an operation is permitted according to +the mode of the file. If it is not, an IOError is raised. + +@param mode the mode to be checked (string) +=end + if mode != @mode + raise IOError, '[Errno 9] Bad file descriptor' + end + end + + def nWrite(n) +=begin edoc +Private method to write a specific number of pending bytes. + +@param n the number of bytes to be written (int) +=end + if n > 0 + begin + buf = "%s%s" % [@wpending[0...n], EOT] + sent = @sock.send(buf, 0) + if sent > n + sent -= EOT.length + end + @wpending = @wpending[sent..-1] + @nWriteErrors = 0 + rescue IOError + @nWriteErrors += 1 + if @nWriteErrors > self.maxtries + raise # assume that an error that occurs 10 times wont go away + end + end + end + end + + def pendingWrite +=begin edoc +Public method that returns the number of bytes waiting to be written. + +@return the number of bytes to be written (int) +=end + ind = @wpending.rindex("\n") + if ind + return ind + 1 + else + return 0 + end + end + + def close +=begin edoc +Public method to close the file. +=end + if not @closed + flush() + begin + @sock.close() + rescue IOError + end + @closed = true + end + end + + def flush +=begin edoc +Public method to write all pending bytes. +=end + nWrite(@wpending.length) + end + + def isatty +=begin edoc +Public method to indicate whether a tty interface is supported. + +@return always false +=end + return false + end + + def fileno +=begin edoc +Public method returning the file number. + +@return file number (int) +=end + return @sock.fileno() + end + + def getSock +=begin edoc +Public method to get the socket object. + +@return the socket object +=end + return @sock + end + + def read(size = -1) +=begin edoc +Public method to read bytes from this file. + +@param size maximum number of bytes to be read (int) +@return the bytes read (any) +=end + checkMode('r') + + if size < 0 + size = 20000 + end + + return @sock.recv(size) + end + + def readline(size = -1) +=begin edoc +Public method to read a line from this file. + +<b>Note</b>: This method will not block and may return +only a part of a line if that is all that is available. + +@param size maximum number of bytes to be read (int) +@return one line of text up to size bytes (string) +=end + checkMode('r') + + if size < 0 + size = 20000 + end + + # The integration of the debugger client event loop and the connection + # to the debugger relies on the two lines of the debugger command being + # delivered as two separate events. Therefore we make sure we only + # read a line at a time. + line = @sock.recv(size, Socket::MSG_PEEK) + + eol = line.index("\n") + + if eol and eol >= 0 + size = eol + 1 + else + size = line.length + end + + # Now we know how big the line is, read it for real. + return @sock.recv(size) + end + + def readlines(sizehint = -1) +=begin edoc +Public method to read all lines from this file. + +@param sizehint hint of the numbers of bytes to be read (int) +@return list of lines read (list of strings) +=end + lines = [] + room = sizehint + + line = readline(room) + linelen = line.length + + while linelen > 0 + lines << line + + if sizehint >= 0 + room = room - linelen + + if room <= 0 + break + end + end + + line = readline(room) + linelen = line.length + end + + return lines + end + + def seek(offset, whence=IO::SEEK_SET) +=begin edoc +Public method to move the filepointer. + +@exception IOError This method is not supported and always raises an + IOError. +=end + raise IOError, '[Errno 29] Illegal seek' + end + + def tell +=begin edoc +Public method to get the filepointer position. + +@exception IOError This method is not supported and always raises an + IOError. +=end + raise IOError, '[Errno 29] Illegal seek' + end + + def <<(s) +=begin edoc +Synonym for write(s). + +@param s bytes to be written (string) +=end + write(s) + end + + def write(s) +=begin edoc +Public method to write a string to the file. + +@param s bytes to be written (string) +=end + checkMode("w") + tries = 0 + s = s.to_s + if @wpending.length == 0 + @wpending = s.dup + elsif @wpending.length + s.length > @@maxbuffersize + # flush wpending if too big + while @wpending.length > 0 + # if we have a persistent error in sending the data, an exception + # will be raised in nWrite + flush + tries += 1 + if tries > @@maxtries + raise IOError, "Too many attempts to send data" + end + end + @wpending = s.dup + else + @wpending << s + end + nWrite(pendingWrite()) + end + + def writelines(list) +=begin edoc +Public method to write a list of strings to the file. + +@param list the list to be written (list of string) +=end + list.each do |s| + write(s) + end + end +end