home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / sbin / cups-genppdupdate.5.0 < prev    next >
Encoding:
Text File  |  2006-05-03  |  14.1 KB  |  538 lines

  1. #! /usr/bin/perl -w
  2. # $Id: cups-genppdupdate.in,v 1.22 2005/12/24 22:41:11 rlk Exp $
  3. # Update CUPS PPDs for Gutenprint queues.
  4. # Copyright (C) 2002-2003 Roger Leigh (rleigh@debian.org)
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2, or (at your option)
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19.  
  20. use strict;
  21. use Getopt::Std;
  22. use Fcntl qw(:mode);
  23. use File::Temp qw(:POSIX);
  24. use File::Copy qw(mv);
  25.  
  26. sub parse_options ();
  27. sub update_ppd ($); # Original PPD filename
  28. sub find_ppd ($$$$); # Gutenprint Filename, driver, language (e.g. en, sv),
  29.              # region (e.g. GB, DE)
  30. sub get_default_types (*); # Source PPD FH
  31. sub get_defaults (*); # Source PPD FH
  32. sub get_options (*\%); # Source PPD FH, default_types hash ref
  33.  
  34. our $opt_d; # Debug mode
  35. our $opt_h; # Help
  36. our $opt_n; # No action
  37. our $opt_q; # Quiet mode
  38. our $opt_s; # Source PPD location
  39. our $opt_v; # Verbose mode
  40.  
  41. my $debug = 0;
  42. my $verbose = 0;   # Verbose output
  43. if ($debug) {
  44.     $verbose = 1;
  45. }
  46. my $quiet = 0;     # No output
  47. my $no_action = 0; # Don't output files
  48. my $version = "5.0";
  49.  
  50. my $ppd_dir = "/etc/cups/ppd"; # Location of in-use CUPS PPDs
  51. my $ppd_root_dir = "/usr/share/ppd";
  52. my $ppd_base_dir = "$ppd_root_dir/gutenprint/$version"; # Available PPDs
  53. my $gzext = ".gz";
  54. my $updated_ppd_count = 0;
  55.  
  56. my @ppd_files; # A list of in-use Gutenprint PPD files
  57.  
  58. # Used to convert a language name to its two letter code
  59. my %languagemappings = (
  60.             "chinese"    => "cn",
  61.             "danish"     => "da",
  62.             "dutch"      => "nl",
  63.             "english"    => "en",
  64.             "finnish"    => "fi",
  65.             "french"     => "fr",
  66.             "german"     => "de",
  67.             "greek"      => "el",
  68.             "italian"    => "it",
  69.             "japanese"   => "jp",
  70.             "norwegian"  => "no",
  71.             "polish"     => "pl",
  72.             "portuguese" => "pt",
  73.             "russian"    => "ru",
  74.             "slovak"     => "sk",
  75.             "spanish"    => "es",
  76.             "swedish"    => "sv",
  77.             "turkish"    => "tr"
  78. );
  79.  
  80.  
  81. # Check command-line options...
  82.  
  83. parse_options();
  84.  
  85.  
  86. # Set a secure umask...
  87.  
  88. umask 0177;
  89.  
  90.  
  91. # Find all in-use Gutenprint PPD files...
  92.  
  93. my @ppdglob = glob("$ppd_dir/*.{ppd,PPD}");
  94. my $ppdlist = join ' ', @ppdglob;
  95. if (@ppdglob) {
  96.     open PPDFILES, '-|', 'egrep', '-i', '-l', 'Gutenprint|Gimp-Print', @ppdglob or die "can't grep $ppd_dir/*: $!";
  97.     while (<PPDFILES>) {
  98.     chomp;
  99.     push @ppd_files,  $_;
  100.     }
  101.     close PPDFILES or ($! == 0) or die "can't close grep pipe: $!";
  102. }
  103.  
  104.  
  105. # Exit if there are not files to update...
  106.  
  107. if (!@ppd_files) {
  108.     print STDOUT "No Gutenprint PPD files to update.\n";
  109.     exit (0);
  110. }
  111.  
  112. # Update each of the Gutenprint PPDs, where possible...
  113.  
  114. foreach (@ppd_files) {
  115.     $updated_ppd_count += update_ppd($_);
  116.  
  117. }
  118.  
  119. if (!$quiet || $verbose) {
  120.     if ($updated_ppd_count > 0) {
  121.     print STDOUT "Updated $updated_ppd_count PPD files.  Restart cupsd for the changes to take effect.\n";
  122.     exit (0);
  123.     } else {
  124.     print STDOUT "Failed to update any PPD files\n";
  125.     exit (0);
  126.     }
  127. }
  128.  
  129.  
  130.  
  131. sub parse_options () {
  132.     getopts("dhnqs:v");
  133.  
  134.     if ($opt_n) {
  135.     $no_action = 1;
  136.     }
  137.     if ($opt_d) {
  138.     $debug = 1;
  139.     }
  140.     if ($opt_s) {
  141.     if (-d $opt_s) {
  142.         $ppd_base_dir = "$opt_s";
  143.     }
  144.     else {
  145.         die "$opt_s: invalid directory: $!";
  146.     }
  147.     }
  148.     if ($opt_v) {
  149.     $verbose = 1;
  150.     $quiet = 0;
  151.     }
  152.     if ($opt_q) {
  153.     $verbose = 0;
  154.     $quiet = 1;
  155.     }
  156.     if ($opt_h) {
  157.     print "Usage: $0 [OPTION]...\n";
  158.     print "Update CUPS+Gutenprint PPD files.\n\n";
  159.         print "  -d          Enable debugging\n";
  160.         print "  -h          Display this help text\n";
  161.     print "  -n          No-action.  Don't overwrite any PPD files.\n";
  162.     print "  -q          Quiet mode.  No messages except errors.\n";
  163.     print "  -s ppd_dir  Use ppd_dir as the source PPD directory.\n";
  164.     print "  -v          Verbose messages.\n";
  165.     exit (0);
  166.     }
  167. }
  168.  
  169.  
  170. # Update the named PPD file.
  171. sub update_ppd ($) {
  172.     my $ppd_source_filename = $_;
  173.  
  174.     open ORIG, $_ or die "$_: can't open PPD file: $!";
  175.     seek (ORIG, 0, 0) or die "can't seek to start of PPD file";
  176.     my @orig_metadata = stat(ORIG);
  177.     if ($debug) {
  178.     print "Source Filename: $ppd_source_filename\n";
  179.     }
  180.     # Get the `PCFileName'; the new source PPD will have the same name.
  181.     my ($filename) = "";
  182.     my ($driver) = "";
  183.     my ($gutenprintdriver) = "";
  184.     my ($locale) = "";
  185.     my ($lingo) = "";
  186.     my ($region) = "";
  187.     my ($valid) = 0;
  188.     while (<ORIG>) {
  189.     if (/\*StpLocale:/) {
  190.         ($locale) = m/^\*StpLocale:\s\"*(.*)\"$/;
  191.         $valid = 1;
  192.     }
  193.     if (/\*LanguageVersion/) {
  194.         ($lingo) = m/^\*LanguageVersion:\s*(.*)$/;
  195.     }
  196.     if (/^\*StpDriverName:/ ) {
  197.         ($driver) = m/^\*StpDriverName:\s*\"(.*)\"$/;
  198.         $valid = 1;
  199.     }
  200.     if (/\*%End of / && $driver eq "") {
  201.         ($driver) = m/^\*%End of\s*(.*).ppd$/;
  202.     }
  203.     if (/^\*StpPPDLocation:/ ) {
  204.         ($filename) = m/^\*StpPPDLocation:\s*\"(.*)\"$/;
  205.         $valid = 1;
  206.     }
  207.     if (/^\*%Gutenprint Filename:/) {
  208.         $valid = 1;
  209.     }
  210.     }
  211.     if (! $valid) {
  212.     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";
  213.     return 0;
  214.     }
  215.     if ($debug) {
  216.     print "Gutenprint Filename: $filename\n";
  217.     print "Locale: $locale\n";
  218.     print "Language: $lingo\n";
  219.     print "Driver: $driver\n";
  220.     }
  221.     if ($locale) {
  222.     # Split into the language and territory.
  223.     ($locale, $region) = split(/-/, $locale);
  224.     } else {
  225.     # Split into the language and territory.
  226.     ($locale, $region) = split(/-/, $lingo);
  227.     # Convert language into language code.
  228.     $locale = $languagemappings{"\L$lingo"};
  229.     if (!defined($locale)) {
  230.         $locale = "C"; # Fallback if there isn't one.
  231.     }
  232.     }
  233.     if (! defined($region)) {
  234.     $region = "";
  235.     }
  236.     if ($debug) {
  237.     print "Locale: $locale\n";
  238.     print "Region: $region\n";
  239.     }
  240.  
  241.     # Search for a PPD matching our criteria...
  242.  
  243.     my $source = find_ppd($filename, $driver, $locale, $region);
  244.     if (!defined($source)) {
  245.         # There wasn't a valid source PPD file, so give up.
  246.         print STDERR "$ppd_source_filename: no valid candidate for replacement.  Skipping\n";
  247.         print STDERR "$ppd_source_filename: please upgrade this PPD manually\n";
  248.     return 0;
  249.     }
  250.     if ($debug) {
  251.     print "Candidate PPD: $source\n";
  252.     }
  253.  
  254.  
  255.     # Read in the new PPD, decompressing it if needed...
  256.  
  257.     my $source_data;
  258.  
  259.     my $suffix = "\\" . $gzext; # Add '\', so m// matches the '.'.
  260.     if ($source =~ m/.gz$/) { # Decompress input buffer
  261.     open GZIN, "gunzip -c $source |"
  262.         or die "$_: can't open for decompression: $!";
  263.     while (<GZIN>) {
  264.         $source_data .= $_;
  265.     }
  266.     close GZIN;
  267.     }
  268.     else {
  269.     open SOURCE, $source
  270.         or die "$source: can't open source file: $!";
  271.     binmode SOURCE;
  272.     my $source_size = (stat(SOURCE))[7];
  273.     read (SOURCE, $source_data, $source_size)
  274.         or die "$source: error reading source: $!";
  275.     close SOURCE or die "$source: can't close file: $!";
  276.     }
  277.  
  278.     # Save new PPD in a temporary file, for processing...
  279.  
  280.     my($tmpfile, $tmpfilename) = tmpnam();
  281.     unlink $tmpfilename or warn "can't unlink temporary file $tmpfile: $!\n";
  282.     print $tmpfile $source_data;
  283.  
  284.  
  285.  
  286.  
  287.     # Extract the default values from the original PPD...
  288.  
  289.     my %orig_default_types = get_default_types(ORIG);
  290.     my %new_default_types = get_default_types($tmpfile);
  291.     my %defaults = get_defaults(ORIG);
  292.     my %options = get_options($tmpfile, %new_default_types);
  293.  
  294.  
  295.     # Close original and temporary files...
  296.  
  297.     close ORIG or die "$_: can't close file: $!";
  298.     close $tmpfile or die "can't close temporary file $tmpfile: $!";
  299.  
  300.  
  301.     if ($debug) {
  302.     print "Original Default Types:\n";
  303.     foreach (sort keys %orig_default_types) {
  304.         print "  $_: $orig_default_types{$_}\n";
  305.     }
  306.     print "New Default Types:\n";
  307.     foreach (sort keys %new_default_types) {
  308.         print "  $_: $new_default_types{$_}\n";
  309.     }
  310.     print "Defaults:\n";
  311.     foreach (sort keys %defaults) {
  312.         print "  $_: $defaults{$_}\n";
  313.     }
  314.     print "Options:\n";
  315.     foreach (sort keys %options) {
  316.         print "  $_:  ";
  317.         foreach my $opt (@{$options{$_}}) {
  318.         print "$opt ";
  319.         }
  320.         print "\n";
  321.     }
  322.  
  323.     }
  324.  
  325.     # Update source buffer with old defaults...
  326.  
  327.     # Loop through each default in turn.
  328. default_loop:
  329.     foreach (sort keys %defaults) {
  330.     my $default_option = $_;
  331.     my $option;
  332.     ($option = $_) =~ s/Default//; # Strip off `Default'
  333.     # Check method is valid
  334.     my $orig_method = $orig_default_types{$option};
  335.     my $new_method = $new_default_types{$option};
  336.     if ((!defined($orig_method) || !defined($new_method)) ||
  337.         $orig_method ne $new_method) {
  338.         next;
  339.     }
  340.     if ($new_method eq "PickOne") {
  341.             # Check the old setting is valid
  342.         foreach (@{$options{$option}}) {
  343.         if ($defaults{$default_option} eq $_) { # Valid option
  344.             # Set the option in the new PPD
  345.             $source_data =~ s/\*($default_option).*/*$1:$defaults{$default_option}/m;
  346.             if ($verbose) {
  347.             print "$ppd_source_filename: Set *$default_option to $defaults{$default_option}\n";
  348.             }
  349.             next default_loop;
  350.         }
  351.         }
  352.         printf STDERR
  353.         "$ppd_source_filename: Invalid option: *$default_option: $defaults{$default_option}.  Skipped.\n";
  354.         next;
  355.     }
  356.     print STDERR
  357.         "$ppd_source_filename: PPD OpenUI method $new_default_types{$_} not understood.  Skipped\n";
  358.     }
  359.  
  360.  
  361.     # Write new PPD...
  362.  
  363.     my $tmpnew = "${ppd_source_filename}.new";
  364.     if (! open NEWPPD, "> $tmpnew") {
  365.     warn "Can't open $tmpnew for writing: $!\n";
  366.     return 0;
  367.     }
  368.     print NEWPPD $source_data;
  369.     if (! close NEWPPD) {
  370.     warn "Can't close ${tmpnew}.new for writing: $!\n";
  371.     unlink $tmpnew;
  372.     return 0;
  373.     }
  374.  
  375.     if (! rename $tmpnew, $ppd_source_filename) {
  376.     warn "Can't rename $tmpnew to $ppd_source_filename: $!\n";
  377.     unlink $tmpnew;
  378.     return 0;
  379.     }
  380.     chown($orig_metadata[4], $orig_metadata[5], $ppd_source_filename);
  381.     chmod(($orig_metadata[2] & 0777), $ppd_source_filename);
  382.  
  383.     if (!$quiet || $verbose) {
  384.     print STDOUT "Updated $ppd_source_filename using $source\n";
  385.     }
  386.     return 1;
  387.     # All done!
  388. }
  389.  
  390. # Find a suitable source PPD file
  391. sub find_ppd ($$$$) {
  392.     my($gutenprintfilename, $drivername, $lang, $region) = @_;
  393.     my $file; # filename to return
  394.     my ($key) = '^\\*FileVersion:[     ]*"5.0.0-rc2"$';
  395.     my ($lingo, $suffix, $base, $basedir);
  396.     my ($current_best_file, $current_best_time);
  397.     my ($stored_name, $stored_dir);
  398.     $stored_name = $gutenprintfilename;
  399.     $stored_name =~ s,.*/([^/]*)(.gz)?$,$1,;
  400.     $stored_dir = $gutenprintfilename;
  401.     $stored_dir =~ s,(.*)/([^/]*)$,$1,;
  402.  
  403.     $current_best_file = "";
  404.     $current_best_time = 0;
  405.  
  406.     # All possible candidates, in order of usefulness and gzippedness
  407.     foreach $lingo ("${lang}_${region}/",
  408.             "$lang/",
  409.             "en/",
  410.             "C/",
  411.             "") {
  412.     foreach $suffix (".ppd$gzext",
  413.              ".ppd") {
  414.         foreach $base ("${drivername}.$version",
  415.                            "stp-${drivername}.$version",
  416.                $stored_name,
  417.                $drivername) {
  418.         foreach $basedir ($ppd_base_dir,
  419.                   $stored_dir,
  420.                   $ppd_root_dir) {
  421.                     if (! $basedir || ! $base) { next; }
  422.             my ($fn) = "$basedir/$lingo$base$suffix";
  423.             if ($debug) {
  424.                         print "Trying $fn for $gutenprintfilename, $lang, $region\n";
  425.                     }
  426. # Check that it is a regular file, owned by root.root, not writable
  427. # by other, and is readable by root.  i.e. the file is secure.
  428.             my @sb = stat $fn or next;
  429.             if (S_ISREG($sb[2]) && ($sb[4] == 0)) {
  430.             # Check that the file is a valid Gutenprint PPD file
  431.             # of the correct version.
  432.             my $file_version;
  433.             if ($fn =~ m/\.gz$/) {
  434.                 $file_version = `gunzip -c $fn | grep '$key'`;
  435.             } else {
  436.                 $file_version = `cat $fn | grep '$key'`;
  437.             }
  438.             if ($file_version ne "") {
  439.                             if ($debug) {
  440.                     print "   Format valid: time $sb[9] best $current_best_time prev $current_best_file cur $fn!\n";
  441.                 }
  442.                 if ($sb[9] > $current_best_time) {
  443.                 $current_best_time = $sb[9];
  444.                 $current_best_file = $fn;
  445.                 }
  446.             } elsif ($debug) {
  447.                 print "   Format invalid\n";
  448.             }
  449.             }
  450.             else {
  451.             $_ = $fn;
  452.             if (! -d $fn && ! /\/$/) {
  453.                 print STDERR "$fn: not a regular file, or insecure ownership and permissions.  Skipped\n";
  454.             }
  455.             }
  456.         }
  457.         }
  458.     }
  459.     }
  460.     if ($current_best_file) {
  461.         return $current_best_file;
  462.     }
  463. # Yikes!  Cannot find a valid PPD file!
  464.     return undef;
  465. }
  466.  
  467. # Return the default options from the given PPD filename
  468. sub get_default_types(*) {
  469.     my $fh = $_[0];
  470.     my %default_types;
  471.  
  472.     # Read each line of the original PPD file, and store all OpenUI
  473.     # names and their types in a hash...
  474.     seek ($fh, 0, 0) or die "can't seek to start of PPD file";
  475.     while (<$fh>) {
  476.     if ( m/^\*OpenUI/ ) {
  477.         chomp;
  478.         my ($key, $value) = /^\*OpenUI\s\*([[:alnum:]]+).*:\s([[:alnum:]]+)/;
  479.         if ($key && $value) {
  480.         $default_types{$key}=$value;
  481.         }
  482.     }
  483.     }
  484.     return %default_types;
  485. }
  486.  
  487.  
  488. # Return the default options from the given PPD filename
  489. sub get_defaults(*) {
  490.     my $fh = $_[0];
  491.     my %defaults;
  492.  
  493.     # Read each line of the original PPD file, and store all default
  494.     # names and their values in a hash...
  495.     seek ($fh, 0, 0) or die "can't seek to start of PPD file";
  496.     while (<$fh>) {
  497.     if ( m/^\*Default/ ) {
  498.         chomp;
  499.         my($key, $value) = /^\*([[:alnum:]]+):\s*([[:alnum:]]+)/;
  500.         if ($key && $value) {
  501.         $defaults{$key}=$value;
  502.         }
  503.     }
  504.     }
  505.     return %defaults;
  506. }
  507.  
  508.  
  509. # Return the available options from the given PPD filename
  510. sub get_options(*\%) {
  511.     my $fh = $_[0];
  512.     my $validopts = $_[1];
  513.     my %options;
  514.  
  515.     # For each valid option name, grab each valid option for that name
  516.     # and store in a hash of arrays...
  517.  
  518.     foreach (sort keys %$validopts) {
  519.     my $tmp = $_;
  520.     my @optionlist;
  521.  
  522.     seek ($fh, 0, 0) or die "can't seek to start of PPD file";
  523.     while (<$fh>) {
  524.         if ( m/^\*$tmp/ ) {
  525.         chomp;
  526.         my ($value) = /^\*$tmp\s*([[:alnum:]]+)[\/:]/;
  527.         if ($value) {
  528.             push @optionlist, $value;
  529.         }
  530.         }
  531.     }
  532.     if (@optionlist) {
  533.         $options{$tmp} = [ @optionlist ];
  534.     }
  535.     }
  536.     return %options;
  537. }
  538.