#!/usr/bin/perl -w
#
# Debhelper option processing library.
#
# Joey Hess GPL copyright 1998-2002

package Debian::Debhelper::Dh_Getopt;
use strict;

use Debian::Debhelper::Dh_Lib;
use Getopt::Long;

my %exclude_package;

sub showhelp {
	my $prog=basename($0);
	print "Usage: $prog [options]\n\n";
	print "  $prog is a part of debhelper. See debhelper(7)\n";
	print "  and $prog(1) for complete usage instructions.\n"; 
	exit(1);
}

# Passed an option name and an option value, adds packages to the list
# of packages. We need this so the list will be built up in the right
# order.
sub AddPackage { my($option,$value)=@_;
	if ($option eq 'i' or $option eq 'indep') {
		push @{$dh{DOPACKAGES}}, getpackages('indep');
		$dh{DOINDEP}=1;
	}
	elsif ($option eq 'a' or $option eq 'arch' or
	       $option eq 's' or $option eq 'same-arch') {
		push @{$dh{DOPACKAGES}}, getpackages('arch');
		$dh{DOARCH}=1;
	}
	elsif ($option eq 'p' or $option eq 'package') {
		push @{$dh{DOPACKAGES}}, $value;
	}
	else {
		error("bad option $option - should never happen!\n");
	}
}

# Adds packages to the list of debug packages.
sub AddDebugPackage { my($option,$value)=@_;
	push @{$dh{DEBUGPACKAGES}}, $value;
}

# Add a package to a list of packages that should not be acted on.
sub ExcludePackage { my($option,$value)=@_;
	$exclude_package{$value}=1;
}

# Add another item to the exclude list.
sub AddExclude { my($option,$value)=@_;
	push @{$dh{EXCLUDE}},$value;
}

# Add a file to the ignore list.
sub AddIgnore { my($option,$file)=@_;
	$dh{IGNORE}->{$file}=1;
}

# This collects non-options values.
sub NonOption {
	push @{$dh{ARGV}}, @_;
}

sub getoptions {
	my $array=shift;
	my %params=@_;

	if (! exists $params{bundling} || $params{bundling}) {
		Getopt::Long::config("bundling");
	}

	my @test;
	my %options=(	
		"v" => \$dh{VERBOSE},
		"verbose" => \$dh{VERBOSE},

		"no-act" => \$dh{NO_ACT},
	
		"i" => \&AddPackage,
		"indep" => \&AddPackage,
	
		"a" => \&AddPackage,
		"arch" => \&AddPackage,
	
		"p=s" => \&AddPackage,
	        "package=s" => \&AddPackage,
		
		"N=s" => \&ExcludePackage,
		"no-package=s" => \&ExcludePackage,
	
		"remaining-packages" => \$dh{EXCLUDE_LOGGED},
	
		"dbg-package=s" => \&AddDebugPackage,
		
		"s" => \&AddPackage,
		"same-arch" => \&AddPackage,
	
		"n" => \$dh{NOSCRIPTS},
		"noscripts" => \$dh{NOSCRIPTS},
		"o" => \$dh{ONLYSCRIPTS},
		"onlyscripts" => \$dh{ONLYSCRIPTS},

		"X=s" => \&AddExclude,
		"exclude=s" => \&AddExclude,
		
		"d" => \$dh{D_FLAG},
	
		"k" => \$dh{K_FLAG},
		"keep" => \$dh{K_FLAG},

		"P=s" => \$dh{TMPDIR},
		"tmpdir=s" => \$dh{TMPDIR},

		"u=s", => \$dh{U_PARAMS},

		"V:s", => \$dh{V_FLAG},

		"A" => \$dh{PARAMS_ALL},
		"all" => \$dh{PARAMS_ALL},
	
		"priority=s" => \$dh{PRIORITY},
		
		"h|help" => \&showhelp,

		"mainpackage=s" => \$dh{MAINPACKAGE},

		"name=s" => \$dh{NAME},

		"error-handler=s" => \$dh{ERROR_HANDLER},
		
		"ignore=s" => \&AddIgnore,

		"O=s" => sub { push @test, $_[1] },
	      
		(ref $params{options} ? %{$params{options}} : ()) ,

		"<>" => \&NonOption,
	);

	if ($params{test}) {
		foreach my $key (keys %options) {
			$options{$key}=sub {};
		}
	}

	my $oldwarn;
	if ($params{test} || $params{ignore_unknown_options}) {
		$oldwarn=$SIG{__WARN__};
		$SIG{__WARN__}=sub {};
	}
	my $ret=Getopt::Long::GetOptionsFromArray($array, %options);
	if ($oldwarn) {
		$SIG{__WARN__}=$oldwarn;
	}

	foreach my $opt (@test) {
		# Try to parse an option, and skip it
		# if it is not known.
		if (getoptions([$opt], %params,
				ignore_unknown_options => 0,
				test => 1)) {
			getoptions([$opt], %params);
		}
	}

	return 1 if $params{ignore_unknown_options};
	return $ret;
}

sub split_options_string {
	my $str=shift;
	$str=~s/^\s+//;
	return split(/\s+/,$str);
}

# Parse options and set %dh values.
sub parseopts {
	my %params=@_;
	
	my @ARGV_extra;

	# DH_INTERNAL_OPTIONS is used to pass additional options from
	# dh through an override target to a command.
	if (defined $ENV{DH_INTERNAL_OPTIONS}) {
		@ARGV_extra=split(/\x1e/, $ENV{DH_INTERNAL_OPTIONS});
		getoptions(\@ARGV_extra, %params);

		# Avoid forcing acting on packages specified in
		# DH_INTERNAL_OPTIONS. This way, -p can be specified
		# at the command line to act on a specific package, but when
		# nothing is specified, the excludes will cause the set of
		# packages DH_INTERNAL_OPTIONS specifies to be acted on.
		if (defined $dh{DOPACKAGES}) {
			foreach my $package (getpackages()) {
				if (! grep { $_ eq $package } @{$dh{DOPACKAGES}}) {
					$exclude_package{$package}=1;
				}
			}
		}
		delete $dh{DOPACKAGES};
		delete $dh{DOINDEP};
		delete $dh{DOARCH};
	}
	
	# DH_OPTIONS can contain additional options to be parsed like @ARGV
	if (defined $ENV{DH_OPTIONS}) {
		@ARGV_extra=split_options_string($ENV{DH_OPTIONS});
		my $ret=getoptions(\@ARGV_extra, %params);
		if (!$ret) {
			warning("warning: ignored unknown options in DH_OPTIONS");
		}
	}

	my $ret=getoptions(\@ARGV, %params);
	if (!$ret) {
		warning("warning: unknown options will be a fatal error in a future debhelper release");
		#error("unknown option; aborting");
	}

	# Check to see if -V was specified. If so, but no parameters were
	# passed, the variable will be defined but empty.
	if (defined($dh{V_FLAG})) {
		$dh{V_FLAG_SET}=1;
	}
	
	# If we have not been given any packages to act on, assume they
	# want us to act on them all. Note we have to do this before excluding
	# packages out, below.
	if (! defined $dh{DOPACKAGES} || ! @{$dh{DOPACKAGES}}) {
		if ($dh{DOINDEP} || $dh{DOARCH}) {
			# User specified that all arch (in)dep package be
			# built, and there are none of that type.
			warning("You asked that all arch in(dep) packages be built, but there are none of that type.");
			exit(0);
		}
		push @{$dh{DOPACKAGES}},getpackages();
	}

	# Remove excluded packages from the list of packages to act on.
	# Also unique the list, in case some options were specified that
	# added a package to it twice.
	my @package_list;
	my $package;
	my %packages_seen;
	foreach $package (@{$dh{DOPACKAGES}}) {
		if (defined($dh{EXCLUDE_LOGGED}) &&
		    grep { $_ eq basename($0) } load_log($package)) {
			$exclude_package{$package}=1;
		}
		if (! $exclude_package{$package}) {
			if (! exists $packages_seen{$package}) {
				$packages_seen{$package}=1;
				push @package_list, $package;	
			}
		}
	}
	@{$dh{DOPACKAGES}}=@package_list;

	if (! defined $dh{DOPACKAGES} || ! @{$dh{DOPACKAGES}}) {
		warning("No packages to build.");
		exit(0);
	}

	if (defined $dh{U_PARAMS}) {
	        # Split the U_PARAMS up into an array.
        	my $u=$dh{U_PARAMS};
        	undef $dh{U_PARAMS};
                push @{$dh{U_PARAMS}}, split(/\s+/,$u);
        }

	# Anything left in @ARGV is options that appeared after a --
	# These options are added to the U_PARAMS array, while the
	# non-option values we collected replace them in @ARGV;
	push @{$dh{U_PARAMS}}, @ARGV, @ARGV_extra;
	@ARGV=@{$dh{ARGV}} if exists $dh{ARGV};
}

1
