#!/usr/bin/perl
# -*- perl -*-

# Foomatic printer XML file generator to get XML files corresponding
# to manufacturer-supplied PostScript PPDs (or also PPDs from driver
# packages).

use Foomatic::Defaults;
use Foomatic::DB;
use Getopt::Std;
use Data::Dumper;
#use strict;

my $debug = 0;

# Program name
$0 =~ m!/([^/]+)\s*$!;
my $progname = ($1 || $0);

help() if !@ARGV;
#my ($opt_h, $opt_d, $opt_p, $opt_A, $opt_P, $opt_w);
getopts("d:r:p:lb:f:nxh");
help() if $opt_h;
my $drivers = $opt_d;
my $rdriver = $opt_r;
my $pdls = $opt_p;
my $ppdlink = $opt_l;
my $basedir = $opt_b;
my $destdir = $opt_f;
my $nomod = $opt_n;
my $nocheck = $opt_x;

$ppdfile = $ARGV[0];

if ($ppdlink && !$drivers) {
    $ppdlink = 0;
    warn("WARNING: \"-l\" set without supplying a driver via \"-d\". No PPD file links will get created!\n");
}

my $ppddlpath;
if ($basedir) {
    $basedir =~ s:/+$::;
    if (! -d $basedir) {
	die ("PPD base directory $basedir does not exist!\n");
    }
    if (! -r $ppdfile) {
	$ppddlpath = $ppdfile;
	$ppdfile = $basedir . "/" . $ppdfile;
	if (! -r $ppdfile) {
	    die ("Given PPD file not found, neither as $ppddlpath nor as $ppdfile!\n");
	}
    } else {
	$ppddlpath = $1 if $ppdfile =~ m:$basedir/(.*)$:;
    }
} else {
    if (! -r $ppdfile) {
	die ("Given PPD file $ppdfile not found!\n");
    }
    $ppddlpath = $ppdfile;
}

my $parameters = {
    ($drivers ? ('drivers' => [split(',', $drivers)]) : ()),
    ($rdriver ? ('recommendeddriver' => $rdriver) : ()),
    ($pdls ? ('pdls' => [split(',', $pdls)]) : ()),
    ($ppdlink ? ('ppdlink' => 1) : ()),
    ($basedir ? ('basedir' => $basedir) : ()),
};

my $db = Foomatic::DB->new();
my $dat = ppdtoperl($ppdfile, $parameters);
if ($ppddlpath eq "") {
    $mk = $dat->{'id'};
    $mk =~ s/^([^\-]+)\-.*$/$1/;
    $ppd = $ppdfile;
    $ppd =~ s:^.*/([^/]+):$1:;
    $pdddlpath = "PPD/$mk/$ppd";    
}
$ppddlpath =~ s/\.gz$//i;
my @existing = ();
if (!$nocheck) {
    @existing = $db->find_printer("$dat->{'make'}|$dat->{'model'}", 4, 1);
    foreach my $product (@{$dat->{ppdproduct}}) {
	my @pids = $db->find_printer("$dat->{'make'}|$product", 4, 1);
	push(@existing,
	     grep {
		 !Foomatic::DB::member($_, @existing);
	     } @pids);
    }
    push(@existing,
	 grep {
	     !Foomatic::DB::member($_, @existing);
	 } map {
	   m:^(.*)\.xml$:; $1;
	 } map {
	   m:([^/]+)$:; $1;
	 } split(/\n/s, 
		 `find $libdir/db/source/printer -name "*.xml" -print0 | xargs -0 grep -l $ppddlpath`));
}
my $entryfound = 0;
foreach my $entry (@existing) {
    my $d = $db->get_printer($entry);
    my $result;
    next if $d->{'noxmlentry'};
    $entryfound = 1;
    last if $nomod;
    $db->{'dat'} = $d;
    if (!defined($parameters->{'drivers'})) {
	$parameters->{'drivers'} = [$dat->{'driver'}];
    }
    if (!defined($parameters->{'pdls'})) {
	$parameters->{'pdls'} = [split(',', $dat->{'general_cmd'})];
    } else {
	push(@{$parameters->{'pdls'}}, split(',', $dat->{'general_cmd'}));
    }
    Foomatic::DB::apply_driver_and_pdl_info($db->{'dat'}, $parameters);
    $db->{'dat'}{'general_ieee'} = $dat->{'general_ieee'} if
	defined($dat->{'general_ieee'}) && 
	!defined($db->{'dat'}{'general_ieee'});
    $db->{'dat'}{'general_mfg'} = $dat->{'general_mfg'} if
	defined($dat->{'general_mfg'}) &&
	!defined($db->{'dat'}{'general_mfg'});
    $db->{'dat'}{'general_mdl'} = $dat->{'general_mdl'} if
	defined($dat->{'general_mdl'}) &&
	!defined($db->{'dat'}{'general_mdl'});
    $db->{'dat'}{'general_des'} = $dat->{'general_des'} if
	defined($dat->{'general_des'}) &&
	!defined($db->{'dat'}{'general_des'});
    $db->{'dat'}{'general_cmd'} = $dat->{'general_cmd'} if
	defined($dat->{'general_cmd'}) &&
	!defined($db->{'dat'}{'general_cmd'});
    $db->{'dat'}{'comment'} .= "\n      <p>\n\n" . $dat->{'comment'};
    my $xml1 = $db->perltoxml('p');
    my $xml2 = $db->get_printer_xml($entry);
    $xml2 =~ s/(<\/functionality>)/$1\n  <driver><\/driver>/s if 
	$xml2 !~ /<driver>/;
    $xml2 = transferregexp($xml1, $xml2,
	'<driver>\S*<\/driver>');
    $xml2 =~ s/(<\/driver>)/$1\n  <drivers>\n  <\/drivers>/s if 
	$xml2 !~ /<drivers>/;
    $xml2 = transferregexp($xml1, $xml2,
	'<drivers>.*<\/drivers>');
    $xml2 =~ s/(<\/(mechanism|url)>)/$1\n  <lang>\n  <\/lang>/s if 
	$xml2 !~ /<lang>/;
    $xml2 = transferregexp($xml1, $xml2,
	'<lang>.*<\/lang>');
    $xml2 =~ s/(<\/lang>)/$1\n  <autodetect>\n  <\/autodetect>/s if 
	$xml2 !~ /<autodetect>/;
    $xml2 =~ s/(<autodetect>)/$1\n    <general>\n    <\/general>/s if 
	$xml2 !~ /<autodetect>[\s\n\r]*<general>/s;
    $xml2 = transferregexp($xml1, $xml2,
	'<autodetect>[\s\n\r]*<general>.*<\/general>');
    $xml2 = transferregexp($xml1, $xml2,
	'<\/drivers>[\s\n\r]*<comments>.*<\/comments>[\s\n\r]*<\/printer>');
    $result = $xml2;
    print "Modifying printer entry $db->{'dat'}{'id'}.xml ...\n";
    open FILE, "> " . ($destdir ? $destdir . "/" : ()) . 
	$db->{'dat'}{'id'} . ".xml" or
	die "Cannot write file $db->{'dat'}{'id'}.xml!\n";
    print FILE $result;
    close FILE;
    delete($db->{'dat'});
}
if (!$entryfound) {
    $db->{'dat'} = $dat;
    $db->{'dat'}{'comment'} =
	"      This database entry was automatically generated\n" .
	"      from the PPD file for this printer.<p>\n\n" .
	$db->{'dat'}{'comment'};
    $db->{'dat'}{'functionality'} = "A";
    foreach my $product (@{$db->{'dat'}{ppdproduct}}) {
	$db->{'dat'}{'model'} =
	    Foomatic::DB::clean_manufacturer_name(Foomatic::DB::clean_model_name($product))
	    if scalar(@{$db->{'dat'}{ppdproduct}}) > 1;
	$db->{'dat'}{'model'} =~ s/^$db->{'dat'}{'make'}\s*//i;
	$db->{'dat'}{'id'} =
	    Foomatic::DB::generatepid($db->{'dat'}{'make'},
				      $db->{'dat'}{'model'});
	if (scalar(@{$db->{'dat'}{ppdproduct}}) > 1) {
	    $db->{'dat'}{'general_mfg'} = $db->{'dat'}{'ppdmanufacturer'} if
		$db->{'dat'}{'ppdmanufacturer'} &&
		!$db->{'dat'}{'general_mfg'};
	    $db->{'dat'}{'general_mdl'} = $product;
	    $db->{'dat'}{'general_ieee'} = "MFG:" .
		$db->{'dat'}{'general_mfg'} .
		";MDL:" . $db->{'dat'}{'general_mdl'} . ";" .
		($db->{'dat'}{'general_cmd'} ?
		 "CMD:" . $db->{'dat'}{'general_cmd'} . ";" : "");
	}
	$result = $db->perltoxml('p');
	print "Creating new printer entry $db->{'dat'}{'id'}.xml ...\n";
	open FILE, "> " . ($destdir ? $destdir . "/" : ()) . 
	    $db->{'dat'}{'id'} . ".xml" or
	    die "Cannot write file $db->{'dat'}{'id'}.xml!\n";
	print FILE $result;
	close FILE;
    }
    delete($db->{'dat'});
}

exit 0;

sub transferregexp {

    my ($src, $dest, $regexp) = @_;

    # This function copies the text fraction matching $regexp out of
    # the first string, cuts the piece of the second matching $regexp
    # out of the second string and replaces it by the piece copied
    # from the first string. This is mainly for transfering XML
    # sections from one XML file to another (strings can be
    # multi-line) without needing to rewrite the unaffected parts of
    # the XML file.

    $src =~ m/($regexp)/s;
    my $totransfer = $1;
    $dest =~ s/$regexp/$totransfer/s if $totransfer;
    return $dest;
}

sub help {
    print <<HELP;

$progname <options> <ppdfile>
$progname -h

 <ppdfile>      : PPD file for which a printer XML file should be created
 -d <drivers>   : Comma-separated list of drivers with which the printer
                  works. First driver is the one for which the PPD file is.
                  If not otherwise stated by the "-r" option, this is also
                  the recommended driver.
 -r <driver>    : Recommended driver. Supply this option to specify another
                  driver than the driver from the PPD/the first one from the
                  "-d" argument as the recommended driver.
 -p <pdls>      : Comma-separated list of known Page Description Languages
                  (PDLs) which the printer supports. This will add all 
                  suitable drivers to the XML entry. Currently supported are:
                  Postscript, PCLXL, PCL6, PCL5e, PCL5c, PCL5, and PCL4.
 -l             : Add a link to the PPD file to the driver entry in the
                  XML file.
 -b <directory> : Base directory for a relative link to the PPD. If the
                  base directory is given, the link set via the -l option 
                  is relative to this directory (and not relative to the
                  current directory). With a base directory given the 
                  <ppdfile> can also be given relative to this directory.
 -f <directory> : Directory where the resulting XML file to write to. The
                  name of the file will be the printer ID with the ".xml"
                  extension.
 -n             : Do not write modified versions of existing XML files,
                  only create XML files for printers for which there is no
                  XML file yet.
 -x             : Create XML files independent of whether there are already
                  XML files for the printers covered by the PPD or not.
 -h             : show help information


HELP
    exit 1;

}
