1#!/usr/bin/perl -w
2
3use strict;
4use Getopt::Long;
5use File::Path;
6use File::Basename;
7
8my $SETFILE = '/Developer/Tools/SetFile';
9
10my $verbose = 1;
11my $quiet = ($verbose ? '' : '-quiet');
12my $rmtmp = 0;
13my $over = 0;
14my $mountpoint = '';
15my $bgimg = '';
16my $volicon = '';
17my $volname = 'OpenNX';
18my $tmpdmg = '';
19my $iconsize = 48;
20my $textsize = 12;
21my $foptfile = '';
22my %windowsize = (
23    'w' => 400,
24    'h' => 300,
25);
26my %windowpos = (
27    'x' => 100,
28    'y' => 50,
29);
30my %fopts;
31
32sub chkrun($) {
33    my $cmd = shift;
34    system($cmd);
35    if ($? == -1) {
36        die "aCan't run $cmd: $!\n";
37    } elsif ($? & 127) {
38        die "child died with signal %d, %s coredump\n",
39        ($? & 127),  ($? & 128) ? 'with' : 'without';
40    }
41    if (($? >> 8) != 0) {
42        die "bCan't run $cmd: $!\n";
43    }
44}
45
46sub usage($) {
47    my $err = shift;
48    print STDERR<<EOF
49Usage: [OPTIONS] output.dmg <source folder>
50  Available OPTIONS:
51    -h|--help            Print this text
52    -n|--name NAME       Use specified volume NAME
53    -m|--mountpoint PATH Use specified PATH as temporary mountpoint
54    -b|--bgimage FILE    Use specified FILE as background image.
55    -o|--overwrite       Overwrite the dmg file.
56    -i|--icon FILE       Use specified FILE as volume Icon.
57    -f|--fileopts FILE   Read icon options from specified FILE.
58EOF
59;    exit($err);
60}
61
62sub runscript($) {
63    my $script = shift;
64    print "Running AppleScript:\n", $script, "\n" if ($verbose);
65    open(P, "|osascript") || die "Can't run osascript: $!\n";
66    print P $script;
67    close P;
68}
69
70sub imgsize($) {
71    my $imgfile = shift;
72    my $w = -1;
73    my $h = -1;
74    if ($imgfile ne '') {
75        open(P, "sips -g pixelHeight -g pixelWidth $imgfile 2>/dev/null |");
76        while (<P>) {
77	    chomp;
78            if (/\s*pixelHeight:\s*(\d+)/) {
79                $windowsize{'h'} = int($1);
80            }
81            if (/\s*pixelWidth:\s*(\d+)/) {
82                $windowsize{'w'} = int($1);
83            }
84        }
85        close P;
86    }
87}
88
89sub readfopts() {
90    if ($foptfile ne '') {
91        open(F, "<$foptfile") || die "Can't read $foptfile: $!\n";
92        while (<F>) {
93            chomp;
94            next if (/^\s*$/);
95            next if (/^\s*\#/);
96            if (/^(\d+)\s+(\d+)\s+([EVev\-]+)\s+(.*)$/) {
97                $fopts{$4} = {'x' => $1, 'y' => $2, 'o' => $3};
98            }
99        }
100        close F;
101    }
102}
103
104sub window_bounds() {
105    my $x2 = $windowpos{'x'} + $windowsize{'w'};
106    my $y2 = $windowpos{'y'} + $windowsize{'h'};
107    return $windowpos{'x'} . ", " .
108        $windowpos{'y'} . ", " . $x2 . ", " .  $y2;
109}
110
111sub file_positions() {
112    my $ret = '';
113    foreach (keys %fopts) {
114        $ret .= 'set position of item "' . $_ .
115            '" to {' . ${$fopts{$_}}{'x'} . ', ' . ${$fopts{$_}}{'y'} . "}\n";
116    }
117    return $ret;
118}
119
120sub file_options() {
121    my $ret = '';
122    foreach (keys %fopts) {
123        my $o = ${$fopts{$_}}{'o'};
124        my $f = "$mountpoint/$_";
125        next if ($o eq '-');
126        next if (! -e $f);
127        chkrun($SETFILE.' -a '.$o.' "'.$f.'"');
128    }
129    return $ret;
130}
131
132sub END() {
133    system("sudo hdiutil detach $mountpoint $quiet -force >/dev/null 2>&1");
134    rmtree($mountpoint) if $rmtmp;
135    rmtree($tmpdmg) if ($tmpdmg ne '');
136}
137
138usage(1) unless GetOptions(
139    "name=s"        => \$volname,
140    "bgimg=s"       => \$bgimg,
141    "icon=s"        => \$volicon,
142    "fileoptions=s" => \$foptfile,
143    "overwrite"     => \$over,
144    "help"          => sub { usage(0); },
145);
146if ($#ARGV < 1) {
147    print STDERR "Missing required argument\n";
148    usage(1);
149}
150if ($#ARGV > 1) {
151    print STDERR "Too many arguments\n";
152    usage(1);
153}
154# This MUST be below /Volumes, because Finder expects it there
155$mountpoint = '/Volumes/'.$volname;
156my $out = shift;
157my $src = shift;
158$tmpdmg = "$out$$.dmg";
159unlink($out) if ($over);
160unlink($tmpdmg) if ($over);
161die "Background image does not exist\n" unless (($bgimg eq '') || (-e $bgimg));
162die "Volume icon does not exist\n" unless (($volicon eq '') || (-e $volicon));
163die "Disk image $out already exists\n" if (-e $out);
164die "Source folder $src does not exist" unless (-d $src);
165$rmtmp = (! -d $mountpoint);
166imgsize($bgimg);
167readfopts();
168mkpath($mountpoint, 0, 0755);
169die "Could not create temporary mountpoint $mountpoint: $!\n" unless (-d $mountpoint);
170chkrun("sudo hdiutil create -format UDRW $quiet -mode 0777 -volname '".$volname."' -srcfolder '".$src."' '".$tmpdmg."'");
171chkrun("sudo hdiutil attach '".$tmpdmg."' -mountpoint '".$mountpoint."' -noautoopen $quiet");
172#chkrun("bless --folder '".$mountpoint."' --openfolder '".$mountpoint."'");
173if ($bgimg ne '') {
174    mkpath("$mountpoint/.bg$$", 0, 0755);
175    chkrun('cp "'.$bgimg.'" "'.$mountpoint.'/.bg'.$$.'"');
176    $bgimg = basename($bgimg);
177}
178if ($volicon ne '') {
179    chkrun('cp "'.$volicon.'" "'.$mountpoint.'/.VolumeIcon.icns"');
180    chkrun($SETFILE.' -a C "'.$mountpoint.'"');
181}
182my $script=<<SCRIPT;
183    tell application "Finder"
184        set mountpoint to POSIX file ("$mountpoint" as string) as alias
185        tell folder mountpoint
186            open
187            tell container window
188                set toolbar visible to false
189                set statusbar visible to false
190                set current view to icon view
191                set the bounds to {@{[window_bounds]}}
192            end tell
193            set icon size of the icon view options of container window to $iconsize
194            set text size of the icon view options of container window to $textsize
195            set arrangement of the icon view options of container window to not arranged
196            @{[file_positions]}
197            set the bounds of the container window to {@{[window_bounds]}}
198            set background picture of the icon view options of container window to file ".bg$$:$bgimg"
199            update without registering applications
200            close
201        end tell
202    end tell
203SCRIPT
204runscript($script);
205file_options();
206if ($bgimg ne '') {
207    chkrun($SETFILE.' -a V "'.$mountpoint.'/.bg'.$$.'"');
208}
209chkrun("sudo hdiutil detach $mountpoint $quiet -force");
210chkrun("sudo hdiutil convert '".$tmpdmg."' $quiet -format UDZO -imagekey zlib-level=9 -o '".$out."'");
211