[comp.lang.perl] Yet another newsrc fixer

dwallach@soda.berkeley.edu (Dan Wallach) (06/08/91)

Well, this is my first perl program, so you'll have to excuse the verbosity.
I'm sure Larry, Randall, or Tom could probably write this in about three lines
of modem noise, but it does what I want. :-)

Basically, you keep a file in your home directory called .news.favorite
which lists, in order, what groups you prefer reading.  When 100 new newsgroups
suddenly get added one day, run the program, and your favorites stay on
top of the newsgroup, and the remainder are sorted first by : or !, then
alphabetically.

JAPH,

Dan Wallach
dwallach@soda.berkeley.edu

P.S.  Kudos to Larry and Randall for the Perl book.  I just read it, sat
      down, and cranked this program straight out.


(I call this fixnewsrc -- you can call it whatever you want :-)
#!/usr/bin/perl

# FixNewsrc V1.0 by Dan Wallach
# dwallach@soda.berkeley.edu

sub counter {
    $counter++;
    print STDERR "$counter..." if ($counter % 100) == 0;
}

sub tally_counter {
    print STDERR "$counter\n";
}

sub clear_counter {
    $counter = 0;
}

sub print_favorites {
    print STDERR "Parsing favorites: ";
    foreach(<FAVORITE>) {
	chop;
	&counter;
	push (@output, "$newsrc{$_}\n");
	undef $newsrc{$_};
    }
    &tally_counter;
}

if(@ARGV) {
    print <<NO_MORE_HELP;
fixnewsrc, V1.0 by Dan Wallach <dwallach@soda.berkeley.edu>

Usage: $0       [no arguments]

This program sorts your .newsrc, putting groups you read on top.  In addition,
if you have a file in your home directory called .news.favorite, then the
list of newsgroups in this file appear at the top of your .newsrc, so you
can still read groups in your favorite order.

Example:

rec.humor.funny
alt.fan.warlord
comp.windows.x.announce
ucb.computing.announce
comp.lang.perl

Here, you will read rec.humor.funny first, and so on through comp.lang.perl,
then you will continue reading active groups in alphabetical order.
NO_MORE_HELP
    exit 0;
}

die "No .newsrc file!" unless -e "$ENV{HOME}/.newsrc";
open(NEWSRC, "$ENV{HOME}/.newsrc") || die "Can't open .newsrc";

# we want to keep this associative array around for printing favorites
# so if we've already printed something, we just delete it from the
# associative array, and go on.

print STDERR "Reading groups: ";
&clear_counter;
foreach(<NEWSRC>) {
    chop;
    &counter;
    local($group) = split(/[:!]/);  # not necessary, but fun
    $newsrc{$group} = $_;
}
&tally_counter;

# output time... clear the counter and let's deal with the favorites file
&clear_counter;

if (open(FAVORITE, "$ENV{HOME}/.news.favorite")) {
    &print_favorites;
} else {
    print "No .news.favorite file found.  Just sorting .newsrc\n";
}

print STDERR "Sorting...";
@newsrc = sort %newsrc;
print STDERR "Generating output: ";

# normally, when we go from the associative array to the scalar array,
# we get a ton of junk -- the associative array's keys and also, for some
# strange reason, blank lines where I undefined stuff earlier.  The if/elsif
# here weeds all that crap out.
foreach(@newsrc) {
    if(/:/) {
	&counter;
	push (@output, "$_\n");
    } elsif (/!/) {
	&counter;
	push (@output2, "$_\n");
    }
}
&tally_counter;

close(NEWSRC);
rename("$ENV{HOME}/.newsrc", "$ENV{HOME}/.newsrc.bak") ||
    die "Can't rename .newsrc";

open(NEWSRC, "> $ENV{HOME}/.newsrc") || die "Can't open .newsrc for writing";
print NEWSRC @output, @output2;

tchrist@convex.COM (Tom Christiansen) (06/08/91)

From the keyboard of dwallach@soda.berkeley.edu (Dan Wallach):
:Well, this is my first perl program, so you'll have to excuse the verbosity.
:I'm sure Larry, Randall, or Tom could probably write this in about three lines
:of modem noise, but it does what I want. :-)

Since modem noise often lacks newlines, this may indeed be possible. :-)

:Basically, you keep a file in your home directory called .news.favorite
:which lists, in order, what groups you prefer reading.  When 100 new newsgroups
:suddenly get added one day, run the program, and your favorites stay on
:top of the newsgroup, and the remainder are sorted first by : or !, then
:alphabetically.

rn adds the new ones to the end anyway, right?, so who do you get
your normal favorites messed up?

:    foreach(<FAVORITE>) {
:	chop;
:	&counter;
:	push (@output, "$newsrc{$_}\n");
:	undef $newsrc{$_};

I think you what you want here is

	delete $newsrc{$_};

because you won't want $newsrc{$_} to have the undefined value:
you want to remove the key $_ entirely.

:print STDERR "Reading groups: ";
:&clear_counter;
:foreach(<NEWSRC>) {
:    chop;
:    &counter;
:    local($group) = split(/[:!]/);  # not necessary, but fun
:    $newsrc{$group} = $_;
:}

You probably don't want to say local() inside an array.  Remember
that local() is a run-time thing, so you'll have a zillion of them 
by the time you're finally done with the loop.

Another way to write this, btw, is 

    ($group) = /^([^!:]+)/;
    $newsrc{$group} = $_;

or even just

    $newsrc{(/^([^!:]+)/)[0]} = $_;  # check your modem here folks

It just looked funny to me to KNOW you'll throw away one of 
the second thing that you split, and to rely on local() to 
supply an array context for split().

:@newsrc = sort %newsrc;
:
:# normally, when we go from the associative array to the scalar array,
:# we get a ton of junk -- the associative array's keys and also, for some
:# strange reason, blank lines where I undefined stuff earlier.  The if/elsif
:# here weeds all that crap out.

That's because when you use an %assoc array as a normal @array, which
you're doing here by sending it off to be sorted, you get all the keys
*AND* all the values in an arbitrary order, kinda like:

    (keyY,valY,keyX,valX,keyW,valW,keyZ,valZ)

Normally you'd just say something like:

    foreach (sort keys %newsrc)

The reason you're getting blank lines for the stuff you undef'd is
that you only gave those keys a value of undef -- you didn't delete
the keys.  Use the delete operator for that, as explained previously.

:close(NEWSRC);
:rename("$ENV{HOME}/.newsrc", "$ENV{HOME}/.newsrc.bak") ||
:    die "Can't rename .newsrc";

Although since you don't print $!, we'll never know why. [pet peeve]

:open(NEWSRC, "> $ENV{HOME}/.newsrc") || die "Can't open .newsrc for writing";
:print NEWSRC @output, @output2;

What happens if the disk fills up?  It might be nice to write to a
temporary file, like ".newsrc.$$", put an explicit close in there, 
check its status, and only if it's ok do the rename.

Just a thought.

--tom
--
Tom Christiansen		tchrist@convex.com	convex!tchrist
	    "Perl is to sed as C is to assembly language."  -me