From dfae5860833782af557deb35e286d7e186fe3cf5 Mon Sep 17 00:00:00 2001 From: Roger Leigh Date: Sun, 26 Oct 2008 16:11:41 +0000 Subject: Imported Upstream version 4.3.99+cvs20050702 --- src/cups/cups-genppdupdate.in | 539 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 src/cups/cups-genppdupdate.in (limited to 'src/cups/cups-genppdupdate.in') 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 () { + 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 () { + 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 () { + $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; +} -- cgit v1.2.3