Fixed.
[riece] / lisp / rubyserv.rb
index d3a82ff..cc29694 100644 (file)
@@ -1,19 +1,22 @@
-# A simple IPC server for executing arbitrary Ruby program.
-# The protocol is based on Assuan protocol of GnuPG.
+# A simple IPC server executing Ruby programs.
 
 require 'thread'
-require 'stringio'
 
 class RubyServ
+  module C
+  end
+
   def initialize
-    @buf = StringIO.new
+    @buf = ''
     @que = Queue.new
+    @thr = Hash.new
+    @cnt = 0
   end
 
   def dispatch(line)
     case line.chomp
     when /\AD /
-      return @buf << unescape($')
+      @buf << $'
     when /\A(\S+)\s*/
       c = $1
       r = $'
@@ -58,22 +61,55 @@ class RubyServ
 
   def dispatch_eval(c, r)
     r = deq_data if r.empty?
-    p r
-    open('|-') do |f|
-      if f
-        d = f.read
-        Process.wait
-        send_data(d)
-        if $?.success?
-          puts("OK\r\n")
-        else
-          puts("ERR #{$?.exitstatus}\r\n")
-        end
+    name = nil
+    Thread.exclusive do
+      while @thr.include?(name = @cnt.to_s)
+       @cnt += 1
+      end
+      @thr[name] = Thread.current
+    end
+    puts("S name #{name}\r\n")
+    puts("OK\r\n")
+    Thread.current[:rubyserv_name] = name
+    begin
+      Thread.current[:rubyserv_error] = false
+      Thread.current[:rubyserv_response] = eval(r, C.module_eval('binding()'))
+    rescue Exception => e
+      Thread.current[:rubyserv_error] = true
+      Thread.current[:rubyserv_response] = e
+    end
+    puts("# exit #{name}\r\n")
+  end
+
+  def dispatch_poll(c, r)
+    thr = @thr[r]
+    if !thr
+      puts("ERR 105 Parameter error: no such name \"#{r}\"\r\n")
+    elsif thr.alive?
+      puts("S running #{r}\r\n")
+      puts("OK\r\n")
+    else
+      if thr[:rubyserv_error]
+        puts("S exited #{r}\r\n")
       else
-        eval(r)
-        exit
+        puts("S finished #{r}\r\n")
       end
+      if d = thr[:rubyserv_response]
+        send_data(d.to_s)
+      end
+      puts("OK\r\n")
+    end
+  end
+
+  def dispatch_exit(c, r)
+    thr = @thr[r]
+    if !thr
+      puts("ERR 105 Parameter error: no such name \"#{r}\"\r\n")
+      return
     end
+    thr.kill if thr.alive?
+    @thr.delete(r)
+    puts("OK\r\n")
   end
 
   def escape(s)
@@ -84,19 +120,23 @@ class RubyServ
     s.gsub(/%([0-9A-Z][0-9A-Z])/, ['\1'].pack('H*'))
   end
 
+  def output(s)
+    puts("# output #{Thread.current[:rubyserv_name]} #{s}\r\n")
+  end
+
   def send_data(d)
+    d = escape(d)
     begin
-      r = [d.length, 998].min   # 998 = 1000 - CRLF
-      (0 ... r).each do |i|
-        r -= 2 if d[i] =~ /[%\r\n]/
-      end
-      puts("D #{escape(d[0 ... r])}\r\n")
-      d = d[r .. -1]
+      len = [d.length, 998].min   # 998 = 1000 - "D "
+      puts("D #{d[0 ... len]}\r\n")
+      d = d[len .. -1]
     end until d.empty?
   end
 
   def enq_data
-    @que.enq(@buf.string)
+    d = unescape(@buf)
+    @buf = ''
+    @que.enq(d)
   end
 
   def deq_data