* rubyserv.rb: New file.
[riece] / lisp / rubyserv.rb
1 # A simple IPC server for executing arbitrary Ruby program.
2 # The protocol is based on Assuan protocol of GnuPG.
3
4 require 'thread'
5 require 'stringio'
6
7 class RubyServ
8   def initialize
9     @buf = StringIO.new
10     @que = Queue.new
11   end
12
13   def dispatch(line)
14     case line.chomp
15     when /\AD /
16       return @buf << unescape($')
17     when /\A(\S+)\s*/
18       c = $1
19       r = $'
20       d = "dispatch_#{c.downcase}"
21       if respond_to?(d, true)
22         Thread.start do
23           self.send(d, c, r)
24         end
25       else
26         puts("ERR 103 Unknown command\r\n")
27       end
28     end
29   end
30
31   def dispatch_cancel(c, r)
32     puts("ERR 100 Not implemented\r\n")
33   end
34
35   def dispatch_bye(c, r)
36     puts("ERR 100 Not implemented\r\n")
37   end
38
39   def dispatch_auth(c, r)
40     puts("ERR 100 Not implemented\r\n")
41   end
42
43   def dispatch_reset(c, r)
44     puts("ERR 100 Not implemented\r\n")
45   end
46
47   def dispatch_end(c, r)
48     enq_data
49   end
50
51   def dispatch_help(c, r)
52     puts("ERR 100 Not implemented\r\n")
53   end
54
55   def dispatch_quit(c, r)
56     puts("ERR 100 Not implemented\r\n")
57   end
58
59   def dispatch_eval(c, r)
60     r = deq_data if r.empty?
61     p r
62     open('|-') do |f|
63       if f
64         d = f.read
65         Process.wait
66         send_data(d)
67         if $?.success?
68           puts("OK\r\n")
69         else
70           puts("ERR #{$?.exitstatus}\r\n")
71         end
72       else
73         eval(r)
74         exit
75       end
76     end
77   end
78
79   def escape(s)
80     s.gsub(/[%\r\n]/) {|m| '%%%02X' % m[0]}
81   end
82
83   def unescape(s)
84     s.gsub(/%([0-9A-Z][0-9A-Z])/, ['\1'].pack('H*'))
85   end
86
87   def send_data(d)
88     begin
89       r = [d.length, 998].min   # 998 = 1000 - CRLF
90       (0 ... r).each do |i|
91         r -= 2 if d[i] =~ /[%\r\n]/
92       end
93       puts("D #{escape(d[0 ... r])}\r\n")
94       d = d[r .. -1]
95     end until d.empty?
96   end
97
98   def enq_data
99     @que.enq(@buf.string)
100   end
101
102   def deq_data
103     @que.deq
104   end
105 end
106
107 if $0 == __FILE__
108   serv = RubyServ.new
109   while gets
110     serv.dispatch($_)
111   end
112 end