Skip to content

Instantly share code, notes, and snippets.

@chneeb
Created May 21, 2011 18:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chneeb/984730 to your computer and use it in GitHub Desktop.
Save chneeb/984730 to your computer and use it in GitHub Desktop.
The missing uninstaller for Mac OS X
#!/usr/bin/ruby -w
require 'fileutils'
include FileUtils::Verbose
def lsbom(bom, relocated_path = nil)
files = []
IO.popen("lsbom -s \"#{bom}\"") do |pipe|
while line = pipe.gets
file = line.slice(1..-2)
file = relocated_path + file unless relocated_path.nil? or relocated_path == './' or relocated_path == '/'
files << file if FileTest.exists?(file)
end
end
return files.sort{|a, b| b <=> a}
end
def plist_lookup(plist, key)
key_found = false
value = nil
IO.foreach(plist) do |line|
case line.chomp
when /^\s*<key>\s*#{key}\s*<\/key>/
key_found = true
when /^\s*<string>\s*(.+)\s*<\/string>/
value = $1
next if key_found
end
end
return value
end
def uninstall(pkg, dry_run=false)
archive_bom = Dir[File.join(pkg, "Contents", "**", "*.bom")].first
puts "Using archive bom #{archive_bom}"
info_plist = File.join(pkg, "Contents", "Info.plist")
raise "#{pkg} is not a package!" unless pkg =~ /\.pkg$/i
raise "#{pkg} is not a directory!" unless FileTest.directory?(pkg)
raise "#{pkg} does not have #{archive_bom}!" unless FileTest.exists?(archive_bom)
raise "#{pkg} does not have #{info_plist}!" unless FileTest.exists?(info_plist)
relocated_path = plist_lookup(info_plist, "IFPkgRelocatedPath")
puts "Using relocated path #{relocated_path}"
files = lsbom(archive_bom, relocated_path)
files.delete_if do |file|
if FileTest.file?(file)
unless dry_run
rm(file) || raise("could not delete file #{file}")
else
puts "Removing #{file}"
end
true
else
false
end
end
files.delete_if do |file|
if FileTest.directory?(file)
if Dir["#{file}/*"].empty?
unless dry_run
rm_r(file) || raise("could not delete directory #{file}")
else
puts "Removing #{file}"
end
else
warn("not removing directory #{file}")
end
true
else
false
end
end
unless dry_run
raise "not all files properly deleted:\n#{files.join("\n")}" unless files.empty?
rm_r(pkg) || raise("could not delete receipt #{pkg}")
end
end
receipt = ARGV[0] || (puts("usage: #{File.basename($0)} <receipt>"); exit)
receipt = File.expand_path(receipt)
uninstall(receipt, false)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment