#!/usr/bin/perl -w use Archive::Tar; $Archive::Tar::DO_NOT_USE_PREFIX = 1; use strict; # I found it way too hard to try to make Getopt::Std and Getopt::Long behave as I'd like # It's much easier to just implement command line options parsing by hand... # Careful: you cannot combine several single letter command line options into one, # They must stay separate. my %opt; while(@ARGV && $ARGV[0] =~ /^-/) { $_ = shift; s/^-i// and $opt{inplace} = $_, next; s/^-o// and $opt{output} = ( length $_ ? $_ : shift ), next; /^--?f/ and $opt{force} = 1, next; /^--?g[a-z]*$/ and $opt{glob} = 1, next; /^--?p[a-z]*$/ and $opt{quickfix} = 0, next; /^--?q[a-z]*$/ and $opt{quickfix} = 1, next; /^--?d[a-z]*$/ and $opt{dryrun} = 1, next; /^--?v[a-z]*$/ and $opt{verbose} = 1, next; last if $_ eq '--'; warn "Unknown command line option: '$_'\n" unless /^--?[h?]/; die <<"^USAGE^"; Command: perl $0 [-i|-i.bak|-o saveas.tar.gz|-d] [-p|-q]? [-f,-g,-v]* distro.tar.gz Options: -i, -i.bak inplace fix of source file, optional suffix for name of backup of original file -o filename save fixed distribution as... (file name) Only use this if you only have one parameter file! -d dry run, do not save the output file -v verbose mode, make it list everything it does -g Apply file globbing to argument(s) (for Windows) -p pedantic fix: look at contents of file to guess the correct file mode This merely sets the x bits for scripts, and clears them for other plain files -q quickfix, just clear world writeable bit -f force, save file even if it did not require fixing ^USAGE^ } @ARGV or die "Please provide a '.tar.gz' file as argument"; if($opt{glob}) { @ARGV = map { /[*?]/ ? glob( / /&&!/^"/ ? qq("$_") : $_ ) : $_ } @ARGV; } unless($opt{inplace} || $opt{output} || $opt{dryrun}) { print "As neither option -i nor -o were given, processing mode has been set to dry run\n"; $opt{dryrun} = 1; } while(@ARGV) { my $dist = shift; $dist =~ /\.t(ar\.)?gz$/ or die "Wrong argument: '$dist'; please provide a '.tar.gz' file as argument"; print "Loading distribution '$dist'\n" if $opt{verbose}; my $fixes; my $tar = Archive::Tar->new; $tar->read($dist); my @files = $tar->get_files; foreach my $file (@files) { my $fixedmode = my $mode = $file->mode; my $filetype = ''; if($file->is_file) { $filetype = 'file'; if($opt{quickfix}) { $fixedmode &= ~2; } elsif(substr(${ $file->get_content_by_ref }, 0, 2) eq '#!') { $fixedmode = 0775; } else { $fixedmode = 0664; } } elsif($file->is_dir) { $filetype = 'dir'; if($opt{quickfix}) { $fixedmode &= ~2; } else { $fixedmode = 0775; } } else { next; } next if $mode eq $fixedmode; $file->mode($fixedmode); $fixes++; printf "Change mode %03o to %03o for %s '%s'\n", $mode, $fixedmode, $filetype, $file->name if $opt{verbose}; } if($fixes || $opt{force}) { if($opt{dryrun}) { print "Dry run: file '$dist' would have been patched ($fixes fixes)\n"; } else { rename $dist, "$dist$opt{inplace}" or die "Cannot rename file '$dist' to '$dist$opt{inplace}': $!" if defined $opt{inplace} && length $opt{inplace}; $dist = $opt{output} if $opt{output}; $tar->write($dist, 9); print "File '$dist' saved.\n"; } } else { print "File '$dist' didn't need fixing, skipped.\n"; } }