#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use File::Find;
use File::Basename;
# here we store the results
my $target=$ENV{'HOME'} . '/Desktop/OSX-Icons';
# you may change the extensions to use
my @extensions= qw( icns );
###### POD #################################################################{{{
=head1 NAME
findOSXicons - recursively search for all icns files in your system
=head1 SYNOPSIS
B<findOSXicons>
[B<-target> I<targetdirectory>]
[B<-extension> I<ext>]...
[F<startpath>...]
B<findOSXicons>
B<-help>
B<findOSXicons>
B<-man>
=head1 DESCRIPTION
B<findOSXicons> recursively traverses the startpaths (or / if none given)
and creates a directory structure containing links to each and every icon
(files ending with .icns) found.
=head1 EXAMPLE
findOSXicons -target ~/Desktop/MyIcons -extension gif -extension icns ~
=head1 OPTIONS
=over 5
=item B<-extension> I<ext>
As a default the extension I<icns> is searched. with this option you
can specify extensions to use instead. If you give this option several
times, each extension is searched
=item B<-target> I<targetdirectory>
As a default the directory OSX-Icons is created on your desktop. With this
option you can specify which output directory to create instead.
=item B<-help>
Short help
=item B<-man>
Long help
=back
=head1 BUGS
None yet except that you get several warnings for directories you are not
allowed to acces. Use 2E<gt>/dev/null to suppress them.
=head1 ACKNOLEDGEMENTS
findOSXicons was entirely written by Skeeve
(findOSXiconsZ<>.Z<>questionsZ<>.Z<>skeeveZ<>@Z<>xoxyZ<>.Z<>net)
It was inspired by several user requests in the german apple forum at
L<http://www.apfeltalk.de/forum>.
=cut
###### END: POD ############################################################}}}
##### commandline options ##################################################{{{
# defaults
&help unless GetOptions(
"help" => \&help,
"man" => \&man,
'target=s' => \$target,
'extension=s' => \@extensions,
);
sub help { exit pod2usage( verbose => 1 ); };
sub man { exit pod2usage( verbose => 2 ); };
##### end commandline options ##############################################}}}
# append a trailing slash
$target=~ s#([^/])$#/#;
# and calculate the length
my $target_length= length($target);
# check which extensions to find
die "No extension specified\n" unless scalar @extensions;
my $extensions= join ('|',map quotemeta,@extensions);
$extensions= qr/(\.(?:$extensions))$/;
# Statistics etc.
my @removed; # list of removed icons
my $count_old= 0; # icons we know of
my $count_new= 0; # new icons
my $count_oapp= 0; # application icons we know of
my $count_napp= 0; # new application icons
# create the target directory unless it exists
mkdir $target or die "Can't create $target: $!\n" unless -d $target;
# hash of files already linked
my %stat;
# build the hash.
find ( {
wanted => sub {
# get the link target
my $lnk= readlink $File::Find::name;
# No link target? Then skip this.
return unless defined $lnk;
# Does the target exist?
if ( -e $lnk ) {
# build the hash
++$stat{$lnk};
# do some statistics
++$count_old;
# is it an applicationicon?
++$count_oapp if $lnk=~ m#/[^/]+\.app/#;
}
else {
# target is missing so remove the link
unlink $File::Find::name;
# and remeber that it was removed
push(@removed, $lnk);
}
},
no_chdir => 1,
}, $target);
# now find
find( {
wanted => sub {
# just files
return unless -f $File::Find::name;
# need to get the basename (no_chdir!)
my $fname= basename $_;
# correct extension?
return unless $fname=~ s#$extensions##;
my $ext= $1;
# we don't want our target to be checked
return if substr($File::Find::dir, 0, $target_length) eq $target;
# check whether or not we already know of this file
return if $stat{$File::Find::name}++;
++$count_new;
# create the subdir in the target dir
my $link_dir= $target;
# 2 possibilities
if ( m#/([^/]+)\.app/# ) { # is it an Application's icon?
# we put it into the Applications-subdirectory
$link_dir.= 'Applications';
mkdir $link_dir or die "Can't create $link_dir: $!\n" unless -d $link_dir;
# into a directory for the application
$link_dir.= "/$1";
++$count_napp;
}
else {
# we put it into an "alphabetical" subdirectory
$link_dir.= uc substr($fname,0,1);
}
# create the directory
mkdir $link_dir or die "Can't create $link_dir: $!\n" unless -d $link_dir;
# now calculate the filename
my $link_name= $link_dir . '/' . $fname;
my $count= '';
--$count while( -e "$link_name$count$ext");
# now link;
symlink $File::Find::name, "$link_name$count$ext";
},
no_chdir => 1,
follow => 0,
}, scalar @ARGV ? @ARGV : '/');
# Statistics
my $icon_count= $count_old + $count_new;
my $app_count= $count_oapp + $count_napp;
my $else_count= $icon_count-$app_count;
my $else_new= $count_new - $count_napp;
my $else_old= $count_old - $count_oapp;
my $miss_count= scalar @removed || 'none';
print <<REPORT;
All icons known: $icon_count
Application icons: $app_count
Non-application icons: $else_count
REPORT
print <<REPORT if $count_new;
New icons found: $count_new
Application icons: $count_napp
Non-application icons: $else_new
Old icons: $count_old
Application icons: $count_oapp
Non-application icons: $else_old
REPORT
print <<REPORT;
Icons removed since last run: $miss_count
REPORT
print "$_\n" foreach (sort @removed);