#!/usr/bin/perl -w # real Perl code begins here # # Adapted from Apple's uninstall-devtools.pl (Version 7 for Xcode Tools 1.2) # # BSD License: c.f. # use strict; use File::Basename; use vars qw ($do_nothing $print_donothing_removals $receipts_dir $verbose $noisy_warnings); use vars qw ($suppress_spin $spin_counter $spin_state $spin_slower_downer); use vars qw (%exception_list $gen_dirs @gen_files @rmfiles @rmdirs @rmpkg); #---------------------------------------------------------------------------------------- $do_nothing = 0; $print_donothing_removals = 1; $verbose = 1; $noisy_warnings = 0; # One of rm -rf in this script uses $receipts_dir -- change with care. $receipts_dir = "/Library/Receipts"; %exception_list = ( # '/usr/bin/aklog', '', ); $gen_dirs = { }; #$gen_dirs->{"/"}->{"Library"}->{"OpenAFS"}->{"Tools"} = {}; @gen_files = ( "/var/db/openafs/etc/cacheinfo", "/var/db/openafs/etc/ThisCell", "/var/db/openafs/etc/config/afsd.options", "/var/db/openafs/etc/config/afs.conf", "/var/db/openafs/etc/CellServDB.save", "/var/db/openafs/etc/CellServDB.master.last", "/var/db/openafs/etc/CellServDB", "/var/db/openafs/etc/config/settings.plist", ); #---------------------------------------------------------------------------------------- $| = 1; #if ($do_nothing == 0 && $< != 0) # { # die "ERROR: Must be run with root permissions--prefix command with 'sudo'.\n"; # } sub main { # commandline args: # 0: dir of packages to remove # 1: flag indicating whether to keep package receipts # 2: flag indicating whether to supress spin indicator if (!@ARGV) { use FindBin qw($Bin); @ARGV = ("$Bin/..", 0, 0); } $suppress_spin = defined ($ARGV[2]) && $ARGV[2]; $spin_counter = 0; $spin_state = 0; spin_rate_slow (); pre_print (); print "Uninstalling OpenAFS package:\n\n"; remove_generated_files (); remove_main_packages (); remove_generated_directories (); if ($do_nothing == 0) { my @rmcmd = ('osascript', '-e', "do shell script \"/bin/rm -f @rmfiles; /bin/rmdir @rmdirs; /bin/rm -rf @rmpkg\" with administrator privileges"); system @rmcmd; my $retcode = $? >> 8; if ($retcode != 0) { print_warning ("Warning: There may have been a problem uninstalling\n"); } } pre_print (); print "\nFinished uninstalling.\n"; } sub remove_main_packages { # opendir (DIR, $ARGV[0]) or die "ERROR: package directory $ARGV[0] not found\n"; # my @pkglist = grep /\.pkg$/, readdir DIR; # closedir DIR; my @pkglist = ("org.openafs.OpenAFS-debug", "org.openafs.OpenAFS", "OpenAFS", "OpenAFS-debug-extension", ); foreach (@pkglist) { s/\.pkg$//; my $pkgname = $_; my $bompath = undef; my $infopath = undef; my $pkgname_plist_newpath = "/var/db/receipts/$pkgname.pkg.plist"; my $pkgname_bom_newpath = "/var/db/receipts/$pkgname.pkg.bom"; my $pkgname_plist_fullpath = "/Library/Receipts/$pkgname.pkg/Contents/Info.plist"; my $pkgname_bom_fullpath = "/Library/Receipts/$pkgname.pkg/Contents/Archive.bom"; if (-f $pkgname_plist_newpath && -r $pkgname_plist_newpath && -f $pkgname_bom_newpath && -r $pkgname_bom_newpath) { print_verbose ("Removing package $pkgname.pkg\n"); $bompath = $pkgname_bom_newpath; $infopath = $pkgname_plist_newpath; } else { if (-f $pkgname_plist_fullpath && -r $pkgname_plist_fullpath && -f $pkgname_bom_fullpath && -r $pkgname_bom_fullpath) { print_verbose ("Removing package $pkgname.pkg\n"); $bompath = $pkgname_bom_fullpath; $infopath = $pkgname_plist_fullpath; } else { next; } } next if (!defined ($bompath)); my $bomroot = ""; open (INFO, "$infopath") or next; while () { m/IFPkgFlagDefaultLocation/ or next; $bomroot = ; } close (INFO); $bomroot =~ />(.*)) { chomp; m#^\.(/.*)$#; next if (!defined ($1) || $1 eq ""); my $filename = $bomroot . $1; remove_a_file ($filename); } close (LSBOM); my $rooth = { }; open (LSBOM, "/usr/bin/lsbom -d -p f '$bompath' |") or next; while () { chomp; m#^\.(/.*)$#; next if (!defined ($1) || $1 eq ""); my $directory = $bomroot . $1; if (-d $directory) { $rooth = add_directory_to_tree ($directory, $rooth); } else { if ($noisy_warnings) { print_warning ("Warning: \"$directory\" listed in BOM but not present on system.\n"); } } } close (LSBOM); spin_rate_fast (); remove_empty_directories ($rooth, "/"); if (-d "$receipts_dir/$pkgname.pkg" ) { remove_package_receipts ("$pkgname.pkg") if (!defined ($ARGV[1]) || !$ARGV[1]) ; } else { unshift(@rmfiles, "$bompath"); unshift(@rmfiles, "$infopath"); } } } sub remove_generated_files { foreach (@gen_files) { remove_a_file ($_); } } sub remove_generated_directories { remove_empty_directories ($gen_dirs, "/"); } sub add_directory_to_tree { my $dir = shift; my $rooth = shift; my $p = $rooth; my @pathcomp = split /\//, $dir; progress_point (); foreach (@pathcomp) { my $cur_name = $_; if ($cur_name eq "" || !defined ($cur_name)) { $cur_name = "/"; } if (!defined ($p->{"$cur_name"})) { $p->{$cur_name} = { }; } $p = $p->{$cur_name}; } return $rooth; } sub remove_empty_directories { my $rooth = shift; my $path = shift; my $children = (scalar (keys %{$rooth})); my $dirs_remain = 0; if ($children > 0) { foreach my $dirname (sort keys %{$rooth}) { my $printpath; $printpath = "$path/$dirname"; $printpath =~ s#^/*#/#; remove_empty_directories ($rooth->{$dirname}, "$printpath"); $dirs_remain = 1 if (-d "$printpath"); } } if ($dirs_remain == 0) { maybe_remove_ds_store ("$path"); } remove_a_dir ("$path"); } sub remove_a_file { my $fn = shift; my $dirname = dirname ($fn); my $basename = basename ($fn); my $ufs_rsrc_file = "$dirname/._$basename"; progress_point (); return if (!defined ($fn) || $fn eq ""); # Leave any files that are shared between packages alone. if (defined($exception_list{$fn})) { if ($noisy_warnings) { print_warning ("Warning: file \"$fn\" intentionally not removed, even though it's in the BOM.\n"); } return; } if (! -f $fn && ! -l $fn) { if ($noisy_warnings) { print_warning ("Warning: file \"$fn\" present in BOM but not found on disc.\n"); } return; } if ($do_nothing == 1) { print_donothing ("rm $fn\n"); print_donothing ("rm $ufs_rsrc_file\n") if ( -f $ufs_rsrc_file); } else { unshift(@rmfiles, "$fn"); unshift(@rmfiles, "$ufs_rsrc_file") if ( -f $ufs_rsrc_file); } } sub remove_a_dir { my $dir = shift; progress_point (); return if (!defined ($dir) || $dir eq "" || $dir eq "/" || $dir eq "/usr"); if (! -d $dir) { if ($noisy_warnings) { print_warning ("Warning: directory \"$dir\" present in BOM but not found on disc.\n"); } return; } if ($do_nothing == 1) { print_donothing ("rmdir $dir\n"); } else { push(@rmdirs, "$dir"); } } sub remove_package_receipts { my $pkgname = shift; $pkgname =~ s#/##g; # There shouldn't be any path seps in the pkg name... return if (!defined ($pkgname) || $pkgname eq "" || $pkgname eq "." || $pkgname eq ".."); my $pkgdir = "$receipts_dir/$pkgname"; return if (!defined ($pkgdir) || $pkgdir eq "" || ! -d $pkgdir); push(@rmpkg, "$pkgdir"); } sub maybe_remove_ds_store { my $path = shift; my $filecount = 0; return if (!defined ($path) || $path eq "" || $path eq "/" || $path eq "/usr"); return if (! -f "$path/.DS_Store"); open (LS, "/bin/ls -a '$path' |"); while () { chomp; next if (m#^\.$# || m#^\.\.$#); $filecount++; } close (LS); if ($filecount == 1 && -f "$path/.DS_Store") { remove_a_file ("$path/.DS_Store"); } } sub print_donothing { my $msg = shift; return if ($print_donothing_removals != 1); pre_print (); print $msg; } sub print_verbose { my $msg = shift; return if ($verbose != 1); pre_print (); print $msg; } sub print_warning { my $msg = shift; pre_print (); print STDERR $msg; } sub print_error { my $msg = shift; pre_print (); print STDERR $msg; } sub pre_print { print " " unless ($suppress_spin); } sub spin_rate_slow { $spin_slower_downer = 150; } sub spin_rate_fast { $spin_slower_downer = 75; } sub progress_point { return if ($suppress_spin); $spin_counter++; if (($spin_counter % $spin_slower_downer) == 0) { my $spin_chars = "|/-\\"; my $c = substr ($spin_chars, $spin_state % 4, 1); $spin_state++; print "$c"; } } main (); #----------------------------------------------------------------------------------------