colloquy-1.35.30/
colloquy-1.35.30/data/lang/
colloquy-1.35.30/data/misc/
colloquy-1.35.30/docs/
-- non-blocking sockets object
-- requires LuaSocket, stringStack.

assert(stringStack, "stringstack is needed.");
assert(select, "LuaSocket is needed.");

-- Usage:
-- To create a client socket from a server socket with a connection waiting, do
--   myClient = clientSocket:accept(myServer);
--
-- To poke some data into the send buffer for a client socket...
--   myClient:send(string);
--
-- To see if there is some data waiting for you...
--   s = myClient:readyRead(pattern);
-- where pattern is a strfind pattern to decide if a whole interesting thing has
-- arrived.  Returns it as a string if it is, returns an empty string when there
-- is nothing waiting, and nil if the socket has been closed.
--
-- To send data to a socket (when read from select())...
--   n = myClient:readySend();
-- n is non-nil if the socket has been closed.

clientSocket = {
   
   recBuff = {},     -- contains a stringStack with received chunks
                     -- of data in it.  When you use the readyRead()
                     -- method, and the pattern passed to it matches,
                     -- it concatanates the string, and returns it.

   sendBuff = {},    -- contains a list of strings that need to be
                     -- sent to the client.  This is used to send
                     -- data when the readySend() method is called.

   socket = nil,     -- LuaSocket's handle for this connection.

   writers = {},     -- contains luasocket handles that have data
                     -- waiting to be written.

   toClose = nil,    -- this socket needs to be closed once all data has
                     -- been sent.

   accept = function(self, server)
       -- This function is called in the form clientSocket:accept(s),
       -- where 's' is the server socket which has just recieved a
       -- connection.  It returns a table to represent that socket,
       -- which has all of the other functions in it.
       
       local r;
       r = {
          recBuff = stringStack:new(),
          sendBuff = {},
          socket = accept(server),
          send = self.send,
          readySend = self.readySend,
          readyPeek = self.readyPeek,
          readyRead = self.readyRead,
          close = self.close,
          echo = nil,
       };

       if (r.socket) then
         r.socket:timeout(0, "b");  -- make socket non-blocking
         return r;
       else
         return nil;
       end;
    end,
   
   new = function(self, ip, port)
    -- This function actually creates a connection to somewhere else...
    
    local r;
    local t = secs;
    r = {
       recBuff = stringStack:new(),
       sendBuff = {},
       socket = nil,
       send = self.send,
       readySend = self.readySend,
       readyPeek = self.readyPeek,
       readyRead = self.readyRead,
       close = self.close,
    };

    while ((secs < t + 3) and r.socket == nil) do
       r.socket = connect(ip, port);
    end;
   
    if (r.socket == nil) then return nil end;

    r.socket:timeout(0, "b");  -- make socket non-blocking
    
    return r;
  end,

   close = function(self)
      -- This function closes a socket, and tidies up.
      if (getn(self.sendBuff) > 0) then
        -- remove ourself from the writers list...
        foreachi(clientSocket.writers, function(i, v)
          if (v == %self.socket) then
            tremove(clientSocket.writers, i);
            return 1;
          end;
        end);
      end;
      self.sendBuff = {};
      self.recBuff = {};
      self.socket:close();
      self.socket = nil;
   end,

   send = function(self, string)
     -- This function adds a string to the send table.
     if (self.toClose and self.toClose > 1) then return nil end;
     if (getn(self.sendBuff) == 0) then
       tinsert(clientSocket.writers, self.socket);
     end;

     tinsert(self.sendBuff, string);
   end,
   
   readySend = function(self)
     -- This function tries to send as much data in the send
     -- table to self.socket before it would block, and the
     -- returns, after updating the send buffer.  It returns
     -- non-nil if the connection has closed.
     local err, sent, string, v;

     while (self.sendBuff.n > 0) do
       string = self.sendBuff[1];
       err, sent = self.socket:send(string);
       if (err == "closed") then
         if (self.toClose == 1) then
           self.toClose = 2;
         end;
         return 1;
       end;

       if (sent < strlen(string)) then
         -- we didn't get to send it all...
         -- update this string to chop what we've sent,
         -- and then return.
         self.sendBuff[1] = strsub(string, sent + 1, -1);
         return nil;
      else
         -- we sent it all!  yay!  remove the entry.
         tremove(self.sendBuff, 1);
      end;
    end;
  
    -- we've sent all waiting data, remove us from the
    -- writers table.
    v = clientSocket.writers;
    for i=1, getn(v) do
      if (v[i] == self.socket) then
        tremove(v, i);
        break;
      end;
    end;

   if (self.toClose == 1) then
     self.toClose = 2;
   end;

   end,

   readyRead = function(self, pattern)
     -- This function reads the available data from the
     -- socket and adds it to the recieved string stack.
     -- If the string read matches 'pattern', the string
     -- is concatenated, returned, and the stringstack
     -- emptied.  If no string is ready, it returns "",
     -- if the connection has been closed, it returns
     -- nil.

    local r, err, s, l;
    repeat
     r, err = self.socket:receive("*a");
     if (err ~= "" and err ~= "timeout") then return nil end;   
     if (not self.toClose) then
       if (self.echo ~= nil) then
         self:send(r);
         dataSent = dataSent + (strlen(r));
       end;

       self.recBuff:add(r);

       -- Check what has just been read, and the last read too, since a peek
       -- may have appeneded our pattern to the stack
       if (self.recBuff.stack.n > 1 and r ~= nil) then
         r = self.recBuff.stack[self.recBuff.stack.n-1] .. r;
       end;

       if (strfind(r, pattern, 1) ~= nil) then
         s = self.recBuff:create();
         self.recBuff = stringStack:new();
         if (debugFile) then
           write(debugFile, "read data from ", tostring(self.socket), ": ", (r or "(no data)"), "\n")
           flush(debugFile)
         end
         return s or "";
       end;
     end;
  until (err == "timeout");

  return "";
  end,

   readyPeek = function(self, pattern)
     -- This function reads the available data from the
     -- socket and adds it to the recieved string stack.
     -- If the string read matches 'pattern', the string
     -- is concatenated, returned, and the stringstack
     -- emptied.  If no string is ready, it returns "",
     -- if the connection has been closed, it returns
     -- nil.

    local r, err, s, l;
    repeat
     r, err = self.socket:receive("*a");
     if (err ~= "" and err ~= "timeout") then return nil end;   
     if (not self.toClose) then

       self.recBuff:add(r);
       if (strfind(self.recBuff:create(), pattern, 1) ~= nil) then
         s = self.recBuff:create();
         return s;
       end;
     end;
  until (err == "timeout");

  return "";
  end,
};