#!/usr/bin/perl # # This module parses NT .INF files containing printer driver information. # The information is turned into a magical set of data strucutures # allowing the driver programs to do something useful with the printer # driver. package inf_nt; use strict; use vars qw($VERSION @ISA @EXPORT_OK); use Carp qw(carp croak); use INF qw(inf_get_key inf_get_keys inf_has_key); require Exporter; $VERSION = "0.01"; @ISA = qw(Exporter); @EXPORT_OK = qw(parse_inf_nt get_models); # Fill in the info hash with information on what the destination directories # for the different file sections will be. The default destination directory # is also determined here. sub fill_destination_dirs { croak("fill_destination_dirs(INF,INFO)") if @_ != 2; my($inf, $info) = @_; # The DestDir member will hold the destination for any file section. $info->{"DestDir"} = {}; # If a DestinationDirs section isn't present, bail out. if (not inf_has_key($inf,"DestinationDirs")) { $info->{"DefaultDestDir"} = "66000"; return; } # Find and handle a DefaultDestDir if it is present. if (exists $inf->{"DestinationDirs"}{"DefaultDestDir"}) { my($num, $subdir) = split(/,/, $inf->{"DestinationDirs"}{"DefaultDestDir"}); $info->{"DefaultDestDir"} = $num; $info->{"DefaultDestDir"} .= "\\$subdir" if $subdir ne ''; } # Loop for each file section and add it to the DestDir hash. foreach my $key (keys %{$inf->{"DestinationDirs"}}) { next if $key eq 'DefaultDestDir'; my $str = $inf->{"DestinationDirs"}{$key}; my($num, $subdir) = split(/,/, $str); my $value = $num; $value .= "\\$subdir" if $subdir ne ''; $info->{"DestDir"}{$key} = $value; } } # The files to be copied for a particular driver are stored as hash refs # inside an array ref. Each hash ref corresponds to a single file. The # array ref stores all the files that will be copied. sub handle_file { croak("handle_file(INFO,SRC_FILENAME,DST_FILENAME,DESTDIR)") if @_ != 4; my($info, $src_filename, $dst_filename, $destdir) = @_; # Create the CopyFiles node if it doesn't exists yet. if (not exists $info->{"CopyFiles"}) { $info->{"CopyFiles"} = []; } my $filehash = {}; $filehash->{"SrcFilename"} = $src_filename; $filehash->{"DstFilename"} = $dst_filename; $filehash->{"DestDir"} = $destdir; push(@{$info->{"CopyFiles"}}, $filehash); } sub handle_CopyFiles { croak("handle_CopyFiles(INFO,INF,FILELIST)") if @_ != 3; my($info, $inf, $filelist) = @_; foreach my $specifier (split(/\s*,\s*/, $filelist)) { if ($specifier =~ /^@/) { # A single file was specified. my $fname = substr($specifier, 1); handle_file($info, $fname, $fname, $info->{DefaultDestDir}); } else { # An entire section containing files to copy was specified. if (not exists $inf->{$specifier}) { print "CopyFiles wants section $specifier not present.\n"; exit(1); } foreach my $key (keys %{$inf->{$specifier}}) { my($dst_filename, $src_filename) = split(/\s*,\s*/, $key); my $destdir; if (exists $info->{"DestDir"}{$specifier}) { $destdir = $info->{"DestDir"}{$specifier}; } else { $destdir = $info->{DefaultDestDir}; } $dst_filename = $src_filename if $dst_filename eq '' and $src_filename ne ''; $src_filename = $dst_filename if $src_filename eq ''; handle_file($info, $src_filename, $dst_filename, $destdir); } } } } sub handle_AddReg { croak("handle_AddReg(INFO, SECTION_HASH)") if @_ != 2; my($info, $section) = @_; foreach my $entry (keys %{$section}) { my @data = split(/\s*,\s*/, $entry); if (not exists $info->{"AddReg"}) { $info->{"AddReg"} = []; } push(@{$info->{"AddReg"}}, [ @data ]); } } sub handle_DelReg { croak("handle_DelReg(INFO, SECTION_HASH)") if @_ != 2; my($info, $section) = @_; foreach my $entry (keys %{$section}) { my @data = split(/\s*,\s*/, $entry); if (not exists $info->{"DelReg"}) { $info->{"DelReg"} = []; } push(@{$info->{"DelReg"}}, [ @data ]); } } sub handle_SourceDisksNames { croak("handle_SourceDisksNames(INFO,SECTION_HASH)") if @_ != 2; my($info, $section) = @_; foreach my $ordinal (keys %{$section}) { my($descr, $tagfile, undef, $path) = split(/\s*,\s*/, $section->{$ordinal}); if (not exists $info->{"SourceDisksNames"}) { $info->{"SourceDisksNames"} = {}; } # If the ordinal already exists, skip over to allow overrides by # the platform-specific sections. next if exists $info->{"SourceDisksNames"}->{$ordinal}; # Insert an entry for this source disk. $info->{"SourceDisksNames"}->{$ordinal} = { 'description' => $descr, 'tagfile' => $tagfile, 'path' => $path }; } } sub handle_SourceDisksFiles { croak("handle_SourceDisksFiles(INFO,SECTION_HASH)") if @_ != 2; my($info, $section) = @_; foreach my $fname (keys %{$section}) { my($ordinal, $subdir, $size) = split(/\s*,\s*/, $section->{$fname}); $fname =~ tr/a-z/A-Z/; if (not exists $info->{"SourceDisksFiles"}) { $info->{"SourceDisksFiles"} = {}; } # If the ordinal already exists, skip over to allow overrides by # the platform-specific sections. next if exists $info->{"SourceDisksFiles"}->{$fname}; # Insert an entry for this source file. $info->{"SourceDisksFiles"}->{$fname} = { 'ordinal' => $ordinal }; } } sub get_models { croak("get_manufacturers(INF, MANUFACTURER)") if @_ != 2; my($inf, $manufacturer) = @_; my($device_section_name); if (inf_has_key(inf_get_key($inf, "Manufacturer"), $manufacturer)) { if (inf_get_keys($inf, "Manufacturer", $manufacturer) eq '') { $device_section_name = $manufacturer; } else { $device_section_name = inf_get_keys($inf, "Manufacturer", $manufacturer); } } else { return undef; } if (inf_has_key($inf, $device_section_name)) { return keys %{inf_get_key($inf, $device_section_name)}; } else { return undef; } } sub parse_inf_nt { croak("parse_inf_nt(INF,MANUFACTURER,MODEL,ARCH)") if @_ != 4; my($inf, $manufacturer, $model, $arch) = @_; my(%info, $device_section_name); # Make sure that the [Manufacturer] section exists. if (not inf_has_key($inf, "Manufacturer")) { print STDERR "[Manufacturer] section not present.\n"; return undef; } # Make sure that the manufacturer is actually listed. if (inf_has_key(inf_get_key($inf, "Manufacturer"), $manufacturer)) { if (inf_get_key(inf_get_key($inf, "Manufacturer"), $manufacturer) eq '') { $device_section_name = $manufacturer; } else { $device_section_name = inf_get_key(inf_get_key($inf, "Manufacturer"), $manufacturer); } } else { print STDERR "ERROR: Manufacturer $manufacturer not listed.\n"; return undef; } # We now know the name of the device section. Try to find the device that # the caller gave us in this section. if (not inf_has_key($inf, $device_section_name)) { print STDERR "Device section `$device_section_name' does not exist.\n"; return undef; } if (not inf_has_key(inf_get_key($inf, $device_section_name), $model)) { print STDERR "Specified model ($model) not present in .INF file.\n"; return undef; } # This model exists so fill in the device name. $info{pName} = $model; # Figure out the name of the install section. The install section is the # heart of the .INF file and determines all actions to be taken. my($install_section_name, @DEVICE_IDS) = split(/\s*,\s*/, inf_get_key(inf_get_key($inf, $device_section_name), $model)); if ($install_section_name eq '') { print STDERR "Install section name is blank.\n"; return undef; } # Determine if this INF is for NT. my $is_NT = 0; $is_NT = 1 if $arch =~ /^W32X86$/i; $is_NT = 1 if $arch =~ /^W32alpha$/i; $is_NT = 1 if $arch =~ /^W32mips$/i; $is_NT = 1 if $arch =~ /^W32ppc$/i; # Make sure the install section exists. if (not inf_has_key($inf, $install_section_name) and not inf_has_key($inf, $install_section_name . ".nt") and not inf_has_key($inf, $install_section_name . ".ntx86")) { print STDERR "Install section ($install_section_name) not present.\n"; return undef; } my $install_section; if ($is_NT and inf_has_key($inf, $install_section_name . ".ntx86")) { $install_section = inf_get_key($inf, $install_section_name . ".ntx86"); } elsif ($is_NT and inf_has_key($inf, $install_section_name . ".nt")) { $install_section = inf_get_key($inf, $install_section_name . ".nt"); } else { $install_section = inf_get_key($inf, $install_section_name); } # See if there is an optional data section. my $data_section = {}; if (inf_has_key($install_section, "DataSection")) { if (not inf_has_key($inf, inf_get_key($install_section, "DataSection"))) { print STDERR "DataSection directive given but section missing.\n"; return undef; } $data_section = inf_get_key($inf, inf_get_key($install_section, "DataSection")); } # Handle the LanguageMonitor directive. if (inf_has_key($data_section, "LanguageMonitor")) { $info{"LanguageMonitor"} = inf_get_key($data_section, "LanguageMonitor"); } elsif (inf_has_key($install_section, "LanguageMonitor")) { $info{"LanguageMonitor"} = inf_get_key($install_section, "LanguageMonitor"); } # Handle the DriverFile directive. if (inf_has_key($data_section, "DriverFile")) { $info{"DriverFile"} = inf_get_key($data_section, "DriverFile"); } elsif (inf_has_key($install_section, "DriverFile")) { $info{"DriverFile"} = inf_get_key($install_section, "DriverFile"); } if ($info{"DriverFile"} eq '') { $info{"DriverFile"} = $install_section_name; } # Handle the DataFile directive. if (inf_has_key($data_section, "DataFile")) { $info{"DataFile"} = inf_get_key($data_section, "DataFile"); } elsif (inf_has_key($install_section, "DataFile")) { $info{"DataFile"} = inf_get_key($install_section, "DataFile"); } if ($info{"DataFile"} eq '') { $info{"DataFile"} = $install_section_name; } # Handle the ConfigFile directive. if (inf_has_key($data_section, "ConfigFile")) { $info{"ConfigFile"} = inf_get_key($data_section, "ConfigFile"); } elsif (inf_has_key($install_section, "ConfigFile")) { $info{"ConfigFile"} = inf_get_key($install_section, "ConfigFile"); } ## Windows NT always splits the driver and config file up ## Windows 9x places them in the same file if ($info{"ConfigFile"} eq '') { # $info{"ConfigFile"} = $install_section_name; $info{"ConfigFile"} = $info{"DriverFile"}; } # Handle the HelpFile directive. if (inf_has_key($data_section, "HelpFile")) { $info{"HelpFile"} = inf_get_key($data_section, "HelpFile"); } elsif (inf_has_key($install_section, "HelpFile")) { $info{"HelpFile"} = inf_get_key($install_section, "HelpFile"); } # Parse the destination directory information. fill_destination_dirs($inf, \%info); # Handle any files that need to be copied. if (inf_has_key($data_section, "CopyFiles")) { handle_CopyFiles(\%info, $inf, inf_get_key($data_section, "CopyFiles")); } if (inf_has_key($install_section, "CopyFiles")) { handle_CopyFiles(\%info, $inf, inf_get_key($install_section, "CopyFiles")); } # Handle any AddReg sections. if (inf_has_key($install_section, "AddReg")) { foreach my $addreg_name (split(/\s*,\s*/, inf_get_key($install_section, "AddReg"))) { if (not inf_has_key($inf, $addreg_name)) { print STDERR "ERROR: Section given in AddReg doesn't exist.\n"; return undef; } handle_AddReg(\%info, inf_get_key($inf, $addreg_name)); } } # Handle any DelReg sections. if (inf_has_key($install_section, "DelReg")) { foreach my $delreg_name (split(/\s*,\s*/, inf_get_key($install_section, "DelReg"))) { if (not inf_has_key($inf, $delreg_name)) { print STDERR "ERROR: Section given in DelReg doesn't exist.\n"; return undef; } handle_DelReg(\%info, inf_get_key($inf, $delreg_name)); } } # Handle any SourceDisksNames sections. if (inf_has_key($inf, "SourceDisksNames.x86")) { handle_SourceDisksNames(\%info, inf_get_key($inf, "SourceDisksNames.x86")); } if (inf_has_key($inf, "SourceDisksNames")) { handle_SourceDisksNames(\%info, inf_get_key($inf, "SourceDisksNames")); } # Handle any SourceDisksFiles section. if (inf_has_key($inf, "SourceDisksFiles.x86")) { handle_SourceDisksFiles(\%info, inf_get_key($inf, "SourceDisksFiles.x86")); } if (inf_has_key($inf, "SourceDisksFiles")) { handle_SourceDisksFiles(\%info, inf_get_key($inf, "SourceDisksFiles")); } return %info; } 1;