#!/usr/bin/perl
# The Missing Textutils, Ondrej Bojar, obo@cuni.cz
# http://www.cuni.cz/~obo/textutils
#
# 'picklog' reads stdin to extract useful snippets of information into a nice
# table.
# 
# Usage: picklog cmd1 cmd2 ... < input
#
# Allowed commands:
#   RE                        ... same as find RE
#   find RE                   ... scan till the line where RE is found
#   pick RE(what)             ... pick something from the current line
#                                 or the first next matching line
#   next                      ... advance to the next line of input
#   nl                        ... add a newline to the output
#   let:VARNAME RE(what)      ... for RE and store it in internal variable
#                                 VARNAME
#   watch:VARNAME RE(what)    ... simultaneously search for RE and store it
#                                 in internal variable VARNAME, whenever found
#   count:VARNAME RE          ... like watch but store just the number of lines
#                                 that matched RE so far
#   print:VARNAME             ... print internal variable VARNAME
#
# A 'nl' is always added at the end of the commands.
# 
# The commands are looped, as long as there are some input lines to read.
#
# The variables are useful for finding the last something before something else
# or for swapping columns in the output.
#
# $Id: picklog,v 1.6 2009-05-28 23:15:49 bojar Exp $
#
# Ondrej Bojar

use strict;

my @cmds = @ARGV;
@ARGV = ();

push @cmds, "nl";


binmode(STDIN, ":utf8");
binmode(STDOUT, ":utf8");
binmode(STDERR, ":utf8");

my $watches;  # maps target varnames to regular expressions for watching
my $counts;  # maps target varnames to regular expressions for counting
my $vars;
my $re = undef;
my $todo = "find";
my @output = ();
while (<>) {
  foreach my $watch (keys %$watches) {
    if (/$watches->{$watch}/) {
      $vars->{$watch} = $1;
    }
  }
  foreach my $count (keys %$counts) {
    if (/$counts->{$count}/) {
      $vars->{$count} ++;
    }
  }
  NEXT_ON_THE_SAME_LINE:

  if (!defined $re) {
    $re = shift @cmds; push @cmds, $re;
    if ($re eq "next") {
      $re = undef; # ask for a new command
      next; # advance the line
    }
    if ($re eq "nl") {
      print join("\t", @output)."\n";
      @output = ();
      $re = undef; # ask for a new command
      goto NEXT_ON_THE_SAME_LINE;
    }
    if ($re =~ /^watch:(.*)$/o) {
      $re = shift @cmds; push @cmds, $re;
      $watches->{$1} = $re; # set the watch
      $re = undef; # ask for a new command
      goto NEXT_ON_THE_SAME_LINE;
    }
    if ($re =~ /^count:(.*)$/o) {
      $re = shift @cmds; push @cmds, $re;
      $counts->{$1} = $re; # set the watch
      $re = undef; # ask for a new command
      goto NEXT_ON_THE_SAME_LINE;
    }
    if ($re =~ /^print:(.*)$/o) {
      push @output, $vars->{$1}; # emit the current value
      $re = undef; # ask for a new command
      goto NEXT_ON_THE_SAME_LINE;
    }
    if ($re =~ /^(find|pick|let:.*)$/o) {
      $todo = $re;
      $re = shift @cmds; push @cmds, $re; # this is the re to search for
      goto NEXT_ON_THE_SAME_LINE;
    }
  }

  if (/$re/) {
    # found our re!
    my $found = $1; # store the picked part
    if ($todo eq "pick") {
      push @output, $found; # put it on the output list
      $re = undef; # ask for a new command
      $todo = "find";
      goto NEXT_ON_THE_SAME_LINE;
    } elsif ($todo =~ /^let:(.*)$/o) {
      $vars->{$1} = $found; # assign the variable
      $re = undef; # ask for a new command
      $todo = "find";
      goto NEXT_ON_THE_SAME_LINE;
    } else {
      # just found, do the next command;
      $re = undef;
      goto NEXT_ON_THE_SAME_LINE;
    }
  }
}

# print final output, if any
print join("\t", @output)."\n" if 0 == scalar @output;


