Viewing file: foomatic-printjob (34.27 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#!/usr/bin/perl # -*- perl -*- # This is foomatic-printjob, a program to print and manage printing # jobs with the same commands independent whether the spooler is CUPS, # LPD, LPRng, or PDQ.
# It also comprises half of a programattic API for user tools: you can # learn and control everything about the properties of printing jobs # here. With the sister program foomatic-configure, you can do # everything related to print queue static state: install, modify, # remove queues, query queue, printer, and driver info.
use Foomatic::Defaults; use Foomatic::DB;
# Read out the program name with which we were called, but discard the path
$0 =~ m!/([^/]+)\s*$!; $progname = $1;
# We use the library Getopt::Long here, so that we can have more than one "-o" # option on one command line.
use Getopt::Long; Getopt::Long::Configure("no_ignore_case", "pass_through"); GetOptions("P=s" => \$opt_P, # which queue (Printer)? "d=s" => \$opt_d, # which queue (Destination)? "s=s" => \$opt_s, # which Spooler? "o=s" => \@opt_o, # printing Options "Q" => \$opt_Q, # Query jobs in queue "R" => \$opt_R, # Remove job(s) "C" => \$opt_C, # Control job(s)/queue(s) "S" => \$opt_S, # set default Spooler "h" => \$opt_h); # Help!
help() if ($opt_h && !$opt_P);
my $db = new Foomatic::DB;
my $in_config = {'queue' => $opt_P, 'options' => \@opt_o, 'spooler' => $opt_s};
# Default action: Printing my $action = 'print';
# Determine the action by the name how we were called if ($progname =~ m!^lpc!) { # 'lpc*' ==> control $action = 'control'; } elsif ($progname =~ m!^lprm!) { # 'lprm*' ==> remove jobs $action = 'remove'; } elsif ($progname =~ m!^lpq!) { # 'lpq*' ==> list jobs $action = 'query'; } elsif (($progname =~ m!^lpr!) || ($progname =~ m!^lp!)) { # 'lpr*', 'lp*' ==> print $action = 'print'; }
# Determine the action by a command line option $action = ($opt_R ? 'remove' : $action); $action = ($opt_Q ? 'query' : $action); $action = ($opt_C ? 'control' : $action);
my $procs = { 'lpd' => { 'print' => \&print_lpd, 'query' => \&query_lpd, 'remove' => \&remove_lpd, 'control' => \&control_lpd }, 'lprng'=>{ 'print' => \&print_lprng, 'query' => \&query_lprng, 'remove' => \&remove_lprng, 'control' => \&control_lpd }, 'cups' =>{ 'print' => \&print_cups, 'query' => \&query_cups, 'remove' => \&remove_cups, 'control' => \&control_cups }, 'pdq' =>{ 'print' => \&print_pdq, 'query' => \&query_pdq, 'remove' => \&remove_pdq, 'control' => \&control_pdq } };
if (!(defined($in_config->{'queue'}))) { # No job handling without knowing the name of the queue # PRINTER environment variable if (defined($opt_d)) { $in_config->{'queue'} = $opt_d; } elsif (defined($ENV{PRINTER})) { $in_config->{'queue'} = $ENV{PRINTER}; } else { # Use spoolers default } }
if (!defined($in_config->{'spooler'})) {
# Personal default spooler if (($> != 0) && (-f "$ENV{'HOME'}/.defaultspooler")) { $s = `cat $ENV{'HOME'}/.defaultspooler`; chomp $s; } # System default spooler if ((!defined($s)) && (-f "$sysdeps->{'foo-etc'}/defaultspooler")) { $s = `cat $sysdeps->{'foo-etc'}/defaultspooler`; chomp $s; } if (!defined($s)) { $s = detect_spooler(); }
die "Unable to identify spooler, please specify one with \"-s\"!\n" unless $s;
if (defined($opt_i)) { print STDERR "You appear to be using $s. Correct? "; my $yn = <STDIN>; die "\n" if ($yn !~ m!^y!i); }
$in_config->{'spooler'} = $s; }
if (defined($opt_S)) { if ($> == 0) { # Program invoked as "root"? # Set system default spooler open DEFAULTFILE, "> $sysdeps->{'foo-etc'}/defaultspooler" || die "Cannot write $sysdeps->{'foo-etc'}/defaultspooler!\n"; print DEFAULTFILE "$in_config->{'spooler'}\n"; close DEFAULTFILE; exit 0; } else { # Set personal default spooler open DEFAULTFILE, "> $ENV{'HOME'}/.defaultspooler" || die "Cannot write $ENV{'HOME'}/.defaultspooler!\n"; print DEFAULTFILE "$in_config->{'spooler'}\n"; close DEFAULTFILE; exit 0; } }
# Exception... help_options($in_config) if ($opt_h);
# Call proper proc exit &{$procs->{$in_config->{'spooler'}}{$action}}($in_config);
### Printing/Job manipulation functions for LPD
sub print_lpd { my ($config) = $_[0];
#sysdeps->{'lpd-lpr'} = "/home/test/lpr-0.71/lpr/lpr";
# Auto-detect whether the "lpr" executable is the VA-Linux version or not my $valinuxlpr = !(system "strings $sysdeps->{'lpd-lpr'} | grep option > /dev/null");
# Printing command my $commandline = "$sysdeps->{'lpd-lpr'}";
# Add the printer queue argument if (defined($config->{'queue'})) { $commandline .= " -P $config->{'queue'}"; }
# Add the driver-specific options supplied by the user, if any # For the VA-Linux implementation of "lpr" (gnulpr) options are passed # with '-o option=value -o switch', for the BSD implementation they are # passe with '-J"option=value switch"'. if ($valinuxlpr) { # VA-Linux/gnulpr if ($#{$config->{'options'}} >= 0) { for (@{$config->{'options'}}) { $commandline .= " -o $_"; } } } else { # BSD if ($#{$config->{'options'}} >= 0) { $commandline .= " -J\""; for (@{$config->{'options'}}) { $commandline .= "$_ "; } $commandline .= "\""; } }
# Add the remaining command line arguments, they are the names of # the files to print and also spooler-specific options $commandline .= " @ARGV";
# Do it!
#print "$commandline\n"; return (system $commandline) >> 8;
}
sub query_lpd { my ($config) = $_[0];
# standard lpq, emulate -a of lpq-cups # Read additional options GetOptions("a" => \$opt_a); # List jobs on all printers if (defined($opt_a)) { # Get all printer queues open QUEUELIST, "$sysdeps->{'lpd-lpc'} status 2>&1 | grep \":\$\" | "; my @queuelist = <QUEUELIST>; close QUEUELIST; # List the jobs on all the queues for (@queuelist) { my $queue = $_; chomp $queue; print "$queue\n"; $queue =~ s/:$//; my $result = (system "$sysdeps->{'lpd-lpq'} -P $queue @ARGV") >> 8; if ($result != 0) {return $result}; } } else { # List the jobs on the specified queue my $queue = ""; if (defined($config->{'queue'})) { $queue = " -P $config->{'queue'}"; } return (system "$sysdeps->{'lpd-lpq'}$queue @ARGV") >> 8; } }
sub remove_lpd { my ($config) = $_[0]; # Remove a job with the standard "lprm" command
# Removing command my $commandline = "$sysdeps->{'lpd-lprm'}";
# Add the printer queue argument if (defined($config->{'queue'})) { $commandline .= " -P $config->{'queue'}"; }
# Add the remaining command line arguments, they are the numbers # of the jobs to kill, the users whose jos to remove and also # spooler-specific options
$commandline .= " @ARGV";
# Do it!
#print "$commandline\n"; return (system $commandline) >> 8;
}
sub control_lpd { my ($config) = $_[0]; # Control the printing system with the standard "lpc" command
# Control command my $commandline = "$sysdeps->{'lpd-lpc'}";
# Add the remaining command line arguments, they are the control command # with its arguments
$commandline .= " @ARGV";
# Do it!
#print "$commandline\n"; return (system $commandline) >> 8;
}
### Printing/Job manipulation functions for LPRng
sub print_lprng { my ($config) = $_[0];
# Printing command my $commandline = "$sysdeps->{'lpd-lpr'}";
# Add the printer queue argument if (defined($config->{'queue'})) { $commandline .= " -P $config->{'queue'}"; }
# Add the driver-specific options supplied by the user, if any if ($#{$config->{'options'}} >= 0) { for (@{$config->{'options'}}) { $commandline .= " -Z $_"; } }
# Add the remaining command line arguments, they are the names of # the files to print and also spooler-specific options $commandline .= " @ARGV";
# Do it!
#print "$commandline\n"; return (system $commandline) >> 8;
}
sub query_lprng { my ($config) = $_[0];
# We filter the output of lpq and rearrange it to have the same format # as of LPD and CUPS.
GetOptions("l" => \$opt_l); # Long, more verbose output
# List the jobs on the specified queue my $queue = ""; if (defined($config->{'queue'})) { $queue = " -P $config->{'queue'}"; } open LPQOUTPUT, "$sysdeps->{'lpd-lpq'}$queue @ARGV |" || return 1; my @lpqoutput = <LPQOUTPUT>; close LPQOUTPUT; # Filter the output for $line (@lpqoutput) { chomp $line; if ($line =~ m!^\s*(\S+)\s+([^@\s]+)@[^@\+\s]+\+[0-9]+\s+\S+\s+([0-9]+)\s+(\S+)\s+([0-9]+)\s+[0-9:]+\s*$!) { my ($rank, $owner, $jobid, $file, $size) = ($1, $2, $3, $4, $5); if (defined($opt_l)) { my $owner_rank = "$owner: $rank"; if (length($owner_rank) > 40) { $owner_rank = substr($owner_rank, 0, 40); } if (length($file) > 40) {$file = substr($file, 0, 40);} print sprintf("\n%-40s [job %d]\n\t%-40s %d bytes\n", $owner_rank, $jobid, $file, $size); } else { if (length($rank) > 6) {$rank = substr($rank, 0, 6)}; if (length($owner) > 8) {$owner = substr($owner, 0, 8)}; if (length($file) > 37) {$file = substr($file, 0, 37)}; print sprintf("%-6s %-8s % 6d %-37s %d bytes\n", $rank, $owner, $jobid, $file, $size); } } elsif ($line =~ m!\s*Rank\s+Owner!) { if (!defined($opt_l)) { print "Rank Owner Job File(s) Total Size\n"; } } else { print("$line\n"); } } }
sub remove_lprng { my ($config) = $_[0]; # Remove a job with the standard "lprm" command and emulate the "-" # option of the lprm command of BSD LPD
# Removing command my $commandline = "$sysdeps->{'lpd-lprm'}";
# Add the printer queue argument if (defined($config->{'queue'})) { $commandline .= " -P $config->{'queue'}"; }
# Replace the "-" option by the "all" option my $alljobs = ""; for ($i = 0; ($i <= $#ARGV); $i++) { if ($ARGV[$i] =~ m!^\s*\-\s*$!) { $alljobs = " all"; splice(@ARGV,$i,1); $i--; } } $commandline .= $alljobs;
# Add the remaining command line arguments, they are the numbers # of the jobs to kill, the users whose jos to remove and also # spooler-specific options
$commandline .= " @ARGV";
# Do it!
#print "$commandline\n"; return (system $commandline) >> 8;
}
sub control_lprng {
# The lpc command of lprng is compatible to the one of LPD, it has only # many more commands. So we use the "control_lpd" function also for # lprng.
}
### Printing/Job manipulation functions for CUPS
sub print_cups { my ($config) = $_[0];
# Printing command my $commandline = "$sysdeps->{'cups-lpr'}";
# Add the printer queue argument if (defined($config->{'queue'})) { $commandline .= " -P $config->{'queue'}"; }
# Add the driver-specific options supplied by the user, if any if ($#{$config->{'options'}} >= 0) { for (@{$config->{'options'}}) { $commandline .= " -o $_"; } }
# Add the remaining command line arguments, they are the names of # the files to print and also spooler-specific options $commandline .= " @ARGV";
# Do it!
#print "$commandline\n"; return (system $commandline) >> 8;
}
sub query_cups { my ($config) = $_[0];
# List the jobs on the specified queue my $queue = ""; if (defined($config->{'queue'})) { $queue = " -P $config->{'queue'}"; } return (system "$sysdeps->{'cups-lpq'}$queue @ARGV") >> 8;
}
sub remove_cups { my ($config) = $_[0]; # Remove a job with the standard "lprm" command
# Removing command my $commandline = "$sysdeps->{'cups-lprm'}";
# Add the printer queue argument if (defined($config->{'queue'})) { $commandline .= " -P $config->{'queue'}"; }
# Add the remaining command line arguments, they are the numbers # of the jobs to kill, the users whose jos to remove and also # spooler-specific options
$commandline .= " @ARGV";
# Do it!
#print "$commandline\n"; return (system $commandline) >> 8;
}
sub control_cups { my ($config) = $_[0];
# CUPS has no LPD/LPRng-compatible lpc command, so we must emulate # this functionality with the command line tools of CUPS.
# The first command line argument (of the remaining ones) is the # control command (standard commands of lpc for LPD/LPRng)
my $command = shift (@ARGV);
if (!defined($command)) { die "You must supply a control command with the \"-C\" option!\n"; } elsif (lc($command) eq "up") { # Turn on queue (queueing/printing) return (system "$sysdeps->{'cups-enable'} @ARGV; $sysdeps->{'cups-accept'} @ARGV") >> 8; } elsif (lc($command) eq "down") { # Turn off queue (queueing/printing) return (system "$sysdeps->{'cups-disable'} @ARGV; $sysdeps->{'cups-reject'} @ARGV") >> 8; } elsif (lc($command) eq "start") { # Turn on queue (printing) return (system "$sysdeps->{'cups-enable'} @ARGV") >> 8; } elsif (lc($command) eq "stop") { # Turn off queue (printing) return (system "$sysdeps->{'cups-disable'} @ARGV") >> 8; } elsif (lc($command) eq "enable") { # Accept new jobs return (system "$sysdeps->{'cups-accept'} @ARGV") >> 8; } elsif (lc($command) eq "disable") { # Reject new jobs return (system "$sysdeps->{'cups-reject'} @ARGV") >> 8; } elsif (lc($command) eq "move") { # Move jobs if (($#ARGV < 1) or ($#ARGV > 2)) { die "Usage of the \"move\" control command:\n\n move oldqueue [ jobID ] newqueue\n\n"; } # The first argument is always the source printer my $fromqueue = shift (@ARGV); # The second argument is the job ID or the destination my $jobid = shift (@ARGV); # The third argument is the destination my $toqueue = shift (@ARGV); if (!defined($toqueue)) { # No job ID given, move all jobs in the given queue $toqueue = $jobid; open LINES, "$sysdeps->{'cups-lpq'} -P $fromqueue |"; my @lines = <LINES>; close LINES; for (@lines) { if ($_ =~ m!^\s*\S+\s+\S+\s+([0-9]+)\s+!) { system "$sysdeps->{'cups-lpmove'} $fromqueue-$1 $toqueue"; } } return; } else { # Treat the specified job return (system "$sysdeps->{'cups-lpmove'} $fromqueue-$jobid $toqueue") >> 8; } } elsif ((lc($command) eq "hold") || # Hold job (lc($command) eq "release") || # Resume job (lc($command) eq "topq")) { # Bring job to the top of the # queue if (($#ARGV < 0) or ($#ARGV > 1)) { die "Usage of the \"$command\" control command:\n\n $command queue [ jobID ] \n\n"; } # Clean up the command $command = lc($command); if ($command eq "release") {$command = "resume";} if ($command eq "topq") {$command = "immediate";} # The first argument is always the queue my $queue = shift (@ARGV); # The second argument is the job ID my $jobid = shift (@ARGV); if (!defined($jobid)) { # No job ID given, treat all jobs in the given queue open LINES, "$sysdeps->{'cups-lpq'} -P $queue |"; my @lines = <LINES>; close LINES; for (@lines) { if ($_ =~ m!^\s*\S+\s+\S+\s+([0-9]+)\s+!) { system "$sysdeps->{'cups-lp'} -i $queue-$1 -H $command"; } } return; } else { # Treat the specified job return (system "$sysdeps->{'cups-lp'} -i $queue-$jobid -H $command") >> 8; } } elsif (lc($command) eq "status") { # Queue status listing return (system "$sysdeps->{'cups-lpc'} status @ARGV") >> 8; } elsif (lc($command) eq "help") { # List the available commands print "The following control commands are available:\n\n"; print " up queue : Turn on queue (queueing/printing)\n"; print " down queue : Turn off queue (queueing/printing)\n"; print " start queue : Turn on printing on queue\n"; print " stop queue : Turn off printing on queue\n"; print " enable queue : Make queue accepting new jobs\n"; print " disable queue : Make queue rejecting new jobs\n"; print " move oldqueue [ jobid ] newqueue : \n"; print " Move job jobid in oldqueue to newqueue\n"; print " Move all jobs in oldqueue to newqueue when jobid not given\n"; print " hold queue [ jobid ] : Hold job jobid or all jobs in queue\n"; print " release queue [ jobid ] : Release job jobid or all jobs in queue\n"; print " topq queue jobid : Print job jobid in queue immediately\n"; print " status [ queue ] : Status of queue or of all queues\n"; print " help : This help message\n\n"; } else { die "Command \"$command\" not recognized!\n"; } }
### Printing/Job manipulation functions for PDQ
sub print_pdq { my ($config) = $_[0];
# Printing command my $commandline = "$sysdeps->{'pdq-print'}";
# Add the printer queue argument if (defined($config->{'queue'})) { $commandline .= " -P $config->{'queue'}"; }
# Add the driver-specific options supplied by the user, if any if ($#{$config->{'options'}} >= 0) { for (@{$config->{'options'}}) { my $option = $_; if ($option =~ m!^\s*([^=]+=[\+\-0-9\.]+)\s*$!) { # Foomatic treats numerical options as PDQ arguments ("-a"), # but there can be enumerated options with numbers as choices, # so we give the option in both styles. Since PDQ silently # ignores non-existent options, the wrong form of the option # will be ignored. $commandline .= " -aOPT_$1"; } # Enumerated and boolean options are PDQ options ("-o"), # the "=" has to be replaced by "_" to work with the # PDQ-O-MATIC-generated configuration $option =~ s/=/_/; # Replace only the first "=" $commandline .= " -o$option"; } }
# The "-#" option for multiple copies is not supported by the print # command "pdq". So we launch "pdq" once per copy. Thw command line # will be modified appropriately directly before the printing command # is launched. # Note: '#' as option name is not supported by the Perl library # Getopt::Long. my $num_copies = 1; my $file_in_args = 0; my $i; for ($i = 0; ($i <= $#ARGV); $i++) { if ($ARGV[$i] =~ m!^\s*\-\#\s*([0-9]+)\s*$!) { $num_copies = $1; splice(@ARGV,$i,1); $i--; } elsif ($ARGV[$i] =~ m!^\s*\-\#\s*$!) { if ((defined $ARGV[$i+1]) && ($ARGV[$i+1] =~ m!^\s*([0-9]+)\s*$!)) { $num_copies = $1; splice(@ARGV,$i,2); $i--; } } elsif ($ARGV[$i] =~ m!^\s*[^\-]+!) { $file_in_args = 1; } }
# Add the remaining command line arguments, they are the names of # the files to print and also spooler-specific options $commandline .= " @ARGV";
# Do it! #print "$commandline\n"; return 0;
if ($num_copies == 1) { return (system $commandline) >> 8; } else { if ($file_in_args == 0) { # We print from standard input, so we must buffer it to be able # to print multiple copies my @job_contents = <STDIN>; my $i; for ($i = 0; $i < $num_copies; $i++) { open PIPE, "| $commandline" || die "Could not launch printing command!\n"; print PIPE @job_contents; close PIPE; } return 0; } else { # We print files my $result = 0; my $i; for ($i = 0; $i < $num_copies; $i++) { $result = (system $commandline) >> 8; if ($result != 0) {return $result}; } return 0; } } }
sub query_pdq { my ($config) = $_[0];
# PDQ has no possiblity to list the printing jobs from the command # line. So we read the *.status files in ~/.printjobs and generate # the job entry lines from that information.
# Read additional options GetOptions("a" => \$opt_a, # List jobs on all printers "l" => \$opt_l); # Long, more verbose output
# Make sure that a printer is specified when the "-a" option is not # given if ((!(defined($opt_a))) && (!(defined($config->{'queue'})))) { $config->{'queue'} = get_pdq_default_printer(); }
# If the user specified job numbers, list them. User names on the # command line do not make much sense, because under PDQ a user can # only see ones own jobs, they are supported here to do not break # front ends my $joblist = {}; my $userlist = {}; my $listalljobs = 1; my $listallusers = 1; my $i; for ($i = 0; ($i <= $#ARGV); $i++) { if ($ARGV[$i] =~ m!^\s*([0-9]+)\s*$!) { my $job=$1; # Fill up the number with zeros so that it has three digits while (length($job) < 3) {$job = "0" . $job;} $joblist->{$job} = 1; $listalljobs = 0; splice(@ARGV,$i,1); $i--; } elsif ($ARGV[$i] =~ m!^\s*[^\-]+!) { my $user=$ARGV[$i]; $userlist->{$user} = 1; $listallusers = 0; splice(@ARGV,$i,1); $i--; } else { die "Unknown option: $ARGV[$i]\n"; } }
# When we list only the jobs for a specific printer, display the # printer status at first. In PDQ the printer status cannot be # retrived from the command line, so we put a dummy line # "<printer> is ready". if (!(defined($opt_a))) { if (!pdq_check_printer($config->{'queue'})) { die "$config->{'queue'}: unknown printer\n"; } print "$config->{'queue'} is ready\n"; }
# Read in the names of all job status files in ~/.printjobs/ my @jobnumbers = (); opendir PJDIR, "$ENV{'HOME'}/.printjobs" || return 0; # No ~/.printjobs/ directory ==> no jobs while ($filename = readdir(PJDIR)) { if ($filename =~ m!^([0-9][0-9][0-9]).status$!) { push (@jobnumbers, $1); } } close PJDIR; # Sort the filenames in descending order to get the most recent jobs # listed at first @jobnumbers = sort {$b cmp $a} @jobnumbers;
# Now list the jobs my $firstline = 1; for ($i = 0; $i <= $#jobnumbers; $i ++) { # Omit this job if job numbers are specified on the command line, but # not the one of this job next if (($listalljobs == 0) && (!(defined($joblist->{$jobnumbers[$i]})))); # Read the job status file next if !open JOBSTATUSFILE, "< $ENV{'HOME'}/.printjobs/$jobnumbers[$i].status"; my $jobstatusdata = join("", <JOBSTATUSFILE>); close JOBSTATUSFILE; # Extract the important fields from the file # Status: my $status = ""; if ($jobstatusdata =~ m!^\s*status\s*\=\s*{([^{}]*)}\s*$!m) { $status = $1; } # Omit this job when it has no status field or when the job is # already finished, cancelled, or aborted next if (($status eq "") || ($status =~ m!aborted!) || ($status =~ m!finished!) || ($status =~ m!cancelled!)); # Avoid spaces in the status field, so that frontends can separate the # fields from the job list more easily. $status =~ s/\s//g; # Printer my $printer; if ($jobstatusdata =~ m!^\s*printer\s*\=\s*{([^{}]*)}\s*$!m) { $printer = $1; } # Omit this job when we are querying only the jobs of another printer next if ((!(defined($opt_a))) && ($printer ne $config->{'queue'})); # Owner my $owner; if ($jobstatusdata =~ m!^\s*env_driver\s*\=\s*{.*\"LOGNAME\"\s*=\s*\"([^\"]*)\".*}\s*$!m) { $owner = $1; } # Omit this job if user names are specified on the command line, but # not the owner of this job next if (($listallusers == 0) && (!(defined($userlist->{$owner})))); # File my $file; if ($jobstatusdata =~ m!^\s*input_filename\s*\=\s*{([^{}]*)}\s*$!m) { $file = $1; } # Size of job input file my $size; if (-f "$ENV{'HOME'}/.printjobs/$jobnumbers[$i].raw") { $size = (stat("$ENV{'HOME'}/.printjobs/$jobnumbers[$i].raw"))[7]; }
# Now get the info nicely onto the screen my $outputline; if ($opt_l) { # Long (3+ lines per job) mode my $owner_status = "$owner: $status"; if (length($owner_status) > 40) { $owner_status = substr($owner_status, 0, 40); } if (length($file) > 40) {$file = substr($file, 0, 40);} $outputline = sprintf("\n%-40s [job %d]\n\t%-40s %d bytes\n", $owner_status, $jobnumbers[$i], $file, $size); } else { # Short (1 line per job) mode if ($firstline == 1) { # headline print "Rank Owner Job File(s) Total Size\n"; $firstline = 0; } if (length($status) > 6) {$status = substr($status, 0, 6);} if (length($owner) > 10) {$owner = substr($owner, 0, 10);} if (length($file) > 37) {$file = substr($file, 0, 37);} $outputline = sprintf("%-6s %-10s % 3d %-37s %d bytes\n", $status, $owner, $jobnumbers[$i], $file, $size); } print $outputline; }
# Say "no entries" if no job was listed if ($firstline == 1) { print "no entries\n"; } }
sub remove_pdq { my ($config) = $_[0];
# PDQ has no possiblity to remove printing jobs from the command # line. "xpdq" cancels jobs by "touch"ing <job id>.cancelled # files in ~/.printjobs and setting the permissions of these files # to 0600.
# Make sure that a printer is specified when the "-a" option is not # given if (!(defined($config->{'queue'}))) { $config->{'queue'} = get_pdq_default_printer(); }
# If the user specified job numbers, list them. User names on the # command line do not make much sense, because under PDQ a user can # only see ones own jobs, they are supported here to do not break # front ends my $joblist = {}; my $userlist = {}; my $nojob = 1; my $nouser = 1; my $opt_alljobs = 0; my $i; for ($i = 0; ($i <= $#ARGV); $i++) { if ($ARGV[$i] =~ m!^\s*([0-9]+)\s*$!) { my $job=$1; # Fill up the number with zeros so that it has three digits while (length($job) < 3) {$job = "0" . $job;} $joblist->{$job} = 1; $nojob = 0; splice(@ARGV,$i,1); $i--; } elsif ($ARGV[$i] =~ m!^\s*[^\-]+!) { my $user=$ARGV[$i]; $userlist->{$user} = 1; $nouser = 0; splice(@ARGV,$i,1); $i--; } elsif ($ARGV[$i] =~ m!^\s*\-\s*$!) { $opt_alljobs = 1; splice(@ARGV,$i,1); $i--; } else { die "Unknown option: $ARGV[$i]\n"; } }
# Does the chosen printer exist if (!pdq_check_printer($config->{'queue'})) { die "$config->{'queue'}: unknown printer\n"; }
# Read in the names of all job status files in ~/.printjobs/ my @jobnumbers = (); opendir PJDIR, "$ENV{'HOME'}/.printjobs" || return 0; # No ~/.printjobs/ directory ==> no jobs while ($filename = readdir(PJDIR)) { if ($filename =~ m!^([0-9][0-9][0-9]).status$!) { push (@jobnumbers, $1); } } close PJDIR; # Sort the filenames in descending order to get the most recent # (probably still waiting) jobs removed at first @jobnumbers = sort {$b cmp $a} @jobnumbers;
# Now search the jobs to remove my $nothingremoved = 1; my $mostrecent = 1; for ($i = 0; $i <= $#jobnumbers; $i ++) { # Read the job status file next if !open JOBSTATUSFILE, "< $ENV{'HOME'}/.printjobs/$jobnumbers[$i].status"; my $jobstatusdata = join("", <JOBSTATUSFILE>); close JOBSTATUSFILE; # Extract the important fields from the file # Status: my $status = ""; if ($jobstatusdata =~ m!^\s*status\s*\=\s*{([^{}]*)}\s*$!m) { $status = $1; } # Omit this job when it is already finished, cancelled, or aborted # (then it cannot be killed any more) next if (($status eq "") || ($status =~ m!aborted!) || ($status =~ m!finished!) || ($status =~ m!cancelled!)); # Printer my $printer; if ($jobstatusdata =~ m!^\s*printer\s*\=\s*{([^{}]*)}\s*$!m) { $printer = $1; } # Omit this job when we want to remove jobs on another printer next if ((!(defined($opt_a))) && ($printer ne $config->{'queue'})); # Owner my $owner; if ($jobstatusdata =~ m!^\s*env_driver\s*\=\s*{.*\"LOGNAME\"\s*=\s*\"([^\"]*)\".*}\s*$!m) { $owner = $1; }
# Kill the job when it is in the scope of jobs defined by the # command line if ((($nojob == 0) && (defined($joblist->{$jobnumbers[$i]}))) || (($nouser == 0) && (defined($userlist->{$owner}))) || (($opt_alljobs == 1) && ($ENV{'LOGNAME'} eq $owner)) || (($opt_alljobs == 1) && ($ENV{'LOGNAME'} eq "root")) || (($mostrecent == 1) && ($nojob == 1) && ($nouser == 1) && ($opt_alljobs == 0))) { system("touch $ENV{'HOME'}/.printjobs/$jobnumbers[$i].cancelled; chmod 0600 $ENV{'HOME'}/.printjobs/$jobnumbers[$i].cancelled"); print STDERR "Cancel request for job $jobnumbers[$i] submitted!\n"; $nothingremoved = 0; } $mostrecent = 0; }
# Say "No cancel request sent" if no job was killed if ($nothingremoved == 1) { print STDERR "no cancel request sent\n"; } }
sub control_pdq {
# PDQ does not have functionality for enabling/disabling queues, # holding/releasing/moving jobs, etc.
die "Advanced queue/job manipulation functionality is not supported under PDQ!\n";
}
sub get_pdq_default_printer {
# Read the help message of PDQ open PDQHELP, "pdq --help 2>&1 |"; $pdqhelp = join ("", <PDQHELP>); close PDQHELP;
# Search the "default" line if ($pdqhelp =~ m!default\s+printer.*\s+(\S+)\s*$!mg) { return $1; } else { die "No default printer defined, you have to specify a printer with \"-P\" or \"-d\"!\n"; }
}
sub pdq_check_printer { my $printer = $_[0];
# Read the help message of PDQ open PDQHELP, "pdq --help 2>&1 |"; $pdqhelp = join ("", <PDQHELP>); close PDQHELP;
# Search the appropriate printer entry return ($pdqhelp =~ m!^\s+$printer\s+\-\s+.*\s+\-\s*$!mg);
}
sub detect_spooler { # If tcp/localhost:631 opens, cups my $page = $db->getpage('http://localhost:631/', 1); if ($page =~ m!Common UNIX Printing System!) { return 'cups'; }
# 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'; } }
return undef; }
sub unimp { die "Sorry, $action for your spooler is unimplemented...\n"; }
sub help {
my $action = 'all'; # Set up the help message depending on how we were called if ($progname =~ m!^lpc!) { # 'lpc*' ==> control $action = 'control'; print STDERR <<EOF; Usage: $progname [ -s spooler ] [ -i ] command [ arguments ] or $progname -h EOF } elsif ($progname =~ m!^lprm!) { # 'lprm*' ==> remove jobs $action = 'remove'; print STDERR <<EOF; Usage: $progname [ -s spooler ] [ -P queuename ] [ - ] [ -i ] [ jobid1 jobid2 ... ] or $progname -h EOF } elsif ($progname =~ m!^lpq!) { # 'lpq*' ==> list jobs $action = 'query'; print STDERR <<EOF; Usage: $progname [ -s spooler ] [ -P queuename ] [ -i ] [ -a ] [ user1 user2 ... ] or $progname -h EOF } elsif (($progname =~ m!^lpr!) || ($progname =~ m!^lp!)) { # 'lpr*', 'lp*' ==> print $action = 'print'; print STDERR <<EOF; Usage: $progname [ -s spooler ] [ -P queuename ] \ [ -o option1=value1 -o option2 ... ] [ -i ] [ file1 file2 ... ] or $progname -S [ -s spooler ] [ -i ] or $progname -h [ -s spooler ] [ -P queuename ] [ -i ] EOF } else { # name does not determine the action print STDERR <<EOF; Usage: $progname [ -s spooler ] [ -P queuename ] \ [ -o option1=value1 -o option2 ... ] [ -i ] \ [ file1 file2 ... ] or $progname -Q [ -s spooler ] [ -P queuename ] [ -i ] [ -a ] \ [ user1 user2 ... ] or $progname -R [ -s spooler ] [ -P queuename ] [ - ] [ -i ] \ [ jobid1 jobid2 ... ] or $progname -C [ -s spooler ] [ -i ] command [ arguments ] or $progname -S [ -s spooler ] [ -i ] or $progname -h [ -s spooler ] [ -P queuename ] [ -i ] EOF }
print STDERR <<EOF;
-s spooler Explicit spooler type (cups,lpd,lprng,pdq) EOF
if ($action ne 'control') { print STDERR <<EOF; -P queuename Command should apply to this queue EOF }
if (($action eq 'print') || ($action eq 'all')) { print STDERR <<EOF; -o option=value Set option to value -o option Set the switch option -\# n Print n copies file1 file2 ... Files to be printed, when no file is given, standard input will be printed EOF }
if ($action eq 'all') { print STDERR <<EOF; -Q Query the jobs in a queue EOF }
if (($action eq 'query') || ($action eq 'all')) { print STDERR <<EOF; -a Query the jobs in all queues user1 user2 ... Users whose jobs should be listed EOF }
if ($action eq 'all') { print STDERR <<EOF; -R Remove a job from a queue EOF }
if (($action eq 'remove') || ($action eq 'all')) { print STDERR <<EOF; - Remove all your jobs jobid1 jobid2 IDs of the jobs to be removed EOF }
if ($action eq 'all') { print STDERR <<EOF; -C Execute control commands for queue/job manipulation EOF }
if (($action eq 'control') || ($action eq 'all')) { print STDERR <<EOF; command [ arguments ] Control command for queue/job manipulation. The commands are the ones of the BSD "lpc" utility. Use the control command "help" to get a list of supported commands. Note: the amount of commands varies with the spooler, but the same commands given under different spoolers do the same thing. EOF }
print STDERR <<EOF; -i Interactive mode: You will be asked if $progname is in doubt about something. Otherwise $progname uses auto-detection or quits with an error. EOF
if (($action eq 'print') || ($action eq 'all')) { print STDERR <<EOF; -S Save the chosen spooler as the default spooler -h Show this message or show a list of available options if a queue is specified
EOF } else { print STDERR <<EOF; -h Show this message
EOF }
exit 0; }
# Help on printer-specific options sub help_options { my ($config) = $_[0]; # Is there an easier way to do this? eval `foomatic-configure -P -n $config->{'queue'} -s $config->{'spooler'}`; print "Available options for queue $config->{'queue'}:\n"; foreach my $arg (@{$QUEUES[0]->{'args'}}) { next if $arg->{'hidden'}; my @vals = ();
print " $arg->{'name'} : < "; foreach my $val (@{$arg->{'vals'}}) { push @vals, $val->{'value'}; } print join(' | ', @vals) . " >\n"; }
exit 0; }
|