split-packages.pl

Revised: 2006-10-03 devin

Download Script – ZIP-File, 1.0 KB

Introduction

This is a perl script that will take a createable transcript (transcript.T) and split it into the following transcripts:
  • transcript-pkgname.pkg.T for each package that was installed, containing only the files mentioned in the package
  • transcript-orphans.T for each file that wasn't mentioned in a package
  • transcript-deletes.T for all the "- f removedfile" entries, since they can't be attributed to packages
Note that packages that have the same file mentioned will each have entries for that file.

To use it, first create a transcript with fsdiff -C -o transcript.T and then run this script on it.

The advantage of splitting up an update into its packages is cleanliness and modularity. Because each package transcript is self-contained, there will be less/no dependencies on other packages and you can easily include or exclude packages. (For instance, only install the developer documentation for some workstations)

Script

split-packages.pl
#!/usr/bin/perl
#
# split a transcript in transcripts per package
#
# If there are no package listings found, exits with error code 5
#
#
#
# IDEAS
# - server switch, use .bom files from server dirs

use strict;
my $transcript=$ARGV[0];
my $trname=$transcript;
$trname =~ s/.T$//i;

sub to_radmind
{
    $_[0] =~ s/\/\\/g;
    $_[0] =~ s/ /\b/g;
    $_[0] =~ s/ /\r/g;
    $_[0] =~ s/ /\n/g;
    return $_[0];
}

sub from_radmind
{
    $_[0] =~ s/\b/ /g;
    $_[0] =~ s/\r/ /g;
    $_[0] =~ s/\n/ /g;
    $_[0] =~ s/\\/\/g;
    return $_[0];
}

my @boms;
open (TR, $transcript) or die "Couldn't open transcript $transcript for reading: $!";
while(<TR>) {
    chomp;
    split;
    if ($_[0] eq 'f' and $_[1] =~ //Library/Receipts/.*.bom$/i) {
        push @boms, from_radmind($_[1]);
    }
}
exit 5 if $#boms == -1;

seek TR, 0, 0 or die;

my %knownfiles;
my %pkgnames;
foreach my $bom (@boms) {
    open(BOM, "lsbom "$bom"|");
    $bom =~ /^.*/([^/]+)/Contents//;
    my $pkgname=$1;
    $pkgnames{$pkgname}=1;
    $pkgname=",$pkgname,";
    while(<BOM>) {
        chomp;
        s/ .*$//;
        next if $_ eq ".";
        s/^.//;
        $knownfiles{to_radmind($_)}.=$pkgname;
    }
    close BOM;
}

use IO::Handle;
my %pkgfiles;
foreach my $pkg (keys %pkgnames) {
    $pkgfiles{$pkg} = IO::Handle->new();
    open ($pkgfiles{$pkg}, ">$trname-$pkg.T") or die "Couldn't open $trname-$pkg.T for writing: $!";
}
open(DELS,">$trname-deletes.T") or die "Couldn't open deletes file for writing: $!";
open(ORPHANS,">$trname-orphans.T") or die "Couldn't open orphans file for writing: $!";

my $owners;
my $line;
while(<TR>) {
    chomp;
    split;
    $line=$_." ";
    if ( $_[0] eq "-") {
        print DELS $line;
    } elsif ( $owners = $knownfiles{$_[1]} ) {
        foreach my $pkg (keys %pkgfiles) {
            if ($owners =~ /,$pkg,/i) {
                my $fh = $pkgfiles{$pkg};
                print $fh $line or warn "Couldn't write to transcript $pkg: $!";
            }
        }
    } elsif ( $_[1] =~ /^/Library/Receipts/([^/]+)/ ) {
        my $pkg = $1;
        if (exists($pkgfiles{$pkg})) {
            my $fh = $pkgfiles{$pkg};
            print $fh $line or warn "Couldn't write to transcript $pkg: $!";
        } else {
            print ORPHANS $line;
        }
    } else {
        print ORPHANS $line;
    }
}
print STDERR "All done, please look at $trname-*.T ";