tms 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ pkg
3
+ doc
4
+ *.gem
5
+ *.swp
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Boba Fat
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,11 @@
1
+ = tms
2
+
3
+ Time Machine Status
4
+
5
+ View avaliable Time Machine backups and show diff
6
+
7
+ Name and idea from http://www.fernlightning.com/doku.php?id=software:misc:tms
8
+
9
+ == Copyright
10
+
11
+ Copyright (c) 2010 Boba Fat. See LICENSE for details.
@@ -0,0 +1,30 @@
1
+ begin
2
+ require 'jeweler'
3
+
4
+ name = 'tms'
5
+
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = name
8
+ gem.summary = %Q{Time Machine Status}
9
+ gem.description = %Q{View avaliable Time Machine backups and show diff}
10
+ gem.homepage = "http://github.com/toy/#{name}"
11
+ gem.authors = ["Boba Fat"]
12
+ gem.add_dependency 'colored'
13
+ gem.add_dependency 'xattr'
14
+ gem.add_dependency 'mutter'
15
+ end
16
+
17
+ Jeweler::GemcutterTasks.new
18
+
19
+ require 'pathname'
20
+ desc "Replace system gem with symlink to this folder"
21
+ task 'ghost' do
22
+ gem_path = Pathname(Gem.searcher.find(name).full_gem_path)
23
+ current_path = Pathname('.').expand_path
24
+ system('rm', '-r', gem_path)
25
+ system('ln', '-s', current_path, gem_path)
26
+ end
27
+
28
+ rescue LoadError
29
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
30
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/bin/tms ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'tms'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'tms'
8
+ end
9
+
10
+ case ARGV.length
11
+ when 0
12
+ Tms.list
13
+ when 1
14
+ id = ARGV[0].to_i
15
+ Tms.diff(id - 1, id)
16
+ when 2
17
+ Tms.diff(ARGV[0].to_i, ARGV[1].to_i)
18
+ else
19
+ abort 'Max two arguments'
20
+ end
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+
3
+ if RUBY_PLATFORM.downcase.include?("darwin")
4
+ with_ldflags($LDFLAGS + ' -framework Foundation'){ true }
5
+ else
6
+ with_ldflags($LIBS + ' -lCoreFoundation'){ true }
7
+ end
8
+
9
+ create_makefile('tms')
@@ -0,0 +1,41 @@
1
+ #include "ruby.h"
2
+ #include <CoreServices/CoreServices.h>
3
+
4
+ static VALUE backup_volume(VALUE self){
5
+ OSStatus status = pathTooLongErr;
6
+ char *path;
7
+ size_t pathLength = 256;
8
+
9
+ CFDataRef aliasData;
10
+ AliasHandle alias;
11
+ FSRef fs;
12
+ Boolean wasChanged;
13
+
14
+ aliasData = CFPreferencesCopyAppValue(CFSTR("BackupAlias"), CFSTR("com.apple.TimeMachine"));
15
+ if (aliasData) {
16
+ if (noErr == PtrToHand(CFDataGetBytePtr(aliasData), (Handle *)&alias, CFDataGetLength(aliasData))) {
17
+ if (noErr == FSResolveAlias(NULL, alias, &fs, &wasChanged)) {
18
+ path = malloc(pathLength);
19
+ while (noErr != (status = FSRefMakePath(&fs, (UInt8*)path, pathLength))) {
20
+ if (pathTooLongErr == status) {
21
+ pathLength += 256;
22
+ path = reallocf(path, pathLength);
23
+ }
24
+ }
25
+ }
26
+ DisposeHandle((Handle)alias);
27
+ }
28
+ CFRelease(aliasData);
29
+ }
30
+
31
+ if (noErr == status) {
32
+ return rb_str_new2(path);
33
+ } else {
34
+ return Qnil;
35
+ }
36
+ }
37
+
38
+ void Init_tms() {
39
+ VALUE cTms = rb_define_module("Tms");
40
+ rb_define_singleton_method(cTms, "backup_volume", backup_volume, 0);
41
+ }
@@ -0,0 +1,69 @@
1
+ require 'pathname'
2
+ require 'colored'
3
+ require 'xattr'
4
+ require 'mutter'
5
+
6
+ class Pathname
7
+ def real_directory?
8
+ directory? && !symlink?
9
+ end
10
+
11
+ def lino
12
+ @ino ||= lstat.ino
13
+ end
14
+
15
+ def postfix
16
+ return '@' if symlink?
17
+ return '/' if directory?
18
+ ''
19
+ end
20
+
21
+ def colored_size(options = {})
22
+ if exist?
23
+ if directory?
24
+ if options[:recursive]
25
+ total = 0
26
+ find do |path|
27
+ total += path.size rescue nil
28
+ end
29
+ Tms::Space.space(total, :color => true)
30
+ else
31
+ ' '
32
+ end
33
+ else
34
+ Tms::Space.space(size, :color => true)
35
+ end
36
+ else
37
+ '!!!!!!'
38
+ end
39
+ end
40
+ end
41
+
42
+ module Tms
43
+ class << self
44
+ def list
45
+ backups = Backup.list
46
+ Mutter::Table.new do
47
+ column :align => :right, :style => :red
48
+ column :align => :right, :style => :blue
49
+ column :align => :right
50
+ column
51
+ end.tap do |table|
52
+ table << ['', '', 'num', 'name']
53
+ backups.each_with_index do |backup, i|
54
+ table << [i, i - backups.length, backup.number, backup.name]
55
+ end
56
+ end.print
57
+ end
58
+
59
+ def diff(a, b)
60
+ backup_a = Backup.list[a] || abort("No backup with id #{a}")
61
+ backup_b = Backup.list[b] || abort("No backup with id #{b}")
62
+ Backup.diff(backup_a, backup_b)
63
+ end
64
+ end
65
+ end
66
+
67
+ require 'tms.so'
68
+ require 'tms/backup'
69
+ require 'tms/space'
@@ -0,0 +1,93 @@
1
+ class Tms::Backup
2
+ class << self
3
+ def backups_dir
4
+ @backups_dir ||= begin
5
+ backup_volume = Tms.backup_volume
6
+ abort 'backup volume not avaliable' if backup_volume.nil?
7
+
8
+ computer_name = `scutil --get ComputerName`.strip
9
+ abort 'can\'t get computer name' unless $?.success?
10
+
11
+ backups_dir = Pathname(backup_volume) + 'Backups.backupdb' + computer_name
12
+ abort "ops backups dir is not a dir" unless backups_dir.directory?
13
+
14
+ backups_dir
15
+ end
16
+ end
17
+
18
+ def list
19
+ @list ||= begin
20
+ backups_dir.children.select do |path|
21
+ path.basename.to_s =~ /^\d{4}-\d{2}-\d{2}-\d{6}$/
22
+ end.map(&method(:new)).sort_by(&:number)
23
+ end
24
+ end
25
+
26
+ def diff(a, b)
27
+ (a.path.children(false) | b.path.children(false)).reject{ |child| child.to_s[0, 1] == '.' }.sort.each do |path|
28
+ compare(a.path + path, b.path + path, Pathname('/') + path)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def compare(a, b, path)
35
+ case
36
+ when !a.exist?
37
+ puts "#{' █'.green} #{b.colored_size(:recursive => true)} #{path}#{b.postfix}"
38
+ when !b.exist?
39
+ puts "#{'█ '.blue} #{a.colored_size(:recursive => true)} #{path}#{a.postfix}"
40
+ when a.ftype != b.ftype
41
+ puts "#{'!!!'.red.bold} #{a.colored_size} #{path} (#{a.ftype}=>#{b.ftype})"
42
+ when a.lino != b.lino
43
+ if a.readable? && b.readable?
44
+ puts "#{'█≠█'.yellow} #{a.colored_size} #{path}#{a.postfix}" unless a.symlink? && a.readlink == b.readlink
45
+ if a.real_directory?
46
+ (a.children(false) | b.children(false)).sort.each do |child|
47
+ compare(a + child, b + child, path + child)
48
+ end
49
+ end
50
+ else
51
+ puts "??? #{path}#{a.postfix}".red.bold
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ attr_reader :path
58
+ def initialize(path)
59
+ @path = path
60
+ end
61
+
62
+ def name
63
+ @name ||= path.basename.to_s
64
+ end
65
+
66
+ def started_at
67
+ @start_date ||= Time.at(xattr.get('com.apple.backupd.SnapshotStartDate').to_i / 1_000_000.0)
68
+ end
69
+ def finished_at
70
+ @finished_at ||= Time.at(xattr.get('com.apple.backupd.SnapshotCompletionDate').to_i / 1_000_000.0)
71
+ end
72
+ def completed_in
73
+ finished_at - started_at
74
+ end
75
+ {
76
+ :state => 'com.apple.backupd.SnapshotState',
77
+ :type => 'com.apple.backupd.SnapshotType',
78
+ :version => 'com.apple.backup.SnapshotVersion',
79
+ :number => 'com.apple.backup.SnapshotNumber',
80
+ }.each do |name, attr|
81
+ class_eval <<-src
82
+ def #{name}
83
+ @#{name} ||= xattr.get('#{attr}').to_i
84
+ end
85
+ src
86
+ end
87
+
88
+ private
89
+
90
+ def xattr
91
+ @xattr ||= Xattr.new(path)
92
+ end
93
+ end
@@ -0,0 +1,39 @@
1
+ module Tms::Space
2
+ SIZE_SYMBOLS = %w[B K M G T P E Z Y].freeze
3
+ COLORS = [].tap do |colors|
4
+ [:white, :black, :yellow, :red].each do |color|
5
+ colors << {:foreground => color}
6
+ colors << {:foreground => color, :extra => :bold}
7
+ end
8
+ colors << {:foreground => :yellow, :extra => :reversed}
9
+ colors << {:foreground => :red, :extra => :reversed}
10
+ end.freeze
11
+
12
+ def space(size, options = {})
13
+ precision = [options[:precision].to_i, 1].max || 2
14
+ length = 4 + precision + (options[:can_be_negative] ? 1 : 0)
15
+
16
+ number = size.to_i
17
+ degree = 0
18
+ while number.abs >= 1000 && degree < SIZE_SYMBOLS.length - 1
19
+ degree += 1
20
+ number /= 1024.0
21
+ end
22
+
23
+ space = "#{(degree == 0 ? number.to_s : "%.#{precision}f" % number).rjust(length)}#{number == 0 ? ' ' : SIZE_SYMBOLS[degree]}"
24
+ if options[:color]
25
+ unless ''.respond_to?(:red)
26
+ require 'toy/fast_gem'
27
+ fast_gem 'colored'
28
+ end
29
+ step = options[:color].is_a?(Hash) && options[:color][:step] || 10
30
+ start = options[:color].is_a?(Hash) && options[:color][:start] || 1
31
+ coef = 10.0 / (step * Math.log(10))
32
+ color = [[Math.log(size) * coef - start, 0].max.to_i, COLORS.length - 1].min rescue 0
33
+ Colored.colorize(space, COLORS[color])
34
+ else
35
+ space
36
+ end
37
+ end
38
+ self.extend self
39
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tms
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Boba Fat
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-09 00:00:00 +03:00
19
+ default_executable: tms
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: colored
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: xattr
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: mutter
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ description: View avaliable Time Machine backups and show diff
64
+ email:
65
+ executables:
66
+ - tms
67
+ extensions:
68
+ - ext/extconf.rb
69
+ extra_rdoc_files:
70
+ - LICENSE
71
+ - README.markdown
72
+ files:
73
+ - .gitignore
74
+ - LICENSE
75
+ - README.markdown
76
+ - Rakefile
77
+ - VERSION
78
+ - bin/tms
79
+ - ext/extconf.rb
80
+ - ext/tms.c
81
+ - lib/tms.rb
82
+ - lib/tms/backup.rb
83
+ - lib/tms/space.rb
84
+ has_rdoc: true
85
+ homepage: http://github.com/toy/tms
86
+ licenses: []
87
+
88
+ post_install_message:
89
+ rdoc_options:
90
+ - --charset=UTF-8
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ hash: 3
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.3.7
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: Time Machine Status
118
+ test_files: []
119
+