--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Mail::Header;
+
+use Bugs::Message;
+use Bugs::Common qw(readconfig opendb);
+
+our $head = Mail::Header->new( \*STDIN );
+
+$head->unfold();
+
+readconfig();
+our $dbh = opendb();
+
+print "Message from ".$head->get('from');
+print "Subject: ".$head->get('subject');
+print "\n";
+
+our @reply;
+
+our @bugs;
+
+our $MATCH_BUGID = "-?[0-9]+";
+
+sub parse_msgid {
+ my $r = shift;
+ print "Resolving msgid \"$r\"\n";
+ if ( $r eq "^" ) {
+ if ( defined $head->get('in-reply-to') ) {
+ chomp(my $m = $head->get('in-reply-to'));
+ return $m;
+ }
+ if (defined $head->get('references')) {
+ my $refs = $head->get('references');
+ my @refs = split /,\s*/, $refs;
+ print "Refs:\n";
+ print foreach (@refs);
+ die "refs";
+ }
+ push @reply, "Cannot resolve message-id `^'\n";
+ return undef;
+ } elsif ( $r eq "!" ) {
+ if ( ! defined $head->get('message-id') ) {
+ push @reply, "Cannot resolve message-id `!'\n";
+ return undef;
+ }
+ chomp(my $m = $head->get('message-id'));
+ return $m;
+ } else {
+ # Assume it is just a message-id...
+ return $r;
+ }
+}
+
+sub parse_bug {
+ my $b = shift;
+
+ return $b if $b > 0;
+
+ if ( -$b-1 > $#bugs ) {
+ push @reply, "Cannot resolve bug backreference $b";
+ return undef;
+ }
+ return $bugs[-$b-1];
+}
+
+sub cmd_create {
+ my $r = parse_msgid(shift);
+ return 1 unless $r;
+
+ my $bug = rand() * 100 % 100;
+
+ push @reply, "Created new bug #$bug rooted at `$r'";
+ push @bugs, $bug;
+ return 0;
+}
+
+sub cmd_title {
+ $_ = shift;
+ m/(${MATCH_BUGID})\s+(.*)/ or return 1;
+ my $b = parse_bug $1;
+ return 1 unless $b;
+ push @reply, "Set title for #$b to `$2'";
+ return 0;
+}
+
+sub cmd_prune {
+ $_ = shift;
+ m/^($MATCH_BUGID)\s(.*)/ or return 1;
+ my $b = parse_bug $1;
+ my $m = parse_msgid $2;
+ return 1 unless $b and $m;
+ push @reply, "Prune `$m' from #$b";
+ return 0;
+}
+
+sub cmd_graft {
+ $_ = shift;
+ m/^($MATCH_BUGID)\s(.*)/ or return 1;
+ my $b = parse_bug $1;
+ my $m = parse_msgid $2;
+ return 1 unless $b and $m;
+ push @reply, "Graft `$m' onto #$b";
+ return 0;
+}
+
+sub cmd_quit {
+ push @reply, "Finished processing.";
+ return 1;
+}
+
+our %cmds = (
+ create => \&cmd_create,
+ title => \&cmd_title,
+ prune => \&cmd_prune,
+ graft => \&cmd_graft,
+ thanks => \&cmd_quit,
+ goodbye => \&cmd_quit,
+ quit => \&cmd_quit,
+ stop => \&cmd_quit,
+);
+
+while (<STDIN>) {
+ chomp();
+ push @reply, "> $_";
+
+ s/#.*//g;
+ next if (m/^$/);
+
+ if ( ! m/(\S+)\s*(.*)/ ) {
+ push @reply, "Failed to parse input. Stopping processing here";
+ last;
+ }
+ if ( ! defined $cmds{$1} ) {
+ push @reply, "Unknown command. Stopping processing here.";
+ last;
+ }
+ last if $cmds{$1}($2);
+}
+
+print "$_\n" foreach ( @reply );
+
+print "\nDONE\n\n";
+my $i = 0;
+while ($i <= $#bugs) {
+ my $j = -($i+1);
+ print "BUG $j == ".$bugs[$i]."\n";
+ $i++;
+}
--- /dev/null
+
+Identifying a message
+=====================
+
+Wherever <MSGID> is used below any of the following is acceptable:
+
+! -- The current message.
+^ -- The message referenced by the In-Reply-To: header or, if that is
+ not present, the first message referenced in the References: header.
+ -- An explicit message id.
+
+Identifying a bug
+=================
+
+Whereever <BUG> is used below any of the following is acceptable:
+
+[0-9]* -- An integer bug number.
+
+-[0-9]* -- A back reference to a bug created by a previous command,
+ e.g. via a preceeding "create <MSGID>". The first such bug is
+ -1, the second is -2, etc.
+
+Comments
+========
+
+Lines beginning with a # are considered comments
+Available Commands
+==================
+
+create <MSGID>
+
+ Create a new bug, rooted at <MSGID>. By default the title will
+ be taken from the Subject line of <MSGID>
+
+graft <BUG> <MSGID>
+
+ Associate the new subthread rooted at <MSGID> with a <BUG>.
+
+prune <BUG> <MSGID>
+
+ Remove the subthread rooted at <MSGID> from a <BUG>
+
+thanks
+goodbye
+stop
+quit
+ Stop processing.
+
+title <BUG> [New title]
+
+ xx