#!/usr/bin/perl -w use strict; use Term::ReadLine; use Term::ANSIColor qw(:constants uncolor); use constant COW_NORMAL => ''; use constant COW_DEAD => '-d'; use constant TRUE => 1; use constant FALSE => 0; sub pick_colour { # Set the text colour to something pleasant my @colours = (RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE); my $colour = $colours[int(rand() * @colours)]; print BOLD if (rand() > 0.5); print $colour; } sub cowsay { my $text = shift; my $flags = shift || COW_NORMAL; chomp $text; return if ($text eq ''); my $cow_pipe = ''; my $cow_depth = $ENV{'COW_DEPTH'}; for (my $i = 1; $i < $cow_depth; $i++) { $cow_pipe .= '| cowsay -n'; } pick_colour() unless $ENV{'COW_BORING'}; open COW, "|cowsay $flags $cow_pipe"; print COW $text; close COW; print RESET; } sub sensible_cowsay { my $lines_ref = shift; my $flags = shift || COW_NORMAL; # If any output line contains more than one word # then print each line in a separate cow; otherwise # join the lines together with spaces. my @lines = @$lines_ref; my $join_together = TRUE; foreach my $line (@lines) { my @tokens = split /\s/, $line; if (@tokens > 1) { $join_together = FALSE; last; } } if ($join_together) { cowsay(join(' ', map { chomp; $_ } @lines), $flags); } else { foreach my $line (@lines) { cowsay($line, $flags); } } } sub cowexec { my $cmd = shift; # Check for special commands if ($cmd =~ /^\s*cd\s+(.*?)\s*$/) { chdir($1) or cowsay('The cow says NO!', COW_DEAD); } elsif ($cmd =~ /^\s*curdle\s+(\w+)\s+(.*?)\s*$/) { $ENV{$1} = $2; } else { pipe OUTRD, OUTWR; pipe ERRRD, ERRWR; my $pid = fork(); if (0 == $pid) { close OUTRD; close ERRRD; open STDOUT, ">&OUTWR"; open STDERR, ">&ERRWR"; exec '/bin/bash -c "' . $cmd . '"'; } else { close OUTWR; close ERRWR; waitpid($pid, 0); my @out_lines = ; my @err_lines = ; sensible_cowsay(\@out_lines); sensible_cowsay(\@err_lines, COW_DEAD); } } } sub prompt { my $term = shift; # Using the readline prompt causes problems with # our ANSI colours... print RESET; #print BLINK if $ENV{'COW_FLASH'}; #print BOLD, WHITE, UNDERLINE, "moo?"; #print RESET, " "; print "\n"; return $term->readline('moo? '); } sub main { $ENV{'COW_DEPTH'} = 1 unless $ENV{'COW_DEPTH'}; $ENV{'COW_FLASH'} = TRUE unless $ENV{'COW_FLASH'}; $ENV{'COW_BORING'} = FALSE unless $ENV{'COW_BORING'}; my $term = new Term::ReadLine 'Cow Shell'; my $prompt = 'moo? '; my $OUT = $term->OUT || \*STDOUT; print CLEAR; while (defined ($_ = prompt($term))) { cowexec($_); $term->addhistory($_) if /\S/; } print RESET, "\n"; system "cowsay byee\\!"; } &main;