Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / ftools.rb
blob5f082331fbde12b24b9544701170df90fda87b80
1
2 # = ftools.rb: Extra tools for the File class
4 # Author:: WATANABE, Hirofumi
5 # Documentation:: Zachary Landau
7 # This library can be distributed under the terms of the Ruby license.
8 # You can freely distribute/modify this library.
10 # It is included in the Ruby standard library.
12 # == Description
14 # ftools adds several (class, not instance) methods to the File class, for
15 # copying, moving, deleting, installing, and comparing files, as well as
16 # creating a directory path.  See the File class for details.
18 # FileUtils contains all or nearly all the same functionality and more, and
19 # is a recommended option over ftools 
21 # When you
23 #   require 'ftools'
25 # then the File class aquires some utility methods for copying, moving, and
26 # deleting files, and more.
28 # See the method descriptions below, and consider using FileUtils as it is
29 # more comprehensive.
31 class File
32 end
34 class << File
36   BUFSIZE = 8 * 1024
38   #
39   # If +to+ is a valid directory, +from+ will be appended to +to+, adding
40   # and escaping backslashes as necessary. Otherwise, +to+ will be returned.
41   # Useful for appending +from+ to +to+ only if the filename was not specified
42   # in +to+. 
43   #
44   def catname(from, to)
45     if directory? to
46       join to.sub(%r([/\\]$), ''), basename(from)
47     else
48       to
49     end
50   end
52   #
53   # Copies a file +from+ to +to+. If +to+ is a directory, copies +from+
54   # to <tt>to/from</tt>.
55   #
56   def syscopy(from, to)
57     to = catname(from, to)
59     fmode = stat(from).mode
60     tpath = to
61     not_exist = !exist?(tpath)
63     from = open(from, "rb")
64     to = open(to, "wb")
66     begin
67       while true
68         to.syswrite from.sysread(BUFSIZE)
69       end
70     rescue EOFError
71       ret = true
72     rescue
73       ret = false
74     ensure
75       to.close
76       from.close
77     end
78     chmod(fmode, tpath) if not_exist
79     ret
80   end
82   #
83   # Copies a file +from+ to +to+ using #syscopy. If +to+ is a directory,
84   # copies +from+ to <tt>to/from</tt>. If +verbose+ is true, <tt>from -> to</tt>
85   # is printed.
86   #
87   def copy(from, to, verbose = false)
88     $stderr.print from, " -> ", catname(from, to), "\n" if verbose
89     syscopy from, to
90   end
92   alias cp copy
94   #
95   # Moves a file +from+ to +to+ using #syscopy. If +to+ is a directory,
96   # copies from +from+ to <tt>to/from</tt>. If +verbose+ is true, <tt>from ->
97   # to</tt> is printed.
98   #
99   def move(from, to, verbose = false)
100     to = catname(from, to)
101     $stderr.print from, " -> ", to, "\n" if verbose
103     if RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/ and file? to
104       unlink to
105     end
106     fstat = stat(from)
107     begin
108       rename from, to
109     rescue
110       begin
111         symlink readlink(from), to and unlink from
112       rescue
113         from_stat = stat(from)
114         syscopy from, to and unlink from
115         utime(from_stat.atime, from_stat.mtime, to)
116         begin
117           chown(fstat.uid, fstat.gid, to)
118         rescue
119         end
120       end
121     end
122   end
124   alias mv move
126   #
127   # Returns +true+ if and only if the contents of files +from+ and +to+ are
128   # identical. If +verbose+ is +true+, <tt>from <=> to</tt> is printed.
129   #
130   def compare(from, to, verbose = false)
131     $stderr.print from, " <=> ", to, "\n" if verbose
133     return false if stat(from).size != stat(to).size
135     from = open(from, "rb")
136     to = open(to, "rb")
138     ret = false
139     fr = tr = ''
141     begin
142       while fr == tr
143         fr = from.read(BUFSIZE)
144         if fr
145           tr = to.read(fr.size)
146         else
147           ret = to.read(BUFSIZE)
148           ret = !ret || ret.length == 0
149           break
150         end
151       end
152     rescue
153       ret = false
154     ensure
155       to.close
156       from.close
157     end
158     ret
159   end
161   alias cmp compare
163   #
164   # Removes a list of files. Each parameter should be the name of the file to
165   # delete. If the last parameter isn't a String, verbose mode will be enabled.
166   # Returns the number of files deleted.
167   #
168   def safe_unlink(*files)
169     verbose = if files[-1].is_a? String then false else files.pop end
170     files.each do |file|
171       begin
172         unlink file
173         $stderr.print "removing ", file, "\n" if verbose
174       rescue Errno::EACCES # for Windows
175         continue if symlink? file
176         begin
177           mode = stat(file).mode
178           o_chmod mode | 0200, file
179           unlink file
180           $stderr.print "removing ", file, "\n" if verbose
181         rescue
182           o_chmod mode, file rescue nil
183         end
184       rescue
185       end
186     end
187   end
189   alias rm_f safe_unlink
191   #
192   # Creates a directory and all its parent directories.
193   # For example,
194   #
195   #     File.makedirs '/usr/lib/ruby'
196   #
197   # causes the following directories to be made, if they do not exist.
198   #     * /usr
199   #     * /usr/lib
200   #     * /usr/lib/ruby
201   #
202   # You can pass several directories, each as a parameter. If the last
203   # parameter isn't a String, verbose mode will be enabled.
204   #
205   def makedirs(*dirs)
206     verbose = if dirs[-1].is_a? String then false else dirs.pop end
207     mode = 0755
208     for dir in dirs
209       parent = dirname(dir)
210       next if parent == dir or directory? dir
211       makedirs parent unless directory? parent
212       $stderr.print "mkdir ", dir, "\n" if verbose
213       if basename(dir) != ""
214         begin
215           Dir.mkdir dir, mode
216         rescue SystemCallError
217           raise unless directory? dir
218         end
219       end
220     end
221   end
223   alias mkpath makedirs
225   alias o_chmod chmod
227   vsave, $VERBOSE = $VERBOSE, false
229   #
230   # Changes permission bits on +files+ to the bit pattern represented
231   # by +mode+. If the last parameter isn't a String, verbose mode will
232   # be enabled.
233   #
234   #   File.chmod 0755, 'somecommand'
235   #   File.chmod 0644, 'my.rb', 'your.rb', true
236   #
237   def chmod(mode, *files)
238     verbose = if files[-1].is_a? String then false else files.pop end
239     $stderr.printf "chmod %04o %s\n", mode, files.join(" ") if verbose
240     o_chmod mode, *files
241   end
242   $VERBOSE = vsave
244   #
245   # If +src+ is not the same as +dest+, copies it and changes the permission
246   # mode to +mode+. If +dest+ is a directory, destination is <tt>dest/src</tt>.
247   # If +mode+ is not set, default is used. If +verbose+ is set to true, the
248   # name of each file copied will be printed.
249   #
250   def install(from, to, mode = nil, verbose = false)
251     to = catname(from, to)
252     unless exist? to and cmp from, to
253       safe_unlink to if exist? to
254       cp from, to, verbose
255       chmod mode, to, verbose if mode
256     end
257   end
261 # vi:set sw=2: