Subject: Re: A biff for news?

From: [email-masked] (Larry Wall)
Date: 10 Feb 92 18:47:31 GMT

In article <[email-masked]> [email-masked] (Erik E. Rantapaa) writes:
: Does anyone know if there is a program which will keep track of 
: news postings you have made and then periodically check to see
: if there are any responses?  This would be a great utility since
: there are many groups I post to but don't read often, and it's
: a pain to keep checking those groups only to find out that nobody
: responded to your post.

At the risk of soaking up all the spare CPU cycles in the world for the
next 5 years or so, here's my keyword scanner.  It won't just scan the
groups you've posted to recently, but rather scans all newsgroups for
things of interest to you, like your name.  You'll have to modify it
for your own purposes, since my keywords are wired in.  I've made no
attempt to make this fit for general consumption, so consider yourself
warned...

Every time you hit this program with a HUP signal, it will restart its
scan on the newest history file, and rename its output files so that
"n0" is the newest set of clippings, "n1" the next newest, etc.

This program may be C-news specific.  I haven't tried it on B-news for
a long time.

Since this program will soak up all your spare CPU cycles, if multiple
people on the same machine want to run this, it might be well to
combine everyone's scan into one program, and have a way of splitting
out the clippings to the right people.  That way you'd factor out all
the I/O and studying so that it wouldn't be done multiple times.

Also included is the pmeter program, which will stop another process when
the load average gets too high, so that your cohorts don't complain about
histlog when it soaks up all your spare CPU cycles when you don't have any
spare CPU cycles...

Larry

#!/bin/sh
: make a subdirectory, cd to it, and run this through sh.
echo 'If this kit is complete, "End of kit" will echo at the end'
echo Extracting histlog
sed >histlog <<'!STUFFY!FUNK!' -e 's/X//'
X#!/usr/local/bin/perl
X
Xrequire "timelocal.pl";
X
X$HOME = $ENV{'HOME'};
X
Xfork && exit;			# avoid nohup behavior
X
X$pid = $$;
Xfork || exec('pmeter', 3, $pid) && die "Can't exec pmeter: $!\n";
X				# suspend when load average goes over 3
Xsetpriority(0, 0, 16);		# set very low priority
X
X$date = shift;
Xchop($date = `cat $HOME/.lasthist`) if !$date && -f "$HOME/.lasthist";
X$date = &idate($date);
X
Xchdir "/usr/spool/news" || die "Can't cd: $!\n";
X
Xselect(STDERR);
X$| = 1;
Xselect(STDOUT);
X$| = 1;
X
X&CATCH;
X$SIG{'HUP'} = 'CATCH';		# send SIGHUP to switch to new history file
X
X$r = "\r" if -t STDOUT;
X
X($dev,$ino,$mode,$nlink,$uid) = stat STDOUT;
X$origuid = $uid;
Xfor (;;) {
X    while (<LOG>) {
X	$pos = tell(LOG);
X	chop;
X	($messid,$date,$nglist) = split(/\t/);
X	next if $nglist =~ /comp\.lang\.perl/;
X	$date = &idate($date);
X	($ng,$art) = split(m![ /]!,$nglist);
X	next if $ng eq 'cancelled';
X	$ng =~ y!.!/!;
X	open(ART,"$ng/$art") || next;
X	next if -s ART > 100000;
X	$count = 0;
X	++$slept;		# to force quick update after big batch
X	$/ = '';
X	$header = <ART>;
X	$_ = '';
X	&hit, next if $header =~ /comp\.lang\.perl/;
X	while (<ART>) {
X	    study;
X	    &hit,next if /larry[^\000]wall/i;
X	    &hit,next if /gloria[^\000]wall/i;
X	    &hit,next if /sharon[^\000]hopkins/i;
X	    &hit,next if /\bbiggar\b/i;
X	    &hit,next if /\bpatch\sprogram\b/;
X	    &hit,next if /\bmetaconfig\b/;
X	    &hit,next if /\bdualmanager\b/i;
X	    &hit,next if /\bnetlabs\b/i;
X	    if (/\bperl\b/i) {
X		if ($' !~ /^@/ && $' !~ /adjusted for inflation/
X		  && $` !~ /!$/) {
X		    &hit;
X		    next;
X		}
X	    }
X	    if (/\bConfigure\b/) {
X		next if $' =~ /^\s(the|it|my|your)\b/;
X		next if $ng =~ /\bwindows\b/;
X		next if $ng =~ /\bmac\b/;
X		&hit;
X		next;
X	    }
X	}
X	close ART;
X	$sleep = 5;
X    }
X    continue {
X	$/ = "\n";
X    }
X    print STDERR "histlog: caught up$r\n" unless $tailing++;
X    sleep $sleep;
X    $slept += $sleep;
X    if ($date != $lastdate && $slept > 300) {
X	($dev,$ino,$mode,$nlink,$uid,$gid) = stat STDOUT;
X	exit unless $uid == $origuid;
X	open(LASTDATE,">$HOME/.lasthist");
X	print LASTDATE &cdate($date),"\n";
X	close LASTDATE;
X	$lastdate = $date;
X	$slept = 0;
X    }
X    $sleep++ if $sleep < 120;
X    seek(LOG,$pos,0);
X}
X
Xsub hit {
X    return if $_ eq "Larry Wall\[email-masked]\n";
X    ($before,$match,$after) = ($`,$&,$');
X    $match =~ s/(.)/_\b$1/g;
X    if (length() > 500) {
X	$o = rindex($before,"\n");
X	$o = rindex($before,"\n",$o - 1) if $o >= 0;
X	$o = rindex($before,"\n",$o - 1) if $o >= 0;
X	$o = rindex($before,"\n",$o - 1) if $o >= 0;
X	if ($o > 0) {
X	    $before = "...\n" . substr($before, $o + 1);
X	}
X	$o = index($after,"\n");
X	$o = index($after,"\n",$o + 1) if $o >= 0;
X	$o = index($after,"\n",$o + 1) if $o >= 0;
X	$o = index($after,"\n",$o + 1) if $o >= 0;
X	if ($o > 0) {
X	    $after = substr($after, 0, $o) . "...\n";
X	}
X    }
X    $_ = "$before$match$after";
X    unless ($count++) {
X	$header =~ /From: (.*)/;
X	($from = $1) =~ s/.*\((.*)\).*/$1/;
X	print("$r\n\@$ng/$art  \tFrom: $from$r\n") || exit;
X	$header =~ /Subject: (.*)/ && print "Subject: $1$r\n";
X	$dots = 0;
X	$0 = "histlog " . &cdate($date);
X    }
X    s/\n/$r\n/g if $r;
X    print() || exit;
X    $dots = 0;
X}
X
Xsub seekdate {
X    local($start) = shift;
X    if (substr($start,0,4) eq '9999') {
X	seek(LOG,0,2);
X	$pos = tell(LOG);
X	print STDERR "$pid starting at eof...$r\n";
X	return;
X    }
X    ($st_dev,$st_ino,$st_mode,$st_nlink,$st_uid,$st_gid,$st_rdev,$st_size,
X	$st_atime,$st_mtime,$st_ctime,$st_blksize,$st_blocks) = stat(LOG);
X    for ($offset = $st_size - 100000; $offset > 0; $offset -= 100000) {
X	if (seek(LOG,$offset,0)) {
X	    $_ = <LOG>;            # probably starts in middle of a line
X	    $_ = <LOG>;
X	    ($messid,$date,$nglist) = split(/\t/);
X	    $date = &idate($date);
X	    last if $date < $start;
X	}
X	else {
X	    $offset = -1;
X	}
X    }
X    seek(LOG,0,0) if $offset < 0;
X    while (<LOG>) {
X	($messid,$date,$nglist) = split(/\t/);
X	$date = &idate($date);
X	last if $date >= $start;
X    }
X    $pos = tell(LOG);
X    $pct = int($pos * 100 / $st_size);
X    print STDERR "$pid starting at $pct% for $start...$r\n";
X}
X
Xsub CATCH {
X    &openout;
X    open(LOG,'/usr/lib/news/history') || die "Can't open log: $!\n";
X    $date = 9999999999 unless $date;
X    &seekdate($date);
X    $tailing = 0;
X    $slept = 300;		# force immediate update of lasthist.
X}
X
Xsub cdate {
X    ($sec,$min,$hr,$mday,$mon,$year) = gmtime($date);
X    sprintf("%02d/%02d/%02d %02d:%02d",$mon+1,$mday,$year,$hr,$min);
X}
X
Xsub idate {
X    $_[0] =~ m#(\d+)/(\d+)/(\d+) (\d+):(\d+)#
X      ? &timegm(0, $5, $4, $2, $1-1, $3)
X      : $_[0];
X}
X
Xsub openout {
X    rename("$HOME/tmp/n8", "$HOME/tmp/n9");
X    rename("$HOME/tmp/n7", "$HOME/tmp/n8");
X    rename("$HOME/tmp/n6", "$HOME/tmp/n7");
X    rename("$HOME/tmp/n5", "$HOME/tmp/n6");
X    rename("$HOME/tmp/n4", "$HOME/tmp/n5");
X    rename("$HOME/tmp/n3", "$HOME/tmp/n4");
X    rename("$HOME/tmp/n2", "$HOME/tmp/n3");
X    rename("$HOME/tmp/n1", "$HOME/tmp/n2");
X    rename("$HOME/tmp/n0", "$HOME/tmp/n1");
X    open(STDOUT, ">$HOME/tmp/n0");
X    open(STDERR, ">&STDOUT");
X}
!STUFFY!FUNK!
echo Extracting pmeter
sed >pmeter <<'!STUFFY!FUNK!' -e 's/X//'
X#!/usr/local/bin/perl
X
X($loadavg, $pid, $debug) = @ARGV;
X$running = 1;
X
Xwhile (kill 0, $pid) {
X    `/usr/ucb/uptime` =~ /load average:\s+([\d.]+)/
X	|| die "Can't run uptime: $!\n";;
X
X    if ($1 > $loadavg) {
X	kill 'STOP', $pid;
X	if ($running) {
X	    print STDERR "stopping at $1\r\n" if $debug;
X	    $0 = "pmeter (stopped $pid at $loadavg)";
X	    $running = 0;
X	}
X    }
X    else {
X	kill 'CONT', $pid;
X	if (!$running) {
X	    print STDERR "starting at $1\r\n" if $debug;
X	    $0 = "pmeter (started $pid at $loadavg)";
X	    $running = 1;
X	}
X    }
X    sleep 120;
X}
!STUFFY!FUNK!
echo ""
echo "End of kit"
: I do not append .signature, but someone might mail this.
exit