Fixed.
[riece] / lisp / rubyserv.rb
1 # A simple IPC server executing Ruby programs.
2
3 require 'thread'
4
5 class RubyServ
6   def initialize
7     @buf = ''
8     @que = Queue.new
9     @thr = Hash.new
10   end
11
12   def dispatch(line)
13     case line.chomp
14     when /\AD /
15       @buf << $'
16     when /\A(\S+)\s*/
17       c = $1
18       r = $'
19       d = "dispatch_#{c.downcase}"
20       if respond_to?(d, true)
21         Thread.start do
22           self.send(d, c, r)
23         end
24       else
25         puts("ERR 103 Unknown command\r\n")
26       end
27     end
28   end
29
30   def dispatch_cancel(c, r)
31     puts("ERR 100 Not implemented\r\n")
32   end
33
34   def dispatch_bye(c, r)
35     puts("ERR 100 Not implemented\r\n")
36   end
37
38   def dispatch_auth(c, r)
39     puts("ERR 100 Not implemented\r\n")
40   end
41
42   def dispatch_reset(c, r)
43     puts("ERR 100 Not implemented\r\n")
44   end
45
46   def dispatch_end(c, r)
47     enq_data
48   end
49
50   def dispatch_help(c, r)
51     puts("ERR 100 Not implemented\r\n")
52   end
53
54   def dispatch_quit(c, r)
55     puts("ERR 100 Not implemented\r\n")
56   end
57
58   def dispatch_eval(c, r)
59     name, code = r.split(/\s+/, 2)
60     if @thr.include?(name) && @thr[name].alive?
61       puts("ERR 105 Parameter error: \"#{name}\" is already in use\r\n")
62       return
63     end
64     code = deq_data unless code
65     p 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("# exit #{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 program running\r\n")
85       puts("OK\r\n")
86     else
87       @thr.delete(r)
88       if thr[:rubyserv_error]
89         puts("S program exited\r\n")
90       else
91         puts("S program 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     d = unescape(@buf)
123     @buf = ''
124     @que.enq(d)
125   end
126
127   def deq_data
128     @que.deq
129   end
130 end
131
132 if $0 == __FILE__
133   serv = RubyServ.new
134   while gets
135     serv.dispatch($_)
136   end
137 end