Guest User

Untitled

a guest
Mar 3rd, 2018
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.74 KB | None | 0 0
  1. require 'fileutils'testтест
  2. require 'iterator'
  3.  
  4. module Kernel
  5. def Path(str)
  6. Path.new(str)
  7. end
  8. end
  9.  
  10. class String
  11. def to_path(separator="/")
  12. Path.new(self, separator)
  13. end
  14. end
  15.  
  16. # you can use Path same as strings for hash-lookups, but not as keys
  17. # Path is immutable
  18. # FIXME: ::_load, #_dump for Marshal
  19. class Path
  20. TruePattern = Object.new
  21. def TruePattern.===(other); true; end
  22. def TruePattern.=~(other); true; end
  23.  
  24. include Comparable
  25.  
  26. class <<self
  27. # returns the current working directory
  28. def current; new(Dir.getwd); end
  29.  
  30. # returns the path of the current script (ENV["_"] if $0 doesn't exist)
  31. def program
  32. if File.exist?($0)
  33. new($0)
  34. elsif ENV.has_key?("_")
  35. new(ENV["_"])
  36. else
  37. nil
  38. end
  39. end
  40.  
  41. # home environment variable if set, else nil
  42. def home
  43. ENV.has_key?("HOME") ? new(ENV["HOME"].to_str) : nil
  44. end
  45. end
  46.  
  47. # always use "/" as separator, no matter on what platform
  48. def initialize(path, separator="/")
  49. raise ArgumentError, "Illegal null character in path #{path.inspect}" if /\0/ =~ path
  50. @pathname = path.to_str
  51. @separator = separator
  52. @segments = @pathname.scan(/[^#{Regexp.escape(@separator)}]+/)
  53. @top = (@segments.length == 0 ? @pathname : @pathname[0, @pathname.index(@segments[0])])
  54. taint if path.tainted?
  55. end
  56.  
  57. # returns a String
  58. def pathname
  59. @pathname.dup
  60. end
  61.  
  62. # returns a String
  63. def dirname
  64. File.dirname(@pathname)
  65. end
  66.  
  67. # returns a String, if without_suffix is given (can be an array), it is removed
  68. def filename(without_suffix=nil)
  69. File.basename(@pathname, without_suffix)
  70. end
  71. alias name filename
  72.  
  73. def extname
  74. File.extname(@pathname)
  75. end
  76.  
  77. # returns a Path
  78. def path
  79. dup
  80. end
  81.  
  82. # returns the Path of the containing directory
  83. def directory
  84. self.class.new(File.dirname(@pathname), @separator)
  85. end
  86.  
  87. # returns the Path of the last segment, if without_suffix is
  88. # given (can be an array), it is removed
  89. def file(without_suffix=nil)
  90. self.class.new(File.basename(@pathname, without_suffix), @separator)
  91. end
  92.  
  93. # checks whether two paths are refering the same file,
  94. # basically a.same_file?(b) is a short for Path(a).expanded.clean == Path(b).expanded.clean.
  95. # does not access file system, also see real_same?
  96. def same?(path, separator=@separator)
  97. expanded.clean == self.class.new(path, separator).expanded.clean
  98. end
  99.  
  100. # checks if this path (assumed to be a directory) is base to other path
  101. # does not access file system, also see real_include?
  102. def include?(path, separator=@separator)
  103. self.class.new(path, separator).expanded.clean.index(expanded.clean) == 0
  104. end
  105.  
  106. # checks whether two paths are refering the same file,
  107. # basically a.real_same_file?(b) is a short for Path(a).real == Path(b).real.
  108. # accesses file system, also see same?
  109. def real_same?(path, separator=@separator)
  110. real == self.class.new(path, separator).real
  111. end
  112.  
  113. # checks if this path (must be a directory) is base to other path
  114. # accesses file system, also see include?
  115. def real_include?(path, separator=@separator)
  116. directory? && self.class.new(path, separator).real.index(real) == 0
  117. end
  118.  
  119. # Path#+ appends a path fragment to this one to produce a new Path object.
  120. # also see Path#join
  121. #
  122. # p1 = Path("/usr") # <Path /usr>
  123. # p2 = p1 + "bin/ruby" # <Path /usr/bin/ruby>
  124. # p3 = p1 + "../bin/ruby" # <Path /bin/ruby>
  125. # p4 = p1 + "bin/../ruby" # <Path /usr/bin/../ruby>
  126. # p5 = p1 + "/etc/passwd" # raises ArgumentError
  127. #
  128. # This method doesn't access the file system; it is pure string manipulation.
  129. def +(other)
  130. other = other.to_str
  131. segments = other.scan(/[^#{Regexp.escape(@separator)}]+/)
  132. top = (segments.length == 0 ? other : other[0, other.index(segments[0])])
  133.  
  134. raise ArgumentError, "Can't append absolute path #{other.inspect}" unless top.empty?
  135.  
  136. current = @segments.dup
  137. while segments.last =~ /\A\.\.?\z/
  138. current.pop if segments.shift == ".."
  139. end
  140. self.class.new(@top + (current + segments).join(@separator), @separator)
  141. end
  142.  
  143. def join(*others)
  144. self+others.join(@separator)
  145. end
  146.  
  147. # returns start of the path if absolute, else nil
  148. # absolute paths start with either the path separator or <drive> ":" <path sep>
  149. # (windows' "C:\")
  150. def absolute?
  151. if @pathname =~ /\A([^#{Regexp.escape(@separator)}]+:#{Regexp.escape(@separator)})/ ||
  152. @pathname =~ /\A(#{Regexp.escape(@separator)})/ then
  153. $1
  154. else
  155. nil
  156. end
  157. end
  158.  
  159. # returns true or false
  160. def relative?
  161. !absolute?
  162. end
  163.  
  164. # true if file/directory exists and its size is zero or it's entries only . and ..
  165. def empty?
  166. if File.directory?(@pathname)
  167. Dir.entries(@pathname).all? { |x| x == '.' || x == '..' }
  168. else
  169. File.size(@pathname) == 0
  170. end
  171. end
  172.  
  173. # prepends the current directory or home if path starts with ~
  174. def expanded
  175. absolute? ? dup : self.class.new(File.expand_path(@pathname), @separator)
  176. end
  177.  
  178. def relative(to=self.class.current)
  179. to = self.class.new(to.to_str) unless to.class == self.class #to.respond_to?(:to_a) - to_a at the moment is implemented in Object, thus a useless test
  180. return dup if relative? && to == self.class.current
  181. segments = self.absolute.to_a
  182. other = to.absolute.to_a
  183. while other[0] == segments[0] && other.length > 0
  184. other.shift
  185. segments.shift
  186. end
  187. segments.unshift *([".."]*other.length)
  188. self.class.new(segments.join(@separator),@separator)
  189. end
  190.  
  191. # Clean the path simply by resolving and removing excess "." and ".." entries.
  192. # Pure stringmanipulation.
  193. def clean(consider_symlink=false)
  194. if consider_symlink then
  195. cleanpath_conservative
  196. else
  197. cleanpath_aggressive
  198. end
  199. end
  200.  
  201. # cleanpath_aggressive assumes:
  202. # * no symlink
  203. # * all path prefix contained in the path is existing directory
  204. def cleanpath_aggressive
  205. return self.class.new('', @separator) if @pathname == ''
  206. absolute = absolute?
  207. names = []
  208. @segments.each { |name|
  209. next if name == '.'
  210. if name == '..'
  211. if names.empty?
  212. next if absolute
  213. else
  214. if names.last != '..'
  215. names.pop
  216. next
  217. end
  218. end
  219. end
  220. names << name
  221. }
  222. return self.class.new(absolute ? absolute : '.', @separator) if names.empty?
  223. path = absolute ? absolute : ''
  224. path << names.join(@separator)
  225. self.class.new(path, @separator)
  226. end
  227. private :cleanpath_aggressive
  228.  
  229. def cleanpath_conservative
  230. return self.class.new('', @separator) if @pathname == ''
  231. names = @segments.dup
  232. absolute = absolute?
  233. last_dot = names.last == '.'
  234. names.delete('.')
  235. names.shift while names.first == '..' if absolute
  236. return self.class.new(absolute ? absolute : '.', @separator) if names.empty?
  237. path = absolute ? absolute : ''
  238. path << names.join(@separator)
  239. if names.last != '..'
  240. if last_dot
  241. path << @separator+'.'
  242. elsif %r{#{@separator}\z} =~ @pathname
  243. path << @separator
  244. end
  245. end
  246. self.class.new(path, @separator)
  247. end
  248. private :cleanpath_conservative
  249.  
  250. # helper for realpath, get's top + segments (top beeing "" for non absolutes and "/" on *nix,
  251. # something like "C:\" on win or whatever else absolute? returns. segments beeing a list
  252. # of segments besides top
  253. def top_segments #:nodoc:
  254. top = absolute? || ""
  255. if top then
  256. path = @pathname
  257. else
  258. path = self.class.current+@pathname
  259. top = path.absolute? || ""
  260. path = path.to_s
  261. end
  262. segments = path[top.length..-1].scan(/[^#{Regexp.escape(@separator)}]+/)
  263. return top, segments
  264. end
  265.  
  266. # Returns a real (absolute) path of +self+ in the actual filesystem.
  267. # The real path doesn't contain symlinks or useless dots.
  268. # Set resolve_nonexisting to true if you don't want it to raise an error
  269. # if a non existing path is resolved.
  270. def real(resolve_nonexisting=false)
  271. top, unresolved = top_segments
  272. resolved = []
  273. inexistent = []
  274. if resolve_nonexisting
  275. while !File.exist?(top + unresolved.join(@separator)) && !unresolved.empty?
  276. inexistent.unshift unresolved.pop
  277. end
  278. end
  279.  
  280. until unresolved.empty?
  281. case unresolved.last
  282. when '.'
  283. unresolved.pop
  284. when '..'
  285. resolved.unshift unresolved.pop
  286. else
  287. loop_check = {}
  288. while (stat = File.lstat(path = top + unresolved.join(@separator))).symlink?
  289. symlink_id = "#{stat.dev}:#{stat.ino}" #∆ won't work on windows (stat.ino always 0), OTOH, windows doesn't seem to have symlinks
  290. raise Errno::ELOOP.new(path) if loop_check[symlink_id]
  291. loop_check[symlink_id] = true
  292. top, unresolved = top_segments
  293. end
  294. next if (filename = unresolved.pop) == '.'
  295. if filename != '..' && resolved.first == '..'
  296. resolved.shift
  297. else
  298. resolved.unshift filename
  299. end
  300. end #case
  301. end
  302.  
  303. resolved.shift while resolved[0] == '..'
  304. resolved.push *inexistent
  305.  
  306. if resolved.empty? then
  307. self.class.new(top.empty? ? '.' : @separator, @separator)
  308. else
  309. self.class.new(top + resolved.join(@separator), @separator)
  310. end
  311. end
  312.  
  313. # creates a directory and all intermediary directories
  314. def create
  315. FileUtils.mkpath(@pathname)
  316. end
  317.  
  318. # creates a file in path, creates intermediary directories if necessary
  319. # warning, if the file already exists it will be deleted
  320. def create_file(delete_existing=false)
  321. directory.create
  322. delete if exists? and delete_existing
  323. FileUtils.touch(@pathname) unless exists?
  324. end
  325.  
  326. # removes file or directory (and all it's contents)
  327. def delete
  328. if File.directory?(@pathname)
  329. FileUtils.rm_r(@pathname)
  330. else
  331. File.unlink(@pathname)
  332. end
  333. end
  334.  
  335. # yield each line of a file, if no block is given
  336. # returns an Iterator over the lines.
  337. def lines(sep_string=$/, &block)
  338. if block then
  339. File.open(@pathname, "rb") { |fh| fh.each_line(sep_string, &block) }
  340. else
  341. Iterator.new { |iter|
  342. File.open(@pathname, "rb") { |fh|
  343. fh.each_line(sep_string) { |line| iter.yield(line) }
  344. }
  345. }
  346. end
  347. end
  348.  
  349. # yields chunks of size bytes, if no block is given returns
  350. # an Iterator over those chunks.
  351. def chunks(size=4096, &block)
  352. if block then
  353. File.open(@pathname, "rb") { |fh|
  354. while line = fh.read(size)
  355. yield(line)
  356. end
  357. }
  358. else
  359. Iterator.new { |iter|
  360. File.open(@pathname, "rb") { |fh|
  361. while line = fh.read(size)
  362. iter.yield(line)
  363. end
  364. }
  365. }
  366. end
  367. end
  368.  
  369. # See +IO.read+
  370. def read(*args, &block); File.read(@pathname, *args, &block); end
  371.  
  372. # See +IO.open+
  373. def open(*args, &block); File.open(@pathname, *args, &block); end
  374.  
  375. # Write text to file and close it
  376. def write(text); File.open(@pathname, "wb") { |fh| fh.write(text) }; end
  377.  
  378. # Append text to file and close it
  379. def append(text); File.open(@pathname, "ab") { |fh| fh.write(text) }; end
  380.  
  381. # Truncates the file _file_name_ to be at most _integer_ bytes long.
  382. # Not available on all platforms.
  383. def truncate(integer); File.truncate(@pathname, integer); end
  384.  
  385. # See +IO.sysopen+
  386. def sysopen(*args, &block); File.sysopen(@pathname, *args, &block); end
  387.  
  388. # all entries in a directory, except . and ..
  389. # if no block is given it will return an Iterator over the entries
  390. def entries(pattern=nil)
  391. pattern ||= TruePattern
  392. if block_given? then
  393. Dir.entries(@pathname).each { |entry|
  394. next if entry == "." or entry == ".."
  395. path = self+entry
  396. yield(path) if pattern =~ entry
  397. }
  398. else
  399. Iterator.new { |iter|
  400. Dir.entries(@pathname).each { |entry|
  401. next if entry == "." or entry == ".."
  402. path = self+entry
  403. iter.yield(path) if pattern =~ entry
  404. }
  405. }
  406. end
  407. end
  408.  
  409. # if a block is given it will yield all files
  410. # matching the optional pattern, else it will return an Iterator (Enumerable)
  411. # over those.
  412. def files(pattern=nil)
  413. pattern ||= TruePattern
  414. if block_given? then
  415. Dir.entries(@pathname).each { |entry|
  416. path = self+entry
  417. yield(path) if pattern =~ entry and path.file?
  418. }
  419. else
  420. Iterator.new { |iter|
  421. Dir.entries(@pathname).each { |entry|
  422. path = self+entry
  423. iter.yield(path) if pattern =~ entry and path.file?
  424. }
  425. }
  426. end
  427. end
  428.  
  429. # if a block is given it will yield all directories (except . and ..)
  430. # matching the optional pattern, else it will return an Iterator (Enumerable)
  431. # over those.
  432. def directories(pattern=nil)
  433. pattern ||= TruePattern
  434. if block_given? then
  435. Dir.entries(@pathname).each { |entry|
  436. next if entry == "." or entry == ".."
  437. path = self+entry
  438. yield(path) if pattern =~ entry and path.directory?
  439. }
  440. else
  441. Iterator.new { |iter|
  442. Dir.entries(@pathname).each { |entry|
  443. next if entry == "." or entry == ".."
  444. path = self+entry
  445. iter.yield(path) if pattern =~ entry and path.directory?
  446. }
  447. }
  448. end
  449. end
  450.  
  451. # Returns the last access time for the file as a Time object.
  452. # If parameter format is given, strftime is applied on the Time
  453. def accessed(format=nil)
  454. format ? File.atime(@pathname).strftime(format) : File.atime(@pathname)
  455. end
  456.  
  457. # Returns the change time for the file (the time at which directory
  458. # information about the file was changed, not the file itself).
  459. # If parameter format is given, strftime is applied on the Time
  460. def changed(format=nil)
  461. format ? File.ctime(@pathname).strftime(format) : File.ctime(@pathname)
  462. end
  463.  
  464. # Returns the modification time for the named file as a Time object.
  465. # If parameter format is given, strftime is applied on the Time
  466. def modified(format=nil)
  467. format ? File.mtime(@pathname).strftime(format) : File.mtime(@pathname)
  468. end
  469.  
  470. # See <tt>File.utime</tt>. Update the access and modification times.
  471. def utime(atime, mtime) File.utime(atime, mtime, @pathname) end
  472. # Returns a +File::Stat+ object for the named file (see +File::Stat+).
  473. def stat; File.stat(@pathname); end
  474. # Same as +Path::stat+, but does not follow the last symbolic link.
  475. # Instead, reports on the link itself.
  476. def lstat; File.lstat(@pathname); end
  477. # Identifies the type of the named file; the return string is one of
  478. # "+file+", "+directory+", "+characterSpecial+", "+blockSpecial+",
  479. # "+fifo+", "+link+", "+socket+", or "+unknown+".
  480. def type; File.ftype(@pathname); end
  481. # See <tt>File.chmod</tt>. Changes permissions.
  482. def chmod(mode) File.chmod(mode, @pathname) end
  483. # See <tt>File.lchmod</tt>.
  484. def lchmod(mode) File.lchmod(mode, @pathname) end
  485. # See <tt>File.chown</tt>. Change owner and group of file.
  486. def chown(owner, group) File.chown(owner, group, @pathname) end
  487. # See <tt>File.lchown</tt>.
  488. def lchown(owner, group) File.lchown(owner, group, @pathname) end
  489. # See <tt>File.fnmatch?</tt>. Return +true+ if the receiver matches the given
  490. # pattern.
  491. def fnmatch?(pattern, *args) File.fnmatch?(pattern, @pathname, *args) end
  492. # See <tt>File.link</tt>. Creates a hard link.
  493. def link(from) File.link(@pathname, from) end
  494. # See <tt>File.readlink</tt>. Read symbolic link.
  495. def readlink() self.class.new(File.readlink(@pathname), @separator) end
  496. # See <tt>File.symlink</tt>. Creates a symbolic link in from
  497. def symlink(from) File.symlink(@pathname, from) end
  498. # See <tt>File.blockdev?</tt>.
  499. def blockdev?; File.blockdev?(@pathname); end
  500. # See <tt>File.chardev?</tt>.
  501. def chardev?; File.chardev?(@pathname); end
  502. # See <tt>File.executable?</tt>.
  503. def executable?; File.executable?(@pathname); end
  504. # See <tt>File.executable_real?</tt>.
  505. def executable_real?; File.executable_real?(@pathname); end
  506. # See <tt>File.exist?</tt>.
  507. def exist?; File.exist?(@pathname); end
  508. # See <tt>File.exist?</tt>.
  509. def exists?; File.exist?(@pathname); end
  510. # See <tt>File.grpowned?</tt>.
  511. def grpowned?; File.grpowned?(@pathname); end
  512. # See <tt>File.directory?</tt>.
  513. def directory?; File.directory?(@pathname); end
  514. # See <tt>File.file?</tt>.
  515. def file?; File.file?(@pathname); end
  516. # See <tt>File.pipe?</tt>.
  517. def pipe?; File.pipe?(@pathname); end
  518. # See <tt>File.socket?</tt>.
  519. def socket?; File.socket?(@pathname); end
  520. # See <tt>File.readable?</tt>.
  521. def readable?; File.readable?(@pathname); end
  522. # See <tt>File.readable_real?</tt>.
  523. def readable_real?; File.readable_real?(@pathname); end
  524. # See <tt>File.setuid?</tt>.
  525. def setuid?; File.setuid?(@pathname); end
  526. # See <tt>File.setgid?</tt>.
  527. def setgid?; File.setgid?(@pathname); end
  528. # See <tt>File.size</tt>.
  529. def size; File.size(@pathname); end
  530. # See <tt>File.size?</tt>.
  531. def size?; File.size?(@pathname); end
  532. # See <tt>File.sticky?</tt>.
  533. def sticky?; File.sticky?(@pathname); end
  534. # See <tt>File.symlink?</tt>.
  535. def symlink?; File.symlink?(@pathname); end
  536. # See <tt>File.writable?</tt>.
  537. def writable?; File.writable?(@pathname); end
  538. # See <tt>File.writable_real?</tt>.
  539. def writable_real?; File.writable_real?(@pathname); end
  540.  
  541.  
  542. def to_str
  543. @pathname.dup
  544. end
  545.  
  546. def inspect # :nodoc:
  547. "<Path #{@pathname}>"
  548. end
  549. alias to_s inspect
  550.  
  551. def to_path
  552. dup
  553. end
  554.  
  555. def dup
  556. self.class.new(@pathname, @separator)
  557. end
  558.  
  559. def hash # :nodoc:
  560. @pathname.hash
  561. end
  562.  
  563. # compares on string basis, can be compared to a String
  564. def <=>(other)
  565. other.to_str.gsub(@separator, 0.chr) <=> @pathname.gsub(@separator, 0.chr)
  566. end
  567.  
  568. # compares on string basis, can be compared to a String
  569. def ==(other)
  570. other.to_str == @pathname
  571. end
  572.  
  573. def eql?(other) # :nodoc:
  574. other.to_str.eql?(@pathname)
  575. end
  576.  
  577. def taint # :nodoc:
  578. super
  579. @pathname.taint
  580. @segments.taint
  581. @segments.map { |e| e.taint }
  582. self
  583. end
  584.  
  585. def untaint # :nodoc:
  586. super
  587. @pathname.untaint
  588. @segments.untaint
  589. @segments.map { |e| e.untaint }
  590. end
  591. end
Add Comment
Please, Sign In to add comment