summaryrefslogtreecommitdiff
path: root/src/cups/cups-genppdupdate.in
diff options
context:
space:
mode:
authorRoger Leigh <rleigh@debian.org>2008-10-26 16:11:41 +0000
committerRoger Leigh <rleigh@debian.org>2008-10-26 16:11:41 +0000
commitdfae5860833782af557deb35e286d7e186fe3cf5 (patch)
treee3b4282ae08e120f78cd0c097f7cb3b570e94da2 /src/cups/cups-genppdupdate.in
parent3b59bb0a607ec27ea60f07d1cd5d1bbb4483c832 (diff)
Imported Upstream version 4.3.99+cvs20050702
Diffstat (limited to 'src/cups/cups-genppdupdate.in')
-rw-r--r--src/cups/cups-genppdupdate.in539
1 files changed, 539 insertions, 0 deletions
diff --git a/src/cups/cups-genppdupdate.in b/src/cups/cups-genppdupdate.in
new file mode 100644
index 0000000..c48723c
--- /dev/null
+++ b/src/cups/cups-genppdupdate.in
@@ -0,0 +1,539 @@
+#! @PERL@ -w
+# $Id: cups-genppdupdate.in,v 1.19 2005/04/30 11:48:26 rleigh Exp $
+# Update CUPS PPDs for Gutenprint queues.
+# Copyright (C) 2002-2003 Roger Leigh (rleigh@debian.org)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+use strict;
+use Getopt::Std;
+use Fcntl qw(:mode);
+use File::Temp qw(:POSIX);
+use File::Copy qw(mv);
+
+sub parse_options ();
+sub update_ppd ($); # Original PPD filename
+sub find_ppd ($$$$); # Gutenprint Filename, driver, language (e.g. en, sv),
+ # region (e.g. GB, DE)
+sub get_default_types (*); # Source PPD FH
+sub get_defaults (*); # Source PPD FH
+sub get_options (*\%); # Source PPD FH, default_types hash ref
+
+our $opt_d; # Debug mode
+our $opt_h; # Help
+our $opt_n; # No action
+our $opt_q; # Quiet mode
+our $opt_s; # Source PPD location
+our $opt_v; # Verbose mode
+
+my $debug = 0;
+my $verbose = 0; # Verbose output
+if ($debug) {
+ $verbose = 1;
+}
+my $quiet = 0; # No output
+my $no_action = 0; # Don't output files
+my $version = "@GUTENPRINT_MAJOR_VERSION@.@GUTENPRINT_MINOR_VERSION@";
+
+my $ppd_dir = "@cups_conf_serverroot@/ppd"; # Location of in-use CUPS PPDs
+my $ppd_root_dir = "@cups_conf_datadir@/model";
+my $ppd_base_dir = "$ppd_root_dir/gutenprint/$version"; # Available PPDs
+my $gzext = ".gz";
+my $updated_ppd_count = 0;
+
+my @ppd_files; # A list of in-use Gutenprint PPD files
+
+# Used to convert a language name to its two letter code
+my %languagemappings = (
+ "chinese" => "cn",
+ "danish" => "da",
+ "dutch" => "nl",
+ "english" => "en",
+ "finnish" => "fi",
+ "french" => "fr",
+ "german" => "de",
+ "greek" => "el",
+ "italian" => "it",
+ "japanese" => "jp",
+ "norwegian" => "no",
+ "polish" => "pl",
+ "portuguese" => "pt",
+ "russian" => "ru",
+ "slovak" => "sk",
+ "spanish" => "es",
+ "swedish" => "sv",
+ "turkish" => "tr"
+);
+
+
+# Check command-line options...
+
+parse_options();
+
+
+# Set a secure umask...
+
+umask 0177;
+
+
+# Find all in-use Gutenprint PPD files...
+
+my @ppdglob = glob("$ppd_dir/*.{ppd,PPD}");
+my $ppdlist = join ' ', @ppdglob;
+if (@ppdglob) {
+ open PPDFILES, "egrep -i -l \"Gutenprint|Gimp-Print\" $ppdlist|" or die "can't grep $ppd_dir/*: $!";
+ while (<PPDFILES>) {
+ chomp;
+ push @ppd_files, $_;
+ }
+ close PPDFILES or ($! == 0) or die "can't close grep pipe: $!";
+}
+
+
+# Exit if there are not files to update...
+
+if (!@ppd_files) {
+ print STDOUT "No Gutenprint PPD files to update.\n";
+ exit (0);
+}
+
+# Update each of the Gutenprint PPDs, where possible...
+
+foreach (@ppd_files) {
+ $updated_ppd_count += update_ppd($_);
+
+}
+
+if (!$quiet || $verbose) {
+ if ($updated_ppd_count > 0) {
+ print STDOUT "Updated $updated_ppd_count PPD files. Restart cupsd for the changes to take effect.\n";
+ exit (0);
+ } else {
+ print STDOUT "Failed to update any PPD files\n";
+ exit (0);
+ }
+}
+
+
+
+sub parse_options () {
+ getopts("dhnqs:v");
+
+ if ($opt_n) {
+ $no_action = 1;
+ }
+ if ($opt_d) {
+ $debug = 1;
+ }
+ if ($opt_s) {
+ if (-d $opt_s) {
+ $ppd_base_dir = "$opt_s";
+ }
+ else {
+ die "$opt_s: invalid directory: $!";
+ }
+ }
+ if ($opt_v) {
+ $verbose = 1;
+ $quiet = 0;
+ }
+ if ($opt_q) {
+ $verbose = 0;
+ $quiet = 1;
+ }
+ if ($opt_h) {
+ print "Usage: $0 [OPTION]...\n";
+ print "Update CUPS+Gutenprint PPD files.\n\n";
+ print " -d Enable debugging\n";
+ print " -h Display this help text\n";
+ print " -n No-action. Don't overwrite any PPD files.\n";
+ print " -q Quiet mode. No messages except errors.\n";
+ print " -s ppd_dir Use ppd_dir as the source PPD directory.\n";
+ print " -v Verbose messages.\n";
+ exit (0);
+ }
+}
+
+
+# Update the named PPD file.
+sub update_ppd ($) {
+ my $ppd_source_filename = $_;
+
+ open ORIG, $_ or die "$_: can't open PPD file: $!";
+ seek (ORIG, 0, 0) or die "can't seek to start of PPD file";
+ if ($debug) {
+ print "Source Filename: $ppd_source_filename\n";
+ }
+ # Get the `PCFileName'; the new source PPD will have the same name.
+ my ($filename) = "";
+ my ($driver) = "";
+ my ($gutenprintdriver) = "";
+ my ($locale) = "";
+ my ($lingo) = "";
+ my ($region) = "";
+ my ($valid) = 0;
+ while (<ORIG>) {
+ if (/\*StpLocale:/) {
+ ($locale) = m/^\*StpLocale:\s\"*(.*)\"$/;
+ $valid = 1;
+ }
+ if (/\*LanguageVersion/) {
+ ($lingo) = m/^\*LanguageVersion:\s*(.*)$/;
+ }
+ if (/^\*StpDriverName:/ ) {
+ ($driver) = m/^\*StpDriverName:\s*\"(.*)\"$/;
+ $valid = 1;
+ }
+ if (/\*%End of / && $driver eq "") {
+ ($driver) = m/^\*%End of\s*(.*).ppd$/;
+ }
+ if (/^\*StpPPDLocation:/ ) {
+ ($filename) = m/^\*StpPPDLocation:\s*\"(.*)\"$/;
+ $valid = 1;
+ }
+ if (/^\*%Gutenprint Filename:/) {
+ $valid = 1;
+ }
+ }
+ if (! $valid) {
+ print STDERR "$ppd_source_filename: this PPD file cannot be upgraded automatically (only files based on Gutenprint 4.3.21 and newer can be)\n";
+ return 0;
+ }
+ if ($debug) {
+ print "Gutenprint Filename: $filename\n";
+ print "Locale: $locale\n";
+ print "Language: $lingo\n";
+ print "Driver: $driver\n";
+ }
+ if ($locale) {
+ # Split into the language and territory.
+ ($locale, $region) = split(/-/, $locale);
+ } else {
+ # Split into the language and territory.
+ ($locale, $region) = split(/-/, $lingo);
+ # Convert language into language code.
+ $locale = $languagemappings{"\L$lingo"};
+ if (!defined($locale)) {
+ $locale = "C"; # Fallback if there isn't one.
+ }
+ }
+ if (! defined($region)) {
+ $region = "";
+ }
+ if ($debug) {
+ print "Locale: $locale\n";
+ print "Region: $region\n";
+ }
+
+ # Search for a PPD matching our criteria...
+
+ my $source = find_ppd($filename, $driver, $locale, $region);
+ if (!defined($source)) {
+ # There wasn't a valid source PPD file, so give up.
+ print STDERR "$ppd_source_filename: no valid candidate for replacement. Skipping\n";
+ print STDERR "$ppd_source_filename: please upgrade this PPD manually\n";
+ return 0;
+ }
+ if ($debug) {
+ print "Candidate PPD: $source\n";
+ }
+
+
+ # Read in the new PPD, decompressing it if needed...
+
+ my $source_data;
+
+ my $suffix = "\\" . $gzext; # Add '\', so m// matches the '.'.
+ if ($source =~ m/.gz$/) { # Decompress input buffer
+ open GZIN, "gunzip -c $source |"
+ or die "$_: can't open for decompression: $!";
+ while (<GZIN>) {
+ $source_data .= $_;
+ }
+ close GZIN;
+ }
+ else {
+ open SOURCE, $source
+ or die "$source: can't open source file: $!";
+ binmode SOURCE;
+ my $source_size = (stat(SOURCE))[7];
+ read (SOURCE, $source_data, $source_size)
+ or die "$source: error reading source: $!";
+ close SOURCE or die "$source: can't close file: $!";
+ }
+
+ # Save new PPD in a temporary file, for processing...
+
+ my($tmpfile, $tmpfilename) = tmpnam();
+ chown(0, 0, $tmpfilename); # root.root
+ chmod(0644, $tmpfilename);
+ unlink $tmpfilename or warn "can't unlink temporary file $tmpfile: $!\n";
+ print $tmpfile $source_data;
+
+
+
+
+ # Extract the default values from the original PPD...
+
+ my %orig_default_types = get_default_types(ORIG);
+ my %new_default_types = get_default_types($tmpfile);
+ my %defaults = get_defaults(ORIG);
+ my %options = get_options($tmpfile, %new_default_types);
+
+
+ # Close original and temporary files...
+
+ close ORIG or die "$_: can't close file: $!";
+ close $tmpfile or die "can't close temporary file $tmpfile: $!";
+
+
+ if ($debug) {
+ print "Original Default Types:\n";
+ foreach (sort keys %orig_default_types) {
+ print " $_: $orig_default_types{$_}\n";
+ }
+ print "New Default Types:\n";
+ foreach (sort keys %new_default_types) {
+ print " $_: $new_default_types{$_}\n";
+ }
+ print "Defaults:\n";
+ foreach (sort keys %defaults) {
+ print " $_: $defaults{$_}\n";
+ }
+ print "Options:\n";
+ foreach (sort keys %options) {
+ print " $_: ";
+ foreach my $opt (@{$options{$_}}) {
+ print "$opt ";
+ }
+ print "\n";
+ }
+
+ }
+
+ # Update source buffer with old defaults...
+
+ # Loop through each default in turn.
+default_loop:
+ foreach (sort keys %defaults) {
+ my $default_option = $_;
+ my $option;
+ ($option = $_) =~ s/Default//; # Strip off `Default'
+ # Check method is valid
+ my $orig_method = $orig_default_types{$option};
+ my $new_method = $new_default_types{$option};
+ if ((!defined($orig_method) || !defined($new_method)) ||
+ $orig_method ne $new_method) {
+ next;
+ }
+ if ($new_method eq "PickOne") {
+ # Check the old setting is valid
+ foreach (@{$options{$option}}) {
+ if ($defaults{$default_option} eq $_) { # Valid option
+ # Set the option in the new PPD
+ $source_data =~ s/\*($default_option).*/*$1:$defaults{$default_option}/m;
+ if ($verbose) {
+ print "$ppd_source_filename: Set *$default_option to $defaults{$default_option}\n";
+ }
+ next default_loop;
+ }
+ }
+ printf STDERR
+ "$ppd_source_filename: Invalid option: *$default_option: $defaults{$default_option}. Skipped.\n";
+ next;
+ }
+ print STDERR
+ "$ppd_source_filename: PPD OpenUI method $new_default_types{$_} not understood. Skipped\n";
+ }
+
+
+ # Write new PPD...
+
+ my $tmpnew = "${ppd_source_filename}.new";
+ if (! open NEWPPD, "> $tmpnew") {
+ warn "Can't open $tmpnew for writing: $!\n";
+ return 0;
+ }
+ chown(0, 0, $tmpnew); # Bad idea to hardcode this...
+ chmod(0644, $tmpnew); # Bad idea to hardcode this...
+ print NEWPPD $source_data;
+ if (! close NEWPPD) {
+ warn "Can't close ${tmpnew}.new for writing: $!\n";
+ unlink $tmpnew;
+ return 0;
+ }
+
+ if (! rename $tmpnew, $ppd_source_filename) {
+ warn "Can't rename $tmpnew to $ppd_source_filename: $!\n";
+ unlink $tmpnew;
+ return 0;
+ }
+
+ if (!$quiet || $verbose) {
+ print STDOUT "Updated $ppd_source_filename using $source\n";
+ }
+ return 1;
+ # All done!
+}
+
+# Find a suitable source PPD file
+sub find_ppd ($$$$) {
+ my($gutenprintfilename, $drivername, $lang, $region) = @_;
+ my $file; # filename to return
+ my ($key) = '^\\*FileVersion:[ ]*"@VERSION@"$';
+ my ($lingo, $suffix, $base, $basedir);
+ my ($current_best_file, $current_best_time);
+ my ($stored_name, $stored_dir);
+ $stored_name = $gutenprintfilename;
+ $stored_name =~ s,.*/([^/]*)(.gz)?$,$1,;
+ $stored_dir = $gutenprintfilename;
+ $stored_dir =~ s,(.*)/([^/]*)$,$1,;
+
+ $current_best_file = "";
+ $current_best_time = 0;
+
+ # All possible candidates, in order of usefulness and gzippedness
+ foreach $lingo ("${lang}_${region}/",
+ "$lang/",
+ "en/",
+ "C/",
+ "") {
+ foreach $suffix (".ppd$gzext",
+ ".ppd") {
+ foreach $base ("${drivername}.$version",
+ "stp-${drivername}.$version",
+ $stored_name,
+ $drivername) {
+ foreach $basedir ($ppd_base_dir,
+ $stored_dir,
+ $ppd_root_dir) {
+ if (! $basedir || ! $base) { next; }
+ my ($fn) = "$basedir/$lingo$base$suffix";
+ if ($debug) {
+ print "Trying $fn for $gutenprintfilename, $lang, $region\n";
+ }
+# Check that it is a regular file, owned by root.root, not writable
+# by other, and is readable by root. i.e. the file is secure.
+ my @sb = stat $fn or next;
+ if (S_ISREG($sb[2]) && ($sb[4] == 0) && ($sb[5] == 0)) {
+# !(S_IWOTH & $sb[2]) && (S_IRUSR & $sb[2])) {
+ # Check that the file is a valid Gutenprint PPD file
+ # of the correct version.
+ my $file_version;
+ if ($fn =~ m/\.gz$/) {
+ $file_version = `gunzip -c $fn | grep '$key'`;
+ } else {
+ $file_version = `cat $fn | grep '$key'`;
+ }
+ if ($file_version ne "") {
+ if ($debug) {
+ print " Format valid: time $sb[9] best $current_best_time prev $current_best_file cur $fn!\n";
+ }
+ if ($sb[9] > $current_best_time) {
+ $current_best_time = $sb[9];
+ $current_best_file = $fn;
+ }
+ } elsif ($debug) {
+ print " Format invalid\n";
+ }
+ }
+ else {
+ $_ = $fn;
+ if (! -d $fn && ! /\/$/) {
+ print STDERR "$fn: not a regular file, or insecure ownership and permissions. Skipped\n";
+ }
+ }
+ }
+ }
+ }
+ }
+ if ($current_best_file) {
+ return $current_best_file;
+ }
+# Yikes! Cannot find a valid PPD file!
+ return undef;
+}
+
+# Return the default options from the given PPD filename
+sub get_default_types(*) {
+ my $fh = $_[0];
+ my %default_types;
+
+ # Read each line of the original PPD file, and store all OpenUI
+ # names and their types in a hash...
+ seek ($fh, 0, 0) or die "can't seek to start of PPD file";
+ while (<$fh>) {
+ if ( m/^\*OpenUI/ ) {
+ chomp;
+ my ($key, $value) = /^\*OpenUI\s\*([[:alnum:]]+).*:\s([[:alnum:]]+)/;
+ if ($key && $value) {
+ $default_types{$key}=$value;
+ }
+ }
+ }
+ return %default_types;
+}
+
+
+# Return the default options from the given PPD filename
+sub get_defaults(*) {
+ my $fh = $_[0];
+ my %defaults;
+
+ # Read each line of the original PPD file, and store all default
+ # names and their values in a hash...
+ seek ($fh, 0, 0) or die "can't seek to start of PPD file";
+ while (<$fh>) {
+ if ( m/^\*Default/ ) {
+ chomp;
+ my($key, $value) = /^\*([[:alnum:]]+):\s*([[:alnum:]]+)/;
+ if ($key && $value) {
+ $defaults{$key}=$value;
+ }
+ }
+ }
+ return %defaults;
+}
+
+
+# Return the available options from the given PPD filename
+sub get_options(*\%) {
+ my $fh = $_[0];
+ my $validopts = $_[1];
+ my %options;
+
+ # For each valid option name, grab each valid option for that name
+ # and store in a hash of arrays...
+
+ foreach (sort keys %$validopts) {
+ my $tmp = $_;
+ my @optionlist;
+
+ seek ($fh, 0, 0) or die "can't seek to start of PPD file";
+ while (<$fh>) {
+ if ( m/^\*$tmp/ ) {
+ chomp;
+ my ($value) = /^\*$tmp\s*([[:alnum:]]+)[\/:]/;
+ if ($value) {
+ push @optionlist, $value;
+ }
+ }
+ }
+ if (@optionlist) {
+ $options{$tmp} = [ @optionlist ];
+ }
+ }
+ return %options;
+}