summaryrefslogtreecommitdiff
path: root/lib/byteback/restore.rb
blob: 7055be3ee9214798a6b682a16d73a286654241f2 (plain)
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
require 'byteback/restore_file'

module Byteback

  class Restore

    def self.find(byteback_root, snapshot, paths)
      x = Byteback::Restore.new(byteback_root)
      x.snapshot = snapshot
      x.find(paths)
      return x
    end

    #
    # This takes a string or array of strings as an argument, and QP encodes
    # each argument.  This is for safe parsing of spaces etc at the remote end.
    #
    # Returns an array of encoded strings.
    #
    def self.encode_args(args)
      [args].flatten.collect{|s| [s].pack("M").gsub(" ","=20").gsub("=\n","")}
    end

    #
    # This takes a string or array of strings, each of which is quoted
    # printable, and unpacks it.
    #
    # Returns an array of decoded strings.
    #
    def self.decode_args(args)
      [args].flatten.collect{|s| (s + "=\n").unpack("M")}.flatten
    end

    def initialize(byteback_root)
      # 
      # We use expand_path here to make sure we have a full path, with no
      # trailing slash.
      #
      @byteback_root = File.expand_path(byteback_root)
      @now     = Time.now
      @snapshot = "*"
      @results = []
    end

    def snapshot=(r)
      if r =~ /^[a-z0-9:\+\*\-]+$/i
        @snapshot = r
      else
        puts "*** Warning: Bad snapshot #{r.inspect}"
      end
    end

    def results
      @results
    end

    def find(paths, opts = {})
      results = []
      #
      # Make sure we've an array, and that we get rid of any ".." nonsense.
      #
      paths = [paths].flatten.collect{|p| File.expand_path(p, "/")}
      seen  = []

      @results = paths.collect do |path|
        Dir.glob(File.expand_path(File.join(@byteback_root, @snapshot, path))).collect do |f|
          Byteback::RestoreFile.new(f, @byteback_root, @now)
        end
      end.flatten

      #
      # If we want an unpruned list, return it now.
      #
      if opts == true or (opts.is_a?(Hash) and opts[:verbose])
        @results = @results.sort{|a,b| [a.path, a.snapshot_time] <=> [b.path, b.snapshot_time]}
        return @results
      end

      @results = @results.sort{|a,b| [a.path, b.snapshot_time] <=> [b.path, a.snapshot_time]}
      pruned_results = []

      @results.each do |r|
        if (opts.is_a?(Hash) and opts[:all])
          pruned_results << r unless pruned_results.include?(r)
        else
          pruned_results << r unless pruned_results.any?{|pr| pr.path == r.path}
        end
      end

      @results = pruned_results
    end

    def list
      heading = %w(snapshot modestring size uid gid mtime path)
      listings = [heading]
      @results.sort.each do |r|
        listing = heading.collect{|m| r.__send__(m.to_sym).to_s }
        if r.symlink?
          listing[-1] << " -> "+r.readlink
        end
        listings << listing
      end

      field_sizes = [0]*heading.length

      listings.each do |fields|
        fields.each_with_index do |field, i|
          field_sizes[i] = (field_sizes[i] > field.length) ? field_sizes[i] : field.length
        end
      end

      fmt = field_sizes.collect{|i| "%-#{i}.#{i}s"}.join(" ")

      bar = "-"*field_sizes.inject(field_sizes.length){|m,s| m+=s}

      output = []
      listings.each do |fields|
        output << sprintf(fmt, *fields)
        if bar
          output << bar
          bar = nil
        end
      end

      return output.join("\n")
    end

  end
end