#!/usr/pkg/bin/perl

=begin metadata

Name: id
Description: show user information
Author: Theo Van Dinter, felicity@kluge.net
License:

=end metadata

=cut


#
# An implementation of the 'id' utility in Perl.  Written for the Perl Power
# Tools (PPT) project by Theo Van Dinter (felicity@kluge.net).
#
# $Id: id,v 1.2 2004/08/05 14:17:43 cwest Exp $
#

use strict;

use File::Basename qw(basename);
use Getopt::Std qw(getopts);

use constant EX_SUCCESS => 0;
use constant EX_FAILURE => 1;

use vars qw($opt_G $opt_n $opt_u $opt_g $opt_r $opt_a $opt_p);

my $Program = basename($0);

getopts('Gnugrap') or help();
if ( ($opt_G + $opt_g + $opt_p + $opt_u) > 1 ) {
	warn "$Program: Choose only one of -G, -g, -p, or -u\n";
	help();
}

my($user,$pw,$uid,$gid,$tp);

if ( @ARGV ) { # user specified
	help() if scalar(@ARGV) > 1;
	($user,$pw,$uid,$gid) = getpwnam $ARGV[0];
	if (!defined($uid) && $ARGV[0] =~ m/\A[0-9]+\Z/) {
		($user,$pw,$uid,$gid) = getpwuid $ARGV[0];
	}
	unless (defined $uid) {
		warn "$Program: $ARGV[0]: No such user\n";
		exit EX_FAILURE;
	}
}

if ( $opt_u ) { # print uid
	$tp = defined $uid ? $uid : $opt_r ? $< : $>;
	$tp = scalar getpwuid $tp || $tp if ( $opt_n );
}
elsif ( $opt_g ) { # print gid
	$tp = defined $gid ? $gid : (split(/\s+/,($opt_r)?$(:$)))[0];
	$tp = scalar getgrgid $tp || $tp if ( $opt_n );
}
elsif ( $opt_p ) { # human-readable form (names when possible, etc.)
	my($rgid,@rgids);
	if ( $user ) {
		$tp.="uid $user\n";
		$tp.="rgid $gid\n";
		@rgids=($gid);
		while ( my($name,$pw,$gid,$members) = getgrent ) {
			push(@rgids,$gid) if ( grep($_ eq $user,split(/\s+/,$members)) );
		}
	}
	else {
		my $login = getlogin;
		unless ($login) {
			warn "$Program: getlogin failed\n";
			exit EX_FAILURE;
		}
		$tp.="login $login\n" if ( $login ne scalar getpwuid $< );

		my($uid) = scalar getpwuid $< || $<;
		$tp.="uid $uid\n";

		my($euid) = scalar getpwuid $> || $>;
		$tp.="euid $euid\n" if ( $< != $> );

		($rgid,@rgids)=split(/\s+/,$();
		my($egid)=split(/\s+/,$));
		my($nrgid) = scalar getgrgid $rgid || $rgid;
		my($negid) = scalar getgrgid $egid || $egid;
		$tp.="rgid $nrgid\n";
		$tp.="egid $negid\n" if ( $rgid != $egid );
	}
	my(%done);
	foreach ( @rgids ) {
		$done{$_} = scalar getgrgid $_ || $_;
	}
	$tp.=join(" ","groups",values %done);
}
elsif ( $opt_G ) { # print full group info
	my(%done);
	my(@rgids);

	if ( $user ) {
		@rgids=($gid);
		while ( my($name,$pw,$gid,$members) = getgrent ) {
			push(@rgids,$gid) if ( grep($_ eq $user,split(/\s+/,$members)) );
		}
	}
	else {
		@rgids = split(/\s+/, $();
	}
	foreach ( @rgids ) {
		if ( $opt_n ) {
			$done{$_} = scalar getgrgid $_ || $_;
		}
		else {
			$done{$_} = $_;
		}
	}
	$tp=join(" ",values %done);
}
else { # uid=#(name) gid=#(name) euid=#(name) egid=#(name) groups=#(name) ...
	my($rgid,@rgids,$egid,$nruid,$neuid,$nrgid,$negid);

	if ( $user ) {
		$egid = $rgid = $gid;
		@rgids=($gid);
		while ( my($name,$pw,$gid,$members) = getgrent ) {
			push(@rgids,$gid) if ( grep($_ eq $user,split(/\s+/,$members)) );
		}
		$nruid = $user;
		$nrgid = scalar getgrgid $gid;
	}
	else {
		($rgid,@rgids)=split(/\s+/,$();
		$egid = (split(/\s+/,$)))[0];
		$nruid = scalar getpwuid $<;
		$neuid = scalar getpwuid $>;
		$nrgid = scalar getgrgid $rgid;
		$negid = scalar getgrgid $egid;
	}

	$tp=join("=","uid",($user)?$uid:$<);
	$tp.=($nruid)?"($nruid) ":" ";

	if ( !($user) && ($< != $>) ) {
		$tp.="euid=$>";
		$tp.=($neuid)?"($neuid) ":" ";
	}

	$tp.=join("=","gid",($user)?$gid:$rgid);
	$tp.=($nrgid)?"($nrgid) ":" ";

	if ( $rgid != $egid ) {
		$tp.="egid=$egid";
		$tp.=($negid)?"($negid) ":" ";
	}

	my(%done);
	$tp.="groups=";
	foreach ( @rgids ) {
		my($i) = scalar getgrgid $_;
		my($i2) = "$_";
		$i2 .= "($i)" if ( $i );
		$done{$_} = "$i2";
	}
	$tp.=join(",",values %done);
}

print "$tp\n";
exit EX_SUCCESS;

sub help {
	require Pod::Usage;
	Pod::Usage::pod2usage({ -exitval => EX_FAILURE, -verbose => 0 });
}

=head1 NAME

id - show user information

=head1 SYNOPSIS

	id [user]
	id -G [-n] [user]
	id -g [-nr] [user]
	id -u [-nr] [user]
	id -p [user]

=head1 DESCRIPTION

id displays the user and group names and numeric IDs of the calling
process.  If the real and effective IDs are different, both are displayed,
otherwise only the real ID is displayed.

If a user (username or user ID) is specified, the information about that user
is displayed instead of the information from the calling process.

=head1 OPTIONS AND ARGUMENTS

=over 4

=item I<-G>
Display all of the group IDs (effective, real, etc) separated by a space.
The IDs are not in a given order.

=item I<-a>
Nothing.  The option is included for compatibility with some versions of id.

=item I<-g>
Display the effective group ID.

=item I<-n>
Force the options C<-G>, C<-g> and C<-u> to display the matching name
instead of the number for the user and group IDs.  If any of the ID
numbers do not have a matching name, the number will be displayed
as usual.

=item I<-p>
Display the user/group information on separate lines.  If the username
returned by getlogin is different than the username associated with
the calling program's user ID, it is displayed preceded by the phrase
"login".  The real ID, effective user ID (if different), real group ID,
and effective group ID (if different) are displayed preceded by "uid",
"euid", "rgid", and "egid" respectively.  Finally, group membership
is displayed with each group separated by a space.  All information is
displayed as names unless there is no name matching the ID.  Then the
ID is shown as usual.

=item I<-r>
Force the options C<-g> and <-u> to display the information associated
with the real user/group IDs instead of the effective ID information.

=item I<-u>
Display the effective user ID.

=back

=head1 EXIT STATUS

id exits with status 0 on success, or 1 if an error occurred.

=head1 AUTHOR

Theo Van Dinter (felicity@kluge.net)

=head1 SEE ALSO

who(1)
