Fixed typo.
[riece] / lisp / server.rb
index bedca01..5de808d 100644 (file)
@@ -1,12 +1,38 @@
-# A simple IPC server executing Ruby programs.
+# server.rb --- A simple IPC server executing Ruby programs.
+# Copyright (C) 1998-2005 Daiki Ueno
+
+# Author: Daiki Ueno <ueno@unixuser.org>
+# Created: 1998-09-28
+# Keywords: IRC, riece, Ruby
+
+# This file is part of Riece.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU Emacs; see the file COPYING.  If not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
 
 require 'thread'
+require 'stringio'
 
 class Server
-  module B
-  end
+  def initialize(outfile, errfile, logfile)
+    @out = $stdout
+    @err = $stderr
+    $stdout = outfile ? File.new(outfile, 'a') : StringIO.new
+    $stderr = errfile ? File.new(errfile, 'a') : StringIO.new
+    @log = File.new(logfile, 'a') if logfile
 
-  def initialize
     @buf = ''
     @que = Queue.new
     @thr = Hash.new
@@ -14,6 +40,7 @@ class Server
   end
 
   def dispatch(line)
+    @log.puts(line) if @log
     case line.chomp
     when /\AD /
       @buf << $'
@@ -26,25 +53,25 @@ class Server
           self.send(d, c, r)
         end
       else
-        puts("ERR 103 Unknown command\r\n")
+        send_line("ERR 103 Unknown command\r\n")
       end
     end
   end
 
   def dispatch_cancel(c, r)
-    puts("ERR 100 Not implemented\r\n")
+    send_line("ERR 100 Not implemented\r\n")
   end
 
   def dispatch_bye(c, r)
-    puts("ERR 100 Not implemented\r\n")
+    send_line("ERR 100 Not implemented\r\n")
   end
 
   def dispatch_auth(c, r)
-    puts("ERR 100 Not implemented\r\n")
+    send_line("ERR 100 Not implemented\r\n")
   end
 
   def dispatch_reset(c, r)
-    puts("ERR 100 Not implemented\r\n")
+    send_line("ERR 100 Not implemented\r\n")
   end
 
   def dispatch_end(c, r)
@@ -52,11 +79,11 @@ class Server
   end
 
   def dispatch_help(c, r)
-    puts("ERR 100 Not implemented\r\n")
+    send_line("ERR 100 Not implemented\r\n")
   end
 
   def dispatch_quit(c, r)
-    puts("ERR 100 Not implemented\r\n")
+    send_line("ERR 100 Not implemented\r\n")
   end
 
   def dispatch_eval(c, r)
@@ -68,48 +95,49 @@ class Server
       end
       @thr[name] = Thread.current
     end
-    puts("S name #{name}\r\n")
-    puts("OK\r\n")
+    send_line("S name #{name}\r\n")
+    send_line("OK\r\n")
     Thread.current[:rubyserv_name] = name
     begin
       Thread.current[:rubyserv_error] = false
-      Thread.current[:rubyserv_response] = eval(r, B.module_eval('binding()'))
+      Thread.current[:rubyserv_response] = eval(r, exec_env.empty_binding)
     rescue Exception => e
       Thread.current[:rubyserv_error] = true
-      Thread.current[:rubyserv_response] = e
+      Thread.current[:rubyserv_response] =
+        e.to_s.sub(/\A.*?\n#{Regexp.quote(__FILE__)}:\d+: /o, '')
     end
-    puts("# exit #{name}\r\n")
+    send_line("# 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")
+      send_line("ERR 105 Parameter error: no such name \"#{r}\"\r\n")
     elsif thr.alive?
-      puts("S running #{r}\r\n")
-      puts("OK\r\n")
+      send_line("S running #{r}\r\n")
+      send_line("OK\r\n")
     else
       if thr[:rubyserv_error]
-        puts("S exited #{r}\r\n")
+        send_line("S exited #{r}\r\n")
       else
-        puts("S finished #{r}\r\n")
+        send_line("S finished #{r}\r\n")
       end
       if d = thr[:rubyserv_response]
         send_data(d.to_s)
       end
-      puts("OK\r\n")
+      send_line("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")
+      send_line("ERR 105 Parameter error: no such name \"#{r}\"\r\n")
       return
     end
     thr.kill if thr.alive?
     @thr.delete(r)
-    puts("OK\r\n")
+    send_line("OK\r\n")
   end
 
   def escape(s)
@@ -117,18 +145,14 @@ class Server
   end
 
   def unescape(s)
-    s.gsub(/%([0-9A-Z][0-9A-Z])/, ['\1'].pack('H*'))
-  end
-
-  def output(s)
-    puts("# output #{Thread.current[:rubyserv_name]} #{s}\r\n")
+    s.gsub(/%([0-9A-Z][0-9A-Z])/) {[$1].pack('H*')}
   end
 
   def send_data(d)
     d = escape(d)
     begin
       len = [d.length, 998].min   # 998 = 1000 - "D "
-      puts("D #{d[0 ... len]}\r\n")
+      send_line("D #{d[0 ... len]}\r\n")
       d = d[len .. -1]
     end until d.empty?
   end
@@ -142,10 +166,60 @@ class Server
   def deq_data
     @que.deq
   end
+
+  def send_line(line)
+    @out.puts(line)
+    @log.puts(line) if @log
+  end
+
+  def exec_env
+    env = Object.new
+    def env.empty_binding
+      binding
+    end
+    out, log = @out, @log
+    env.instance_eval {@out, @log = out, log}
+    def env.send_line(line)
+      @out.puts(line)
+      @log.puts(line) if @log
+    end
+    def env.output(s)
+      send_line("# output #{Thread.current[:rubyserv_name]} #{s}\r\n")
+    end
+    env
+  end
 end
 
 if $0 == __FILE__
-  server = Server.new
+  require 'optparse'
+
+  opt_outfile, opt_errfile, opt_logfile = nil, nil, nil
+  opts = OptionParser.new do |opts|
+    opts.banner = <<"End"
+Usage: #{$0} [OPTIONS]
+End
+    opts.on('-o', '--out OUTFILE', 'Send stdout to OUTFILE.') do |outfile|
+      opt_outfile = outfile
+    end
+    opts.on('-e', '--err ERRFILE', 'Send stderr to ERRFILE.') do |errfile|
+      opt_errfile = errfile
+    end
+    opts.on('-l', '--log LOGFILE', 'Send log output to LOGFILE.') do |logfile|
+      opt_logfile = logfile
+    end
+    opts.on_tail('--help', '-h', 'Show this message.') do
+      $stdout.print(opts.to_s)
+      exit(0)
+    end
+  end
+  begin
+    opts.parse!(ARGV)
+  rescue OptionParser::ParseError
+    $stderr.print(opts.to_s)
+    exit(1)
+  end
+
+  server = Server.new(opt_outfile, opt_errfile, opt_logfile)
   while gets
     server.dispatch($_)
   end