news.utdallas.edu!wupost!darwin.sura.net!sgiblab!munnari.oz.au!metro!usage!news Mon Feb 22 07:54:35 CST 1993
Article: 1071 of comp.lang.perl
Xref: feenix.metronet.com comp.lang.perl:1071
Newsgroups: comp.lang.perl
Path: feenix.metronet.com!news.utdallas.edu!wupost!darwin.sura.net!sgiblab!munnari.oz.au!metro!usage!news
From:
[email protected]
#Subject: Re: Mail Filter
Message-ID: <cameron-930218135036-1-01024@fuligin>
To:
[email protected] (Matthew B Landry)
Followup-To: comp.lang.perl
Sender:
[email protected]
Nntp-Posting-Host: fuligin.spectrum.cs.unsw.oz.au
Reply-To:
[email protected]
Organization: CS&E Computing Facility, Uni Of NSW, Oz
References: <
[email protected]>
Errors-To:
[email protected]
Date: Thu, 18 Feb 1993 02:51:09 GMT
Return-Receipt-To:
[email protected]
Lines: 1118
[email protected] (Matthew B Landry) writes:
| I'm looking for a program to sort email. Specifically, I want
| something that can take all the mail from two specific addresses (digests,
| actually), and save it into two corresponding files, leaving the rest of
| my mail in the system box.
| I could have the program run regularly from cron to scan the
| mailbox, so a .forward shouldn't be necessary.
| I'm posting this here because I've heard that perl would be best
| for this sort of thing. If there's anyone who has a program that can be
| adapted for this (or who can tell me how to build my own), I'd greatly
| appreciate it.
I use the filemail command appended below, which allows recurse rule based
filing. Filemail expects a single item on stdin, so I run the mail file through
splitmail (appended) to do the job.
Thus:
mv $MAIL /tmp/split$$ && \
splitmail -m maildir /tmp/split$$ && \
rm /tmp/split$$
I haven't used splitmail for a long time, so test it before use. I use filemail
all the time; I append the .file ruleset in my inbox so you can see how it's
used. [Types...] There are some library routines too, appended, take what you
need.
- Cameron Simpson
[email protected], DoD#743
--
I don't waste my money; I invest it in ventures with high negative returns.
#/bin/sh
sed 's/^X//' > filemail <<'EOF-/home/cs/spectrum/fuligin/1/cameron/bin/filemail'
X#!/usr/local/bin/perl
X#
X# Usage: filemail [-a announce] [mailbox] <email
X# -a announce Where to announce arrival, otherwise $CONSOLE,
X# otherwise /dev/console.
X# mailbox Specify mailbox, otherwise $MAILBOX,
X# otherwise $HOME/.incoming-mail.
X#
X
X($cmd=$0) =~ s:.*/::;
X
X$usage="Usage: $cmd [-a announce] [mailbox]\n";
X
Xrequire 'libcs.pl';
Xrequire 'cs/env/mail.pl';
Xrequire 'cs/rfc822.pl';
Xrequire 'errno.ph';
X
Xdefined($MAILBOX) || ($ENV{'MAILBOX'}=$MAILBOX="$HOME/.incoming-mail");
X
X$badopts=0;
Xwhile ($#ARGV >= $[ && $ARGV[$[] =~ /^-/)
X { $_=shift @ARGV;
X if ($_ eq '-a')
X { $ENV{'CONSOLE'}=$CONSOLE=shift @ARGV;
X }
X else
X { print STDERR "$cmd: bad option \"$_\"\n";
X $badopts=1;
X }
X }
X
Xif (defined($inbox=shift))
X { $ENV{'MAILBOX'}=$MAILBOX=$inbox;
X }
X
Xif ($#ARGV >= $[)
X { print STDERR "$cmd: extra arguments: @ARGV\n";
X $badopts=1;
X }
X
Xdie $usage if $badopts;
X
X# snarf stdin first up
X@INPUT=<STDIN>;
Xclose(STDIN);
X
Xdie "$cmd: no input!\n" if $#INPUT < 0;
X
Xopen(STDOUT,">>$CONSOLE") || die "can't open $CONSOLE: $!\n";
Xopen(STDERR,">&STDOUT") || die "can't dup STDOUT to STDERR\n";
X
Xundef $from_, $from, $From, $to, $subject, %hdrs;
X$INPUT[0] =~ s/^From\s/From_: /;
X
X@hdrs=(); undef %hdrs;
X@INPUT=&add822lines(@INPUT);
Xwhile ($#INPUT >= $[ && $INPUT[$#INPUT] eq "\n")
X { pop(@INPUT);
X }
X
X$subject=$hdrs{'subject'};
X$F_HAS_SUBJECT=length($subject);
X$from='';
X$from_='';
X
X$From=$hdrs{'from'};
X@From=&getaddrs($From);
Xwhile ($#From >= $[)
X { local($pre,$addr,$post);
X
X $pre=shift @From;
X $addr=shift @From;
X $post=shift @From;
X
X if ($addr =~ /^<([^>\s]*)>/)
X { $addr=$1;
X }
X
X next if $addr eq $USER || $addr eq $EMAIL;
X
X $from=$addr;
X last;
X }
X
X$to=$hdrs{'to'};
X$cc=$hdrs{'cc'};
X$newsgroups=$hdrs{'newsgroups'};
X
Xif (length($hdrs{'from-'}))
X { $from_=$hdrs{'from-'};
X }
X
Xif (!length($subject))
X { $subject='NO SUBJECT SUPPLIED BY SENDER';
X unshift(@hdrs,"Subject: $subject");
X $hdrs{'subject'}=$subject;
X }
X
X$legend=(length($from)
X ? "From: $From"
X : (length($from_) && ($from_ ne $USER && $from_ ne $EMAIL
X || !length($to))
X ? "From_ $from_"
X : (length($to)
X ? "To: $to"
X : (length($newsgroups)
X ? "Newsgroups: $newsgroups"
X : "No source or destination."
X )
X )
X )
X );
X
Xlength($subject) && ($legend.="; $subject");
Xlength($from) || ($from=$from_);
X
X($reply_to=$hdrs{'reply-to'}) =~ s/^\s+//; $reply_to =~ s/\s+$//;
Xlength($reply_to) || ($reply_to=$from);
X
X($errors_to=$hdrs{'errors-to'}) =~ s/^\s+//; $errors_to =~ s/\s+$//;
Xlength($errors_to) || ($errors_to=$reply_to);
X
X($sec,$min,$hr,$mday,$mon,$yr,$wday,@etc)=gmtime(time);
X
Xundef %FILED; # places we have already filed this item
X@MAILBOXES=($MAILBOX); # places to try filing this item
X@INNAMES=(''); # prefix for name
X@FILES=(); # originals, used for links
X$xit=0;
X
XMAILBOX:
X while (defined($MAILBOX=shift @MAILBOXES))
X { { local($filed,$oMAILBOX);
X
X $INNAME=shift @INNAMES;
X
X # loop until the item is filed or we lack a .file file
X
X # Code to file incoming mail.
X # When called we have
X # @INPUT All the lines in the mail item.
X # We are guarrenteed a From_ line as $INPUT[0].
X # %hdrs Bodies of header lines keyed by downcased names.
X # @hdrs Complete headers.
X # $legend "From: who; subject"
X # $MAILBOX The normal inbox directory.
X # $HOME Home directory.
X # $USER User name.
X # $subject, $s Subject field.
X # $from, $f From: or From_.
X # $reply_to Reply-To: or $from.
X # $errors_to Errors-To: or $reply_to.
X # $to To:
X # $cc CC:
X #
X # If this message was successfully filed then $filed will
X # be true at the end.
X #
X # It is possible to arrange that filemail files the mail
X # in another place by changing $MAILBOX. If you wish a specific
X # name for the filed item, set $INNAME; otherwise the
X # pick-a-number method used for ordinary mail will be used.
X #
X
X DOTFILE:
X while (-f "$MAILBOX/.file") # implies -d "$MAILBOX/."
X { $oMAILBOX=$MAILBOX;
X $filed=0;
X { local($s,$f,$to,$cc)
X =($hdrs{'subject'},$hdrs{'from'},$hdrs{'to'},$hdrs{'cc'});
X local($_)=$s;
X
X do "$MAILBOX/.file";
X warn $@ if $@;
X }
X
X next MAILBOX if $filed;
X last DOTFILE if $oMAILBOX eq $MAILBOX;
X }
X }
X
X # it should be a file or a directory
X if (! -f $MAILBOX && !&mkdir($MAILBOX))
X { &legend("can't make directory $MAILBOX");
X $xit=1;
X next MAILBOX;
X }
X
X if (-d $MAILBOX)
X { local($filed);
X
X $filed=&fileitem($MAILBOX,$INNAME);
X
X if (!defined($filed))
X { &legend("can't save in $MAILBOX/$INNAME\n");
X $xit=1;
X }
X }
X else
X { if (!open(MAILBOX,">>$MAILBOX"))
X { &legend("can't append to $MAILBOX: $!");
X if (!open(MAILBOX,">>$MAILBOX.$$"))
X { &legend("can't append to $MAILBOX.$$: $!");
X }
X }
X
X &writeitem(MAILBOX);
X close(MAILBOX);
X &legend(&shorten($MAILBOX)." $legend");
X }
X }
X
Xexit $xit;
X######################
X
X# On failure $! is useful.
Xsub mklink # (fname) -> ok
X { local($fname)=@_;
X
X for (@FILES)
X { if (link($_,$fname))
X { &legend(&shorten($_)." <-> ".&shorten($fname));
X return 1;
X }
X
X last if ($! != &EXDEV);
X }
X
X local($tmp);
X
X # stash @INPUT
X $tmp=&dirname($fname)."/.$cmd-$$";
X
X if (!open(TMP,">> $tmp\0"))
X { &legend("can't append to $tmp: $!");
X return 0;
X }
X
X &writeitem(TMP);
X close(TMP);
X
X if (link($tmp,$fname))
X { unlink($tmp) || &legend("unlink($tmp): $!");
X push(@FILES,$fname);
X &legend(&shorten($fname).": $legend");
X return 1;
X }
X
X return 0;
X }
X
Xsub writeitem # ($FILE)
X { local($FILE)=@_;
X
X for (@hdrs)
X { print $FILE $_, "\n";
X }
X
X print $FILE "\n";
X for (@INPUT)
X { print $FILE $_;
X }
X }
X
X# file an item in directory $MAILBOX, with prefix $INNAME.
X#
Xsub fileitem # ($MAILBOX,$INNAME) -> basename-of-filed-item
X { local($MAILBOX,$INNAME)=@_;
X local($filed);
X
X # attempt link to unadorned INNAME
X if (length($INNAME) && &mklink("$MAILBOX/$INNAME"))
X { return "$MAILBOX/$INNAME";
X }
X
X # not linked to simple name, try INNAME_n
X local($n)=1;
X
X # walk directory, picking $n > any already present
X if (!opendir(MAILBOX,$MAILBOX))
X { &legend("warning: can't opendir($MAILBOX): $!\n");
X return undef;
X }
X
X local(@dir)=readdir(MAILBOX);
X closedir(MAILBOX);
X
X local($ptn)=$INNAME;
X
X $ptn =~ s,\W,\\$&,g;
X eval
X ' for (grep(/^'.$ptn.'/,@dir))
X { s/^'.$ptn.'//;
X /^\d+$/ || next;
X
X if ($& >= $n)
X { $n=$&+1;
X }
X }
X ';
X
X local($ok)=1;
X
X while (!&mklink("$MAILBOX/$INNAME$n"))
X { $ok=0;
X last if $! != &EEXIST;
X $ok=1;
X $n++;
X }
X
X if ($ok)
X { return "$MAILBOX/$INNAME$n";
X }
X
X return undef;
X }
X
X# forward a mail item and say so
Xsub forw # (subj,who,@WHAT)
X { local($subj)=shift;
X local($who)=shift;
X
X $filed=&forward($who,@_);
X $filed && &legend("==> $who: $subj");
X }
X
Xsub forward
X { local($to,@INPUT)=@_;
X local($shifted,@fields,@bodies,%ndx);
X local($[)=1;
X local($_);
X local($i);
X
X die "$cmd: &forward($to): no input!\n" if $#INPUT < 1;
X
X $shifted=shift(@INPUT) if $INPUT[1] =~ '^From ';
X
X die "$cmd: &forward($to): short input!\n" if $#INPUT < 1;
X die "$cmd: &forward($to): malformed input\n" if $INPUT[1] =~ /^\s/;
X
X while (defined($_=shift(@INPUT)))
X { if (/^[ \t]/)
X { @bodies[$#bodies].=$_;
X }
X elsif (/^(\S*):[ \t]*/)
X { local($hdr)=$1;
X
X push(@fields,$hdr);
X push(@bodies,$');
X $hdr =~ tr/A-Z/a-z/;
X $ndx{$hdr}=$#fields;
X }
X else
X # not a header line
X { last;
X }
X }
X
X if (defined($_))
X { unshift(@INPUT,$_);
X }
X
X # tidy up Sender: line
X if (($i=$ndx{'sender'}) >= 1)
X { $fields[$i]="Original-".$fields[$i];
X delete $ndx{'sender'};
X $ndx{'original-sender'}=$i;
X }
X
X push(@fields,'Sender');
X push(@bodies,"$USER\n");
X $ndx{'sender'}=$#fields;
X
X # create Reply-To: if missing
X if (($i=$ndx{'reply-to'}) < 1
X && ($j=$ndx{'from'}) >= 1)
X { push(@fields,'Reply-To');
X push(@bodies,$bodies[$j]);
X $ndx{'reply-to'}=$#fields;
X }
X
X while (defined($_=pop(@fields)))
X { unshift(@INPUT,$_.': '.pop(@bodies));
X }
X
X unshift(@INPUT,$shifted) if defined($shifted);
X
X &sendmail($to,@INPUT);
X }
X
Xsub sendmail # ($to,@INPUT) -> success
X { local($to)=shift;
X
X if (open(SENDMAIL,"|sendmail -oi $to"))
X { if ($_[0] =~ /^From /)
X { shift;
X }
X
X for (@_)
X { print SENDMAIL $_;
X }
X
X close(SENDMAIL);
X return 1;
X }
X else
X { &legend("can't pipe to sendmail: $!");
X }
X
X 0;
X }
X
Xsub fileas # (inbox,inname) -> void
X { local($inbox,$inname)=@_;
X
X push(@MAILBOXES,"$inbox");
X push(@INNAMES,"$inname");
X }
X
Xsub shorten # (pathname) -> indicator
X { local($_)=@_;
X
X if (length($_) > length($HOME)
X && substr($_,$[,length($HOME)) eq $HOME)
X { $_=substr($_,$[+length($HOME));
X s,^/+,,;
X }
X
X s,^private/+,,;
X s,^etc/mail/+,+,;
X
X $_;
X }
X
Xsub legend # (message) -> void
X { local($_)=@_;
X
X if ($didlegend)
X { print " " x $didlegend;
X }
X else
X { local($str)=&datestr(time,1).": ";
X print $str;
X $didlegend=length($str);
X }
X
X s/\n+$//;
X printf("%.160s\n",$_);
X }
EOF-/home/cs/spectrum/fuligin/1/cameron/bin/filemail
sed 's/^X//' > splitmail <<'EOF-/home/cs/spectrum/fuligin/1/cameron/bin/splitmail'
X#!/usr/local/bin/perl
X#
X# Split up an ordinary mailbox (From_ separated).
X# Uses filemail to deposit the mail, so the .file refiler works.
X# - Cameron Simpson, February 1992
X#
X
X($cmd=$0) =~ s,.*/,,;
X$usage="Usage: $cmd [-m mailbox] [mailfiles...]
X -m mailbox Specify directory into which to place mail.
X";
X
X# option defaults
Xif (!defined($ENV{'MAILBOX'}))
X { $mailbox='.';
X }
Xelse
X{ $mailbox=$ENV{'MAILBOX'};
X}
X
X# option parsing
Xif ($#ARGV > 0 && $ARGV[0] eq '-m')
X { shift;
X $mailbox=shift;
X }
X
X# export to filemail
X$ENV{'MAILBOX'}=$mailbox;
X
X$xit=0;
Xif ($#ARGV < 0)
X { &splitmail('STDIN','stdin');
X }
Xelse
X{ for (@ARGV)
X { if (!open(IN,"< $_\0"))
X { print STDERR "$cmd: can't open $_: $!\n";
X $xit=1;
X next;
X }
X
X &splitmail('IN',$_);
X close(IN);
X }
X}
X
Xexit $xit;
X
Xsub splitmail # (STREAM,fname)
X { local($F,$f)=@_;
X local($hot);
X
X $hot=0; # is our pipe hot?
X while (<$F>)
X { if (/^From /o)
X { if ($hot)
X { close(PIPE); # Phew!
X }
X
X if (open(PIPE,"|filemail"))
X { $hot=1;
X }
X else
X { print STDERR "$cmd: can't pipe to filemail ($!)\n";
X $hot=0;
X $xit=1;
X }
X }
X
X if ($hot)
X { print PIPE $_;
X }
X else
X { print STDERR "$cmd: discarding: $_";
X $xit=1;
X }
X }
X
X if ($hot)
X { close(PIPE);
X }
X }
EOF-/home/cs/spectrum/fuligin/1/cameron/bin/splitmail
sed 's/^X//' > dotfile.inbox <<'EOF-/home/cs/spectrum/fuligin/1/cameron/etc/mail/inbox/.file'
X#!/usr/local/bin/perl
X
Xif (!$F_HAS_SUBJECT)
X { &legend("no subject, rejecting message from $f");
X if (!open(M,"| m -S -s 'your message has been rejected' '$errors_to'"))
X { &legend("can't pipe to m: $!");
X }
X else
X { print M <<'X'
XYour message has been rejected because you did not supply a subject line.
XThe rejected message is appended below in case you want to resend it.
XX
X;
X
X if (defined($SIGNATURE)
X && length($SIGNATURE)
X && open(S,"< $SIGNATURE\0"))
X { while (<S>) { print M $_; }
X close(S);
X }
X
X if (open(S,"sig |"))
X { while (<S>) { print M $_; }
X close(S);
X }
X
X print M "\n";
X for (@hdrs)
X { print M $_, "\n";
X }
X
X print M "\n", @INPUT;
X close(M);
X }
X }
X
Xif (/^ACSnet badhandler/)
X { &forw($_,'neilb',@INPUT); # Forward to NeilB.
X }
X# Stuff sent over ACSnet.
Xelsif (/^(Files|".*") from (\w+) at \S+$/)
X { &legend("$s for $to");
X if ($1 eq 'Files')
X { for (@INPUT)
X { next unless /^\s+Mode\s+Size\s+Modify time\s+Name/../^Please use/;
X next if /^Please use/ || /^\s*$/;
X print "\t\t$_";
X }
X }
X else
X { local($fname,$who)=($1,$2);
X
X $fname =~ s/"(.*)"/$1/;
X
X if ($fname eq "$who.acc")
X { system("cd \$HOME/admin/mkacc && getfile -Y '$fname' 2>&1");
X }
X }
X
X $filed=1;
X }
Xelsif ($from eq '
[email protected]'
X && $s eq 'I am on vacation at the moment.')
X { &legend("$from is on vacation");
X $files=1;
X }
Xelsif ($to =~ /
[email protected]/
X || $cc =~ /
[email protected]/)
X { $MAILBOX.='/faces';
X }
Xelsif ($f eq '
[email protected] (rec.humor.funny autoreply)')
X { &legend("joke received by rec.humour.funny: $s");
X $filed=1;
X }
Xelsif ($f eq '
[email protected]' && $s eq 'Receipt for mail')
X { $filed=1; # swallow these
X }
Xelsif ($from =~ /^(\w+!)*(postmaster|(mailer-)?daemon|mailer-agent|uucp)@/i
X && ($s eq 'Returned mail: Return receipt'
X || $s eq 'Return receipt' || $s eq 'Return Receipt'
X || $s eq 'Delivery report: Return Receipt'
X )
X )
X { &legend("mail acknowledgement from $from");
X $filed=1;
X }
Xelsif (defined($hdrs{'x-msmail-mailclass'})
X && $hdrs{'x-msmail-mailclass'} eq 'IPM.Microsoft Mail.Read Receipt')
X { &legend("$from has read \"$hdrs{'x-msmail-entitled'}\"");
X $filed=1;
X }
Xelsif ($f eq '
[email protected]'
X && $s =~ /^archie reply: /
X )
X { $MAILBOX="$ENV{'HOME'}/doc/archives/archie.au";
X }
EOF-/home/cs/spectrum/fuligin/1/cameron/etc/mail/inbox/.file
sed 's/^X//' > dotfile.prog <<'EOF-/usr/local/doc/misc/archives/archie.au/prog/.file'
X#!/usr/local/bin/perl
X#
X# Filing routine for my mail autofiler.
X# We expect output from archie's email `prog' query facility.
X#
X
Xif ($s =~ /^archie reply: prog\s+(.*\S)/)
X { $F="$MAILBOX/$1.Z";
X if (open(UNPROG,"| unprog | compress > '$F'\0"))
X { for (@INPUT)
X { print UNPROG $_;
X }
X
X close(UNPROG);
X $legend="saved as $F";
X $filed=1;
X }
X else
X { print STDERR "$cmd: can't pipe to unprog: $!\n";
X }
X }
EOF-/usr/local/doc/misc/archives/archie.au/prog/.file
sed 's/^X//' > libcs.pl <<'EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/libcs.pl'
X#!/usr/local/bin/perl
X#
X# This will be an autoload library at some stage.
X#
X
X# numerical comparitor for sorts
Xsub ncmp
X { $a <=> $b;
X }
X
Xsub min { local($min)=shift;
X for (@_) { ($_ < $min) && ($min=$_); }
X $min;
X }
X
Xsub max { local($max)=shift;
X for (@_) { ($_ > $max) && ($max=$_); }
X $max;
X }
X
Xsub basename # (@pathnames) -> @basenames
X { local(@paths)=@_;
X
X for (@paths)
X { s,/+$,,;
X s,.*/,,;
X length || ($_='.');
X }
X
X return @paths;
X }
X
Xsub dirname # (@pathnames) -> @dirnames
X { local(@paths)=@_;
X local($pfx);
X
X for (@paths)
X { m,^(/?/?)/*,; $pfx=$1; $_=$'; # collect leading slashes
X s,/+$,,; # strip trailing slashes
X s,[^/]+$,,; # strip basename
X s,/+$,,; # strip trailing slashes again
X length($pfx) || ($pfx='./'); # no null paths
X $_=$pfx.$_; # prefix + tail
X }
X
X return @paths;
X }
X
Xsub eval # string -> result
X { print STDERR "eval($_[0])\n";
X eval $_[0];
X }
X
Xsub prt { print STDERR $_[0];
X 1;
X }
Xsub err { &prt($_[0]);
X 0;
X }
X
X# ensure a directory exists
Xsub mkdir # (dir) -> ok
X { local($dir)=@_;
X
X -d $dir
X || (&mkdir(&dirname($dir))
X && (-d $dir
X || mkdir($dir,0777)
X )
X )
X ;
X }
X
Xsub open # (handle,filename,mode) -> ok
X { local($handle,$file,$mode)=@_;
X
X &mkdir(&dirname($file)) && open($handle,"$mode$file");
X }
X
Xsub isatty
X { local($_)=$_[0];
X local($dev,$ino,$mode,@etc);
X
X if (/^\d+$/)
X { if (!open(_FD_ISATTY,"<&$_"))
X { print STDERR "isatty: can't open &$_ ($!)\n";
X return undef;
X }
X ($dev,$ino,$mode,@etc)=stat _FD_ISATTY;
X # no close since it may eat the fd
X }
X elsif (/^[A-Z_]+$/)
X { ($dev,$ino,$mode,@etc)=eval "stat $_";
X }
X else
X { ($dev,$ino,$mode,@etc)=stat($_);
X }
X
X return (defined($mode)
X ? (($mode&(&S_IFMT)) == &S_IFCHR)
X : undef);
X }
X
Xsub catpath # (dir,path) -> fullpath
X { local($_,$path)=@_;
X
X if (length == 0)
X { return $path;
X }
X elsif (length($path) == 0)
X { return $_;
X }
X else
X { return m,/$, ? "$_$path" : "$_/$path";
X }
X }
X
Xsub detab # (tabbed,tabsize) -> untabbed
X { local($line,$tabsize)=@_;
X local($_,$chunk);
X
X defined($tabsize) || ($tabsize=8);
X
X # Bug in regexps?
X # s/\t/' ' x ($tabsize-(length($`)%$tabsize))/eg;
X
X $_='';
X for $chunk (split(/\t/,$line))
X { $_.=$chunk;
X $_.=(' ' x ($tabsize-(length($_) % $tabsize)));
X }
X
X s/[ \t]+$//;
X
X return $_;
X }
X
X# safe rename - doesn't tromp target file if present
Xsub rename # (from,to) -> success
X { local($from,$to)=@_;
X local($ok);
X
X $ok=0;
X if (link($from,$to))
X { $ok=1;
X if (!unlink($from))
X { print STDERR "$cmd: unlink($from): $!, $from still linked to $to\n";
X }
X }
X elsif ($! == &EXDEV)
X # cross device link
X { if (lstat($to))
X { print STDERR "$cmd: $to exists\n";
X }
X else
X { if (!open(RENAME_FROM,"<$from"))
X { print STDERR "$cmd: can't open $from for read: $!\n";
X }
X else
X { if (!open(RENAME_TO,">$to"))
X { print STDERR "$cmd: can't open $to for write: $!\n";
X }
X else
X { while (<RENAME_FROM>)
X { print RENAME_TO;
X }
X
X close(RENAME_TO);
X
X if (unlink($from))
X { $ok=1;
X }
X else
X { print STDERR "$cmd: can't unlink $from ($!), unlinking $to\n";
X if (!unlink($to))
X { print STDERR "$cmd: can't unlink $to: $!\n\tboth $from and $to now exist\n";
X }
X }
X }
X
X close(RENAME_FROM);
X }
X }
X }
X else
X { print STDERR "$cmd: link($from,$to): $!\n";
X }
X
X return $ok;
X }
X
X# weekday names
X@wday_names=('sun','mon','tue','wed','thu','fri','sat');
X@Wday_names=('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
X@Weekday_names=('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
X
X# month names
X@mon_names=('jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec');
X@Mon_names=('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
X@Month_names=('January','February','March','April','May','June','July','August','September','October','November','December');
X
Xsub datestr # (time,uselocaltime) -> "MMmonYY, hh:mm:ss"
X { local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
X =($_[1] ? localtime($_[0]) : gmtime($_[0]));
X
X sprintf("%02d%s%02d, %02d:%02d:%02d",
X $mday,$mon_names[$[+$mon],$year,$hour,$min,$sec);
X }
X
Xsub timestr # (time) -> "[[[days, ]hours, ]minutes, ]seconds"
X { local($time)=$_[0];
X local($str,$slop);
X
X $str="";
X if ($time >= 86400)
X { $slop=$time%86400;
X $time-=$slop;
X $str.=($time/86400)." days, ";
X $time=$slop;
X }
X
X if ($time >= 3600)
X { $slop=$time%3600;
X $time-=$slop;
X $str.=($time/3600)." hours, ";
X $time=$slop;
X }
X
X if ($time >= 60)
X { $slop=$time%60;
X $time-=$slop;
X $str.=($time/60)." minutes, ";
X $time=$slop;
X }
X
X $str.$time." seconds";
X }
X
X$_subopen_handler_number=0;
Xsub subopen # (open-name) -> handle
X { local($file)=@_;
X local($handle)='_SUBOPEN_HANDLE_'.$_subopen_handler_number++;
X
X if (open($handle,$file))
X { return $handle;
X }
X
X undef;
X }
X
Xrequire 'cs/env/misc.pl';
X
X1; # make require happy
EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/libcs.pl
sed 's/^X//' > cs.env.mail.pl <<'EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/cs/env/mail.pl'
Xrequire 'cs/env/misc.pl';
X
Xdefined($SITENAME) || ($ENV{'SITENAME'}=$SITENAME='cs.unsw.oz.au');
X$ENV{'ORGANIZATION'}=$ORGANIZATION='CS&E Computing Facility, Uni Of NSW, Oz';
X
Xdefined($MAILDIR) || ($ENV{'MAILDIR'}=$MAILDIR="$HOME/etc/mail");
Xdefined($MAILRC) || ($ENV{'MAILRC'}=$MAILRC="$MAILDIR/mailrc");
Xdefined($SIGNATURE) || ($ENV{'SIGNATURE'}=$SIGNATURE="$MAILDIR/signature");
Xdefined($OUTMAIL) || ($ENV{'OUTMAIL'}=$OUTMAIL="$MAILDIR/outmail");
Xdefined($DEADMAIL) || ($ENV{'DEADMAIL'}=$DEADMAIL="$MAILDIR/dead.letter");
Xdefined($PFX) || ($ENV{'PFX'}=$PFX='| ');
Xdefined($EMAIL) || ($ENV{'EMAIL'}=$EMAIL="$LUSER\@$SITENAME");
Xdefined($REPLY_TO) || ($ENV{'REPLY_TO'}=$REPLY_TO=$EMAIL);
X
X# for filemail
Xdefined($ANNOUNCE) || ($ENV{'ANNOUNCE'}=$ANNOUNCE=$CONSOLE);
X
X1; # for require
EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/cs/env/mail.pl
sed 's/^X//' > cs.rfc822.pl <<'EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/cs/rfc822.pl'
X@rfc822'mailhdrs=('to','cc','bcc','from','sender','reply-to','return-recipt-to',
X 'errors-to');
X@rfc822'newshdrs=('newsgroups','followup-to');
X$rfc822'mailptn=join('|',@mailhdrs);
X$rfc822'newsptn=join('|',@newshdrs);
X$rfc822'listfieldptn="$rfc822'mailptn|$rfc822'newsptn";
X
X# add a line to @hdrs and %hdrs
X# just adds to @hdrs until it gets "" or "\n" and then sets up %hdrs
X# field names matching $commaptn are concatenated with commas,
X# otherwise with "\n\t".
Xsub add822lines # @lines -> @remaining_lines
X { local($commaptn)=$rfc822'listfieldptn;
X local($_,$hdr);
X
X $hdr='';
X while (defined($_=shift))
X { s/\r?\n$//;
X last if !length;
X
X if (/^\s/)
X { $hdr.="\n$_";
X }
X else
X { length($hdr) && push(@hdrs,$hdr);
X $hdr=$_;
X }
X }
X
X length($hdr) && push(@hdrs,$hdr);
X
X if (defined)
X # parse headers
X { local($key,$field);
X
X undef %hdrs;
X for (@hdrs)
X { if (/^([^\s:]+):\s*/)
X { $key=$1;
X $field=$'; $field =~ s/^\s+//;
X $key =~ tr/_A-Z/-a-z/;
X if (defined($hdrs{$key}))
X { if ($key =~ /^$commaptn$/o)
X { $hdrs{$key}.=', ';
X }
X
X $hdrs{$key}.="\n\t";
X }
X
X $hdrs{$key}.=$field;
X }
X }
X }
X
X @_;
X }
X
X# parse an RFC822 address list returning a list of tuples
X# (leading command, address, trailing comment, ...)
Xsub getaddrs # (addrlist) -> @(precomment, addr, postcomment)
X { local($_)=@_;
X local(@parsed,$pre,$addr,$post);
X
X s/^\s+//;
X while (length)
X { if (/^,/)
X { $_=$';
X if (length($pre) && !length($addr))
X { $addr=$pre; $pre='';
X }
X
X if (length($pre) || length($addr) || length($post))
X { push(@parsed,$pre,$addr,$post);
X }
X
X $pre='';
X $addr='';
X $post='';
X }
X elsif (!length($addr) && /^[-\w_.]+@[-\w_.]+/)
X { $_=$';
X $addr=$&;
X }
X elsif (/^"([^"]|\\")*"/ || /^'([^']|\\')*'/)
X { $_=$';
X if (length($addr))
X { $post .= " $&";
X }
X else
X { $pre .= " $&";
X }
X }
X elsif (/^<[^>\s]*>/)
X { $_=$';
X if (length($addr))
X { $pre.=" $addr";
X }
X
X $addr=$&;
X }
X elsif (/^[^,\s]+/)
X { $_=$';
X if (length($addr))
X { $post.=" $&";
X }
X else
X { $pre.=" $&";
X }
X }
X else
X { print STDERR "trouble parsing, remaining address is \"$_\"\n";
X }
X
X s/^\s+//;
X }
X
X if (length($pre) && !length($addr))
X { $addr=$pre; $pre='';
X }
X
X if (length($pre) || length($addr) || length($post))
X { push(@parsed,$pre,$addr,$post);
X }
X
X for (@parsed)
X { s/^\s+//;
X }
X
X @parsed;
X }
X
Xsub msgid
X { local($sec,$min,$hour,$mday,$mon,$year,@etc)=localtime(time);
X
X $_msgid_count++;
X sprintf("<%s-%02d%02d%02d%02d%02d%02d-%d-%05d@%s>",
X $USER,
X $year,$mon+1,$mday,$hour,$min,$sec,
X $_msgid_count,
X $$,
X $HOSTNAME);
X }
X
X1; # for require
EOF-/home/cs/spectrum/fuligin/1/cameron/etc/pl/cs/rfc822.pl
exit 0