wok-4.x rev 1850

Add: tazndis, replacement for ndiswrapper util.
author Eric Joseph-Alexandre <erjo@slitaz.org>
date Sun Dec 14 10:53:02 2008 +0100 (2008-12-14)
parents ae564d97575d
children f9c021d9b2fc
files tazndis/receipt tazndis/stuff/tazndis
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tazndis/receipt	Sun Dec 14 10:53:02 2008 +0100
     1.3 @@ -0,0 +1,18 @@
     1.4 +# SliTaz package receipt.
     1.5 +
     1.6 +PACKAGE="tazndis"
     1.7 +VERSION="1.53"
     1.8 +CATEGORY="networking"
     1.9 +SHORT_DESC="ndiswrapper replacement for SliTaz"
    1.10 +MAINTAINER="erjo@slitaz.org"
    1.11 +DEPENDS="perl-core"
    1.12 +TARBALL="$PACKAGE-$VERSION.tar.gz"
    1.13 +WEB_SITE="http://www.slitaz.org"
    1.14 +
    1.15 +# Rules to gen a SliTaz package suitable for Tazpkg.
    1.16 +genpkg_rules()
    1.17 +{
    1.18 +	mkdir -p $fs/usr/bin
    1.19 +	cp -a stuff/${PACKAGE} $fs/usr/bin
    1.20 +}
    1.21 +
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/tazndis/stuff/tazndis	Sun Dec 14 10:53:02 2008 +0100
     2.3 @@ -0,0 +1,973 @@
     2.4 +#!/usr/bin/perl
     2.5 +
     2.6 +#/*
     2.7 +#*  tazndis,  install, remove or list of NDIS drivers.
     2.8 +#*  
     2.9 +#*  This program  is a replacement for ndiswrapper utility written by
    2.10 +#*  Pontus Fuchs and Giridhar Pemmasani.
    2.11 +#*  Most part of code come from the original ndiswrapper PERL script.
    2.12 +#*
    2.13 +#*  If you need more complexe commands consider to use the original ndiswrapper  
    2.14 +#*  instead. 
    2.15 +#*
    2.16 +#*  Copyright (C) 2008 Eric Joseph-Alexandre
    2.17 +#*  Copyright (C) 2005-2006 Pontus Fuchs, Giridhar Pemmasani
    2.18 +#*
    2.19 +#*
    2.20 +#*  This program is free software; you can redistribute it and/or modify
    2.21 +#*  it under the terms of the GNU General Public License as published by
    2.22 +#*  the Free Software Foundation; either version 2 of the License, or
    2.23 +#*  (at your option) any later version.
    2.24 +#*
    2.25 +#*  This program is distributed in the hope that it will be useful,
    2.26 +#*  but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.27 +#*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    2.28 +#*  GNU General Public License for more details.
    2.29 +#*
    2.30 +#*/
    2.31 +
    2.32 +$ENV{PATH} = "/sbin:/usr/sbin:$ENV{PATH}";
    2.33 +
    2.34 +my $WRAP_PCI_BUS = 5;
    2.35 +my $WRAP_PCMCIA_BUS = 8;
    2.36 +my $WRAP_USB_BUS = 15;
    2.37 +
    2.38 +my %sections;
    2.39 +my %parsed_sections;
    2.40 +my $confdir = "/etc/ndiswrapper";
    2.41 +my $src_dir;
    2.42 +my $driver_name;
    2.43 +my @source_disks_files;
    2.44 +
    2.45 +my $re_dev_id = "([[:xdigit:]]{4})";
    2.46 +my $re_sub_dev_conf = "$re_dev_id:$re_dev_id:$re_dev_id:$re_dev_id" .
    2.47 +  "\.([[:xdigit:]]+)\.conf";
    2.48 +my $re_dev_conf = "$re_dev_id:$re_dev_id\.([[:xdigit:]]+)\.conf";
    2.49 +
    2.50 +# fixup list for parameters.
    2.51 +my %param_fixlist = ("EnableRadio|0" => "1",
    2.52 +		     "IBSSGMode|0" => "2",
    2.53 +		     "PrivacyMode|0" => "2",
    2.54 +		     "MapRegisters|256" => "64",
    2.55 +		     "AdhocGMode|1" => "0");
    2.56 +
    2.57 +if (@ARGV < 1) {
    2.58 +	usage();
    2.59 +	exit(1);
    2.60 +}
    2.61 +
    2.62 +my $res;
    2.63 +my $dbg_file;
    2.64 +
    2.65 +$dbg_file = "/dev/null";
    2.66 +
    2.67 +# "-D" is for development/debugging only
    2.68 +if ($ARGV[0] eq "-D") {
    2.69 +    $dbg_file = "/tmp/ndiswrapper.dbg";
    2.70 +    $confdir = "/tmp/ndiswrapper";
    2.71 +	open(DBG, "> $dbg_file") or die "couldn't open $dbg_file: $!";
    2.72 +    shift;
    2.73 +}
    2.74 +
    2.75 +if ($ARGV[0] eq "-i" and @ARGV == 2) {
    2.76 +    $res = install($ARGV[1]);
    2.77 +} elsif (($ARGV[0] eq "-r") and @ARGV == 2) {
    2.78 +    $res = remove_driver($ARGV[1]);
    2.79 +} elsif ($ARGV[0] eq "-l" and @ARGV == 1) {
    2.80 +    $res = list_drivers();
    2.81 +} elsif ($ARGV[0] eq "-v" and @ARGV == 1) {
    2.82 +    $res = check_version();
    2.83 +} else {
    2.84 +    usage();
    2.85 +}
    2.86 +close(DBG);
    2.87 +exit($res);
    2.88 +
    2.89 +sub usage() {
    2.90 +    print "install/manage Windows drivers for ndiswrapper\n\n" .
    2.91 +        "usage: ndiswrapper OPTION\n" .
    2.92 +	"-i inffile       install driver described by 'inffile'\n" .
    2.93 +	"-r driver        remove 'driver'\n" .
    2.94 +	"-l               list installed drivers\n" .
    2.95 +	"-v               report version information\n\n" .
    2.96 +	"If you need more complex operation, you may use ndiswrapper instead.\n" ;
    2.97 +}
    2.98 +
    2.99 +#/*
   2.100 +#* Begin of PERL modules substition
   2.101 +#*
   2.102 +#* Replaced File.pm 
   2.103 +
   2.104 +sub basename(@_){
   2.105 +	$var = $_[0];
   2.106 +	$var =~ s#.*/(.*)$#$1#;
   2.107 +	return $var;
   2.108 +}
   2.109 +
   2.110 +sub dirname {
   2.111 +	local $var = $_[0];
   2.112 +	$var =~ s#(.*)/.*$#$1#;
   2.113 +	return $var;
   2.114 +}
   2.115 +
   2.116 +# delete given single tree with no sub directories.
   2.117 +sub rmtree {
   2.118 +	local $dir = $_[0];
   2.119 +	opendir(DIR, $dir) or die "couldn't delete $dir: $!\n";
   2.120 +    @dirs = grep(!/^.{1,2}$/, readdir(DIR));
   2.121 +	foreach $file (@dirs){
   2.122 +		unlink "$dir/$file" or die "couldn't delete file $file $!";
   2.123 +	}
   2.124 +	if(rmdir "$dir"){
   2.125 +		return 1;
   2.126 +	}else{
   2.127 +		return 0;
   2.128 +	}
   2.129 +}
   2.130 +
   2.131 +# return current path.
   2.132 +sub cwd {
   2.133 +	local $var;
   2.134 +	chomp($var = `pwd`);
   2.135 +	return $var;
   2.136 +}
   2.137 +
   2.138 +# 
   2.139 +sub copy {
   2.140 +	local ($file1, $file2) = @_;
   2.141 +	open (F1, "$file1") or 
   2.142 +	 	die "couldn't open file $file1 for reading $!\n";
   2.143 +	open (F2, ">$file2") or 
   2.144 +	 	die "couldn't open file $file2 for writing $!\n";
   2.145 +	if ($file1 =~ /\.((bin)|(sys)|(cat))$/) {
   2.146 +		binmode F1;
   2.147 +		binmode F2;
   2.148 +		while (read(F1,$buffer,1)) {
   2.149 +      		print(F2 $buffer);
   2.150 +   		}
   2.151 +	} else {
   2.152 +		while (<F1>) {
   2.153 +			print F2 $_;
   2.154 +		}
   2.155 +	}
   2.156 +	close F1;
   2.157 +	close F2;
   2.158 +}
   2.159 +
   2.160 +##
   2.161 +## End of PERL modules substition
   2.162 +##
   2.163 +
   2.164 +sub remove_driver {
   2.165 +    my $driver = shift;
   2.166 +    if (!rmtree("$confdir/$driver", 0, 1)) {
   2.167 +	warn "couldn't delete $confdir/$driver: $!\n";
   2.168 +    }
   2.169 +    return 0;
   2.170 +}
   2.171 +
   2.172 +sub abort {
   2.173 +    remove_driver($driver_name);
   2.174 +    exit 1;
   2.175 +}
   2.176 +
   2.177 +sub check_version {
   2.178 +    my ($utils_version, $module_utils_version, $res);
   2.179 +    $res = 0;
   2.180 +    $utils_version = `loadndisdriver -v`;
   2.181 +    chomp($utils_version);
   2.182 +    $utils_version =~ s/^version: //;
   2.183 +    if (length($utils_version) eq 0) {
   2.184 +	printf "utils version is too old!\n";
   2.185 +	$res = -1;
   2.186 +    }
   2.187 +    $module_utils_version = 0;
   2.188 +    open(MODINFO, "modinfo ndiswrapper |");
   2.189 +    while (my $line = <MODINFO>) {
   2.190 +	if ($line =~ /utils_version:.*read only:\s([0-9\.]+)/) {
   2.191 +	    $module_utils_version = $1;
   2.192 +	    last;
   2.193 +	}
   2.194 +    }
   2.195 +    if ($module_utils_version eq 0) {
   2.196 +	printf "module version is too old!\n";
   2.197 +	$res = -1;
   2.198 +    } elsif ($utils_version ne $module_utils_version) {
   2.199 +	printf "utils version '%s' is incompatible with utils version needed" .
   2.200 +	  " by driver ('%s')!\n", $utils_version, $module_utils_version;
   2.201 +	$res = -1;
   2.202 +    }
   2.203 +    printf "utils version: '%s', utils version needed by module: '%s'\n",
   2.204 +      $utils_version, $module_utils_version;
   2.205 +    printf "module details:\n";
   2.206 +    system("modinfo ndiswrapper | grep -E '^(version|vermagic|filename)'");
   2.207 +
   2.208 +    if ($res) {
   2.209 +	printf "\nYou may need to upgrade driver and/or utils to latest " .
   2.210 +	  "versions available at\n" .
   2.211 +	  "http://ndiswrapper.sourceforge.net\n";
   2.212 +    }
   2.213 +    return $res;
   2.214 +}
   2.215 +
   2.216 +sub install {
   2.217 +    my $inf = shift;
   2.218 +    chomp($inf);
   2.219 +    $src_dir = dirname($inf);
   2.220 +    $driver_name = lc(basename($inf));
   2.221 +       
   2.222 +    unless ($driver_name =~ s/\.inf$//) {
   2.223 +		die "install argument must be .inf file\n";
   2.224 +    }
   2.225 +
   2.226 +    if (! -d $confdir) {
   2.227 +		mkdir($confdir) or die "couldn't create $confdir: $!";
   2.228 +    }
   2.229 +    (-d "$confdir/$driver_name") and
   2.230 +      die "driver $driver_name is already installed\n";
   2.231 +      
   2.232 +    read_sections($inf);
   2.233 +    parse_section("Strings");
   2.234 +    parse_section("Version");
   2.235 +    parse_source_disks_files();
   2.236 +    mkdir("$confdir/$driver_name") or
   2.237 +      die "couldn't create $confdir/$driver_name: $!";
   2.238 +    print "installing $driver_name ...\n";
   2.239 +    parse_mfr();
   2.240 +    copy_file(basename($inf), basename($inf));
   2.241 +    create_fuzzy_conf($driver_name);
   2.242 +    return 0;
   2.243 +}
   2.244 +
   2.245 +# return lines in section
   2.246 +sub get_section {
   2.247 +    my $name = shift;
   2.248 +    foreach my $key (keys %sections) {
   2.249 +	if (lc($key) eq lc($name)) {
   2.250 +	    printf DBG "section: $key\n";
   2.251 +	    return \@{$sections{$key}};
   2.252 +	}
   2.253 +    }
   2.254 +    printf DBG "couldn't find section \"$name\"\n";
   2.255 +    return 0;
   2.256 +}
   2.257 +
   2.258 +# load inf and split into different sections.
   2.259 +sub read_sections {
   2.260 +    my $filename = shift;
   2.261 +    open(INF, $filename) or die "couldn't open $filename: $!";
   2.262 +
   2.263 +    my $name = "none";
   2.264 +    @{$sections{$name}} = ();
   2.265 +    while (my $line = <INF>) {
   2.266 +	# convert from unicode
   2.267 +	$line =~ s/\xff\xfe//;
   2.268 +	$line =~ s/\0//g;
   2.269 +
   2.270 +	chomp($line);
   2.271 +	$line = trim($line);
   2.272 +	next if ($line =~ /^$/);
   2.273 +	if ($line =~ /^\[(.+)\]/) {
   2.274 +	    $name = $1;
   2.275 +	    @{$sections{$name}} = ();
   2.276 +	} else {
   2.277 +	    push(@{$sections{$name}}, $line);
   2.278 +	}
   2.279 +    }
   2.280 +    close(INF);
   2.281 +    foreach $name (keys %sections) {
   2.282 +	printf DBG "section: %s\n", $name;
   2.283 +	foreach my $line (@{$sections{$name}}) {
   2.284 +	    printf DBG "%s: %s\n", $name, $line;
   2.285 +	}
   2.286 +    }
   2.287 +}
   2.288 +
   2.289 +sub parse_section {
   2.290 +    my $name = shift;
   2.291 +    my $lines = get_section($name);
   2.292 +    if (!$lines) {
   2.293 +	return;
   2.294 +    }
   2.295 +    $parsed_sections{$name} = ();
   2.296 +    foreach my $line (@{$lines}) {
   2.297 +	(my $key, my $val) = parse_key_value($line);
   2.298 +	if ($key) {
   2.299 +	    $val = strip_quotes($val);
   2.300 +	    $parsed_sections{$name}->{$key} = $val;
   2.301 +	    printf DBG "$name: %s = %s\n", $key, $val;
   2.302 +	}
   2.303 +    }
   2.304 +}
   2.305 +
   2.306 +sub parse_mfr() {
   2.307 +    my $lines = get_section("Manufacturer");
   2.308 +    $lines or die "couldn't get manufacturer section - " .
   2.309 +      "installation may be incomplete\n";
   2.310 +    foreach  my $line (@{$lines}) {
   2.311 +	my ($strkey, $val) = parse_key_value($line);
   2.312 +	if ($strkey) {
   2.313 +	    my ($models, @targets) = split(",", $val);
   2.314 +	    if ($models) {
   2.315 +		printf DBG "mfr: %s, %s\n", $line, $models;
   2.316 +		my $target = choose_target_os(@targets);
   2.317 +		printf DBG "target: '%s'\n", $target;
   2.318 +		parse_models($models, $target);
   2.319 +	    }
   2.320 +	}
   2.321 +    }
   2.322 +}
   2.323 +
   2.324 +sub parse_models {
   2.325 +    my ($models, $target) = @_;
   2.326 +    printf DBG "models: target: '%s'.'%s'\n", $models, $target;
   2.327 +    my $lines = get_target_os_section($models, $target);
   2.328 +    if (!$lines) {
   2.329 +	warn "couldn't find models section \"$models\" -\n" .
   2.330 +	  "installation may be incomplete\n";
   2.331 +	return -1;
   2.332 +    }
   2.333 +    foreach my $line (@{$lines}) {
   2.334 +	$line = del_comment($line);
   2.335 +	next if (length($line) eq 0);
   2.336 +	(my $dev_desc, my $val) = parse_key_value($line);
   2.337 +	my @fields = split(",", $val);
   2.338 +	if (@fields le 1) {
   2.339 +	    printf "couldn't find install directive: %s\n", $line;
   2.340 +	    next;
   2.341 +	}
   2.342 +	my $section = trim($fields[0]);
   2.343 +	my $hwid = trim($fields[1]);
   2.344 +	if ($hwid =~ /^%.+%$/) {
   2.345 +	    $hwid = get_string_value($hwid);
   2.346 +	}
   2.347 +	# TODO: deal with compatible IDs as hwid?
   2.348 +	my ($bus_type, $vendor, $device, $subvendor, $subdevice) =
   2.349 +	  parse_hwid($hwid);
   2.350 +	next if (!$vendor);
   2.351 +	printf DBG "models: %s, %s, %s\n", $section, $hwid, $vendor;
   2.352 +	parse_install($section, $target, $bus_type, $vendor, $device,
   2.353 +		      $subvendor, $subdevice);
   2.354 +    }
   2.355 +}
   2.356 +
   2.357 +sub parse_install {
   2.358 +    my ($section, $target, $bus_type, $vendor, $device,
   2.359 +	$subvendor, $subdevice) = @_;
   2.360 +    my $lines = get_target_os_section($section, $target);
   2.361 +    if (!$lines) {
   2.362 +	warn "couldn't find install section \"$section\" -\n" .
   2.363 +	  "installation may be incomplete\n";
   2.364 +	return -1;
   2.365 +    }
   2.366 +
   2.367 +    my $filename = "$vendor:$device";
   2.368 +    if ($subvendor) {
   2.369 +	$filename .= ":$subvendor:$subdevice"
   2.370 +    }
   2.371 +    $filename .= sprintf(".%X.conf", $bus_type);
   2.372 +
   2.373 +    my (@addregs, @copyfiles);
   2.374 +    foreach my $line (@{$lines}) {
   2.375 +	$line =~ s/^;\s*//;
   2.376 +	$line = trim(del_comment($line));
   2.377 +	my ($key, $val) = parse_key_value($line);
   2.378 +	my @array;
   2.379 +	if ($key) {
   2.380 +	    if (lc($key) eq "addreg") {
   2.381 +		@array = split(",", $val);
   2.382 +		foreach my $reg (@array) {
   2.383 +		    push @addregs, trim($reg);
   2.384 +		}
   2.385 +	    } elsif (lc($key) eq "copyfiles") {
   2.386 +		printf DBG "copyfiles: %s\n", $val;
   2.387 +		@array = split(",", $val);
   2.388 +		foreach my $copy_file_dirs (@array) {
   2.389 +		    my @copy_sec = split(",", $copy_file_dirs);
   2.390 +		    foreach my $file (@copy_sec) {
   2.391 +			push @copyfiles, trim($file);
   2.392 +		    }
   2.393 +		}
   2.394 +	    } elsif (lc($key) eq "bustype") {
   2.395 +		printf DBG "bustype: %s\n", $val;
   2.396 +		$bus_type = $val;
   2.397 +	    }
   2.398 +	}
   2.399 +    }
   2.400 +
   2.401 +    open(CONF, ">$confdir/$driver_name/$filename") or
   2.402 +      die "couldn't create file $confdir/$driver_name/$filename: $!";
   2.403 +
   2.404 +    printf CONF "sys_files|";
   2.405 +    foreach my $file (@copyfiles) {
   2.406 +	parse_copy_file($file);
   2.407 +    }
   2.408 +    printf CONF "\n";
   2.409 +
   2.410 +    my $version = get_section_value("Version", "DriverVer");
   2.411 +    my $provider = get_section_value("Version", "Provider");
   2.412 +    my $classguid = get_section_value("Version", "ClassGUID");
   2.413 +    my $providerstring = trim(strip_quotes(get_string_value(trim($provider))));
   2.414 +    $classguid =~ s/^\s*{//;
   2.415 +    $classguid =~ s/}\s*$//;
   2.416 +
   2.417 +    printf CONF "NdisVersion|0x50001\n";
   2.418 +    printf CONF "Environment|1\n";
   2.419 +    printf CONF "class_guid|%s\n", $classguid;
   2.420 +    printf CONF "driver_version|%s,%s\n", $providerstring, $version;
   2.421 +    printf CONF "BusType|%s\n", $bus_type;
   2.422 +    printf CONF "SlotNumber|01\n";
   2.423 +    printf CONF "NetCfgInstanceId|{28022A01-1234-5678-ABCDE-123813291A00}\n";
   2.424 +    printf CONF "\n";
   2.425 +    close(CONF);
   2.426 +
   2.427 +    open(CONF, "|sort|uniq >>$confdir/$driver_name/$filename") or
   2.428 +      die "couldn't create file $confdir/$driver_name/$filename: $!";
   2.429 +
   2.430 +    foreach my $reg (@addregs) {
   2.431 +	parse_registry($reg);
   2.432 +    }
   2.433 +    close(CONF);
   2.434 +}
   2.435 +
   2.436 +sub parse_registry {
   2.437 +    my ($reg, $conf) = @_;
   2.438 +    my $lines = get_section($reg);
   2.439 +    if (!$lines) {
   2.440 +	warn "couldn't find section \"$reg\" -\n" .
   2.441 +	  "installation may be incomplete\n";
   2.442 +	return -1;
   2.443 +    }
   2.444 +
   2.445 +    my $driver_desc = 0;
   2.446 +    foreach my $line (@{$lines}) {
   2.447 +	$line = del_comment($line);
   2.448 +	my @fields = split(",", $line);
   2.449 +	next if (@fields lt 4);
   2.450 +	my $value;
   2.451 +	my $param = trim($fields[1]);
   2.452 +	if ($param =~ /^ndi\\/i) {
   2.453 +	    if ($param =~ /^ndi\\params\\(.+)/i) {
   2.454 +		$param = strip_quotes(trim($1));
   2.455 +		$param =~ s/\\.*$//;
   2.456 +		next if (lc(trim($fields[2])) ne "default");
   2.457 +		$value = strip_quotes(trim($fields[4]));
   2.458 +	    } else {
   2.459 +		printf DBG "ignoring parameter $line\n";
   2.460 +		next;
   2.461 +	    }
   2.462 +	} else {
   2.463 +	    $param = strip_quotes(trim($fields[2]));
   2.464 +	    next if (length($param) eq 0);
   2.465 +	    $value = strip_quotes(trim($fields[4]));
   2.466 +	}
   2.467 +	$value = get_string_value($value);
   2.468 +	if (length($param) gt 0) {
   2.469 +	    if ($param_fixlist{"$param|$value"}) {
   2.470 +		my $orig_value = $value;
   2.471 +		$value = $param_fixlist{"$param|$value"};
   2.472 +		printf "forcing parameter $param from $orig_value to $value\n";
   2.473 +	    }
   2.474 +	    printf CONF "%s|%s\n", $param, $value;
   2.475 +	    if ($param =~ /^DriverDesc$/) {
   2.476 +		$driver_desc = 1;
   2.477 +	    }
   2.478 +	}
   2.479 +    }
   2.480 +    if ($driver_desc == 0) {
   2.481 +	printf CONF "DriverDesc|NDIS Network Adapter\n";
   2.482 +    }
   2.483 +}
   2.484 +
   2.485 +sub parse_copy_file {
   2.486 +    my $copy_name = shift;
   2.487 +
   2.488 +    if ($copy_name =~ /^\@/) {
   2.489 +	$copy_name =~ s/^\@//;
   2.490 +	$copy_name = trim($copy_name);
   2.491 +	if (valid_copy_file_name($copy_name)) {
   2.492 +	    return copy_file($copy_name, $copy_name);
   2.493 +	}
   2.494 +    }
   2.495 +
   2.496 +    my $lines = get_section($copy_name);
   2.497 +    if (!$lines) {
   2.498 +	warn "couldn't find section \"$copy_name\" -\n" .
   2.499 +	  "installation may be incomplete\n";
   2.500 +	return -1;
   2.501 +    }
   2.502 +    foreach my $line (@{$lines}) {
   2.503 +	$line = trim($line);
   2.504 +
   2.505 +	# some inf files have file names commented out; get file names from them
   2.506 +	$line =~ s/^\s*;//;
   2.507 +	my @files = split(",", $line);
   2.508 +	if (@files == 0) {
   2.509 +	    printf DBG "copyfiles section $copy_name has no files\n";
   2.510 +	    return -1;
   2.511 +	}
   2.512 +	my $src, my $dst;
   2.513 +	if (@files > 1 and length(trim($files[1])) > 0) {
   2.514 +	    $src = $files[1];
   2.515 +	    if (length(trim($files[0])) > 0) {
   2.516 +		$dst = $files[0];
   2.517 +	    } else {
   2.518 +		$dst = $src;
   2.519 +	    }
   2.520 +	} else {
   2.521 +	    $src = $files[0];
   2.522 +	    $dst = $src;
   2.523 +	}
   2.524 +	$src =~ s/^.*\\//;
   2.525 +	$dst =~ s/^.*\\//;
   2.526 +	printf DBG "src: '%s', dst: '%s'\n", $src, $dst;
   2.527 +	$src = trim(del_comment($src));
   2.528 +	next if (length($src) eq 0);
   2.529 +	if (valid_copy_file_name($src)) {
   2.530 +	    $dst = trim(del_comment($dst));
   2.531 +	    printf DBG "src: '%s', dst: '%s'\n", $src, $dst;
   2.532 +	    copy_file($src, $dst);
   2.533 +	} else {
   2.534 +	    printf DBG "invalid file '%s' ignored\n", $src;
   2.535 +	}
   2.536 +    }
   2.537 +    return 0;
   2.538 +}
   2.539 +
   2.540 +sub parse_hwid {
   2.541 +    my $hwid = uc(shift);
   2.542 +    if ($hwid =~ /(PCI\\)?VEN_(\w+)&DEV_(\w+)&SUBSYS_(\w{4})(\S{4})/) {
   2.543 +	return ($WRAP_PCI_BUS, $2, $3, $4, $5);
   2.544 +    } elsif ($hwid =~ /(PCI\\)?VEN_(\w+)&DEV_(\w+)/) {
   2.545 +	return ($WRAP_PCI_BUS, $2, $3, 0, 0);
   2.546 +    } elsif ($hwid =~ /(USB\\)?VID_(\w+)&PID_(\w+)/) {
   2.547 +	return ($WRAP_USB_BUS, $2, $3, 0, 0);
   2.548 +    } else {
   2.549 +	return 0;
   2.550 +    }
   2.551 +}
   2.552 +
   2.553 +sub parse_key_value {
   2.554 +    my $line = shift;
   2.555 +
   2.556 +    $line = del_comment($line);
   2.557 +    if ($line =~ /([^=]+)=(.+)/) {
   2.558 +	return (trim($1), trim($2));
   2.559 +    } else {
   2.560 +	return 0;
   2.561 +    }
   2.562 +}
   2.563 +
   2.564 +sub choose_target_os {
   2.565 +    my @targets = @_;
   2.566 +    my $arch = `uname -m`;
   2.567 +    chomp($arch);
   2.568 +    printf DBG "arch: %s\n", $arch;
   2.569 +    if ($arch =~ /64$/) {
   2.570 +	$arch = "amd64";
   2.571 +    } else {
   2.572 +	$arch = "x86";
   2.573 +    }
   2.574 +    printf DBG "arch: %s\n", $arch;
   2.575 +    my @prefs = ("NT($arch)\.5\.1", "NT($arch)\.5", "NT($arch)",
   2.576 +		 "NT\.5\.1", "NT\.5", "NT");
   2.577 +    foreach my $pref (@prefs) {
   2.578 +	foreach my $target (@targets) {
   2.579 +	    $target = trim($target);
   2.580 +	    printf DBG "target: '%s', pref: '%s'\n", $target, $pref;
   2.581 +	    if ($target =~ /NT((amd64)|(x86))/i) {
   2.582 +		printf DBG "target arch: '%s'\n", $1;
   2.583 +		next if ($1 !~ /$arch/i);
   2.584 +	    }
   2.585 +	    if ($target =~ /$pref/i) {
   2.586 +		return $target;
   2.587 +	    }
   2.588 +	}
   2.589 +    }
   2.590 +    return "";
   2.591 +}
   2.592 +
   2.593 +sub get_target_os_section {
   2.594 +    my ($section, $target) = @_;
   2.595 +    my $lines;
   2.596 +
   2.597 +    chomp($section);
   2.598 +    $section =~ s/^\s*"\s*//;
   2.599 +    $section =~ s/\s*"\s*$//;
   2.600 +    printf DBG "section: \"%s\", target: \"%s\"\n", $section, $target;
   2.601 +
   2.602 +    if (length($target) gt 0) {
   2.603 +	$lines = get_section($section . "." . $target);
   2.604 +	return $lines if ($lines);
   2.605 +    }
   2.606 +
   2.607 +    my $arch = `uname -m`;
   2.608 +    chomp($arch);
   2.609 +    printf DBG "arch: %s\n", $arch;
   2.610 +    if ($arch =~ /64$/) {
   2.611 +	$arch = "AMD64";
   2.612 +    } else {
   2.613 +	$arch = "X86";
   2.614 +    }
   2.615 +    printf DBG "arch: %s\n", $arch;
   2.616 +
   2.617 +    my @prefs = ("NT$arch.5.1", "NT$arch.5", "NT$arch",
   2.618 +		 "NT.5.1", "NT.5", "NT");
   2.619 +    foreach my $pref (@prefs) {
   2.620 +	$lines = get_section($section . "." . $pref);
   2.621 +	return $lines if ($lines);
   2.622 +    }
   2.623 +    $lines = get_section($section);
   2.624 +    return $lines if ($lines);
   2.625 +
   2.626 +    printf DBG "couldn't find section \"$section\" for \"$arch\"\n";
   2.627 +    return 0;
   2.628 +}
   2.629 +
   2.630 +sub get_section_value {
   2.631 +    (my $section, my $name) = @_;
   2.632 +    return $parsed_sections{$section}->{$name};
   2.633 +}
   2.634 +
   2.635 +sub get_string_value {
   2.636 +    my $key = shift;
   2.637 +    if ($key =~ /%(.+)%/) {
   2.638 +	$key = $1;
   2.639 +	return get_section_value("Strings", $key);
   2.640 +    } else {
   2.641 +	return $key;
   2.642 +    }
   2.643 +}
   2.644 +
   2.645 +sub copy_file {
   2.646 +    my ($src, $dst) = @_;
   2.647 +
   2.648 +    # ignore files not needed
   2.649 +    return 0 if (lc($src) =~ /\.((exe)|(dll)|(cpl)|(hlp))$/);
   2.650 +    my $real_file = get_file($src);
   2.651 +    if (length($real_file) gt 0) {
   2.652 +	$dst = lc($dst);
   2.653 +	printf DBG "copying \"$src_dir/$real_file\" to " .
   2.654 +	  "\"$confdir/$driver_name/$dst\"\n";
   2.655 +	copy("$src_dir/$real_file", "$confdir/$driver_name/$dst") or
   2.656 +	  warn "couldn't copy \"$src_dir/$real_file\" to " .
   2.657 +	    "\"$confdir/$driver_name\": $! -\n" .
   2.658 +	      "installation may be incomplete\n";
   2.659 +	printf DBG "chmod: $confdir/$driver_name/$dst\n";
   2.660 +	chmod(0644, "$confdir/$driver_name/$dst");
   2.661 +	if ($dst =~ /\.sys$/) {
   2.662 +	    printf CONF "%s ", $dst;
   2.663 +	}
   2.664 +    } else {
   2.665 +	warn "couldn't find \"$src\" in \"$src_dir\"; make sure " .
   2.666 +	  "all driver files, including .inf, .sys (and any firmware files) " .
   2.667 +	    "are in \"$src_dir\" -\n" .
   2.668 +	      "installation may be incomplete\n";
   2.669 +    }
   2.670 + }
   2.671 +
   2.672 +
   2.673 +# for conf files with subvendor and subdevice, create conf files with just
   2.674 +# vendor and device
   2.675 +sub create_fuzzy_conf {
   2.676 +    my $driver = shift;
   2.677 +    my $cwd = cwd();
   2.678 +    chdir("$confdir/$driver") or die "couldn't chdir to $confdir/$driver: $!";
   2.679 +    open(LS, "ls -1 . |") or die "couldn't open $confdir/$driver: $!";
   2.680 +    while (my $file = <LS>) {
   2.681 +	chomp($file);
   2.682 +	if ($file =~ /$re_sub_dev_conf/) {
   2.683 +	    my $fuzzy_file = "$1:$2.$5.conf";
   2.684 +	    printf DBG "file: $file, fuzzy file: $fuzzy_file\n";
   2.685 +	    if (! -e "$confdir/$driver/$fuzzy_file") {
   2.686 +		symlink("$file", "$fuzzy_file") or
   2.687 +		  warn "couldn't link $confdir/$driver/$file " .
   2.688 +		    "to $confdir/$driver/$fuzzy_file: $!\n";
   2.689 +	    }
   2.690 +	}
   2.691 +    }
   2.692 +    close(LS);
   2.693 +    chdir($cwd) or warn "couldn't chdir to $cwd: $!";
   2.694 +    return 0;
   2.695 +}
   2.696 +
   2.697 +# find a file in a case-insensitive way.
   2.698 +sub get_file {
   2.699 +    my $file = lc(shift);
   2.700 +    if (opendir(DIR, "$src_dir")) {
   2.701 +	my @allfiles = readdir(DIR);
   2.702 +	foreach my $real_file (@allfiles) {
   2.703 +	    if (lc($real_file) eq $file) {
   2.704 +		closedir(DIR);
   2.705 +		return $real_file;
   2.706 +	    }
   2.707 +	}
   2.708 +	closedir(DIR);
   2.709 +    } else {
   2.710 +	warn "couldn't open \"$src_dir\": $! -\n" .
   2.711 +	  "installation may be incomplete\n";
   2.712 +    }
   2.713 +    return "";
   2.714 +}
   2.715 +
   2.716 +sub strip_quotes {
   2.717 +    my $s = shift;
   2.718 +    $s =~ s/"(.*)"/$1/;
   2.719 +    return $s;
   2.720 +}
   2.721 +
   2.722 +sub del_comment {
   2.723 +    my $s = shift;
   2.724 +    $s =~ s/;.*//;
   2.725 +    return $s;
   2.726 +}
   2.727 +
   2.728 +# remove whitsepace at front and end.
   2.729 +sub trim {
   2.730 +    my $s = shift;
   2.731 +    $s =~ s/^\s*//;
   2.732 +    $s =~ s/\s*$//;
   2.733 +    return $s;
   2.734 +}
   2.735 +
   2.736 +sub valid_copy_file_name {
   2.737 +    my $file = shift;
   2.738 +    $file = lc($file);
   2.739 +    printf DBG "file name: %s\n", $file;
   2.740 +    foreach my $disk_file (@source_disks_files) {
   2.741 +	return 1 if ($file eq $disk_file);
   2.742 +    }
   2.743 +    # some inf files may not have SourceDisksFiles section, so use
   2.744 +    # known file names
   2.745 +    return 1 if ($file =~ /\.((sys)|(bin)|(out))$/);
   2.746 +    return 0;
   2.747 +}
   2.748 +
   2.749 +sub parse_source_disks_files {
   2.750 +    my $lines = get_source_disks_files();
   2.751 +    if ($lines) {
   2.752 +	foreach my $line (@{$lines}) {
   2.753 +	    $line = del_comment($line);
   2.754 +	    next if (length($line) eq 0);
   2.755 +	    my @file = split("=", $line);
   2.756 +	    next if (@file eq 0 or length($file[0] eq 0));
   2.757 +	    printf DBG "source disk file: \"%s\"\n", trim($file[0]);
   2.758 +	    push (@source_disks_files, lc(trim($file[0])));
   2.759 +	}
   2.760 +    } else {
   2.761 +	warn "couldn't find SourceDisksFiles section - " .
   2.762 +	  "continuing anyway...\n";
   2.763 +    }
   2.764 +}
   2.765 +
   2.766 +sub get_source_disks_files {
   2.767 +    my $arch = `uname -m`;
   2.768 +    chomp($arch);
   2.769 +    if ($arch =~ /64$/) {
   2.770 +	$arch = "AMD64";
   2.771 +    } else {
   2.772 +	$arch = "X86";
   2.773 +    }
   2.774 +
   2.775 +    my $lines = get_section("SourceDisksFiles." . $arch);
   2.776 +    return $lines if ($lines);
   2.777 +
   2.778 +    $lines = get_section("SourceDisksFiles");
   2.779 +    return $lines if ($lines);
   2.780 +
   2.781 +    return 0;
   2.782 +}
   2.783 +
   2.784 +sub device_driver_alias {
   2.785 +    my ($devid, $driver) = @_;
   2.786 +    my $done = 0;
   2.787 +
   2.788 +    $devid = uc($devid);
   2.789 +    if (!($devid =~ /^$re_dev_id:$re_dev_id$/)) {
   2.790 +	printf "'$devid' is not a valid device ID\n";
   2.791 +	return 1;
   2.792 +    }
   2.793 +    open(LS, "ls -1 $confdir/$driver/ |") or
   2.794 +      die "couldn't open $confdir/$driver: $!";
   2.795 +
   2.796 +    while (my $f = <LS>) {
   2.797 +	chomp($f);
   2.798 +	if ($f =~ /\.([[:xdigit:]]+)\.conf$/) {
   2.799 +	    if (stat("$confdir/$driver/$devid.$1.conf")) {
   2.800 +		printf "Driver '$driver' is already used for '$devid'\n";
   2.801 +		$done = 1;
   2.802 +		last;
   2.803 +	    }
   2.804 +	    if (symlink("$f", "$confdir/$driver/$devid.$1.conf")) {
   2.805 +		printf "WARNING: Driver '$driver' will be used for '$devid'\n" .
   2.806 +		  "This is safe _only_ if driver $driver is meant for " .
   2.807 +		    "chip in device $devid\n";
   2.808 +		$done = 1;
   2.809 +		last;
   2.810 +	    } else {
   2.811 +		warn "couldn't create symlink for \"$f\": $! -\n" .
   2.812 +		  "installation may be incomplete\n";
   2.813 +	    }
   2.814 +	}
   2.815 +    }
   2.816 +    close(LS);
   2.817 +    if ($done == 0) {
   2.818 +	printf "driver '$driver' is not installed (properly)!\n";
   2.819 +	return 1;
   2.820 +    }
   2.821 +    return 0;
   2.822 +}
   2.823 +
   2.824 +sub list_drivers {
   2.825 +    my $cards = get_cards();
   2.826 +
   2.827 +    open(LS, "/bin/ls -1 $confdir|") or die "couldn't open $confdir: $!";
   2.828 +    while (my $driver = <LS>) {
   2.829 +	chomp($driver);
   2.830 +	if (-e "$confdir/$driver") {
   2.831 +	    printf "%s : %s\n", $driver, install_status($cards, $driver);
   2.832 +	}
   2.833 +    }
   2.834 +    close(LS);
   2.835 +    return 0;
   2.836 +}
   2.837 +
   2.838 +
   2.839 +sub get_cards {
   2.840 +#01:00.0 Class 0300: 1002:4c66 (rev 01)
   2.841 +#        Subsystem: 1043:1732
   2.842 +    my @cards = ();
   2.843 +    if (open(LSPCI, "/usr/bin/lspci -vn|")) {
   2.844 +	my $card;
   2.845 +	while (my $line = <LSPCI>) {
   2.846 +	    if ($line =~ /^[0-9a-f]+.+\s$re_dev_id:$re_dev_id/) {
   2.847 +		$card = {vendor => uc($1), device => uc($2)};
   2.848 +		printf DBG "card: %s, %s\n", $1, $2;
   2.849 +	    } elsif ($line =~ /.+Subsystem:\s$re_dev_id:$re_dev_id/) {
   2.850 +		$card->{subvendor} = uc($1);
   2.851 +		$card->{subdevice} = uc($2);
   2.852 +		printf DBG "sub: %s, %s\n", $1, $2;
   2.853 +		push(@cards, $card);
   2.854 +	    }
   2.855 +	}
   2.856 +	close(LSPCI);
   2.857 +    }
   2.858 +
   2.859 +    if (open(LSUSB, "lsusb |")) {
   2.860 +	my $card;
   2.861 +	while (my $line = <LSUSB>) {
   2.862 +	    if ($line =~ /.+: ID\s$re_dev_id:$re_dev_id/) {
   2.863 +		$card = {vendor => uc($1), device => uc($2)};
   2.864 +		push(@cards, $card);
   2.865 +	    }
   2.866 +	}
   2.867 +	close(LSUSB);
   2.868 +    }
   2.869 +    return \@cards;
   2.870 +}
   2.871 +
   2.872 +sub install_status {
   2.873 +    my ($cards, $driver) = @_;
   2.874 +
   2.875 +    if (!$cards or !$driver) {
   2.876 +	return;
   2.877 +    }
   2.878 +
   2.879 +    my ($sys, $conf, $inf);
   2.880 +    my ($vendor, $device, $subvendor, $subdevice, $busid, $ret);
   2.881 +
   2.882 +    $sys = $conf = $inf = 0;
   2.883 +    open(LS2, "/bin/ls -1 $confdir/$driver|") or
   2.884 +      die "couldn't open $confdir/$driver: $!";
   2.885 +    while (my $file = <LS2>) {
   2.886 +	chomp($file);
   2.887 +	if ($file =~ /\.sys$/) {
   2.888 +	    $sys = 1;
   2.889 +	} elsif ($file =~ /\.inf$/) {
   2.890 +	    $inf = 1;
   2.891 +	} elsif ($file =~ /^$re_sub_dev_conf$/) {
   2.892 +	    $busid = hex($5);
   2.893 +	    $conf = 1 if ($busid eq $WRAP_PCI_BUS);
   2.894 +	} elsif ($file =~ /^$re_dev_conf$/) {
   2.895 +	    $busid = hex($3);
   2.896 +	    $conf = 1 if ($busid eq $WRAP_USB_BUS or $busid eq 0 or
   2.897 +			  $busid eq $WRAP_PCI_BUS);
   2.898 +	}
   2.899 +    }
   2.900 +    close(LS2);
   2.901 +    printf DBG "status: $sys, $inf, $conf\n";
   2.902 +    if ($sys eq 0 or $inf eq 0 or $conf eq 0) {
   2.903 +	$ret = "invalid driver!";
   2.904 +	return $ret;
   2.905 +    }
   2.906 +    $ret = "driver installed";
   2.907 +    open(LS2, "/bin/ls -1 $confdir/$driver|") or
   2.908 +      die "couldn't open $confdir/$driver: $!";
   2.909 +
   2.910 +    while (my $file = <LS2>) {
   2.911 +	chomp($file);
   2.912 +	next if ($file !~ /\.conf$/);
   2.913 +	$conf = 0;
   2.914 +	if ($file =~ /^$re_sub_dev_conf$/) {
   2.915 +	    ($vendor, $device, $subvendor, $subdevice, $busid) =
   2.916 +	      (uc($1), uc($2), uc($3), uc($4), hex($5));
   2.917 +	    $conf = 1;
   2.918 +	    foreach my $card (@{$cards}) {
   2.919 +		if ($card->{vendor} eq $vendor and
   2.920 +		    $card->{device} eq $device and
   2.921 +		    $card->{subvendor} eq $subvendor and
   2.922 +		    $card->{subdevice} eq $subdevice and
   2.923 +		    $busid eq $WRAP_PCI_BUS) {
   2.924 +		    $ret .= "\n\tdevice ($vendor:$device" .
   2.925 +		      ":$subvendor:$subdevice) present";
   2.926 +		    $conf = 2;
   2.927 +		    last;
   2.928 +		}
   2.929 +	    }
   2.930 +	} elsif ($file =~ /^$re_dev_conf/) {
   2.931 +	    ($vendor, $device, $subvendor, $subdevice, $busid) =
   2.932 +	      (uc($1), uc($2), "\\*", "\\*", hex($3));
   2.933 +	    $conf = 1;
   2.934 +	    foreach my $card (@{$cards}) {
   2.935 +		if ($card->{vendor} eq $vendor and
   2.936 +		    $card->{device} eq $device and
   2.937 +		    ($busid eq $WRAP_USB_BUS or $busid eq 0 or
   2.938 +		     $busid eq $WRAP_PCI_BUS)) {
   2.939 +		    $ret .= "\n\tdevice ($vendor:$device) present";
   2.940 +		    $conf = 2;
   2.941 +		    last;
   2.942 +		}
   2.943 +	    }
   2.944 +	}
   2.945 +	next if ($conf le 1);
   2.946 +	# find if kernel knows of an alternate driver for this device
   2.947 +	my $devstring;
   2.948 +	if ($busid eq $WRAP_USB_BUS or $busid eq 0) {
   2.949 +	    $devstring = sprintf("usb:v%sp%sd", $vendor, $device);
   2.950 +	} elsif ($busid eq $WRAP_PCI_BUS) {
   2.951 +	    $devstring = sprintf("pci:v0000%sd0000%ssv", $vendor, $device);
   2.952 +	} else {
   2.953 +	    next;
   2.954 +	}
   2.955 +	open(MODPROBE, "modprobe -c|") or next;
   2.956 +	while (my $line = <MODPROBE>) {
   2.957 +	    my $alt;
   2.958 +	    chomp($line);
   2.959 +	    next if $line !~ /$devstring/;
   2.960 +	    $alt = (split(' ', $line))[-1];
   2.961 +	    chomp($alt);
   2.962 +	    if (length($alt) gt 0 and $alt ne "ndiswrapper") {
   2.963 +		$ret .= " (alternate driver: $alt)";
   2.964 +		last;
   2.965 +	    }
   2.966 +	}
   2.967 +	close(MODPROBE);
   2.968 +    }
   2.969 +    close(LS2);
   2.970 +    printf DBG "driver: $driver, $ret\n";
   2.971 +    return $ret;
   2.972 +}
   2.973 +
   2.974 +## Local Variables: ##
   2.975 +## cperl-indent-level: 4 ##
   2.976 +## End: ##