macos-installer-20060801
[openafs.git] / src / packaging / MacOS / Uninstall
1 #!/usr/bin/perl -w
2 # real Perl code begins here
3 #
4 # Adapted from Apple's uninstall-devtools.pl (Version 7 for Xcode Tools 1.2)
5 #
6 # BSD License: c.f. <http://www.opensource.org/licenses/bsd-license>
7 #
8
9 use strict;
10 use File::Basename;
11
12 use vars qw ($do_nothing $print_donothing_removals $receipts_dir $verbose $noisy_warnings);
13 use vars qw ($suppress_spin $spin_counter $spin_state $spin_slower_downer);
14 use vars qw (%exception_list $gen_dirs @gen_files @rmfiles @rmdirs @rmpkg);
15
16 #----------------------------------------------------------------------------------------
17
18 $do_nothing = 0;
19 $print_donothing_removals = 1;
20 $verbose = 1;
21 $noisy_warnings = 0;
22
23 # One of rm -rf in this script uses $receipts_dir -- change with care.
24 $receipts_dir = "/Library/Receipts";
25
26 %exception_list = (
27 #  '/usr/bin/aklog', '',
28 );
29
30 $gen_dirs = { };
31 #$gen_dirs->{"/"}->{"Library"}->{"OpenAFS"}->{"Tools"} = {};
32
33 @gen_files = (
34     "/var/db/openafs/etc/cacheinfo",
35     "/var/db/openafs/etc/ThisCell",
36     "/var/db/openafs/etc/config/afsd.options",
37     "/var/db/openafs/etc/CellServDB.save",
38     "/var/db/openafs/etc/CellServDB.master.last",
39     "/var/db/openafs/etc/CellServDB",
40     "/var/db/openafs/etc/config/settings.plist",
41 );
42
43 #----------------------------------------------------------------------------------------
44
45 $| = 1;
46 #if ($do_nothing == 0 && $< != 0)
47 #  {
48 #    die "ERROR: Must be run with root permissions--prefix command with 'sudo'.\n";
49 #  }
50
51 sub main
52   {
53     # commandline args: 
54     #     0: dir of packages to remove
55     #     1: flag indicating whether to keep package receipts
56     #     2: flag indicating whether to supress spin indicator
57
58     if (!@ARGV) {
59         use FindBin qw($Bin);
60         @ARGV = ("$Bin/..", 0, 0);
61     }
62     $suppress_spin = defined ($ARGV[2]) && $ARGV[2];
63
64     $spin_counter = 0;
65     $spin_state = 0;
66     spin_rate_slow ();
67
68     pre_print ();
69     print "Uninstalling OpenAFS package:\n\n";
70
71     remove_generated_files ();
72     remove_main_packages ();
73     remove_generated_directories ();
74
75     if ($do_nothing == 0) {
76     my @rmcmd = ('osascript', '-e', "do shell script \"/bin/rm -f @rmfiles; /bin/rmdir @rmdirs; /bin/rm -rf @rmpkg\" with administrator privileges");
77     system @rmcmd;
78     my $retcode = $? >> 8;
79     if ($retcode != 0) {
80            print_warning ("Warning:  There may have been a problem uninstalling\n");
81     }
82     }
83
84     pre_print ();
85     print "\nFinished uninstalling.\n";
86   }
87
88 sub remove_main_packages
89   {
90 #    opendir (DIR, $ARGV[0]) or die "ERROR: package directory $ARGV[0] not found\n";
91 #    my @pkglist = grep /\.pkg$/, readdir DIR;
92 #    closedir DIR;
93      my @pkglist = ("OpenAFS",
94                     );
95     
96     foreach (@pkglist)
97       {
98         s/\.pkg$//;
99         my $pkgname = $_;
100         my $pkgname_fullpath = "$receipts_dir/$pkgname.pkg";
101         my $pkgname_bom_fullpath = undef;
102
103         next if (! -d "$pkgname_fullpath" );
104         
105         print_verbose ("Removing package $pkgname.pkg\n");
106
107         my $f  = "$pkgname_fullpath/Contents/Resources/$pkgname.bom";
108         if (-f $f && -r $f) {
109             $pkgname_bom_fullpath = $f;
110         }
111
112         next if (!defined ($pkgname_bom_fullpath));
113         
114         my $bomroot = "";
115         open (INFO, "$pkgname_fullpath/Contents/Info.plist") or next;
116         while (<INFO>) {
117             m/IFPkgFlagDefaultLocation/ or next;
118             $bomroot = <INFO>;
119         }
120         close (INFO);
121
122         $bomroot =~ />(.*)</;
123         $bomroot = $1;
124         $bomroot =~ s/^\/$//;
125
126         spin_rate_slow ();
127
128         open (LSBOM, "/usr/bin/lsbom -l -f -p f '$pkgname_bom_fullpath' |") or next;
129         while (<LSBOM>)
130           {
131             chomp;
132             m#^\.(/.*)$#;
133             next if (!defined ($1) || $1 eq "");
134             my $filename = $bomroot . $1;
135
136             remove_a_file ($filename);
137           }
138         close (LSBOM);
139
140         my $rooth = { };
141
142         open (LSBOM, "/usr/bin/lsbom -d -p f '$pkgname_bom_fullpath' |") or next;
143         while (<LSBOM>)
144           {
145             chomp;
146             m#^\.(/.*)$#;
147             next if (!defined ($1) || $1 eq "");
148             my $directory = $bomroot . $1;
149             if (-d $directory)
150               {
151                 $rooth = add_directory_to_tree ($directory, $rooth);
152               }
153             else
154               {
155                 if ($noisy_warnings)
156                   {
157                     print_warning ("Warning: \"$directory\" listed in BOM but not present on system.\n");
158                   }
159               }
160           }
161         close (LSBOM);
162
163         spin_rate_fast ();
164         remove_empty_directories ($rooth, "/");
165
166         remove_package_receipts ("$pkgname.pkg") if (!defined ($ARGV[1]) || !$ARGV[1]) ;
167       }
168   }
169
170 sub remove_generated_files
171   {
172    foreach (@gen_files)
173      {
174        remove_a_file ($_);
175      }
176   }
177
178 sub remove_generated_directories
179   {
180     remove_empty_directories ($gen_dirs, "/");
181   }
182
183 sub add_directory_to_tree
184   {
185     my $dir = shift;
186     my $rooth = shift;
187     my $p = $rooth;
188
189     my @pathcomp = split /\//, $dir;
190
191     progress_point ();
192     foreach (@pathcomp)
193       {
194         my $cur_name = $_;
195         if ($cur_name eq "" || !defined ($cur_name))
196           {
197             $cur_name = "/";
198           }
199         if (!defined ($p->{"$cur_name"}))
200           {
201             $p->{$cur_name} = { };
202           }
203         $p = $p->{$cur_name};
204       }
205     return $rooth;
206   }
207
208 sub remove_empty_directories
209   {
210     my $rooth = shift;
211     my $path = shift;
212     my $children = (scalar (keys %{$rooth}));
213     my $dirs_remain = 0;
214
215     if ($children > 0)
216       {
217         foreach my $dirname (sort keys %{$rooth})
218           {
219             my $printpath;
220             $printpath = "$path/$dirname";
221             $printpath =~ s#^/*#/#;
222             remove_empty_directories ($rooth->{$dirname}, "$printpath");
223             $dirs_remain = 1 if (-d "$printpath");
224           } 
225       }
226
227      if ($dirs_remain == 0)
228        {
229          maybe_remove_ds_store ("$path");
230        }
231
232      remove_a_dir ("$path");
233   }
234
235 sub remove_a_file
236   {
237     my $fn = shift;
238     my $dirname = dirname ($fn);
239     my $basename = basename ($fn);
240     my $ufs_rsrc_file = "$dirname/._$basename";
241
242     progress_point ();
243     return if (!defined ($fn) || $fn eq "");
244
245     # Leave any files that are shared between packages alone.
246     if (defined($exception_list{$fn}))
247       {
248         if ($noisy_warnings)
249           {
250             print_warning ("Warning: file \"$fn\" intentionally not removed, even though it's in the BOM.\n");
251           }
252         return;
253       }
254
255     if (! -f $fn && ! -l $fn)
256       {
257         if ($noisy_warnings)
258           {
259             print_warning ("Warning: file \"$fn\" present in BOM but not found on disc.\n");
260           }
261         return;
262       }
263
264     if ($do_nothing == 1) 
265       {
266         print_donothing ("rm $fn\n");
267         print_donothing ("rm $ufs_rsrc_file\n") if ( -f $ufs_rsrc_file);
268       }
269     else
270       {
271           unshift(@rmfiles, "$fn");
272           unshift(@rmfiles, "$fn") if ( -f $ufs_rsrc_file);
273       }
274   }
275
276 sub remove_a_dir
277   {
278     my $dir = shift;
279
280     progress_point ();
281     return if (!defined ($dir) || $dir eq "" || $dir eq "/" || $dir eq "/usr");
282     if (! -d $dir)
283       {
284         if ($noisy_warnings)
285           {
286             print_warning ("Warning: directory \"$dir\" present in BOM but not found on disc.\n");
287           }
288         return;
289       }
290
291     if ($do_nothing == 1) 
292       {
293         print_donothing ("rmdir $dir\n");
294       }
295     else
296       {
297         push(@rmdirs, "$dir");
298       }
299   }
300
301 sub remove_package_receipts
302   {
303     my $pkgname = shift;
304     $pkgname =~ s#/##g;  # There shouldn't be any path seps in the pkg name...
305     return if (!defined ($pkgname) || $pkgname eq "" 
306                || $pkgname eq "." || $pkgname eq "..");
307
308     my $pkgdir = "$receipts_dir/$pkgname";
309     return if (!defined ($pkgdir) || $pkgdir eq "" || ! -d $pkgdir);
310
311     push(@rmpkg, "$pkgdir");
312   }
313
314
315 sub maybe_remove_ds_store
316   {
317     my $path = shift;
318     my $filecount = 0;
319     return if (!defined ($path) || $path eq "" || $path eq "/" || $path eq "/usr");
320     return if (! -f "$path/.DS_Store");
321
322     open (LS, "/bin/ls -a '$path' |");
323     while (<LS>)
324       {
325         chomp;
326         next if (m#^\.$# || m#^\.\.$#);
327         $filecount++;
328       }
329     close (LS);
330
331     if ($filecount == 1 && -f "$path/.DS_Store")
332       {
333         remove_a_file ("$path/.DS_Store");
334       }
335   }
336
337 sub print_donothing
338   {
339     my $msg = shift;
340     return if ($print_donothing_removals != 1);
341     pre_print ();
342     print $msg;
343   }
344
345 sub print_verbose
346   {
347     my $msg = shift;
348     return if ($verbose != 1);
349     pre_print ();
350     print $msg;
351   }
352
353 sub print_warning
354   {
355     my $msg = shift;
356     pre_print ();
357     print STDERR $msg;
358   }
359
360 sub print_error
361   {
362     my $msg = shift;
363     pre_print ();
364     print STDERR $msg;
365   }
366
367 sub pre_print 
368   {
369     print " \b" unless ($suppress_spin);
370   }
371
372 sub spin_rate_slow
373   {
374     $spin_slower_downer = 150;
375   }
376
377 sub spin_rate_fast
378   {
379     $spin_slower_downer = 75;
380   }
381
382 sub progress_point
383   {
384     return if ($suppress_spin);
385     $spin_counter++;
386     if (($spin_counter % $spin_slower_downer) == 0)
387       {
388         my $spin_chars = "|/-\\";
389         my $c = substr ($spin_chars, $spin_state % 4, 1);
390         $spin_state++;
391         print "\e[7m$c\e[m\b";
392       }
393   }
394
395 main ();
396
397 #----------------------------------------------------------------------------------------