#!/usr/bin/perl

#
# This script adds PJL options, polled from the printer, into the Foomatic
# database. Use "foomatic-getpjloptions" to poll the options, use
# "foomatic-addpjloptions -h" for help.
#
 
#
# Till Kamppeter (till.kamppeter@gmx.net)
#
# Copyright 2001 Till Kamppeter
#
# This software may be freely redistributed under the terms of the GNU
# General Public License (http://www.gnu.org/).
#
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
#
 
use strict;
use FileHandle;

my $dir = "./foomatic-db/opt";

sub usage(){
    print STDERR <<EOF;
Usage: foomatic-addpjloptions [-dafis][-o option_dir] -p printer_ID [<pjl option file>]
       foomatic-addpjloptions [-dqaf] [<pjl option file>]
       foomatic-addpjloptions -h

   -a                 Include all options
   -d                 enable debugging output
   -f                 Do not ask user whether he really wants to add the
                      PJL options
   -i                 Include only important options
   -s                 Create a single option file
   -q                 Summary format
   -h                 This help message
   -p printer_ID      Printer where to add the PJL options
   -o option_dir      directory for saving option files (default $dir)
   <pjl option file>  File with the PJL options returned by the printer,
                      if this filename is not provided, the PJL options are
                      taken from standard input.
EOF

    exit(1);
}

# ignore the following entries by default
my $opt = {};
my $debug = 0;
my $exclusions =
	{ 'PAPER'=>1,  # already handled by Ghostscript/PS
	  'RESOLUTION'=>1, # already handled by Ghostscript/PS
	  'LANG'=>1,       # only affects printer display
	  'PERSONALITY'=>1,# the page description language
			# is set by the driver
	  'PAGECOUNT'=>1,  # printer-internal counters,
	  'LPAGECOUNT'=>1, # no influence on print job
	  'PAGES'=>1,
	  'MAINTINTERVAL'=>1,
	  'FORMLINES'=>1,  # only for text printing
	  'AUTOCRLF'=>1,   # but Foomatic prints text as
	  'AUTOLFCR'=>1,   # PostScript
	  'FONTSOURCE'=>1,
	  'FONTNUMBER'=>1,
	  'PITCH'=>1,
	  'PTSIZE'=>1,
	  'SYMSET'=>1,
	  'LFONTPRIORITY'=>1,
	  'POWERSAVE'=>1,  # not a per-job setting
	  'TIMEOUT'=>1,
	  'USERNAME'=>1,   # String options
	  'JOBNAME'=>1,
	  };

# important options
    my $longsubs = { 
	'copies' => 'Number of Copies',
	'orientation' => 'Orientation',
	'ret' => 'Resolution Enhancement',
	'manualfeed' => 'Manual Feed of Paper',
	'pageprotect' => 'Page Protection',
	'mptray' => 'Multi-Purpose Tray',
	'density' => 'Toner Density',
	'lowtoner' => 'Behaviour when Toner Low',
	'outbin' => 'Output Bin',
	'joboffset' => 'Job Offset',
	'economode' => 'Toner Saving',
	'ecoprint' => 'Toner Saving',
	'intray1size' => 'Paper Size in Tray 1',
	'intray2size' => 'Paper Size in Tray 2',
	'intray3size' => 'Paper Size in Tray 3',
	'intray4size' => 'Paper Size in Tray 4',
	'intray5size' => 'Paper Size in Tray 5',
	'intray6size' => 'Paper Size in Tray 6',
	'intray7size' => 'Paper Size in Tray 7',
	'intray8size' => 'Paper Size in Tray 8',
	'intray9size' => 'Paper Size in Tray 9',
	'intray10size' => 'Paper Size in Tray 10',
	'widea4' => 'Wide A4',
	'bitsperpixel' => 'Bits Per Pixel',
	'holdtype' => 'Hold Job Type',
	'hold' => 'Hold Job',
	'pr1200speed' => 'Speed at 1200 dpi',
	'outbinprocess' => 'Output Bin Process',
	'finish' => 'Finisher',
	'stapleoption' => 'Stapling Mode',
	'extendedprintarea' => 'Extended Print Area',
	'heldjobtimeout' => 'Timeout for Held Jobs',
	'imageadapt' => 'Image Adaptation',
	'autocont' => 'Autocont',
	'fastraster' => 'Fast Raster',
	'intray1' => 'Input Tray 1',
	'intray2' => 'Input Tray 2',
	'intray3' => 'Input Tray 3',
	'intray4' => 'Input Tray 4',
	'intray5' => 'Input Tray 5',
	'intray6' => 'Input Tray 6',
	'intray7' => 'Input Tray 7',
	'intray8' => 'Input Tray 8',
	'intray9' => 'Input Tray 9',
	'intray10' => 'Input Tray 10',
	'duplex' => 'Double-Sided Printing',
	'binding' => 'Binding',
	'holdkey' => 'Key for Held Jobs',
	'lparm_postscript_prtpserrs' => 'Print PostScript Errors',
	'limageenhance' => 'Image Enhancement',
	'limageenhancetype' => 'Image Enhancement Type',
	'lblankpages' => 'Blank Pages',
	'lcollation' => 'Collation',
	'limagerotation' => 'Image Rotation',
	'linmpfeedersize' => 'Paper Size in MP Feeder',
	'lsubstitutesize' => 'Substitute Paper Size',
	'lpicturegrade' => 'Picture Grade',
	'ljamrecovery' => 'Jam Recovery',
	'lpagemode' => 'Page Mode',
	'lmultipageborder' => 'Border for N-up Printing',
	'lmultipageorder' => 'Order for N-up Printing',
	'lmultipageprint' => 'Pages per Sheet (N-up Printing)',
	'lmultipageview' => 'Orientation for N-up Printing',
	'lparm_pcl_lbitmaprounding' => 'Bitmap Rounding',
	'lparm_pcl_lcolorextensiond' => 'Colour Extensions',
	'lseparatorsheets' => 'Separator Sheets',
	'lseparatorsource' => 'Take Separator Sheets From',
	'econolevel' => 'Toner Saving Level',
	'mediatype' => 'Media Type',
	'mediasource' => 'Media Source',
	'sourcetray' => 'Media Source',
	'kintrayselect' => 'Media Source',
	'okipaperfeed' => 'Media Source',
	'lpapersource' => 'Media Source',
	'errorprint' => 'Print Error Messages',
	'lcolormodel' => 'Colour Model',
	'lcolorcorrection' => 'Colour Correction',
	'limagebrightness' => 'Image Brightness',
	'limagecontrast' => 'Image Contrast',
	'limagesmoothing' => 'Image Smoothing',
	'lprintquality' => 'Printout Quality',
	'okipapersizecheck' => 'Paper Size Checking'
	#'papersize' => 'Media Size',
	#'paper' => 'Media Size',
	#'lang' => 'Language',
	#'resolution' => 'Resolution',
	#'personality' => 'Personality',
	#'timeout' => 'Timeout',
	#'cplock' => 'CP Lock',
	#'powersave' => 'Power Saving',
	#'powersavetime' => 'Power Saving Timeout',
	#'iobuffer' => 'I/O Buffer',
	#'iosize' => 'I/O Size',
	#'resourcesave' => 'Resource Saving',
	#'disklock' => 'Disk Lock',
	#'qty' => 'Quantity',
	#'courier' => 'Courier',
	#'formlines' => 'Form Lines',
	#'reprint' => 'Reprint',
	#'password' => 'Password',
	#'username' => 'User Name',
	#'jobname' => 'Job Name',
	#'testpage' => 'Test Page',
	#'planesinuse' => 'Planes in Use',
	#'outlinepointsize' => 'Outline Point Size',
	#'maintinterval' => 'Maintenance Interval',
	#'finishertype' => 'Finisher Type',
	#'finisheroption' => 'Finisher Option',
	#'hitransfer' => 'Hi Transfer',
	#'parallel' => 'Parallel Port Mode',
	#'xoffset' => 'X Offset',
	#'yoffset' => 'Y Offset',
	#'lpowersaver' => 'Power Saver',
	#'ladvancedstatus' => 'Advanced Status',
	#'lalarmcontrol' => 'Alarm Control',
	#'lcancel' => 'Cancel',
	#'lresourcesave' => 'Resource Saving',
	#'lleftmarginoffset' => 'Left Margin Offset',
	#'ltopmarginoffset' => 'Top Margin Offset',
	#'okieurosign' => 'Euro Character',	
	};

# Read command line options
use Getopt::Std;
# Printer ID, force (no printer ID confirmation)
getopts("p:o:afisqh",$opt) || usage();
 
# Show usage info
if ($opt->{h}) {
	usage();
}
$debug = $opt->{d} if $opt->{d};
if( $opt->{a} ){
	$opt->{i} = 0;
	$exclusions = {};
}

$dir = $opt->{o} if $opt->{o};


my $libdir = ".";

my($printer,$manufacturer,$model,$templatename);
# Check presence of a printer ID
if ($opt->{p}) {
    $printer = $opt->{p};
} elsif( not $opt->{q} ){
    die "No printer ID given! Use the '-p <Foomatic printer ID>' option.";
}
if( $printer ){
	print "# PRINTER '$printer'\n";
    # Does the chosen printer ID exist in the database?
    my $printerdbentryfile = `grep -l printer/$printer $libdir/db/source/printer/*.xml 2>/dev/null`
		|| die "No printer with the Foomatic ID $printer in the $libdir/db/source/printer/*.xml local database";
    print "# Printer $printerdbentryfile\n" if $debug;
    my @p = split(" ", $printerdbentryfile);
    if( @p != 1 ){
	    print STDERR <<EOF;
Multiple matches for $printer
EOF
	    print STDERR join("\n  ",@p) . "\n";
	    exit 1;
    }

    $manufacturer = `grep "<.*make.*>" $printerdbentryfile`;
    $manufacturer =~ m!<\s*make\s*>\s*(.*)\s*<\s*/make\s*>!;
    $manufacturer = $1;
    $model = `grep "<.*model.*>" $printerdbentryfile`;
    $model =~ m!<\s*model\s*>\s*(.*)\s*<\s*/model\s*>!;
    $model = $1;
}

# Check presence of the PJL option snapshot file
my $snapshotfile = "STDIN";
if ($ARGV[0]) {
    $snapshotfile = $ARGV[0];

    # Does the chosen snapshot file exist?
    if (!-f $snapshotfile) {
	die "The given PJL snapshot file $snapshotfile does not exist!";
    }
} else {
    # interactive mode not supported with PJL snapshot from STDIN
    $opt->{f} = 1;
}

# Tell the user what we will do
if( not $opt->{q} ){
	print "# The PJL options from the file $snapshotfile\n";
	print "# will be added to the $manufacturer $model.\n\n";
}

# Ask whether this is correct
if (!$opt->{f}) {
    print "Is this correct (y/n)? ";
    my $input=<STDIN>;
    if ($input ne "y\n") {exit 0;}
    print "\n";
}

# Read the PJL snapshot file
my @snapshot = ();
if ($snapshotfile ne "STDIN") {
    open PJLSNAPSHOT, "$snapshotfile" or die "Cannot open $snapshotfile!";
    @snapshot = <PJLSNAPSHOT>;
    close PJLSNAPSHOT;
} else {
    # Read STDIN
    @snapshot = <STDIN>;
}

if( !$opt->{q} ){
    # OK, make the db hierarchy alongside the templates one...
	print "# options in directory $dir\n";
	print `mkdir -p $dir` . "\n" if (not -d $dir );
	die "cannot create directory $dir" if ( not -d $dir );
}

# Read the options and add them to the database
my $inoptionlisting = 0; # 1 if we are after a line "@PJL INFO VARIABLES"
                         # and no other "@PJL ..." line appeared
my $option = "";         # Name of current option
my $optiontype = -1;     # 0: enum, 1: int, 2: float, -1: not found yet
my $numchoices = -1;     # Number of choices, -1: not found yet
my $default = "";        # Default setting
my @choices = ();        # List of possible choices
my $choicesfound = 0;    # How many choices were already found
my $NEWF;
my $outputline = "";
if( $opt->{s} and not $opt->{q} ){
    my $dbfilename = "foomatic-db/opt/pjl-${manufacturer}-${model}.xml";
	print "# writing $dbfilename\n";
	$dbfilename =~ s/\s/_/g;
	$NEWF = new FileHandle  "> $dbfilename" or die "cannot open $dbfilename";
}
for my $line (@snapshot) {
    chomp $line;
    $line =~ s/\015//;
    print "xxx $line\n" if $debug;
    if ($line !~ m/^\s*$/) { # Ignore blank lines
	if ($inoptionlisting == 0) { # Start of option block not found yet
	    if ($line =~ m/^\s*\@PJL\s*INFO\s*VARIABLES\s*$/) {
		$inoptionlisting = 1; # Start of option block
                print "# >>> OPTION BLOCK FOUND\n";
	    }
	} else { # we are in the option block now
	    if ($line =~ m/^\s*\@PJL/) {
		$inoptionlisting = 0; # End of option block
                print "# >>> END OF OPTION BLOCK\n";
	    } elsif (
			$line =~ m/^\s*([\w\s:]*)\s*=\s*\"?([\w\.\?]*)\"?\s*\[\s*([0-9]*)\s*(\w*)\s*(\w*)\]/
			or
			$line =~ m/^\s*([\w\s:]*)\s*=\s*\"(.*)\"\s*\[\s*(\w*)\s*\]/
			#              option           default        numchoices
			) { # Option header
		if ($numchoices > -1) {
		    print "# WARNING: Choice(s) missing, option ignored!\n";
		}
		if( $outputline ){
			print $outputline . "\n";
			$outputline = "";
		}
		my $typestr;
                my $modifierstr;
		($option, $default, $numchoices, $typestr, $modifierstr) = 
		    ($1, $2, $3, $4, $5);
		if( $numchoices eq "STRING" ){
				$typestr = $numchoices;
				$numchoices = "";
		}
		print "# Option: '$option', Default '$default', Numchoices '$numchoices', Typestr '$typestr', Modifier '$modifierstr'\n";
                if ($modifierstr eq "READONLY") { # Ignore READONLY options
		    $numchoices = -2;
		    print "# WARNING: Read-only option, ignored!\n";
		} elsif (($modifierstr eq "STRING") || ($typestr eq "STRING")) { 
		    if( defined $exclusions->{$option} ){
		    $numchoices = -2;
			print "# WARNING: Option does not make sense for Foomatic, ignored!\n";
			    next;
		    };
		    if( $opt->{q} ){
				my $optname = $option;
				$optname =~ s/\W/_/g;
				$outputline = $optname;
				if( $optname ne $option ){
					$optname = "($option)";
					$optname =~ s/\s/+/g;
				} else {
					$optname = "";
				}
				$outputline .= "=${optname}STRING";
		    }
		    my $shortoption = $option;
		    $shortoption =~ tr/A-Z :/a-z__/; # $shortoption is 
						     # lowercase and without
						     # spaces
		    my $longoption = $shortoption;
		    my $optionid = "pjl-$printer-$shortoption";
		    # Substitution list to for the menu texts of the options
		    # ($longoption)
		    # Only options of major importance are substituted, so 
		    # one can separate options of lower importance
		    # ($longoption begins with a lowercase letter) into
		    # an "advanced options" group. The "-i" (Important)
		    # option removes these options of low importance
		    # Do the substitutions
		    my $substituted = 0;
		    my ($substr);
		    for $substr (keys(%$longsubs)) {
			my $substitution = $longsubs->{$substr};
			if (($longoption =~ m!^$substr$!) ){
			    $longoption =~ s!^$substr$!$substitution!g;
			    # Remove option if not substituted and "-i"
			    # flag is set (option is not "important"
			    $substituted = 1;
				next;
			} 
		    }
		    if( $opt->{i} and not $substituted ){
			# Reset variables
			$option = "";
			$optiontype = -1;
			$numchoices = -1;
			$default = "";
			@choices = ();
			$choicesfound = 0;
			next;
		    }
			my $numtype = "";
		    my $optionsubs = 
		    { 'NUMTYPE' => $numtype,
			  'OPTIONID' => $optionid,
			  'SHORTOPTION' => $shortoption,
			  'LONGOPTION' => $longoption,
			  'OPTION' => $option,
			  'MAKE' => $manufacturer,
			  'MODEL' => $model,
			  'DEFAULT' => $default,
			  };
		    # load template file for the choice entry
			$templatename = "/usr/local/share/foomatic/templates/pjl_string_option.xml";
		    open TMPL, $templatename or die "cannot open $templatename";
		    my @datafile = <TMPL>;
		    close TMPL;
		    my $template = join('',@datafile);
		    # Do the substitutions
		    my ($substr);
		    for $substr (keys(%$optionsubs)) {
			my $substitution = $optionsubs->{$substr};
			$template =~ s!\@\@$substr\@\@!$substitution!g;
		    }
		    # Anything not substituted?
		    grep (m!\@\@([^\@]+)\@\@!g
			  && do { warn "  Unknown substitution $1 in template file '$templatename'!\n"; },
			  split("\n",$template));
		    my $dbfilename = "foomatic-db/opt/$optionid.xml";
			if( $opt->{q} ){
			    print $outputline."\n" if $outputline;
				$outputline = "";
			} else {
				if( not $opt->{s} ){
					$NEWF = new FileHandle  "> $dbfilename" or die "cannot open $dbfilename";
				} else {
					$dbfilename = "$optionid";
				}
				print "# writing $dbfilename...";
				print $NEWF $template;
				print " done.\n";
				if( not $opt->{s} ){
					close $NEWF;
				}
			}
		    # Reset variables
		    $option = "";
		    $optiontype = -1;
		    $numchoices = -1;
		    $default = "";
		    @choices = ();
		    $choicesfound = 0;
		    next;
		} elsif ($numchoices < 2) { 
		                       # Options with less than two choices 
		                       # do not make any sense, discard them
		    $numchoices = -2;
		    print "# WARNING: Less than two choices, option ignored!\n";
		} else {
                    # Option type 2 (float) is determined when
                    # the choices are read.
			my $optval = $option;
			
		    if( $opt->{q} ){
				my $optname = $option;
				$optname =~ s/\W/_/g;
				$outputline = $optname;
				if( $optname ne $option ){
					$optname = "($option)";
					$optname =~ s/\s/+/g;
				} else {
					$optname = "";
				}
				$outputline .= "=${optname}${typestr};";
		    }
		    if ($typestr eq "ENUMERATED") {$optiontype = 0;}
		    elsif ($typestr eq "RANGE") {
			$optiontype = 1;
			# Is the default value really a number?
			if ($default =~ m/[^0-9\.]/) {
			    print "# WARNING: Default value for numerical option not a number, option ignored!\n";
			    $numchoices = -2;
				$outputline = "";
			}
                    } else {
			$numchoices = -2;
			print "# WARNING: Unsupported option type, option ignored!\n";
		    }
		}
		if ($numchoices > -2) {
		    # Remove option if it is on exclusion list
		    # These options do not make sense for PostScript-only
		    # printing
			if( defined $exclusions->{$option} ){
			    $numchoices = -2;
			    print "# WARNING: Option does not make sense for Foomatic, ignored!\n";
			};
		    }
	    } elsif ($numchoices > 1) { # We have a valid option header
		                        # for which not all choices are
		                        # found yet
		$line =~ m/^\s*(\S*)\s*$/;
		my $choice = $1;
		push (@choices, $choice);
                if (($optiontype == 1) && ($1 =~ /\./)) { # float option
		    $optiontype = 2;
		}
		$choicesfound ++;
		if ($optiontype > 0) {
		    # Is the value really a number?
		    if ($default =~ m/[^0-9\.]/) {
			print "# WARNING: Value for numerical option not a number, option ignored!\n";
			$numchoices = -2;
		    } elsif ($choicesfound == 1) { # Minimum value
			if ($default < $choice) {
			    print "# WARNING: Default value out of range, option ignored!\n";
			    $numchoices = -2;
				$outputline = "";
			}
		    } elsif ($choicesfound == 2) { # Maximum value
			if ($default > $choice) {
			    print "# WARNING: Default value out of range, option ignored!\n";
			    $numchoices = -2;
				$outputline = "";
			} elsif ($choices[0] == $choices[1]) { # Max = Min?
			    print "# WARNING: Maximum and minimum are equal, option ignored!\n";
			    $numchoices = -2;
				$outputline = "";
			}
		    }
		}
		if ($numchoices == -2) {
		    # Reset variables
		    $option = "";
		    $optiontype = -1;
		    $default = "";
		    @choices = ();
		    $choicesfound = 0;
		} elsif ($choicesfound == $numchoices) { # All choices for 
		                                         # the current 
		                                         # option read
		    #print "$option, $default, $numchoices, $optiontype\n";
		    #print "@choices\n";
		    if( $opt->{q} ){
			$outputline .= join(",",@choices);
		    }

		    # Check, if the default is in the list of choices
		    if ($optiontype == 0) {
			# Enum option: Default is in the choice list?
			my $defaultfound = 0;
			for $choice (@choices) {
			    if ($default == $choice) {$defaultfound = 1};
			}
			if ($defaultfound == 0) {
			    # Add default value to the list
			    push(@choices, $default);
                            print("# WARNING: Default added!\n");
			}
		    }
		    # We have a complete option now, enter it into the
		    # template XML files to generate a Foomatic database
		    # entry
		    my $numtype = '';
                    if ($optiontype == 2) {$numtype = "float";}
		    elsif ($optiontype == 1) {$numtype = "int";}
                    else {$numtype = "enum";}
		    my $shortoption = $option;
		    $shortoption =~ tr/A-Z :/a-z__/; # $shortoption is 
                                                     # lowercase and without
		                                     # spaces
                    my $longoption = $shortoption;
                    my $optionid = "pjl-$printer-$shortoption";
		    my $choiceblock = '';
		    my @choiceblocklist = ();
                    my $max = '';
                    my $min = '';
		    # Substitution list to for the menu texts of the options
		    # ($longoption)
		    # Only options of major importance are substituted, so 
		    # one can separate options of lower importance
		    # ($longoption begins with a lowercase letter) into
		    # an "advanced options" group. The "-i" (Important)
		    # option removes these options of low importance
		    # Do the substitutions
		    my $substituted = 0;
		    my ($substr);
		    for $substr (keys(%$longsubs)) {
			my $substitution = $longsubs->{$substr};
			if (($longoption =~ m!^$substr$!) ){
			    $longoption =~ s!^$substr$!$substitution!g;
			    # Remove option if not substituted and "-i"
			    # flag is set (option is not "important"
			    $substituted = 1;
				last;
			} 
		    }
		    # Substitute $shortoption if it is one of the options
		    # of the "General" PPD section, so that CUPS and its
		    # graphical frontends insert it correctly
		    my $shortsubs = { 
			'duplex' => 'Duplex',
			'mediatype' => 'MediaType',
			'mediasource' => 'InputSlot',
			'sourcetray' => 'InputSlot',
			'kintrayselect' => 'InputSlot',
			'okipaperfeed' => 'InputSlot',
			'lpapersource' => 'InputSlot'
			};
		    # Do the substitutions
		    my $shortsubstituted = 0;
		    my ($substr);
		    for $substr (keys(%$shortsubs)) {
			my $substitution = $shortsubs->{$substr};
			if (($shortoption =~ m!^$substr$!) && 
			    ($shortsubstituted == 0)) {
			    $shortoption =~ s!^$substr$!$substitution!g;
			    $shortsubstituted = 1;
			} 
		    }
		    if ($optiontype == 0) {
			# enumerated option, generate choice entries
			for $choice (@choices) {
                            # list of items to substitute in the choice 
			    # template
			    my $shortchoice = $choice;	
			    $shortchoice =~ tr/A-Z? :/a-zX__/;#$shortchoice 
				                             # is lowercase
			                                     # and without 
			                                     # spaces
			    if ($shortchoice =~ m/X/) {
				$shortchoice = 'undef';
			    }
			    my $longchoice = $shortchoice;
			    my $choiceid = "pjl-$printer-$shortchoice";
			    if ($default == $choice) {
				$default = $choiceid;
			    }
			    my $longchoicesubs = { 
				'on' => 'On',
				'off' => 'Off',
				'enable' => 'Enabled',
				'disable' => 'Disabled',
				'enabled' => 'Enabled',
				'disabled' => 'Disabled',
				'yes' => 'Yes',
				'no' => 'No',
				'none' => 'None',
				'auto' => 'Automatic',
				'default' => 'Default',
				'intray' => 'Tray',
				'intray1' => 'Tray 1',
				'intray2' => 'Tray 2',
				'intray3' => 'Tray 3',
				'intray4' => 'Tray 4',
				'intray5' => 'Tray 5',
				'intray6' => 'Tray 6',
				'intray7' => 'Tray 7',
				'intray8' => 'Tray 8',
				'intray9' => 'Tray 9',
				'intray10' => 'Tray 10',
				'tray' => 'Tray',
				'tray1' => 'Tray 1',
				'tray2' => 'Tray 2',
				'tray3' => 'Tray 3',
				'tray4' => 'Tray 4',
				'tray5' => 'Tray 5',
				'tray6' => 'Tray 6',
				'tray7' => 'Tray 7',
				'tray8' => 'Tray 8',
				'tray9' => 'Tray 9',
				'tray10' => 'Tray 10',
				'upper' => 'Upper',
				'middle' => 'Middle',
				'lower' => 'Lower',
				'first' => 'First',
				'cassette' => 'Cassette',
				'manual' => 'Manual',
				'manualenvelope' => 'Manual Envelope Feeder',
				'manualpaper' => 'Manual Paper Feeder',
				'manualfeed' => 'Manual Feeder',
				'locked' => 'Locked',
				'unlocked' => 'Unlocked',
				'letter' => 'Letter',
				'legal' => 'legal',
				'a0' => 'A0',
				'a1' => 'A1',
				'a2' => 'A2',
				'a3' => 'A3',
				'a4' => 'A4',
				'a5' => 'A5',
				'a6' => 'A6',
				'a7' => 'A7',
				'a8' => 'A8',
				'a9' => 'A9',
				'a10' => 'A10',
				'b5paper' => 'B5 Paper',
				'executive' => 'Executive',
				'com10' => 'Com 10',
				'com9' => 'Com 9',
				'monarch' => 'Monarch',
				'dl' => 'DL',
				'c5' => 'C5',
				'b5' => 'B5',
				'otherenvelope' => 'Other Envelope',
				'jisb5' => 'JIS B5',
				'jisexec' => 'JIS Exec',
				'roc16k' => 'ROC 16K',
				'b5' => 'B5',
				'b5' => 'B5',
				'b5' => 'B5',
				'custom' => 'Custom Size',
				'portrait' => 'Portrait',
				'landscape' => 'Landscape',
				'light' => 'Light',
				'medium' => 'Medium',
				'dark' => 'Dark',
				'regular' => 'Regular',
				'job' => 'Job',
				'draft' => 'Draft',
				'tonersaver' => 'Toner Saving',
				'paper' => 'Paper',
				'normal' => 'Normal',
				'plain' => 'Plain',
				'bond' => 'Bond',
				'rough' => 'Rough',
				'cardstock' => 'Card Stock',
				'labels' => 'Labels',
				'transparency' => 'Transparency',
				'glossy' => 'Glossy',
				'letterhead' => 'Letter Head',
				'preprinted' => 'Preprinted',
				'colored' => 'Coloured',
				'customtype' => 'Custom Type',
				'customtype1' => 'Custom Type 1',
				'customtype2' => 'Custom Type 2',
				'customtype3' => 'Custom Type 3',
				'customtype4' => 'Custom Type 4',
				'customtype5' => 'Custom Type 5',
				'customtype6' => 'Custom Type 6',
				'customtype7' => 'Custom Type 7',
				'customtype8' => 'Custom Type 8',
				'customtype9' => 'Custom Type 9',
				'customtype10' => 'Custom Type 10',
				'envelope' => 'Envelope',
				'print' => 'Print',
				'donotprint' => 'Do Not Print',
				'ram' => 'RAM',
				'single' => 'Single',
				'continue' => 'Continue',
				'continuous' => 'Continuous',
				'stop' => 'Stop',
				'minimum' => 'Minimum',
				'moderate' => 'Moderate',
				'maximum' => 'Maximum',
				'public' => 'Public',
				'private' => 'Private',
				'store' => 'Store',
				'proof' => 'Proof',
				'half' => 'Half',
				'staple' => 'Staple',
				'one' => '1',
				'two' => '2',
				'three' => '3',
				'four' => '4',
				'five' => '5',
				'six' => '6',
				'seven' => '7',
				'eight' => '8',
				'nine' => '9',
				'ten' => '10',
				'oneangled' => 'One Angled',
				'oneopposed' => 'One Opposed',
				'option' => 'Option',
				'option1' => 'Option 1',
				'option2' => 'Option 2',
				'option3' => 'Option 3',
				'option4' => 'Option 4',
				'option5' => 'Option 5',
				'option6' => 'Option 6',
				'option7' => 'Option 7',
				'option8' => 'Option 8',
				'option9' => 'Option 9',
				'option10' => 'Option 10',
				'onehour' => '1 Hour',
				'fourhours' => '4 Hours',
				'oneday' => '1 Day',
				'oneweek' => '1 Week',
				'slow' => 'Slow',
				'fast' => 'Fast',
				'smooth' => 'Smooth',
				'short' => 'Short',
				'heavy' => 'Heavy',
				'long' => 'Long',
				'longedge' => 'Long Edge',
				'shortedge' => 'Short Edge',
				'undef' => 'Undefined'
				};
			    # Do the substitutions
			    my $chsubstituted = 0;
			    my ($substr);
			    for $substr (keys(%$longchoicesubs)) {
				my $substitution = $longchoicesubs->{$substr};
				if (($longchoice =~ m!^$substr$!) && 
				    ($chsubstituted == 0)) {
				    $longchoice =~ s!^$substr$!$substitution!g;
				    $chsubstituted = 1;
				} 
			    }
			    my $choicesubs = 
			    { 'CHOICE' => $choice,
			      'SHORTCHOICE' => $shortchoice,
			      'LONGCHOICE' => $longchoice,
			      'CHOICEID' => $choiceid
			      };
			    # load template file for the choice entry
			    open TMPL, "/usr/local/share/foomatic/templates/pjl_enum_choice.xml" or die "cannot open /usr/local/share/foomatic/templates/pjl_enum_choice.xml";
			    my @datafile = <TMPL>;
			    close TMPL;
			    my $template = join('',@datafile);
			    # Do the substitutions
			    my ($substr);
			    for $substr (keys(%$choicesubs)) {
				my $substitution = $choicesubs->{$substr};
				$template =~ s!\@\@$substr\@\@!$substitution!g;
			    }
			    # Anything not substituted?
			    grep (m!\@\@([^\@]+)\@\@!g
				  && do { warn "  Unknown substitution $1 in choice template file!\n"; },
				  split("\n",$template));
			    # Store choice entry
			    push(@choiceblocklist, $template);
			}
			# Make the choices ready for being inserted into the
                        # option file
			$choiceblock = join('',@choiceblocklist);
		    } else {
			# numeric option, set minimum and maximum
			$min = $choices[0];
			$max = $choices[1];
		    }
		    # Set up list of items to substitute in the template
		    # option files
                    my $optionsubs = 
		    { 'NUMTYPE' => $numtype,
		      'OPTIONID' => $optionid,
                      'SHORTOPTION' => $shortoption,
                      'LONGOPTION' => $longoption,
		      'OPTION' => $option,
		      'MAKE' => $manufacturer,
		      'MODEL' => $model,
		      'DEFAULT' => $default,
                      'CHOICES' => $choiceblock,
                      'MAX' => $max,
                      'MIN' => $min
		      };
		    # load template file for the option entry
		    if ($optiontype == 0) {
			$templatename = "pjl_enum_option.xml";
		    } else {
			$templatename = "pjl_num_option.xml";
		    }
		    open TMPL, "/usr/local/share/foomatic/templates/$templatename" or die "cannot open /usr/local/share/foomatic/templates/$templatename";
		    my @datafile = <TMPL>;
		    close TMPL;
		    my $template = join('',@datafile);
		    # Do the substitutions
		    my ($substr);
		    for $substr (keys(%$optionsubs)) {
			my $substitution = $optionsubs->{$substr};
			$template =~ s!\@\@$substr\@\@!$substitution!g;
		    }
		    # Anything not substituted?
		    grep (m!\@\@([^\@]+)\@\@!g
			  && do { warn "  Unknown substitution $1 in option template file '$templatename'!\n"; },
			  split("\n",$template));
		    # Finally, write out the new file
		    # Options are under opt/
		    # Do not write option when "-i" flag was used and
		    # option is not in substitution list ("important"
		    # option)
                    if (($substituted == 1) || (!$opt->{i})) {
			if( $opt->{q} ){
			    print $outputline . "\n" if $outputline;
			    $outputline = "";
			} else {
			my $dbfilename = "foomatic-db/opt/$optionid.xml";
			    if( not $opt->{s} ){
				    $NEWF = new FileHandle  "> $dbfilename" or die "cannot open $dbfilename";
			    } else {
				    $dbfilename = "$optionid";
			    }
			    print "# writing $dbfilename...";
			    print $NEWF $template;
			    print " done.\n";
			    if( not $opt->{s} ){
				close $NEWF;
			    }
			}
		    } else {
			print("# WARNING: Option '$option' of minor importance and \"-i\" set, option ignored!\n");
		    }
		    # Reset variables
		    $option = "";
		    $optiontype = -1;
		    $numchoices = -1;
		    $default = "";
		    @choices = ();
		    $choicesfound = 0;
		}
	    } else { # All choices for current header are read but the
		     # current line is not a new header
		if ($numchoices > -2) {
		    print "# WARNING: Too many choice lines for option '$option'!\n";
		    $numchoices = -2;
		}
	    }
	}
    }
}

exit 0;
