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