unmac 0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2009 Xavier Noria
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.
21
+
data/README ADDED
@@ -0,0 +1,71 @@
1
+ unmac -- Delete spurious Mac files under a directory or volume
2
+
3
+ Usage:
4
+ unmac [OPTIONS] directory ...
5
+
6
+ Options:
7
+ -h, --help Print this help
8
+ -v, --verbose Show processing
9
+ -p, --pretend Show what would be deleted, but don't do it
10
+ -s, --keep-spotlight Do not delete Spotlight data
11
+ -f, --keep-fsevents Do not delete Time Machine stuff
12
+ -t, --keep-trashes Do not delete volume trashes
13
+ -m, --keep-macosx Do not delete "__MACOSX" directories
14
+ -r, --keep-dsstore Do not delete ".DS_Store" files
15
+ -d, --keep-apple-double Do not delete "._*" ghost files
16
+ -o, --keep-apple-double-orphans Delete "._foo.txt" only if "foo.txt" exists
17
+ -i, --keep-custom-folder-icons Do not delete custom folder icons
18
+
19
+ Description:
20
+ When a Mac copies files to volumes that have a different file system it adds
21
+ some auxiliary files that represent different kinds of metadata. This typically
22
+ happens with memory sticks, network drives, SD cards, etc. Mac archivers like
23
+ zip(1) add special files as well, like "__MACOSX", you'll see them for example
24
+ if you extract a Zip file like that on Windows.
25
+
26
+ The purpose of these special files is to let another Mac rebuild the metadata
27
+ from them, Spotlight integration, etc. But if you are not interested in these
28
+ features your directory or volume just gets cluttered. This utility removes
29
+ all those extra files.
30
+
31
+ What's deleted:
32
+ This is just a summary, for more detailed explanations and pointers see the
33
+ comments in unmacer.rb:
34
+
35
+ * Spotlight leaves a folder called ".Spotlight-V100" in the root directory
36
+ of volumes.
37
+
38
+ * Time Machine relies on a folder called ".fseventsd" in the root directory
39
+ of volumes.
40
+
41
+ * A folder called ".Trashes" in the root directory of volumes stores their
42
+ trashes. Those ones are taken into account by the Trash in the Dock.
43
+
44
+ * Some archivers create auxiliary directories called "__MACOSX".
45
+
46
+ * Finder and Spotlight data related to each folder gets stored in an
47
+ extra file called ".DS_Store".
48
+
49
+ * For each file "foo.txt", its resource fork and some additional stuff
50
+ are stored in an accompaining ghost file called "._foo.txt". The pattern
51
+ is to prepend "._" to the original file name.
52
+
53
+ * If a folder has a custom icon you get a file "Icon^M" (that ^M is a hard CR).
54
+
55
+ Some stuff is only found in the root folder of volumes, unmac looks for them
56
+ in any directory you pass as argument, but not on their subdirectories.
57
+
58
+ Installation:
59
+ unmac is distributed as a Ruby gem. Once you have Ruby and RubyGems installed
60
+ execute
61
+
62
+ gem sources -a http://gems.github.com # you need this only once
63
+ gem install fxn-unmac
64
+
65
+ This program is portable.
66
+
67
+ License:
68
+ See the MIT-LICENSE file included in the distribution.
69
+
70
+ Copyright:
71
+ Copyright (C) 2009 Xavier Noria.
@@ -0,0 +1,7 @@
1
+ task :default => [:test]
2
+
3
+ task :test do
4
+ Dir['test/test_*.rb'].each do |test|
5
+ ruby "-Ilib #{test}"
6
+ end
7
+ end
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'getoptlong'
4
+ require 'rubygems'
5
+ require 'unmacer'
6
+
7
+ def usage
8
+ puts <<USAGE
9
+ unmac -- Delete spurious Mac files under a directory or volume
10
+
11
+ Usage:
12
+ unmac [OPTIONS] directory ...
13
+
14
+ Options:
15
+ -h, --help Print this help
16
+ -v, --verbose Show processing
17
+ -p, --pretend Show what would be deleted, but don't do it
18
+ -s, --keep-spotlight Do not delete Spotlight data
19
+ -f, --keep-fsevents Do not delete Time Machine stuff
20
+ -t, --keep-trashes Do not delete volume trashes
21
+ -m, --keep-macosx Do not delete "__MACOSX" directories
22
+ -r, --keep-dsstore Do not delete ".DS_Store" files
23
+ -d, --keep-apple-double Do not delete "._*" ghost files
24
+ -o, --keep-apple-double-orphans Delete "._foo.txt" only if "foo.txt" exists
25
+ -i, --keep-custom-folder-icons Do not delete custom folder icons
26
+
27
+ Description:
28
+ When a Mac copies files to volumes that have a different file system it adds
29
+ some auxiliary files that represent different kinds of metadata. This typically
30
+ happens with memory sticks, network drives, SD cards, etc. Mac archivers like
31
+ zip(1) add special files as well, like "#{Unmacer::MACOSX}", you'll see them
32
+ for example if you extract a Zip file like that on Windows.
33
+
34
+ The purpose of these special files is to let another Mac rebuild the metadata
35
+ from them, Spotlight integration, etc. But if you are not interested in these
36
+ features your directory or volume just gets cluttered. This utility removes
37
+ all those extra files.
38
+
39
+ What's deleted:
40
+ This is just a summary, for more detailed explanations and pointers see the
41
+ comments in unmacer.rb:
42
+
43
+ * Spotlight leaves a folder called "#{Unmacer::SPOTLIGHT}" in the root directory
44
+ of volumes.
45
+
46
+ * Time Machine relies on a folder called "#{Unmacer::FSEVENTS}" in the root directory
47
+ of volumes.
48
+
49
+ * A folder called "#{Unmacer::TRASHES}" in the root directory of volumes stores their
50
+ trashes. Those ones are taken into account by the Trash in the Dock.
51
+
52
+ * Some archivers create auxiliary directories called "#{Unmacer::MACOSX}".
53
+
54
+ * Finder and Spotlight data related to each folder gets stored in an
55
+ extra file called "#{Unmacer::DSSTORE}".
56
+
57
+ * For each file "foo.txt", its resource fork and some additional stuff
58
+ are stored in an accompaining ghost file called "._foo.txt". The pattern
59
+ is to prepend "._" to the original file name.
60
+
61
+ * If a folder has a custom icon you get a file "Icon^M" (that ^M is a hard CR).
62
+
63
+ Some stuff is only found in the root folder of volumes, unmac looks for them
64
+ in any directory you pass as argument, but not on their subdirectories.
65
+ USAGE
66
+ end
67
+
68
+ opts = GetoptLong.new(
69
+ ['--help', '-h', GetoptLong::NO_ARGUMENT],
70
+ ['--verbose', '-v', GetoptLong::NO_ARGUMENT],
71
+ ['--pretend', '-p', GetoptLong::NO_ARGUMENT],
72
+ ['--keep-spotlight', '-s', GetoptLong::NO_ARGUMENT],
73
+ ['--keep-fsevents', '-f', GetoptLong::NO_ARGUMENT],
74
+ ['--keep-trashes', '-t', GetoptLong::NO_ARGUMENT],
75
+ ['--keep-macosx', '-m', GetoptLong::NO_ARGUMENT],
76
+ ['--keep-dsstore', '-r', GetoptLong::NO_ARGUMENT],
77
+ ['--keep-apple-double', '-d', GetoptLong::NO_ARGUMENT],
78
+ ['--keep-apple-double-orphans', '-o', GetoptLong::NO_ARGUMENT],
79
+ ['--keep-custom-folder-icons', '-i', GetoptLong::NO_ARGUMENT],
80
+ ['--test', GetoptLong::NO_ARGUMENT]
81
+ )
82
+
83
+ test = false
84
+ begin
85
+ unmacer = Unmacer.new
86
+ opts.each do |opt, val|
87
+ case opt
88
+ when '--help'
89
+ usage and exit
90
+ when '--verbose'
91
+ unmacer.verbose = true
92
+ when '--pretend'
93
+ unmacer.pretend = true
94
+ when /\A--(keep-.*)/
95
+ flag = $1.gsub('-', '_')
96
+ unmacer.send("#{flag}=", true)
97
+ when '--test'
98
+ test = true
99
+ end
100
+ end
101
+ unmacer.unmac!(ARGV) unless test
102
+ rescue GetoptLong::InvalidOption
103
+ usage and exit 1
104
+ end
105
+
106
+ # The test suite for this file expects the unmacer to be the last expression.
107
+ unmacer
@@ -0,0 +1,160 @@
1
+ require 'fileutils'
2
+ require 'find'
3
+
4
+ class Unmacer
5
+ attr_accessor :verbose,
6
+ :pretend,
7
+ :keep_spotlight,
8
+ :keep_fsevents,
9
+ :keep_trashes,
10
+ :keep_macosx,
11
+ :keep_dsstore,
12
+ :keep_apple_double,
13
+ :keep_apple_double_orphans,
14
+ :keep_custom_folder_icons
15
+
16
+ SPOTLIGHT = '.Spotlight-V100'
17
+ FSEVENTS = '.fseventsd'
18
+ TRASHES = '.Trashes'
19
+ MACOSX = '__MACOSX'
20
+ DSSTORE = '.DS_Store'
21
+ ICON = "Icon\cM"
22
+
23
+ def initialize
24
+ self.verbose = false
25
+ self.pretend = false
26
+ self.keep_spotlight = false
27
+ self.keep_fsevents = false
28
+ self.keep_trashes = false
29
+ self.keep_macosx = false
30
+ self.keep_dsstore = false
31
+ self.keep_apple_double = false
32
+ self.keep_apple_double_orphans = false
33
+ self.keep_custom_folder_icons = false
34
+ end
35
+
36
+ def unmac!(dirnames)
37
+ dirnames.each do |dirname|
38
+ unmac_root(dirname)
39
+ find_skipping_root(dirname) do |f|
40
+ unmac_folder(f) if File.directory?(f)
41
+ end
42
+ end
43
+ end
44
+
45
+ def find_skipping_root(path)
46
+ root_seen = false
47
+ Find.find(path) do |f|
48
+ if !root_seen
49
+ root_seen = true
50
+ else
51
+ yield f
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def unmac_root(dirname)
59
+ # Order is important because ".Trashes" has "._.Trashes". Otherwise,
60
+ # "._.Trashes" could be left as an orphan.
61
+ unmac_folder(dirname)
62
+ delete_spotlight(dirname) unless keep_spotlight
63
+ delete_fsevents(dirname) unless keep_fsevents
64
+ delete_trashes(dirname) unless keep_trashes
65
+ end
66
+
67
+ def unmac_folder(dirname)
68
+ delete_macosx(dirname) unless keep_macosx
69
+ delete_dsstore(dirname) unless keep_dsstore
70
+ delete_apple_double(dirname) unless keep_apple_double
71
+ delete_custom_folder_icon(dirname) unless keep_custom_folder_icons
72
+ end
73
+
74
+ def delete(parent, file_or_directory)
75
+ name = File.join(parent, file_or_directory)
76
+ if File.exists?(name)
77
+ if pretend
78
+ puts "would delete #{name}"
79
+ else
80
+ # Vanilla rm_r cannot delete a directory that has AppleDoubles.
81
+ FileUtils.rmtree(name)
82
+ puts "deleted #{name}" if verbose
83
+ end
84
+ end
85
+ rescue Exception => e
86
+ $stderr.puts("could not delete #{name}: #{e.message}")
87
+ end
88
+
89
+ # Spotlight saves all its index-related files in the .Spotlight-V100 directory
90
+ # at the root level of a volume it has indexed.
91
+ #
92
+ # See http://www.thexlab.com/faqs/stopspotlightindex.html.
93
+ def delete_spotlight(dirname)
94
+ delete(dirname, SPOTLIGHT)
95
+ end
96
+
97
+ # The FSEvents framework has a daemon that dumps events from /dev/fsevents
98
+ # into log files stored in a .fseventsd directory at the root of the volume
99
+ # the events are for.
100
+ #
101
+ # See http://arstechnica.com/reviews/os/mac-os-x-10-5.ars/7.
102
+ def delete_fsevents(dirname)
103
+ delete(dirname, FSEVENTS)
104
+ end
105
+
106
+ # A volume may have a ".Trashes" folder in the root directory. The Trash in
107
+ # the Dock shows the contents of the ".Trash" folder in your home directory
108
+ # and the content of all the ".Trashes/uid" in the rest of volumes.
109
+ #
110
+ # See http://discussions.apple.com/thread.jspa?messageID=1145130.
111
+ def delete_trashes(dirname)
112
+ delete(dirname, TRASHES)
113
+ end
114
+
115
+ # Apple's builtin Zip archiver stores some metadata in a directory called
116
+ # "__MACOSX".
117
+ #
118
+ # See http://floatingsun.net/2007/02/07/whats-with-__macosx-in-zip-files.
119
+ def delete_macosx(dirname)
120
+ delete(dirname, MACOSX)
121
+ end
122
+
123
+ # In each directory the ".DS_Store" file stores info about Finder window
124
+ # settings and Spotlight comments of its files.
125
+ #
126
+ # See http://en.wikipedia.org/wiki/.DS_Store.
127
+ def delete_dsstore(dirname)
128
+ delete(dirname, DSSTORE)
129
+ end
130
+
131
+ # When a file is copied to a volume that does not natively support HFS file
132
+ # characteristics its data fork is stored under the file's regular name, and
133
+ # the additional HFS information (resource fork, type & creator codes, etc.)
134
+ # is stored in a second file in AppleDouble format, with a name that starts
135
+ # with "._". So if the original file is called "foo.txt" you get a spurious
136
+ # ghost file called "._foo.txt".
137
+ #
138
+ # If you drag & drop a file onto a Windows partition, or you unpack a tarball
139
+ # on Linux that was built with Apple's tar(1) archiver you'll get that extra
140
+ # stuff. Reason is if the tarball is extracted into another Mac the metadata
141
+ # is preserved with this hack.
142
+ #
143
+ # See http://www.westwind.com/reference/OS-X/invisibles.html.
144
+ # See http://en.wikipedia.org/wiki/Resource_fork.
145
+ # See http://en.wikipedia.org/wiki/AppleSingle.
146
+ def delete_apple_double(dirname)
147
+ basenames = Dir.entries(dirname)
148
+ basenames.select do |basename|
149
+ basename =~ /\A\._(.*)/ &&
150
+ !keep_apple_double_orphans ||
151
+ basenames.include?($1)
152
+ end.each do |basename|
153
+ delete(dirname, basename)
154
+ end
155
+ end
156
+
157
+ def delete_custom_folder_icon(dirname)
158
+ delete(dirname, ICON)
159
+ end
160
+ end
@@ -0,0 +1,241 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'stringio'
4
+ require 'unmacer'
5
+ require 'test/unit'
6
+
7
+ UNMAC_RB = File.join(File.dirname(__FILE__), '..', 'bin', 'unmac')
8
+ UNMAC_SRC = File.open(UNMAC_RB) { |f| f.read }
9
+
10
+ class TestUnmac < Test::Unit::TestCase
11
+ def call_unmac(*args)
12
+ ARGV.clear
13
+ ARGV.concat(['--test', *args])
14
+ eval UNMAC_SRC
15
+ end
16
+
17
+ def test_no_options
18
+ unmacer = call_unmac('dummy')
19
+ assert !unmacer.verbose
20
+ assert !unmacer.pretend
21
+ assert !unmacer.keep_spotlight
22
+ assert !unmacer.keep_fsevents
23
+ assert !unmacer.keep_trashes
24
+ assert !unmacer.keep_macosx
25
+ assert !unmacer.keep_dsstore
26
+ assert !unmacer.keep_apple_double
27
+ assert !unmacer.keep_apple_double_orphans
28
+ assert !unmacer.keep_custom_folder_icons
29
+ assert_equal %w(dummy), ARGV
30
+ end
31
+
32
+ def test_verbose
33
+ for opt in ['--verbose', '-v']
34
+ unmacer = call_unmac(opt, 'dummy')
35
+ assert unmacer.verbose
36
+ assert !unmacer.pretend
37
+ assert !unmacer.keep_spotlight
38
+ assert !unmacer.keep_fsevents
39
+ assert !unmacer.keep_trashes
40
+ assert !unmacer.keep_macosx
41
+ assert !unmacer.keep_dsstore
42
+ assert !unmacer.keep_apple_double
43
+ assert !unmacer.keep_apple_double_orphans
44
+ assert !unmacer.keep_custom_folder_icons
45
+ assert_equal %w(dummy), ARGV
46
+ end
47
+ end
48
+
49
+ def test_pretend
50
+ for opt in ['--pretend', '-p']
51
+ unmacer = call_unmac(opt, 'dummy')
52
+ assert !unmacer.verbose
53
+ assert unmacer.pretend
54
+ assert !unmacer.keep_spotlight
55
+ assert !unmacer.keep_fsevents
56
+ assert !unmacer.keep_trashes
57
+ assert !unmacer.keep_macosx
58
+ assert !unmacer.keep_dsstore
59
+ assert !unmacer.keep_apple_double
60
+ assert !unmacer.keep_apple_double_orphans
61
+ assert !unmacer.keep_custom_folder_icons
62
+ assert_equal %w(dummy), ARGV
63
+ end
64
+ end
65
+
66
+ def test_spotlight
67
+ for opt in ['--keep-spotlight', '-s']
68
+ unmacer = call_unmac(opt, 'dummy')
69
+ assert !unmacer.verbose
70
+ assert !unmacer.pretend
71
+ assert unmacer.keep_spotlight
72
+ assert !unmacer.keep_fsevents
73
+ assert !unmacer.keep_trashes
74
+ assert !unmacer.keep_macosx
75
+ assert !unmacer.keep_dsstore
76
+ assert !unmacer.keep_apple_double
77
+ assert !unmacer.keep_apple_double_orphans
78
+ assert !unmacer.keep_custom_folder_icons
79
+ assert_equal %w(dummy), ARGV
80
+ end
81
+ end
82
+
83
+ def test_fsevents
84
+ for opt in ['--keep-fsevents', '-f']
85
+ unmacer = call_unmac(opt, 'dummy')
86
+ assert !unmacer.verbose
87
+ assert !unmacer.pretend
88
+ assert !unmacer.keep_spotlight
89
+ assert unmacer.keep_fsevents
90
+ assert !unmacer.keep_trashes
91
+ assert !unmacer.keep_macosx
92
+ assert !unmacer.keep_dsstore
93
+ assert !unmacer.keep_apple_double
94
+ assert !unmacer.keep_apple_double_orphans
95
+ assert !unmacer.keep_custom_folder_icons
96
+ assert_equal %w(dummy), ARGV
97
+ end
98
+ end
99
+
100
+ def test_trashes
101
+ for opt in ['--keep-trashes', '-t']
102
+ unmacer = call_unmac(opt, 'dummy')
103
+ assert !unmacer.verbose
104
+ assert !unmacer.pretend
105
+ assert !unmacer.keep_spotlight
106
+ assert !unmacer.keep_fsevents
107
+ assert unmacer.keep_trashes
108
+ assert !unmacer.keep_macosx
109
+ assert !unmacer.keep_dsstore
110
+ assert !unmacer.keep_apple_double
111
+ assert !unmacer.keep_apple_double_orphans
112
+ assert !unmacer.keep_custom_folder_icons
113
+ assert_equal %w(dummy), ARGV
114
+ end
115
+ end
116
+
117
+ def test_macosx
118
+ for opt in ['--keep-macosx', '-m']
119
+ unmacer = call_unmac(opt, 'dummy')
120
+ assert !unmacer.verbose
121
+ assert !unmacer.pretend
122
+ assert !unmacer.keep_spotlight
123
+ assert !unmacer.keep_fsevents
124
+ assert !unmacer.keep_trashes
125
+ assert unmacer.keep_macosx
126
+ assert !unmacer.keep_dsstore
127
+ assert !unmacer.keep_apple_double
128
+ assert !unmacer.keep_apple_double_orphans
129
+ assert !unmacer.keep_custom_folder_icons
130
+ assert_equal %w(dummy), ARGV
131
+ end
132
+ end
133
+
134
+ def test_dsstore
135
+ for opt in ['--keep-dsstore', '-r']
136
+ unmacer = call_unmac(opt, 'dummy')
137
+ assert !unmacer.verbose
138
+ assert !unmacer.pretend
139
+ assert !unmacer.keep_spotlight
140
+ assert !unmacer.keep_fsevents
141
+ assert !unmacer.keep_trashes
142
+ assert !unmacer.keep_macosx
143
+ assert unmacer.keep_dsstore
144
+ assert !unmacer.keep_apple_double
145
+ assert !unmacer.keep_apple_double_orphans
146
+ assert !unmacer.keep_custom_folder_icons
147
+ assert_equal %w(dummy), ARGV
148
+ end
149
+ end
150
+
151
+ def test_apple_double
152
+ for opt in ['--keep-apple-double', '-d']
153
+ unmacer = call_unmac(opt, 'dummy')
154
+ assert !unmacer.verbose
155
+ assert !unmacer.pretend
156
+ assert !unmacer.keep_spotlight
157
+ assert !unmacer.keep_fsevents
158
+ assert !unmacer.keep_trashes
159
+ assert !unmacer.keep_macosx
160
+ assert !unmacer.keep_dsstore
161
+ assert unmacer.keep_apple_double
162
+ assert !unmacer.keep_apple_double_orphans
163
+ assert !unmacer.keep_custom_folder_icons
164
+ assert_equal %w(dummy), ARGV
165
+ end
166
+ end
167
+
168
+ def test_apple_double_orphans
169
+ for opt in ['--keep-apple-double-orphans', '-o']
170
+ unmacer = call_unmac(opt, 'dummy')
171
+ assert !unmacer.verbose
172
+ assert !unmacer.pretend
173
+ assert !unmacer.keep_spotlight
174
+ assert !unmacer.keep_fsevents
175
+ assert !unmacer.keep_trashes
176
+ assert !unmacer.keep_macosx
177
+ assert !unmacer.keep_dsstore
178
+ assert !unmacer.keep_apple_double
179
+ assert unmacer.keep_apple_double_orphans
180
+ assert !unmacer.keep_custom_folder_icons
181
+ assert_equal %w(dummy), ARGV
182
+ end
183
+ end
184
+
185
+ def test_custom_folder_icons
186
+ for opt in ['--keep-custom-folder-icons', '-i']
187
+ unmacer = call_unmac(opt, 'dummy')
188
+ assert !unmacer.verbose
189
+ assert !unmacer.pretend
190
+ assert !unmacer.keep_spotlight
191
+ assert !unmacer.keep_fsevents
192
+ assert !unmacer.keep_trashes
193
+ assert !unmacer.keep_macosx
194
+ assert !unmacer.keep_dsstore
195
+ assert !unmacer.keep_apple_double
196
+ assert !unmacer.keep_apple_double_orphans
197
+ assert unmacer.keep_custom_folder_icons
198
+ assert_equal %w(dummy), ARGV
199
+ end
200
+ end
201
+
202
+ def test_mix_1
203
+ unmacer = call_unmac(*%w(-v -f -m -d -i dummy))
204
+ assert unmacer.verbose
205
+ assert !unmacer.pretend
206
+ assert !unmacer.keep_spotlight
207
+ assert unmacer.keep_fsevents
208
+ assert !unmacer.keep_trashes
209
+ assert unmacer.keep_macosx
210
+ assert !unmacer.keep_dsstore
211
+ assert unmacer.keep_apple_double
212
+ assert !unmacer.keep_apple_double_orphans
213
+ assert unmacer.keep_custom_folder_icons
214
+ assert_equal %w(dummy), ARGV
215
+ end
216
+
217
+ def test_mix_2
218
+ unmacer = call_unmac(*%w(-s -t -r -o dummy))
219
+ assert !unmacer.verbose
220
+ assert !unmacer.pretend
221
+ assert unmacer.keep_spotlight
222
+ assert !unmacer.keep_fsevents
223
+ assert unmacer.keep_trashes
224
+ assert !unmacer.keep_macosx
225
+ assert unmacer.keep_dsstore
226
+ assert !unmacer.keep_apple_double
227
+ assert unmacer.keep_apple_double_orphans
228
+ assert !unmacer.keep_custom_folder_icons
229
+ assert_equal %w(dummy), ARGV
230
+ end
231
+
232
+ def test_help
233
+ buf = ''
234
+ $> = StringIO.open(buf, 'w')
235
+ call_unmac('-h')
236
+ $> = $stdout
237
+ assert_match /^unmac\b/, buf
238
+ assert_match /^Usage\b/, buf
239
+ assert ARGV.empty?
240
+ end
241
+ end
@@ -0,0 +1,234 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'stringio'
4
+ require 'fileutils'
5
+ require 'set'
6
+ require 'unmacer'
7
+ require 'test/unit'
8
+
9
+ TEST_DIR = File.expand_path(File.dirname(__FILE__))
10
+ TMP_DIR = File.join(TEST_DIR, 'tmp')
11
+
12
+ class TestUnmacer < Test::Unit::TestCase
13
+ def in_test_dir
14
+ Dir.chdir(TEST_DIR) { yield }
15
+ end
16
+
17
+ def in_tmp_dir
18
+ Dir.chdir(TMP_DIR) { yield }
19
+ end
20
+
21
+ def setup
22
+ in_test_dir { Dir.mkdir('tmp') }
23
+ @unmacer = Unmacer.new
24
+ end
25
+
26
+ def teardown
27
+ in_test_dir { FileUtils.rm_r('tmp') }
28
+ end
29
+
30
+ def unmac!
31
+ @unmacer.unmac!(TMP_DIR)
32
+ end
33
+
34
+ def create_struct(dirnames=[], fnames=[])
35
+ in_tmp_dir do
36
+ FileUtils.mkdir_p([*dirnames])
37
+ FileUtils.touch([*fnames])
38
+ end
39
+ end
40
+
41
+ def read_struct
42
+ entries = []
43
+ in_tmp_dir do
44
+ @unmacer.find_skipping_root('.') do |f|
45
+ # File.find returns "./foo.txt", remove the leading stuff.
46
+ entries << f.sub(%r{\A\.[/\\]}, '')
47
+ end
48
+ end
49
+ entries
50
+ end
51
+
52
+ def test_an_empty_directory_should_remain_empty
53
+ create_struct
54
+ unmac!
55
+ assert read_struct.empty?
56
+ end
57
+
58
+ def test_pretend
59
+ dirs = [Unmacer::SPOTLIGHT, Unmacer::FSEVENTS, Unmacer::TRASHES, Unmacer::MACOSX]
60
+ create_struct(dirs, '._dummy')
61
+ buf = ''
62
+ $> = StringIO.new(buf, 'w')
63
+ @unmacer.pretend = true
64
+ unmac!
65
+ $> = $stdout
66
+ assert Set.new(dirs + ['._dummy']), Set.new(read_struct)
67
+ assert_match /^would delete.*\.Spotlight/, buf
68
+ assert_match /^would delete.*\.fsevents/, buf
69
+ assert_match /^would delete.*\.Trashes/, buf
70
+ assert_match /^would delete.*__MACOSX/, buf
71
+ assert_match /^would delete.*\._dummy/, buf
72
+ end
73
+
74
+ def test_spotlight
75
+ create_struct(Unmacer::SPOTLIGHT)
76
+ unmac!
77
+ assert read_struct.empty?
78
+ end
79
+
80
+ def test_spotlight_ignored_if_not_in_root
81
+ fake = File.join('dummy', Unmacer::SPOTLIGHT)
82
+ create_struct(fake)
83
+ unmac!
84
+ assert read_struct.include?(fake)
85
+ end
86
+
87
+ def test_keep_spotlight
88
+ create_struct(Unmacer::SPOTLIGHT)
89
+ @unmacer.keep_spotlight = true
90
+ unmac!
91
+ assert [Unmacer::SPOTLIGHT], read_struct
92
+ end
93
+
94
+ def test_fsevents
95
+ create_struct(Unmacer::FSEVENTS)
96
+ unmac!
97
+ assert read_struct.empty?
98
+ end
99
+
100
+ def test_fsevents_ignored_if_not_in_root
101
+ fake = File.join('dummy', Unmacer::FSEVENTS)
102
+ create_struct(fake)
103
+ unmac!
104
+ assert read_struct.include?(fake)
105
+ end
106
+
107
+ def test_keep_fsevents
108
+ create_struct(Unmacer::FSEVENTS)
109
+ @unmacer.keep_fsevents = true
110
+ unmac!
111
+ assert_equal [Unmacer::FSEVENTS], read_struct
112
+ end
113
+
114
+ def test_trashes
115
+ create_struct(Unmacer::TRASHES)
116
+ unmac!
117
+ assert read_struct.empty?
118
+ end
119
+
120
+ def test_trashes_ignored_if_not_in_root
121
+ fake = File.join('dummy', Unmacer::TRASHES)
122
+ create_struct(fake)
123
+ unmac!
124
+ assert read_struct.include?(fake)
125
+ end
126
+
127
+ def test_keep_trashes
128
+ create_struct(Unmacer::TRASHES)
129
+ @unmacer.keep_trashes = true
130
+ unmac!
131
+ assert_equal [Unmacer::TRASHES], read_struct
132
+ end
133
+
134
+ def test_macosx
135
+ create_struct(Unmacer::MACOSX)
136
+ unmac!
137
+ assert read_struct.empty?
138
+ end
139
+
140
+ def test_keep_macosx
141
+ create_struct(Unmacer::MACOSX)
142
+ @unmacer.keep_macosx = true
143
+ unmac!
144
+ assert_equal [Unmacer::MACOSX], read_struct
145
+ end
146
+
147
+ def test_dsstore
148
+ create_struct([], Unmacer::DSSTORE)
149
+ unmac!
150
+ assert read_struct.empty?
151
+ end
152
+
153
+ def test_keep_dsstore
154
+ create_struct([], Unmacer::DSSTORE)
155
+ @unmacer.keep_dsstore = true
156
+ unmac!
157
+ assert_equal [Unmacer::DSSTORE], read_struct
158
+ end
159
+
160
+ def test_apple_double_with_pair
161
+ create_struct([], %w(foo.txt ._foo.txt))
162
+ unmac!
163
+ assert_equal %w(foo.txt), read_struct
164
+ end
165
+
166
+ def test_keep_apple_double_with_pair
167
+ create_struct([], %w(foo.txt ._foo.txt))
168
+ @unmacer.keep_apple_double = true
169
+ unmac!
170
+ assert_equal Set.new(%w(foo.txt ._foo.txt)), Set.new(read_struct)
171
+ end
172
+
173
+ def test_apple_double_with_orphan
174
+ create_struct([], '._foo.txt')
175
+ unmac!
176
+ assert read_struct.empty?
177
+ end
178
+
179
+ def test_keep_apple_double_with_orphan
180
+ create_struct([], '._foo.txt')
181
+ @unmacer.keep_apple_double = true
182
+ unmac!
183
+ assert ['._foo.txt'], read_struct
184
+ end
185
+
186
+ def test_keep_apple_double_orphans
187
+ create_struct([], '._foo.txt')
188
+ @unmacer.keep_apple_double_orphans = true
189
+ unmac!
190
+ assert_equal %w(._foo.txt), read_struct
191
+ end
192
+
193
+ def test_custom_folder_icons
194
+ create_struct([], Unmacer::ICON)
195
+ unmac!
196
+ assert read_struct.empty?
197
+ end
198
+
199
+ def test_keep_custom_folder_icons
200
+ create_struct([], Unmacer::ICON)
201
+ @unmacer.keep_custom_folder_icons = true
202
+ unmac!
203
+ assert_equal [Unmacer::ICON], read_struct
204
+ end
205
+
206
+ def test_a_handful_of_files
207
+ dummy = 'dummy'
208
+ dummy2 = File.join(dummy, 'dummy2')
209
+ ghost = File.join(dummy, '._ghost')
210
+ remain = File.join(dummy2, 'should_remain')
211
+ create_struct([
212
+ Unmacer::SPOTLIGHT,
213
+ Unmacer::FSEVENTS,
214
+ Unmacer::TRASHES,
215
+ Unmacer::MACOSX,
216
+ dummy,
217
+ dummy2
218
+ ], [
219
+ Unmacer::DSSTORE,
220
+ File.join(dummy, Unmacer::DSSTORE),
221
+ File.join(dummy2, Unmacer::DSSTORE),
222
+ File.join(dummy, Unmacer::MACOSX),
223
+ File.join(dummy2, Unmacer::MACOSX),
224
+ File.join(dummy, Unmacer::ICON),
225
+ File.join(dummy2, Unmacer::ICON),
226
+ 'foo.txt',
227
+ '._foo.txt',
228
+ ghost,
229
+ remain
230
+ ])
231
+ unmac!
232
+ assert_equal Set.new(['foo.txt', dummy, dummy2, remain]), Set.new(read_struct)
233
+ end
234
+ end
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'unmac'
3
+ spec.version = '0.6'
4
+ spec.summary = 'Delete spurious Mac files under a directory or volume'
5
+ spec.homepage = 'http://github.com/fxn/unmac/tree/master'
6
+ spec.executables = %w(unmac)
7
+ spec.author = 'Xavier Noria'
8
+ spec.email = 'fxn@hashref.com'
9
+ spec.rubyforge_project = 'unmac'
10
+
11
+ spec.test_files = %w(
12
+ test/test_unmacer.rb
13
+ test/test_unmac.rb
14
+ )
15
+
16
+ spec.files = %w(
17
+ unmac.gemspec
18
+ Rakefile
19
+ README
20
+ MIT-LICENSE
21
+ bin/unmac
22
+ lib/unmacer.rb
23
+ ) + spec.test_files
24
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unmac
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.6"
5
+ platform: ruby
6
+ authors:
7
+ - Xavier Noria
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-08 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: fxn@hashref.com
18
+ executables:
19
+ - unmac
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - unmac.gemspec
26
+ - Rakefile
27
+ - README
28
+ - MIT-LICENSE
29
+ - bin/unmac
30
+ - lib/unmacer.rb
31
+ - test/test_unmacer.rb
32
+ - test/test_unmac.rb
33
+ has_rdoc: false
34
+ homepage: http://github.com/fxn/unmac/tree/master
35
+ post_install_message:
36
+ rdoc_options: []
37
+
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ requirements: []
53
+
54
+ rubyforge_project: unmac
55
+ rubygems_version: 1.3.1
56
+ signing_key:
57
+ specification_version: 2
58
+ summary: Delete spurious Mac files under a directory or volume
59
+ test_files:
60
+ - test/test_unmacer.rb
61
+ - test/test_unmac.rb