class Gomiko

Attributes

trashdir[R]

Public Class Methods

new(dir: nil, verbose: true) click to toggle source

class NotFoundError < StandardError; end

# File lib/gomiko.rb, line 19
def initialize(dir: nil, verbose: true)
  if dir
    @trashdir = dir
  else
    if ENV['UID'] == 0 # this is needed for 'su -m'
      @trashdir ='/root/.trash' 
    else
      @trashdir = ENV['HOME'] + "/.trash"
    end
  end
  FileUtils.mkdir_p(@trashdir)
end

Public Instance Methods

dir_list() click to toggle source

ls, dir_list return stocked directories.

# File lib/gomiko.rb, line 183
def dir_list
  Dir.glob("#{@trashdir}/*").select { |path|
    FileTest.directory? path
  } .map { |path|
    path.sub(/^#{@trashdir}\//, '').split[0]
  } . sort
end
empty(ids: [], mtime: 0, time: Time.now, verbose: true, io: $stdout) click to toggle source
# File lib/gomiko.rb, line 66
def empty(ids: [], mtime: 0, time: Time.now, verbose: true, io: $stdout)
  if ids.empty?
    tgts = (dir_list + yaml_list.map{|v| v.sub(".yaml", "")}).sort.uniq
  else
    tgts = ids.map {|v|  path2id(v.sub(".yaml", ""))}
  end
  tgts = tgts.select { |v|
    begin
      str2datetime(v) - time < 86400 * mtime
    rescue Errno::ENOENT
      io.puts "Not found: #{v}"
    end
  }
  tgts.map! {|v|  @trashdir + '/' + v}
  if tgts.empty?
    io.puts "No directory was emptied."
  else
    tgts.each do |path|
      ["", ".yaml"].each do |ext|
        new_path = path + ext
        FileUtils.rm_rf(new_path, :verbose => verbose) if File.exist? new_path
      end
    end
  end
end
info(id) click to toggle source

Example of return data: [

236K,
20170623-021233,
[/home/ippei/tmp/a.txt, /home/ippei/tmp/b.txt ]

]

# File lib/gomiko.rb, line 111
def info(id)
  id = path2id id
  cur_trash_dir = Pathname.new(@trashdir) + id
  results = []
  results << `du --human-readable --max-depth=0 #{cur_trash_dir}`.split(' ')[0]
  results << id

  trash_paths = Dir.glob("#{cur_trash_dir}/**/*", File::FNM_DOTMATCH).sort
  trash_paths = trash_paths.select { |t_path| ! /\/\.$/.match t_path } # '/.', で終わるのを除外

  candidates = [] # fo rm target
  results_long = []
  additions = []
  #flag_conflict = false
  flag_include_file = false
  trash_paths.each do |trash_path|
    orig_path = trash_path.sub(/^#{cur_trash_dir}/, '')
    trash_type = ftype_str(trash_path)
    flag_include_file = true unless File.directory? trash_path
    if FileTest.exist? orig_path
      orig_type = ftype_str(orig_path)
      if File.ftype(trash_path) != File.ftype(orig_path)
        #flag_conflict = true
        additions << 'conflict'
        candidates << trash_path
      end
    else
      candidates << trash_path
      orig_type   = ' '
    end
    results_long << [ trash_type, orig_type,
                      trash_path.sub(/^#{cur_trash_dir}/, '') ]
  end
  unless flag_include_file
    additions << 'only directory'
    candidates << trash_paths[-1]
  end

  results <<  YAML.load_file(cur_trash_dir.to_s + ".yaml")['paths'][0]
  ## if no candidate, last file is adopted.
  if trash_paths.empty?
    #results << '(empty)'
    results << []
  else
    additions << 'conflict' if candidates.empty?
    #candidates = candidates.map    {|path|
    #  tmp = path.sub(/^#{cur_trash_dir}/, '')
    #  tmp += '/' if FileTest.directory? path
    #  tmp
    #}
    #results << candidates[0]

    ## output '...' when multiple.
    candidates = candidates.select{|pa| ! pa.include? candidates[0]}
    results[-1] += ' ...' unless candidates.empty?
    #results[2] += ' (conflict)' if flag_conflict
    results[2] += ' (' + additions.join(',') + ')' unless additions.empty?
    results << results_long
  end
  results
end
throw(paths: , time: Time.new, verbose: true, io: $stdout) click to toggle source

If paths includes exist and not exist files, throw all exist file and report not exist files.

# File lib/gomiko.rb, line 34
def throw(paths: , time: Time.new, verbose: true, io: $stdout)
  paths = paths.select do |path|
    flag = FileTest.symlink?(path) || FileTest.exist?(path) # for deadlink
    unless flag
      if verbose
        io.puts "gomiko rm: cannot remove '#{path}': No such file or directory"
      end
    end
    flag
  end
  return if paths.empty?

  trash_subdir = mkdir_time(time)
  File.open(trash_subdir + ".yaml", "w") do |yaml_io|
    YAML.dump( { 'pwd' => ENV["PWD"], 'paths' => paths }, yaml_io)
  end

  paths.each do |path|
    dst = trash_subdir + File.expand_path(path)
    dst_dir = File.dirname dst
    FileUtils.mkdir_p(dst_dir)
    if path == '.'
      io.puts "gomiko rm: failed to remove '.': Invalid argument" if verbose
      next
    else
      FileUtils.mv(path, dst_dir + '/')
      io.puts "mv #{path} #{dst_dir}" if verbose
      File.utime(time, time, trash_subdir)
    end
  end
end
undo(id, verbose: true, io: $stdout) click to toggle source
# File lib/gomiko.rb, line 92
def undo(id, verbose: true, io: $stdout)
  id = path2id id
  fullpath = Pathname.new(@trashdir) + id
  Dir.glob("#{fullpath}/*").sort.each do |path|
    graft(fullpath, '', dst_root: '/', verbose: verbose)
  end
  if Dir.glob("#{fullpath}/**/*").find {|path| FileTest.file? path}
    io.puts "Unable to complete undo: #{fullpath}"
  else
    FileUtils.rm_rf fullpath # risky?
  end
end
yaml_list() click to toggle source

ls, yaml_list return stocked yaml.

# File lib/gomiko.rb, line 175
def yaml_list
  Dir.glob("#{@trashdir}/*.yaml").map { |path|
    path.sub(/^#{@trashdir}\//, '').split[0]
  } . sort
end

Private Instance Methods

ftype_str(path) click to toggle source
# File lib/gomiko.rb, line 239
def ftype_str(path)
  if File.directory? path
    result = '/'
  elsif File.symlink? path
    result = '@'
  elsif File.file? path
    result = '.'
  else
    result = '?'
  end
  result
end
graft(src_root, path, dst_root: '/', verbose: true) click to toggle source

e.g., root_path = ~/.trash/20170123-012345

path      = home/ippei/foo
# File lib/gomiko.rb, line 200
def graft(src_root, path, dst_root: '/', verbose: true)
  src_root = Pathname.new(src_root)
  path     = Pathname.new(path)
  dst_path = Pathname.new(dst_root) + path

  if FileTest.directory? (dst_path)
    Dir.glob("#{src_root + path}/*") do |next_path|
      next_path = Pathname.new(next_path).relative_path_from(src_root)
      graft(src_root, next_path, dst_root: dst_root, verbose: verbose)
    end
  elsif FileTest.exist? (dst_path)
    puts "normal file already exist: #{dst_path}" if verbose
  else
    FileUtils.mv(src_root + path, dst_path, noop: false, verbose: verbose )
  end
  return
end
mkdir_time(time) click to toggle source
# File lib/gomiko.rb, line 218
def mkdir_time(time)
  time_str = time.strftime('/%Y%m%d-%H%M%S')
  dirname = nil

  i = 0
  while ! dirname

    begin
      #self.ls
      try_name = @trashdir + "#{time_str}"
      try_name += "-#{i}" if 1 <= i
      FileUtils.mkdir(try_name)
      dirname = try_name
    rescue Errno::EEXIST
      i += 1
      next
    end
  end
  dirname
end
path2id(path) click to toggle source
# File lib/gomiko.rb, line 252
def path2id(path)
  path.sub(/^#{@trashdir}\//, '')
end
str2datetime(str) click to toggle source
# File lib/gomiko.rb, line 193
def str2datetime(str)
  /(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2})/ =~ str
  Time.new( $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i)
end