#!/usr/bin/perl

$| = 1;

%builtin = (cd => sub { chdir $_[0] 
                          || warn "Couldn't chdir to $_[0]: $!\n"; },
            exit => sub { exit $_[0] || 0 },
            );

while (1) {
  print "perl% ";
  my $command = <STDIN>;
  last if $command eq '';
  chomp $command;
  my (%command) = parse($command);

  if (defined $builtin{$command{program}}) {
    $builtin{$command{program}}->(@{$command{args}});
    next;
  }

  my $pid = fork;
  if (! defined $pid) { print STDERR "Couldn't fork: $!\n" }
  elsif ($pid != 0) { wait }
  else { 
    if (defined $command{stdin}) {
      open STDIN, "<", $command{stdin} 
        or die "Couldn't redirect stdin from $command{stdin}: $!\n";
    }
    if (defined $command{stdout}) {
      open STDOUT, $command{append}, $command{stdout} 
        or die "Couldn't redirect stdout to $command{stdout}: $!\n";
    }
    exec $command{program}, @{$command{args}};
    die "Couldn't exec: $!\n";
  }
}


sub parse {
  my @words = grep $_ =~ /\S/, split /(\s+|<|>>|>)/, $_[0];
  my %d;
  $d{program} = shift @words;
  while (@words) {
    local $_ = shift @words;
    if ($_ eq '<') {
      $d{stdin} = shift @words;
    } elsif ($_ eq '>' || $_ eq '>>') {
      $d{stdout} = shift @words;
      $d{append} = $_;
    } else {
      push @{$d{args}}, $_;
    }
  }
  %d;
}
