netrc 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.md +40 -0
- data/data/permissive.netrc +4 -0
- data/data/sample.netrc +4 -0
- data/lib/netrc.rb +134 -0
- data/test/test_lex.rb +52 -0
- data/test/test_netrc.rb +90 -0
- data/test/test_parse.rb +34 -0
- metadata +52 -0
data/Readme.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Netrc
|
2
|
+
|
3
|
+
This library reads and writes `.netrc` files.
|
4
|
+
|
5
|
+
## API
|
6
|
+
|
7
|
+
Read a netrc file:
|
8
|
+
|
9
|
+
n = Netrc.read("sample.netrc")
|
10
|
+
|
11
|
+
If the file doesn't exist, Netrc.read will return an empty object.
|
12
|
+
|
13
|
+
Read the user's default netrc file (`$HOME/.netrc` on Unix;
|
14
|
+
`%HOME%\_netrc` on Windows):
|
15
|
+
|
16
|
+
n = Netrc.read
|
17
|
+
|
18
|
+
Look up a username and password:
|
19
|
+
|
20
|
+
user, pass = n["example.com"]
|
21
|
+
|
22
|
+
Write a username and password:
|
23
|
+
|
24
|
+
n["example.com"] = user, newpass
|
25
|
+
n.save
|
26
|
+
|
27
|
+
If you make an entry that wasn't there before, it will be appended
|
28
|
+
to the end of the file. Sometimes people want to include a comment
|
29
|
+
explaining that the entry was added automatically. You can do it
|
30
|
+
like this:
|
31
|
+
|
32
|
+
n.new_item_prefix = "# This entry was added automatically\n"
|
33
|
+
n["example.com"] = user, newpass
|
34
|
+
n.save
|
35
|
+
|
36
|
+
Have fun!
|
37
|
+
|
38
|
+
## Running Tests
|
39
|
+
|
40
|
+
$ turn test
|
data/data/sample.netrc
ADDED
data/lib/netrc.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
class Netrc
|
2
|
+
VERSION = "0.1"
|
3
|
+
|
4
|
+
Windows = false
|
5
|
+
def self.default_path
|
6
|
+
File.join(ENV["HOME"], default_name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.default_name
|
10
|
+
if Windows
|
11
|
+
return "_netrc"
|
12
|
+
end
|
13
|
+
".netrc"
|
14
|
+
end
|
15
|
+
|
16
|
+
# Reads path and parses it as a .netrc file. If path doesn't
|
17
|
+
# exist, returns an empty object.
|
18
|
+
def self.read(path=default_path)
|
19
|
+
perm = File.stat(path).mode & 0777
|
20
|
+
if perm != 0600
|
21
|
+
raise Error, "Permission bits should be 0600, but are "+perm.to_s(8)
|
22
|
+
end
|
23
|
+
new(path, parse(lex(IO.readlines(path))))
|
24
|
+
rescue Errno::ENOENT
|
25
|
+
new(path, parse(lex([])))
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.lex(lines)
|
29
|
+
tokens = []
|
30
|
+
for line in lines
|
31
|
+
content, comment = line.split(/(\s*#.*)/m)
|
32
|
+
tokens += content.split(/(?<=\s)(?=\S)|(?<=\S)(?=\s)/)
|
33
|
+
if comment
|
34
|
+
tokens << comment
|
35
|
+
end
|
36
|
+
end
|
37
|
+
tokens
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.skip?(s)
|
41
|
+
s =~ /^\s/
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns two values, a header and a list of items.
|
45
|
+
# Each item is a 7-tuple, containing:
|
46
|
+
# - machine keyword (including trailing whitespace+comments)
|
47
|
+
# - machine name
|
48
|
+
# - login keyword (including surrounding whitespace+comments)
|
49
|
+
# - login
|
50
|
+
# - password keyword (including surrounding whitespace+comments)
|
51
|
+
# - password
|
52
|
+
# - trailing chars
|
53
|
+
# This lets us change individual fields, then write out the file
|
54
|
+
# with all its original formatting.
|
55
|
+
def self.parse(ts)
|
56
|
+
cur, item = [], []
|
57
|
+
|
58
|
+
def ts.take
|
59
|
+
if count < 1
|
60
|
+
raise Error, "unexpected EOF"
|
61
|
+
end
|
62
|
+
shift
|
63
|
+
end
|
64
|
+
|
65
|
+
def ts.readto
|
66
|
+
l = []
|
67
|
+
while count > 0 && ! yield(self[0])
|
68
|
+
l << shift
|
69
|
+
end
|
70
|
+
return l.join
|
71
|
+
end
|
72
|
+
|
73
|
+
pre = ts.readto{|t| t == "machine"}
|
74
|
+
while ts.count > 0
|
75
|
+
cur << ts.take + ts.readto{|t| ! skip?(t)}
|
76
|
+
cur << ts.take
|
77
|
+
cur << ts.readto{|t| t == "login"} + ts.take + ts.readto{|t| ! skip?(t)}
|
78
|
+
cur << ts.take
|
79
|
+
cur << ts.readto{|t| t == "password"} + ts.take + ts.readto{|t| ! skip?(t)}
|
80
|
+
cur << ts.take
|
81
|
+
cur << ts.readto{|t| t == "machine"}
|
82
|
+
item << cur
|
83
|
+
cur = []
|
84
|
+
end
|
85
|
+
|
86
|
+
[pre, item]
|
87
|
+
end
|
88
|
+
|
89
|
+
def initialize(path, data)
|
90
|
+
@path = path
|
91
|
+
@pre, @data = data
|
92
|
+
end
|
93
|
+
|
94
|
+
attr_accessor :new_item_prefix
|
95
|
+
|
96
|
+
def [](k)
|
97
|
+
for v in @data
|
98
|
+
if v[1] == k
|
99
|
+
return v[3], v[5]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def []=(k, info)
|
106
|
+
for v in @data
|
107
|
+
if v[1] == k
|
108
|
+
v[3], v[5] = info
|
109
|
+
return
|
110
|
+
end
|
111
|
+
end
|
112
|
+
@data << new_item(k, info[0], info[1])
|
113
|
+
end
|
114
|
+
|
115
|
+
def count
|
116
|
+
@data.count
|
117
|
+
end
|
118
|
+
|
119
|
+
def new_item(m, l, p)
|
120
|
+
[new_item_prefix+"machine ", m, "\n login ", l, "\n password ", p, "\n"]
|
121
|
+
end
|
122
|
+
|
123
|
+
def save
|
124
|
+
File.write(path, unparse)
|
125
|
+
end
|
126
|
+
|
127
|
+
def unparse
|
128
|
+
@pre + @data.map(&:join).join
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
class Netrc::Error < ::StandardError
|
134
|
+
end
|
data/test/test_lex.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
$VERBOSE = true
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
require 'netrc'
|
5
|
+
|
6
|
+
class TestLex < MiniTest::Unit::TestCase
|
7
|
+
def test_lex_empty
|
8
|
+
t = Netrc.lex([])
|
9
|
+
assert_equal([], t)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_lex_comment
|
13
|
+
t = Netrc.lex(["# foo\n"])
|
14
|
+
assert_equal(["# foo\n"], t)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_lex_comment_after_space
|
18
|
+
t = Netrc.lex([" # foo\n"])
|
19
|
+
assert_equal([" # foo\n"], t)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_lex_comment_after_word
|
23
|
+
t = Netrc.lex(["x # foo\n"])
|
24
|
+
assert_equal(["x", " # foo\n"], t)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_lex_comment_with_hash
|
28
|
+
t = Netrc.lex(["x # foo # bar\n"])
|
29
|
+
assert_equal(["x", " # foo # bar\n"], t)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_lex_word
|
33
|
+
t = Netrc.lex(["x"])
|
34
|
+
assert_equal(["x"], t)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_lex_two_lines
|
38
|
+
t = Netrc.lex(["x\ny\n"])
|
39
|
+
assert_equal(["x", "\n", "y", "\n"], t)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_lex_word_and_comment
|
43
|
+
t = Netrc.lex(["x\n", "# foo\n"])
|
44
|
+
assert_equal(["x", "\n", "# foo\n"], t)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_lex_six_words
|
48
|
+
t = Netrc.lex(["machine m login l password p\n"])
|
49
|
+
e = ["machine", " ", "m", " ", "login", " ", "l", " ", "password", " ", "p", "\n"]
|
50
|
+
assert_equal(e, t)
|
51
|
+
end
|
52
|
+
end
|
data/test/test_netrc.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
$VERBOSE = true
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
require 'netrc'
|
5
|
+
|
6
|
+
class TestNetrc < MiniTest::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
File.chmod(0600, "data/sample.netrc")
|
9
|
+
File.chmod(0644, "data/permissive.netrc")
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_parse_empty
|
13
|
+
pre, items = Netrc.parse(Netrc.lex([]))
|
14
|
+
assert_equal("", pre)
|
15
|
+
assert_equal([], items)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_parse_file
|
19
|
+
pre, items = Netrc.parse(Netrc.lex(IO.readlines("data/sample.netrc")))
|
20
|
+
assert_equal("# this is my netrc\n", pre)
|
21
|
+
exp = [["machine ",
|
22
|
+
"m",
|
23
|
+
"\n login ",
|
24
|
+
"l",
|
25
|
+
" # this is my username\n password ",
|
26
|
+
"p",
|
27
|
+
"\n"]]
|
28
|
+
assert_equal(exp, items)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_missing_file
|
32
|
+
n = Netrc.read("data/nonexistent.netrc")
|
33
|
+
assert_equal(0, n.count)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_permission_error
|
37
|
+
Netrc.read("data/permissive.netrc")
|
38
|
+
assert false, "Should raise an error if permissions are wrong."
|
39
|
+
rescue Netrc::Error
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_round_trip
|
43
|
+
n = Netrc.read("data/sample.netrc")
|
44
|
+
assert_equal(IO.read("data/sample.netrc"), n.unparse)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_set
|
48
|
+
n = Netrc.read("data/sample.netrc")
|
49
|
+
n["m"] = "a", "b"
|
50
|
+
exp = "# this is my netrc\n"+
|
51
|
+
"machine m\n"+
|
52
|
+
" login a # this is my username\n"+
|
53
|
+
" password b\n"
|
54
|
+
assert_equal(exp, n.unparse)
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_set_get
|
58
|
+
n = Netrc.read("data/sample.netrc")
|
59
|
+
n["m"] = "a", "b"
|
60
|
+
l, p = n["m"]
|
61
|
+
assert_equal(["a", "b"], n["m"])
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_add
|
65
|
+
n = Netrc.read("data/sample.netrc")
|
66
|
+
n.new_item_prefix = "# added\n"
|
67
|
+
n["x"] = "a", "b"
|
68
|
+
exp = "# this is my netrc\n"+
|
69
|
+
"machine m\n"+
|
70
|
+
" login l # this is my username\n"+
|
71
|
+
" password p\n"+
|
72
|
+
"# added\n"+
|
73
|
+
"machine x\n"+
|
74
|
+
" login a\n"+
|
75
|
+
" password b\n"
|
76
|
+
assert_equal(exp, n.unparse)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_add_get
|
80
|
+
n = Netrc.read("data/sample.netrc")
|
81
|
+
n.new_item_prefix = "# added\n"
|
82
|
+
n["x"] = "a", "b"
|
83
|
+
assert_equal(["a", "b"], n["x"])
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_get_missing
|
87
|
+
n = Netrc.read("data/sample.netrc")
|
88
|
+
assert_equal(nil, n["x"])
|
89
|
+
end
|
90
|
+
end
|
data/test/test_parse.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
$VERBOSE = true
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
require 'netrc'
|
5
|
+
|
6
|
+
class TestParse < MiniTest::Unit::TestCase
|
7
|
+
def test_parse_empty
|
8
|
+
pre, items = Netrc.parse([])
|
9
|
+
assert_equal("", pre)
|
10
|
+
assert_equal([], items)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_parse_comment
|
14
|
+
pre, items = Netrc.parse(["# foo\n"])
|
15
|
+
assert_equal("# foo\n", pre)
|
16
|
+
assert_equal([], items)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_parse_item
|
20
|
+
t = ["machine", " ", "m", " ", "login", " ", "l", " ", "password", " ", "p", "\n"]
|
21
|
+
pre, items = Netrc.parse(t)
|
22
|
+
assert_equal("", pre)
|
23
|
+
e = [["machine ", "m", " login ", "l", " password ", "p", "\n"]]
|
24
|
+
assert_equal(e, items)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_parse_two_items
|
28
|
+
t = ["machine", " ", "m", " ", "login", " ", "l", " ", "password", " ", "p", "\n"] * 2
|
29
|
+
pre, items = Netrc.parse(t)
|
30
|
+
assert_equal("", pre)
|
31
|
+
e = [["machine ", "m", " login ", "l", " password ", "p", "\n"]] * 2
|
32
|
+
assert_equal(e, items)
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: netrc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Keith Rarick
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-15 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: This library can read and update netrc files, preserving formatting including
|
15
|
+
comments and whitespace.
|
16
|
+
email: kr@xph.us
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- Readme.md
|
22
|
+
- data/permissive.netrc
|
23
|
+
- data/sample.netrc
|
24
|
+
- lib/netrc.rb
|
25
|
+
- test/test_lex.rb
|
26
|
+
- test/test_netrc.rb
|
27
|
+
- test/test_parse.rb
|
28
|
+
homepage: https://github.com/kr/netrc
|
29
|
+
licenses: []
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 1.8.10
|
49
|
+
signing_key:
|
50
|
+
specification_version: 3
|
51
|
+
summary: Library to read and write netrc files.
|
52
|
+
test_files: []
|