#!/usr/bin/perl -w
#
# mode.pl, a program for setting permission on files and directories recursively.
# 
# Copyright (c) 2006 Marcus Libäck <marcus@terminal.se>
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

use strict;
use Getopt::Std;

# Variables
# -------------
my (
	%opts, 
	$dirmode, 
	$filemode, 
);

my $recursive = 1;
my $dircount = 0;
my $filecount = 0;
my $version = "1.1-051212";

# Main
# -------------
getopts('d:f:Rh', \%opts);

if (exists $opts{'h'}) {
	help();
}

# Either -f, -d or both must be present.
unless (exists $opts{'f'} or exists $opts{'d'}) {
	help();
}

# Recursive
unless (exists $opts{'R'}) {
	undef $recursive;
}

# Directory mode
if (exists $opts{'d'}) {
	if ($opts{'d'} =~ m/^[0-7]{3,4}$/ ) {
		$dirmode = oct $opts{'d'};
	} else {
		die "Invalid directory mode $opts{'d'}";
	}
}

# File mode
if (exists $opts{'f'}) {
	if ($opts{'f'} =~ m/^[0-7]{3,4}$/ ) {
		$filemode = oct $opts{'f'};
	} else {
		die "Invalid file mode $opts{'f'}";
	}
}

# Run mode() on each file specified in @ARGV.
foreach (@ARGV) {
	if (defined $recursive) {
		mode_r ("$_");
	} else {
		mode_n ("$_");
	}
}
print "Mode adjusted for $dircount directories and $filecount files\n";

# Subroutines
# -------------

# mode_r(), sets recursive permissions.
sub mode_r {
	# Recursive
	my $entry = shift;
	
	if (opendir (DIR, $entry)) {
		# We end up here if $entry is a dir...
		
		if (defined $dirmode ) {
			chmod ($dirmode, $entry) or die $!;
			$dircount++;
		}
		
		foreach (sort readdir (DIR)) {
			next if $_ eq "." or $_ eq "..";
			next if -l $_;
			
			# Recursive magic happens here.
			mode_r ("$entry/$_");
		}
	} else {
		# ...and here if it is a file.
		
		if (defined $filemode) {
			chmod ($filemode, $entry) or die $!;
			$filecount++;
		}
	}
	closedir (DIR);
}

# mode_n(), sets non-recursive permissions.
sub mode_n {
	# Non recursive		
	my $entry = shift;
	
	# Regular file
	if (not -d $entry and defined $filemode) {
		chmod ($filemode, $entry) or die $!;
		$filecount++;
	# Directory, descends into $entry and chmods non-recursive.
	} elsif (-d $entry) {		
		chdir "$entry";
		opendir (DIR, ".");
		foreach (sort readdir (DIR)) {
			next if $_ eq "..";
        	next if -l $_;
			
			if (-d $_ and defined $dirmode) {
				chmod ($dirmode, $_) or die $!;
				$dircount++;
			} elsif (not -d $_ and defined $filemode) {
				chmod ($filemode, $_) or die $!;
				$filecount++;
			}
		}
		closedir (DIR);
		chdir "..";
	}
}

# Help function.
sub help {
	print "Version $version
	
Usage: $0 [options] [files]

Options:
 -h     display this text.

 -R     set modes recursively.
 -d NN  directory mode.
 -f NN  file mode.\n";

	exit;
}
