#!/usr/bin/perl

    # A lastlog record format for use by pack and unpack.

$lastlog_t = "L a8 a16";        # (Your machine's may differ.)

    # Find the length of the fixed-length record.

$LEN = length(pack($lastlog_t, 0, '', ''));

    # Open the database of last logins.  Fixed length records.

open(LASTLOG, "/usr/adm/lastlog")
    || die "Can't open lastlog file: $!\n";

    # Open the database of accounts.  Variable length records.

open(PASSWD, "/etc/passwd")
    || die "Can't open passwd file: $!\n";

    # So we can look up month names later.

@month = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec);

    # Now we iterate through variable length records by using
    # the <> input symbol.  In this particular database, the
    # delimiter just happens to be \n.  How lucky can you get?

while ($varrec = <PASSWD>) {

    # And the field delimiter happens to be a colon.  Amazing!

    ($login, $pass, $uid, $gid, $gcos) = split(/:/, $varrec);

    # Extract name from gcos field portably.

    $fullname = $gcos;
    $fullname =~ s/^[^,]*-(.*)\(.*/$1/ || $fullname =~ s/,.*//;
    if ($fullname =~ /&/) {     # You don't want to know.
	$name = $login;
	substr($name,0,1) =~ tr/a-z/A-Z/;
	$fullname =~ s/&/$name/;
    }

    # Now we random access the lastlog database.  It's keyed
    # by position based on the user id we want to look up.
    # Note that we multiply by the record length, since Unix
    # always wants byte offsets.

    seek(LASTLOG, $uid * $LEN, 0);
    read(LASTLOG, $lastlog, $LEN);

    # Now that we've fetched the lastlog record, we can
    # break it out into its fields.  Note that variable-
    # length fields above are separated with the split
    # operator, while these fixed-length fields are separated
    # using unpack.

    ($time, $line, $host) = unpack($lastlog_t, $lastlog);

    # Now remember it so we can sort easily.

    push(@records, "$time:$login:$uid:$fullname");
}

    # Now sort the records, oldest first.  See the
    # "numerically" subroutine below.

@records = sort numerically @records;

foreach $record (@records) {
    ($time,$login,$uid,$fullname) = split(/:/, $record);

    # Break out the login time, stored as seconds since
    # January 1, 1970, into normal date numbers.

    if ($time) {
	($sec,$min,$hour,$mday,$mon,$year) = localtime($time);
	$year += 1900;
	$date = "$mday $month[$mon] $year";
    }
    else {
	$date = "Never";
    }

    # Write using the format below.

    write;
}

sub numerically { $a <=> $b; }

format top =
  Uid  Login    Full Name                         Last Login

.

format STDOUT =
@>>>>  @<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @||||||||||
$uid,  $login,  $fullname,                        $date
.
