termtter 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/README.rdoc +2 -2
- data/Rakefile +41 -75
- data/VERSION +1 -0
- data/bin/termtter +9 -2
- data/doc/Termtter-1.0-Release-Note-English.txt +37 -0
- data/doc/Termtter-1.0-Release-Note.txt +37 -0
- data/lib/plugins/another_prompt.rb +8 -8
- data/lib/plugins/ar.rb +102 -0
- data/lib/plugins/async.rb +1 -1
- data/lib/plugins/babelfish.rb +34 -0
- data/lib/plugins/confirm.rb +2 -0
- data/lib/plugins/crypt.rb +44 -0
- data/lib/plugins/defaults/auto_reload.rb +1 -1
- data/lib/plugins/defaults/command_line.rb +34 -17
- data/lib/plugins/defaults/confirm.rb +30 -0
- data/lib/plugins/defaults/hashtag.rb +1 -1
- data/lib/plugins/defaults/irb.rb +30 -0
- data/lib/plugins/defaults/keyword.rb +58 -0
- data/lib/plugins/defaults/list.rb +155 -0
- data/lib/plugins/defaults/plugin.rb +59 -0
- data/lib/plugins/defaults/retweet.rb +75 -23
- data/lib/plugins/defaults/standard_commands.rb +60 -87
- data/lib/plugins/defaults/standard_completion.rb +25 -15
- data/lib/plugins/defaults/stdout.rb +49 -10
- data/lib/plugins/defaults/switch.rb +1 -1
- data/lib/plugins/defaults/users.rb +63 -0
- data/lib/plugins/draft.rb +58 -0
- data/lib/plugins/expand-tinyurl.rb +5 -9
- data/lib/plugins/favotter.rb +1 -1
- data/lib/plugins/footer.rb +22 -0
- data/lib/plugins/friends.rb +5 -4
- data/lib/plugins/g.rb +9 -16
- data/lib/plugins/gem_install.rb +24 -0
- data/lib/plugins/gist.rb +20 -0
- data/lib/plugins/grass.rb +1 -1
- data/lib/plugins/gyazo.rb +78 -0
- data/lib/plugins/http_server.rb +1 -1
- data/lib/plugins/hugeurl.rb +6 -13
- data/lib/plugins/irc_gw.rb +15 -11
- data/lib/plugins/me.rb +1 -1
- data/lib/plugins/notify-send.rb +1 -1
- data/lib/plugins/notify-send3.rb +1 -1
- data/lib/plugins/open.rb +1 -1
- data/lib/plugins/open_url.rb +5 -1
- data/lib/plugins/pool.rb +1 -1
- data/lib/plugins/random.rb +1 -1
- data/lib/plugins/reply_retweet.rb +42 -0
- data/lib/plugins/screen-notify.rb +1 -1
- data/lib/plugins/sl.rb +3 -3
- data/lib/plugins/storage.rb +7 -10
- data/lib/plugins/storage/sqlite3.rb +155 -0
- data/lib/plugins/storage/status.rb +2 -0
- data/lib/plugins/stream.rb +1 -1
- data/lib/plugins/tinyurl.rb +3 -9
- data/lib/plugins/trends.rb +2 -2
- data/lib/plugins/truncate.rb +1 -1
- data/lib/plugins/w3mimg.rb +1 -1
- data/lib/termtter.rb +19 -20
- data/lib/termtter/active_rubytter.rb +4 -0
- data/lib/termtter/api.rb +22 -5
- data/lib/termtter/client.rb +55 -40
- data/lib/termtter/command.rb +3 -2
- data/lib/termtter/config_setup.rb +1 -1
- data/lib/termtter/config_template.erb +5 -0
- data/lib/termtter/default_config.rb +18 -0
- data/lib/termtter/hookable.rb +1 -0
- data/lib/termtter/httppool.rb +44 -0
- data/lib/termtter/memory_cache.rb +32 -0
- data/lib/termtter/optparse.rb +8 -15
- data/lib/termtter/rubytter_proxy.rb +65 -4
- data/lib/termtter/system_extensions.rb +40 -9
- data/lib/termtter/task.rb +2 -1
- data/spec/plugins/defaults/hashtag_spec.rb +8 -7
- data/spec/plugins/defaults/list_spec.rb +33 -0
- data/spec/plugins/defaults/plugin_spec.rb +17 -0
- data/spec/plugins/defaults/retweet_spec.rb +205 -0
- data/spec/plugins/draft_spec.rb +59 -0
- data/spec/plugins/expand-tinyurl_spec.rb +21 -0
- data/spec/plugins/footer_spec.rb +50 -0
- data/spec/plugins/storage/sqlite3_spec.rb +41 -0
- data/spec/termtter/api_spec.rb +1 -1
- data/spec/termtter/client_spec.rb +21 -21
- data/spec/termtter/command_spec.rb +8 -8
- data/spec/termtter/config_spec.rb +2 -2
- data/spec/termtter/memory_cache_spec.rb +20 -0
- data/spec/termtter/optparse_spec.rb +1 -1
- data/spec/termtter/rubytter_proxy_spec.rb +38 -0
- data/spec/termtter/system_extensions_spec.rb +25 -23
- data/spec/termtter/task_manager_spec.rb +1 -1
- data/spec/termtter_spec.rb +4 -2
- metadata +88 -19
- data/lib/plugins/defaults/lists.rb +0 -14
- data/lib/plugins/irb.rb +0 -6
- data/lib/plugins/storage/DB.rb +0 -37
- data/lib/termtter/version.rb +0 -4
- data/spec/plugins/defaults/lists_spec.rb +0 -34
- data/spec/plugins/storage/DB_spec_.rb +0 -12
data/lib/termtter/api.rb
CHANGED
@@ -1,9 +1,26 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
3
|
config.set_default(:host, 'twitter.com')
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
if ENV.has_key?('HTTP_PROXY')
|
5
|
+
require 'uri'
|
6
|
+
proxy = ENV['HTTP_PROXY']
|
7
|
+
proxy = "http://" + proxy if proxy !~ /^http:\/\//
|
8
|
+
u = URI.parse(proxy)
|
9
|
+
config.proxy.set_default(:host, u.host)
|
10
|
+
config.proxy.set_default(:port, u.port.to_s)
|
11
|
+
|
12
|
+
if u.userinfo.nil?
|
13
|
+
config.proxy.set_default(:host, nil)
|
14
|
+
config.proxy.set_default(:port, nil)
|
15
|
+
else
|
16
|
+
user_name,password = u.userinfo.split(/:/)
|
17
|
+
config.proxy.set_default(:user_name, user_name)
|
18
|
+
config.proxy.set_default(:password, password)
|
19
|
+
end
|
20
|
+
else
|
21
|
+
config.proxy.set_default(:host, nil)
|
22
|
+
config.proxy.set_default(:port, nil)
|
23
|
+
end
|
7
24
|
config.proxy.set_default(:user_name, nil)
|
8
25
|
config.proxy.set_default(:password, nil)
|
9
26
|
config.set_default(:enable_ssl, false)
|
@@ -13,7 +30,6 @@ module Termtter
|
|
13
30
|
class << self
|
14
31
|
attr_reader :connection, :twitter
|
15
32
|
def setup
|
16
|
-
|
17
33
|
3.times do
|
18
34
|
begin
|
19
35
|
if twitter = try_auth
|
@@ -40,6 +56,8 @@ module Termtter
|
|
40
56
|
|
41
57
|
if config.user_name.empty?
|
42
58
|
config.user_name = ui.ask('Username: ')
|
59
|
+
else
|
60
|
+
puts "Username: #{config.user_name}"
|
43
61
|
end
|
44
62
|
if config.password.empty?
|
45
63
|
config.password = ui.ask('Password: ') { |q| q.echo = false}
|
@@ -50,7 +68,6 @@ module Termtter
|
|
50
68
|
twitter.verify_credentials
|
51
69
|
return twitter
|
52
70
|
rescue Rubytter::APIError
|
53
|
-
config.__clear__(:user_name)
|
54
71
|
config.__clear__(:password)
|
55
72
|
end
|
56
73
|
return nil
|
data/lib/termtter/client.rb
CHANGED
@@ -15,12 +15,6 @@ module Termtter
|
|
15
15
|
@filters = []
|
16
16
|
@since_id = nil
|
17
17
|
|
18
|
-
config.set_default(:logger, nil)
|
19
|
-
config.set_default(:update_interval, 120)
|
20
|
-
config.set_default(:prompt, '> ')
|
21
|
-
config.set_default(:devel, false)
|
22
|
-
config.set_default(:timeout, 5)
|
23
|
-
|
24
18
|
Thread.abort_on_exception = true
|
25
19
|
|
26
20
|
class << self
|
@@ -33,6 +27,11 @@ module Termtter
|
|
33
27
|
name.each {|i| plug(i, options) }
|
34
28
|
return
|
35
29
|
end
|
30
|
+
|
31
|
+
name = name.to_s
|
32
|
+
|
33
|
+
return if config.system.disable_plugins.include?(name.gsub('defaults/', ''))
|
34
|
+
|
36
35
|
options.each do |key, value|
|
37
36
|
config.plugins.__refer__(name.gsub(/-/, '_').to_sym).__assign__(key.to_sym, value)
|
38
37
|
end
|
@@ -93,7 +92,7 @@ module Termtter
|
|
93
92
|
def register_macro(name, macro, options = {})
|
94
93
|
command = {
|
95
94
|
:name => name.to_sym,
|
96
|
-
:exec_proc => lambda {|arg|
|
95
|
+
:exec_proc => lambda {|arg| execute(macro % arg)}
|
97
96
|
}.merge(options)
|
98
97
|
register_command(command)
|
99
98
|
end
|
@@ -137,11 +136,8 @@ module Termtter
|
|
137
136
|
}
|
138
137
|
end
|
139
138
|
|
140
|
-
def
|
141
|
-
|
142
|
-
# 0: done
|
143
|
-
# 1: canceled
|
144
|
-
status = 0
|
139
|
+
def execute(text)
|
140
|
+
text = text.strip
|
145
141
|
|
146
142
|
@task_manager.invoke_and_wait do
|
147
143
|
# FIXME: This block can become Maybe Monad
|
@@ -151,29 +147,30 @@ module Termtter
|
|
151
147
|
}
|
152
148
|
return if text.empty?
|
153
149
|
|
154
|
-
|
155
|
-
raise CommandNotFound, text
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
150
|
+
command = find_command(text)
|
151
|
+
raise CommandNotFound, text unless command
|
152
|
+
|
153
|
+
command_str, modified_arg = command.split_command_line(text)
|
154
|
+
command_str.strip!
|
155
|
+
modified_arg ||= ''
|
156
|
+
|
157
|
+
# FIXME: This block can become Maybe Monad
|
158
|
+
get_hooks("modify_arg_for_#{command.name.to_s}").each {|hook|
|
159
|
+
break if modified_arg == false # interrupt if hook return false
|
160
|
+
modified_arg.strip!
|
161
|
+
modified_arg = hook.call(command_str, modified_arg) || ''
|
162
|
+
}
|
163
|
+
modified_arg.strip!
|
164
|
+
|
165
|
+
begin
|
166
|
+
call_hooks("pre_exec_#{command.name.to_s}", command, modified_arg)
|
167
|
+
result = command.call(command_str, modified_arg, text) # exec command
|
168
|
+
call_hooks("post_exec_#{command.name.to_s}", command_str, modified_arg, result)
|
169
|
+
call_hooks("post_command", text)
|
170
|
+
rescue CommandCanceled
|
171
|
+
return false
|
174
172
|
end
|
175
|
-
|
176
|
-
status
|
173
|
+
return true
|
177
174
|
end
|
178
175
|
rescue TimeoutError
|
179
176
|
call_hooks("timeout", text)
|
@@ -184,8 +181,12 @@ module Termtter
|
|
184
181
|
@commands.values.any? {|command| command.match?(text) }
|
185
182
|
end
|
186
183
|
|
187
|
-
def
|
188
|
-
@commands.
|
184
|
+
def find_command(text)
|
185
|
+
@commands.
|
186
|
+
values.
|
187
|
+
select {|command| command.match?(text) }.
|
188
|
+
sort_by {|command| command.name.to_s.split(' ').size }.
|
189
|
+
last
|
189
190
|
end
|
190
191
|
|
191
192
|
def pause
|
@@ -235,11 +236,13 @@ module Termtter
|
|
235
236
|
end
|
236
237
|
|
237
238
|
def logger
|
238
|
-
@logger
|
239
|
+
@logger || setup_logger
|
239
240
|
end
|
240
241
|
|
241
242
|
def setup_logger
|
242
243
|
@logger = config.logger || default_logger
|
244
|
+
@logger.level = config.devel ? Logger::DEBUG : Logger::INFO
|
245
|
+
@logger
|
243
246
|
end
|
244
247
|
|
245
248
|
def default_logger
|
@@ -282,9 +285,18 @@ module Termtter
|
|
282
285
|
@task_manager = Termtter::TaskManager.new(1)
|
283
286
|
end
|
284
287
|
|
288
|
+
def parse_options
|
289
|
+
Termtter::OptParser.parse!(ARGV)
|
290
|
+
end
|
291
|
+
|
292
|
+
def show_splash
|
293
|
+
puts TermColor.parse(config.splash)
|
294
|
+
end
|
295
|
+
|
285
296
|
def run
|
297
|
+
show_splash
|
298
|
+
parse_options()
|
286
299
|
load_config()
|
287
|
-
setup_logger()
|
288
300
|
setup_task_manager()
|
289
301
|
load_plugins()
|
290
302
|
eval_init_block()
|
@@ -298,12 +310,15 @@ module Termtter
|
|
298
310
|
end
|
299
311
|
end
|
300
312
|
|
301
|
-
config.system.run_commands.each {|cmd|
|
313
|
+
config.system.run_commands.each {|cmd| execute(cmd) }
|
302
314
|
|
303
315
|
unless config.system.cmd_mode
|
304
316
|
@task_manager.run()
|
305
317
|
call_hooks(:initialize)
|
306
|
-
|
318
|
+
add_task(:name => :call_hooks_after_launched, :after => 1) do
|
319
|
+
call_hooks(:launched)
|
320
|
+
end
|
321
|
+
call_hooks(:init_command_line)
|
307
322
|
end
|
308
323
|
end
|
309
324
|
|
data/lib/termtter/command.rb
CHANGED
@@ -37,6 +37,7 @@ module Termtter
|
|
37
37
|
|
38
38
|
# complement :: String -> [String]
|
39
39
|
def complement(input)
|
40
|
+
input = input.sub(/^\s*/, '')
|
40
41
|
if match?(input) && input =~ /^[^\s]+\s/
|
41
42
|
if completion_proc
|
42
43
|
command_str, command_arg = split_command_line(input)
|
@@ -45,7 +46,7 @@ module Termtter
|
|
45
46
|
[]
|
46
47
|
end
|
47
48
|
else
|
48
|
-
[
|
49
|
+
[]
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
@@ -95,7 +96,7 @@ module Termtter
|
|
95
96
|
def split_command_line(line)
|
96
97
|
command_words_count = command_words.size
|
97
98
|
parts = line.strip.split(/\s+/, command_words_count + 1)
|
98
|
-
[parts[0...command_words_count].join(' '), parts[command_words_count]]
|
99
|
+
[parts[0...command_words_count].join(' '), parts[command_words_count] || '']
|
99
100
|
end
|
100
101
|
end
|
101
102
|
end
|
@@ -12,7 +12,7 @@ module Termtter
|
|
12
12
|
plugins = Dir.glob(File.expand_path(File.dirname(__FILE__) + "/../plugins/*.rb")).map {|f|
|
13
13
|
f.match(%r|lib/plugins/(.*?).rb$|)[1]
|
14
14
|
}
|
15
|
-
standard_plugins = %w[stdout standard_commands auto_reload]
|
15
|
+
standard_plugins = %w[stdout standard_commands auto_reload defaults]
|
16
16
|
|
17
17
|
template = open(File.dirname(__FILE__) + '/config_template.erb').read
|
18
18
|
config = ERB.new(template, nil, '-').result(binding) # trim_mode => '-'
|
@@ -2,12 +2,17 @@
|
|
2
2
|
|
3
3
|
config.user_name = '<%= user_name %>'
|
4
4
|
#config.update_interval = 120
|
5
|
+
#config.timeout = 5
|
6
|
+
#config.retry = 3
|
5
7
|
#config.enable_ssl = true
|
6
8
|
#config.proxy.host = 'proxy host'
|
7
9
|
#config.proxy.port = '8080'
|
8
10
|
#config.proxy.user_name = 'proxy user'
|
9
11
|
#config.proxy.password = 'proxy password'
|
10
12
|
|
13
|
+
#config.plugins.keyword.keywords = ["ruby", "termtter"]
|
14
|
+
#config.plugins.stdout.colors = (31..36).to_a + (91..96).to_a
|
15
|
+
|
11
16
|
Termtter::Client.init do |t|
|
12
17
|
<%- (plugins - standard_plugins).each do |plugin| -%>
|
13
18
|
# t.plug '<%= plugin %>'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
config.set_default(:logger, nil)
|
2
|
+
config.set_default(:update_interval, 120)
|
3
|
+
config.set_default(:prompt, '> ')
|
4
|
+
config.set_default(:devel, false)
|
5
|
+
config.set_default(:timeout, 5)
|
6
|
+
config.set_default(:retry, 1)
|
7
|
+
config.set_default(:splash, <<SPLASH)
|
8
|
+
|
9
|
+
<cyan><(@)//_</cyan> . . <on_green> Termtter <underline>#{Termtter::VERSION}</underline> </on_green>
|
10
|
+
<cyan>\\\\</cyan> <on_green> http://termtter.org/ </on_green>
|
11
|
+
|
12
|
+
SPLASH
|
13
|
+
|
14
|
+
config.system.set_default :cmd_mode, false
|
15
|
+
config.system.set_default :run_commands, []
|
16
|
+
config.system.set_default :load_plugins, []
|
17
|
+
config.system.set_default :disable_plugins, []
|
18
|
+
config.system.set_default :eval_scripts, []
|
data/lib/termtter/hookable.rb
CHANGED
@@ -41,6 +41,7 @@ module Termtter
|
|
41
41
|
|
42
42
|
# return last hook return value
|
43
43
|
def call_hooks(point, *args)
|
44
|
+
Termtter::Client.logger.debug "call_hooks: [:point => #{point}, :args => [#{args.map {|a| a.inspect.split(//)[0..10].join}.join(', ')}]]"
|
44
45
|
result = nil
|
45
46
|
get_hooks(point).each {|hook|
|
46
47
|
break if result == false # interrupt if hook return false
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Termtter
|
5
|
+
module HTTPpool
|
6
|
+
@@connections = {}
|
7
|
+
|
8
|
+
def self.start(host, port = 80)
|
9
|
+
begin
|
10
|
+
yield(connection(host, port))
|
11
|
+
rescue EOFError
|
12
|
+
finish(host, port)
|
13
|
+
retry
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.connection(host, port = 80)
|
18
|
+
key = connection_key(host, port)
|
19
|
+
@@connections[key] ||= http_class.start(host, port)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.finish(host, port = 80)
|
23
|
+
key = connection_key(host, port)
|
24
|
+
@@connections[key] && @@connections[key].do_finish rescue nil
|
25
|
+
@@connections.delete(key)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.connection_key(host, port)
|
29
|
+
port == 80 ? host : [host, port.to_s].join(':')
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.http_class
|
33
|
+
if config.proxy.host.nil? or config.proxy.host.empty?
|
34
|
+
Net::HTTP
|
35
|
+
else
|
36
|
+
Net::HTTP::Proxy(config.proxy.host,
|
37
|
+
config.proxy.port,
|
38
|
+
config.proxy.user_name,
|
39
|
+
config.proxy.password)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Termtter
|
4
|
+
class MemoryCache < SimpleDelegator
|
5
|
+
attr_reader :limit
|
6
|
+
|
7
|
+
def initialize(limit = 10000)
|
8
|
+
super(Hash.new)
|
9
|
+
@keys = []
|
10
|
+
@limit = limit
|
11
|
+
end
|
12
|
+
|
13
|
+
def adjust(key)
|
14
|
+
unless @keys.include?(key)
|
15
|
+
@keys << key
|
16
|
+
while @keys.size > limit
|
17
|
+
delete(@keys.shift)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def []=(key, value)
|
23
|
+
super
|
24
|
+
adjust(key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def store(key, value)
|
28
|
+
super
|
29
|
+
adjust(key)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/termtter/optparse.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module Termtter
|
3
2
|
module OptParser
|
4
3
|
class << self
|
@@ -10,39 +9,30 @@ module Termtter
|
|
10
9
|
@optionparser = OptionParser.new { |opt|
|
11
10
|
opt.program_name = 'Termtter'
|
12
11
|
|
13
|
-
opt.on('-f', '--config-file file', 'Set path to configfile') do |val|
|
14
|
-
config.system.__assign__(:conf_file, val)
|
15
|
-
end
|
16
|
-
|
17
|
-
opt.on('-t', '--termtter-directory directory', 'Set termtter directory') do |val|
|
18
|
-
config.system.__assign__(:conf_dir, val)
|
19
|
-
end
|
20
|
-
|
21
12
|
opt.on('-d', '--devel', 'Start in developer mode') do |flg|
|
22
13
|
config.__assign__(:devel, true) if flg
|
23
14
|
end
|
24
15
|
|
25
|
-
config.system.cmd_mode = false
|
26
16
|
opt.on('-c', '--command-mode', 'Run as command mode') do |flg|
|
27
17
|
config.system.cmd_mode = flg
|
28
18
|
end
|
29
19
|
|
30
|
-
config.system.run_commands = []
|
31
20
|
opt.on('-r', '--run-command command', 'Run command') do |cmd|
|
32
21
|
config.system.run_commands << cmd
|
33
22
|
end
|
34
23
|
|
35
|
-
config.system.load_plugins = []
|
36
24
|
opt.on('-p', '--plugin plugin', 'Load plugin') do |plugin|
|
37
25
|
config.system.load_plugins << plugin
|
38
26
|
end
|
39
27
|
|
40
|
-
|
28
|
+
opt.on('-n', '--disable-plugin plugin', 'Disable plugin') do |plugin|
|
29
|
+
config.system.disable_plugins << plugin
|
30
|
+
end
|
31
|
+
|
41
32
|
opt.on('-e', '--eval-script script', 'Eval script') do |script|
|
42
33
|
config.system.eval_scripts << script
|
43
34
|
end
|
44
35
|
|
45
|
-
config.system.eval_scripts = []
|
46
36
|
opt.on('-m', '--monochrome', 'No shell escapes for color highlightings') do |script|
|
47
37
|
require 'termcolor'
|
48
38
|
module ::TermColor
|
@@ -55,9 +45,12 @@ module Termtter
|
|
55
45
|
end
|
56
46
|
end
|
57
47
|
|
48
|
+
opt.on('-u', '--user user', 'Login username') do |val|
|
49
|
+
config.user_name = val
|
50
|
+
end
|
51
|
+
|
58
52
|
opt.version = Termtter::VERSION
|
59
53
|
}
|
60
|
-
|
61
54
|
end
|
62
55
|
end
|
63
56
|
|