barrett@Daisy.EE.UND.AC.ZA (Alan P. Barrett) (12/17/90)
If you use nn and also another newsreader (I use both nn and trn) then
you may wish for a way of sorting your .newsrc file so that the other
newsreader uses approximately the same presentation sequence as nn does.
Here is a script that sorts a .newsrc file into the order specified by a
.nn/init file. It works OK with my .newsrc and my .nn/init files; your
mileage may vary.
Even if you don't use nn, you might find this script useful; just fake
up a .nn/init file for it. I hope that the summary included in the
source is sufficient to enable you to do this.
The script runs very slowly; perhaps one or more of the perl gurus can
suggest improvements.
--apb
Alan Barrett, Dept. of Electronic Eng., Univ. of Natal, Durban, South Africa
Internet: barrett@ee.und.ac.za (or %ee.und.ac.za@saqqara.cis.ohio-state.edu)
UUCP: m2xenix!quagga!undeed!barrett PSI-Mail: PSI%(6550)13601353::BARRETT
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Alan P Barrett <barrett@undeed> on Sun Dec 16 23:58:24 1990
#
# This archive contains:
# sort-newsrc
#
LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH
echo x - sort-newsrc
sed 's/^@//' >sort-newsrc <<'@EOF'
#!/usr/bin/perl
# Sort a .newsrc file, according to sequence information like that
# used by nn.
# A. P. Barrett <barrett@ee.und.ac.za>, December 1990.
# Inputs:
# 1. The old .newsrc file. Presented on stdin.
# 2. A .nn/init file, or reasonable facsimilie thereof. Named
# as a command line argument, or defaulted to $HOME/.nn/init
# if that exists.
#
# Output:
# A new .newsrc file. Sent to stdout.
#
# Action:
# The .newsrc file is sorted according to the sequence information
# in the .nn/init file. This will not necessarily be identical to the
# order used by nn, because we do not read the global nn/init file, and
# we make no attempt to deal with non-vanilla features of nninit files
# or newsrc files.
#
# Format of nninit file:
# The format is actually more complicated; this script supports only the
# following subset.
# 1. '#' comments work as expected.
# 2. Everything up to and including a line containing just the word
# 'sequence' is ignored.
# 3. Everything from the 'sequence' line to the end of the file defines
# the presentation sequence.
# 4. A group or set of groups in included by listing its name. 'comp.unix'
# matches just one group; 'comp.unix.' matches all subgroups of comp.unix,
# but does not match the parent group; 'comp.unix*' matches both the
# parent and subgroups. '.test' matcehs all groups whose name ends with
# .test.
# 5. A group is excluded by placing a '!' before its name.
# 6. A group is pushed to the start or end of the sequence by preceding it
# with a '<' or a '>'.
# 7. A group that has been excluded may be reconsidered between a pair of
# '%' signs. Thus, '! comp.misc comp. % comp.misc %' will force
# comp.misc to appear after all other comp groups.
# 8. Nn allows several groups may be combined by separating them with commas
# instead of spaces. This script treats commas in the same way as it
# treats spaces.
# 9. Nn allows a group to have a save file and an entry sequence associated
# with it. This script ignores such options.
########################################################################
@@OLDNEWSRC = (); # group names from old newscr file,
# in the order that they were read.
# (unused at present; when the RC flag in the nninit
# file is implemented, this array will probably be
# useful.)
%INFO = (); # information about each group. This is the line
# from the newsrc file (apart from the group name
# itself).
@@SELECTED = (); # group names for new newsrc file,
# in the order selected by the nninit file.
@@TAILEND = (); # groups forced to end of sequence by '>' in nninit.
%UNTOUCHED = (); # groups not yet either selected or deselected
# by the nninit file.
%DESELECTED = (); # groups deselected by '!' in nninit.
$debug = 0; # debugging?
$NNINIT = $ENV{"HOME"}."/.nn/init"; # default nninit file
#
# Process command line args.
#
# This is probably one of the stupidest command line parsers you
# have seen for quite a while.
#
while ($_ = shift (@ARGV)) {
if (/-v/) {
$debug++;
}
else {
$NNINIT = $_;
}
}
#
# Read the .newsrc file from stdin.
#
while (<>) {
# split into group name and other info
($group,$info) = /(.*)([!:].*)/;
warn "read from newsrc: $group\n" if $debug >= 4;
# remember the info (subscribed or unsubscribed; what has been read)
$INFO{$group} = $info;
# remember the order that the groups appeared in the old newsrc
push (@OLDNEWSRC, $group);
# the group is still waiting to be selected
$UNTOUCHED{$group} = 1;
}
#
# Open, slurp and close nninit file
#
open (NNINIT, $NNINIT) || die "cannot open nn init file: $NNINIT: $!\n";
undef $/; # get ready to slurp
$_ = <NNINIT>; # slurp the file
close (NNINIT); # hope there is no error here
#
# Now process the nninit file.
#
undef $lastword; # previous word
$between_percents = 0; # this is true inside a '% ... %' construct.
s/#[^\n]*//g; # ignore comments
s/^(.*\n)*\s*sequence\s*\n//; # ignore stuff before sequence line
s/\(([^\)]|\\.)*\)//g; # ignore entry action inside parentheses
# (allow for \ escapes)
s/,/ /g; # treat commas like spaces
s#\b[+/~]\S*##g; # ignore save file names (words
# starting with +, / or ~)
s/!:\S+/!/; # treat '!:stuff' like '!'
s/([!%<>])/ $1 /; # put spaces around special chars
# to facilitate breaking into words
s/^\s+//; s/\s+$//; # kill leading and trailing blanks
foreach $word (split (/\s+/,$_)) { # process each word
warn "word from nninit: $word\n" if $debug > 3;
# '!', '<' and '>' must be followed by group name
if (($lastword =~ /[!<>]/) && ($word =~ /[%!<>]/)) {
die "$NNINIT line $.: !, < or > must be followed by group name\n";
}
# '%' toggles state
elsif ($word eq '%') {
$between_percents = ! $between_percents;
}
# '!', '<' and '>' just get remembered
elsif ($word =~ /[!<>]/) {
# do nothing
}
# otherwise it must be a group name to be selected or deselected
else {
@MATCHED = ();
# check the untouched groups
&find_matches (*UNTOUCHED, *MATCHED, $word);
# if between percents, also check deselected groups
&find_matches (*DESELECTED, *MATCHED, $word) if $between_percents;
if ($#MATCHED < 0) {
warn "nothing matched: $word\n" if $debug;
}
# process the groups just matched
elsif ($lastword eq '!') {
foreach $group (@MATCHED) {
$DESELECTED{$group} = 1;
}
warn "deselect: ".join(" ",sort(@MATCHED))."\n" if $debug;
}
elsif ($lastword eq '<') {
unshift (@SELECTED, sort(@MATCHED));
warn "force to start: ".join(" ",sort(@MATCHED))."\n" if $debug;
}
elsif ($lastword eq '>') {
push (@TAILEND, sort(@MATCHED));
warn "force to end: ".join(" ",sort(@MATCHED))."\n" if $debug;
}
else {
push (@SELECTED, sort(@MATCHED));
warn "select: ".join(" ",sort(@MATCHED))."\n" if $debug;
}
}
# remember the word just read
$lastword = $word;
} # process next word
#
# Now we can output the results.
# As well as the selected groups (from the @SELECTED and
# @TAILEND arrays), we also output deselected groups
# and untouched groups
#
warn "SELECTED: ".join(" ",@SELECTED)."\n" if $debug >= 2;
warn "TAILEND: ".join(" ",@TAILEND)."\n"
if ($debug >= 2 && $#TAILEND >= 0);
warn "UNTOUCHED: ".join(" ",keys(%UNTOUCHED))."\n"
if ($debug >= 1 && $#UNTOUCHED >= 0);
warn "DESELECTED: ".join(" ",keys(%DESELECTED))."\n"
if ($debug >= 1 && $#DESELECTED >= 0);
foreach $group (@SELECTED, @TAILEND, keys(%UNTOUCHED), keys(%DESELECTED)) {
print $group, $INFO{$group}, "\n";
}
#############
sub find_matches
#
# find all groups matching a pattern. delete them from the input
# associative array and push them onto the output array.
# Returns number of matches.
#
{
local (*IN, *OUT, $pattern) = @_;
local ($count) = 0;
# if the pattern is not a wildcard, quickly do what is required.
if (($pattern !~ /^\./) && ($pattern !~ /[.*]$/)) {
if (delete $IN{$pattern}) {
push (@OUT, $pattern);
$count++;
warn "matched: $pattern\n" if $debug >= 3;
}
}
# if the pattern is a wildcard, check each group for a match
else {
$pattern =~ s/(\W)/\\$1/g; # quote all special chars in the pattern
$pattern = "^$pattern$"; # anchor the pattern
$pattern =~ s/\\\*/.*/g; # '*' becomes '.*'
$pattern =~ s/\\\.\$$/\\./; # '.' at end un-anchors pattern
$pattern =~ s/^\^\\\./\\./; # '.' at start un-anchors pattern
$pattern =~ s/^\^\.\*//; # '^.*' at start is useless
$pattern =~ s/\.\*\$$//; # '.*$' at end is useless
warn "matching pattern: /$pattern/\n" if $debug >= 3;
foreach $group (keys (%IN)) {
if ($group =~ /$pattern/) {
delete $IN{$group};
push (@OUT, $group);
$count++;
warn "matched: $group\n" if $debug >= 3;
}
}
}
# return result
$count;
}
@EOF
chmod 755 sort-newsrc
exit 0rock@warp.Eng.Sun.COM (Bill Petro) (12/18/90)
barrett@Daisy.EE.UND.AC.ZA (Alan P. Barrett) writes: >If you use nn and also another newsreader (I use both nn and trn) then >you may wish for a way of sorting your .newsrc file so that the other >newsreader uses approximately the same presentation sequence as nn does. >Here is a script that sorts a .newsrc file into the order specified by a >.nn/init file. It works OK with my .newsrc and my .nn/init files; your >mileage may vary. I have had the problem (after running nntidy, I think) that running rn or trn after nn causes rn to complain severely about the way that .newsrc was left. Error messages like: Warning! Somebody reset news.announce.important--assuming nothing read. Unread news in news.announce.important 11 articles Warning! Somebody reset comp.binaries.mac--assuming nothing read. Unread news in comp.binaries.mac 1036 articles and on. After taking care of this, and cleaning it up for rn, if I go back to nn, it displays all my newsgroups as NEW. Any ideas? -- Bill Petro {decwrl,hplabs,ucbvax}!sun!Eng!rock "UNIX for the sake of the kingdom of heaven" Matthew 19:12