#!/usr/bin/perl use Set::IntSpan; use Tie::File; use Fcntl ':flock'; sub mhc; my $FILENAME = '.mjdseq'; my %canonical = ( '' => 'show', # I AM THE DEFAULT '+' => 'union', 'u' => 'union', 'o' => 'union', 'or' => 'union', 'union' => 'union', 'add' => 'union', '*' => 'intersect', 'a' => 'intersect', 'and' => 'intersect', 'i' => 'intersect', 'intersection' => 'intersect', 'intersect' => 'intersect', '-' => 'diff', 'd' => 'diff', 'w' => 'diff', 'without' => 'diff', 'difference' => 'diff', 'diff' => 'diff', 's' => 'show', 'p' => 'show', 'show' => 'show', 'print' => 'show', '?' => 'show', '=' => 'copy', 'copy' => 'copy', 'c' => 'copy', 'assign' => 'copy', 'set' => 'copy', 'x' => 'delete', '0' => 'delete', 'del' => 'delete', 'delete' => 'delete', ); sub usage { warn "Usage: $0 [+folder] [sequence [OP [msglist]]] OPs are:\n"; my %syn; for my $k (keys %canonical) { push @{$syn{$canonical{$k}}}, $k; } for my $op (sort keys %syn) { warn join " ", "", "", sort(@{$syn{$op}}), "\n"; } exit 1; } my (@SEQUENCE, $TIED); my $folder; if ($ARGV[0] =~ /^\+(.*)/) { $folder = shift; mhc "folder -nocreate $folder"; $folder =~ s/^\+//; } else { $folder = mhc "folder -fast"; } my $seqfile = seqfile($folder); my $TIED = tie my @SEQUENCE, 'Tie::File', $seqfile or die "Couldn't tie $seqfile: $!"; $TIED->flock(LOCK_EX) or die "Couldn't lock $seqfile: $!"; unless (@ARGV) { list_seqs(); exit 0; } my $seqname = shift; my $oop = shift; my $op = $canonical{$oop}; unless (defined $op) { warn "Unknown operator '$oop'\n\n"; usage(); } my $seq = seq_read($seqname); if ($op eq 'show') { # special case print join "\n", $seq->elements, ''; exit 0; } elsif ($op eq 'delete') { # another seq_delete($seqname); exit 0; } $seq = $seq->$op(\@ARGV); seq_write($seqname, $seq); exit 0; ################################################################ sub seqfile { my $folder = shift; my $folderdir = mhc "mhpath +$folder"; "$folderdir/$FILENAME"; } sub seq_read { my ($seqname) = @_; for my $n (0 .. $#SEQUENCE) { for ($SEQUENCE[$n]) { if (/^\Q$seqname\E:\s+(.*)/) { return Set::IntSpan->new($1); } } } return Set::IntSpan->new(); } sub seq_write { my ($seqname, $data) = @_; my $newline = "$seqname: " . $data->run_list; for my $n (0 .. $#SEQUENCE) { for ($SEQUENCE[$n]) { if (/^\Q$seqname\E:\s+/) { $_ = $newline; return; } } } push @SEQUENCE, $newline; } sub seq_delete { my ($seqname) = @_; for my $n (0 .. $#SEQUENCE) { for ($SEQUENCE[$n]) { if (/^\Q$seqname\E:\s+(.*)/) { splice @SEQUENCE, $n, 1; return; } } } } sub list_seqs { for my $n (0 .. $#SEQUENCE) { for ($SEQUENCE[$n]) { if (/^([^:]*):/) { print "$1\n"; } else { warn "Malformed line $n in sequence file!\n"; } } } } sub mhc { my $cmd = shift; my $result = qx{$cmd}; exit 1 unless defined $result; chomp $result; $result; } =head1 NAME mark - mark messages in MH folders =head1 SYNOPSIS mark [+folder] mark [+folder] sequencename mark [+folder] sequencename OP messagelist =head1 DESCRIPTION In all cases, the C<+folder> argument I be first. It specifies the MH folder on which to operate. Sequence namespaces are local to each folder. That is, the message set named C for folder C<+inbox> is different from the message set named C for folder C<+perl>. if C<+folder> is omitted, the current MH folder is used. C with no other arguments lists the names of all the marks you have defined for the specified folder. C with just a sequence name prints out a space-separated list of the message numbers for the specified sequence. It is not an error if the sequence does not exist; the output will be empty. C with an operator and a message list alters the specified sequence. The available operators are: =over 4 =item C Adds the specified messages to the sequence. Synonyms: C<+>, C, C, C, C. =item C Removes the specified messages from the sequence. Synonyms: C<->, C, C, C, C. =item C Sets the sequence contents to the specified message list, discarding the old value entirely. Synonyms: C<=>, C, C. =item C Removes all messages from the sequence except for those that appear in the specified list. Synonyms: C<*>, C, C, C, C. B If you want to use the C<*> symbol in the shell, you will probably have to put it in quotes. =item C Deletes the sequence entirely. Synonyms: C<0>, C, C. =item C Does not modify the sequence. Instead, the sequence contents are printed. Synonyms: C, C, C, C

. B If you want to use the C symbol in the shell, you will probably have to put it in quotes. =back =head1 AUTHOR Mark Jason Dominus, C =head1 COPYRIGHT Copyright 2002 Mark Jason Dominus. Unauthorized distribution prohibited. =cut