5 $KCODE = "U" if RUBY_VERSION < "1.9" # -KU
11 VERSION_STRING = 'rmate version 1.3 (2011-10-18)'
14 attr_accessor :host, :port, :wait, :force, :verbose
17 @host, @port = 'localhost', 52698
19 @host = ENV['RMATE_HOST'].to_s if ENV.has_key? 'RMATE_HOST'
20 @port = ENV['RMATE_PORT'].to_i if ENV.has_key? 'RMATE_PORT'
22 if @host == 'auto' and (conn = ENV['SSH_CONNECTION'])
23 @host = conn.split(' ').first
34 def read_disk_settings
39 OptionParser.new do |o|
40 o.on( '--host=name', "Connect to host.", "Use 'auto' to detect the host from SSH.", "Defaults to #{@host}.") { |v| @host = v }
41 o.on('-p', '--port=#', Integer, "Port number to use for connection.", "Defaults to #{@port}.") { |v| @port = v }
42 o.on('-w', '--[no-]wait', 'Wait for file to be closed by TextMate.') { |v| @wait = v }
43 o.on('-f', '--force', 'Open even if the file is not writable.') { |v| @force = v }
44 o.on('-v', '--verbose', 'Verbose logging messages.') { |v| @verbose = v }
45 o.on_tail('-h', '--help', 'Show this message.') { puts o; exit }
46 o.on_tail( '--version', 'Show version.') { puts VERSION_STRING; exit }
61 @variables[name] = value
65 @size = File.size(path)
66 @data = File.open(path, "rb") { |io| io.read(@size) }
71 @variables.each_pair do |name, value|
72 value = 'yes' if value === true
73 socket.puts "#{name}: #{value}"
76 socket.puts "data: #{@size}"
83 def handle_save(socket, variables, data)
84 path = variables["token"]
85 $stderr.puts "Saving #{path}" if $settings.verbose
87 FileUtils.cp(path, "#{path}~") if File.exist? path
88 File.open(path, 'wb') { |file| file << data }
89 File.unlink("#{path}~") if File.exist? "#{path}~"
91 # TODO We probably want some way to notify the server app that the save failed
92 $stderr.puts "Save failed! #{$!}" if $settings.verbose
96 def handle_close(socket, variables, data)
97 path = variables["token"]
98 $stderr.puts "Closed #{path}" if $settings.verbose
101 def handle_cmd(socket)
102 cmd = socket.readline.chomp
107 while line = socket.readline.chomp
109 name, value = line.split(': ', 2)
110 variables[name] = value
111 data << socket.read(value.to_i) if name == "data"
113 variables.delete("data")
116 when "save" then handle_save(socket, variables, data)
117 when "close" then handle_close(socket, variables, data)
118 else abort "Received unknown command “#{cmd}”, exiting."
122 def connect_and_handle_cmds(host, port, cmds)
123 socket = TCPSocket.new(host, port)
124 server_info = socket.readline.chomp
125 $stderr.puts "Connect: ‘#{server_info}’" if $settings.verbose
127 cmds.each { |cmd| cmd.send(socket) }
130 handle_cmd(socket) while !socket.eof?
132 $stderr.puts "Done" if $settings.verbose
137 $settings = Settings.new
142 abort "File #{path} is not writable! Use -f/--force to open anyway." unless $settings.force or File.writable? path or not File.exists? path
143 $stderr.puts "File #{path} is not writable. Opening anyway." if not File.writable? path and File.exists? path and $settings.verbose
144 cmd = Command.new("open")
145 cmd['display-name'] = "#{Socket.gethostname}:#{path}"
146 cmd['real-path'] = File.expand_path(path)
147 cmd['data-on-save'] = true
148 cmd['re-activate'] = true
150 cmd.read_file(path) if File.exist? path
151 cmd['data'] = "0" unless File.exist? path
155 unless $settings.wait
157 connect_and_handle_cmds($settings.host, $settings.port, cmds)
161 connect_and_handle_cmds($settings.host, $settings.port, cmds)