#!/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:Dc:Cf: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 $addonlyrequesteddrivers = $opt_D;
my $comment = $opt_c;
my $nodefaultcomment = $opt_C;
my $destdir = $opt_f;
my $nomod = $opt_n;
my $nocheck = $opt_x;

my @ppddrivers = ();
my @driverslist = ($drivers ? split(',', $drivers) : ());
my $commentadded = 0;

foreach $ppdfile (@ARGV) {

    my $ppddriver;
    if ($ppdfile =~ /^([^\:]+)\:(.*)$/) {
	$ppddriver = $1;
	$ppdfile = $2;
    } else {
	$ppddriver = $driverslist[0];
    }
    $ppdlink = $opt_l;
    if ($ppdlink && !$ppddriver) {
	$ppdlink = 0;
	warn("WARNING: \"-l\" set without assigning a driver to the PPD file \"$ppdfile\" and without supplying a driver via \"-d\". No links for this PPD file will get created!\n");
    } elsif ($ppdlink && Foomatic::DB::member($ppddriver, @ppddrivers)) {
	$ppdlink = 0;
	warn("WARNING: \"-l\" set and PPD file \"$ppdfile\" assigned to the driver \"$ppddriver\", to which another PPD file was assigned already. No links for this PPD file will get created!\n");
    }
    if ($ppddriver && !Foomatic::DB::member($ppddriver, @ppddrivers)) {
	push(@ppddrivers, $ppddriver);
    }
    if ($ppddriver && !Foomatic::DB::member($ppddriver, @driverslist)) {
	push(@driverslist, $ppddriver);
    }
    print "PPD file $ppdfile";
    if ($ppddriver) {
	print " for driver $ppddriver\n";
    } else {
	print "\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 = {
	(@driverslist ? ('drivers' => [@driverslist]) : ()),
	($rdriver ? ('recommendeddriver' => $rdriver) : ()),
	($pdls ? ('pdls' => [split(',', $pdls)]) : ()),
	($ppddriver ? ('ppddriver' => $ppddriver) : ()),
	($ppdlink ? ('ppdlink' => 1) : ()),
	($basedir ? ('basedir' => $basedir) : ()),
	($comment && !$commentadded ? ('comment' => $comment) : ()),
	($nodefaultcomment && !$commentadded ? ('nodefaultcomment' => 1) : ()),
	($addonlyrequesteddrivers ? ('addonlyrequesteddrivers' => 1) : ()),
    };
    $commentadded = 1;

    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;

    $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'} if !$parameters->{'nodefaultcomment'};
    $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";
	my $f = ($destdir ? $destdir . "/" : ()) . 
	    $db->{'dat'}{'id'} . ".xml";
	open FILE, "> " . $f or
	    die "Cannot write file $db->{'dat'}{'id'}.xml!\n";
	print FILE $result;
	close FILE;
    }
    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`));
    }
    delete($db->{'dat'});
    foreach my $entry (@existing) {
	my $d = $db->get_printer($entry);
	my $result;
	next if $d->{'noxmlentry'};
	next if $nomod and $entry !~ /\.xml$/i;
	$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;
	if ($entry !~ /\.xml$/i) {
	    print "  Modifying printer entry $db->{'dat'}{'id'}.xml ...\n";
	} else {
	    print "  Adding PPD file to $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'});
    }
    undef($db);
    undef($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> [<driver>:]<ppdfile> [[<driver>:]<ppdfile> ...]
$progname -h

 <driver>       : Driver to which the PPD file should get assigned. This
                  is optional. If not supplied, the first driver in the driver 
		  list supplied via the "-d" option is used. Note that the PPD
		  files have to be supplied to different drivers, so with more
                  than one PPD drivers need to be specified here.
 <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. Drivers already specified together
		  with PPDs do no need to get specified with this option again.
		  The option is not needed if for all PPDs drivers are
		  specified.
 -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 (or the one to which
		  the first PPD file is assigned if the "-d" option is not
		  used)..
 -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.
 -D             : Add only the driver requested by "-d" and "-r" to the
                  driver list in the XML file, do not add drivers based on
                  PDLs, not even on the PDLs supplied by the "-p" option.
 -c <comment>   : Add the given comment to the comment field of the XML
                  file. Comments are in HTML.
 -C             : Suppress any default comments (max paper width, pages
                  per minute, automatically generated XML, ...). Does not
                  suppress the comment supplied with "-c".
 -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. This
		  makes the process vastly faster if you have PPD files for
		  printers which are for sure not yet in the Foomatic
		  database.
 -h             : show help information


HELP
    exit 1;

}
