1class File < IO
2  attr_accessor :path
3
4  def initialize(fd_or_path, mode = "r", perm = 0666)
5    if fd_or_path.kind_of? Fixnum
6      super(fd_or_path, mode)
7    else
8      @path = fd_or_path
9      fd = IO.sysopen(@path, mode, perm)
10      super(fd, mode)
11    end
12  end
13
14  def self.join(*names)
15    return "" if names.empty?
16
17    names.map! do |name|
18      case name
19      when String
20        name
21      when Array
22        if names == name
23          raise ArgumentError, "recursive array"
24        end
25        join(*name)
26      else
27        raise TypeError, "no implicit conversion of #{name.class} into String"
28      end
29    end
30
31    return names[0] if names.size == 1
32
33    if names[0][-1] == File::SEPARATOR
34      s = names[0][0..-2]
35    else
36      s = names[0].dup
37    end
38
39    (1..names.size-2).each { |i|
40      t = names[i]
41      if t[0] == File::SEPARATOR and t[-1] == File::SEPARATOR
42        t = t[1..-2]
43      elsif t[0] == File::SEPARATOR
44        t = t[1..-1]
45      elsif t[-1] == File::SEPARATOR
46        t = t[0..-2]
47      end
48      s += File::SEPARATOR + t if t != ""
49    }
50    if names[-1][0] == File::SEPARATOR
51      s += File::SEPARATOR + names[-1][1..-1]
52    else
53      s += File::SEPARATOR + names[-1]
54    end
55    s
56  end
57
58  def self._concat_path(path, base_path)
59    if path[0] == "/" || path[1] == ':' # Windows root!
60      expanded_path = path
61    elsif path[0] == "~"
62      if (path[1] == "/" || path[1] == nil)
63        dir = path[1, path.size]
64        home_dir = _gethome
65
66        unless home_dir
67          raise ArgumentError, "couldn't find HOME environment -- expanding '~'"
68        end
69
70        expanded_path = home_dir
71        expanded_path += dir if dir
72        expanded_path += "/"
73      else
74        splitted_path = path.split("/")
75        user = splitted_path[0][1, splitted_path[0].size]
76        dir = "/" + splitted_path[1, splitted_path.size].join("/")
77
78        home_dir = _gethome(user)
79
80        unless home_dir
81          raise ArgumentError, "user #{user} doesn't exist"
82        end
83
84        expanded_path = home_dir
85        expanded_path += dir if dir
86        expanded_path += "/"
87      end
88    else
89      expanded_path = _concat_path(base_path, _getwd)
90      expanded_path += "/" + path
91    end
92
93    expanded_path
94  end
95
96  def self.expand_path(path, default_dir = '.')
97    expanded_path = _concat_path(path, default_dir)
98    drive_prefix = ""
99    if File::ALT_SEPARATOR && expanded_path.size > 2 &&
100        ("A".."Z").include?(expanded_path[0].upcase) && expanded_path[1] == ":"
101      drive_prefix = expanded_path[0, 2]
102      expanded_path = expanded_path[2, expanded_path.size]
103    end
104    expand_path_array = []
105    if File::ALT_SEPARATOR && expanded_path.include?(File::ALT_SEPARATOR)
106      expanded_path.gsub!(File::ALT_SEPARATOR, '/')
107    end
108    while expanded_path.include?('//')
109      expanded_path = expanded_path.gsub('//', '/')
110    end
111
112    if expanded_path != "/"
113      expanded_path.split('/').each do |path_token|
114        if path_token == '..'
115          if expand_path_array.size > 1
116            expand_path_array.pop
117          end
118        elsif path_token == '.'
119          # nothing to do.
120        else
121          expand_path_array << path_token
122        end
123      end
124
125      expanded_path = expand_path_array.join("/")
126      if expanded_path.empty?
127        expanded_path = '/'
128      end
129    end
130    if drive_prefix.empty?
131      expanded_path
132    else
133      drive_prefix + expanded_path.gsub("/", File::ALT_SEPARATOR)
134    end
135  end
136
137  def self.foreach(file)
138    if block_given?
139      self.open(file) do |f|
140        f.each {|l| yield l}
141      end
142    else
143      return self.new(file)
144    end
145  end
146
147  def self.directory?(file)
148    FileTest.directory?(file)
149  end
150
151  def self.exist?(file)
152    FileTest.exist?(file)
153  end
154
155  def self.exists?(file)
156    FileTest.exists?(file)
157  end
158
159  def self.file?(file)
160    FileTest.file?(file)
161  end
162
163  def self.pipe?(file)
164    FileTest.pipe?(file)
165  end
166
167  def self.size(file)
168    FileTest.size(file)
169  end
170
171  def self.size?(file)
172    FileTest.size?(file)
173  end
174
175  def self.socket?(file)
176    FileTest.socket?(file)
177  end
178
179  def self.symlink?(file)
180    FileTest.symlink?(file)
181  end
182
183  def self.zero?(file)
184    FileTest.zero?(file)
185  end
186
187  def self.extname(filename)
188    fname = self.basename(filename)
189    return '' if fname[0] == '.' || fname.index('.').nil?
190    ext = fname.split('.').last
191    ext.empty? ? '' : ".#{ext}"
192  end
193
194  def self.path(filename)
195    if filename.kind_of?(String)
196      filename
197    elsif filename.respond_to?(:to_path)
198      filename.to_path
199    else
200      raise TypeError, "no implicit conversion of #{filename.class} into String"
201    end
202  end
203end
204