net-ftp-list 3.2.11 → 3.4.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +21 -0
- data/.gitignore +11 -0
- data/Gemfile +1 -5
- data/Rakefile +7 -42
- data/lib/net/ftp/list/entry.rb +34 -54
- data/lib/net/ftp/list/microsoft.rb +14 -16
- data/lib/net/ftp/list/netware.rb +8 -10
- data/lib/net/ftp/list/parser.rb +35 -18
- data/lib/net/ftp/list/rumpus.rb +9 -10
- data/lib/net/ftp/list/unix.rb +32 -40
- data/lib/net/ftp/list/unknown.rb +3 -6
- data/lib/net/ftp/list.rb +3 -4
- data/net-ftp-list.gemspec +17 -56
- data/test/test_net_ftp_list.rb +2 -3
- data/test/test_net_ftp_list_entry.rb +20 -21
- data/test/test_net_ftp_list_microsoft.rb +6 -7
- data/test/test_net_ftp_list_netware.rb +6 -8
- data/test/test_net_ftp_list_rumpus.rb +5 -6
- data/test/test_net_ftp_list_unix.rb +76 -62
- metadata +73 -17
- data/.ruby-version +0 -1
- data/Gemfile.lock +0 -63
- data/VERSION.yml +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a7055fe548d1984de166f451dfb2aa7240c8606a04c3f06b149a6b3892573027
|
|
4
|
+
data.tar.gz: 67ac2d02694ca97a6ab76da05b0dc75f3b06bbadd11b28c8ca82b4dd0f4053e7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eda43bbfa999e5d4f1c163f842e1a87cd0fa787fd6c7b241683ad9bacf8e2d71c645a8b20ff63d96714d9edd986cd4a00da8966463646874e2aaeccc6fc2719a
|
|
7
|
+
data.tar.gz: 6f16a6ca4991c274356c6ff4899be14cea0ab7009960f8de6c99f0ea8696c79466bcad1963aa8a6c8c08dd1edc0ce5ce8a6b33edf5c5bbef26cd9d57244a6e49
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
ruby:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
ruby-version: ["2.5", "2.6", "2.7", "3.0", "3.1", "3.2", "3.3", "3.4", "4.0"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v2
|
|
17
|
+
- uses: ruby/setup-ruby@v1
|
|
18
|
+
with:
|
|
19
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
- run: bundle exec rake
|
data/.gitignore
ADDED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
|
@@ -1,54 +1,19 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
3
|
-
require 'yaml'
|
|
4
|
-
|
|
5
|
-
begin
|
|
6
|
-
require 'jeweler'
|
|
7
|
-
Jeweler::Tasks.new do |gem|
|
|
8
|
-
gem.name = "net-ftp-list"
|
|
9
|
-
gem.summary = %Q{Parse FTP LIST command output.}
|
|
10
|
-
gem.email = "enquiries@statelesssystems.com"
|
|
11
|
-
gem.homepage = "http://github.com/stateless-systems/net-ftp-list"
|
|
12
|
-
gem.authors = ["Stateless Systems"]
|
|
13
|
-
end
|
|
14
|
-
Jeweler::RubygemsDotOrgTasks.new
|
|
15
|
-
rescue LoadError
|
|
16
|
-
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
|
17
|
-
end
|
|
18
|
-
|
|
1
|
+
require 'bundler/setup'
|
|
2
|
+
require 'bundler/gem_tasks'
|
|
19
3
|
require 'rake/testtask'
|
|
4
|
+
require 'rdoc/task'
|
|
5
|
+
|
|
20
6
|
Rake::TestTask.new(:test) do |test|
|
|
21
7
|
test.libs << 'lib' << 'test'
|
|
22
8
|
test.pattern = 'test/**/test_*.rb'
|
|
23
9
|
test.verbose = true
|
|
24
10
|
end
|
|
25
11
|
|
|
26
|
-
begin
|
|
27
|
-
require 'rcov/rcovtask'
|
|
28
|
-
Rcov::RcovTask.new do |test|
|
|
29
|
-
test.libs << 'test'
|
|
30
|
-
test.pattern = 'test/**/test_*.rb'
|
|
31
|
-
test.verbose = true
|
|
32
|
-
end
|
|
33
|
-
rescue LoadError
|
|
34
|
-
task :rcov do
|
|
35
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
task :default => :test
|
|
40
|
-
|
|
41
|
-
require 'rdoc/task'
|
|
42
12
|
Rake::RDocTask.new do |rdoc|
|
|
43
|
-
if File.exist?('VERSION.yml')
|
|
44
|
-
config = YAML.load(File.read('VERSION.yml'))
|
|
45
|
-
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
|
46
|
-
else
|
|
47
|
-
version = ""
|
|
48
|
-
end
|
|
49
|
-
|
|
50
13
|
rdoc.rdoc_dir = 'rdoc'
|
|
51
|
-
rdoc.title =
|
|
14
|
+
rdoc.title = 'net-ftp-list'
|
|
52
15
|
rdoc.rdoc_files.include('README*')
|
|
53
16
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
54
17
|
end
|
|
18
|
+
|
|
19
|
+
task default: %i[test]
|
data/lib/net/ftp/list/entry.rb
CHANGED
|
@@ -2,17 +2,28 @@
|
|
|
2
2
|
class Net::FTP::List::Entry
|
|
3
3
|
include Comparable
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
attr_reader :raw, :basename, :type, :filesize, :mtime, :server_type, :symlink_destination
|
|
6
|
+
alias to_s raw
|
|
7
|
+
alias name basename
|
|
8
|
+
alias size filesize
|
|
6
9
|
|
|
7
10
|
# Create a new entry object. The additional argument is the list of metadata keys
|
|
8
11
|
# that can be used on the object. By default just takes and set the raw list entry.
|
|
9
12
|
# Net::FTP::List.parse(raw_list_string) # => Net::FTP::List::Parser instance.
|
|
10
|
-
def initialize(raw_ls_line,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
def initialize(raw_ls_line,
|
|
14
|
+
basename: nil,
|
|
15
|
+
type: nil,
|
|
16
|
+
mtime: nil,
|
|
17
|
+
filesize: nil,
|
|
18
|
+
server_type: nil,
|
|
19
|
+
symlink_destination: nil) #:nodoc:
|
|
20
|
+
@raw = raw_ls_line.respond_to?(:force_encoding) ? raw_ls_line.dup.force_encoding('utf-8') : raw_ls_line
|
|
21
|
+
@basename = basename || ''
|
|
22
|
+
@type = type
|
|
23
|
+
@mtime = mtime || Time.now
|
|
24
|
+
@filesize = filesize || 0
|
|
25
|
+
@server_type = server_type || 'Unknown'
|
|
26
|
+
@symlink_destination = symlink_destination
|
|
16
27
|
end
|
|
17
28
|
|
|
18
29
|
# Tests for objects equality (value and type).
|
|
@@ -22,83 +33,52 @@ class Net::FTP::List::Entry
|
|
|
22
33
|
# @return [true, false] true if the objects are equal and have the same type; false otherwise.
|
|
23
34
|
#
|
|
24
35
|
def eql?(other)
|
|
25
|
-
return false
|
|
26
|
-
return true if
|
|
36
|
+
return false unless other.is_a?(::Net::FTP::List::Entry)
|
|
37
|
+
return true if equal?(other)
|
|
27
38
|
|
|
28
|
-
|
|
39
|
+
raw == other.raw # if it's exactly the same line then the objects are the same
|
|
29
40
|
end
|
|
30
41
|
|
|
31
|
-
|
|
32
42
|
# Compares the receiver against another object.
|
|
33
43
|
#
|
|
34
44
|
# @param (see #eql?)
|
|
35
45
|
#
|
|
36
|
-
# @return [
|
|
46
|
+
# @return [Integer] -1, 0, or +1 depending on whether the receiver is less than, equal to, or greater than the other object.
|
|
37
47
|
#
|
|
38
48
|
def <=>(other)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
case other
|
|
50
|
+
when ::Net::FTP::List::Entry
|
|
51
|
+
return filesize <=> other.filesize
|
|
52
|
+
when Numeric
|
|
53
|
+
return filesize <=> other
|
|
43
54
|
end
|
|
44
|
-
raise ArgumentError.new('comparison of %s with %s failed!' % [self.class, other.class])
|
|
45
|
-
end
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
def raw
|
|
49
|
-
@raw ||= ''
|
|
50
|
-
end
|
|
51
|
-
alias_method :to_s, :raw
|
|
52
|
-
|
|
53
|
-
# The items basename (filename).
|
|
54
|
-
def basename
|
|
55
|
-
@basename ||= ''
|
|
56
|
-
end
|
|
57
|
-
alias name basename
|
|
58
|
-
|
|
59
|
-
# Symlink destination, the "/etc" in "bar -> /etc". May be nil.
|
|
60
|
-
def symlink_destination
|
|
61
|
-
@symlink_destination
|
|
56
|
+
raise ArgumentError, format('comparison of %<self>s with %<other>s failed!', self: self.class, other: other.class)
|
|
62
57
|
end
|
|
63
58
|
|
|
64
59
|
# Looks like a directory, try CWD.
|
|
65
60
|
def dir?
|
|
66
|
-
|
|
61
|
+
type == :dir
|
|
67
62
|
end
|
|
68
63
|
alias directory? dir?
|
|
69
64
|
|
|
70
65
|
# Looks like a file, try RETR.
|
|
71
66
|
def file?
|
|
72
|
-
|
|
67
|
+
type == :file
|
|
73
68
|
end
|
|
74
69
|
|
|
75
70
|
# Looks like a symbolic link.
|
|
76
71
|
def symlink?
|
|
77
|
-
|
|
72
|
+
type == :symlink
|
|
78
73
|
end
|
|
79
74
|
|
|
80
75
|
# Looks like a device.
|
|
81
76
|
def device?
|
|
82
|
-
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Returns the modification time of the file/directory or the current time if unknown
|
|
86
|
-
def mtime
|
|
87
|
-
@mtime || Time.now
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Returns the filesize of the entry or 0 for directorties
|
|
91
|
-
def filesize
|
|
92
|
-
@filesize || 0
|
|
93
|
-
end
|
|
94
|
-
alias size filesize
|
|
95
|
-
|
|
96
|
-
# Returns the detected server type if this entry
|
|
97
|
-
def server_type
|
|
98
|
-
@server_type || "Unknown"
|
|
77
|
+
type == :device
|
|
99
78
|
end
|
|
100
79
|
|
|
80
|
+
# Is the entry type unknown?
|
|
101
81
|
def unknown?
|
|
102
|
-
|
|
82
|
+
!dir? && !file? && !symlink? && !device?
|
|
103
83
|
end
|
|
104
84
|
end
|
|
@@ -11,36 +11,34 @@ require 'date'
|
|
|
11
11
|
class Net::FTP::List::Microsoft < Net::FTP::List::Parser
|
|
12
12
|
REGEXP = %r!
|
|
13
13
|
^\s*
|
|
14
|
-
([0-9
|
|
14
|
+
([0-9\-:/]{5,})\s+([0-9\-:]{3,}(?:[aApP][mM])?)\s+
|
|
15
15
|
(?:(<DIR>)|([0-9]+))\s+
|
|
16
16
|
(\S.*)
|
|
17
17
|
\s*$
|
|
18
|
-
!x
|
|
18
|
+
!x.freeze
|
|
19
19
|
|
|
20
20
|
# Parse a Microsoft(NT) like FTP LIST entries.
|
|
21
|
-
def self.parse(raw)
|
|
21
|
+
def self.parse(raw, timezone: :utc)
|
|
22
22
|
match = REGEXP.match(raw.strip) or return false
|
|
23
23
|
|
|
24
|
-
date_match =
|
|
24
|
+
date_match = /(\d\d).(\d\d).(\d\d(?:\d\d)?)/.match(match[1])
|
|
25
25
|
date_format = date_match[1].to_i > 12 ? '%d-%m-%y' : '%m-%d-%y'
|
|
26
|
-
date_format.sub!(
|
|
26
|
+
date_format.sub!(/%y/, '%Y') if date_match[3].length > 2
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
date_format.
|
|
30
|
-
date_format.
|
|
28
|
+
unless /-/.match?(match[1])
|
|
29
|
+
date_format.tr!('-', '/') if %r{/}.match?(match[1])
|
|
30
|
+
date_format.tr!('-', ':') if /:/.match?(match[1])
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
mtime =
|
|
34
|
-
|
|
35
|
-
filesize = is_dir ? 0 : match[4].to_i
|
|
33
|
+
mtime = parse_time("#{match[1]} #{match[2]}", format: "#{date_format} %H:%M%p", timezone: timezone)
|
|
34
|
+
type = match[3] == '<DIR>' ? :dir : :file
|
|
36
35
|
|
|
37
36
|
emit_entry(
|
|
38
37
|
raw,
|
|
39
|
-
:
|
|
40
|
-
:
|
|
41
|
-
:
|
|
42
|
-
:
|
|
43
|
-
:mtime => mtime
|
|
38
|
+
type: type,
|
|
39
|
+
filesize: match[4].to_i,
|
|
40
|
+
basename: match[5],
|
|
41
|
+
mtime: mtime,
|
|
44
42
|
)
|
|
45
43
|
end
|
|
46
44
|
end
|
data/lib/net/ftp/list/netware.rb
CHANGED
|
@@ -9,27 +9,25 @@ require 'time'
|
|
|
9
9
|
class Net::FTP::List::Netware < Net::FTP::List::Parser
|
|
10
10
|
# Stolen straight from the ASF's commons Java FTP LIST parser library.
|
|
11
11
|
# http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
|
|
12
|
-
REGEXP =
|
|
12
|
+
REGEXP = /^
|
|
13
13
|
(d|-){1}\s+
|
|
14
14
|
\[(.*?)\]\s+
|
|
15
15
|
(\S+)\s+(\d+)\s+
|
|
16
16
|
(\S+\s+\S+\s+((\d+:\d+)|(\d{4})))
|
|
17
17
|
\s+(.*)
|
|
18
|
-
|
|
18
|
+
$/x.freeze
|
|
19
19
|
|
|
20
20
|
# Parse a Netware like FTP LIST entries.
|
|
21
|
-
def self.parse(raw)
|
|
21
|
+
def self.parse(raw, timezone: :utc)
|
|
22
22
|
match = REGEXP.match(raw.strip) or return false
|
|
23
|
-
|
|
24
|
-
is_dir = match[1] == 'd'
|
|
23
|
+
type = match[1] == 'd' ? :dir : :file
|
|
25
24
|
|
|
26
25
|
emit_entry(
|
|
27
26
|
raw,
|
|
28
|
-
:
|
|
29
|
-
:
|
|
30
|
-
:
|
|
31
|
-
:
|
|
32
|
-
:basename => match[9]
|
|
27
|
+
mtime: parse_time(match[5], timezone: timezone),
|
|
28
|
+
filesize: match[4].to_i,
|
|
29
|
+
type: type,
|
|
30
|
+
basename: match[9],
|
|
33
31
|
)
|
|
34
32
|
end
|
|
35
33
|
end
|
data/lib/net/ftp/list/parser.rb
CHANGED
|
@@ -1,28 +1,45 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
|
|
1
3
|
# Abstract FTP LIST parser. It really just defines and documents the interface.
|
|
2
4
|
class Net::FTP::List::Parser
|
|
5
|
+
class << self
|
|
6
|
+
# Returns registered parsers.
|
|
7
|
+
def parsers
|
|
8
|
+
@parsers ||= []
|
|
9
|
+
end
|
|
3
10
|
|
|
4
|
-
|
|
11
|
+
# Manuall register a parser.
|
|
12
|
+
def register(parser)
|
|
13
|
+
parsers.push(parser) unless parsers.include?(parser)
|
|
14
|
+
end
|
|
5
15
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
16
|
+
# Automatically add an inheriting parser to the list of known parsers.
|
|
17
|
+
def inherited(klass)
|
|
18
|
+
super
|
|
19
|
+
register(klass)
|
|
20
|
+
end
|
|
11
21
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
# The main parse method. Return false from it if parsing fails (this is cheaper than raising an exception)
|
|
23
|
+
def parse(_raw, **)
|
|
24
|
+
false
|
|
25
|
+
end
|
|
16
26
|
|
|
17
|
-
|
|
18
|
-
def self.parse(raw)
|
|
19
|
-
return false
|
|
20
|
-
end
|
|
27
|
+
private
|
|
21
28
|
|
|
22
|
-
|
|
29
|
+
# Automatically adds the name of the parser class to the server_type field
|
|
30
|
+
def emit_entry(raw, **extra)
|
|
31
|
+
Net::FTP::List::Entry.new raw, **extra, server_type: to_s.split('::').pop
|
|
32
|
+
end
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
def parse_time(str, timezone:, format: nil)
|
|
35
|
+
case timezone
|
|
36
|
+
when :utc
|
|
37
|
+
(format ? DateTime.strptime(str, format) : DateTime.parse(str)).to_time
|
|
38
|
+
when :local
|
|
39
|
+
format ? Time.strptime(str, format) : Time.parse(str)
|
|
40
|
+
else
|
|
41
|
+
raise ArgumentError, "invalid timezone #{timezone}, only :utc and :local are allowed"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
27
44
|
end
|
|
28
45
|
end
|
data/lib/net/ftp/list/rumpus.rb
CHANGED
|
@@ -6,28 +6,27 @@ require 'net/ftp/list/parser'
|
|
|
6
6
|
# drwxr-xr-x folder 0 Nov 30 10:03 houdini
|
|
7
7
|
# -rw-r--r-- 0 101426 101426 Jun 7 2008 imap with spaces.rb
|
|
8
8
|
class Net::FTP::List::Rumpus < Net::FTP::List::Parser
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
([drwxr-]{10})\s+
|
|
9
|
+
REGEXP = /^
|
|
10
|
+
([drwx-]{10})\s+
|
|
12
11
|
(folder|0\s+\d+)\s+
|
|
13
12
|
(\d+)\s+
|
|
14
13
|
(\w+)\s+
|
|
15
14
|
(\d{1,2})\s+
|
|
16
15
|
(\d{2}:\d{2}|\d{4})\s+
|
|
17
16
|
(.+)
|
|
18
|
-
|
|
17
|
+
$/x.freeze
|
|
19
18
|
|
|
20
19
|
# Parse a Rumpus FTP LIST entry.
|
|
21
|
-
def self.parse(raw)
|
|
20
|
+
def self.parse(raw, timezone: :utc)
|
|
22
21
|
match = REGEXP.match(raw.strip) or return false
|
|
22
|
+
type = match[2] == 'folder' ? :dir : :file
|
|
23
23
|
|
|
24
24
|
emit_entry(
|
|
25
25
|
raw,
|
|
26
|
-
:
|
|
27
|
-
:
|
|
28
|
-
:
|
|
29
|
-
:
|
|
30
|
-
:filesize => match[3].to_i
|
|
26
|
+
basename: match[7],
|
|
27
|
+
mtime: parse_time(match[4..6].join(' '), timezone: timezone),
|
|
28
|
+
type: type,
|
|
29
|
+
filesize: match[3].to_i,
|
|
31
30
|
)
|
|
32
31
|
end
|
|
33
32
|
end
|
data/lib/net/ftp/list/unix.rb
CHANGED
|
@@ -8,83 +8,75 @@ require 'net/ftp/list/parser'
|
|
|
8
8
|
# drwxr-xr-x 4 steve group 4096 Dec 10 20:23 etc
|
|
9
9
|
# -rw-r--r-- 1 root other 531 Jan 29 03:26 README.txt
|
|
10
10
|
class Net::FTP::List::Unix < Net::FTP::List::Parser
|
|
11
|
-
|
|
12
11
|
# Stolen straight from the ASF's commons Java FTP LIST parser library.
|
|
13
12
|
# http://svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/
|
|
14
13
|
REGEXP = %r{
|
|
15
|
-
([
|
|
14
|
+
([pbcdlfmSs-])
|
|
16
15
|
(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\+?\s+
|
|
17
16
|
(?:(\d+)\s+)?
|
|
18
|
-
(\S+)?\s+
|
|
19
|
-
(?:(\S+(?:\s\S+)*)\s+)?
|
|
17
|
+
(\S+)?\s+(\S+)?\s+
|
|
20
18
|
(?:\d+,\s+)?
|
|
21
19
|
(\d+)\s+
|
|
22
20
|
((?:\d+[-/]\d+[-/]\d+)|(?:\S+\s+\S+))\s+
|
|
23
21
|
(\d+(?::\d+)?)\s+
|
|
24
22
|
(\S*)(\s*.*)
|
|
25
|
-
}x
|
|
23
|
+
}x.freeze
|
|
26
24
|
|
|
27
25
|
ONE_YEAR = (60 * 60 * 24 * 365)
|
|
28
26
|
|
|
29
27
|
# Parse a Unix like FTP LIST entries.
|
|
30
|
-
def self.parse(raw)
|
|
28
|
+
def self.parse(raw, timezone: :utc)
|
|
31
29
|
match = REGEXP.match(raw.strip) or return false
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
end
|
|
40
|
-
return false unless dir or symlink or file or device
|
|
30
|
+
type = case match[1]
|
|
31
|
+
when /d/ then :dir
|
|
32
|
+
when /l/ then :symlink
|
|
33
|
+
when /[f-]/ then :file
|
|
34
|
+
when /[psbc]/ then :device
|
|
35
|
+
end
|
|
36
|
+
return false if type.nil?
|
|
41
37
|
|
|
42
38
|
# Don't match on rumpus (which looks very similar to unix)
|
|
43
|
-
return false if match[17].nil?
|
|
39
|
+
return false if match[17].nil? && ((match[15].nil? && (match[16].to_s == 'folder')) || (match[15].to_s == '0'))
|
|
44
40
|
|
|
45
41
|
# TODO: Permissions, users, groups, date/time.
|
|
46
42
|
filesize = match[18].to_i
|
|
47
|
-
|
|
48
43
|
mtime_month_and_day = match[19]
|
|
49
44
|
mtime_time_or_year = match[20]
|
|
50
45
|
|
|
51
46
|
# Unix mtimes specify a 4 digit year unless the data is within the past 180
|
|
52
|
-
# days or so. Future dates always specify a 4 digit year.
|
|
47
|
+
# days or so. Future dates always specify a 4 digit year.
|
|
53
48
|
# If the parsed date, with today's year, could be in the future, then
|
|
54
49
|
# the date must be for the previous year
|
|
55
|
-
mtime_string =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
mtime = Time.parse(mtime_string)
|
|
50
|
+
mtime_string = case mtime_time_or_year
|
|
51
|
+
when /^[0-9]{1,2}:[0-9]{2}$/
|
|
52
|
+
if parse_time("#{mtime_month_and_day} #{Time.now.year}", timezone: timezone) > Time.now
|
|
53
|
+
"#{mtime_month_and_day} #{mtime_time_or_year} #{Time.now.year - 1}"
|
|
54
|
+
else
|
|
55
|
+
"#{mtime_month_and_day} #{mtime_time_or_year} #{Time.now.year}"
|
|
56
|
+
end
|
|
57
|
+
when /^[0-9]{4}$/
|
|
58
|
+
"#{mtime_month_and_day} #{mtime_time_or_year}"
|
|
59
|
+
end
|
|
66
60
|
|
|
61
|
+
mtime = parse_time(mtime_string, timezone: timezone)
|
|
67
62
|
basename = match[21].strip
|
|
68
63
|
|
|
69
64
|
# filenames with spaces will end up in the last match
|
|
70
65
|
basename += match[22] unless match[22].nil?
|
|
71
66
|
|
|
72
67
|
# strip the symlink stuff we don't care about
|
|
73
|
-
if symlink
|
|
74
|
-
basename.sub!(/\s
|
|
75
|
-
symlink_destination =
|
|
68
|
+
if type == :symlink
|
|
69
|
+
basename.sub!(/\s+->(.+)$/, '')
|
|
70
|
+
symlink_destination = Regexp.last_match(1).strip if Regexp.last_match(1)
|
|
76
71
|
end
|
|
77
72
|
|
|
78
73
|
emit_entry(
|
|
79
74
|
raw,
|
|
80
|
-
:
|
|
81
|
-
:
|
|
82
|
-
:
|
|
83
|
-
:
|
|
84
|
-
:
|
|
85
|
-
:basename => basename,
|
|
86
|
-
:symlink_destination => symlink_destination,
|
|
87
|
-
:mtime => mtime
|
|
75
|
+
type: type,
|
|
76
|
+
filesize: filesize,
|
|
77
|
+
basename: basename,
|
|
78
|
+
symlink_destination: symlink_destination,
|
|
79
|
+
mtime: mtime,
|
|
88
80
|
)
|
|
89
81
|
end
|
|
90
82
|
end
|
data/lib/net/ftp/list/unknown.rb
CHANGED
|
@@ -3,12 +3,9 @@ require 'net/ftp/list/parser'
|
|
|
3
3
|
# If all other attempts to parse the entry fail this is the parser that is going to be used.
|
|
4
4
|
# It might be a good idea to fail loudly.
|
|
5
5
|
class Net::FTP::List::Unknown < Net::FTP::List::Parser
|
|
6
|
-
def self.parse(raw)
|
|
7
|
-
if Net::FTP::List.raise_on_failed_server_detection
|
|
8
|
-
raise Net::FTP::List::ParseError, "Could not parse #{raw} since none of the parsers was up to the task"
|
|
9
|
-
end
|
|
6
|
+
def self.parse(raw, **)
|
|
7
|
+
raise Net::FTP::List::ParseError, "Could not parse #{raw} since none of the parsers was up to the task" if Net::FTP::List.raise_on_failed_server_detection
|
|
10
8
|
|
|
11
|
-
emit_entry(raw
|
|
9
|
+
emit_entry(raw)
|
|
12
10
|
end
|
|
13
11
|
end
|
|
14
|
-
|
data/lib/net/ftp/list.rb
CHANGED
|
@@ -21,9 +21,9 @@ module Net::FTP::List
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# Parse a line from FTP LIST responsesa and return a Net::FTP::List::Entry
|
|
24
|
-
def self.parse(*args)
|
|
25
|
-
Parser.
|
|
26
|
-
entry =
|
|
24
|
+
def self.parse(*args, **opts)
|
|
25
|
+
Parser.parsers.each do |parser|
|
|
26
|
+
entry = parser.parse(*args, **opts)
|
|
27
27
|
return entry if entry
|
|
28
28
|
end
|
|
29
29
|
end
|
|
@@ -32,4 +32,3 @@ module Net::FTP::List
|
|
|
32
32
|
class ParseError < RuntimeError
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
|
-
|
data/net-ftp-list.gemspec
CHANGED
|
@@ -1,61 +1,22 @@
|
|
|
1
|
-
# Generated by jeweler
|
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
|
4
|
-
# -*- encoding: utf-8 -*-
|
|
5
|
-
# stub: net-ftp-list 3.2.11 ruby lib
|
|
6
|
-
|
|
7
1
|
Gem::Specification.new do |s|
|
|
8
|
-
s.name =
|
|
9
|
-
s.version =
|
|
2
|
+
s.name = 'net-ftp-list'
|
|
3
|
+
s.version = '3.4.1'
|
|
4
|
+
s.authors = ['Stateless Systems']
|
|
5
|
+
s.email = 'enquiries@statelesssystems.com'
|
|
6
|
+
s.summary = 'Parse FTP LIST command output.'
|
|
7
|
+
s.homepage = 'http://github.com/stateless-systems/net-ftp-list'
|
|
8
|
+
s.license = 'MIT'
|
|
10
9
|
|
|
11
|
-
s.
|
|
12
|
-
s.
|
|
13
|
-
s.
|
|
14
|
-
s.
|
|
15
|
-
s.email = "enquiries@statelesssystems.com".freeze
|
|
16
|
-
s.extra_rdoc_files = [
|
|
17
|
-
"README.txt"
|
|
18
|
-
]
|
|
19
|
-
s.files = [
|
|
20
|
-
".ruby-version",
|
|
21
|
-
"Gemfile",
|
|
22
|
-
"Gemfile.lock",
|
|
23
|
-
"README.txt",
|
|
24
|
-
"Rakefile",
|
|
25
|
-
"VERSION.yml",
|
|
26
|
-
"lib/net/ftp/list.rb",
|
|
27
|
-
"lib/net/ftp/list/entry.rb",
|
|
28
|
-
"lib/net/ftp/list/microsoft.rb",
|
|
29
|
-
"lib/net/ftp/list/netware.rb",
|
|
30
|
-
"lib/net/ftp/list/parser.rb",
|
|
31
|
-
"lib/net/ftp/list/rumpus.rb",
|
|
32
|
-
"lib/net/ftp/list/unix.rb",
|
|
33
|
-
"lib/net/ftp/list/unknown.rb",
|
|
34
|
-
"net-ftp-list.gemspec",
|
|
35
|
-
"test/test_net_ftp_list.rb",
|
|
36
|
-
"test/test_net_ftp_list_entry.rb",
|
|
37
|
-
"test/test_net_ftp_list_microsoft.rb",
|
|
38
|
-
"test/test_net_ftp_list_netware.rb",
|
|
39
|
-
"test/test_net_ftp_list_rumpus.rb",
|
|
40
|
-
"test/test_net_ftp_list_unix.rb"
|
|
41
|
-
]
|
|
42
|
-
s.homepage = "http://github.com/stateless-systems/net-ftp-list".freeze
|
|
43
|
-
s.rubygems_version = "2.7.6".freeze
|
|
44
|
-
s.summary = "Parse FTP LIST command output.".freeze
|
|
10
|
+
s.files = `git ls-files -z`.split("\x0").reject {|f| f.start_with?('test/') }
|
|
11
|
+
s.test_files = `git ls-files -z -- test/*`.split("\x0")
|
|
12
|
+
s.require_paths = ['lib']
|
|
13
|
+
s.required_ruby_version = '>= 2.5'
|
|
45
14
|
|
|
46
|
-
|
|
47
|
-
s.specification_version = 4
|
|
15
|
+
s.add_dependency 'net-ftp'
|
|
48
16
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
s.add_dependency(%q<test-unit>.freeze, [">= 0"])
|
|
55
|
-
end
|
|
56
|
-
else
|
|
57
|
-
s.add_dependency(%q<jeweler>.freeze, [">= 0"])
|
|
58
|
-
s.add_dependency(%q<test-unit>.freeze, [">= 0"])
|
|
59
|
-
end
|
|
17
|
+
s.add_development_dependency 'bigdecimal'
|
|
18
|
+
s.add_development_dependency 'rake'
|
|
19
|
+
s.add_development_dependency 'rdoc'
|
|
20
|
+
s.add_development_dependency 'test-unit'
|
|
21
|
+
s.add_development_dependency 'timecop'
|
|
60
22
|
end
|
|
61
|
-
|
data/test/test_net_ftp_list.rb
CHANGED
|
@@ -2,14 +2,13 @@ require 'test/unit'
|
|
|
2
2
|
require 'net/ftp/list'
|
|
3
3
|
|
|
4
4
|
class TestNetFTPList < Test::Unit::TestCase
|
|
5
|
-
|
|
6
5
|
def test_all
|
|
7
6
|
one = Net::FTP::List.parse('drwxr-xr-x 4 user group 4096 Dec 10 20:23 etc')
|
|
8
7
|
assert_kind_of Net::FTP::List::Entry, one
|
|
9
8
|
assert one.dir?
|
|
10
9
|
assert !one.file?
|
|
11
10
|
|
|
12
|
-
two = Net::FTP::List.parse(
|
|
11
|
+
two = Net::FTP::List.parse('++ unknown garbage +++')
|
|
13
12
|
assert_kind_of Net::FTP::List::Entry, two
|
|
14
13
|
assert two.unknown?
|
|
15
14
|
assert_equal '', two.basename
|
|
@@ -18,7 +17,7 @@ class TestNetFTPList < Test::Unit::TestCase
|
|
|
18
17
|
def test_raise_when_flag_set
|
|
19
18
|
Net::FTP::List.raise_on_failed_server_detection = true
|
|
20
19
|
assert_raise(Net::FTP::List::ParseError) do
|
|
21
|
-
Net::FTP::List.parse(
|
|
20
|
+
Net::FTP::List.parse('++ unknown garbage +++')
|
|
22
21
|
end
|
|
23
22
|
ensure
|
|
24
23
|
Net::FTP::List.raise_on_failed_server_detection = false
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
require 'test/unit'
|
|
2
2
|
require 'net/ftp/list'
|
|
3
|
+
require 'bigdecimal'
|
|
3
4
|
|
|
4
5
|
class TestNetFTPEntry < Test::Unit::TestCase
|
|
5
|
-
|
|
6
6
|
def test_equality
|
|
7
|
-
a = Net::FTP::List::Entry.new('foo1',
|
|
8
|
-
b = Net::FTP::List::Entry.new('foo2',
|
|
9
|
-
c = Net::FTP::List::Entry.new('foo1',
|
|
7
|
+
a = Net::FTP::List::Entry.new('foo1', basename: 'foo1')
|
|
8
|
+
b = Net::FTP::List::Entry.new('foo2', basename: 'foo2')
|
|
9
|
+
c = Net::FTP::List::Entry.new('foo1', basename: 'foo1')
|
|
10
10
|
|
|
11
|
-
assert a.eql?
|
|
11
|
+
assert a.eql?(c)
|
|
12
12
|
assert a.eql? a
|
|
13
13
|
assert !a.eql?(b)
|
|
14
14
|
assert !a.eql?(1)
|
|
15
|
-
assert !a.eql?(1.0)
|
|
15
|
+
assert !a.eql?(BigDecimal('1.0'))
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def test_comparison
|
|
@@ -26,24 +26,24 @@ class TestNetFTPEntry < Test::Unit::TestCase
|
|
|
26
26
|
|
|
27
27
|
assert file2 > file1
|
|
28
28
|
assert file2 >= file1
|
|
29
|
-
assert file2 ==
|
|
29
|
+
assert file2 == Net::FTP::List.parse(raw2)
|
|
30
|
+
assert file2 <= file3
|
|
31
|
+
assert file2 < file3
|
|
32
|
+
assert file2 >= file1
|
|
33
|
+
assert file2 > file1
|
|
34
|
+
assert file2 != file1
|
|
35
|
+
assert file2 != file3
|
|
30
36
|
assert file2 <= file3
|
|
31
37
|
assert file2 < file3
|
|
32
|
-
assert !(file2 < file1)
|
|
33
|
-
assert !(file2 <= file1)
|
|
34
|
-
assert !(file2 == file1)
|
|
35
|
-
assert !(file2 == file3)
|
|
36
|
-
assert !(file2 > file3)
|
|
37
|
-
assert !(file2 >= file3)
|
|
38
38
|
|
|
39
|
-
assert
|
|
40
|
-
assert
|
|
39
|
+
assert file2 >= 2
|
|
40
|
+
assert file2 >= 2.0
|
|
41
41
|
assert file2 <= 2
|
|
42
42
|
assert file2 <= 2.0
|
|
43
43
|
assert file2 == 2
|
|
44
|
-
assert file2 == 2.0
|
|
45
|
-
assert
|
|
46
|
-
assert
|
|
44
|
+
assert file2 == BigDecimal('2.0')
|
|
45
|
+
assert file2 <= 2
|
|
46
|
+
assert file2 <= 2.0
|
|
47
47
|
assert file2 >= 2
|
|
48
48
|
assert file2 >= 2.0
|
|
49
49
|
|
|
@@ -52,7 +52,7 @@ class TestNetFTPEntry < Test::Unit::TestCase
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def test_raise_on_unknown_options
|
|
55
|
-
assert_raise(ArgumentError) { Net::FTP::List::Entry.new(
|
|
55
|
+
assert_raise(ArgumentError) { Net::FTP::List::Entry.new('foo', bar: 'baz') }
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def test_default_values
|
|
@@ -67,7 +67,6 @@ class TestNetFTPEntry < Test::Unit::TestCase
|
|
|
67
67
|
assert !e.file?
|
|
68
68
|
assert !e.symlink?
|
|
69
69
|
assert_kind_of Time, e.mtime
|
|
70
|
-
assert_equal
|
|
70
|
+
assert_equal 'Unknown', e.server_type
|
|
71
71
|
end
|
|
72
|
-
|
|
73
72
|
end
|
|
@@ -2,15 +2,14 @@ require 'test/unit'
|
|
|
2
2
|
require 'net/ftp/list'
|
|
3
3
|
|
|
4
4
|
class TestNetFTPListMicrosoft < Test::Unit::TestCase
|
|
5
|
-
|
|
6
5
|
def setup
|
|
7
6
|
@dir = Net::FTP::List.parse('06-25-07 01:08PM <DIR> etc')
|
|
8
7
|
@file = Net::FTP::List.parse('11-27-07 08:45PM 23437 README.TXT')
|
|
9
8
|
end
|
|
10
9
|
|
|
11
10
|
def test_parse_new
|
|
12
|
-
assert_equal
|
|
13
|
-
assert_equal
|
|
11
|
+
assert_equal 'Microsoft', @dir.server_type, 'LIST M$ directory'
|
|
12
|
+
assert_equal 'Microsoft', @file.server_type, 'LIST M$ directory'
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
def test_parse_dd_mm_yyyy
|
|
@@ -62,12 +61,12 @@ class TestNetFTPListMicrosoft < Test::Unit::TestCase
|
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
def test_rubbish_lines
|
|
65
|
-
assert_instance_of Net::FTP::List::Entry, Net::FTP::List.parse(
|
|
64
|
+
assert_instance_of Net::FTP::List::Entry, Net::FTP::List.parse('++ bah! ++')
|
|
66
65
|
end
|
|
67
66
|
|
|
68
67
|
def test_ruby_microsoft_mtime
|
|
69
|
-
assert_equal
|
|
70
|
-
assert_equal
|
|
68
|
+
assert_equal Time.utc(2007, 6, 25, 13, 8), @dir.mtime
|
|
69
|
+
assert_equal Time.utc(2007, 11, 27, 20, 45), @file.mtime
|
|
71
70
|
end
|
|
72
71
|
|
|
73
72
|
def test_ruby_microsoft_like_dir
|
|
@@ -89,6 +88,6 @@ class TestNetFTPListMicrosoft < Test::Unit::TestCase
|
|
|
89
88
|
|
|
90
89
|
def test_zero_hour
|
|
91
90
|
file = Net::FTP::List.parse('10-15-09 00:34AM <DIR> aspnet_client')
|
|
92
|
-
assert_equal
|
|
91
|
+
assert_equal Time.utc(2009, 10, 15, 0, 34), file.mtime
|
|
93
92
|
end
|
|
94
93
|
end
|
|
@@ -2,20 +2,19 @@ require 'test/unit'
|
|
|
2
2
|
require 'net/ftp/list'
|
|
3
3
|
|
|
4
4
|
class TestNetFTPListNetware < Test::Unit::TestCase
|
|
5
|
-
|
|
6
5
|
def setup
|
|
7
|
-
@dir
|
|
8
|
-
@file
|
|
6
|
+
@dir = Net::FTP::List.parse('d [RWCEAFMS] dpearce 512 Jun 27 23:46 public.www')
|
|
7
|
+
@file = Net::FTP::List.parse('- [RWCEAFMS] dpearce 2767 Jun 22 06:22 about.html', timezone: :local)
|
|
9
8
|
end
|
|
10
9
|
|
|
11
10
|
def test_parse_new
|
|
12
|
-
assert_equal
|
|
13
|
-
assert_equal
|
|
11
|
+
assert_equal 'Netware', @dir.server_type, 'LIST Netware directory'
|
|
12
|
+
assert_equal 'Netware', @file.server_type, 'LIST Netware file'
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
def test_ruby_netware_mtime
|
|
17
|
-
assert_equal
|
|
18
|
-
assert_equal
|
|
16
|
+
assert_equal Time.utc(Time.now.year, 6, 27, 23, 46), @dir.mtime
|
|
17
|
+
assert_equal Time.local(Time.now.year, 6, 22, 6, 22), @file.mtime
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def test_ruby_netware_like_dir
|
|
@@ -34,5 +33,4 @@ class TestNetFTPListNetware < Test::Unit::TestCase
|
|
|
34
33
|
assert_equal 512, @dir.filesize
|
|
35
34
|
assert_equal 2767, @file.filesize
|
|
36
35
|
end
|
|
37
|
-
|
|
38
36
|
end
|
|
@@ -2,20 +2,19 @@ require 'test/unit'
|
|
|
2
2
|
require 'net/ftp/list'
|
|
3
3
|
|
|
4
4
|
class TestNetFTPListRumpus < Test::Unit::TestCase
|
|
5
|
-
|
|
6
5
|
def setup
|
|
7
6
|
@dir = Net::FTP::List.parse 'drwxrwxrwx folder 0 Nov 16 22:12 Alias'
|
|
8
|
-
@file = Net::FTP::List.parse '-rw-r--r-- 0 101426 101426 Jun 7 2008 imap with spaces.rb'
|
|
7
|
+
@file = Net::FTP::List.parse '-rw-r--r-- 0 101426 101426 Jun 7 2008 imap with spaces.rb', timezone: :local
|
|
9
8
|
end
|
|
10
9
|
|
|
11
10
|
def test_parsed
|
|
12
|
-
assert_equal
|
|
13
|
-
assert_equal
|
|
11
|
+
assert_equal 'Rumpus', @dir.server_type, 'LIST Rumpus directory'
|
|
12
|
+
assert_equal 'Rumpus', @file.server_type, 'LIST Rumpus file with spaces'
|
|
14
13
|
end
|
|
15
14
|
|
|
16
15
|
def test_ruby_unix_like_date
|
|
17
|
-
assert_equal Time.
|
|
18
|
-
assert_equal Time.
|
|
16
|
+
assert_equal Time.utc(Time.now.year, 11, 16, 22, 12), @dir.mtime
|
|
17
|
+
assert_equal Time.local(2008, 6, 7), @file.mtime
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def test_dir
|
|
@@ -1,97 +1,110 @@
|
|
|
1
1
|
require 'test/unit'
|
|
2
2
|
require 'net/ftp/list'
|
|
3
|
+
require 'timecop'
|
|
3
4
|
|
|
4
5
|
class TestNetFTPListUnix < Test::Unit::TestCase
|
|
6
|
+
def parse(*args, **opts)
|
|
7
|
+
Net::FTP::List.parse(*args, **opts)
|
|
8
|
+
end
|
|
9
|
+
|
|
5
10
|
def setup
|
|
6
|
-
@dir
|
|
7
|
-
@file =
|
|
8
|
-
@other_dir =
|
|
9
|
-
@spaces =
|
|
10
|
-
@symlink =
|
|
11
|
-
@empty_symlink =
|
|
12
|
-
@
|
|
13
|
-
@
|
|
14
|
-
@
|
|
15
|
-
@
|
|
16
|
-
@
|
|
17
|
-
@
|
|
18
|
-
@
|
|
19
|
-
@
|
|
11
|
+
@dir = parse('drwxr-xr-x 4 user group 4096 Jan 1 00:00 etc')
|
|
12
|
+
@file = parse('-rw-r--r-- 1 root other 531 Dec 31 23:59 README')
|
|
13
|
+
@other_dir = parse('drwxr-xr-x 8 1791 600 4096 Mar 11 07:57 forums')
|
|
14
|
+
@spaces = parse('drwxrwxr-x 2 danial danial 72 May 23 12:52 spaces suck')
|
|
15
|
+
@symlink = parse('lrwxrwxrwx 1 danial danial 4 Oct 30 15:26 bar -> /etc')
|
|
16
|
+
@empty_symlink = parse('lrwxrwxrwx 1 danial danial 4 Oct 30 15:26 foo')
|
|
17
|
+
@block_dev = parse('brw-r----- 1 root disk 1, 0 Apr 13 2006 ram0', timezone: :local)
|
|
18
|
+
@char_dev = parse('crw-rw-rw- 1 root root 1, 3 Apr 13 2006 null')
|
|
19
|
+
@socket_dev = parse('srw-rw-rw- 1 root root 0 Aug 20 14:15 log')
|
|
20
|
+
@pipe_dev = parse('prw-r----- 1 root adm 0 Nov 22 10:30 xconsole')
|
|
21
|
+
@file_no_inodes = parse('-rw-r--r-- foo@utchost foo@utchost 6034 May 14 23:13 index.html')
|
|
22
|
+
@file_today = parse('crw-rw-rw- 1 root root 1, 3 Aug 16 14:28 today.txt')
|
|
23
|
+
@no_user = parse('-rw-rw---- 2786 Jul 7 01:57 README')
|
|
24
|
+
@older_date = parse('-rwxrwxrwx 1 owner group 154112 Feb 15 2008 participando.xls')
|
|
25
|
+
@file_with_date = parse('-rw-r--r-- 1 tig tig 39757835 Jan 26 2014 PSA 23 99 May-2010.pdf')
|
|
20
26
|
end
|
|
21
27
|
|
|
22
28
|
def test_parse_new
|
|
23
|
-
assert_equal
|
|
24
|
-
assert_equal
|
|
25
|
-
assert_equal
|
|
26
|
-
assert_equal
|
|
27
|
-
assert_equal
|
|
28
|
-
assert_equal
|
|
29
|
-
assert_equal
|
|
30
|
-
assert_equal
|
|
31
|
-
assert_equal
|
|
32
|
-
assert_equal
|
|
33
|
-
assert_equal
|
|
34
|
-
assert_equal
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class << self
|
|
39
|
-
def time_travel(time)
|
|
40
|
-
@traveled_to_time = time
|
|
41
|
-
|
|
42
|
-
begin
|
|
43
|
-
yield
|
|
44
|
-
ensure
|
|
45
|
-
@traveled_to_time = nil
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
alias_method :original_now, :now
|
|
50
|
-
def now
|
|
51
|
-
@traveled_to_time || original_now
|
|
52
|
-
end
|
|
53
|
-
end
|
|
29
|
+
assert_equal 'Unix', @dir.server_type, 'LIST unixish directory'
|
|
30
|
+
assert_equal 'Unix', @file.server_type, 'LIST unixish file'
|
|
31
|
+
assert_equal 'Unix', @other_dir.server_type, 'LIST unixish directory'
|
|
32
|
+
assert_equal 'Unix', @spaces.server_type, 'LIST unixish directory with spaces'
|
|
33
|
+
assert_equal 'Unix', @symlink.server_type, 'LIST unixish symlink'
|
|
34
|
+
assert_equal 'Unix', @empty_symlink.server_type, 'LIST unixish symlink'
|
|
35
|
+
assert_equal 'Unix', @block_dev.server_type, 'LIST unix block device'
|
|
36
|
+
assert_equal 'Unix', @char_dev.server_type, 'LIST unix char device'
|
|
37
|
+
assert_equal 'Unix', @socket_dev.server_type, 'LIST unix socket device'
|
|
38
|
+
assert_equal 'Unix', @pipe_dev.server_type, 'LIST unix socket device'
|
|
39
|
+
assert_equal 'Unix', @file_no_inodes.server_type, 'LIST unixish file with no inodes'
|
|
40
|
+
assert_equal 'Unix', @file_today.server_type, 'LIST unixish file'
|
|
41
|
+
assert_equal 'Unix', @no_user.server_type, 'LIST unixish file with no user/group'
|
|
42
|
+
assert_equal 'Unix', @older_date.server_type, 'LIST unixish file with older date'
|
|
43
|
+
assert_equal 'Unix', @file_with_date.server_type, 'LIST unix with file containing a date'
|
|
54
44
|
end
|
|
55
45
|
|
|
56
46
|
# mtimes in the past, same year.
|
|
57
47
|
def test_ruby_unix_like_date_past_same_year
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
assert_equal Time.local(2009, 1, 1), Net::FTP::List.parse(@dir.raw).mtime
|
|
48
|
+
Timecop.freeze(Time.utc(2009, 1, 1)) do
|
|
49
|
+
assert_equal Time.utc(2009, 1, 1), parse(@dir.raw).mtime
|
|
61
50
|
end
|
|
62
|
-
|
|
63
|
-
assert_equal Time.
|
|
51
|
+
Timecop.freeze(Time.utc(2008, 4, 1)) do
|
|
52
|
+
assert_equal Time.utc(2008, 3, 11, 7, 57), parse(@other_dir.raw).mtime
|
|
64
53
|
end
|
|
65
54
|
end
|
|
66
55
|
|
|
67
56
|
# mtimes in the past, previous year
|
|
68
57
|
def test_ruby_unix_like_date_past_previous_year
|
|
69
|
-
|
|
70
|
-
assert_equal Time.
|
|
58
|
+
Timecop.freeze(Time.utc(2008, 2, 4)) do
|
|
59
|
+
assert_equal Time.utc(2007, 10, 30, 15, 26), parse(@symlink.raw).mtime
|
|
71
60
|
end
|
|
72
61
|
end
|
|
73
62
|
|
|
74
63
|
# mtime in the future.
|
|
75
64
|
def test_ruby_unix_like_date_future
|
|
76
|
-
|
|
77
|
-
assert_equal Time.
|
|
65
|
+
Timecop.freeze(Time.utc(2006, 3, 1)) do
|
|
66
|
+
assert_equal Time.utc(2006, 4, 13), parse(@char_dev.raw).mtime
|
|
78
67
|
end
|
|
79
68
|
end
|
|
80
69
|
|
|
81
70
|
# Parsed during a leap year.
|
|
82
71
|
def test_ruby_unix_like_date_leap_year
|
|
83
|
-
|
|
84
|
-
assert_equal Time.
|
|
72
|
+
Timecop.freeze(Time.utc(2012, 1, 2)) do
|
|
73
|
+
assert_equal Time.utc(2011, 10, 30, 15, 26), parse(@symlink.raw).mtime
|
|
85
74
|
end
|
|
86
75
|
end
|
|
87
76
|
|
|
88
77
|
# mtimes today, same year.
|
|
89
78
|
def test_ruby_unix_like_date_today_same_year
|
|
90
|
-
|
|
91
|
-
assert_equal Time.
|
|
79
|
+
Timecop.freeze(Time.utc(2013, 8, 16)) do
|
|
80
|
+
assert_equal Time.utc(2013, 8, 16, 14, 28), parse(@file_today.raw).mtime
|
|
92
81
|
end
|
|
93
82
|
end
|
|
94
83
|
|
|
84
|
+
def test_basename
|
|
85
|
+
assert_equal 'etc', @dir.basename
|
|
86
|
+
assert_equal 'README', @file.basename
|
|
87
|
+
assert_equal 'forums', @other_dir.basename
|
|
88
|
+
assert_equal 'spaces suck', @spaces.basename
|
|
89
|
+
assert_equal 'bar', @symlink.basename
|
|
90
|
+
assert_equal 'foo', @empty_symlink.basename
|
|
91
|
+
assert_equal 'ram0', @block_dev.basename
|
|
92
|
+
assert_equal 'null', @char_dev.basename
|
|
93
|
+
assert_equal 'log', @socket_dev.basename
|
|
94
|
+
assert_equal 'xconsole', @pipe_dev.basename
|
|
95
|
+
assert_equal 'index.html', @file_no_inodes.basename
|
|
96
|
+
assert_equal 'today.txt', @file_today.basename
|
|
97
|
+
assert_equal 'README', @no_user.basename
|
|
98
|
+
assert_equal 'participando.xls', @older_date.basename
|
|
99
|
+
assert_equal 'PSA 23 99 May-2010.pdf', @file_with_date.basename
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_mtime
|
|
103
|
+
assert_equal Time.utc(2008, 2, 15), @older_date.mtime
|
|
104
|
+
assert_equal Time.local(2006, 4, 13), @block_dev.mtime
|
|
105
|
+
assert_equal Time.utc(2014, 1, 26), @file_with_date.mtime
|
|
106
|
+
end
|
|
107
|
+
|
|
95
108
|
def test_ruby_unix_like_dir
|
|
96
109
|
assert_equal 'etc', @dir.basename
|
|
97
110
|
assert @dir.dir?
|
|
@@ -104,7 +117,7 @@ class TestNetFTPListUnix < Test::Unit::TestCase
|
|
|
104
117
|
|
|
105
118
|
def test_ruby_unix_like_symlink
|
|
106
119
|
assert_equal 'bar', @symlink.basename
|
|
107
|
-
assert_equal
|
|
120
|
+
assert_equal '/etc', @symlink.symlink_destination
|
|
108
121
|
assert @symlink.symlink?
|
|
109
122
|
assert !@symlink.dir?
|
|
110
123
|
assert !@symlink.file?
|
|
@@ -152,12 +165,13 @@ class TestNetFTPListUnix < Test::Unit::TestCase
|
|
|
152
165
|
end
|
|
153
166
|
|
|
154
167
|
def test_single_digit_hour
|
|
155
|
-
|
|
168
|
+
Timecop.freeze(Time.utc(2014, 8, 16)) do
|
|
169
|
+
single_digit_hour = nil
|
|
156
170
|
assert_nothing_raised do
|
|
157
|
-
|
|
171
|
+
single_digit_hour = parse('-rw-r--r-- 1 root other 531 Dec 31 3:59 README')
|
|
158
172
|
end
|
|
159
173
|
|
|
160
|
-
assert_equal Time.
|
|
174
|
+
assert_equal Time.utc(2013, 12, 31, 3, 59), single_digit_hour.mtime
|
|
161
175
|
end
|
|
162
176
|
end
|
|
163
177
|
end
|
metadata
CHANGED
|
@@ -1,17 +1,58 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: net-ftp-list
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stateless Systems
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
13
|
+
name: net-ftp
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: bigdecimal
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rake
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rdoc
|
|
15
56
|
requirement: !ruby/object:Gem::Requirement
|
|
16
57
|
requirements:
|
|
17
58
|
- - ">="
|
|
@@ -38,19 +79,30 @@ dependencies:
|
|
|
38
79
|
- - ">="
|
|
39
80
|
- !ruby/object:Gem::Version
|
|
40
81
|
version: '0'
|
|
41
|
-
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: timecop
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
42
96
|
email: enquiries@statelesssystems.com
|
|
43
97
|
executables: []
|
|
44
98
|
extensions: []
|
|
45
|
-
extra_rdoc_files:
|
|
46
|
-
- README.txt
|
|
99
|
+
extra_rdoc_files: []
|
|
47
100
|
files:
|
|
48
|
-
- ".
|
|
101
|
+
- ".github/workflows/test.yml"
|
|
102
|
+
- ".gitignore"
|
|
49
103
|
- Gemfile
|
|
50
|
-
- Gemfile.lock
|
|
51
104
|
- README.txt
|
|
52
105
|
- Rakefile
|
|
53
|
-
- VERSION.yml
|
|
54
106
|
- lib/net/ftp/list.rb
|
|
55
107
|
- lib/net/ftp/list/entry.rb
|
|
56
108
|
- lib/net/ftp/list/microsoft.rb
|
|
@@ -67,9 +119,9 @@ files:
|
|
|
67
119
|
- test/test_net_ftp_list_rumpus.rb
|
|
68
120
|
- test/test_net_ftp_list_unix.rb
|
|
69
121
|
homepage: http://github.com/stateless-systems/net-ftp-list
|
|
70
|
-
licenses:
|
|
122
|
+
licenses:
|
|
123
|
+
- MIT
|
|
71
124
|
metadata: {}
|
|
72
|
-
post_install_message:
|
|
73
125
|
rdoc_options: []
|
|
74
126
|
require_paths:
|
|
75
127
|
- lib
|
|
@@ -77,16 +129,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
77
129
|
requirements:
|
|
78
130
|
- - ">="
|
|
79
131
|
- !ruby/object:Gem::Version
|
|
80
|
-
version: '
|
|
132
|
+
version: '2.5'
|
|
81
133
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
134
|
requirements:
|
|
83
135
|
- - ">="
|
|
84
136
|
- !ruby/object:Gem::Version
|
|
85
137
|
version: '0'
|
|
86
138
|
requirements: []
|
|
87
|
-
|
|
88
|
-
rubygems_version: 2.7.6
|
|
89
|
-
signing_key:
|
|
139
|
+
rubygems_version: 4.0.10
|
|
90
140
|
specification_version: 4
|
|
91
141
|
summary: Parse FTP LIST command output.
|
|
92
|
-
test_files:
|
|
142
|
+
test_files:
|
|
143
|
+
- test/test_net_ftp_list.rb
|
|
144
|
+
- test/test_net_ftp_list_entry.rb
|
|
145
|
+
- test/test_net_ftp_list_microsoft.rb
|
|
146
|
+
- test/test_net_ftp_list_netware.rb
|
|
147
|
+
- test/test_net_ftp_list_rumpus.rb
|
|
148
|
+
- test/test_net_ftp_list_unix.rb
|
data/.ruby-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.0.0-p451
|
data/Gemfile.lock
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
GEM
|
|
2
|
-
remote: https://rubygems.org/
|
|
3
|
-
specs:
|
|
4
|
-
addressable (2.4.0)
|
|
5
|
-
builder (3.2.3)
|
|
6
|
-
descendants_tracker (0.0.4)
|
|
7
|
-
thread_safe (~> 0.3, >= 0.3.1)
|
|
8
|
-
faraday (0.9.2)
|
|
9
|
-
multipart-post (>= 1.2, < 3)
|
|
10
|
-
git (1.3.0)
|
|
11
|
-
github_api (0.16.0)
|
|
12
|
-
addressable (~> 2.4.0)
|
|
13
|
-
descendants_tracker (~> 0.0.4)
|
|
14
|
-
faraday (~> 0.8, < 0.10)
|
|
15
|
-
hashie (>= 3.4)
|
|
16
|
-
mime-types (>= 1.16, < 3.0)
|
|
17
|
-
oauth2 (~> 1.0)
|
|
18
|
-
hashie (3.5.7)
|
|
19
|
-
highline (1.7.10)
|
|
20
|
-
jeweler (2.3.9)
|
|
21
|
-
builder
|
|
22
|
-
bundler
|
|
23
|
-
git (>= 1.2.5)
|
|
24
|
-
github_api (~> 0.16.0)
|
|
25
|
-
highline (>= 1.6.15)
|
|
26
|
-
nokogiri (>= 1.5.10)
|
|
27
|
-
psych
|
|
28
|
-
rake
|
|
29
|
-
rdoc
|
|
30
|
-
semver2
|
|
31
|
-
jwt (1.5.6)
|
|
32
|
-
mime-types (2.99.3)
|
|
33
|
-
mini_portile2 (2.3.0)
|
|
34
|
-
multi_json (1.13.1)
|
|
35
|
-
multi_xml (0.6.0)
|
|
36
|
-
multipart-post (2.0.0)
|
|
37
|
-
nokogiri (1.8.2)
|
|
38
|
-
mini_portile2 (~> 2.3.0)
|
|
39
|
-
oauth2 (1.4.0)
|
|
40
|
-
faraday (>= 0.8, < 0.13)
|
|
41
|
-
jwt (~> 1.0)
|
|
42
|
-
multi_json (~> 1.3)
|
|
43
|
-
multi_xml (~> 0.5)
|
|
44
|
-
rack (>= 1.2, < 3)
|
|
45
|
-
power_assert (1.1.1)
|
|
46
|
-
psych (3.0.2)
|
|
47
|
-
rack (2.0.4)
|
|
48
|
-
rake (12.3.1)
|
|
49
|
-
rdoc (6.0.3)
|
|
50
|
-
semver2 (3.4.2)
|
|
51
|
-
test-unit (3.2.7)
|
|
52
|
-
power_assert
|
|
53
|
-
thread_safe (0.3.6)
|
|
54
|
-
|
|
55
|
-
PLATFORMS
|
|
56
|
-
ruby
|
|
57
|
-
|
|
58
|
-
DEPENDENCIES
|
|
59
|
-
jeweler
|
|
60
|
-
test-unit
|
|
61
|
-
|
|
62
|
-
BUNDLED WITH
|
|
63
|
-
1.16.1
|