#!/usr/bin/perl

###
### abbs2nf.pl v0.4 - AREAS.BBS to newsfeeds converter
###
### (c) 1998 rafal wiosna, 2:480/33	mailto: rafamiga@uucp.polbox.pl
###
###	v0.1 98.03.15	first version, developed under OS/2's
###			perl 5.003, tested on 5.004 under Linux
###	v0.2 98.03.16	implemented @IGNORENODES and $COMPACT
###	v0.3 98.03.21	some fixes based on the real-world ifmail config
###	v0.4 98.03.23	comments inside filed 3 caused innd to prompt
###			error; now they're after field 4
###
### If you want to receive an e-mail with the newest and future versions
### of this program just send me a letter with the subject:
###
### SEND NEWEST FUTURE abbs2nf.pl
###

$VERSION = "v0.4";

##
## This program reads the AREAS.BBS file and then outputs newsfeeds-like
## structure on standard output.
##
## Usage:
##
## abbs2nf.pl ./AREAS.BBS /etc/ifmail/Areas >/var/lib/news/etc/newsfeeds
##
## The first argument is AREAS.BBS-style file. It should be generated by your
## DOS mailprocessor, like GEcho, FastEcho, FMail...
##
## The second argument is Areas file with the FIDONET.NAME to news.name
## breakdown. It's not generated by a program but instead you should find
## someone in your net/region that has up-to-date Areas file. Ask yor
## boss/uplink/NC/RC for the nearest ifmail site.
##
## The output should be redirected to newsfeeds file. [See below.]
##
## Notice:
##
## The AREAS.BBS file I used to test this program was generated by GEcho 1.20
## and the sample is included in the distribution directory. If your AREAS.BBS
## file causes my program to behave strangely just send ma an e-mail with your
## AREAS.BBS and program's [bad] output.
##
## The same goes to Areas file, the one I used is included in the directory.
## It's usual ifmail Areas file. If you don't have one don't bother using this
## nice program. 8^P
##
## The output is like this:
##
##	FQHN:\
##		fido.newsgroup.1,\
##		fido.newsgroup.2,\
##		fido.newsgroup.3,\
##		...
##		fido.newsgroup.last,\
##		!junk,!control:\
##			Twf,Wn:
##
## PAY ATTENTION! After generating newsfeeds file always take look at it! At
## the bottom of the file there is a listing of areas missing from Areas file
## you've provided. Unless intentional, the Areas file should be completed.
## (Of course, this footer won't be generated if you set $COMPACT to "1".)
##

##
## configuration section
##

# something you SHOULD change
$myZONE = 2;	# your zone
$myNET = 480;	# your network
$myNODE = 33;	# your node number or node number of your boss


# this array contains [sub]strings of the f:i/d.o netadresses we
# would like to ignore in the newsfeeds file; you could use perl's regex if
# you want to and know what you're doing

@IGNORENODES =	("73:", "9:", "2:480/999");


# some ways to alter program behaviour

$COMPACT = 0;	# put "1" if you don't want any unnecessary, but helpfull
		# comments in newsfeeds file; the header is still there thou
$DEBUG = 0;	# put "1" here for debug output but there's really nothing cool
		# WARNING: produces HUGE newsfeeds
$WARNINGS = 1;	# put "1" here for warning messages on STDERR [recomended]


##
## private variables definition DO NOT CHANGE! You have been warned.
##

%nodes = ();	# nodes hash holding the addresses used in AREAS.BBS file
%arean = ();	# areaname-to-newsgroup hash
%lonely = ();	# hash containing names of areas missing in Areas file

##
## program starts here
##
## initialisation
##

$AREAS_BBS = $ARGV[0];	# this could be done in if statement but what
$Areasfile = $ARGV[1];	# do I care, let the code be nice and clean 8^)

if ((! defined $AREAS_BBS) || (! defined $Areasfile))	# no filenames
	{
	die "ERROR: in arguments; read the program text for instructions!\n";
	}

if (! $DEBUG)	# if we DEBUG we don't need sanity checking
{
die "ERROR: the file $AREAS_BBS does not exist!\n" if (! -e $AREAS_BBS);
die "ERROR: the file $Areasfile does not exist!\n" if (! -e $Areasfile);
}

##
## the flow
##

$lnum = 0;

open (ABBS, "$AREAS_BBS") || die "ERROR: cannot open $AREAS_BBS file: $!";

while (<ABBS>)
	{
	next if (/^;/ || ! $lnum++);	# skip the 1st line and all the lines
					# beginning with ";"
	chomp;
	s/(\s+)/ /g;			# get rid of unnecessary white spaces
	@entr = split / /;		# split entries
	$entr[1] =~ tr/a-z/A-Z/;	# fido areas are UPPERCASE
	$nentr = scalar @entr;		# get number of entries

	if ($nentr < 3)		# if there are no links just ignore this line
				# with optional warning
				# In GEcho, special areas like dupes or
				# personal have no links and we definitely
				# don't want them in newsfeeds 8^)
		{
		print STDERR "WARNING: area $entr[1] has no links, ignoring\n" if ($WARNINGS);
		next;
		}
	
if ($DEBUG)
{
	print "## DEBUG: $nentr -> ";	# this DEBUG prints entries and
					# quantity
	foreach $tmp (@entr)
		{
		print "$tmp,";
		}
	print "\n";
}

##
## now fix the links entries by adding the missing bits, i.e.
##	"2:480/19" "33" ".7" "55" "66.1" "1:120/53" will become
##	"2:480/19" "2:480/33" "2:480/33.7" "2:480/55" "2:480/66.1" "1:120/53"
##
## additionally push area entry to @[FQHN] array, where [FQHN] is, guess, FQHN
## and then make an entry in %nodes hash like this: FQHN => f:i/d.o
##
	$czone = $myZONE;	# in case AREAS.BBS entries are wacky
	$cnet = $myNET;		# we should fix it also by preloading
	$cnode = $myNODE;	# address variables
	$cpoint = 0;

	$mentr = 1;	# start from 3rd line element
			# [yes, here's 1, no mistake]

	while ($mentr++ < $nentr-1)
		{

		$_ = $entr[$mentr];

		($czone,$_) = split /:/ if /:/;		# get zone
		$cpoint = 0;				# if there's no point
							# assume .0
		($_,$cpoint) = split /\./ if /\./;	# get point
		($cnet,$_) = split /\// if /\//;	# get net
		$cnode = $_ if ($_);			# get node; if empty
							# remember the previous

		$_ = "$czone:$cnet/$cnode.$cpoint";	# just one more slight
		s/(.*)\.0/$1/g if /\.0$/;		# to fix unwanted .0's

		$entr[$mentr] = $_;	# and we're finished! peeking the value
					# back to @entr is my good will 8^)

##
## now convert f:i/d.o address into FQHN [fully qualified host name]
##

		$fqhn = "";			# start with empty string
		$fqhn = "p$cpoint." if /\./;	# if there's point prepend
						# pxxx. to FQHN
		$fqhn .= "f$cnode.n$cnet.z$czone.fidonet.org";
						# the FQHN in all it's glory

		push @{$fqhn}, $entr[1];	# push the area name onto
						# array named by FQHN

		$nodes{$fqhn} = $_;	# put f:i/d.o into %nodes hash; it's
					# hash because node could be a link
					# in many conferences and hash
					# simplifies the process [no dupes]

if ($DEBUG)	# DEBUG print the FQHN and hash entry
{
		print "## DEBUG: FQHN: $fqhn HASH: $nodes{$fqhn}\n";
}

		}


	}

close (ABBS);	# we don't need AREAS.BBS file no more

if ($DEBUG)	# DEBUG print FQDN, fido addres and areas
{
	print "\n## DEBUG: FQHN to f:i/d.o breakdown with areas\n";

	foreach $tmp (sort keys %nodes)
		{
		print "## DEBUG: key $tmp value $nodes{$tmp}\n## DEBUG: areas: @{$tmp}\n";
		}
}

##
## since we have nice arrays with areas for every link ever appeared in
## AREAS.BBS file now we have to open Areas file, read it in and convert
## the area tag names to newsgroup names
##
## we could issue some warnings about areas that have no newsgroup associated,
## the user will be more than happy indeed
##

if ($DEBUG)
{
	print "\n## DEBUG: Areas file read\n";
}

open (AREAS, "$Areasfile") || die "ERROR: cannot open $Areasfile file: $!";

while (<AREAS>)
	{
	chomp;
	next if (/^[#\*]/ || /^$/ || /^\s$/);	# lines we could forget
	s/(\s+)/ /g;				# white space exterminator
	($aname,$ngroup,$tmp) = split / /;
	$aname =~ tr/a-z/A-Z/;			# fido areas are UPPERCASE
	$ngroup =~ tr/A-Z/a-z/;			# newsgroups are lowercase

if ($DEBUG)
{
	print "## DEBUG: $aname -> $ngroup\n";
}

	$arean{$aname} = $ngroup;	# hash key areaname with newsgroup

	}

close (AREAS);	# sayonara Areas file

##
## now we have areaname to newsgruop hash ready, stdout newsfeeds style
##
## the %lonely hash is created, with names of areas not found in Areas file
##

$time = localtime;	# nice touch

print "##\n## abbs2nf.pl $VERSION -- AREAS.BBS to newsfeeds converter\n";
print "##\n## (c) 1998 rafal wiosna   mailto: rafamiga\@uucp.polbox.pl\n##\n";
print "## Read program text for instruction on requirements and usage.\n";
print "## If this file's a mess you've probably switched places of\n";
print "## Areas and AREAS.BBS files in the command line! 8^)\n";
print "##\n## THIS FILE HAS BEEN GENERATED AUTOMAGICALLY, change on your own risk!\n";
print "## Generation date: $time\n##\n";

$nnum = 0;

foreach $tmp (sort keys %nodes)		# main loop thru nodes hash
	{
	@comments = ( );	# array of comments to add after field 4

	$nnode = $nodes{$tmp};	# get f:i/d.o address

	$wayout = 0;

	foreach $ignore (@IGNORENODES)	# ignore nodes magic
		{
		$_ = $nnode;
		$wayout++ if /$ignore/;	# if matches increment

if ($DEBUG)	# this DEBUG prints the ignore results; had some troubles witit
	{
	print "\n## DEBUG: node $nnode ignore(s) \"@IGNORENODES\" wayout $wayout";
	}

		}

	if ($wayout)			# if $wayyout > 0 this node matched
		{
		if ($WARNINGS)
			{		
			print STDERR "WARNING: The node $_ is ignored\n";
			}
		if (! $COMPACT)		# friendly mode
			{
			print "\n# $_ node ignore ruled by \@IGNORENODES, next!\n";
			}
		next;			# please
		}

	$nnum++;

	print "\n";

	if (! $COMPACT)		# friendly mode
		{
		print "# *** NODE #$nnum -- $nodes{$tmp}\n#\n";
		}

##
## this idea is obsolete
##
##	$nnode =~ tr/:.\//_/;		# translate :./ to _ for nickname

	print "$tmp:\\\n";		# 1st line; FQHN

	sort @{tmp};			# sort the areas nicely

	foreach $narea (@{$tmp})	# loop thru array containing areanames
					# for this link
		{
		if (! defined $arean{$narea})	# if this area has no
						# coresponding newsgroup
						# warn and ignore
			{
			if ($WARNINGS)
				{
				print STDERR "WARNING: $narea not in $Areasfile file, ignoring\n";
				}
			if (! $COMPACT)		# friendly mode
				{
				push @comments, "# IGNORED: $narea, not in $Areasfile";
				}
			$lonely{$narea}++;	# increment hash
			}
		else				# standard output for areas
			{
			print "\t$arean{$narea},\\\n";
			}
		}

	print "\t!junk,!control:\\\n";		# end of field 3;
						# exclude unwanted stuph
 	print "\t\tTf,Wfb,B4096/1024:\n";	# field 4; flags

	if (! $COMPACT && scalar @comments gt 0)	# friendly mode
		{
		print "# Notice:\n#\n";		# print out all the ignored
						# areas at the end of feed
						# definition
		foreach $ignore (@comments)
			{
			print "$ignore\n";
			}

		print "#\n";
		}
	}

##
## print the lonely areas
##

if (! $COMPACT)		# friendly mode
	{
	print "\n##\n## End of newsfeeds definition\n##\n\n##\n## The following areas ";
	print "are missing in Areas file:\n##\n";

	foreach $tmp (sort keys %lonely)
		{
		print "## $tmp -- $lonely{$tmp} occurence";
		print "s" if ($lonely{$tmp} > 1);	# be sooo clever
		print "\n";
		}
	}

##
## it's bed time, Dexter
##

print	"\n##\n## End of newsfeeds file\n##\n\n";	# say goodbye

##
## end of program
##
