#!/usr/bin/perl
##
## cow{say,think}
##
## This file is part of cowsay.  (c) 1999-2002 Tony Monroe.
##

use strict;

my $rcs_id = q$Id: cowsay,v 1.4 2002/03/23 01:38:43 tony Exp $;

use Acme::Cow;
use File::Basename;
use File::Spec;
use Getopt::Std;

my $VERSION = "4.0";
my $progname = basename($0);
my $cowpath = $ENV{'COWPATH'} || "/usr/local/share/cows";

## One of these days, we'll get it ported to Windows.  Yeah, right.

my $pathsep;

if (($^O eq "MSWin32") or ($^O eq "Windows_NT")) {	## Many perls, eek!
    $pathsep = ';';
} else {
    $pathsep = ':';
}

my %opts = (
    'e'		=>	'oo',
    'n'		=>	0,
    'T'		=>	'  ',
    'W'		=>	40,
);

getopts('bde:f:ghlLnNpstT:vwW:y', \%opts);

&display_usage() if ($opts{'h'} or $opts{'v'} or $opts{'?'});
&list_cowfiles() if $opts{'l'};

my $borg = $opts{'b'};
my $dead = $opts{'d'};
my $greedy = $opts{'g'};
my $paranoid = $opts{'p'};
my $stoned = $opts{'s'};
my $tired = $opts{'t'};
my $wired = $opts{'w'};
my $young = $opts{'y'};
my $eyes = substr($opts{'e'}, 0, 2);
my $tongue = substr($opts{'T'}, 0, 2);

my $cow;

my @text = &slurp_input();
if ($opts{'f'}) {
    $cow = &variant_cow($opts{'f'});
} else {
    $cow = new Acme::Cow();
}
&construct_face($cow);
if (index($0, "think") != -1) {
    $cow->think(@text);
} else {
    $cow->say(@text);
}
$cow->fill(not $opts{'n'});
$cow->wrap($opts{'W'});
$cow->print();

sub list_cowfiles {
    my $basedir;
    for my $d (split(/$pathsep/, $cowpath)) {
	print "Cow files in $d:\n";
	opendir(COWDIR, $d) || die "$0: Cannot open $d\n";
	for my $file (readdir COWDIR) {
	    if ($file =~ s/\.cow$//) {
		print $file, "\n";
	    }
	}
	closedir(COWDIR);
    }
    print "Cow files installed as modules:\n";
    for my $d (@INC) {
	my @files = glob("$d/Acme/Cow/*.pm");
	for my $file (@files) {
	    next if (basename($file) eq "TextBalloon.pm");
	    print basename($file, ".pm"), "\n";
	}
    }
    exit(0);
}

sub slurp_input 
{
    my @message;
    unless ($ARGV[0]) {
	chomp(@message = <STDIN>);
    } else {
	&display_usage if $opts{'n'};
	@message = join(' ', @ARGV);
    }
    return @message;
}

sub construct_face 
{
    my $cow = shift;
    if ($borg) { $eyes = "=="; }
    if ($dead) { $eyes = "xx"; $tongue = "U "; }
    if ($greedy) { $eyes = "\$\$"; }
    if ($paranoid) { $eyes = "@@"; }
    if ($stoned) { $eyes = "**"; $tongue = "U "; }
    if ($tired) { $eyes = "--"; } 
    if ($wired) { $eyes = "OO"; } 
    if ($young) { $eyes = ".."; }
    $cow->{'el'} = substr($eyes, 0, 1);
    $cow->{'er'} = substr($eyes, 1, 1);
    $cow->{'U'} = $tongue;
}

sub variant_cow 
{
    my $f = shift;
    my $full;
    my $fn;
    if ($f =~ /^\w[\d\w]+$/) {
	my $package = "Acme::Cow::$f";
	eval "require $package;";
	if (not $@) {
	    return $package->new();
	}
    }
    if ($f =~ m,/,) {
	$full = $f;
    } else {
	for my $d (split(/$pathsep/, $cowpath)) {
	    $fn = File::Spec->catfile($d, $f);
	    if (-f $fn) {
		$full = $fn;
		last;
	    } elsif (-f "$fn.cow") {
		$full = "$fn.cow";
		last;
	    }
	}
	if (not $full) {
	    die "$progname: Could not find $f cow variant!\n";
	}
    }
    return new Acme::Cow(File => $full);
}

sub display_usage {
	die <<EOF;
cow{say,think} version $VERSION, (c) 1999-2002 Tony Monroe

Usage: $progname [-bdgpstwy] [-hv?] [-e eyes] [-f cowfile] [-l] [-n] 
	[-T tongue] [-W wrapcolumn] [message]

$rcs_id
EOF
}
__END__
=pod

=head1 NAME

cow{say,think} - A configurable talking (or thinking) cow

=head1 SYNOPSIS

Usage: $progname [B<-bdgpstwy>] [B<-hv?>] [B<-e> eyes] [B<-f> I<cowfile>] 
          [B<-l>] [B<-n>] [B<-T> I<tongue>] [B<-W> I<wrapcolumn>] [I<message>]

=head1 DESCRIPTION

generates an ASCII picture of a cow saying something provided by
the user.  If run with no arguments, it accepts standard input,
word-wraps the message given at about 40 columns, and prints the
cow saying the given message on standard output.

To aid in the use of arbitrary messages with arbitrary whitespace,
B<-n> option.  If it is specified, the given message will not be
word-wrapped.  This is possibly useful if you want to make the cow
think or speak in B<figlet(6)>.  If B<-n> is specified, there must
not be any command-line arguments left after all the switches have
been processed.

The B<-W> specifies roughly (where the message should be wrapped.
The default is equivalent to invoking with B<-W 40>, i.e. wrap
words at or before the 40th column.

If any command-line arguments are left over after all switches have
been processed, they become the cow's message.  The program will
not accept standard input for a message in this case.

There are several provided modes which change the appearance of
the cow depending on its particular emotional/physical state.  The
B<-b> option initiates Borg mode; B<-d> causes the cow to appear
dead; B<-g> invokes greedy mode; B<-p> causes a state of paranoia
to come over the cow; B<-s> makes the cow appear thoroughly stoned;
B<-t> yields a tired cow; B<-w> is somewhat the opposite of B<-t>,
and initiates wired mode; B<-y> brings on the cow's youthful
appearance.

The user may specify the B<-e> option to select the appearance of
the cow's eyes, in which case the first two characters of the
argument string I<eyes> will be used.  The default eyes are 'oo'.
The tongue is similarly configurable through B<-T>; it must be two
characters and does not appear by default.  However, it does appear
in the 'dead' and 'stoned' modes.  Any configuration done by B<-e>
and B<-T> will be lost if one of the provided modes is used.

The B<-f> option specifies a particular cow picture file ("cowfile")
to use.  If the cowfile spec contains a slash ("/") then it will
be interpreted as a path relative to the current directory.
Otherwise, cowsay will first search C<@INC> for installed cows
under the C<Acme::Cow::> hierarchy, and then search the path
specified in the C<COWPATH> environment variable.  To list all
cowfiles installed as modules (in C<@INC>) and on the current
C<COWPATH>, invoke cowsay with the B<-l> switch.

If the program is invoked as cowthink (or any word that has "think"
as a substring) then the cow will think its message instead of
saying it.

If you would like to write your own cow files, please consult
L<Acme::Cow> and the cowfiles provided with the distribution (in
the C<cows> subdirectory).

=head1 WARNINGS

Cow files for cowsay 4 are not compatible with earlier versions;
they must be converted.

The author is not responsible if people are offended or institutionalized
because of the use of this software.  (The author is also not
responsible in general, but that's a separate matter.)

=head1 SEE ALSO

perl(1), figlet(6), L<Acme::Cow>

=head1 AUTHOR

Tony Monroe E<lt>tmonroe plus perl at nog dot netE<gt>.  Has been called
a freak many times for even thinking of such a piece of software,
let alone writing it.

The following have contributed cow files, directly or indirectly: 

Shannon Appel, Jordan K Hubbard, Donald Kubasak, Marshall Kirk
McKusick, Lincoln Myers, David Petrou, Anthony S Polito, Geordan
Rosario, Eric Rowe, Lars Smith, E<lt>pborys at p-soft dot silesia
dot linux dot org dot plE<gt>.

If your name is not on this list, and it should be, then feel free
to mail the author and remind him gently.

=head1 BUGS

tend to hang around cows.  Cows sometimes try to shoo them away
with their tails.

Bugs should be reported to the author as soon as they are found,
so he may correct them, or quickly write them off as user error.
:-)

=head1 HISTORY

Cowsay 1 (1996?) was a very limited cow, which only said things in
a particular format: I<subject> is I<verb>ing I<object>.  Cowsay
2 (1997?) overcame that limitation, and added alternate cow files,
and thought capabilities.  Cowsay 3 (1999) was a rewrite in Perl
5.  Cowsay 4 (2002) leverages Perl's object facilities to make the
author's life easier.
