* rubyserv.rb: Rewrite.
[riece] / lisp / rubyserv.rb
1 # A simple IPC server executing Ruby programs.
2
3 require 'thread'
4 require 'stringio'
5
6 class RubyServ
7   def initialize
8     @buf = StringIO.new
9     @que = Queue.new
10     @thr = Hash.new
11   end
12
13   def dispatch(line)
14     case line.chomp
15     when /\AD /
16       @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     name, code = r.split(/\s+/, 2)
61     if @thr.include?(name) && @thr[name].alive?
62       puts("ERR 105 Parameter error: \"#{name}\" is already in use\r\n")
63       return
64     end
65     code = deq_data unless code
66     puts("OK\r\n")
67     @thr[name] = Thread.current
68     Thread.current[:rubyserv_name] = name
69     begin
70       Thread.current[:rubyserv_error] = false
71       Thread.current[:rubyserv_response] = eval(code)
72     rescue Exception => e
73       Thread.current[:rubyserv_error] = true
74       Thread.current[:rubyserv_response] = e
75     end
76     puts("# exited #{name}\r\n")
77   end
78
79   def dispatch_poll(c, r)
80     thr = @thr[r]
81     if !thr
82       puts("ERR 105 Parameter error: no such name \"#{r}\"\r\n")
83     elsif thr.alive?
84       puts("S running\r\n")
85       puts("OK\r\n")
86     else
87       @thr.delete(r)
88       if thr[:rubyserv_error]
89         puts("S exited\r\n")
90       else
91         puts("S finished\r\n")
92       end
93       if d = thr[:rubyserv_response]
94         send_data(d.to_s)
95       end
96       puts("OK\r\n")
97     end
98   end
99
100   def escape(s)
101     s.gsub(/[%\r\n]/) {|m| '%%%02X' % m[0]}
102   end
103
104   def unescape(s)
105     s.gsub(/%([0-9A-Z][0-9A-Z])/, ['\1'].pack('H*'))
106   end
107
108   def output(s)
109     puts("# output #{Thread.current[:rubyserv_name]} #{s}\r\n")
110   end
111
112   def send_data(d)
113     d = escape(d)
114     begin
115       len = [d.length, 998].min   # 998 = 1000 - "D "
116       puts("D #{d[0 ... len]}\r\n")
117       d = d[len .. -1]
118     end until d.empty?
119   end
120
121   def enq_data
122     @que.enq(@buf.string)
123   end
124
125   def deq_data
126     @que.deq
127   end
128 end
129
130 if $0 == __FILE__
131   serv = RubyServ.new
132   while gets
133     serv.dispatch($_)
134   end
135 end