#!/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/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/share/foomatic/templates/pjl_enum_choice.xml" or die "cannot open /usr/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/share/foomatic/templates/$templatename" or die "cannot open /usr/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;