#!/usr/bin/env perl # # $Id: new,v 1.3 2003/11/22 10:26:22 lukem Exp $ # # Copyright 1997-1999,2003 Luke Mewburn # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Install as the following programs: # new -- # list all folders with unseen messages # fp -- # move to previous folder with unseen messages # fn -- # move to next folder with unseen messages # unseen -- # list all unseen messages # # The script uses `mhpath +`/.folders as a list of mh folders to # search for the 'unseen:' line in the .mh_sequences files of. # # This is generated by the 'Find all folders' option in exmh, # or by invoking any of these commands with '-r' as the first argument. # # Thanks to Simon Burge for the concept, and Matt Green for fixing # some minor bugs. # # http://www.mewburn.net/luke/src/new # # # By default, the method used to determine the unseen sequence `cheats' and # examines the .mh_sequences files directly. This is much faster than using # `pick unseen`, but doesn't work for nmh's private (aka -nopublic) sequences. # If you want the slower method, change $USE_PICK=0 to $USE_PICK=1. # $USE_PICK=0; # save invocation name of script, set output to flush after each line # ($PROG=$0) =~ s!.*/!!; $| = 1; # cd to ~/Mail # ($mhplus) = mhpath("+"); chdir($mhplus) || die("$PROG: can't cd $mhplus - $!\n"); # parse cmd line options # if ($ARGV[0] eq "-r") { rebuild_folders(); } # get list of unseen messages as well as maximum folder name length # ($maxlen, %unseen) = get_unseen(); unless (keys %unseen) { print "No new messages...\n"; exit(0); } # get current folder # $cur = backtick("folder -fast -nocreate", 1); die("$PROG: no valid current folder defined\n") unless ($cur); # determine mode of operation # if ($PROG eq 'new') { new(); # if invoked as "new", summarise unseen folders } elsif ($PROG eq 'unseen') { unseen(); # if invoked as "unseen", scan unseen in each folder } elsif ($PROG eq 'fp') { fmove(-1); # if invoked as "fp", move to previous folder } elsif ($PROG eq 'fn') { fmove(1); # if invoked as "fn", move to next folder } else { die("$PROG: unknown invocation name!\n"); } exit(0); # ---- # # backtick -- # run a command in a backtick, and return the result. # barf on error unless ignoreerror is set sub backtick { local($cmd, $ignoreerror) = @_; die("Usage ${PROG}::backtick(cmd [,ignoreerror])\n") unless ($cmd); local($result, $retval); chop($result = `$cmd 2>/dev/null`); $retval = $? >> 8; unless ($ignoreerror || $retval == 0) { die ("$PROG: $cmd returned $retval, $result\n") } return $result; } # mhpath -- # determine path to given folder # sub mhpath { local($path) = $_[0] || die("Usage ${PROG}::mhpath(dir)\n"); return backtick("mhpath $path"); } # mhparam -- # get the value of a .mh_profile parameter # sub mhparam { local($what) = $_[0] || die("Usage ${PROG}::mhparam(what)\n"); return backtick("mhparam $what"); } # weightinbox -- # sort routine to weight inbox to front of list # sub weightinbox { if ($a eq "inbox" || $a eq "Inbox") { return -1; } elsif ($b eq "inbox" || $b eq "Inbox") { return 1; } else { return $a cmp $b; } } # rebuild_folders -- # rebuild +/.folders # sub rebuild_folders { local(*FCMD, *FOLD); local($line, %list); open(FCMD, "folder -fast -all -recurse 2>/dev/null |") || die("$PROG: can't run folder to rebuild .folders - $!\n"); print "Rebuilding folders list. Found:"; while (defined($line = )) { chop($line); if ($line =~ /^\./) { # skip folders with a leading "." next; } $list{$line}++; print " $line"; print FOLD $line, "\n"; } close(FCMD) || warn("$PROG: can't close folder pipe - $!\n"); print "\n"; open(FOLD, "> .folders") || die("$PROG: can't write .folders - $!\n"); foreach $line (sort weightinbox keys %list) { print FOLD "$line\n"; } close(FOLD) || warn("$PROG: can't close .folders - $!\n"); } # get_unseen -- # build map of unseen folders. # returns ($maxlen, %unseenlist), where $maxlen is length() of # longest name in list, and %unseenlist is map of folder=>unseenlist # sub get_unseen { local(*FLIST, *SEQF); local($folder, $file); local($max, %result); local($line, $unseen); open(FLIST, ".folders") || die("$PROG: can't open .folders - $!\n"); $unseen = mhparam("unseen-sequence") || "unseen"; while (defined($folder = )) { chop($folder); # fast way; doesn't support private folders # if (! $USE_PICK) { $file = "$folder/.mh_sequences"; unless (-f $file && -r $file) { next; } open(SEQF, $file) || do { warn("$PROG: can't open $file - $!\n"); next; }; while (defined($line = )) { chop($line); unless ($line =~ /^${unseen}:\s+(\d.*)/o) { next; } $result{$folder} = $1; if (length($folder) > $max) { $max = length($folder); } last; } close(SEQF) || warn("can't close $file - $!\n"); } else { # mrg's way; slow, but always works # $unseen = backtick("pick +$folder unseen", 1); $unseen =~ s/\s+/ /g; $unseen =~ s/^\s+//; $unseen =~ s/\s+$//; unless (defined($unseen) && $unseen ne "") { next; } $result{$folder} = $unseen; if (length($folder) > $max) { $max = length($folder); } } } close(FLIST) || warn("can't close .folders - $!\n"); return ($max, %result); } # sequence_len -- # return the number of items in the sequence. # sub sequence_len { local($seq) = $_[0] || die("Usage ${PROG}::sequence_len(sequence)\n"); local($count, $elem); $count = 0; foreach $elem (split(/ /, $seq)) { if ($elem =~ /^(\d+)-(\d+)$/) { $count += $2 - $1 + 1; } elsif ($elem =~ /^(\d+)$/) { $count ++; } else { die("$PROG: unknown sequence element $elem\n"); } } return $count; } # bracket -- # return the given value wrapped in your favourite brackets # sub bracket { local($val) = $_[0] || die("Usage ${PROG}::bracket(val)\n"); return $val . "."; # return "<" . $val . ">"; # return "{" . $val . "}"; # return "[" . $val . "]"; # return "(" . $val . ")"; } # new -- # list all folders with unseen messages # sub new { local($elem, $count, $total); $total = 0; if ($maxlen < 5) { # length of "total" $maxlen = 5; } foreach $elem (sort weightinbox keys %unseen) { $count = sequence_len($unseen{$elem}); $total += $count; printf("+%-${maxlen}s %6s%s %s\n", $elem, bracket($count), $cur eq $elem ? "*" : " ", $unseen{$elem}); } printf(" %-${maxlen}s %7s\n", "total", bracket($total)); } # unseen -- # list all unseen messages # sub unseen { local($elem, $count); foreach $elem (sort weightinbox keys %unseen) { $count = sequence_len($unseen{$elem}); printf("\n%d unseen message%s in $elem%s\n", $count, $count == 1 ? "" : "s", $cur eq $elem ? " (*: current folder)" : ""); system("scan +$elem unseen 2>/dev/null"); } backtick("folder -fast +$cur"); } # fmove -- # move to next folder with unseen messages # (or previous if $direction < 0) # sub fmove { local($direction) = $_[0]; local($elem, $first, @ukeys); @ukeys = sort weightinbox (keys %unseen, ($unseen{$cur}) ? () : $cur); if ($direction < 0) { @ukeys = reverse @ukeys; } $first = $ukeys[0]; while ($elem = shift(@ukeys)) { if ($elem eq $cur) { last; } } $elem = shift(@ukeys) || $first; backtick("folder -fast +$elem"); printf("+%-${maxlen}s %s\n", $elem, $unseen{$elem}); }