From 8a97ffe0941804a1c431c116c1ce4cd33cf14c6c Mon Sep 17 00:00:00 2001 From: shugo Date: Sat, 12 Sep 2015 08:57:17 +0000 Subject: * lib/net/ftp.rb (mlst, mlsd): support new commands MLST and MLSD specified in RFC 3659. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51835 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/net/ftp.rb | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'lib/net/ftp.rb') diff --git a/lib/net/ftp.rb b/lib/net/ftp.rb index 6aa102f6f0..13bab21abe 100644 --- a/lib/net/ftp.rb +++ b/lib/net/ftp.rb @@ -17,6 +17,7 @@ require "socket" require "monitor" require "net/protocol" +require "time" module Net @@ -767,6 +768,79 @@ module Net alias ls list alias dir list + MLSxEntry = Struct.new(:facts, :pathname) + + CASE_DEPENDENT_PARSER = ->(value) { value } + CASE_INDEPENDENT_PARSER = ->(value) { value.downcase } + INTEGER_PARSER = ->(value) { value.to_i } + TIME_PARSER = ->(value) { + t = Time.strptime(value.sub(/\.\d+\z/, "") + "+00:00", + "%Y%m%d%H%M%S%z").utc + fractions = value.slice(/\.(\d+)\z/, 1) + if fractions + t + fractions.to_i.quo(10 ** fractions.size) + else + t + end + } + FACT_PARSERS = Hash.new(CASE_DEPENDENT_PARSER) + FACT_PARSERS["size"] = INTEGER_PARSER + FACT_PARSERS["modify"] = TIME_PARSER + FACT_PARSERS["create"] = TIME_PARSER + FACT_PARSERS["type"] = CASE_INDEPENDENT_PARSER + FACT_PARSERS["unique"] = CASE_DEPENDENT_PARSER + FACT_PARSERS["perm"] = CASE_INDEPENDENT_PARSER + FACT_PARSERS["lang"] = CASE_INDEPENDENT_PARSER + FACT_PARSERS["media-type"] = CASE_INDEPENDENT_PARSER + FACT_PARSERS["charset"] = CASE_INDEPENDENT_PARSER + + def parse_mlsx_entry(entry) + facts, pathname = entry.split(" ") + return MLSxEntry.new( + facts.scan(/(.*?)=(.*?);/).each_with_object({}) { + |(factname, value), h| + name = factname.downcase + h[name] = FACT_PARSERS[name].(value) + }, + pathname) + end + private :parse_mlsx_entry + + # + # Returns data (e.g., size, last modification time, entry type, etc.) + # about the file or directory specified by +pathname+. + # If +pathname+ is omitted, the current directory is assumed. + # + def mlst(pathname = nil) + cmd = pathname ? "MLST #{pathname}" : "MLST" + resp = sendcmd(cmd) + if !resp.start_with?("250") + raise FTPReplyError, resp + end + entry = resp.lines[1].sub(/\A(250-| *)/, "") + return parse_mlsx_entry(entry) + end + + # + # Returns an array of the entries of the directory specified by + # +pathname+. + # Each entry has the facts (e.g., size, last modification time, etc.) + # and the pathname. + # If a block is given, it iterates through the listing. + # If +pathname+ is omitted, the current directory is assumed. + # + def mlsd(pathname = nil, &block) # :yield: entry + cmd = pathname ? "MLSD #{pathname}" : "MLSD" + entries = [] + retrlines(cmd) do |line| + entries << parse_mlsx_entry(line) + end + if block + entries.each(&block) + end + return entries + end + # # Renames a file on the server. # -- cgit v1.2.3