File: fileutils.rb

package info (click to toggle)
ruby-fakefs 3.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 544 kB
  • sloc: ruby: 7,622; makefile: 5
file content (329 lines) | stat: -rw-r--r-- 8,897 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# frozen_string_literal: true

module FakeFS
  # FileUtils module
  module FileUtils
    extend self

    def mkdir_p(list, options = {})
      list = [list] unless list.is_a?(Array)
      list.each do |path|
        # FileSystem.add call adds all the necessary parent directories but
        # can't set their mode. Thus, we have to collect created directories
        # here and set the mode later.
        if options[:mode]
          created_dirs = []
          dir = path

          until Dir.exist?(dir)
            created_dirs << dir
            dir = File.dirname(dir)
          end
        end

        FileSystem.add(path, FakeDir.new)

        next unless options[:mode]
        created_dirs.each do |d|
          File.chmod(options[:mode], d)
        end
      end
    end

    alias mkpath mkdir_p
    alias makedirs mkdir_p

    def mkdir(list, _ignored_options = {})
      list = [list] unless list.is_a?(Array)
      list.each do |path|
        parent = path.to_s.split('/')
        parent.pop
        unless parent.join == '' || parent.join == '.' || FileSystem.find(parent.join('/'))
          raise Errno::ENOENT, path.to_s
        end
        raise Errno::EEXIST, path.to_s if FileSystem.find(path)
        FileSystem.add(path, FakeDir.new)
      end
    end

    def rmdir(list, _options = {})
      list = [list] unless list.is_a?(Array)
      list.each do |l|
        parent = l.to_s.split('/')
        parent.pop
        raise Errno::ENOENT, l.to_s unless parent.join == '' || FileSystem.find(parent.join('/'))
        raise Errno::ENOENT, l.to_s unless FileSystem.find(l)
        raise Errno::ENOTEMPTY, l.to_s unless FileSystem.find(l).empty?
        rm(l)
      end
    end

    def rm(list, options = {})
      Array(list).each do |path|
        FileSystem.delete(path) ||
          (!options[:force] && raise(Errno::ENOENT, path.to_s))
      end
    end
    alias rm_r rm
    alias remove rm

    def rm_f(list, options = {})
      rm(list, options.merge(force: true))
    end

    def rm_rf(list, options = {})
      rm_r(list, options.merge(force: true))
    end
    alias rmtree rm_rf
    alias safe_unlink rm_f

    def remove_entry_secure(path, force = false)
      rm_rf(path, force: force)
    end

    def ln_s(target, path, options = {})
      options = { force: false }.merge(options)
      raise(Errno::EEXIST, path.to_s) if FileSystem.find(path) && !options[:force]
      FileSystem.delete(path)

      if !options[:force] && !Dir.exist?(File.dirname(path))
        raise Errno::ENOENT, path.to_s
      end

      FileSystem.add(path, FakeSymlink.new(target))
    end

    def ln_sf(target, path)
      ln_s(target, path, force: true)
    end

    alias symlink ln_s

    def cp(src, dest, options = {})
      raise Errno::ENOTDIR, dest.to_s if src.is_a?(Array) && !File.directory?(dest)

      raise Errno::ENOENT, dest.to_s unless File.exist?(dest) || File.exist?(File.dirname(dest))

      # handle `verbose' flag
      RealFileUtils.cp src, dest, **options.merge(noop: true)

      # handle `noop' flag
      return if options[:noop]

      Array(src).each do |source|
        dst_file = FileSystem.find(dest)
        src_file = FileSystem.find(source)

        raise Errno::ENOENT, source.to_s unless src_file

        if dst_file && File.directory?(dst_file)
          FileSystem.add(
            File.join(dest, File.basename(source)), src_file.entry.clone(dst_file)
          )
        else
          FileSystem.delete(dest)
          FileSystem.add(dest, src_file.entry.clone)
        end
      end

      nil
    end

    alias copy cp

    def copy_file(src, dest, _preserve = false, _dereference = true)
      # Not a perfect match, but similar to what regular FileUtils does.
      cp(src, dest)
    end

    def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
      cp_r(
        src, dest,
        preserve: preserve,
        dereference_root: dereference_root,
        remove_destination: remove_destination
      )
    end

    def cp_r(src, dest, options = {})
      # handle `verbose' flag
      RealFileUtils.cp_r src, dest, **options.merge(noop: true)

      # handle `noop' flag
      return if options[:noop]

      Array(src).each do |source|
        dir = FileSystem.find(source)
        raise Errno::ENOENT, source.to_s unless dir

        new_dir = FileSystem.find(dest)
        raise Errno::EEXIST, dest.to_s if new_dir && !File.directory?(dest)
        raise Errno::ENOENT, dest.to_s if !new_dir && !FileSystem.find(dest.to_s + '/../')

        update_times = proc { |f| f.atime = f.mtime = Time.now }
        # This last bit is a total abuse and should be thought hard
        # about and cleaned up.
        if new_dir
          if src.to_s.end_with?('/.')
            dir.entries.each do |f|
              copy = f.clone(new_dir)
              walk_hierarchy(copy, &update_times) unless options[:preserve]
              new_dir[f.name] = copy
            end
          else
            copy = dir.entry.clone(new_dir)
            walk_hierarchy(copy, &update_times) unless options[:preserve]
            new_dir[dir.name] = copy
          end
        else
          copy = dir.entry.clone
          walk_hierarchy(copy, &update_times) unless options[:preserve]
          FileSystem.add(dest, copy)
        end
      end

      nil
    end

    def mv(src, dest, options = {})
      # handle `verbose' flag
      RealFileUtils.mv src, dest, **options.merge(noop: true)

      # handle `noop' flag
      return if options[:noop]

      Array(src).each do |path|
        if (target = FileSystem.find(path))
          dest_path =
            if File.directory?(dest)
              File.join(dest, File.basename(path))
            else
              dest
            end
          if File.directory?(dest_path)
            raise Errno::EEXIST, dest_path.to_s unless options[:force]
          elsif File.directory?(File.dirname(dest_path))
            FileSystem.delete(dest_path)
            FileSystem.delete(path)
            FileSystem.add(dest_path, target.entry.clone)
          else
            raise Errno::ENOENT, dest_path.to_s unless options[:force]
          end
        else
          raise Errno::ENOENT, path.to_s
        end
      end

      nil
    end

    alias move mv

    def chown(user, group, list, _options = {})
      list = Array(list)
      list.each do |f|
        if File.exist?(f)
          uid =
            if user
              user.to_s =~ /\d+/ ? user.to_i : Etc.getpwnam(user).uid
            end
          gid =
            if group
              group.to_s =~ /\d+/ ? group.to_i : Etc.getgrnam(group).gid
            end
          File.chown(uid, gid, f)
        else
          raise Errno::ENOENT, f.to_s
        end
      end
      list
    end

    def chown_R(user, group, list, _options = {})
      list = Array(list)
      list.each do |file|
        chown(user, group, file)
        [FileSystem.find_with_glob("#{file}/**/**")].flatten.each do |f|
          chown(user, group, f.to_s)
        end
      end
      list
    end

    def chmod(mode, list, _options = {})
      list = Array(list)
      list.each do |f|
        if File.exist?(f)
          File.chmod(mode, f)
        else
          raise Errno::ENOENT, f.to_s
        end
      end
      list
    end

    def chmod_R(mode, list, _options = {})
      list = Array(list)
      list.each do |file|
        chmod(mode, file)
        [FileSystem.find_with_glob("#{file}/**/**")].flatten.each do |f|
          chmod(mode, f.to_s)
        end
      end
      list
    end

    def touch(list, options = {})
      Array(list).each do |f|
        if (fs = FileSystem.find(f))
          now = Time.now
          fs.mtime = options[:mtime] || now
          fs.atime = now
        else
          file = File.open(f, 'w')
          file.close

          if (mtime = options[:mtime])
            fs = FileSystem.find(f)
            fs.mtime = mtime
          end
        end
      end
    end

    def cd(dir, &block)
      FileSystem.chdir(dir, &block)
    end
    alias chdir cd

    def compare_file(file1, file2)
      # we do a strict comparison of both files content
      File.readlines(file1) == File.readlines(file2)
    end

    alias cmp compare_file
    alias identical? compare_file

    def uptodate?(new, old_list)
      return false unless File.exist?(new)
      new_time = File.mtime(new)
      old_list.each do |old|
        if File.exist?(old)
          return false unless new_time > File.mtime(old)
        end
      end
      true
    end

    private

    # Walks through the file system hierarchy recursively, starting from the given entry,
    # and calls the given block with it.
    def walk_hierarchy(entry, &block)
      yield entry
      if entry.is_a? FakeDir
        entry.entries.each { |child| walk_hierarchy(child, &block) }
      end
    end
  end
end