#!/usr/bin/perl -wT
#
# Create cdb file from flat alias file. DPC: 15/10/98.
# Args:      source  (may be relative or absolute)
#            target  (may be relative or absolute. Default = source)
# Generates: target.cdb
#            target.tmp
#
# Little Perl script to convert flat file into CDB file. Two advantages over
# cdbmake-12 awk script that is distributed with CDB:
#  1) Handles 'dpc22:dpc22@hermes' as well as 'dpc22 dpc22@hermes'
#  2) Perl works with arbitary length strings: awk chokes at 1,024 chars
#
# $Cambridge: hermes/src/admin/mkcdb,v 1.13 2010/02/16 17:39:22 fanf2 Exp $

use strict;

$ENV{'ENV'} = '';
$ENV{'PATH'} = "";
umask(022);

my $CDB = '/opt/cdb/bin/cdbmake';

my $prog = $0;
$prog =~ s|(.*/)?([^/]+)|$2|;

my $aliases;
if (@ARGV > 1 and $ARGV[0] eq '-a') {
	$aliases = 1;
	shift;
}

my $re_localpart = qr{[.?!#$%&*+=^`'|/_A-Za-z0-9-]+};
my $re_label = qr{[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?};
my $re_domain = qr{($re_label[.])*$re_label};
# note exim treats email addresses starting with # as comments
my $re_address = qr{(?!#)$re_localpart[@]$re_domain};

my $source;
my $target;
if (@ARGV == 1) {
    $source = shift(@ARGV);
    $target = $source;
} elsif (@ARGV == 2) {
    $source = shift(@ARGV);
    $target = shift(@ARGV);
} else {
    die("usage: $prog [-a] <source> [<target>]\n"
      . "	-a	check aliases file syntax\n");
}
# trust the invoker ?!
$source =~ /(.*)/;
$source = $1;
$target =~ /(.*)/;
$target = $1;

open(SOURCE, "< ${source}")
    or die("$prog: open < $source: $!\n");

open(PIPE, "| $CDB $target.cdb $target.tmp")
    or die("$prog: open | $CDB $target: $!\n");

sub add_item ($$) {
    my $key = shift;
    my $val = shift;
    if ($aliases) {
	my $error;
	if ($key !~ /^$re_localpart$/) {
	    warn "$prog: \"$key\" is not a valid email address local part\n";
	    $error++;
	}
	for my $addr (split /\s*,\s*/, $val) {
	    if ($addr !~ /^$re_address$/) {
		warn "$prog: \"$addr\" is not a valid email address\n";
		$error++;
	    }
	}
	if ($error) {
	    warn "$prog: skipping alias \"$key\"\n";
	    return;
	}
    }
    printf PIPE ("+%d,%d:%s->%s\n", length($key), length($val), $key, $val);
}

sub add_line ($) {
    my $line = shift;
    if ($line =~ /^([^\s:]+)\s*:\s*(.*)$/s) {   # key : values
	return add_item($1,$2);
    }
    if ($line =~ /^(\S+)\s+(.*)$/s) {       # key values
	return add_item($1,$2);
    }
    if ($line =~ /^(\S+)$/s) {              # key (empty value)
	return add_item($1,'');
    }
    warn "$prog: unrecognized item: $line";
}

my $data;
while(<SOURCE>) {
    next if /^#/ or /^\s*$/;
    m/^(\s*)(\S.*?)\s+$/s;
    if (length($1) == 0) {
	    add_line($data) if defined $data;
	    $data = $2;
    } else {
	    $data .= " $2";
    }
}
add_line($data) if defined $data;
print PIPE "\n";

close(SOURCE)
    or die("$prog: close < $source: $!\n");
close(PIPE)
    or die($! ? "$prog: close | $CDB $target: $!\n"
	   : "$prog: close | $CDB $target: exited $?\n");

exit 0;
