Dave Cross: Still Munging Data With Perl: Online event - Mar 17 Learn more

#!/usr/local/bin/perl -w
use strict;
my ($VERSION) = '1.2';
my $warnings = 0;
# Print a usuage message on a unknown option.
# Requires my patch to Getopt::Std of 25 Feb 1999.
$SIG {__WARN__} = sub {
if (substr ($_ [0], 0, 14) eq "Unknown option") {die "Usage"};
require File::Basename;
$0 = File::Basename::basename ($0);
$warnings = 1;
warn "$0: @_";
};
$SIG {__DIE__} = sub {
require File::Basename;
$0 = File::Basename::basename ($0);
if (substr ($_ [0], 0, 5) eq "Usage") {
die <<EOF;
$0 (Perl bin utils) $VERSION
$0 [-R [-H | -L | -P]] mode file [files ...]
EOF
}
die "$0: @_";
};
# Get the options.
# We can't use Getopts, as the order is important.
my %options;
while (@ARGV && $ARGV [0] =~ /^-/) {
my $opt = reverse shift;
chop $opt;
if ($opt eq '-') {shift; last;}
die "Usage" unless $opt =~ /^[RHLP]+$/;
local $_;
while (length ($_ = chop $opt)) {
/R/ && do {$options {R} = 1; next};
die "Usage" unless $options {R};
/H/ && do {$options {L} = $options {P} = 0; $options {H} = 1; next};
/L/ && do {$options {H} = $options {P} = 0; $options {L} = 1; next};
/P/ && do {$options {H} = $options {L} = 0; $options {P} = 1; next};
}
}
die "Usage" unless @ARGV > 1;
my $mode = shift;
my $symbolic = 0;
if ($mode =~ /[^0-7]/) {
require SymbolicMode;
$symbolic = 1;
}
elsif ($mode !~ /^[0-7]{1,4}$/) {
die "invalid mode: $mode\n"
}
my %ARGV;
%ARGV = map {$_ => 1} @ARGV if $options {H};
sub modify_file;
if (exists $options {R}) {
# Recursion.
require File::Find;
File::Find::find (\&modify_file, @ARGV);
}
else {
foreach my $file (@ARGV) {
modify_file $file;
}
}
# File::Find is weird. If called with a directory, it will call
# the sub with "." as file name, while having chdir()ed to the
# directory. But it doesn't do that in recursion, just the top
# level ones. And it ain't true that $File::Find::name eq
# "$File::Find::dir/$_" in all cases.
# But it shouldn't matter in this case.
sub modify_file {
my $file = @_ ? shift : $_;
# Now, if this is a symbolic link, it points somewhere,
# *and* we are following symbolic links, we recurse.
# This may never end as symlinks can form loops.
if (-l $file && -e $file &&
($options {L} || $options {H} && $ARGV {$file})) {
# We don't want to recurse symlinks that just happen to
# have the same name as one of the arguments, hence the local.
# Remember that $file is relative to the current directory.
local $ARGV {readlink $file} = 0;
File::Find::find (\&modify_file, readlink $file);
return;
}
unless (-e $file) {
warn "$file does not exist\n";
return;
}
my $realmode = $mode;
if ($symbolic) {
$realmode = SymbolicMode::mod ($mode, $file) or
die "invalid mode: $mode\n";
}
chmod oct ($realmode), $file or warn "$!\n";
}
exit $warnings;
__END__
=pod
=head1 NAME
chmod - change permissions of files
=head1 SYNOPSIS
B<chmod> [B<-R> [B<-H> | B<-L> | B<-P>]] I<mode> I<file> [I<files> ...]
=head1 DESCRIPTION
B<chmod> sets the permissions of files. The first argument after the
options is permission the files should be given.
=head2 OPTIONS
B<chmod> accepts the options described below. The options B<-L>,
B<-H> and B<-P> are mutally exclusive, and only the last given
option will be honoured. All of B<-L>, B<-H> and B<-P> require the
B<-R> option to be set first.
=over 4
=item B<-R>
Recurse into directories. Any directories are recursively traversed,
and all files and directories will change owner.
=item B<-L>
Follow symbolic links. By default, B<chmod> will not follow symbolic
links. This is a potential dangerous option, as B<chmod> will not
check for cycles. Be careful. This option requires the B<-R> option to be set.
=item B<-H>
Follow symbolic links of command line files/directories only. This option
requires the B<-R> option to be set.
=item B<-P>
Do not follow symbolic links at all. This option requires the B<-R> option
to be set.
=back
=head2 MODES
I<Mode>s are either absolute, or symbolic. An absolute I<mode> is an octal
number, calculated by OR-ing the following values:
=for html <DL>
<DT><STRONG>4000</STRONG><DD><P>Suid on execution.</DD>
<DT><STRONG>2000</STRONG><DD><P>Guid on execution.</DD>
<DT><STRONG>1000</STRONG><DD><P>The <EM>sticky(8)</EM> bit is turned on.</DD>
<DT><STRONG>0400</STRONG><DD><P>Read permission for the ownwer of the file.</DD>
<DT><STRONG>0200</STRONG><DD>
<P>Write permission for the ownwer of the file.</DD>
<DT><STRONG>0100</STRONG><DD>
<P>Execute permission for the ownwer of the file.</DD>
<DT><STRONG>0040</STRONG><DD>
<P>Read permission for those in the group as the group of the file.</DD>
<DT><STRONG>0020</STRONG><DD>
<P>Write permission for those in the group as the group of the file.</DD>
<DT><STRONG>0010</STRONG><DD>
<P>Execute permission for those in the group as the group of the file.</DD>
<DT><STRONG>0004</STRONG><DD><P>Read permission for all others.</DD>
<DT><STRONG>0002</STRONG><DD><P>Write permission for all others.</DD>
<DT><STRONG>0001</STRONG><DD><P>Execute permission for all others.</DD>
=for html </DL><!--
=over
=item B<4000>
Suid on execution.
=item B<2000>
Guid on execution.
=item B<1000>
The I<sticky(8)> bit is turned on.
=item B<0400>
Read permission for the owner of the file.
=item B<0200>
Write permission for the owner of the file.
=item B<0100>
Execute permission for the owner of the file.
=item B<0040>
Read permission for those in the group as the group of the file.
=item B<0020>
Write permission for those in the group as the group of the file.
=item B<0010>
Execute permission for those in the group as the group of the file.
=item B<0004>
Read permission for all others.
=item B<0002>
Write permission for all others.
=item B<0001>
Execute permission for all others.
=back
=for html -->
Symbolic I<mode> is a comma separated list of I<action>s. Each I<action> has
the following format:
=for html <PRE>
[<EM>who</EM>] <EM>operator</EM> [<EM>permissions</EM>] [<EM>operator</EM> [<EM>permissions</EM>] ...]
</PRE> <!--
S< [I<who>] I<operator> [I<permissions>] [I<operator> [I<permissions>] ...]>
=for html -->
I<who> consists of zero or more of the following symbols:
=over
=item B<u>
Permissions for the user (owner) of the file.
=item B<g>
Permissions for the group of the file.
=item B<o>
Permissions for all others.
=item B<a>
Permissions for everyone.
=back
If I<who> is omitted, it will default to B<a>, but the value of your
I<umask> is taken into account. B<chmod> will then not set a permission
that is masked by your I<umask>.
The I<operator> is one of:
=over
=item B<+>
Add permissions. If no I<permission>s are given, this action
is ignored. If I<who> is absent, set the permission bit as
indicated by I<permission>s, but respect the I<umask> settings.
If I<who> is given, set the permission bits as indicated by
I<permission>s, for those groups indicated by I<who>.
=for html <DT><STRONG><A NAME="item_5">-</A></STRONG><DD><!--
=item B<E<45>>
=for html -->
Revoke permissions. If no I<permission>s are given, this action
is ignored. If I<who> is absent, revoke the I<permission> bit as
indicated by permissions, but respect the umask settings.
If I<who> is given, revoke the permission bits as indicated by
I<permission>s, for those groups indicated by I<who>.
=for html <DT><STRONG><A NAME="item_1">-</A></STRONG><DD><!--
=item B<E<61>>
=for html -->
Set permissions. The permission bits indicated by I<who> are
all cleared. If I<who> isn't given, all permission bits are
cleared. Then, if I<who> isn't given, those permission bits
in I<permission> whose corresponding bit in the I<umask> is clear
are set. Otherwise, the permission bits are set as indicated
by I<who> and I<permission>.
=back
I<permission> consists of zero or more of:
=over
=item B<r>
The read permission bit.
=item B<w>
The write permission bit.
=item B<x>
The execute permission bit.
=item B<X>
The execute permission bit, but only if the target is either a directory,
or has at least one execution bit set in the unmodified permission
bits. Furthermore, this permission is ignored if I<operator> is either
B<E<45>> or B<E<61>>.
=item B<s>
The suid and guid bit. These will have no effect in combination
with the B<o> I<who> setting.
=item B<t>
The sticky bit. This makes sense only for the others group,
however, it will be ignored if B<o> is the only group in the
I<who> part of the I<clause>.
=item B<u>
The original permissions of the user.
=item B<g>
The original permissions of the group.
=item B<o>
The original permissions for others.
=back
=head1 ENVIRONMENT
The working of B<chmod> is not influenced by any environment variables.
=head1 BUGS
It is unlikely there are no bugs. The documentation is at best ambiguous.
The OpenBSD documentation does not match the OpenBSD implementation.
Furthermore, the implementations of Solaris, SunOS, HP, and GNU all differ
from each other, and from OpenBSD.
This manual page needs work. The module I<SymbolicMode> needs to be
documented.
B<chmod> parses a symbolic mode once for each file. That is too much
redundant work.
B<chmod> can loop forever when symbolic links create cycles.
B<chmod> uses I<File::Find> to recurse.
This manual should have been written in Texinfo, LaTeX, or a funky
SGML application. B<pod2man>, B<pod2html>, B<pod2text> and B<pod2latex>
are all broken beyond belief.
=head1 STANDARDS
It does not make sense to talk about standards in a chmod manual page.
=head1 REVISION HISTORY
$Log: chmod,v $
Revision 1.2 2004/08/05 14:17:43 cwest
cleanup, new version number on website
Revision 1.1 2004/07/23 20:10:01 cwest
initial import
Revision 1.3 1999/03/09 02:44:57 abigail
Fixed SybolicMode -> SymbolicMode typo.
Revision 1.2 1999/03/08 03:07:28 abigail
Major documentation tweaks.
Revision 1.1 1999/03/07 12:03:54 abigail
Initial revision
=head1 AUTHOR
The Perl implementation of B<chmod> was written by Abigail, I<abigail@fnx.com>.
=head1 COPYRIGHT and LICENSE
This program is copyright by Abigail 1999.
This program is free and open software. You may use, copy, modify, distribute,
and sell this program (and any modified variants) in any way you wish,
provided you do not restrict others from doing the same.
=cut