rstyx 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +482 -0
- data/ChangeLog +4 -0
- data/Manifest.txt +17 -0
- data/NEWS +2 -0
- data/README +41 -0
- data/Rakefile +55 -0
- data/examples/readstyxfile.rb +48 -0
- data/lib/rstyx/authmodules.rb +90 -0
- data/lib/rstyx/client.rb +709 -693
- data/lib/rstyx/common.rb +71 -0
- data/lib/rstyx/errors.rb +35 -2
- data/lib/rstyx/messages.rb +377 -1113
- data/lib/rstyx/server.rb +1305 -0
- data/lib/rstyx/version.rb +2 -2
- data/lib/rstyx.rb +11 -4
- data/tests/tc_client.rb +260 -45
- data/tests/tc_message.rb +245 -388
- data/tests/tc_styxservproto.rb +596 -0
- metadata +51 -26
    
        data/lib/rstyx/client.rb
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            #!/usr/bin/ruby
         | 
| 2 2 | 
             
            #
         | 
| 3 | 
            -
            # Copyright (C) 2005 Rafael Sevilla
         | 
| 3 | 
            +
            # Copyright (C) 2005-2007 Rafael Sevilla
         | 
| 4 4 | 
             
            # This file is part of RStyx
         | 
| 5 5 | 
             
            #
         | 
| 6 6 | 
             
            # RStyx is free software; you can redistribute it and/or modify
         | 
| @@ -15,646 +15,599 @@ | |
| 15 15 | 
             
            #
         | 
| 16 16 | 
             
            # You should have received a copy of the GNU Lesser General Public
         | 
| 17 17 | 
             
            # License along with RStyx; if not, write to the Free Software
         | 
| 18 | 
            -
            # Foundation, Inc.,  | 
| 19 | 
            -
            #  | 
| 18 | 
            +
            # Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA
         | 
| 19 | 
            +
            # 02110-1301 USA.
         | 
| 20 20 | 
             
            #
         | 
| 21 21 | 
             
            # Styx Client
         | 
| 22 22 | 
             
            #
         | 
| 23 23 | 
             
            # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
         | 
| 24 | 
            -
            # Copyright:: Copyright (c) 2005 Rafael R. Sevilla
         | 
| 24 | 
            +
            # Copyright:: Copyright (c) 2005-2007 Rafael R. Sevilla
         | 
| 25 25 | 
             
            # License:: GNU Lesser General Public License
         | 
| 26 26 | 
             
            #
         | 
| 27 | 
            -
            # $Id: client.rb | 
| 27 | 
            +
            # $Id: client.rb 201 2007-06-08 17:13:58Z dido $
         | 
| 28 28 | 
             
            #
         | 
| 29 29 |  | 
| 30 | 
            -
            require 'English'
         | 
| 31 | 
            -
            require 'socket'
         | 
| 32 30 | 
             
            require 'thread'
         | 
| 33 31 | 
             
            require 'timeout'
         | 
| 34 | 
            -
            require ' | 
| 35 | 
            -
            require ' | 
| 32 | 
            +
            require 'rubygems'
         | 
| 33 | 
            +
            require 'eventmachine'
         | 
| 36 34 |  | 
| 37 35 | 
             
            module RStyx
         | 
| 38 36 |  | 
| 39 37 | 
             
              module Client
         | 
| 40 38 |  | 
| 41 39 | 
             
                ##
         | 
| 42 | 
            -
                #  | 
| 40 | 
            +
                # Message receiving module for the Styx client.  The client will
         | 
| 41 | 
            +
                # assemble all inbound messages. 
         | 
| 43 42 | 
             
                #
         | 
| 44 | 
            -
                 | 
| 43 | 
            +
                module StyxClient
         | 
| 44 | 
            +
                  attr_accessor :sentmessages
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  def post_init
         | 
| 47 | 
            +
                    @msgbuffer = ""
         | 
| 48 | 
            +
                    @lock = Mutex.new
         | 
| 49 | 
            +
                    @sentmessages = {}
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 45 52 | 
             
                  ##
         | 
| 46 | 
            -
                  #  | 
| 53 | 
            +
                  # Send a message asynchronously.
         | 
| 47 54 | 
             
                  #
         | 
| 48 | 
            -
                  # + | 
| 49 | 
            -
                  # + | 
| 50 | 
            -
                  #  | 
| 51 | 
            -
                  # return:: an unused value
         | 
| 55 | 
            +
                  # +message+:: [StyxMessage] the message to be sent
         | 
| 56 | 
            +
                  # +block+:: [Proc] the callback to use
         | 
| 57 | 
            +
                  # return:: [Fixnum] the tag number used.
         | 
| 52 58 | 
             
                  #
         | 
| 53 | 
            -
                  def  | 
| 54 | 
            -
                     | 
| 55 | 
            -
                     | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
                         | 
| 59 | 
            -
             | 
| 60 | 
            -
                         | 
| 61 | 
            -
                         | 
| 59 | 
            +
                  def send_message_async(message, &block)
         | 
| 60 | 
            +
                    # store the message and callback indexed by tag
         | 
| 61 | 
            +
                    @lock.synchronize do
         | 
| 62 | 
            +
                      if message.tag.nil?
         | 
| 63 | 
            +
                        # If a tag has not been explicitly specified, get
         | 
| 64 | 
            +
                        # a new tag for the message. We use the current
         | 
| 65 | 
            +
                        # thread's object ID as the base and use what
         | 
| 66 | 
            +
                        # amounts to a linear probing algorithm to
         | 
| 67 | 
            +
                        # determine a new tag in case of collisions.
         | 
| 68 | 
            +
                        tag = Thread.current.object_id % MAX_TAG
         | 
| 69 | 
            +
                        while @sentmessages.has_key?(tag)
         | 
| 70 | 
            +
                          tag += 1
         | 
| 71 | 
            +
                        end
         | 
| 72 | 
            +
                        message.tag = tag
         | 
| 62 73 | 
             
                      end
         | 
| 74 | 
            +
                      @sentmessages[message.tag] = [message, block]
         | 
| 63 75 | 
             
                    end
         | 
| 64 76 |  | 
| 65 | 
            -
                     | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
                     | 
| 69 | 
            -
                    return(val)
         | 
| 77 | 
            +
                    DEBUG > 0 && puts(" >> #{message.to_s}")
         | 
| 78 | 
            +
                    DEBUG > 1 && puts(" >> #{message.to_bytes.unpack("H*").inspect}")
         | 
| 79 | 
            +
                    send_data(message.to_bytes)
         | 
| 80 | 
            +
                    return(message.tag)
         | 
| 70 81 | 
             
                  end
         | 
| 71 82 |  | 
| 72 | 
            -
                end
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                ##
         | 
| 75 | 
            -
                # An abstract class for Styx connections. DO NOT INSTANTIATE THIS
         | 
| 76 | 
            -
                # CLASS DIRECTLY!
         | 
| 77 | 
            -
                #
         | 
| 78 | 
            -
                # A +Connection+ subclass instance acts in the same way as the
         | 
| 79 | 
            -
                # +File+ class would under Ruby, and many of the class methods for
         | 
| 80 | 
            -
                # File are implemented as instance methods for Connection, and are
         | 
| 81 | 
            -
                # relevant to the Styx filesystem the Connection is connected to.
         | 
| 82 | 
            -
                # There are a few differences: for instance, new is not equivalent
         | 
| 83 | 
            -
                # to open, for obvious reasons.
         | 
| 84 | 
            -
                #
         | 
| 85 | 
            -
                class Connection
         | 
| 86 | 
            -
                  attr_reader :msize, :version, :rootfid, :user
         | 
| 87 | 
            -
             | 
| 88 83 | 
             
                  ##
         | 
| 89 | 
            -
                  #  | 
| 90 | 
            -
                  #  | 
| 91 | 
            -
                  # finishes.
         | 
| 84 | 
            +
                  # Send a message synchronously.  If an error occurs, a
         | 
| 85 | 
            +
                  # StyxException is raised.
         | 
| 92 86 | 
             
                  #
         | 
| 93 | 
            -
                  # + | 
| 87 | 
            +
                  # +message+:: The Styx message to send.
         | 
| 88 | 
            +
                  # +timeout+:: optional timeout for receiving the response.
         | 
| 94 89 | 
             
                  #
         | 
| 95 | 
            -
                  def  | 
| 96 | 
            -
                     | 
| 97 | 
            -
                     | 
| 98 | 
            -
                     | 
| 99 | 
            -
                     | 
| 100 | 
            -
                     | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
                     | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 90 | 
            +
                  def send_message(message, timeout=0)
         | 
| 91 | 
            +
                    # The queue here holds the response message, and is used
         | 
| 92 | 
            +
                    # to communicate with the receive_data thread, which ultimately
         | 
| 93 | 
            +
                    # calls the block passed to send_message_async.
         | 
| 94 | 
            +
                    queue = Queue.new
         | 
| 95 | 
            +
                    send_message_async(message) do |tx,rx|
         | 
| 96 | 
            +
                      # Enqueue the response message -- this runs in the
         | 
| 97 | 
            +
                      # receive_data thread
         | 
| 98 | 
            +
                      queue << rx
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
                    Timeout::timeout(timeout, StyxException.new("timeout waiting for a reply to #{message.to_s}")) do
         | 
| 101 | 
            +
                      # This will block until the queue contains something
         | 
| 102 | 
            +
                      resp = queue.shift
         | 
| 103 | 
            +
                      # Check the response to see if it is the response to
         | 
| 104 | 
            +
                      # the transmitted message.
         | 
| 105 | 
            +
                      if resp.class == Message::Rerror
         | 
| 106 | 
            +
                        raise StyxException.new("#{resp.ename}")
         | 
| 107 | 
            +
                      end
         | 
| 113 108 |  | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
                        yield self
         | 
| 117 | 
            -
                      ensure
         | 
| 118 | 
            -
                        self.disconnect
         | 
| 109 | 
            +
                      if resp.ident != message.ident + 1
         | 
| 110 | 
            +
                        raise StyxException.new("Unexpected #{resp.to_s} received in response to #{message.to_s}")
         | 
| 119 111 | 
             
                      end
         | 
| 120 | 
            -
             | 
| 121 | 
            -
                      return(self)
         | 
| 112 | 
            +
                      return(resp)
         | 
| 122 113 | 
             
                    end
         | 
| 123 114 | 
             
                  end
         | 
| 124 115 |  | 
| 125 | 
            -
                  protected
         | 
| 126 | 
            -
             | 
| 127 116 | 
             
                  ##
         | 
| 128 | 
            -
                  #  | 
| 129 | 
            -
                  # a subclass of IO (e.g. a TCPSocket) on which the read and write
         | 
| 130 | 
            -
                  # methods can be used.
         | 
| 117 | 
            +
                  # Receive data from the network connection, called by EventMachine.
         | 
| 131 118 | 
             
                  #
         | 
| 132 | 
            -
                  def  | 
| 133 | 
            -
                     | 
| 134 | 
            -
             | 
| 119 | 
            +
                  def receive_data(data)
         | 
| 120 | 
            +
                    @msgbuffer << data
         | 
| 121 | 
            +
                    DEBUG > 1 && puts(" << #{data.unpack("H*").inspect}")
         | 
| 122 | 
            +
                    while @msgbuffer.length > 4
         | 
| 123 | 
            +
                      length = @msgbuffer.unpack("V")[0]
         | 
| 124 | 
            +
                      # Break out if there is not enough data in the message
         | 
| 125 | 
            +
                      # buffer to construct a message.
         | 
| 126 | 
            +
                      if @msgbuffer.length < length
         | 
| 127 | 
            +
                        break
         | 
| 128 | 
            +
                      end
         | 
| 135 129 |  | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
             | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 147 | 
            -
                         | 
| 148 | 
            -
                      rescue Exception => e
         | 
| 149 | 
            -
                        done = true
         | 
| 150 | 
            -
                        raise StyxException.new("error reading from socket: #{e.message}")
         | 
| 130 | 
            +
                      # Decode the received data
         | 
| 131 | 
            +
                      message, @msgbuffer = @msgbuffer.unpack("a#{length}a*")
         | 
| 132 | 
            +
                      styxmsg = Message::StyxMessage.from_bytes(message)
         | 
| 133 | 
            +
                      DEBUG > 0 && puts(" << #{styxmsg.to_s}")
         | 
| 134 | 
            +
                      # and look for its tag in the hash of received messages
         | 
| 135 | 
            +
                      tmsg, cb = @lock.synchronize do
         | 
| 136 | 
            +
                        @sentmessages.delete(styxmsg.tag)
         | 
| 137 | 
            +
                      end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                      if tmsg.nil?
         | 
| 140 | 
            +
                        # Discard unrecognized messages.
         | 
| 141 | 
            +
                        next
         | 
| 151 142 | 
             
                      end
         | 
| 152 143 |  | 
| 153 | 
            -
                       | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
                         | 
| 157 | 
            -
                        if  | 
| 158 | 
            -
                           | 
| 159 | 
            -
             | 
| 160 | 
            -
                          styxmsg = Message::StyxMessage.decode(message)
         | 
| 161 | 
            -
             | 
| 162 | 
            -
                          # let everyone know there's a new message available
         | 
| 163 | 
            -
                          @lock.synchronize do
         | 
| 164 | 
            -
                            @rmessages[styxmsg.tag] = styxmsg
         | 
| 165 | 
            -
                            @condvar.signal
         | 
| 144 | 
            +
                      if styxmsg.class == Message::Rflush
         | 
| 145 | 
            +
                        # We need to delete the oldtag as well, and send the
         | 
| 146 | 
            +
                        # rflush to the original sender if possible, so they
         | 
| 147 | 
            +
                        # don't wait forever.
         | 
| 148 | 
            +
                        if tmsg.respond_to?(:oldtag)
         | 
| 149 | 
            +
                          otmsg, ocb = @lock.synchronize do
         | 
| 150 | 
            +
                            @sentmessages.delete(tmsg.oldtag)
         | 
| 166 151 | 
             
                          end
         | 
| 167 152 | 
             
                        end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                        if !otmsg.nil? && !ocb.nil?
         | 
| 155 | 
            +
                          ocb.call(otmsg, styxmsg)
         | 
| 156 | 
            +
                        end
         | 
| 168 157 | 
             
                      end
         | 
| 169 | 
            -
                    end
         | 
| 170 | 
            -
                  end
         | 
| 171 158 |  | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
             | 
| 176 | 
            -
             | 
| 177 | 
            -
             | 
| 178 | 
            -
             | 
| 159 | 
            +
                      # Now, activate the callback block.
         | 
| 160 | 
            +
                      if !(tmsg.nil? || cb.nil?)
         | 
| 161 | 
            +
                        cb.call(tmsg, styxmsg)
         | 
| 162 | 
            +
                      end
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                      # after all this is done, there may still be enough data in
         | 
| 165 | 
            +
                      # the message buffer for more messages so keep looping.
         | 
| 166 | 
            +
                    end
         | 
| 167 | 
            +
                    # If we get here, we don't have enough data in the buffer to
         | 
| 168 | 
            +
                    # build a new message.
         | 
| 179 169 | 
             
                  end
         | 
| 180 170 |  | 
| 181 171 | 
             
                  ##
         | 
| 182 | 
            -
                  #  | 
| 183 | 
            -
                  #
         | 
| 184 | 
            -
                  # +message+:: [StyxMessage] the message to be sent
         | 
| 185 | 
            -
                  # return:: [Fixnum] the tag number used
         | 
| 172 | 
            +
                  # Disconnect from the remote server.
         | 
| 186 173 | 
             
                  #
         | 
| 187 | 
            -
                  def  | 
| 188 | 
            -
                     | 
| 189 | 
            -
             | 
| 174 | 
            +
                  def disconnect
         | 
| 175 | 
            +
                    # flush all outstanding messages before disconnect
         | 
| 176 | 
            +
                    sentmessages.each_key do |tag|
         | 
| 177 | 
            +
                      rflush = send_message(Message::Tflush.new(:oldtag => tag))
         | 
| 190 178 | 
             
                    end
         | 
| 191 179 |  | 
| 192 | 
            -
                     | 
| 193 | 
            -
             | 
| 194 | 
            -
                    # when we get the Rclunk corresponding to it.
         | 
| 195 | 
            -
                    if message.mtype == Message::StyxMessage::TCLUNK
         | 
| 196 | 
            -
                      @pendingclunks[message.tag] = message.fid
         | 
| 197 | 
            -
                    end
         | 
| 180 | 
            +
                    EventMachine::stop_event_loop
         | 
| 181 | 
            +
                  end
         | 
| 198 182 |  | 
| 199 | 
            -
             | 
| 200 | 
            -
             | 
| 201 | 
            -
             | 
| 183 | 
            +
                end                         # module StyxClient
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                class Connection
         | 
| 186 | 
            +
                  attr_accessor :usedfids, :pendingclunks, :umask
         | 
| 187 | 
            +
                  attr_reader :connectstate, :msize, :version
         | 
| 188 | 
            +
                  attr_reader :rootdirectory, :rootfid
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                  def initialize(auth=nil)
         | 
| 191 | 
            +
                    @usedfids = []
         | 
| 192 | 
            +
                    @pendingclunks = {}
         | 
| 193 | 
            +
                    @rpendingclunks = {}
         | 
| 194 | 
            +
                    @conn = nil
         | 
| 195 | 
            +
                    @rootfid = nil
         | 
| 196 | 
            +
                    @eventthread = nil
         | 
| 197 | 
            +
                    @authenticator = auth
         | 
| 198 | 
            +
                    @clunklock = Mutex.new
         | 
| 199 | 
            +
                    @umask = ::File.umask
         | 
| 202 200 | 
             
                  end
         | 
| 203 201 |  | 
| 204 202 | 
             
                  ##
         | 
| 205 | 
            -
                  #  | 
| 206 | 
            -
                  #
         | 
| 207 | 
            -
                  # +tag+:: [Fixnum] Tag to wait for
         | 
| 208 | 
            -
                  # return:: [Message::StyxMessage] the message received
         | 
| 203 | 
            +
                  # Get a new free FID.
         | 
| 209 204 | 
             
                  #
         | 
| 210 | 
            -
                  def  | 
| 211 | 
            -
                     | 
| 212 | 
            -
                     | 
| 213 | 
            -
             | 
| 214 | 
            -
                       | 
| 215 | 
            -
                         | 
| 205 | 
            +
                  def get_free_fid
         | 
| 206 | 
            +
                    found = false
         | 
| 207 | 
            +
                    val = nil
         | 
| 208 | 
            +
                    0.upto(MAX_FID) do |i|
         | 
| 209 | 
            +
                      unless @usedfids.include?(i)
         | 
| 210 | 
            +
                        val = i
         | 
| 211 | 
            +
                        break
         | 
| 216 212 | 
             
                      end
         | 
| 217 | 
            -
                      msg = @rmessages[tag]
         | 
| 218 | 
            -
                      @rmessages.delete(tag)
         | 
| 219 | 
            -
                    end
         | 
| 220 | 
            -
             | 
| 221 | 
            -
                    # puts "Received message #{msg.to_s}"
         | 
| 222 | 
            -
                    # Special things to do for certain messages
         | 
| 223 | 
            -
                    case msg.mtype
         | 
| 224 | 
            -
                    when Message::StyxMessage::RERROR
         | 
| 225 | 
            -
                      # raise an exception when we receive an Rerror
         | 
| 226 | 
            -
                      raise StyxException.new(msg.ename)
         | 
| 227 | 
            -
                    when Message::StyxMessage::RCLUNK
         | 
| 228 | 
            -
                      # find the fid corresponding to the original Tclunk and free it
         | 
| 229 | 
            -
                      fid = @pendingclunks[tag]
         | 
| 230 | 
            -
                      @pendingclunks.delete(tag)
         | 
| 231 | 
            -
                      return_fid(fid)
         | 
| 232 213 | 
             
                    end
         | 
| 233 | 
            -
                    return(msg)
         | 
| 234 | 
            -
                  end
         | 
| 235 214 |  | 
| 236 | 
            -
             | 
| 237 | 
            -
             | 
| 238 | 
            -
             | 
| 239 | 
            -
             | 
| 240 | 
            -
             | 
| 241 | 
            -
                  def get_free_fid
         | 
| 242 | 
            -
                    fid = Util.get_first_unused(0xffffffff,
         | 
| 243 | 
            -
                                                lambda {|i| @usedfids.include?(i) },
         | 
| 244 | 
            -
                                                "fids")
         | 
| 245 | 
            -
                    @usedfids << fid
         | 
| 246 | 
            -
                    return(fid)
         | 
| 215 | 
            +
                    if val.nil?
         | 
| 216 | 
            +
                      raise StyxException.new("No more free fids")
         | 
| 217 | 
            +
                    end
         | 
| 218 | 
            +
                    @usedfids << val
         | 
| 219 | 
            +
                    return(val)
         | 
| 247 220 | 
             
                  end
         | 
| 248 221 |  | 
| 249 222 | 
             
                  ##
         | 
| 250 | 
            -
                  #  | 
| 251 | 
            -
                  #
         | 
| 252 | 
            -
                  # +fid+:: [Fixnum] the fid to return
         | 
| 223 | 
            +
                  # Returns a fid after we're done using it.
         | 
| 253 224 | 
             
                  #
         | 
| 254 225 | 
             
                  def return_fid(fid)
         | 
| 255 226 | 
             
                    @usedfids.delete(fid)
         | 
| 256 227 | 
             
                  end
         | 
| 257 228 |  | 
| 258 | 
            -
                   | 
| 259 | 
            -
                  # Send a message and wait for the reply
         | 
| 260 | 
            -
                  #
         | 
| 261 | 
            -
                  # +message+:: [StyxMessage] the message to be sent
         | 
| 262 | 
            -
                  # return:: [StyxMessage] the reply to our message
         | 
| 263 | 
            -
                  #
         | 
| 264 | 
            -
                  def send_message(message)
         | 
| 265 | 
            -
                    tag = send_message_async(message)
         | 
| 266 | 
            -
                    return(get_reply(tag))
         | 
| 267 | 
            -
                  end
         | 
| 229 | 
            +
                  protected
         | 
| 268 230 |  | 
| 269 231 | 
             
                  ##
         | 
| 270 | 
            -
                  #  | 
| 232 | 
            +
                  # This method is used to prepare the connection, and should be
         | 
| 233 | 
            +
                  # defined by subclasses.
         | 
| 271 234 | 
             
                  #
         | 
| 272 | 
            -
                  def  | 
| 273 | 
            -
                     | 
| 274 | 
            -
                    # clunked last.
         | 
| 275 | 
            -
                    while (@usedfids.length > 0)
         | 
| 276 | 
            -
                      # The fid is automatically removed from @usedfids when the
         | 
| 277 | 
            -
                      # Rclunk message is received
         | 
| 278 | 
            -
                      rclunk = send_message(Message::Tclunk.new(@usedfids[-1]))
         | 
| 279 | 
            -
                    end
         | 
| 280 | 
            -
             | 
| 281 | 
            -
                    # Flush all outstanding messages
         | 
| 282 | 
            -
                    @rmessages.each_key do |tag|
         | 
| 283 | 
            -
                      rflush = send_message(Message::Tflush.new(tag))
         | 
| 284 | 
            -
                    end
         | 
| 285 | 
            -
                    # kill the receiver thread (FIXME: there has got to be a cleaner way!)
         | 
| 286 | 
            -
                    @receiver.kill
         | 
| 287 | 
            -
                    @conn.close
         | 
| 235 | 
            +
                  def prepare_connection
         | 
| 236 | 
            +
                    raise StyxException.new("No prepare_connection method defined")
         | 
| 288 237 | 
             
                  end
         | 
| 289 238 |  | 
| 239 | 
            +
                  public
         | 
| 240 | 
            +
             | 
| 290 241 | 
             
                  ##
         | 
| 291 | 
            -
                  #  | 
| 292 | 
            -
                  #  | 
| 293 | 
            -
                  #  | 
| 294 | 
            -
                  # terminates.
         | 
| 295 | 
            -
                  #
         | 
| 296 | 
            -
                  # +path+:: the path to the Styx file to be opened
         | 
| 297 | 
            -
                  # +mode+:: the mode to open the file (which can be r, w, r+, or e)
         | 
| 298 | 
            -
                  # +perm+:: the (optional) permissions bit mask.  This is only
         | 
| 299 | 
            -
                  #          relevant if the open creates a file.
         | 
| 300 | 
            -
                  # +rfid+:: the root fid.  If this is nil, use the root fid we got
         | 
| 301 | 
            -
                  #          when we connected.
         | 
| 302 | 
            -
                  #
         | 
| 303 | 
            -
                  # All parameters are optional.  If no path is specified, it defaults
         | 
| 304 | 
            -
                  # to reading the root directory on the Styx server.
         | 
| 305 | 
            -
                  #
         | 
| 306 | 
            -
                  # FIXME: This doesn't deal with the case where MAXWELEM is reached
         | 
| 307 | 
            -
                  #        yet.
         | 
| 308 | 
            -
                  # TODO: Allow the caller to make use of the Styx file access mode
         | 
| 309 | 
            -
                  #       values (OREAD, OWRITE, ORDWR, OEXEC, OTRUNC, ORCLOSE).
         | 
| 242 | 
            +
                  # Connect to a remote Styx server.  If a block is passed, yield
         | 
| 243 | 
            +
                  # self to the block and then do a disconnect when the block
         | 
| 244 | 
            +
                  # finishes.
         | 
| 310 245 | 
             
                  #
         | 
| 311 | 
            -
                  def  | 
| 312 | 
            -
                     | 
| 313 | 
            -
                    fid = get_free_fid
         | 
| 246 | 
            +
                  def connect(&block)
         | 
| 247 | 
            +
                    prepare_connection()
         | 
| 314 248 |  | 
| 315 | 
            -
                     | 
| 316 | 
            -
             | 
| 317 | 
            -
                    end
         | 
| 318 | 
            -
             | 
| 319 | 
            -
                    #
         | 
| 320 | 
            -
                    # The mode string is the standard Ruby mode string, viz.
         | 
| 321 | 
            -
                    # "r" - read-only, start at beginning of file
         | 
| 322 | 
            -
                    # "r+" - read/write, start at beginning of file
         | 
| 323 | 
            -
                    # "w" - write only, truncate existing file or create it if it
         | 
| 324 | 
            -
                    #       doesn't exist.
         | 
| 325 | 
            -
                    # "w+" - read/write, truncate existing file or create it if it
         | 
| 326 | 
            -
                    #        doesn't exist
         | 
| 327 | 
            -
                    # "a" - write only, starts at end of file if it exists otherwise
         | 
| 328 | 
            -
                    #       creates a new file for writing.
         | 
| 329 | 
            -
                    # "a+" - read/write, starts at end of file if it exists, otherwise
         | 
| 330 | 
            -
                    #        creates a new file for writing.
         | 
| 331 | 
            -
                    #
         | 
| 332 | 
            -
                    # There is also a mode specific to RStyx, not a standard mode
         | 
| 333 | 
            -
                    # for Ruby:
         | 
| 249 | 
            +
                    # Connection has been established.  Begin the handshaking process
         | 
| 250 | 
            +
                    # with the remote server.
         | 
| 334 251 | 
             
                    #
         | 
| 335 | 
            -
                    #  | 
| 252 | 
            +
                    # 1. Send a Tversion message and check the response from the
         | 
| 253 | 
            +
                    #    remote Styx server.
         | 
| 336 254 | 
             
                    #
         | 
| 337 | 
            -
                     | 
| 338 | 
            -
             | 
| 339 | 
            -
                     | 
| 340 | 
            -
             | 
| 341 | 
            -
                    create = false
         | 
| 342 | 
            -
                    truncate = false
         | 
| 343 | 
            -
                    append = false
         | 
| 344 | 
            -
                    mval = Message::Topen::OREAD
         | 
| 345 | 
            -
                    case mode
         | 
| 346 | 
            -
                    when "r"
         | 
| 347 | 
            -
                      # all default above
         | 
| 348 | 
            -
                    when "r+"
         | 
| 349 | 
            -
                      write = true
         | 
| 350 | 
            -
                      mval = Message::Topen::ORDWR
         | 
| 351 | 
            -
                    when "w"
         | 
| 352 | 
            -
                      read = false
         | 
| 353 | 
            -
                      write = true
         | 
| 354 | 
            -
                      create = true
         | 
| 355 | 
            -
                      truncate = true
         | 
| 356 | 
            -
                      mval = Message::Topen::OWRITE
         | 
| 357 | 
            -
                    when "w+"
         | 
| 358 | 
            -
                      read = true
         | 
| 359 | 
            -
                      write = true
         | 
| 360 | 
            -
                      create = true
         | 
| 361 | 
            -
                      truncate = true
         | 
| 362 | 
            -
                      mval = Message::Topen::ORDWR
         | 
| 363 | 
            -
                    when "a"
         | 
| 364 | 
            -
                      read = false
         | 
| 365 | 
            -
                      write = true
         | 
| 366 | 
            -
                      create = true
         | 
| 367 | 
            -
                      append = true
         | 
| 368 | 
            -
                      mval = Message::Topen::OWRITE
         | 
| 369 | 
            -
                    when "a+"
         | 
| 370 | 
            -
                      read = true
         | 
| 371 | 
            -
                      write = true
         | 
| 372 | 
            -
                      create = true
         | 
| 373 | 
            -
                      append = true
         | 
| 374 | 
            -
                      mval = Message::Topen::ORDWR
         | 
| 375 | 
            -
                    when "e"
         | 
| 376 | 
            -
                      read = true
         | 
| 377 | 
            -
                      write = true
         | 
| 378 | 
            -
                      mval = Message::Topen::OEXEC
         | 
| 379 | 
            -
                    else
         | 
| 380 | 
            -
                      raise StyxException.new("Invalid mode string '#{mode}'")
         | 
| 381 | 
            -
                    end
         | 
| 382 | 
            -
             | 
| 383 | 
            -
                    if truncate
         | 
| 384 | 
            -
                      mval |= Message::Topen::OTRUNC
         | 
| 255 | 
            +
                    rver = send_message(Message::Tversion.new(:msize => 8216,
         | 
| 256 | 
            +
                                                              :version => "9P2000"))
         | 
| 257 | 
            +
                    if (rver.version != "9P2000")
         | 
| 258 | 
            +
                      raise StyxException.new("Server uses unsupported Styx version #{rver.version}")
         | 
| 385 259 | 
             
                    end
         | 
| 386 | 
            -
             | 
| 387 | 
            -
                     | 
| 388 | 
            -
                     | 
| 389 | 
            -
             | 
| 390 | 
            -
             | 
| 391 | 
            -
                      #  | 
| 392 | 
            -
                       | 
| 393 | 
            -
                       | 
| 394 | 
            -
             | 
| 395 | 
            -
             | 
| 396 | 
            -
             | 
| 397 | 
            -
             | 
| 398 | 
            -
                      # would have its Qid.qtype high bit set to 1.  In this case,
         | 
| 399 | 
            -
                      # mode can only be 'r', and instead of a StyxIO, a StyxDir
         | 
| 400 | 
            -
                      # instance is created instead.
         | 
| 260 | 
            +
                    @msize = rver.msize
         | 
| 261 | 
            +
                    @version = rver.version
         | 
| 262 | 
            +
                    rfid = nil
         | 
| 263 | 
            +
                    # 2. Attach to the remote server
         | 
| 264 | 
            +
                    if @auth.nil?
         | 
| 265 | 
            +
                      # unauthenticated connection
         | 
| 266 | 
            +
                      rfid = get_free_fid
         | 
| 267 | 
            +
                      rattach = send_message(Message::Tattach.new(:fid => rfid,
         | 
| 268 | 
            +
                                                                  :afid => NOFID,
         | 
| 269 | 
            +
                                                                  :uname => ENV['USER'],
         | 
| 270 | 
            +
                                                                  :aname => ""))
         | 
| 271 | 
            +
                    else
         | 
| 401 272 | 
             
                      #
         | 
| 402 | 
            -
                      #  | 
| 403 | 
            -
                      # | 
| 273 | 
            +
                      # 3. Perform authentication based on the passed authenticator
         | 
| 274 | 
            +
                      #    object.
         | 
| 404 275 | 
             
                      #
         | 
| 405 | 
            -
                       | 
| 406 | 
            -
             | 
| 407 | 
            -
             | 
| 408 | 
            -
                       | 
| 409 | 
            -
             | 
| 410 | 
            -
                    rescue StyxException => se
         | 
| 411 | 
            -
                      # If the walk generated an error, see if the create flag
         | 
| 412 | 
            -
                      # was true.  If so, the error may be ignored, and the
         | 
| 413 | 
            -
                      # open_msgclass set to Message::Tcreate.  Otherwise, return
         | 
| 414 | 
            -
                      # the fid and reraise the exception.
         | 
| 415 | 
            -
                      if create
         | 
| 416 | 
            -
                        # Walk to the directory of the file to be made, if possible
         | 
| 417 | 
            -
                        begin
         | 
| 418 | 
            -
                          rwalk = send_message(Message::Twalk.new(rfid, fid,
         | 
| 419 | 
            -
                                                                  File.dirname(path)))
         | 
| 420 | 
            -
                        rescue StyxException => se
         | 
| 421 | 
            -
                          return_fid(fid)
         | 
| 422 | 
            -
                          raise se            # reraise the exception
         | 
| 423 | 
            -
                        end
         | 
| 424 | 
            -
                        open_msg = Message::Tcreate.new(fid, File.basename(path),
         | 
| 425 | 
            -
                                                        mval, perm)
         | 
| 426 | 
            -
                      else
         | 
| 427 | 
            -
                        return_fid(fid)
         | 
| 428 | 
            -
                        raise se            # reraise the exception
         | 
| 429 | 
            -
                      end
         | 
| 276 | 
            +
                      # If we have an authenticator object, we call its authenticator
         | 
| 277 | 
            +
                      # method with ourself.
         | 
| 278 | 
            +
                      #
         | 
| 279 | 
            +
                      rfid = @auth.authenticate(self)
         | 
| 430 280 | 
             
                    end
         | 
| 431 281 |  | 
| 432 | 
            -
                     | 
| 433 | 
            -
             | 
| 434 | 
            -
             | 
| 435 | 
            -
                      fp = iclass.new(self, path, fid, ropen.iounit, mode)
         | 
| 436 | 
            -
             | 
| 437 | 
            -
                      # if we're supposed to append, seek to the end of the file
         | 
| 438 | 
            -
                      if append
         | 
| 439 | 
            -
                        rstat = send_message(Message::Tstat.new(fid))
         | 
| 440 | 
            -
                        fp.seek(rstat.stat.length, :seek_set)
         | 
| 441 | 
            -
                      end
         | 
| 282 | 
            +
                    # If we get here, we're connected, and rfid represents the root
         | 
| 283 | 
            +
                    # fid of the connection
         | 
| 284 | 
            +
                    @rootfid = rfid
         | 
| 442 285 |  | 
| 443 | 
            -
             | 
| 444 | 
            -
             | 
| 445 | 
            -
                        yield  | 
| 446 | 
            -
             | 
| 447 | 
            -
             | 
| 448 | 
            -
                        end
         | 
| 449 | 
            -
                      else
         | 
| 450 | 
            -
                        return(fp)
         | 
| 286 | 
            +
                    if block_given?
         | 
| 287 | 
            +
                      begin
         | 
| 288 | 
            +
                        yield self
         | 
| 289 | 
            +
                      ensure
         | 
| 290 | 
            +
                        self.disconnect
         | 
| 451 291 | 
             
                      end
         | 
| 452 | 
            -
                     | 
| 453 | 
            -
                       | 
| 454 | 
            -
                      raise se            # reraise the exception
         | 
| 292 | 
            +
                    else
         | 
| 293 | 
            +
                      return(self)
         | 
| 455 294 | 
             
                    end
         | 
| 456 295 | 
             
                  end
         | 
| 457 296 |  | 
| 458 297 | 
             
                  ##
         | 
| 459 | 
            -
                  #  | 
| 460 | 
            -
                  # should also work properly on directories
         | 
| 298 | 
            +
                  # Disconnect from the remote server.
         | 
| 461 299 | 
             
                  #
         | 
| 462 | 
            -
                  def  | 
| 463 | 
            -
                     | 
| 464 | 
            -
             | 
| 465 | 
            -
             | 
| 300 | 
            +
                  def disconnect
         | 
| 301 | 
            +
                    # Clunk all outstanding fids in reverse order so the root fid
         | 
| 302 | 
            +
                    # gets clunked last.
         | 
| 303 | 
            +
                    while (@usedfids.length > 0)
         | 
| 466 304 | 
             
                      begin
         | 
| 467 | 
            -
                         | 
| 468 | 
            -
                      rescue | 
| 469 | 
            -
                         | 
| 470 | 
            -
                         | 
| 305 | 
            +
                        rclunk = tclunk(@usedfids[-1], true)
         | 
| 306 | 
            +
                      rescue
         | 
| 307 | 
            +
                        # An error is most likely a no such fid error.  Return the fid
         | 
| 308 | 
            +
                        # manually in this case.
         | 
| 309 | 
            +
                        return_fid(@usedfids[-1])
         | 
| 471 310 | 
             
                      end
         | 
| 311 | 
            +
                    end
         | 
| 472 312 |  | 
| 473 | 
            -
             | 
| 474 | 
            -
             | 
| 475 | 
            -
             | 
| 476 | 
            -
             | 
| 477 | 
            -
             | 
| 478 | 
            -
             | 
| 479 | 
            -
             | 
| 480 | 
            -
             | 
| 481 | 
            -
             | 
| 313 | 
            +
                    @conn.disconnect
         | 
| 314 | 
            +
                    @eventthread.kill
         | 
| 315 | 
            +
                  end
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                  ##
         | 
| 318 | 
            +
                  # Send a message, and return the response.  Delegates to
         | 
| 319 | 
            +
                  # @conn#send_message.  Do not use this method to send Tclunk
         | 
| 320 | 
            +
                  # messages!
         | 
| 321 | 
            +
                  #
         | 
| 322 | 
            +
                  def send_message(msg, timeout=0)
         | 
| 323 | 
            +
                    @conn.send_message(msg, timeout)
         | 
| 324 | 
            +
                  end
         | 
| 325 | 
            +
             | 
| 326 | 
            +
                  ##
         | 
| 327 | 
            +
                  # Fire and forget a Tclunk for some fid.  When the Rclunk is
         | 
| 328 | 
            +
                  # received, return the fid.  USE THIS METHOD, AND THIS METHOD
         | 
| 329 | 
            +
                  # ONLY, to send Tclunk messages.
         | 
| 330 | 
            +
                  #
         | 
| 331 | 
            +
                  def tclunk(fid, sync=false)
         | 
| 332 | 
            +
                    if @rpendingclunks.has_key?(fid)
         | 
| 333 | 
            +
                      return
         | 
| 334 | 
            +
                    end
         | 
| 335 | 
            +
                    q = nil
         | 
| 336 | 
            +
                    if sync
         | 
| 337 | 
            +
                      q = Queue.new
         | 
| 338 | 
            +
                    end
         | 
| 339 | 
            +
                    tag = @conn.send_message_async(Message::Tclunk.new(:fid => fid)) do |tx,rx|
         | 
| 340 | 
            +
                      # Test whether the response is an Rclunk.
         | 
| 341 | 
            +
                      if rx.class != Message::Rclunk
         | 
| 342 | 
            +
                        # this is an error condition, but it will only get reported
         | 
| 343 | 
            +
                        # if Thread.abort_on_exception is set to true, or if
         | 
| 344 | 
            +
                        # the tclunk is synchronous
         | 
| 345 | 
            +
                        exc = StyxException.new("#{tx.to_s} received #{rx.to_s}")
         | 
| 346 | 
            +
                        if sync
         | 
| 347 | 
            +
                          q << exc
         | 
| 348 | 
            +
                        else
         | 
| 349 | 
            +
                          raise exc
         | 
| 482 350 | 
             
                        end
         | 
| 483 | 
            -
             | 
| 351 | 
            +
                      end
         | 
| 352 | 
            +
                      # return the FID
         | 
| 353 | 
            +
                      fid = @pendingclunks.delete(tag)
         | 
| 354 | 
            +
                      @rpendingclunks.delete(fid)
         | 
| 355 | 
            +
                      return_fid(fid)
         | 
| 356 | 
            +
                      if sync
         | 
| 357 | 
            +
                        q << fid
         | 
| 358 | 
            +
                      end
         | 
| 359 | 
            +
                    end
         | 
| 360 | 
            +
                    @pendingclunks[tag] = fid
         | 
| 361 | 
            +
                    @rpendingclunks[fid] = tag
         | 
| 362 | 
            +
                    if sync
         | 
| 363 | 
            +
                      res = q.shift
         | 
| 364 | 
            +
                      if res.class == StyxException
         | 
| 365 | 
            +
                        raise res
         | 
| 484 366 | 
             
                      end
         | 
| 485 367 | 
             
                    end
         | 
| 486 368 | 
             
                  end
         | 
| 487 369 |  | 
| 488 | 
            -
                  alias rmdir delete
         | 
| 489 | 
            -
                  alias unlink delete
         | 
| 490 | 
            -
             | 
| 491 370 | 
             
                  ##
         | 
| 492 | 
            -
                  #  | 
| 493 | 
            -
                  #  | 
| 371 | 
            +
                  # Open a file on the remote server, throwing a StyxException if the
         | 
| 372 | 
            +
                  # file can't be found or opened in a given mode.
         | 
| 494 373 | 
             
                  #
         | 
| 495 | 
            -
                   | 
| 496 | 
            -
             | 
| 497 | 
            -
                       | 
| 498 | 
            -
             | 
| 499 | 
            -
                       | 
| 500 | 
            -
             | 
| 501 | 
            -
             | 
| 502 | 
            -
             | 
| 503 | 
            -
             | 
| 374 | 
            +
                  # +path+:: The path of the file relative to the server root.
         | 
| 375 | 
            +
                  # +mode+:: Integer representing the mode, or one of "r", "r+",
         | 
| 376 | 
            +
                  #          "w", "w+", "a", "a+", "e" as aliases
         | 
| 377 | 
            +
                  # +return+:: A File object representing the opened file, or
         | 
| 378 | 
            +
                  #          possibly a Directory object if the file was a directory.
         | 
| 379 | 
            +
                  #
         | 
| 380 | 
            +
                  # If a block is passed, it will yield the file object to the block
         | 
| 381 | 
            +
                  # and close the file when the block finishes (actually it will pass
         | 
| 382 | 
            +
                  # the block on to the StyxFile#open method, which does just that).
         | 
| 383 | 
            +
                  #
         | 
| 384 | 
            +
                  def open(path, mode="r", perm=0666, &block)
         | 
| 385 | 
            +
                    file = File.new(self, path)
         | 
| 386 | 
            +
             | 
| 387 | 
            +
                    append = false
         | 
| 388 | 
            +
                    create = false
         | 
| 389 | 
            +
                    numeric_mode = nil
         | 
| 390 | 
            +
                    if mode.is_a?(Integer)
         | 
| 391 | 
            +
                      numeric_mode = mode
         | 
| 392 | 
            +
                    else
         | 
| 393 | 
            +
                      case mode.to_s
         | 
| 394 | 
            +
                      when "r"
         | 
| 395 | 
            +
                        numeric_mode = OREAD
         | 
| 396 | 
            +
                      when "r+"
         | 
| 397 | 
            +
                        numeric_mode = ORDWR
         | 
| 398 | 
            +
                      when "w"
         | 
| 399 | 
            +
                        numeric_mode = OTRUNC | OWRITE
         | 
| 400 | 
            +
                        create = true
         | 
| 401 | 
            +
                      when "w+"
         | 
| 402 | 
            +
                        numeric_mode = OTRUNC | ORDWR
         | 
| 403 | 
            +
                        create = true
         | 
| 404 | 
            +
                      when "a"
         | 
| 405 | 
            +
                        numeric_mode = OWRITE
         | 
| 406 | 
            +
                        append = true
         | 
| 407 | 
            +
                        create = true
         | 
| 408 | 
            +
                      when "a+"
         | 
| 409 | 
            +
                        numeric_mode = ORDWR
         | 
| 410 | 
            +
                        append = true
         | 
| 411 | 
            +
                        create = true
         | 
| 412 | 
            +
                      when "e"
         | 
| 413 | 
            +
                        numeric_mode = OEXEC
         | 
| 414 | 
            +
                      end
         | 
| 504 415 | 
             
                    end
         | 
| 505 416 |  | 
| 506 | 
            -
                     | 
| 507 | 
            -
             | 
| 508 | 
            -
             | 
| 509 | 
            -
                                                                  Message::Topen::OREAD,
         | 
| 510 | 
            -
                                                                  perm))
         | 
| 511 | 
            -
                    rescue StyxException => se
         | 
| 512 | 
            -
                      return_fid(fid)
         | 
| 513 | 
            -
                      raise se
         | 
| 514 | 
            -
                    ensure
         | 
| 515 | 
            -
                      rclunk = send_message(Message::Tclunk.new(fid))
         | 
| 417 | 
            +
                    fp = file.open(numeric_mode, perm, create, &block)
         | 
| 418 | 
            +
                    if append
         | 
| 419 | 
            +
                      fp.seek(0, 2)
         | 
| 516 420 | 
             
                    end
         | 
| 421 | 
            +
                    return(fp)
         | 
| 517 422 | 
             
                  end
         | 
| 518 423 |  | 
| 519 | 
            -
                end
         | 
| 424 | 
            +
                end                         # class Connection
         | 
| 520 425 |  | 
| 521 426 | 
             
                ##
         | 
| 522 | 
            -
                # TCP connection  | 
| 427 | 
            +
                # TCP connection subclass.
         | 
| 523 428 | 
             
                #
         | 
| 524 429 | 
             
                class TCPConnection < Connection
         | 
| 525 | 
            -
                   | 
| 526 | 
            -
             | 
| 527 | 
            -
                  ##
         | 
| 528 | 
            -
                  # Create a Styx connection over TCP
         | 
| 529 | 
            -
                  #
         | 
| 530 | 
            -
                  # +host+:: [String] the host to connect to
         | 
| 531 | 
            -
                  # +port+:: [Fixnum] the port on which to connect
         | 
| 532 | 
            -
                  # +user+:: [String] the user to connect as
         | 
| 533 | 
            -
                  #
         | 
| 534 | 
            -
                  def initialize(host, port, user="")
         | 
| 430 | 
            +
                  def initialize(host, port, auth=nil)
         | 
| 535 431 | 
             
                    @host = host
         | 
| 536 432 | 
             
                    @port = port
         | 
| 537 | 
            -
                    super( | 
| 433 | 
            +
                    super(auth)
         | 
| 538 434 | 
             
                  end
         | 
| 539 435 |  | 
| 436 | 
            +
                  protected
         | 
| 437 | 
            +
             | 
| 540 438 | 
             
                  ##
         | 
| 541 | 
            -
                  # TCP connection  | 
| 439 | 
            +
                  # Prepare a TCP connection to the Styx server
         | 
| 542 440 | 
             
                  #
         | 
| 543 | 
            -
                  def  | 
| 544 | 
            -
                     | 
| 545 | 
            -
                     | 
| 441 | 
            +
                  def prepare_connection
         | 
| 442 | 
            +
                    queue = Queue.new
         | 
| 443 | 
            +
                    @eventthread = Thread.new do
         | 
| 444 | 
            +
                      EventMachine::run do
         | 
| 445 | 
            +
                        queue << EventMachine::connect(@host, @port, StyxClient)
         | 
| 446 | 
            +
                      end
         | 
| 447 | 
            +
                    end
         | 
| 448 | 
            +
             | 
| 449 | 
            +
                    @conn = queue.shift
         | 
| 546 450 | 
             
                  end
         | 
| 547 451 |  | 
| 548 | 
            -
             | 
| 452 | 
            +
                  public
         | 
| 453 | 
            +
             | 
| 454 | 
            +
                end                         # class TCPConnection
         | 
| 549 455 |  | 
| 550 456 | 
             
                ##
         | 
| 551 | 
            -
                #  | 
| 552 | 
            -
                 | 
| 553 | 
            -
             | 
| 457 | 
            +
                # A Styx client's view of a file.  This class should probably
         | 
| 458 | 
            +
                # never be directly instantiated, but only via Connection#open.
         | 
| 459 | 
            +
                # The buffering algorithm in use here is somewhat based on the
         | 
| 460 | 
            +
                # Buffering mix-in module in the Ruby OpenSSL module written by
         | 
| 461 | 
            +
                # Goto Yuuzou, but modified a bit to provide for offset
         | 
| 462 | 
            +
                # handling.  Note that this class isn't thread-safe.
         | 
| 463 | 
            +
                #
         | 
| 464 | 
            +
                class File
         | 
| 465 | 
            +
                  include Enumerable
         | 
| 554 466 |  | 
| 555 | 
            -
                   | 
| 556 | 
            -
             | 
| 557 | 
            -
             | 
| 467 | 
            +
                  attr_reader :conn, :path
         | 
| 468 | 
            +
                  attr_accessor :mode, :fid, :qid, :iounit, :sync
         | 
| 469 | 
            +
             | 
| 470 | 
            +
                  def initialize(conn, path)
         | 
| 471 | 
            +
                    @conn = conn            # the connection on which the file sits
         | 
| 472 | 
            +
                    @path = path            # pathname of the file
         | 
| 473 | 
            +
                    @fid = -1               # the client's file identifier
         | 
| 474 | 
            +
                    @qid = nil              # the server's unique identifier for this file
         | 
| 475 | 
            +
                    # maximum number of bytes that can be read or written to the file at a time
         | 
| 476 | 
            +
                    @iounit = 0
         | 
| 477 | 
            +
                    @mode = -1              # mode under which the file is opened, -1 == not open
         | 
| 478 | 
            +
                    @offset = 0             # File offset.  This is the same as @boffset only after a seek
         | 
| 479 | 
            +
                    @rboffset = 0           # Read buffer offset
         | 
| 480 | 
            +
                    @eof = false
         | 
| 481 | 
            +
                    @rbuffer = ""
         | 
| 482 | 
            +
                    @sync = false           # whether or not to buffer writes
         | 
| 558 483 | 
             
                  end
         | 
| 559 484 |  | 
| 560 485 | 
             
                  ##
         | 
| 561 | 
            -
                  #  | 
| 486 | 
            +
                  # Open the file on the server.  If a block is passed to this method
         | 
| 487 | 
            +
                  # it will pass the file to the block and close the file automatically
         | 
| 488 | 
            +
                  # when the block terminates.
         | 
| 562 489 | 
             
                  #
         | 
| 563 | 
            -
                   | 
| 564 | 
            -
             | 
| 565 | 
            -
                   | 
| 490 | 
            +
                  # This follows more or less the same semantics as sys-open(2)
         | 
| 491 | 
            +
                  # on Inferno, performing an open with truncate, when a file
         | 
| 492 | 
            +
                  # is opened that doesn't exist.
         | 
| 493 | 
            +
                  #
         | 
| 494 | 
            +
                  # XXX - twalk should handle the case of MAXWELEM, as well as for
         | 
| 495 | 
            +
                  #       when the twalk message is too large to fit in the server's
         | 
| 496 | 
            +
                  #       designated msize.
         | 
| 497 | 
            +
                  #
         | 
| 498 | 
            +
                  # +mode+:: Integer representing the mode - see the constants in common.rb
         | 
| 499 | 
            +
                  # +perm+:: Permissions of the file (only used on open/create)
         | 
| 500 | 
            +
                  # +create+:: should we create the file if it doesn't exist?
         | 
| 501 | 
            +
                  #
         | 
| 502 | 
            +
                  def open(mode, perm, create, &block)
         | 
| 503 | 
            +
                    dfid = @conn.get_free_fid
         | 
| 504 | 
            +
                    basename = ::File.basename(@path)
         | 
| 505 | 
            +
                    dirname = ::File.dirname(@path)
         | 
| 506 | 
            +
                    # Walk to the dirname first
         | 
| 507 | 
            +
                    twalk = Message::Twalk.new(:fid => @conn.rootfid, :newfid => dfid)
         | 
| 508 | 
            +
                    twalk.path = dirname
         | 
| 509 | 
            +
                    rwalk = @conn.send_message(twalk)
         | 
| 510 | 
            +
                    # if the rwalk has some other length than the number of path
         | 
| 511 | 
            +
                    # elements in the original twalk, we have failed.
         | 
| 512 | 
            +
                    if rwalk.qids.length != twalk.wnames.length
         | 
| 513 | 
            +
                      raise StyxException.new(("#{path} no such file or directory"))
         | 
| 514 | 
            +
                    end
         | 
| 515 | 
            +
                    ropen = fid = nil
         | 
| 516 | 
            +
                    begin
         | 
| 517 | 
            +
                      # Next, walk to the basename from there, using a new fid
         | 
| 518 | 
            +
                      fid = @conn.get_free_fid
         | 
| 519 | 
            +
                      twalk = Message::Twalk.new(:fid => dfid, :newfid => fid)
         | 
| 520 | 
            +
                      twalk.path = basename
         | 
| 521 | 
            +
                      rwalk = @conn.send_message(twalk)
         | 
| 522 | 
            +
                      # Do a Topen if this succeeds
         | 
| 523 | 
            +
                      open_msg = Message::Topen.new(:fid => fid, :mode => mode)
         | 
| 524 | 
            +
                      ropen = @conn.send_message(open_msg)
         | 
| 525 | 
            +
                      @conn.tclunk(dfid)
         | 
| 526 | 
            +
                    rescue Exception => e
         | 
| 527 | 
            +
                      # If we are being directed to create the file if it doesn't
         | 
| 528 | 
            +
                      # already exist, send a Tcreate message, and use the response
         | 
| 529 | 
            +
                      # for it as the ropen (the two classes respond to exactly the
         | 
| 530 | 
            +
                      # same messages--ah the wonders of duck typing!).  If not,
         | 
| 531 | 
            +
                      # or if that fails, this should propagate an exception upwards.
         | 
| 532 | 
            +
                      if create
         | 
| 533 | 
            +
                        # Alter the submitted permissions mask according to
         | 
| 534 | 
            +
                        # the connection's umask
         | 
| 535 | 
            +
                        perm &= ~(@conn.umask)
         | 
| 536 | 
            +
                        create_msg = Message::Tcreate.new(:fid => dfid, :name => basename,
         | 
| 537 | 
            +
                                                          :perm => perm, :mode => mode)
         | 
| 538 | 
            +
                        ropen = @conn.send_message(create_msg)
         | 
| 539 | 
            +
                        fid = dfid
         | 
| 540 | 
            +
                      else
         | 
| 541 | 
            +
                        raise e
         | 
| 542 | 
            +
                      end
         | 
| 543 | 
            +
                    end
         | 
| 566 544 |  | 
| 567 | 
            -
             | 
| 568 | 
            -
             | 
| 569 | 
            -
             | 
| 570 | 
            -
                     | 
| 545 | 
            +
                    # If we get here, we were able to successfully open or create
         | 
| 546 | 
            +
                    # the file.
         | 
| 547 | 
            +
                    @mode = mode
         | 
| 548 | 
            +
                    @fid = fid
         | 
| 549 | 
            +
                    @qid = ropen.qid
         | 
| 550 | 
            +
                    @iounit = ropen.iounit
         | 
| 551 | 
            +
                    # Determine if the file is actually a directory.  Such a file
         | 
| 552 | 
            +
                    # would have its Qid.qtype high bit set to 1.  In this case, instead
         | 
| 553 | 
            +
                    # of returning self, we return self wrapped in a Directory object.
         | 
| 554 | 
            +
                    retval = self
         | 
| 555 | 
            +
                    if twalk.wnames.length == 0 || (rwalk.qids[-1].qtype & 0x80 != 0)
         | 
| 556 | 
            +
                      retval = Directory.new(self)
         | 
| 557 | 
            +
                    end
         | 
| 558 | 
            +
                    if block_given?
         | 
| 559 | 
            +
                      begin
         | 
| 560 | 
            +
                        yield retval
         | 
| 561 | 
            +
                      ensure
         | 
| 562 | 
            +
                        retval.close
         | 
| 563 | 
            +
                      end
         | 
| 564 | 
            +
                    else
         | 
| 565 | 
            +
                      return(retval)
         | 
| 566 | 
            +
                    end
         | 
| 571 567 | 
             
                  end
         | 
| 572 568 |  | 
| 573 569 | 
             
                  ##
         | 
| 574 | 
            -
                  #  | 
| 570 | 
            +
                  # Read the Stat information for the file.
         | 
| 575 571 | 
             
                  #
         | 
| 576 | 
            -
                   | 
| 577 | 
            -
             | 
| 578 | 
            -
                  end
         | 
| 579 | 
            -
             | 
| 580 | 
            -
                  ##
         | 
| 581 | 
            -
                  # Empty the buffer.  Return all the data in the buffer
         | 
| 572 | 
            +
                  # returns: the RStyx::Message::Stat instance corresponding to
         | 
| 573 | 
            +
                  #          the file
         | 
| 582 574 | 
             
                  #
         | 
| 583 | 
            -
                  def  | 
| 584 | 
            -
                     | 
| 585 | 
            -
                     | 
| 586 | 
            -
                    return(retval)
         | 
| 575 | 
            +
                  def stat
         | 
| 576 | 
            +
                    rstat = @conn.send_message(Message::Tstat.new(:fid => @fid))
         | 
| 577 | 
            +
                    return(rstat.stat)
         | 
| 587 578 | 
             
                  end
         | 
| 588 579 |  | 
| 589 580 | 
             
                  ##
         | 
| 590 | 
            -
                  #  | 
| 591 | 
            -
                  #  | 
| 581 | 
            +
                  # Close the file.  This will flush all unwritten buffered data
         | 
| 582 | 
            +
                  # if any.
         | 
| 592 583 | 
             
                  #
         | 
| 593 | 
            -
                  def  | 
| 594 | 
            -
                     | 
| 595 | 
            -
                    @ | 
| 596 | 
            -
             | 
| 597 | 
            -
                      @ | 
| 584 | 
            +
                  def close
         | 
| 585 | 
            +
                    flush rescue nil
         | 
| 586 | 
            +
                    if @fid >= 0
         | 
| 587 | 
            +
                      # Clunk the fid
         | 
| 588 | 
            +
                      @conn.tclunk(@fid)
         | 
| 598 589 | 
             
                    end
         | 
| 599 | 
            -
                     | 
| 590 | 
            +
                    @fid = -1
         | 
| 591 | 
            +
                    @iounit = 0
         | 
| 592 | 
            +
                    @mode = -1
         | 
| 600 593 | 
             
                  end
         | 
| 601 594 |  | 
| 602 | 
            -
                   | 
| 603 | 
            -
             | 
| 604 | 
            -
                  # written to the buffer (the excess) if the buffer is full.
         | 
| 605 | 
            -
                  def fill(data)
         | 
| 606 | 
            -
                    if data.length > slack
         | 
| 607 | 
            -
                      excess = data[slack..(data.length)]
         | 
| 608 | 
            -
                      data = data[0..(slack-1)]
         | 
| 609 | 
            -
                    end
         | 
| 610 | 
            -
                    @data << data
         | 
| 611 | 
            -
                    return(excess)
         | 
| 595 | 
            +
                  def closed?
         | 
| 596 | 
            +
                    return(@mode < 0)
         | 
| 612 597 | 
             
                  end
         | 
| 613 598 |  | 
| 614 | 
            -
             | 
| 615 | 
            -
             | 
| 616 | 
            -
                ##
         | 
| 617 | 
            -
                # A file on a Styx server.  This should probably not be instantiated
         | 
| 618 | 
            -
                # directly, but only by the use of Connection#open.
         | 
| 619 | 
            -
                #
         | 
| 620 | 
            -
                # TODO: This should later become a true subclass of IO.
         | 
| 621 | 
            -
                #
         | 
| 622 | 
            -
                class StyxIO
         | 
| 623 | 
            -
                  attr_reader :name, :mode, :closed, :offset, :iounit, :fid
         | 
| 624 | 
            -
                  attr_accessor :sync
         | 
| 625 | 
            -
             | 
| 626 | 
            -
                  # Whether or not to buffer writes.  Unlike regular IO objects,
         | 
| 627 | 
            -
                  # StyxIO has this true by default.
         | 
| 628 | 
            -
                  attr_accessor :sync
         | 
| 629 | 
            -
                  include Enumerable
         | 
| 630 | 
            -
             | 
| 631 | 
            -
                  def initialize(conn, name, fid, iounit, mode="r")
         | 
| 632 | 
            -
                    @conn = conn
         | 
| 633 | 
            -
                    @name = name
         | 
| 634 | 
            -
                    @mode = mode
         | 
| 635 | 
            -
                    @closed = false         # closed flag
         | 
| 636 | 
            -
                    @fid = fid
         | 
| 637 | 
            -
                    @iounit = iounit        # maximum payload of a Twrite or Rread message
         | 
| 638 | 
            -
                    @offset = 0             # file offset
         | 
| 639 | 
            -
                    @sync = true
         | 
| 640 | 
            -
             | 
| 641 | 
            -
                    @buffer = Buffer.new(iounit) # the read buffer
         | 
| 642 | 
            -
                    @boffset = 0   # the offset of the last buffered read or write
         | 
| 643 | 
            -
                    @lastop = :none         # the last operation performed
         | 
| 644 | 
            -
                    @sync = false           # Buffer or not buffer writes
         | 
| 645 | 
            -
                  end
         | 
| 599 | 
            +
                  private
         | 
| 646 600 |  | 
| 647 601 | 
             
                  ##
         | 
| 648 602 | 
             
                  # Read at most +size+ bytes from +offset+
         | 
| 649 603 | 
             
                  # If the size argument is negative or omitted, read until EOF.
         | 
| 650 | 
            -
                  # This  | 
| 651 | 
            -
                  # not be used directly.
         | 
| 604 | 
            +
                  # This should probably not be used directly.
         | 
| 652 605 | 
             
                  #
         | 
| 653 606 | 
             
                  # +size+:: number of bytes to read from the file
         | 
| 654 607 | 
             
                  # +offset+:: the offset to read from.
         | 
| 655 608 | 
             
                  # return:: the data followed by the new offset
         | 
| 656 609 | 
             
                  #
         | 
| 657 | 
            -
                  def  | 
| 610 | 
            +
                  def _sysread(size=-1, offset=0)
         | 
| 658 611 | 
             
                    contents = ""
         | 
| 659 612 | 
             
                    bytes_to_read = size
         | 
| 660 613 | 
             
                    loop do
         | 
| @@ -665,7 +618,10 @@ module RStyx | |
| 665 618 | 
             
                      else
         | 
| 666 619 | 
             
                        n = bytes_to_read
         | 
| 667 620 | 
             
                      end
         | 
| 668 | 
            -
                      rread = | 
| 621 | 
            +
                      rread =
         | 
| 622 | 
            +
                        @conn.send_message(Message::Tread.new(:fid => @fid,
         | 
| 623 | 
            +
                                                              :offset => offset,
         | 
| 624 | 
            +
                                                              :count => n))
         | 
| 669 625 | 
             
                      if rread.data.length == 0
         | 
| 670 626 | 
             
                        break                 # EOF
         | 
| 671 627 | 
             
                      end
         | 
| @@ -681,16 +637,17 @@ module RStyx | |
| 681 637 | 
             
                  ##
         | 
| 682 638 | 
             
                  #
         | 
| 683 639 | 
             
                  # Write data to the file at +offset+.  No buffering is
         | 
| 684 | 
            -
                  # performed.  This  | 
| 685 | 
            -
                  # directly.
         | 
| 640 | 
            +
                  # performed.  This should probably not be used directly.
         | 
| 686 641 | 
             
                  #
         | 
| 687 | 
            -
                  # + | 
| 642 | 
            +
                  # +d+:: data to be written
         | 
| 688 643 | 
             
                  # +offset+:: the offset to write at
         | 
| 689 644 | 
             
                  #
         | 
| 690 | 
            -
                  # returns the new offset
         | 
| 645 | 
            +
                  # returns the new offset and the number of bytes written
         | 
| 691 646 | 
             
                  #
         | 
| 692 | 
            -
                  def  | 
| 647 | 
            +
                  def _syswrite(d, offset)
         | 
| 648 | 
            +
                    str = d.to_s
         | 
| 693 649 | 
             
                    pos = 0
         | 
| 650 | 
            +
                    count = 0
         | 
| 694 651 | 
             
                    loop do
         | 
| 695 652 | 
             
                      bytes_left = str.length - pos
         | 
| 696 653 | 
             
                      if bytes_left <= 0
         | 
| @@ -700,256 +657,323 @@ module RStyx | |
| 700 657 | 
             
                      else
         | 
| 701 658 | 
             
                        n = bytes_left
         | 
| 702 659 | 
             
                      end
         | 
| 703 | 
            -
                      rwrite = @conn.send_message(Message::Twrite.new(@fid, | 
| 704 | 
            -
                                                                       | 
| 705 | 
            -
             | 
| 706 | 
            -
                        raise StyxException.new("error writing data")
         | 
| 707 | 
            -
                      end
         | 
| 660 | 
            +
                      rwrite = @conn.send_message(Message::Twrite.new(:fid => @fid,
         | 
| 661 | 
            +
                                                                      :offset => offset,
         | 
| 662 | 
            +
                                                                      :data => str[pos..(pos+n)]))
         | 
| 708 663 | 
             
                      pos += n
         | 
| 709 664 | 
             
                      offset += n
         | 
| 665 | 
            +
                      count += rwrite.count
         | 
| 710 666 | 
             
                    end
         | 
| 711 | 
            -
                    return(offset)
         | 
| 667 | 
            +
                    return([offset, count])
         | 
| 712 668 | 
             
                  end
         | 
| 713 669 |  | 
| 714 | 
            -
                  public
         | 
| 715 | 
            -
             | 
| 716 670 | 
             
                  ##
         | 
| 717 | 
            -
                  #  | 
| 718 | 
            -
                  # operations should be possible.
         | 
| 671 | 
            +
                  # Add up to @iounit bytes to the read buffer.
         | 
| 719 672 | 
             
                  #
         | 
| 720 | 
            -
                  def  | 
| 721 | 
            -
                     | 
| 722 | 
            -
                     | 
| 723 | 
            -
             | 
| 673 | 
            +
                  def fill_rbuff
         | 
| 674 | 
            +
                    d, @rboffset = _sysread(@iounit, @rboffset)
         | 
| 675 | 
            +
                    if d.empty?
         | 
| 676 | 
            +
                      @eof = true
         | 
| 677 | 
            +
                    end
         | 
| 678 | 
            +
                    @rbuffer << d
         | 
| 724 679 | 
             
                  end
         | 
| 725 680 |  | 
| 726 681 | 
             
                  ##
         | 
| 727 | 
            -
                  #  | 
| 728 | 
            -
                  # If the size argument is negative or omitted, read until EOF.
         | 
| 729 | 
            -
                  # Returns nil if we read from the end of stream.
         | 
| 730 | 
            -
                  #
         | 
| 731 | 
            -
                  # This is a buffered read.
         | 
| 732 | 
            -
                  #
         | 
| 733 | 
            -
                  # +size+:: number of bytes to read from the file
         | 
| 682 | 
            +
                  # Consume +size+ bytes from the read buffer.
         | 
| 734 683 | 
             
                  #
         | 
| 735 | 
            -
                  def  | 
| 736 | 
            -
                     | 
| 737 | 
            -
             | 
| 738 | 
            -
             | 
| 684 | 
            +
                  def consume_rbuff(size=nil)
         | 
| 685 | 
            +
                    if @rbuffer.empty?
         | 
| 686 | 
            +
                      nil
         | 
| 687 | 
            +
                    else
         | 
| 688 | 
            +
                      size ||= @rbuffer.size
         | 
| 689 | 
            +
                      ret = @rbuffer[0, size]
         | 
| 690 | 
            +
                      @rbuffer[0, size] = ""
         | 
| 691 | 
            +
                      return(ret)
         | 
| 739 692 | 
             
                    end
         | 
| 740 | 
            -
             | 
| 741 | 
            -
             | 
| 742 | 
            -
                    bytes_to_read = size
         | 
| 743 | 
            -
                    contents = ""
         | 
| 744 | 
            -
                    loop do
         | 
| 745 | 
            -
                      if @buffer.empty?
         | 
| 746 | 
            -
                        # fill the buffer if the buffer goes empty
         | 
| 747 | 
            -
                        rdata, @boffset = sysread_int(@buffer.slack, @boffset)
         | 
| 748 | 
            -
                        if rdata.length == 0
         | 
| 749 | 
            -
                          break
         | 
| 750 | 
            -
                        end
         | 
| 751 | 
            -
                        @buffer.fill(rdata)
         | 
| 752 | 
            -
                      end
         | 
| 753 | 
            -
             | 
| 754 | 
            -
                      d = ""
         | 
| 755 | 
            -
             | 
| 756 | 
            -
                      if size < 0 || bytes_to_read > @buffer.bufsize
         | 
| 757 | 
            -
                        d = @buffer.empty
         | 
| 758 | 
            -
                      else
         | 
| 759 | 
            -
                        d = @buffer.consume(bytes_to_read)
         | 
| 760 | 
            -
                      end
         | 
| 693 | 
            +
                  end
         | 
| 761 694 |  | 
| 762 | 
            -
             | 
| 763 | 
            -
                      contents << d
         | 
| 764 | 
            -
                      if size >= 0
         | 
| 765 | 
            -
                        bytes_to_read -= d.length
         | 
| 766 | 
            -
                      end
         | 
| 695 | 
            +
                  public
         | 
| 767 696 |  | 
| 768 | 
            -
             | 
| 697 | 
            +
                  ##
         | 
| 698 | 
            +
                  # Read at most +size+ bytes from the Styx file or to the end of
         | 
| 699 | 
            +
                  # file if omitted.  Returns nil if called at end of file.
         | 
| 700 | 
            +
                  #
         | 
| 701 | 
            +
                  def read(size=-1)
         | 
| 702 | 
            +
                    until @eof
         | 
| 703 | 
            +
                      # Fill up the buffer until we have at least the requested
         | 
| 704 | 
            +
                      # size, or until end of file if size is negative.
         | 
| 705 | 
            +
                      if size > 0 && size <= @rbuffer.size
         | 
| 769 706 | 
             
                        break
         | 
| 770 707 | 
             
                      end
         | 
| 708 | 
            +
                      fill_rbuff
         | 
| 771 709 | 
             
                    end
         | 
| 772 | 
            -
             | 
| 773 | 
            -
             | 
| 710 | 
            +
             | 
| 711 | 
            +
                    # We managed to slurp the entire file!
         | 
| 712 | 
            +
                    if size < 0
         | 
| 713 | 
            +
                      size = @rbuffer.size
         | 
| 774 714 | 
             
                    end
         | 
| 775 | 
            -
             | 
| 715 | 
            +
             | 
| 716 | 
            +
                    @offset += size
         | 
| 717 | 
            +
                    retval = consume_rbuff(size) || ""
         | 
| 718 | 
            +
                    (size && retval.empty?) ? nil : retval
         | 
| 776 719 | 
             
                  end
         | 
| 777 720 |  | 
| 778 721 | 
             
                  ##
         | 
| 779 | 
            -
                  #  | 
| 722 | 
            +
                  # Reads the next "line" from the Styx file; lines are separated by
         | 
| 723 | 
            +
                  # +eol+.  An +eol+ of nil reads the entire contents.  Returns nil
         | 
| 724 | 
            +
                  # if called at end of file.
         | 
| 780 725 | 
             
                  #
         | 
| 781 | 
            -
                  def  | 
| 782 | 
            -
                     | 
| 783 | 
            -
             | 
| 726 | 
            +
                  def gets(eol=$/)
         | 
| 727 | 
            +
                    idx = @rbuffer.index(eol)
         | 
| 728 | 
            +
                    until @eof
         | 
| 729 | 
            +
                      if idx
         | 
| 730 | 
            +
                        break
         | 
| 731 | 
            +
                      end
         | 
| 732 | 
            +
                      fill_rbuff
         | 
| 733 | 
            +
                      idx = @rbuffer.index(eol)
         | 
| 784 734 | 
             
                    end
         | 
| 785 | 
            -
                     | 
| 786 | 
            -
             | 
| 787 | 
            -
             | 
| 735 | 
            +
                    if eol.is_a?(Regexp)
         | 
| 736 | 
            +
                      size = idx ? idx+$&.size : nil
         | 
| 737 | 
            +
                    else
         | 
| 738 | 
            +
                      size = idx ? idx+eol.size : nil
         | 
| 788 739 | 
             
                    end
         | 
| 789 | 
            -
                     | 
| 740 | 
            +
                    @offset += size
         | 
| 741 | 
            +
                    return(consume_rbuff(size))
         | 
| 790 742 | 
             
                  end
         | 
| 791 743 |  | 
| 792 744 | 
             
                  ##
         | 
| 793 | 
            -
                  #  | 
| 794 | 
            -
                  #  | 
| 795 | 
            -
                  # for reading.
         | 
| 745 | 
            +
                  # Executes the block for evely line in the Styx file, where lines
         | 
| 746 | 
            +
                  # are separated by +eol+.
         | 
| 796 747 | 
             
                  #
         | 
| 797 | 
            -
                  def  | 
| 798 | 
            -
                     | 
| 799 | 
            -
                       | 
| 800 | 
            -
                    end
         | 
| 801 | 
            -
             | 
| 802 | 
            -
                    while (!(c=read(1)).nil?)
         | 
| 803 | 
            -
                      yield c[0]
         | 
| 748 | 
            +
                  def each(eol=$/)
         | 
| 749 | 
            +
                    while line = self.gets(eol)
         | 
| 750 | 
            +
                      yield line
         | 
| 804 751 | 
             
                    end
         | 
| 805 752 | 
             
                  end
         | 
| 806 753 |  | 
| 754 | 
            +
                  alias each_line each
         | 
| 755 | 
            +
             | 
| 807 756 | 
             
                  ##
         | 
| 808 | 
            -
                  # Reads the  | 
| 809 | 
            -
                  #  | 
| 810 | 
            -
                  # | 
| 811 | 
            -
                   | 
| 812 | 
            -
             | 
| 813 | 
            -
             | 
| 814 | 
            -
             | 
| 815 | 
            -
                  # of file.
         | 
| 816 | 
            -
                  #
         | 
| 817 | 
            -
                  def gets(sep_str=$RS)
         | 
| 818 | 
            -
                    line = ""
         | 
| 819 | 
            -
                    while (!(c=read(1)).nil?)
         | 
| 820 | 
            -
                      line << c
         | 
| 821 | 
            -
                      if c == $RS
         | 
| 822 | 
            -
                        break
         | 
| 823 | 
            -
                      end
         | 
| 824 | 
            -
                    end
         | 
| 825 | 
            -
                    if line.length == 0
         | 
| 826 | 
            -
                      return(nil)
         | 
| 757 | 
            +
                  # Reads all of the lines in the Styx file, and returns them in an
         | 
| 758 | 
            +
                  # array.  Lines are separated by an optional separator +eol+.
         | 
| 759 | 
            +
                  #
         | 
| 760 | 
            +
                  def readlines(eol=$/)
         | 
| 761 | 
            +
                    ary = []
         | 
| 762 | 
            +
                    while line = self.gets(eol)
         | 
| 763 | 
            +
                      ary << line
         | 
| 827 764 | 
             
                    end
         | 
| 828 | 
            -
                     | 
| 765 | 
            +
                    ary
         | 
| 829 766 | 
             
                  end
         | 
| 830 767 |  | 
| 831 768 | 
             
                  ##
         | 
| 832 | 
            -
                  #  | 
| 833 | 
            -
                   | 
| 834 | 
            -
             | 
| 835 | 
            -
             | 
| 836 | 
            -
                   | 
| 837 | 
            -
                    if !block_given?
         | 
| 838 | 
            -
                      raise LocalJumpError("no block given")
         | 
| 839 | 
            -
                    end
         | 
| 769 | 
            +
                  # Reads a line as with gets, but 
         | 
| 770 | 
            +
                  def readline(eol=$/)
         | 
| 771 | 
            +
                    raise EOFError if eof?
         | 
| 772 | 
            +
                    return(gets(eol))
         | 
| 773 | 
            +
                  end
         | 
| 840 774 |  | 
| 841 | 
            -
             | 
| 842 | 
            -
             | 
| 775 | 
            +
                  def getc
         | 
| 776 | 
            +
                    c = read(1)
         | 
| 777 | 
            +
                    return(c ? c[0] : nil)
         | 
| 778 | 
            +
                  end
         | 
| 779 | 
            +
             | 
| 780 | 
            +
                  def readchar
         | 
| 781 | 
            +
                    raise EOFError if eof?
         | 
| 782 | 
            +
                    getc
         | 
| 783 | 
            +
                  end
         | 
| 784 | 
            +
             | 
| 785 | 
            +
                  def ungetc(c)
         | 
| 786 | 
            +
                    @rbuffer[0,0] = c.chr
         | 
| 787 | 
            +
                    @offset -= 1
         | 
| 788 | 
            +
                  end
         | 
| 789 | 
            +
             | 
| 790 | 
            +
                  def eof?
         | 
| 791 | 
            +
                    if !@eof && @rbuffer.empty?
         | 
| 792 | 
            +
                      fill_rbuff
         | 
| 843 793 | 
             
                    end
         | 
| 794 | 
            +
                    return(@eof && @rbuffer.empty?)
         | 
| 844 795 | 
             
                  end
         | 
| 845 796 |  | 
| 797 | 
            +
                  alias eof eof?
         | 
| 798 | 
            +
             | 
| 799 | 
            +
                  private
         | 
| 800 | 
            +
             | 
| 846 801 | 
             
                  ##
         | 
| 847 | 
            -
                  #  | 
| 802 | 
            +
                  # Write data to the buffer if the buffer is not yet full, or
         | 
| 803 | 
            +
                  # if it has been determined (e.g. by an end of line marker) that
         | 
| 804 | 
            +
                  # we should flush the buffer, and actually write.
         | 
| 848 805 | 
             
                  #
         | 
| 849 | 
            -
                  def  | 
| 850 | 
            -
                     | 
| 851 | 
            -
                      @ | 
| 806 | 
            +
                  def do_write(s)
         | 
| 807 | 
            +
                    unless defined?(@wbuffer)
         | 
| 808 | 
            +
                      @wbuffer = ""
         | 
| 809 | 
            +
                      # we obviously start writing at the current offset
         | 
| 810 | 
            +
                      @wboffset = @offset
         | 
| 852 811 | 
             
                    end
         | 
| 853 | 
            -
                    @ | 
| 854 | 
            -
             | 
| 855 | 
            -
                    # | 
| 856 | 
            -
                    #  | 
| 857 | 
            -
                     | 
| 858 | 
            -
             | 
| 859 | 
            -
             | 
| 812 | 
            +
                    @wbuffer << s
         | 
| 813 | 
            +
                    @offset += s.length
         | 
| 814 | 
            +
                    #
         | 
| 815 | 
            +
                    # We flush the buffer if at least one of the following conditions
         | 
| 816 | 
            +
                    # has been met:
         | 
| 817 | 
            +
                    # 
         | 
| 818 | 
            +
                    # 1. The sync flag is set to true.
         | 
| 819 | 
            +
                    # 2. The write buffer size has equalled or exceeded the connection's
         | 
| 820 | 
            +
                    #    iounit.
         | 
| 821 | 
            +
                    # 3. The write buffer now contains an end of line marker, in which
         | 
| 822 | 
            +
                    #    cas we flush only until the end of line marker.
         | 
| 823 | 
            +
                    #
         | 
| 824 | 
            +
                    if @sync || @wbuffer.size >= @iounit || (idx = @wbuffer.rindex($/))
         | 
| 825 | 
            +
                      remain = idx ? idx + $/.size : @wbuffer.length
         | 
| 826 | 
            +
                      nwritten = 0
         | 
| 827 | 
            +
                      ofs = @wboffset
         | 
| 828 | 
            +
                      while remain > 0
         | 
| 829 | 
            +
                        str = @wbuffer[nwritten, remain]
         | 
| 830 | 
            +
                        ofs, nwrote = _syswrite(str, ofs)
         | 
| 831 | 
            +
                        remain -= nwrote
         | 
| 832 | 
            +
                        nwritten += nwrote
         | 
| 833 | 
            +
                      end
         | 
| 834 | 
            +
                      @wbuffer[0, nwritten] = ""
         | 
| 835 | 
            +
                      @wboffset += nwritten
         | 
| 860 836 | 
             
                    end
         | 
| 837 | 
            +
                  end
         | 
| 838 | 
            +
             | 
| 839 | 
            +
                  public
         | 
| 840 | 
            +
             | 
| 841 | 
            +
                  def write(s)
         | 
| 842 | 
            +
                    do_write(s)
         | 
| 843 | 
            +
                    return(s.length)
         | 
| 844 | 
            +
                  end
         | 
| 845 | 
            +
             | 
| 846 | 
            +
                  def <<(s)
         | 
| 847 | 
            +
                    do_write(s)
         | 
| 848 | 
            +
                    return(self)
         | 
| 849 | 
            +
                  end
         | 
| 861 850 |  | 
| 862 | 
            -
             | 
| 863 | 
            -
                     | 
| 864 | 
            -
                     | 
| 865 | 
            -
             | 
| 866 | 
            -
             | 
| 867 | 
            -
             | 
| 868 | 
            -
             | 
| 851 | 
            +
                  def puts(*args)
         | 
| 852 | 
            +
                    s = ""
         | 
| 853 | 
            +
                    if args.empty?
         | 
| 854 | 
            +
                      s << "\n"
         | 
| 855 | 
            +
                    end
         | 
| 856 | 
            +
                    args.each do |arg|
         | 
| 857 | 
            +
                      s << arg.to_s
         | 
| 858 | 
            +
                      if $/ && /\n\z/ !~ s
         | 
| 859 | 
            +
                        s << "\n"
         | 
| 869 860 | 
             
                      end
         | 
| 870 | 
            -
                      data = @buffer.fill(data)
         | 
| 871 861 | 
             
                    end
         | 
| 872 | 
            -
                     | 
| 873 | 
            -
                    return( | 
| 862 | 
            +
                    do_write(s)
         | 
| 863 | 
            +
                    return(nil)
         | 
| 864 | 
            +
                  end
         | 
| 865 | 
            +
             | 
| 866 | 
            +
                  def print(*args)
         | 
| 867 | 
            +
                    s = ""
         | 
| 868 | 
            +
                    args.each{ |arg| s << arg.to_s }
         | 
| 869 | 
            +
                    do_write(s)
         | 
| 870 | 
            +
                    return(nil)
         | 
| 871 | 
            +
                  end
         | 
| 872 | 
            +
             | 
| 873 | 
            +
                  def printf(s, *args)
         | 
| 874 | 
            +
                    do_write(s % args)
         | 
| 875 | 
            +
                    return(nil)
         | 
| 874 876 | 
             
                  end
         | 
| 875 877 |  | 
| 876 | 
            -
                  ##
         | 
| 877 | 
            -
                  # Flushes any buffered data
         | 
| 878 | 
            -
                  #
         | 
| 879 878 | 
             
                  def flush
         | 
| 880 | 
            -
                     | 
| 881 | 
            -
             | 
| 882 | 
            -
             | 
| 883 | 
            -
                     | 
| 879 | 
            +
                    osync = @sync
         | 
| 880 | 
            +
                    @sync = true
         | 
| 881 | 
            +
                    do_write ""
         | 
| 882 | 
            +
                    @sync = osync
         | 
| 884 883 | 
             
                  end
         | 
| 885 884 |  | 
| 886 885 | 
             
                  ##
         | 
| 887 | 
            -
                  #  | 
| 886 | 
            +
                  # Seek in the file.  The whence values may be one of SEEK_SET
         | 
| 887 | 
            +
                  # SEEK_CUR, or SEEK_END, as defined in rstyx/common, and they
         | 
| 888 | 
            +
                  # result in the offset being taken from the beginning of the
         | 
| 889 | 
            +
                  # file, relative to the current offset, or from the end of the
         | 
| 890 | 
            +
                  # file respectively.
         | 
| 888 891 | 
             
                  #
         | 
| 889 | 
            -
                  #  | 
| 892 | 
            +
                  # XXX: A seek, no matter where it goes, will always invalidate
         | 
| 893 | 
            +
                  #      any buffering.  This is fine for write buffers, but is
         | 
| 894 | 
            +
                  #      somewhat wasteful for the read buffers.
         | 
| 890 895 | 
             
                  #
         | 
| 891 | 
            -
                  def seek(offset, whence | 
| 892 | 
            -
                    # flush the buffers | 
| 896 | 
            +
                  def seek(offset, whence)
         | 
| 897 | 
            +
                    # Before seeking, flush the write buffers
         | 
| 893 898 | 
             
                    flush
         | 
| 899 | 
            +
                    s = self.stat
         | 
| 894 900 | 
             
                    case whence
         | 
| 895 | 
            -
                    when  | 
| 896 | 
            -
             | 
| 897 | 
            -
             | 
| 898 | 
            -
             | 
| 899 | 
            -
                     | 
| 901 | 
            +
                    when 0
         | 
| 902 | 
            +
                      @offset = offset
         | 
| 903 | 
            +
                    when 1
         | 
| 904 | 
            +
                      @offset += offset
         | 
| 905 | 
            +
                    when 2
         | 
| 906 | 
            +
                      # We have to obtain the stat of the file to do this kind of seek.
         | 
| 907 | 
            +
                      @offset = s.length + offset
         | 
| 908 | 
            +
                    else
         | 
| 909 | 
            +
                      raise StyxException.new("Invalid seek")
         | 
| 900 910 | 
             
                    end
         | 
| 901 | 
            -
                     | 
| 902 | 
            -
                    @ | 
| 911 | 
            +
                    # After seeking, discard the read buffers
         | 
| 912 | 
            +
                    @rbuffer = ""
         | 
| 913 | 
            +
                    @rboffset = @offset
         | 
| 914 | 
            +
                    @eof = (@offset >= s.length)
         | 
| 915 | 
            +
                    return(@offset)
         | 
| 916 | 
            +
                  end
         | 
| 917 | 
            +
             | 
| 918 | 
            +
                  def tell
         | 
| 919 | 
            +
                    return(@offset)
         | 
| 903 920 | 
             
                  end
         | 
| 904 921 |  | 
| 905 | 
            -
                  ##
         | 
| 906 | 
            -
                  # Seek to the start of the file
         | 
| 907 | 
            -
                  #
         | 
| 908 922 | 
             
                  def rewind
         | 
| 909 | 
            -
                    seek(0,  | 
| 923 | 
            +
                    seek(0, 0)
         | 
| 910 924 | 
             
                  end
         | 
| 911 925 |  | 
| 912 926 | 
             
                  ##
         | 
| 913 | 
            -
                  #  | 
| 927 | 
            +
                  # Reads +size+ bytes from the Styx file and returns them as a string.
         | 
| 928 | 
            +
                  # Do not mix with other methods that read from the Styx file or you
         | 
| 929 | 
            +
                  # may get unpredictable results.
         | 
| 914 930 | 
             
                  #
         | 
| 915 | 
            -
                  def  | 
| 916 | 
            -
                     | 
| 931 | 
            +
                  def sysread(size=-1)
         | 
| 932 | 
            +
                    data, @offset = _sysread(size, @offset)
         | 
| 933 | 
            +
                    if data.length == 0
         | 
| 934 | 
            +
                      return(nil)
         | 
| 935 | 
            +
                    end
         | 
| 936 | 
            +
                    return(data)
         | 
| 917 937 | 
             
                  end
         | 
| 918 938 |  | 
| 919 939 | 
             
                  ##
         | 
| 920 | 
            -
                  #  | 
| 921 | 
            -
                  #
         | 
| 922 | 
            -
                   | 
| 923 | 
            -
             | 
| 924 | 
            -
                     | 
| 940 | 
            +
                  # Writes +data+ to the Styx file.  Returns the number of bytes written.
         | 
| 941 | 
            +
                  # do not mix with other methods that write to the Styx file or you
         | 
| 942 | 
            +
                  # may get unpredictable results.
         | 
| 943 | 
            +
                  def syswrite(data)
         | 
| 944 | 
            +
                    @offset, count = _syswrite(data, @offset)
         | 
| 945 | 
            +
                    return(count)
         | 
| 925 946 | 
             
                  end
         | 
| 926 947 |  | 
| 927 | 
            -
                end
         | 
| 948 | 
            +
                end                         # class File
         | 
| 928 949 |  | 
| 929 950 | 
             
                ##
         | 
| 930 | 
            -
                #  | 
| 931 | 
            -
                #  | 
| 951 | 
            +
                # Styx directory.  This obtains the entries inside a directory, and
         | 
| 952 | 
            +
                # works by delegating to File.
         | 
| 932 953 | 
             
                #
         | 
| 933 | 
            -
                class  | 
| 954 | 
            +
                class Directory
         | 
| 934 955 | 
             
                  include Enumerable
         | 
| 935 956 |  | 
| 936 | 
            -
                  def initialize( | 
| 937 | 
            -
                    @io =  | 
| 957 | 
            +
                  def initialize(fp)
         | 
| 958 | 
            +
                    @io = fp
         | 
| 938 959 | 
             
                    # directory entry buffer
         | 
| 939 960 | 
             
                    @read_direntries = []
         | 
| 961 | 
            +
                    # byte buffer
         | 
| 962 | 
            +
                    @data = ""
         | 
| 940 963 | 
             
                  end
         | 
| 941 964 |  | 
| 942 965 | 
             
                  def close
         | 
| 943 966 | 
             
                    @io.close
         | 
| 944 967 | 
             
                  end
         | 
| 945 968 |  | 
| 946 | 
            -
                  ##
         | 
| 947 | 
            -
                  # get the fid corresponding to this directory
         | 
| 948 | 
            -
                  #
         | 
| 949 969 | 
             
                  def fid
         | 
| 950 970 | 
             
                    @io.fid
         | 
| 951 971 | 
             
                  end
         | 
| 952 972 |  | 
| 973 | 
            +
                  def qid
         | 
| 974 | 
            +
                    @io.qid
         | 
| 975 | 
            +
                  end
         | 
| 976 | 
            +
             | 
| 953 977 | 
             
                  ##
         | 
| 954 978 | 
             
                  # Read the next directory entry from the dir and return the file
         | 
| 955 979 | 
             
                  # name as a string.  Returns nil at the end of stream.
         | 
| @@ -961,18 +985,25 @@ module RStyx | |
| 961 985 | 
             
                      return(@read_direntries.shift.name)
         | 
| 962 986 | 
             
                    end
         | 
| 963 987 | 
             
                    # read iounit bytes from the directory--this must be unbuffered
         | 
| 964 | 
            -
                     | 
| 988 | 
            +
                    d = @io.sysread
         | 
| 989 | 
            +
                    if d.nil?
         | 
| 990 | 
            +
                      return(nil)
         | 
| 991 | 
            +
                    end
         | 
| 992 | 
            +
                    @data << d
         | 
| 965 993 |  | 
| 966 | 
            -
                    if (data. | 
| 994 | 
            +
                    if (@data.empty?)
         | 
| 967 995 | 
             
                      return(nil)
         | 
| 968 996 | 
             
                    end
         | 
| 969 997 |  | 
| 970 998 | 
             
                    # decode the directory entries in the iounit
         | 
| 971 | 
            -
                     | 
| 972 | 
            -
                      delen = data | 
| 973 | 
            -
                       | 
| 974 | 
            -
             | 
| 975 | 
            -
                       | 
| 999 | 
            +
                    loop do
         | 
| 1000 | 
            +
                      delen = @data.unpack("v")[0]
         | 
| 1001 | 
            +
                      if delen.nil? || delen + 1 > @data.length
         | 
| 1002 | 
            +
                        break
         | 
| 1003 | 
            +
                      end
         | 
| 1004 | 
            +
                      edirent = @data[0..(delen + 1)]
         | 
| 1005 | 
            +
                      @data = @data[(delen + 2)..-1]
         | 
| 1006 | 
            +
                      @read_direntries << Message::Stat.from_bytes(edirent)
         | 
| 976 1007 | 
             
                    end
         | 
| 977 1008 | 
             
                    de = @read_direntries.shift
         | 
| 978 1009 | 
             
                    if (de.nil?)
         | 
| @@ -982,36 +1013,21 @@ module RStyx | |
| 982 1013 | 
             
                  end
         | 
| 983 1014 |  | 
| 984 1015 | 
             
                  ##
         | 
| 985 | 
            -
                  #  | 
| 986 | 
            -
                  #  | 
| 987 | 
            -
                  #
         | 
| 988 | 
            -
                  def rewind
         | 
| 989 | 
            -
                    @io.rewind
         | 
| 990 | 
            -
                  end
         | 
| 991 | 
            -
             | 
| 992 | 
            -
                  ##
         | 
| 993 | 
            -
                  # same as StyxIO#stat
         | 
| 994 | 
            -
                  #
         | 
| 995 | 
            -
                  def stat
         | 
| 996 | 
            -
                    return(@io.stat)
         | 
| 997 | 
            -
                  end
         | 
| 998 | 
            -
             | 
| 999 | 
            -
                  ##
         | 
| 1000 | 
            -
                  # Call the block once for each entry in the directory, passing the
         | 
| 1001 | 
            -
                  # filename of each entry as a parameter to the block.
         | 
| 1016 | 
            +
                  # Call the block once for each entry in the directory, passing
         | 
| 1017 | 
            +
                  # the filename of each entry as a parameter to the block.
         | 
| 1002 1018 | 
             
                  #
         | 
| 1003 1019 | 
             
                  def each
         | 
| 1004 1020 | 
             
                    if !block_given?
         | 
| 1005 | 
            -
                      raise LocalJumpError("no block given")
         | 
| 1021 | 
            +
                      raise LocalJumpError.new("no block given")
         | 
| 1006 1022 | 
             
                    end
         | 
| 1007 1023 |  | 
| 1008 1024 | 
             
                    self.rewind
         | 
| 1009 | 
            -
                     | 
| 1025 | 
            +
                    until (de = read).nil?
         | 
| 1010 1026 | 
             
                      yield de
         | 
| 1011 1027 | 
             
                    end
         | 
| 1012 1028 | 
             
                  end
         | 
| 1013 1029 |  | 
| 1014 | 
            -
                end
         | 
| 1030 | 
            +
                end                         # class Directory
         | 
| 1015 1031 |  | 
| 1016 1032 | 
             
              end                           # module Client
         | 
| 1017 1033 |  |