Auto 4.0.0.alpha.1-x86-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.yardopts +7 -0
- data/Gemfile +19 -0
- data/LICENSE.md +31 -0
- data/README.md +109 -0
- data/Rakefile +41 -0
- data/bin/auto +110 -0
- data/bin/auto-conf +45 -0
- data/conf/example.json +100 -0
- data/conf/example.yml +125 -0
- data/docs/Contributing.md +77 -0
- data/docs/Events.md +103 -0
- data/docs/Todo.md +21 -0
- data/docs/Upgrade.md +16 -0
- data/ext/dsl_base.c +49 -0
- data/ext/libauto/auto.h +20 -0
- data/ext/libauto/extconf.rb +16 -0
- data/ext/libauto/libauto.c +29 -0
- data/ext/libauto/libauto.h +28 -0
- data/ext/libauto/logger.c +177 -0
- data/ext/libauto/logger.h +44 -0
- data/lib/auto.rb +43 -0
- data/lib/auto/api.rb +7 -0
- data/lib/auto/api/events.rb +166 -0
- data/lib/auto/api/object.rb +29 -0
- data/lib/auto/api/plugin.rb +155 -0
- data/lib/auto/api/timers.rb +93 -0
- data/lib/auto/bot.rb +338 -0
- data/lib/auto/config.rb +181 -0
- data/lib/auto/configure.rb +410 -0
- data/lib/auto/configure/shell.rb +154 -0
- data/lib/auto/dsl/base.rb +74 -0
- data/lib/auto/dsl/irc.rb +13 -0
- data/lib/auto/irc.rb +8 -0
- data/lib/auto/irc/common.rb +63 -0
- data/lib/auto/irc/library.rb +89 -0
- data/lib/auto/irc/object/channel.rb +21 -0
- data/lib/auto/irc/object/entity.rb +90 -0
- data/lib/auto/irc/object/message.rb +99 -0
- data/lib/auto/irc/object/user.rb +139 -0
- data/lib/auto/irc/protocol.rb +164 -0
- data/lib/auto/irc/protocol/numerics.rb +60 -0
- data/lib/auto/irc/sasl/diffie_hellman.rb +36 -0
- data/lib/auto/irc/sasl/mech.rb +15 -0
- data/lib/auto/irc/sasl/mech/dh_blowfish.rb +83 -0
- data/lib/auto/irc/sasl/mech/plain.rb +39 -0
- data/lib/auto/irc/server.rb +301 -0
- data/lib/auto/irc/state/channel_manager.rb +6 -0
- data/lib/auto/irc/state/support.rb +142 -0
- data/lib/auto/irc/state/user_manager.rb +6 -0
- data/lib/auto/irc/std/commands.rb +99 -0
- data/lib/auto/irc/std/numerics.rb +216 -0
- data/lib/auto/rubyext/integer.rb +25 -0
- data/lib/auto/rubyext/string.rb +10 -0
- data/lib/auto/version.rb +18 -0
- data/lib/libauto.so +0 -0
- data/spec/api_events_spec.rb +68 -0
- data/spec/config_json_spec.rb +116 -0
- data/spec/config_other_spec.rb +29 -0
- data/spec/config_yaml_spec.rb +136 -0
- data/spec/helper.rb +19 -0
- data/spec/irc_object_entity_spec.rb +51 -0
- data/spec/logger_spec.rb +30 -0
- data/spec/plugin_base_spec.rb +35 -0
- data/spec/timers_spec.rb +42 -0
- metadata +238 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Auto
|
7
|
+
|
8
|
+
class Configure
|
9
|
+
|
10
|
+
# A class which allows for interaction with the current configuration,
|
11
|
+
# in contrast to generation of an entirely new one.
|
12
|
+
class Shell
|
13
|
+
|
14
|
+
# A list of commands.
|
15
|
+
COMMANDS = {
|
16
|
+
'lib.add' => "Add a library to loading.",
|
17
|
+
'lib.ls' => "Display a list of libraries currently loaded and available.",
|
18
|
+
'lib.rm' => "Remove a library from loading."
|
19
|
+
}
|
20
|
+
|
21
|
+
# @param [HighLine] hl HighLine instance.
|
22
|
+
def initialize(hl)
|
23
|
+
|
24
|
+
@hl = hl # HighLine
|
25
|
+
|
26
|
+
msg = <<-eom
|
27
|
+
Auto Configure Shell
|
28
|
+
|
29
|
+
This shell allows you to make changes to your current configuration. See
|
30
|
+
https://github.com/Auto/Auto/wiki/Auto-Configure for guidance.
|
31
|
+
eom
|
32
|
+
puts msg.yellow
|
33
|
+
|
34
|
+
# Check for ~/.config/autobot/auto.yml
|
35
|
+
puts ">> I am looking for your configuration file.....".blue
|
36
|
+
path = nil
|
37
|
+
if File.exists? File.join Auto::Configure::AUTODIR, 'auto.yml'
|
38
|
+
puts ">> I found #{File.join(Auto::Configure::AUTODIR, 'auto.yml').bold}!".green
|
39
|
+
path = File.join Auto::Configure::AUTODIR, 'auto.yml'
|
40
|
+
end
|
41
|
+
|
42
|
+
if path.nil?
|
43
|
+
|
44
|
+
puts ">> Sorry. I could not find your configuration file.".red
|
45
|
+
path = ask_for_config
|
46
|
+
|
47
|
+
else
|
48
|
+
|
49
|
+
unless @hl.agree("#$S Should I use this configuration? ") { |q| q.default = 'y' }
|
50
|
+
path = ask_for_config
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
data = nil
|
56
|
+
File.open(path) { |f| data = f.read }
|
57
|
+
|
58
|
+
# Parse it with ol' YAML Ain't Markup Language.
|
59
|
+
@conf = YAML.parse(data).to_ruby
|
60
|
+
|
61
|
+
@init = true
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
# A wrapper for {#go_without_safety} with safety.
|
66
|
+
def go
|
67
|
+
begin
|
68
|
+
go_without_safety
|
69
|
+
rescue => e
|
70
|
+
puts e.backtrace
|
71
|
+
puts "Auto Configure Shell has suffered an error. Closing shell...".red.bold
|
72
|
+
exit 1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Begin.
|
77
|
+
def go_without_safety
|
78
|
+
|
79
|
+
# Produce le menu.
|
80
|
+
@hl.choose do |menu|
|
81
|
+
puts ">> See 'help' for assistance using this shell.".green unless @init == false
|
82
|
+
@init = false
|
83
|
+
|
84
|
+
menu.shell = true
|
85
|
+
menu.prompt = ">> Please enter an action: "
|
86
|
+
|
87
|
+
# add all of our commands
|
88
|
+
COMMANDS.each_pair do |cmd, help|
|
89
|
+
menu.choice(cmd, help) { act cmd }
|
90
|
+
end
|
91
|
+
|
92
|
+
menu.choice(:help, "Display help.") do |cmd, data|
|
93
|
+
|
94
|
+
p data
|
95
|
+
args = data.split(/\s+/)
|
96
|
+
p args
|
97
|
+
if args.length < 1
|
98
|
+
puts ">> See https://github.com/Auto/Auto/wiki/Auto-Configure for help using this utility.".green
|
99
|
+
puts ">> Command list:".green
|
100
|
+
COMMANDS.each_pair { |c, h| puts "- #{c}: #{h}".yellow }
|
101
|
+
else
|
102
|
+
if COMMANDS.include? args[0]
|
103
|
+
puts ">> Help for #{args[0].bold}:".green
|
104
|
+
puts COMMANDS[args[0]].yellow
|
105
|
+
else
|
106
|
+
puts ">> No help available for #{args[0].bold}!".red
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
go
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
# Act.
|
119
|
+
def act(cmd)
|
120
|
+
|
121
|
+
case cmd
|
122
|
+
|
123
|
+
when 'lib.add'
|
124
|
+
|
125
|
+
when 'lib.ls'
|
126
|
+
|
127
|
+
when 'lib.rm'
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
go
|
132
|
+
|
133
|
+
end # def act
|
134
|
+
|
135
|
+
#######
|
136
|
+
private
|
137
|
+
#######
|
138
|
+
|
139
|
+
def ask_for_config
|
140
|
+
path = @hl.ask("#$S Where is your configuration file? ") { |q| q.default = 'conf/auto.yml' }
|
141
|
+
until File.exists? path
|
142
|
+
puts ">> Sorry. That does not appear to exist.".red
|
143
|
+
path = @hl.ask("#$S Where is your configuration file? ") { |q| q.default = 'conf/auto.yml' }
|
144
|
+
end
|
145
|
+
path
|
146
|
+
end
|
147
|
+
|
148
|
+
end # class Shell
|
149
|
+
|
150
|
+
end # class Configure
|
151
|
+
|
152
|
+
end # class Auto
|
153
|
+
|
154
|
+
# vim: set ts=4 sts=2 sw=2 et:
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
# Namespace: Auto
|
5
|
+
module Auto
|
6
|
+
|
7
|
+
# Namespace: DSL
|
8
|
+
module DSL
|
9
|
+
|
10
|
+
# A domain-specific language (DSL) wrapper mixin for simple usage of the events
|
11
|
+
# system, {Auto::API::Events}, and the timers system, {Auto::API::Timers}.
|
12
|
+
#
|
13
|
+
# @api DSL
|
14
|
+
# @author noxgirl
|
15
|
+
# @since 4.0.0
|
16
|
+
#
|
17
|
+
# @see Auto::API::Events
|
18
|
+
# @see Auto::API::Timers
|
19
|
+
module Base
|
20
|
+
|
21
|
+
# @see Auto::API::Timers#spawn
|
22
|
+
def clock_do(*args); $m.clock.spawn(*args); end
|
23
|
+
# @see Auto::API::Timers#del
|
24
|
+
def clock_stop(*args); $m.clock.del(*args); end
|
25
|
+
|
26
|
+
# Hook onto an event.
|
27
|
+
#
|
28
|
+
# @param [Symbol] system The events system to access.
|
29
|
+
# @param [Symbol] event The event onto which to hook.
|
30
|
+
#
|
31
|
+
# @see Auto::API::Events#on
|
32
|
+
def on(sys, event, &prc)
|
33
|
+
if sys == :auto # central system
|
34
|
+
$m.events.on(event, prc)
|
35
|
+
else
|
36
|
+
$m.send(sys).events.on(event, prc) if $m.respond_to? sys
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Emit an event.
|
41
|
+
#
|
42
|
+
# @param [Symbol] system The events system to access.
|
43
|
+
# @param [Symbol] event The event onto which to hook.
|
44
|
+
#
|
45
|
+
# @see Auto::API::Events#call
|
46
|
+
def emit(sys, event, *args)
|
47
|
+
if sys == :auto # central system
|
48
|
+
$m.events.call(event, *args)
|
49
|
+
else
|
50
|
+
$m.send(sys).events.call(event, *args) if $m.respond_to? sys
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Delete a hook.
|
55
|
+
#
|
56
|
+
# @param [Symbol] system The events system to access.
|
57
|
+
# @param [Array(Symbol, Integer, String)] hook The identification data of the hook.
|
58
|
+
#
|
59
|
+
# @see Auto::API::Events#del
|
60
|
+
def undo_on(sys, hook)
|
61
|
+
if sys == :auto # central system
|
62
|
+
$m.events.del(hook)
|
63
|
+
else
|
64
|
+
$m.send(sys).events.del(hook) if $m.respond_to? sys
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end # module Base
|
69
|
+
|
70
|
+
end # module DSL
|
71
|
+
|
72
|
+
end # module Auto
|
73
|
+
|
74
|
+
# vim: set ts=4 sts=2 sw=2 et:
|
data/lib/auto/dsl/irc.rb
ADDED
data/lib/auto/irc.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
module Auto
|
5
|
+
|
6
|
+
module IRC
|
7
|
+
|
8
|
+
# A class which manages such common IRC functions as autojoining channels,
|
9
|
+
# and identifying to services the traditional PRIVMSG way.
|
10
|
+
class Common
|
11
|
+
|
12
|
+
# Construct a new common function handler.
|
13
|
+
#
|
14
|
+
# @param [Auto::IRC::Library] lib The IRC library instance.
|
15
|
+
def initialize lib
|
16
|
+
@lib = lib
|
17
|
+
$m.events.on :die, &method(:do_die)
|
18
|
+
@lib.events.on :connected, &method(:do_autojoin)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Automatically identify with services the traditional way, which is
|
22
|
+
# to say by a /msg.
|
23
|
+
#
|
24
|
+
# @param [Auto::IRC::Server] irc The IRC connection.
|
25
|
+
def do_identify irc
|
26
|
+
if $m.conf['irc'][irc.s]['nickIdentify']
|
27
|
+
|
28
|
+
# Assume the service is NickServ if not specified
|
29
|
+
service = $m.conf['irc'][irc.s]['nickIdentify']['service'] || 'NickServ'
|
30
|
+
# and assume the command is IDENTIFY if not specified
|
31
|
+
command = $m.conf['irc'][irc.s]['nickIdentify']['command'] || 'IDENTIFY'
|
32
|
+
|
33
|
+
# we can't actually /msg anyone yet.....
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Automatically join IRC channels upon connection.
|
38
|
+
#
|
39
|
+
# @param [Auto::IRC::Server] irc The IRC connection.
|
40
|
+
def do_autojoin irc
|
41
|
+
if $m.conf['irc'][irc.s]['autojoin']
|
42
|
+
|
43
|
+
$m.conf['irc'][irc.s]['autojoin'].each do |chan|
|
44
|
+
irc.join(chan['name'], chan['key']||nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Disconnect from servers on termination.
|
51
|
+
#
|
52
|
+
# @param [String] reason Reason for termination.
|
53
|
+
def do_die reason
|
54
|
+
@lib.connections.each { |net, irc| irc.disconnect reason }
|
55
|
+
end
|
56
|
+
|
57
|
+
end # class Common
|
58
|
+
|
59
|
+
end # module IRC
|
60
|
+
|
61
|
+
end # module Auto
|
62
|
+
|
63
|
+
# vim: set ts=4 sts=2 sw=2 et:
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
require 'auto/irc/server'
|
5
|
+
require 'auto/irc/object/entity'
|
6
|
+
require 'auto/irc/object/channel'
|
7
|
+
require 'auto/irc/object/user'
|
8
|
+
require 'auto/irc/protocol'
|
9
|
+
require 'auto/irc/common'
|
10
|
+
|
11
|
+
module Auto
|
12
|
+
|
13
|
+
module IRC
|
14
|
+
|
15
|
+
# The base of the IRC framework.
|
16
|
+
#
|
17
|
+
# @!attribute [r] events
|
18
|
+
# @return [Auto::API::Events] The IRC event system.
|
19
|
+
#
|
20
|
+
# @!attribute [r] connections
|
21
|
+
# @return [Hash{String => Auto::IRC::Server}] Collection of IRC connections.
|
22
|
+
class Library
|
23
|
+
|
24
|
+
attr_reader :events, :connections
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
|
28
|
+
# Initialize our event system.
|
29
|
+
@events = Auto::API::Events.new
|
30
|
+
# Prepare our collection of IRC server connections.
|
31
|
+
@connections = Hash.new
|
32
|
+
|
33
|
+
# Be ready to accept data.
|
34
|
+
$m.events.on :net_receive, 1, &method(:receive)
|
35
|
+
|
36
|
+
# Start connections when Auto is started.
|
37
|
+
$m.events.on :start, &method(:start)
|
38
|
+
|
39
|
+
# Parse data.
|
40
|
+
@parser = Auto::IRC::Protocol.new self
|
41
|
+
|
42
|
+
# Handle common functions.
|
43
|
+
@common = Auto::IRC::Common.new self
|
44
|
+
|
45
|
+
end # def initialize
|
46
|
+
|
47
|
+
# Process incoming network data.
|
48
|
+
#
|
49
|
+
# @param [Object] socket_object The socket object, which in the case of
|
50
|
+
# ourselves should be an {Auto::IRC::Server}, or we won't handle it.
|
51
|
+
def receive socket_object
|
52
|
+
if socket_object.instance_of? Auto::IRC::Server
|
53
|
+
socket_object.recv
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Initiate IRC connections.
|
58
|
+
def start
|
59
|
+
|
60
|
+
# Iterate through each IRC server in the config, and connect to it.
|
61
|
+
$m.conf['irc'].each do |name, hash|
|
62
|
+
begin
|
63
|
+
# Configure the IRC instance.
|
64
|
+
@connections[name] = Auto::IRC::Server.new(name) do |c|
|
65
|
+
c.address = hash['address']
|
66
|
+
c.port = hash['port']
|
67
|
+
c.nick = hash['nickname'][0]
|
68
|
+
c.user = hash['username']
|
69
|
+
c.real = hash['realName']
|
70
|
+
c.ssl = hash['useSSL']
|
71
|
+
end
|
72
|
+
|
73
|
+
# Connect.
|
74
|
+
$m.sockets << @connections[name]
|
75
|
+
@connections[name].connect
|
76
|
+
rescue => e
|
77
|
+
$m.error("Connection to #{name} failed: #{e}", false, e.backtrace)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end # def start
|
82
|
+
|
83
|
+
end # class Library
|
84
|
+
|
85
|
+
end # module IRC
|
86
|
+
|
87
|
+
end # module Auto
|
88
|
+
|
89
|
+
# vim: set ts=4 sts=2 sw=2 et:
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
# Namespace: IRC
|
5
|
+
module IRC
|
6
|
+
|
7
|
+
# Namespace: Object
|
8
|
+
module Object
|
9
|
+
|
10
|
+
# A class which represents an individual IRC channel, its associated
|
11
|
+
# properties, the users which it contains, and methods to interact with it.
|
12
|
+
class Channel
|
13
|
+
|
14
|
+
|
15
|
+
end # class Channel
|
16
|
+
|
17
|
+
end # module Object
|
18
|
+
|
19
|
+
end # module IRC
|
20
|
+
|
21
|
+
# vim: set ts=4 sts=2 sw=2 et:
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
module Auto
|
5
|
+
|
6
|
+
module IRC
|
7
|
+
|
8
|
+
module Object
|
9
|
+
|
10
|
+
# A superclass which represents an IRC 'entity'; i.e., a channel or user.
|
11
|
+
# It acts as a base for {Auto::IRC::Object::User} and {Auto::IRC::Object::Channel}.
|
12
|
+
#
|
13
|
+
# @api IRC
|
14
|
+
# @since 4.0.0
|
15
|
+
# @author noxgirl
|
16
|
+
#
|
17
|
+
# @!attribute [r] irc
|
18
|
+
# @return [Auto::IRC::Server] The IRC server on which this entity exists.
|
19
|
+
#
|
20
|
+
# @!attribute [r] name
|
21
|
+
# @return [String] The name of the entity.
|
22
|
+
class Entity
|
23
|
+
|
24
|
+
attr_reader :irc, :name
|
25
|
+
|
26
|
+
# New instance.
|
27
|
+
#
|
28
|
+
# @param [Auto::IRC::Server] irc The server.
|
29
|
+
# @param [Symbol] type Either +:channel+ or +:user+.
|
30
|
+
# @param [String] name The name of this entity (nickname or channel name).
|
31
|
+
def initialize(irc, type, name)
|
32
|
+
@irc = irc
|
33
|
+
@entity_type = type
|
34
|
+
@name = name
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [true, false] Whether we're a channel.
|
38
|
+
def channel?
|
39
|
+
@entity_type == :channel ? true : false
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [true, false] Whether we're a user.
|
43
|
+
def user?
|
44
|
+
@entity_type == :user ? true : false
|
45
|
+
end
|
46
|
+
|
47
|
+
# Send a message to this entity. This will additionally divide messages
|
48
|
+
# which are too long into multiple messages.
|
49
|
+
#
|
50
|
+
# This will call irc:onMsg with +self+ and +message+.
|
51
|
+
#
|
52
|
+
# @param [String] message The message.
|
53
|
+
# @param [true, false] notice Whether this should be a /notice as opposed
|
54
|
+
# to a /msg.
|
55
|
+
def msg(message, notice=false)
|
56
|
+
|
57
|
+
command = (notice == false ? 'PRIVMSG' : 'NOTICE')
|
58
|
+
len = ":#{@irc.nick}!#{@irc.user}@#{@irc.mask} #{command} #@name :".length
|
59
|
+
raw = ":#{@irc.nick}!#{@irc.user}@#{@irc.mask} #{command} #@name :#{message}\r\n"
|
60
|
+
|
61
|
+
if raw.length > 512
|
62
|
+
|
63
|
+
msgs = []
|
64
|
+
nmessage = message
|
65
|
+
until raw.length <= 512
|
66
|
+
msgs << nmessage.slice!(0, 512-len)
|
67
|
+
raw = ":#{@irc.nick}!#{@irc.user}@#{@irc.mask} #{command} #@name :#{nmessage}\r\n"
|
68
|
+
end
|
69
|
+
|
70
|
+
msgs.each { |m| @irc.snd "#{command} #@name :#{m}" }
|
71
|
+
|
72
|
+
else
|
73
|
+
@irc.snd "#{command} #@name :#{message}"
|
74
|
+
end
|
75
|
+
|
76
|
+
$m.events.call 'irc:onMsg', self, message
|
77
|
+
|
78
|
+
end # def msg
|
79
|
+
|
80
|
+
def to_s; @name; end
|
81
|
+
|
82
|
+
end # class Entity
|
83
|
+
|
84
|
+
end # module Object
|
85
|
+
|
86
|
+
end # module IRC
|
87
|
+
|
88
|
+
end # module Auto
|
89
|
+
|
90
|
+
# vim: set ts=4 sts=2 sw=2 et:
|