#!/usr/bin/perl -w
use strict; # -*- perl -*-

# This is foomatic-configure, a program to establish and configure
# print queues, drivers, spoolers, etc using the foomatic database and
# companion filters.

# It also comprises half of a programatic API for user tools: you can
# learn and control everything about the static properties of print
# queues here. With the sister program foomatic-printjob, you can do
# everything related to print queue dynamic state: submit jobs, and
# query, cancel, reorder, and redirect them.

use Foomatic::Defaults;
use Foomatic::DB;
use Data::Dumper;

# Connect syntax:
#
# This differs a tad from CUPS's, partly because everything is
# supposed to be a file, and CUPS doesn't entirely reflect that.
# But I'm not really very particular...
#
# If a certain URI is not supported by all the spoolers, the spoolers
# which support it are listed in parantheses, "direct" means direct, 
# spooler-less printing.
#
# usb:/path/device                # Local USB printer
# usb://make/model?serial=xxx     # Printer-bound USB connection (CUPS)
# parallel:/path/device           # Local parallel printer
# serial:/path/device             # Local serial printer
# file:/path/file                 # includes usb, lp, named pipes, other
# hp:/bus/model?serial=xxx        # HPLIP print queue (hpinkjet.sf.net)
# hpfax:/bus/model?serial=xxx     # HPLIP fax queue (hpinkjet.sf.net)
# ptal:/provider:bus:name         # HPOJ MLC protocol (hpoj.sf.net,obsolete)
# mtink:/path/device              # Epson inkjet through mtink daemon
#                                 # (for ink level monitoring when printing,
#                                 #  http://xwtools.automatix.de/)
# lpd://host/queue                # LPD protocol
# lpd://host                      # LPD protocol (default queue, CUPS only)
# socket://host:port              # TCP aka appsocket
# socket://host                   # TCP aka appsocket (port 9100)
# ncp://user:pass@host/queue      # Netware (LPD, LPRng, direct)
# smb://user:pass@wgrp/host/queue # Windows (CUPS, PPR, LPD, LPRng, direct)
# stdout                          # Standard output (direct)
# postpipe:"<command line>"       # Free-formed backend command line
#                                 # (LPD, LPRng, direct)
#

# Read out the program name with which we were called, but discard the path

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

# We use the library Getopt::Long here, so that we can have more than
# one "-o" option on one command line.

my($opt_q, $opt_f, $opt_w, $opt_n, $opt_N, $opt_L, $opt_ppd,
   $opt_d, $opt_p, $opt_s, $opt_C, $opt_R, $opt_D, $opt_Q, $opt_P,
   $opt_O, $opt_X, $opt_c, @opt_o, $opt_r, $opt_dd, $opt_nodd, 
   $opt_att, $opt_delay, $opt_h);
use Getopt::Long;
Getopt::Long::Configure("no_ignore_case");
GetOptions("q"   => \$opt_q,         # Quiet, non-interactive operation
	   "f"   => \$opt_f,         # Force rebuild of PPD from database
	   "w"   => \$opt_w,         # Cut GUI strings in the PPD to 39
				     # characters (for CUPS Windows driver)
	   "n=s" => \$opt_n,         # queue Name
	   "N=s" => \$opt_N,         # human-readable Name (Model, 
	                             # Description)
	   "L=s" => \$opt_L,         # Location
	   "ppd=s" => \$opt_ppd,     # PPD file
	   "d=s" => \$opt_d,         # Driver
	   "p=s" => \$opt_p,         # Printer
	   "s=s" => \$opt_s,         # Spooler
	   "C"   => \$opt_C,         # Copy queue
           "R"   => \$opt_R,         # Remove queue
           "D"   => \$opt_D,         # set Default queue
	   "Q"   => \$opt_Q,         # Query queue info
	   "P"   => \$opt_P,         # Perl queue/printer/driver info output
	   "O"   => \$opt_O,         # get printer support Overview
	   "X"   => \$opt_X,         # query XML printer/driver/combo info
	   "c=s" => \$opt_c,         # printer Connection type
	   "o=s" => \@opt_o,         # default printing Options
	   "r"   => \$opt_r,         # list Remote queues
	   "backend-dont-disable=s" => \$opt_dd, # Do not disable CUPS 
	                             # backends
	   "backend-attempts=s" => \$opt_att, # Try that often when backend
	                             # fails
	   "backend-delay=s" => \$opt_delay, # Delay in seconds between
	                             # retries of failed backend
	   "h"   => \$opt_h,         # Help!
	   "help"=> \$opt_h) || help();

help() if $opt_h;

my $db = new Foomatic::DB;

overview() if $opt_O;

get_xml() if $opt_X;

my $force = ($opt_f ? 1 : 0); 

my $shortgui = ($opt_w ? 1 : 0); 

my $in_config = {'queue'    => $opt_n,
		 'desc'     => $opt_N,
		 'loc'      => $opt_L,
		 'ppdfile'  => $opt_ppd,
		 'driver'   => $opt_d,
		 'printer'  => $opt_p,
		 'spooler'  => $opt_s,
		 'connect'  => $opt_c,
		 'options'  => \@opt_o,
		 'force'    => $force,
		 'shortgui' => $shortgui,
		 'dd'       => $opt_dd,
		 'att'      => $opt_att,
		 'delay'    => $opt_delay,
	         'foomatic' => 1};

# If description and location contain only whitespace, use an empty string
# instead

if ((defined($in_config->{'desc'})) && ($in_config->{'desc'} =~ m!^\s*$!)) {
    $in_config->{'desc'} = "";
}
if ((defined($in_config->{'loc'})) && ($in_config->{'loc'} =~ m!^\s*$!)) {
    $in_config->{'loc'} = "";
}

my $action = ($opt_R ? 'delete' : 'configure');
$action = ($opt_D ? 'default' : $action);
$action = ($opt_Q ? 'query' : $action);
$action = ($opt_P ? 'query' : $action);

my $procs = { 'lpd' => { 'delete'    => \&delete_lpd,
                         'configure' => \&setup_lpd,
                         'default'   => \&default_lpd,
                         'query'     => \&query_lpd },
              'lprng'=>{ 'delete'    => \&delete_lpd,
                         'query'     => \&query_lpd,
                         'default'   => \&default_lprng,
                         'configure' => \&setup_lpd },
              'cups' =>{ 'delete'    => \&delete_cups,
                         'query'     => \&query_cups,
                         'default'   => \&default_cups,
                         'configure' => \&setup_cups },
              'pdq'  =>{ 'delete'    => \&delete_pdq,
                         'query'     => \&query_pdq,
                         'default'   => \&default_pdq,
                         'configure' => \&setup_pdq },
              'ppr'  =>{ 'delete'    => \&delete_ppr,
                         'query'     => \&query_ppr,
                         'default'   => \&default_ppr,
                         'configure' => \&setup_ppr },
              'direct'=>{'delete'    => \&delete_direct,
                         'query'     => \&query_direct,
                         'default'   => \&default_direct,
                         'configure' => \&setup_direct } };

if (!($opt_Q or $opt_P or defined($in_config->{'queue'}))) {
    # No queue manipulation without knowing the name of the queue
    print STDERR "You must specify a queue name with -n!\n";
	help();
	exit 1;
}

if (!defined($in_config->{'spooler'})) {

    my $takenfromconfigfile = 0;

    # Personal default spooler
    my $s;
    if (($> != 0) && (-f "$ENV{'HOME'}/.defaultspooler")) {
        $s = `cat $ENV{'HOME'}/.defaultspooler`;
        chomp $s;
	$takenfromconfigfile = 1;
    }
 
    # System default spooler
    if ((!defined($s)) && (-f "$sysdeps->{'foo-etc'}/defaultspooler")) {
        $s = `cat $sysdeps->{'foo-etc'}/defaultspooler`;
        chomp $s;
	$takenfromconfigfile = 1;
    }
 
    if (!defined($s)) {
	$s = detect_spooler();
    }

    die "Unable to identify spooler, please specify with -s\n"
	unless $s;

    if ((!$opt_q) && (!$takenfromconfigfile)) {
	print STDERR "You appear to be using $s.  Correct? ";
	my $yn = <STDIN>;
	die "\n" if ($yn !~ m!^y!i);
    }

    $in_config->{'spooler'} = $s;
}

if ($in_config->{'printer'}) {
    # If the user supplies an old numerical printer ID, translate it to
    # a new clear-text ID
    $in_config->{'printer'} =
	Foomatic::DB::translate_printer_id($in_config->{'printer'});
}

# Call proper proc
&{$procs->{$in_config->{'spooler'}}{$action}}($in_config);
exit(0);

# Common parts for queue creation/modification functions

sub getoldqueuedata {

    my ($config, $reconf) = @_;
    my ($sourcespooler, $sourcequeue, $olddatablob, $beh);

    # Copy a queue
    if ($opt_C) {
	if ($#ARGV == 0) {  # 1 argument -> queue from same spooler
	    $sourcespooler = $config->{'spooler'};
	    $sourcequeue = $ARGV[0];
	} elsif ($#ARGV == 1) {  # 2 arguments -> queue from given spooler
	    $sourcespooler = $ARGV[0];
	    $sourcequeue = $ARGV[1];
	} else {
	    die "Unsufficient options to copy a queue, " .
		"try \"$progname -h\"!\n";
	}
	# Read data from source queue
	if (!($olddatablob = load_datablob($sourcespooler, $sourcequeue))) {
	    # It is not possible to copy the given source queue
	    die "The source queue $sourcequeue does not exist " .
		"or is corrupted!\n";
	}
	# PPD file of the source queue, if it exists, and if the user
	# does not insist on using another PPD file, we must copy it
	my $sourceppd = $olddatablob->{'ppdfile'};
	if ((-r $sourceppd) && (!$config->{'ppdfile'})) {
	    $config->{'ppdfile'} = $sourceppd;
	}
	# Stuff data into the $config structure, all items must be defined,
	# so that an old queue gets overwritten
	if ($olddatablob->{'queuedata'}) {
 	    my $i;
	    for $i (('desc', 'loc', 'printer', 'driver', 'connect',
		     'ppdfile', 'dd', 'att', 'delay')) {
		if (!defined($config->{$i})) {
		    if ($olddatablob->{'queuedata'}{$i}){
			$config->{$i} = $olddatablob->{'queuedata'}{$i};
		    } elsif ($i eq 'dd') {
			$config->{$i} = 0;
		    } elsif ($i eq 'att') {
			$config->{$i} = 1;
		    } elsif ($i eq 'delay') {
			$config->{$i} = 30;
		    } else {
			$config->{$i} = "";
		    }
		}
	    }
	    # Check consistency of the printer/driver settings
	    if ((($config->{'driver'} eq "") || 
		 ($config->{'driver'} eq "raw") || # No new driver, printer,
		 ($config->{'printer'} eq "")) &&  # PPD file
		($config->{'ppdfile'} eq "") &&
		((!defined($olddatablob->{'args'})) || # No existing options
		 ($#{$olddatablob->{'args'}} < 0))) {  # -> source queue raw
		$config->{'driver'} = "raw";
		$config->{'printer'} = undef;
	    }
	    # We do not need the queue data block any more
	    delete($olddatablob->{'queuedata'});
	} else {
	    # No Foomatic/PPD data
	    $olddatablob = undef;
	}
    } else {
	# Load the datablob of the former configuration
	if ($reconf) {
	    if ($olddatablob = load_datablob($config->{'spooler'}, 
					     $config->{'queue'})) {
		# If the user has supplied only a printer or only a driver
		# fill in the second of the two fields in $config
		if ((!$config->{'ppdfile'}) &&
		    ($olddatablob->{'queuedata'}{'foomatic'})) {
		    if ((!$config->{'driver'}) && ($config->{'printer'})) {
			$config->{'driver'} = $olddatablob->{'driver'};
		    }
		    if ((!$config->{'printer'}) && ($config->{'driver'})) {
			$config->{'printer'} = $olddatablob->{'id'};
		    }
		}
		# Extract URI and backend error handling data
		if ($config->{'spooler'} eq "cups") {
		    $beh->{'uri'} = $olddatablob->{'queuedata'}{'connect'};
		    $beh->{'dd'} = $olddatablob->{'queuedata'}{'dd'};
		    $beh->{'att'} = $olddatablob->{'queuedata'}{'att'};
		    $beh->{'delay'} = $olddatablob->{'queuedata'}{'delay'};
		}
		# We do not need the queue data block here
		delete($olddatablob->{'queuedata'});
	    } else {
		$olddatablob = undef;
	    }
	}
    }
    
    # If the user does not supply info about his printer and/or driver
    # and the queue did not exist before we assume that he wants to set up a
    # raw queue. To make a raw queue out of a formerly filtered one, one
    # has to use the driver name "raw".
    $config->{'driver'} = "" if not defined $config->{'driver'};
    $config->{'printer'} = "" if not defined $config->{'printer'};
    $config->{'ppdfile'} = "" if not defined $config->{'ppdfile'};
    my $nodriver = (((!$config->{'driver'}) && (!$config->{'printer'}) &&
		     (!$config->{'ppdfile'})) ||
		    ($config->{'driver'} eq "raw"));

    # Set to 1 when we retrieve a data set from the Foomatic database
    my $newfoomaticdata = 0;
    if ($nodriver) {
	if ($olddatablob) {
	    if ($config->{'driver'} ne "raw") {
		# We couldn't determine a certain driver, probably we had a
		# native PostScript PPD file
		$db->{'dat'} = $olddatablob;
	    } else {
		# For a raw queue overtake at least the $postpipe
		if (defined($olddatablob->{'postpipe'})) {
		    $db->{'dat'}{'postpipe'} = $olddatablob->{'postpipe'};
		}
	    }
	}
    } elsif ($config->{'ppdfile'}) {
	if (! -r $config->{'ppdfile'}) {
	    die "The PPD file \'$config->{'ppdfile'}\' does not exist or is " .
		"readable.\n";
	}
	# Load the data from the PPD file
	$db->getdatfromppd($config->{'ppdfile'});
	# Overtake the former default settings
	if ($olddatablob) {overtake_defaults($olddatablob)};
	# Overtake the former $postpipe
	if (defined($olddatablob->{'postpipe'})) {
	    $db->{'dat'}{'postpipe'} = $olddatablob->{'postpipe'};
	}
    } else {
	if (($olddatablob) &&
	    ($olddatablob->{'driver'} eq $config->{'driver'}) &&
	    ($olddatablob->{'id'} eq $config->{'printer'}) &&
	    (!$config->{'force'})) {
	    # Overtake data from the former configuration
	    $db->{'dat'} = $olddatablob;
	} else {
	    # Retrieve data from the Foomatic database
	    if (!$config->{'driver'}) {
		die "You also need to specify a driver with \"-d\"!\n";
	    }
	    if (!$config->{'printer'}) {
		die "You also need to specify a printer with \"-p\"!\n";
	    }
	    # The printer is supported by the chosen driver? If yes, load
	    # its data
	    my $possible = $db->getdat($config->{'driver'}, 
				       $config->{'printer'});
	    die "That printer and driver combination is not possible.\n"
		if (!$possible);
	    die "There is neither a custom PPD file nor the driver database entry contains sufficient data to build a PPD file.\n"
		if (!$db->{'dat'}{'cmd'}) && (!$db->{'dat'}{'ppdfile'});
	    $newfoomaticdata = 1;
	    # Overtake the former default settings
	    if ($olddatablob) {overtake_defaults($olddatablob)};
	    # Overtake the former $postpipe
	    if (defined($olddatablob->{'postpipe'})) {
		$db->{'dat'}{'postpipe'} = $olddatablob->{'postpipe'};
	    }
	}
    }

    # When we have no arguments in the current configuration, we must have 
    # a raw queue
    my $rawqueue = ((!defined($db->{'dat'}{'args'})) ||
		     ($#{$db->{'dat'}{'args'}} < 0));

    # Set the default printing options supplied on the command line
    if (!$rawqueue) {
	set_default_options($config, $db->{'dat'});
    }

    # Printer model name (for comment field of the queue configuration)
    my ($make, $model, $makemodel);
    if (defined($db->{'dat'})) {
	$make = $db->{'dat'}{'make'};
	$model = $db->{'dat'}{'model'};
	$makemodel = $db->{'dat'}{'makemodel'};
	if (($make) && ($model)) { 
	    $makemodel = "$make $model";
	}
    }
 
    return ($rawqueue, $newfoomaticdata, $makemodel,
	    ($config->{'spooler'} eq "cups" ? $beh : ()));
}

#fix to work on Ubuntu, where cupd runs not as root, but as cupsys user
#like system ("chown cupsys $ppdfile"), but
#changeowner function changes owner only if user exists on system
sub changeowner {

    my ($username, $file) = @_;

    my ($uid,$gid) = (-1, -1);
    my $l;
    $l = getpwnam($username); $uid = $l if defined($l);
    $l = getgrnam($username); $gid = $l if defined($l);
    chown $uid, $gid, $file;

}

sub writeppdfile {

    my ($config, $ppdfile, $rawqueue, $newfoomaticdata) = @_;

    # Save old $ppdfile, if any
    system("cp -f \'$ppdfile\' \'$ppdfile.old\'") 
	if (-f $ppdfile);
    if ($rawqueue) {
	# Raw queue with $postpipe, use a "PPD" only containing the
	# $postpipe (LPRng, LPD, and no spooler only)
	if (((defined $db->{'dat'}{'postpipe'} && $db->{'dat'}{'postpipe'} ne "") &&
	     (($config->{'spooler'} eq 'lprng') ||
	      ($config->{'spooler'} eq 'lpd'))) ||
	    ($config->{'spooler'} eq 'direct')) {
	    open PPDFILE, "> $ppdfile" or die "Cannot write \'$ppdfile\'!\n";
	    print PPDFILE "*PPD-Adobe: \"4.3\"\n*%\n";
	    print PPDFILE "*% This is a raw (driverless/unfiltered) " .
		"queue, this PPD file only carries\n" .
		"*% the postpipe.\n*%\n";
	    close PPDFILE;
	    $db->ppdsetdefaults($ppdfile);
	    chmod 0644, $ppdfile;
            #fix to work on Ubuntu, where cupd runs not as root, but as cupsys user
            #system ("chown cupsys $ppdfile");
	    #changeowner function changes owner only if user exists on system
	    changeowner("cupsys", $ppdfile);
	} else {
	    if (-f $ppdfile) {
		unlink "$ppdfile" or die "Cannot delete \'$ppdfile\'!\n";
	    }
	}
    } else {
	if ($config->{'ppdfile'}) {
	    # Copy in the PPD file specified on the command line
	    if ($config->{'ppdfile'} !~ /\.gz$/i) {
		# Uncompressed PPD file
		system("cp -f \'$config->{'ppdfile'}\' \'$ppdfile\'") and
		    die "Cannot copy \'$config->{'ppdfile'}\' to \'$ppdfile\'!\n";
	    } else {
		# Compressed PPD file
		system("$sysdeps->{'gzip'} -dc " .
		       "\'$config->{'ppdfile'}\' > " .
		       "\'$ppdfile\'") and
		    die "Cannot copy \'$config->{'ppdfile'}\' to \'$ppdfile\'!\n";
	    }
	    # Set default option settings and $postpipe
	    $db->ppdsetdefaults($ppdfile);
	} elsif ($newfoomaticdata) {
	    # Generate the PPD file from the Foomatic database
	    open PPDFILE, "> $ppdfile" or die "Cannot write \'$ppdfile\'!\n";
	    print PPDFILE $db->getppd($config->{'shortgui'});
	    close PPDFILE;
	} else {
	    # Keep the previous PPD file, only set the options and the
	    # $postpipe
	    $db->ppdsetdefaults($ppdfile);
	}
	# Correct the permissions of the PPD file
	chmod 0644, $ppdfile;
	#fix to work on Ubuntu, where cupd runs not as root, but as cupsys user
	#system ("chown cupsys $ppdfile");
	#changeowner function changes owner only if user exists on system
	changeowner("cupsys", $ppdfile);
    }
}


### Queue manipulation functions for both LPD and LPRng

sub setup_lpd {
    my ($config) = $_[0];

    # Read the previous /etc/printcap
    my $pcap = load_lpd_printcap();

    my ($ppdfile, $entry, $reconf, $p);
    for $p (@{$pcap}) {
	if ($p->{'names'}[0] eq $config->{'queue'}) {
	    $entry = $p;
	    $reconf = 1;
	    print "Reconfigure of ", Dumper($p) if $debug;
	    last;
	}
    }

    # PPD file name
    $ppdfile = sprintf('%s/lpd/%s.ppd',
			      $sysdeps->{'foo-etc'},
			      $config->{'queue'}) if !$ppdfile;

    # Get the data from the former queue if we reconfigure or copy a queue
    # do also some checking of the user-supplied parameters
    my ($rawqueue, $newfoomaticdata, $makemodel) =
	getoldqueuedata($config, $reconf);

    # Set the printer queue name line in /etc/printcap
    if (!$reconf) {
	if (!$rawqueue) {
	    $entry->{'names'}[0] = $config->{'queue'}; 
	    $entry->{'names'}[1] = $config->{'desc'};
	    $entry->{'names'}[2] = "$makemodel";
	    $entry->{'names'}[3] = $config->{'loc'};
	} else {
	    $entry->{'names'}[0] = $config->{'queue'}; 
	    $entry->{'names'}[1] = $config->{'desc'};
	    $entry->{'names'}[2] = "Raw queue";
	    $entry->{'names'}[3] = $config->{'loc'};
	}
    } else {
	if (!$rawqueue) {
	    $entry->{'names'}[2] = "$makemodel";
	} else {
	    if (($entry->{'names'}[2] eq "Raw queue") ||
		($config->{'driver'} eq "raw")) {
		$rawqueue = 1;
		$entry->{'names'}[2] = "Raw queue";
	    }
	}
	if (defined($config->{'desc'})) {
	    $entry->{'names'}[1] = $config->{'desc'};
	}
	if (defined($config->{'loc'})) {
	    $entry->{'names'}[3] = $config->{'loc'};
	}
    }

    # These lines are always in /etc/printcap
    $entry->{'str'}{'sd'} = sprintf('%s/%s',
				    $sysdeps->{'lpd-dir'},
				    $config->{'queue'});
    $entry->{'str'}{'lf'} = $sysdeps->{'lpd-log'};
    $entry->{'num'}{'mx'} = '0';
    $entry->{'bool'}{'sh'} = 1;

    # Lines depending on the printer/spooler
    if (!$rawqueue) {
	if ($config->{'spooler'} eq "lpd") {
	    $entry->{'str'}{'ppdfile'} = $ppdfile; # For the GPR printing GUI
	    delete $entry->{'str'}{'ppd'};
	    $entry->{'str'}{'if'} = $sysdeps->{'foomatic-rip'};
	    $entry->{'str'}{'af'} = $ppdfile;
	    delete $entry->{'bool'}{'force_localhost'};
	    delete $entry->{'str'}{'filter_options'};
	} elsif ($config->{'spooler'} eq "lprng") {
	    $entry->{'str'}{'ppd'} = $ppdfile; # for LPRng PPD support
	    $entry->{'str'}{'if'} = $sysdeps->{'foomatic-rip'};
	    $entry->{'bool'}{'force_localhost'} = 1;
	    delete $entry->{'str'}{'ppdfile'};
	    delete $entry->{'str'}{'af'};
	    delete $entry->{'str'}{'filter_options'};
	} else {
	    die "The spooler $config->{'spooler'} is not supported " .
		"by this function!\n";
	}
    } else {
	delete $entry->{'str'}{'if'};
	delete $entry->{'str'}{'af'};
	delete $entry->{'str'}{'filter_options'};
	delete $entry->{'str'}{'ppd'};
	if ($config->{'spooler'} eq "lpd") {
	    delete $entry->{'bool'}{'force_localhost'};
	} elsif ($config->{'spooler'} eq "lprng") {
	    $entry->{'bool'}{'force_localhost'} = 1;
	} else {
	    die "The spooler $config->{'spooler'} is not supported " .
		"by this function!\n";
	}
    }

    # If printing job has to be passed through a special program, put the
    # command line into $postpipe (for example for Socket, Samba, ...)
    my $postpipe = "";

    if ((!$reconf) or ($config->{'connect'})) {
	# Set up connection type

	# Remove "rm" and "rp" tags to avoid problems when overwriting a
	# raw queue
	delete $entry->{'str'}{'rm'};
	delete $entry->{'str'}{'rp'};

	# All URIs ("-c" option) have the same syntax as URIs in CUPS ("-v"
	# option of "lpadmin").
	my $file;
	if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)!) {
	    # Local printer or printing to a file
	    $file = $2;
	    if ($config->{'connect'} =~ m!^usb://!) {
		# Queue with printer-bound USB URI transferred from CUPS,
		# as LPD/LPRng does not support these URIs, translate it
		# back to a standard USB device URI
		$file = cups_usb_printer_uri_to_device_uri($file);
	    }
	    if (! -e $file) {
		warn "The device or file $file doesn't exist? " .
		    "Working anyway.\n";
	    }
	    if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) ||
		($file =~ m!^/dev/ptal-printd/(.+)$!) ||
		($file =~ m!^/var/run/ptal-printd/(.+)$!)) {
		# Translate URI for ptal-printd to postpipe using the
		# "ptal-connect" command
		my $devname = $1;
		$devname =~ s/_/:/;
		$devname =~ s/_/:/;
		$postpipe = "$sysdeps->{'ptal-connect'} $devname -print";
		$entry->{'str'}{'lp'} = "/dev/null";
	    } else {
		$entry->{'str'}{'lp'} = $file;
	    }
	} elsif ($config->{'connect'} =~ m!^ptal://?([^/].*)$!) {
	    # HPOJ MLC protocol
	    my $devname = $1;
	    $postpipe = "$sysdeps->{'ptal-connect'} $devname -print";
	    $entry->{'str'}{'lp'} = "/dev/null";
	} elsif ($config->{'connect'} =~ m!^mtink:/(.+)$!) {
	    # Printing through "mtinkd"
	    $entry->{'str'}{'lp'} = "$sysdeps->{'mtink-pipes'}/$1";
	} elsif ($config->{'connect'} =~ m!^lpd://([^/]+)/([^/]+)$!) {
	    # Remote LPD
	    my $remhost = $1;
	    my $remqueue = $2;
	    if (($rawqueue) && ($config->{'spooler'} eq "lpd")) {
		$entry->{'str'}{'rm'} = $remhost;
		$entry->{'str'}{'rp'} = $remqueue;
		delete $entry->{'str'}{'lp'};
	    } elsif( ($config->{'spooler'} eq "lprng")) {
		delete $entry->{'str'}{'rm'};
		delete $entry->{'str'}{'rp'};
		$entry->{'str'}{'lp'} = "$remqueue\@$remhost";
	    } else {
		# classic LPD does not support sending jobs to a server with the
		# "rm" and "rp" tags in /etc/printcap and filtering it
		# before ("if" tag). So when we do not set up a raw queue,
		# we do not
		#
		#   $entry->{'str'}{'rm'} = $remhost;
		#   $entry->{'str'}{'rp'} = $remqueue;
		#
		# but use "rlpr" in a $postpipe. Note that "rlpr" prints a
		# banner page by default, "-h" suppresses it. "rlpr" must
		# be SUID "root".
		$postpipe = "$sysdeps->{'rlpr'} -q -h -P " .
		    "$remqueue\@$remhost";
		$entry->{'str'}{'lp'} = "/dev/null";
	    }
	} elsif ($config->{'connect'} =~
		 m!^socket://([^/:]+):([0-9]+)/?$!) {
	    # Socket (AppSocket/HP JetDirect)
	    my $remhost = $1;
	    my $remport = $2;
	    if( ($config->{'spooler'} eq "lprng")) {
		$entry->{'str'}{'lp'} = "$remhost\%$remport";
	    } else {
	    $postpipe = "$sysdeps->{'nc'} -w 1 $remhost $remport";
	    $entry->{'str'}{'lp'} = "/dev/null";
	    }
	} elsif ($config->{'connect'} =~ m!^smb://(.*)$!) {
	    # SMB (Printer on Windows server)
	    my $parameters = $1;
	    # Get the user's login and password from the URI
	    my $smbuser = "";
	    my $smbpassword = "";
	    if ($parameters =~ m!([^@]*)@([^@]+)!) {
		my $login = $1;
		$parameters = $2;
		if ($login =~ m!([^:]*):([^:]*)!) {
		    $smbuser = $1;
		    $smbpassword = $2;
		} else {
		    $smbuser = $login;
		    $smbpassword = "";
		}
	    } else {
		$smbuser = "GUEST";
		$smbpassword = "";
	    }
	    # Get the workgroup, server, and share name
	    my $workgroup = "";
	    my $smbserver = "";
	    my $smbshare = "";
	    if ($parameters =~ m!([^/]*)/([^/]+)/([^/]+)$!) {
		$workgroup = $1;
		$smbserver = $2;
		$smbshare = $3;
	    } elsif ($parameters =~ m!([^/]+)/([^/]+)$!) {
		$workgroup = "";
		$smbserver = $1;
		$smbshare = $2;
	    } else {
		die "The \"smb://\" URI must at least contain the " .
		    "server name and the share name!\n";
	    }
	    # Set up the command line for printing on the SMB server
	    $postpipe = "$sysdeps->{'smbclient'} '//$smbserver/$smbshare'";
	    if ($smbpassword ne "") {
		warn("WARNING: smbclient password is visible in PPD file\n");
		$postpipe .= " '$smbpassword'";
	    }
	    if ($smbuser ne "") {$postpipe .= " -U '$smbuser'";}
	    if ($workgroup ne "") {$postpipe .= " -W '$workgroup'";}
	    $postpipe .= " -N -P -c 'print -' ";
	    $entry->{'str'}{'lp'} = "/dev/null";
	} elsif ($config->{'connect'} =~ m!^ncp://(.*)$!) {
	    my $parameters = $1;
	    # Get the user's login and password from the URI
	    my $ncpuser = "";
	    my $ncppassword = "";
	    if ($parameters =~ m!([^@]*)@([^@]+)!) {
		my $login = $1;
		$parameters = $2;
		if ($login =~ m!([^:]*):([^:]*)!) {
		    $ncpuser = $1;
		    $ncppassword = $2;
		} else {
		    $ncpuser = $login;
		    $ncppassword = "";
		}
	    } else {
		$ncpuser = "";
		$ncppassword = "";
	    }
	    # Get the server and share name
	    my $ncpserver = "";
	    my $ncpqueue = "";
	    if ($parameters =~ m!([^/]+)/([^/]+)$!) {
		$ncpserver = $1;
		$ncpqueue = $2;
	    } else {
		die "The \"ncp://\" URI must at least contain the " .
		    "server name and the queue name!\n";
	    }
	    # Set up the command line for printing on the Netware server
	    $postpipe = "$sysdeps->{'nprint'} -S $ncpserver";
	    if ($ncpuser ne "") {
		$postpipe .= " -U $ncpuser";
		if ($ncppassword ne "") {
		    warn("WARNING: ncp password is visible in PPD file\n");
		    $postpipe .= " -P $ncppassword";
		} else {
		    $postpipe .= " -n";
		}
	    }
	    $postpipe .= " -q $ncpqueue -N - 2>/dev/null";
	    $entry->{'str'}{'lp'} = "/dev/null";
	} elsif ($config->{'connect'} =~ m!^postpipe:(.*)$!) {
	    # Pipe output into a command
	    $postpipe = $1;
	    $entry->{'str'}{'lp'} = "/dev/null";
	} elsif ($config->{'connect'}) {
	    $entry->{'str'}{'lp'} = '/dev/null';
	    die ("The URI \"$config->{'connect'}\" is not supported " .
		 "for LPD/LPRng or you have\nmistyped.\n");
	} else {
	    print STDERR "You must specify a connection with -c.\n";
		help();
		exit(1);
	}
	# Put $postpipe into the data structure, so that it will be
	# inserted into the PPD file
	if ($postpipe ne "") {
	    $postpipe = "| $postpipe";
	    $db->{'dat'}{'postpipe'} = $postpipe;
	} else {
	    undef $db->{'dat'}{'postpipe'};
	}
    } else {
	# Keep previous connection type
	# Use previous $postpipe
	if (defined($db->{'dat'}{'postpipe'})) {
	    $postpipe = $db->{'dat'}{'postpipe'};
	}
    }

    # When we have a $postpipe we never write to a device
    if ($postpipe ne "") {
	$entry->{'str'}{'lp'} = '/dev/null';
	if ($config->{'spooler'} eq "lpd") {
	    $entry->{'str'}{'if'} = $sysdeps->{'foomatic-rip'};
	    $entry->{'str'}{'af'} = $ppdfile;
	} elsif ($config->{'spooler'} eq "lprng") {
	    $entry->{'str'}{'if'} = $sysdeps->{'foomatic-rip'};
	    $entry->{'str'}{'ppd'} = $ppdfile;
	    $entry->{'bool'}{'force_localhost'} = 1;
	} else {
	    die "The spooler $config->{'spooler'} is not supported " .
		"by this function!\n";
	}
    }

    # Various file setup
    mkdir $sysdeps->{'foo-etc'}, 0755;
    mkdir "$sysdeps->{'foo-etc'}/lpd", 0755;
    mkdir $entry->{'str'}{'sd'}, 0755;

    # Lead with a blank line for new entries
    push (@{$entry->{'comments'}}, "\n")
	if (!$reconf);

    # Put in a useful comment for both new and old entries
    push (@{$entry->{'comments'}},
	  sprintf ("\# Entry edited %s by $progname.",
		   scalar(localtime(time))),
	  "\# Additional configuration atop $ppdfile");

    # Add to the printcap if a new entry
    if (!$reconf) {
	push(@{$pcap}, $entry);
    }

    # Generate/write te PPD file
    writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);

    # Make sure that /var/spool/lp-errs exists
    system "touch $sysdeps->{'lpd-log'}";
    chmod 0600, $sysdeps->{'lpd-log'};
    my ($lpuid, $lpgid) = (-1, -1);
    my $l;
    $l = getpwnam("lp"); $lpuid = $l if defined($l);
    $l = getgrnam("lp"); $lpgid = $l if defined($l);
    chown $lpuid, $lpgid, $sysdeps->{'lpd-log'};

    # Write back /etc/printcap
    my $printcap = $sysdeps->{'lpd-pcap'};
    rename $printcap, "$printcap.old" or die "Cannot backup $printcap!\n";
    open PRINTCAP, "> $printcap" or die "Cannot write $printcap!\n";
    print PRINTCAP dump_lpd_printcap($config, $pcap);
    close PRINTCAP;
    chmod 0644, $printcap;

    # In case of LPRng, give SIGHUP to the daemon, LPRng needs this to 
    # recognize a new queue
    if ($config->{'spooler'} eq "lprng") {
	# first check configuration
	system("$sysdeps->{'lprng-checkpc'} -f > /dev/null 2>&1");
	# now signal to use it
	system("$sysdeps->{'lpd-lpc'} reread > /dev/null 2>&1");
    }

    return 1;
}

sub default_lpd {
    my ($config) = $_[0];

    my $name = $config->{'queue'};

    my $pcap = load_lpd_printcap();

    # Add the alias "lp" to the /etc/printcap entry to make LPD considering
    # the chosen printer as default printer

    # Some stuff for renaming a queue named "lp"
    my $nppdfile = undef;
    my $newname = undef;
    my $rawqueue = 0;

    my @newcap;
    for (@{$pcap}) {
	my $p = $_;
	if ($p->{'names'}[0] eq $name) {
	    $p->{'names'}[4] = 'lp';
	} else {
	    # Rename a printer whose first name is 'lp'
	    if ($p->{'names'}[0] eq 'lp') {
		# Do we have a raw queue?
		if ((!defined($p->{'str'}{'if'})) ||
		    ($p->{'str'}{'if'} ne $sysdeps->{'foomatic-rip'})) {
		    $rawqueue = 1;
		}
		# Search for a free name
		my $i = 0;
		my $namefound = 0;
		while(!$namefound) {
		    my $pp;
		    my $nameinuse = 0;
		    for $pp (@{$pcap}) {
			if (defined($pp->{'names'})) {
			    my $n;
			    for $n (@{$pp->{'names'}}) {
				if ($n eq "lp$i") {
				    $nameinuse = 1;
				    last;
				}
			    }
			    if ($nameinuse) {
				$i++;
				last;
			    }
			}
		    }
		    $namefound = 1 - $nameinuse;
		}
		$newname = "lp$i";

		# Old PPD file name
		my $ppdfile = sprintf('%s/lpd/lp.ppd',
				   $sysdeps->{'foo-etc'});
		
		# New PPD file name
		my $nppdfile = sprintf('%s/lpd/%s.ppd',
				    $sysdeps->{'foo-etc'},
				    $newname);
		
		# Rename the printer
		$p->{'names'}[0] = $newname;
		my $oldspooldir = $p->{'str'}{'sd'};
		$p->{'str'}{'sd'} = sprintf('%s/%s',
					    $sysdeps->{'lpd-dir'},
					    $newname);
		if ($p->{'str'}{'af'} =~ /\.ppd$/) {
		    $p->{'str'}{'af'} = $nppdfile;
		}

		# Rename old $ppdfile, if any
		rename $ppdfile, $nppdfile
		    if (-f $ppdfile);
		
		# Rename the spool directory
		rename $oldspooldir, $p->{'str'}{'sd'}
		    if (-d $oldspooldir);

		# Put out warning
		warn("WARNING: Printer \"lp\" renamed to \"$newname\".\n");
	    }
	    # Remove 'lp' as alias name
	    my $n;
	    for $n (@{$p->{'names'}}) {
		if ($n eq 'lp') {
		    $n = '';
		}
	    }
	}
	push (@newcap, $p);
    }

    my @newprintcap = dump_lpd_printcap($config, \@newcap);

    my $printcap = $sysdeps->{'lpd-pcap'};
    rename $printcap, "$printcap.old" or die "Cannot backup $printcap!\n";
    open PRINTCAP, "> $printcap" or die "Cannot write $printcap!\n";
    print PRINTCAP @newprintcap;
    close PRINTCAP;
    chmod 0644, $printcap;

    return 1;
}

sub default_lprng {
    my ($config) = $_[0];

    my $name = $config->{'queue'};

    my $pcap = load_lpd_printcap();

    # Move the /etc/printcap entry for the chosen printer to the first place
    # so that LPRng considers it as the default printer

    my @newcap;
    for (@{$pcap}) {
	push (@newcap, $_)
	    if ($_->{'names'}[0] eq $name);
    }
    for (@{$pcap}) {
	push (@newcap, $_)
	    unless ($_->{'names'}[0] eq $name);
    }

    my @newprintcap = dump_lpd_printcap($config, \@newcap);

    my $printcap = $sysdeps->{'lpd-pcap'};
    rename $printcap, "$printcap.old" or die "Cannot backup $printcap!\n";
    open PRINTCAP, "> $printcap" or die "Cannot write $printcap!\n";
    print PRINTCAP @newprintcap;
    close PRINTCAP;
    chmod 0644, $printcap;

    # In case of LPRng, give SIGHUP to the daemon, LPRng needs this to 
    # recognize the changes
    if ($config->{'spooler'} eq "lprng") {
	system("$sysdeps->{'lpd-lpc'} reread > /dev/null 2>&1");
	system("$sysdeps->{'lprng-checkpc'} -f > /dev/null 2>&1");
    }

    return 1;
}

sub delete_lpd {
    my ($config) = $_[0];

    my $name = $config->{'queue'};

    my $pcap = load_lpd_printcap();

    my @newcap;
    for (@{$pcap}) {
	push (@newcap, $_)
	    unless ($_->{'names'}[0] eq $name);
    }

    my @newprintcap = dump_lpd_printcap($config, \@newcap);

    my $printcap = $sysdeps->{'lpd-pcap'};
    rename $printcap, "$printcap.old" or die "Cannot backup $printcap!\n";
    open PRINTCAP, "> $printcap" or die "Cannot write $printcap!\n";
    print PRINTCAP @newprintcap;
    close PRINTCAP;
    chmod 0644, $printcap;

    # PPD file name
    my $ppdfile = sprintf('%s/lpd/%s.ppd',
			  $sysdeps->{'foo-etc'},
			  $config->{'queue'});

    # Rename old $ppdfile, if any
    rename $ppdfile, "$ppdfile.old" 
	if (-f $ppdfile);

    # In case of LPRng, give SIGHUP to the daemon, LPRng needs this to 
    # recognize the changes
    if ($config->{'spooler'} eq "lprng") {
	system("$sysdeps->{'lpd-lpc'} reread > /dev/null 2>&1");
	system("$sysdeps->{'lprng-checkpc'} -f > /dev/null 2>&1");
    }

    return 1;
}

sub query_lpd {
    my ($config) = @_;

    # User requests data of a printer/driver combo to see the options before
    # installing a queue
    if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) &&
	($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) {
	if ($opt_n) {
	    my $olddatablob = load_lpd_datablob($opt_n);
	    print_perl_combo_data($config, $olddatablob);
	} else {
	    print_perl_combo_data($config);
	}
	return;
    }

    my $i = $ARGV[0];
    if (!defined($i)) {$i = 0;}

    my $pcap = load_lpd_printcap();
    my $p;

    if (!$opt_P) {
	print "<queues>\n";
    }

    # Query the default printer
    my $default;
    if (!defined($config->{'queue'})) {
	if ($config->{'spooler'} eq "lpd") {
	    # Under LPD the default printer is the printer which has
	    # "lp" as its name or as an alias name
	    my $def_firstname = undef;
	    for $p (@{$pcap}) {
		if (defined($p->{'names'})) {
		    my $n;
		    for $n (@{$p->{'names'}}) {
			if ($n eq 'lp') {
			    $def_firstname = $p->{'names'}[0];
			    last;
			}
		    }
		    if (defined($def_firstname)) {
			last;
		    }
		}
	    }
	    if (defined($def_firstname)) {
		$default = $def_firstname;
		if (!$opt_P) {
		    print "<defaultqueue>$def_firstname</defaultqueue>\n";
		}
	    }
	} else {
	    # Under LPRng the default printer is the first entry in
	    # /etc/printcap
	    for $p (@{$pcap}) {
		if (defined($p->{'names'})) {
		    $default = $p->{'names'}[0];
		    if (!$opt_P) {
			print "<defaultqueue>$p->{'names'}[0]" .
			    "</defaultqueue>\n";
		    }
		    last;
		}
	    }
	}
    }

    for $p (@{$pcap}) {
	# enpty end entry for trailing comments
	next if !defined($p->{'names'});
	
	# were we invoked for only one queue?
	next if (defined($config->{'queue'})
		 and $config->{'queue'} ne $p->{'names'}[0]);

	# load the queue data
	$db->{'dat'} = load_lpd_datablob($p->{'names'}[0]);

	# extract the queue data block
        my $c = $db->{'dat'}{'queuedata'};

	if ($opt_P) {
	    if ($p->{'names'}[0] eq $default) {
		$db->{'dat'}{'queuedata'}{'default'} = 1;
	    } else {
		$db->{'dat'}{'queuedata'}{'default'} = 0;
	    }
	    $db->{'dat'}{'queuedata'}{'remote'} = 0;
	    my $asciidata = $db->getascii();
	    $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g;
	    print $asciidata;
	    $i ++;
	} else {
	    # and get it to standard output
	    dump_config($c);
	}
    }

    if (!$opt_P) {
	print "</queues>\n";
    }

    return;
}

### Queue manipulation functions for CUPS

sub setup_cups {
    my ($config) = $_[0];

    # PPD file name
    # (/etc/foomatic/cups/ will be a link to /etc/cups/ppd/)
    my $ppdfile = sprintf('%s/ppd/%s.ppd',
			      $sysdeps->{'cups-etc'},
			      $config->{'queue'});

    # Get the data from the former queue if we reconfigure or copy a queue
    # do also some checking of the user-supplied parameters
    my ($rawqueue, $newfoomaticdata, $makemodel, $beh) =
	getoldqueuedata($config, 1);

    # Here we set up the command line for the "lpadmin" command
    my $lpadminline =
	"$sysdeps->{'cups-admin'} -p \"$config->{'queue'}\" -E";

    # Use manufacturer and model as description when no description is
    # provided
    if (defined($config->{'desc'})) {
	$lpadminline .= " -D \"$config->{'desc'}\"";
    } else {
	# Before we overwrite the description field with manufacturer
	# and model, check if there is some old contents
	my $pconf = load_cups_printersconf();
	my $p;
	my $olddesc;
	for $p (@{$pconf}) {
	    next if (defined($config->{'queue'})
		     and $config->{'queue'} ne $p->{'name'});
	    $olddesc = $p->{'Info'};
	}
	if (!$olddesc) {
	    if (!$rawqueue) {
		$lpadminline .= " -D \"$makemodel\"";
	    } else {
		$lpadminline .= " -D \"Raw queue\"";
	    }
	}
    }

    # Fill in the "location" field if something for it is provided.
    if (defined($config->{'loc'})) {
	$lpadminline .= " -L \"$config->{'loc'}\"";
    }

    # PPD file argument for the printer
    if (!$rawqueue) {
	$lpadminline .= " -P \'$ppdfile\'";
    }

    # All URIs ("-c" option) have the same syntax as URIs in CUPS
    # ("-v" option of "lpadmin"). Here the old "file:/" URIs are
    # translated to the form which CUPS needs. All other URIs are
    # simply passed to lpadmin.

    my $cupsuri = "";
    if (defined($config->{'connect'})) {
	if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)$!) {
	    # Translate "file:/" into the prefix needed by CUPS, if
	    # necessary
	    $cupsuri = $2;
	    if ((($cupsuri =~ m!$sysdeps->{'ptal-pipes'}/(.+)$!) ||
		 ($cupsuri =~ m!/dev/ptal-printd/(.+)$!) ||
		 ($cupsuri =~ m!/var/run/ptal-printd/(.+)$!)) &&
		(-x "$sysdeps->{'cups-backends'}/ptal")) {
		# Translate URI for ptal-printd (does not work with CUPS
		# 1.1.12 and newer) to URI for the "ptal" CUPS backend
		# script (if the script is there)
		my $devname = $1;
		$devname =~ s/_/:/;
		$devname =~ s/_/:/;
		$cupsuri = "ptal:/$devname";
	    } elsif ((($cupsuri =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) ||
		      ($cupsuri =~ m!^/var/mtink/(.+)$!)) &&
		     (-x "$sysdeps->{'cups-backends'}/mtink")) {
		# Translate URI for mtinkd (does not work with CUPS
		# 1.1.12 and newer) to URI for the "mtink" CUPS backend
		# script (if the script is there)
		$cupsuri = "mtink:/$1";
	    } elsif ($config->{'connect'} =~ m!usb!i) {
		$cupsuri = cups_usb_device_uri_to_printer_uri($cupsuri);
		$cupsuri = "usb:$cupsuri";
	    } elsif (($cupsuri =~ m!lp[0-9]!) || ($cupsuri =~ m!LP[0-9]!)|| 
		     ($cupsuri =~ m!parallel!)) {
		$cupsuri = "parallel:$cupsuri";
	    } elsif (($cupsuri =~ m!tty!) || ($cupsuri =~ m!TTY!) || 
		     ($cupsuri =~ m!serial!)) {
		$cupsuri = "serial:$cupsuri";
	    } else {
		$cupsuri = "file:$cupsuri";
	    }
	} elsif (($config->{'connect'} =~ m!^ptal://?([^/].*)$!) &&
		 (!-x "$sysdeps->{'cups-backends'}/ptal")) {
	    # If there is no "ptal" backend script for CUPS, use an URI
	    # pointing to the pipe set up by ptal-printd.
	    my $devname = $1;
	    $devname =~ tr/:/_/;
	    $cupsuri = "file:$sysdeps->{'ptal-pipes'}/$devname";
	} elsif (($config->{'connect'} =~ m!^mtink:/(.*)$!) &&
		 (!-x "$sysdeps->{'cups-backends'}/mtink")) {
	    # If there is no "mtink" backend script for CUPS, use an URI
	    # pointing to the pipe set up by mtinkd.
	    $cupsuri = "file:$sysdeps->{'mtink-pipes'}/$1";
	} else {
	    $cupsuri=$config->{'connect'};
	}
	# Correct PTAL URIs: "ptal:/..." for HPOJ 0.9, "ptal://..." for newer
	# HPOJ
	if ($cupsuri =~ m!^ptal:/!) {
	    $cupsuri = cups_correct_ptal_uri($cupsuri);
	}
    }

    # Are there changes in the error handling of the backend?
    if (((defined($config->{'dd'})) && 
	 (((defined($beh->{'dd'})) && 
	   ($config->{'dd'} ne $beh->{'dd'})) ||
	  ($config->{'dd'} != 0))) ||
	((defined($config->{'att'})) && 
	 (((defined($beh->{'att'})) && 
	   ($config->{'att'} ne $beh->{'att'})) ||
	  ($config->{'att'} != 1))) ||
	((defined($config->{'delay'})) && 
	 (((defined($beh->{'delay'})) &&
	   ($config->{'delay'} ne $beh->{'delay'})) ||
	  ($config->{'delay'} != 30)))) {
	if (!defined($config->{'dd'})) {
	    $config->{'dd'} = (defined($beh->{'dd'}) ? $beh->{'dd'} : 0);
	}
	if (!defined($config->{'att'})) {
	    $config->{'att'} = (defined($beh->{'att'}) ? $beh->{'att'} : 1);
	}
	if (!defined($config->{'delay'})) {
	    $config->{'delay'} = (defined($beh->{'delay'}) ? 
				  $beh->{'delay'} : 30);
	}
	$cupsuri = $beh->{'uri'} if !$cupsuri;
	# Do only add the "beh" wrapper backend when it is really needed
	# (More than one retry and/or no disabling) and if the queue is not
	# using the HPLIP ("hp") backend, as otherwise the "hp-toolbox"
	# will not list the printer any more. HPLIP does infinite retries
	# in 30-sec intervals anyway.
	if (($cupsuri) && ($cupsuri !~ m!^hp(fax|):/!) &&
	    (($config->{'dd'} != 0) || ($config->{'att'} != 1))) {
	    $cupsuri = sprintf("beh:/%d/%d/%d/%s",
			       $config->{'dd'}, $config->{'att'},
			       $config->{'delay'}, $cupsuri);
	}
    }

    if ($cupsuri) {
	$lpadminline .= " -v \"$cupsuri\"";
    }

    # Directory setup, let the Foomatic PPD directory for CUPS be the same 
    # as /etc/cups/ppd/ (where CUPS stores the PPDs of the installed queues)
    mkdir $sysdeps->{'foo-etc'}, 0755;
    symlink "$sysdeps->{'cups-etc'}/ppd/", "$sysdeps->{'foo-etc'}/cups";

    # In CUPS we never have a $postpipe
    # (when we get a $postpipe from a source PPD file from another
    # spooler, we don't need to remove it really, because it will be
    # ignored by foomatic-rip, uncomment this to remove it)

    #$db->{'dat'}{'postpipe'} = "";

    # Generate/write te PPD file
    writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);

    # Execute the lpadmin command to set up the new queue

    if (system $lpadminline) {
	# Remove the config files
	unlink "$ppdfile"
	    if (-f "$ppdfile");
	# Revert changed config files
	rename "$ppdfile.old", "$ppdfile"
	    if (-f "$ppdfile.old");
	die "Could not set up/change the queue \"$config->{'queue'}\"!\n";
    }

    return 1;
}

sub default_cups {
    my ($config) = $_[0];
 
    if ($< == 0) {
	# (/etc/cups/printers.conf can only be manipulated by root)
	# This line sets the default printer in /etc/cups/printers.conf
	my $command = "$sysdeps->{'cups-admin'} -d " .
	    "\"$config->{'queue'}\" > /dev/null";
 
	# Do it! (Ignore errors silently)
	system $command;
    }
 
    # This line sets the default printer in /etc/cups/lpoptions
    # (required for setting a remote queue as default)
    my $command = "$sysdeps->{'cups-lpoptions'} -d " .
	"\"$config->{'queue'}\" > /dev/null";
 
    # Do it!
    system $command and
        die "Unable to set queue \"$config->{'queue'}\" as default!\n";
 
}

sub delete_cups {
    my ($config) = $_[0];

    # This line deletes the old printer queue
    my $queuedeleteline =
	"$sysdeps->{'cups-admin'} -x \"$config->{'queue'}\"";

    # Do it!
    system $queuedeleteline and
	die "Unable to delete queue \"$config->{'queue'}\"!\n";

    return 1;
}

sub query_cups {
    my ($config) = @_;

    # User requests data of a printer/driver combo to see the options before
    # installing a queue
    if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) &&
	($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) {
	if ($opt_n) {
	    my $olddatablob = load_cups_datablob($opt_n);
	    print_perl_combo_data($config, $olddatablob);
	} else {
	    print_perl_combo_data($config);
	}
	return;
    }

    my $i = $ARGV[0];
    if (!defined($i)) {$i = 0;}

    my $pconf = load_cups_printersconf();
    if (defined($opt_r)) {$opt_r = undef;}
    my $p;

    if (!$opt_P) {
	print "<queues>\n";
    }

    # Query the default printer
    my $default = '';
    if (!defined($config->{'queue'})) {
	open DEFAULT, "$sysdeps->{'cups-lpstat'} -d |" or
	    die "Could not run $sysdeps->{'cups-lpstat'}!\n";
	my $defaultstr = <DEFAULT>;
	close DEFAULT;
	if ($defaultstr =~ m!\S+:\s+(\S+)$!) {
	    $default = $1;
	    if (!$opt_P) {
		print "<defaultqueue>$default</defaultqueue>\n";
	    }
	}
    }

    for $p (@{$pconf}) {
	
	# were we invoked for only one queue?
	next if (defined($config->{'queue'})
		 and $config->{'queue'} ne $p->{'name'});

	# load the queue data
	$db->{'dat'} = load_cups_datablob($p->{'name'});

	# Enter info for remote queue
	if ($p->{'remote'}) {
	    $db->{'dat'}{'queuedata'}{'foomatic'} = 0;
	    $db->{'dat'}{'queuedata'}{'spooler'} = 'cups';
	    $db->{'dat'}{'queuedata'}{'queue'} = $p->{'name'};
	    $db->{'dat'}{'queuedata'}{'connect'} = $p->{'DeviceURI'};
	    $db->{'dat'}{'queuedata'}{'description'} = $p->{'Info'};
	    $db->{'dat'}{'queuedata'}{'loc'} = $p->{'Location'};
	    $db->{'dat'}{'queuedata'}{'remote'} = 1;
	} else {
	    $db->{'dat'}{'queuedata'}{'remote'} = 0;
	}

	# extract the queue data block
	my $c = $db->{'dat'}{'queuedata'};

	if ($opt_P) {
	    if ($p->{'name'} eq $default) {
		$db->{'dat'}{'queuedata'}{'default'} = 1;
	    } else {
		$db->{'dat'}{'queuedata'}{'default'} = 0;
	    }
	    my $asciidata = $db->getascii();
	    $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g;
	    print $asciidata;
	    $i ++;
	} else {
	    # and get it to standard output
	    dump_config($c);
	}
    }

    if (!$opt_P) {
	print "</queues>\n";
    }
    
    return;
}

### Queue manipulation functions for PDQ

sub setup_pdq {
    my ($config) = $_[0];

    # Read the previous /usr/lib/pdq/printrc
    my $printrc = load_pdq_printrc();

    my ($ppdfile, $driverfile, $entry, $reconf, $p);
    $reconf = 0;
    for $p (@{$printrc}) {
	if ((defined($p->{'name'})) &&
	    ($p->{'name'} eq $config->{'queue'})) {
	    $entry = $p;
	    $reconf = 1;
	    last;

	    use Data::Dumper;
	    print "Reconfigure of ", Dumper($p);
	}
    }

    # Config file names
    $ppdfile = sprintf('%s/pdq/%s.ppd',
			      $sysdeps->{'foo-etc'},
			      $config->{'queue'});
    $driverfile = sprintf('%s/pdq/driverdescr/%s.pdq',
			      $sysdeps->{'foo-etc'},
			      $config->{'queue'});

    # Get the data from the former queue if we reconfigure or copy a queue
    # do also some checking of the user-supplied parameters
    my ($rawqueue, $newfoomaticdata, $makemodel) =
	getoldqueuedata($config, $reconf);

    # Set the initial line of the "printer" block in /usr/lib/pdq/printrc
    $entry->{'name'} = $config->{'queue'};

    # Location field
    if ((defined($config->{'loc'})) || (!$reconf)) {
	$entry->{'location'} = "\"$config->{'loc'}\"";
    }

    # Model/Description field
    if (defined($config->{'desc'})) {
	$entry->{'model'} = "\"$config->{'desc'}\"";
    } elsif (!$entry->{'model'}) {
	if (!$rawqueue) {
	    $entry->{'model'} = "\"$makemodel\"";
	} else {
	    $entry->{'model'} = "\"Raw printer\"";
	}
    }

    # Create directories
    mkdir $sysdeps->{'foo-etc'}, 0755;
    mkdir $sysdeps->{'foo-etc'} . '/pdq', 0755;
    mkdir $sysdeps->{'foo-etc'} . '/pdq/driverdescr', 0755;
    # Make the printer driver descriptions in /etc/foomatic/pdq visible
    # for PDQ
    # symlink $sysdeps->{'foo-etc'} . '/pdq', $sysdeps->{'pdq-foomatic'};

    # Save old driver file, use the "~" to make it appear an editor
    # backup so that PDQ does not parse it.
    # Save old $driverfile, if any
    rename $driverfile, "$driverfile.old~" 
	if (-f $driverfile);

    # Generate/write the PPD file
    writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);

    # Create driver description file
    if ($rawqueue) {
	system("$sysdeps->{'foomatic-rip'} --genrawpdq $driverfile") and
	    die "Cannot create $driverfile!\n";
    } else {
	system("$sysdeps->{'foomatic-rip'} --ppd \'$ppdfile\' --genpdq " .
	       "$driverfile") and
	    die "Cannot create $driverfile!\n";
    }

    # PDQ configuration file

    # Driver fields

    # Extract driver name
    my $driverdesc = `cat $driverfile`;
    $driverdesc =~ m!^\s*driver\s*(\"\S*\-\d+\")!m;

    # Driver-specific entries
    $entry->{'driver'} = $1;
    $entry->{'driver_opts'} = "\{ \}";
    $entry->{'driver_args'} = "\{ \}";

    # Interface fields

    # All URIs ("-c" option) have the same syntax as URIs in CUPS ("-v"
    # option of "lpadmin").
    if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)!) {
	# Local printer or printing to a file
	my $file = $2;
	if ($config->{'connect'} =~ m!^usb://!) {
	    # Queue with printer-bound USB URI transferred from CUPS,
	    # as PDQ does not support these URIs, translate it
	    # back to a standard USB device URI
	    $file = cups_usb_printer_uri_to_device_uri($file);
	}
	if (! -e $file) {
	    warn "The device or file $file doesn't exist? " .
		"Working anyway.\n";
	}
	$entry->{'interface'} = "\"local-port\"";
	$entry->{'interface_opts'} = "\{ \}";
	$entry->{'interface_args'} = "\{ \"PORT\" = \"$file\" \}";
    } elsif ($config->{'connect'} =~ m!^ptal://?([^/].*)$!) {
	# HPOJ MLC protocol
	my $devname = $1;
	$devname =~ tr/:/_/;
	$entry->{'interface'} = "\"local-port\"";
	$entry->{'interface_opts'} = "\{ \}";
	$entry->{'interface_args'} = "\{ \"PORT\" = " .
	    "\"$sysdeps->{'ptal-pipes'}/$devname\" \}";
    } elsif ($config->{'connect'} =~ m!^mtink:/(.+)$!) {
	# Printing through "mtinkd"
	$entry->{'interface'} = "\"local-port\"";
	$entry->{'interface_opts'} = "\{ \}";
	$entry->{'interface_args'} = "\{ \"PORT\" = " .
	    "\"$sysdeps->{'mtink-pipes'}/$1\" \}";
    } elsif ($config->{'connect'} =~ m!^lpd://([^/]+)/([^/]+)$!) {
	# Remote LPD
	my $remhost = $1;
        my $remqueue = $2;
	$entry->{'interface'} = "\"bsd-lpd\"";
	$entry->{'interface_opts'} = "\{ \}";
	$entry->{'interface_args'} = 
	    "\{ \"QUEUE\" = \"$remqueue\", \"REMOTE_HOST\" = " .
	    "\"$remhost\" \}";
    } elsif ($config->{'connect'} =~ m!^socket://([^/:]+):([0-9]+)/?$!) {
	# Socket (AppSocket/HP JetDirect)
	my $remhost = $1;
        my $remport = $2;
	$entry->{'interface'} = "\"tcp-port\"";
	$entry->{'interface_opts'} = "\{ \}";
	$entry->{'interface_args'} = 
	    "\{ \"REMOTE_PORT\" = \"$remport\", \"REMOTE_HOST\" = " .
	    "\"$remhost\" \}";
    } elsif ($config->{'connect'}) {
	die ("The URI \"$config->{'connect'}\" is not supported " .
	     "for PDQ or you have\nmistyped.\n");
    } elsif (!$reconf) {
	die "You must specify a connection with -c.\n";
    }

    # Add to the printrc if it is a new entry
    if (!$reconf) {
	push(@{$printrc}, $entry);
    }

    # Write back the modified printrc file
    my $printrcname = $sysdeps->{'pdq-printrc'};
    rename $printrcname, "$printrcname.old" or
	die "Cannot backup $printrcname!\n";
    open PRINTRC, "> $printrcname" or die "Cannot write $printrcname!\n";
    print PRINTRC dump_pdq_printrc($printrc);
    close PRINTRC;
    chmod 0644, $printrcname;

    return 1;
}

sub default_pdq {
    my ($config) = $_[0];

    # Determine the name of the config file to modify
    my $printrcname = "";
    if ($< == 0) {
	$printrcname = "$sysdeps->{'pdq-printrc'}";
	if (!(-f $printrcname)) {die "No file $printrcname!"};
    } else {
	$printrcname = "$ENV{HOME}/.printrc";
	if (!(-f $printrcname)) {system "touch $printrcname"};
    }

    # Read the config file
    open PRINTRC, "$printrcname" or die "Cannot open $printrcname!";
    my @printrc = <PRINTRC>;
    close PRINTRC;

    # Remove all valid "default_printer" lines
    ($_ =~ /^\s*default_printer/ and $_="") foreach @printrc;
 
    # Insert the new "default_printer" line
    push @printrc, "default_printer $config->{'queue'}\n";

    # Write back the modified config file
    open PRINTRC, "> $printrcname" or die "Cannot open $printrcname!";
    print PRINTRC @printrc;
    close PRINTRC;

}

sub delete_pdq {
    my ($config) = $_[0];

    my $name = $config->{'queue'};

    my $printrc = load_pdq_printrc();

    my @newrc;
    for (@{$printrc}) {
	push (@newrc, $_)
	    unless (defined($_->{'name'}) && ($_->{'name'} eq $name));
    }

    my @newprintrc = dump_pdq_printrc(\@newrc);

    my $printrcname = $sysdeps->{'pdq-printrc'};
    rename $printrcname, "$printrcname.old" or
	die "Cannot backup $printrcname!\n";
    open PRINTRC, "> $printrcname" or die "Cannot write $printrcname!\n";
    print PRINTRC @newprintrc;
    close PRINTRC;
    chmod 0644, $printrcname;

    # Config file names
    my $ppdfile = sprintf('%s/pdq/%s.ppd',
			  $sysdeps->{'foo-etc'},
			  $config->{'queue'});
    my $driverfile = sprintf('%s/pdq/driverdescr/%s.pdq',
			     $sysdeps->{'foo-etc'},
			     $config->{'queue'});

    # Rename old $ppdfile, if any
    rename $ppdfile, "$ppdfile.old" 
	if (-f $ppdfile);
    # Rename old driverfile, if any, use the "~" to make it appear an 
    # editor backup so that PDQ does not parse it.
    # Rename old $driverfile, if any
    rename $driverfile, "$driverfile.old~" 
	if (-f $driverfile);

    return 1;
}

sub query_pdq {
    my ($config) = @_;

    # User requests data of a printer/driver combo to see the options before
    # installing a queue
    if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) &&
	($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) {
	if ($opt_n) {
	    my $olddatablob = load_pdq_datablob($opt_n);
	    print_perl_combo_data($config, $olddatablob);
	} else {
	    print_perl_combo_data($config);
	}
	return;
    }

    my $i = $ARGV[0];
    if (!defined($i)) {$i = 0;}

    my $printrc = load_pdq_printrc();
    my $p;

    if (!$opt_P) {
	print "<queues>\n";
    }

    # Query the default printer
    my $default;
    if (!defined($config->{'queue'})) {
	open DEFAULT, "$sysdeps->{'pdq-print'} -h 2>&1 |" or
	    die "Could not run $sysdeps->{'pdq-print'}!\n";
	my $defaultstr = join('', <DEFAULT>);
	close DEFAULT;
	if ($defaultstr =~ m!The\s+default\s+printer\s+is\s+(\S+)$!m) {
	    $default = $1;
	    if (!$opt_P) {
		print "<defaultqueue>$default</defaultqueue>\n";
	    }
	}
    }

    for $p (@{$printrc}) {

	# Omit non-printer-block items
	next if (!(defined($p->{'name'})));
	
	# were we invoked for only one queue?
	next if (defined($config->{'queue'})
		 and $config->{'queue'} ne $p->{'name'});

	# load the queue data
	$db->{'dat'} = load_pdq_datablob($p->{'name'});

	# extract the queue data block
        my $c = $db->{'dat'}{'queuedata'};

	if ($opt_P) {
	    if ($p->{'name'} eq $default) {
		$db->{'dat'}{'queuedata'}{'default'} = 1;
	    } else {
		$db->{'dat'}{'queuedata'}{'default'} = 0;
	    }
	    $db->{'dat'}{'queuedata'}{'remote'} = 0;
	    my $asciidata = $db->getascii();
	    $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g;
	    print $asciidata;
	    $i ++;
	} else {
	    # and get it to standard output
	    dump_config($c);
	}
    }

    if (!$opt_P) {
	print "</queues>\n";
    }
    
    return;
}

### Queue manipulation functions for PPR

sub setup_ppr {
    my ($config) = $_[0];

    # Read the previous configuration
    my $printrc = load_ppr_printers_conf();

    my ($ppdfile, $entry, $reconf, $p);
    $reconf = 0;
    for $p (@{$printrc}) {
	if ((defined($p->{'name'})) &&
	    ($p->{'name'} eq $config->{'queue'})) {
	    $entry = $p;
	    $reconf = 1;
	    last;

	    use Data::Dumper;
	    print "Reconfigure of ", Dumper($p);
	}
    }

    # PPD file name
    $ppdfile = sprintf('%s/ppr/%s.ppd',
			      $sysdeps->{'foo-etc'},
			      $config->{'queue'});

    # Determine the PPR version in use
    my $pprversion;
    if (open VER, "$sysdeps->{'ppr-pprd'} --version |") {
	my $ver = <VER>;
	close VER;
	$ver =~ /^\D*(\d+)\.(\d+)(\.(\d+)|)((a|alpha|b|beta|r|rc)(\d+|)|)/;
	$pprversion = (1e8 * $1 + 1e6 * $2 + 1e4 * $4 +
		       ($5 ? 100 * (ord(uc($6)) - 64) + $7 : 9999)) / 1e8;
    } else {
	# Could not determine version, so we set it to 0 (oldest possible)
	$pprversion = 0;
    }

    # Get the data from the former queue if we reconfigure or copy a queue
    # do also some checking of the user-supplied parameters
    my ($rawqueue, $newfoomaticdata, $makemodel) =
	getoldqueuedata($config, $reconf);

    # Read out previous interface settings
    my $interface = "";
    my $address = "";
    my $options = "";
    my $interface_options = "";
    if ($reconf) {
	$interface = $entry->{'Interface'};
	$address = $entry->{'Address'};
	$interface_options = $entry->{'Options'};
	if (($interface eq "foomatic-rip") ||
	    ($interface eq "ppromatic")) {
	    if ($interface_options =~ /backend=(\S+)/) {
		$interface = $1;
		$interface_options =~ s/backend=(\S+)//;
		if ($interface_options =~ /^\s*$/) {
		    $interface_options = "";
		}
	    } else {
		$interface = "";
	    }
	}
    }

    # All URIs ("-c" option) have the same syntax as URIs in CUPS ("-v"
    # option of "lpadmin").

    if (defined($config->{'connect'})) {
	$interface_options =~ s/smbuser=(\S+)//;
	$interface_options =~ s/smbpassword=(\S+)//;
	if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)!) {
	    # Local printer or printing to a file
	    $address = $2;
	    if ($config->{'connect'} =~ m!^usb://!) {
		# Queue with printer-bound USB URI transferred from CUPS,
		# as PPR does not support these URIs, translate it
		# back to a standard USB device URI
		$address = cups_usb_printer_uri_to_device_uri($address);
	    }
	    if (! -e $address) {
		warn "The device or file $address doesn't exist? " .
		    "Working anyway.\n";
	    }
	    if (($address =~ m!usb!) || ($address =~ m!USB!) ||
		($address =~ m!$sysdeps->{'ptal-pipes'}!) || 
		($address =~ m!/dev/ptal-printd!) ||
		($address =~ m!/var/run/ptal-printd!) ||
		($address =~ m!$sysdeps->{'mtink-pipes'}!) || 
		($address =~ m!/var/mtink!)) {
		$interface = "simple";
	    } elsif (($address =~ m!lp[0-9]!) || ($address =~ m!LP[0-9]!) || 
		     ($address =~ m!parallel!)) {
		$interface = "parallel";
	    } elsif (($address =~ m!tty!) || ($address =~ m!TTY!) || 
		     ($address =~ m!serial!)) {
		$interface = "serial";
	    } else {
		$interface = "dummy";
	    }
	    $options = "";
	} elsif ($config->{'connect'} =~ m!^ptal://?([^/].*)$!) {
	    # HPOJ MLC protocol
	    my $devname = $1;
	    $devname =~ tr/:/_/;
	    $address = "$sysdeps->{'ptal-pipes'}/$devname";
	    $interface = "simple";
	    $options = "";
	} elsif ($config->{'connect'} =~ m!^mtink:/(.+)$!) {
	    # Printing through "mtinkd"
	    $address = "$sysdeps->{'mtink-pipes'}/$1";
	    $interface = "simple";
	    $options = "";
	} elsif ($config->{'connect'} =~ m!^lpd://([^/]+)/([^/]+)$!) {
	    # Remote LPD
	    my $remhost = $1;
	    my $remqueue = $2;
	    $address = "${remqueue}\@${remhost}";
	    $interface = "lpr";
	    $options = "";
	} elsif ($config->{'connect'} =~
		 m!^socket://([^/:]+):([0-9]+)/?$!) {
	    # Socket (AppSocket/HP JetDirect)
	    my $remhost = $1;
	    my $remport = $2;
	    $address = "$remhost:$remport";
	    $interface = "tcpip";
	    $options = "";
	} elsif ($config->{'connect'} =~ m!^smb://(.*)$!) {
	    # SMB (Printer on Windows server)
	    my $parameters = $1;
	    # Get the user's login and password from the URI
	    my $smbuser = "";
	    my $smbpassword = "";
	    if ($parameters =~ m!([^@]*)@([^@]+)!) {
		my $login = $1;
		$parameters = $2;
		if ($login =~ m!([^:]*):([^:]*)!) {
		    $smbuser = $1;
		    $smbpassword = $2;
		} else {
		    $smbuser = $login;
		    $smbpassword = "";
		}
	    } else {
		$smbuser = "GUEST";
		$smbpassword = "";
	    }
	    # When a password is given, a user name should be given, too.
	    if (($smbpassword ne "") && ($smbuser eq "")) {
		$smbuser = "GUEST";
	    }
	    # The "smb" interface of PPR uses "ppr" as the SMB user when no
	    # user name is given. Usually one does not have such a user name
	    # under Windows. So use "GUEST" if no user name is given.
	    if ($smbuser eq "") {
		$smbuser = "GUEST";
	    }
	    # Set the options for PPR's "smb" interface
	    $options = "";
	    if ($smbuser ne "") {
		$options = "smbuser=\"$smbuser\"";
		if ($smbpassword ne "") {
		    $options .= " smbpassword=\"$smbpassword\"";
		}
	    }
	    # Get the workgroup, server, and share name
	    my $workgroup = "";
	    my $smbserver = "";
	    my $smbshare = "";
	    if ($parameters =~ m!([^/]*)/([^/]+)/([^/]+)$!) {
		$workgroup = $1;
		$smbserver = $2;
		$smbshare = $3;
	    } elsif ($parameters =~ m!([^/]+)/([^/]+)$!) {
		$workgroup = "";
		$smbserver = $1;
		$smbshare = $2;
	    } else {
		die "The \"smb://\" URI must at least contain the " .
		    "server name and the share name!\n";
	    }
	    $address = "//$smbserver/$smbshare";
	    $interface = "smb";
	} else {
	    die ("The URI \"$config->{'connect'}\" is not supported for " .
		 "PPR or you have\nmistyped.\n");
	}
    } elsif (!$reconf) {
	die "You must specify a connection with -c.\n";
    }

    # Here we set up the command line for the "ppad interface" and the
    # "ppad options" commands
    my $ppad_interface = "";
    my $ppad_options = "";
    my $ppad_rip = "";
    if ($rawqueue) {
	$ppad_interface = "$sysdeps->{'ppr-ppad'} interface " .
	    "\"$config->{'queue'}\" $interface \"$address\"";
	$ppad_options = "$sysdeps->{'ppr-ppad'} options " .
	    "\"$config->{'queue'}\" $options $interface_options";
	$ppad_rip = "$sysdeps->{'ppr-ppad'} " .
	    "rip \"$config->{'queue'}\"";
    } else {
	if ($pprversion >= 1.50000102 ) { #1.50a2
	    $ppad_interface = "$sysdeps->{'ppr-ppad'} interface " .
		"\"$config->{'queue'}\" $interface \"$address\"";
	    $ppad_options = "$sysdeps->{'ppr-ppad'} options " .
		"\"$config->{'queue'}\" $options $interface_options";
	    if ($db->{'dat'}{'id'}) {
		$ppad_rip = "$sysdeps->{'ppr-ppad'} " .
		    "rip \"$config->{'queue'}\" foomatic-rip x" .
		    # PPR 1.50a2 has a bug and needs at least one option for
		    # the command line of the PPR RIP, therefore we add the
		    # "0" in this case. The number is very likely not the
		    # name of any boolean option, so it will be ignored by 
		    # foomatic-rip
		    (($pprversion < 1.50000103 ) ? " 0" : "");
	    } else {
		$ppad_rip = "$sysdeps->{'ppr-ppad'} " .
		    "rip \"$config->{'queue'}\"";
	    }
	} else {
	    $ppad_interface = "$sysdeps->{'ppr-ppad'} interface " .
		"\"$config->{'queue'}\" foomatic-rip \"$address\"";
	    $ppad_options = "$sysdeps->{'ppr-ppad'} options " .
		"\"$config->{'queue'}\" backend=\"$interface\" " .
		"$options $interface_options";
	    $ppad_rip = "$sysdeps->{'ppr-ppad'} " .
		"rip \"$config->{'queue'}\"";
	}
    }

    # Execute the ppad commands to set up the new queue

    if ((system $ppad_interface) ||
	(system $ppad_options) ||
	(system $ppad_rip)) {
	die "Could not set up/change the queue \"$config->{'queue'}\"!\n";
    }

    # Use manufacturer and model as description when no description is
    # provided
	my($comment, $olddesc);
    if (defined($config->{'desc'})) {
	$comment = $config->{'desc'};
    } else {
	# Before we overwrite the description field with manufacturer
	# and model, check if there is some old contents
	if (($reconf) && ($entry->{'Comment'})) {
	    $olddesc = $entry->{'Comment'};
	}
	if (!$olddesc) {
	    if (!$rawqueue) {
		$comment = "$makemodel";
	    } else {
		$comment = "Raw queue";
	    }
	}
    }
    if ($comment) {
	my $ppad_comment = "$sysdeps->{'ppr-ppad'} comment " .
	    "\"$config->{'queue'}\" \"$comment\"";
	if (system $ppad_comment) {
	    warn "Could not set description for the queue " .
		"\"$config->{'queue'}\"!\n";
	}
    }

    # Fill in the "location" field if something for it is provided.
    if (defined($config->{'loc'})) {
	my $ppad_location = "$sysdeps->{'ppr-ppad'} location " .
	    "\"$config->{'queue'}\" \"$config->{'loc'}\"";
	if (system $ppad_location) {
	    warn "Could not set location for the queue " .
		"\"$config->{'queue'}\"!\n";
	}
    }

    # Various file setup
    mkdir $sysdeps->{'foo-etc'}, 0755;
    mkdir $sysdeps->{'foo-etc'} . '/ppr', 0755;

    # Generate/write the PPD file
    writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);

    if ($rawqueue) {
	my $ppad_ppd = "$sysdeps->{'ppr-ppad'} ppd " .
	    "\"$config->{'queue'}\" \"\" 2> /dev/null";
	if (!system $ppad_ppd) {
	    # Automatic input tray selection not activated by default,
	    # because the feature requires manual choice of the paper types
	    # in the trays and other spoolers than PPR do not have automatic
	    # paper tray selection. In addition "ppop media <queue>" is
	    # broken for printers with a high number of input trays in their
	    # PPD files.
	    #my $ppad_bins = "$sysdeps->{'ppr-ppad'} bins delete " .
	        #"\"$config->{'queue'}\" \"" . 
		#join ('" "', @{$entry->{'Bins'}}) . "\"";
	    #if (system $ppad_bins) {
		#warn "Could not set paper input trays for the " .
	        #"queue \"$config->{'queue'}\"!\n";
	    #}
	    my $ppad_deffiltopts = "$sysdeps->{'ppr-ppad'} " .
		"deffiltopts \"$config->{'queue'}\" 2> /dev/null";
	    if (system $ppad_deffiltopts) {
		warn "Could not set \"DefFiltOpts\" entry for " .
		    "the queue \"$config->{'queue'}\"!\n";
	    }
	} else {
	    die "Could not set PPD for the queue \"$config->{'queue'}\"!\n";
	}
    } else {
	my $ppad_ppd = "$sysdeps->{'ppr-ppad'} ppd " .
	    "\"$config->{'queue'}\" \"$ppdfile\" 2> /dev/null";
	if (!system $ppad_ppd) {
	    # Automatic input tray selection not activated by default,
	    # because the feature requires manual choice of the paper types
	    # in the trays and other spoolers than PPR do not have automatic
	    # paper tray selection. In addition "ppop media <queue>" is
	    # broken for printers with a high number of input trays in their
	    # PPD files.
	    #my $ppad_bins = "$sysdeps->{'ppr-ppad'} bins ppd " .
	    #"\"$config->{'queue'}\"";
	    #if (system $ppad_bins) {
		#warn "Could not set paper input trays for the " .
	        #"queue \"$config->{'queue'}\"!\n";
	    #}
	    my $ppad_deffiltopts = "$sysdeps->{'ppr-ppad'} " .
		"deffiltopts \"$config->{'queue'}\" 2> /dev/null";
	    if (system $ppad_deffiltopts) {
		warn "Could not set \"DefFiltOpts\" entry for the " .
		    "queue \"$config->{'queue'}\"!\n";
	    }
	} else {
	    die "Could not set PPD for the queue \"$config->{'queue'}\"!\n";
	}
    }


    if ($rawqueue) {

	# If we have a raw queue, delete the PPD file if there is still
	# one from a former queue.

	unlink "$ppdfile"
	    if (-f "$ppdfile");
    } else {

	# Clean up "Switchset" entry

	my @switchset = split('|', $entry->{'Switchset'});
	my @newswitchset = ();
	for my $option (@switchset) {
	    if (!(($option =~ /^F\s*\*([^\*\s=:]+)\s+([^\*\s=:]+)\s*$/) ||
		  ($option =~ /^F\s*([^\*\s=:]+)\s*=\s*([^\*\s=:]+)\s*$/) ||
		  ($option =~ /^F\s*\*([^\*\s=:]+)\s*$/) ||
		  ($option =~ /^F\s*([^\*\s=:]+)\s*$/))) {
		# The option is not a PPD option, keep it.
		# PPD options are incorporated in the PPD file now and so
		# they can be dropped in the "Switchset".
		if ($option =~ /^\s*(\S)(.*)$/) {
		    push (@newswitchset, "-$1 \"$2\"");
		}
	    }
	    
	}
	my $ppad_switchset = "$sysdeps->{'ppr-ppad'} switchset " .
	    "\"$config->{'queue'}\" " . join (' ', @newswitchset);
	if (system $ppad_switchset) {
	    warn "Could not set switchset for the queue " .
		"\"$config->{'queue'}\"!\n";
	}

	# Check, if there is a PJL option and set the "Jobbreak" to "none"
	# because otherwise there is a Ctrl+D between the PJL frame added
	# by foomatic-rip and the PostScript job. This breaks printing of
	# certain PS files as the CUPS test page.

	my $pjloption = 0;
	for my $arg (@{$db->{'dat'}->{'args'}}) {
	    if ($arg->{'style'} eq "J") {
		$pjloption = 1;
		last;
	    }
	}
	if ($pjloption) {
	    my $ppad_jobbreak = "$sysdeps->{'ppr-ppad'} jobbreak " .
		"\"$config->{'queue'}\" none";
	    if (system $ppad_jobbreak) {
		warn "Could not set \"Jobbreak\" entry for the " .
		    "queue \"$config->{'queue'}\"!\n";
	    }
	}
    }

    return 1;
}

sub default_ppr {
    my ($config) = $_[0];
 
    # The default printer under PPR is the printer named "default". To be
    # able to easily switch the default printer we set up a printer group
    # named "default" containing the chosen default printer as its only
    # member. If there is already a printer called "default", we rename it.

    my $name = $config->{'queue'};
    my $printrc = load_ppr_printers_conf();
    my $printerfound = 0;
    for my $p (@{$printrc}) {
	if ($p->{'name'} eq $name) {
	    $printerfound = 1;
	}
	# Rename a printer whose name is 'default'
	if ($p->{'name'} eq 'default') {
	    # Search for a free name
	    my $i = 0;
	    my $namefound = 0;
	    my $newname = "";
	    while(!$namefound) {
		my $pp;
		my $nameinuse = 0;
		for $pp (@{$printrc}) {
		    if (defined($pp->{'name'})) {
			if ($pp->{'name'} eq "default$i") {
			    $nameinuse = 1;
			    $i++;
			    last;
			}
		    }
		}
		$namefound = 1 - $nameinuse;
	    }
	    $newname = "default$i";
	    # If the printer we want to use as default printer has the
	    # name "default", we must use the new name as the member name
	    # in the default group.
	    if ($name eq "default") {
		$name = $newname;
	    }
	    # Do the renaming
	    # Copy the queue ...
	    if (system("foomatic-configure -s ppr -n $newname -C default")){
		die "Could not copy the queue \"default\" into the " .
		    "queue \"$newname\"!\n";
	    }
	    # ... and remove the original one
	    if (system("foomatic-configure -s ppr -n default -R")) {
		die "Could not remove the queue \"default\"!\n";
	    }
	    warn "Renamed the printer\"default\" to \"$newname\"!\n";
	}
    }

    # The desired default printer exists? Then make it the default
    if ($printerfound) {
	# Create a group named "default" with only this printer as member
	my $ppad_group = "$sysdeps->{'ppr-ppad'} group members " .
	    "default \"$name\"";
	if (system $ppad_group) {
	    warn "Could not create a group to make the queue \"$name\" " .
		"the default!\n";
	}
    }

}

sub delete_ppr {
    my ($config) = $_[0];

    # This line deletes the old printer queue
    my $queuedeleteline = "$sysdeps->{'ppr-ppad'} delete " .
	"\"$config->{'queue'}\"";

    # Do it!
    system $queuedeleteline and
	die "Unable to delete queue \"$config->{'queue'}\"!\n";

    # Rename the PPD file

    # PPD file name
    my $ppdfile = sprintf('%s/ppr/%s.ppd',
			  $sysdeps->{'foo-etc'},
			  $config->{'queue'});

    # Rename old $ppdfile, if any
    rename "$ppdfile", "$ppdfile.old" 
	if (-f "$ppdfile");

    return 1;
}

sub query_ppr {
    my ($config) = @_;

    # User requests data of a printer/driver combo to see the options before
    # installing a queue
    if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) &&
	($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) {
	if ($opt_n) {
	    my $olddatablob = load_ppr_datablob($opt_n);
	    print_perl_combo_data($config, $olddatablob);
	} else {
	    print_perl_combo_data($config);
	}
	return;
    }

    my $i = $ARGV[0];
    if (!defined($i)) {$i = 0;}

    my $pconf = load_ppr_printers_conf();
    if (defined($opt_r)) {$opt_r = undef;}
    my $p;

    if (!$opt_P) {
	print "<queues>\n";
    }

    # Query the default printer
    my $default;
    if (!defined($config->{'queue'})) {
	for $p (@{$pconf}) {
	    if ($p->{'default'}) {
		$default = $p->{'name'};
		if (!$opt_P) {
		    print "<defaultqueue>$p->{'name'}</defaultqueue>\n";
		}
		last;
	    }
	}
    }

    for $p (@{$pconf}) {
	
	# were we invoked for only one queue?
	next if (defined($config->{'queue'})
		 and $config->{'queue'} ne $p->{'name'});

	# load the queue data
	$db->{'dat'} = load_ppr_datablob($p->{'name'});

	# extract the queue data block
	my $c = $db->{'dat'}{'queuedata'};
	    
	if ($opt_P) {
	    if ($p->{'name'} eq $default) {
		$db->{'dat'}{'queuedata'}{'default'} = 1;
	    } else {
		$db->{'dat'}{'queuedata'}{'default'} = 0;
	    }
	    $db->{'dat'}{'queuedata'}{'remote'} = 0;
	    my $asciidata = $db->getascii();
	    $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g;
	    print $asciidata;
	    $i ++;
	} else {
	    # and get it to standard output
	    dump_config($c);
	}
    }

    if (!$opt_P) {
	print "</queues>\n";
    }
    
    return;
}

### Queue manipulation functions for direct, spooler-less printing

sub setup_direct {
    my ($config) = $_[0];

    # Read the previous config file
    my $pconfig = load_direct_config();

    my ($entry, $reconf, $p);
    for $p (@{$pconfig}) {
	if ($p->{'name'} eq $config->{'queue'}) {
	    $entry = $p;
	    $reconf = 1;
	    last;

	    use Data::Dumper;
	    print "Reconfigure of ", Dumper($p);
	}
    }

    # PPD file name
    my $ppdfile = sprintf('%s/direct/%s.ppd',
			  $sysdeps->{'foo-etc'},
			  $config->{'queue'});

    # Get the data from the former queue if we reconfigure or copy a queue
    # do also some checking of the user-supplied parameters
    my ($rawqueue, $newfoomaticdata, $makemodel) =
	getoldqueuedata($config, $reconf);

    # Set the printer queue name
    $entry->{'name'} = $config->{'queue'};

    # Use manufacturer and model as description when no description is
    # provided
    if (defined($config->{'desc'})) {
	$entry->{'desc'} = $config->{'desc'};
    } else {
	# Before we overwrite the description field with manufacturer
	# and model, check if there is some old contents
	my( $olddesc );
	if (($reconf) && ($entry->{'desc'})) {
	    $olddesc = $entry->{'desc'};
	}
	if (!$olddesc) {
	    $entry->{'desc'} = "$makemodel";
	}
    }

    # Fill in the "location" field if something for it is provided.
    if (defined($config->{'loc'})) {
	$entry->{'loc'} = $config->{'loc'};
    }

    # If the printing jobs should not be passed to standard output, put the
    # command line into $postpipe (for example for Socket, Samba, parallel
    # port ...)
    my $postpipe = "";

    if ((!$reconf) or ($config->{'connect'})) {
	# Set up connection type

	# All URIs ("-c" option) have the same syntax as URIs in CUPS ("-v"
	# option of "lpadmin").
	if ($config->{'connect'} =~ m!^(file|usb|parallel|serial):(.*)!) {
	    # Local printer or printing to a file
	    my $file = $2;
	    if ($config->{'connect'} =~ m!^usb://!) {
		# Queue with printer-bound USB URI transferred from CUPS,
		# as spooler-less printing does not support these URIs, 
		# translate it back to a standard USB device URI
		$file = cups_usb_printer_uri_to_device_uri($file);
	    }
	    if (! -e $file) {
		warn "The device or file $file doesn't exist? " .
		    "Working anyway.\n";
	    }
	    if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) ||
		($file =~ m!^/dev/ptal-printd/(.+)$!) ||
		($file =~ m!^/var/run/ptal-printd/(.+)$!)) {
		# Translate URI for ptal-printd to postpipe using the
		# "ptal-connect" command
		my $devname = $1;
		$devname =~ s/_/:/;
		$devname =~ s/_/:/;
		$postpipe = "$sysdeps->{'ptal-connect'} $devname -print";
	    } else {
		$postpipe = "$sysdeps->{'cat'} > $file";
	    }
	} elsif ($config->{'connect'} =~ m!^ptal://?([^/].*)$!) {
	    # HPOJ MLC protocol
	    my $devname = $1;
	    $postpipe = "$sysdeps->{'ptal-connect'} $devname -print";
	} elsif ($config->{'connect'} =~ m!^mtink:/(.+)$!) {
	    # Printing through "mtinkd"
	    $postpipe = "$sysdeps->{'cat'} > $sysdeps->{'mtink-pipes'}/$1";
	} elsif ($config->{'connect'} =~ m!^lpd://([^/]+)/([^/]+)$!) {
	    # Remote LPD
	    my $remhost = $1;
	    my $remqueue = $2;
	    $postpipe = "$sysdeps->{'rlpr'} -q -h -P $remqueue\@$remhost";
	} elsif ($config->{'connect'} =~
		 m!^socket://([^/:]+):([0-9]+)/?$!){
	    # Socket (AppSocket/HP JetDirect)
	    my $remhost = $1;
	    my $remport = $2;
	    $postpipe = "$sysdeps->{'nc'} -w 1 $remhost $remport";
	} elsif ($config->{'connect'} =~ m!^smb://(.*)$!) {
	    # SMB (Printer on Windows server)
	    my $parameters = $1;
	    # Get the user's login and password from the URI
	    my $smbuser = "";
	    my $smbpassword = "";
	    if ($parameters =~ m!([^@]*)@([^@]+)!) {
		my $login = $1;
		$parameters = $2;
		if ($login =~ m!([^:]*):([^:]*)!) {
		    $smbuser = $1;
		    $smbpassword = $2;
		} else {
		    $smbuser = $login;
		    $smbpassword = "";
		}
	    } else {
		$smbuser = "GUEST";
		$smbpassword = "";
	    }
	    # Get the workgroup, server, and share name
	    my $workgroup = "";
	    my $smbserver = "";
	    my $smbshare = "";
	    if ($parameters =~ m!([^/]*)/([^/]+)/([^/]+)$!) {
		$workgroup = $1;
		$smbserver = $2;
		$smbshare = $3;
	    } elsif ($parameters =~ m!([^/]+)/([^/]+)$!) {
		$workgroup = "";
		$smbserver = $1;
		$smbshare = $2;
	    } else {
		die "The \"smb://\" URI must at least contain the " .
		    "server name and the share name!\n";
	    }
	    # Set up the command line for printing on the SMB server
	    $postpipe = "$sysdeps->{'smbclient'} \"//$smbserver/$smbshare\"";
	    if ($smbpassword ne "") {
		warn("WARNING: smbclient password is visible in PPD file\n");
		$postpipe .= " $smbpassword";
	    }
	    if ($smbuser ne "") {$postpipe .= " -U $smbuser";}
	    if ($workgroup ne "") {$postpipe .= " -W $workgroup";}
	    $postpipe .= " -N -P -c 'print -' ";
	} elsif ($config->{'connect'} =~ m!^ncp://(.*)$!) {
	    my $parameters = $1;
	    # Get the user's login and password from the URI
	    my $ncpuser = "";
	    my $ncppassword = "";
	    if ($parameters =~ m!([^@]*)@([^@]+)!) {
		my $login = $1;
		$parameters = $2;
		if ($login =~ m!([^:]*):([^:]*)!) {
		    $ncpuser = $1;
		    $ncppassword = $2;
		} else {
		    $ncpuser = $login;
		    $ncppassword = "";
		}
	    } else {
		$ncpuser = "";
		$ncppassword = "";
	    }
	    # Get the server and share name
	    my $ncpserver = "";
	    my $ncpqueue = "";
	    if ($parameters =~ m!([^/]+)/([^/]+)$!) {
		$ncpserver = $1;
		$ncpqueue = $2;
	    } else {
		die "The \"ncp://\" URI must at least contain the server " .
		    "name and the queue name!\n";
	    }
	    # Set up the command line for printing on the Netware server
	    $postpipe = "$sysdeps->{'nprint'} -S $ncpserver";
	    if ($ncpuser ne "") {
		$postpipe .= " -U $ncpuser";
		if ($ncppassword ne "") {
		    warn("WARNING: ncp password is visible in PPD file\n");
		    $postpipe .= " -P $ncppassword";
		} else {
		    $postpipe .= " -n";
		}
	    }
	    $postpipe .= " -q $ncpqueue -N - 2>/dev/null";
	} elsif ($config->{'connect'} =~ m!^postpipe:(.*)$!) {
	    # Pipe output into a command
	    $postpipe = $1;
	} elsif ($config->{'connect'} =~ m!^stdout!) {
	    $postpipe = "";
	} elsif ($config->{'connect'}) {
	    die ("The URI \"$config->{'connect'}\" is not supported for " .
		 "spooler-less printing or you have\nmistyped.\n");
	} else {
	    die "You must specify a connection with -c.\n";
	}
	# Put $postpipe into the data structure, so that it will be
	# inserted into the PPD file
	if ($postpipe ne "") {
	    $postpipe = "| $postpipe";
	    $db->{'dat'}{'postpipe'} = $postpipe;
	} else {
	    undef $db->{'dat'}{'postpipe'};
	}
    } else {
	# Keep previous connection type
	# Use previous $postpipe
	if (defined($db->{'dat'}{'postpipe'})) {
	    $postpipe = $db->{'dat'}{'postpipe'};
	}
    }

    # Various file setup
    mkdir $sysdeps->{'foo-etc'}, 0755;
    mkdir $sysdeps->{'foo-etc'} . "/direct", 0755;

    # Add to the config file if a new entry
    if (!$reconf) {
	push(@{$pconfig}, $entry);
    }

    # Generate/write the PPD file
    writeppdfile($config, $ppdfile, $rawqueue, $newfoomaticdata);

    # Write back /etc/foomatic/direct/.config
    my $pconfigname = $sysdeps->{'direct-config'};
    rename $pconfigname, "$pconfigname.old";
    open PCONFIG, "> $pconfigname" or die "Cannot write $pconfigname!\n";
    print PCONFIG dump_direct_config($pconfig);
    close PCONFIG;
    chmod 0644, $pconfigname;

    return 1;
}

sub default_direct {
    my ($config) = $_[0];

    my $name = $config->{'queue'};

    my $pconfig = load_direct_config();

    # Modify the "default" fields of the printers appropriately

    for (@{$pconfig}) {
	$_->{'default'} = ($_->{'name'} eq $name);
    }

    my @newpconfig = dump_direct_config($pconfig);

    my $pconfigname = $sysdeps->{'direct-config'};
    rename $pconfigname, "$pconfigname.old";
    open PCONFIG, "> $pconfigname" or die "Cannot write $pconfigname!\n";
    print PCONFIG @newpconfig;
    close PCONFIG;
    chmod 0644, $pconfigname;

    return 1;
}

sub delete_direct {
    my ($config) = $_[0];

    my $name = $config->{'queue'};

    my $pconfig = load_direct_config();

    # Overtake all entries except the one of the deleted printer to the
    # new config file

    my @newconf;
    for (@{$pconfig}) {
	push (@newconf, $_)
	    unless ($_->{'name'} eq $name);
    }

    my @newpconfig = dump_direct_config(\@newconf);

    my $pconfigname = $sysdeps->{'direct-config'};
    rename $pconfigname, "$pconfigname.old";
    open PCONFIG, "> $pconfigname" or die "Cannot write $pconfigname!\n";
    print PCONFIG @newpconfig;
    close PCONFIG;
    chmod 0644, $pconfigname;

    # PPD file name
    my $ppdfile = sprintf('%s/direct/%s.ppd',
			  $sysdeps->{'foo-etc'},
			  $config->{'queue'});

    # Rename old $ppdfile, if any
    rename $ppdfile, "$ppdfile.old" 
	if (-f $ppdfile);

    return 1;
}

sub query_direct {
    my ($config) = @_;

    # User requests data of a printer/driver combo to see the options before
    # installing a queue
    if (($opt_P) && ((($config->{'driver'}) && ($config->{'printer'}) &&
	($config->{'driver'} ne "raw")) || ($config->{'ppdfile'}))) {
	if ($opt_n) {
	    my $olddatablob = load_direct_datablob($opt_n);
	    print_perl_combo_data($config, $olddatablob);
	} else {
	    print_perl_combo_data($config);
	}
	return;
    }

    my $i = $ARGV[0];
    if (!defined($i)) {$i = 0;}

    my $pconf = load_direct_config();
    if (defined($opt_r)) {$opt_r = undef;}
    my $p;

    if (!$opt_P) {
	print "<queues>\n";
    }

    # Query the default printer
    my $default;
    if (!defined($config->{'queue'})) {
	for $p (@{$pconf}) {
	    if ($p->{'default'}) {
		$default = $p->{'name'};
		if (!$opt_P) {
		    print "<defaultqueue>$p->{'name'}</defaultqueue>\n";
		}
		last;
	    }
	}
    }

    for $p (@{$pconf}) {
	
	# were we invoked for only one queue?
	next if (defined($config->{'queue'})
		 and $config->{'queue'} ne $p->{'name'});

	# load the queue data
	$db->{'dat'} = load_direct_datablob($p->{'name'});

	# extract the queue data block
	my $c = $db->{'dat'}{'queuedata'};
	    
	if ($opt_P) {
	    if ($p->{'name'} eq $default) {
		$db->{'dat'}{'queuedata'}{'default'} = 1;
	    } else {
		$db->{'dat'}{'queuedata'}{'default'} = 0;
	    }
	    $db->{'dat'}{'queuedata'}{'remote'} = 0;
	    my $asciidata = $db->getascii();
	    $asciidata =~ s/\$VAR1/\$QUEUES[$i]/g;
	    print $asciidata;
	    $i ++;
	} else {
	    # and get it to standard output
	    dump_config($c);
	}
    }

    if (!$opt_P) {
	print "</queues>\n";
    }
    
    return;
}

### Functions used by the queue manipulation functions from above

sub dump_config {
    my $c = $_[0];

    print 
	sprintf("<queue foomatic=\"%d\" spooler=\"%s\">\n", 
		($c->{'foomatic'} ? 1 : 0),
		$c->{'spooler'}),

	_tag('name',$c->{'queue'}),
	_tag('printer',$c->{'printer'}),
	_tag('driver',$c->{'driver'}),
	_tag('connect',$c->{'connect'}),
	_tag('location',$c->{'loc'}),
	_tag('description',$c->{'desc'}),
	($c->{'spooler'} eq "cups" ?
	 (_tag('dontdisable',$c->{'dd'}),
	  _tag('attempts',$c->{'att'}),
	  _tag('delay',$c->{'delay'}),
	  (defined($c->{'quotaperiod'}) ?
	   _tag('quotaperiod',$c->{'quotaperiod'}) : ()),
	  (defined($c->{'pagelimit'}) ?
	   _tag('pagelimit',$c->{'pagelimit'}) : ()),
	  (defined($c->{'klimit'}) ?
	   _tag('klimit',$c->{'klimit'}) : ()),
	  (defined($c->{'laststatechange'}) ?
	   _tag('laststatechange',$c->{'laststatechange'}) : ()),
	  (defined($c->{'shared'}) ?
	   _tag('shared',$c->{'shared'}) : ()),
	  (defined($c->{'operationpolicy'}) ?
	   _tag('operationpolicy',$c->{'operationpolicy'}) : ()),
	  (defined($c->{'errorpolicy'}) ?
	   _tag('errorpolicy',$c->{'errorpolicy'}) : ())) : ()),
	"</queue>\n";
    
    return;
}

sub _tag {
    my ($t, $v) = @_;

    return '' if !defined($v);

    $v =~ s!\&!\&amp\;!g;
    $v =~ s!\<!\&lt\;!g;

    return "  <$t>$v</$t>\n";
}

sub dump_lpd_printcap {
    my ($config, $pcap )= @_;

    my @retval;

    my $item;
    my $backslash = "\\";
    $backslash = "" if $config->{'spooler'} eq 'lprng';
    for $item (@{$pcap}) {
	for (@{$item->{'comments'}}) {
	    push (@retval, "$_\n");
	}
	if (defined($item->{'names'})) {
	    map { $_ = '' if not defined $_; } @{$item->{'names'}};
	    push (@retval, (join('|', @{$item->{'names'}}) . ":${backslash}\n"));
	}
	for (keys(%{$item->{'str'}})) {
	    # special case of 'tc' items, as there can be more than one
	    if ($_ =~ /^tc\d+$/) {
		push (@retval, 
		      sprintf("    :tc=%s:${backslash}\n", $item->{'str'}{$_}));
	    } else {
		push (@retval, 
		      sprintf("    :$_=%s:${backslash}\n", $item->{'str'}{$_}));
	    }
	}
	for (keys(%{$item->{'bool'}})) {
	    if ($item->{'bool'}{$_}) {
		push (@retval, "    :$_:${backslash}\n");
	    }
	}
	for (keys(%{$item->{'num'}})) {
	    push (@retval, 
		  sprintf("    :$_#%s:${backslash}\n", $item->{'num'}{$_}));
	}
	if( $backslash ){
	my $lastline = pop(@retval);
		$lastline =~ s!:\\!:!;
	push (@retval, $lastline);
    }
    }
	print "PRINTCAP (spooler '" . $config->{'spooler'} . "') " . Dumper(\@retval) . "\n" if $debug;

    return @retval;
}

sub load_lpd_printcap {

    # list-o-printers, each with comments

    open PCAP, $sysdeps->{'lpd-pcap'} or die "Cannot read printcap file!\n";
    my $pcap = join('', <PCAP>);
    close PCAP;
	print "PC '$pcap'\n" if $debug;

#    die( "Cannot currently parse lprng style printcaps created by " .
#	 "lprngtool!\n" .
#	 "See the BUGS section in the manpage for details.\n")
#      if $pcap =~ m/\n\s*(:.*[^\\]\n\s*:)/m;

	# watch out for comments with \ at end of line - ignore \
    $pcap =~ s!^(\s*\#.*\\)$!${1}MEMEMEM!gm;
	# now we join lines with \ at end
    $pcap =~ s!\\\n!!gms;
	# remove \ in comment lines
    $pcap =~ s!\\MEMEMEM!\\!g;
	print "AFTER '$pcap'\n" if $debug;

    my (@comment, @items, @comments_in_pc_entry);

    my ($pline, $pcentry);
    $pcentry = "";
    for $pline (split('\n',$pcap)) {
	$pline =~ s/^\s+//;
	print "LINE '$pline', pcentry '$pcentry'\n" if $debug;
	next if $pline eq "";
	if ($pline =~ m!^\#!) {
	    if( $pcentry ){
	    	push (@comments_in_pc_entry, $pline);
	    } else {
	    push (@comment, $pline);
	    }
	} elsif ($pline =~ m!^:!) {
	    push( @comment, @comments_in_pc_entry );
	    @comments_in_pc_entry = ();
	    if( $pcentry ne "" ){
		$pcentry .= $pline;
	} else {
		die( "bad printcap entry at '$pline'" );
	    }
	} elsif( $pcentry ne "" ){
	    push (@items, { 'itemstr' => $pcentry,
			    'comments' => [ @comment ] });
	    @comment = @comments_in_pc_entry;
	    @comments_in_pc_entry = ();
	    $pcentry = $pline;
	} else {
	    $pcentry = $pline;
	}	
    }
    if( $pcentry ){
	    push( @comment, @comments_in_pc_entry );
	    @comments_in_pc_entry = ();
	    push (@items, { 'itemstr' => $pcentry,
			    'comments' => [ @comment ] });
	    @comment = ();
    }
    # Trailing comments get stuck on as empty item later...
    print "Printcap:\n" . Dumper(\@items ) if $debug;

    my $p;
    for $p (@items) {
	my $item;
	my $first = 1;
	my $tci = 0;
	for $item (split(/:\s*/, $p->{'itemstr'})) {
	    next if $item =~ m!^\s*$!;
	    if ($first) {
		my $name;
		for $name (split('\|',$item)) {
		    $name =~ s!\s*(.+)\s*!$1!;
		    push (@{$p->{'names'}}, $name);
		}
		$first = 0;
	    } else {
		if ($item =~ m!^([^=]*)=(.+)!) {
		    # special case of 'tc' items, as there can be more 
		    # than one
		    if ($1 eq 'tc') { $p->{'str'}{"tc$tci"} = $2; $tci++; }
		    else { $p->{'str'}{$1} = $2; }
		} elsif ($item =~ m!^([^\#]*)\#(.+)!) {
		    $p->{'num'}{$1} = $2;
		} elsif ($item =~ m!^([^\@]*)\@?!) {
		    $p->{'bool'}{$1} = 1;
		}
	    }
	}
    }

    # Trailing comments from way above...
    if (scalar(@comment)) {
	push (@items, {'comments' => [ @comment ]});
    }

    return \@items;
}

sub load_cups_printersconf {

    # list-o-printers
    my @items = ();
    my $itemshash = {};
	
    if ($< == 0) {
	# Get info from /etc/cups/printers.conf, works only as "root" and
	# with locally defined printers
	my @pconf = ();
	if (open PCONF, $sysdeps->{'cups-pconf'}) {
	    @pconf = <PCONF>;
	close PCONF;
	}
	
	my $line;
	my $p = {};
	my $linecount = 0;
	for $line (@pconf) {
	    $linecount ++;
	    if (!($line =~ m!^\s*\#!) && (!($line =~ m!^\s*$!))) {
		if ($line =~ m!^\s*<(.*)Printer\s+([^\s>]+)>\s*$!) {
		    # Beginning of new <Printer ...> block
		    $p->{'name'} = $2;
		    $p->{'default'} = ($1 eq "Default");
		} elsif ($line =~ m!^\s*</Printer>\s*$!) {
		    # End of <Printer ...> block
		    push (@items, $p);
		    $itemshash->{$p->{name}} = $#items;
		    $p = {};
		} elsif (defined($p->{'name'})) {
		    # Inside <Printer ...> block
		    if (($line =~ m!^\s*(\S+)\s+(\S.*)$!) and
			($1 ne '')) {$p->{$1} = $2};
		} else {
		    # Outside <Printer ...> block
		    die "Line $linecount in $sysdeps->{'cups-pconf'} " .
			"invalid!\n";
		}
	    }	
	}
    }
    if (($< != 0) || (($opt_r) && (($opt_Q) || ($opt_P)))) {
	# Get info with the "lpstat" command, works for normal users and for
	# remote printers.
	open LPSTAT, "$sysdeps->{'cups-lpstat'} -l -d -p -v |" or 
	    die "Cannot execute \"lpstat\".\n";
	my @lpstat = <LPSTAT>;
	close LPSTAT;
	
	my $line;
	my $linecount = 0;
	my $defaultprinter = '';
	my $currentitem = -1;
	for $line (@lpstat) {
	    chomp ($line);
	    $linecount ++;
	    if (!($line =~ m!^\s*$!)) {
		if ($line =~
		    m!^\s*system\s+default\s+destination:\s+(\S+)\s*$!) {
		    # Default printer
		    $defaultprinter = $1;
		} elsif ($line =~ m!^printer\s+(\S+)\s+(\S.*)$!) {
		    # Beginning of new printer's entry
		    my $name = $1;
		    my $state = $2;
		    $state =~ s/\s+-$//;
		    if (!defined($itemshash->{$name})) {
			push(@items, {});
			$itemshash->{$name} = $#items;
			# If we are root and didn't see this entry
			# in /etc/cups/printers.conf, this printer
			# is remotely defined
			if ($< == 0) {
			    $items[$itemshash->{$name}]{'remote'} = 1;
			}
		    }
		    $currentitem = $itemshash->{$name};
		    $items[$currentitem]{'name'} ||= $name;
		    $items[$currentitem]{'State'} ||= $state;
		    $items[$currentitem]{'default'} = 
			($name eq $defaultprinter);
		} elsif ($line =~ m!^\s+Description:\s+(\S.*)$!) {
		    # Description field
		    if ($currentitem != -1) {
			$items[$currentitem]{'Info'} ||= $1;
		    }
		} elsif ($line =~ m!^\s+Location:\s+(\S.*)$!) {
		    # Location field
		    if ($currentitem != -1) {
			$items[$currentitem]{'Location'} ||= $1;
		    }
		} elsif ($line =~ m!^\s+Connection:\s+remote!) {
		    # Remote printer, only keep it when the "-r" option is
		    # given
		    if (!$opt_r) {
			# "delete" does not work on arrays with Perl 5.0.x
			# Thanks to Olaf Till (i7tiol@t-online.de) who 
			# contributed this fix
			splice(@items, $currentitem, 1);
			#delete($items[$currentitem]);
			$currentitem = -1;
		    } else {
			if ($currentitem != -1) {
			    $items[$currentitem]{'remote'} = 1;
			}
		    }
		} elsif ($line =~ m!^device\s+for\s+(\S+):\s+(\S.*)$!) {
		    # "device for ..." line, extract URI
		    my $name = $1;
		    my $uri = $2;
		    if (defined($itemshash->{$name})) {
			if ($uri !~ /:/) {$uri = "file:" . $uri};
			$currentitem = $itemshash->{$name};
			if (($currentitem <= $#items) &&
			    ($items[$currentitem]{'name'} eq $name)) {
			    $items[$currentitem]{'DeviceURI'} ||= $uri;
			}
		    }
		}
	    }
	}
    }

    return \@items;
}

sub dump_pdq_printrc {
    my $printrc = $_[0];

    my @retval;

    my $item;
    for $item (@{$printrc}) {
	if (defined($item->{'name'})) {
	    # $item is a "printer" block
	    push (@retval, "printer \"$item->{'name'}\" \{\n");
	    for my $key (keys(%{$item})) {
		if (($key ne 'name') && ($key ne 'others')) {
		    push (@retval, "\t$key $item->{$key}\n");
		}
	    }
	    push (@retval, "\}\n");
	} elsif (defined($item->{'others'})) {
	    # $item is not a "printer" block
	    push (@retval, $item->{'others'});
	}
    }

    # Check whether there is a already a 'try_include "/etc/foomatic/pdq/*"'
    # line in the config file
    if (!(join("", @retval) =~
	  m!^\s*try_include\s*\"$sysdeps->{'foo-etc'}/pdq/driverdescr/\*\"\s*$!m)) {
	splice(@retval,0,0,"# Line inserted by $progname\ntry_include " .
	       "\"$sysdeps->{'foo-etc'}/pdq/driverdescr/*\"\n\n");
    }

    # De-activate old line from Foomatic 2.0.x
    ($_ =~ s!^\s*try_include\s*\"$sysdeps->{'foo-etc'}/pdq/\*\"\s*$!\#$&!m)
	foreach @retval;

    return @retval;
}

sub load_pdq_printrc {

    # list-o-printers, with storage of non-printer-specific lines

    open PRINTRC, $sysdeps->{'pdq-printrc'} or 
	die "Cannot read printrc file!\n";
    my @printrc = <PRINTRC>;
    close PRINTRC;

    my @items;
    my @others;
    my $line;
    my $p;
    my $linecount = 0;
    my $inprinterblock = 0;
    my $nonprinterlines = 0;
    for $line (@printrc) {
	$linecount ++;
	if ($line =~ m!^\s*printer\s+\"(.+)\"\s*{\s*$!) {
	    if ($inprinterblock == 1) {
		die "New printer block started without previous one " .
		    "being closed!\nLine $linecount in " .
		    "$sysdeps->{'pdq-printrc'}.\n";
	    }
	    # Beginning of new "printer" block
	    # Store all non-printer-block stuff at first
	    if ($nonprinterlines == 1) {
		push (@items, {'others' => join ("", @others )});
		$nonprinterlines = 0;
		@others = ();
	    }
	    # Read printer block name
	    $inprinterblock = 1;
	    $p->{'name'} = $1;
	} elsif ($inprinterblock == 1) {
	    # Inside "printer" block
	    if ($line =~ m!^\s*}\s*$!) {
		# End of "printer" block
		$inprinterblock = 0;
		push (@items, $p);
		$p = {};
	    } elsif ($line =~ m!^\s*(\S+)\s*(\S+.*)$!) {
		$p->{$1} = $2;
	    } elsif ((!($line =~ m!^\s*\#!)) && 
		     (!($line =~ m!^\s*$!))) {
		die "Line $linecount in $sysdeps->{'pdq-printrc'} " .
		    "invalid!\n";
	    }
	} else {
	    # Outside "printer" block
	    push(@others, $line);
	    $nonprinterlines = 1;
	}
    }
    # Trailing non-printer lines get stuck on as empty item
    if ($nonprinterlines == 1) {
	my $lines = join ("", @others);
	# Make sure that the last line line ends with a newline character
	if (!($lines =~ m!\n$!s)) {$lines .= "\n";}
	push (@items, {'others' => $lines});
    }

    return \@items;
}

sub load_ppr_printers_conf {

    # Check whether there is a group named "default" to see what is the
    # default printer.
    
    my $defaultfromgroup = "  ";
    if (open SHOWDEFAULTGROUP,
	"$sysdeps->{'ppr-ppad'} group show default 2>/dev/null |"){
	for my $line (<SHOWDEFAULTGROUP>) {
	    chomp $line;
	    if ($line =~ /\s*Members:\s*([^\s,]+)\s*$/) {
		$defaultfromgroup = $1;
		last;
	    }
	}
	close SHOWDEFAULTGROUP;
    }

    # list-o-printers
    my @items = ();
    my $itemshash = {};
	
    if ($< == 0) {
	# Get info from /etc/ppr/printers/<queue name>, works only as
	# "root"
	opendir PCONFDIR, "$sysdeps->{'ppr-etc'}/printers" or
	    die "Cannot read $sysdeps->{'ppr-etc'}/printers directory!\n";
	my $name;
	while ($name = readdir(PCONFDIR)) {
	    # Do not consider "." and ".." as a printer queue
	    next if ($name =~ /^\./);
	    my $line;
	    my $p = {};
	    $p->{'name'} = $name;
	    $p->{'default'} = (($name eq "default") ||
			       ($name eq $defaultfromgroup));
	    @{$p->{'Bins'}} = ();
	    my $linecount = 0;
	    open PCONFFILE, "$sysdeps->{'ppr-etc'}/printers/$name" or
		die "Cannot read $sysdeps->{'ppr-etc'}/printers/$name!\n";
	    for my $line (<PCONFFILE>) {
		chomp $line;
		$linecount ++;
		if (!($line =~ m!^\s*\#!) && (!($line =~ m!^\s*$!))) {
		    if (($line =~ m!^\s*([^\s:]+)\s*:\s*(\S.*)$!) ||
			($line =~ m!^\s*([^\s:]+)\s*:\s*()$!)) {
			# <keyword>: <value1> <value2> ...
			my $keyword = $1;
			my $values = $2;
			if (($values) && ($values ne "")) {
			    # If the value is enclosed in double quotes,
			    # remove the quotes
			    $values =~ s/^\"(.*)\"$/$1/;
			    if ($keyword eq "Bin") {
				push (@{$p->{'Bins'}}, $values);
			    } else {
				$p->{$keyword} = $values;
			    }
			}
		    } else {
			warn "Line $linecount in " .
			    "$sysdeps->{'ppr-etc'}/printers/$name " .
			    "corrupted:\n    $line\n";
		    }
		}
	    }
	    close PCONFFILE;
	    push (@items, $p);
	    $itemshash->{$p->{'name'}} = $#items;
	}
    }
    if ($< != 0) {
	# Get info with the "ppop"/"ppad" commands, works for normal users,
	# but needs installed and running PPR printing system
	open PPOP_DEST, "$sysdeps->{'ppr-ppop'} destination all |" or 
	    die "Cannot execute \"ppop\".\n";
	my @ppop_dest = <PPOP_DEST>;
	close PPOP_DEST;
	
	my $line;
	my $linecount = 0;
	my $currentitem = -1;
	for $line (@ppop_dest) {
	    chomp ($line);
	    $linecount ++;
	    if (($line !~ m!^\s*-+\s*$!) && 
		($line !~ m!^\s*Destination\s+Type\s+Status\s+Charge\s*$!)){
		if ($line =~ m!^\s*(\S+)\s+printer!) {
		    my $name = $1;
		    open PPAD_SHOW,"$sysdeps->{'ppr-ppad'} show $name |" or 
			die "Cannot execute \"ppad\".\n";
		    my $lcount = 0;
		    if (!defined($itemshash->{$name})) {
			push(@items, {});
			$itemshash->{$name} = $#items;
			#print Dumper($itemshash);
		    }
		    $currentitem = $itemshash->{$name};
		    $items[$currentitem]{'name'} ||= $name;
		    $items[$currentitem]{'default'} = 
			(($name eq "default") ||
			 ($name eq $defaultfromgroup));
		    for my $line (<PPAD_SHOW>) {
			chomp $line;
			$lcount ++;
			if ((!($line =~ m!^\s*\#!)) && 
			    (!($line =~ m!^\s*$!))) {
			    if ($line =~ 
				m!^\s*([^\s:][^:]*)\s*:\s*(.*)$!) {
				# <keyword>: <value1> <value2> ...
				my $keyword = $1;
				my $values = $2;
				if (($values) && ($values ne "")) {
				    # If the value is enclosed in double 
				    # quotes, remove the quotes
				    $values =~ s/^\"(.*)\"$/$1/;
				    if ($keyword eq "Bins") {
					@{$items[$currentitem]{'Bins'}} = 
					    split(", ", $values);
				    } else {
					if ($keyword eq "Switchset") {
					    $values =~ s/ -(\S) /\|$1/g;
					    $values =~ s/-(\S) /$1/g;
					    $values =~ s/\'//g;
					    $values =~ s/^|//g;
					}
					$items[$currentitem]{$keyword} = 
					    $values;
				    }
				}
			    } else {
				warn "Line $lcount in \"ppad show " .
				    "$name\" corrupted:\n    $line\n";
			    }
			}
		    }
		    close PPAD_SHOW;
		}
	    }
	}
    }

    return \@items;
}

sub dump_direct_config {
    my $config = $_[0];

    my @retval;

    my $defaultprinter = undef;
    my $item;
    for $item (@{$config}) {
	if (defined($item->{'name'})) {
	    if (defined($item->{'desc'})) {
		push (@retval, "$item->{'name'} desc:$item->{'desc'}\n");
	    }
	    if (defined($item->{'loc'})) {
		push (@retval, "$item->{'name'} loc:$item->{'loc'}\n");
	    }
	    if ($item->{'default'}) {
		$defaultprinter = $item->{'name'};
	    }
	}
    }
    if (defined($defaultprinter)) {
	unshift(@retval, "default: $defaultprinter\n");
    }
    
    return @retval;
}

sub load_direct_config {

    # list-o-printers
    my @items = ();
    my $itemshash = {};
	
    # Configured printers are represented by PPD files in /etc/foomatic/
    opendir PCONFDIR, "$sysdeps->{'foo-etc'}/direct" or
	die "Cannot read $sysdeps->{'foo-etc'}/direct directory!\n";
	my $name;
    while ($name = readdir(PCONFDIR)) {
	# Files beginning with a dot or ending with a tilde are never
	# printers
	next if (($name =~ /^\./) || ($name =~ /~$/));
	# Only ".ppd" files are printer descriptions.
	next unless ($name =~ /\.ppd$/i);
	$name =~ s/\.ppd$//i;
	# Do not make two entries when there is both a ".ppd" AND ".PPD"
	# file for the same printer name.
	next if (defined($itemshash->{$name}));
	my $p = {};
	$p->{'name'} = $name;
	push (@items, $p);
	$itemshash->{$p->{'name'}} = $#items;
    }

    # Get additional info from /etc/foomatic/direct/.config (default
    # printer, description, location
    if (open CONFIG, "< $sysdeps->{'direct-config'}") {
	while (my $line = <CONFIG>) {
	    chomp $line;
	    if ($line =~ /^default\s*:\s*([^:\s]+)\s*$/) {
		my $currentitem = $itemshash->{$1};
		$items[$currentitem]{'default'} = 1;
	    } elsif ($line =~ /^\s*([^:\s]+)\s+([^:\s]+)\s*:(.*)$/) {
		my $currentitem = $itemshash->{$1};
		$items[$currentitem]{$2} = $3;
	    }
	}
	close CONFIG;
    }

    return \@items;
}

sub cups_correct_ptal_uri {

    # HPOJ 0.9 uses "ptal:..." URIs with one slash
    # ("ptal:/mlc:usb:dj450") and the current CVS of HPOJ uses two
    # slashes ("ptal://mlc:usb:dj450"). Correct the user-supplied URI
    # according to what "lpinfo -v" reports.

    my ($uri) = @_;
    $uri =~ m!^ptal://?([^/].*)$!;
    my $device = $1;

    # PTAL URIs listed by "lpinfo -v"
    open F, "$sysdeps->{'cups-lpinfo'} -v |" or return (@_);
    while (my $line = <F>) {
	chomp($line);
	my $d = quotemeta($device);
	if ($line =~ m!(ptal://?$d)$!) {
	    my $realdevice = $1;
	    close F;
	    return $realdevice;
	}
    }
    close F;

    # Nothing found, do not correct the input
    return @_;
}

sub cups_generate_usb_device_lists {
    # Generate two lists: One of the actual USB device files in the
    # file system, another of the USB URIs listed by CUPS' "lpinfo -v"

    # Actual devices
    my @usbdevices;
    for my $pattern ("/dev/usb/lp*", "/dev/usb/usblp*") {
	open F, "ls -1 $pattern 2>/dev/null |" or next;
	@usbdevices = sort { Foomatic::DB::normalizename($a) cmp 
			     Foomatic::DB::normalizename($b) } 
	              grep { chomp } <F>;
	close F;
	last if $#usbdevices >= 0;
    }
    return ([], []) if $#usbdevices < 0;

    # USB URIs listed by "lpinfo -v"
    open F, "$sysdeps->{'cups-lpinfo'} -v |" or return ([], []);
    my @usburis = grep { s!^direct usb:!! and chomp } <F>;
    close F;

    return ([], []) if $#usburis < 0;

    # Results
    return (\@usbdevices, \@usburis);
}

sub cups_usb_device_uri_to_printer_uri {

    # Transfer a device file name into a printer-bound CUPS URI for
    # the printer currently connected
    my ($device) = @_;
    return $device if $device =~ m!^//!;
    my @devicelists = cups_generate_usb_device_lists();
    return $device if (($#{$devicelists[0]} < 0) ||
		       ($#{$devicelists[1]} < 0));
    for (my $i = 0; $i <= $#{$devicelists[0]}; $i ++) {
	last if !$devicelists[1][$i];
	if ($device eq $devicelists[0][$i]) {
	    return $devicelists[1][$i];
	}
    }
    return $device;
}

sub cups_usb_printer_uri_to_device_uri {

    # Transfer a device file name into a printer-bound CUPS URI for
    # the printer currently connected
    my ($device) = @_;
    return $device if $device =~ m!^/[^/]!;
    $device =~ s/ /\%20/g;
    my @devicelists = cups_generate_usb_device_lists();
    return $device if (($#{$devicelists[0]} < 0) ||
		       ($#{$devicelists[1]} < 0));
    for (my $i = 0; $i <= $#{$devicelists[1]}; $i ++) {
	last if !$devicelists[0][$i];
	if ($device eq $devicelists[1][$i]) {
	    return $devicelists[0][$i];
	}
    }
    return $device;
}

sub load_datablob {

    my ($spooler, $queue) = @_;

    my $spoolersubdir;
    my $datablob;
    if (($spooler eq "lpd") ||
	($spooler eq "lprng")) {
	$datablob = load_lpd_datablob($queue);
	$spoolersubdir = 'lpd';
    } elsif ($spooler eq "cups") {
	$datablob = load_cups_datablob($queue);
	$spoolersubdir = 'cups';
    } elsif ($spooler eq "pdq") {
	$datablob = load_pdq_datablob($queue);
	$spoolersubdir = 'pdq';
    } elsif ($spooler eq "ppr") {
	$datablob = load_ppr_datablob($queue);
	$spoolersubdir = 'ppr';
    } elsif ($spooler eq "direct") {
	$datablob = load_direct_datablob($queue);
	$spoolersubdir = 'direct';
    } else {
	die "Unsupported spooler: $spooler\n";
    }
    # Is the given queue a valid queue?
    if (!$datablob) {
	return undef;
    }
    return ($datablob);
}

sub load_lpd_datablob {
    my ($queue) = $_[0];
    # Load the PPD file
    my $ppdfile = sprintf('%s/lpd/%s.ppd',
			  $sysdeps->{'foo-etc'},
			  $queue);
    my $dat = ppdtoperl($ppdfile);
    if (defined($dat)) {
	$dat->{'ppdfile'} = $ppdfile;
    }
    my $postpipe = (defined($dat) ? $dat->{'postpipe'} : "");
    # Get additional info from /etc/printcap
    my $pcap = load_lpd_printcap();
    my $p;
    for $p (@{$pcap}) {
	# enpty end entry for trailing comments
	next if !defined($p->{'names'});
	# Search for the correct queue
	next if ($queue ne $p->{'names'}[0]);
	# Collect values
	my $c = {};
	my $name = $c->{'queue'} = $p->{'names'}[0];
	$c->{'desc'} = $p->{'names'}[1] if $p->{'names'}[1];
	$c->{'loc'} = $p->{'names'}[3] if $p->{'names'}[3];
	$c->{'foomatic'} = 0;
	my $if = ($p->{'str'}{'if'} || "");
	if ($if =~ m!foomatic-rip$!) {
	    $c->{'foomatic'} = 1;
	    $c->{'printer'} = $dat->{'id'};
	    $c->{'driver'} = $dat->{'driver'};
	}
	if (!$p->{'bool'}{'force_localhost'}) {
	    # LPD
	    $c->{'spooler'} = 'lpd';
	} else {
	    # LPRng
	    $c->{'spooler'} = 'lprng';
	}
	# TODO Raw queue for LPD
#	if (0 and $p->{'str'}{'if'} eq $file) {  # Raw queue with $postpipe
#	    if (open FILE, "$file") {
#		# The first line is #!/bin/sh
#		$line = <FILE>;
#		# The second line is a comment
#		$line = <FILE>;
#		# The remaining line(s) are the $postpipe
#		$line = join('', <FILE>);
#		chomp $line;
#		$postpipe = "| $line";
#		close FILE;
#	    }
#	}
	if (defined($postpipe)) {
	    if ($postpipe =~ 
		m!^\s*\|\s*($sysdeps->{'cat'}|cat)\s+-?\s*>\s*([^\s]+)\s*$!) {
		my $file = $2;
		if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) ||
		    ($file =~ m!^/dev/ptal-printd/(.+)$!) ||
		    ($file =~ m!^/var/run/ptal-printd/(.+)$!)) {
		    # Translate device for ptal-printd to ptal URI
		    my $devname = $1;
		    $devname =~ s/_/:/;
		    $devname =~ s/_/:/;
		    $c->{'connect'} = "ptal:/$devname";
		} elsif (($file =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) ||
			 ($file =~ m!^/var/mtink/(.+)$!)) {
		    # Translate device for mtinkd to mtink URI
		    $c->{'connect'} = "mtink:/$1";
		} elsif ($file =~ m!usb!i) {
		    $c->{'connect'} = "usb:$file";
		} elsif ($file =~ m!(tty|serial)!i) {
		    $c->{'connect'} = "serial:$file";
		} elsif ($file =~ m!(lp[0-9]|parallel)!i) {
		    $c->{'connect'} = "parallel:$file";
		} else {
		    $c->{'connect'} = "file:$file";
		}
	    } elsif ($postpipe =~ 
		m!^\s*\|\s*($sysdeps->{'ptal-connect'}|ptal-connect|ptal-print)\s+(-print\s+|)([^\s]+)(\s+-print|)\s*$!){
		$c->{'connect'} = "ptal:/$3";
	    } elsif ($postpipe =~ 
		m!^\s*\|\s*($sysdeps->{'nc'}|netcat|nc)\s+(-w\s*1\s+|)([^\s]+)\s+([^\s]+)\s*$!){
		$c->{'connect'} = "socket://$3:$4";
	    } elsif ($postpipe =~ 
		     m!^\s*\|\s*$sysdeps->{'rlpr'}\s.*-P\s*([^\s\\\@]+)\@([^\s\\\@]+)\s*$!) {
		$c->{'connect'} = "lpd://$2/$1";
	    } elsif ($postpipe =~ 
		     m!^.*\|\s*$sysdeps->{'smbclient'}\s+\"//([^/\s]+)/([^/\s]+)\"\s+(\S.*)$!s) {
		my $servershare = "$1/$2";
		my $parameters = $3;
		my $password = "";
		if ($parameters =~ m!^([^-]\S*)\s+(\S.*)$!) {
		    $password = $1;
		    $parameters = $2;
		}
		my $username = "";
		if ($parameters =~ m!^-U\s+(\S*)\s+(\S.*)$!) {
		    $username = $1;
		    $parameters = $2;
		}
		my $workgroup = "";
		if ($parameters =~ m!^-W\s+(\S*)\s+(\S.*)$!) {
		    $workgroup = "$1/";
		}
		my $identity = "";
		if (($username eq "GUEST") && ($password eq "")) {
		    $identity = "";
		} elsif (($username eq "") && ($password eq "")) {
		    $identity = "";
		} elsif (($username ne "") && ($password eq "")) {
		    $identity = "$username\@";
		} elsif (($username eq "") && ($password ne "")) {
		    $identity = ":$password\@";
		} else {
		    $identity = "$username:$password\@";
		}
		$c->{'connect'} = "smb://$identity$workgroup$servershare";
	    } elsif ($postpipe =~ 
		     m!^\s*\|\s*$sysdeps->{'nprint'}\s+(\S.*)$!s) {
		my $parameters = $1;
		my $server = "";
		if ($parameters =~ m!^-S\s+(\S*)\s+(\S.*)$!) {
		    $server = $1;
		    $parameters = $2;
		}
		my $username = "";
		if ($parameters =~ m!^-U\s+(\S*)\s+(\S.*)$!) {
		    $username = $1;
		    $parameters = $2;
		}
		my $password = "";
		if ($parameters =~ m!^-P\s+(\S*)\s+(\S.*)$!) {
		    $password = $1;
		    $parameters = $2;
		}
		if ($parameters =~ m!^-n\s+(\S.*)$!) {
		    $parameters = $1;
		}
		my $queue = "";
		if ($parameters =~ m!^-q\s+(\S*)\s+(\S.*)$!) {
		    $queue = $1;
		}
		my $identity = "";
		if (($username eq "") && ($password eq "")) {
		    $identity = "";
		} elsif (($username ne "") && ($password eq "")) {
		    $identity = "$username\@";
		} elsif (($username eq "") && ($password ne "")) {
		    $identity = ":$password\@";
		} else {
		    $identity = "$username:$password\@";
		}
		$c->{'connect'} = "ncp://$identity$server/$queue";
	    } elsif( $postpipe ){
		$postpipe =~ m!\s*\|\s*(\S.*)$!;
		$c->{'connect'} = "postpipe:\"$1\"";
	    }
	} else {
	    my $lp = $p->{'str'}{'lp'};
	    if (defined($lp) and $lp and $lp ne '/dev/null') {
		if (($lp =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) ||
		    ($lp =~ m!^/dev/ptal-printd/(.+)$!) ||
		    ($lp =~ m!^/var/run/ptal-printd/(.+)$!)) {
		    # Translate device for ptal-printd to ptal URI
		    my $devname = $1;
		    $devname =~ s/_/:/;
		    $devname =~ s/_/:/;
		    $c->{'connect'} = "ptal:/$devname";
		} elsif (($lp =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) ||
			 ($lp =~ m!^/var/mtink/(.+)$!)) {
		    # Translate device for mtinkd to mtink URI
		    $c->{'connect'} = "mtink:/$1";
		} elsif ($lp =~ m!^\w+:!i) {
		    $c->{'connect'} = $lp;
		} else {
		    $c->{'connect'} = "file:$lp";
		}
	    }
	    my ($rm, $rp) = ($p->{'str'}{'rm'}, $p->{'str'}{'rp'});
	    if (defined($rm) and defined($rp)) {
		$c->{'connect'} = "lpd://$rm/$rp";
	    }
	}
	$dat->{'queuedata'} = $c;
    }
    if (!defined($dat->{'queuedata'})) {$dat = undef};
    return $dat;
}

sub load_cups_datablob {
    my ($queue) = $_[0];
    # Load the PPD file
    my $ppdfile = sprintf('%s/ppd/%s.ppd',
		       $sysdeps->{'cups-etc'},
		       $queue);
    #my $ppdfile = sprintf('%s/%s.ppd',
    #			  $sysdeps->{'foo-etc'},
    #			  $queue);
    my $dat = ppdtoperl($ppdfile);
    if (defined($dat)) {
	$dat->{'ppdfile'} = $ppdfile;
    }
    # Get additional info from /etc/cups/printers.conf
    my $pconf = load_cups_printersconf();
    my $p;
    for $p (@{$pconf}) {
	
	# were we invoked for only one queue?
	next if ($queue ne $p->{'name'});

	# Collect values
	my $c = {};
	$c->{'spooler'} = 'cups';
	$c->{'queue'} = $p->{'name'};
	$c->{'foomatic'} = 0;
	if (defined($dat->{'id'}) and defined($dat->{'driver'})) {
	    $c->{'foomatic'} = 1;
	    $c->{'printer'} = $dat->{'id'};
	    $c->{'driver'} = $dat->{'driver'};
	}
	$c->{'desc'} = $p->{'Info'};
	$c->{'loc'} = $p->{'Location'};
	my $uri = $p->{'DeviceURI'};
	# Is the beh (Backend Error Handler) wrapper backend in use?
	# If yes, read out its parameters and isolate the original URI.
	if ($uri =~ m!^beh:/(\d+)/(\d+)/(\d+)/(\S+)$!) {
	    $c->{'dd'} = $1;
	    $c->{'att'} = $2;
	    $c->{'delay'} = $3;
	    $uri = $4;
	} else {
	    $c->{'dd'} = 0;
	    $c->{'att'} = 1;
	    $c->{'delay'} = 30;
	}
	if (($uri =~ m!^file:$sysdeps->{'ptal-pipes'}/(.+)$!) ||
	    ($uri =~ m!^file:/dev/ptal-printd/(.+)$!) ||
	    ($uri =~ m!^file:/var/run/ptal-printd/(.+)$!)) {
	    # Translate URI for ptal-printd to ptal URI
	    my $devname = $1;
	    $devname =~ s/_/:/;
	    $devname =~ s/_/:/;
	    $uri = "ptal:/$devname";
	} elsif (($uri =~ m!^file:$sysdeps->{'mtink-pipes'}/(.+)$!) ||
		 ($uri =~ m!^file:/var/mtink/(.+)$!)) {
	    # Translate URI for mtinkd to mtink URI
	    $uri = "mtink:/$1";
	}
	$c->{'connect'} = $uri;
	# CUPS-specific extra info
	$c->{'quotaperiod'} = $p->{'QuotaPeriod'}
	    if defined($p->{'QuotaPeriod'});
	$c->{'pagelimit'} = $p->{'PageLimit'}
	    if defined($p->{'PageLimit'});
	$c->{'klimit'} = $p->{'KLimit'}
	    if defined($p->{'KLimit'});
	# CUPS 1.2-specific settings
	$c->{'laststatechange'} = $p->{'StateTime'}
	    if defined($p->{'StateTime'});
	$c->{'shared'} = $p->{'Shared'}
	    if defined($p->{'Shared'});
	$c->{'operationpolicy'} = $p->{'OpPolicy'}
	    if defined($p->{'OpPolicy'});
	$c->{'errorpolicy'} = $p->{'ErrorPolicy'}
	    if defined($p->{'ErrorPolicy'});
	$dat->{'queuedata'} = $c;
    }
    if (!defined($dat->{'queuedata'})) {$dat = undef};
    return $dat;
}

sub load_pdq_datablob {
    my ($queue) = $_[0];
    # Load the PPD file
    my $ppdfile = sprintf('%s/pdq/%s.ppd',
			  $sysdeps->{'foo-etc'},
			  $queue);
    my $dat = ppdtoperl($ppdfile);
    if (defined($dat)) {
	$dat->{'ppdfile'} = $ppdfile;
    }
    if (defined($dat)) {
	my $printrc = load_pdq_printrc();
	my $p;
	my $pdqopts;
	my $pdqargs;
	for $p (@{$printrc}) {
	    # Omit non-printer-block items
	    next if (!(defined($p->{'name'})));
	    # Search the current queue
	    next if ($queue ne $p->{'name'});
	    $pdqopts = $p->{'driver_opts'};
	    $pdqargs = $p->{'driver_args'};
	}
	my @printrcdefaults = split(",", $pdqopts);
	push (@printrcdefaults, split(",", $pdqargs));
	
	my $c;
	@{$c->{'options'}} = ();
	for my $option (@printrcdefaults) {
	    if ($option =~
		m!^\s*\{?\s*\"(OPT_|)(.+?)\"\s*=\s*\"(.*)\"\s*\}?\s*$!) {
		push (@{$c->{'options'}}, "$2=$3");
	    } elsif ($option =~
		     m!^\s*\{?\s*\"(OPT_|)([^_]+?)_(.+?)\"\s*\}?\s*$!) {
		push (@{$c->{'options'}}, "$2=$3");
	    } elsif ($option =~ m!^\s*\{?\s*\"(OPT_|)(.+?)\"\s*\}?\s*$!) {
		push (@{$c->{'options'}}, "$2");
	    }
	}
	set_default_options($c, $dat);
    }
    # Get additional info from printrc
    my $printrc = load_pdq_printrc();
    my $p;
    for $p (@{$printrc}) {
	# Omit non-printer-block items
	next if (!(defined($p->{'name'})));
	# Search for the appropriate queue
	next if ($queue ne $p->{'name'});
	my $c = {};
	$c->{'spooler'} = 'pdq';
	$c->{'queue'} = $p->{'name'};
	$c->{'foomatic'} = 0;
	if (defined($dat->{'id'}) and defined($dat->{'driver'})) {
	    $c->{'foomatic'} = 1;
	    $c->{'printer'} = $dat->{'id'};
	    $c->{'driver'} = $dat->{'driver'};
	}
	if (defined($p->{'model'})) {
	    my $desc = $p->{'model'};
	    $desc =~ s!^\"!!;
	    $desc =~ s!\"$!!;
	    if ($desc ne '') {$c->{'desc'} = $desc;}
	}
	if (defined($p->{'location'})) {
	    my $loc = $p->{'location'};
	    $loc =~ s!^\"!!;
	    $loc =~ s!\"$!!;
	    if ($loc ne '') {$c->{'loc'} = $loc;}
	}
	if ($p->{'interface'} =~ m!local-port!) {
	    # Local printer
	    $p->{'interface_args'} =~ m!\"?PORT\"?\s*=\s*\"?([^\"\s]+)\"?!;
	    my $file = $1;
	    if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) ||
		($file =~ m!^/dev/ptal-printd/(.+)$!) ||
		($file =~ m!^/var/run/ptal-printd/(.+)$!)) {
		# Translate device for ptal-printd to ptal URI
		my $devname = $1;
		$devname =~ s/_/:/;
		$devname =~ s/_/:/;
		$c->{'connect'} = "ptal:/$devname";
	    } elsif (($file =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) ||
		     ($file =~ m!^/var/mtink/(.+)$!)) {
		# Translate device for mtinkd to mtink URI
		$c->{'connect'} = "mtink:/$1";
	    } elsif ($file =~ m!usb!i) {
		$c->{'connect'} = "usb:$file";
	    } elsif ($file =~ m!(tty|serial)!i) {
		$c->{'connect'} = "serial:$file";
	    } elsif ($file =~ m!(lp[0-9]|parallel)!i) {
		$c->{'connect'} = "parallel:$file";
	    } else {
		$c->{'connect'} = "file:$file";
	    }
	} elsif ($p->{'interface'} =~ m!bsd-lpd!) {
	    # Remote LPD
	    $p->{'interface_args'} =~
		m!\"?REMOTE_HOST\"?\s*=\s*\"?([^\"\s]+)\"?!;
	    my $remhost = $1;
	    $p->{'interface_args'} =~
		m!\"?QUEUE\"?\s*=\s*\"?([^\"\s]+)\"?!;
	    my $remqueue = $1;
	    $c->{'connect'} = "lpd://$remhost/$remqueue";
	} elsif ($p->{'interface'} =~ m!tcp-port!) {
	    # Socket
	    $p->{'interface_args'} =~
		m!\"?REMOTE_HOST\"?\s*=\s*\"?([^\"\s]+)\"?!;
	    my $remhost = $1;
	    $p->{'interface_args'} =~
		m!\"?REMOTE_PORT\"?\s*=\s*\"?([^\"\s]+)\"?!;
	    my $remport = $1;
	    $c->{'connect'} = "socket://$remhost:$remport";
	}
	$dat->{'queuedata'} = $c;
    }
    if (!defined($dat->{'queuedata'})) {$dat = undef};
    return $dat;
}

sub load_ppr_datablob {
    my ($queue) = $_[0];
    # Load the PPD file
    my $ppdfile = sprintf('%s/ppr/%s.ppd',
			  $sysdeps->{'foo-etc'},
			  $queue);
    my $dat = ppdtoperl($ppdfile);
    if (defined($dat)) {
	$dat->{'ppdfile'} = $ppdfile;
    }
    # Get additional info from /etc/ppr/*
    my $pconf = load_ppr_printers_conf();
    my $p;
    for $p (@{$pconf}) {

	# were we invoked for only one queue?
	next if ($queue ne $p->{'name'});

	# Collect values
	my $c = {};
	$c->{'spooler'} = 'ppr';
	$c->{'queue'} = $p->{'name'};
	$c->{'foomatic'} = 0;
	if (defined($dat->{'id'}) and defined($dat->{'driver'})) {
	    $c->{'foomatic'} = 1;
	    $c->{'printer'} = $dat->{'id'};
	    $c->{'driver'} = $dat->{'driver'};
	}
	$c->{'desc'} = $p->{'Comment'};
	$c->{'loc'} = $p->{'Location'};
	if (defined($dat)) {
	    my @printerdefaults = split('|', $p->{'Switchset'});
	    my $o;
	    @{$o->{'options'}} = ();
	    for my $option (@printerdefaults) {
		if (($option =~ 
		     /^F\s*\*([^\*\s=:]+)\s+([^\*\s=:]+)\s*$/) ||
		    ($option =~ 
		     /^F\s*([^\*\s=:]+)\s*=\s*([^\*\s=:]+)\s*$/)) {
		    push (@{$o->{'options'}}, "$1=$2");
		} elsif (($option =~ /^F\s*\*([^\*\s=:]+)\s*$/) ||
			 ($option =~ /^F\s*([^\*\s=:]+)\s*$/)) {	
		    push (@{$o->{'options'}}, "$1");
		}
	    }
	    set_default_options($o, $dat);
	}
	my $address = $p->{'Address'};
	my $interface = $p->{'Interface'};
	my $interface_options = $p->{'Options'};
	if (($interface eq "foomatic-rip") ||
	    ($interface eq "ppromatic")) {
	    if ($interface_options =~ /backend=(\S+)/) {
		$interface = $1;
		$interface_options =~ s/backend=(\S+)//;
		if ($interface_options =~ /^\s*$/) {
		    $interface_options = "";
		}
	    } else {
		$interface = "";
	    }
	}
	my $uri = "";
	if (($interface eq "simple") || ($interface eq "parallel") ||
	    ($interface eq "serial") || ($interface eq "dummy")) {
	    # local printer
	    if (($address =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) ||
		($address =~ m!^/dev/ptal-printd/(.+)$!) ||
		($address =~ m!^/var/run/ptal-printd/(.+)$!)) {
		# Translate device for ptal-printd to ptal URI
		my $devname = $1;
		$devname =~ s/_/:/;
		$devname =~ s/_/:/;
		$uri = "ptal:/$devname";
	    } elsif (($address =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) ||
		     ($address =~ m!^/var/mtink/(.+)$!)) {
		# Translate device for mtinkd to mtink URI
		$uri = "mtink:/$1";
	    } elsif ($address =~ m!^\w+:!i) {
		$c->{'connect'} = $address;
	    } else {
		$uri = "file:$address";
	    }
	} elsif ($interface eq "lpr") {
	    # Remote LPD
	    if ($address =~ /^([^\@]+)\@([^\@]+)$/) {
		my $remhost = $2;
		my $remqueue = $1;
		$uri = "lpd://$remhost/$remqueue";
	    } else {
		die "Remote LPD configuration of the queue $p->{'name'} " .
		    "broken!\n";
	    }
	} elsif ($interface eq "tcpip") {
	    # Socket (AppSocket/HP JetDirect)
	    $uri = "socket://$address";
	} elsif ($interface eq "smb") {
	    # SMB (Printer on Windows server)
	    if ($address =~ m!^//([^/]+)/([^/]+)$!) {
		my $smbserver = $1;
		my $smbshare = $2;
		my $smbuser = "";
		if ($interface_options =~ /smbuser=(\S+)/) {
		    $smbuser = $1;
		} else {
		    # The PPR interface for SMB uses the user name "ppr"
		    # when no user name is given.
		    $smbuser = "ppr";
		}
		my $smbpassword = "";
		if ($interface_options =~ /smbpassword=(\S+)/) {
		    $smbpassword = $1;
		}
		if (($smbpassword ne "") && ($smbuser eq "")) {
		    $smbuser = "GUEST";
		}
		$uri = "$smbserver/$smbshare";
		if ($smbuser ne "") {
		    if ($smbpassword ne "") {
			$smbuser .= ":$smbpassword";
		    }
		    $uri = "$smbuser\@$uri";
		}
		$uri = "smb://$uri";
	    } else {
		die "SMB configuration of the queue $p->{'name'} broken!\n";
	    }
	} else {
	    # Interface not supported by Foomatic
	    $uri = "$interface:$address";
	}
	$c->{'connect'} = $uri;
	$dat->{'queuedata'} = $c;
    }
    if (!defined($dat->{'queuedata'})) {$dat = undef};
    return $dat;
}

sub load_direct_datablob {
    my ($queue) = $_[0];
    # Load the PPD file
    my $ppdfile = sprintf('%s/direct/%s.ppd',
			  $sysdeps->{'foo-etc'},
			  $queue);
    my $dat = ppdtoperl($ppdfile);
    if (defined($dat)) {
	$dat->{'ppdfile'} = $ppdfile;
    }
    my $postpipe = (defined($dat) ? $dat->{'postpipe'} : "");
    # Get additional info from /etc/foomatic/direct/.config
    my $config = load_direct_config();
    my $p;
    for $p (@{$config}) {
	# invalid entry
	next if !defined($p->{'name'});
	# Search for the correct queue
	next if ($queue ne $p->{'name'});
	# Collect values
	my $c = {};
	my $name = $c->{'queue'} = $p->{'name'};
	$c->{'desc'} = $p->{'desc'};
	$c->{'loc'} = $p->{'loc'};
	$c->{'foomatic'} = 0;
	if (defined($dat->{'id'}) and defined($dat->{'driver'})) {
	    $c->{'foomatic'} = 1;
	    $c->{'printer'} = $dat->{'id'};
	    $c->{'driver'} = $dat->{'driver'};
	}
	$c->{'spooler'} = 'direct';
	if (defined($postpipe)) {
	    if ($postpipe =~ 
		m!^\s*\|\s*($sysdeps->{'cat'}|cat)\s+-?\s*>\s*([^\s]+)\s*$!) {
		my $file = $2;
		if (($file =~ m!^$sysdeps->{'ptal-pipes'}/(.+)$!) ||
		    ($file =~ m!^/dev/ptal-printd/(.+)$!) ||
		    ($file =~ m!^/var/run/ptal-printd/(.+)$!)) {
		    # Translate device for ptal-printd to ptal URI
		    my $devname = $1;
		    $devname =~ s/_/:/;
		    $devname =~ s/_/:/;
		    $c->{'connect'} = "ptal:/$devname";
		} elsif (($file =~ m!^$sysdeps->{'mtink-pipes'}/(.+)$!) ||
		    ($file =~ m!^/var/mtink/(.+)$!)) {
		    # Translate device for mtinkd to mtink URI
		    $c->{'connect'} = "mtink:/$1";
		} elsif ($file =~ m!usb!i) {
		    $c->{'connect'} = "usb:$file";
		} elsif ($file =~ m!(tty|serial)!i) {
		    $c->{'connect'} = "serial:$file";
		} elsif ($file =~ m!(lp[0-9]|parallel)!i) {
		    $c->{'connect'} = "parallel:$file";
		} else {
		    $c->{'connect'} = "file:$file";
		}
	    } elsif ($postpipe =~ 
		m!^\s*\|\s*($sysdeps->{'ptal-connect'}|ptal-connect|ptal-print)\s+(-print\s+|)([^\s]+)(\s+-print|)\s*$!){
		$c->{'connect'} = "ptal:/$3";
	    } elsif ($postpipe =~ 
		m!^\s*\|\s*($sysdeps->{'nc'}|netcat|nc)\s+(-w\s*1\s+|)([^\s]+)\s+([^\s]+)\s*$!){
		$c->{'connect'} = "socket://$3:$4";
	    } elsif ($postpipe =~ 
		     m!^\s*\|\s*$sysdeps->{'rlpr'}\s.*-P\s*([^\s\\\@]+)\@([^\s\\\@]+)\s*$!) {
		$c->{'connect'} = "lpd://$2/$1";
	    } elsif ($postpipe =~ 
		     m!^.*\|\s*$sysdeps->{'smbclient'}\s+\"//([^/\s]+)/([^/\s]+)\"\s+(\S.*)$!s) {
		my $servershare = "$1/$2";
		my $parameters = $3;
		my $password = "";
		if ($parameters =~ m!^([^-]\S*)\s+(\S.*)$!) {
		    $password = $1;
		    $parameters = $2;
		}
		my $username = "";
		if ($parameters =~ m!^-U\s+(\S*)\s+(\S.*)$!) {
		    $username = $1;
		    $parameters = $2;
		}
		my $workgroup = "";
		if ($parameters =~ m!^-W\s+(\S*)\s+(\S.*)$!) {
		    $workgroup = "$1/";
		}
		my $identity = "";
		if (($username eq "GUEST") && ($password eq "")) {
		    $identity = "";
		} elsif (($username eq "") && ($password eq "")) {
		    $identity = "";
		} elsif (($username ne "") && ($password eq "")) {
		    $identity = "$username\@";
		} elsif (($username eq "") && ($password ne "")) {
		    $identity = ":$password\@";
		} else {
		    $identity = "$username:$password\@";
		}
		$c->{'connect'} = "smb://$identity$workgroup$servershare";
	    } elsif ($postpipe =~ 
		     m!^\s*\|\s*$sysdeps->{'nprint'}\s+(\S.*)$!s) {
		my $parameters = $1;
		my $server = "";
		if ($parameters =~ m!^-S\s+(\S*)\s+(\S.*)$!) {
		    $server = $1;
		    $parameters = $2;
		}
		my $username = "";
		if ($parameters =~ m!^-U\s+(\S*)\s+(\S.*)$!) {
		    $username = $1;
		    $parameters = $2;
		}
		my $password = "";
		if ($parameters =~ m!^-P\s+(\S*)\s+(\S.*)$!) {
		    $password = $1;
		    $parameters = $2;
		}
		if ($parameters =~ m!^-n\s+(\S.*)$!) {
		    $parameters = $1;
		}
		my $queue = "";
		if ($parameters =~ m!^-q\s+(\S*)\s+(\S.*)$!) {
		    $queue = $1;
		}
		my $identity = "";
		if (($username eq "") && ($password eq "")) {
		    $identity = "";
		} elsif (($username ne "") && ($password eq "")) {
		    $identity = "$username\@";
		} elsif (($username eq "") && ($password ne "")) {
		    $identity = ":$password\@";
		} else {
		    $identity = "$username:$password\@";
		}
		$c->{'connect'} = "ncp://$identity$server/$queue";
	    } else {
		$postpipe =~ m!\s*\|\s*(\S.*)$!;
		$c->{'connect'} = "postpipe:\"$1\"";
	    }
	} else {
	    $c->{'connect'} = "stdout";
	}
	$dat->{'queuedata'} = $c;
    }
    if (!defined($dat->{'queuedata'})) {$dat = undef};
    return $dat;
}

sub overtake_defaults {
    # overtake the option default settings from $olddatablob
    my ($olddatablob) = $_[0];
    my $c;
    @{$c->{'options'}} = ();
    for my $opt (@{$olddatablob->{'args'}}) {
	push (@{$c->{'options'}}, "$opt->{'name'}=$opt->{'default'}");
    }
    set_default_options($c, $db->{'dat'});
}

sub set_default_options {

    # Set the default printing options by doing changes on the Perl
    # structure produced by "getdat", before the spooler-specific
    # datafile is generated

    my ($config) = $_[0];
    my ($dest) = $_[1];

    if ($#{$config->{'options'}} >= 0) {
	for (@{$config->{'options'}}) {
	    my $option = $_;
	    if ($option =~ m!^\s*([^=]+)=([^=]*)\s*$!) {
		# evaluated or numerical option, boolean option with
		# value "True", "False", "Yes", "No", "On", "Off", "1", "0" 
		# given
		my $optname = $1;
		my $optvalue = $2;
   		if (defined($dest->{'args_byname'}{$optname})) {
		    if ($dest->{'args_byname'}{$optname}{'type'} eq
			'bool') {
			if ((lc($optvalue) eq 'true') ||
			    (lc($optvalue) eq 'on') ||
			    (lc($optvalue) eq 'yes')) {
			    $optvalue = '1';
			} elsif ((lc($optvalue) eq 'false') ||
				 (lc($optvalue) eq 'off') ||
				 (lc($optvalue) eq 'no')) {
			    $optvalue = '0';
			}
			if (($optvalue eq '1') || ($optvalue eq '0')) {
			    $dest->{'args_byname'}{$optname}{'default'} = 
				$optvalue;
			}
		    } elsif (($dest->{'args_byname'}{$optname}{'type'} eq
			      'int') || 
			     ($dest->{'args_byname'}{$optname}{'type'} eq
			      'float')) {
			if (($optvalue =~ 
			     m!^\s*[\+\-]?\s*[0-9]*\.?[0-9]*\s*$!) &&
			    ($optvalue >=
			     $dest->{'args_byname'}{$optname}{'min'}) &&
			    ($optvalue <=
			     $dest->{'args_byname'}{$optname}{'max'})) {
			    $dest->{'args_byname'}{$optname}{'default'} = 
				$optvalue;
			}
		    } elsif (($dest->{'args_byname'}{$optname}{'type'} eq
			      'string') || 
			     ($dest->{'args_byname'}{$optname}{'type'} eq
			      'password')) {
			$optvalue = Foomatic::DB::checkoptionvalue
			    ($dest, $optname, $optvalue, 0);
			$dest->{'args_byname'}{$optname}{'default'} = 
			    $optvalue
			    if defined($optvalue);
		    } else {
			if (defined($dest->{'args_byname'}{$optname}{'vals_byname'}{$optvalue})) {
			    $dest->{'args_byname'}{$optname}{'default'} = 
				$optvalue;
			}
		    }
		}
	    } else {
		if (($option =~ /^no(.+?)$/) && 
		    (defined($dest->{'args_byname'}{$1})) &&
		    ($dest->{'args_byname'}{$1}{'type'} eq
		     'bool')) {
		    $dest->{'args_byname'}{$1}{'default'} = '0';
		} elsif ((defined($dest->{'args_byname'}{$option})) &&
		    ($dest->{'args_byname'}{$option}{'type'} eq
		     'bool')) {
		    $dest->{'args_byname'}{$option}{'default'} = '1';
		}
	    }
	}
    }
}

sub print_perl_combo_data {
    my ($config, $olddatablob) = @_;

    # Get the data
    if ($config->{'ppdfile'}) { 
	# From PPD file
	my $dat = ppdtoperl($config->{'ppdfile'});
	if (!defined($dat)) {
	    die ("Unable to open PPD file \'$config->{'ppdfile'}\'\n");
	}
	$db->{'dat'} = $dat;
    } else {
	# From Foomatic XML database
	my $possible = $db->getdat($config->{'driver'}, 
				   $config->{'printer'});
	die "That printer and driver combination is not possible.\n"
	    if (!$possible);
	die "There is neither a custom PPD file nor the driver database entry contains sufficient data to build a PPD file.\n"
	    if (!$db->{'dat'}{'cmd'}) && (!$db->{'dat'}{'ppdfile'});
	# Generate the PPD and extract it to Perl again (to get in the
	# composite options)
	my $ppd = $db->getppd($config->{'shortgui'});
	delete ($db->{'dat'});
	$db->{'dat'} = ppdfromvartoperl([split(/\n/, $ppd)]);
    }

    # The data can be viewed with the option defaults of an existing
    # queue set
    if ($olddatablob) {
	my $c;
	@{$c->{'options'}} = ();
	for my $opt (@{$olddatablob->{'args'}}) {
	    push (@{$c->{'options'}}, "$opt->{'name'}=$opt->{'default'}");
	}
	set_default_options($c, $db->{'dat'});
    }

    # User can view the data of the combo also with options given on the
    # command line
    set_default_options($config, $db->{'dat'});

    # Put it out
    my $asciidata = $db->getascii();
    $asciidata =~ s/\$VAR1/\$COMBODATA/g;
    print $asciidata;
    return;
    
}

sub detect_spooler {

    # If tcp/localhost:631 opens, cups CUPS is the most sophisticated
    # spooler, if it is running, it is usually the primary printing
    # system
    my $page = ($db->getpage('http://localhost:631/', 1) || "");
    if ($page =~ m!Common UNIX Printing System!) {
	return 'cups';
    }

    # PPR is also very sophisticated so check for this spooler if there is
    # no CUPS running.
    if (-x $sysdeps->{'ppr-ppr'}) {
	# There's a /usr/bin/ppr
	return 'ppr';
    }
    
    # Else if /etc/printcap, some sort of lpd thing
    if (-f $sysdeps->{'lpd-pcap'}) {
	# If -f /etc/lpd.conf, lprng
	if (-f $sysdeps->{'lprng-conf'}) {
	    return 'lprng';
	} elsif (-x $sysdeps->{'lpd-bin'}) {
	    # There's a /usr/sbin/lpd
	    return 'lpd';
	}
    }

    # pdq executable in our path somewhere?
    for (split(':', $ENV{'PATH'})) {
	if (-x "$_/pdq") {
	    return 'pdq';
	}
    }

    # If there is no known spooler, set up printers for direct, spooler-less
    # printing.
    return "direct";
}

sub unimp {
    die "Sorry, $action for your spooler is unimplemented...\n";
}

sub overview {
    print $db->get_overview_xml($opt_f);
    exit(0);
}

sub get_xml {
    my $x = undef;
    if (($opt_p) and ($opt_d)) {
	$x = $db->get_combo_data_xml($opt_d,$opt_p);
    } elsif ($opt_p) {
        $x = $db->get_printer_xml($opt_p);
    } elsif ($opt_d) {
	$x = $db->get_driver_xml($opt_d);
    } else {
	die "You must specify a -p printer and/or -d driver.\n";
    }

    if (defined($x)) {
	print $x;
    } else {
	die "Unable to find object.\n";
    }

    exit(0);
}

sub help {
    print STDERR <<EOH;
Usage: $progname [ -s spooler ] -n queuename \\
			  [ -N 'Name/Descr.' ] [ -L 'Location Info' ] \\
			  [ -c connect ] \\
			  [ -d driver ] [ -p printer ] [ -f ] [ -w ] \\
			  [ --ppd ppdfile ] \\
			  [ -o option1=value1 -o option2 ... ] \\
			  [ --backend-dont-disable=value ] \\
			  [ --backend-attempts=value ] \\
			  [ --backend-delay=value ] \\
			  [ -q ]
    or $progname -C [ -s spooler ] -n queuename \\
	                  [ sourcespooler ] sourcequeue \\
	                  [ -N 'Name/Descr.' ] [ -L 'Location Info' ] \\
			  [ -c connect ] \\
			  [ -d driver ] [ -p printer ] [ -f ] [ -w ] \\
			  [ --ppd ppdfile ] \\
			  [ -o option1=value1 -o option2 ... ] \\
			  [ --backend-dont-disable=value ] \\
			  [ --backend-attempts=value ] \\
			  [ --backend-delay=value ] \\
			  [ -q ]
    or $progname -D [ -s spooler ] -n queuename [ -q ]
    or $progname -R [ -s spooler ] -n queuename [ -q ]
    or $progname -Q [ -s spooler ] [ -n queuename ] [ -q ] [ -r ]
    or $progname -P [ -s spooler ] [ -n queuename ] [ -q ] [ N ]
    or $progname -P [ -s spooler ] [ -n queuename ] \\
	                  [ --ppd ppdfile ] [ -d driver -p printer ] \\
	                  [ -o option1=value1 -o option2 ... ] [ -q ]
    or $progname -O
    or $progname -X [ -p printer ] [ -d driver ]

 -n queuename    Configure/create/delete/query this print queue
 -N Name/Descr.  Long name/Short Description. An empty string ("") deletes
                 the description.
 -L Location     Short phrase describing this printer's location. An empty
                 string ("") deletes the location.
 -c connection   Printer is connected thusly (ex file:/dev/lp0), must
                 be given when a new queue is created
 --ppd ppdfile   Set up the queue using the PPD file ppdfile (can be a
                 manufacturer-supplied PPD file for a PostScript printer).
                 gzip-compressed PPD files are allowed, they must have the
                 extension ".gz".
 -d driver       Foomatic database name for desired printer driver or "raw"
                 for a raw queue. When a non-raw queue is created, the
                 printer must be specified in addition ("-p" option)
 -p printer      Foomatic id for printer. When a non-raw queue is created,
                 the driver must be specified in addition ("-d" option)
 -s spooler      Explicit spooler type (cups, lpd, lprng, pdq, ppr, direct)
 -o option=value Use value as the default for option in this queue
 -o option       Set the switch option by default in this queue
 --backend-dont-disable=value  1: Do not disable CUPS queue when backend
                 fails, 0: Original CUPS behaviour, queue gets disabled
                 when backend fails. Default: 0 (CUPS only)
 --backend-attempts=value  Try that often when backend fails, for infinite
                 retries set the value to zero, for standard CUPS
                 behaviour to 1. Default: 1 (CUPS only)
 --backend-delay=value  Delay in seconds between retries of failed backend.
                 Default: 30 (CUPS only)
 -C [sourcespooler] sourcequeue  Create a copy of a queue. All 
                 characteristics including default option settings are 
                 overtaken. Additional arguments modify the copy. This
                 facility allows to overtake one's configured queues when
                 one changes the spooler.
 -D              Set this queue as the queue used by default.
 -R              Remove this whole queue entirely (just give -n queuename)
 -Q              Query existing configuration (gives XML summary). Supplying
                 no queue name gives info about all installed queues for the
                 current/selected spooler, including the default queue.
 -r              list also remote queues (CUPS only).
 -P              Query existing configuration (gives Perl data structure of
                 the complete information about the queue, including
                 options, possible choices, default settings, ..., for use 
                 by frontends, the output is done as a Perl array, one
                 element per queue), With printer ID and driver name instead
                 of queue name supplied the Perl data structure of the 
                 appropriate printer/driver combo is generated, supplied
                 options are entered as default settings then, from a
                 supplied queue the option default settings are used.
                 Supplying no queue, printer, and driver gives info about
                 all installed queues for the current/selected spooler.
 N               The first index of the Perl array, default: 0
 -O              Print XML Overview of all known printer/drivers
 -X              Print XML data for -p printer and/or -d driver object
 -f              Force rebuild of PPD file from database
 -w              Generate PPD which is compatible to the CUPS PostScript
                 driver for Windows (GUI strings are limited to 39 characters).
                 This applies only to PPDs built from the Foomatic database, it
                 has no influence on PPDs supplied with the "--ppd" option.
 -q              Run quietly and non-interactive
 -h  --help      Show this help message

EOH

#'# Fix emacs syntax highlighting

    exit 0;
}
