Guest User

Untitled

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