Class: Net::FTP::List::Unix

Inherits:
Parser
  • Object
show all
Defined in:
lib/net/ftp/list/unix.rb

Overview

Parse Unix like FTP LIST entries.

MATCHES

drwxr-xr-x   4 steve    group       4096 Dec 10 20:23 etc
-rw-r--r--   1 root     other        531 Jan 29 03:26 README.txt

Constant Summary collapse

REGEXP =

Stolen straight from the ASF’s commons Java FTP LIST parser library. svn.apache.org/repos/asf/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/

%r{
  ([pbcdlfmSs-])
  (((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\+?\s+
  (?:(\d+)\s+)?
  (\S+)?\s+
  (?:(\S+(?:\s\S+)*)\s+)?
  (?:\d+,\s+)?
  (\d+)\s+
  ((?:\d+[-/]\d+[-/]\d+)|(?:\S+\s+\S+))\s+
  (\d+(?::\d+)?)\s+
  (\S*)(\s*.*)
}x.freeze
ONE_YEAR =
(60 * 60 * 24 * 365)

Class Method Summary collapse

Class Method Details

.parse(raw, timezone: :utc) ⇒ Object

Parse a Unix like FTP LIST entries.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/net/ftp/list/unix.rb', line 29

def self.parse(raw, timezone: :utc)
  match = REGEXP.match(raw.strip) or return false
  type  = case match[1]
          when /d/      then :dir
          when /l/      then :symlink
          when /[f-]/   then :file
          when /[psbc]/ then :device
          end
  return false if type.nil?

  # Don't match on rumpus (which looks very similar to unix)
  return false if match[17].nil? && ((match[15].nil? && (match[16].to_s == 'folder')) || (match[15].to_s == '0'))

  # TODO: Permissions, users, groups, date/time.
  filesize = match[18].to_i
  mtime_month_and_day = match[19]
  mtime_time_or_year = match[20]

  # Unix mtimes specify a 4 digit year unless the data is within the past 180
  # days or so. Future dates always specify a 4 digit year.
  # If the parsed date, with today's year, could be in the future, then
  # the date must be for the previous year
  mtime_string = case mtime_time_or_year
                 when /^[0-9]{1,2}:[0-9]{2}$/
                   if parse_time("#{mtime_month_and_day} #{Time.now.year}", timezone: timezone) > Time.now
                     "#{mtime_month_and_day} #{mtime_time_or_year} #{Time.now.year - 1}"
                   else
                     "#{mtime_month_and_day} #{mtime_time_or_year} #{Time.now.year}"
                   end
                 when /^[0-9]{4}$/
                   "#{mtime_month_and_day} #{mtime_time_or_year}"
                 end

  mtime = parse_time(mtime_string, timezone: timezone)
  basename = match[21].strip

  # filenames with spaces will end up in the last match
  basename += match[22] unless match[22].nil?

  # strip the symlink stuff we don't care about
  if type == :symlink
    basename.sub!(/\s+->(.+)$/, '')
    symlink_destination = Regexp.last_match(1).strip if Regexp.last_match(1)
  end

  emit_entry(
    raw,
    type: type,
    filesize: filesize,
    basename: basename,
    symlink_destination: symlink_destination,
    mtime: mtime,
  )
end