On Thu, Apr 16, 2015 at 07:08:50AM +0200, M. Fioretti wrote:
> On 2015-04-16 03:28, Luis Mochan wrote:
> >>The system is not too
> >polished but serves me fine and has some similarities to gmails label
> >system. If interested, I can share it.
> 
> I **am** interested, please do it

Dear Marco,
Please find attached my perl script. You should have the packages
Term::Readline (I use Term::Readline::Gnu) and File::Copy. It takes
two arguments, an action and the name of the file with the message to
edit. The action should be one of menu, edit, append, remove, show,
clean, list (in practive I only use 'edit'). If a change is performed,
the message is replaced by the edited version. To use the programs I
made some mutt macros. The relevant part of my muttrc file is:


############
# labels
# remember original editor. 
# Might not be what you want if editor is changed afterwards
set my_editor="$editor"

macro index y "<enter-command>set editor=\"~/.mutt/editlabel.pl edit\"\n\
<tag-prefix><edit><next-undeleted>\
<enter-command>set editor=\"$my_editor\"\n" "Edit labels"

macro index YE "<enter-command>set editor=\"~/.mutt/editlabel.pl edit\"\n\
<tag-prefix><edit><next-undeleted>\
<enter-command>set editor=\"$my_editor\"\n" "Edit labels"

macro index YM "<enter-command>set editor=\"~/.mutt/editlabel.pl menu\"\n\
<tag-prefix><edit><next-undeleted>\
<enter-command>set editor=\"$my_editor\"\n" "Loop over label menu"

macro index YA "<enter-command>set editor=\"~/.mutt/editlabel.pl append\"\n\
<tag-prefix><edit><next-undeleted>\
<enter-command>set editor=\"$my_editor\"\n" "Append labels"

macro index YR "<enter-command>set editor=\"~/.mutt/editlabel.pl remove\"\n\
<tag-prefix><edit><next-undeleted>\
<enter-command>set editor=\"$my_editor\"\n" "Remove labels"

macro index YS "<enter-command>set editor=\"~/.mutt/editlabel.pl show\"\n\
<tag-prefix><edit>\
<enter-command>set editor=\"$my_editor\"\n" "Show labels"

macro index YC "<enter-command>set editor=\"~/.mutt/editlabel.pl clean\"\n\
<tag-prefix><edit><next-undeleted>\
<enter-command>set editor=\"$my_editor\"\n" "Clean labels"

macro index YL "<enter-command>set editor=\"~/.mutt/editlabel.pl list\"\n\
<tag-prefix><edit>\
<enter-command>set editor=\"$my_editor\"\n" "List labels"

macro index YX "<limit>!~h X-Label:<enter>" "Limit to unlabelled"
macro index YY "<limit>~h X-Label:<enter>" "Limit to labelled"

macro index \Cy "<limit>~y " "Limit view to label"

#show labels in index
set index_format="%4C %Z %{%b %d} %-15.15L %?M?(#%03M)&(%5l)? %?y?(%.20Y) ?%s"

#display labels in messages
unignore X-Label:
##############

In practice I only use
  YX to display only unlabelled (i.e., unprocessed messages),
  YY to only show processed messages (afterwards I use T to tag those
     messages and move all of them to mbox) 
  y to insert/add/modify space separated labels
  \Cy to display only messages with a given label
The program has history and completion to ease label edition. To that
end, I use a file .mutt/Xlabels to store all the previously used
labels. 

I use this system under debian linux using perl 5.18 (haven't upgraded
it) under perlbrew.

Regards,
Luis


> 
> Thanks,
> Marco
> -- 
> http://mfioretti.com
> 

-- 

                                                                  o
W. Luis Mochán,                      | tel:(52)(777)329-1734     /<(*)
Instituto de Ciencias Físicas, UNAM  | fax:(52)(777)317-5388     `>/   /\
Apdo. Postal 48-3, 62251             |                           (*)/\/  \
Cuernavaca, Morelos, México          | moc...@fis.unam.mx   /\_/\__/
Consider using GnuPrivacyGuard https://www.gnupg.org/
My key: 791EB9EB, C949 3F81 6D9B 1191 9A16  C2DF 5F0A C52B 791E B9EB, yours?



#! /usr/bin/env perl

use strict;
use warnings;
use Fcntl qw(:DEFAULT :flock);
use Term::ReadLine;
use File::Copy;
use locale;

my ($action, $filename)=@ARGV;
my $labelfilename=$ENV{HOME} . "/.mutt/Xlabels";
die "$filename not readable" unless -r $filename;
#die "$labelfilename not readable. Create it." unless -r $labelfilename;
die "strange filename" unless $filename=~m{([^/]*)$};
my $basefilename=$1;
my $tmpfilename="/tmp/editlabels-$basefilename.$$";
my $history="$ENV{HOME}/.editlabelhistory";
my $maxhistory=100; #how many labels to remember in history
my $current=''; #current labels
my %current;
my %previouscurrent;
my %labels;
my %previouslabels;


sysopen LABELS, $labelfilename, O_RDWR|O_CREAT or die "Can't open $labelfilename $!"; 
flock(LABELS, LOCK_SH) 
    or die "Can't lock $labelfilename $!"; #puts a shared lock
chomp(my @labels=<LABELS>);
foreach(@labels){ #use %labels to remove duplicates
    $labels{$_}++;
}
%previouslabels=%labels;

my $term=new Term::ReadLine 'X-Labels'; # prepare terminal to read labels
my $attribs=$term->Attribs;
$attribs->{completion_entry_function}=
    $attribs->{list_completion_function};
$term->using_history;
$term->ReadHistory($history) if -r $history;

my $wrong="";

if($action eq "menu"){
    for(;;){
	$attribs->{completion_word} = 
	    [qw(append clean edit list remove show quit 
                Append Clean Edit List Remove Show Quit)];
	$action=lc $term->
	    readline("$wrong Append/Edit/Remove/Show/Clean/List/Quit: ");
	$action=~s/ .*$//;
	last unless once($action);
    }
} else {
    once($action);
}

# update labels 
unless(comparehash(\%previouslabels, \%labels)){ #if labels changed
    flock(LABELS, LOCK_EX)
	or die "Can't lock $labelfilename $!"; #puts an exclusive lock
    seek(LABELS, 0, 0); truncate(LABELS, 0);
    print LABELS join("\n", sort {$a cmp $b} keys %labels), "\n";
}
close(LABELS);
# update history
$term->StifleHistory($maxhistory);
$term->WriteHistory($history) or die "Couldn't write $history $!";
#DONE

sub comparehash { #compares the keys of two hash references.
    my ($ha, $hb)=@_;
    if ( keys %$ha != keys %$hb) {
	return 0; # they don't have the same number of keys;
    }
    my %cmp = map { $_ => 1 } keys %$ha;
    for my $key (keys %$hb) {
	last unless exists $cmp{$key};
	delete $cmp{$key};
    }
    return ! %cmp;
}

sub once {
    my $action = shift;
    $wrong="";
    edit(), return 1 if $action eq "edit";
    append(), return 1 if $action eq "append";
    remove(), return 1 if $action eq "remove";
    show(), return 1   if $action eq "show";
    clean(), return 1  if $action eq "clean";
    list(), return 1   if $action eq "list";
    return 0 if $action eq "quit";
    $wrong="Wrong action.";
    return 1;
}



sub getlabels {
    chomp($current = `formail -c -x X-Label < $filename`);
    $current=~s/^\s*//;
    $current=~s/\s*$//;
    my @current = split /,\s*|\s+/, $current;
    map {$current{$_}++} @current;
    %previouscurrent=%current;
}    

sub modifylabels {
    # do nothing if labels are the same
    return if comparehash(\%previouscurrent, \%current);
    my $xlabel="X-Label:";
    $xlabel.=" ". join(' ', sort {$a cmp $b} keys %current) if %current;
    print "$xlabel\n";
    system "formail -I \"$xlabel\" < $filename > $tmpfilename";
    move($tmpfilename, $filename) 
	or die "Couldn't rename $tmpfilename to $filename $!"
}


sub edit {
    getlabels();
    $attribs->{completion_word} = [keys %labels];
    my $input=$term->readline("Edit label(s): ", $current);
    my @input=split /\s+/, $input;
    %current=(); # empty current labels.
    # update labels that don't begin in colon
    # colon-keywords are used for label groups
    map {$labels{$_}++, $current{$_}++ unless /^\:/} @input; 
    modifylabels();
}

sub append {
    getlabels();
    print join(' ', sort {$a cmp $b} keys %current), "\n";
    $attribs->{completion_word} = [keys %labels];
    my $input=$term->readline("Add label(s): ");
    my @input=split /\s+/, $input;
    map {$labels{$_}++, $current{$_}++} @input; 
    modifylabels();
}

sub remove {
    getlabels();
    print join(' ', sort {$a cmp $b} keys %current), "\n";
    $attribs->{completion_word} = [keys %current];
    my $input=$term->readline("Remove label(s): ");
    my @input=split /\s+/, $input;
    map {delete $current{$_}} @input; 
    modifylabels();
}

sub show {
    getlabels();
    print join(' ', sort {$a cmp $b} keys %current), "\n";
    $term->readline("<Enter> to continue ");
}

sub list {
    open(LESS, "| fold -s | less");
    print LESS join " ", sort {$a cmp $b} keys %labels;
    close(LESS);
}

sub clean {
    getlabels();
    %current=();
    modifylabels();
}


Reply via email to